diff --git a/spec/atom/editor-spec.coffee b/spec/atom/editor-spec.coffee index b700fae06..68d2f4850 100644 --- a/spec/atom/editor-spec.coffee +++ b/spec/atom/editor-spec.coffee @@ -756,6 +756,27 @@ describe "Editor", -> expect(range.end).toEqual({row: 5, column: 27}) expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27) + describe "multiple cursor placement", -> + fit "places multiple cursor with meta-click", -> + editor.attachToDom() + editor.lines.trigger mousedownEvent(editor: editor, point: [3, 0]) + editor.lines.trigger mousedownEvent(editor: editor, point: [6, 0], metaKey: true) + + [cursor1, cursor2] = editor.find('.cursor').map -> $(this).view() + expect(cursor1.position()).toEqual(top: 3 * editor.lineHeight, left: 0) + expect(cursor1.getBufferPosition()).toEqual [3, 0] + expect(cursor2.position()).toEqual(top: 6 * editor.lineHeight, left: 0) + expect(cursor2.getBufferPosition()).toEqual [6, 0] + + fit "inserts text for all cursors", -> + editor.setCursorScreenPosition([3, 0]) + editor.addCursorAtScreenPosition([6, 0]) + + editor.insertText("abc") + + expect(editor.lineForBufferRow(3)).toBe "abc var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineForBufferRow(6)).toBe "abc current < pivot ? left.push(current) : right.push(current);" + describe "buffer manipulation", -> describe "when text input events are triggered on the hidden input element", -> describe "when there is no selection", -> diff --git a/src/atom/composite-cursor.coffee b/src/atom/composite-cursor.coffee new file mode 100644 index 000000000..6c93ebb0b --- /dev/null +++ b/src/atom/composite-cursor.coffee @@ -0,0 +1,30 @@ +Cursor = require 'cursor' + +module.exports = +class CompositeCursor + constructor: (@editor) -> + @cursors = [] + @addCursor() + + getCursors: -> + @cursors + + addCursor: -> + cursor = new Cursor(@editor) + @cursors.push(cursor) + @editor.lines.append(cursor) + @editor.addSelectionForCursor(cursor) + cursor + + addCursorAtScreenPosition: (screenPosition) -> + cursor = @addCursor() + cursor.setScreenPosition(screenPosition) + + setScreenPosition: (screenPosition) -> + cursor.setScreenPosition(screenPosition) for cursor in @cursors + + getScreenPosition: -> + @cursors[0].getScreenPosition() + + handleBufferChange: (e) -> + cursor.handleBufferChange(e) for cursor in @cursors diff --git a/src/atom/composite-selection.coffee b/src/atom/composite-selection.coffee new file mode 100644 index 000000000..e97caca01 --- /dev/null +++ b/src/atom/composite-selection.coffee @@ -0,0 +1,14 @@ +Selection = require 'selection' + +module.exports = +class CompositeSeleciton + constructor: (@editor) -> + @selections = [] + + addSelectionForCursor: (cursor) -> + selection = new Selection({@editor, cursor}) + @selections.push(selection) + @editor.lines.append(selection) + + insertText: (text) -> + selection.insertText(text) for selection in @selections diff --git a/src/atom/cursor.coffee b/src/atom/cursor.coffee index e1d61d05b..c8c35b3ed 100644 --- a/src/atom/cursor.coffee +++ b/src/atom/cursor.coffee @@ -12,10 +12,14 @@ class Cursor extends View bufferPosition: null initialize: (@editor) -> + @screenPosition = new Point(0, 0) @one 'attach', => @updateAppearance() - bufferChanged: (e) -> - @setBufferPosition(e.newRange.end) + handleBufferChange: (e) -> + { newRange, oldRange } = e + if oldRange.end.row == newRange.end.row == @getBufferPosition().row + delta = newRange.end.subtract(oldRange.end) + @setBufferPosition(@getBufferPosition().add(delta)) setScreenPosition: (position, options={}) -> position = Point.fromObject(position) diff --git a/src/atom/editor.coffee b/src/atom/editor.coffee index 8cda01692..7ce69e967 100644 --- a/src/atom/editor.coffee +++ b/src/atom/editor.coffee @@ -1,12 +1,12 @@ {View, $$} = require 'space-pen' AceOutdentAdaptor = require 'ace-outdent-adaptor' Buffer = require 'buffer' -Cursor = require 'cursor' +CompositeCursor = require 'composite-cursor' +CompositeSelection = require 'composite-selection' Gutter = require 'gutter' Renderer = require 'renderer' Point = require 'point' Range = require 'range' -Selection = require 'selection' EditSession = require 'edit-session' $ = require 'jquery' @@ -102,11 +102,14 @@ class Editor extends View @on 'close', => @remove(); false buildCursorAndSelection: -> - @cursor = new Cursor(this) - @lines.append(@cursor) + @compositeSelection = new CompositeSelection(this) + @compositeCursor = new CompositeCursor(this) - @selection = new Selection(this) - @lines.append(@selection) + addCursorAtScreenPosition: (screenPosition) -> + @compositeCursor.addCursorAtScreenPosition(screenPosition) + + addSelectionForCursor: (cursor) -> + @compositeSelection.addSelectionForCursor(cursor) handleEvents: -> @on 'focus', => @@ -130,7 +133,11 @@ class Editor extends View clickCount = e.originalEvent.detail if clickCount == 1 - @setCursorScreenPosition @screenPositionFromMouseEvent(e) + screenPosition = @screenPositionFromMouseEvent(e) + if e.metaKey + @addCursorAtScreenPosition(screenPosition) + else + @setCursorScreenPosition(screenPosition) else if clickCount == 2 @selection.selectWord() else if clickCount >= 3 @@ -142,7 +149,7 @@ class Editor extends View @insertText(e.originalEvent.data) @on 'cursor:position-changed', => - position = @pixelPositionForScreenPosition(@cursor.getScreenPosition()) + position = @pixelPositionForScreenPosition(@getCursorScreenPosition()) if @softWrap position.left = Math.min(position.left, @horizontalScroller.width() - @charWidth) @hiddenInput.css(position) @@ -213,7 +220,7 @@ class Editor extends View @editSession.scrollLeft = @horizontalScroller.scrollLeft() handleBufferChange: (e) -> - @cursor.bufferChanged(e) if @isFocused + @compositeCursor.handleBufferChange(e) if @isFocused handleRendererChange: (e) -> { oldRange, newRange } = e @@ -349,8 +356,8 @@ class Editor extends View getCurrentScreenLine: -> @buffer.lineForRow(@getCursorScreenRow()) getCurrentBufferLine: -> @buffer.lineForRow(@getCursorBufferRow()) - setCursorScreenPosition: (position) -> @cursor.setScreenPosition(position) - getCursorScreenPosition: -> @cursor.getScreenPosition() + setCursorScreenPosition: (position) -> @compositeCursor.setScreenPosition(position) + getCursorScreenPosition: -> @compositeCursor.getScreenPosition() setCursorBufferPosition: (position) -> @cursor.setBufferPosition(position) getCursorBufferPosition: -> @cursor.getBufferPosition() setCursorScreenRow: (row) -> @cursor.setScreenRow(row) @@ -376,11 +383,12 @@ class Editor extends View getBufferLineLength: (row) -> @buffer.getLineLength(row) getTextInRange: (range) -> @buffer.getTextInRange(range) getEofPosition: -> @buffer.getEofPosition() + lineForBufferRow: (row) -> @buffer.lineForRow(row) insertText: (text) -> - { text, shouldOutdent } = @autoIndentText(text) - @selection.insertText(text) - @autoOutdentText() if shouldOutdent + # { text, shouldOutdent } = @autoIndentText(text) + @compositeSelection.insertText(text) + # @autoOutdentText() if shouldOutdent autoIndentText: (text) -> if @autoIndent diff --git a/src/atom/selection.coffee b/src/atom/selection.coffee index a88a48598..1374efa5d 100644 --- a/src/atom/selection.coffee +++ b/src/atom/selection.coffee @@ -12,9 +12,8 @@ class Selection extends View modifyingSelection: null regions: null - initialize: (@editor) -> + initialize: ({@editor, @cursor}) -> @regions = [] - @cursor = @editor.cursor @cursor.on 'cursor:position-changed', => if @modifyingSelection @updateAppearance()