From e5b9b1f3cfef1c35e3a71c38eb6a1bf4c33daca7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 27 Sep 2013 14:57:17 -0700 Subject: [PATCH 1/4] Favor the word when double clicking between word/non-word Closes #523 --- spec/editor-spec.coffee | 16 +++++++++++++++ src/cursor.coffee | 43 +++++++++++++++++++++++++++++++++++------ src/selection.coffee | 2 ++ 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 3890f1448..5eb3cc84e 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -440,6 +440,22 @@ 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" + 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..b188e2685 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 determin 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,19 @@ class Cursor range = [[row, Math.min(0, column - 1)], [row, Math.max(0, column + 1)]] /^\s+$/.test @editSession.getTextInBufferRange(range) + isBetweenWordAndNonWord: -> + return false if @isAtBeginningOfLine() or @isAtEndOfLine() + + {row, column} = @getBufferPosition() + range = [[row, column - 1], [row, column + 1]] + [before, after] = @editSession.getTextInBufferRange(range) + if before and after + nonWordCharacters = config.get('editor.nonWordCharacters').split('') + _.contains(nonWordCharacters, before) isnt _.contains(nonWordCharacters, after) + else + false + + # Public: Returns whether this cursor is between a word's start and end. isInsideWord: -> {row, column} = @getBufferPosition() @@ -280,6 +304,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 +316,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) @@ -345,6 +372,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 +383,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) @@ -388,6 +418,7 @@ class Cursor getCurrentWordBufferRange: (options={}) -> startOptions = _.extend(_.clone(options), allowPrevious: false) endOptions = _.extend(_.clone(options), allowNext: false) + new Range(@getBeginningOfCurrentWordBufferPosition(startOptions), @getEndOfCurrentWordBufferPosition(endOptions)) # Public: Returns the buffer Range for the current line. 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 From 542d17913d1dd8636c67bda2916abb5a52cad7a2 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 27 Sep 2013 15:07:19 -0700 Subject: [PATCH 2/4] :memo: doc Cursor.isBetweenWordAndNonWord() --- src/cursor.coffee | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cursor.coffee b/src/cursor.coffee index b188e2685..44081ef52 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -137,6 +137,11 @@ 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. + # + # Returns a Boolean. isBetweenWordAndNonWord: -> return false if @isAtBeginningOfLine() or @isAtEndOfLine() @@ -149,7 +154,6 @@ class Cursor else false - # Public: Returns whether this cursor is between a word's start and end. isInsideWord: -> {row, column} = @getBufferPosition() @@ -418,7 +422,6 @@ class Cursor getCurrentWordBufferRange: (options={}) -> startOptions = _.extend(_.clone(options), allowPrevious: false) endOptions = _.extend(_.clone(options), allowNext: false) - new Range(@getBeginningOfCurrentWordBufferPosition(startOptions), @getEndOfCurrentWordBufferPosition(endOptions)) # Public: Returns the buffer Range for the current line. From cd4e64a8f8ca40471d0beafb426b6d5294213d03 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 27 Sep 2013 15:15:25 -0700 Subject: [PATCH 3/4] :memo: correct typos in comments --- src/cursor.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cursor.coffee b/src/cursor.coffee index 44081ef52..621a6f1b4 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -102,7 +102,7 @@ class Cursor # Public: Returns the visibility of the cursor. isVisible: -> @visible - # Public: Get the RegExp used by the cursor to determin what a "word" is. + # Public: Get the RegExp used by the cursor to determine what a "word" is. # # * options: # + includeNonWordCharacters: @@ -328,7 +328,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() @@ -350,7 +350,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() From e2daef477cf261a03b36c91ae1c022106f16efab Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 27 Sep 2013 15:27:11 -0700 Subject: [PATCH 4/4] Return false if either character is whitespace --- spec/editor-spec.coffee | 7 +++++++ src/cursor.coffee | 12 +++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 5eb3cc84e..afb7bf8c8 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -456,6 +456,13 @@ describe "Editor", -> 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 621a6f1b4..0b3941f22 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -141,6 +141,9 @@ class Cursor # 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() @@ -148,11 +151,10 @@ class Cursor {row, column} = @getBufferPosition() range = [[row, column - 1], [row, column + 1]] [before, after] = @editSession.getTextInBufferRange(range) - if before and after - nonWordCharacters = config.get('editor.nonWordCharacters').split('') - _.contains(nonWordCharacters, before) isnt _.contains(nonWordCharacters, after) - else - false + 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: ->