diff --git a/spec/tree-sitter-language-mode-spec.js b/spec/tree-sitter-language-mode-spec.js
index e2671e2ea..c23849d30 100644
--- a/spec/tree-sitter-language-mode-spec.js
+++ b/spec/tree-sitter-language-mode-spec.js
@@ -402,11 +402,7 @@ describe('TreeSitterLanguageMode', () => {
attribute_name: 'attr'
},
injectionRegExp: 'html',
- injectionPoints: [{
- type: 'raw_element',
- language () { return 'javascript' },
- content (node) { return node.child(1) }
- }]
+ injectionPoints: [SCRIPT_TAG_INJECTION_POINT]
})
})
@@ -1081,6 +1077,61 @@ describe('TreeSitterLanguageMode', () => {
'property_identifier'
])
})
+
+ it('includes nodes in injected syntax trees', async () => {
+ const jsGrammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, {
+ id: 'javascript',
+ parser: 'tree-sitter-javascript',
+ scopes: {},
+ injectionRegExp: 'javascript',
+ injectionPoints: [HTML_TEMPLATE_LITERAL_INJECTION_POINT]
+ })
+
+ const htmlGrammar = new TreeSitterGrammar(atom.grammars, htmlGrammarPath, {
+ id: 'html',
+ parser: 'tree-sitter-html',
+ scopes: {},
+ injectionRegExp: 'html',
+ injectionPoints: [SCRIPT_TAG_INJECTION_POINT]
+ })
+
+ atom.grammars.addGrammar(jsGrammar)
+ atom.grammars.addGrammar(htmlGrammar)
+
+ buffer.setText(`
+
+
+
+ `)
+
+ const languageMode = new TreeSitterLanguageMode({buffer, grammar: htmlGrammar, grammars: atom.grammars})
+ buffer.setLanguageMode(languageMode)
+ await nextHighlightingUpdate(languageMode)
+ await nextHighlightingUpdate(languageMode)
+ await nextHighlightingUpdate(languageMode)
+
+ 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'
+ ])
+ })
})
describe('TextEditor.selectLargerSyntaxNode and .selectSmallerSyntaxNode', () => {
@@ -1247,3 +1298,9 @@ const HTML_TEMPLATE_LITERAL_INJECTION_POINT = {
return node.lastChild
}
}
+
+const SCRIPT_TAG_INJECTION_POINT = {
+ type: 'raw_element',
+ language () { return 'javascript' },
+ content (node) { return node.child(1) }
+}
diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js
index c431e51b9..7d0377f28 100644
--- a/src/tree-sitter-language-mode.js
+++ b/src/tree-sitter-language-mode.js
@@ -389,23 +389,41 @@ class TreeSitterLanguageMode {
scopeDescriptorForPosition (point) {
if (!this.tree) return this.rootScopeDescriptor
-
point = Point.fromObject(point)
- let node = this.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
+ const iterators = []
+ this._forEachTreeWithRange(new Range(point, point), tree => {
+ const rootStartIndex = tree.rootNode.startIndex
+ let node = tree.rootNode.descendantForPosition(point)
- const result = []
- while (node) {
- result.push(node.type)
- node = node.parent
+ // 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 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()
+ }
}
- result.push(this.grammar.id)
- return new ScopeDescriptor({scopes: result.reverse()})
+
+ scopes.push(this.grammar.id)
+ return new ScopeDescriptor({scopes: scopes.reverse()})
}
getGrammar () {
@@ -1011,6 +1029,13 @@ 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 pointIsGreater (left, right) {
return left.row > right.row || left.row === right.row && left.column > right.column
}