From ce6989e9f33f570f70af3c5cbfca0eb5c960b3db Mon Sep 17 00:00:00 2001 From: Andrew Dupont Date: Wed, 24 Jul 2024 20:52:01 -0700 Subject: [PATCH] =?UTF-8?q?Use=20first=20significant=20content=20on=20the?= =?UTF-8?q?=20line=20(not=20column=200)=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …when determining suggested indent. There's a lot of nuance here about which layer gets asked the question of what the current line's indentation level should be. To make sure we're asking the right layer, we want to consider the deepest layer that is active at a given point. But since injections rarely cover the whitespace that precedes them, we generally want to look at the beginnings and ends of the _content_ on various lines. So if the first ten characters of a line are whitespace, we should ask what the controlling layer is at column 10, not at column 0. --- spec/wasm-tree-sitter-language-mode-spec.js | 47 +++++++++++++++++++++ src/wasm-tree-sitter-language-mode.js | 13 +++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/spec/wasm-tree-sitter-language-mode-spec.js b/spec/wasm-tree-sitter-language-mode-spec.js index f47424389..bb42a6ddf 100644 --- a/spec/wasm-tree-sitter-language-mode-spec.js +++ b/spec/wasm-tree-sitter-language-mode-spec.js @@ -1750,6 +1750,53 @@ describe('WASMTreeSitterLanguageMode', () => { expect(Array.from(map.values())).toEqual([0, 1, 1, 2, 1, 0]); }); + + it('works correctly when straddling an injection boundary, even in the presence of whitespace', async () => { + const jsGrammar = new WASMTreeSitterGrammar(atom.grammars, jsGrammarPath, jsConfig); + + jsGrammar.addInjectionPoint(HTML_TEMPLATE_LITERAL_INJECTION_POINT); + + const htmlGrammar = new WASMTreeSitterGrammar( + atom.grammars, + htmlGrammarPath, + htmlConfig + ); + + htmlGrammar.addInjectionPoint(SCRIPT_TAG_INJECTION_POINT); + + atom.grammars.addGrammar(jsGrammar); + atom.grammars.addGrammar(htmlGrammar); + + // This is just like the test above, except that we're indented a bit. + // Now the edge of the injection isn't at the beginning of the line; it's + // at the beginning of the first _text_ on the line. + buffer.setText(dedent` + + + + + + `); + + const languageMode = new WASMTreeSitterLanguageMode({ + grammar: htmlGrammar, + buffer, + config: atom.config, + grammars: atom.grammars + }); + + buffer.setLanguageMode(languageMode); + await languageMode.ready; + + let map = languageMode.suggestedIndentForBufferRows(0, 9, editor.getTabLength()); + + expect(Array.from(map.values())).toEqual([0, 1, 2, 3, 3, 4, 3, 2, 1, 0]); + }) }); describe('folding', () => { diff --git a/src/wasm-tree-sitter-language-mode.js b/src/wasm-tree-sitter-language-mode.js index 07219572c..7f007233a 100644 --- a/src/wasm-tree-sitter-language-mode.js +++ b/src/wasm-tree-sitter-language-mode.js @@ -1605,6 +1605,8 @@ class WASMTreeSitterLanguageMode { indentDelta -= clamp(dedentNextDelta, 0, 1); let dedentDelta = 0; + let line = this.buffer.lineForRow(row); + let rowStartingColumn = Math.max(line.search(/\S/), 0); if (!options.skipDedentCheck) { scopeResolver.reset(); @@ -1613,7 +1615,11 @@ class WASMTreeSitterLanguageMode { // starting indent is on the current line. But it might not extend to the // current line, so we should determine which layer is in charge of the // second phase. - let rowStart = new Point(row, 0); + // + // The comparison point we use is that of the first character on the + // line. If we start earlier than that, we might not pick up on the + // presence of an injection layer. + let rowStart = new Point(row, rowStartingColumn); let dedentControllingLayer = this.controllingLayerAtPoint( rowStart, (layer) => { @@ -1885,8 +1891,11 @@ class WASMTreeSitterLanguageMode { // By the time this function runs, we probably know enough to be sure of // which layer controls the beginning of this row, even if we don't know // which one owns the position at the cursor. + // + // Use the position of the first text on the line as the reference point. + let rowStartingColumn = Math.max(line.search(/\S/), 0); let controllingLayer = this.controllingLayerAtPoint( - new Point(row, 0), + new Point(row, rowStartingColumn), (layer) => !!layer.indentsQuery );