Change syntax.selectGrammar to choose the highest-scoring grammar

This sets us up to switch to a grammar when it is loaded if it is a
better match for the current file.
This commit is contained in:
Corey Johnson & Nathan Sobo
2013-04-03 17:49:14 -06:00
parent ed1c5d3417
commit 05d6adc6c7
4 changed files with 51 additions and 49 deletions

View File

@@ -5,5 +5,7 @@ class NullGrammar
name: 'Null Grammar'
scopeName: 'text.plain.null-grammar'
getScore: -> 0
tokenizeLine: (line) ->
{ tokens: [new Token(value: line, scopes: ['null-grammar.text.plain'])] }

View File

@@ -5,8 +5,6 @@ Specificity = require 'specificity'
fsUtils = require 'fs-utils'
EventEmitter = require 'event-emitter'
NullGrammar = require 'null-grammar'
nodePath = require 'path'
pathSplitRegex = new RegExp("[#{nodePath.sep}.]")
module.exports =
class Syntax
@@ -18,13 +16,13 @@ class Syntax
syntax
constructor: ->
@grammars = []
@nullGrammar = new NullGrammar
@grammars = [@nullGrammar]
@grammarsByFileType = {}
@grammarsByScopeName = {}
@grammarOverridesByPath = {}
@scopedPropertiesIndex = 0
@scopedProperties = []
@nullGrammar = new NullGrammar
serialize: ->
{ deserializer: @constructor.name, @grammarOverridesByPath }
@@ -50,51 +48,10 @@ class Syntax
@grammarOverridesByPath = {}
selectGrammar: (filePath, fileContents) ->
return @grammarsByFileType["txt"] ? @nullGrammar unless filePath
@grammarOverrideForPath(filePath) ?
@grammarByFirstLineRegex(filePath, fileContents) ?
@grammarByPath(filePath) ?
@grammarsByFileType["txt"] ?
@nullGrammar
_.max @grammars, (grammar) -> grammar.getScore(filePath, fileContents)
grammarOverrideForPath: (path) ->
@grammarsByScopeName[@grammarOverridesByPath[path]]
grammarByPath: (path) ->
pathComponents = path.split(pathSplitRegex)
for fileType, grammar of @grammarsByFileType
fileTypeComponents = fileType.split(pathSplitRegex)
pathSuffix = pathComponents[-fileTypeComponents.length..-1]
return grammar if _.isEqual(pathSuffix, fileTypeComponents)
grammarByFirstLineRegex: (filePath, fileContents) ->
try
fileContents ?= fsUtils.read(filePath)
catch e
return
return unless fileContents
lines = fileContents.split('\n')
_.find @grammars, (grammar) ->
regex = grammar.firstLineRegex
return unless regex?
escaped = false
numberOfNewlinesInRegex = 0
for character in regex.source
switch character
when '\\'
escaped = !escaped
when 'n'
numberOfNewlinesInRegex++ if escaped
escaped = false
else
escaped = false
regex.test(lines[0..numberOfNewlinesInRegex].join('\n'))
@grammarOverridesByPath[path]
grammarForScopeName: (scopeName) ->
@grammarsByScopeName[scopeName]

View File

@@ -2,8 +2,9 @@ _ = require 'underscore'
fsUtils = require 'fs-utils'
plist = require 'plist'
Token = require 'token'
CSON = require 'cson'
{OnigRegExp, OnigScanner} = require 'oniguruma'
nodePath = require 'path'
pathSplitRegex = new RegExp("[#{nodePath.sep}.]")
module.exports =
class TextMateGrammar
@@ -38,6 +39,48 @@ class TextMateGrammar
data = {patterns: [data], tempName: name} if data.begin? or data.match?
@repository[name] = new Rule(this, data)
getScore: (path, contents) ->
contents = fsUtils.read(path) if not contents? and fsUtils.isFile(path)
if syntax.grammarOverrideForPath(path) is @scopeName
Infinity
else if @matchesContents(contents)
3
else if @matchesPath(path)
2
else if @isTextGrammar()
1
else
-1
matchesContents: (contents) ->
return false unless contents? and @firstLineRegex?
escaped = false
numberOfNewlinesInRegex = 0
for character in @firstLineRegex.source
switch character
when '\\'
escaped = !escaped
when 'n'
numberOfNewlinesInRegex++ if escaped
escaped = false
else
escaped = false
lines = contents.split('\n')
@firstLineRegex.test(lines[0..numberOfNewlinesInRegex].join('\n'))
matchesPath: (path) ->
return false unless path?
pathComponents = path.split(pathSplitRegex)
_.find @fileTypes, (fileType) ->
fileTypeComponents = fileType.split(pathSplitRegex)
pathSuffix = pathComponents[-fileTypeComponents.length..-1]
_.isEqual(pathSuffix, fileTypeComponents)
isTextGrammar: ->
@scopeName is 'text.plain'
tokenizeLine: (line, ruleStack=[@initialRule], firstLine=false) ->
originalRuleStack = ruleStack
ruleStack = new Array(ruleStack...) # clone ruleStack