From fd6f9d39bd8ae763bb3a9276a43b27943ae20cee Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Mon, 20 Aug 2018 10:01:13 -0700 Subject: [PATCH] Make tree-sitter scope descriptors match HTML classes, not syntax tree --- spec/tree-sitter-language-mode-spec.js | 60 +++++++++++++------------- src/tree-sitter-grammar.js | 12 ++++++ src/tree-sitter-language-mode.js | 48 +++------------------ 3 files changed, 50 insertions(+), 70 deletions(-) diff --git a/spec/tree-sitter-language-mode-spec.js b/spec/tree-sitter-language-mode-spec.js index 3862df6d6..01b053e35 100644 --- a/spec/tree-sitter-language-mode-spec.js +++ b/spec/tree-sitter-language-mode-spec.js @@ -1323,39 +1323,49 @@ describe('TreeSitterLanguageMode', () => { describe('.scopeDescriptorForPosition', () => { it('returns a scope descriptor representing the given position in the syntax tree', async () => { const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { - scopeName: 'javascript', - parser: 'tree-sitter-javascript' + scopeName: 'source.js', + parser: 'tree-sitter-javascript', + scopes: { + program: 'source.js', + property_identifier: 'property.name' + } }) buffer.setText('foo({bar: baz});') buffer.setLanguageMode(new TreeSitterLanguageMode({buffer, grammar})) await nextHighlightingUpdate(buffer.getLanguageMode()) - expect(editor.scopeDescriptorForBufferPosition([0, 6]).getScopesArray()).toEqual([ - 'javascript', - 'program', - 'expression_statement', - 'call_expression', - 'arguments', - 'object', - 'pair', - 'property_identifier' + expect(editor.scopeDescriptorForBufferPosition([0, 'foo({b'.length]).getScopesArray()).toEqual([ + 'source.js', + 'property.name' + ]) + expect(editor.scopeDescriptorForBufferPosition([0, 'foo({'.length]).getScopesArray()).toEqual([ + 'source.js', + 'property.name' ]) }) it('includes nodes in injected syntax trees', async () => { const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { - scopeName: 'javascript', + scopeName: 'source.js', parser: 'tree-sitter-javascript', - scopes: {}, + scopes: { + program: 'source.js', + template_string: 'string.quoted', + interpolation: 'meta.embedded', + property_identifier: 'property.name' + }, injectionRegExp: 'javascript', injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT] }) const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, { - scopeName: 'html', + scopeName: 'text.html', parser: 'tree-sitter-html', - scopes: {}, + scopes: { + fragment: 'text.html', + raw_element: 'script.tag' + }, injectionRegExp: 'html', injectionPoints: [SCRIPT_TAG_INJECTION_POINT] }) @@ -1381,20 +1391,12 @@ describe('TreeSitterLanguageMode', () => { const position = buffer.findSync('name').start expect(languageMode.scopeDescriptorForPosition(position).getScopesArray()).toEqual([ - 'html', - 'fragment', - 'element', - 'raw_element', - 'raw_text', - 'program', - 'expression_statement', - 'call_expression', - 'template_string', - 'fragment', - 'element', - 'template_substitution', - 'member_expression', - 'property_identifier' + 'text.html', + 'script.tag', + 'source.js', + 'string.quoted', + 'text.html', + 'property.name' ]) }) }) diff --git a/src/tree-sitter-grammar.js b/src/tree-sitter-grammar.js index 447a8c87f..fc572221a 100644 --- a/src/tree-sitter-grammar.js +++ b/src/tree-sitter-grammar.js @@ -52,6 +52,7 @@ class TreeSitterGrammar { this.languageModule = require(languageModulePath) this.scopesById = new Map() + this.conciseScopesById = new Map() this.idsByScope = {} this.nextScopeId = 256 + 1 this.registration = null @@ -75,6 +76,17 @@ class TreeSitterGrammar { return this.scopesById.get(id) } + scopeNameForScopeId (id) { + let result = this.conciseScopesById.get(id) + if (!result) { + result = this.scopesById.get(id) + .slice('syntax--'.length) + .replace(/ syntax--/g, '.') + this.conciseScopesById.set(id, result) + } + return result + } + activate () { this.registration = this.registry.addGrammar(this) } diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index 56ceba642..c046cf776 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -398,42 +398,15 @@ class TreeSitterLanguageMode { } scopeDescriptorForPosition (point) { - if (!this.tree) return this.rootScopeDescriptor - point = Point.fromObject(point) - - const iterators = [] - this._forEachTreeWithRange(new Range(point, point), tree => { - const rootStartIndex = tree.rootNode.startIndex - let node = tree.rootNode.descendantForPosition(point) - - // Don't include anonymous token types like '(' because they prevent scope chains - // from being parsed as CSS selectors by the `slick` parser. Other css selector - // parsers like `postcss-selector-parser` do allow arbitrary quoted strings in - // selectors. - if (!node.isNamed) node = node.parent - iterators.push({node, rootStartIndex}) - }) - - iterators.sort(compareScopeDescriptorIterators) - + const iterator = this.buildHighlightIterator() const scopes = [] - for (;;) { - const {length} = iterators - if (!length) break - const iterator = iterators[length - 1] - scopes.push(iterator.node.type) - iterator.node = iterator.node.parent - if (iterator.node) { - let i = length - 1 - while (i > 0 && compareScopeDescriptorIterators(iterator, iterators[i - 1]) < 0) i-- - if (i < length - 1) iterators.splice(i, 0, iterators.pop()) - } else { - iterators.pop() - } + for (const scope of iterator.seek(point)) { + scopes.push(this.grammar.scopeNameForScopeId(scope, false)) } - - scopes.push(this.grammar.scopeName) - return new ScopeDescriptor({scopes: scopes.reverse()}) + for (const scope of iterator.getOpenScopeIds()) { + scopes.push(this.grammar.scopeNameForScopeId(scope, false)) + } + return new ScopeDescriptor({scopes}) } getGrammar () { @@ -1073,13 +1046,6 @@ function nodeIsSmaller (left, right) { return left.endIndex - left.startIndex < right.endIndex - right.startIndex } -function compareScopeDescriptorIterators (a, b) { - return ( - a.node.startIndex - b.node.startIndex || - a.rootStartIndex - b.rootStartIndex - ) -} - function last (array) { return array[array.length - 1] }