zed-industries / zed

Code at the speed of thought – Zed is a high-performance, multiplayer code editor from the creators of Atom and Tree-sitter.
https://zed.dev
Other
47.97k stars 2.83k forks source link

Add support for choices in snippet placeholders #12739

Open rapgenic opened 4 months ago

rapgenic commented 4 months ago

Check for existing issues

Describe the bug / provide steps to reproduce it

I am currently writing a VHDL extension.

Sometimes the language server returns a snippet as a completion.

Some relevant RPC messages follow (containing an example of the snippets produced by the language server):

// Send:
{"jsonrpc":"2.0","id":125,"method":"completionItem/resolve","params":{"label":"spi instantiation","kind":9,"insertText":"${1:spi_inst}: entity ${2|work,default|}.spi\n generic map(\n    N => ${4:N}\n)\n port map(\n    clk => ${5:clk},\n    reset => ${6:reset},\n    sck => ${7:sck},\n    sdi => ${8:sdi},\n    sdo => ${9:sdo},\n    start => ${10:start},\n    busy => ${11:busy},\n    length => ${12:length},\n    data_tx => ${13:data_tx},\n    data_rx => ${14:data_rx}\n);","insertTextFormat":2}}
// Receive:
{"jsonrpc":"2.0","id":125,"result":{"insertText":"${1:spi_inst}: entity ${2|work,default|}.spi\n generic map(\n    N => ${4:N}\n)\n port map(\n    clk => ${5:clk},\n    reset => ${6:reset},\n    sck => ${7:sck},\n    sdi => ${8:sdi},\n    sdo => ${9:sdo},\n    start => ${10:start},\n    busy => ${11:busy},\n    length => ${12:length},\n    data_tx => ${13:data_tx},\n    data_rx => ${14:data_rx}\n);","insertTextFormat":2,"kind":9,"label":"spi instantiation"}}

When I accept the completion labeled spi_completion, as in the specific example, nothing appears inside the editor (this always happens, actually, probably because the snippets provided by this language server have all the same structure).

Note that everything works correctly in vscode, where the completion appears in the editor and i can tab through all the snippet placeholders.

Looking at the terminal I launched Zed from I see the following message:

[2024-06-06T17:02:53+02:00 ERROR util] crates/editor/src/editor.rs:3938: failed to parse snippet

Caused by:
    expected a closing brace

Environment

Zed: v0.140.0 (Zed Nightly 4fd698a093cb4be8f29d258ed3988155aa175122) OS: Linux 1.0.0 Memory: 7.4 GiB Architecture: x86_64

If applicable, add mockups / screenshots to help explain present your vision of the feature

No response

If applicable, attach your ~/Library/Logs/Zed/Zed.log file to this issue.

No response

osiewicz commented 4 months ago

Hey, thanks for the report. Indeed, it looks like we're not parsing choice placeholders (${X|a|b|c}) syntax correctly.

osiewicz commented 4 months ago

For anybody willing to tackle this, what we need to do is modify parser in a snippet crate so that it is capable of handling choices in the snippet. Then, we also need to show the completion menu in the editor when user is currently editing a given snippet. To test this change, you can use the completion that @rapgenic shared. I'd be happy to pair if you need help. For more details on choices in placeholders, see: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax

Anthony-Eid commented 4 months ago

I can work on this!

Anthony-Eid commented 3 months ago

In the LSP snippet choice spec they say this

Placeholders can have choices as values. The syntax is a comma separated enumeration of values, enclosed with the pipe-character, for example ${1|one,two,three|}. When the snippet is inserted and the placeholder selected, choices will prompt the user to pick one of the values.

Would $1|one,two,three| be a valid placeholder choice under the above criteria?

Also, is the edge case possible where there's only one choice? Such as ${1|one|}. I'm assuming it's not because that's just a regular placeholder, but I want to make sure I don't overlook this possibility in my code if it can happen.

osiewicz commented 3 months ago

Would $1|one,two,three| be a valid placeholder choice under the above criteria?

So like if you can have ${1|${1|one,two,three|}|} where there's just one choice? Yes, spec kinda allows that, with a caveat that comma and pipe characters have to be escaped. See grammar section of: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax Thus I'd expect the valid syntax in that case to be: ${1|\$\{1\|one\,two\,tree\|\}|}

Also, is the edge case possible where there's only one choice? Such as ${1|one|}. I'm assuming it's not because that's just a regular placeholder, but I want to make sure I don't overlook this possibility in my code if it can happen.

I think it is totally possible (though it would be useless I guess?); if it were a regular placeholder then we should put your cursor on that position and then let you type. However, if there's a choice we should also show a completion menu with that single option.

I think we need to assume that it's possible.

quinncomendant commented 1 month ago

@osiewicz, the Zed snippet parser is also missing support for the following features. Should I open separate tickets for these, or would it be easier to add their functionality while doing the work for snippet choice support?

Regex variable transforms

Example snippet:

  "Transform example": {
    "prefix": "love",
    "body": "${1:text} → ${1/./❤️/g}",
    "description": "Regex variable transform test"
  }

Output: text → ❤️❤️❤️❤️

Command substitution

Example:

  "Command substitution example": {
    "prefix": "date",
    "body": "`date '+%F %T'`",
    "description": "Current date and time (ISO format)"
  },

Output: 2024-08-29 17:39:49