This commit is contained in:
the-mikedavis
2025-07-16 19:58:01 +00:00
parent 04b614ab0d
commit d04ab4a776
3 changed files with 7 additions and 7 deletions

View File

@@ -30,7 +30,7 @@
<asciinema-player src="/file-explorer.cast" cols="94" rows="25"></asciinema-player>
<p>25.07 adds a file explorer under <code><space>e</code>. The file explorer is a <em>picker</em>, a <a href="https://github.com/nvim-telescope/telescope.nvim">telescope</a>-like UI component central to Helix. Like most other pickers you can fuzzy search within the options. Selecting a directory with Enter opens a new file explorer under that directory and selecting a file opens that file. This is useful for examining a directory as a hierarchy. In contrast the usual file explorer (<code><space>f</code>) opens a picker with the contents of a directory recursively. For sprawling projects, the file explorer can be a more precise tool.</p>
<h2 id="lsp-documentcolors">LSP documentColors</h2>
<p>One of the flashier features of the Language Server Protocol (LSP) spec is the <a href="https://helix-editor.com/news/release-25-07-highlights/microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentColor">Document Color Request</a>. This request allows the client (Helix) to ask a language server like <code>tailwindcss-language-server</code> or <code>vscode-css-language-server</code> what ranges of the document correspond to RGB colors.</p>
<p>One of the flashier features of the Language Server Protocol (LSP) spec is the <a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentColor">Document Color Request</a>. This request allows the client (Helix) to ask a language server like <code>tailwindcss-language-server</code> or <code>vscode-css-language-server</code> what ranges of the document correspond to RGB colors.</p>
<p>In 25.07 Helix now requests document colors from language servers and displays the swatch (a small box) with the color, inline. This is exactly like the LSP inlay hints feature - which shows types - but for colors.</p>
<asciinema-player src="/lsp-document-colors.cast" cols="94" rows="25"></asciinema-player>
<h2 id="new-command-mode-features">New command mode features</h2>
@@ -62,7 +62,7 @@
<p>Like syntax trees, the applications for queries are only limited by your imagination. We currently use queries in Helix for highlighting, indentation and textobjects (recognizing functions, parameters, etc.). In the future, code folding, spell checking and code navigation could use tree-sitter queries as well.</p>
<h4 id="history-in-helix">History in Helix</h4>
<p>Helix depended on Tree-sitter for syntax highlighting even before its initial public release via the official Rust bindings to the C library, the <a href="https://crates.io/crates/tree-sitter"><code>tree-sitter</code></a> crate. The <code>tree-sitter</code> crate wraps the C library and is fairly low level. We also need a highlighter and that is provided by a separate crate: <code>tree-sitter-highlight</code>.</p>
<p><a href="https://crates.io/crates/tree-sitter-highlight"><code>tree-sitter-highlight</code></a> provides a syntax highlighter which takes the queries for a language and a document's text to highlight and can be iterated to produce highlight events. Helix could then consume highlight iterators while rendering the viewable documents. This works out-of-the-box with <code>tree-sitter-highlight</code> and for or simple use-cases like highlighting a document once, <code>tree-sitter-highlight</code> is all you need.</p>
<p><a href="https://crates.io/crates/tree-sitter-highlight"><code>tree-sitter-highlight</code></a> provides a syntax highlighter which takes the queries for a language and a document's text to highlight and can be iterated to produce highlight events. Helix could then consume highlight iterators while rendering the viewable documents. This works out-of-the-box with <code>tree-sitter-highlight</code> and for our simple use-cases like highlighting a document once, <code>tree-sitter-highlight</code> is all you need.</p>
<p>The problem with <code>tree-sitter-highlight</code> is that it doesn't work incrementally. Creating a new highlight iterator means fully re-parsing the document as well as re-analyzing the queries. This is wasteful since Tree-sitter can reuse queries. Plus parsing in Tree-sitter can work incrementally: you can give the old syntax tree to Tree-sitter and it will parse the new version of the document faster.</p>
<p>So Helix's early highlighter was a fork of <code>tree-sitter-highlight</code>, inspired by Tree-sitter's use in the <a href="https://github.com/atom/atom">Atom editor</a>, which factored out the parsed tree (a <code>Syntax</code> type) and <code>tree_sitter::Query</code>s from the highlighter. Ideally we wanted to extract this highlighter into its own crate one day so we could share it easily with other tools.</p>
<p>This highlighter grew to become unmaintainable, though. Fixes for longstanding bugs were too large to make or completely incompatible with this highlighter's design and the code was hard to reason about.</p>
@@ -94,7 +94,7 @@
<p>In a Rust file like this, the <em>root</em> layer covering the full document is Rust, naturally. Then each doc line comment (<code>///</code>) has the content past it parsed as a Markdown document, <em>combined</em> - meaning that the ranges are collectively treated as one Markdown layer. And nested within that, the indented block should act like a code-fence which is another Rust layer.</p>
<p>Internally Tree-house represents this <em>layer</em> concept as a tree. The overall <code>Syntax</code> type has a root layer for its file type, and children layers under that for all of its injections. And the children layers can inject other layers themselves, and so on. So the layers form a tree. And each layer is parsed so that it has its own syntax tree, making a kind of <em>tree of trees</em>.</p>
<h4 id="incremental-injections">Incremental injections</h4>
<p>Injections were previously discussed way back in the <a href="https://helix-editor.com/news/release-25-07-highlights/./2022-03-28-release-22.03-highlights.md">22.03 release notes</a> which added support for <em>combined</em> injections, like those Markdown comments. Later that year, <a href="https://helix-editor.com/news/release-25-07-highlights/content/news/2022-12-06-release-22.12-highlights.md">22.12</a> brought <em>incremental injections</em>. That change reduced the unnecessary work done to re-parse and rerun injections queries for documents with many injections. The switch to Tree-house improves upon incremental injections so that injection layers are re-parsed and injection queries are rerun only for layers which actually changed from any set of edits.</p>
<p>Injections were previously discussed way back in the <a href="https://helix-editor.com/news/release-22-03-highlights/">22.03 release notes</a> which added support for <em>combined</em> injections, like those Markdown comments. Later that year, <a href="https://helix-editor.com/news/release-22-12-highlights/">22.12</a> brought <em>incremental injections</em>. That change reduced the unnecessary work done to re-parse and rerun injections queries for documents with many injections. The switch to Tree-house improves upon incremental injections so that injection layers are re-parsed and injection queries are rerun only for layers which actually changed from any set of edits.</p>
<p>For a more intuitive idea of how this works, imagine a large Markdown list. The Markdown Tree-sitter parser is actually split into two: one for block syntax like code fences and another for "inline" syntax like bold, italics and inline code. The Markdown parser injects the "inline Markdown" parser for situations like list items, so a very large list in Markdown means thousands of small injections of the "inline" parser for each list item.</p>
<p>With the switch to Tree-house, editing within one list item in a large list will only cause the re-parsing and rerun of injections queries for root layer and the edited "inline" layer - the minimum amount of work required.</p>
<h4 id="locals">Locals</h4>

View File

@@ -47,7 +47,7 @@
<asciinema-player src="/file-explorer.cast" cols="94" rows="25"></asciinema-player>
<p>25.07 adds a file explorer under <code>&lt;space&gt;e</code>. The file explorer is a <em>picker</em>, a <a href="https://github.com/nvim-telescope/telescope.nvim">telescope</a>-like UI component central to Helix. Like most other pickers you can fuzzy search within the options. Selecting a directory with Enter opens a new file explorer under that directory and selecting a file opens that file. This is useful for examining a directory as a hierarchy. In contrast the usual file explorer (<code>&lt;space&gt;f</code>) opens a picker with the contents of a directory recursively. For sprawling projects, the file explorer can be a more precise tool.</p>
<h2 id="lsp-documentcolors">LSP documentColors</h2>
<p>One of the flashier features of the Language Server Protocol (LSP) spec is the <a href="https://helix-editor.com/news/release-25-07-highlights/microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentColor">Document Color Request</a>. This request allows the client (Helix) to ask a language server like <code>tailwindcss-language-server</code> or <code>vscode-css-language-server</code> what ranges of the document correspond to RGB colors.</p>
<p>One of the flashier features of the Language Server Protocol (LSP) spec is the <a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentColor">Document Color Request</a>. This request allows the client (Helix) to ask a language server like <code>tailwindcss-language-server</code> or <code>vscode-css-language-server</code> what ranges of the document correspond to RGB colors.</p>
<p>In 25.07 Helix now requests document colors from language servers and displays the swatch (a small box) with the color, inline. This is exactly like the LSP inlay hints feature - which shows types - but for colors.</p>
<asciinema-player src="/lsp-document-colors.cast" cols="94" rows="25"></asciinema-player>
<h2 id="new-command-mode-features">New command mode features</h2>
@@ -79,7 +79,7 @@
<p>Like syntax trees, the applications for queries are only limited by your imagination. We currently use queries in Helix for highlighting, indentation and textobjects (recognizing functions, parameters, etc.). In the future, code folding, spell checking and code navigation could use tree-sitter queries as well.</p>
<h4 id="history-in-helix">History in Helix</h4>
<p>Helix depended on Tree-sitter for syntax highlighting even before its initial public release via the official Rust bindings to the C library, the <a href="https://crates.io/crates/tree-sitter"><code>tree-sitter</code></a> crate. The <code>tree-sitter</code> crate wraps the C library and is fairly low level. We also need a highlighter and that is provided by a separate crate: <code>tree-sitter-highlight</code>.</p>
<p><a href="https://crates.io/crates/tree-sitter-highlight"><code>tree-sitter-highlight</code></a> provides a syntax highlighter which takes the queries for a language and a document's text to highlight and can be iterated to produce highlight events. Helix could then consume highlight iterators while rendering the viewable documents. This works out-of-the-box with <code>tree-sitter-highlight</code> and for or simple use-cases like highlighting a document once, <code>tree-sitter-highlight</code> is all you need.</p>
<p><a href="https://crates.io/crates/tree-sitter-highlight"><code>tree-sitter-highlight</code></a> provides a syntax highlighter which takes the queries for a language and a document's text to highlight and can be iterated to produce highlight events. Helix could then consume highlight iterators while rendering the viewable documents. This works out-of-the-box with <code>tree-sitter-highlight</code> and for our simple use-cases like highlighting a document once, <code>tree-sitter-highlight</code> is all you need.</p>
<p>The problem with <code>tree-sitter-highlight</code> is that it doesn't work incrementally. Creating a new highlight iterator means fully re-parsing the document as well as re-analyzing the queries. This is wasteful since Tree-sitter can reuse queries. Plus parsing in Tree-sitter can work incrementally: you can give the old syntax tree to Tree-sitter and it will parse the new version of the document faster.</p>
<p>So Helix's early highlighter was a fork of <code>tree-sitter-highlight</code>, inspired by Tree-sitter's use in the <a href="https://github.com/atom/atom">Atom editor</a>, which factored out the parsed tree (a <code>Syntax</code> type) and <code>tree_sitter::Query</code>s from the highlighter. Ideally we wanted to extract this highlighter into its own crate one day so we could share it easily with other tools.</p>
<p>This highlighter grew to become unmaintainable, though. Fixes for longstanding bugs were too large to make or completely incompatible with this highlighter's design and the code was hard to reason about.</p>
@@ -111,7 +111,7 @@
<p>In a Rust file like this, the <em>root</em> layer covering the full document is Rust, naturally. Then each doc line comment (<code>///</code>) has the content past it parsed as a Markdown document, <em>combined</em> - meaning that the ranges are collectively treated as one Markdown layer. And nested within that, the indented block should act like a code-fence which is another Rust layer.</p>
<p>Internally Tree-house represents this <em>layer</em> concept as a tree. The overall <code>Syntax</code> type has a root layer for its file type, and children layers under that for all of its injections. And the children layers can inject other layers themselves, and so on. So the layers form a tree. And each layer is parsed so that it has its own syntax tree, making a kind of <em>tree of trees</em>.</p>
<h4 id="incremental-injections">Incremental injections</h4>
<p>Injections were previously discussed way back in the <a href="https://helix-editor.com/news/release-25-07-highlights/./2022-03-28-release-22.03-highlights.md">22.03 release notes</a> which added support for <em>combined</em> injections, like those Markdown comments. Later that year, <a href="https://helix-editor.com/news/release-25-07-highlights/content/news/2022-12-06-release-22.12-highlights.md">22.12</a> brought <em>incremental injections</em>. That change reduced the unnecessary work done to re-parse and rerun injections queries for documents with many injections. The switch to Tree-house improves upon incremental injections so that injection layers are re-parsed and injection queries are rerun only for layers which actually changed from any set of edits.</p>
<p>Injections were previously discussed way back in the <a href="https://helix-editor.com/news/release-22-03-highlights/">22.03 release notes</a> which added support for <em>combined</em> injections, like those Markdown comments. Later that year, <a href="https://helix-editor.com/news/release-22-12-highlights/">22.12</a> brought <em>incremental injections</em>. That change reduced the unnecessary work done to re-parse and rerun injections queries for documents with many injections. The switch to Tree-house improves upon incremental injections so that injection layers are re-parsed and injection queries are rerun only for layers which actually changed from any set of edits.</p>
<p>For a more intuitive idea of how this works, imagine a large Markdown list. The Markdown Tree-sitter parser is actually split into two: one for block syntax like code fences and another for "inline" syntax like bold, italics and inline code. The Markdown parser injects the "inline Markdown" parser for situations like list items, so a very large list in Markdown means thousands of small injections of the "inline" parser for each list item.</p>
<p>With the switch to Tree-house, editing within one list item in a large list will only cause the re-parsing and rerun of injections queries for root layer and the edited "inline" layer - the minimum amount of work required.</p>
<h4 id="locals">Locals</h4>

File diff suppressed because one or more lines are too long