protesilaos / denote

Simple notes for Emacs with an efficient file-naming scheme
https://protesilaos.com/emacs/denote
GNU General Public License v3.0
539 stars 55 forks source link

Hacking `denote-rename-file` to allow changing the ID #278

Open mentalisttraceur opened 8 months ago

mentalisttraceur commented 8 months ago

I often want to change the ID part of a Denote-style file name while reusing all of the logic in denote-rename-file.


Currently I achieve this by advising denote-retrieve-filename-identifier to return the desired ID during the call to denote-rename-file. In other words:

(with-advice ('denote-retrieve-rename-file :override (lambda (&rest _) new-id))
    (denote-rename-file ...))

(with-advice calls advice-add and its body in an unwind-protect, with the corresponding advice-remove in its cleanup clause.)

Of course, this only replaces the datetime in the file name. For files with front matter, this does not update the front matter's date and identifier fields. That would require much more involved code and I personally haven't needed it.


My use case for things like this is:

  1. I have a basic task list system based on Denote where each task is a note file. For scheduled-for-a-specific-time tasks I use the datetime in the file name to indicate when the task is to be done. For unscheduled tasks, I just use this to sort tasks relative to each other. (Here, I benefit most heavily from the automatic Dired refresh, and sometimes from the ability to do this from the task's note or to have the task's not open in the background but have the buffer refresh automatically. My code disables Denote's confirmation prompts in these "task reschedule" functions, so I wouldn't miss that.)

  2. Fixing typos in the names of pictures or other files where I might need to manually type the date when first converting them to Denote's file naming convention. More generally: the more I go "all in" on using Denote's file naming convention for all sorts of files, the more relevant it is for me to have quick and easy ways to add, change, or remove the ID in a Denote-style file name.

  3. It's nice to have as an option precisely because having it available helps me creatively notice future situations where it would be useful.

  4. Importing my blog posts into Denote notes. Right now I have a lot of blog posts which were written before I switched to Denote, which only exist online. With my current setup, it's not extremely ergonomic to create a new note without even thinking about the date yet, and then paste in the post, add the tags, and change the ID to reflect the original publish date.

The benefit of going through denote-rename-file is I get all the other nice features: the visiting buffer renames to match, all Dired buffers that have the file's directory open refresh, and so on (with my other hack to enable denote-rename-file to act even if the file doesn't exist yet but there's a visiting buffer for it, a newly created note doesn't even have to be saved yet for me to change any part of its name - and with this hack, that really means any part of its name).

Another way to look at it is that it's more consisten and universal coverage - every other part of a Denoted file name is changeable, and only in some possible uses (linking, etc) it's helpful that it be unchangeable. I prefer orthogonal building blocks: I have two-key-press keybinds for each of adding keywords, removing keywords, editing the title, and editing the datetime. Naturally I wouldn't call the latter for files I was already linking without accepting the overhead of finding and editing all the links or link rot from not doing so.

jeanphilippegg commented 8 months ago

I mentionned this once in the past in another issue, but I see the ID part of the file name as something that users should not really mess with. Its initial purpose was to allow linking between files. It would not even have existed in the file name if some kind of linking (that does not involve a database) existed. In general, I would recommend using title, keywords and signature for a note's meaningful data.

However, the identifier in file names have been made to be the current date, the manual refers to it as a date (as opposed to an identifier) and it is part of the front matter, by default. So we probably want it to mean more than just an identifier and I don't think it is that much of an issue to do so.

Starting from tomorrow, I will start making pull requests and try to remove restrictions in the file names, allow reordering the components, general cleanups to the codebase. I will look into adding a command to change a note's identifier. Such a command can be useful when doing some tests as well.

I have not decided in what order these will be done. It may be a few weeks for this function to be added.

mentalisttraceur commented 8 months ago

I see the ID part of the file name as something that users should not really mess with.

Alas, I know users, users are friends of mine, also enemies, in fact I too am a user sometimes, and I can confirm from personal experience we users mess with everything.

It would not even have existed in the file name if some kind of linking (that does not involve a database) existed

I totally get that. But, since it does exist, it's going to naturally inspire creative repurposing, and I think that's a good thing.

Of course, you don't have to support any particular creative repurposing, I'm just saying part of the power of the Denote naming convention is that the ID is (parseable as) a date+time and thus can serve any purpose that a datetime could.

mentalisttraceur commented 8 months ago

I will look into adding a command to change a note's identifier.

:bulb: What about overloading+renaming the ask-date optional argument to denote-rename-file?

So the signature becomes

(denote-rename-file FILE TITLE KEYWORDS SIGNATURE &optional DATE)

and the docstring would say

If FILE has a Denote-compliant identifier:

1. If optional DATE is nil, t, or an integer, retain the existing
   identifier while updating the TITLE, KEYWORDS, and SIGNATURE
   components of the file name.

2. If optional DATE is a string which is a valid identifier,
   update all components of the file name.

Else create an identifier based on the following conditions:

1. If optional DATE is t or an integer (such as with a prefix
   argument), prompt for a date and use it to derive the
   identifier.

2. If optional DATE is a string which is a valid identifier,
   use that as the identifier.

3. If optional DATE is nil (this is the case without a prefix
   argument), use the file attributes to determine the last
   modified date and format it as an identifier.

4. As a fallback, derive an identifier from the current time.

5. If the resulting identifier is not unique among the files in
   the variable ‘denote-directory’, increment it such that it
   becomes unique.
mentalisttraceur commented 8 months ago

It may be a few weeks for this function to be added.

Well if you want some help, I'm happy to make some PRs for some of these things I'm asking for.

jeanphilippegg commented 8 months ago

Well if you want some help, I'm happy to make some PRs for some of these things I'm asking for.

Sure! Some of them may also have been done by then.

protesilaos commented 8 months ago

Hey folks I am reading through all the discussions, but it is hard to write at length.

I mentionned this once in the past in another issue, but I see the ID part of the file name as something that users should not really mess with. Its initial purpose was to allow linking between files. It would not even have existed in the file name if some kind of linking (that does not involve a database) existed.

The ID is indeed needed for linking purposes, though it is also useful as a timestamp. For example, I often search for something like ^2024 to see what I published in a given year. But yeah, I get the point you are making.

I often want to change the ID part of a Denote-style file name [...]

The option to rewrite the ID has its use-cases. My concern though with such a feature is that it gives users too much power to shoot themselves in the foot. For example, if a file has links pointing to it and its identifier is changed, those links are now broken.

More generally, it makes things harder to explain/document because now we cannot guarantee that Denote never touches the identifiers.

I think the right approach here is to accommodate power users by making the code more flexible/hackable, but not expose any functionality that can be used directly, like a command, user option, or prefix argument.

What do you think?

Well if you want some help, I'm happy to make some PRs for some of these things I'm asking for.

These are welcome! Note that for non-trivial changes you will need to assign copyright to the Free Software Foundation, because Denote is distributed via GNU ELPA and this is the legal requirement we have to conform with.

mentalisttraceur commented 8 months ago

I think the right approach here is to accommodate power users by making the code more flexible/hackable, but not expose any functionality that can be used directly, like a command, user option, or prefix argument.

What do you think?

100% agree + works for me.

In fact, I now think this issue can be reduced down to just adding a function like denote-rewrite-front-matter (or adding an extra optional parameter to the end of the signature of that function), to enable updating the identifier and date front matter fields.

(Because I now realize that all the value of denote-rename-file is easy enough to reimplement with other functions that Denote already provides.)

jeanphilippegg commented 8 months ago

In my fork of the code, I have made a big refactor (mostly transparent and backward-compatible though) in which denote-rename-file gains the ability to change the type and the subdirectory.

I will probably include the ability to change the ID as well (with obvious warnings as to the consequences of doing so).

protesilaos commented 8 months ago

From: Jean-Philippe Gagné Guay @.***> Date: Mon, 25 Mar 2024 19:22:45 -0700

In my fork of the code, I have made a big refactor (mostly transparent and backward-compatible though) in which denote-rename-file gains the ability to change the type and the subdirectory.

I will probably include the ability to change the ID as well (with obvious warnings as to the consequences of doing so).

Great! Note that because these deviate from what we now have, we want to make them opt-in features to not create any surprises. But yeah, having this feature set is the natural progression of our renaming facility.

-- Protesilaos Stavrou https://protesilaos.com

jeanphilippegg commented 6 months ago

I have an idea regarding the Denote identifier.

Right now, the identifier comes with some limitations. We cannot change its format and its presence is required for linking reasons. It also has a semantic purpose as the date.

My proposition is to separate the "linking identifier" from the "semantic date" entities in the filename (and front matter, but this is already done) and make the identifier more general and be an arbitrary unique string.

Here are the general points of this idea:

This would be an opt-in feature. If users do nothing, they will keep the classic Denote identifier and it will continue to serve its dual purpose as a "linking id" and a "semantic date". However, if a filename has a date component, it would have precedence.

This would all be backward compatible. It would allow the modification of the date component and still keep the linking system robust with unique ids.

Example file names:

@@1--my-first-note__emacs.org
@@2--my-second-note__idea.txt
@@3--a-note-with-a-date++2024-05-05 11:00.txt      <------- A note with a date component
20240405T012423--a-classic-note.org     <---------------- This remains the default
a-classic-note-with-id-at-the-end@@20240404T010101.org
@@j2024-05-01__journal.txt
@@j2024-05-02__journal.txt
@@j2024-05-03__journal.txt
@@j2024-05-04__journal.txt
@@j2024-05-05__journal.txt
@@j2024-05-06__journal.txt
@@j2024-05-07__journal.txt
protesilaos commented 6 months ago

From: Jean-Philippe Gagné Guay @.***> Date: Thu, 16 May 2024 20:00:02 -0700

I have an idea regarding the Denote identifier.

[... 40 lines elided]

To avoid repetition, I commented on this point here: https://github.com/protesilaos/denote/issues/332#issuecomment-2116605498

-- Protesilaos Stavrou https://protesilaos.com

mentalisttraceur commented 6 months ago

@jeanphilippegg I think that's a good thought/idea/brainstorm, I like the backwards compatibility, and overall the design is really sound, but I find myself agreeing with @protesilaos about the complexity increase.

I think it's not quite worth the gain currently, but it's the kind of idea I'd hold onto in case we notice even more ways it would help.

jeanphilippegg commented 6 months ago

I also agree. This was an idea that I would not have worked on for the next version of Denote anyway. We can keep it in mind if more generality/flexibility is needed in the future. I mentioned it now, because we were discussing the delimiter for the id and we could need another delimiter in the future, but we can think about this if needed at that moment.

mentalisttraceur commented 6 months ago

@jeanphilippegg ah, good point, we should think it through enough to know if the possibility of split later should inform the choice of delimiter now.

If the split happens, I think @@ should go to the identifier, per the above mnemonic. To me that adds to @@ feeling correct for the identifier/datetime now - consistent and backwards compatible for linking, which feels like the right thing to keep stable if they split.

protesilaos commented 6 months ago

From: Jean-Philippe Gagné Guay @.***> Date: Fri, 17 May 2024 15:35:11 -0700

I also agree. This was an idea that I would not have worked on for the next version of Denote anyway. We can keep it in mind if more generality/flexibility is needed in the future. I mentioned it now, because we were discussing the delimiter for the id and we could need another delimiter in the future, but we can think about this if needed at that moment.

All good!

-- Protesilaos Stavrou https://protesilaos.com