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

This commit is contained in:
Kevin Sawicki
2013-03-07 11:44:46 -08:00
88 changed files with 2744 additions and 2191 deletions

View File

@@ -10,11 +10,14 @@ originalSendMessageToBrowserProcess = atom.sendMessageToBrowserProcess
_.extend atom,
exitWhenDone: window.location.params.exitWhenDone
devMode: window.location.params.devMode
loadedThemes: []
pendingBrowserProcessCallbacks: {}
loadedPackages: []
activatedAtomPackages: []
atomPackageStates: {}
presentingModal: false
pendingModals: [[]]
getPathToOpen: ->
@getWindowState('pathToOpen') ? window.location.params.pathToOpen
@@ -101,15 +104,50 @@ _.extend atom,
@sendMessageToBrowserProcess('newWindow', args)
confirm: (message, detailedMessage, buttonLabelsAndCallbacks...) ->
args = [message, detailedMessage]
callbacks = []
while buttonLabelsAndCallbacks.length
args.push(buttonLabelsAndCallbacks.shift())
callbacks.push(buttonLabelsAndCallbacks.shift())
@sendMessageToBrowserProcess('confirm', args, callbacks)
wrapCallback = (callback) => => @dismissModal(callback)
@presentModal =>
args = [message, detailedMessage]
callbacks = []
while buttonLabelsAndCallbacks.length
do =>
buttonLabel = buttonLabelsAndCallbacks.shift()
buttonCallback = buttonLabelsAndCallbacks.shift()
args.push(buttonLabel)
callbacks.push(=> @dismissModal(buttonCallback))
@sendMessageToBrowserProcess('confirm', args, callbacks)
showSaveDialog: (callback) ->
@sendMessageToBrowserProcess('showSaveDialog', [], callback)
@presentModal =>
@sendMessageToBrowserProcess('showSaveDialog', [], (path) => @dismissModal(callback, path))
presentModal: (fn) ->
if @presentingModal
@pushPendingModal(fn)
else
@presentingModal = true
fn()
dismissModal: (fn, args...) ->
@pendingModals.push([]) # prioritize any modals presented during dismiss callback
fn?(args...)
@presentingModal = false
if fn = @shiftPendingModal()
_.delay (=> @presentModal(fn)), 50 # let view update before next dialog
pushPendingModal: (fn) ->
# pendingModals is a stack of queues. enqueue to top of stack.
stackSize = @pendingModals.length
@pendingModals[stackSize - 1].push(fn)
shiftPendingModal: ->
# pop pendingModals stack if its top queue is empty, otherwise shift off the topmost queue
stackSize = @pendingModals.length
currentQueueSize = @pendingModals[stackSize - 1].length
if stackSize > 1 and currentQueueSize == 0
@pendingModals.pop()
@shiftPendingModal()
else
@pendingModals[stackSize - 1].shift()
toggleDevTools: ->
@sendMessageToBrowserProcess('toggleDevTools')

View File

@@ -73,7 +73,7 @@ class BufferChangeOperation
event = { oldRange, newRange, oldText, newText }
@updateMarkers(event)
@buffer.trigger 'changed', event
@buffer.scheduleStoppedChangingEvent()
@buffer.scheduleModifiedEvents()
@resumeMarkerObservation()
@buffer.trigger 'markers-updated'

View File

@@ -69,7 +69,7 @@ class Buffer
@file.on "removed", =>
@updateCachedDiskContents()
@trigger "contents-modified", {differsFromDisk: true}
@triggerModifiedStatusChanged(@isModified())
@file.on "moved", =>
@trigger "path-changed", this
@@ -78,6 +78,7 @@ class Buffer
@trigger 'will-reload'
@updateCachedDiskContents()
@setText(@cachedDiskContents)
@triggerModifiedStatusChanged(false)
@trigger 'reloaded'
updateCachedDiskContents: ->
@@ -252,6 +253,7 @@ class Buffer
@setPath(path)
@cachedDiskContents = @getText()
@file.write(@getText())
@triggerModifiedStatusChanged(false)
@trigger 'saved'
isModified: ->
@@ -424,13 +426,20 @@ class Buffer
return unless path
git?.checkoutHead(path)
scheduleStoppedChangingEvent: ->
scheduleModifiedEvents: ->
clearTimeout(@stoppedChangingTimeout) if @stoppedChangingTimeout
stoppedChangingCallback = =>
@stoppedChangingTimeout = null
@trigger 'contents-modified', {differsFromDisk: @isModified()}
modifiedStatus = @isModified()
@trigger 'contents-modified', modifiedStatus
@triggerModifiedStatusChanged(modifiedStatus)
@stoppedChangingTimeout = setTimeout(stoppedChangingCallback, @stoppedChangingDelay)
triggerModifiedStatusChanged: (modifiedStatus) ->
return if modifiedStatus is @previousModifiedStatus
@previousModifiedStatus = modifiedStatus
@trigger 'modified-status-changed', modifiedStatus
fileExists: ->
@file.exists()

View File

@@ -14,17 +14,22 @@ module.exports =
class EditSession
registerDeserializer(this)
@deserialize: (state, project) ->
@deserialize: (state) ->
if fs.exists(state.buffer)
session = project.buildEditSessionForPath(state.buffer)
session = project.buildEditSession(state.buffer)
else
console.warn "Could not build edit session for path '#{state.buffer}' because that file no longer exists" if state.buffer
session = project.buildEditSessionForPath(null)
session = project.buildEditSession(null)
session.setScrollTop(state.scrollTop)
session.setScrollLeft(state.scrollLeft)
session.setCursorScreenPosition(state.cursorScreenPosition)
session
@identifiedBy: 'path'
@deserializesToSameObject: (state, editSession) ->
state.path
scrollTop: 0
scrollLeft: 0
languageMode: null
@@ -43,17 +48,38 @@ class EditSession
@addCursorAtScreenPosition([0, 0])
@buffer.retain()
@subscribe @buffer, "path-changed", => @trigger "path-changed"
@subscribe @buffer, "path-changed", =>
@project.setPath(fs.directory(@getPath())) unless @project.getPath()?
@trigger "title-changed"
@trigger "path-changed"
@subscribe @buffer, "contents-conflicted", => @trigger "contents-conflicted"
@subscribe @buffer, "markers-updated", => @mergeCursors()
@subscribe @buffer, "modified-status-changed", => @trigger "modified-status-changed"
@preserveCursorPositionOnBufferReload()
@subscribe @displayBuffer, "changed", (e) =>
@trigger 'screen-lines-changed', e
getViewClass: ->
require 'editor'
getTitle: ->
if path = @getPath()
fs.base(path)
else
'untitled'
getLongTitle: ->
if path = @getPath()
fileName = fs.base(path)
directory = fs.base(fs.directory(path))
"#{fileName} - #{directory}"
else
'untitled'
destroy: ->
throw new Error("Edit session already destroyed") if @destroyed
return if @destroyed
@destroyed = true
@unsubscribe()
@buffer.release()
@@ -130,6 +156,7 @@ class EditSession
saveAs: (path) -> @buffer.saveAs(path)
getFileExtension: -> @buffer.getExtension()
getPath: -> @buffer.getPath()
getUri: -> @getPath()
isBufferRowBlank: (bufferRow) -> @buffer.isRowBlank(bufferRow)
nextNonBlankBufferRow: (bufferRow) -> @buffer.nextNonBlankRow(bufferRow)
getEofBufferPosition: -> @buffer.getEofPosition()

View File

@@ -16,7 +16,6 @@ class Editor extends View
fontSize: 20
showInvisibles: false
showIndentGuide: false
autosave: false
autoIndent: true
autoIndentOnPaste: false
nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`~?-"
@@ -49,8 +48,6 @@ class Editor extends View
lineCache: null
isFocused: false
activeEditSession: null
closedEditSessions: null
editSessions: null
attached: false
lineOverdraw: 10
pendingChanges: null
@@ -58,15 +55,12 @@ class Editor extends View
newSelections: null
redrawOnReattach: false
@deserialize: (state) ->
editor = new Editor(mini: state.mini, deserializing: true)
editSessions = state.editSessions.map (state) -> EditSession.deserialize(state, project)
editor.pushEditSession(editSession) for editSession in editSessions
editor.setActiveEditSessionIndex(state.activeEditSessionIndex)
editor.isFocused = state.isFocused
editor
initialize: (editSessionOrOptions) ->
if editSessionOrOptions instanceof EditSession
editSession = editSessionOrOptions
else
{editSession, @mini} = (editSessionOrOptions ? {})
initialize: ({editSession, @mini, deserializing} = {}) ->
requireStylesheet 'editor.css'
@id = Editor.nextEditorId++
@@ -76,8 +70,6 @@ class Editor extends View
@handleEvents()
@cursorViews = []
@selectionViews = []
@editSessions = []
@closedEditSessions = []
@pendingChanges = []
@newCursors = []
@newSelections = []
@@ -91,18 +83,8 @@ class Editor extends View
tabLength: 2
softTabs: true
)
else if not deserializing
throw new Error("Editor must be constructed with an 'editSession' or 'mini: true' param")
serialize: ->
@saveScrollPositionForActiveEditSession()
deserializer: "Editor"
editSessions: @editSessions.map (session) -> session.serialize()
activeEditSessionIndex: @getActiveEditSessionIndex()
isFocused: @isFocused
copy: ->
Editor.deserialize(@serialize(), rootView)
else
throw new Error("Must supply an EditSession or mini: true")
bindKeys: ->
editorBindings =
@@ -155,9 +137,6 @@ class Editor extends View
'core:select-down': @selectDown
'core:select-to-top': @selectToTop
'core:select-to-bottom': @selectToBottom
'core:close': @destroyActiveEditSession
'editor:save': @save
'editor:save-as': @saveAs
'editor:newline-below': @insertNewlineBelow
'editor:newline-above': @insertNewlineAbove
'editor:toggle-soft-tabs': @toggleSoftTabs
@@ -167,32 +146,14 @@ class Editor extends View
'editor:fold-current-row': @foldCurrentRow
'editor:unfold-current-row': @unfoldCurrentRow
'editor:fold-selection': @foldSelection
'editor:split-left': @splitLeft
'editor:split-right': @splitRight
'editor:split-up': @splitUp
'editor:split-down': @splitDown
'editor:show-next-buffer': @loadNextEditSession
'editor:show-buffer-1': => @setActiveEditSessionIndex(0) if @editSessions[0]
'editor:show-buffer-2': => @setActiveEditSessionIndex(1) if @editSessions[1]
'editor:show-buffer-3': => @setActiveEditSessionIndex(2) if @editSessions[2]
'editor:show-buffer-4': => @setActiveEditSessionIndex(3) if @editSessions[3]
'editor:show-buffer-5': => @setActiveEditSessionIndex(4) if @editSessions[4]
'editor:show-buffer-6': => @setActiveEditSessionIndex(5) if @editSessions[5]
'editor:show-buffer-7': => @setActiveEditSessionIndex(6) if @editSessions[6]
'editor:show-buffer-8': => @setActiveEditSessionIndex(7) if @editSessions[7]
'editor:show-buffer-9': => @setActiveEditSessionIndex(8) if @editSessions[8]
'editor:show-previous-buffer': @loadPreviousEditSession
'editor:toggle-line-comments': @toggleLineCommentsInSelection
'editor:log-cursor-scope': @logCursorScope
'editor:checkout-head-revision': @checkoutHead
'editor:close-other-edit-sessions': @destroyInactiveEditSessions
'editor:close-all-edit-sessions': @destroyAllEditSessions
'editor:select-grammar': @selectGrammar
'editor:copy-path': @copyPathToPasteboard
'editor:move-line-up': @moveLineUp
'editor:move-line-down': @moveLineDown
'editor:duplicate-line': @duplicateLine
'editor:undo-close-session': @undoDestroySession
'editor:toggle-indent-guide': => config.set('editor.showIndentGuide', !config.get('editor.showIndentGuide'))
'editor:save-debug-snapshot': @saveDebugSnapshot
@@ -343,7 +304,7 @@ class Editor extends View
checkoutHead: -> @getBuffer().checkoutHead()
setText: (text) -> @getBuffer().setText(text)
getText: -> @getBuffer().getText()
getPath: -> @getBuffer().getPath()
getPath: -> @activeEditSession?.getPath()
getLineCount: -> @getBuffer().getLineCount()
getLastBufferRow: -> @getBuffer().getLastRow()
getTextInRange: (range) -> @getBuffer().getTextInRange(range)
@@ -367,13 +328,11 @@ class Editor extends View
false
@hiddenInput.on 'focus', =>
rootView?.editorFocused(this)
@isFocused = true
@addClass 'is-focused'
@hiddenInput.on 'focusout', =>
@isFocused = false
@autosave() if config.get "editor.autosave"
@removeClass 'is-focused'
@underlayer.on 'click', (e) =>
@@ -436,10 +395,7 @@ class Editor extends View
e.pageX = @renderedLines.offset().left
onMouseDown(e)
@subscribe syntax, 'grammars-loaded', =>
@reloadGrammar()
for session in @editSessions
session.reloadGrammar() unless session is @activeEditSession
@subscribe syntax, 'grammars-loaded', => @reloadGrammar()
@scrollView.on 'scroll', =>
if @scrollView.scrollLeft() == 0
@@ -481,86 +437,16 @@ class Editor extends View
@trigger 'editor:attached', [this]
edit: (editSession) ->
index = @editSessions.indexOf(editSession)
index = @pushEditSession(editSession) if index == -1
@setActiveEditSessionIndex(index)
pushEditSession: (editSession) ->
index = @editSessions.length
@editSessions.push(editSession)
@closedEditSessions = @closedEditSessions.filter ({path})->
path isnt editSession.getPath()
editSession.on 'destroyed', => @editSessionDestroyed(editSession)
@trigger 'editor:edit-session-added', [editSession, index]
index
getBuffer: -> @activeEditSession.buffer
undoDestroySession: ->
return unless @closedEditSessions.length > 0
{path, index} = @closedEditSessions.pop()
rootView.open(path)
activeIndex = @getActiveEditSessionIndex()
@moveEditSessionToIndex(activeIndex, index) if index < activeIndex
destroyActiveEditSession: ->
@destroyEditSessionIndex(@getActiveEditSessionIndex())
destroyEditSessionIndex: (index, callback) ->
return if @mini
editSession = @editSessions[index]
destroySession = =>
path = editSession.getPath()
@closedEditSessions.push({path, index}) if path
editSession.destroy()
callback?(index)
if editSession.isModified() and not editSession.hasEditors()
@promptToSaveDirtySession(editSession, destroySession)
else
destroySession()
destroyInactiveEditSessions: ->
destroyIndex = (index) =>
index++ if index is @getActiveEditSessionIndex()
@destroyEditSessionIndex(index, destroyIndex) if @editSessions[index]
destroyIndex(0)
destroyAllEditSessions: ->
destroyIndex = (index) =>
@destroyEditSessionIndex(index, destroyIndex) if @editSessions[index]
destroyIndex(0)
editSessionDestroyed: (editSession) ->
index = @editSessions.indexOf(editSession)
@loadPreviousEditSession() if index is @getActiveEditSessionIndex() and @editSessions.length > 1
_.remove(@editSessions, editSession)
@trigger 'editor:edit-session-removed', [editSession, index]
@remove() if @editSessions.length is 0
loadNextEditSession: ->
nextIndex = (@getActiveEditSessionIndex() + 1) % @editSessions.length
@setActiveEditSessionIndex(nextIndex)
loadPreviousEditSession: ->
previousIndex = @getActiveEditSessionIndex() - 1
previousIndex = @editSessions.length - 1 if previousIndex < 0
@setActiveEditSessionIndex(previousIndex)
getActiveEditSessionIndex: ->
return index for session, index in @editSessions when session == @activeEditSession
setActiveEditSessionIndex: (index) ->
throw new Error("Edit session not found") unless @editSessions[index]
return if editSession is @activeEditSession
if @activeEditSession
@autosave() if config.get "editor.autosave"
@saveScrollPositionForActiveEditSession()
@activeEditSession.off(".editor")
@activeEditSession = @editSessions[index]
@activeEditSession = editSession
return unless @activeEditSession?
@activeEditSession.setVisible(true)
@activeEditSession.on "contents-conflicted.editor", =>
@@ -571,11 +457,18 @@ class Editor extends View
@trigger 'editor:path-changed'
@trigger 'editor:path-changed'
@trigger 'editor:active-edit-session-changed', [@activeEditSession, index]
@resetDisplay()
if @attached and @activeEditSession.buffer.isInConflict()
setTimeout(( =>@showBufferConflictAlert(@activeEditSession)), 0) # Display after editSession has a chance to display
_.defer => @showBufferConflictAlert(@activeEditSession) # Display after editSession has a chance to display
getModel: ->
@activeEditSession
setModel: (editSession) ->
@edit(editSession)
getBuffer: -> @activeEditSession.buffer
showBufferConflictAlert: (editSession) ->
atom.confirm(
@@ -585,30 +478,6 @@ class Editor extends View
"Cancel"
)
moveEditSessionToIndex: (fromIndex, toIndex) ->
return if fromIndex is toIndex
editSession = @editSessions.splice(fromIndex, 1)
@editSessions.splice(toIndex, 0, editSession[0])
@trigger 'editor:edit-session-order-changed', [editSession, fromIndex, toIndex]
@setActiveEditSessionIndex(toIndex)
moveEditSessionToEditor: (fromIndex, toEditor, toIndex) ->
fromEditSession = @editSessions[fromIndex]
toEditSession = fromEditSession.copy()
@destroyEditSessionIndex(fromIndex)
toEditor.edit(toEditSession)
toEditor.moveEditSessionToIndex(toEditor.getActiveEditSessionIndex(), toIndex)
activateEditSessionForPath: (path) ->
for editSession, index in @editSessions
if editSession.buffer.getPath() == path
@setActiveEditSessionIndex(index)
return @activeEditSession
false
getOpenBufferPaths: ->
editSession.buffer.getPath() for editSession in @editSessions when editSession.buffer.getPath()?
scrollTop: (scrollTop, options={}) ->
return @cachedScrollTop or 0 unless scrollTop?
maxScrollTop = @verticalScrollbar.prop('scrollHeight') - @verticalScrollbar.height()
@@ -723,22 +592,6 @@ class Editor extends View
@removeClass 'soft-wrap'
$(window).off 'resize', @_setSoftWrapColumn
save: (session=@activeEditSession, onSuccess) ->
if @getPath()
session.save()
onSuccess?()
else
@saveAs(session, onSuccess)
saveAs: (session=@activeEditSession, onSuccess) ->
atom.showSaveDialog (path) =>
if path
session.saveAs(path)
onSuccess?()
autosave: ->
@save() if @getPath()?
setFontSize: (fontSize) ->
headTag = $("head")
styleTag = headTag.find("style.font-size")
@@ -781,54 +634,33 @@ class Editor extends View
@updateLayerDimensions()
@requestDisplayUpdate()
newSplitEditor: (editSession) ->
new Editor { editSession: editSession ? @activeEditSession.copy() }
splitLeft: (items...) ->
@pane()?.splitLeft(items...).activeView
splitLeft: (editSession) ->
@pane()?.splitLeft(@newSplitEditor(editSession)).wrappedView
splitRight: (items...) ->
@pane()?.splitRight(items...).activeView
splitRight: (editSession) ->
@pane()?.splitRight(@newSplitEditor(editSession)).wrappedView
splitUp: (items...) ->
@pane()?.splitUp(items...).activeView
splitUp: (editSession) ->
@pane()?.splitUp(@newSplitEditor(editSession)).wrappedView
splitDown: (editSession) ->
@pane()?.splitDown(@newSplitEditor(editSession)).wrappedView
splitDown: (items...) ->
@pane()?.splitDown(items...).activeView
pane: ->
@parent('.pane').view()
promptToSaveDirtySession: (session, callback) ->
path = session.getPath()
filename = if path then fs.base(path) else "untitled buffer"
atom.confirm(
"'#{filename}' has changes, do you want to save them?"
"Your changes will be lost if you don't save them"
"Save", => @save(session, callback),
"Cancel", null
"Don't Save", callback
)
@closest('.pane').view()
remove: (selector, keepData) ->
return super if keepData or @removed
@trigger 'editor:will-be-removed'
if @pane() then @pane().remove() else super
super
rootView?.focus()
afterRemove: ->
@removed = true
@destroyEditSessions()
@activeEditSession?.destroy()
$(window).off(".editor-#{@id}")
$(document).off(".editor-#{@id}")
getEditSessions: ->
new Array(@editSessions...)
destroyEditSessions: ->
for session in @getEditSessions()
session.destroy()
getCursorView: (index) ->
index ?= @cursorViews.length - 1
@cursorViews[index]
@@ -933,7 +765,8 @@ class Editor extends View
@pendingDisplayUpdate = false
updateDisplay: (options={}) ->
return unless @attached
return unless @attached and @activeEditSession
return if @activeEditSession.destroyed
@updateRenderedLines()
@highlightCursorLine()
@updateCursorViews()

View File

@@ -28,6 +28,7 @@ class Git
statuses: null
upstream: null
statusTask: null
constructor: (path, options={}) ->
@statuses = {}
@@ -58,7 +59,11 @@ class Git
@path ?= fs.absolute(@getRepo().getPath())
destroy: ->
@statusTask?.abort()
if @statusTask?
@statusTask.abort()
@statusTask.off()
@statusTask = null
@getRepo().destroy()
@repo = null
@unsubscribe()
@@ -130,8 +135,16 @@ class Git
@getRepo().isSubmodule(@relativize(path))
refreshStatus: ->
@statusTask = new RepositoryStatusTask(this)
@statusTask.start()
if @statusTask?
@statusTask.off()
@statusTask.one 'task-completed', =>
@statusTask = null
@refreshStatus()
else
@statusTask = new RepositoryStatusTask(this)
@statusTask.one 'task-completed', =>
@statusTask = null
@statusTask.start()
getDirectoryStatus: (directoryPath) ->
directoryPath = "#{directoryPath}/"

View File

@@ -1,4 +1,6 @@
'body':
'meta-s': 'core:save'
'meta-S': 'core:save-as'
'enter': 'core:confirm'
'escape': 'core:cancel'
'meta-w': 'core:close'
@@ -30,6 +32,26 @@
'ctrl-tab': 'window:focus-next-pane'
'ctrl-meta-f': 'window:toggle-full-screen'
'ctrl-|': 'pane:split-right'
'ctrl-w v': 'pane:split-right'
'ctrl--': 'pane:split-down'
'ctrl-w s': 'pane:split-down'
'meta-{': 'pane:show-previous-item'
'meta-}': 'pane:show-next-item'
'alt-meta-left': 'pane:show-previous-item'
'alt-meta-right': 'pane:show-next-item'
'meta-1': 'pane:show-item-1'
'meta-2': 'pane:show-item-2'
'meta-3': 'pane:show-item-3'
'meta-4': 'pane:show-item-4'
'meta-5': 'pane:show-item-5'
'meta-6': 'pane:show-item-6'
'meta-7': 'pane:show-item-7'
'meta-8': 'pane:show-item-8'
'meta-9': 'pane:show-item-9'
'meta-T': 'pane:reopen-closed-item'
'.tool-panel':
'meta-escape': 'tool-panel:unfocus'
'escape': 'core:close'

View File

@@ -1,9 +1,4 @@
'body':
'meta-T': 'editor:undo-close-session'
'.editor':
'meta-s': 'editor:save'
'meta-S': 'editor:save-as'
'enter': 'editor:newline'
'meta-enter': 'editor:newline-below'
'meta-shift-enter': 'editor:newline-above'
@@ -15,26 +10,9 @@
'ctrl-{': 'editor:fold-all'
'ctrl-}': 'editor:unfold-all'
'alt-meta-ctrl-f': 'editor:fold-selection'
'ctrl-|': 'editor:split-right'
'ctrl-w v': 'editor:split-right'
'ctrl--': 'editor:split-down'
'ctrl-w s': 'editor:split-down'
'shift-tab': 'editor:outdent-selected-rows'
'meta-[': 'editor:outdent-selected-rows'
'meta-]': 'editor:indent-selected-rows'
'meta-{': 'editor:show-previous-buffer'
'meta-}': 'editor:show-next-buffer'
'alt-meta-left': 'editor:show-previous-buffer'
'alt-meta-right': 'editor:show-next-buffer'
'meta-1': 'editor:show-buffer-1'
'meta-2': 'editor:show-buffer-2'
'meta-3': 'editor:show-buffer-3'
'meta-4': 'editor:show-buffer-4'
'meta-5': 'editor:show-buffer-5'
'meta-6': 'editor:show-buffer-6'
'meta-7': 'editor:show-buffer-7'
'meta-8': 'editor:show-buffer-8'
'meta-9': 'editor:show-buffer-9'
'meta-/': 'editor:toggle-line-comments'
'ctrl-W': 'editor:select-word'
'meta-alt-p': 'editor:log-cursor-scope'

View File

@@ -2,7 +2,7 @@ $ = require 'jquery'
{View} = require 'space-pen'
module.exports =
class PaneGrid extends View
class PaneAxis extends View
@deserialize: ({children}) ->
childViews = children.map (child) -> deserialize(child)
new this(childViews)

View File

@@ -1,9 +1,9 @@
$ = require 'jquery'
_ = require 'underscore'
PaneGrid = require 'pane-grid'
PaneAxis = require 'pane-axis'
module.exports =
class PaneColumn extends PaneGrid
class PaneColumn extends PaneAxis
@content: ->
@div class: 'column'

View File

@@ -0,0 +1,97 @@
{View} = require 'space-pen'
Pane = require 'pane'
$ = require 'jquery'
module.exports =
class PaneContainer extends View
registerDeserializer(this)
@deserialize: ({root}) ->
container = new PaneContainer
container.append(deserialize(root)) if root
container
@content: ->
@div id: 'panes'
initialize: ->
@destroyedItemStates = []
serialize: ->
deserializer: 'PaneContainer'
root: @getRoot()?.serialize()
focusNextPane: ->
panes = @getPanes()
if panes.length > 1
currentIndex = panes.indexOf(@getFocusedPane())
nextIndex = (currentIndex + 1) % panes.length
panes[nextIndex].focus()
true
else
false
makeNextPaneActive: ->
panes = @getPanes()
currentIndex = panes.indexOf(@getActivePane())
nextIndex = (currentIndex + 1) % panes.length
panes[nextIndex].makeActive()
reopenItem: ->
if lastItemState = @destroyedItemStates.pop()
if activePane = @getActivePane()
activePane.showItem(deserialize(lastItemState))
true
else
@append(new Pane(deserialize(lastItemState)))
itemDestroyed: (item) ->
state = item.serialize?()
state.uri ?= item.getUri?()
@destroyedItemStates.push(state) if state?
itemAdded: (item) ->
itemUri = item.getUri?()
@destroyedItemStates = @destroyedItemStates.filter (itemState) ->
itemState.uri isnt itemUri
getRoot: ->
@children().first().view()
saveAll: ->
pane.saveItems() for pane in @getPanes()
getPanes: ->
@find('.pane').views()
indexOfPane: (pane) ->
@getPanes().indexOf(pane.view())
paneAtIndex: (index) ->
@getPanes()[index]
eachPane: (callback) ->
callback(pane) for pane in @getPanes()
paneAttached = (e) -> callback($(e.target).view())
@on 'pane:attached', paneAttached
cancel: => @off 'pane:attached', paneAttached
getFocusedPane: ->
@find('.pane:has(:focus)').view()
getActivePane: ->
@find('.pane.active').view() ? @find('.pane:first').view()
getActivePaneItem: ->
@getActivePane()?.activeItem
getActiveView: ->
@getActivePane()?.activeView
adjustPaneDimensions: ->
if root = @getRoot()
root.css(width: '100%', height: '100%', top: 0, left: 0)
root.adjustDimensions()
afterAttach: ->
@adjustPaneDimensions()

View File

@@ -1,9 +1,9 @@
$ = require 'jquery'
_ = require 'underscore'
PaneGrid = require 'pane-grid'
PaneAxis = require 'pane-axis'
module.exports =
class PaneRow extends PaneGrid
class PaneRow extends PaneAxis
@content: ->
@div class: 'row'

View File

@@ -1,4 +1,6 @@
{View} = require 'space-pen'
$ = require 'jquery'
_ = require 'underscore'
PaneRow = require 'pane-row'
PaneColumn = require 'pane-column'
@@ -6,58 +8,314 @@ module.exports =
class Pane extends View
@content: (wrappedView) ->
@div class: 'pane', =>
@subview 'wrappedView', wrappedView if wrappedView
@div class: 'item-views', outlet: 'itemViews'
@deserialize: ({wrappedView}) ->
new Pane(deserialize(wrappedView))
@deserialize: ({items, focused, activeItemUri}) ->
pane = new Pane(items.map((item) -> deserialize(item))...)
pane.showItemForUri(activeItemUri) if activeItemUri
pane.focusOnAttach = true if focused
pane
activeItem: null
items: null
initialize: (@items...) ->
@viewsByClassName = {}
@showItem(@items[0])
@command 'core:close', @destroyActiveItem
@command 'core:save', @saveActiveItem
@command 'core:save-as', @saveActiveItemAs
@command 'pane:save-items', @saveItems
@command 'pane:show-next-item', @showNextItem
@command 'pane:show-previous-item', @showPreviousItem
@command 'pane:show-item-1', => @showItemAtIndex(0)
@command 'pane:show-item-2', => @showItemAtIndex(1)
@command 'pane:show-item-3', => @showItemAtIndex(2)
@command 'pane:show-item-4', => @showItemAtIndex(3)
@command 'pane:show-item-5', => @showItemAtIndex(4)
@command 'pane:show-item-6', => @showItemAtIndex(5)
@command 'pane:show-item-7', => @showItemAtIndex(6)
@command 'pane:show-item-8', => @showItemAtIndex(7)
@command 'pane:show-item-9', => @showItemAtIndex(8)
@command 'pane:split-left', => @splitLeft()
@command 'pane:split-right', => @splitRight()
@command 'pane:split-up', => @splitUp()
@command 'pane:split-down', => @splitDown()
@command 'pane:close', => @destroyItems()
@command 'pane:close-other-items', => @destroyInactiveItems()
@on 'focus', => @activeView.focus(); false
@on 'focusin', => @makeActive()
@on 'focusout', => @autosaveActiveItem()
afterAttach: (onDom) ->
if @focusOnAttach and onDom
@focusOnAttach = null
@focus()
return if @attached
@attached = true
@trigger 'pane:attached'
makeActive: ->
for pane in @getContainer().getPanes() when pane isnt this
pane.makeInactive()
wasActive = @isActive()
@addClass('active')
@trigger 'pane:became-active' unless wasActive
makeInactive: ->
@removeClass('active')
isActive: ->
@hasClass('active')
getItems: ->
new Array(@items...)
showNextItem: =>
index = @getActiveItemIndex()
if index < @items.length - 1
@showItemAtIndex(index + 1)
else
@showItemAtIndex(0)
showPreviousItem: =>
index = @getActiveItemIndex()
if index > 0
@showItemAtIndex(index - 1)
else
@showItemAtIndex(@items.length - 1)
getActiveItemIndex: ->
@items.indexOf(@activeItem)
showItemAtIndex: (index) ->
@showItem(@itemAtIndex(index))
itemAtIndex: (index) ->
@items[index]
showItem: (item) ->
return if !item? or item is @activeItem
if @activeItem
@activeItem.off? 'title-changed', @activeItemTitleChanged
@autosaveActiveItem()
isFocused = @is(':has(:focus)')
@addItem(item)
item.on? 'title-changed', @activeItemTitleChanged
view = @viewForItem(item)
@itemViews.children().not(view).hide()
@itemViews.append(view) unless view.parent().is(@itemViews)
view.show()
view.focus() if isFocused
@activeItem = item
@activeView = view
@trigger 'pane:active-item-changed', [item]
activeItemTitleChanged: =>
@trigger 'pane:active-item-title-changed'
addItem: (item) ->
return if _.include(@items, item)
index = @getActiveItemIndex() + 1
@items.splice(index, 0, item)
@getContainer().itemAdded(item)
@trigger 'pane:item-added', [item, index]
item
destroyActiveItem: =>
@destroyItem(@activeItem)
false
destroyItem: (item) ->
container = @getContainer()
reallyDestroyItem = =>
@removeItem(item)
container.itemDestroyed(item)
item.destroy?()
@autosaveItem(item)
if item.isModified?()
@promptToSaveItem(item, reallyDestroyItem)
else
reallyDestroyItem()
destroyItems: ->
@destroyItem(item) for item in @getItems()
destroyInactiveItems: ->
@destroyItem(item) for item in @getItems() when item isnt @activeItem
promptToSaveItem: (item, nextAction) ->
uri = item.getUri()
atom.confirm(
"'#{item.getTitle()}' has changes, do you want to save them?"
"Your changes will be lost if close this item without saving."
"Save", => @saveItem(item, nextAction)
"Cancel", null
"Don't Save", nextAction
)
saveActiveItem: =>
@saveItem(@activeItem)
saveActiveItemAs: =>
@saveItemAs(@activeItem)
saveItem: (item, nextAction) ->
if item.getUri?()
item.save()
nextAction?()
else
@saveItemAs(item, nextAction)
saveItemAs: (item, nextAction) ->
return unless item.saveAs?
atom.showSaveDialog (path) =>
if path
item.saveAs(path)
nextAction?()
saveItems: =>
@saveItem(item) for item in @getItems()
autosaveActiveItem: ->
@autosaveItem(@activeItem)
autosaveItem: (item) ->
@saveItem(item) if config.get('core.autosave') and item.getUri?()
removeItem: (item) ->
index = @items.indexOf(item)
return if index == -1
@showNextItem() if item is @activeItem and @items.length > 1
_.remove(@items, item)
@cleanupItemView(item)
@trigger 'pane:item-removed', [item, index]
moveItem: (item, newIndex) ->
oldIndex = @items.indexOf(item)
@items.splice(oldIndex, 1)
@items.splice(newIndex, 0, item)
@trigger 'pane:item-moved', [item, newIndex]
moveItemToPane: (item, pane, index) ->
@removeItem(item)
pane.addItem(item, index)
itemForUri: (uri) ->
_.detect @items, (item) -> item.getUri?() is uri
showItemForUri: (uri) ->
@showItem(@itemForUri(uri))
cleanupItemView: (item) ->
if item instanceof $
viewToRemove = item
else
viewClass = item.getViewClass()
otherItemsForView = @items.filter (i) -> i.getViewClass?() is viewClass
unless otherItemsForView.length
viewToRemove = @viewsByClassName[viewClass.name]
viewToRemove?.setModel(null)
delete @viewsByClassName[viewClass.name]
if @items.length > 0
viewToRemove?.remove()
else
@remove()
viewForItem: (item) ->
if item instanceof $
item
else
viewClass = item.getViewClass()
if view = @viewsByClassName[viewClass.name]
view.setModel(item)
else
view = @viewsByClassName[viewClass.name] = new viewClass(item)
view
viewForActiveItem: ->
@viewForItem(@activeItem)
serialize: ->
deserializer: "Pane"
wrappedView: @wrappedView?.serialize()
focused: @is(':has(:focus)')
activeItemUri: @activeItem.getUri?() if typeof @activeItem.serialize is 'function'
items: _.compact(@getItems().map (item) -> item.serialize?())
adjustDimensions: -> # do nothing
horizontalGridUnits: ->
1
horizontalGridUnits: -> 1
verticalGridUnits: ->
1
verticalGridUnits: -> 1
splitUp: (view) ->
@split(view, 'column', 'before')
splitUp: (items...) ->
@split(items, 'column', 'before')
splitDown: (view) ->
@split(view, 'column', 'after')
splitDown: (items...) ->
@split(items, 'column', 'after')
splitLeft: (view) ->
@split(view, 'row', 'before')
splitLeft: (items...) ->
@split(items, 'row', 'before')
splitRight: (view) ->
@split(view, 'row', 'after')
splitRight: (items...) ->
@split(items, 'row', 'after')
split: (view, axis, side) ->
split: (items, axis, side) ->
unless @parent().hasClass(axis)
@buildPaneAxis(axis)
.insertBefore(this)
.append(@detach())
pane = new Pane(view)
items = [@copyActiveItem()] unless items.length
pane = new Pane(items...)
this[side](pane)
rootView.adjustPaneDimensions()
view.focus?()
@getContainer().adjustPaneDimensions()
pane.focus()
pane
remove: (selector, keepData) ->
return super if keepData
# find parent elements before removing from dom
parentAxis = @parent('.row, .column')
super
if parentAxis.children().length == 1
sibling = parentAxis.children().detach()
parentAxis.replaceWith(sibling)
rootView.adjustPaneDimensions()
buildPaneAxis: (axis) ->
switch axis
when 'row' then new PaneRow
when 'column' then new PaneColumn
getContainer: ->
@closest('#panes').view()
copyActiveItem: ->
deserialize(@activeItem.serialize())
remove: (selector, keepData) ->
return super if keepData
# find parent elements before removing from dom
container = @getContainer()
parentAxis = @parent('.row, .column')
if @is(':has(:focus)')
container.focusNextPane() or rootView?.focus()
else if @isActive()
container.makeNextPaneActive()
super
if parentAxis.children().length == 1
sibling = parentAxis.children()
siblingFocused = sibling.is(':has(:focus)')
sibling.detach()
parentAxis.replaceWith(sibling)
sibling.focus() if siblingFocused
container.adjustPaneDimensions()
container.trigger 'pane:removed', [this]
afterRemove: ->
item.destroy?() for item in @getItems()

View File

@@ -95,10 +95,10 @@ class Project
getSoftWrap: -> @softWrap
setSoftWrap: (@softWrap) ->
buildEditSessionForPath: (filePath, editSessionOptions={}) ->
@buildEditSession(@bufferForPath(filePath), editSessionOptions)
buildEditSession: (filePath, editSessionOptions={}) ->
@buildEditSessionForBuffer(@bufferForPath(filePath), editSessionOptions)
buildEditSession: (buffer, editSessionOptions) ->
buildEditSessionForBuffer: (buffer, editSessionOptions) ->
options = _.extend(@defaultEditSessionOptions(), editSessionOptions)
options.project = this
options.buffer = buffer

View File

@@ -10,28 +10,29 @@ Project = require 'project'
Pane = require 'pane'
PaneColumn = require 'pane-column'
PaneRow = require 'pane-row'
PaneContainer = require 'pane-container'
EditSession = require 'edit-session'
module.exports =
class RootView extends View
registerDeserializers(this, Pane, PaneRow, PaneColumn, Editor)
@version: 1
@configDefaults:
ignoredNames: [".git", ".svn", ".DS_Store"]
disabledPackages: []
@content: ->
@content: ({panes}={}) ->
@div id: 'root-view', =>
@div id: 'horizontal', outlet: 'horizontal', =>
@div id: 'vertical', outlet: 'vertical', =>
@div id: 'panes', outlet: 'panes'
@subview 'panes', panes ? new PaneContainer
@deserialize: ({ panesViewState, packageStates, projectPath }) ->
atom.atomPackageStates = packageStates ? {}
rootView = new RootView
rootView.setRootPane(deserialize(panesViewState)) if panesViewState
rootView
title: null
@deserialize: ({ panes, packages, projectPath }) ->
atom.atomPackageStates = packages ? {}
panes = deserialize(panes) if panes?.deserializer is 'PaneContainer'
new RootView({panes})
initialize: ->
@command 'toggle-dev-tools', => atom.toggleDevTools()
@@ -39,12 +40,11 @@ class RootView extends View
@subscribe $(window), 'focus', (e) =>
@handleFocus(e) if document.activeElement is document.body
@on 'root-view:active-path-changed', (e, path) =>
if path
project.setPath(path) unless project.getRootDirectory()
@setTitle(fs.base(path))
else
@setTitle("untitled")
project.on 'path-changed', => @updateTitle()
@on 'pane:became-active', => @updateTitle()
@on 'pane:active-item-changed', '.active.pane', => @updateTitle()
@on 'pane:removed', => @updateTitle() unless @getActivePane()
@on 'pane:active-item-title-changed', '.active.pane', => @updateTitle()
@command 'window:increase-font-size', =>
config.set("editor.fontSize", config.get("editor.fontSize") + 1)
@@ -53,26 +53,31 @@ class RootView extends View
fontSize = config.get "editor.fontSize"
config.set("editor.fontSize", fontSize - 1) if fontSize > 1
@command 'window:focus-next-pane', => @focusNextPane()
@command 'window:save-all', => @saveAll()
@command 'window:toggle-invisibles', =>
config.set("editor.showInvisibles", !config.get("editor.showInvisibles"))
@command 'window:toggle-ignored-files', =>
config.set("core.hideGitIgnoredFiles", not config.core.hideGitIgnoredFiles)
@command 'window:toggle-auto-indent', =>
config.set("editor.autoIndent", !config.get("editor.autoIndent"))
@command 'window:toggle-auto-indent-on-paste', =>
config.set("editor.autoIndentOnPaste", !config.get("editor.autoIndentOnPaste"))
@command 'pane:reopen-closed-item', =>
@panes.reopenItem()
serialize: ->
version: RootView.version
deserializer: 'RootView'
panesViewState: @panes.children().view()?.serialize()
packageStates: atom.serializeAtomPackages()
panes: @panes.serialize()
packages: atom.serializeAtomPackages()
handleFocus: (e) ->
if @getActiveEditor()
@getActiveEditor().focus()
if @getActivePane()
@getActivePane().focus()
false
else
@setTitle(null)
@@ -92,118 +97,57 @@ class RootView extends View
open: (path, options = {}) ->
changeFocus = options.changeFocus ? true
allowActiveEditorChange = options.allowActiveEditorChange ? false
unless editSession = @openInExistingEditor(path, allowActiveEditorChange, changeFocus)
editSession = project.buildEditSessionForPath(path)
editor = new Editor({editSession})
pane = new Pane(editor)
@panes.append(pane)
if changeFocus
editor.focus()
path = project.resolve(path) if path?
if activePane = @getActivePane()
if editSession = activePane.itemForUri(path)
activePane.showItem(editSession)
else
@makeEditorActive(editor, changeFocus)
editSession = project.buildEditSession(path)
activePane.showItem(editSession)
else
editSession = project.buildEditSession(path)
activePane = new Pane(editSession)
@panes.append(activePane)
activePane.focus() if changeFocus
editSession
openInExistingEditor: (path, allowActiveEditorChange, changeFocus) ->
if activeEditor = @getActiveEditor()
activeEditor.focus() if changeFocus
path = project.resolve(path) if path
if editSession = activeEditor.activateEditSessionForPath(path)
return editSession
if allowActiveEditorChange
for editor in @getEditors()
if editSession = editor.activateEditSessionForPath(path)
@makeEditorActive(editor, changeFocus)
return editSession
editSession = project.buildEditSessionForPath(path)
activeEditor.edit(editSession)
editSession
editorFocused: (editor) ->
@makeEditorActive(editor) if @panes.containsElement(editor)
makeEditorActive: (editor, focus) ->
if focus
editor.focus()
return
previousActiveEditor = @panes.find('.editor.active').view()
previousActiveEditor?.removeClass('active').off('.root-view')
editor.addClass('active')
if not editor.mini
editor.on 'editor:path-changed.root-view', =>
@trigger 'root-view:active-path-changed', editor.getPath()
if not previousActiveEditor or editor.getPath() != previousActiveEditor.getPath()
@trigger 'root-view:active-path-changed', editor.getPath()
activeKeybindings: ->
keymap.bindingsForElement(document.activeElement)
getTitle: ->
@title or "untitled"
updateTitle: ->
if projectPath = project.getPath()
if item = @getActivePaneItem()
@setTitle("#{item.getTitle()} - #{projectPath}")
else
@setTitle(projectPath)
else
@setTitle('untitled')
setTitle: (title) ->
projectPath = project.getPath()
if not projectPath
@title = "untitled"
else if title
@title = "#{title} #{projectPath}"
else
@title = projectPath
@updateWindowTitle()
updateWindowTitle: ->
document.title = @title
document.title = title
getEditors: ->
@panes.find('.pane > .editor').map(-> $(this).view()).toArray()
@panes.find('.pane > .item-views > .editor').map(-> $(this).view()).toArray()
getModifiedBuffers: ->
modifiedBuffers = []
for editor in @getEditors()
for session in editor.editSessions
modifiedBuffers.push session.buffer if session.buffer.isModified()
for pane in @getPanes()
for item in pane.getItems() when item instanceof EditSession
modifiedBuffers.push item.buffer if item.buffer.isModified()
modifiedBuffers
getOpenBufferPaths: ->
_.uniq(_.flatten(@getEditors().map (editor) -> editor.getOpenBufferPaths()))
getActiveEditor: ->
if (editor = @panes.find('.editor.active')).length
editor.view()
else
@panes.find('.editor:first').view()
getActivePane: ->
@panes.getActivePane()
getActiveEditSession: ->
@getActiveEditor()?.activeEditSession
getActivePaneItem: ->
@panes.getActivePaneItem()
focusNextPane: ->
panes = @panes.find('.pane')
currentIndex = panes.toArray().indexOf(@getFocusedPane()[0])
nextIndex = (currentIndex + 1) % panes.length
panes.eq(nextIndex).view().wrappedView.focus()
getActiveView: ->
@panes.getActiveView()
getFocusedPane: ->
@panes.find('.pane:has(:focus)')
setRootPane: (pane) ->
@panes.empty()
@panes.append(pane)
@adjustPaneDimensions()
adjustPaneDimensions: ->
rootPane = @panes.children().first().view()
rootPane?.css(width: '100%', height: '100%', top: 0, left: 0)
rootPane?.adjustDimensions()
focusNextPane: -> @panes.focusNextPane()
getFocusedPane: -> @panes.getFocusedPane()
remove: ->
editor.remove() for editor in @getEditors()
@@ -211,7 +155,16 @@ class RootView extends View
super
saveAll: ->
editor.save() for editor in @getEditors()
@panes.saveAll()
eachPane: (callback) ->
@panes.eachPane(callback)
getPanes: ->
@panes.getPanes()
indexOfPane: (pane) ->
@panes.indexOfPane(pane)
eachEditor: (callback) ->
callback(editor) for editor in @getEditors()
@@ -223,10 +176,3 @@ class RootView extends View
eachBuffer: (callback) ->
project.eachBuffer(callback)
indexOfPane: (pane) ->
index = -1
for p, idx in @panes.find('.pane')
if pane.is(p)
index = idx
break
index

View File

@@ -14,7 +14,9 @@ class SortableList extends View
@on 'drop', '.sortable', @onDrop
onDragStart: (event) =>
return false if !@shouldAllowDrag(event)
unless @shouldAllowDrag(event)
event.preventDefault()
return
el = @getSortableElement(event)
el.addClass 'is-dragging'
@@ -45,9 +47,8 @@ class SortableList extends View
true
getDroppedElement: (event) ->
idx = event.originalEvent.dataTransfer.getData 'sortable-index'
@find ".sortable:eq(#{idx})"
index = event.originalEvent.dataTransfer.getData('sortable-index')
@find(".sortable:eq(#{index})")
getSortableElement: (event) ->
el = $(event.target)
if !el.hasClass('sortable') then el.closest('.sortable') else el
$(event.target).closest('.sortable')

View File

@@ -151,8 +151,16 @@ window.registerDeserializers = (args...) ->
window.registerDeserializer = (klass) ->
deserializers[klass.name] = klass
window.unregisterDeserializer = (klass) ->
delete deserializers[klass.name]
window.deserialize = (state) ->
deserializers[state?.deserializer]?.deserialize(state)
if deserializer = getDeserializer(state)
return if deserializer.version? and deserializer.version isnt state.version
deserializer.deserialize(state)
window.getDeserializer = (state) ->
deserializers[state?.deserializer]
window.measure = (description, fn) ->
start = new Date().getTime()