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?