diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee index ebe1cb328..9d9f9fd1b 100644 --- a/spec/app/edit-session-spec.coffee +++ b/spec/app/edit-session-spec.coffee @@ -313,6 +313,36 @@ describe "EditSession", -> editSession.moveCursorToBeginningOfWord() expect(editSession.getCursorBufferPosition()).toEqual [9, 2] + describe ".moveCursorToPreviousWordBoundary()", -> + it "moves the cursor to the previous word boundary", -> + editSession.setCursorBufferPosition [0, 8] + editSession.addCursorAtBufferPosition [2, 0] + editSession.addCursorAtBufferPosition [2, 4] + editSession.addCursorAtBufferPosition [3, 14] + [cursor1, cursor2, cursor3, cursor4] = editSession.getCursors() + + editSession.moveCursorToPreviousWordBoundary() + + expect(cursor1.getBufferPosition()).toEqual [0, 4] + expect(cursor2.getBufferPosition()).toEqual [1, 30] + expect(cursor3.getBufferPosition()).toEqual [2, 0] + expect(cursor4.getBufferPosition()).toEqual [3, 13] + + describe ".moveCursorToNextWordBoundary()", -> + it "moves the cursor to the previous word boundary", -> + editSession.setCursorBufferPosition [0, 8] + editSession.addCursorAtBufferPosition [2, 40] + editSession.addCursorAtBufferPosition [3, 0] + editSession.addCursorAtBufferPosition [3, 30] + [cursor1, cursor2, cursor3, cursor4] = editSession.getCursors() + + editSession.moveCursorToNextWordBoundary() + + expect(cursor1.getBufferPosition()).toEqual [0, 13] + expect(cursor2.getBufferPosition()).toEqual [3, 0] + expect(cursor3.getBufferPosition()).toEqual [3, 4] + expect(cursor4.getBufferPosition()).toEqual [3, 31] + describe ".moveCursorToEndOfWord()", -> it "moves the cursor to the end of the word", -> editSession.setCursorBufferPosition [0, 6] @@ -668,6 +698,46 @@ describe "EditSession", -> expect(selection2.getBufferRange()).toEqual [[3,48], [3,51]] expect(selection2.isReversed()).toBeFalsy() + describe ".selectToPreviousWordBoundary()", -> + it "select to the previous word boundary", -> + editSession.setCursorBufferPosition [0, 8] + editSession.addCursorAtBufferPosition [2, 0] + editSession.addCursorAtBufferPosition [3, 4] + editSession.addCursorAtBufferPosition [3, 14] + + editSession.selectToPreviousWordBoundary() + + expect(editSession.getSelections().length).toBe 4 + [selection1, selection2, selection3, selection4] = editSession.getSelections() + expect(selection1.getBufferRange()).toEqual [[0,8], [0,4]] + expect(selection1.isReversed()).toBeTruthy() + expect(selection2.getBufferRange()).toEqual [[2,0], [1,30]] + expect(selection2.isReversed()).toBeTruthy() + expect(selection3.getBufferRange()).toEqual [[3,4], [3,0]] + expect(selection3.isReversed()).toBeTruthy() + expect(selection4.getBufferRange()).toEqual [[3,14], [3,13]] + expect(selection4.isReversed()).toBeTruthy() + + describe ".selectToNextWordBoundary()", -> + it "select to the next word boundary", -> + editSession.setCursorBufferPosition [0, 8] + editSession.addCursorAtBufferPosition [2, 40] + editSession.addCursorAtBufferPosition [4, 0] + editSession.addCursorAtBufferPosition [3, 30] + + editSession.selectToNextWordBoundary() + + expect(editSession.getSelections().length).toBe 4 + [selection1, selection2, selection3, selection4] = editSession.getSelections() + expect(selection1.getBufferRange()).toEqual [[0,8], [0,13]] + expect(selection1.isReversed()).toBeFalsy() + expect(selection2.getBufferRange()).toEqual [[2,40], [3,0]] + expect(selection2.isReversed()).toBeFalsy() + expect(selection3.getBufferRange()).toEqual [[4,0], [4,4]] + expect(selection3.isReversed()).toBeFalsy() + expect(selection4.getBufferRange()).toEqual [[3,30], [3,31]] + expect(selection4.isReversed()).toBeFalsy() + describe ".selectWord()", -> describe "when the cursor is inside a word", -> it "selects the entire word", -> diff --git a/src/app/cursor.coffee b/src/app/cursor.coffee index 6db87b6e3..58e854417 100644 --- a/src/app/cursor.coffee +++ b/src/app/cursor.coffee @@ -244,6 +244,16 @@ class Cursor if position = @getBeginningOfNextWordBufferPosition() @setBufferPosition(position) + # Moves the cursor to the previous word boundary. + moveToPreviousWordBoundary: -> + if position = @getMovePreviousWordBoundaryBufferPosition() + @setBufferPosition(position) + + # Moves the cursor to the next word boundary. + moveToNextWordBoundary: -> + if position = @getMoveNextWordBoundaryBufferPosition() + @setBufferPosition(position) + # Retrieves the buffer position of where the current word starts. # # options - A hash with one option: @@ -265,6 +275,49 @@ class Cursor beginningOfWordPosition or currentBufferPosition + # Retrieves buffer position of previous word boiundry. It might be on the + # current word, or the previous word. + getMovePreviousWordBoundaryBufferPosition: (options = {}) -> + currentBufferPosition = @getBufferPosition() + previousNonBlankRow = @editSession.buffer.previousNonBlankRow(currentBufferPosition.row) + scanRange = [[previousNonBlankRow, 0], currentBufferPosition] + + beginningOfWordPosition = null + @editSession.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => + if range.start.row < currentBufferPosition.row and currentBufferPosition.column > 0 + # force it to stop at the beginning of each line + beginningOfWordPosition = new Point(currentBufferPosition.row, 0) + else if range.end.isLessThan(currentBufferPosition) + beginningOfWordPosition = range.end + else + beginningOfWordPosition = range.start + + if not beginningOfWordPosition?.isEqual(currentBufferPosition) + stop() + + beginningOfWordPosition or currentBufferPosition + + # Retrieves buffer position of previous word boiundry. It might be on the + # current word, or the previous word. + getMoveNextWordBoundaryBufferPosition: (options = {}) -> + currentBufferPosition = @getBufferPosition() + scanRange = [currentBufferPosition, @editSession.getEofBufferPosition()] + + endOfWordPosition = null + @editSession.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => + if range.start.row > currentBufferPosition.row + # force it to stop at the beginning of each line + endOfWordPosition = new Point(range.start.row, 0) + else if range.start.isGreaterThan(currentBufferPosition) + endOfWordPosition = range.start + else + endOfWordPosition = range.end + + if not endOfWordPosition?.isEqual(currentBufferPosition) + stop() + + endOfWordPosition or currentBufferPosition + # Retrieves the buffer position of where the current word ends. # # options - A hash with one option: diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 92a5c8dfd..9a2277be3 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -1101,6 +1101,12 @@ class EditSession moveCursorToBeginningOfNextWord: -> @moveCursors (cursor) -> cursor.moveToBeginningOfNextWord() + moveCursorToPreviousWordBoundary: -> + @moveCursors (cursor) -> cursor.moveToPreviousWordBoundary() + + moveCursorToNextWordBoundary: -> + @moveCursors (cursor) -> cursor.moveToNextWordBoundary() + # Internal: moveCursors: (fn) -> fn(cursor) for cursor in @getCursors() @@ -1154,6 +1160,12 @@ class EditSession selectToEndOfLine: -> @expandSelectionsForward (selection) => selection.selectToEndOfLine() + selectToPreviousWordBoundary: -> + @expandSelectionsBackward (selection) => selection.selectToPreviousWordBoundary() + + selectToNextWordBoundary: -> + @expandSelectionsForward (selection) => selection.selectToNextWordBoundary() + # Selects the current line. selectLine: -> @expandSelectionsForward (selection) => selection.selectLine() diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 4a3f8cf22..79da4126b 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -141,11 +141,15 @@ class Editor extends View 'editor:move-to-beginning-of-word': @moveCursorToBeginningOfWord 'editor:move-to-end-of-word': @moveCursorToEndOfWord 'editor:move-to-beginning-of-next-word': @moveCursorToBeginningOfNextWord + 'editor:move-to-previous-word-boundary': @moveCursorToPreviousWordBoundary + 'editor:move-to-next-word-boundary': @moveCursorToNextWordBoundary 'editor:select-to-end-of-line': @selectToEndOfLine 'editor:select-to-beginning-of-line': @selectToBeginningOfLine 'editor:select-to-end-of-word': @selectToEndOfWord 'editor:select-to-beginning-of-word': @selectToBeginningOfWord 'editor:select-to-beginning-of-next-word': @selectToBeginningOfNextWord + 'editor:select-to-next-word-boundary': @selectToNextWordBoundary + 'editor:select-to-previous-word-boundary': @selectToPreviousWordBoundary 'editor:select-to-first-character-of-line': @selectToFirstCharacterOfLine 'editor:add-selection-below': @addSelectionBelow 'editor:add-selection-above': @addSelectionAbove @@ -238,6 +242,12 @@ class Editor extends View # {Delegates to: EditSession.moveCursorToFirstCharacterOfLine} moveCursorToFirstCharacterOfLine: -> @activeEditSession.moveCursorToFirstCharacterOfLine() + # {Delegates to: EditSession.moveCursorToPreviousWordBoundary} + moveCursorToPreviousWordBoundary: -> @activeEditSession.moveCursorToPreviousWordBoundary() + + # {Delegates to: EditSession.moveCursorToNextWordBoundary} + moveCursorToNextWordBoundary: -> @activeEditSession.moveCursorToNextWordBoundary() + # {Delegates to: EditSession.moveCursorToEndOfLine} moveCursorToEndOfLine: -> @activeEditSession.moveCursorToEndOfLine() @@ -334,6 +344,12 @@ class Editor extends View # {Delegates to: EditSession.selectToEndOfLine} selectToEndOfLine: -> @activeEditSession.selectToEndOfLine() + # {Delegates to: EditSession.selectToPreviousWordBoundary} + selectToPreviousWordBoundary: -> @activeEditSession.selectToPreviousWordBoundary() + + # {Delegates to: EditSession.selectToNextWordBoundary} + selectToNextWordBoundary: -> @activeEditSession.selectToNextWordBoundary() + # {Delegates to: EditSession.addSelectionBelow} addSelectionBelow: -> @activeEditSession.addSelectionBelow() diff --git a/src/app/selection.coffee b/src/app/selection.coffee index ae631aa87..ed3b88417 100644 --- a/src/app/selection.coffee +++ b/src/app/selection.coffee @@ -218,6 +218,14 @@ class Selection selectToBeginningOfNextWord: -> @modifySelection => @cursor.moveToBeginningOfNextWord() + # Selects text to the previous word boundary. + selectToPreviousWordBoundary: -> + @modifySelection => @cursor.moveToPreviousWordBoundary() + + # Selects text to the next word boundary. + selectToNextWordBoundary: -> + @modifySelection => @cursor.moveToNextWordBoundary() + # Moves the selection down one row. addSelectionBelow: -> range = (@goalBufferRange ? @getBufferRange()).copy()