helix icon indicating copy to clipboard operation
helix copied to clipboard

Add a snippet system

Open pascalkuthe opened this issue 5 months ago • 17 comments

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.

pascalkuthe avatar Mar 04 '24 01:03 pascalkuthe

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 ?

pascalkuthe avatar Mar 04 '24 01:03 pascalkuthe

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.

dead10ck avatar Mar 04 '24 03:03 dead10ck

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.

pascalkuthe avatar Mar 04 '24 09:03 pascalkuthe

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

archseer avatar Mar 04 '24 10:03 archseer

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.

dead10ck avatar Mar 04 '24 14:03 dead10ck

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.

dead10ck avatar Mar 04 '24 14:03 dead10ck

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

pascalkuthe avatar Mar 08 '24 15:03 pascalkuthe

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

pascalkuthe avatar Apr 01 '24 23:04 pascalkuthe

Can I use this feature if I build helix from source ?

noor-tg avatar Apr 06 '24 22:04 noor-tg

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

zetashift avatar Apr 06 '24 23:04 zetashift

Is there any docs about making a snippet ? Or is it simple like vs code snippets ? In json format ?

noor-tg avatar Apr 07 '24 11:04 noor-tg

it's vscode snippets but this doesn't handle loading user snippets, it only handles snippets send by the lsp

pascalkuthe avatar Apr 07 '24 11:04 pascalkuthe

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 ?

noor-tg avatar Apr 07 '24 22:04 noor-tg

Yes

pascalkuthe avatar Apr 07 '24 22:04 pascalkuthe

@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.

danillos avatar Apr 07 '24 22:04 danillos

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

pascalkuthe avatar Apr 07 '24 22:04 pascalkuthe

Started to test it today. Generally, it is working very well with the mentioned lsp before.

Thank you very much, @pascalkuthe

noor-tg avatar Apr 08 '24 00:04 noor-tg