mirror of
https://github.com/atom/atom.git
synced 2026-01-25 23:08:18 -05:00
248 lines
6.8 KiB
CoffeeScript
248 lines
6.8 KiB
CoffeeScript
Anchor = require 'anchor'
|
|
Cursor = require 'cursor'
|
|
AceOutdentAdaptor = require 'ace-outdent-adaptor'
|
|
Point = require 'point'
|
|
Range = require 'range'
|
|
{View, $$} = require 'space-pen'
|
|
|
|
module.exports =
|
|
class Selection extends View
|
|
@content: ->
|
|
@div()
|
|
|
|
anchor: null
|
|
retainSelection: null
|
|
regions: null
|
|
|
|
initialize: ({@editor, @cursor} = {}) ->
|
|
@regions = []
|
|
|
|
handleBufferChange: (e) ->
|
|
return unless @anchor
|
|
@anchor.handleBufferChange(e)
|
|
|
|
placeAnchor: ->
|
|
return if @anchor
|
|
@anchor = new Anchor(@editor)
|
|
@anchor.setScreenPosition @cursor.getScreenPosition()
|
|
|
|
isEmpty: ->
|
|
@getBufferRange().isEmpty()
|
|
|
|
isReversed: ->
|
|
not @isEmpty() and @cursor.getBufferPosition().isLessThan(@anchor.getBufferPosition())
|
|
|
|
intersectsWith: (otherSelection) ->
|
|
@getScreenRange().intersectsWith(otherSelection.getScreenRange())
|
|
|
|
clearSelection: ->
|
|
@anchor = null
|
|
@updateAppearance()
|
|
|
|
updateAppearance: ->
|
|
return unless @cursor
|
|
|
|
@clearRegions()
|
|
|
|
range = @getScreenRange()
|
|
|
|
@editor.highlightSelectedFolds()
|
|
return if range.isEmpty()
|
|
|
|
rowSpan = range.end.row - range.start.row
|
|
|
|
if rowSpan == 0
|
|
@appendRegion(1, range.start, range.end)
|
|
else
|
|
@appendRegion(1, range.start, null)
|
|
if rowSpan > 1
|
|
@appendRegion(rowSpan - 1, { row: range.start.row + 1, column: 0}, null)
|
|
@appendRegion(1, { row: range.end.row, column: 0 }, range.end)
|
|
|
|
|
|
appendRegion: (rows, start, end) ->
|
|
{ lineHeight, charWidth } = @editor
|
|
css = @editor.pixelPositionForScreenPosition(start)
|
|
css.height = lineHeight * rows
|
|
if end
|
|
css.width = @editor.pixelPositionForScreenPosition(end).left - css.left
|
|
else
|
|
css.right = 0
|
|
|
|
region = ($$ -> @div class: 'selection').css(css)
|
|
@append(region)
|
|
@regions.push(region)
|
|
|
|
clearRegions: ->
|
|
region.remove() for region in @regions
|
|
@regions = []
|
|
|
|
getScreenRange: ->
|
|
if @anchor
|
|
new Range(@anchor.getScreenPosition(), @cursor.getScreenPosition())
|
|
else
|
|
new Range(@cursor.getScreenPosition(), @cursor.getScreenPosition())
|
|
|
|
setScreenRange: (range, {reverse}={}) ->
|
|
{ start, end } = range
|
|
[start, end] = [end, start] if reverse
|
|
|
|
@cursor.setScreenPosition(start)
|
|
@modifySelection => @cursor.setScreenPosition(end)
|
|
|
|
getBufferRange: ->
|
|
@editor.bufferRangeForScreenRange(@getScreenRange())
|
|
|
|
setBufferRange: (bufferRange, options) ->
|
|
@setScreenRange(@editor.screenRangeForBufferRange(bufferRange), options)
|
|
|
|
getText: ->
|
|
@editor.buffer.getTextInRange @getBufferRange()
|
|
|
|
intersectsBufferRange: (bufferRange) ->
|
|
@getBufferRange().intersectsWith(bufferRange)
|
|
|
|
insertText: (text) ->
|
|
{ text, shouldOutdent } = @autoIndentText(text)
|
|
oldBufferRange = @getBufferRange()
|
|
@editor.destroyFoldsContainingBufferRow(oldBufferRange.end.row)
|
|
isReversed = @isReversed()
|
|
@clearSelection()
|
|
newBufferRange = @editor.buffer.change(oldBufferRange, text)
|
|
@cursor.setBufferPosition(newBufferRange.end, skipAtomicTokens: true) if isReversed
|
|
@autoOutdentText() if shouldOutdent
|
|
|
|
indentSelectedRows: ->
|
|
range = @getBufferRange()
|
|
for row in [range.start.row..range.end.row]
|
|
@editor.buffer.insert([row, 0], @editor.tabText) unless @editor.buffer.lineLengthForRow(row) == 0
|
|
|
|
outdentSelectedRows: ->
|
|
range = @getBufferRange()
|
|
buffer = @editor.buffer
|
|
leadingTabRegex = new RegExp("^#{@editor.tabText}")
|
|
for row in [range.start.row..range.end.row]
|
|
if leadingTabRegex.test buffer.lineForRow(row)
|
|
buffer.delete [[row, 0], [row, @editor.tabText.length]]
|
|
|
|
autoIndentText: (text) ->
|
|
if @editor.autoIndent
|
|
mode = @editor.getCurrentMode()
|
|
row = @cursor.getScreenPosition().row
|
|
state = @editor.stateForScreenRow(row)
|
|
if text[0] == "\n"
|
|
indent = mode.getNextLineIndent(state, @cursor.getCurrentBufferLine(), @editor.tabText)
|
|
text = text[0] + indent + text[1..]
|
|
else if mode.checkOutdent(state, @cursor.getCurrentBufferLine(), text)
|
|
shouldOutdent = true
|
|
|
|
{text, shouldOutdent}
|
|
|
|
autoOutdentText: ->
|
|
screenRow = @cursor.getScreenPosition().row
|
|
bufferRow = @cursor.getBufferPosition().row
|
|
state = @editor.renderer.lineForRow(screenRow).state
|
|
@editor.getCurrentMode().autoOutdent(state, new AceOutdentAdaptor(@editor.buffer, @editor), bufferRow)
|
|
|
|
backspace: ->
|
|
@selectLeft() if @isEmpty()
|
|
@deleteSelectedText()
|
|
|
|
backspaceToBeginningOfWord: ->
|
|
@selectToBeginningOfWord() if @isEmpty()
|
|
@deleteSelectedText()
|
|
|
|
delete: ->
|
|
@selectRight() if @isEmpty()
|
|
@deleteSelectedText()
|
|
|
|
deleteToEndOfWord: ->
|
|
@selectToEndOfWord() if @isEmpty()
|
|
@deleteSelectedText()
|
|
|
|
deleteSelectedText: ->
|
|
range = @getBufferRange()
|
|
@editor.buffer.delete(range) unless range.isEmpty()
|
|
@clearSelection()
|
|
|
|
merge: (otherSelection, options) ->
|
|
@setScreenRange(@getScreenRange().union(otherSelection.getScreenRange()), options)
|
|
otherSelection.remove()
|
|
|
|
remove: ->
|
|
@cursor?.remove()
|
|
super
|
|
|
|
modifySelection: (fn) ->
|
|
@placeAnchor()
|
|
@retainSelection = true
|
|
fn()
|
|
@retainSelection = false
|
|
|
|
selectWord: ->
|
|
@setBufferRange(@cursor.getCurrentWordBufferRange())
|
|
|
|
expandOverWord: ->
|
|
@setBufferRange(@getBufferRange().union(@cursor.getCurrentWordBufferRange()))
|
|
|
|
selectLine: (row=@cursor.getBufferPosition().row) ->
|
|
@setBufferRange(@editor.rangeForBufferRow(row))
|
|
|
|
expandOverLine: ->
|
|
@setBufferRange(@getBufferRange().union(@cursor.getCurrentLineBufferRange()))
|
|
|
|
selectToScreenPosition: (position) ->
|
|
@modifySelection => @cursor.setScreenPosition(position)
|
|
|
|
selectRight: ->
|
|
@modifySelection => @cursor.moveRight()
|
|
|
|
selectLeft: ->
|
|
@modifySelection => @cursor.moveLeft()
|
|
|
|
selectUp: ->
|
|
@modifySelection => @cursor.moveUp()
|
|
|
|
selectDown: ->
|
|
@modifySelection => @cursor.moveDown()
|
|
|
|
selectToTop: ->
|
|
@modifySelection => @cursor.moveToTop()
|
|
|
|
selectToBottom: ->
|
|
@modifySelection => @cursor.moveToBottom()
|
|
|
|
selectAll: ->
|
|
@setBufferRange(@editor.buffer.getRange())
|
|
|
|
selectToBeginningOfLine: ->
|
|
@modifySelection => @cursor.moveToBeginningOfLine()
|
|
|
|
selectToEndOfLine: ->
|
|
@modifySelection => @cursor.moveToEndOfLine()
|
|
|
|
selectToBeginningOfWord: ->
|
|
@modifySelection => @cursor.moveToBeginningOfWord()
|
|
|
|
selectToEndOfWord: ->
|
|
@modifySelection => @cursor.moveToEndOfWord()
|
|
|
|
cutToEndOfLine: (maintainPasteboard) ->
|
|
@selectToEndOfLine() if @isEmpty()
|
|
@cut(maintainPasteboard)
|
|
|
|
cut: (maintainPasteboard=false) ->
|
|
@copy(maintainPasteboard)
|
|
@delete()
|
|
|
|
copy: (maintainPasteboard=false) ->
|
|
return if @isEmpty()
|
|
text = @editor.buffer.getTextInRange(@getBufferRange())
|
|
text = $native.readFromPasteboard() + "\n" + text if maintainPasteboard
|
|
$native.writeToPasteboard text
|
|
|
|
fold: ->
|
|
range = @getBufferRange()
|
|
@editor.createFold(range.start.row, range.end.row)
|
|
@cursor.setBufferPosition([range.end.row + 1, 0])
|