mirror of
https://github.com/atom/atom.git
synced 2026-04-28 03:01:47 -04:00
Merge remote-tracking branch 'origin/dev' into cefode
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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}/"
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)
|
||||
@@ -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'
|
||||
|
||||
|
||||
97
src/app/pane-container.coffee
Normal file
97
src/app/pane-container.coffee
Normal 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()
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user