Use first significant content on the line (not column 0)…

…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.
This commit is contained in:
Andrew Dupont 2024-07-24 20:52:01 -07:00
parent 358cb024a5
commit ce6989e9f3
2 changed files with 58 additions and 2 deletions

View File

@ -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`
<html>
<head>
<script>
let foo;
if (foo) {
debug(true);
}
</script>
</head>
</html>
`);
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', () => {

View File

@ -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
);