Skip to content

Commit c222849

Browse files
Max Brunsfeldmaxbrunsfeld
authored andcommitted
Merge pull request atom#17956 from atom/mb-tree-sitter-fixes
Fix some Tree-sitter bugs found when enabling by default
1 parent 389a0ee commit c222849

File tree

2 files changed

+84
-10
lines changed

2 files changed

+84
-10
lines changed

spec/tree-sitter-language-mode-spec.js

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ describe('TreeSitterLanguageMode', () => {
175175
])
176176

177177
buffer.append(')')
178-
await nextHighlightingUpdate(languageMode)
179178
expectTokensToEqual(editor, [
180179
[
181180
{text: 'a', scopes: ['function']},
@@ -459,6 +458,50 @@ describe('TreeSitterLanguageMode', () => {
459458
})
460459
})
461460

461+
describe('when changes are small enough to be re-parsed synchronously', () => {
462+
it('can incorporate multiple consecutive synchronous updates', () => {
463+
const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
464+
parser: 'tree-sitter-javascript',
465+
scopes: {
466+
'property_identifier': 'property',
467+
'call_expression > identifier': 'function',
468+
'call_expression > member_expression > property_identifier': 'method',
469+
}
470+
})
471+
472+
const languageMode = new TreeSitterLanguageMode({buffer, grammar})
473+
buffer.setLanguageMode(languageMode)
474+
buffer.setText('a');
475+
expectTokensToEqual(editor, [[
476+
{text: 'a', scopes: []},
477+
]])
478+
479+
buffer.append('.')
480+
expectTokensToEqual(editor, [[
481+
{text: 'a.', scopes: []},
482+
]])
483+
484+
buffer.append('b')
485+
expectTokensToEqual(editor, [[
486+
{text: 'a.', scopes: []},
487+
{text: 'b', scopes: ['property']},
488+
]])
489+
490+
buffer.append('()')
491+
expectTokensToEqual(editor, [[
492+
{text: 'a.', scopes: []},
493+
{text: 'b', scopes: ['method']},
494+
{text: '()', scopes: []},
495+
]])
496+
497+
buffer.delete([[0, 1], [0, 2]])
498+
expectTokensToEqual(editor, [[
499+
{text: 'ab', scopes: ['function']},
500+
{text: '()', scopes: []},
501+
]])
502+
})
503+
})
504+
462505
describe('injectionPoints and injectionPatterns', () => {
463506
let jsGrammar, htmlGrammar
464507

@@ -526,7 +569,6 @@ describe('TreeSitterLanguageMode', () => {
526569
const range = buffer.findSync('html')
527570
buffer.setTextInRange(range, 'xml')
528571
await nextHighlightingUpdate(languageMode)
529-
await nextHighlightingUpdate(languageMode)
530572

531573
expectTokensToEqual(editor, [
532574
[
@@ -1371,6 +1413,23 @@ describe('TreeSitterLanguageMode', () => {
13711413
'property.name'
13721414
])
13731415
})
1416+
1417+
it('includes the root scope name even when the given position is in trailing whitespace at EOF', () => {
1418+
const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
1419+
scopeName: 'source.js',
1420+
parser: 'tree-sitter-javascript',
1421+
scopes: {
1422+
program: 'source.js',
1423+
property_identifier: 'property.name'
1424+
}
1425+
})
1426+
1427+
buffer.setText('a; ')
1428+
buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar}))
1429+
expect(editor.scopeDescriptorForBufferPosition([0, 3]).getScopesArray()).toEqual([
1430+
'source.js'
1431+
])
1432+
})
13741433
})
13751434

13761435
describe('.bufferRangeForScopeAtPosition(selector?, position)', () => {

src/tree-sitter-language-mode.js

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,9 @@ class TreeSitterLanguageMode {
429429
for (const scope of iterator.getOpenScopeIds()) {
430430
scopes.push(this.grammar.scopeNameForScopeId(scope, false))
431431
}
432+
if (scopes.length === 0 || scopes[0] !== this.grammar.scopeName) {
433+
scopes.unshift(this.grammar.scopeName)
434+
}
432435
return new ScopeDescriptor({scopes})
433436
}
434437

@@ -515,7 +518,9 @@ class LanguageLayer {
515518
async update (nodeRangeSet) {
516519
if (!this.currentParsePromise) {
517520
do {
518-
this.currentParsePromise = this._performUpdate(nodeRangeSet)
521+
const params = {async: false}
522+
this.currentParsePromise = this._performUpdate(nodeRangeSet, params)
523+
if (!params.async) break
519524
await this.currentParsePromise
520525
} while (this.tree && this.tree.rootNode.hasChanges())
521526
this.currentParsePromise = null
@@ -532,7 +537,7 @@ class LanguageLayer {
532537
}
533538
}
534539

535-
async _performUpdate (nodeRangeSet) {
540+
async _performUpdate (nodeRangeSet, params) {
536541
let includedRanges = null
537542
if (nodeRangeSet) {
538543
includedRanges = nodeRangeSet.getRanges()
@@ -551,7 +556,10 @@ class LanguageLayer {
551556
this.tree,
552557
includedRanges
553558
)
554-
if (tree.then) tree = await tree
559+
if (tree.then) {
560+
params.async = true
561+
tree = await tree
562+
}
555563
tree.buffer = this.languageMode.buffer
556564

557565
const changes = this.patchSinceCurrentParseStarted.getChanges()
@@ -590,7 +598,11 @@ class LanguageLayer {
590598
}
591599
}
592600

593-
await this._populateInjections(affectedRange, nodeRangeSet)
601+
const injectionPromise = this._populateInjections(affectedRange, nodeRangeSet)
602+
if (injectionPromise) {
603+
params.async = true
604+
return injectionPromise
605+
}
594606
}
595607

596608
_populateInjections (range, nodeRangeSet) {
@@ -651,11 +663,14 @@ class LanguageLayer {
651663
}
652664
}
653665

654-
const promises = []
655-
for (const [marker, nodeRangeSet] of markersToUpdate) {
656-
promises.push(marker.languageLayer.update(nodeRangeSet))
666+
if (markersToUpdate.size > 0) {
667+
this.lastUpdateWasAsync = true
668+
const promises = []
669+
for (const [marker, nodeRangeSet] of markersToUpdate) {
670+
promises.push(marker.languageLayer.update(nodeRangeSet))
671+
}
672+
return Promise.all(promises)
657673
}
658-
return Promise.all(promises)
659674
}
660675

661676
_treeEditForBufferChange (start, oldEnd, newEnd, oldText, newText) {

0 commit comments

Comments
 (0)