diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 3890f1448..afb7bf8c8 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -440,6 +440,29 @@ describe "Editor", -> editor.renderedLines.trigger mousedownEvent(editor: editor, point: [3, 12], originalEvent: {detail: 1}, shiftKey: true) expect(editor.getSelectedBufferRange()).toEqual [[3, 10], [3, 12]] + describe "when clicking between a word and a non-word", -> + it "selects the word", -> + expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) + editor.renderedLines.trigger mousedownEvent(editor: editor, point: [1, 21], originalEvent: {detail: 1}) + editor.renderedLines.trigger 'mouseup' + editor.renderedLines.trigger mousedownEvent(editor: editor, point: [1, 21], originalEvent: {detail: 2}) + editor.renderedLines.trigger 'mouseup' + expect(editor.getSelectedText()).toBe "function" + + editor.setCursorBufferPosition([0, 0]) + editor.renderedLines.trigger mousedownEvent(editor: editor, point: [1, 22], originalEvent: {detail: 1}) + editor.renderedLines.trigger 'mouseup' + editor.renderedLines.trigger mousedownEvent(editor: editor, point: [1, 22], originalEvent: {detail: 2}) + editor.renderedLines.trigger 'mouseup' + expect(editor.getSelectedText()).toBe "items" + + editor.setCursorBufferPosition([0, 0]) + editor.renderedLines.trigger mousedownEvent(editor: editor, point: [0, 28], originalEvent: {detail: 1}) + editor.renderedLines.trigger 'mouseup' + editor.renderedLines.trigger mousedownEvent(editor: editor, point: [0, 28], originalEvent: {detail: 2}) + editor.renderedLines.trigger 'mouseup' + expect(editor.getSelectedText()).toBe "{" + describe "triple/quardruple/etc-click", -> it "selects the line under the cursor", -> expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) diff --git a/src/cursor.coffee b/src/cursor.coffee index 01a0b0b61..0b3941f22 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -102,10 +102,21 @@ class Cursor # Public: Returns the visibility of the cursor. isVisible: -> @visible - # Public: Returns a RegExp of what the cursor considers a "word" - wordRegExp: -> - nonWordCharacters = config.get("editor.nonWordCharacters") - new RegExp("^[\t ]*$|[^\\s#{_.escapeRegExp(nonWordCharacters)}]+|[#{_.escapeRegExp(nonWordCharacters)}]+", "g") + # Public: Get the RegExp used by the cursor to determine what a "word" is. + # + # * options: + # + includeNonWordCharacters: + # A Boolean indicating whether to include non-word characters in the regex. + # + # Returns a RegExp. + wordRegExp: ({includeNonWordCharacters}={})-> + includeNonWordCharacters ?= true + nonWordCharacters = config.get('editor.nonWordCharacters') + segments = ["^[\t ]*$"] + segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+") + if includeNonWordCharacters + segments.push("[#{_.escapeRegExp(nonWordCharacters)}]+") + new RegExp(segments.join("|"), "g") # Public: Identifies if this cursor is the last in the {EditSession}. # @@ -126,6 +137,25 @@ class Cursor range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]] /^\s+$/.test @editSession.getTextInBufferRange(range) + # Public: Returns whether the cursor is currently between a word and non-word + # character. The non-word characters are defined by the + # `editor.nonWordCharacters` config value. + # + # This method returns false if the character before or after the cursor is + # whitespace. + # + # Returns a Boolean. + isBetweenWordAndNonWord: -> + return false if @isAtBeginningOfLine() or @isAtEndOfLine() + + {row, column} = @getBufferPosition() + range = [[row, column - 1], [row, column + 1]] + [before, after] = @editSession.getTextInBufferRange(range) + return false if /\s/.test(before) or /\s/.test(after) + + nonWordCharacters = config.get('editor.nonWordCharacters').split('') + _.contains(nonWordCharacters, before) isnt _.contains(nonWordCharacters, after) + # Public: Returns whether this cursor is between a word's start and end. isInsideWord: -> {row, column} = @getBufferPosition() @@ -280,6 +310,9 @@ class Cursor # * options: # + wordRegex: # A RegExp indicating what constitutes a "word" (default: {.wordRegExp}) + # + includeNonWordCharacters: + # A Boolean indicating whether to include non-word characters in the + # default word regex. Has no effect if wordRegex is set. # # Returns a {Range}. getBeginningOfCurrentWordBufferPosition: (options = {}) -> @@ -289,7 +322,7 @@ class Cursor scanRange = [[previousNonBlankRow, 0], currentBufferPosition] beginningOfWordPosition = null - @editSession.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => + @editSession.backwardsScanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) => if range.end.isGreaterThanOrEqual(currentBufferPosition) or allowPrevious beginningOfWordPosition = range.start if not beginningOfWordPosition?.isEqual(currentBufferPosition) @@ -297,7 +330,7 @@ class Cursor beginningOfWordPosition or currentBufferPosition - # Public: Retrieves buffer position of previous word boundry. It might be on + # Public: Retrieves buffer position of previous word boundary. It might be on # the current word, or the previous word. getPreviousWordBoundaryBufferPosition: (options = {}) -> currentBufferPosition = @getBufferPosition() @@ -319,7 +352,7 @@ class Cursor beginningOfWordPosition or currentBufferPosition - # Public: Retrieves buffer position of the next word boundry. It might be on + # Public: Retrieves buffer position of the next word boundary. It might be on # the current word, or the previous word. getMoveNextWordBoundaryBufferPosition: (options = {}) -> currentBufferPosition = @getBufferPosition() @@ -345,6 +378,9 @@ class Cursor # * options: # + wordRegex: # A RegExp indicating what constitutes a "word" (default: {.wordRegExp}) + # + includeNonWordCharacters: + # A Boolean indicating whether to include non-word characters in the + # default word regex. Has no effect if wordRegex is set. # # Returns a {Range}. getEndOfCurrentWordBufferPosition: (options = {}) -> @@ -353,7 +389,7 @@ class Cursor scanRange = [currentBufferPosition, @editSession.getEofBufferPosition()] endOfWordPosition = null - @editSession.scanInBufferRange (options.wordRegex ? @wordRegExp()), scanRange, ({range, stop}) => + @editSession.scanInBufferRange (options.wordRegex ? @wordRegExp(options)), scanRange, ({range, stop}) => if range.start.isLessThanOrEqual(currentBufferPosition) or allowNext endOfWordPosition = range.end if not endOfWordPosition?.isEqual(currentBufferPosition) diff --git a/src/selection.coffee b/src/selection.coffee index c48d05cf9..7b184a64c 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -115,6 +115,8 @@ class Selection selectWord: -> options = {} options.wordRegex = /[\t ]*/ if @cursor.isSurroundedByWhitespace() + if @cursor.isBetweenWordAndNonWord() + options.includeNonWordCharacters = false @setBufferRange(@cursor.getCurrentWordBufferRange(options)) @wordwise = true