mirror of
https://github.com/atom/atom.git
synced 2026-02-06 12:44:59 -05:00
453 lines
13 KiB
CoffeeScript
453 lines
13 KiB
CoffeeScript
Point = require 'point'
|
|
Buffer = require 'buffer'
|
|
Renderer = require 'renderer'
|
|
Cursor = require 'cursor'
|
|
Selection = require 'selection'
|
|
EventEmitter = require 'event-emitter'
|
|
_ = require 'underscore'
|
|
|
|
module.exports =
|
|
class EditSession
|
|
@idCounter: 1
|
|
|
|
@deserialize: (state, editor, rootView) ->
|
|
buffer = Buffer.deserialize(state.buffer, rootView.project)
|
|
session = new EditSession(
|
|
buffer: buffer
|
|
tabText: editor.tabText
|
|
autoIndent: editor.autoIndent
|
|
softTabs: editor.softTabs
|
|
)
|
|
session.setScrollTop(state.scrollTop)
|
|
session.setScrollLeft(state.scrollLeft)
|
|
session.setCursorScreenPosition(state.cursorScreenPosition)
|
|
session
|
|
|
|
scrollTop: 0
|
|
scrollLeft: 0
|
|
renderer: null
|
|
cursors: null
|
|
selections: null
|
|
autoIndent: true
|
|
softTabs: true
|
|
|
|
constructor: ({@buffer, @tabText, @autoIndent, @softTabs, softWrapColumn}) ->
|
|
@id = @constructor.idCounter++
|
|
@softTabs ?= true
|
|
@renderer = new Renderer(@buffer, { softWrapColumn, @tabText })
|
|
@languageMode = @renderer.languageMode
|
|
@cursors = []
|
|
@selections = []
|
|
@addCursorAtScreenPosition([0, 0])
|
|
|
|
@buffer.on "change.edit-session-#{@id}", (e) =>
|
|
for selection in @getSelections()
|
|
selection.handleBufferChange(e)
|
|
|
|
@renderer.on "change.edit-session-#{@id}", (e) =>
|
|
@trigger 'screen-lines-change', e
|
|
@moveCursors (cursor) -> cursor.refreshScreenPosition() unless e.bufferChanged
|
|
|
|
destroy: ->
|
|
@buffer.off ".edit-session-#{@id}"
|
|
@renderer.off ".edit-session-#{@id}"
|
|
@renderer.destroy()
|
|
|
|
serialize: ->
|
|
buffer: @buffer.serialize()
|
|
scrollTop: @getScrollTop()
|
|
scrollLeft: @getScrollLeft()
|
|
cursorScreenPosition: @getCursorScreenPosition().serialize()
|
|
|
|
isEqual: (other) ->
|
|
return false unless other instanceof EditSession
|
|
@buffer == other.buffer and
|
|
@scrollTop == other.getScrollTop() and
|
|
@scrollLeft == other.getScrollLeft() and
|
|
@getCursorScreenPosition().isEqual(other.getCursorScreenPosition())
|
|
|
|
getRenderer: -> @renderer
|
|
|
|
setScrollTop: (@scrollTop) ->
|
|
getScrollTop: -> @scrollTop
|
|
|
|
setScrollLeft: (@scrollLeft) ->
|
|
getScrollLeft: -> @scrollLeft
|
|
|
|
setSoftWrapColumn: (softWrapColumn) -> @renderer.setSoftWrapColumn(softWrapColumn)
|
|
setAutoIndent: (@autoIndent) ->
|
|
setSoftTabs: (@softTabs) ->
|
|
|
|
screenPositionForBufferPosition: (bufferPosition, options) ->
|
|
@renderer.screenPositionForBufferPosition(bufferPosition, options)
|
|
|
|
bufferPositionForScreenPosition: (screenPosition, options) ->
|
|
@renderer.bufferPositionForScreenPosition(screenPosition, options)
|
|
|
|
clipScreenPosition: (screenPosition, options) ->
|
|
@renderer.clipScreenPosition(screenPosition, options)
|
|
|
|
clipBufferPosition: (bufferPosition, options) ->
|
|
{ row, column } = Point.fromObject(bufferPosition)
|
|
row = 0 if row < 0
|
|
column = 0 if column < 0
|
|
row = Math.min(@buffer.getLastRow(), row)
|
|
column = Math.min(@buffer.lineLengthForRow(row), column)
|
|
|
|
new Point(row, column)
|
|
|
|
getEofBufferPosition: ->
|
|
@buffer.getEofPosition()
|
|
|
|
bufferRangeForBufferRow: (row) ->
|
|
@buffer.rangeForRow(row)
|
|
|
|
lineForBufferRow: (row) ->
|
|
@buffer.lineForRow(row)
|
|
|
|
scanInRange: (args...) ->
|
|
@buffer.scanInRange(args...)
|
|
|
|
backwardsScanInRange: (args...) ->
|
|
@buffer.backwardsScanInRange(args...)
|
|
|
|
getCurrentMode: ->
|
|
@buffer.getMode()
|
|
|
|
insertText: (text) ->
|
|
@mutateSelectedText (selection) -> selection.insertText(text)
|
|
|
|
insertNewline: ->
|
|
@insertText('\n')
|
|
|
|
insertNewlineBelow: ->
|
|
@moveCursorToEndOfLine()
|
|
@insertNewline()
|
|
|
|
insertTab: ->
|
|
if @getSelection().isEmpty()
|
|
if @softTabs
|
|
@insertText(@tabText)
|
|
else
|
|
@insertText('\t')
|
|
else
|
|
@activeEditSession.indentSelectedRows()
|
|
|
|
backspace: ->
|
|
@mutateSelectedText (selection) -> selection.backspace()
|
|
|
|
backspaceToBeginningOfWord: ->
|
|
@mutateSelectedText (selection) -> selection.backspaceToBeginningOfWord()
|
|
|
|
delete: ->
|
|
@mutateSelectedText (selection) -> selection.delete()
|
|
|
|
deleteToEndOfWord: ->
|
|
@mutateSelectedText (selection) -> selection.deleteToEndOfWord()
|
|
|
|
indentSelectedRows: ->
|
|
@mutateSelectedText (selection) -> selection.indentSelectedRows()
|
|
|
|
outdentSelectedRows: ->
|
|
@mutateSelectedText (selection) -> selection.outdentSelectedRows()
|
|
|
|
toggleLineCommentsInSelection: ->
|
|
@mutateSelectedText (selection) -> selection.toggleLineComments()
|
|
|
|
cutToEndOfLine: ->
|
|
maintainPasteboard = false
|
|
@mutateSelectedText (selection) ->
|
|
selection.cutToEndOfLine(maintainPasteboard)
|
|
maintainPasteboard = true
|
|
|
|
cutSelectedText: ->
|
|
maintainPasteboard = false
|
|
@mutateSelectedText (selection) ->
|
|
selection.cut(maintainPasteboard)
|
|
maintainPasteboard = true
|
|
|
|
copySelectedText: ->
|
|
maintainPasteboard = false
|
|
for selection in @getSelections()
|
|
selection.copy(maintainPasteboard)
|
|
maintainPasteboard = true
|
|
|
|
pasteText: ->
|
|
@insertText($native.readFromPasteboard())
|
|
|
|
undo: ->
|
|
if ranges = @buffer.undo()
|
|
@setSelectedBufferRanges(ranges)
|
|
|
|
redo: ->
|
|
if ranges = @buffer.redo()
|
|
@setSelectedBufferRanges(ranges)
|
|
|
|
foldSelection: ->
|
|
selection.fold() for selection in @getSelections()
|
|
|
|
foldAll: ->
|
|
@renderer.foldAll()
|
|
|
|
toggleFold: ->
|
|
row = @renderer.bufferPositionForScreenPosition(@getCursorScreenPosition()).row
|
|
@renderer.toggleFoldAtBufferRow(row)
|
|
|
|
createFold: (startRow, endRow) ->
|
|
@renderer.createFold(startRow, endRow)
|
|
|
|
destroyFoldsContainingBufferRow: (bufferRow) ->
|
|
@renderer.destroyFoldsContainingBufferRow(bufferRow)
|
|
|
|
unfoldCurrentRow: (row) ->
|
|
@renderer.largestFoldForBufferRow(@getLastCursor().getCurrentBufferRow())?.destroy()
|
|
|
|
destroyFold: (foldId) ->
|
|
fold = @renderer.foldsById[foldId]
|
|
fold.destroy()
|
|
@setCursorBufferPosition([fold.startRow, 0])
|
|
|
|
isFoldedAtScreenRow: (screenRow) ->
|
|
@lineForScreenRow(screenRow).fold?
|
|
|
|
autoIndentTextAfterBufferPosition: (text, bufferPosition) ->
|
|
return { text } unless @autoIndent
|
|
@languageMode.autoIndentTextAfterBufferPosition(text, bufferPosition)
|
|
|
|
autoOutdentBufferRow: (bufferRow) ->
|
|
@languageMode.autoOutdentBufferRow(bufferRow)
|
|
|
|
toggleLineCommentsInRange: (range) ->
|
|
@languageMode.toggleLineCommentsInRange(range)
|
|
|
|
mutateSelectedText: (fn) ->
|
|
selections = @getSelections()
|
|
@buffer.startUndoBatch(@getSelectedBufferRanges())
|
|
fn(selection) for selection in selections
|
|
@buffer.endUndoBatch(@getSelectedBufferRanges())
|
|
|
|
lineForScreenRow: (row) ->
|
|
@renderer.lineForRow(row)
|
|
|
|
stateForScreenRow: (screenRow) ->
|
|
@renderer.stateForScreenRow(screenRow)
|
|
|
|
getCursors: -> new Array(@cursors...)
|
|
|
|
getCursor: (index=0) ->
|
|
@cursors[index]
|
|
|
|
getLastCursor: ->
|
|
_.last(@cursors)
|
|
|
|
addCursorAtScreenPosition: (screenPosition) ->
|
|
@addCursor(new Cursor(editSession: this, screenPosition: screenPosition))
|
|
|
|
addCursorAtBufferPosition: (bufferPosition) ->
|
|
@addCursor(new Cursor(editSession: this, bufferPosition: bufferPosition))
|
|
|
|
addCursor: (cursor=new Cursor(editSession: this, screenPosition: [0,0])) ->
|
|
@cursors.push(cursor)
|
|
@trigger 'add-cursor', cursor
|
|
@addSelectionForCursor(cursor)
|
|
cursor
|
|
|
|
removeCursor: (cursor) ->
|
|
_.remove(@cursors, cursor)
|
|
|
|
addSelectionForCursor: (cursor) ->
|
|
selection = new Selection(editSession: this, cursor: cursor)
|
|
@selections.push(selection)
|
|
@trigger 'add-selection', selection
|
|
selection
|
|
|
|
addSelectionForBufferRange: (bufferRange, options) ->
|
|
@addCursor().selection.setBufferRange(bufferRange, options)
|
|
|
|
setSelectedBufferRange: (bufferRange, options) ->
|
|
@clearSelections()
|
|
@getLastSelection().setBufferRange(bufferRange, options)
|
|
|
|
setSelectedBufferRanges: (bufferRanges, options) ->
|
|
selections = @getSelections()
|
|
for bufferRange, i in bufferRanges
|
|
if selections[i]
|
|
selections[i].setBufferRange(bufferRange, options)
|
|
else
|
|
@addSelectionForBufferRange(bufferRange, options)
|
|
@mergeIntersectingSelections()
|
|
|
|
removeSelection: (selection) ->
|
|
_.remove(@selections, selection)
|
|
|
|
clearSelections: ->
|
|
lastSelection = @getLastSelection()
|
|
for selection in @getSelections() when selection != lastSelection
|
|
selection.destroy()
|
|
lastSelection.clear()
|
|
|
|
getSelections: -> new Array(@selections...)
|
|
|
|
getSelection: (index) ->
|
|
index ?= @selections.length - 1
|
|
@selections[index]
|
|
|
|
getLastSelection: ->
|
|
_.last(@selections)
|
|
|
|
getSelectionsOrderedByBufferPosition: ->
|
|
@getSelections().sort (a, b) ->
|
|
aRange = a.getBufferRange()
|
|
bRange = b.getBufferRange()
|
|
aRange.end.compare(bRange.end)
|
|
|
|
getLastSelectionInBuffer: ->
|
|
_.last(@getSelectionsOrderedByBufferPosition())
|
|
|
|
selectionIntersectsBufferRange: (bufferRange) ->
|
|
_.any @getSelections(), (selection) ->
|
|
selection.intersectsBufferRange(bufferRange)
|
|
|
|
setCursorScreenPosition: (position) ->
|
|
@moveCursors (cursor) -> cursor.setScreenPosition(position)
|
|
|
|
getCursorScreenPosition: ->
|
|
@getLastCursor().getScreenPosition()
|
|
|
|
setCursorBufferPosition: (position) ->
|
|
@moveCursors (cursor) -> cursor.setBufferPosition(position)
|
|
|
|
getCursorBufferPosition: ->
|
|
@getLastCursor().getBufferPosition()
|
|
|
|
getSelectedScreenRange: ->
|
|
@getLastSelection().getScreenRange()
|
|
|
|
getSelectedBufferRange: ->
|
|
@getLastSelection().getBufferRange()
|
|
|
|
getSelectedBufferRanges: ->
|
|
selection.getBufferRange() for selection in @selections
|
|
|
|
getSelectedText: ->
|
|
@getLastSelection().getText()
|
|
|
|
moveCursorUp: ->
|
|
@moveCursors (cursor) -> cursor.moveUp()
|
|
|
|
moveCursorDown: ->
|
|
@moveCursors (cursor) -> cursor.moveDown()
|
|
|
|
moveCursorLeft: ->
|
|
@moveCursors (cursor) -> cursor.moveLeft()
|
|
|
|
moveCursorRight: ->
|
|
@moveCursors (cursor) -> cursor.moveRight()
|
|
|
|
moveCursorToTop: ->
|
|
@moveCursors (cursor) -> cursor.moveToTop()
|
|
|
|
moveCursorToBottom: ->
|
|
@moveCursors (cursor) -> cursor.moveToBottom()
|
|
|
|
moveCursorToBeginningOfLine: ->
|
|
@moveCursors (cursor) -> cursor.moveToBeginningOfLine()
|
|
|
|
moveCursorToFirstCharacterOfLine: ->
|
|
@moveCursors (cursor) -> cursor.moveToFirstCharacterOfLine()
|
|
|
|
moveCursorToEndOfLine: ->
|
|
@moveCursors (cursor) -> cursor.moveToEndOfLine()
|
|
|
|
moveCursorToNextWord: ->
|
|
@moveCursors (cursor) -> cursor.moveToNextWord()
|
|
|
|
moveCursorToBeginningOfWord: ->
|
|
@moveCursors (cursor) -> cursor.moveToBeginningOfWord()
|
|
|
|
moveCursorToEndOfWord: ->
|
|
@moveCursors (cursor) -> cursor.moveToEndOfWord()
|
|
|
|
moveCursors: (fn) ->
|
|
fn(cursor) for cursor in @getCursors()
|
|
@mergeCursors()
|
|
|
|
selectToScreenPosition: (position) ->
|
|
lastSelection = @getLastSelection()
|
|
lastSelection.selectToScreenPosition(position)
|
|
@mergeIntersectingSelections(reverse: lastSelection.isReversed())
|
|
|
|
selectRight: ->
|
|
@expandSelectionsForward (selection) => selection.selectRight()
|
|
|
|
selectLeft: ->
|
|
@expandSelectionsBackward (selection) => selection.selectLeft()
|
|
|
|
selectUp: ->
|
|
@expandSelectionsBackward (selection) => selection.selectUp()
|
|
|
|
selectDown: ->
|
|
@expandSelectionsForward (selection) => selection.selectDown()
|
|
|
|
selectToTop: ->
|
|
@expandSelectionsBackward (selection) => selection.selectToTop()
|
|
|
|
selectAll: ->
|
|
@expandSelectionsForward (selection) => selection.selectAll()
|
|
|
|
selectToBottom: ->
|
|
@expandSelectionsForward (selection) => selection.selectToBottom()
|
|
|
|
selectToBeginningOfLine: ->
|
|
@expandSelectionsBackward (selection) => selection.selectToBeginningOfLine()
|
|
|
|
selectToEndOfLine: ->
|
|
@expandSelectionsForward (selection) => selection.selectToEndOfLine()
|
|
|
|
selectLine: ->
|
|
@expandSelectionsForward (selection) => selection.selectLine()
|
|
|
|
expandLastSelectionOverLine: ->
|
|
@getLastSelection().expandOverLine()
|
|
|
|
selectToBeginningOfWord: ->
|
|
@expandSelectionsBackward (selection) => selection.selectToBeginningOfWord()
|
|
|
|
selectToEndOfWord: ->
|
|
@expandSelectionsForward (selection) => selection.selectToEndOfWord()
|
|
|
|
selectWord: ->
|
|
@expandSelectionsForward (selection) => selection.selectWord()
|
|
|
|
expandLastSelectionOverWord: ->
|
|
@getLastSelection().expandOverWord()
|
|
|
|
mergeCursors: ->
|
|
positions = []
|
|
for cursor in new Array(@getCursors()...)
|
|
position = cursor.getBufferPosition().toString()
|
|
if position in positions
|
|
cursor.destroy()
|
|
else
|
|
positions.push(position)
|
|
|
|
expandSelectionsForward: (fn) ->
|
|
fn(selection) for selection in @getSelections()
|
|
@mergeIntersectingSelections()
|
|
|
|
expandSelectionsBackward: (fn) ->
|
|
fn(selection) for selection in @getSelections()
|
|
@mergeIntersectingSelections(reverse: true)
|
|
|
|
mergeIntersectingSelections: (options) ->
|
|
for selection in @getSelections()
|
|
otherSelections = @getSelections()
|
|
_.remove(otherSelections, selection)
|
|
for otherSelection in otherSelections
|
|
if selection.intersectsWith(otherSelection)
|
|
selection.merge(otherSelection, options)
|
|
@mergeIntersectingSelections(options)
|
|
return
|
|
|
|
_.extend(EditSession.prototype, EventEmitter)
|