From be5020f05fb3ca682fd6889b8649e1113533b4bd Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 13 Jul 2018 13:03:56 -0700 Subject: [PATCH] Rework handling of edited ranges --- spec/tree-sitter-language-mode-spec.js | 75 +++++++++++--------------- src/tree-sitter-language-mode.js | 66 +++++++++++++---------- 2 files changed, 69 insertions(+), 72 deletions(-) diff --git a/spec/tree-sitter-language-mode-spec.js b/spec/tree-sitter-language-mode-spec.js index 7243fe070..80ee892f6 100644 --- a/spec/tree-sitter-language-mode-spec.js +++ b/spec/tree-sitter-language-mode-spec.js @@ -37,7 +37,7 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [[ {text: 'aa.', scopes: ['source']}, @@ -67,7 +67,7 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [[ {text: 'a', scopes: ['source', 'variable']}, @@ -94,7 +94,7 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ @@ -122,9 +122,8 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) - editor.screenLineForScreenRow(0) expect( languageMode.tree.rootNode.descendantForPosition(Point(1, 2), Point(1, 6)).toString() ).toBe('(declaration (primitive_type) (identifier) (MISSING))') @@ -168,7 +167,7 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) // missing closing paren expectTokensToEqual(editor, [ @@ -179,7 +178,7 @@ describe('TreeSitterLanguageMode', () => { ]) buffer.append(')') - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ {text: 'a', scopes: ['function']}, @@ -209,8 +208,8 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [{text: '// abc', scopes: ['comment']}], [{text: '', scopes: []}], @@ -223,7 +222,7 @@ describe('TreeSitterLanguageMode', () => { ]) buffer.insert([2, 0], ' ') - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [{text: '// abc', scopes: ['comment']}], [{text: '', scopes: []}], @@ -248,9 +247,10 @@ describe('TreeSitterLanguageMode', () => { }); buffer.setText('`\na${1}\nb${2}\n`;') + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ @@ -288,7 +288,8 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) + await new Promise(process.nextTick) expectTokensToEqual(editor, [ [ @@ -314,8 +315,7 @@ describe('TreeSitterLanguageMode', () => { ], ]) - await languageMode.reparsePromise - expect(languageMode.reparsePromise).not.toBeNull() + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ {text: 'new ', scopes: []}, @@ -324,8 +324,7 @@ describe('TreeSitterLanguageMode', () => { ], ]) - await languageMode.reparsePromise - expect(languageMode.reparsePromise).toBeNull() + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ {text: 'new ', scopes: []}, @@ -378,7 +377,8 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ @@ -407,7 +407,8 @@ describe('TreeSitterLanguageMode', () => { const range = buffer.findSync('html') buffer.setTextInRange(range, 'xml') - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ @@ -437,7 +438,8 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ @@ -473,8 +475,9 @@ describe('TreeSitterLanguageMode', () => { buffer.setText('node.innerHTML = html `\na ${b}\n`;') const languageMode = new TreeSitterLanguageMode({buffer, grammar: jsGrammar, grammars: atom.grammars}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise - expectTokensToEqual(editor, [ + + await nextHighlightingUpdate(languageMode) + expectTokensToEqual(editor, [ [ {text: 'node.', scopes: []}, {text: 'innerHTML', scopes: ['property']}, @@ -495,7 +498,7 @@ describe('TreeSitterLanguageMode', () => { ]) atom.grammars.addGrammar(htmlGrammar) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) expectTokensToEqual(editor, [ [ {text: 'node.', scopes: []}, @@ -626,9 +629,7 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise - - editor.screenLineForScreenRow(0) + await nextHighlightingUpdate(languageMode) expect(editor.isFoldableAtBufferRow(0)).toBe(false) expect(editor.isFoldableAtBufferRow(1)).toBe(true) @@ -690,9 +691,7 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise - - editor.screenLineForScreenRow(0) + await nextHighlightingUpdate(languageMode) expect(editor.isFoldableAtBufferRow(0)).toBe(true) expect(editor.isFoldableAtBufferRow(1)).toBe(false) @@ -741,9 +740,7 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise - - editor.screenLineForScreenRow(0) + await nextHighlightingUpdate(languageMode) expect(editor.isFoldableAtBufferRow(0)).toBe(true) expect(editor.isFoldableAtBufferRow(1)).toBe(false) @@ -818,9 +815,7 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise - - editor.screenLineForScreenRow(0) + await nextHighlightingUpdate(languageMode) editor.foldBufferRow(3) expect(getDisplayText(editor)).toBe(dedent ` @@ -902,7 +897,7 @@ describe('TreeSitterLanguageMode', () => { const languageMode = new TreeSitterLanguageMode({buffer, grammar}) buffer.setLanguageMode(languageMode) - await languageMode.reparsePromise + await nextHighlightingUpdate(languageMode) // Void elements have only one child expect(editor.isFoldableAtBufferRow(1)).toBe(false) @@ -938,9 +933,7 @@ describe('TreeSitterLanguageMode', () => { `) buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - await buffer.getLanguageMode().reparsePromise - - editor.screenLineForScreenRow(0) + await nextHighlightingUpdate(buffer.getLanguageMode()) editor.foldBufferRow(0) expect(getDisplayText(editor)).toBe(dedent ` @@ -1040,9 +1033,7 @@ describe('TreeSitterLanguageMode', () => { buffer.setText('foo({bar: baz});') buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - await buffer.getLanguageMode().reparsePromise - - editor.screenLineForScreenRow(0) + await nextHighlightingUpdate(buffer.getLanguageMode()) expect(editor.scopeDescriptorForBufferPosition([0, 6]).getScopesArray()).toEqual([ 'javascript', 'program', @@ -1071,9 +1062,7 @@ describe('TreeSitterLanguageMode', () => { `) buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) - await buffer.getLanguageMode().reparsePromise - - editor.screenLineForScreenRow(0) + await nextHighlightingUpdate(buffer.getLanguageMode()) editor.setCursorBufferPosition([1, 3]) editor.selectLargerSyntaxNode() diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index 850458ccb..235eac805 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -113,10 +113,6 @@ class TreeSitterLanguageMode { return this.rootLanguageLayer.tree } - get reparsePromise () { - return this.rootLanguageLayer.currentParsePromise - } - updateForInjection (grammar) { this.rootLanguageLayer.updateInjections(grammar) } @@ -474,12 +470,22 @@ class LanguageLayer { this.tree.edit(this._treeEditForBufferChange( oldRange.start, oldRange.end, newRange.end, oldText, newText )) + + if (this.editedRange) { + if (newRange.start.isLessThan(this.editedRange.start)) { + this.editedRange.start = newRange.start + } + if (oldRange.end.isLessThan(this.editedRange.end)) { + this.editedRange.end = newRange.end.traverse(this.editedRange.end.traversalFrom(oldRange.end)) + } else { + this.editedRange.end = newRange.end + } + } else { + this.editedRange = newRange.copy() + } } - if (this.currentParsePromise) { - if (!this.patchSinceCurrentParseStarted) { - this.patchSinceCurrentParseStarted = new Patch() - } + if (this.patchSinceCurrentParseStarted) { this.patchSinceCurrentParseStarted.splice( oldRange.start, oldRange.end, @@ -500,22 +506,12 @@ class LanguageLayer { } async update (nodeRangeSet) { - if (this.currentParsePromise) return this.currentParsePromise - - this.currentParsePromise = this._performUpdate(nodeRangeSet) - await this.currentParsePromise - this.currentParsePromise = null - - if (this.patchSinceCurrentParseStarted) { - const changes = this.patchSinceCurrentParseStarted.getChanges() - for (let i = changes.length - 1; i >= 0; i--) { - const {oldStart, oldEnd, newEnd, oldText, newText} = changes[i] - this.tree.edit(this._treeEditForBufferChange( - oldStart, oldEnd, newEnd, oldText, newText - )) - } - this.patchSinceCurrentParseStarted = null - this.update(nodeRangeSet) + if (!this.currentParsePromise) { + do { + this.currentParsePromise = this._performUpdate(nodeRangeSet) + await this.currentParsePromise + } while (this.tree && this.tree.rootNode.hasChanges()) + this.currentParsePromise = null } } @@ -536,6 +532,10 @@ class LanguageLayer { if (includedRanges.length === 0) return } + let affectedRange = this.editedRange + this.editedRange = null + + this.patchSinceCurrentParseStarted = new Patch() const tree = await this.languageMode.parse( this.grammar.languageModule, this.tree, @@ -543,14 +543,20 @@ class LanguageLayer { ) tree.buffer = this.languageMode.buffer - let affectedRange - if (this.tree) { - const editedRange = this.tree.getEditedRange() - if (!editedRange) return - affectedRange = rangeForNode(editedRange) + const changes = this.patchSinceCurrentParseStarted.getChanges() + this.patchSinceCurrentParseStarted = null + for (let i = changes.length - 1; i >= 0; i--) { + const {oldStart, oldEnd, newEnd, oldText, newText} = changes[i] + tree.edit(this._treeEditForBufferChange( + oldStart, oldEnd, newEnd, oldText, newText + )) + } + if (this.tree) { const rangesWithSyntaxChanges = this.tree.getChangedRanges(tree) this.tree = tree + + if (!affectedRange) return if (rangesWithSyntaxChanges.length > 0) { for (const range of rangesWithSyntaxChanges) { this.languageMode.emitRangeUpdate(rangeForNode(range)) @@ -560,6 +566,8 @@ class LanguageLayer { rangesWithSyntaxChanges[0].startPosition, last(rangesWithSyntaxChanges).endPosition )) + } else { + this.languageMode.emitRangeUpdate(affectedRange) } } else { this.tree = tree