From 9fa320a9544189661197d9a185a8f7111b869479 Mon Sep 17 00:00:00 2001 From: Ashi Krishnan Date: Tue, 24 Jul 2018 12:49:41 -0400 Subject: [PATCH 1/6] Regex and exact match rules support in the scope map. --- src/tree-sitter-grammar.js | 22 ++++++++++++++++++---- src/tree-sitter-language-mode.js | 27 +++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/tree-sitter-grammar.js b/src/tree-sitter-grammar.js index acea24213..7f5412020 100644 --- a/src/tree-sitter-grammar.js +++ b/src/tree-sitter-grammar.js @@ -2,6 +2,23 @@ const path = require('path') const SyntaxScopeMap = require('./syntax-scope-map') const Module = require('module') +tap = x => (console.log(x), x) + +const toSyntaxClasses = scopes => + typeof scopes == 'string' + ? scopes + .split('.') + .map(s => `syntax--${s}`) + .join(' ') + : + Array.isArray(scopes) + ? scopes.map(toSyntaxClasses) + : + scopes.match + ? tap({match: new RegExp(scopes.match), scopes: toSyntaxClasses(scopes.scopes)}) + : + Object.assign({}, scopes, {scopes: toSyntaxClasses(scopes.scopes)}) + module.exports = class TreeSitterGrammar { constructor (registry, filePath, params) { @@ -21,10 +38,7 @@ class TreeSitterGrammar { const scopeSelectors = {} for (const key in params.scopes || {}) { - scopeSelectors[key] = params.scopes[key] - .split('.') - .map(s => `syntax--${s}`) - .join(' ') + scopeSelectors[key] = toSyntaxClasses(params.scopes[key]) } this.scopeMap = new SyntaxScopeMap(scopeSelectors) diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index 7d0377f28..46d2c66df 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -904,11 +904,11 @@ class LayerHighlightIterator { // Private methods currentScopeName () { - return this.languageLayer.grammar.scopeMap.get( + return applyLeafRules(this.languageLayer.grammar.scopeMap.get( this.containingNodeTypes, this.containingNodeChildIndices, this.treeCursor.nodeIsNamed - ) + ), this.treeCursor) } idForScope (scopeName) { @@ -916,6 +916,29 @@ class LayerHighlightIterator { } } +const applyLeafRules = (rules, cursor) => { + if (!rules || typeof rules === 'string') return rules + if (Array.isArray(rules)) { + let i = rules.length; while (i --> 0) { + const result = applyLeafRules(rules[i], cursor) + if (result) return result + } + return undefined + } + if (typeof rules === 'object') { + if (rules.exact) { + return cursor.nodeText === rules.exact + ? applyLeafRules(rules.scopes, cursor) + : undefined + } + if (rules.match) { + return rules.match.test(cursor.nodeText) + ? applyLeafRules(rules.scopes, cursor) + : undefined + } + } +} + class NullHighlightIterator { seek () { return [] } moveToSuccessor () {} From 97ae903cabf6903ceed0a58a696b37babd13a904 Mon Sep 17 00:00:00 2001 From: Ashi Krishnan Date: Tue, 24 Jul 2018 13:04:34 -0400 Subject: [PATCH 2/6] It feels more natural for these rules to be applied top to bottom rather than bottom to top. --- src/tree-sitter-language-mode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index 46d2c66df..de28fc216 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -919,7 +919,7 @@ class LayerHighlightIterator { const applyLeafRules = (rules, cursor) => { if (!rules || typeof rules === 'string') return rules if (Array.isArray(rules)) { - let i = rules.length; while (i --> 0) { + for (let i = 0, {length} = rules; i != length; ++i) { const result = applyLeafRules(rules[i], cursor) if (result) return result } From f2e54a70f021a65149bcac5bf76ca1695bc3ac30 Mon Sep 17 00:00:00 2001 From: Ashi Krishnan Date: Tue, 24 Jul 2018 13:34:41 -0400 Subject: [PATCH 3/6] Lint. --- spec/tree-sitter-language-mode-spec.js | 33 ++++++++++++++++++++++++++ src/tree-sitter-grammar.js | 29 ++++++++++------------ src/tree-sitter-language-mode.js | 2 +- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/spec/tree-sitter-language-mode-spec.js b/spec/tree-sitter-language-mode-spec.js index c23849d30..90c90654b 100644 --- a/spec/tree-sitter-language-mode-spec.js +++ b/spec/tree-sitter-language-mode-spec.js @@ -313,6 +313,39 @@ describe('TreeSitterLanguageMode', () => { ]) }) + it('applies rules when specified', async () => { + const grammar = new TreeSitterGrammar(atom.grammars, jsGrammarPath, { + parser: 'tree-sitter-javascript', + scopes: { + 'identifier': [ + {match: '^(exports|document|window|global)$', scopes: 'global'}, + {match: '^[A-Z_]+$', scopes: 'constant'}, + {match: '^[A-Z]', scopes: 'constructor'}, + 'variable' + ], + } + }) + + buffer.setText(`exports.object = Class(SOME_CONSTANT, x)`) + + const languageMode = new TreeSitterLanguageMode({buffer, grammar}) + buffer.setLanguageMode(languageMode) + await nextHighlightingUpdate(languageMode) + + expectTokensToEqual(editor, [ + [ + {text: 'exports', scopes: ['global']}, + {text: '.object = ', scopes: []}, + {text: 'Class', scopes: ['constructor']}, + {text: '(', scopes: []}, + {text: 'SOME_CONSTANT', scopes: ['constant']}, + {text: ', ', scopes: []}, + {text: 'x', scopes: ['variable']}, + {text: ')', scopes: []}, + ] + ]) + }) + 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, { diff --git a/src/tree-sitter-grammar.js b/src/tree-sitter-grammar.js index 7f5412020..ee93ded4f 100644 --- a/src/tree-sitter-grammar.js +++ b/src/tree-sitter-grammar.js @@ -2,23 +2,6 @@ const path = require('path') const SyntaxScopeMap = require('./syntax-scope-map') const Module = require('module') -tap = x => (console.log(x), x) - -const toSyntaxClasses = scopes => - typeof scopes == 'string' - ? scopes - .split('.') - .map(s => `syntax--${s}`) - .join(' ') - : - Array.isArray(scopes) - ? scopes.map(toSyntaxClasses) - : - scopes.match - ? tap({match: new RegExp(scopes.match), scopes: toSyntaxClasses(scopes.scopes)}) - : - Object.assign({}, scopes, {scopes: toSyntaxClasses(scopes.scopes)}) - module.exports = class TreeSitterGrammar { constructor (registry, filePath, params) { @@ -86,3 +69,15 @@ class TreeSitterGrammar { if (this.registration) this.registration.dispose() } } + +const toSyntaxClasses = scopes => + typeof scopes === 'string' + ? scopes + .split('.') + .map(s => `syntax--${s}`) + .join(' ') + : Array.isArray(scopes) + ? scopes.map(toSyntaxClasses) + : scopes.match + ? {match: new RegExp(scopes.match), scopes: toSyntaxClasses(scopes.scopes)} + : Object.assign({}, scopes, {scopes: toSyntaxClasses(scopes.scopes)}) diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index de28fc216..62c1631d5 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -919,7 +919,7 @@ class LayerHighlightIterator { const applyLeafRules = (rules, cursor) => { if (!rules || typeof rules === 'string') return rules if (Array.isArray(rules)) { - for (let i = 0, {length} = rules; i != length; ++i) { + for (let i = 0, {length} = rules; i !== length; ++i) { const result = applyLeafRules(rules[i], cursor) if (result) return result } From f693df23147f8c2c3d4eae0cd402cc85171184bf Mon Sep 17 00:00:00 2001 From: Ashi Krishnan Date: Tue, 24 Jul 2018 14:35:02 -0400 Subject: [PATCH 4/6] Remove unnecessary function and equally unnecessary whitespace. --- src/tree-sitter-language-mode.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index 8adea67d2..8166aa59d 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -843,7 +843,7 @@ class LayerHighlightIterator { return this.openTags.slice() } - // Private methods + // Private methods _moveUp (atLastChild) { let result = false const {endIndex} = this.treeCursor @@ -914,11 +914,7 @@ class LayerHighlightIterator { if (scopes) { return this.languageLayer.languageMode.grammar.idForScope(scopes) } - } - - idForScope (scopeName) { - return this.languageLayer.languageMode.grammar.idForScope(scopeName) - } + } } const applyLeafRules = (rules, cursor) => { From f6d3b350c57849f0a7de723a5f3ace85d99deaf2 Mon Sep 17 00:00:00 2001 From: Ashi Krishnan Date: Tue, 24 Jul 2018 14:36:00 -0400 Subject: [PATCH 5/6] Bump tree-sitter version to prerelease. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c951fb3f3..a719e6f89 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "sinon": "1.17.4", "temp": "^0.8.3", "text-buffer": "13.14.5", - "tree-sitter": "0.13.0", + "tree-sitter": "0.13.2-0", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.8", "winreg": "^1.2.1", From aaa52d4036d0be7292889476659b2aadf4fae75e Mon Sep 17 00:00:00 2001 From: Ashi Krishnan Date: Tue, 24 Jul 2018 15:00:54 -0400 Subject: [PATCH 6/6] Trailing ws --- src/tree-sitter-language-mode.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree-sitter-language-mode.js b/src/tree-sitter-language-mode.js index 8166aa59d..640347437 100644 --- a/src/tree-sitter-language-mode.js +++ b/src/tree-sitter-language-mode.js @@ -914,7 +914,7 @@ class LayerHighlightIterator { if (scopes) { return this.languageLayer.languageMode.grammar.idForScope(scopes) } - } + } } const applyLeafRules = (rules, cursor) => {