Skip to content
This repository has been archived by the owner on Mar 3, 2023. It is now read-only.

Perform parsing off the main thread when Tree-sitter is enabled #17339

Merged
merged 7 commits into from
May 23, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Reparse again if there were changes since the last parse started
  • Loading branch information
maxbrunsfeld committed May 23, 2018
commit f6d2d5729944abbff8a2db1ab2a35d223e998b8c
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"sinon": "1.17.4",
"temp": "^0.8.3",
"text-buffer": "13.14.3",
"tree-sitter": "0.12.1-1",
"tree-sitter": "0.12.4",
"typescript-simple": "1.0.0",
"underscore-plus": "^1.6.6",
"winreg": "^1.2.1",
Expand Down
61 changes: 61 additions & 0 deletions spec/tree-sitter-language-mode-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,67 @@ describe('TreeSitterLanguageMode', () => {
]
])
})

describe('when the buffer changes during a parse', () => {
it('immediately parses again when the current parse completes', async () => {
const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
parser: 'tree-sitter-javascript',
scopes: {
'identifier': 'variable',
'call_expression > identifier': 'function',
'new_expression > call_expression > identifier': 'constructor'
}
})
const languageMode = new TreeSitterLanguageMode({buffer, grammar})
buffer.setLanguageMode(languageMode)

buffer.setText('abc;');
await languageMode.reparsePromise
expectTokensToEqual(editor, [
[
{text: 'abc', scopes: ['variable']},
{text: ';', scopes: []}
],
])

buffer.setTextInRange([[0, 3], [0, 3]], '()');
expectTokensToEqual(editor, [
[
{text: 'abc()', scopes: ['variable']},
{text: ';', scopes: []}
],
])

buffer.setTextInRange([[0, 0], [0, 0]], 'new ');
expectTokensToEqual(editor, [
[
{text: 'new ', scopes: []},
{text: 'abc()', scopes: ['variable']},
{text: ';', scopes: []}
],
])

await languageMode.reparsePromise
expect(languageMode.reparsePromise).not.toBeNull()
expectTokensToEqual(editor, [
[
{text: 'new ', scopes: []},
{text: 'abc', scopes: ['function']},
{text: '();', scopes: []}
],
])

await languageMode.reparsePromise
expect(languageMode.reparsePromise).toBeNull()
expectTokensToEqual(editor, [
[
{text: 'new ', scopes: []},
{text: 'abc', scopes: ['constructor']},
{text: '();', scopes: []}
],
])
})
})
})

describe('folding', () => {
Expand Down
52 changes: 36 additions & 16 deletions src/tree-sitter-language-mode.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ class TreeSitterLanguageMode {
this.emitter = new Emitter()
this.isFoldableCache = []
this.hasQueuedParse = false
this.buffer.onDidChangeText(async () => {
if (!this.reparsePromise) {
this.reparsePromise = this.reparse().then(() => {
this.reparsePromise = null
})
this.changeListsSinceCurrentParse = []
this.buffer.onDidChangeText(async ({changes}) => {
if (this.reparsePromise) {
this.changeListsSinceCurrentParse.push(changes)
} else {
this.reparsePromise = this.reparse()
}
})

Expand All @@ -38,29 +39,35 @@ class TreeSitterLanguageMode {
return this.grammar.id
}

bufferDidChange ({oldRange, newRange, oldText, newText}) {
bufferDidChange (change) {
const {oldRange, newRange} = change
const startRow = oldRange.start.row
const oldEndRow = oldRange.end.row
const newEndRow = newRange.end.row
this.isFoldableCache.splice(startRow, oldEndRow - startRow, ...new Array(newEndRow - startRow))
this.tree.edit({
startIndex: this.buffer.characterIndexForPosition(oldRange.start),
lengthRemoved: oldText.length,
lengthAdded: newText.length,
startPosition: oldRange.start,
extentRemoved: oldRange.getExtent(),
extentAdded: newRange.getExtent()
})
this.tree.edit(this.treeEditForBufferChange(change))
}

/*
Section - Highlighting
*/

treeEditForBufferChange ({oldRange, newRange, oldText, newText}) {
const startIndex = this.buffer.characterIndexForPosition(oldRange.start)
return {
startIndex,
oldEndIndex: startIndex + oldText.length,
newEndIndex: startIndex + newText.length,
startPosition: oldRange.start,
oldEndPosition: oldRange.end,
newEndPosition: newRange.end
}
}

async reparse () {
const tree = await this.parser.parseTextBuffer(this.buffer.buffer, this.tree)
const invalidatedRanges = tree.getChangedRanges(this.tree)
this.tree = tree
const invalidatedRanges = this.tree.getChangedRanges(tree)

for (let i = 0, n = invalidatedRanges.length; i < n; i++) {
const range = invalidatedRanges[i]
const startRow = range.start.row
Expand All @@ -70,6 +77,19 @@ class TreeSitterLanguageMode {
}
this.emitter.emit('did-change-highlighting', range)
}

this.tree = tree
if (this.changeListsSinceCurrentParse.length > 0) {
for (const changeList of this.changeListsSinceCurrentParse) {
for (let i = changeList.length - 1; i >= 0; i--) {
this.tree.edit(this.treeEditForBufferChange(changeList[i]))
}
}
this.changeListsSinceCurrentParse.length = 0
this.reparsePromise = this.reparse()
} else {
this.reparsePromise = null
}
}

buildHighlightIterator () {
Expand Down