diff --git a/spec/app/selection-spec.coffee b/spec/app/selection-spec.coffee index 7fabac1bc..b10c1724b 100644 --- a/spec/app/selection-spec.coffee +++ b/spec/app/selection-spec.coffee @@ -17,8 +17,8 @@ describe "Selection", -> it "places the anchor at the start of the range and the cursor at the end", -> range = new Range({row: 2, column: 7}, {row: 3, column: 18}) selection.setBufferRange(range) - expect(selection.anchor.getScreenPosition()).toEqual range.start - expect(selection.cursor.getScreenPosition()).toEqual range.end + expect(selection.selection.anchor.getScreenPosition()).toEqual range.start + expect(selection.selection.cursor.getScreenPosition()).toEqual range.end describe ".deleteSelectedText()", -> describe "when nothing is selected", -> diff --git a/src/app/cursor-view.coffee b/src/app/cursor-view.coffee index 866f3dff6..c47c03eec 100644 --- a/src/app/cursor-view.coffee +++ b/src/app/cursor-view.coffee @@ -14,7 +14,6 @@ class CursorView extends View hidden: false initialize: (@cursor, @editor) -> - @cursor.on 'change-screen-position', (position, options) => @updateAppearance() unless options.bufferChange diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index b27e25aab..a9e9d1279 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -32,7 +32,8 @@ class EditSession @addCursorAtScreenPosition([0, 0]) @buffer.on "change.edit-session-#{@id}", (e) => - @moveCursors (cursor) -> cursor.handleBufferChange(e) + for selection in @getSelections() + selection.handleBufferChange(e) @renderer.on "change.edit-session-#{@id}", (e) => @trigger 'screen-lines-change', e @@ -107,7 +108,7 @@ class EditSession cursor addSelectionForCursor: (cursor) -> - selection = new Selection(cursor) + selection = new Selection(editSession: this, cursor: cursor) @selections.push(selection) @trigger 'add-selection', selection selection diff --git a/src/app/selection-view.coffee b/src/app/selection-view.coffee index e15278063..8068228af 100644 --- a/src/app/selection-view.coffee +++ b/src/app/selection-view.coffee @@ -16,6 +16,9 @@ class SelectionView extends View initialize: ({@editor, @selection} = {}) -> @cursor = @selection.cursor @regions = [] + @selection.view = this + @selection.on 'change-screen-range', => + @updateAppearance() handleBufferChange: (e) -> return unless @anchor @@ -28,17 +31,16 @@ class SelectionView extends View @anchor.setScreenPosition @cursor.getScreenPosition() isEmpty: -> - @getBufferRange().isEmpty() + @selection.isEmpty() isReversed: -> - not @isEmpty() and @cursor.getBufferPosition().isLessThan(@anchor.getBufferPosition()) + @selection.isReversed() intersectsWith: (otherSelection) -> @getScreenRange().intersectsWith(otherSelection.getScreenRange()) clearSelection: -> - @anchor = null - @updateAppearance() + @selection.clear() updateAppearance: -> return unless @cursor @@ -60,7 +62,6 @@ class SelectionView extends View @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) @@ -79,21 +80,13 @@ class SelectionView extends View @regions = [] getScreenRange: -> - if @anchor - new Range(@anchor.getScreenPosition(), @cursor.getScreenPosition()) - else - new Range(@cursor.getScreenPosition(), @cursor.getScreenPosition()) + @selection.getScreenRange() - setScreenRange: (range, {reverse}={}) -> - range = Range.fromObject(range) - { start, end } = range - [start, end] = [end, start] if reverse - - @cursor.setScreenPosition(start) - @modifySelection => @cursor.setScreenPosition(end) + setScreenRange: (range, options)-> + @selection.setScreenRange(range, options) getBufferRange: -> - @editor.bufferRangeForScreenRange(@getScreenRange()) + @selection.getBufferRange() setBufferRange: (bufferRange, options) -> @setScreenRange(@editor.screenRangeForBufferRange(bufferRange), options) @@ -182,10 +175,7 @@ class SelectionView extends View super modifySelection: (fn) -> - @placeAnchor() - @retainSelection = true - fn() - @retainSelection = false + @selection.modifySelection(fn) selectWord: -> @setBufferRange(@cursor.getCurrentWordBufferRange()) diff --git a/src/app/selection.coffee b/src/app/selection.coffee index 535653c4e..1b6c52116 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -1,3 +1,69 @@ +Range = require 'range' +Anchor = require 'new-anchor' +EventEmitter = require 'event-emitter' +_ = require 'underscore' + module.exports = class Selection - constructor: (@cursor) -> + anchor: null + + constructor: ({@cursor, @editSession}) -> + @cursor.on 'change-screen-position', (e) => + @trigger 'change-screen-range', @getScreenRange() unless e.bufferChanged + + getScreenRange: -> + if @anchor + new Range(@anchor.getScreenPosition(), @cursor.getScreenPosition()) + else + new Range(@cursor.getScreenPosition(), @cursor.getScreenPosition()) + + setScreenRange: (screenRange, options={})-> + screenRange = Range.fromObject(screenRange) + { start, end } = screenRange + [start, end] = [end, start] if options.reverse + + @modifyScreenRange => + @placeAnchor() unless @anchor + @modifySelection => + @anchor.setScreenPosition(start) + @cursor.setScreenPosition(end) + + getBufferRange: -> + if @anchor + new Range(@anchor.getBufferPosition(), @cursor.getBufferPosition()) + else + new Range(@cursor.getBufferPosition(), @cursor.getBufferPosition()) + + clear: -> + @modifyScreenRange => @anchor = null + + isEmpty: -> + @getBufferRange().isEmpty() + + isReversed: -> + not @isEmpty() and @cursor.getBufferPosition().isLessThan(@anchor.getBufferPosition()) + + handleBufferChange: (e) -> + @modifyScreenRange => + @anchor?.handleBufferChange(e) + @cursor.handleBufferChange(e) + + modifySelection: (fn) -> + @retainSelection = true + @view?.retainSelection = true + @placeAnchor() unless @anchor + fn() + @retainSelection = false + @view?.retainSelection = false + + modifyScreenRange: (fn) -> + oldScreenRange = @getScreenRange() + fn() + newScreenRange = @getScreenRange() + @trigger 'change-screen-range', newScreenRange unless oldScreenRange.isEqual(newScreenRange) + + placeAnchor: -> + @anchor = new Anchor(@editSession) + @anchor.setScreenPosition(@cursor.getScreenPosition()) + +_.extend Selection.prototype, EventEmitter