LuaSnip icon indicating copy to clipboard operation
LuaSnip copied to clipboard

modify `ls.available()` to include each snippets origin file path.

Open molleweide opened this issue 2 years ago • 10 comments

Discussed in https://github.com/L3MON4D3/LuaSnip/discussions/716

Originally posted by molleweide January 6, 2023

(I tried the "transfer" feature and assumed that the existing discussion would be removed...)

Proposed solution

After digging in the source a little bit I am thinking that the appropriate way to go about this would be

  1. Attach the origin_file_path to each snippet when loading files with load_files(ft, files) from luasnip/loaders/from_lua.lua.
  2. Update the luasnip/init.lua func get_context() so that it also grabs the path of each snip.
  3. Modify the available function to mean "available in a broader sense".
    local function available(ft:<string|table>)        
    -- ft could be either a string or a table.
    -- 1. if ft == nil, then do the exact same as current `available()` and deprecate the available function.
    -- 2. if ft == string, retrieve snips for this specific file type.
    -- 3. if ft == table, then return all snippets for the containing filetypes.
    
  4. optionally, also do this same procedure for snipmate, and vscode as well.

With these three additions it would be possible to extend the existing telescope-luasnip extension repo to become a CRUD manager. I also have some more ideas for the picker that would make life easier, eg. add snippet to new filetype and then create all of the boilerplate in your default luasnippets path for a new filetype.

If @L3MON4D3 if you agree with my proposed solution I will make a PR for this. This should NOT introduce any breaking changes.

molleweide avatar Jan 06 '23 15:01 molleweide

(I tried the "transfer" feature and assumed that the existing discussion would be removed...)

No worries, I'll leave a comment to redirect here :D

I think that's a pretty cool usecase, we already have open-by-filetype, open-by-snippet is a slick addition.

But! I would propose to implement this by adding source (or metadata, for arbritary data, and add a config-option for what is put in there) as a key to add_snippets, this would make it more versatile (can also be done if a snippets is added via add_snippets). Not sure what you are thinking of with attaching the source to the snippet, but I'd rather not include metadata inside the snippet itself. I think a table mapping snippet-id -> source/metadata+ a function to retrieve that should do it.

Creating the boilerplate could be done by passing extend in edit_snippet_files's opts, which is pretty elegant imo, but similar functionality can be added to telescope-luasnip, if that fits your workflow better :D

L3MON4D3 avatar Jan 06 '23 22:01 L3MON4D3

Thanks for appreciating my idea!!

jump-to-snip

I think that's a pretty cool usecase, we already have open-by-filetype, open-by-snippet is a slick addition.

Yeah right, feels like we could reduce the number of keystrokes for getting to any snippet by 10 - 25 keystrokes or even more in some cases with my crud idea described below.

But! I would propose to implement this by adding source (or metadata, for arbritary data, and add a config-option for what is put in there) as a key to add_snippets, this would make it more versatile (can also be done if a snippets is added via add_snippets).

Aaah yes, I see what you mean. This was excellent advice.

Not sure what you are thinking of with attaching the source to the snippet, but I'd rather not include metadata inside the snippet itself. I think a table mapping snippet-id -> source/metadata+ a function to retrieve that should do it.

This was a hack for my own picker because I did not understand how to get the loaders to work properly at first. I just made a custom loader and did `snippet["origin_file_path"] = ..." but using a id/source mapping is smarter.

I did not succeed in getting the builtin loader working properly for myself at first, so I never experienced the ui.select flow to edit but this was very nice now that it worked for me.

Creating the boilerplate could be done by passing extend in edit_snippet_files's opts, which is pretty elegant imo, but similar functionality can be added to telescope-luasnip, if that fits your workflow better :D

Ohh shit, this made me realise that my local fork was quite an old commit so I pulled latest just now...

mappings

The solution should ofc work regardless of telescope . If you have the completion menu open or even if you are within an active snippet, we could have an insert mode mapping that (rolls back the snippet and) exits to normal mode and opens up the current snippet in a split or something.

crud (brainstorming)

When the jump-to-snip feature is working, what do you think about having something like utils/crud.lua OR manage.lua where we could put all functions related to managing single snippets. Eg.:

  1. jump to snip
  2. remove snip
  3. update -> eg. you make a bind for updateing the trig or descr and then move cursor and enter insert with treesitter etc. Basically make a comprehensive manager function that allows you to jump specifically to any node within a snippet instantly and then take it from there with whatever user opts or configs in order to reduce keystrokes as much as possible.

In telescope or insert mode we could have:

  • <C-u><C-t> jump to trig string -<C-u><C-i> jump to first insert node etc. so that we achieve a super fine grained control of jumping to snip.

molleweide avatar Jan 07 '23 06:01 molleweide

progress so far

https://github.com/molleweide/LuaSnip/commits/molleweide

updated solution / summary

  1. update add_snippets() with id/source key value pair. DONE!
  2. add new retrieve_source_from_snippet() function DONE!
  3. update edit_snippets_file() to take an optional snippet argument and then handle the jump-to-snip accordingly in this function. DONE!
  4. find snip by snip.name DONE!
  5. add cmp / if ls.active_snip mapping that allows you to jump-to-snip without telescope. -> I realise that this might be outside of the scope of luasnip and up for each users config, right? BUT if I get this to work I could add the config to wiki or something.
  6. extend the existing telescope-luasnip repo with a hook to the above.
  7. add comprehensive crud stuff with treesitter.

question

		if data.name then
	    local feed_str = vim.api.nvim_replace_termcodes("/"..data.name.."<CR>", true, true, true)
			vim.fn.feedkeys(feed_str, "n")
		end

I used this rather hacky method for moving cursor to the snippet location. Do you have a better/preferred suggestion for moving the cursor? As I mentionned above my intention is to use treesitter and query the file later in order to get more details from the code but now I have to take a break.

molleweide avatar Jan 07 '23 14:01 molleweide

treesitter idea / crud api

 1. require ts 
 2. local snip_tree = query for snippet containing name or trig string
 3. depending on which mapping was used filter out required subcomponent.
 4. create some api funcs
     - move cursor to snip
     - add new snippet to same snip file as selected and expand template snip.
     - remove snip 
     - move snip from snip file A to B (not super important lol)
     - move cursor to snip node N
     - if has `fmt()` move cursor to format string

Which would be the most useful ones? Do you have any additional ideas you'd like?

molleweide avatar Jan 07 '23 15:01 molleweide

Considering the amount of data, should the "storing metadata" part be opt-in?

leiserfg avatar Jan 07 '23 17:01 leiserfg

Considering the amount of data, should the "storing metadata" part be opt-in?

Good idea, and maybe one could also configure which specific dirs to add source maps for.

I guess one way to reduce amount of data would be to store snippets by source path as a subtable within each ft table, but this might introduce a breaking changes (right, or would it??) but it could be an interesting idea. EDIT. Hmm nah bc it wouldn't solve the jump to snip issue anyways I believe.

snippets -> {ft = {["path1"] = { snips... }}}

molleweide avatar Jan 07 '23 17:01 molleweide

The solution should ofc work regardless of telescope . If you have the completion menu open or even if you are within an active snippet, we could have an insert mode mapping that (rolls back the snippet and) exits to normal mode and opens up the current snippet in a split or something.

Right, delete faulty snippet -> edit source of snippet sounds like a good workflow :+1:

crud (brainstorming)

When the jump-to-snip feature is working, what do you think about having something like utils/crud.lua OR manage.lua where we could put all functions related to managing single snippets. Eg.:

  1. jump to snip
  2. remove snip
  3. update -> eg. you make a bind for updateing the trig or descr and then move cursor and enter insert with treesitter etc. Basically make a comprehensive manager function that allows you to jump specifically to any node within a snippet instantly and then take it from there with whatever user opts or configs in order to reduce keystrokes as much as possible.

In telescope or insert mode we could have:

  • <C-u><C-t> jump to trig string -<C-u><C-i> jump to first insert node etc. so that we achieve a super fine grained control of jumping to snip.

I don't think it's worth it to reduce keystrokes beyond moving the cursor onto the line where the snippet is defined. Stuff like remove/updating some part of a snippet seems too specific to me.

treesitter idea / crud api

 1. require ts 
 2. local snip_tree = query for snippet containing name or trig string
 3. depending on which mapping was used filter out required subcomponent.
 4. create some api funcs
     - move cursor to snip
     - add new snippet to same snip file as selected and expand template snip.
     - remove snip 
     - move snip from snip file A to B (not super important lol)
     - move cursor to snip node N
     - if has `fmt()` move cursor to format string

Which would be the most useful ones? Do you have any additional ideas you'd like?

Same as above, anything beyond finding where the snippet is defined seems too granular. But, if you want to implement this, don't let my opinion stop you :D I'm not convinced it's useful, but maybe my workflow is just different from yours

Considering the amount of data, should the "storing metadata" part be opt-in?

Right, yes, it probably should. I'm thinking of adding a config-option loader_snip_metadata (name WIP xD) which takes a table like {lua = ..., vscode = ..., snipmate = ...}, and ... are functions which get as much data as a loader can provide (only the file I think), and generate the metatdata that should be attached to its snippets. By default function() return nil end such that nothing is stored, we could, as usual :D, provide some sensible defaults in luasnip.extras. This would also cover excluding some dirs.

How to store it, yeah, that's a question.. Most straightforward would be snip_id -> metadata, but that might be a bit too wasteful. Perhaps snip_id -> metadata_id -> metadata?

L3MON4D3 avatar Jan 07 '23 21:01 L3MON4D3

I'm not convinced it's useful,

Lol, me neither. I consider this RnD. Now that I got the jump to snip functionality to work I feel pretty much satisfied because it is very fast (and fast enough..). However, I might play around with my picker and see what happens. Just like you say, it is stupid to over optimise etc. so we'll see what happens maybe some patterns arise that become useful. Also, I'm less experienced as a coder/luasnipper so I really need to consult with you before I spend time on something really stupid. Now I also need to get deeper in general with luasnip so that I don't accidentally reinvent any wheels.

Anyways, I'm thinking that now I'll

  1. add a config option for my solution that defaults as false (opt-in),
  2. add this option to the docs,
  3. and then I'll submit the PR so that we can have this solution working for now until we implement something smarter in the future.

Does this sound good?

molleweide avatar Jan 07 '23 21:01 molleweide

Lol, me neither. I consider this RnD

Ah, alright xD Go ahead then :D

Does this sound good?

Yeup :+1: I'll wait for your PR and then take a closer look at the implementation

One more idea: make it possible to attach more metadata to the snippets. But for now, it should be enough to have one metatdata-entry per add_snippets-call, and then point all snippets to that

L3MON4D3 avatar Jan 08 '23 09:01 L3MON4D3

PR submitted.

make it possible to attach more metadata to the snippets.

I believe that my implementation makes this possible but might need minor changes. I created a table { source = path} for each add_snippets call and then point each snip to this table.

Here is a link to my custom picker: https://github.com/molleweide/doom-nvim/blob/merging-doom-neovim/lua/doom/modules/features/extra_snippets/pickers.lua

And my mappings for calling the picker fyi: https://github.com/molleweide/doom-nvim/blob/7f67e56d3517a1aa1f66f82bf81e04217f22cdf8/lua/doom/modules/features/extra_snippets/init.lua#L177

Right, delete faulty snippet -> edit source of snippet sounds like a good workflow 👍

Next step would be to implement this now, and also maybe if the cmp menu is open make it possible to edit snip from there instead of expanding.

molleweide avatar Jan 08 '23 18:01 molleweide