mirror of
https://github.com/atom/atom.git
synced 2026-02-07 21:25:05 -05:00
Previously, we were rendering every prefix of the dot-separated scope as its own class. So the scope meta.delimiter.method.period.coffee would make a token w/ classes: class="meta, meta-delimiter, meta-delimiter-method, meta-delimiter-method-period…" Now we just give the token each piece of the scope as a class: class="meta delimiter method period coffee" We lose a bit of meaning, in that a scope selector method.period.coffee would match this element in CSS even though it *wouldn't* in TextMate. But we also gain the behavior where longer prefixes are more specific by naturally producing more specific css selectors. So '.meta.delimiter.method' is always more specific than '.meta.delimiter', whereas '.meta-delimiter-method' ties with '.meta-delimiter'. If prefix ambiguities become a problem later we may need to revisit this approach, but I think it's good enough for now.
111 lines
3.2 KiB
CoffeeScript
111 lines
3.2 KiB
CoffeeScript
_ = require 'underscore'
|
|
fs = require 'fs'
|
|
plist = require 'plist'
|
|
|
|
module.exports =
|
|
class TextMateTheme
|
|
@themesByName: {}
|
|
|
|
@loadAll: ->
|
|
for themePath in fs.list(require.resolve("themes"))
|
|
@registerTheme(TextMateTheme.load(themePath))
|
|
|
|
@load: (path) ->
|
|
plistString = fs.read(require.resolve(path))
|
|
theme = null
|
|
plist.parseString plistString, (err, data) ->
|
|
throw new Error("Error loading theme at '#{path}': #{err}") if err
|
|
theme = new TextMateTheme(data[0])
|
|
theme
|
|
|
|
@registerTheme: (theme) ->
|
|
@themesByName[theme.name] = theme
|
|
|
|
@getNames: ->
|
|
_.keys(@themesByName)
|
|
|
|
@getTheme: (name) ->
|
|
@themesByName[name]
|
|
|
|
@activate: (name) ->
|
|
if theme = @getTheme(name)
|
|
theme.activate()
|
|
else
|
|
throw new Error("No theme with name '#{name}'")
|
|
|
|
constructor: ({@name, settings}) ->
|
|
@rulesets = []
|
|
globalSettings = settings[0]
|
|
@buildGlobalSettingsRulesets(settings[0])
|
|
@buildScopeSelectorRulesets(settings[1..])
|
|
|
|
activate: ->
|
|
applyStylesheet(@name, @getStylesheet())
|
|
|
|
getStylesheet: ->
|
|
lines = []
|
|
for {selector, properties} in @getRulesets()
|
|
lines.push("#{selector} {")
|
|
for name, value of properties
|
|
lines.push " #{name}: #{value};"
|
|
lines.push("}\n")
|
|
lines.join("\n")
|
|
|
|
getRulesets: -> @rulesets
|
|
|
|
buildGlobalSettingsRulesets: ({settings}) ->
|
|
{ background, foreground, caret, selection } = settings
|
|
|
|
@rulesets.push
|
|
selector: '.editor'
|
|
properties:
|
|
'background-color': @translateColor(background)
|
|
'color': @translateColor(foreground)
|
|
|
|
@rulesets.push
|
|
selector: '.editor.focused .cursor'
|
|
properties:
|
|
'border-color': @translateColor(caret)
|
|
|
|
@rulesets.push
|
|
selector: '.editor.focused .selection'
|
|
properties:
|
|
'background-color': @translateColor(selection)
|
|
|
|
buildScopeSelectorRulesets: (scopeSelectorSettings) ->
|
|
for { name, scope, settings } in scopeSelectorSettings
|
|
continue unless scope
|
|
@rulesets.push
|
|
comment: name
|
|
selector: @translateScopeSelector(scope)
|
|
properties: @translateScopeSelectorSettings(settings)
|
|
|
|
translateScopeSelector: (textmateScopeSelector) ->
|
|
scopes = textmateScopeSelector.split(/\s+/).map (scope) -> '.' + scope
|
|
scopes.join(' ')
|
|
|
|
translateScopeSelectorSettings: ({ foreground, background, fontStyle }) ->
|
|
properties = {}
|
|
|
|
if fontStyle
|
|
fontStyles = fontStyle.split(/\s+/)
|
|
# properties['font-weight'] = 'bold' if _.contains(fontStyles, 'bold')
|
|
# properties['font-style'] = 'italic' if _.contains(fontStyles, 'italic')
|
|
properties['text-decoration'] = 'underline' if _.contains(fontStyles, 'underline')
|
|
|
|
properties['color'] = @translateColor(foreground) if foreground
|
|
properties['background-color'] = @translateColor(background) if background
|
|
properties
|
|
|
|
translateColor: (textmateColor) ->
|
|
if textmateColor.length <= 7
|
|
textmateColor
|
|
else
|
|
r = parseInt(textmateColor[1..2], 16)
|
|
g = parseInt(textmateColor[3..4], 16)
|
|
b = parseInt(textmateColor[5..6], 16)
|
|
a = parseInt(textmateColor[7..8], 16)
|
|
a = Math.round((a / 255.0) * 100) / 100
|
|
|
|
"rgba(#{r}, #{g}, #{b}, #{a})"
|