Merge remote-tracking branch 'origin/master' into cefode

Conflicts:
	native/v8_extensions/native.mm
	spec/app/config-spec.coffee
	spec/app/window-spec.coffee
	spec/spec-helper.coffee
	spec/stdlib/fs-utils-spec.coffee
	src/app/atom-package.coffee
	src/app/config.coffee
	src/app/window.coffee
	src/packages/fuzzy-finder/lib/load-paths-handler.coffee
	src/packages/markdown-preview/lib/markdown-preview-view.coffee
	src/packages/tree-view/spec/tree-view-spec.coffee
	src/stdlib/require.coffee
This commit is contained in:
Kevin Sawicki & Nathan Sobo
2013-03-20 10:46:50 -06:00
104 changed files with 6970 additions and 1164 deletions

View File

@@ -7,70 +7,22 @@ CSON = require 'cson'
module.exports =
class AtomPackage extends Package
metadata: null
packageMain: null
mainModule: null
deferActivation: false
load: ({activateImmediately}={}) ->
load: ->
try
@loadMetadata()
@loadKeymaps()
@loadStylesheets()
if @metadata.activationEvents and not activateImmediately
@subscribeToActivationEvents()
if @deferActivation = @metadata.activationEvents?
@registerDeferredDeserializers()
else
@activatePackageMain()
@requireMainModule()
catch e
console.warn "Failed to load package named '#{@name}'", e.stack
this
disableEventHandlersOnBubblePath: (event) ->
bubblePathEventHandlers = []
disabledHandler = ->
element = $(event.target)
while element.length
if eventHandlers = element.data('events')?[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
unsubscribeFromActivationEvents: (activateHandler) ->
if _.isArray(@metadata.activationEvents)
rootView.off(event, activateHandler) for event in @metadata.activationEvents
else
rootView.off(event, selector, activateHandler) for event, selector of @metadata.activationEvents
subscribeToActivationEvents: () ->
activateHandler = (event) =>
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
@activatePackageMain()
$(event.target).trigger(event)
@restoreEventHandlersOnBubblePath(bubblePathEventHandlers)
@unsubscribeFromActivationEvents(activateHandler)
if _.isArray(@metadata.activationEvents)
rootView.command(event, activateHandler) for event in @metadata.activationEvents
else
rootView.command(event, selector, activateHandler) for event, selector of @metadata.activationEvents
activatePackageMain: ->
mainPath = @path
mainPath = fs.join(mainPath, @metadata.main) if @metadata.main
try
mainPath = require.resolve(mainPath)
catch e
return
if fs.isFile(mainPath)
@packageMain = require(mainPath)
config.setDefaults(@name, @packageMain.configDefaults)
atom.activateAtomPackage(this)
loadMetadata: ->
if metadataPath = fs.resolveExtension(fs.join(@path, 'package'), ['cson', 'json'])
@metadata = CSON.readObject(metadataPath)
@@ -90,3 +42,68 @@ class AtomPackage extends Package
stylesheetDirPath = fs.join(@path, 'stylesheets')
for stylesheetPath in fs.list(stylesheetDirPath)
requireStylesheet(stylesheetPath)
activate: ->
if @deferActivation
@subscribeToActivationEvents()
else
try
if @requireMainModule()
config.setDefaults(@name, @mainModule.configDefaults)
atom.activateAtomPackage(this)
catch e
console.warn "Failed to activate package named '#{@name}'", e.stack
requireMainModule: ->
return @mainModule if @mainModule
mainPath =
if @metadata.main
fs.join(@path, @metadata.main)
else
fs.join(@path, 'index')
mainPath = fs.resolveExtension(mainPath, ["", _.keys(require.extensions)...])
@mainModule = require(mainPath) if fs.isFile(mainPath)
registerDeferredDeserializers: ->
for deserializerName in @metadata.deferredDeserializers ? []
registerDeferredDeserializer deserializerName, => @requireMainModule()
subscribeToActivationEvents: () ->
return unless @metadata.activationEvents?
activateHandler = (event) =>
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
@deferActivation = false
@activate()
$(event.target).trigger(event)
@restoreEventHandlersOnBubblePath(bubblePathEventHandlers)
@unsubscribeFromActivationEvents(activateHandler)
if _.isArray(@metadata.activationEvents)
rootView.command(event, activateHandler) for event in @metadata.activationEvents
else
rootView.command(event, selector, activateHandler) for event, selector of @metadata.activationEvents
unsubscribeFromActivationEvents: (activateHandler) ->
if _.isArray(@metadata.activationEvents)
rootView.off(event, activateHandler) for event in @metadata.activationEvents
else
rootView.off(event, selector, activateHandler) for event, selector of @metadata.activationEvents
disableEventHandlersOnBubblePath: (event) ->
bubblePathEventHandlers = []
disabledHandler = ->
element = $(event.target)
while element.length
if eventHandlers = element.data('events')?[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

View File

@@ -6,10 +6,10 @@ module.exports =
class AtomTheme extends Theme
loadStylesheet: (stylesheetPath)->
@stylesheets[stylesheetPath] = fs.read(stylesheetPath)
@stylesheets[stylesheetPath] = window.loadStylesheet(stylesheetPath)
load: ->
if fs.extension(@path) is '.css'
if fs.extension(@path) in ['.css', '.less']
@loadStylesheet(@path)
else
metadataPath = fs.resolveExtension(fs.join(@path, 'package'), ['cson', 'json'])
@@ -18,6 +18,6 @@ class AtomTheme extends Theme
if stylesheetNames
@loadStylesheet(fs.join(@path, name)) for name in stylesheetNames
else
@loadStylesheet(stylesheetPath) for stylesheetPath in fs.list(@path, ['.css'])
@loadStylesheet(stylesheetPath) for stylesheetPath in fs.list(@path, ['.css', '.less'])
super

View File

@@ -24,10 +24,10 @@ _.extend atom,
activateAtomPackage: (pack) ->
@activatedAtomPackages.push(pack)
pack.packageMain.activate(@atomPackageStates[pack.name] ? {})
pack.mainModule.activate(@atomPackageStates[pack.name] ? {})
deactivateAtomPackages: ->
pack.packageMain.deactivate?() for pack in @activatedAtomPackages
pack.mainModule.deactivate?() for pack in @activatedAtomPackages
@activatedAtomPackages = []
serializeAtomPackages: ->
@@ -35,7 +35,7 @@ _.extend atom,
for pack in @loadedPackages
if pack in @activatedAtomPackages
try
packageStates[pack.name] = pack.packageMain.serialize?()
packageStates[pack.name] = pack.mainModule.serialize?()
catch e
console?.error("Exception serializing '#{pack.name}' package's module\n", e.stack)
else
@@ -61,6 +61,9 @@ _.extend atom,
new LoadTextMatePackagesTask(textMatePackages).start() if textMatePackages.length > 0
activatePackages: ->
pack.activate() for pack in @loadedPackages
getLoadedPackages: ->
_.clone(@loadedPackages)

View File

@@ -19,6 +19,7 @@ class Config
userPackagesDirPath: userPackagesDirPath
defaultSettings: null
settings: null
configFileHasErrors: null
constructor: ->
@defaultSettings =
@@ -34,18 +35,17 @@ class Config
fs.makeDirectory(@configDirPath)
templateConfigDirPath = fs.resolve(window.resourcePath, 'dot-atom')
onConfigDirFile = (path) =>
templatePath = fs.join(templateConfigDirPath, path)
configPath = fs.join(@configDirPath, path)
fs.write(configPath, fs.read(templatePath))
relativePath = path.substring(templateConfigDirPath.length + 1)
configPath = fs.join(@configDirPath, relativePath)
fs.write(configPath, fs.read(path))
fs.traverseTreeSync(templateConfigDirPath, onConfigDirFile, (path) -> true)
configThemeDirPath = fs.join(@configDirPath, 'themes')
onThemeDirFile = (path) ->
templatePath = fs.join(bundledThemesDirPath, path)
configPath = fs.join(configThemeDirPath, path)
fs.write(configPath, fs.read(templatePath))
relativePath = path.substring(bundledThemesDirPath.length + 1)
configPath = fs.join(configThemeDirPath, relativePath)
fs.write(configPath, fs.read(path))
fs.traverseTreeSync(bundledThemesDirPath, onThemeDirFile, (path) -> true)
load: ->
@@ -54,8 +54,13 @@ class Config
loadUserConfig: ->
if fs.exists(@configFilePath)
userConfig = CSON.readObject(@configFilePath)
_.extend(@settings, userConfig)
try
userConfig = CSON.readObject(@configFilePath)
_.extend(@settings, userConfig)
catch e
@configFileHasErrors = true
console.error "Failed to load user config '#{@configFilePath}'", e.message
console.error e.stack
get: (keyPath) ->
_.valueForKeyPath(@settings, keyPath) ?
@@ -91,6 +96,7 @@ class Config
subscription
update: ->
return if @configFileHasErrors
@save()
@trigger 'updated'

View File

@@ -61,7 +61,7 @@ class Editor extends View
else
{editSession, @mini} = (editSessionOrOptions ? {})
requireStylesheet 'editor.css'
requireStylesheet 'editor.less'
@id = Editor.nextEditorId++
@lineCache = []

View File

@@ -9,6 +9,7 @@ class PaneContainer extends View
@deserialize: ({root}) ->
container = new PaneContainer
container.append(deserialize(root)) if root
container.removeEmptyPanes()
container
@content: ->
@@ -61,6 +62,23 @@ class PaneContainer extends View
saveAll: ->
pane.saveItems() for pane in @getPanes()
confirmClose: ->
deferred = $.Deferred()
modifiedItems = []
for pane in @getPanes()
modifiedItems.push(item) for item in pane.getItems() when item.isModified?()
cancel = => deferred.reject()
saveNextModifiedItem = =>
if modifiedItems.length == 0
deferred.resolve()
else
item = modifiedItems.pop()
@paneAtIndex(0).promptToSaveItem item, saveNextModifiedItem, cancel
saveNextModifiedItem()
deferred.promise()
getPanes: ->
@find('.pane').views()
@@ -93,5 +111,9 @@ class PaneContainer extends View
root.css(width: '100%', height: '100%', top: 0, left: 0)
root.adjustDimensions()
removeEmptyPanes: ->
for pane in @getPanes() when pane.getItems().length == 0
pane.remove()
afterAttach: ->
@adjustPaneDimensions()

View File

@@ -11,7 +11,8 @@ class Pane extends View
@div class: 'item-views', outlet: 'itemViews'
@deserialize: ({items, focused, activeItemUri}) ->
pane = new Pane(items.map((item) -> deserialize(item))...)
deserializedItems = _.compact(items.map((item) -> deserialize(item)))
pane = new Pane(deserializedItems...)
pane.showItemForUri(activeItemUri) if activeItemUri
pane.focusOnAttach = true if focused
pane
@@ -21,7 +22,7 @@ class Pane extends View
initialize: (@items...) ->
@viewsByClassName = {}
@showItem(@items[0])
@showItem(@items[0]) if @items.length > 0
@command 'core:close', @destroyActiveItem
@command 'core:save', @saveActiveItem
@@ -46,7 +47,7 @@ class Pane extends View
@command 'pane:split-down', => @splitDown()
@command 'pane:close', => @destroyItems()
@command 'pane:close-other-items', => @destroyInactiveItems()
@on 'focus', => @activeView.focus(); false
@on 'focus', => @activeView?.focus(); false
@on 'focusin', => @makeActive()
@on 'focusout', => @autosaveActiveItem()
@@ -72,6 +73,12 @@ class Pane extends View
isActive: ->
@hasClass('active')
getNextPane: ->
panes = @getContainer()?.getPanes()
return unless panes.length > 1
nextIndex = (panes.indexOf(this) + 1) % panes.length
panes[nextIndex]
getItems: ->
new Array(@items...)
@@ -152,13 +159,13 @@ class Pane extends View
destroyInactiveItems: ->
@destroyItem(item) for item in @getItems() when item isnt @activeItem
promptToSaveItem: (item, nextAction) ->
promptToSaveItem: (item, nextAction, cancelAction) ->
uri = item.getUri()
atom.confirm(
"'#{item.getTitle()}' has changes, do you want to save them?"
"'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?"
"Your changes will be lost if close this item without saving."
"Save", => @saveItem(item, nextAction)
"Cancel", null
"Cancel", cancelAction
"Don't Save", nextAction
)

View File

@@ -75,6 +75,9 @@ class RootView extends View
panes: @panes.serialize()
packages: atom.serializeAtomPackages()
confirmClose: ->
@panes.confirmClose()
handleFocus: (e) ->
if @getActivePane()
@getActivePane().focus()
@@ -115,7 +118,7 @@ class RootView extends View
updateTitle: ->
if projectPath = project.getPath()
if item = @getActivePaneItem()
@setTitle("#{item.getTitle()} - #{projectPath}")
@setTitle("#{item.getTitle?() ? 'untitled'} - #{projectPath}")
else
@setTitle(projectPath)
else

View File

@@ -20,7 +20,7 @@ class SelectList extends View
cancelling: false
initialize: ->
requireStylesheet 'select-list.css'
requireStylesheet 'select-list.less'
@miniEditor.getBuffer().on 'changed', => @schedulePopulateList()
@miniEditor.on 'focusout', => @cancel() unless @cancelling

View File

@@ -32,6 +32,8 @@ class TextMatePackage extends Package
console.warn "Failed to load package at '#{@path}'", e.stack
this
activate: -> # no-op
getGrammars: -> @grammars
readGrammars: ->

View File

@@ -11,7 +11,7 @@ class Theme
if fs.exists(name)
path = name
else
path = fs.resolve(config.themeDirPaths..., name, ['', '.tmTheme', '.css'])
path = fs.resolve(config.themeDirPaths..., name, ['', '.tmTheme', '.css', 'less'])
throw new Error("No theme exists named '#{name}'") unless path

View File

@@ -1,11 +1,13 @@
fs = require 'fs-utils'
$ = require 'jquery'
ChildProcess = require 'child_process'
{less} = require 'less'
{spawn} = require 'child_process'
require 'jquery-extensions'
require 'underscore-extensions'
require 'space-pen-extensions'
deserializers = {}
deferredDeserializers = {}
# This method is called in any window needing a general environment, including specs
window.setUpEnvironment = ->
@@ -23,17 +25,17 @@ window.setUpEnvironment = ->
$(document).on 'keydown', keymap.handleKeyEvent
keymap.bindDefaultKeys()
requireStylesheet 'reset.css'
requireStylesheet 'atom.css'
requireStylesheet 'tabs.css'
requireStylesheet 'tree-view.css'
requireStylesheet 'status-bar.css'
requireStylesheet 'command-panel.css'
requireStylesheet 'fuzzy-finder.css'
requireStylesheet 'overlay.css'
requireStylesheet 'popover-list.css'
requireStylesheet 'notification.css'
requireStylesheet 'markdown.css'
requireStylesheet 'reset.less'
requireStylesheet 'atom.less'
requireStylesheet 'tabs.less'
requireStylesheet 'tree-view.less'
requireStylesheet 'status-bar.less'
requireStylesheet 'command-panel.less'
requireStylesheet 'fuzzy-finder.less'
requireStylesheet 'overlay.less'
requireStylesheet 'popover-list.less'
requireStylesheet 'notification.less'
requireStylesheet 'markdown.less'
if nativeStylesheetPath = require.resolve("#{platform}.css")
requireStylesheet(nativeStylesheetPath)
@@ -52,10 +54,11 @@ window.startup = ->
handleWindowEvents()
config.load()
atom.loadTextPackage()
buildProjectAndRootView()
keymap.loadBundledKeymaps()
atom.loadThemes()
atom.loadPackages()
buildProjectAndRootView()
atom.activatePackages()
keymap.loadUserKeymaps()
atom.requireUserInitScript()
$(window).on 'beforeunload', -> shutdown(); false
@@ -81,14 +84,13 @@ window.installAtomCommand = (commandPath) ->
bundledCommandPath = fs.resolve(window.resourcePath, 'atom.sh')
if bundledCommandPath?
fs.write(commandPath, fs.read(bundledCommandPath))
ChildProcess.exec("chmod u+x '#{commandPath}'")
spawn("chmod u+x '#{commandPath}'")
window.handleWindowEvents = ->
$(window).on 'core:close', => window.close()
$(window).command 'window:close', => window.close()
$(window).command 'window:toggle-full-screen', => atom.toggleFullScreen()
$(window).on 'focus', -> $("body").removeClass('is-blurred')
$(window).on 'blur', -> $("body").addClass('is-blurred')
$(window).command 'window:close', => confirmClose()
window.buildProjectAndRootView = ->
RootView = require 'root-view'
@@ -115,10 +117,21 @@ window.stylesheetElementForId = (id) ->
window.requireStylesheet = (path) ->
if fullPath = require.resolve(path)
window.applyStylesheet(fullPath, fs.read(fullPath))
unless fullPath
content = window.loadStylesheet(fullPath)
window.applyStylesheet(fullPath, content)
else
console.log "bad", path
throw new Error("Could not find a file at path '#{path}'")
window.loadStylesheet = (path) ->
content = fs.read(path)
if fs.extension(path) == '.less'
(new less.Parser).parse content, (e, tree) ->
throw new Error(e.message, file, e.line) if e
content = tree.toCSS()
content
window.removeStylesheet = (path) ->
unless fullPath = require.resolve(path)
throw new Error("Could not find a file at path '#{path}'")
@@ -151,6 +164,9 @@ window.registerDeserializers = (args...) ->
window.registerDeserializer = (klass) ->
deserializers[klass.name] = klass
window.registerDeferredDeserializer = (name, fn) ->
deferredDeserializers[name] = fn
window.unregisterDeserializer = (klass) ->
delete deserializers[klass.name]
@@ -160,7 +176,11 @@ window.deserialize = (state) ->
deserializer.deserialize(state)
window.getDeserializer = (state) ->
deserializers[state?.deserializer]
name = state?.deserializer
if deferredDeserializers[name]
deferredDeserializers[name]()
delete deferredDeserializers[name]
deserializers[name]
window.measure = (description, fn) ->
start = new Date().getTime()
@@ -175,3 +195,6 @@ window.profile = (description, fn) ->
value = fn()
console.profileEnd(description)
value
confirmClose = ->
rootView.confirmClose().done -> window.close()