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

Renaming and moving notes #200

Open
mickael-menu opened this issue Apr 28, 2022 Discussed in #199 · 3 comments
Open

Renaming and moving notes #200

mickael-menu opened this issue Apr 28, 2022 Discussed in #199 · 3 comments
Labels
feature request Something is missing help wanted Extra attention is needed

Comments

@mickael-menu
Copy link
Member

Discussed in #199

Originally posted by srid April 27, 2022
Would this be in scope for zk? Basically:

zk rename Foo "Bar Qux"

will rename Foo.md to Bar Qux.md while taking care of references. By which I mean it will replace all occurrences of [[Foo]] with [[Bar Qux]] in the notebook. As well as [[Foo|...]] with [[Bar Qux|..]]. This is exactly what the vscode-memo extension does.

The replacement can be delegated to sd.

EDIT: It should probably take directory and relative wiki links too into consideration? eg: [[Parent/Foo]] -> [[Parent/Bar Qux]]. Might have to disambiguate multiple Foo.md as well (in different parents).

@mickael-menu mickael-menu added the feature request Something is missing label Apr 28, 2022
@mickael-menu mickael-menu changed the title Renaming notes Renaming and moving notes May 14, 2022
@mickael-menu
Copy link
Member Author

Here's a spec, feel free to review and comment.

We're talking about two separate features using the same renaming process under the hood:

  • Renaming a note title, which potentially updates its filename as well.
  • Moving a note to a different FS path (different folder, or changing its filename manually).

In both cases, the path of the note, its title and all the inbound or outbound links might need to be updated in a single atomic operation.

The actual FS changes will be handled by POSIX diff and patch.

New commands

We'll need one new zk command per feature. Both commands will fail if the new generated filenames already exist.

zk rename

Usage: zk rename <path> <new-title>

Rename the title of a note, updating all the backlinks and the note filename, if needed.

Arguments:
  <path>         Path to the note to rename.
  <new-title>    New title of the note.

Flags:
  -i, --interactive            Edit the changes before they are applied.
  -n, --dry-run                Don't actually rename the note. Instead, prints the changes on stdout
                               and the updated note path on stderr.

zk move

Usage: zk move <source> ... <target>

Move a note (or several) to a different path, updating all the backlinks.

Arguments:
  <source>    Paths to the notes or directories to move.
  <target>    Target directory or path if a single source note is given.

Flags:
  -i, --interactive            Edit the changes before they are applied.
  -n, --dry-run                Don't actually move the notes. Instead, prints the changes on stdout
                               and the updated note paths on stderr.

Interactive flag (-i)

The interactive flag offers an opportunity to edit the changes before they are applied. The confirmation is done in two steps, by editing temporary files (similar to git commit).

Step 1: Validate new paths

This edits a file containing all the filename/paths changes, with the following format:

  • one file change per line
  • source > target
  • whitespaces (except inside a filename) are ignored
source.md > target.md
Foo.md > Bar Qux.md

The user can edit the target paths, or remove a line to cancel a file move.
Emptying the file cancels the whole process.

Step2: Validate diff changes

After validating the new paths, zk will generate all the file changes required (renaming titles, updating links) and generate a POSIX diff.

The user can edit any part of the diff, as long as the output is still a valid POSIX diff file. It will be applied as-is.
Emptying the diff file cancels the whole process.

Dry run (-n)

The dry run flag prints the content of the changes (as edited in the interactive flag) without applying them.

LCP integration

If the client sends a workspace.didRenameFiles event, the server will trigger the renaming process to update the links.

The user can update the title of a note manually using a RenameRequest when the caret is over the note title. This will trigger the renaming process to update the links and note filepath.

The LCP integration is not compatible with the interactive mode. The user is expected to backup their notes before such operation.

New config options

The user can configure the diff and patch commands used by zk from the config file.

[tool]
# Diff command used to edit the changes applied when renaming notes.
diff = "colordiff -u"

# Editor used to modify a diff when using the interactive mode.
# When missing, falls back on tool.editor.
diff-editor = "nvim"

# Patch command applying the changes when renaming notes.
# Use `patch --backup` to make backup copies of the files before they are modified.
patch = "patch"

Processes

Rename process

Given the note N that we want to retitle "T".

  1. If the note.filename option for the note group of N contains the keyword title:
    1. Regenerate a new filename for N using the provided title T.
      • If the note.filename template contains a generated id, it will be randomized again. This should be fine as the inbound links will be updated anyway, but the user can decide to revert the id in interactive (-i) mode.
    2. If the process is in interactive mode, confirm the new filename with the user using tool.diff-editor.
      • The whole process is interrupted if the user empties the changes file.
    3. Move the file N to its new location.
  2. Create a new compound diff file under .zk/DIFF.
  3. Append to DIFF the result of replacing in the note content:
    • the old title of N by the new one T
    • the old filename by the new generated one, if needed
  4. Iterate over all the notes linking to N, then for each link:
    1. Generate a new link using format.link-format with the new title T and generated filepath.
    2. Append to DIFF the result of replacing the old link by the new generated one.
  5. If the process is in interactive mode, allow the user to edit the DIFF file using tool.diff-editor.
    • The whole process is interrupted if the user empties the changes file.
  6. Apply the diff patch using tool.patch.
  7. Re-index the notebook.

It's not a priority, but the link replacement algorithm could be smarter to try to preserve case changes or custom titles in a link.

Move process (single note as source)

Given the note N that we want to move to the new path P.

  1. If P is a directory, append the filename of N to P.
  2. Move the file N to its new location P.
  3. Create a new compound diff file under .zk/DIFF.
  4. Append to DIFF the result of replacing in the content of N:
    • the old filename by the new one
  5. Iterate over all the notes linking to N, then for each link:
    1. Generate a new link using format.link-format with the new path P.
    2. Append to DIFF the result of replacing the old link by the new generated one.
  6. If the process is in interactive mode, allow the user to edit the DIFF file using tool.diff-editor.
  7. Apply the diff patch using tool.patch.
  8. Re-index the notebook.

Move process (several notes or a folder as source)

Given the sources Sn that we want to move to the new folder P.

  1. We expand each notes under Sn and append the child path portions to P to resolve the rename pairs.
  2. If the process is in interactive mode, confirm the renames with the user using tool.diff-editor.
    * The whole process is interrupted if the user empties the changes file.
    * If only some lines of the file are removed, these files are skipped.
  3. To simplify, we apply the "single note move process" described above to each rename atomically.
  4. If there are empty folders in Sn, they are deleted.

Improvements

It's not a priority, but the link replacement algorithm could be smarter to try to preserve case changes or custom titles in a link.

@igxlin
Copy link

igxlin commented Nov 14, 2022

@mickael-menu Look like some logic missing in moving process logic. The links inside the moved file should be updated too. For example, file foo.md move to bar/. Then the link ./path/to/file.md inside foo.md may need to be updated as ../path/to/file.md.

@tjex tjex added the help wanted Extra attention is needed label Apr 8, 2024
@mhmood-sf
Copy link

I believe this should be in scope for zk, because the README highlights do mention notebook housekeeping (and renaming is definitely a big part of it, not to mention the "housekeeping" only consists of two features as of now).

The renaming features suggested by @mickael-menu are quite comprehensive; personally I would be very happy with a simple renaming command that lets me do batch renaming using templates, something like the following:

# Change name format from "{{timestamp}}-{{CamelCaseTitle}}" to "{{timestamp}}-{{slug title}}"
zk rename Wiki/ "{{metadata.ctimestamp}}-{{slug title}}"

Since the notes already exist, the rename command can allow metadata and all other relevant fields in the template. This could provide very powerful renaming capabilities.

I am not a heavy user of the linking capabilities so I'm not sure how those would be handled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Something is missing help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants