Start work on custom scope -> filetypes configuration

This commit is contained in:
Max Brunsfeld 2015-07-29 11:27:24 -07:00
parent 5fb456309a
commit 989bb86199
4 changed files with 96 additions and 3 deletions

View File

@ -110,6 +110,11 @@ describe "the `grammars` global", ->
expect(-> atom.grammars.selectGrammar(null, '')).not.toThrow()
expect(-> atom.grammars.selectGrammar(null, null)).not.toThrow()
describe "when the user has custom grammar filetypes", ->
it "considers the custom filetypes as well as those defined in the grammar", ->
atom.config.set('core.fileTypesByScope', 'source.ruby': ['Cheffile'])
expect(atom.grammars.selectGrammar('build/Cheffile', 'cookbook "postgres"').scopeName).toBe 'source.ruby'
describe ".removeGrammar(grammar)", ->
it "removes the grammar, so it won't be returned by selectGrammar", ->
grammar = atom.grammars.selectGrammar('foo.js')

View File

@ -26,6 +26,9 @@ module.exports =
default: []
items:
type: 'string'
fileTypesByScope:
type: 'object'
default: {}
themes:
type: 'array'
default: ['one-dark-ui', 'one-dark-syntax']

View File

@ -1,7 +1,11 @@
_ = require 'underscore-plus'
{Emitter} = require 'event-kit'
{includeDeprecatedAPIs, deprecate} = require 'grim'
FirstMate = require 'first-mate'
Token = require './token'
fs = require 'fs-plus'
PathSplitRegex = new RegExp("[/.]")
# Extended: Syntax class holding the grammars used for tokenizing.
#
@ -39,7 +43,7 @@ class GrammarRegistry extends FirstMate.GrammarRegistry
bestMatch = null
highestScore = -Infinity
for grammar in @grammars
score = grammar.getScore(filePath, fileContents)
score = @getGrammarScore(grammar, filePath, fileContents)
if score > highestScore or not bestMatch?
bestMatch = grammar
highestScore = score
@ -47,6 +51,87 @@ class GrammarRegistry extends FirstMate.GrammarRegistry
bestMatch = grammar unless grammar.bundledPackage
bestMatch
# Extended: Returns a {Number} representing how well the grammar matches the
# `filePath` and `contents`.
getGrammarScore: (grammar, filePath, contents) ->
contents = fs.readFileSync(filePath, 'utf8') if not contents? and fs.isFileSync(filePath)
if @grammarOverrideForPath(filePath) is grammar.scopeName
2 + (filePath?.length ? 0)
else if @grammarMatchesContents(grammar, contents)
1 + (filePath?.length ? 0)
else
@getGrammarPathScore(grammar, filePath)
getGrammarPathScore: (grammar, filePath) ->
return -1 unless filePath
filePath = filePath.replace(/\\/g, '/') if process.platform is 'win32'
pathComponents = filePath.toLowerCase().split(PathSplitRegex)
pathScore = -1
fileTypes = grammar.fileTypes
if customFileTypes = atom.config.get('core.fileTypesByScope')?[grammar.scopeName]
fileTypes = fileTypes.concat(customFileTypes)
for fileType in fileTypes
fileTypeComponents = fileType.toLowerCase().split(PathSplitRegex)
pathSuffix = pathComponents[-fileTypeComponents.length..-1]
if _.isEqual(pathSuffix, fileTypeComponents)
pathScore = Math.max(pathScore, fileType.length)
pathScore
grammarMatchesContents: (grammar, contents) ->
return false unless contents? and grammar.firstLineRegex?
escaped = false
numberOfNewlinesInRegex = 0
for character in grammar.firstLineRegex.source
switch character
when '\\'
escaped = !escaped
when 'n'
numberOfNewlinesInRegex++ if escaped
escaped = false
else
escaped = false
lines = contents.split('\n')
grammar.firstLineRegex.testSync(lines[0..numberOfNewlinesInRegex].join('\n'))
# Public: Get the grammar override for the given file path.
#
# * `filePath` A {String} file path.
#
# Returns a {Grammar} or undefined.
grammarOverrideForPath: (filePath) ->
@grammarOverridesByPath[filePath]
# Public: Set the grammar override for the given file path.
#
# * `filePath` A non-empty {String} file path.
# * `scopeName` A {String} such as `"source.js"`.
#
# Returns a {Grammar} or undefined.
setGrammarOverrideForPath: (filePath, scopeName) ->
if filePath
@grammarOverridesByPath[filePath] = scopeName
# Public: Remove the grammar override for the given file path.
#
# * `filePath` A {String} file path.
#
# Returns undefined.
clearGrammarOverrideForPath: (filePath) ->
delete @grammarOverridesByPath[filePath]
undefined
# Public: Remove all grammar overrides.
#
# Returns undefined.
clearGrammarOverrides: ->
@grammarOverridesByPath = {}
undefined
clearObservers: ->
@off() if includeDeprecatedAPIs
@emitter = new Emitter

View File

@ -68,7 +68,7 @@ class TokenizedBuffer extends Model
if grammar.injectionSelector?
@retokenizeLines() if @hasTokenForSelector(grammar.injectionSelector)
else
newScore = grammar.getScore(@buffer.getPath(), @getGrammarSelectionContent())
newScore = atom.grammars.getGrammarScore(grammar, @buffer.getPath(), @getGrammarSelectionContent())
@setGrammar(grammar, newScore) if newScore > @currentGrammarScore
setGrammar: (grammar, score) ->
@ -76,7 +76,7 @@ class TokenizedBuffer extends Model
@grammar = grammar
@rootScopeDescriptor = new ScopeDescriptor(scopes: [@grammar.scopeName])
@currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @getGrammarSelectionContent())
@currentGrammarScore = score ? atom.grammars.getGrammarScore(grammar, @buffer.getPath(), @getGrammarSelectionContent())
@grammarUpdateDisposable?.dispose()
@grammarUpdateDisposable = @grammar.onDidUpdate => @retokenizeLines()