From e5b9b1f3cfef1c35e3a71c38eb6a1bf4c33daca7 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 27 Sep 2013 14:57:17 -0700 Subject: [PATCH 01/10] 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 02/10] :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 03/10] :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 04/10] 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: -> From e3b34b8ddc31ecb7f5483d6c5228cddc6253dd0c Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 27 Sep 2013 16:05:50 -0700 Subject: [PATCH 05/10] Remove light/dark style direction --- CONTRIBUTING.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4e56232bc..f71699b60 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,8 +16,6 @@ styleguides * Include thoughtfully worded [Jasmine](http://pivotal.github.com/jasmine/) specs - * Style new elements in both the light and dark default themes when - appropriate * Add 3rd-party packages as a `package.json` dependency * Commit messages are in the present tense * Commit messages that improve the format of the code start with :lipstick: From 4f8198b53abdce04201a1bb44e2ff68d60d33044 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Fri, 27 Sep 2013 16:08:21 -0700 Subject: [PATCH 06/10] Mention double click improvement --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 396d90567..c2dbb29b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +* Improved: Double-click selection behavior between word/non-word * Added: Solarized theme now bundled by default * Added: Base16 Tomorrow Dark theme now bundled by default From cc185fc9a7c02c0906e5b712ff1475f6d0d1457a Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sun, 29 Sep 2013 20:13:20 +0800 Subject: [PATCH 07/10] Update to atom-shell v0.5.3. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a75decff8..847d9209f 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "bugs": { "url": "https://github.com/atom/atom/issues" }, - "atomShellVersion": "0.5.1", + "atomShellVersion": "0.5.3", "dependencies": { "async": "0.2.6", "bootstrap": "git://github.com/twbs/bootstrap.git#v3.0.0", From 6cad026a56dcee9a4f474290b92f63d6fbc77841 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Sun, 29 Sep 2013 21:55:30 -0700 Subject: [PATCH 08/10] Mention find-and-replace --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2dbb29b4..96b6201dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,4 @@ +* Improved: Faster and better looking find and replace * Improved: Double-click selection behavior between word/non-word * Added: Solarized theme now bundled by default * Added: Base16 Tomorrow Dark theme now bundled by default From b120109cbf07408488e06aabd1d7abdd622e4d47 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Sun, 29 Sep 2013 22:08:45 -0700 Subject: [PATCH 09/10] Expose shell.beep() through atom global --- src/atom.coffee | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/atom.coffee b/src/atom.coffee index 5c68888bc..723f9a7f4 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -4,6 +4,7 @@ _ = require './underscore-extensions' Package = require './package' ipc = require 'ipc' remote = require 'remote' +shell = require 'shell' crypto = require 'crypto' path = require 'path' dialog = remote.require 'dialog' @@ -284,6 +285,9 @@ window.atom = crashRenderProcess: -> process.crash() + beep: -> + shell.beep() + requireUserInitScript: -> userInitScriptPath = path.join(config.configDirPath, "user.coffee") try From b9328c82e55d12e9e71c9a52a641f3c197cefb31 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Mon, 30 Sep 2013 08:56:12 -0700 Subject: [PATCH 10/10] Remove themes folder from .atom template Themes now go in ~/.atom/packages --- dot-atom/themes/README.md | 1 - spec/config-spec.coffee | 1 - 2 files changed, 2 deletions(-) delete mode 100644 dot-atom/themes/README.md diff --git a/dot-atom/themes/README.md b/dot-atom/themes/README.md deleted file mode 100644 index dbedd5e13..000000000 --- a/dot-atom/themes/README.md +++ /dev/null @@ -1 +0,0 @@ -All themes in this directory will be automatically loaded diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 7e8129746..7e60ba0d3 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -181,7 +181,6 @@ describe "Config", -> expect(fs.exists(config.configDirPath)).toBeTruthy() expect(fs.exists(path.join(config.configDirPath, 'packages'))).toBeTruthy() expect(fs.exists(path.join(config.configDirPath, 'snippets'))).toBeTruthy() - expect(fs.exists(path.join(config.configDirPath, 'themes'))).toBeTruthy() expect(fs.isFileSync(path.join(config.configDirPath, 'config.cson'))).toBeTruthy() describe ".loadUserConfig()", ->