I had an idea for a snippet plugin, and right now the idea mostly relies on python(and potentially rust via pyO3), though I’m not sure how much of it is necessary, in the sense that I could be duplicating some functionality built into kakoune.
few features worth mentioning.
the snippet syntax would be a superset of the accepted snippet syntax making it easy to migrate from standard text editors, and would would be specified in a json or toml file.
If json is used, this means that they could literally copy over their vscode snippets to use in kakoune.
snippets could be set to autotrigger via the snippet definition, but the default is to be manually completed.
my hope is also to conditionally change completion order, the kak autocompletion menu should set snippets as the lowest priority unless it’s an exact match, then it should be at the top. I’m not sure how to do this in kakoune though.
since all input is being piped to the daemon, snippet completion could be a bit smarter, only checking for snippets if it makes sense, for example not in a comment, or only in a comment.
each language would have multiple sets of snippets, a base set, which is always loaded, and conditional sets which are either user specified or loaded if certain snippet is triggered( for example import opencv would load opencv related snippets into the trie(or set of contextual tries) for that window.
related to the last 3 points, it uses tries as the data structure for storing snippets. easily updated if not compressed, from my understanding as fast as a dictionary (keys still have to be parsed char by char to check if match), and can tell if the current word is a valid prefix.
the autotrigger snippets should be there for the completion of unambiguous syntax. while loops tend to be unambiguous whereas for loops could be range based or iterable based. the former should autotrigger, the latter should not.
the rough sketch of the program
in kakoune
start the snippet daemon
the daemon creates a trie(or set of tries for different contexts) of all the relevant snippets for the window
if insertion mode pipe either all input or current word boundary to the daemon
if snippet is recieved, insert at start of word boundary
else if completion option recieved add to menu based on priority
in the python daemon
creates a trie(or set of tries for different contexts) of all the relevant snippets for the window
monitor for context(comments, newlines, first word, etc)
if given context is true then check if current word is a trigger
if not return all snippets/triggers that current word is a prefix for and send to the completion menu with low priority
if it is then return snippet/subsnippets if type is autotrigger else send to the completion menu with high priority.
in last step if snippet is has attached snippets then update the trie(s).
Questions
Part of the reason I’m doing the majority of the work in python, besides being turing complete, is because I’m still not comfortable with the internal kakoune stuff. I don’t know what would be simpler(in terms of writing) or more performant to handle via kakoune itself. based on the rough sketch of the execution, what do you think that kakoune should handle?
How would I go about setting priority for completion items?
I don’t want to depend on the lsp, but how could I integrate it if it is available, and what could it offer that I couldn’t get from monitoring the input.
do you think making the syntax a superset of the standard syntax (where snippets set via the standard will work with the plugin, but plugin snippets won’t likely work with another editor) is advisable? what is an alternative if you think that isn’t a good idea?
right now, I’m thinking of a design using a daemon per window, rather than per session, because a session could be per project, and include multiple file types. Do you think this is better or worse. It would use more memory, especially if you have 10+ text editors open(I using tiling wms and it’s common enough to be a concern), but having a single daemon with multiple filetypes, seems like that could add a layer of complexity that is unnecessary in most use cases.
I appreciate any help you can provide, this will be a first for me in a couple of ways(monitoring input, kakoune plugin, implementing a trie actually meant to be used, etc).
each language would have multiple sets of snippets, a base set, which is always loaded, and conditional sets which are either user specified or loaded if certain snippet is triggered( for example import opencv would load opencv related snippets into the trie(or set of contextual tries) for that window.
That’s an awesome feature !
That’s normal, kakoune want’s you to do most of the job outside kakoune. Kakoune just give you the minial glue to connect with external script.
How would I go about setting priority for completion items?
Idk myself, but you might want to check this post:
For the moment the only maintained kakoune snippets plugin is GitHub - alexherbo2/snippet.kak: Snippets integration for Kakoune ( I use it, it work well ) maybe you want to build on top of that. ( you can go back a few commit if you want an even more simple plugin to start from )
I’d never heard of crystal before now, though looking at the benchmarks I’m not sure why that is. It definitely looks impressive. it seems easy to follow too.
Would you be okay with me just trying to contribute some of the ideas I had to your project? I’m currently in college and prepping for technical interviews/job hunting, so I can’t guarantee that I’ll consistently contributing but I’ll definitely document any code I add and keep it easy to follow if I fall off the face of the earth
Honestly I do too, it made it easier to modify the snippets, and to see what was available(I use ranger-cd for directory traversal). the only (non) problem I see with it is setting variables (description, snippet-type, snippets to load on trigger) seems like it would be janky. the files would look like a chopped up toml file. I suppose I could set metadata in a separate file, or possibly set variable values via some kind of comment that won’t get confused for language syntax and won’t get stored as part of the content of the snippet. given that the majority of snippets won’t really need anything extra (variables are only necessary for non-standard features such as autotriggering or adding branches to the trie), it would also keep the process for adding new snippets exactly the same in most cases.
I also wanted to make it easy to migrate to kakoune from whatever-editor(I’m pretty sure both atom and vscode use json), but I could probably just write a converter in python that just turns one into the other, and that would also fix any issues of non-standard snippet stuff, since it was mostly just instructions for the editor, it won’t be copied to the json output.
Would you be okay with me just trying to contribute some of the ideas I had to your project?
Sure.
Here is an example of the snippets show command, and the generated JSON from the snippets get all command. Snippets are scoped by their tree structure. So for example, you only have the spec completion when you edit specs, or if you use a framework such as Rails, in a Rails project.
Here is the actual source code of the snippets project; it’s pretty new so not much features.
@alexherbo2 I just installed snippets, but invoking snippets show or snippets get all outputs nothing. Do I need to manually setup snippet files or something?
If a snippet style is not of your liking you can add your own in ~/.config/snippets/<custom> and follow the same structure than in ~/.config/snippets/base.
looking through the snippets I like how it seems you handled multiple placeholders and also nested snippets. I saw for the html snippets that < expands to tag+ which is itself a snippet.
I haven’t had a chance to look through the source code in depth but I’m guess when the snippet is returned it’s parsed for embedded snippets?
Also, if I was going to add additional optional variables to the snippet files (such as type=autotrigger) I think doing so in a comment meant for whatever editor is using the snippets would be best. should I just make a language agnostic comment (some start never used in a language such as @@) or just use the languages comment syntax and specify something that behaves like a modeline (# snippet:var=val)?
I liked your snippets plugin, thanks for that!
One thing I noticed though for example if I press <a-ret> to trigger snippet’s menu and change my mind, I have to press <esc> two times first to escape a filter prompt and then a menu itself.
I removed the menu command to support snippets through the completers.
Just type /def<c-n> to select the entry, and <ret> to expand. If you don’t want to select, you can press <a-ret> directly.
There is a snippets-enter command detailed in the configuration section to play well with other plugins registering their command on Enter, such as auto-pairs.