Merge pull request #17738 from atom/regex-matchers-for-style-map

Allow scope mappings to be refined via match rules
This commit is contained in:
Ashi Krishnan
2018-07-30 14:57:01 -04:00
committed by GitHub
4 changed files with 74 additions and 9 deletions

View File

@@ -159,7 +159,7 @@
"temp": "^0.8.3",
"text-buffer": "13.14.5",
"timecop": "https://www.atom.io/api/packages/timecop/versions/0.36.2/tarball",
"tree-sitter": "0.13.0",
"tree-sitter": "0.13.2",
"tree-view": "https://www.atom.io/api/packages/tree-view/versions/0.222.0/tarball",
"typescript-simple": "1.0.0",
"underscore-plus": "^1.6.8",

View File

@@ -314,6 +314,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: []},
]
])
})
it('handles nodes that start before their first child and end after their last child', async () => {
const grammar = new TreeSitterGrammar(atom.grammars, rubyGrammarPath, {
parser: 'tree-sitter-ruby',

View File

@@ -22,10 +22,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)
@@ -74,6 +71,18 @@ class TreeSitterGrammar {
}
}
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)})
const NODE_NAME_REGEX = /[\w_]+/
function matcherForSpec (spec) {

View File

@@ -870,7 +870,6 @@ class LayerHighlightIterator {
}
// Private methods
_moveUp (atLastChild) {
let result = false
const {endIndex} = this.treeCursor
@@ -928,13 +927,37 @@ class LayerHighlightIterator {
}
_currentScopeId () {
const name = this.languageLayer.grammar.scopeMap.get(
const rules = this.languageLayer.grammar.scopeMap.get(
this.containingNodeTypes,
this.containingNodeChildIndices,
this.treeCursor.nodeIsNamed
)
if (name) {
return this.languageLayer.languageMode.grammar.idForScope(name)
const scopes = applyLeafRules(rules, this.treeCursor)
if (scopes) {
return this.languageLayer.languageMode.grammar.idForScope(scopes)
}
}
}
const applyLeafRules = (rules, cursor) => {
if (!rules || typeof rules === 'string') return rules
if (Array.isArray(rules)) {
for (let i = 0, {length} = rules; i !== length; ++i) {
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
}
}
}