Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

annotations and sorting for activities-completing-read #83

Merged
merged 49 commits into from
Dec 25, 2024

Conversation

jdtsmith
Copy link
Contributor

@jdtsmith jdtsmith commented Apr 20, 2024

(updated) This provides custom annotation and sorting functions for activities-completing-read. It shows the number of buffers (and files), for the last or default state, a magit-style color-coded activity age, and flags for being active or having a modified set of buffers.

This is shown below with vertico but it should support any UI which handles annotation-functions (most do). For theme friendliness, it creates no new faces, instead borrowing the vc-annotate color palette building its own color palette (along with success and warning faces). Happy to take any input/ideas.

image

Copy link
Owner

@alphapapa alphapapa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this. Here are a few comments.

activities.el Outdated Show resolved Hide resolved
activities.el Outdated Show resolved Hide resolved
activities.el Outdated Show resolved Hide resolved
activities.el Outdated Show resolved Hide resolved
activities.el Outdated Show resolved Hide resolved
activities.el Outdated Show resolved Hide resolved
activities.el Outdated Show resolved Hide resolved
@jdtsmith
Copy link
Contributor Author

BTW, are there other pieces of information inside an activity that would be relevant to show that occur to you?

@alphapapa
Copy link
Owner

BTW, are there other pieces of information inside an activity that would be relevant to show that occur to you?

I can't think of any. For myself, the only one that matters is last-used time, anyway. Let's keep this as simple as possible.

@jdtsmith
Copy link
Contributor Author

I could show both default and last ages, but that might be confusing.

Along that line, do you think having the default and last buf+file cnt is useful or TMI? Another approach would be to use some indicator (like *) that the state is "dirty" in the sense that its display buffer list has changed from the default. So buffer cnt + file cnt + dirty flag + last mod age.

@alphapapa
Copy link
Owner

I could show both default and last ages, but that might be confusing.

I don't think that's necessary. This is just for completing-read, not for "management."

Along that line, do you think having the default and last buf+file cnt is useful or TMI? Another approach would be to use some indicator (like *) that the state is "dirty" in the sense that its display buffer list has changed from the default. So buffer cnt + file cnt + dirty flag + last mod age.

For me, it's TMI. I select activities by name, not by a number of how many windows or files are visible in it. I can't think of a situation where I would need to know that.

However, knowing whether an activity has a last-used state could be useful, I think.

@jdtsmith
Copy link
Contributor Author

jdtsmith commented Apr 23, 2024

For me, it's TMI.

I do think I agree. I like having the last buf/file count, since I use multiple "similar" activities, some richer than others (based on screen size). But for the default counts, I'm just comparing to see if they changed at all (i.e. computing a "dirty" flag in my head). For such a flag, what would you recommend? I was thinking "same number of buffers and same buffer names = not dirty".

Update, like this:

image

@alphapapa
Copy link
Owner

I'm not sure what you mean by "dirty." For me what would be interesting is whether the activity has a last-used state or just the default.

@jdtsmith
Copy link
Contributor Author

Sorry, I'm not sure I understand your idea. If you have activity auto-saving on (which is the default), an activity will pretty much always have a "last" state, even if it's only lightly modified from the default (different window points, sizes, etc.). So my idea of "dirty" is if the last state differs is an interesting way from the default — i.e. different buffers showing.

I did need to accommodate a missing "last" state, but that's a pretty rare case for me, like when I've just defined a new state.

@alphapapa
Copy link
Owner

If you have activity auto-saving on (which is the default), an activity will pretty much always have a "last" state, even if it's only lightly modified from the default (different window points, sizes, etc.) ... I did need to accommodate a missing "last" state, but that's a pretty rare case for me, like when I've just defined a new state.

I often use activities-kill, which means that the activity won't have a last-used state.

So my idea of "dirty" is if the last state differs is an interesting way from the default — i.e. different buffers showing.

I feel like that concept might be hard to communicate to the user, but if you want to add an asterisk or something and document it to mean that, I won't object.

@jdtsmith
Copy link
Contributor Author

OK sounds good; lack of asterisk would also mean "no last state".

One oddity I've found: when I revert an activity (which I use instead of kill), I'd expect its last state to be removed. It isn't, and sometimes as a result there are two distinct sets of buffer names for the last and default state, even right after revert runs. This happens if a buffer is a duplicate which has a uniquifyd name. I.e. last might have main.c while default has main.c<proj>. Have you seen this? Perhaps revert should just remove last, like kill does.

@alphapapa
Copy link
Owner

One oddity I've found: when I revert an activity (which I use instead of kill), I'd expect its last state to be removed. It isn't, and sometimes as a result there are two distinct sets of buffer names for the last and default state, even right after revert runs. This happens if a buffer is a duplicate which has a uniquifyd name. I.e. last might have main.c while default has main.c<proj>. Have you seen this? Perhaps revert should just remove last, like kill does.

I can see pros and cons on either side. Please feel free to open a new issue about that if you like.

@jdtsmith
Copy link
Contributor Author

Thanks. I've pushed the * flag and a README update. Let me know if you see anything else that needs addressing.

Copy link
Owner

@alphapapa alphapapa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Also, could you post a final screenshot showing what it looks like with the asterisk?

activities.el Outdated Show resolved Hide resolved
activities.el Outdated Show resolved Hide resolved
activities.el Outdated Show resolved Hide resolved
activities.el Outdated Show resolved Hide resolved
@jdtsmith jdtsmith force-pushed the activity-annotate branch 2 times, most recently from 3e2e13a to 6c26c59 Compare April 23, 2024 23:49
@jdtsmith
Copy link
Contributor Author

Here's how it's looking (note the final * for modified activities):

image

Couple things I think still need a bit of thought:

  1. Active activities get saved automatically, which always updates their time. I guess that could be considered OK, since it shows the activity being continuously saved, but when there are no changes to any of the activity's configuration, it is confusing that the activity age is updated. I suppose we could replace the age annotation information with <active> for all active activities.
  2. I had assumed activities were being sorted most recent first, since that's how mine had sorted. But as the screenshot shows, this isn't the case. Easy to do by pre-sorting the names based on recency. Let me know if that should be a separate PR.

@alphapapa
Copy link
Owner

Thanks for the screenshot.

Couple things I think still need a bit of thought:

1. Active activities get saved automatically, which always updates their `time`.  I guess that could be considered OK, since it shows the activity being continuously saved, but when there are no changes to any of the activity's configuration, it is confusing that the activity age is updated.   I suppose we could replace the age annotation information with `<active>` for all active activities.

Well, I do think that active activities should be marked in some way; it would seem like an obviously missing feature otherwise, IMO.

Replacing the age with some other string to indicate its being active would be one possibility. OTOH the multiple colors already make that column a bit busy-looking. I'm not sure what the best solution is.

We should probably also keep in mind potential window width in terms of the multiple columns of information. Have you tested with a half-screen-width window?

2. I had assumed activities were being sorted most recent first, since that's how mine had sorted.  But as the screenshot shows, this isn't the case.  Easy to do by pre-sorting the names based on recency.  Let me know if that should be a separate PR.

I think a customizable sorting option should be part of this PR, if you're willing to work on it. Having the age column but not sorting by it seems weird. I'd guess that active ones should be sorted first, then the rest by age.

There's also https://github.com/alphapapa/frecency.el, which might be good to use. But I think that library should be rewritten; I've learned a lot since I wrote it, and its API is a bit awkward, so I wouldn't suggest using it now. But if the sorting function were customizeable, it could be used later.

@jdtsmith
Copy link
Contributor Author

I do think that active activities should be marked in some way; it would seem like an obviously missing feature otherwise, IMO.

Yeah, agree. Perhaps an <A> before the buffer count? Or maybe better, change the codes up:

  • *=active, modified
  • +=inactive, modified
  • -=active, unmodified
  • =inactive, unmodified

Have you tested with a half-screen-width window?

With the flex-spacer it's responsive to small widths:

image

If we like it better, could measure the maximum activity width and pile everything up left (but still aligned). I tend to prefer flush right.

Will work on a simple sorting scheme.

@jdtsmith
Copy link
Contributor Author

Something else: window-state-buffers differ depending on whether that state is loaded and active, or not:

  1. Buffers which get renamed by uniquify are updated in the last state to their name<dir> versions.
  2. Buffers visiting symlinked files are named after the symlink (e.g. .emacs) in default, but the resolved file (e.g. init.el) in last (or maybe whichever state was loaded).

This leads to false-positive modified states. Both of these mean I should check for equivalent files (if available) or buffer names (if not).

@jdtsmith jdtsmith force-pushed the activity-annotate branch from 311b042 to b67a3f5 Compare April 24, 2024 15:45
@jdtsmith
Copy link
Contributor Author

jdtsmith commented Apr 24, 2024

OK I've got something I'm reasonably happy with, with @ meaning active, * meaning modified. I've also fixed the modification check to compare true filenames and then buffers as a backup, and it fixes the 2 corner cases mentioned above. Here it is for the absurdly small window width of 45 chars:

image

Obviously still need to sort, and couple of small structural bothers:

  1. When you revert, since last is not immediately updated or removed, the activity still shows up as modified for some time (see Reverting an activity should either remove the last state or immediately save #86).
  2. As mentioned above, active activities have their timestamps continuously incremented, so they will all mention the same 5 seconds age (or something small). It would be better of course to update the timestamp (and data) for a given activity only if something actually changed in the window state. I confirmed that cl-tree-equal works on window-states.

@jdtsmith
Copy link
Contributor Author

jdtsmith commented Apr 24, 2024

I got sort working, but I forgot that vertico substitutes it's own "most recent used then shortest candidate" sort function. You can disable it by let-binding vertico-sort-function nil but that's a bit odd to throw in. Thoughts?

Update: Got this working by factoring out a new completion-table and setting display-sort-function metadata, which vertico (and presumably others) respect.

@jdtsmith jdtsmith changed the title Initial annotation support for activities-completing-read annotations and sorting for activities-completing-read Apr 25, 2024
@jdtsmith
Copy link
Contributor Author

Have you ever seen the persist file disappear? It's happened to me twice in the past few days while hacking on this. I note that persist removes the file if the value is ever the default value on save (nil). I don't see any reason it would be getting set to nil, but perhaps edebug is involved. Auto-save is on.

@alphapapa
Copy link
Owner

Have you ever seen the persist file disappear? It's happened to me twice in the past few days while hacking on this. I note that persist removes the file if the value is ever the default value on save (nil). I don't see any reason it would be getting set to nil, but perhaps edebug is involved. Auto-save is on.

It has happened to me with another package, but only when I used my unpackaged/reload-package command to reload the package's symbols after it was already loaded. Since I stopped doing that, it hasn't happened again. I'm guessing there's some kind of unexpected state and actions that can happen when developing if it involves reloading the file in which the variable is defined, or if C-M-x is used on the variable's definition. So AFAICT it shouldn't ever affect users, but developers could find it quite inconvenient. It would probably be worth filing an issue about and trying to nail it down and possibly fix it.

@jdtsmith
Copy link
Contributor Author

OK, see #87. Give the sort and active marker a look and let me know what you think about preventing the time-tick from updating unless "things have changed".

@jdtsmith
Copy link
Contributor Author

jdtsmith commented May 3, 2024

Anything further needed here? If you like I can look into updating activity timestamp on save only if buffers changed.

Introduces new custom variable `activities-annotation-colors'.
These are the ages that are actually showing during completion.
@jdtsmith
Copy link
Contributor Author

I have the "keep email address private" option selected, which means github refuses pushes with your actual email as author:

remote: error: GH007: Your push would publish a private email address.        
remote: You can make your email public or disable this protection by visiting:        
remote: https://github.com/settings/emails        

But since it's been mentioned here I temporarily changed that and force-pushed with updated author email. It was a lot of commits! Luckily magit with rebase-interactive and a quick keyboard macro made quick work of it.

@jdtsmith
Copy link
Contributor Author

BTW, RE our discussions above of richer abbreviated dates, my related patch was merged to master recently, and will soon be added to compat (which I notice you do not use?). For the future.

@alphapapa alphapapa merged commit 31ec515 into alphapapa:master Dec 25, 2024
@alphapapa
Copy link
Owner

But since it's been mentioned here I temporarily changed that and force-pushed with updated author email. It was a lot of commits! Luckily magit with rebase-interactive and a quick keyboard macro made quick work of it.

Thanks.

So, after about 8 months, this is finally merged. Thanks for your contribution and, of course, your patience.

BTW, RE our discussions above of richer abbreviated dates, my related patch was merged to master recently, and will soon be added to compat (which I notice you do not use?). For the future.

I use compat in my packages where it's needed. There hasn't been a need in this one yet. If it helps, or allows removing code, by all means, let's use it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants