Merge master

This commit is contained in:
Garen Torikian
2013-03-27 19:47:14 -07:00
121 changed files with 1025 additions and 1260 deletions

View File

@@ -1,6 +1,6 @@
TextMateGrammar = require 'text-mate-grammar'
Package = require 'package'
fs = require 'fs-utils'
fsUtils = require 'fs-utils'
_ = require 'underscore'
$ = require 'jquery'
CSON = require 'cson'
@@ -8,8 +8,13 @@ CSON = require 'cson'
module.exports =
class AtomPackage extends Package
metadata: null
keymaps: null
stylesheets: null
grammars: null
scopedProperties: null
mainModulePath: null
resolvedMainModulePath: false
mainModule: null
deferActivation: false
load: ->
try
@@ -18,7 +23,7 @@ class AtomPackage extends Package
@loadStylesheets()
@loadGrammars()
@loadScopedProperties()
if @deferActivation = @metadata.activationEvents?
if @metadata.activationEvents?
@registerDeferredDeserializers()
else
@requireMainModule()
@@ -26,59 +31,91 @@ class AtomPackage extends Package
console.warn "Failed to load package named '#{@name}'", e.stack
this
activate: ({immediate}={}) ->
keymap.add(path, map) for [path, map] in @keymaps
applyStylesheet(path, content) for [path, content] in @stylesheets
syntax.addGrammar(grammar) for grammar in @grammars
syntax.addProperties(path, selector, properties) for [path, selector, properties] in @scopedProperties
if @metadata.activationEvents? and not immediate
@subscribeToActivationEvents()
else
@activateNow()
activateNow: ->
try
if @requireMainModule()
config.setDefaults(@name, @mainModule.configDefaults)
@mainModule.activate(atom.getPackageState(@name) ? {})
catch e
console.warn "Failed to activate package named '#{@name}'", e.stack
loadMetadata: ->
if metadataPath = fs.resolveExtension(fs.join(@path, 'package'), ['cson', 'json'])
if metadataPath = fsUtils.resolveExtension(fsUtils.join(@path, 'package'), ['cson', 'json'])
@metadata = CSON.readObject(metadataPath)
@metadata ?= {}
loadKeymaps: ->
keymapsDirPath = fs.join(@path, 'keymaps')
@keymaps = @getKeymapPaths().map (path) -> [path, CSON.readObject(path)]
getKeymapPaths: ->
keymapsDirPath = fsUtils.join(@path, 'keymaps')
if @metadata.keymaps
for path in @metadata.keymaps
keymapPath = fs.resolve(keymapsDirPath, path, ['cson', 'json', ''])
keymap.load(keymapPath)
@metadata.keymaps.map (name) -> fsUtils.resolve(keymapsDirPath, name, ['cson', 'json', ''])
else
keymap.loadDirectory(keymapsDirPath)
fsUtils.list(keymapsDirPath, ['cson', 'json']) ? []
loadStylesheets: ->
stylesheetDirPath = fs.join(@path, 'stylesheets')
for stylesheetPath in fs.list(stylesheetDirPath) ? []
requireStylesheet(stylesheetPath)
@stylesheets = @getStylesheetPaths().map (path) -> [path, loadStylesheet(path)]
getStylesheetPaths: ->
stylesheetDirPath = fsUtils.join(@path, 'stylesheets')
if @metadata.stylesheets
@metadata.stylesheets.map (name) -> fsUtils.resolve(stylesheetDirPath, name, ['css', 'less', ''])
else
fsUtils.list(stylesheetDirPath, ['css', 'less']) ? []
loadGrammars: ->
grammarsDirPath = fs.join(@path, 'grammars')
for grammarPath in fs.list(grammarsDirPath, ['.cson', '.json']) ? []
grammarContent = fs.readObject(grammarPath)
grammar = new TextMateGrammar(grammarContent)
syntax.addGrammar(grammar)
@grammars = []
grammarsDirPath = fsUtils.join(@path, 'grammars')
for grammarPath in fsUtils.list(grammarsDirPath, ['.cson', '.json']) ? []
@grammars.push(TextMateGrammar.loadSync(grammarPath))
loadScopedProperties: ->
scopedPropertiessDirPath = fs.join(@path, 'scoped-properties')
for scopedPropertiesPath in fs.list(scopedPropertiessDirPath, ['.cson', '.json']) ? []
for selector, properties of fs.readObject(scopedPropertiesPath)
syntax.addProperties(selector, properties)
@scopedProperties = []
scopedPropertiessDirPath = fsUtils.join(@path, 'scoped-properties')
for scopedPropertiesPath in fsUtils.list(scopedPropertiessDirPath, ['.cson', '.json']) ? []
for selector, properties of fsUtils.readObject(scopedPropertiesPath)
@scopedProperties.push([scopedPropertiesPath, selector, properties])
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
serialize: ->
try
@mainModule?.serialize?()
catch e
console.error "Error serializing package '#{@name}'", e.stack
deactivate: ->
@unsubscribeFromActivationEvents()
syntax.removeGrammar(grammar) for grammar in @grammars
syntax.removeProperties(path) for [path] in @scopedProperties
keymap.remove(path) for [path] in @keymaps
removeStylesheet(path) for [path] in @stylesheets
@mainModule?.deactivate?()
requireMainModule: ->
return @mainModule if @mainModule
mainPath =
mainModulePath = @getMainModulePath()
@mainModule = require(mainModulePath) if fsUtils.isFile(mainModulePath)
getMainModulePath: ->
return @mainModulePath if @resolvedMainModulePath
@resolvedMainModulePath = true
mainModulePath =
if @metadata.main
fs.join(@path, @metadata.main)
fsUtils.join(@path, @metadata.main)
else
fs.join(@path, 'index')
mainPath = fs.resolveExtension(mainPath, ["", _.keys(require.extensions)...])
@mainModule = require(mainPath) if fs.isFile(mainPath)
fsUtils.join(@path, 'index')
@mainModulePath = fsUtils.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
registerDeferredDeserializers: ->
for deserializerName in @metadata.deferredDeserializers ? []
@@ -86,25 +123,23 @@ class AtomPackage extends Package
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
rootView.command(event, @handleActivationEvent) for event in @metadata.activationEvents
else
rootView.command(event, selector, activateHandler) for event, selector of @metadata.activationEvents
rootView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
unsubscribeFromActivationEvents: (activateHandler) ->
handleActivationEvent: (event) =>
bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event)
@activateNow()
$(event.target).trigger(event)
@restoreEventHandlersOnBubblePath(bubblePathEventHandlers)
@unsubscribeFromActivationEvents()
unsubscribeFromActivationEvents: ->
if _.isArray(@metadata.activationEvents)
rootView.off(event, activateHandler) for event in @metadata.activationEvents
rootView.off(event, @handleActivationEvent) for event in @metadata.activationEvents
else
rootView.off(event, selector, activateHandler) for event, selector of @metadata.activationEvents
rootView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents
disableEventHandlersOnBubblePath: (event) ->
bubblePathEventHandlers = []

View File

@@ -13,59 +13,91 @@ _.extend atom,
loadedThemes: []
pendingBrowserProcessCallbacks: {}
loadedPackages: []
activatedAtomPackages: []
atomPackageStates: {}
activePackages: []
packageStates: {}
presentingModal: false
pendingModals: [[]]
getPathToOpen: ->
@getWindowState('pathToOpen') ? window.location.params.pathToOpen
activateAtomPackage: (pack) ->
@activatedAtomPackages.push(pack)
pack.mainModule.activate(@atomPackageStates[pack.name] ? {})
getPackageState: (name) ->
@packageStates[name]
deactivateAtomPackages: ->
pack.mainModule.deactivate?() for pack in @activatedAtomPackages
@activatedAtomPackages = []
serializeAtomPackages: ->
packageStates = {}
for pack in @loadedPackages
if pack in @activatedAtomPackages
try
packageStates[pack.name] = pack.mainModule.serialize?()
catch e
console?.error("Exception serializing '#{pack.name}' package's module\n", e.stack)
else
packageStates[pack.name] = @atomPackageStates[pack.name]
packageStates
loadPackages: ->
textMatePackages = []
paths = @getPackagePaths().filter (path) -> fs.base(path) isnt 'text.tmbundle'
for path in paths
pack = Package.build(path)
@loadedPackages.push(pack)
pack.load()
setPackageState: (name, state) ->
@packageStates[name] = state
activatePackages: ->
pack.activate() for pack in @loadedPackages
@activatePackage(pack.path) for pack in @getLoadedPackages()
activatePackage: (id, options) ->
if pack = @loadPackage(id, options)
@activePackages.push(pack)
pack.activate(options)
pack
deactivatePackages: ->
@deactivatePackage(pack.path) for pack in @getActivePackages()
deactivatePackage: (id) ->
if pack = @getActivePackage(id)
@setPackageState(pack.name, state) if state = pack.serialize?()
pack.deactivate()
_.remove(@activePackages, pack)
else
throw new Error("No active package for id '#{id}'")
getActivePackage: (id) ->
if path = @resolvePackagePath(id)
_.detect @activePackages, (pack) -> pack.path is path
isPackageActive: (id) ->
if path = @resolvePackagePath(id)
_.detect @activePackages, (pack) -> pack.path is path
getActivePackages: ->
_.clone(@activePackages)
loadPackages: ->
@loadPackage(path) for path in @getPackagePaths() when not @isPackageDisabled(path)
loadPackage: (id, options) ->
if @isPackageDisabled(id)
return console.warn("Tried to load disabled packaged '#{id}'")
if path = @resolvePackagePath(id)
return pack if pack = @getLoadedPackage(id)
pack = Package.load(path, options)
@loadedPackages.push(pack)
pack
else
throw new Error("Could not resolve '#{id}' to a package path")
resolvePackagePath: _.memoize (id) ->
return id if fs.isDirectory(id)
path = fs.resolve(config.packageDirPaths..., id)
path if fs.isDirectory(path)
getLoadedPackage: (id) ->
if path = @resolvePackagePath(id)
_.detect @loadedPackages, (pack) -> pack.path is path
isPackageLoaded: (id) ->
@getLoadedPackage(id)?
getLoadedPackages: ->
_.clone(@loadedPackages)
isPackageDisabled: (id) ->
if path = @resolvePackagePath(id)
_.include(config.get('core.disabledPackages') ? [], fs.base(path))
getPackagePaths: ->
disabledPackages = config.get("core.disabledPackages") ? []
packagePaths = []
for packageDirPath in config.packageDirPaths
for packagePath in fs.list(packageDirPath)
continue if not fs.isDirectory(packagePath)
continue if fs.base(packagePath) in disabledPackages
continue if packagePath in packagePaths
packagePaths.push(packagePath)
packagePaths
packagePaths.push(packagePath) if fs.isDirectory(packagePath)
_.uniq(packagePaths)
loadThemes: ->
themeNames = config.get("core.themes") ? ['atom-dark-ui', 'atom-dark-syntax']

View File

@@ -14,8 +14,9 @@ class BindingSet
commandsByKeystrokes: null
commandForEvent: null
parser: null
name: null
constructor: (@selector, commandsByKeystrokes, @index) ->
constructor: (@selector, commandsByKeystrokes, @index, @name) ->
BindingSet.parser ?= PEG.buildParser(fs.read(require.resolve 'keystroke-pattern.pegjs'))
@specificity = Specificity(@selector)
@commandsByKeystrokes = @normalizeCommandsByKeystrokes(commandsByKeystrokes)

View File

@@ -584,9 +584,15 @@ class EditSession
cursor = @addCursor(marker)
selection = new Selection({editSession: this, marker, cursor})
@selections.push(selection)
selectionBufferRange = selection.getBufferRange()
@mergeIntersectingSelections()
@trigger 'selection-added', selection
selection
if selection.destroyed
for selection in @getSelections()
if selection.intersectsBufferRange(selectionBufferRange)
return selection
else
@trigger 'selection-added', selection
selection
addSelectionForBufferRange: (bufferRange, options={}) ->
options = _.defaults({invalidationStrategy: 'never'}, options)
@@ -837,12 +843,18 @@ class EditSession
getGrammar: -> @languageMode.grammar
setGrammar: (grammar) ->
@languageMode.grammar = grammar
@handleGrammarChange()
reloadGrammar: ->
if @languageMode.reloadGrammar()
@unfoldAll()
@displayBuffer.tokenizedBuffer.resetScreenLines()
@trigger 'grammar-changed'
true
@handleGrammarChange() if @languageMode.reloadGrammar()
handleGrammarChange: ->
@unfoldAll()
@displayBuffer.tokenizedBuffer.resetScreenLines()
@trigger 'grammar-changed'
true
getDebugSnapshot: ->
[

View File

@@ -150,7 +150,6 @@ class Editor extends View
'editor:toggle-line-comments': @toggleLineCommentsInSelection
'editor:log-cursor-scope': @logCursorScope
'editor:checkout-head-revision': @checkoutHead
'editor:select-grammar': @selectGrammar
'editor:copy-path': @copyPathToPasteboard
'editor:move-line-up': @moveLineUp
'editor:move-line-down': @moveLineDown
@@ -374,7 +373,7 @@ class Editor extends View
else if clickCount == 3
@activeEditSession.selectLine() unless e.shiftKey
@selectOnMousemoveUntilMouseup()
@selectOnMousemoveUntilMouseup() unless e.originalEvent.which > 1
@renderedLines.on 'mousedown', onMouseDown
@@ -590,6 +589,7 @@ class Editor extends View
@setSoftWrapColumn(softWrapColumn) if @attached
if @activeEditSession.getSoftWrap()
@addClass 'soft-wrap'
@scrollView.scrollLeft(0)
@_setSoftWrapColumn = => @setSoftWrapColumn()
$(window).on "resize.editor-#{@id}", @_setSoftWrapColumn
else
@@ -659,7 +659,7 @@ class Editor extends View
super
rootView?.focus()
afterRemove: ->
beforeRemove: ->
@removed = true
@activeEditSession?.destroy()
$(window).off(".editor-#{@id}")
@@ -1143,19 +1143,23 @@ class Editor extends View
else
@highlightedLine = null
getGrammar: -> @activeEditSession.getGrammar()
getGrammar: ->
@activeEditSession.getGrammar()
selectGrammar: ->
GrammarView = require 'grammar-view'
new GrammarView(this)
setGrammar: (grammar) ->
throw new Error("Only mini-editors can explicity set their grammar") unless @mini
@activeEditSession.setGrammar(grammar)
@handleGrammarChange()
reloadGrammar: ->
grammarChanged = @activeEditSession.reloadGrammar()
if grammarChanged
@clearRenderedLines()
@updateDisplay()
@handleGrammarChange() if grammarChanged
grammarChanged
handleGrammarChange: ->
@clearRenderedLines()
@updateDisplay()
bindToKeyedEvent: (key, event, callback) ->
binding = {}
binding[key] = event

View File

@@ -8,6 +8,7 @@ BindingSet = require 'binding-set'
module.exports =
class Keymap
bindingSets: null
nextBindingSetIndex: 0
bindingSetsByFirstKeystroke: null
queuedKeystrokes: null
@@ -40,20 +41,39 @@ class Keymap
@load(filePath) for filePath in fs.list(directoryPath, ['.cson', '.json']) ? []
load: (path) ->
@add(CSON.readObject(path))
@add(path, CSON.readObject(path))
add: (keymap) ->
add: (args...) ->
name = args.shift() if args.length > 1
keymap = args.shift()
for selector, bindings of keymap
@bindKeys(selector, bindings)
@bindKeys(name, selector, bindings)
bindKeys: (selector, bindings) ->
bindingSet = new BindingSet(selector, bindings, @bindingSets.length)
remove: (name) ->
for bindingSet in @bindingSets.filter((bindingSet) -> bindingSet.name is name)
_.remove(@bindingSets, bindingSet)
for keystrokes of bindingSet.commandsByKeystrokes
keystroke = keystrokes.split(' ')[0]
_.remove(@bindingSetsByFirstKeystroke[keystroke], bindingSet)
bindKeys: (args...) ->
name = args.shift() if args.length > 2
[selector, bindings] = args
bindingSet = new BindingSet(selector, bindings, @nextBindingSetIndex++, name)
@bindingSets.unshift(bindingSet)
for keystrokes of bindingSet.commandsByKeystrokes
keystroke = keystrokes.split(' ')[0] # only index by first keystroke
@bindingSetsByFirstKeystroke[keystroke] ?= []
@bindingSetsByFirstKeystroke[keystroke].push(bindingSet)
unbindKeys: (selector, bindings) ->
bindingSet = _.detect @bindingSets, (bindingSet) ->
bindingSet.selector is selector and bindingSet.bindings is bindings
if bindingSet
console.log "binding set", bindingSet
_.remove(@bindingSets, bindingSet)
bindingsForElement: (element) ->
keystrokeMap = {}
currentNode = $(element)

View File

@@ -20,7 +20,6 @@
'meta-U': 'editor:lower-case'
'alt-meta-w': 'editor:close-other-edit-sessions'
'meta-P': 'editor:close-all-edit-sessions'
'meta-L': 'editor:select-grammar'
'ctrl-C': 'editor:copy-path'
'ctrl-meta-up': 'editor:move-line-up'
'ctrl-meta-down': 'editor:move-line-down'

View File

@@ -11,6 +11,11 @@ class Package
else
new AtomPackage(path)
@load: (path, options) ->
pack = @build(path)
pack.load(options)
pack
name: null
path: null

View File

@@ -324,5 +324,5 @@ class Pane extends View
container.adjustPaneDimensions()
container.trigger 'pane:removed', [this]
afterRemove: ->
beforeRemove: ->
item.destroy?() for item in @getItems()

View File

@@ -1,10 +1,12 @@
_ = require 'underscore'
module.exports =
class Point
@fromObject: (object) ->
if object instanceof Point
object
else
if object instanceof Array
if _.isArray(object)
[row, column] = object
else
{ row, column } = object

View File

@@ -31,7 +31,7 @@ class Range
new Range(@start.copy(), @end.copy())
isEqual: (other) ->
if other instanceof Array and other.length == 2
if _.isArray(other) and other.length == 2
other = new Range(other...)
other.start.isEqual(@start) and other.end.isEqual(@end)

View File

@@ -29,8 +29,7 @@ class RootView extends View
@div id: 'vertical', outlet: 'vertical', =>
@subview 'panes', panes ? new PaneContainer
@deserialize: ({ panes, packages, projectPath }) ->
atom.atomPackageStates = packages ? {}
@deserialize: ({ panes }) ->
panes = deserialize(panes) if panes?.deserializer is 'PaneContainer'
new RootView({panes})
@@ -73,7 +72,6 @@ class RootView extends View
version: RootView.version
deserializer: 'RootView'
panes: @panes.serialize()
packages: atom.serializeAtomPackages()
confirmClose: ->
@panes.confirmClose()
@@ -94,10 +92,6 @@ class RootView extends View
afterAttach: (onDom) ->
@focus() if onDom
deactivate: ->
atom.deactivateAtomPackages()
@remove()
open: (path, options = {}) ->
changeFocus = options.changeFocus ? true
path = project.resolve(path) if path?

View File

@@ -22,7 +22,6 @@ class Syntax
@grammarsByFileType = {}
@grammarsByScopeName = {}
@grammarOverridesByPath = {}
@globalProperties = {}
@scopedPropertiesIndex = 0
@scopedProperties = []
@nullGrammar = new NullGrammar
@@ -32,9 +31,14 @@ class Syntax
addGrammar: (grammar) ->
@grammars.push(grammar)
for fileType in grammar.fileTypes
@grammarsByFileType[fileType] = grammar
@grammarsByScopeName[grammar.scopeName] = grammar
@grammarsByFileType[fileType] = grammar for fileType in grammar.fileTypes
@grammarsByScopeName[grammar.scopeName] = grammar
removeGrammar: (grammar) ->
if _.include(@grammars, grammar)
_.remove(@grammars, grammar)
delete @grammarsByFileType[fileType] for fileType in grammar.fileTypes
delete @grammarsByScopeName[grammar.scopeName]
setGrammarOverrideForPath: (path, scopeName) ->
@grammarOverridesByPath[path] = scopeName
@@ -46,7 +50,9 @@ class Syntax
@grammarOverridesByPath = {}
selectGrammar: (filePath, fileContents) ->
return @grammarsByFileType["txt"] ? @nullGrammar unless filePath
@grammarOverrideForPath(filePath) ?
@grammarByFirstLineRegex(filePath, fileContents) ?
@grammarByPath(filePath) ?
@@ -94,18 +100,24 @@ class Syntax
@grammarsByScopeName[scopeName]
addProperties: (args...) ->
selector = args.shift() if args.length > 1
properties = args.shift()
name = args.shift() if args.length > 2
[selector, properties] = args
if selector
@scopedProperties.unshift(
selector: selector,
properties: properties,
specificity: Specificity(selector),
index: @scopedPropertiesIndex++
)
else
_.extend(@globalProperties, properties)
@scopedProperties.unshift(
name: name
selector: selector,
properties: properties,
specificity: Specificity(selector),
index: @scopedPropertiesIndex++
)
removeProperties: (name) ->
for properties in @scopedProperties.filter((properties) -> properties.name is name)
_.remove(@scopedProperties, properties)
clearProperties: ->
@scopedProperties = []
@scopedPropertiesIndex = 0
getProperty: (scope, keyPath) ->
for object in @propertiesForScope(scope, keyPath)
@@ -121,7 +133,7 @@ class Syntax
while element
matchingProperties.push(@matchingPropertiesForElement(element, candidates)...)
element = element.parentNode
matchingProperties.concat([@globalProperties])
matchingProperties
matchingPropertiesForElement: (element, candidates) ->
matchingScopedProperties = candidates.filter ({selector}) ->

View File

@@ -1,10 +1,8 @@
Package = require 'package'
fs = require 'fs'
fsUtils = require 'fs-utils'
plist = require 'plist'
_ = require 'underscore'
TextMateGrammar = require 'text-mate-grammar'
CSON = require 'cson'
async = require 'async'
module.exports =
@@ -31,7 +29,16 @@ class TextMatePackage extends Package
TextMatePackage.getLoadQueue().push(this)
@loadScopedProperties()
legalGrammarExtensions: ['plist', 'tmLanguage', 'tmlanguage', 'cson', 'json']
activate: ->
syntax.addGrammar(grammar) for grammar in @grammars
for { selector, properties } in @scopedProperties
syntax.addProperties(@path, selector, properties)
deactivate: ->
syntax.removeGrammar(grammar) for grammar in @grammars
syntax.removeProperties(@path)
legalGrammarExtensions: ['plist', 'tmLanguage', 'tmlanguage']
loadGrammars: (done) ->
fsUtils.isDirectoryAsync @syntaxesPath, (isDirectory) =>
@@ -52,30 +59,22 @@ class TextMatePackage extends Package
addGrammar: (grammar) ->
@grammars.push(grammar)
syntax.addGrammar(grammar)
activate: -> # no-op
syntax.addGrammar(grammar) if atom.isPackageActive(@path)
getGrammars: -> @grammars
loadScopedProperties: ->
for { selector, properties } in @getScopedProperties()
syntax.addProperties(selector, properties)
getScopedProperties: ->
scopedProperties = []
@scopedProperties = []
for grammar in @getGrammars()
if properties = @propertiesFromTextMateSettings(grammar)
selector = syntax.cssSelectorFromScopeSelector(grammar.scopeName)
scopedProperties.push({selector, properties})
@scopedProperties.push({selector, properties})
for {scope, settings} in @getTextMatePreferenceObjects()
if properties = @propertiesFromTextMateSettings(settings)
selector = syntax.cssSelectorFromScopeSelector(scope) if scope?
scopedProperties.push({selector, properties})
scopedProperties
@scopedProperties.push({selector, properties})
getTextMatePreferenceObjects: ->
preferenceObjects = []

View File

@@ -61,10 +61,12 @@ window.shutdown = ->
return if not project and not rootView
atom.setWindowState('pathToOpen', project.getPath())
atom.setWindowState('project', project.serialize())
atom.setWindowState('rootView', rootView.serialize())
atom.setWindowState('syntax', syntax.serialize())
atom.setWindowState('rootView', rootView.serialize())
atom.deactivatePackages()
atom.setWindowState('packageStates', atom.packageStates)
rootView.remove()
atom.saveWindowState()
rootView.deactivate()
project.destroy()
git?.destroy()
$(window).off('focus blur before')
@@ -78,7 +80,7 @@ window.installAtomCommand = (commandPath) ->
bundledCommandPath = fs.resolve(window.resourcePath, 'atom.sh')
if bundledCommandPath?
fs.write(commandPath, fs.read(bundledCommandPath))
spawn("chmod u+x '#{commandPath}'")
spawn('chmod', ['u+x', commandPath])
window.handleWindowEvents = ->
$(window).command 'window:toggle-full-screen', => atom.toggleFullScreen()
@@ -95,6 +97,7 @@ window.deserializeWindowState = ->
windowState = atom.getWindowState()
atom.packageStates = windowState.packageStates ? {}
window.project = deserialize(windowState.project) ? new Project(pathToOpen)
window.rootView = deserialize(windowState.rootView) ? new RootView

View File

@@ -14,7 +14,7 @@ describe "Autocomplete", ->
describe "@activate()", ->
it "activates autocomplete on all existing and future editors (but not on autocomplete's own mini editor)", ->
spyOn(AutocompleteView.prototype, 'initialize').andCallThrough()
autocompletePackage = window.loadPackage("autocomplete")
autocompletePackage = atom.activatePackage("autocomplete")
expect(AutocompleteView.prototype.initialize).not.toHaveBeenCalled()
leftEditor = rootView.getActiveView()
@@ -41,7 +41,7 @@ describe "AutocompleteView", ->
beforeEach ->
window.rootView = new RootView
editor = new Editor(editSession: project.buildEditSession('sample.js'))
window.loadPackage('autocomplete')
atom.activatePackage('autocomplete')
autocomplete = new AutocompleteView(editor)
miniEditor = autocomplete.miniEditor

View File

@@ -6,7 +6,7 @@ describe "Autoflow package", ->
beforeEach ->
window.rootView = new RootView
rootView.open()
window.loadPackage 'autoflow'
atom.activatePackage('autoflow')
editor = rootView.getActiveView()
config.set('editor.preferredLineLength', 30)

View File

@@ -6,7 +6,7 @@ describe "bracket matching", ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
window.loadPackage('bracket-matcher')
atom.activatePackage('bracket-matcher')
rootView.attachToDom()
editor = rootView.getActiveView()
editSession = editor.activeEditSession

View File

@@ -7,7 +7,7 @@ describe "CommandLogger", ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
commandLogger = window.loadPackage('command-logger').mainModule
commandLogger = atom.activatePackage('command-logger').mainModule
commandLogger.eventLog = {}
editor = rootView.getActiveView()

View File

@@ -13,7 +13,6 @@ class CommandPaletteView extends SelectList
filterKey: 'eventDescription'
previouslyFocusedElement: null
keyBindings: null
initialize: ->
@@ -30,10 +29,14 @@ class CommandPaletteView extends SelectList
attach: ->
super
@keyBindings = _.losslessInvert(keymap.bindingsForElement(@previouslyFocusedElement))
if @previouslyFocusedElement[0]
@eventElement = @previouslyFocusedElement
else
@eventElement = rootView
@keyBindings = _.losslessInvert(keymap.bindingsForElement(@eventElement))
events = []
for eventName, eventDescription of _.extend($(window).events(), @previouslyFocusedElement.events())
for eventName, eventDescription of _.extend($(window).events(), @eventElement.events())
events.push({eventName, eventDescription}) if eventDescription
events = _.sortBy events, (e) -> e.eventDescription
@@ -53,4 +56,4 @@ class CommandPaletteView extends SelectList
confirmed: ({eventName}) ->
@cancel()
@previouslyFocusedElement.trigger(eventName)
@eventElement.trigger(eventName)

View File

@@ -9,7 +9,7 @@ describe "CommandPalette", ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
window.loadPackage("command-palette")
atom.activatePackage("command-palette")
rootView.attachToDom().focus()
rootView.trigger 'command-palette:toggle'
palette = rootView.find('.command-palette').view()
@@ -81,3 +81,20 @@ describe "CommandPalette", ->
expect(activeEditor.isFocused).toBeTruthy()
expect(eventHandler).toHaveBeenCalled()
expect(palette.hasParent()).toBeFalsy()
describe "when no element has focus", ->
it "uses the root view as the element to display and trigger events for", ->
rootView.trigger 'command-palette:toggle'
$(':focus').blur()
rootView.trigger 'command-palette:toggle'
keyBindings = _.losslessInvert(keymap.bindingsForElement(rootView.getActiveView()))
for eventName, description of rootView.events()
eventLi = palette.list.children("[data-event-name='#{eventName}']")
if description
expect(eventLi).toExist()
expect(eventLi.find('.label')).toHaveText(description)
expect(eventLi.find('.label').attr('title')).toBe(eventName)
for binding in keyBindings[eventName] ? []
expect(eventLi.find(".key-binding:contains(#{binding})")).toExist()
else
expect(eventLi).not.toExist()

View File

@@ -11,8 +11,12 @@ class CommandInterpreter
@lastRelativeAddress = compositeCommand if compositeCommand.isRelativeAddress()
compositeCommand.execute(@project, activeEditSession)
repeatRelativeAddress: (activeEditSession) ->
@lastRelativeAddress?.execute(@project, activeEditSession)
repeatRelativeAddress: (activeEditSession, {reverse}={}) ->
return unless @lastRelativeAddress
reverse ?= false
previousSelectionRange = activeEditSession.getSelection().getBufferRange()
address = if reverse then @lastRelativeAddress.reverse() else @lastRelativeAddress
repeatRelativeAddressInReverse: (activeEditSession) ->
@lastRelativeAddress?.reverse().execute(@project, activeEditSession)
address.execute(@project, activeEditSession).done ->
currentSelectionRange = activeEditSession.getSelection().getBufferRange()
$native.beep() if previousSelectionRange.isEqual(currentSelectionRange)

View File

@@ -44,7 +44,7 @@ class CommandPanelView extends View
rootView.command 'command-panel:find-in-file', => @attach('/')
rootView.command 'command-panel:find-in-project', => @attach('Xx/')
rootView.command 'command-panel:repeat-relative-address', => @repeatRelativeAddress()
rootView.command 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddressInReverse()
rootView.command 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddress(reverse: true)
rootView.command 'command-panel:set-selection-as-regex-address', => @setSelectionAsLastRelativeAddress()
@on 'click', '.expand', @onExpandAll
@@ -65,6 +65,9 @@ class CommandPanelView extends View
destroy: ->
@previewList.destroy()
rootView.off "command-panel:toggle-preview command-panel:find-in-file command-panel:find-in-project \
command-panel:repeat-relative-address command-panel:repeat-relative-address-in-reverse command-panel:set-selection-as-regex-address"
@remove()
toggle: ->
if @miniEditor.isFocused
@@ -138,6 +141,7 @@ class CommandPanelView extends View
else
@detach()
catch error
@loadingMessage.hide()
if error.name is "SyntaxError"
@flashError()
return
@@ -154,11 +158,8 @@ class CommandPanelView extends View
@historyIndex++
@miniEditor.setText(@history[@historyIndex] or '')
repeatRelativeAddress: ->
@commandInterpreter.repeatRelativeAddress(rootView.getActivePaneItem())
repeatRelativeAddressInReverse: ->
@commandInterpreter.repeatRelativeAddressInReverse(rootView.getActivePaneItem())
repeatRelativeAddress: (options) ->
@commandInterpreter.repeatRelativeAddress(rootView.getActivePaneItem(), options)
setSelectionAsLastRelativeAddress: ->
selection = rootView.getActiveView().getSelectedText()

View File

@@ -6,7 +6,7 @@ module.exports =
class Address extends Command
compile: (project, buffer, ranges) ->
deferred = $.Deferred()
deferred.resolve ranges.map (range) =>
operations = ranges.map (range) =>
newRange = @getRange(buffer, range)
new Operation
@@ -15,6 +15,7 @@ class Address extends Command
bufferRange: newRange
errorMessage: @errorMessage
deferred.resolve(operations)
deferred.promise()
isAddress: -> true

View File

@@ -11,7 +11,7 @@ describe "CommandPanel", ->
rootView.enableKeymap()
editSession = rootView.getActivePaneItem()
buffer = editSession.buffer
commandPanelMain = window.loadPackage('command-panel', activateImmediately: true).mainModule
commandPanelMain = atom.activatePackage('command-panel', immediate: true).mainModule
commandPanel = commandPanelMain.commandPanelView
commandPanel.history = []
commandPanel.historyIndex = 0
@@ -28,11 +28,8 @@ describe "CommandPanel", ->
rootView.trigger 'command-panel:toggle'
expect(commandPanel.miniEditor.isFocused).toBeTruthy()
rootViewState = rootView.serialize()
rootView.deactivate()
window.rootView = RootView.deserialize(rootViewState)
rootView.attachToDom()
window.loadPackage('command-panel')
atom.deactivatePackage('command-panel')
atom.activatePackage('command-panel')
expect(rootView.find('.command-panel')).not.toExist()
rootView.trigger 'command-panel:toggle'
@@ -55,13 +52,12 @@ describe "CommandPanel", ->
expect(commandPanel.history[2]).toBe('/test3')
expect(commandPanel.historyIndex).toBe(3)
rootViewState = rootView.serialize()
rootView.deactivate()
RootView.deserialize(rootViewState).attachToDom()
window.loadPackage('command-panel')
rootView.trigger 'command-panel:toggle'
atom.deactivatePackage('command-panel')
atom.activatePackage('command-panel')
rootView.trigger 'command-panel:toggle'
commandPanel = rootView.find('.command-panel').view()
expect(commandPanel.history.length).toBe(2)
expect(commandPanel.history[0]).toBe('/test2')
expect(commandPanel.history[1]).toBe('/test3')
@@ -216,39 +212,69 @@ describe "CommandPanel", ->
expect(commandPanel.miniEditor.hiddenInput).not.toMatchSelector ':focus'
describe "when command-panel:repeat-relative-address is triggered on the root view", ->
it "repeats the last search command if there is one", ->
rootView.trigger 'command-panel:repeat-relative-address'
describe "when there is more than one match", ->
it "repeats the last search command if there is one", ->
rootView.trigger 'command-panel:repeat-relative-address'
editSession.setCursorScreenPosition([4, 0])
editSession.setCursorScreenPosition([4, 0])
commandPanel.execute("/current")
expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]]
commandPanel.execute("/current")
expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]]
rootView.trigger 'command-panel:repeat-relative-address'
expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]]
rootView.trigger 'command-panel:repeat-relative-address'
expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]]
commandPanel.execute('s/r/R/g')
commandPanel.execute('s/r/R/g')
rootView.trigger 'command-panel:repeat-relative-address'
expect(editSession.getSelectedBufferRange()).toEqual [[6,34], [6,41]]
rootView.trigger 'command-panel:repeat-relative-address'
expect(editSession.getSelectedBufferRange()).toEqual [[6,34], [6,41]]
commandPanel.execute('0')
commandPanel.execute('/sort/ s/r/R/') # this contains a substitution... won't be repeated
commandPanel.execute('0')
commandPanel.execute('/sort/ s/r/R/') # this contains a substitution... won't be repeated
rootView.trigger 'command-panel:repeat-relative-address'
expect(editSession.getSelectedBufferRange()).toEqual [[3,31], [3,38]]
rootView.trigger 'command-panel:repeat-relative-address'
expect(editSession.getSelectedBufferRange()).toEqual [[3,31], [3,38]]
describe "when there is only one match and it is selected", ->
it "maintains the current selection and plays a beep", ->
editSession.setCursorScreenPosition([0, 0])
waitsForPromise ->
commandPanel.execute("/Array")
runs ->
expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]]
spyOn($native, 'beep')
rootView.trigger 'command-panel:repeat-relative-address'
waitsFor ->
$native.beep.callCount > 0
runs ->
expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]]
describe "when command-panel:repeat-relative-address-in-reverse is triggered on the root view", ->
it "it repeats the last relative address in the reverse direction", ->
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
describe "when there is more than one match", ->
it "it repeats the last relative address in the reverse direction", ->
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
editSession.setCursorScreenPosition([6, 0])
editSession.setCursorScreenPosition([6, 0])
commandPanel.execute("/current")
expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]]
commandPanel.execute("/current")
expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]]
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]]
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]]
describe "when there is only one match and it is selected", ->
it "maintains the current selection and plays a beep", ->
editSession.setCursorScreenPosition([0, 0])
waitsForPromise ->
commandPanel.execute("/Array")
runs ->
expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]]
spyOn($native, 'beep')
rootView.trigger 'command-panel:repeat-relative-address-in-reverse'
waitsFor ->
$native.beep.callCount > 0
runs ->
expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]]
describe "when command-panel:set-selection-as-regex-address is triggered on the root view", ->
it "sets the @lastRelativeAddress to a RegexAddress of the current selection", ->
@@ -319,13 +345,15 @@ describe "CommandPanel", ->
# there shouldn't be any dangling operations after this
describe "if the command is malformed", ->
it "adds and removes an error class to the command panel and does not close it", ->
it "adds and removes an error class to the command panel and does not close it or display a loading message", ->
rootView.attachToDom()
rootView.trigger 'command-panel:toggle'
commandPanel.miniEditor.insertText 'garbage-command!!'
commandPanel.miniEditor.hiddenInput.trigger keydownEvent('enter')
expect(commandPanel.parent()).toExist()
expect(commandPanel).toHaveClass 'error'
expect(commandPanel.loadingMessage).toBeHidden()
advanceClock 400

View File

@@ -8,7 +8,7 @@ describe "Preview List", ->
beforeEach ->
window.rootView = new RootView()
rootView.attachToDom()
commandPanelMain = window.loadPackage('command-panel', activateImmediately: true).mainModule
commandPanelMain = atom.activatePackage('command-panel', immediate: true).mainModule
commandPanelView = commandPanelMain.commandPanelView
previewList = commandPanelView.previewList
rootView.trigger 'command-panel:toggle'

View File

@@ -17,7 +17,7 @@ describe "EditorStats", ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
editorStats = window.loadPackage('editor-stats').mainModule.stats
editorStats = atom.activatePackage('editor-stats').mainModule.stats
describe "when a keyup event is triggered", ->
beforeEach ->

View File

@@ -163,8 +163,10 @@ class FuzzyFinderView extends SelectList
@loadPathsTask.start()
populateOpenBufferPaths: ->
editSessions = project.getEditSessions().filter (editSession)->
editSessions = project.getEditSessions().filter (editSession) ->
editSession.getPath()?
editSessions = _.uniq editSessions, (editSession) ->
editSession.getPath()
editSessions = _.sortBy editSessions, (editSession) =>
if editSession is rootView.getActivePaneItem()

View File

@@ -13,7 +13,7 @@ describe 'FuzzyFinder', ->
window.rootView = new RootView
rootView.open('sample.js')
rootView.enableKeymap()
finderView = window.loadPackage("fuzzy-finder").mainModule.createView()
finderView = atom.activatePackage("fuzzy-finder").mainModule.createView()
describe "file-finder behavior", ->
describe "toggling", ->
@@ -143,8 +143,8 @@ describe 'FuzzyFinder', ->
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
rootView.open()
states = rootView.serialize().packages
states = _.map states['fuzzy-finder'], (path, time) -> [ path, time ]
atom.deactivatePackage('fuzzy-finder')
states = _.map atom.getPackageState('fuzzy-finder'), (path, time) -> [ path, time ]
states = _.sortBy states, (path, time) -> -time
paths = [ 'sample-with-tabs.coffee', 'sample.txt', 'sample.js' ]
@@ -166,6 +166,13 @@ describe 'FuzzyFinder', ->
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
expect(rootView.find('.fuzzy-finder')).not.toExist()
describe "when multiple sessions are opened on the same path", ->
it "does not display duplicates for that path in the list", ->
rootView.open 'sample.js'
rootView.getActivePane().splitRight()
rootView.trigger 'fuzzy-finder:toggle-buffer-finder'
expect(_.pluck(finderView.list.children('li'), 'outerText')).toEqual ['sample.js']
describe "when a path selection is confirmed", ->
[editor1, editor2] = []

View File

@@ -5,7 +5,7 @@ describe "GitHub Flavored Markdown grammar", ->
beforeEach ->
spyOn(syntax, "addGrammar").andCallThrough()
window.loadPackage("gfm")
atom.activatePackage("gfm")
expect(syntax.addGrammar).toHaveBeenCalled()
grammar = syntax.addGrammar.argsForCall[0][0]

View File

@@ -8,7 +8,7 @@ class Gists
constructor: ->
rootView.command 'gist:create', '.editor', => @createGist()
createGist: (editor) ->
createGist: ->
editor = rootView.getActiveView()
return unless editor?

View File

@@ -7,7 +7,7 @@ describe "Gists package", ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
window.loadPackage('gists')
atom.activatePackage('gists')
editor = rootView.getActiveView()
spyOn($, 'ajax')

View File

@@ -0,0 +1,3 @@
'.editor':
'meta-L': 'grammar-selector:show'

View File

@@ -1,18 +1,23 @@
SelectList = require 'select-list'
Editor = require 'editor'
{$$} = require 'space-pen'
module.exports =
class GrammarView extends SelectList
class GrammarSelector extends SelectList
@viewClass: -> "#{super} grammar-selector from-top overlay mini"
@viewClass: -> "#{super} grammar-view from-top overlay mini"
@activate: ->
rootView.command 'grammar-selector:show', '.editor', => new GrammarSelector()
filterKey: 'name'
initialize: (@editor) ->
initialize: ->
@editor = rootView.getActiveView()
return unless @editor instanceof Editor
@currentGrammar = @editor.getGrammar()
@path = @editor.getPath()
@autoDetect = name: 'Auto Detect'
@command 'editor:select-grammar', =>
@command 'grammar-selector:show', =>
@cancel()
false
super
@@ -55,6 +60,5 @@ class GrammarView extends SelectList
attach: ->
super
rootView.append(this)
@miniEditor.focus()
@miniEditor.focus()

View File

@@ -0,0 +1,2 @@
'main': 'lib/grammar-selector'
'activationEvents': ['grammar-selector:show']

View File

@@ -0,0 +1,47 @@
GrammarSelector = require '../lib/grammar-selector'
RootView = require 'root-view'
_ = require 'underscore'
describe "GrammarSelector", ->
[editor, textGrammar, jsGrammar] = []
beforeEach ->
window.rootView = new RootView
atom.activatePackage('grammar-selector')
atom.activatePackage('text.tmbundle', sync: true)
atom.activatePackage('javascript.tmbundle', sync: true)
rootView.open('sample.js')
editor = rootView.getActiveView()
textGrammar = _.find syntax.grammars, (grammar) -> grammar.name is 'Plain Text'
expect(textGrammar).toBeTruthy()
jsGrammar = _.find syntax.grammars, (grammar) -> grammar.name is 'JavaScript'
expect(jsGrammar).toBeTruthy()
expect(editor.getGrammar()).toBe jsGrammar
describe "when grammar-selector:show is triggered", ->
it "displays a list of all the available grammars", ->
editor.trigger 'grammar-selector:show'
grammarView = rootView.find('.grammar-selector').view()
expect(grammarView).toExist()
grammars = syntax.grammars
expect(grammarView.list.children('li').length).toBe grammars.length + 1
expect(grammarView.list.children('li:first').text()).toBe 'Auto Detect'
describe "when a grammar is selected", ->
it "sets the new grammar on the editor", ->
editor.trigger 'grammar-selector:show'
grammarView = rootView.find('.grammar-selector').view()
grammarView.confirmed(textGrammar)
expect(editor.getGrammar()).toBe textGrammar
describe "when auto-detect is selected", ->
it "restores the auto-detected grammar on the editor", ->
editor.trigger 'grammar-selector:show'
grammarView = rootView.find('.grammar-selector').view()
grammarView.confirmed(textGrammar)
expect(editor.getGrammar()).toBe textGrammar
editor.trigger 'grammar-selector:show'
grammarView = rootView.find('.grammar-selector').view()
grammarView.confirmed(grammarView.array[0])
expect(editor.getGrammar()).toBe jsGrammar

View File

@@ -22,4 +22,4 @@ module.exports =
nextPane.showItem(new MarkdownPreviewView(editSession.buffer))
else
activePane.splitRight(new MarkdownPreviewView(editSession.buffer))
activePane.focus()
activePane.focus()

View File

@@ -6,7 +6,7 @@ describe "MarkdownPreview package", ->
beforeEach ->
project.setPath(project.resolve('markdown'))
window.rootView = new RootView
window.loadPackage("markdown-preview", activateImmediately: true)
atom.activatePackage("markdown-preview", immediate: true)
spyOn(MarkdownPreviewView.prototype, 'fetchRenderedMarkdown')
describe "markdown-preview:show", ->

View File

@@ -7,7 +7,7 @@ describe 'Package Generator', ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
window.loadPackage("package-generator")
atom.activatePackage("package-generator")
describe "when package-generator:generate is triggered", ->
it "displays a miniEditor", ->

View File

@@ -10,7 +10,7 @@ fdescribe "__PackageName__View", ->
beforeEach ->
window.rootView = new RootView
__packageName__ = window.loadPackage('__packageName__', activateImmediately: true)
__packageName__ = atom.activatePackage('__packageName__', immediate: true)
describe "when the __package-name__:toggle event is triggered", ->
it "attaches and then detaches the view", ->

View File

@@ -9,16 +9,14 @@ Package = require 'package'
describe "Snippets extension", ->
[buffer, editor, editSession] = []
beforeEach ->
atom.activatePackage('javascript.tmbundle', sync: true)
window.rootView = new RootView
rootView.open('sample.js')
packageWithSnippets = window.loadPackage("package-with-snippets")
spyOn(atom, "getLoadedPackages").andCallFake ->
textMatePackages = window.textMatePackages.filter (pack) -> /package-with-a-cson-grammar|test|textmate-package|javascript/.test(pack.name)
textMatePackages.concat([packageWithSnippets])
packageWithSnippets = atom.loadPackage("package-with-snippets")
spyOn(require("snippets/lib/snippets"), 'loadAll')
window.loadPackage("snippets")
atom.activatePackage("snippets")
editor = rootView.getActiveView()
editSession = rootView.getActivePaneItem()
@@ -238,6 +236,9 @@ describe "Snippets extension", ->
describe "snippet loading", ->
beforeEach ->
atom.loadPackage('package-with-broken-snippets.tmbundle', sync: true)
atom.loadPackage('package-with-snippets')
jasmine.unspy(window, "setTimeout")
jasmine.unspy(snippets, 'loadAll')
spyOn(snippets, 'loadAtomSnippets').andCallFake (path, done) -> done()

View File

@@ -1,6 +1,7 @@
{View} = require 'space-pen'
Range = require 'range'
CorrectionsView = require './corrections-view'
SpellChecker = require 'spellchecker'
module.exports =
class MisspellingView extends View
@@ -30,7 +31,7 @@ class MisspellingView extends View
screenRange = @getScreenRange()
misspelling = @editor.getTextInRange(@editor.bufferRangeForScreenRange(screenRange))
corrections = $native.getCorrectionsForMisspelling(misspelling)
corrections = SpellChecker.getCorrectionsForMisspelling(misspelling)
@correctionsView?.remove()
@correctionsView = new CorrectionsView(@editor, corrections, screenRange)

View File

@@ -1,3 +1,5 @@
SpellChecker = require 'spellchecker'
module.exports =
findMisspellings: (text) ->
wordRegex = /(?:^|[\s\[\]])([a-zA-Z']+)(?=[\s\.\[\]]|$)/g
@@ -6,7 +8,7 @@ module.exports =
for line in text.split('\n')
while matches = wordRegex.exec(line)
word = matches[1]
continue unless $native.isMisspelled(word)
continue unless SpellChecker.isMisspelled(word)
startColumn = matches.index + matches[0].length - word.length
endColumn = startColumn + word.length
misspellings.push([[row, startColumn], [row, endColumn]])

View File

@@ -4,10 +4,12 @@ describe "Spell check", ->
[editor] = []
beforeEach ->
atom.activatePackage('text.tmbundle', sync: true)
atom.activatePackage('javascript.tmbundle', sync: true)
window.rootView = new RootView
rootView.open('sample.js')
config.set('spell-check.grammars', [])
window.loadPackage('spell-check', activateImmediately: true)
atom.activatePackage('spell-check', immediate: true)
syntax.trigger 'grammars-loaded'
rootView.attachToDom()
editor = rootView.getActiveView()

View File

@@ -34,7 +34,7 @@ class StatusBarView extends View
@updateCursorPositionText()
@subscribe @editor, 'cursor:moved', => @updateCursorPositionText()
@subscribe @grammarName, 'click', => @editor.trigger 'editor:select-grammar'
@subscribe @grammarName, 'click', => @editor.trigger 'grammar-selector:show'
@subscribe @editor, 'editor:grammar-changed', => @updateGrammarText()
if git?
@subscribe git, 'status-changed', (path, status) =>

View File

@@ -32,7 +32,7 @@ describe "StatusBar", ->
describe "when associated with an unsaved buffer", ->
it "displays 'untitled' instead of the buffer's path, but still displays the buffer position", ->
rootView.deactivate()
rootView.remove()
window.rootView = new RootView
rootView.open()
rootView.simulateDomAttachment()
@@ -180,6 +180,11 @@ describe "StatusBar", ->
expect(statusBar.gitStatusIcon).toHaveText('+1')
describe "grammar label", ->
beforeEach ->
atom.activatePackage('text.tmbundle', sync: true)
atom.activatePackage('javascript.tmbundle', sync: true)
syntax.trigger 'grammars-loaded'
it "displays the name of the current grammar", ->
expect(statusBar.find('.grammar-name').text()).toBe 'JavaScript'
@@ -200,8 +205,8 @@ describe "StatusBar", ->
expect(statusBar.find('.grammar-name').text()).toBe 'Plain Text'
describe "when clicked", ->
it "toggles the editor:select-grammar event", ->
it "toggles the grammar-selector:show event", ->
eventHandler = jasmine.createSpy('eventHandler')
editor.on 'editor:select-grammar', eventHandler
editor.on 'grammar-selector:show', eventHandler
statusBar.find('.grammar-name').click()
expect(eventHandler).toHaveBeenCalled()

View File

@@ -10,7 +10,7 @@ describe "StripTrailingWhitespace", ->
window.rootView = new RootView
rootView.open(path)
window.loadPackage('strip-trailing-whitespace')
atom.activatePackage('strip-trailing-whitespace')
rootView.focus()
editor = rootView.getActiveView()

View File

@@ -8,7 +8,7 @@ describe "SymbolsView", ->
beforeEach ->
window.rootView = new RootView
window.loadPackage("symbols-view")
atom.activatePackage("symbols-view")
rootView.attachToDom()
setArraySpy = spyOn(SymbolsView.prototype, 'setArray').andCallThrough()

View File

@@ -11,7 +11,7 @@ describe "Tabs package main", ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
window.loadPackage("tabs")
atom.activatePackage("tabs")
describe ".activate()", ->
it "appends a tab bar all existing and new panes", ->

View File

@@ -5,7 +5,7 @@ describe "TOML grammar", ->
beforeEach ->
spyOn(syntax, "addGrammar")
window.loadPackage("toml")
atom.activatePackage("toml")
expect(syntax.addGrammar).toHaveBeenCalled()
grammar = syntax.addGrammar.argsForCall[0][0]

View File

@@ -40,8 +40,8 @@ class TreeView extends ScrollView
else
@selectActiveFile()
rootView.on 'pane:active-item-changed pane:became-active', => @selectActiveFile()
project.on 'path-changed', => @updateRoot()
rootView.on 'pane:active-item-changed.tree-view pane:became-active.tree-view', => @selectActiveFile()
project.on 'path-changed.tree-view', => @updateRoot()
@observeConfig 'core.hideGitIgnoredFiles', => @updateRoot()
if @root
@@ -67,6 +67,9 @@ class TreeView extends ScrollView
deactivate: ->
@root?.unwatchEntries()
rootView.off('.tree-view')
project.off('.tree-view')
@remove()
toggle: ->
if @hasFocus()

View File

@@ -13,7 +13,7 @@ describe "TreeView", ->
project.setPath(project.resolve('tree-view'))
window.rootView = new RootView
window.loadPackage("tree-view")
atom.activatePackage("tree-view")
rootView.trigger 'tree-view:toggle'
treeView = rootView.find(".tree-view").view()
treeView.root = treeView.find('ol > li:first').view()
@@ -47,10 +47,8 @@ describe "TreeView", ->
describe "when the project has no path", ->
beforeEach ->
project.setPath(undefined)
rootView.deactivate()
window.rootView = new RootView()
rootView.open()
treeView = window.loadPackage("tree-view").mainModule.createView()
atom.deactivatePackage("tree-view")
treeView = atom.activatePackage("tree-view").mainModule.createView()
it "does not attach to the root view or create a root node when initialized", ->
expect(treeView.hasParent()).toBeFalsy()
@@ -66,6 +64,7 @@ describe "TreeView", ->
describe "when the project is assigned a path because a new buffer is saved", ->
it "creates a root directory view but does not attach to the root view", ->
rootView.open()
rootView.getActivePaneItem().saveAs("/tmp/test.txt")
expect(treeView.hasParent()).toBeFalsy()
expect(treeView.root.getPath()).toBe '/tmp'
@@ -73,16 +72,16 @@ describe "TreeView", ->
describe "when the root view is opened to a file path", ->
it "does not attach to the root view but does create a root node when initialized", ->
rootView.deactivate()
window.rootView = new RootView
atom.deactivatePackage("tree-view")
atom.packageStates = {}
rootView.open('tree-view.js')
treeView = window.loadPackage("tree-view").mainModule.createView()
treeView = atom.activatePackage("tree-view").mainModule.createView()
expect(treeView.hasParent()).toBeFalsy()
expect(treeView.root).toExist()
describe "when the root view is opened to a directory", ->
it "attaches to the root view", ->
treeView = window.loadPackage("tree-view").mainModule.createView()
treeView = atom.activatePackage("tree-view").mainModule.createView()
expect(treeView.hasParent()).toBeTruthy()
expect(treeView.root).toExist()
@@ -91,10 +90,8 @@ describe "TreeView", ->
treeView.find('.directory:contains(dir1)').click()
sampleJs.click()
rootViewState = rootView.serialize()
rootView.deactivate()
window.rootView = RootView.deserialize(rootViewState)
window.loadPackage("tree-view")
atom.deactivatePackage("tree-view")
atom.activatePackage("tree-view")
treeView = rootView.find(".tree-view").view()
expect(treeView).toExist()
@@ -105,13 +102,8 @@ describe "TreeView", ->
rootView.attachToDom()
treeView.focus()
expect(treeView.find(".tree-view")).toMatchSelector ':focus'
rootViewState = rootView.serialize()
rootView.deactivate()
window.rootView = RootView.deserialize(rootViewState)
rootView.attachToDom()
window.loadPackage("tree-view")
atom.deactivatePackage("tree-view")
atom.activatePackage("tree-view")
treeView = rootView.find(".tree-view").view()
expect(treeView.find(".tree-view")).toMatchSelector ':focus'
@@ -599,7 +591,7 @@ describe "TreeView", ->
[dirView, fileView, rootDirPath, dirPath, filePath] = []
beforeEach ->
rootView.deactivate()
atom.deactivatePackage('tree-view')
rootDirPath = fs.join(fs.absolute("/tmp"), "atom-tests")
fs.remove(rootDirPath) if fs.exists(rootDirPath)
@@ -611,8 +603,8 @@ describe "TreeView", ->
fs.write(filePath, "doesn't matter")
project.setPath(rootDirPath)
window.rootView = new RootView(rootDirPath)
window.loadPackage('tree-view')
atom.activatePackage('tree-view')
rootView.trigger 'tree-view:toggle'
treeView = rootView.find(".tree-view").view()
dirView = treeView.root.entries.find('.directory:contains(test-dir)').view()

View File

@@ -6,7 +6,7 @@ describe "WrapGuide", ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
window.loadPackage('wrap-guide')
atom.activatePackage('wrap-guide')
rootView.attachToDom()
editor = rootView.getActiveView()
wrapGuide = rootView.find('.wrap-guide').view()

View File

@@ -1,17 +1,17 @@
# This file is loaded within Task's worker thread. It will attempt to invoke
# This file is loaded within Task's worker process. It will attempt to invoke
# any message with a 'method' and 'args' key on the global `handler` object. The
# initial `handler` object contains the `start` method, which is called by the
# task itself to relay information from the window thread and bootstrap the
# worker's environment. The `start` method then replaces the handler with an
# object required from the given `handlerPath`.
self.window = {}
self.attachEvent = ->
global.window = {}
global.attachEvent = ->
console =
warn: -> callTaskMethod 'warn', arguments...
log: -> callTaskMethod 'log', arguments...
error: -> callTaskMethod 'error', arguments...
self.__defineGetter__ 'console', -> console
global.__defineGetter__ 'console', -> console
window.document =
createElement: ->
@@ -24,21 +24,21 @@ window.document =
getElementById: -> {}
createComment: -> {}
createDocumentFragment: -> {}
self.document = window.document
global.document = window.document
# `callTaskMethod` can be used to invoke method's on the parent `Task` object
# back in the window thread.
self.callTaskMethod = (method, args...) ->
postMessage(method: method, args: args)
global.callTaskMethod = (method, args...) ->
process.send(method: method, args: args)
# The worker's initial handler replaces itself when `start` is invoked
self.handler =
# The worker's initial handler replaces itglobal when `start` is invoked
global.handler =
start: ({globals, handlerPath}) ->
for key, value of globals
self[key] = value
global[key] = value
window[key] = value
self.handler = require(handlerPath)
global.handler = require(handlerPath)
callTaskMethod 'started'
self.addEventListener 'message', ({data}) ->
process.on 'message', (data) ->
handler[data.method]?(data.args...) if data.method

View File

@@ -1,4 +1,5 @@
_ = require 'underscore'
child_process = require 'child_process'
EventEmitter = require 'event-emitter'
fs = require 'fs-utils'
@@ -11,9 +12,11 @@ class Task
start: ->
throw new Error("Task already started") if @worker?
blob = new Blob(["require('coffee-script'); require('task-shell');"], type: 'text/javascript')
@worker = new Worker(URL.createObjectURL(blob))
@worker.onmessage = ({data}) =>
# Equivalent with node --eval "...".
blob = "require('coffee-script'); require('task-shell');"
@worker = child_process.fork '--eval', [ blob ], cwd: __dirname
@worker.on 'message', (data) =>
if @aborted
@done()
return
@@ -22,6 +25,7 @@ class Task
this[data.method](data.args...)
else
@onMessage(data)
@startWorker()
log: -> console.log(arguments...)
@@ -43,14 +47,14 @@ class Task
@postMessage({method, args})
postMessage: (data) ->
@worker.postMessage(data)
@worker.send(data)
abort: ->
@aborted = true
done: ->
@abort()
@worker?.terminate()
@worker?.kill()
@worker = null
@trigger 'task-completed'