mirror of
https://github.com/atom/atom.git
synced 2026-01-25 14:59:03 -05:00
Add tests and docs for addInjectionPoint
Also, replace `addInjectionPattern` API with a single `injectionRegExp` field on the grammar. Co-Authored-By: Ashi Krishnan <queerviolet@github.com>
This commit is contained in:
@@ -525,6 +525,34 @@ describe('GrammarRegistry', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('.addInjectionPoint(languageId, {type, language, content})', () => {
|
||||
const injectionPoint = {
|
||||
type: 'some_node_type',
|
||||
language() { return 'some_language_name' },
|
||||
content(node) { return node }
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
atom.config.set('core.useTreeSitterParsers', true)
|
||||
})
|
||||
|
||||
it('adds an injection point to the grammar with the given id', async () => {
|
||||
await atom.packages.activatePackage('language-javascript')
|
||||
atom.grammars.addInjectionPoint('javascript', injectionPoint)
|
||||
const grammar = atom.grammars.grammarForId('javascript')
|
||||
expect(grammar.injectionPoints).toContain(injectionPoint)
|
||||
})
|
||||
|
||||
describe('when called before a grammar with the given id is loaded', () => {
|
||||
it('adds the injection point once the grammar is loaded', async () => {
|
||||
atom.grammars.addInjectionPoint('javascript', injectionPoint)
|
||||
await atom.packages.activatePackage('language-javascript')
|
||||
const grammar = atom.grammars.grammarForId('javascript')
|
||||
expect(grammar.injectionPoints).toContain(injectionPoint)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('serialization', () => {
|
||||
it('persists editors\' grammar overrides', async () => {
|
||||
const buffer1 = new TextBuffer()
|
||||
|
||||
@@ -324,9 +324,7 @@ describe('TreeSitterLanguageMode', () => {
|
||||
tag_name: 'tag',
|
||||
attribute_name: 'attr'
|
||||
},
|
||||
injections: [
|
||||
name => name.toLowerCase().includes('html')
|
||||
]
|
||||
injectionRegExp: 'html'
|
||||
})
|
||||
|
||||
atom.grammars.addGrammar(jsGrammar)
|
||||
|
||||
@@ -391,6 +391,32 @@ class GrammarRegistry {
|
||||
return this.textmateRegistry.onDidUpdateGrammar(callback)
|
||||
}
|
||||
|
||||
// Experimental: Specify a type of syntax node that may embed other languages.
|
||||
//
|
||||
// * `grammarId` The {String} id of the parent language
|
||||
// * `injectionPoint` An {Object} with the following keys:
|
||||
// * `type` The {String} type of syntax node that may embed other languages
|
||||
// * `language` A {Function} that is called with syntax nodes of the specified `type` and
|
||||
// returns a {String} that will be tested against other grammars' `injectionRegExp` in
|
||||
// order to determine what language should be embedded.
|
||||
// * `content` A {Function} that is called with syntax nodes of the specified `type` and
|
||||
// returns another syntax node that contains the embedded source code.
|
||||
addInjectionPoint (grammarId, injectionPoint) {
|
||||
const grammar = this.treeSitterGrammarsById[grammarId]
|
||||
if (grammar) {
|
||||
grammar.injectionPoints.push(injectionPoint)
|
||||
} else {
|
||||
this.treeSitterGrammarsById[grammarId] = {
|
||||
injectionPoints: [injectionPoint]
|
||||
}
|
||||
}
|
||||
return new Disposable(() => {
|
||||
const grammar = this.treeSitterGrammarsById[grammarId]
|
||||
const index = grammar.injectionPoints.indexOf(injectionPoint)
|
||||
if (index !== -1) grammar.injectionPoints.splice(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
get nullGrammar () {
|
||||
return this.textmateRegistry.nullGrammar
|
||||
}
|
||||
@@ -409,12 +435,14 @@ class GrammarRegistry {
|
||||
|
||||
addGrammar (grammar) {
|
||||
if (grammar instanceof TreeSitterGrammar) {
|
||||
const existingParams = this.treeSitterGrammarsById[grammar.id] || {}
|
||||
this.treeSitterGrammarsById[grammar.id] = grammar
|
||||
if (grammar.legacyScopeName) {
|
||||
this.config.setLegacyScopeAliasForNewScope(grammar.id, grammar.legacyScopeName)
|
||||
this.textMateScopeNamesByTreeSitterLanguageId.set(grammar.id, grammar.legacyScopeName)
|
||||
this.treeSitterLanguageIdsByTextMateScopeName.set(grammar.legacyScopeName, grammar.id)
|
||||
}
|
||||
if (existingParams.injectionPoints) grammar.injectionPoints.push(...existingParams.injectionPoints)
|
||||
this.grammarAddedOrUpdated(grammar)
|
||||
return new Disposable(() => this.removeGrammar(grammar))
|
||||
} else {
|
||||
@@ -517,13 +545,9 @@ class GrammarRegistry {
|
||||
|
||||
treeSitterGrammarForLanguageString (languageString) {
|
||||
for (const id in this.treeSitterGrammarsById) {
|
||||
const grammar = this.treeSitterGrammarsById[id];
|
||||
if (grammar.injections) {
|
||||
for (const injection of grammar.injections) {
|
||||
if (injection(languageString)) {
|
||||
return grammar
|
||||
}
|
||||
}
|
||||
const grammar = this.treeSitterGrammarsById[id]
|
||||
if (grammar.injectionRegExp && grammar.injectionRegExp.test(languageString)) {
|
||||
return grammar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ class TreeSitterGrammar {
|
||||
this.name = params.name
|
||||
this.legacyScopeName = params.legacyScopeName
|
||||
if (params.contentRegExp) this.contentRegExp = new RegExp(params.contentRegExp)
|
||||
if (params.injectionRegExp) this.injectionRegExp = new RegExp(params.injectionRegExp)
|
||||
|
||||
this.folds = params.folds || []
|
||||
|
||||
@@ -29,7 +30,6 @@ class TreeSitterGrammar {
|
||||
this.scopeMap = new SyntaxScopeMap(scopeSelectors)
|
||||
this.fileTypes = params.fileTypes
|
||||
this.injectionPoints = params.injectionPoints || []
|
||||
this.injections = params.injections || []
|
||||
|
||||
// TODO - When we upgrade to a new enough version of node, use `require.resolve`
|
||||
// with the new `paths` option instead of this private API.
|
||||
|
||||
@@ -30,7 +30,7 @@ class TreeSitterLanguageMode {
|
||||
this.emitRangeUpdate = this.emitRangeUpdate.bind(this)
|
||||
|
||||
this.subscription = this.buffer.onDidChangeText(({changes}) => {
|
||||
for (let i = changes.length; i --> 0;) {
|
||||
for (let i = changes.length - 1; i >= 0; i--) {
|
||||
const {oldRange, newRange} = changes[i]
|
||||
const startRow = oldRange.start.row
|
||||
const oldEndRow = oldRange.end.row
|
||||
@@ -421,7 +421,7 @@ class LanguageLayer {
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
destroy () {
|
||||
for (const marker of this.languageMode.injectionsMarkerLayer.getMarkers()) {
|
||||
if (marker.parentLanguageLayer === this) {
|
||||
marker.languageLayer.destroy()
|
||||
@@ -439,7 +439,7 @@ class LanguageLayer {
|
||||
|
||||
if (this.patchSinceCurrentParseStarted) {
|
||||
const changes = this.patchSinceCurrentParseStarted.getChanges()
|
||||
for (let i = changes.length; i --> 0;) {
|
||||
for (let i = changes.length - 1; i >= 0; i--) {
|
||||
const {oldStart, oldEnd, newEnd, oldText, newText} = changes[i]
|
||||
this.tree.edit(this._treeEditForBufferChange(
|
||||
oldStart, oldEnd, newEnd, oldText, newText
|
||||
@@ -474,8 +474,8 @@ class LanguageLayer {
|
||||
let affectedRange
|
||||
let existingInjectionMarkers
|
||||
if (this.tree) {
|
||||
const changedTokenRange = this.tree.getEditedRange()
|
||||
affectedRange = new Range(changedTokenRange.startPosition, changedTokenRange.endPosition)
|
||||
const editedRange = this.tree.getEditedRange()
|
||||
affectedRange = new Range(editedRange.startPosition, editedRange.endPosition)
|
||||
|
||||
const rangesWithSyntaxChanges = this.tree.getChangedRanges(tree)
|
||||
for (const range of rangesWithSyntaxChanges) {
|
||||
@@ -506,7 +506,7 @@ class LanguageLayer {
|
||||
injectionPoint.type,
|
||||
affectedRange.start,
|
||||
affectedRange.end
|
||||
);
|
||||
)
|
||||
|
||||
for (const node of nodes) {
|
||||
const languageName = injectionPoint.language(node, getNodeText)
|
||||
|
||||
Reference in New Issue
Block a user