Is there something like a "draft context" but for buffer content?

The problem

I’ve been experimenting with a plugin to assist with local editing of GitHub wikis. The special rules for GitHub wikis are:

  • nearly every supported markup syntax adds support for MediaWiki-style links, so you can link to a paged named “Foo” with [[Foo]]
  • When you create a page named “Eval / Exec”, the page content is stored in a file named Eval---Exec.md
    • that is, spaces and slashes are replaced with hyphens
    • there may be other replaced characters too
  • [[Eval / Exec]] and [[Eval/ /Exec]] and [[Eval---Exec]] all link to the same page

So when the user tries to follow a [[Some Page]] link, I have to map that into Some-Page.md, then tell Kakoune to edit the resulting file. Simple!

However, it gets more complicated. For example, while GitHub squashes unsafe characters to hyphens, the Python-Markdown wiki link extension squashes (only) spaces to underscores, and other wiki systems probably use other schemes, so I’d like to make the pattern and the replacement configurable.

Attempt 1

My first idea was to make a regex option for “unsafe character pattern”, and a string option for “unsafe character replacement”, and then my code would use sed to process the page name and produce a filename for Kakoune to edit. However, a Kakoune regex option validates against Kakoune’s regex syntax, not sed’s, and it felt silly to have Kakoune (a mighty text-processing tool) shell out to a simple text-processing tool just to do a search-and-replace.

Attempt 2

My second idea was to have the “squash unsafe characters” option be a command rather than a pattern and a replacement, and I’d use it something like this:

# kakrc
set global wiki_squash_unsafe_characters "exec s[<space>/]<ret>c-<ret>"

# in my plugin
eval -draft %opt{wiki_squash_unsafe_characters}
wiki-edit-page-named %val{selection}

…except that the wiki_squash_unsafe_characters command modifies the buffer! So let’s put an undo after it:

eval -draft %opt{wiki_squash_unsafe_characters}
wiki-edit-page-named %val{selection}
exec u

…except that wiki-edit-page-named is going to change which buffer is active, and the undo is going to go to the wrong buffer! That’s OK, we can work with that:

eval -save-regs b %{
    reg b %val{buffile}
    eval -draft %opt{wiki_squash_unsafe_characters}
    wiki-edit-page-named %val{selection}
    exec -buffer %reg{b} u
}

…except that it’s possible to have a page name without any unsafe characters, like [[Home]], in which case wiki_squash_unsafe_characters is going to throw an error. Well, we can fix that with a try command:

eval -save-regs b %{
    reg b %val{buffile}

    try %{
        eval -draft %opt{wiki_squash_unsafe_characters}
        wiki-edit-page-named %val{selection}
        exec -buffer %reg{b} u
    } catch %{
        wiki-edit-page-named %val{selection}
    }
}

…and that works. Unfortunately, it means that wiki_squash_unsafe_characters must do exactly one undoable operation, no more or less, and it means that any legitimate errors in wiki-edit-page-named will become very difficult to debug, and leave the buffer in an inconsistent state (with the squash action not undone).

What (I think) I want, option 1

There should be something that’s like eval -draft, but it restores buffer content afterward, or copies the buffer into a temporary scratch buffer or something. If such an option existed — let’s call it -scratch — my code could look like this:

evaluate-commands -scratch %{
    try %{ eval -draft %opt{wiki_squash_unsafe_characters} }
    wiki-edit-page-named %val{selection}
}

Much simpler, and more reliable!

What (I think) I want, option 2

Alternatively, and perhaps simpler to implement, would be a way to navigate through the history tree to a specific point. <u> and <U> step along one branch of the undo tree, while <a-u> and <a-U> step between branches, but imagine there was some other binding like <c-a-u> that took you straight to the history node whose number was given by the count. Then my code could look like this:

eval -save-regs bh %{
    reg b %val{buffile}
    reg h %val{history_id}

    try %{ eval -draft %opt{wiki_squash_unsafe_characters} }
    wiki-edit-page-named %val{selection}
    exec -buffer %reg{b} %reg{h} <c-a-u>
}

Not quite as clean as the previous idea, but still nicer than what I’m using currently.

Conclusion

Are there any Kakoune features I’ve missed that would help me out with this? Is there a better approach I could be taking?

2 Likes

I also think Kakoune would benefit from having better ways to view the history tree, which would also require new commands to navigate it.

But WRT to option one I can’t think of any reason you can’t do this with existing commands, even though a built in short cut might be nice. Just open a scratch buffer with :edit -scratch and copy the pre-edit line to it.

But an easier solution is just to copy the edit to a register and evaluate it’s contents after the undo.

eval -draft %opt{wiki_squash_unsafe_characters}
# this can all be done in eval -draft command but wrote it out explicitly here to demonstrate
reg m %val{selection}
exec u
# navigate to the edited line that was saved to a register.
exec ': wiki-edit-page-named %reg{m}
1 Like