Iterate on GrammarRegistry APIs

Signed-off-by: Nathan Sobo <nathan@github.com>
This commit is contained in:
Max Brunsfeld
2017-11-06 09:36:06 -08:00
committed by Nathan Sobo
parent e6c3891e62
commit 351f96d5dd
6 changed files with 210 additions and 100 deletions

View File

@@ -1,4 +1,5 @@
const _ = require('underscore-plus')
const Grim = require('grim')
const FirstMate = require('first-mate')
const {Disposable, CompositeDisposable} = require('event-kit')
const TokenizedBuffer = require('./tokenized-buffer')
@@ -7,7 +8,7 @@ const fs = require('fs-plus')
const {Point, Range} = require('text-buffer')
const GRAMMAR_SELECTION_RANGE = Range(Point.ZERO, Point(10, 0)).freeze()
const PathSplitRegex = new RegExp('[/.]')
const PATH_SPLIT_REGEX = new RegExp('[/.]')
// Extended: Syntax class holding the grammars used for tokenizing.
//
@@ -20,7 +21,7 @@ class GrammarRegistry extends FirstMate.GrammarRegistry {
constructor ({config} = {}) {
super({maxTokensPerLine: 100, maxLineLength: 1000})
this.config = config
this.grammarOverridesByPath = {}
this.languageNameOverridesByBufferId = new Map()
this.grammarScoresByBuffer = new Map()
this.subscriptions = new CompositeDisposable()
@@ -33,40 +34,60 @@ class GrammarRegistry extends FirstMate.GrammarRegistry {
return new Token({value, scopes})
}
maintainGrammar (buffer) {
this.assignLanguageModeToBuffer(buffer)
maintainLanguageMode (buffer) {
const languageNameOverride = this.languageNameOverridesByBufferId.get(buffer.id)
if (languageNameOverride) {
this.assignLanguageMode(buffer, languageNameOverride)
} else {
this.assignLanguageMode(buffer, null)
}
const pathChangeSubscription = buffer.onDidChangePath(() => {
this.grammarScoresByBuffer.delete(buffer)
this.assignLanguageModeToBuffer(buffer)
if (!this.languageNameOverridesByBufferId.has(buffer.id)) {
this.assignLanguageMode(buffer, null)
}
})
this.subscriptions.add(pathChangeSubscription)
return new Disposable(() => {
this.subscriptions.remove(pathChangeSubscription)
this.grammarScoresByBuffer.delete(buffer)
pathChangeSubscription.dispose()
})
}
assignLanguageModeToBuffer (buffer) {
let grammar = null
const overrideScopeName = this.grammarOverridesByPath[buffer.getPath()]
if (overrideScopeName) {
grammar = this.grammarForScopeName(overrideScopeName)
this.grammarScoresByBuffer.set(buffer, null)
}
assignLanguageMode (buffer, languageName) {
if (buffer.getBuffer) buffer = buffer.getBuffer()
if (!grammar) {
let grammar
if (languageName != null) {
const lowercaseLanguageName = languageName.toLowerCase()
grammar = this.grammarForLanguageName(lowercaseLanguageName)
this.languageNameOverridesByBufferId.set(buffer.id, lowercaseLanguageName)
this.grammarScoresByBuffer.set(buffer, null)
} else {
const result = this.selectGrammarWithScore(
buffer.getPath(),
buffer.getTextInRange(GRAMMAR_SELECTION_RANGE)
)
grammar = result.grammar
this.grammarScoresByBuffer.set(buffer, result.score)
const currentScore = this.grammarScoresByBuffer.get(buffer)
if (currentScore == null || result.score > currentScore) {
grammar = result.grammar
this.languageNameOverridesByBufferId.delete(buffer.id)
this.grammarScoresByBuffer.set(buffer, result.score)
}
}
buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer))
if (grammar) {
if (grammar.name !== buffer.getLanguageMode().getLanguageName()) {
buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer))
}
return true
} else {
return false
}
}
languageModeForGrammarAndBuffer (grammar, buffer) {
@@ -120,7 +141,7 @@ class GrammarRegistry extends FirstMate.GrammarRegistry {
if (!filePath) { return -1 }
if (process.platform === 'win32') { filePath = filePath.replace(/\\/g, '/') }
const pathComponents = filePath.toLowerCase().split(PathSplitRegex)
const pathComponents = filePath.toLowerCase().split(PATH_SPLIT_REGEX)
let pathScore = -1
let customFileTypes
@@ -135,7 +156,7 @@ class GrammarRegistry extends FirstMate.GrammarRegistry {
for (let i = 0; i < fileTypes.length; i++) {
const fileType = fileTypes[i]
const fileTypeComponents = fileType.toLowerCase().split(PathSplitRegex)
const fileTypeComponents = fileType.toLowerCase().split(PATH_SPLIT_REGEX)
const pathSuffix = pathComponents.slice(-fileTypeComponents.length)
if (_.isEqual(pathSuffix, fileTypeComponents)) {
pathScore = Math.max(pathScore, fileType.length)
@@ -170,23 +191,30 @@ class GrammarRegistry extends FirstMate.GrammarRegistry {
return grammar.firstLineRegex.testSync(lines.slice(0, numberOfNewlinesInRegex + 1).join('\n'))
}
// Get the grammar override for the given file path.
// Deprecated: Get the grammar override for the given file path.
//
// * `filePath` A {String} file path.
//
// Returns a {String} such as `"source.js"`.
grammarOverrideForPath (filePath) {
return this.grammarOverridesByPath[filePath]
Grim.deprecate('Use buffer.getLanguageMode().getLanguageName() instead')
const buffer = atom.project.findBufferForPath(filePath)
if (buffer) return this.languageNameOverridesByBufferId.get(buffer.id)
}
// Set the grammar override for the given file path.
// Deprecated: Set the grammar override for the given file path.
//
// * `filePath` A non-empty {String} file path.
// * `scopeName` A {String} such as `"source.js"`.
//
// Returns undefined.
setGrammarOverrideForPath (filePath, scopeName) {
this.grammarOverridesByPath[filePath] = scopeName
Grim.deprecate('Use atom.grammars.assignLanguageMode(buffer, languageName) instead')
const buffer = atom.project.findBufferForPath(filePath)
if (buffer) {
const grammar = this.grammarForScopeName(scopeName)
if (grammar) this.languageNameOverridesByBufferId.set(buffer.id, grammar.name)
}
}
// Remove the grammar override for the given file path.
@@ -195,7 +223,14 @@ class GrammarRegistry extends FirstMate.GrammarRegistry {
//
// Returns undefined.
clearGrammarOverrideForPath (filePath) {
delete this.grammarOverridesByPath[filePath]
Grim.deprecate('Use atom.grammars.assignLanguageMode(buffer, null) instead')
const buffer = atom.project.findBufferForPath(filePath)
if (buffer) this.languageNameOverridesByBufferId.delete(buffer.id)
}
grammarForLanguageName (languageName) {
const lowercaseLanguageName = languageName.toLowerCase()
return this.getGrammars().find(grammar => grammar.name.toLowerCase() === lowercaseLanguageName)
}
grammarAddedOrUpdated (grammar) {
@@ -208,19 +243,16 @@ class GrammarRegistry extends FirstMate.GrammarRegistry {
return
}
const grammarOverride = this.grammarOverridesByPath[buffer.getPath()]
if (grammarOverride) {
if (grammar.scopeName === grammarOverride) {
buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer))
}
} else {
if (grammar.name === buffer.getLanguageMode().getLanguageName()) {
buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer))
} else if (!this.languageNameOverridesByBufferId.has(buffer.id)) {
const score = this.getGrammarScore(
grammar,
buffer.getPath(),
buffer.getTextInRange(GRAMMAR_SELECTION_RANGE)
)
let currentScore = this.grammarScoresByBuffer.get(buffer)
const currentScore = this.grammarScoresByBuffer.get(buffer)
if (currentScore == null || score > currentScore) {
buffer.setLanguageMode(this.languageModeForGrammarAndBuffer(grammar, buffer))
this.grammarScoresByBuffer.set(buffer, score)

View File

@@ -106,7 +106,7 @@ class Project extends Model {
return Promise.all(bufferPromises).then(buffers => {
this.buffers = buffers.filter(Boolean)
for (let buffer of this.buffers) {
this.grammarRegistry.maintainGrammar(buffer)
this.grammarRegistry.maintainLanguageMode(buffer)
this.subscribeToBuffer(buffer)
}
this.setPaths(state.paths || [], {mustExist: true, exact: true})
@@ -650,7 +650,7 @@ class Project extends Model {
addBuffer (buffer, options = {}) {
this.buffers.push(buffer)
this.grammarRegistry.maintainGrammar(buffer)
this.grammarRegistry.maintainLanguageMode(buffer)
this.subscribeToBuffer(buffer)
this.emitter.emit('did-add-buffer', buffer)
return buffer

View File

@@ -3556,8 +3556,8 @@ class TextEditor {
//
// * `grammar` {Grammar}
setGrammar (grammar) {
Grim.deprecate('Use atom.grammars.setGrammarOverrideForPath(filePath) instead')
atom.grammars.setGrammarOverrideForPath(this.getPath(), grammar.scopeName)
Grim.deprecate('Use atom.grammars.assignLanguageMode(buffer, languageName) instead')
atom.grammars.assignLanguageMode(this.getBuffer(), grammar.name)
}
// Experimental: Get a notification when async tokenization is completed.

View File

@@ -55,6 +55,10 @@ class TokenizedBuffer {
return this.grammar
}
getLanguageName () {
return this.grammar.name
}
getNonWordCharacters (scope) {
return this.config.get('editor.nonWordCharacters', {scope})
}