Merge branch 'master' into absolute-paths-in-fuzzy-finder

Conflicts:
	src/packages/fuzzy-finder/lib/load-paths-handler.coffee
This commit is contained in:
Chris Wanstrath
2013-03-27 19:10:50 -07:00
340 changed files with 3200 additions and 38710 deletions

View File

@@ -1,20 +1,29 @@
TextMateGrammar = require 'text-mate-grammar'
Package = require 'package'
fs = require 'fs'
fsUtils = require 'fs-utils'
_ = require 'underscore'
$ = require 'jquery'
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
@loadMetadata()
@loadKeymaps()
@loadStylesheets()
if @deferActivation = @metadata.activationEvents?
@loadGrammars()
@loadScopedProperties()
if @metadata.activationEvents?
@registerDeferredDeserializers()
else
@requireMainModule()
@@ -22,43 +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'])
@metadata = fs.readObject(metadataPath)
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)]
activate: ->
if @deferActivation
@subscribeToActivationEvents()
getStylesheetPaths: ->
stylesheetDirPath = fsUtils.join(@path, 'stylesheets')
if @metadata.stylesheets
@metadata.stylesheets.map (name) -> fsUtils.resolve(stylesheetDirPath, name, ['css', 'less', ''])
else
try
if @requireMainModule()
config.setDefaults(@name, @mainModule.configDefaults)
atom.activateAtomPackage(this)
catch e
console.warn "Failed to activate package named '#{@name}'", e.stack
fsUtils.list(stylesheetDirPath, ['css', 'less']) ? []
loadGrammars: ->
@grammars = []
grammarsDirPath = fsUtils.join(@path, 'grammars')
for grammarPath in fsUtils.list(grammarsDirPath, ['.cson', '.json']) ? []
@grammars.push(TextMateGrammar.loadSync(grammarPath))
loadScopedProperties: ->
@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])
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 = @path
mainPath = fs.join(mainPath, @metadata.main) if @metadata.main
mainPath = require.resolve(mainPath)
@mainModule = require(mainPath) if fs.isFile(mainPath)
mainModulePath = @getMainModulePath()
@mainModule = require(mainModulePath) if fsUtils.isFile(mainModulePath)
getMainModulePath: ->
return @mainModulePath if @resolvedMainModulePath
@resolvedMainModulePath = true
mainModulePath =
if @metadata.main
fsUtils.join(@path, @metadata.main)
else
fsUtils.join(@path, 'index')
@mainModulePath = fsUtils.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...])
registerDeferredDeserializers: ->
for deserializerName in @metadata.deferredDeserializers ? []
@@ -66,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

@@ -1,5 +1,6 @@
fs = require 'fs'
fs = require 'fs-utils'
Theme = require 'theme'
CSON = require 'cson'
module.exports =
class AtomTheme extends Theme
@@ -13,7 +14,7 @@ class AtomTheme extends Theme
else
metadataPath = fs.resolveExtension(fs.join(@path, 'package'), ['cson', 'json'])
if fs.isFile(metadataPath)
stylesheetNames = fs.readObject(metadataPath)?.stylesheets
stylesheetNames = CSON.readObject(metadataPath)?.stylesheets
if stylesheetNames
@loadStylesheet(fs.join(@path, name)) for name in stylesheetNames
else

View File

@@ -1,9 +1,8 @@
fs = require 'fs'
fs = require 'fs-utils'
_ = require 'underscore'
Package = require 'package'
TextMatePackage = require 'text-mate-package'
Theme = require 'theme'
LoadTextMatePackagesTask = require 'load-text-mate-packages-task'
messageIdCounter = 1
originalSendMessageToBrowserProcess = atom.sendMessageToBrowserProcess
@@ -14,70 +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
loadTextPackage: ->
textPackagePath = _.find @getPackagePaths(), (path) -> fs.base(path) is 'text.tmbundle'
pack = Package.build(textPackagePath)
@loadedPackages.push(pack)
pack.load()
loadPackages: ->
textMatePackages = []
paths = @getPackagePaths().filter (path) -> fs.base(path) isnt 'text.tmbundle'
for path in paths
pack = Package.build(path)
@loadedPackages.push(pack)
if pack instanceof TextMatePackage
textMatePackages.push(pack)
else
pack.load()
new LoadTextMatePackagesTask(textMatePackages).start() if textMatePackages.length > 0
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']
@@ -179,17 +199,6 @@ _.extend atom,
toggleFullScreen: ->
@sendMessageToBrowserProcess('toggleFullScreen')
getRootViewStateForPath: (path) ->
if json = localStorage[path]
JSON.parse(json)
setRootViewStateForPath: (path, state) ->
return unless path
if state?
localStorage[path] = JSON.stringify(state)
else
delete localStorage[path]
sendMessageToBrowserProcess: (name, data=[], callbacks) ->
messageId = messageIdCounter++
data.unshift(messageId)
@@ -209,12 +218,25 @@ _.extend atom,
windowState
getWindowState: (keyPath) ->
windowState = JSON.parse($native.getWindowState())
windowState = JSON.parse(@getInMemoryWindowState() ? @getSavedWindowState() ? '{}')
if keyPath
_.valueForKeyPath(windowState, keyPath)
else
windowState
getInMemoryWindowState: ->
inMemoryState = $native.getWindowState()
if inMemoryState.length > 0
inMemoryState
else
null
getSavedWindowState: ->
localStorage[window.location.params.pathToOpen]
saveWindowState: ->
localStorage[@getPathToOpen()] = JSON.stringify(@getWindowState())
update: ->
@sendMessageToBrowserProcess('update')

View File

@@ -1,6 +1,6 @@
$ = require 'jquery'
_ = require 'underscore'
fs = require 'fs'
fs = require 'fs-utils'
Specificity = require 'specificity'
PEG = require 'pegjs'
@@ -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

@@ -1,6 +1,7 @@
fs = require 'fs'
fs = require 'fs-utils'
_ = require 'underscore'
EventEmitter = require 'event-emitter'
CSON = require 'cson'
configDirPath = fs.absolute("~/.atom")
bundledPackagesDirPath = fs.join(resourcePath, "src/packages")
@@ -10,8 +11,6 @@ vendoredThemesDirPath = fs.join(resourcePath, "vendor/themes")
userThemesDirPath = fs.join(configDirPath, "themes")
userPackagesDirPath = fs.join(configDirPath, "packages")
require.paths.unshift userPackagesDirPath
module.exports =
class Config
configDirPath: configDirPath
@@ -36,19 +35,18 @@ class Config
fs.makeDirectory(@configDirPath)
templateConfigDirPath = fs.resolve(window.resourcePath, 'dot-atom')
onConfigDirFile = (path) =>
relativePath = path.substring(templateConfigDirPath.length + 1)
configPath = fs.join(@configDirPath, relativePath)
fs.write(configPath, fs.read(path))
fs.traverseTree(templateConfigDirPath, onConfigDirFile, (path) -> true)
fs.traverseTreeSync(templateConfigDirPath, onConfigDirFile, (path) -> true)
configThemeDirPath = fs.join(@configDirPath, 'themes')
onThemeDirFile = (path) ->
relativePath = path.substring(bundledThemesDirPath.length + 1)
configPath = fs.join(configThemeDirPath, relativePath)
fs.write(configPath, fs.read(path))
fs.traverseTree(bundledThemesDirPath, onThemeDirFile, (path) -> true)
fs.traverseTreeSync(bundledThemesDirPath, onThemeDirFile, (path) -> true)
load: ->
@initializeConfigDirectory()
@@ -57,7 +55,7 @@ class Config
loadUserConfig: ->
if fs.exists(@configFilePath)
try
userConfig = fs.readObject(@configFilePath)
userConfig = CSON.readObject(@configFilePath)
_.extend(@settings, userConfig)
catch e
@configFileHasErrors = true
@@ -103,6 +101,6 @@ class Config
@trigger 'updated'
save: ->
fs.writeObject(@configFilePath, @settings)
CSON.writeObject(@configFilePath, @settings)
_.extend Config.prototype, EventEmitter

View File

@@ -1,5 +1,6 @@
_ = require 'underscore'
fs = require 'fs'
fsUtils = require 'fs-utils'
File = require 'file'
EventEmitter = require 'event-emitter'
@@ -7,23 +8,27 @@ module.exports =
class Directory
path: null
constructor: (@path) ->
constructor: (@path, @symlink=false) ->
getBaseName: ->
fs.base(@path)
fsUtils.base(@path)
getPath: -> @path
getEntries: ->
directories = []
files = []
for path in fs.list(@path)
if fs.isDirectory(path)
directories.push(new Directory(path))
else if fs.isFile(path)
files.push(new File(path))
else
console.error "#{path} is neither a file nor a directory."
for path in fsUtils.list(@path)
try
stat = fs.lstatSync(path)
symlink = stat.isSymbolicLink()
stat = fs.statSync(path) if symlink
catch e
continue
if stat.isDirectory()
directories.push(new Directory(path, symlink))
else if stat.isFile()
files.push(new File(path, symlink))
directories.concat(files)
@@ -34,10 +39,12 @@ class Directory
@unsubscribeFromNativeChangeEvents() if @subscriptionCount() == 0
subscribeToNativeChangeEvents: ->
@watchId = $native.watchPath @path, (eventType) =>
@watchSubscription = fsUtils.watchPath @path, (eventType) =>
@trigger "contents-changed" if eventType is "contents-change"
unsubscribeFromNativeChangeEvents: ->
$native.unwatchPath(@path, @watchId)
if @watchSubscription?
@watchSubscription.unwatch()
@watchSubscription = null
_.extend Directory.prototype, EventEmitter

View File

@@ -1,5 +1,5 @@
Point = require 'point'
Buffer = require 'buffer'
Buffer = require 'text-buffer'
LanguageMode = require 'language-mode'
DisplayBuffer = require 'display-buffer'
Cursor = require 'cursor'
@@ -8,7 +8,7 @@ EventEmitter = require 'event-emitter'
Subscriber = require 'subscriber'
Range = require 'range'
_ = require 'underscore'
fs = require 'fs'
fs = require 'fs-utils'
module.exports =
class EditSession
@@ -25,11 +25,6 @@ class EditSession
session.setCursorScreenPosition(state.cursorScreenPosition)
session
@identifiedBy: 'path'
@deserializesToSameObject: (state, editSession) ->
state.path
scrollTop: 0
scrollLeft: 0
languageMode: null
@@ -589,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)
@@ -842,11 +843,18 @@ class EditSession
getGrammar: -> @languageMode.grammar
setGrammar: (grammar) ->
@languageMode.grammar = grammar
@handleGrammarChange()
reloadGrammar: ->
if @languageMode.reloadGrammar()
@unfoldAll()
@displayBuffer.tokenizedBuffer.resetScreenLines()
true
@handleGrammarChange() if @languageMode.reloadGrammar()
handleGrammarChange: ->
@unfoldAll()
@displayBuffer.tokenizedBuffer.resetScreenLines()
@trigger 'grammar-changed'
true
getDebugSnapshot: ->
[

View File

@@ -1,12 +1,12 @@
{View, $$} = require 'space-pen'
Buffer = require 'buffer'
Buffer = require 'text-buffer'
Gutter = require 'gutter'
Point = require 'point'
Range = require 'range'
EditSession = require 'edit-session'
CursorView = require 'cursor-view'
SelectionView = require 'selection-view'
fs = require 'fs'
fs = require 'fs-utils'
$ = require 'jquery'
_ = require 'underscore'
@@ -16,6 +16,7 @@ class Editor extends View
fontSize: 20
showInvisibles: false
showIndentGuide: false
showLineNumbers: true
autoIndent: true
autoIndentOnPaste: false
nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`~?-"
@@ -61,7 +62,7 @@ class Editor extends View
else
{editSession, @mini} = (editSessionOrOptions ? {})
requireStylesheet 'editor.less'
requireStylesheet 'editor'
@id = Editor.nextEditorId++
@lineCache = []
@@ -149,13 +150,13 @@ 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
'editor:duplicate-line': @duplicateLine
'editor:toggle-indent-guide': => config.set('editor.showIndentGuide', !config.get('editor.showIndentGuide'))
'editor:save-debug-snapshot': @saveDebugSnapshot
'editor:toggle-line-numbers': => config.set('editor.showLineNumbers', !config.get('editor.showLineNumbers'))
documentation = {}
for name, method of editorBindings
@@ -316,6 +317,7 @@ class Editor extends View
backwardsScanInRange: (args...) -> @getBuffer().backwardsScanInRange(args...)
configure: ->
@observeConfig 'editor.showLineNumbers', (showLineNumbers) => @gutter.setShowLineNumbers(showLineNumbers)
@observeConfig 'editor.showInvisibles', (showInvisibles) => @setShowInvisibles(showInvisibles)
@observeConfig 'editor.showIndentGuide', (showIndentGuide) => @setShowIndentGuide(showIndentGuide)
@observeConfig 'editor.invisibles', (invisibles) => @setInvisibles(invisibles)
@@ -371,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
@@ -395,8 +397,6 @@ class Editor extends View
e.pageX = @renderedLines.offset().left
onMouseDown(e)
@subscribe syntax, 'grammars-loaded', => @reloadGrammar()
@scrollView.on 'scroll', =>
if @scrollView.scrollLeft() == 0
@gutter.removeClass('drop-shadow')
@@ -456,6 +456,9 @@ class Editor extends View
@reloadGrammar()
@trigger 'editor:path-changed'
@activeEditSession.on "grammar-changed.editor", =>
@trigger 'editor:grammar-changed'
@trigger 'editor:path-changed'
@resetDisplay()
@@ -586,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
@@ -635,19 +639,19 @@ class Editor extends View
@requestDisplayUpdate()
splitLeft: (items...) ->
@pane()?.splitLeft(items...).activeView
@getPane()?.splitLeft(items...).activeView
splitRight: (items...) ->
@pane()?.splitRight(items...).activeView
@getPane()?.splitRight(items...).activeView
splitUp: (items...) ->
@pane()?.splitUp(items...).activeView
@getPane()?.splitUp(items...).activeView
splitDown: (items...) ->
@pane()?.splitDown(items...).activeView
@getPane()?.splitDown(items...).activeView
pane: ->
@closest('.pane').view()
getPane: ->
@parent('.item-views').parent('.pane').view()
remove: (selector, keepData) ->
return super if keepData or @removed
@@ -655,7 +659,7 @@ class Editor extends View
super
rootView?.focus()
afterRemove: ->
beforeRemove: ->
@removed = true
@activeEditSession?.destroy()
$(window).off(".editor-#{@id}")
@@ -713,8 +717,6 @@ class Editor extends View
fragment.remove()
updateLayerDimensions: ->
@gutter.calculateWidth()
height = @lineHeight * @screenLineCount()
unless @layerHeight == height
@renderedLines.height(height)
@@ -1141,20 +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()
@trigger 'editor:grammar-changed'
grammarChanged = @activeEditSession.reloadGrammar()
@handleGrammarChange() if grammarChanged
grammarChanged
handleGrammarChange: ->
@clearRenderedLines()
@updateDisplay()
bindToKeyedEvent: (key, event, callback) ->
binding = {}
binding[key] = event

View File

@@ -1,6 +1,7 @@
EventEmitter = require 'event-emitter'
fs = require 'fs'
fsUtils = require 'fs-utils'
_ = require 'underscore'
module.exports =
@@ -8,33 +9,34 @@ class File
path: null
cachedContents: null
constructor: (@path) ->
if @exists() and not fs.isFile(@path)
throw new Error(@path + " is a directory")
constructor: (@path, @symlink=false) ->
try
if fs.statSync(@path).isDirectory()
throw new Error("#{@path} is a directory")
setPath: (@path) ->
getPath: -> @path
getBaseName: ->
fs.base(@path)
fsUtils.base(@path)
write: (text) ->
previouslyExisted = @exists()
@cachedContents = text
fs.write(@getPath(), text)
fsUtils.write(@getPath(), text)
@subscribeToNativeChangeEvents() if not previouslyExisted and @subscriptionCount() > 0
read: (flushCache)->
if not @exists()
@cachedContents = null
else if not @cachedContents? or flushCache
@cachedContents = fs.read(@getPath())
@cachedContents = fsUtils.read(@getPath())
else
@cachedContents
exists: ->
fs.exists(@getPath())
fsUtils.exists(@getPath())
afterSubscribe: ->
@subscribeToNativeChangeEvents() if @exists() and @subscriptionCount() == 1
@@ -67,10 +69,12 @@ class File
@trigger "removed"
subscribeToNativeChangeEvents: ->
@watchId = $native.watchPath @path, (eventType, path) =>
@watchSubscription = fsUtils.watchPath @path, (eventType, path) =>
@handleNativeChangeEvent(eventType, path)
unsubscribeFromNativeChangeEvents: ->
$native.unwatchPath(@path, @watchId)
if @watchSubscription
@watchSubscription.unwatch()
@watchSubscription = null
_.extend File.prototype, EventEmitter

View File

@@ -1,9 +1,9 @@
_ = require 'underscore'
fs = require 'fs'
fs = require 'fs-utils'
Subscriber = require 'subscriber'
EventEmitter = require 'event-emitter'
GitRepository = require 'git-repository'
RepositoryStatusTask = require 'repository-status-task'
GitUtils = require 'git-utils'
module.exports =
class Git
@@ -14,26 +14,18 @@ class Git
catch e
null
statusFlags:
index_new: 1 << 0
index_modified: 1 << 1
index_deleted: 1 << 2
index_renamed: 1 << 3
index_typechange: 1 << 4
working_dir_new: 1 << 7
working_dir_modified: 1 << 8
working_dir_delete: 1 << 9
working_dir_typechange: 1 << 10
ignore: 1 << 14
statuses: null
upstream: null
statusTask: null
constructor: (path, options={}) ->
@repo = GitUtils.open(path)
unless @repo?
throw new Error("No Git repository found searching path: #{path}")
@statuses = {}
@upstream = {ahead: 0, behind: 0}
@repo = GitRepository.open(path)
refreshOnWindowFocus = options.refreshOnWindowFocus ? true
if refreshOnWindowFocus
$ = require 'jquery'
@@ -64,12 +56,14 @@ class Git
@statusTask.off()
@statusTask = null
@getRepo().destroy()
@repo = null
if @repo?
@repo.release()
@repo = null
@unsubscribe()
getWorkingDirectory: ->
@getPath()?.replace(/\/\.git\/?$/, '')
@getRepo().getWorkingDirectory()
getHead: ->
@getRepo().getHead() ? ''
@@ -88,22 +82,14 @@ class Git
isPathIgnored: (path) ->
@getRepo().isIgnored(@relativize(path))
isStatusModified: (status=0) ->
modifiedFlags = @statusFlags.working_dir_modified |
@statusFlags.working_dir_delete |
@statusFlags.working_dir_typechange |
@statusFlags.index_modified |
@statusFlags.index_deleted |
@statusFlags.index_typechange
(status & modifiedFlags) > 0
isStatusModified: (status) ->
@getRepo().isStatusModified(status)
isPathModified: (path) ->
@isStatusModified(@getPathStatus(path))
isStatusNew: (status=0) ->
newFlags = @statusFlags.working_dir_new |
@statusFlags.index_new
(status & newFlags) > 0
isStatusNew: (status) ->
@getRepo().isStatusNew(status)
isPathNew: (path) ->
@isStatusNew(@getPathStatus(path))
@@ -116,12 +102,7 @@ class Git
path
getShortHead: ->
head = @getHead()
return head.substring(11) if head.indexOf('refs/heads/') is 0
return head.substring(10) if head.indexOf('refs/tags/') is 0
return head.substring(13) if head.indexOf('refs/remotes/') is 0
return head.substring(0, 7) if head.match(/[a-fA-F0-9]{40}/)
return head
@getRepo().getShortHead()
checkoutHead: (path) ->
headCheckedOut = @getRepo().checkoutHead(@relativize(path))
@@ -129,7 +110,7 @@ class Git
headCheckedOut
getDiffStats: (path) ->
@getRepo().getDiffStats(@relativize(path)) ? added: 0, deleted: 0
@getRepo().getDiffStats(@relativize(path))
isSubmodule: (path) ->
@getRepo().isSubmodule(@relativize(path))
@@ -154,10 +135,7 @@ class Git
directoryStatus
getAheadBehindCounts: ->
@getRepo().getAheadBehindCounts() ? ahead: 0, behind: 0
getLineDiffs: (path, text) ->
@getRepo().getLineDiffs(@relativize(path), text) ? []
@getRepo().getAheadBehindCount()
_.extend Git.prototype, Subscriber
_.extend Git.prototype, EventEmitter

View File

@@ -1,60 +0,0 @@
SelectList = require 'select-list'
{$$} = require 'space-pen'
module.exports =
class GrammarView extends SelectList
@viewClass: -> "#{super} grammar-view from-top overlay mini"
filterKey: 'name'
initialize: (@editor) ->
@currentGrammar = @editor.getGrammar()
@path = @editor.getPath()
@autoDetect = name: 'Auto Detect'
@command 'editor:select-grammar', =>
@cancel()
false
super
@populate()
@attach()
itemForElement: (grammar) ->
if grammar is @currentGrammar
grammarClass = 'active-item'
else
grammarClass = 'inactive-item'
$$ ->
@li grammar.name, class: grammarClass
populate: ->
grammars = new Array(syntax.grammars...)
grammars.sort (grammarA, grammarB) ->
if grammarA.scopeName is 'text.plain'
-1
else if grammarB.scopeName is 'text.plain'
1
else if grammarA.name < grammarB.name
-1
else if grammarA.name > grammarB.name
1
else
0
grammars.unshift(@autoDetect)
@setArray(grammars)
confirmed: (grammar) ->
@cancel()
if grammar is @autoDetect
project.removeGrammarOverrideForPath(@path)
else
project.addGrammarOverrideForPath(@path, grammar)
@editor.reloadGrammar()
attach: ->
super
rootView.append(this)
@miniEditor.focus()

View File

@@ -1,5 +1,6 @@
{View, $$, $$$} = require 'space-pen'
Range = require 'range'
_ = require 'underscore'
module.exports =
class Gutter extends View
@@ -20,18 +21,11 @@ class Gutter extends View
editor.on 'cursor:moved', highlightLines
editor.on 'selection:changed', highlightLines
@calculateWidth()
editor: ->
@parentView
calculateLineNumberPadding: ->
widthTesterElement = $$ -> @div {class: 'line-number'}, ""
widthTesterElement.width(0)
@append(widthTesterElement)
lineNumberPadding = widthTesterElement.outerWidth()
widthTesterElement.remove()
lineNumberPadding
setShowLineNumbers: (showLineNumbers) ->
if showLineNumbers then @lineNumbers.show() else @lineNumbers.hide()
updateLineNumbers: (changes, renderFrom, renderTo) ->
if renderFrom < @firstScreenRow or renderTo > @lastScreenRow
@@ -48,6 +42,7 @@ class Gutter extends View
renderLineNumbers: (startScreenRow, endScreenRow) ->
editor = @editor()
maxDigits = editor.getLineCount().toString().length
rows = editor.bufferRowsForScreenRows(startScreenRow, endScreenRow)
cursorScreenRow = editor.getCursorScreenPosition().row
@@ -56,25 +51,20 @@ class Gutter extends View
if row == lastScreenRow
rowValue = ''
else
rowValue = row + 1
rowValue = (row + 1).toString()
classes = ['line-number']
classes.push('fold') if editor.isFoldedAtBufferRow(row)
@div rowValue, class: classes.join(' ')
@div class: classes.join(' '), =>
rowValuePadding = _.multiplyString('&nbsp;', maxDigits - rowValue.length)
@raw("#{rowValuePadding}#{rowValue}")
lastScreenRow = row
@calculateWidth()
@firstScreenRow = startScreenRow
@lastScreenRow = endScreenRow
@highlightedRows = null
@highlightLines()
calculateWidth: ->
highestNumberWidth = @editor().getLineCount().toString().length * @editor().charWidth
if highestNumberWidth != @highestNumberWidth
@highestNumberWidth = highestNumberWidth
@lineNumbers.width(highestNumberWidth + @calculateLineNumberPadding())
@widthChanged?(@outerWidth())
removeLineHighlights: ->
return unless @highlightedLineNumbers
for line in @highlightedLineNumbers

View File

@@ -1,12 +1,14 @@
$ = require 'jquery'
_ = require 'underscore'
fs = require 'fs'
fs = require 'fs-utils'
CSON = require 'cson'
BindingSet = require 'binding-set'
module.exports =
class Keymap
bindingSets: null
nextBindingSetIndex: 0
bindingSetsByFirstKeystroke: null
queuedKeystrokes: null
@@ -30,29 +32,48 @@ class Keymap
$(document).command 'open-dev', => atom.openDev()
loadBundledKeymaps: ->
@loadDirectory(require.resolve('keymaps'))
@loadDirectory(fs.resolveOnLoadPath('keymaps'))
loadUserKeymaps: ->
@loadDirectory(fs.join(config.configDirPath, 'keymaps'))
loadDirectory: (directoryPath) ->
@load(filePath) for filePath in fs.list(directoryPath, ['.cson', '.json'])
@load(filePath) for filePath in fs.list(directoryPath, ['.cson', '.json']) ? []
load: (path) ->
@add(fs.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

@@ -1,7 +1,7 @@
Range = require 'range'
_ = require 'underscore'
require 'underscore-extensions'
OnigRegExp = require 'onig-reg-exp'
{OnigRegExp} = require 'oniguruma'
module.exports =
class LanguageMode
@@ -17,10 +17,7 @@ class LanguageMode
path = @buffer.getPath()
pathContents = @buffer.cachedDiskContents
previousGrammar = @grammar
if @buffer.project?
@grammar = @buffer.project.grammarForFilePath(path, pathContents)
else
@grammar = syntax.grammarForFilePath(path, pathContents)
@grammar = syntax.selectGrammar(path, pathContents)
throw new Error("No grammar found for path: #{path}") unless @grammar
previousGrammar isnt @grammar
@@ -30,13 +27,13 @@ class LanguageMode
buffer = @editSession.buffer
commentStartRegexString = _.escapeRegExp(commentStartString).replace(/(\s+)$/, '($1)?')
commentStartRegex = OnigRegExp.create("^(\\s*)(#{commentStartRegexString})")
commentStartRegex = new OnigRegExp("^(\\s*)(#{commentStartRegexString})")
shouldUncomment = commentStartRegex.test(buffer.lineForRow(start))
if commentEndString = syntax.getProperty(scopes, "editor.commentEnd")
if shouldUncomment
commentEndRegexString = _.escapeRegExp(commentEndString).replace(/^(\s+)/, '($1)?')
commentEndRegex = OnigRegExp.create("(#{commentEndRegexString})(\\s*)$")
commentEndRegex = new OnigRegExp("(#{commentEndRegexString})(\\s*)$")
startMatch = commentStartRegex.search(buffer.lineForRow(start))
endMatch = commentEndRegex.search(buffer.lineForRow(end))
if startMatch and endMatch
@@ -152,12 +149,12 @@ class LanguageMode
increaseIndentRegexForScopes: (scopes) ->
if increaseIndentPattern = syntax.getProperty(scopes, 'editor.increaseIndentPattern')
OnigRegExp.create(increaseIndentPattern)
new OnigRegExp(increaseIndentPattern)
decreaseIndentRegexForScopes: (scopes) ->
if decreaseIndentPattern = syntax.getProperty(scopes, 'editor.decreaseIndentPattern')
OnigRegExp.create(decreaseIndentPattern)
new OnigRegExp(decreaseIndentPattern)
foldEndRegexForScopes: (scopes) ->
if foldEndPattern = syntax.getProperty(scopes, 'editor.foldEndPattern')
OnigRegExp.create(foldEndPattern)
new OnigRegExp(foldEndPattern)

View File

@@ -1,5 +0,0 @@
TextMatePackage = require 'text-mate-package'
module.exports =
loadPackage: (path) ->
callTaskMethod('packageLoaded', new TextMatePackage(path).readGrammars())

View File

@@ -1,26 +0,0 @@
Task = require 'task'
module.exports =
class LoadTextMatePackagesTask extends Task
constructor: (@packages) ->
super('load-text-mate-packages-handler')
started: ->
@loadNextPackage()
loadNextPackage: ->
unless @packages.length
@done()
syntax.trigger 'grammars-loaded'
return
@package = @packages.shift()
@loadPackage(@package.path)
loadPackage: (path) ->
@callWorkerMethod('loadPackage', path)
packageLoaded: (grammars) ->
@package.loadGrammars(grammars)
@loadNextPackage()

View File

@@ -0,0 +1,9 @@
Token = require 'token'
module.exports =
class NullGrammar
name: 'Null Grammar'
scopeName: 'text.plain.null-grammar'
tokenizeLine: (line) ->
{ tokens: [new Token(value: line, scopes: ['null-grammar.text.plain'])] }

View File

@@ -1,4 +1,4 @@
fs = require 'fs'
fs = require 'fs-utils'
module.exports =
class Package
@@ -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,16 +1,19 @@
{hex_md5} = require 'md5'
crypto = require 'crypto'
module.exports =
class Pasteboard
signatureForMetadata: null
md5: (text) ->
crypto.createHash('md5').update(text, 'utf8').digest('hex')
write: (text, metadata) ->
@signatureForMetadata = hex_md5(text)
@signatureForMetadata = @md5(text)
@metadata = metadata
$native.writeToPasteboard(text)
read: ->
text = $native.readFromPasteboard()
value = [text]
value.push(@metadata) if @signatureForMetadata == hex_md5(text)
value.push(@metadata) if @signatureForMetadata == @md5(text)
value

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

@@ -1,19 +1,19 @@
fs = require 'fs'
fs = require 'fs-utils'
_ = require 'underscore'
$ = require 'jquery'
Range = require 'range'
Buffer = require 'buffer'
Buffer = require 'text-buffer'
EditSession = require 'edit-session'
EventEmitter = require 'event-emitter'
Directory = require 'directory'
ChildProcess = require 'child-process'
BufferedProcess = require 'buffered-process'
module.exports =
class Project
registerDeserializer(this)
@deserialize: (state) ->
new Project(state.path, state.grammarOverridesByPath)
new Project(state.path)
tabLength: 2
softTabs: true
@@ -21,9 +21,8 @@ class Project
rootDirectory: null
editSessions: null
ignoredPathRegexes: null
grammarOverridesByPath: null
constructor: (path, @grammarOverridesByPath={}) ->
constructor: (path) ->
@setPath(path)
@editSessions = []
@buffers = []
@@ -31,23 +30,10 @@ class Project
serialize: ->
deserializer: 'Project'
path: @getPath()
grammarOverridesByPath: @grammarOverridesByPath
destroy: ->
editSession.destroy() for editSession in @getEditSessions()
addGrammarOverrideForPath: (path, grammar) ->
@grammarOverridesByPath[path] = grammar.scopeName
removeGrammarOverrideForPath: (path) ->
delete @grammarOverridesByPath[path]
grammarOverrideForPath: (path) ->
syntax.grammarForScopeName(@grammarOverridesByPath[path])
grammarForFilePath: (path, contents) ->
@grammarOverrideForPath(path) or syntax.grammarForFilePath(path, contents)
getPath: ->
@rootDirectory?.path
@@ -67,9 +53,11 @@ class Project
getFilePaths: ->
deferred = $.Deferred()
fs.getAllFilePathsAsync @getPath(), (paths) =>
paths = paths.filter (path) => not @isPathIgnored(path)
deferred.resolve(paths)
paths = []
onFile = (path) => paths.push(path) unless @isPathIgnored(path)
onDirectory = -> true
fs.traverseTreeSync(@getPath(), onFile, onDirectory)
deferred.resolve(paths)
deferred.promise()
isPathIgnored: (path) ->
@@ -160,9 +148,7 @@ class Project
_.remove(@buffers, buffer)
scan: (regex, iterator) ->
command = "#{require.resolve('ag')} --ackmate '#{regex.source}' '#{@getPath()}'"
bufferedData = ""
state = 'readingPath'
path = null
@@ -194,11 +180,22 @@ class Project
match = lineText.substr(column, length)
iterator({path, range, match})
ChildProcess.exec command , bufferLines: true, stdout: (data) ->
deferred = $.Deferred()
exit = (code) ->
if code is -1
deferred.reject({command, code})
else
deferred.resolve()
stdout = (data) ->
lines = data.split('\n')
lines.pop() # the last segment is a spurious '' because data always ends in \n due to bufferLines: true
for line in lines
readPath(line) if state is 'readingPath'
readLine(line) if state is 'readingLines'
command = require.resolve('ag')
args = ['--ackmate', regex.source, @getPath()]
new BufferedProcess({command, args, stdout, exit})
deferred
_.extend Project.prototype, EventEmitter

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

@@ -1,5 +1,5 @@
Git = require 'git'
fs = require 'fs'
Git = require 'git-utils'
fs = require 'fs-utils'
module.exports =
loadStatuses: (path) ->
@@ -7,10 +7,10 @@ module.exports =
if repo?
workingDirectoryPath = repo.getWorkingDirectory()
statuses = {}
for path, status of repo.getRepo().getStatuses()
for path, status of repo.getStatus()
statuses[fs.join(workingDirectoryPath, path)] = status
upstream = repo.getAheadBehindCounts()
repo.destroy()
upstream = repo.getAheadBehindCount()
repo.release()
else
upstream = {}
statuses = {}

View File

@@ -1,10 +1,10 @@
$ = require 'jquery'
{$$} = require 'space-pen'
fs = require 'fs'
fs = require 'fs-utils'
_ = require 'underscore'
{View} = require 'space-pen'
Buffer = require 'buffer'
Buffer = require 'text-buffer'
Editor = require 'editor'
Project = require 'project'
Pane = require 'pane'
@@ -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

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

View File

@@ -271,7 +271,7 @@ class Selection
if @isEmpty()
start = @cursor.getScreenRow()
range = @editSession.bufferRowsForScreenRows(start, start + 1)
if range[1]
if range[1] > range[0]
@editSession.buffer.deleteRows(range[0], range[1] - 1)
else
@editSession.buffer.deleteRow(range[0])

View File

@@ -2,40 +2,72 @@ _ = require 'underscore'
jQuery = require 'jquery'
Specificity = require 'specificity'
{$$} = require 'space-pen'
fs = require 'fs'
fs = require 'fs-utils'
EventEmitter = require 'event-emitter'
NullGrammar = require 'null-grammar'
nodePath = require 'path'
pathSplitRegex = new RegExp("[#{nodePath.sep}.]")
module.exports =
class Syntax
registerDeserializer(this)
@deserialize: ({grammarOverridesByPath}) ->
syntax = new Syntax()
syntax.grammarOverridesByPath = grammarOverridesByPath
syntax
constructor: ->
@grammars = []
@grammarsByFileType = {}
@grammarsByScopeName = {}
@globalProperties = {}
@grammarOverridesByPath = {}
@scopedPropertiesIndex = 0
@scopedProperties = []
@nullGrammar = new NullGrammar
serialize: ->
{ deserializer: @constructor.name, @grammarOverridesByPath }
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
grammarForFilePath: (filePath, fileContents) ->
return @grammarsByFileType["txt"] unless filePath
removeGrammar: (grammar) ->
if _.include(@grammars, grammar)
_.remove(@grammars, grammar)
delete @grammarsByFileType[fileType] for fileType in grammar.fileTypes
delete @grammarsByScopeName[grammar.scopeName]
extension = fs.extension(filePath)?[1..]
if filePath and extension.length == 0
extension = fs.base(filePath)
setGrammarOverrideForPath: (path, scopeName) ->
@grammarOverridesByPath[path] = scopeName
@grammarByFirstLineRegex(filePath, fileContents) or
@grammarsByFileType[extension] or
@grammarByFileTypeSuffix(filePath) or
@grammarsByFileType["txt"]
clearGrammarOverrideForPath: (path) ->
delete @grammarOverridesByPath[path]
grammarByFileTypeSuffix: (filePath) ->
clearGrammarOverrides: ->
@grammarOverridesByPath = {}
selectGrammar: (filePath, fileContents) ->
return @grammarsByFileType["txt"] ? @nullGrammar unless filePath
@grammarOverrideForPath(filePath) ?
@grammarByFirstLineRegex(filePath, fileContents) ?
@grammarByPath(filePath) ?
@grammarsByFileType["txt"] ?
@nullGrammar
grammarOverrideForPath: (path) ->
@grammarsByScopeName[@grammarOverridesByPath[path]]
grammarByPath: (path) ->
pathComponents = path.split(pathSplitRegex)
for fileType, grammar of @grammarsByFileType
return grammar if _.endsWith(filePath, fileType)
fileTypeComponents = fileType.split(pathSplitRegex)
pathSuffix = pathComponents[-fileTypeComponents.length..-1]
return grammar if _.isEqual(pathSuffix, fileTypeComponents)
grammarByFirstLineRegex: (filePath, fileContents) ->
try
@@ -68,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)
@@ -95,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}) ->
@@ -125,4 +163,13 @@ class Syntax
else
element[0]
cssSelectorFromScopeSelector: (scopeSelector) ->
scopeSelector.split(', ').map((commaFragment) ->
commaFragment.split(' ').map((spaceFragment) ->
spaceFragment.split('.').map((dotFragment) ->
'.' + dotFragment.replace(/\+/g, '\\+')
).join('')
).join(' ')
).join(', ')
_.extend(Syntax.prototype, EventEmitter)

View File

@@ -1,5 +1,5 @@
_ = require 'underscore'
fs = require 'fs'
fs = require 'fs-utils'
File = require 'file'
Point = require 'point'
Range = require 'range'

View File

@@ -1,22 +1,24 @@
_ = require 'underscore'
fs = require 'fs'
fs = require 'fs-utils'
plist = require 'plist'
Token = require 'token'
OnigRegExp = require 'onig-reg-exp'
OnigScanner = require 'onig-scanner'
CSON = require 'cson'
{OnigRegExp, OnigScanner} = require 'oniguruma'
module.exports =
class TextMateGrammar
@readFromPath: (path) ->
grammarContent = null
if fs.isObjectPath(path)
grammarContent = fs.readObject(path)
else
plist.parseString fs.read(path), (e, data) ->
throw new Error(e) if e
grammarContent = data[0]
throw new Error("Failed to load grammar at path `#{path}`") unless grammarContent
grammarContent
fs.readPlist(path)
@load: (path, done) ->
fs.readObjectAsync path, (err, object) ->
if err
done(err)
else
done(null, new TextMateGrammar(object))
@loadSync: (path) ->
new TextMateGrammar(fs.readObject(path))
name: null
fileTypes: null
@@ -28,7 +30,7 @@ class TextMateGrammar
constructor: ({ @name, @fileTypes, @scopeName, patterns, repository, @foldingStopMarker, firstLineMatch}) ->
@initialRule = new Rule(this, {@scopeName, patterns})
@repository = {}
@firstLineRegex = OnigRegExp.create(firstLineMatch) if firstLineMatch
@firstLineRegex = new OnigRegExp(firstLineMatch) if firstLineMatch
@fileTypes ?= []
for name, data of repository
@@ -39,9 +41,10 @@ class TextMateGrammar
ruleStack = new Array(ruleStack...) # clone ruleStack
tokens = []
position = 0
loop
scopes = scopesFromStack(ruleStack)
previousRuleStackLength = ruleStack.length
previousPosition = position
if line.length == 0
tokens = [new Token(value: "", scopes: scopes)]
@@ -69,6 +72,10 @@ class TextMateGrammar
))
break
if position == previousPosition and ruleStack.length == previousRuleStackLength
console.error("Popping rule because it loops at column #{position} of line '#{line}'", _.clone(ruleStack))
ruleStack.pop()
ruleStack.forEach (rule) -> rule.clearAnchorPosition()
{ tokens, ruleStack }
@@ -111,7 +118,7 @@ class Rule
regex = pattern.regexSource
regexes.push regex if regex
regexScanner = OnigScanner.create(regexes)
regexScanner = new OnigScanner(regexes)
regexScanner.patterns = patterns
@scannersByBaseGrammarName[baseGrammar.name] = regexScanner unless anchored
regexScanner
@@ -152,10 +159,10 @@ class Pattern
backReferences: null
anchored: false
constructor: (@grammar, { name, contentName, @include, match, begin, end, captures, beginCaptures, endCaptures, patterns, @popRule, hasBackReferences}) ->
constructor: (@grammar, { name, contentName, @include, match, begin, end, captures, beginCaptures, endCaptures, patterns, @popRule, @hasBackReferences}) ->
@scopeName = name ? contentName # TODO: We need special treatment of contentName
if match
if @hasBackReferences = hasBackReferences ? /\\\d+/.test(match)
if (end or @popRule) and @hasBackReferences ?= /\\\d+/.test(match)
@match = match
else
@regexSource = match

View File

@@ -1,101 +1,89 @@
Package = require 'package'
fs = require 'fs'
fsUtils = require 'fs-utils'
plist = require 'plist'
_ = require 'underscore'
TextMateGrammar = require 'text-mate-grammar'
async = require 'async'
module.exports =
class TextMatePackage extends Package
@testName: (packageName) ->
/(\.|_|-)tmbundle$/.test(packageName)
@cssSelectorFromScopeSelector: (scopeSelector) ->
scopeSelector.split(', ').map((commaFragment) ->
commaFragment.split(' ').map((spaceFragment) ->
spaceFragment.split('.').map((dotFragment) ->
'.' + dotFragment.replace(/\+/g, '\\+')
).join('')
).join(' ')
).join(', ')
@getLoadQueue: ->
return @loadQueue if @loadQueue
@loadQueue = async.queue (pack, done) -> pack.loadGrammars(done)
@loadQueue.drain = -> syntax.trigger 'grammars-loaded'
@loadQueue
constructor: ->
super
@preferencesPath = fs.join(@path, "Preferences")
@syntaxesPath = fs.join(@path, "Syntaxes")
@preferencesPath = fsUtils.join(@path, "Preferences")
@syntaxesPath = fsUtils.join(@path, "Syntaxes")
@grammars = []
load: ->
try
@loadGrammars()
catch e
console.warn "Failed to load package at '#{@path}'", e.stack
this
load: ({sync}={}) ->
if sync
@loadGrammarsSync()
else
TextMatePackage.getLoadQueue().push(this)
@loadScopedProperties()
activate: -> # no-op
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) =>
if isDirectory
fsUtils.listAsync @syntaxesPath, @legalGrammarExtensions, (err, paths) =>
return console.log("Error loading grammars of TextMate package '#{@path}':", err.stack, err) if err
async.eachSeries paths, @loadGrammarAtPath, done
loadGrammarAtPath: (path, done) =>
TextMateGrammar.load path, (err, grammar) =>
return console.log("Error loading grammar at path '#{path}':", err.stack ? err) if err
@addGrammar(grammar)
done()
loadGrammarsSync: ->
for path in fsUtils.list(@syntaxesPath, @legalGrammarExtensions) ? []
@addGrammar(TextMateGrammar.loadSync(path))
addGrammar: (grammar) ->
@grammars.push(grammar)
syntax.addGrammar(grammar) if atom.isPackageActive(@path)
getGrammars: -> @grammars
readGrammars: ->
grammars = []
for grammarPath in fs.list(@syntaxesPath)
try
grammars.push(TextMateGrammar.readFromPath(grammarPath))
catch e
console.warn "Failed to load grammar at path '#{grammarPath}'", e.stack
grammars
addGrammar: (rawGrammar) ->
grammar = new TextMateGrammar(rawGrammar)
@grammars.push(grammar)
syntax.addGrammar(grammar)
loadGrammars: (rawGrammars) ->
rawGrammars = @readGrammars() unless rawGrammars?
@grammars = []
@addGrammar(rawGrammar) for rawGrammar in rawGrammars
@loadScopedProperties()
loadScopedProperties: ->
for { selector, properties } in @getScopedProperties()
syntax.addProperties(selector, properties)
getScopedProperties: ->
scopedProperties = []
@scopedProperties = []
for grammar in @getGrammars()
if properties = @propertiesFromTextMateSettings(grammar)
selector = @cssSelectorFromScopeSelector(grammar.scopeName)
scopedProperties.push({selector, properties})
selector = syntax.cssSelectorFromScopeSelector(grammar.scopeName)
@scopedProperties.push({selector, properties})
for {scope, settings} in @getTextMatePreferenceObjects()
if properties = @propertiesFromTextMateSettings(settings)
selector = @cssSelectorFromScopeSelector(scope) if scope?
scopedProperties.push({selector, properties})
scopedProperties
readObjectFromPath: (path, callback) ->
object = null
error = null
if fs.isObjectPath(path)
object = fs.readObject(path)
else
plist.parseString fs.read(path), (e, data) ->
error = e
object = data[0]
error = throw new Error("Failed to load object at path `#{path}`") unless object
callback(error, object)
selector = syntax.cssSelectorFromScopeSelector(scope) if scope?
@scopedProperties.push({selector, properties})
getTextMatePreferenceObjects: ->
preferenceObjects = []
if fs.exists(@preferencesPath)
for preferencePath in fs.list(@preferencesPath)
@readObjectFromPath preferencePath, (e, object) =>
if e
console.warn "Failed to parse preference at path '#{preferencePath}'", e.stack
else
preferenceObjects.push(object)
if fsUtils.exists(@preferencesPath)
for preferencePath in fsUtils.list(@preferencesPath)
try
preferenceObjects.push(fsUtils.readObject(preferencePath))
catch e
console.warn "Failed to parse preference at path '#{preferencePath}'", e.stack
preferenceObjects
propertiesFromTextMateSettings: (textMateSettings) ->
@@ -113,6 +101,3 @@ class TextMatePackage extends Package
foldEndPattern: textMateSettings.foldingStopMarker
)
{ editor: editorProperties } if _.size(editorProperties) > 0
cssSelectorFromScopeSelector: (scopeSelector) ->
@constructor.cssSelectorFromScopeSelector(scopeSelector)

View File

@@ -1,5 +1,5 @@
_ = require 'underscore'
fs = require 'fs'
fs = require 'fs-utils'
plist = require 'plist'
Theme = require 'theme'
@@ -18,10 +18,9 @@ class TextMateTheme extends Theme
super
buildRulesets: ->
plist.parseString fs.read(@path), (error, [{settings}]) =>
throw new Error("Error loading theme at '#{@path}': #{error}") if error
@buildGlobalSettingsRulesets(settings[0])
@buildScopeSelectorRulesets(settings[1..])
{settings} = plist.parseFileSync(@path)
@buildGlobalSettingsRulesets(settings[0])
@buildScopeSelectorRulesets(settings[1..])
getStylesheet: ->
lines = []

View File

@@ -1,4 +1,4 @@
fs = require 'fs'
fs = require 'fs-utils'
module.exports =
class Theme

View File

@@ -1,7 +1,7 @@
fs = require 'fs'
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'
@@ -17,27 +17,21 @@ window.setUpEnvironment = ->
Keymap = require 'keymap'
window.rootViewParentSelector = 'body'
window.platform = $native.getPlatform()
window.config = new Config
window.syntax = new Syntax
window.syntax = deserialize(atom.getWindowState('syntax')) ? new Syntax
window.pasteboard = new Pasteboard
window.keymap = new Keymap()
$(document).on 'keydown', keymap.handleKeyEvent
keymap.bindDefaultKeys()
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'
requireStylesheet 'reset'
requireStylesheet 'atom'
requireStylesheet 'overlay'
requireStylesheet 'popover-list'
requireStylesheet 'notification'
requireStylesheet 'markdown'
if nativeStylesheetPath = require.resolve("#{platform}.css")
if nativeStylesheetPath = fs.resolveOnLoadPath(process.platform, ['css', 'less'])
requireStylesheet(nativeStylesheetPath)
# This method is only called when opening a real application window
@@ -53,11 +47,10 @@ window.startup = ->
handleWindowEvents()
config.load()
atom.loadTextPackage()
keymap.loadBundledKeymaps()
atom.loadThemes()
atom.loadPackages()
buildProjectAndRootView()
deserializeWindowState()
atom.activatePackages()
keymap.loadUserKeymaps()
atom.requireUserInitScript()
@@ -67,10 +60,13 @@ window.startup = ->
window.shutdown = ->
return if not project and not rootView
atom.setWindowState('pathToOpen', project.getPath())
atom.setRootViewStateForPath project.getPath(),
project: project.serialize()
rootView: rootView.serialize()
rootView.deactivate()
atom.setWindowState('project', project.serialize())
atom.setWindowState('syntax', syntax.serialize())
atom.setWindowState('rootView', rootView.serialize())
atom.deactivatePackages()
atom.setWindowState('packageStates', atom.packageStates)
rootView.remove()
atom.saveWindowState()
project.destroy()
git?.destroy()
$(window).off('focus blur before')
@@ -84,7 +80,7 @@ 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).command 'window:toggle-full-screen', => atom.toggleFullScreen()
@@ -92,13 +88,16 @@ window.handleWindowEvents = ->
$(window).on 'blur', -> $("body").addClass('is-blurred')
$(window).command 'window:close', => confirmClose()
window.buildProjectAndRootView = ->
window.deserializeWindowState = ->
RootView = require 'root-view'
Project = require 'project'
Git = require 'git'
pathToOpen = atom.getPathToOpen()
windowState = atom.getRootViewStateForPath(pathToOpen) ? {}
windowState = atom.getWindowState()
atom.packageStates = windowState.packageStates ? {}
window.project = deserialize(windowState.project) ? new Project(pathToOpen)
window.rootView = deserialize(windowState.rootView) ? new RootView
@@ -115,25 +114,30 @@ window.buildProjectAndRootView = ->
window.stylesheetElementForId = (id) ->
$("head style[id='#{id}']")
window.resolveStylesheet = (path) ->
if fs.extension(path).length > 0
fs.resolveOnLoadPath(path)
else
fs.resolveOnLoadPath(path, ['css', 'less'])
window.requireStylesheet = (path) ->
if fullPath = require.resolve(path)
if fullPath = window.resolveStylesheet(path)
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
throw new Error(e.message, path, e.line) if e
content = tree.toCSS()
content
window.removeStylesheet = (path) ->
unless fullPath = require.resolve(path)
unless fullPath = window.resolveStylesheet(path)
throw new Error("Could not find a file at path '#{path}'")
window.stylesheetElementForId(fullPath).remove()
@@ -189,6 +193,12 @@ window.measure = (description, fn) ->
console.log description, result
value
window.profile = (description, fn) ->
measure description, ->
console.profile(description)
value = fn()
console.profileEnd(description)
value
confirmClose = ->
rootView.confirmClose().done -> window.close()
rootView.confirmClose().done -> window.close()