mirror of
https://github.com/atom/atom.git
synced 2026-01-22 21:38:10 -05:00
Merge pull request #3718 from atom/bo-scoped-user-config
Add scoped settings to user config
This commit is contained in:
@@ -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",
|
||||
@@ -50,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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ require '../src/window'
|
||||
atom.initialize()
|
||||
atom.restoreWindowDimensions()
|
||||
|
||||
require 'jasmine-json'
|
||||
require '../vendor/jasmine-jquery'
|
||||
path = require 'path'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
@@ -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] = []
|
||||
|
||||
@@ -2214,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", ->
|
||||
@@ -2268,6 +2261,140 @@ 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 "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 '
|
||||
|
||||
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 '::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 ->
|
||||
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 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 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 "
|
||||
|
||||
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
|
||||
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}"
|
||||
|
||||
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 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
|
||||
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 when editor.showIndentGuide to false", ->
|
||||
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
|
||||
@@ -2304,3 +2431,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]
|
||||
|
||||
@@ -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]
|
||||
@@ -3038,6 +3060,51 @@ describe "TextEditor", ->
|
||||
atom.workspace.open(null, softTabs: false).then (editor) ->
|
||||
expect(editor.getSoftTabs()).toBeFalsy()
|
||||
|
||||
describe '.getTabLength()', ->
|
||||
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
|
||||
|
||||
afterEach: ->
|
||||
atom.packages.deactivatePackages()
|
||||
atom.packages.unloadPackages()
|
||||
|
||||
it 'returns correct values based on the scope of the set grammars', ->
|
||||
atom.config.set '.source.coffee', 'editor.tabLength', 6
|
||||
|
||||
expect(editor.getTabLength()).toBe 2
|
||||
expect(coffeeEditor.getTabLength()).toBe 6
|
||||
|
||||
it 'retokenizes 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 'retokenizes when the editor.tabLength setting 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 'updates 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)
|
||||
@@ -3077,14 +3144,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)", ->
|
||||
@@ -3191,6 +3259,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)
|
||||
@@ -3240,6 +3333,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 "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()
|
||||
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")
|
||||
@@ -3252,10 +3376,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')
|
||||
@@ -3263,9 +3395,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
|
||||
|
||||
@@ -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,26 @@ class Config
|
||||
@watchSubscription = null
|
||||
|
||||
save: ->
|
||||
CSON.writeFileSync(@configFilePath, @settings)
|
||||
allSettings = @scopedSettingsStore.propertiesForSource('user-config')
|
||||
allSettings.global = @settings
|
||||
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 +791,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 +811,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {}
|
||||
@@ -55,17 +54,27 @@ class DisplayBuffer extends Model
|
||||
@decorationsByMarkerId = {}
|
||||
@updateAllScreenLines()
|
||||
@createFoldForMarker(marker) for marker in @buffer.findMarkers(@getFoldMarkerAttributes())
|
||||
@subscribe @tokenizedBuffer.observeGrammar @subscribeToScopedConfigSettings
|
||||
@subscribe @tokenizedBuffer.onDidChange @handleTokenizedBufferChange
|
||||
@subscribe @buffer.onDidUpdateMarkers @handleBufferMarkersUpdated
|
||||
@subscribe @buffer.onDidCreateMarker @handleBufferMarkerCreated
|
||||
|
||||
@subscribe atom.config.onDidChange 'editor.preferredLineLength', =>
|
||||
@updateWrappedScreenLines() if @isSoftWrapped() and atom.config.get('editor.softWrapAtPreferredLineLength')
|
||||
@updateAllScreenLines()
|
||||
|
||||
@subscribe atom.config.onDidChange 'editor.softWrapAtPreferredLineLength', =>
|
||||
subscribeToScopedConfigSettings: =>
|
||||
@scopedConfigSubscriptions?.dispose()
|
||||
@scopedConfigSubscriptions = subscriptions = new CompositeDisposable
|
||||
|
||||
scopeDescriptor = @getRootScopeDescriptor()
|
||||
|
||||
subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrap', =>
|
||||
@updateWrappedScreenLines()
|
||||
|
||||
subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.softWrapAtPreferredLineLength', =>
|
||||
@updateWrappedScreenLines() if @isSoftWrapped()
|
||||
|
||||
@updateAllScreenLines()
|
||||
subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.preferredLineLength', =>
|
||||
@updateWrappedScreenLines() if @isSoftWrapped() and atom.config.get(scopeDescriptor, 'editor.softWrapAtPreferredLineLength')
|
||||
|
||||
serializeParams: ->
|
||||
id: @id
|
||||
@@ -319,7 +328,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(@getRootScopeDescriptor(), 'editor.scrollPastEnd')
|
||||
scrollHeight = scrollHeight + @height - (lineHeight * 3)
|
||||
|
||||
scrollHeight
|
||||
@@ -412,11 +421,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(@getRootScopeDescriptor(), 'editor.softWrap') ? false
|
||||
|
||||
# Set the number of characters that fit horizontally in the editor.
|
||||
#
|
||||
@@ -438,8 +451,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(@getRootScopeDescriptor(), 'editor.softWrapAtPreferredLineLength')
|
||||
Math.min(@getEditorWidthInChars(), atom.config.get(@getRootScopeDescriptor(), 'editor.preferredLineLength'))
|
||||
else
|
||||
@getEditorWidthInChars()
|
||||
|
||||
@@ -1034,6 +1047,9 @@ class DisplayBuffer extends Model
|
||||
line = @tokenizedLineForScreenRow(row).text
|
||||
console.log row, @bufferRowForScreenRow(row), line, line.length
|
||||
|
||||
getRootScopeDescriptor: ->
|
||||
@tokenizedBuffer.grammarScopeDescriptor
|
||||
|
||||
handleTokenizedBufferChange: (tokenizedBufferChange) =>
|
||||
{start, end, delta, bufferChange} = tokenizedBufferChange
|
||||
@updateScreenLines(start, end + 1, delta, delayChangeEvent: bufferChange?)
|
||||
|
||||
@@ -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...)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@scopedConfigSubscriptions?.dispose()
|
||||
@scopedConfigSubscriptions = subscriptions = new CompositeDisposable
|
||||
|
||||
scopeDescriptor = editor.getRootScopeDescriptor()
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -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,14 +84,12 @@ 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)
|
||||
@@ -113,9 +111,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 +157,20 @@ class TextEditor extends Model
|
||||
@subscribe @displayBuffer.onDidAddDecoration (decoration) => @emit 'decoration-added', decoration
|
||||
@subscribe @displayBuffer.onDidRemoveDecoration (decoration) => @emit 'decoration-removed', decoration
|
||||
|
||||
@subscribeToScopedConfigSettings()
|
||||
|
||||
subscribeToScopedConfigSettings: ->
|
||||
@scopedConfigSubscriptions?.dispose()
|
||||
@scopedConfigSubscriptions = subscriptions = new CompositeDisposable
|
||||
|
||||
scopeDescriptor = @getRootScopeDescriptor()
|
||||
|
||||
subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.showInvisibles', => @updateInvisibles()
|
||||
subscriptions.add atom.config.onDidChange scopeDescriptor, 'editor.invisibles', => @updateInvisibles()
|
||||
|
||||
getViewClass: ->
|
||||
require './text-editor-view'
|
||||
|
||||
destroyed: ->
|
||||
@unsubscribe()
|
||||
selection.destroy() for selection in @getSelections()
|
||||
@@ -251,10 +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.
|
||||
# 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.
|
||||
#
|
||||
# * `callback` {Function}
|
||||
# * `grammar` {Grammar}
|
||||
#
|
||||
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
|
||||
onDidChangeGrammar: (callback) ->
|
||||
@@ -485,10 +507,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
|
||||
@@ -2176,9 +2197,11 @@ class TextEditor extends Model
|
||||
# Returns a {Number}.
|
||||
getTabLength: -> @displayBuffer.getTabLength()
|
||||
|
||||
# Essential: Set the on-screen length of tab characters.
|
||||
# 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
|
||||
# * `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.
|
||||
@@ -2351,7 +2374,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
|
||||
@getRootScopeDescriptor()
|
||||
getCursorScopes: ->
|
||||
deprecate 'Use TextEditor::scopesAtCursor() instead'
|
||||
@scopesAtCursor()
|
||||
@@ -2378,6 +2405,9 @@ class TextEditor extends Model
|
||||
bufferRangeForScopeAtCursor: (selector) ->
|
||||
@displayBuffer.bufferRangeForScopeAtPosition(selector, @getCursorBufferPosition())
|
||||
|
||||
getRootScopeDescriptor: ->
|
||||
@displayBuffer.getRootScopeDescriptor()
|
||||
|
||||
logCursorScope: ->
|
||||
console.log @scopesAtCursor()
|
||||
|
||||
@@ -2429,7 +2459,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
|
||||
|
||||
@@ -2647,14 +2677,14 @@ class TextEditor extends Model
|
||||
###
|
||||
|
||||
shouldAutoIndent: ->
|
||||
atom.config.get("editor.autoIndent")
|
||||
atom.config.get(@getRootScopeDescriptor(), "editor.autoIndent")
|
||||
|
||||
shouldShowInvisibles: ->
|
||||
not @mini and atom.config.get('editor.showInvisibles')
|
||||
not @mini and atom.config.get(@getRootScopeDescriptor(), 'editor.showInvisibles')
|
||||
|
||||
updateInvisibles: ->
|
||||
if @shouldShowInvisibles()
|
||||
@displayBuffer.setInvisibles(atom.config.get('editor.invisibles'))
|
||||
@displayBuffer.setInvisibles(atom.config.get(@getRootScopeDescriptor(), 'editor.invisibles'))
|
||||
else
|
||||
@displayBuffer.setInvisibles(null)
|
||||
|
||||
@@ -2666,6 +2696,8 @@ class TextEditor extends Model
|
||||
@softTabs = @usesSoftTabs() ? @softTabs
|
||||
|
||||
handleGrammarChange: ->
|
||||
@updateInvisibles()
|
||||
@subscribeToScopedConfigSettings()
|
||||
@unfoldAll()
|
||||
@emit 'grammar-changed'
|
||||
@emitter.emit 'did-change-grammar'
|
||||
|
||||
@@ -25,18 +25,12 @@ 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()
|
||||
|
||||
@subscribe @$tabLength.changes, (tabLength) => @retokenizeLines()
|
||||
|
||||
@subscribe atom.config.onDidChange 'editor.tabLength', ({newValue}) => @setTabLength(newValue)
|
||||
|
||||
@reloadGrammar()
|
||||
|
||||
serializeParams: ->
|
||||
@@ -48,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
|
||||
|
||||
@@ -81,9 +79,16 @@ 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()
|
||||
|
||||
@grammarTabLengthSubscription?.dispose()
|
||||
@grammarTabLengthSubscription = atom.config.onDidChange @grammarScopeDescriptor, 'editor.tabLength', =>
|
||||
@retokenizeLines()
|
||||
@subscribe @grammarTabLengthSubscription
|
||||
|
||||
@emit 'grammar-changed', grammar
|
||||
@emitter.emit 'did-change-grammar', grammar
|
||||
|
||||
@@ -112,16 +117,11 @@ class TokenizedBuffer extends Model
|
||||
setVisible: (@visible) ->
|
||||
@tokenizeInBackground() if @visible
|
||||
|
||||
# Retrieves the current tab length.
|
||||
#
|
||||
# Returns a {Number}.
|
||||
getTabLength: ->
|
||||
@tabLength
|
||||
@tabLength ? atom.config.get(@grammarScopeDescriptor, 'editor.tabLength')
|
||||
|
||||
# Specifies the tab length.
|
||||
#
|
||||
# tabLength - A {Number} that defines the new tab length.
|
||||
setTabLength: (@tabLength) ->
|
||||
@retokenizeLines()
|
||||
|
||||
setInvisibles: (invisibles) ->
|
||||
unless _.isEqual(invisibles, @invisibles)
|
||||
|
||||
Reference in New Issue
Block a user