mirror of
https://github.com/atom/atom.git
synced 2026-02-11 15:14:59 -05:00
260 lines
8.4 KiB
CoffeeScript
260 lines
8.4 KiB
CoffeeScript
TextMateGrammar = require './text-mate-grammar'
|
|
Package = require './package'
|
|
fs = require 'fs-plus'
|
|
path = require 'path'
|
|
_ = require 'underscore-plus'
|
|
{$} = require './space-pen-extensions'
|
|
CSON = require 'season'
|
|
{Emitter} = require 'emissary'
|
|
|
|
### Internal: Loads and resolves packages. ###
|
|
|
|
module.exports =
|
|
class AtomPackage extends Package
|
|
Emitter.includeInto(this)
|
|
|
|
@stylesheetsDir: 'stylesheets'
|
|
|
|
metadata: null
|
|
keymaps: null
|
|
menus: null
|
|
stylesheets: null
|
|
grammars: null
|
|
scopedProperties: null
|
|
mainModulePath: null
|
|
resolvedMainModulePath: false
|
|
mainModule: null
|
|
|
|
constructor: (path, {@metadata}) ->
|
|
super(path)
|
|
@reset()
|
|
|
|
getType: -> 'atom'
|
|
|
|
getStylesheetType: -> 'bundled'
|
|
|
|
load: ->
|
|
@measure 'loadTime', =>
|
|
try
|
|
@metadata ?= Package.loadMetadata(@path)
|
|
|
|
@loadKeymaps()
|
|
@loadMenus()
|
|
@loadStylesheets()
|
|
@loadGrammars()
|
|
@loadScopedProperties()
|
|
|
|
if @metadata.activationEvents?
|
|
@registerDeferredDeserializers()
|
|
else
|
|
@requireMainModule()
|
|
|
|
catch e
|
|
console.warn "Failed to load package named '#{@name}'", e.stack ? e
|
|
this
|
|
|
|
enable: ->
|
|
atom.config.removeAtKeyPath('core.disabledPackages', @metadata.name)
|
|
|
|
disable: ->
|
|
atom.config.pushAtKeyPath('core.disabledPackages', @metadata.name)
|
|
|
|
reset: ->
|
|
@stylesheets = []
|
|
@keymaps = []
|
|
@menus = []
|
|
@grammars = []
|
|
@scopedProperties = []
|
|
|
|
activate: ({immediate}={}) ->
|
|
@measure 'activateTime', =>
|
|
@activateResources()
|
|
if @metadata.activationEvents? and not immediate
|
|
@subscribeToActivationEvents()
|
|
else
|
|
@activateNow()
|
|
|
|
activateNow: ->
|
|
try
|
|
@activateConfig()
|
|
@activateStylesheets()
|
|
if @requireMainModule()
|
|
@mainModule.activate(atom.packages.getPackageState(@name) ? {})
|
|
@mainActivated = true
|
|
catch e
|
|
console.warn "Failed to activate package named '#{@name}'", e.stack
|
|
|
|
activateConfig: ->
|
|
return if @configActivated
|
|
|
|
@requireMainModule()
|
|
if @mainModule?
|
|
config.setDefaults(@name, @mainModule.configDefaults)
|
|
@mainModule.activateConfig?()
|
|
@configActivated = true
|
|
|
|
activateStylesheets: ->
|
|
return if @stylesheetsActivated
|
|
|
|
type = @getStylesheetType()
|
|
for [stylesheetPath, content] in @stylesheets
|
|
atom.themes.applyStylesheet(stylesheetPath, content, type)
|
|
@stylesheetsActivated = true
|
|
|
|
activateResources: ->
|
|
atom.keymap.add(keymapPath, map) for [keymapPath, map] in @keymaps
|
|
atom.contextMenu.add(menuPath, map['context-menu']) for [menuPath, map] in @menus
|
|
atom.menu.add(map.menu) for [menuPath, map] in @menus when map.menu
|
|
syntax.addGrammar(grammar) for grammar in @grammars
|
|
for [scopedPropertiesPath, selector, properties] in @scopedProperties
|
|
syntax.addProperties(scopedPropertiesPath, selector, properties)
|
|
|
|
loadKeymaps: ->
|
|
@keymaps = @getKeymapPaths().map (keymapPath) -> [keymapPath, CSON.readFileSync(keymapPath)]
|
|
|
|
loadMenus: ->
|
|
@menus = @getMenuPaths().map (menuPath) -> [menuPath, CSON.readFileSync(menuPath)]
|
|
|
|
getKeymapPaths: ->
|
|
keymapsDirPath = path.join(@path, 'keymaps')
|
|
if @metadata.keymaps
|
|
@metadata.keymaps.map (name) -> fs.resolve(keymapsDirPath, name, ['json', 'cson', ''])
|
|
else
|
|
fs.listSync(keymapsDirPath, ['cson', 'json'])
|
|
|
|
getMenuPaths: ->
|
|
menusDirPath = path.join(@path, 'menus')
|
|
if @metadata.menus
|
|
@metadata.menus.map (name) -> fs.resolve(menusDirPath, name, ['json', 'cson', ''])
|
|
else
|
|
fs.listSync(menusDirPath, ['cson', 'json'])
|
|
|
|
loadStylesheets: ->
|
|
@stylesheets = @getStylesheetPaths().map (stylesheetPath) ->
|
|
[stylesheetPath, atom.themes.loadStylesheet(stylesheetPath)]
|
|
|
|
getStylesheetsPath: ->
|
|
path.join(@path, @constructor.stylesheetsDir)
|
|
|
|
getStylesheetPaths: ->
|
|
stylesheetDirPath = @getStylesheetsPath()
|
|
|
|
if @metadata.stylesheetMain
|
|
[fs.resolve(@path, @metadata.stylesheetMain)]
|
|
else if @metadata.stylesheets
|
|
@metadata.stylesheets.map (name) -> fs.resolve(stylesheetDirPath, name, ['css', 'less', ''])
|
|
else if indexStylesheet = fs.resolve(@path, 'index', ['css', 'less'])
|
|
[indexStylesheet]
|
|
else
|
|
fs.listSync(stylesheetDirPath, ['css', 'less'])
|
|
|
|
loadGrammars: ->
|
|
@grammars = []
|
|
grammarsDirPath = path.join(@path, 'grammars')
|
|
for grammarPath in fs.listSync(grammarsDirPath, ['.json', '.cson'])
|
|
@grammars.push(TextMateGrammar.loadSync(grammarPath))
|
|
|
|
loadScopedProperties: ->
|
|
@scopedProperties = []
|
|
scopedPropertiessDirPath = path.join(@path, 'scoped-properties')
|
|
for scopedPropertiesPath in fs.listSync(scopedPropertiessDirPath, ['.json', '.cson'])
|
|
for selector, properties of fs.readObjectSync(scopedPropertiesPath)
|
|
@scopedProperties.push([scopedPropertiesPath, selector, properties])
|
|
|
|
serialize: ->
|
|
if @mainActivated
|
|
try
|
|
@mainModule?.serialize?()
|
|
catch e
|
|
console.error "Error serializing package '#{@name}'", e.stack
|
|
|
|
deactivate: ->
|
|
@unsubscribeFromActivationEvents()
|
|
@deactivateResources()
|
|
@deactivateConfig()
|
|
@mainModule?.deactivate?() if @mainActivated
|
|
@emit('deactivated')
|
|
|
|
deactivateConfig: ->
|
|
@mainModule?.deactivateConfig?()
|
|
@configActivated = false
|
|
|
|
deactivateResources: ->
|
|
syntax.removeGrammar(grammar) for grammar in @grammars
|
|
syntax.removeProperties(scopedPropertiesPath) for [scopedPropertiesPath] in @scopedProperties
|
|
atom.keymap.remove(keymapPath) for [keymapPath] in @keymaps
|
|
atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in @stylesheets
|
|
@stylesheetsActivated = false
|
|
|
|
reloadStylesheets: ->
|
|
oldSheets = _.clone(@stylesheets)
|
|
@loadStylesheets()
|
|
atom.themes.removeStylesheet(stylesheetPath) for [stylesheetPath] in oldSheets
|
|
@reloadStylesheet(stylesheetPath, content) for [stylesheetPath, content] in @stylesheets
|
|
|
|
reloadStylesheet: (stylesheetPath, content) ->
|
|
atom.themes.applyStylesheet(stylesheetPath, content, @getStylesheetType())
|
|
|
|
requireMainModule: ->
|
|
return @mainModule if @mainModule?
|
|
mainModulePath = @getMainModulePath()
|
|
@mainModule = require(mainModulePath) if fs.isFileSync(mainModulePath)
|
|
|
|
getMainModulePath: ->
|
|
return @mainModulePath if @resolvedMainModulePath
|
|
@resolvedMainModulePath = true
|
|
mainModulePath =
|
|
if @metadata.main
|
|
path.join(@path, @metadata.main)
|
|
else
|
|
path.join(@path, 'index')
|
|
@mainModulePath = fs.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
|
|
|
|
registerDeferredDeserializers: ->
|
|
for deserializerName in @metadata.deferredDeserializers ? []
|
|
registerDeferredDeserializer deserializerName, =>
|
|
@activateStylesheets()
|
|
@requireMainModule()
|
|
|
|
subscribeToActivationEvents: ->
|
|
return unless @metadata.activationEvents?
|
|
if _.isArray(@metadata.activationEvents)
|
|
rootView.command(event, @handleActivationEvent) for event in @metadata.activationEvents
|
|
else if _.isString(@metadata.activationEvents)
|
|
rootView.command(@metadata.activationEvents, @handleActivationEvent)
|
|
else
|
|
rootView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
|
|
|
handleActivationEvent: (event) =>
|
|
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
|
|
@activateNow()
|
|
$(event.target).trigger(event)
|
|
@restoreEventHandlersOnBubblePath(bubblePathEventHandlers)
|
|
@unsubscribeFromActivationEvents()
|
|
|
|
unsubscribeFromActivationEvents: ->
|
|
if _.isArray(@metadata.activationEvents)
|
|
rootView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
|
|
else if _.isString(@metadata.activationEvents)
|
|
rootView.off(@metadata.activationEvents, @handleActivationEvent)
|
|
else
|
|
rootView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
|
|
|
|
disableEventHandlersOnBubblePath: (event) ->
|
|
bubblePathEventHandlers = []
|
|
disabledHandler = ->
|
|
element = $(event.target)
|
|
while element.length
|
|
if eventHandlers = element.handlers()?[event.type]
|
|
for eventHandler in eventHandlers
|
|
eventHandler.disabledHandler = eventHandler.handler
|
|
eventHandler.handler = disabledHandler
|
|
bubblePathEventHandlers.push(eventHandler)
|
|
element = element.parent()
|
|
bubblePathEventHandlers
|
|
|
|
restoreEventHandlersOnBubblePath: (eventHandlers) ->
|
|
for eventHandler in eventHandlers
|
|
eventHandler.handler = eventHandler.disabledHandler
|
|
delete eventHandler.disabledHandler
|