diff --git a/spec/text-editor-registry-spec.js b/spec/text-editor-registry-spec.js index 985f9b0a9..77ce3ad38 100644 --- a/spec/text-editor-registry-spec.js +++ b/spec/text-editor-registry-spec.js @@ -394,5 +394,28 @@ describe('TextEditorRegistry', function () { atom.config.set('editor.undoGroupingInterval', 300) expect(editor.getUndoGroupingInterval()).toBe(300) }) + + it('sets the non-word characters based on the config', function () { + atom.config.set('editor.nonWordCharacters', '(){}') + registry.maintainConfig(editor) + expect(editor.getNonWordCharacters()).toBe('(){}') + + atom.config.set('editor.nonWordCharacters', '(){}[]') + expect(editor.getNonWordCharacters()).toBe('(){}[]') + }) + + it('gives the editor a scoped-settings delegate based on the config', function () { + atom.config.set('editor.nonWordCharacters', '()') + atom.config.set('editor.nonWordCharacters', '(){}', {scopeSelector: '.a.b .c.d'}) + atom.config.set('editor.nonWordCharacters', '(){}[]', {scopeSelector: '.e.f *'}) + + registry.maintainConfig(editor) + + let delegate = editor.getScopedSettingsDelegate() + + expect(delegate.getNonWordCharacters(['a.b', 'c.d'])).toBe('(){}') + expect(delegate.getNonWordCharacters(['e.f', 'g.h'])).toBe('(){}[]') + expect(delegate.getNonWordCharacters(['i.j'])).toBe('()') + }) }) }) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 37f5bbe77..26caa6699 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1645,28 +1645,29 @@ describe "TextEditor", -> editor.selectWordsContainingCursors() expect(editor.getSelectedBufferRange()).toEqual [[12, 2], [12, 6]] - describe 'when editor.nonWordCharacters is set scoped to a grammar', -> - coffeeEditor = null - beforeEach -> - waitsForPromise -> - atom.packages.activatePackage('language-coffee-script') - waitsForPromise -> - atom.workspace.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o + it "selects words based on the non-word characters configured at the cursor's current scope", -> + editor.setText("one-one; 'two-two'; three-three"); - it 'selects the correct surrounding word for the given scoped setting', -> - coffeeEditor.setCursorBufferPosition [0, 9] # in the middle of quicksort - coffeeEditor.selectWordsContainingCursors() - expect(coffeeEditor.getSelectedBufferRange()).toEqual [[0, 6], [0, 15]] + editor.setCursorBufferPosition([0, 1]) + editor.addCursorAtBufferPosition([0, 12]) - atom.config.set 'editor.nonWordCharacters', 'qusort', scopeSelector: '.source.coffee' + scopeDescriptors = editor.getCursors().map (c) -> c.getScopeDescriptor() + expect(scopeDescriptors[0].getScopesArray()).toEqual(['source.js']) + expect(scopeDescriptors[1].getScopesArray()).toEqual(['source.js', 'string.quoted.single.js']) - coffeeEditor.setCursorBufferPosition [0, 9] - coffeeEditor.selectWordsContainingCursors() - expect(coffeeEditor.getSelectedBufferRange()).toEqual [[0, 8], [0, 11]] + editor.setScopedSettingsDelegate({ + getNonWordCharacters: (scopes) -> + result = '/\()"\':,.;<>~!@#$%^&*|+=[]{}`?' + if (scopes.some (scope) -> scope.startsWith('string')) + result + else + result + '-' + }) - editor.setCursorBufferPosition [0, 7] - editor.selectWordsContainingCursors() - expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [0, 13]] + editor.selectWordsContainingCursors() + + expect(editor.getSelections()[0].getText()).toBe('one') + expect(editor.getSelections()[1].getText()).toBe('two-two') describe ".selectToFirstCharacterOfLine()", -> it "moves to the first character of the current line or the beginning of the line if it's already on the first character", -> diff --git a/src/cursor.coffee b/src/cursor.coffee index e2eaa8c05..006801cf8 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -18,7 +18,7 @@ class Cursor extends Model visible: true # Instantiated by a {TextEditor} - constructor: ({@editor, @marker, @config, id}) -> + constructor: ({@editor, @marker, id}) -> @emitter = new Emitter @assignId(id) @@ -160,8 +160,8 @@ class Cursor extends Model [before, after] = @editor.getTextInBufferRange(range) return false if /\s/.test(before) or /\s/.test(after) - nonWordCharacters = @config.get('editor.nonWordCharacters', scope: @getScopeDescriptor()).split('') - _.contains(nonWordCharacters, before) isnt _.contains(nonWordCharacters, after) + nonWordCharacters = @getNonWordCharacters() + nonWordCharacters.includes(before) isnt nonWordCharacters.includes(after) # Public: Returns whether this cursor is between a word's start and end. # @@ -608,9 +608,7 @@ class Cursor extends Model # # Returns a {RegExp}. wordRegExp: (options) -> - scope = @getScopeDescriptor() - nonWordCharacters = _.escapeRegExp(@config.get('editor.nonWordCharacters', {scope})) - + nonWordCharacters = _.escapeRegExp(@getNonWordCharacters()) source = "^[\t ]*$|[^\\s#{nonWordCharacters}]+" if options?.includeNonWordCharacters ? true source += "|" + "[#{nonWordCharacters}]+" @@ -624,7 +622,7 @@ class Cursor extends Model # # Returns a {RegExp}. subwordRegExp: (options={}) -> - nonWordCharacters = @config.get('editor.nonWordCharacters', scope: @getScopeDescriptor()) + nonWordCharacters = @getNonWordCharacters() lowercaseLetters = 'a-z\\u00DF-\\u00F6\\u00F8-\\u00FF' uppercaseLetters = 'A-Z\\u00C0-\\u00D6\\u00D8-\\u00DE' snakeCamelSegment = "[#{uppercaseLetters}]?[#{lowercaseLetters}]+" @@ -647,6 +645,14 @@ class Cursor extends Model Section: Private ### + getNonWordCharacters: -> + ( + @editor + .scopedSettingsDelegate + ?.getNonWordCharacters?(@getScopeDescriptor().getScopesArray()) ? + @editor.getNonWordCharacters() + ) + changePosition: (options, fn) -> @clearSelection(autoscroll: false) fn() diff --git a/src/text-editor-registry.js b/src/text-editor-registry.js index 082aa5f28..af0d2059a 100644 --- a/src/text-editor-registry.js +++ b/src/text-editor-registry.js @@ -18,6 +18,7 @@ const EDITOR_SETTER_NAMES_BY_SETTING_KEY = [ ['editor.autoIndentOnPaste', 'setAutoIndentOnPaste'], ['editor.scrollPastEnd', 'setScrollPastEnd'], ['editor.undoGroupingInterval', 'setUndoGroupingInterval'], + ['editor.nonWordCharacters', 'setNonWordCharacters'], ] // Experimental: This global registry tracks registered `TextEditors`. @@ -39,6 +40,7 @@ export default class TextEditorRegistry { this.emitter = new Emitter() this.scopesWithConfigSubscriptions = new Set() this.editorsWithMaintainedConfig = new Set() + this.scopedSettingsDelegate = new ScopedSettingsDelegate(config) } destroy () { @@ -90,6 +92,7 @@ export default class TextEditorRegistry { maintainConfig (editor) { this.editorsWithMaintainedConfig.add(editor) this.subscribeToSettingsForEditorScope(editor) + editor.setScopedSettingsDelegate(this.scopedSettingsDelegate) const configOptions = {scope: editor.getRootScopeDescriptor()} for (const [settingKey, setterName] of EDITOR_SETTER_NAMES_BY_SETTING_KEY) { @@ -165,3 +168,13 @@ function shouldEditorUseSoftTabs (editor, tabType, softTabs) { } } } + +class ScopedSettingsDelegate { + constructor (config) { + this.config = config + } + + getNonWordCharacters(scope) { + return this.config.get('editor.nonWordCharacters', {scope: scope}) + } +} diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 71631bffc..c9b2e19cf 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -154,6 +154,7 @@ class TextEditor extends Model @autoIndent ?= true @autoIndentOnPaste ?= true @undoGroupingInterval ?= 300 + @nonWordCharacters ?= "/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-…" @buffer ?= new TextBuffer @tokenizedBuffer ?= new TokenizedBuffer({ @@ -2094,7 +2095,7 @@ class TextEditor extends Model # Add a cursor based on the given {DisplayMarker}. addCursor: (marker) -> - cursor = new Cursor(editor: this, marker: marker, config: @config) + cursor = new Cursor(editor: this, marker: marker) @cursors.push(cursor) @cursorsByMarkerId.set(marker.id, cursor) @decorateMarker(marker, type: 'line-number', class: 'cursor-line') @@ -3328,6 +3329,10 @@ class TextEditor extends Model Section: Config ### + setScopedSettingsDelegate: (@scopedSettingsDelegate) -> + + getScopedSettingsDelegate: -> @scopedSettingsDelegate + setAutoIndent: (@autoIndent) -> setAutoIndentOnPaste: (@autoIndentOnPaste) -> @@ -3344,6 +3349,10 @@ class TextEditor extends Model getUndoGroupingInterval: -> @undoGroupingInterval + setNonWordCharacters: (@nonWordCharacters) -> + + getNonWordCharacters: -> @nonWordCharacters + ### Section: Event Handlers ###