Closes #59620 ## Problem A language whose name contains a `/`, such as a custom `PL/X` extension (`lsp_id` → `pl/x`), broke snippets end to end: - The `snippets: configure snippets` action wrote the file to `~/.config/zed/snippets/pl/x.json`, i.e. inside a `pl/` subdirectory. - The snippet scanner reads `snippets/` non-recursively and skips directories, so that file was never loaded and the snippet could never be used. - The completion lookup keyed off the raw `lsp_id` (`pl/x`), which wouldn't have matched the file-stem key even if the file had been scanned. So Zed's own UI created a snippet file it could never read back. ## Fix Add `LanguageName::snippet_scope_id()` (the `lsp_id` with `/` and `\` removed) and use it everywhere a language maps to its snippet file name or lookup key: - the Configure Snippets writer and its "already configured" label, and - the two completion lookups in `editor`. `PL/X` now maps to a flat `plx.json`, as suggested in the issue. The `editor::InsertSnippet` action is intentionally left unchanged: its `language` field is documented to be the snippet file name stem, which is already separator-free. Note: files created under the old behavior (nested `foo/bar.json`) aren't migrated; re-running Configure Snippets writes the corrected flat file. ## Testing - Added a `language_core` unit test asserting `snippet_scope_id()` strips `/` and `\` (e.g. `PL/X` → `plx`). - `cargo test -p language_core` passes. - `./script/clippy -p language_core -p language` passes. - Docs Prettier passes. Release Notes: - Fixed snippets being unusable for languages whose name contains a `/` character
1.8 KiB
1.8 KiB
title, description
| title | description |
|---|---|
| Snippets - Zed | Create and use code snippets in Zed with tab stops, placeholders, variables, and language-scoped triggers. |
Snippets
Use the {#action snippets::ConfigureSnippets} action to create a new snippets file or edit an existing snippets file for a specified scope.
The snippets are located in ~/.config/zed/snippets directory to which you can navigate with the {#action snippets::OpenFolder} action.
Example configuration
{
// Each snippet must have a name and body, but the prefix and description are optional.
// The prefix is used to trigger the snippet, but when omitted then the name is used.
// Use placeholders like $1, $2 or ${1:defaultValue} to define tab stops.
// The $0 determines the final cursor position.
// Placeholders with the same value are linked.
// If the snippet contains the $ symbol outside of a placeholder, it must be escaped with two slashes (e.g. \\$var).
"Log to console": {
"prefix": "log",
"body": ["console.info(\"Hello, ${1:World}!\")", "$0"],
"description": "Logs to console"
}
}
Scopes
The scope is determined by the language name in lowercase e.g. python.json for Python, shell script.json for Shell Script, but there are some exceptions to this rule:
| Scope | Filename |
|---|---|
| Global | snippets.json |
| JSX | javascript.json |
| Plain Text | plaintext.json |
To create JSX snippets you have to use javascript.json snippets file, instead of jsx.json, but this does not apply to TSX and TypeScript which follow the above rule.
Path separators (/ and \) are removed from the file name, so a language named PL/X uses plx.json.
Known Limitations
- Only the first prefix is used when a list of prefixes is passed in.
- Currently only the
jsonsnippet file format is supported.