mirror of
https://github.com/atom/atom.git
synced 2026-02-14 08:35:11 -05:00
This is required for the property-list bundle to highlight .plist files that maybe in XML or non-XML formats. Also specify the cached buffer disk contents to grammarForFilePath so fs.read doesn't need to be called again if the contents are already read.
107 lines
3.2 KiB
CoffeeScript
107 lines
3.2 KiB
CoffeeScript
_ = require 'underscore'
|
|
jQuery = require 'jquery'
|
|
Specificity = require 'specificity'
|
|
{$$} = require 'space-pen'
|
|
fs = require 'fs'
|
|
|
|
module.exports =
|
|
class Syntax
|
|
constructor: ->
|
|
@grammars = []
|
|
@grammarsByFileType = {}
|
|
@grammarsByScopeName = {}
|
|
@globalProperties = {}
|
|
@scopedPropertiesIndex = 0
|
|
@scopedProperties = []
|
|
|
|
addGrammar: (grammar) ->
|
|
@grammars.push(grammar)
|
|
for fileType in grammar.fileTypes
|
|
@grammarsByFileType[fileType] = grammar
|
|
@grammarsByScopeName[grammar.scopeName] = grammar
|
|
|
|
grammarForFilePath: (filePath, fileContents) ->
|
|
return @grammarsByFileType["txt"] unless filePath
|
|
|
|
extension = fs.extension(filePath)?[1..]
|
|
if filePath and extension.length == 0
|
|
extension = fs.base(filePath)
|
|
|
|
@grammarByFirstLineRegex(filePath, fileContents) or
|
|
@grammarsByFileType[extension] or
|
|
@grammarByFileTypeSuffix(filePath) or
|
|
@grammarsByFileType["txt"]
|
|
|
|
grammarByFileTypeSuffix: (filePath) ->
|
|
for fileType, grammar of @grammarsByFileType
|
|
return grammar if _.endsWith(filePath, fileType)
|
|
|
|
grammarByFirstLineRegex: (filePath, fileContents) ->
|
|
try
|
|
fileContents = fs.read(filePath) unless fileContents?
|
|
catch e
|
|
null
|
|
|
|
_.find @grammars, (grammar) -> grammar.firstLineRegex?.test(fileContents)
|
|
|
|
grammarForScopeName: (scopeName) ->
|
|
@grammarsByScopeName[scopeName]
|
|
|
|
addProperties: (args...) ->
|
|
selector = args.shift() if args.length > 1
|
|
properties = args.shift()
|
|
|
|
if selector
|
|
@scopedProperties.unshift(
|
|
selector: selector,
|
|
properties: properties,
|
|
specificity: Specificity(selector),
|
|
index: @scopedPropertiesIndex++
|
|
)
|
|
else
|
|
_.extend(@globalProperties, properties)
|
|
|
|
getProperty: (scope, keyPath) ->
|
|
for object in @propertiesForScope(scope, keyPath)
|
|
value = _.valueForKeyPath(object, keyPath)
|
|
return value if value?
|
|
undefined
|
|
|
|
propertiesForScope: (scope, keyPath) ->
|
|
matchingProperties = []
|
|
candidates = @scopedProperties.filter ({properties}) -> _.valueForKeyPath(properties, keyPath)?
|
|
if candidates.length
|
|
element = @buildScopeElement(scope)
|
|
while element
|
|
matchingProperties.push(@matchingPropertiesForElement(element, candidates)...)
|
|
element = element.parentNode
|
|
matchingProperties.concat([@globalProperties])
|
|
|
|
matchingPropertiesForElement: (element, candidates) ->
|
|
matchingScopedProperties = candidates.filter ({selector}) ->
|
|
jQuery.find.matchesSelector(element, selector)
|
|
matchingScopedProperties.sort (a, b) ->
|
|
if a.specificity == b.specificity
|
|
b.index - a.index
|
|
else
|
|
b.specificity - a.specificity
|
|
_.pluck matchingScopedProperties, 'properties'
|
|
|
|
buildScopeElement: (scope) ->
|
|
scope = new Array(scope...)
|
|
element = $$ ->
|
|
elementsForRemainingScopes = =>
|
|
classString = scope.shift()
|
|
classes = classString.replace(/^\./, '').replace(/\./g, ' ')
|
|
if scope.length
|
|
@div class: classes, elementsForRemainingScopes
|
|
else
|
|
@div class: classes
|
|
elementsForRemainingScopes()
|
|
|
|
deepestChild = element.find(":not(:has(*))")
|
|
if deepestChild.length
|
|
deepestChild[0]
|
|
else
|
|
element[0]
|