helix
helix copied to clipboard
Add a snippet system
This PR finally implements a real snippet system that can scale Fo fully support all features that you expect from snippets. It has been moved to helix core to reflect that these snippets can be sourced from other sources besides LSPs in the future.
I kept the snippet parsers and some of the logic around generating transactions but basically had to fully rewrite the snippet rendering since there were a ton of cases that weren't covered. Our old code around handling snippet indentation was also needlessly complicated, inefficient and incorrect which I fixed here too.
The actual tabstop handling (and its interaction with multi cursor) turned out to be quite tricky too. I created the notion of an active snippet which tracks the currently active tabstops on a document and maps them trough changes as needed. A snippet stays valid as long as all multi cursors are within the entire body of any snippet. I always hated how some vim snippet plugins made snippets way too sticky (so I would jump somewhere else, do something else hit tab and be back at the other end of a file at a renegade tabstop). This approach prevents that while still allowing you to enter normal mode and perform other edits on the snippet without immidietly discarding tabstops.
One you jump to a tabstop the placeholder will be selected, if you are in insertmode hitting any key not bound to a command will delete the placeholder text before inserting the corresponding character. This reuses (and expanded version) of our on_next_key mechanism.
Commits are best reviewed separately otherwise the diff may be hard to follow.
I am not yet sure I am happy with the keybindings, there is no way to jump back to a previous tabstop, the interaction with smart tab is hardcoded and normal mode bindings are missing. All of this somewhat conflicts with the jump to parent node end stuff. What are your thoughts @dead10ck ?
Hard coding the interaction with smart tab doesn't seem wrong on the face of it. The desired behavior is for tab to do different things in different situations, and this seems like a new situation.
That said, that doesn't solve going back. This kind of seems like a feature that deserves its own special mode, like view mode. In this mode, we could have tab / shift-tab in addition to a set of duplicate bindings for non-kitty protocol terminals. And it would also help avoid having to write a smart-untab command.
I thought we couldn't bins-tab at all? Or is that only because of the conflict with c-i? In insertmode c-i shouldn't be bound either so if we can create shift-tab bindings then that may work too..
One concern I had about hardworking tab is that I sometimes overshoot my target with tabstops and then endup doing a pretty for jump with the TS motion. So I guess I am not too much of a fan of having multiple things bound to the same key. Maybe that's just me but I guess having some configurability for smart tab could be nice.
When I started on this I figured snippets could be a separate sticky mode similar to how the debugger functions. An overlay on top of the regular keymap. Tabbing through all the tabstops would exit the mode too
Yeah, exactly my thought too. If it's a special mode / overlay, then we don't have to worry about conflicts or interacting with smart tab.
Also @pascalkuthe tab does conflict with Ctrl-i, so that couldn't be a default binding in normal mode. We'll have to figure out a different keymap for non-kitty terminals anyway though. Mod-tab only works in kitty terminals.
We could save the special mode for a follow-up PR though, and for now just come up with the bindings for non-kitty terminals. It will just probably not be the tab key.
yeah I think having it be mode based would be nice. It's entirely possible to bind <S-tab> in insert mode it just bound to insert_tab by default which would be changed by the mode.
I think minor modes need some love in general. That would be a larger change tough so I think its best to just push that to follow-up work and only keep the very basic smart tab binding we have right now
rebased on master, fixed @the-mikedavis comments from above plus a bug he mentioned to me where accepting a (snippet) completion by continuing typing a word char would not correctly replace the placeholder
Can I use this feature if I build helix from source ?
Can I use this feature if I build helix from source ?
If you build from the right branch yes! This is the branch you want to checkout: https://github.com/helix-editor/helix/tree/snippet_placeholder
And if you're a nix user you can try it out in a single command:
nix profile install github:helix-editor/helix#snippet_placeholder
Is there any docs about making a snippet ? Or is it simple like vs code snippets ? In json format ?
it's vscode snippets but this doesn't handle loading user snippets, it only handles snippets send by the lsp
it's vscode snippets but this doesn't handle loading user snippets, it only handles snippets send by the lsp
For now I am using snippets lsp. But the stop points are not working. So if I build helix with this branch . The lsp will work with stop points ?
Yes
@pascalkuthe I was testing and tabstop wasn't working. So I realized that it only works when smart-tab is enabled.
Shouldn't it work even when smart-tag disabled? Since they are different features.
More advanced keybindings are left to future work. You can bind the commands to jump to the previous/next tabstop commands manually to different keys
Started to test it today. Generally, it is working very well with the mentioned lsp before.
Thank you very much, @pascalkuthe