From 416898e27868ecb785009b7d2a77a24c833fb3ec Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 3 Oct 2014 11:33:36 -0700 Subject: [PATCH 01/26] Scope usage of editor.nonWordCharacters --- spec/text-editor-spec.coffee | 29 +++++++++++++++++++++++++++++ src/cursor.coffee | 4 ++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 1f1f8dcbd..6a94cacbe 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3686,3 +3686,32 @@ describe "TextEditor", -> editor.setPlaceholderText('OK') expect(handler).toHaveBeenCalledWith 'OK' expect(editor.getPlaceholderText()).toBe 'OK' + + describe '.selectWordsContainingCursors()', -> + it 'selects the word containing the cursor', -> + editor.setCursorBufferPosition [0, 7] # in the middle of quicksort + editor.selectWordsContainingCursors() + expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [0, 13]] + + describe 'when editor.nonWordCharacters is set scoped to editor.coffee', -> + coffeeEditor = null + beforeEach -> + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + waitsForPromise -> + atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o + + it 'selects to the bounds set by the new config in coffee files, but not in js files', -> + coffeeEditor.setCursorBufferPosition [0, 9] # in the middle of quicksort + coffeeEditor.selectWordsContainingCursors() + expect(coffeeEditor.getSelectedBufferRange()).toEqual [[0, 6], [0, 15]] + + atom.config.set '.source.coffee', 'editor.nonWordCharacters', 'qusort' + + coffeeEditor.setCursorBufferPosition [0, 9] + coffeeEditor.selectWordsContainingCursors() + expect(coffeeEditor.getSelectedBufferRange()).toEqual [[0, 8], [0, 11]] + + editor.setCursorBufferPosition [0, 7] + editor.selectWordsContainingCursors() + expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [0, 13]] diff --git a/src/cursor.coffee b/src/cursor.coffee index 7ad9ac5f6..2bda09219 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -198,7 +198,7 @@ class Cursor extends Model [before, after] = @editor.getTextInBufferRange(range) return false if /\s/.test(before) or /\s/.test(after) - nonWordCharacters = atom.config.get('editor.nonWordCharacters').split('') + nonWordCharacters = atom.config.get(@getScopes(), 'editor.nonWordCharacters').split('') _.contains(nonWordCharacters, before) isnt _.contains(nonWordCharacters, after) # Public: Returns whether this cursor is between a word's start and end. @@ -617,7 +617,7 @@ class Cursor extends Model # Returns a {RegExp}. wordRegExp: ({includeNonWordCharacters}={}) -> includeNonWordCharacters ?= true - nonWordCharacters = atom.config.get('editor.nonWordCharacters') + nonWordCharacters = atom.config.get(@getScopes(), 'editor.nonWordCharacters') segments = ["^[\t ]*$"] segments.push("[^\\s#{_.escapeRegExp(nonWordCharacters)}]+") if includeNonWordCharacters From 339cb022691010844055af84b6fb54aaa8f96296 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 3 Oct 2014 15:48:49 -0700 Subject: [PATCH 02/26] Scope editor.tabLength --- spec/text-editor-spec.coffee | 19 +++++++++++++++++++ src/display-buffer.coffee | 4 ++-- src/text-editor.coffee | 14 +++++++++----- src/tokenized-buffer.coffee | 15 ++++----------- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 6a94cacbe..01ba9e65e 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3038,6 +3038,25 @@ describe "TextEditor", -> atom.workspace.open(null, softTabs: false).then (editor) -> expect(editor.getSoftTabs()).toBeFalsy() + describe '.getTabLength(scopeDescriptor)', -> + describe 'when scoped settings are used', -> + coffeeEditor = null + beforeEach -> + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + waitsForPromise -> + atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o + + it 'will return correct values based on the scope of the set grammars', -> + atom.config.set '.source.coffee', 'editor.tabLength', 6 + atom.config.set '.source.coffee .class', 'editor.tabLength', 4 + + expect(editor.getTabLength()).toBe 2 + expect(coffeeEditor.getTabLength()).toBe 6 + + coffeeEditor.setCursorBufferPosition [0, 10] + expect(coffeeEditor.getTabLength(coffeeEditor.scopesAtCursor())).toBe 4 + describe ".indentLevelForLine(line)", -> it "returns the indent level when the line has only leading whitespace", -> expect(editor.indentLevelForLine(" hello")).toBe(2) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index fc465e5d7..f32c4efd4 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -396,8 +396,8 @@ class DisplayBuffer extends Model # Retrieves the current tab length. # # Returns a {Number}. - getTabLength: -> - @tokenizedBuffer.getTabLength() + getTabLength: (scopeDescriptor) -> + @tokenizedBuffer.getTabLength(scopeDescriptor) # Specifies the tab length. # diff --git a/src/text-editor.coffee b/src/text-editor.coffee index d1fca0412..eec6432e1 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2173,12 +2173,16 @@ class TextEditor extends Model # Essential: Get the on-screen length of tab characters. # - # Returns a {Number}. - getTabLength: -> @displayBuffer.getTabLength() - - # Essential: Set the on-screen length of tab characters. + # * `scopeDescriptor` (optional) {Array} of {String}s. # - # * `tabLength` {Number} length of a single tab + # Returns a {Number}. + getTabLength: (scopeDescriptor) -> @displayBuffer.getTabLength(scopeDescriptor) + + # Essential: Set the on-screen length of tab characters. Setting this to a + # {Number} This will override the `editor.tabLength` setting. + # + # * `tabLength` {Number} length of a single tab. Setting to `null` will + # fallback to using the `editor.tabLength` config setting setTabLength: (tabLength) -> @displayBuffer.setTabLength(tabLength) # Extended: Determine if the buffer uses hard or soft tabs. diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index ae2fa559a..23a6fe882 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -25,16 +25,14 @@ class TokenizedBuffer extends Model constructor: ({@buffer, @tabLength, @invisibles}) -> @emitter = new Emitter - @tabLength ?= atom.config.get('editor.tabLength') - @subscribe atom.syntax.onDidAddGrammar(@grammarAddedOrUpdated) @subscribe atom.syntax.onDidUpdateGrammar(@grammarAddedOrUpdated) @subscribe @buffer.onDidChange (e) => @handleBufferChange(e) @subscribe @buffer.onDidChangePath (@bufferPath) => @reloadGrammar() + # TODO: FIXME: make this work for scoped properties @subscribe @$tabLength.changes, (tabLength) => @retokenizeLines() - @subscribe atom.config.onDidChange 'editor.tabLength', ({newValue}) => @setTabLength(newValue) @reloadGrammar() @@ -81,6 +79,7 @@ class TokenizedBuffer extends Model return if grammar is @grammar @unsubscribe(@grammar) if @grammar @grammar = grammar + @grammarScopeDescriptor = [@grammar.scopeName] @currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @buffer.getText()) @subscribe @grammar.onDidUpdate => @retokenizeLines() @retokenizeLines() @@ -112,15 +111,9 @@ class TokenizedBuffer extends Model setVisible: (@visible) -> @tokenizeInBackground() if @visible - # Retrieves the current tab length. - # - # Returns a {Number}. - getTabLength: -> - @tabLength + getTabLength: (scopeDescriptor) -> + @tabLength ? atom.config.get(scopeDescriptor ? @grammarScopeDescriptor, 'editor.tabLength') - # Specifies the tab length. - # - # tabLength - A {Number} that defines the new tab length. setTabLength: (@tabLength) -> setInvisibles: (invisibles) -> From 8cd217e50afe48d224aa7f8a29f16835e4ec8499 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Fri, 3 Oct 2014 16:22:16 -0700 Subject: [PATCH 03/26] Handle changes to the tabLength setting --- spec/text-editor-spec.coffee | 18 ++++++++++++++++++ src/text-editor.coffee | 3 +-- src/tokenized-buffer.coffee | 10 ++++++---- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 01ba9e65e..385fa12eb 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3057,6 +3057,24 @@ describe "TextEditor", -> coffeeEditor.setCursorBufferPosition [0, 10] expect(coffeeEditor.getTabLength(coffeeEditor.scopesAtCursor())).toBe 4 + it 'will retokenize when the tab length is updated', -> + expect(editor.getTabLength()).toBe 2 + expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 + + atom.config.set '.source.js', 'editor.tabLength', 6 + expect(editor.getTabLength()).toBe 6 + expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6 + + it 'will update the tab length when the grammar changes', -> + atom.config.set '.source.coffee', 'editor.tabLength', 6 + + expect(editor.getTabLength()).toBe 2 + expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 + + editor.setGrammar(coffeeEditor.getGrammar()) + expect(editor.getTabLength()).toBe 6 + expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6 + describe ".indentLevelForLine(line)", -> it "returns the indent level when the line has only leading whitespace", -> expect(editor.indentLevelForLine(" hello")).toBe(2) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index eec6432e1..3d6934cc8 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -485,10 +485,9 @@ class TextEditor extends Model # Create an {TextEditor} with its initial state based on this object copy: -> - tabLength = @getTabLength() displayBuffer = @displayBuffer.copy() softTabs = @getSoftTabs() - newEditor = new TextEditor({@buffer, displayBuffer, tabLength, softTabs, suppressCursorCreation: true, registerEditor: true}) + newEditor = new TextEditor({@buffer, displayBuffer, @tabLength, softTabs, suppressCursorCreation: true, registerEditor: true}) for marker in @findMarkers(editorId: @id) marker.copy(editorId: newEditor.id, preserveFolds: true) newEditor diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 23a6fe882..4f7ba6c65 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -31,10 +31,6 @@ class TokenizedBuffer extends Model @subscribe @buffer.onDidChange (e) => @handleBufferChange(e) @subscribe @buffer.onDidChangePath (@bufferPath) => @reloadGrammar() - # TODO: FIXME: make this work for scoped properties - @subscribe @$tabLength.changes, (tabLength) => @retokenizeLines() - @subscribe atom.config.onDidChange 'editor.tabLength', ({newValue}) => @setTabLength(newValue) - @reloadGrammar() serializeParams: -> @@ -83,6 +79,12 @@ class TokenizedBuffer extends Model @currentGrammarScore = score ? grammar.getScore(@buffer.getPath(), @buffer.getText()) @subscribe @grammar.onDidUpdate => @retokenizeLines() @retokenizeLines() + + @grammarTabLengthSubscription?.dispose() + @grammarTabLengthSubscription = atom.config.onDidChange @grammarScopeDescriptor, 'editor.tabLength', => + @retokenizeLines() + @subscribe @grammarTabLengthSubscription + @emit 'grammar-changed', grammar @emitter.emit 'did-change-grammar', grammar From 857fd5eaf447704cdd7dba85634d55f315e31292 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 6 Oct 2014 14:13:28 -0700 Subject: [PATCH 04/26] Retokenize when setTabLength() was called. --- spec/text-editor-spec.coffee | 10 +++++++++- src/tokenized-buffer.coffee | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 385fa12eb..24ce7e33c 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3057,7 +3057,15 @@ describe "TextEditor", -> coffeeEditor.setCursorBufferPosition [0, 10] expect(coffeeEditor.getTabLength(coffeeEditor.scopesAtCursor())).toBe 4 - it 'will retokenize when the tab length is updated', -> + it 'will retokenize when the tab length is updated via .setTabLength()', -> + expect(editor.getTabLength()).toBe 2 + expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 + + editor.setTabLength(6) + expect(editor.getTabLength()).toBe 6 + expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6 + + it 'will retokenize when the editor.tabLength setting is updated', -> expect(editor.getTabLength()).toBe 2 expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 4f7ba6c65..c79712edf 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -117,6 +117,7 @@ class TokenizedBuffer extends Model @tabLength ? atom.config.get(scopeDescriptor ? @grammarScopeDescriptor, 'editor.tabLength') setTabLength: (@tabLength) -> + @retokenizeLines() setInvisibles: (invisibles) -> unless _.isEqual(invisibles, @invisibles) From fbcaabacab1f70a8fcdfc28d7e0f23b723ab536d Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 6 Oct 2014 14:28:49 -0700 Subject: [PATCH 05/26] Fix weird spec --- spec/text-editor-spec.coffee | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 24ce7e33c..55c466cc8 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3047,6 +3047,10 @@ describe "TextEditor", -> waitsForPromise -> atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o + afterEach: -> + atom.packages.deactivatePackages() + atom.packages.unloadPackages() + it 'will return correct values based on the scope of the set grammars', -> atom.config.set '.source.coffee', 'editor.tabLength', 6 atom.config.set '.source.coffee .class', 'editor.tabLength', 4 @@ -3297,10 +3301,18 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(5)).toBe " i=1" describe "soft and hard tabs", -> + afterEach -> + atom.packages.deactivatePackages() + atom.packages.unloadPackages() + it "resets the tab style when tokenization is complete", -> editor.destroy() - atom.project.open('sample-with-tabs-and-leading-comment.coffee').then (o) -> editor = o - expect(editor.softTabs).toBe true + + waitsForPromise -> + atom.project.open('sample-with-tabs-and-leading-comment.coffee').then (o) -> editor = o + + runs -> + expect(editor.softTabs).toBe true waitsForPromise -> atom.packages.activatePackage('language-coffee-script') @@ -3308,9 +3320,6 @@ describe "TextEditor", -> runs -> expect(editor.softTabs).toBe false - atom.packages.deactivatePackage('language-coffee-script') - atom.packages.unloadPackage('language-coffee-script') - describe ".destroy()", -> it "destroys all markers associated with the edit session", -> expect(buffer.getMarkerCount()).toBeGreaterThan 0 From 938f216cab38046e60afcc7d979afc607eeec8ef Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 6 Oct 2014 15:30:35 -0700 Subject: [PATCH 06/26] Scope softWrap, softWrapAtPreferredLineLength, preferredLineLength `editor.*` config settings --- spec/text-editor-spec.coffee | 49 ++++++++++++++++++++++++++++++++++++ src/display-buffer.coffee | 41 +++++++++++++++++++++--------- 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 55c466cc8..297dfee52 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3769,3 +3769,52 @@ describe "TextEditor", -> editor.setCursorBufferPosition [0, 7] editor.selectWordsContainingCursors() expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [0, 13]] + + describe 'scoped config settings', -> + coffeeEditor = null + beforeEach -> + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + waitsForPromise -> + atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o + + afterEach: -> + atom.packages.deactivatePackages() + atom.packages.unloadPackages() + + describe 'soft wrap config settings', -> + beforeEach -> + atom.config.set '.source.coffee', 'editor.softWrap', true + atom.config.set '.source.coffee', 'editor.preferredLineLength', 17 + atom.config.set '.source.coffee', 'editor.softWrapAtPreferredLineLength', true + + editor.setEditorWidthInChars(20) + coffeeEditor.setEditorWidthInChars(20) + + it 'isSoftWrapped() returns true for coffeescript', -> + expect(editor.isSoftWrapped()).toBe false + expect(coffeeEditor.isSoftWrapped()).toBe true + + it 'correctly wraps coffeescript file', -> + expect(editor.lineTextForScreenRow(2)).toEqual ' if (items.length <= 1) return items;' + expect(coffeeEditor.lineTextForScreenRow(3)).toEqual ' return items ' + + it 'updates the wrapped lines when editor.preferredLineLength changes', -> + atom.config.set '.source.coffee', 'editor.preferredLineLength', 20 + expect(coffeeEditor.lineTextForScreenRow(2)).toEqual ' return items if ' + + it 'updates the wrapped lines when editor.softWrapAtPreferredLineLength changes', -> + atom.config.set '.source.coffee', 'editor.softWrapAtPreferredLineLength', false + expect(coffeeEditor.lineTextForScreenRow(2)).toEqual ' return items if ' + + it 'updates the wrapped lines when editor.softWrap changes', -> + atom.config.set '.source.coffee', 'editor.softWrap', false + expect(coffeeEditor.lineTextForScreenRow(2)).toEqual ' return items if items.length <= 1' + + atom.config.set '.source.coffee', 'editor.softWrap', true + expect(coffeeEditor.lineTextForScreenRow(3)).toEqual ' return items ' + + it 'updates the wrapped lines when the grammar changes', -> + editor.setGrammar(coffeeEditor.getGrammar()) + expect(editor.isSoftWrapped()).toBe true + expect(editor.lineTextForScreenRow(0)).toEqual 'var quicksort = ' diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index f32c4efd4..3409e277c 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -3,7 +3,7 @@ EmitterMixin = require('emissary').Emitter guid = require 'guid' Serializable = require 'serializable' {Model} = require 'theorist' -{Emitter} = require 'event-kit' +{CompositeDisposable, Emitter} = require 'event-kit' {Point, Range} = require 'text-buffer' TokenizedBuffer = require './tokenized-buffer' RowMap = require './row-map' @@ -45,7 +45,6 @@ class DisplayBuffer extends Model @emitter = new Emitter - @softWrapped ?= atom.config.get('editor.softWrap') ? false @tokenizedBuffer ?= new TokenizedBuffer({tabLength, buffer, @invisibles}) @buffer = @tokenizedBuffer.buffer @charWidthsByScope = {} @@ -56,16 +55,27 @@ class DisplayBuffer extends Model @updateAllScreenLines() @createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes()) @subscribe @tokenizedBuffer.onDidChange @handleTokenizedBufferChange + @subscribe @tokenizedBuffer.onDidChangeGrammar @subscribeForSoftWrapConfigChanges @subscribe @buffer.onDidUpdateMarkers @handleBufferMarkersUpdated @subscribe @buffer.onDidCreateMarker @handleBufferMarkerCreated - @subscribe atom.config.onDidChange 'editor.preferredLineLength', => - @updateWrappedScreenLines() if @isSoftWrapped() and atom.config.get('editor.softWrapAtPreferredLineLength') + @subscribeForSoftWrapConfigChanges() + @updateAllScreenLines() - @subscribe atom.config.onDidChange 'editor.softWrapAtPreferredLineLength', => + subscribeForSoftWrapConfigChanges: => + @softWrapConfigSubscriptions?.dispose() + @softWrapConfigSubscriptions = new CompositeDisposable + + scopeDescriptor = @getCurrentScopeDescriptor() + + @softWrapConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrap', => + @updateWrappedScreenLines() + + @softWrapConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrapAtPreferredLineLength', => @updateWrappedScreenLines() if @isSoftWrapped() - @updateAllScreenLines() + @softWrapConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.preferredLineLength', => + @updateWrappedScreenLines() if @isSoftWrapped() and atom.config.get(scopeDescriptor, 'editor.softWrapAtPreferredLineLength') serializeParams: -> id: @id @@ -412,11 +422,15 @@ class DisplayBuffer extends Model if softWrapped isnt @softWrapped @softWrapped = softWrapped @updateWrappedScreenLines() - @emit 'soft-wrap-changed', @softWrapped - @emitter.emit 'did-change-soft-wrapped', @softWrapped - @softWrapped + softWrapped = @isSoftWrapped() + @emit 'soft-wrap-changed', softWrapped + @emitter.emit 'did-change-soft-wrapped', softWrapped + softWrapped + else + @isSoftWrapped() - isSoftWrapped: -> @softWrapped + isSoftWrapped: -> + @softWrapped ? atom.config.get(@getCurrentScopeDescriptor(), 'editor.softWrap') ? false # Set the number of characters that fit horizontally in the editor. # @@ -438,8 +452,8 @@ class DisplayBuffer extends Model @editorWidthInChars getSoftWrapColumn: -> - if atom.config.get('editor.softWrapAtPreferredLineLength') - Math.min(@getEditorWidthInChars(), atom.config.get('editor.preferredLineLength')) + if atom.config.get(@getCurrentScopeDescriptor(), 'editor.softWrapAtPreferredLineLength') + Math.min(@getEditorWidthInChars(), atom.config.get(@getCurrentScopeDescriptor(), 'editor.preferredLineLength')) else @getEditorWidthInChars() @@ -1034,6 +1048,9 @@ class DisplayBuffer extends Model line = @tokenizedLineForScreenRow(row).text console.log row, @bufferRowForScreenRow(row), line, line.length + getCurrentScopeDescriptor: -> + @tokenizedBuffer.grammarScopeDescriptor + handleTokenizedBufferChange: (tokenizedBufferChange) => {start, end, delta, bufferChange} = tokenizedBufferChange @updateScreenLines(start, end + 1, delta, delayChangeEvent: bufferChange?) From c1ff53b02c577748f12dbdae3b79bca5c3380ba7 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 6 Oct 2014 16:37:00 -0700 Subject: [PATCH 07/26] getCurrentScopeDescriptor -> getGrammarScopeDescriptor --- src/display-buffer.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 3409e277c..1fb58d87a 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -66,7 +66,7 @@ class DisplayBuffer extends Model @softWrapConfigSubscriptions?.dispose() @softWrapConfigSubscriptions = new CompositeDisposable - scopeDescriptor = @getCurrentScopeDescriptor() + scopeDescriptor = @getGrammarScopeDescriptor() @softWrapConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrap', => @updateWrappedScreenLines() @@ -329,7 +329,7 @@ class DisplayBuffer extends Model return 0 unless lineHeight > 0 scrollHeight = @getLineCount() * lineHeight - if @height? and atom.config.get('editor.scrollPastEnd') + if @height? and atom.config.get(@getGrammarScopeDescriptor(), 'editor.scrollPastEnd') scrollHeight = scrollHeight + @height - (lineHeight * 3) scrollHeight @@ -430,7 +430,7 @@ class DisplayBuffer extends Model @isSoftWrapped() isSoftWrapped: -> - @softWrapped ? atom.config.get(@getCurrentScopeDescriptor(), 'editor.softWrap') ? false + @softWrapped ? atom.config.get(@getGrammarScopeDescriptor(), 'editor.softWrap') ? false # Set the number of characters that fit horizontally in the editor. # @@ -452,8 +452,8 @@ class DisplayBuffer extends Model @editorWidthInChars getSoftWrapColumn: -> - if atom.config.get(@getCurrentScopeDescriptor(), 'editor.softWrapAtPreferredLineLength') - Math.min(@getEditorWidthInChars(), atom.config.get(@getCurrentScopeDescriptor(), 'editor.preferredLineLength')) + if atom.config.get(@getGrammarScopeDescriptor(), 'editor.softWrapAtPreferredLineLength') + Math.min(@getEditorWidthInChars(), atom.config.get(@getGrammarScopeDescriptor(), 'editor.preferredLineLength')) else @getEditorWidthInChars() @@ -1048,7 +1048,7 @@ class DisplayBuffer extends Model line = @tokenizedLineForScreenRow(row).text console.log row, @bufferRowForScreenRow(row), line, line.length - getCurrentScopeDescriptor: -> + getGrammarScopeDescriptor: -> @tokenizedBuffer.grammarScopeDescriptor handleTokenizedBufferChange: (tokenizedBufferChange) => From 237c668ef0e210d85e322f197ccab800f9db28c3 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 6 Oct 2014 16:37:43 -0700 Subject: [PATCH 08/26] Scope editor.invisibles and editor.showInvisibles --- spec/text-editor-component-spec.coffee | 102 +++++++++++++++++++++++++ spec/text-editor-spec.coffee | 49 ------------ src/text-editor.coffee | 40 +++++++--- 3 files changed, 131 insertions(+), 60 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index b3f268b56..6d8cb5d3b 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2268,6 +2268,108 @@ describe "TextEditorComponent", -> expect(editor.getCursorBufferPosition()).toEqual [0, 1] + describe 'scoped config settings', -> + [coffeeEditor, coffeeComponent] = [] + + beforeEach -> + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + waitsForPromise -> + atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o + + afterEach: -> + atom.packages.deactivatePackages() + atom.packages.unloadPackages() + + describe 'soft wrap settings', -> + beforeEach -> + atom.config.set '.source.coffee', 'editor.softWrap', true + atom.config.set '.source.coffee', 'editor.preferredLineLength', 17 + atom.config.set '.source.coffee', 'editor.softWrapAtPreferredLineLength', true + + editor.setEditorWidthInChars(20) + coffeeEditor.setEditorWidthInChars(20) + + it 'isSoftWrapped() returns true for coffeescript', -> + expect(editor.isSoftWrapped()).toBe false + expect(coffeeEditor.isSoftWrapped()).toBe true + + it 'correctly wraps coffeescript file', -> + expect(editor.lineTextForScreenRow(2)).toEqual ' if (items.length <= 1) return items;' + expect(coffeeEditor.lineTextForScreenRow(3)).toEqual ' return items ' + + it 'updates the wrapped lines when editor.preferredLineLength changes', -> + atom.config.set '.source.coffee', 'editor.preferredLineLength', 20 + expect(coffeeEditor.lineTextForScreenRow(2)).toEqual ' return items if ' + + it 'updates the wrapped lines when editor.softWrapAtPreferredLineLength changes', -> + atom.config.set '.source.coffee', 'editor.softWrapAtPreferredLineLength', false + expect(coffeeEditor.lineTextForScreenRow(2)).toEqual ' return items if ' + + it 'updates the wrapped lines when editor.softWrap changes', -> + atom.config.set '.source.coffee', 'editor.softWrap', false + expect(coffeeEditor.lineTextForScreenRow(2)).toEqual ' return items if items.length <= 1' + + atom.config.set '.source.coffee', 'editor.softWrap', true + expect(coffeeEditor.lineTextForScreenRow(3)).toEqual ' return items ' + + it 'updates the wrapped lines when the grammar changes', -> + editor.setGrammar(coffeeEditor.getGrammar()) + expect(editor.isSoftWrapped()).toBe true + expect(editor.lineTextForScreenRow(0)).toEqual 'var quicksort = ' + + describe 'invisibles settings', -> + [jsInvisibles, coffeeInvisibles] = [] + beforeEach -> + jsInvisibles = + eol: 'J' + space: 'A' + tab: 'V' + cr: 'A' + + coffeeInvisibles = + eol: 'C' + space: 'O' + tab: 'F' + cr: 'E' + + atom.config.set '.source.js', 'editor.showInvisibles', true + atom.config.set '.source.js', 'editor.invisibles', jsInvisibles + + atom.config.set '.source.coffee', 'editor.showInvisibles', false + atom.config.set '.source.coffee', 'editor.invisibles', coffeeInvisibles + + editor.setText " a line with tabs\tand spaces \n" + nextAnimationFrame() + + it "renders the invisibles for the javascript scoped invisibles", -> + expect(component.lineNodeForScreenRow(0).textContent).toBe "#{jsInvisibles.space}a line with tabs#{jsInvisibles.tab}and spaces#{jsInvisibles.space}#{jsInvisibles.eol}" + + it "does not renders the invisibles for the coffeescript scope because editor.showInvisibles is false", -> + editor.setGrammar(coffeeEditor.getGrammar()) + nextAnimationFrame() + expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces " + + it "re-renders the invisibles for the when the invisible settings change", -> + jsGrammar = editor.getGrammar() + editor.setGrammar(coffeeEditor.getGrammar()) + atom.config.set '.source.coffee', 'editor.showInvisibles', true + nextAnimationFrame() + expect(component.lineNodeForScreenRow(0).textContent).toBe "#{coffeeInvisibles.space}a line with tabs#{coffeeInvisibles.tab}and spaces#{coffeeInvisibles.space}#{coffeeInvisibles.eol}" + + newInvisibles = + eol: 'N' + space: 'E' + tab: 'W' + cr: 'I' + atom.config.set '.source.coffee', 'editor.invisibles', newInvisibles + nextAnimationFrame() + expect(component.lineNodeForScreenRow(0).textContent).toBe "#{newInvisibles.space}a line with tabs#{newInvisibles.tab}and spaces#{newInvisibles.space}#{newInvisibles.eol}" + + editor.setGrammar(jsGrammar) + nextAnimationFrame() + expect(component.lineNodeForScreenRow(0).textContent).toBe "#{jsInvisibles.space}a line with tabs#{jsInvisibles.tab}and spaces#{jsInvisibles.space}#{jsInvisibles.eol}" + buildMouseEvent = (type, properties...) -> properties = extend({bubbles: true, cancelable: true}, properties...) properties.detail ?= 1 diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 297dfee52..55c466cc8 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3769,52 +3769,3 @@ describe "TextEditor", -> editor.setCursorBufferPosition [0, 7] editor.selectWordsContainingCursors() expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [0, 13]] - - describe 'scoped config settings', -> - coffeeEditor = null - beforeEach -> - waitsForPromise -> - atom.packages.activatePackage('language-coffee-script') - waitsForPromise -> - atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o - - afterEach: -> - atom.packages.deactivatePackages() - atom.packages.unloadPackages() - - describe 'soft wrap config settings', -> - beforeEach -> - atom.config.set '.source.coffee', 'editor.softWrap', true - atom.config.set '.source.coffee', 'editor.preferredLineLength', 17 - atom.config.set '.source.coffee', 'editor.softWrapAtPreferredLineLength', true - - editor.setEditorWidthInChars(20) - coffeeEditor.setEditorWidthInChars(20) - - it 'isSoftWrapped() returns true for coffeescript', -> - expect(editor.isSoftWrapped()).toBe false - expect(coffeeEditor.isSoftWrapped()).toBe true - - it 'correctly wraps coffeescript file', -> - expect(editor.lineTextForScreenRow(2)).toEqual ' if (items.length <= 1) return items;' - expect(coffeeEditor.lineTextForScreenRow(3)).toEqual ' return items ' - - it 'updates the wrapped lines when editor.preferredLineLength changes', -> - atom.config.set '.source.coffee', 'editor.preferredLineLength', 20 - expect(coffeeEditor.lineTextForScreenRow(2)).toEqual ' return items if ' - - it 'updates the wrapped lines when editor.softWrapAtPreferredLineLength changes', -> - atom.config.set '.source.coffee', 'editor.softWrapAtPreferredLineLength', false - expect(coffeeEditor.lineTextForScreenRow(2)).toEqual ' return items if ' - - it 'updates the wrapped lines when editor.softWrap changes', -> - atom.config.set '.source.coffee', 'editor.softWrap', false - expect(coffeeEditor.lineTextForScreenRow(2)).toEqual ' return items if items.length <= 1' - - atom.config.set '.source.coffee', 'editor.softWrap', true - expect(coffeeEditor.lineTextForScreenRow(3)).toEqual ' return items ' - - it 'updates the wrapped lines when the grammar changes', -> - editor.setGrammar(coffeeEditor.getGrammar()) - expect(editor.isSoftWrapped()).toBe true - expect(editor.lineTextForScreenRow(0)).toEqual 'var quicksort = ' diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 3d6934cc8..9e5d68bcf 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -5,7 +5,7 @@ Delegator = require 'delegato' {deprecate} = require 'grim' {Model} = require 'theorist' EmitterMixin = require('emissary').Emitter -{Emitter} = require 'event-kit' +{CompositeDisposable, Emitter} = require 'event-kit' {Point, Range} = require 'text-buffer' LanguageMode = require './language-mode' DisplayBuffer = require './display-buffer' @@ -84,20 +84,20 @@ class TextEditor extends Model @cursors = [] @selections = [] - if @shouldShowInvisibles() - invisibles = atom.config.get('editor.invisibles') - @displayBuffer?.setInvisibles(invisibles) - @displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrapped, invisibles}) + @displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrapped}) @buffer = @displayBuffer.buffer @softTabs = @usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true + @updateInvisibles() + for marker in @findMarkers(@getSelectionMarkerAttributes()) marker.setProperties(preserveFolds: true) @addSelection(marker) @subscribeToBuffer() @subscribeToDisplayBuffer() + @subscribeToInvisiblesConfigChanges() if @getCursors().length is 0 and not suppressCursorCreation initialLine = Math.max(parseInt(initialLine) or 0, 0) @@ -113,9 +113,6 @@ class TextEditor extends Model @emit 'scroll-left-changed', scrollLeft @emitter.emit 'did-change-scroll-left', scrollLeft - @subscribe atom.config.onDidChange 'editor.showInvisibles', => @updateInvisibles() - @subscribe atom.config.onDidChange 'editor.invisibles', => @updateInvisibles() - atom.workspace?.editorAdded(this) if registerEditor serializeParams: -> @@ -162,6 +159,18 @@ class TextEditor extends Model @subscribe @displayBuffer.onDidAddDecoration (decoration) => @emit 'decoration-added', decoration @subscribe @displayBuffer.onDidRemoveDecoration (decoration) => @emit 'decoration-removed', decoration + subscribeToInvisiblesConfigChanges: -> + @invisiblesConfigSubscriptions?.dispose() + @invisiblesConfigSubscriptions = new CompositeDisposable + + scopeDescriptor = @getGrammarScopeDescriptor() + + @invisiblesConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.showInvisibles', => @updateInvisibles() + @invisiblesConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.invisibles', => @updateInvisibles() + + getViewClass: -> + require './text-editor-view' + destroyed: -> @unsubscribe() selection.destroy() for selection in @getSelections() @@ -2354,7 +2363,11 @@ class TextEditor extends Model # position. See {::scopesForBufferPosition} for more information. # # Returns an {Array} of {String}s. - scopesAtCursor: -> @getLastCursor().getScopes() + scopesAtCursor: -> + if cursor = @getLastCursor() + cursor.getScopes() + else + @getGrammarScopeDescriptor() getCursorScopes: -> deprecate 'Use TextEditor::scopesAtCursor() instead' @scopesAtCursor() @@ -2381,6 +2394,9 @@ class TextEditor extends Model bufferRangeForScopeAtCursor: (selector) -> @displayBuffer.bufferRangeForScopeAtPosition(selector, @getCursorBufferPosition()) + getGrammarScopeDescriptor: -> + @displayBuffer.getGrammarScopeDescriptor() + logCursorScope: -> console.log @scopesAtCursor() @@ -2653,11 +2669,11 @@ class TextEditor extends Model atom.config.get("editor.autoIndent") shouldShowInvisibles: -> - not @mini and atom.config.get('editor.showInvisibles') + not @mini and atom.config.get(@getGrammarScopeDescriptor(), 'editor.showInvisibles') updateInvisibles: -> if @shouldShowInvisibles() - @displayBuffer.setInvisibles(atom.config.get('editor.invisibles')) + @displayBuffer.setInvisibles(atom.config.get(@getGrammarScopeDescriptor(), 'editor.invisibles')) else @displayBuffer.setInvisibles(null) @@ -2669,6 +2685,8 @@ class TextEditor extends Model @softTabs = @usesSoftTabs() ? @softTabs handleGrammarChange: -> + @updateInvisibles() + @subscribeToInvisiblesConfigChanges() @unfoldAll() @emit 'grammar-changed' @emitter.emit 'did-change-grammar' From 6958e0af1074390b1d380f7a9cbceca0bfcf991a Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 6 Oct 2014 18:10:24 -0700 Subject: [PATCH 09/26] Scope editor.normalizeIndentOnPaste --- spec/text-editor-spec.coffee | 42 +++++++++++++++++++++++++++++++----- src/text-editor.coffee | 2 +- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 55c466cc8..509feacbf 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3126,14 +3126,15 @@ describe "TextEditor", -> expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBeGreaterThan 1 describe "auto-indent", -> - copyText = (text, {startColumn}={}) -> + copyText = (text, {startColumn, textEditor}={}) -> startColumn ?= 0 - editor.setCursorBufferPosition([0, 0]) - editor.insertText(text) + textEditor ?= editor + textEditor.setCursorBufferPosition([0, 0]) + textEditor.insertText(text) numberOfNewlines = text.match(/\n/g)?.length endColumn = text.match(/[^\n]*$/)[0]?.length - editor.getLastSelection().setBufferRange([[0,startColumn], [numberOfNewlines,endColumn]]) - editor.cutSelectedText() + textEditor.getLastSelection().setBufferRange([[0,startColumn], [numberOfNewlines,endColumn]]) + textEditor.cutSelectedText() describe "editor.autoIndent", -> describe "when editor.autoIndent is false (default)", -> @@ -3289,6 +3290,37 @@ describe "TextEditor", -> expect(editor.lineTextForBufferRow(3)).toBe " }" expect(editor.lineTextForBufferRow(4)).toBe "" + describe 'when scoped settings are used', -> + coffeeEditor = null + beforeEach -> + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + waitsForPromise -> + atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o + + runs -> + atom.config.set('.source.js', 'editor.normalizeIndentOnPaste', true) + atom.config.set('.source.coffee', 'editor.normalizeIndentOnPaste', false) + + afterEach: -> + atom.packages.deactivatePackages() + atom.packages.unloadPackages() + + it "does not normalize the indentation level for coffee files, but does for js files", -> + copyText(" while (true) {\n foo();\n }\n", {startColumn: 2, textEditor: coffeeEditor}) + coffeeEditor.setCursorBufferPosition([4, 4]) + coffeeEditor.pasteText() + expect(coffeeEditor.lineTextForBufferRow(4)).toBe " while (true) {" + expect(coffeeEditor.lineTextForBufferRow(5)).toBe " foo();" + expect(coffeeEditor.lineTextForBufferRow(6)).toBe " }" + + copyText(" while (true) {\n foo();\n }\n", {startColumn: 2}) + editor.setCursorBufferPosition([3, 4]) + editor.pasteText() + expect(editor.lineTextForBufferRow(3)).toBe " while (true) {" + expect(editor.lineTextForBufferRow(4)).toBe " foo();" + expect(editor.lineTextForBufferRow(5)).toBe " }" + it "autoIndentSelectedRows auto-indents the selection", -> editor.setCursorBufferPosition([2, 0]) editor.insertText("function() {\ninside=true\n}\n i=1\n") diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 9e5d68bcf..a0b561861 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2448,7 +2448,7 @@ class TextEditor extends Model return - else if atom.config.get("editor.normalizeIndentOnPaste") and metadata?.indentBasis? + else if atom.config.get(@scopesAtCursor(), "editor.normalizeIndentOnPaste") and metadata?.indentBasis? if !@getLastCursor().hasPrecedingCharactersOnLine() or containsNewlines options.indentBasis ?= metadata.indentBasis From ca4c40936a9ec534c5d5c02f1cbc6e932279db44 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Mon, 6 Oct 2014 18:19:07 -0700 Subject: [PATCH 10/26] Scope editor.autoIndent --- spec/text-editor-spec.coffee | 25 +++++++++++++++++++++++++ src/text-editor.coffee | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 509feacbf..5805eee64 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3241,6 +3241,31 @@ describe "TextEditor", -> editor.insertText('foo') expect(editor.indentationForBufferRow(2)).toBe editor.indentationForBufferRow(1) + 1 + describe 'when scoped settings are used', -> + coffeeEditor = null + beforeEach -> + waitsForPromise -> + atom.packages.activatePackage('language-coffee-script') + waitsForPromise -> + atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o + + runs -> + atom.config.set('.source.js', 'editor.autoIndent', true) + atom.config.set('.source.coffee', 'editor.autoIndent', false) + + afterEach: -> + atom.packages.deactivatePackages() + atom.packages.unloadPackages() + + it "does not auto-indent the line for javascript files", -> + editor.setCursorBufferPosition([1, 30]) + editor.insertText("\n") + expect(editor.lineTextForBufferRow(2)).toBe " " + + coffeeEditor.setCursorBufferPosition([1, 18]) + coffeeEditor.insertText("\n") + expect(coffeeEditor.lineTextForBufferRow(2)).toBe "" + describe "editor.normalizeIndentOnPaste", -> beforeEach -> atom.config.set('editor.normalizeIndentOnPaste', true) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index a0b561861..9fface7bf 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2666,7 +2666,7 @@ class TextEditor extends Model ### shouldAutoIndent: -> - atom.config.get("editor.autoIndent") + atom.config.get(@getGrammarScopeDescriptor(), "editor.autoIndent") shouldShowInvisibles: -> not @mini and atom.config.get(@getGrammarScopeDescriptor(), 'editor.showInvisibles') From fbe4cf5677fcbdc1b16db0af9344ec36a642e448 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 10:07:51 -0700 Subject: [PATCH 11/26] Invisibles will be set in a call to updateInvisibles() --- src/text-editor.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 9fface7bf..8c713c0c3 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -84,7 +84,6 @@ class TextEditor extends Model @cursors = [] @selections = [] - @displayBuffer?.setInvisibles(invisibles) @displayBuffer ?= new DisplayBuffer({buffer, tabLength, softWrapped}) @buffer = @displayBuffer.buffer @softTabs = @usesSoftTabs() ? @softTabs ? atom.config.get('editor.softTabs') ? true From 2605044f19e90912cecbe435396b8d9149777f3e Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 10:34:33 -0700 Subject: [PATCH 12/26] Scope editor.showIndentGuide --- spec/text-editor-component-spec.coffee | 44 ++++++++++++++++++++++---- src/text-editor-component.coffee | 18 ++++++++--- src/text-editor.coffee | 4 +++ 3 files changed, 56 insertions(+), 10 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 6d8cb5d3b..10db3b271 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -446,12 +446,6 @@ describe "TextEditorComponent", -> foldedLineNode = component.lineNodeForScreenRow(4) expect(foldedLineNode.querySelector('.fold-marker')).toBeFalsy() - getLeafNodes = (node) -> - if node.children.length > 0 - flatten(toArray(node.children).map(getLeafNodes)) - else - [node] - describe "gutter rendering", -> [gutter] = [] @@ -2370,6 +2364,38 @@ describe "TextEditorComponent", -> nextAnimationFrame() expect(component.lineNodeForScreenRow(0).textContent).toBe "#{jsInvisibles.space}a line with tabs#{jsInvisibles.tab}and spaces#{jsInvisibles.space}#{jsInvisibles.eol}" + describe 'editor.showIndentGuide', -> + beforeEach -> + atom.config.set '.source.js', 'editor.showIndentGuide', true + atom.config.set '.source.coffee', 'editor.showIndentGuide', false + + it "has an 'indent-guide' class when using the javascript grammar, but not when using the coffeescript grammar", -> + line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1)) + expect(line1LeafNodes[0].textContent).toBe ' ' + expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe true + expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false + + editor.setGrammar(coffeeEditor.getGrammar()) + + line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1)) + expect(line1LeafNodes[0].textContent).toBe ' ' + expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe false + expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false + + it "removes the 'indent-guide' class unsetting the value for javascript", -> + line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1)) + expect(line1LeafNodes[0].textContent).toBe ' ' + expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe true + expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false + + atom.config.set '.source.js', 'editor.showIndentGuide', false + + line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1)) + expect(line1LeafNodes[0].textContent).toBe ' ' + expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe false + expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false + + buildMouseEvent = (type, properties...) -> properties = extend({bubbles: true, cancelable: true}, properties...) properties.detail ?= 1 @@ -2406,3 +2432,9 @@ describe "TextEditorComponent", -> lineHasClass = (screenRow, klass) -> component.lineNodeForScreenRow(screenRow).classList.contains(klass) + + getLeafNodes = (node) -> + if node.children.length > 0 + flatten(toArray(node.children).map(getLeafNodes)) + else + [node] diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index c2f84e6dd..4194d3c57 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -5,6 +5,7 @@ React = require 'react-atom-fork' scrollbarStyle = require 'scrollbar-style' {Range, Point} = require 'text-buffer' grim = require 'grim' +{CompositeDisposable} = require 'event-kit' GutterComponent = require './gutter-component' InputComponent = require './input-component' @@ -352,6 +353,7 @@ TextEditorComponent = React.createClass observeEditor: -> {editor} = @props @subscribe editor.onDidChange(@onScreenLinesChanged) + @subscribe editor.observeGrammar(@onGrammarChanged) @subscribe editor.observeCursors(@onCursorAdded) @subscribe editor.observeSelections(@onSelectionAdded) @subscribe editor.observeDecorations(@onDecorationAdded) @@ -406,11 +408,20 @@ TextEditorComponent = React.createClass event.target.value = '' observeConfig: -> - @subscribe atom.config.observe 'editor.showIndentGuide', @setShowIndentGuide - @subscribe atom.config.observe 'editor.showLineNumbers', @setShowLineNumbers - @subscribe atom.config.observe 'editor.scrollSensitivity', @setScrollSensitivity @subscribe atom.config.observe 'editor.useHardwareAcceleration', @setUseHardwareAcceleration + onGrammarChanged: -> + {editor} = @props + + @scopedPropertySubscriptions?.dispose() + @scopedPropertySubscriptions = subscriptions = new CompositeDisposable + + scopeDescriptor = editor.getGrammarScopeDescriptor() + + subscriptions.add atom.config.observe scopeDescriptor, 'editor.showIndentGuide', @setShowIndentGuide + subscriptions.add atom.config.observe scopeDescriptor, 'editor.showLineNumbers', @setShowLineNumbers + subscriptions.add atom.config.observe scopeDescriptor, 'editor.scrollSensitivity', @setScrollSensitivity + onFocus: -> @refs.input.focus() if @isMounted() @@ -435,7 +446,6 @@ TextEditorComponent = React.createClass inputNode.value = event.data if editor.insertText(event.data) - onInputFocused: -> @setState(focused: true) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 8c713c0c3..5e7e88c63 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -259,6 +259,10 @@ class TextEditor extends Model onDidChangeSoftWrapped: (callback) -> @displayBuffer.onDidChangeSoftWrapped(callback) + observeGrammar: (callback) -> + callback(@getGrammar()) + @onDidChangeGrammar(callback) + # Extended: Calls your `callback` when the grammar that interprets and colorizes the text has # been changed. # From d3b00f67f2df95e9c3c1c2ab96e9895de3aef223 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 10:39:26 -0700 Subject: [PATCH 13/26] Fix spec --- spec/text-editor-component-spec.coffee | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 10db3b271..77e406bfe 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2208,7 +2208,6 @@ describe "TextEditorComponent", -> it "does not render invisible characters", -> atom.config.set('editor.invisibles', eol: 'E') atom.config.set('editor.showInvisibles', true) - nextAnimationFrame() expect(component.lineNodeForScreenRow(0).textContent).toBe 'var quicksort = function () {' it "does not assign an explicit line-height on the editor contents", -> From f662b3d745482995181767b4ee5a650551c59184 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 11:26:11 -0700 Subject: [PATCH 14/26] :lipstick: Normalize the names of related subscription things --- src/display-buffer.coffee | 15 +++++++-------- src/text-editor-component.coffee | 4 ++-- src/text-editor.coffee | 15 ++++++++------- src/tokenized-buffer.coffee | 4 ++++ 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 1fb58d87a..ec3ec53f9 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -54,27 +54,26 @@ class DisplayBuffer extends Model @decorationsByMarkerId = {} @updateAllScreenLines() @createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes()) + @subscribe @tokenizedBuffer.observeGrammar @subscribeToScopedConfigSettings @subscribe @tokenizedBuffer.onDidChange @handleTokenizedBufferChange - @subscribe @tokenizedBuffer.onDidChangeGrammar @subscribeForSoftWrapConfigChanges @subscribe @buffer.onDidUpdateMarkers @handleBufferMarkersUpdated @subscribe @buffer.onDidCreateMarker @handleBufferMarkerCreated - @subscribeForSoftWrapConfigChanges() @updateAllScreenLines() - subscribeForSoftWrapConfigChanges: => - @softWrapConfigSubscriptions?.dispose() - @softWrapConfigSubscriptions = new CompositeDisposable + subscribeToScopedConfigSettings: => + @scopedConfigSubscriptions?.dispose() + @scopedConfigSubscriptions = subscriptions = new CompositeDisposable scopeDescriptor = @getGrammarScopeDescriptor() - @softWrapConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrap', => + subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrap', => @updateWrappedScreenLines() - @softWrapConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrapAtPreferredLineLength', => + subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrapAtPreferredLineLength', => @updateWrappedScreenLines() if @isSoftWrapped() - @softWrapConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.preferredLineLength', => + subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.preferredLineLength', => @updateWrappedScreenLines() if @isSoftWrapped() and atom.config.get(scopeDescriptor, 'editor.softWrapAtPreferredLineLength') serializeParams: -> diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 4194d3c57..442f42a7e 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -413,8 +413,8 @@ TextEditorComponent = React.createClass onGrammarChanged: -> {editor} = @props - @scopedPropertySubscriptions?.dispose() - @scopedPropertySubscriptions = subscriptions = new CompositeDisposable + @scopedConfigSubscriptions?.dispose() + @scopedConfigSubscriptions = subscriptions = new CompositeDisposable scopeDescriptor = editor.getGrammarScopeDescriptor() diff --git a/src/text-editor.coffee b/src/text-editor.coffee index 5e7e88c63..efad10108 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -96,7 +96,6 @@ class TextEditor extends Model @subscribeToBuffer() @subscribeToDisplayBuffer() - @subscribeToInvisiblesConfigChanges() if @getCursors().length is 0 and not suppressCursorCreation initialLine = Math.max(parseInt(initialLine) or 0, 0) @@ -158,14 +157,16 @@ class TextEditor extends Model @subscribe @displayBuffer.onDidAddDecoration (decoration) => @emit 'decoration-added', decoration @subscribe @displayBuffer.onDidRemoveDecoration (decoration) => @emit 'decoration-removed', decoration - subscribeToInvisiblesConfigChanges: -> - @invisiblesConfigSubscriptions?.dispose() - @invisiblesConfigSubscriptions = new CompositeDisposable + @subscribeToScopedConfigSettings() + + subscribeToScopedConfigSettings: -> + @scopedConfigSubscriptions?.dispose() + @scopedConfigSubscriptions = subscriptions = new CompositeDisposable scopeDescriptor = @getGrammarScopeDescriptor() - @invisiblesConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.showInvisibles', => @updateInvisibles() - @invisiblesConfigSubscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.invisibles', => @updateInvisibles() + subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.showInvisibles', => @updateInvisibles() + subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.invisibles', => @updateInvisibles() getViewClass: -> require './text-editor-view' @@ -2689,7 +2690,7 @@ class TextEditor extends Model handleGrammarChange: -> @updateInvisibles() - @subscribeToInvisiblesConfigChanges() + @subscribeToScopedConfigSettings() @unfoldAll() @emit 'grammar-changed' @emitter.emit 'did-change-grammar' diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index c79712edf..f8836a3f3 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -42,6 +42,10 @@ class TokenizedBuffer extends Model params.buffer = atom.project.bufferForPathSync(params.bufferPath) params + observeGrammar: (callback) -> + callback(@grammar) + @onDidChangeGrammar(callback) + onDidChangeGrammar: (callback) -> @emitter.on 'did-change-grammar', callback From b44a5dd1f0cebdfeb49b115549b2b33d43aadd15 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 15:18:29 -0700 Subject: [PATCH 15/26] Use jasmine json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index ac17137eb..a77ab5ae5 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "git-utils": "^2.1.5", "grim": "0.12.0", "guid": "0.0.10", + "jasmine-json": "~0.0", "jasmine-tagged": "^1.1.2", "less-cache": "0.15.0", "mixto": "^1", From a3bbbc19b57d6ea7356ed7d5e5884bdfd5638346 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 15:19:55 -0700 Subject: [PATCH 16/26] =?UTF-8?q?Read=20and=20write=20scoped=20settings=20?= =?UTF-8?q?from=20the=20user=E2=80=99s=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spec/config-spec.coffee | 109 ++++++++++++++++++++++++++++++++++------ spec/spec-helper.coffee | 1 + src/config.coffee | 38 ++++++++++---- src/syntax.coffee | 1 + 4 files changed, 123 insertions(+), 26 deletions(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index c42add20c..21c327533 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -200,7 +200,7 @@ describe "Config", -> expect(CSON.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.json")) writtenConfig = CSON.writeFileSync.argsForCall[0][1] - expect(writtenConfig).toBe atom.config.settings + expect(writtenConfig).toEqual global: atom.config.settings describe "when ~/.atom/config.json doesn't exist", -> it "writes any non-default properties to ~/.atom/config.cson", -> @@ -214,9 +214,29 @@ describe "Config", -> atom.config.save() expect(CSON.writeFileSync.argsForCall[0][0]).toBe(path.join(atom.config.configDirPath, "atom.config.cson")) - CoffeeScript = require 'coffee-script' writtenConfig = CSON.writeFileSync.argsForCall[0][1] - expect(writtenConfig).toEqual atom.config.settings + expect(writtenConfig).toEqual global: atom.config.settings + + describe "when scoped settings are defined", -> + it 'writes out explicitly set config settings', -> + atom.config.set('.source.ruby', 'foo.bar', 'ruby') + atom.config.set('.source.ruby', 'foo.omg', 'wow') + atom.config.set('.source.coffee', 'foo.bar', 'coffee') + + CSON.writeFileSync.reset() + atom.config.save() + + writtenConfig = CSON.writeFileSync.argsForCall[0][1] + expect(writtenConfig).toEqualJson + global: + atom.config.settings + '*.ruby.source': + foo: + bar: 'ruby' + omg: 'wow' + '*.coffee.source': + foo: + bar: 'coffee' describe ".setDefaults(keyPath, defaults)", -> it "assigns any previously-unassigned keys to the object at the key path", -> @@ -356,6 +376,23 @@ describe "Config", -> afterEach -> fs.removeSync(dotAtomPath) + describe "when the config file contains scoped settings", -> + beforeEach -> + fs.writeFileSync atom.config.configFilePath, """ + global: + foo: + bar: 'baz' + + '.source.ruby': + foo: + bar: 'more-specific' + """ + atom.config.loadUserConfig() + + it "updates the config data based on the file contents", -> + expect(atom.config.get("foo.bar")).toBe 'baz' + expect(atom.config.get(['.source.ruby'], "foo.bar")).toBe 'more-specific' + describe "when the config file contains valid cson", -> beforeEach -> fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'") @@ -430,7 +467,15 @@ describe "Config", -> atom.config.configDirPath = dotAtomPath atom.config.configFilePath = path.join(atom.config.configDirPath, "atom.config.cson") expect(fs.existsSync(atom.config.configDirPath)).toBeFalsy() - fs.writeFileSync(atom.config.configFilePath, "foo: bar: 'baz'") + fs.writeFileSync atom.config.configFilePath, """ + global: + foo: + bar: 'baz' + scoped: false + '.source.ruby': + foo: + scoped: true + """ atom.config.loadUserConfig() atom.config.observeUserConfig() updatedHandler = jasmine.createSpy("updatedHandler") @@ -474,6 +519,38 @@ describe "Config", -> expect(atom.config.get('foo.bar')).toEqual ['baz', 'ok'] expect(atom.config.get('foo.omg')).toBe 'another' + describe 'when scoped settings are used', -> + it "fires a change event for scoped settings that are removed", -> + atom.config.onDidChange ['.source.ruby'], 'foo.scoped', scopedSpy = jasmine.createSpy() + + fs.writeFileSync atom.config.configFilePath, """ + global: + foo: + scoped: false + """ + waitsFor 'update event', -> updatedHandler.callCount > 0 + runs -> + expect(scopedSpy).toHaveBeenCalled() + expect(atom.config.get(['.source.ruby'], 'foo.scoped')).toBe false + + it "does not fire a change event for paths that did not change", -> + atom.config.onDidChange ['.source.ruby'], 'foo.scoped', noChangeSpy = jasmine.createSpy() + + fs.writeFileSync atom.config.configFilePath, """ + global: + foo: + bar: 'baz' + '.source.ruby': + foo: + scoped: true + """ + waitsFor 'update event', -> updatedHandler.callCount > 0 + runs -> + expect(noChangeSpy).not.toHaveBeenCalled() + expect(atom.config.get(['.source.ruby'], 'foo.bar')).toBe 'baz' + expect(atom.config.get(['.source.ruby'], 'foo.scoped')).toBe true + + describe "when the config file changes to omit a setting with a default", -> it "resets the setting back to the default", -> fs.writeFileSync(atom.config.configFilePath, "foo: { baz: 'new'}") @@ -866,9 +943,9 @@ describe "Config", -> describe "scoped settings", -> describe ".get(scopeDescriptor, keyPath)", -> it "returns the property with the most specific scope selector", -> - atom.config.addScopedSettings(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) - atom.config.addScopedSettings(".source .string.quoted.double", foo: bar: baz: 22) - atom.config.addScopedSettings(".source", foo: bar: baz: 11) + atom.config.addScopedSettings("config", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) + atom.config.addScopedSettings("config", ".source .string.quoted.double", foo: bar: baz: 22) + atom.config.addScopedSettings("config", ".source", foo: bar: baz: 11) expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42 expect(atom.config.get([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22 @@ -876,8 +953,8 @@ describe "Config", -> expect(atom.config.get([".text"], "foo.bar.baz")).toBeUndefined() it "favors the most recently added properties in the event of a specificity tie", -> - atom.config.addScopedSettings(".source.coffee .string.quoted.single", foo: bar: baz: 42) - atom.config.addScopedSettings(".source.coffee .string.quoted.double", foo: bar: baz: 22) + atom.config.addScopedSettings("config", ".source.coffee .string.quoted.single", foo: bar: baz: 42) + atom.config.addScopedSettings("config", ".source.coffee .string.quoted.double", foo: bar: baz: 22) expect(atom.config.get([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42 expect(atom.config.get([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22 @@ -889,9 +966,9 @@ describe "Config", -> describe ".set(scope, keyPath, value)", -> it "sets the value and overrides the others", -> - atom.config.addScopedSettings(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) - atom.config.addScopedSettings(".source .string.quoted.double", foo: bar: baz: 22) - atom.config.addScopedSettings(".source", foo: bar: baz: 11) + atom.config.addScopedSettings("config", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) + atom.config.addScopedSettings("config", ".source .string.quoted.double", foo: bar: baz: 22) + atom.config.addScopedSettings("config", ".source", foo: bar: baz: 11) expect(atom.config.get([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42 @@ -917,11 +994,11 @@ describe "Config", -> expect(changeSpy).toHaveBeenCalledWith(12) changeSpy.reset() - disposable1 = atom.config.addScopedSettings(".source .string.quoted.double", foo: bar: baz: 22) + disposable1 = atom.config.addScopedSettings("a", ".source .string.quoted.double", foo: bar: baz: 22) expect(changeSpy).toHaveBeenCalledWith(22) changeSpy.reset() - disposable2 = atom.config.addScopedSettings("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) + disposable2 = atom.config.addScopedSettings("b", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) expect(changeSpy).toHaveBeenCalledWith(42) changeSpy.reset() @@ -946,11 +1023,11 @@ describe "Config", -> expect(changeSpy).toHaveBeenCalledWith({oldValue: undefined, newValue: 12, keyPath}) changeSpy.reset() - disposable1 = atom.config.addScopedSettings(".source .string.quoted.double", foo: bar: baz: 22) + disposable1 = atom.config.addScopedSettings("a", ".source .string.quoted.double", foo: bar: baz: 22) expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: 22, keyPath}) changeSpy.reset() - disposable2 = atom.config.addScopedSettings("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) + disposable2 = atom.config.addScopedSettings("b", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) expect(changeSpy).toHaveBeenCalledWith({oldValue: 22, newValue: 42, keyPath}) changeSpy.reset() diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index e483d25a5..0396f4673 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -2,6 +2,7 @@ require '../src/window' atom.initialize() atom.restoreWindowDimensions() +require 'jasmine-json' require '../vendor/jasmine-jquery' path = require 'path' _ = require 'underscore-plus' diff --git a/src/config.coffee b/src/config.coffee index bb45a09ec..5ccb835c5 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -1,7 +1,7 @@ _ = require 'underscore-plus' fs = require 'fs-plus' EmitterMixin = require('emissary').Emitter -{Disposable, Emitter} = require 'event-kit' +{CompositeDisposable, Disposable, Emitter} = require 'event-kit' CSON = require 'season' path = require 'path' async = require 'async' @@ -312,6 +312,7 @@ class Config @defaultSettings = {} @settings = {} @scopedSettingsStore = new ScopedPropertyStore + @usersScopedSettings = new CompositeDisposable @configFileHasErrors = false @configFilePath = fs.resolve(@configDirPath, 'config', ['json', 'cson']) @configFilePath ?= path.join(@configDirPath, 'config.cson') @@ -658,7 +659,7 @@ class Config try userConfig = CSON.readFileSync(@configFilePath) - @setAll(userConfig) + @resetUserSettings(userConfig) @configFileHasErrors = false catch error @configFileHasErrors = true @@ -678,18 +679,30 @@ class Config @watchSubscription = null save: -> - CSON.writeFileSync(@configFilePath, @settings) + allSettings = global: @settings + + for settingsObject in @scopedSettingsStore.propertiesForSource('user-config') + for selector, properties of settingsObject + allSettings[selector] = properties + + CSON.writeFileSync(@configFilePath, allSettings) ### Section: Private methods managing global settings ### - setAll: (newSettings) -> + resetUserSettings: (newSettings) -> unless isPlainObject(newSettings) @settings = {} @emitter.emit 'did-change' return + if newSettings.global? + scopedSettings = newSettings + newSettings = newSettings.global + delete scopedSettings.global + @resetUserScopedSettings(scopedSettings) + unsetUnspecifiedValues = (keyPath, value) => if isPlainObject(value) keys = if keyPath? then keyPath.split('.') else [] @@ -782,12 +795,13 @@ class Config Section: Private Scoped Settings ### - addScopedSettings: (name, selector, value) -> - if arguments.length < 3 - value = selector - selector = name - name = null + resetUserScopedSettings: (newScopedSettings) -> + @usersScopedSettings?.dispose() + @usersScopedSettings = new CompositeDisposable + @usersScopedSettings.add @scopedSettingsStore.addProperties('user-config', newScopedSettings) + @emitter.emit 'did-change' + addScopedSettings: (name, selector, value) -> settingsBySelector = {} settingsBySelector[selector] = value disposable = @scopedSettingsStore.addProperties(name, settingsBySelector) @@ -801,7 +815,11 @@ class Config newValue = {} _.setValueForKeyPath(newValue, keyPath, value) value = newValue - @addScopedSettings(null, selector, value) + + settingsBySelector = {} + settingsBySelector[selector] = value + @usersScopedSettings.add @scopedSettingsStore.addProperties('user-config', settingsBySelector) + @emitter.emit 'did-change' getRawScopedValue: (scopeDescriptor, keyPath) -> scopeChain = scopeDescriptor diff --git a/src/syntax.coffee b/src/syntax.coffee index 84bb2e0bb..8c04a424a 100644 --- a/src/syntax.coffee +++ b/src/syntax.coffee @@ -40,6 +40,7 @@ class Syntax extends GrammarRegistry atom.config.scopedSettingsStore addProperties: (args...) -> + args.unshift(null) if args.length == 2 deprecate 'Consider using atom.config.set() instead. A direct (but private) replacement is available at atom.config.addScopedSettings().' atom.config.addScopedSettings(args...) From 2a73d7052d3810f19b9c882846701a17fa85cbf1 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 16:10:00 -0700 Subject: [PATCH 17/26] :lipstick: Clean up spec names --- spec/text-editor-component-spec.coffee | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 77e406bfe..1e2b6cee5 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2283,11 +2283,7 @@ describe "TextEditorComponent", -> editor.setEditorWidthInChars(20) coffeeEditor.setEditorWidthInChars(20) - it 'isSoftWrapped() returns true for coffeescript', -> - expect(editor.isSoftWrapped()).toBe false - expect(coffeeEditor.isSoftWrapped()).toBe true - - it 'correctly wraps coffeescript file', -> + it 'wraps only the editor with scoped `editor.softWrap` set to true', -> expect(editor.lineTextForScreenRow(2)).toEqual ' if (items.length <= 1) return items;' expect(coffeeEditor.lineTextForScreenRow(3)).toEqual ' return items ' @@ -2311,6 +2307,11 @@ describe "TextEditorComponent", -> expect(editor.isSoftWrapped()).toBe true expect(editor.lineTextForScreenRow(0)).toEqual 'var quicksort = ' + describe '::isSoftWrapped()', -> + it 'returns the correct value based on the scoped settings', -> + expect(editor.isSoftWrapped()).toBe false + expect(coffeeEditor.isSoftWrapped()).toBe true + describe 'invisibles settings', -> [jsInvisibles, coffeeInvisibles] = [] beforeEach -> @@ -2335,10 +2336,10 @@ describe "TextEditorComponent", -> editor.setText " a line with tabs\tand spaces \n" nextAnimationFrame() - it "renders the invisibles for the javascript scoped invisibles", -> + it "renders the invisibles for the editor when scoped editor.showInvisibles is true", -> expect(component.lineNodeForScreenRow(0).textContent).toBe "#{jsInvisibles.space}a line with tabs#{jsInvisibles.tab}and spaces#{jsInvisibles.space}#{jsInvisibles.eol}" - it "does not renders the invisibles for the coffeescript scope because editor.showInvisibles is false", -> + it "does not renders the invisibles for when scoped editor.showInvisibles is false", -> editor.setGrammar(coffeeEditor.getGrammar()) nextAnimationFrame() expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces " @@ -2368,7 +2369,7 @@ describe "TextEditorComponent", -> atom.config.set '.source.js', 'editor.showIndentGuide', true atom.config.set '.source.coffee', 'editor.showIndentGuide', false - it "has an 'indent-guide' class when using the javascript grammar, but not when using the coffeescript grammar", -> + it "has an 'indent-guide' class when scoped editor.showIndentGuide is true, but not when scoped editor.showIndentGuide is false", -> line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1)) expect(line1LeafNodes[0].textContent).toBe ' ' expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe true @@ -2381,7 +2382,7 @@ describe "TextEditorComponent", -> expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe false expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false - it "removes the 'indent-guide' class unsetting the value for javascript", -> + it "removes the 'indent-guide' class when setting scoped editor.showIndentGuide to false", -> line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1)) expect(line1LeafNodes[0].textContent).toBe ' ' expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe true From 26524e87b0d12ce89f3fdbf87244a6e7396ed14d Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 17:30:36 -0700 Subject: [PATCH 18/26] :lipstick: tests --- spec/text-editor-component-spec.coffee | 2 +- spec/text-editor-spec.coffee | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 1e2b6cee5..1d1780438 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2344,7 +2344,7 @@ describe "TextEditorComponent", -> nextAnimationFrame() expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces " - it "re-renders the invisibles for the when the invisible settings change", -> + it "re-renders the invisibles when the invisible settings change", -> jsGrammar = editor.getGrammar() editor.setGrammar(coffeeEditor.getGrammar()) atom.config.set '.source.coffee', 'editor.showInvisibles', true diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 5805eee64..e8a73fc87 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3038,7 +3038,7 @@ describe "TextEditor", -> atom.workspace.open(null, softTabs: false).then (editor) -> expect(editor.getSoftTabs()).toBeFalsy() - describe '.getTabLength(scopeDescriptor)', -> + describe '.getTabLength()', -> describe 'when scoped settings are used', -> coffeeEditor = null beforeEach -> @@ -3061,7 +3061,7 @@ describe "TextEditor", -> coffeeEditor.setCursorBufferPosition [0, 10] expect(coffeeEditor.getTabLength(coffeeEditor.scopesAtCursor())).toBe 4 - it 'will retokenize when the tab length is updated via .setTabLength()', -> + it 'retokenizes when the tab length is updated via .setTabLength()', -> expect(editor.getTabLength()).toBe 2 expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 @@ -3069,7 +3069,7 @@ describe "TextEditor", -> expect(editor.getTabLength()).toBe 6 expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6 - it 'will retokenize when the editor.tabLength setting is updated', -> + it 'retokenizes when the editor.tabLength setting is updated', -> expect(editor.getTabLength()).toBe 2 expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 @@ -3077,7 +3077,7 @@ describe "TextEditor", -> expect(editor.getTabLength()).toBe 6 expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6 - it 'will update the tab length when the grammar changes', -> + it 'updates the tab length when the grammar changes', -> atom.config.set '.source.coffee', 'editor.tabLength', 6 expect(editor.getTabLength()).toBe 2 From c315631efdc2337b97d26963d0119b1f5d1c7fc6 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 17:30:57 -0700 Subject: [PATCH 19/26] Remove scopeDescriptor from getTabLength --- src/display-buffer.coffee | 4 ++-- src/text-editor.coffee | 4 +--- src/tokenized-buffer.coffee | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index ec3ec53f9..9dd844189 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -405,8 +405,8 @@ class DisplayBuffer extends Model # Retrieves the current tab length. # # Returns a {Number}. - getTabLength: (scopeDescriptor) -> - @tokenizedBuffer.getTabLength(scopeDescriptor) + getTabLength: -> + @tokenizedBuffer.getTabLength() # Specifies the tab length. # diff --git a/src/text-editor.coffee b/src/text-editor.coffee index efad10108..acfa46d95 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -2185,10 +2185,8 @@ class TextEditor extends Model # Essential: Get the on-screen length of tab characters. # - # * `scopeDescriptor` (optional) {Array} of {String}s. - # # Returns a {Number}. - getTabLength: (scopeDescriptor) -> @displayBuffer.getTabLength(scopeDescriptor) + getTabLength: -> @displayBuffer.getTabLength() # Essential: Set the on-screen length of tab characters. Setting this to a # {Number} This will override the `editor.tabLength` setting. diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index f8836a3f3..260f018f0 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -117,8 +117,8 @@ class TokenizedBuffer extends Model setVisible: (@visible) -> @tokenizeInBackground() if @visible - getTabLength: (scopeDescriptor) -> - @tabLength ? atom.config.get(scopeDescriptor ? @grammarScopeDescriptor, 'editor.tabLength') + getTabLength: -> + @tabLength ? atom.config.get(@grammarScopeDescriptor, 'editor.tabLength') setTabLength: (@tabLength) -> @retokenizeLines() From 8910dd1a11321f503e17c735d78d2a2268f264d4 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 17:33:08 -0700 Subject: [PATCH 20/26] Update to not new format from propertiesForSource --- spec/config-spec.coffee | 4 ++-- src/config.coffee | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 21c327533..8f6dad245 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -230,11 +230,11 @@ describe "Config", -> expect(writtenConfig).toEqualJson global: atom.config.settings - '*.ruby.source': + '.ruby.source': foo: bar: 'ruby' omg: 'wow' - '*.coffee.source': + '.coffee.source': foo: bar: 'coffee' diff --git a/src/config.coffee b/src/config.coffee index 5ccb835c5..bda7f2014 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -679,12 +679,8 @@ class Config @watchSubscription = null save: -> - allSettings = global: @settings - - for settingsObject in @scopedSettingsStore.propertiesForSource('user-config') - for selector, properties of settingsObject - allSettings[selector] = properties - + allSettings = @scopedSettingsStore.propertiesForSource('user-config') + allSettings.global = @settings CSON.writeFileSync(@configFilePath, allSettings) ### From d7cd0de0f85db205d5e52039febfd29067ceb94e Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 17:40:57 -0700 Subject: [PATCH 21/26] Upgrade scoped-property-store --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a77ab5ae5..40cb1008f 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "reactionary-atom-fork": "^1.0.0", "runas": "1.0.1", "scandal": "1.0.2", - "scoped-property-store": "^0.11.0", + "scoped-property-store": "^0.12.0", "scrollbar-style": "^1.0.2", "season": "^1.0.2", "semver": "1.1.4", From f2d480fc7241179ee0db664ece2950f840d6dca2 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 17:49:16 -0700 Subject: [PATCH 22/26] getGrammarScopeDescriptor -> getRootScopeDescriptor --- src/display-buffer.coffee | 12 ++++++------ src/text-editor-component.coffee | 2 +- src/text-editor.coffee | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 9dd844189..16c964908 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -65,7 +65,7 @@ class DisplayBuffer extends Model @scopedConfigSubscriptions?.dispose() @scopedConfigSubscriptions = subscriptions = new CompositeDisposable - scopeDescriptor = @getGrammarScopeDescriptor() + scopeDescriptor = @getRootScopeDescriptor() subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrap', => @updateWrappedScreenLines() @@ -328,7 +328,7 @@ class DisplayBuffer extends Model return 0 unless lineHeight > 0 scrollHeight = @getLineCount() * lineHeight - if @height? and atom.config.get(@getGrammarScopeDescriptor(), 'editor.scrollPastEnd') + if @height? and atom.config.get(@getRootScopeDescriptor(), 'editor.scrollPastEnd') scrollHeight = scrollHeight + @height - (lineHeight * 3) scrollHeight @@ -429,7 +429,7 @@ class DisplayBuffer extends Model @isSoftWrapped() isSoftWrapped: -> - @softWrapped ? atom.config.get(@getGrammarScopeDescriptor(), 'editor.softWrap') ? false + @softWrapped ? atom.config.get(@getRootScopeDescriptor(), 'editor.softWrap') ? false # Set the number of characters that fit horizontally in the editor. # @@ -451,8 +451,8 @@ class DisplayBuffer extends Model @editorWidthInChars getSoftWrapColumn: -> - if atom.config.get(@getGrammarScopeDescriptor(), 'editor.softWrapAtPreferredLineLength') - Math.min(@getEditorWidthInChars(), atom.config.get(@getGrammarScopeDescriptor(), 'editor.preferredLineLength')) + if atom.config.get(@getRootScopeDescriptor(), 'editor.softWrapAtPreferredLineLength') + Math.min(@getEditorWidthInChars(), atom.config.get(@getRootScopeDescriptor(), 'editor.preferredLineLength')) else @getEditorWidthInChars() @@ -1047,7 +1047,7 @@ class DisplayBuffer extends Model line = @tokenizedLineForScreenRow(row).text console.log row, @bufferRowForScreenRow(row), line, line.length - getGrammarScopeDescriptor: -> + getRootScopeDescriptor: -> @tokenizedBuffer.grammarScopeDescriptor handleTokenizedBufferChange: (tokenizedBufferChange) => diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 442f42a7e..925e6fa63 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -416,7 +416,7 @@ TextEditorComponent = React.createClass @scopedConfigSubscriptions?.dispose() @scopedConfigSubscriptions = subscriptions = new CompositeDisposable - scopeDescriptor = editor.getGrammarScopeDescriptor() + scopeDescriptor = editor.getRootScopeDescriptor() subscriptions.add atom.config.observe scopeDescriptor, 'editor.showIndentGuide', @setShowIndentGuide subscriptions.add atom.config.observe scopeDescriptor, 'editor.showLineNumbers', @setShowLineNumbers diff --git a/src/text-editor.coffee b/src/text-editor.coffee index acfa46d95..abed65842 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -163,7 +163,7 @@ class TextEditor extends Model @scopedConfigSubscriptions?.dispose() @scopedConfigSubscriptions = subscriptions = new CompositeDisposable - scopeDescriptor = @getGrammarScopeDescriptor() + scopeDescriptor = @getRootScopeDescriptor() subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.showInvisibles', => @updateInvisibles() subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.invisibles', => @updateInvisibles() @@ -2369,7 +2369,7 @@ class TextEditor extends Model if cursor = @getLastCursor() cursor.getScopes() else - @getGrammarScopeDescriptor() + @getRootScopeDescriptor() getCursorScopes: -> deprecate 'Use TextEditor::scopesAtCursor() instead' @scopesAtCursor() @@ -2396,8 +2396,8 @@ class TextEditor extends Model bufferRangeForScopeAtCursor: (selector) -> @displayBuffer.bufferRangeForScopeAtPosition(selector, @getCursorBufferPosition()) - getGrammarScopeDescriptor: -> - @displayBuffer.getGrammarScopeDescriptor() + getRootScopeDescriptor: -> + @displayBuffer.getRootScopeDescriptor() logCursorScope: -> console.log @scopesAtCursor() @@ -2668,14 +2668,14 @@ class TextEditor extends Model ### shouldAutoIndent: -> - atom.config.get(@getGrammarScopeDescriptor(), "editor.autoIndent") + atom.config.get(@getRootScopeDescriptor(), "editor.autoIndent") shouldShowInvisibles: -> - not @mini and atom.config.get(@getGrammarScopeDescriptor(), 'editor.showInvisibles') + not @mini and atom.config.get(@getRootScopeDescriptor(), 'editor.showInvisibles') updateInvisibles: -> if @shouldShowInvisibles() - @displayBuffer.setInvisibles(atom.config.get(@getGrammarScopeDescriptor(), 'editor.invisibles')) + @displayBuffer.setInvisibles(atom.config.get(@getRootScopeDescriptor(), 'editor.invisibles')) else @displayBuffer.setInvisibles(null) From 841412bd017d1c53d7e3d09055b59a56ef2c2daa Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 17:54:31 -0700 Subject: [PATCH 23/26] Fix spec --- spec/text-editor-spec.coffee | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index e8a73fc87..40a6448fe 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -3051,16 +3051,12 @@ describe "TextEditor", -> atom.packages.deactivatePackages() atom.packages.unloadPackages() - it 'will return correct values based on the scope of the set grammars', -> + it 'returns correct values based on the scope of the set grammars', -> atom.config.set '.source.coffee', 'editor.tabLength', 6 - atom.config.set '.source.coffee .class', 'editor.tabLength', 4 expect(editor.getTabLength()).toBe 2 expect(coffeeEditor.getTabLength()).toBe 6 - coffeeEditor.setCursorBufferPosition [0, 10] - expect(coffeeEditor.getTabLength(coffeeEditor.scopesAtCursor())).toBe 4 - it 'retokenizes when the tab length is updated via .setTabLength()', -> expect(editor.getTabLength()).toBe 2 expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2 From c2081fa569c495954d9c1736659d86e172b3b2f5 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 18:01:29 -0700 Subject: [PATCH 24/26] :lipstick: test --- spec/text-editor-spec.coffee | 55 ++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 40a6448fe..68722a90f 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -1259,7 +1259,6 @@ describe "TextEditor", -> editor.selectWordsContainingCursors() expect(editor.getSelectedText()).toBe 'var' - describe "when the cursor is inside a region of whitespace", -> it "selects the whitespace region", -> editor.setCursorScreenPosition([5, 2]) @@ -1277,6 +1276,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.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o + + 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]] + + atom.config.set '.source.coffee', 'editor.nonWordCharacters', 'qusort' + + coffeeEditor.setCursorBufferPosition [0, 9] + coffeeEditor.selectWordsContainingCursors() + expect(coffeeEditor.getSelectedBufferRange()).toEqual [[0, 8], [0, 11]] + + editor.setCursorBufferPosition [0, 7] + editor.selectWordsContainingCursors() + expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [0, 13]] + 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", -> editor.setCursorScreenPosition [0,5] @@ -3327,7 +3349,7 @@ describe "TextEditor", -> atom.packages.deactivatePackages() atom.packages.unloadPackages() - it "does not normalize the indentation level for coffee files, but does for js files", -> + it "normalizes the indentation level based on scoped settings", -> copyText(" while (true) {\n foo();\n }\n", {startColumn: 2, textEditor: coffeeEditor}) coffeeEditor.setCursorBufferPosition([4, 4]) coffeeEditor.pasteText() @@ -3793,32 +3815,3 @@ describe "TextEditor", -> editor.setPlaceholderText('OK') expect(handler).toHaveBeenCalledWith 'OK' expect(editor.getPlaceholderText()).toBe 'OK' - - describe '.selectWordsContainingCursors()', -> - it 'selects the word containing the cursor', -> - editor.setCursorBufferPosition [0, 7] # in the middle of quicksort - editor.selectWordsContainingCursors() - expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [0, 13]] - - describe 'when editor.nonWordCharacters is set scoped to editor.coffee', -> - coffeeEditor = null - beforeEach -> - waitsForPromise -> - atom.packages.activatePackage('language-coffee-script') - waitsForPromise -> - atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o - - it 'selects to the bounds set by the new config in coffee files, but not in js files', -> - coffeeEditor.setCursorBufferPosition [0, 9] # in the middle of quicksort - coffeeEditor.selectWordsContainingCursors() - expect(coffeeEditor.getSelectedBufferRange()).toEqual [[0, 6], [0, 15]] - - atom.config.set '.source.coffee', 'editor.nonWordCharacters', 'qusort' - - coffeeEditor.setCursorBufferPosition [0, 9] - coffeeEditor.selectWordsContainingCursors() - expect(coffeeEditor.getSelectedBufferRange()).toEqual [[0, 8], [0, 11]] - - editor.setCursorBufferPosition [0, 7] - editor.selectWordsContainingCursors() - expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [0, 13]] From a28fed8bae5e29f281a12b89cba7d194bf5f08b9 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Tue, 7 Oct 2014 18:06:00 -0700 Subject: [PATCH 25/26] :memo: Expose TextEditor::observeGrammar --- src/text-editor.coffee | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index abed65842..000359077 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -260,14 +260,23 @@ class TextEditor extends Model onDidChangeSoftWrapped: (callback) -> @displayBuffer.onDidChangeSoftWrapped(callback) + # Extended: Calls your `callback` when the grammar that interprets and + # colorizes the text has been changed. Immediately calls your callback with + # the current grammar. + # + # * `callback` {Function} + # * `grammar` {Grammar} + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. observeGrammar: (callback) -> callback(@getGrammar()) @onDidChangeGrammar(callback) - # Extended: Calls your `callback` when the grammar that interprets and colorizes the text has - # been changed. + # Extended: Calls your `callback` when the grammar that interprets and + # colorizes the text has been changed. # # * `callback` {Function} + # * `grammar` {Grammar} # # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. onDidChangeGrammar: (callback) -> From c154b8f4ec984c4d3536ea610f543fce7852f8d3 Mon Sep 17 00:00:00 2001 From: Ben Ogle Date: Wed, 8 Oct 2014 15:17:04 -0700 Subject: [PATCH 26/26] :lipstick: edit spec descriptions --- spec/text-editor-component-spec.coffee | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 1d1780438..68cb388e1 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -2283,7 +2283,7 @@ describe "TextEditorComponent", -> editor.setEditorWidthInChars(20) coffeeEditor.setEditorWidthInChars(20) - it 'wraps only the editor with scoped `editor.softWrap` set to true', -> + it "wraps lines when editor.softWrap is true for a matching scope", -> expect(editor.lineTextForScreenRow(2)).toEqual ' if (items.length <= 1) return items;' expect(coffeeEditor.lineTextForScreenRow(3)).toEqual ' return items ' @@ -2336,10 +2336,10 @@ describe "TextEditorComponent", -> editor.setText " a line with tabs\tand spaces \n" nextAnimationFrame() - it "renders the invisibles for the editor when scoped editor.showInvisibles is true", -> + it "renders the invisibles when editor.showInvisibles is true for a given grammar", -> expect(component.lineNodeForScreenRow(0).textContent).toBe "#{jsInvisibles.space}a line with tabs#{jsInvisibles.tab}and spaces#{jsInvisibles.space}#{jsInvisibles.eol}" - it "does not renders the invisibles for when scoped editor.showInvisibles is false", -> + it "does not render the invisibles when editor.showInvisibles is false for a given grammar", -> editor.setGrammar(coffeeEditor.getGrammar()) nextAnimationFrame() expect(component.lineNodeForScreenRow(0).textContent).toBe " a line with tabs and spaces " @@ -2382,7 +2382,7 @@ describe "TextEditorComponent", -> expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe false expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false - it "removes the 'indent-guide' class when setting scoped editor.showIndentGuide to false", -> + it "removes the 'indent-guide' class when editor.showIndentGuide to false", -> line1LeafNodes = getLeafNodes(component.lineNodeForScreenRow(1)) expect(line1LeafNodes[0].textContent).toBe ' ' expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe true @@ -2395,7 +2395,6 @@ describe "TextEditorComponent", -> expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe false expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false - buildMouseEvent = (type, properties...) -> properties = extend({bubbles: true, cancelable: true}, properties...) properties.detail ?= 1