mirror of
https://github.com/atom/atom.git
synced 2026-02-16 01:25:13 -05:00
Merge remote-tracking branch 'refs/remotes/origin/master' into wl-electron-37
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
ChildProcess = require 'child_process'
|
||||
path = require 'path'
|
||||
fs = require 'fs-plus'
|
||||
BufferedProcess = require '../src/buffered-process'
|
||||
|
||||
describe "BufferedProcess", ->
|
||||
@@ -15,20 +16,20 @@ describe "BufferedProcess", ->
|
||||
describe "when there is an error handler specified", ->
|
||||
describe "when an error event is emitted by the process", ->
|
||||
it "calls the error handler and does not throw an exception", ->
|
||||
process = new BufferedProcess
|
||||
command: 'bad-command-nope'
|
||||
bufferedProcess = new BufferedProcess
|
||||
command: 'bad-command-nope1'
|
||||
args: ['nothing']
|
||||
options: {}
|
||||
options: {shell: false}
|
||||
|
||||
errorSpy = jasmine.createSpy().andCallFake (error) -> error.handle()
|
||||
process.onWillThrowError(errorSpy)
|
||||
bufferedProcess.onWillThrowError(errorSpy)
|
||||
|
||||
waitsFor -> errorSpy.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(window.onerror).not.toHaveBeenCalled()
|
||||
expect(errorSpy).toHaveBeenCalled()
|
||||
expect(errorSpy.mostRecentCall.args[0].error.message).toContain 'spawn bad-command-nope ENOENT'
|
||||
expect(errorSpy.mostRecentCall.args[0].error.message).toContain 'spawn bad-command-nope1 ENOENT'
|
||||
|
||||
describe "when an error is thrown spawning the process", ->
|
||||
it "calls the error handler and does not throw an exception", ->
|
||||
@@ -37,13 +38,13 @@ describe "BufferedProcess", ->
|
||||
error.code = 'EAGAIN'
|
||||
throw error
|
||||
|
||||
process = new BufferedProcess
|
||||
bufferedProcess = new BufferedProcess
|
||||
command: 'ls'
|
||||
args: []
|
||||
options: {}
|
||||
|
||||
errorSpy = jasmine.createSpy().andCallFake (error) -> error.handle()
|
||||
process.onWillThrowError(errorSpy)
|
||||
bufferedProcess.onWillThrowError(errorSpy)
|
||||
|
||||
waitsFor -> errorSpy.callCount > 0
|
||||
|
||||
@@ -53,56 +54,24 @@ describe "BufferedProcess", ->
|
||||
expect(errorSpy.mostRecentCall.args[0].error.message).toContain 'Something is really wrong'
|
||||
|
||||
describe "when there is not an error handler specified", ->
|
||||
it "calls the error handler and does not throw an exception", ->
|
||||
process = new BufferedProcess
|
||||
command: 'bad-command-nope'
|
||||
it "does throw an exception", ->
|
||||
new BufferedProcess
|
||||
command: 'bad-command-nope2'
|
||||
args: ['nothing']
|
||||
options: {}
|
||||
options: {shell: false}
|
||||
|
||||
waitsFor -> window.onerror.callCount > 0
|
||||
|
||||
runs ->
|
||||
expect(window.onerror).toHaveBeenCalled()
|
||||
expect(window.onerror.mostRecentCall.args[0]).toContain 'Failed to spawn command `bad-command-nope`'
|
||||
expect(window.onerror.mostRecentCall.args[0]).toContain 'Failed to spawn command `bad-command-nope2`'
|
||||
expect(window.onerror.mostRecentCall.args[4].name).toBe 'BufferedProcessError'
|
||||
|
||||
describe "on Windows", ->
|
||||
originalPlatform = null
|
||||
|
||||
beforeEach ->
|
||||
# Prevent any commands from actually running and affecting the host
|
||||
originalSpawn = ChildProcess.spawn
|
||||
spyOn(ChildProcess, 'spawn').andCallFake ->
|
||||
# Just spawn something that won't actually modify the host
|
||||
if originalPlatform is 'win32'
|
||||
originalSpawn('dir')
|
||||
else
|
||||
originalSpawn('ls')
|
||||
|
||||
originalPlatform = process.platform
|
||||
Object.defineProperty process, 'platform', value: 'win32'
|
||||
|
||||
afterEach ->
|
||||
Object.defineProperty process, 'platform', value: originalPlatform
|
||||
|
||||
describe "when the explorer command is spawned on Windows", ->
|
||||
it "doesn't quote arguments of the form /root,C...", ->
|
||||
new BufferedProcess({command: 'explorer.exe', args: ['/root,C:\\foo']})
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"explorer.exe /root,C:\\foo"'
|
||||
|
||||
it "spawns the command using a cmd.exe wrapper", ->
|
||||
new BufferedProcess({command: 'dir'})
|
||||
expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'cmd.exe'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe '/s'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/d'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '/c'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"dir"'
|
||||
|
||||
it "calls the specified stdout, stderr, and exit callbacks", ->
|
||||
stdout = ''
|
||||
stderr = ''
|
||||
exitCallback = jasmine.createSpy('exit callback')
|
||||
process = new BufferedProcess
|
||||
new BufferedProcess
|
||||
command: atom.packages.getApmPath()
|
||||
args: ['-h']
|
||||
options: {}
|
||||
@@ -116,29 +85,51 @@ describe "BufferedProcess", ->
|
||||
expect(stderr).toContain 'apm - Atom Package Manager'
|
||||
expect(stdout).toEqual ''
|
||||
|
||||
it "calls the specified stdout callback only with whole lines", ->
|
||||
it "calls the specified stdout callback with whole lines", ->
|
||||
exitCallback = jasmine.createSpy('exit callback')
|
||||
baseContent = "There are dozens of us! Dozens! It's as Ann as the nose on Plain's face. Can you believe that the only reason the club is going under is because it's in a terrifying neighborhood? She calls it a Mayonegg. Waiting for the Emmys. BTW did you know won 6 Emmys and was still canceled early by Fox? COME ON. I'll buy you a hundred George Michaels that you can teach to drive! Never once touched my per diem. I'd go to Craft Service, get some raw veggies, bacon, Cup-A-Soup…baby, I got a stew goin'"
|
||||
content = (baseContent for _ in [1..200]).join('\n')
|
||||
loremPath = require.resolve("./fixtures/lorem.txt")
|
||||
content = fs.readFileSync(loremPath).toString()
|
||||
baseContent = content.split('\n')
|
||||
stdout = ''
|
||||
endLength = 10
|
||||
outputAlwaysEndsWithStew = true
|
||||
process = new BufferedProcess
|
||||
command: '/bin/echo'
|
||||
args: [content]
|
||||
allLinesEndWithNewline = true
|
||||
new BufferedProcess
|
||||
command: if process.platform is 'win32' then 'type' else 'cat'
|
||||
args: [loremPath]
|
||||
options: {}
|
||||
stdout: (lines) ->
|
||||
endsWithNewline = (lines.charAt lines.length - 1) is '\n'
|
||||
if not endsWithNewline then allLinesEndWithNewline = false
|
||||
stdout += lines
|
||||
|
||||
end = baseContent.substr(baseContent.length - endLength, endLength)
|
||||
lineEndsWithStew = lines.substr(lines.length - endLength, endLength) is end
|
||||
expect(lineEndsWithStew).toBeTrue
|
||||
|
||||
outputAlwaysEndsWithStew = outputAlwaysEndsWithStew and lineEndsWithStew
|
||||
exit: exitCallback
|
||||
|
||||
waitsFor -> exitCallback.callCount is 1
|
||||
|
||||
runs ->
|
||||
expect(outputAlwaysEndsWithStew).toBeTrue
|
||||
expect(stdout).toBe content += '\n'
|
||||
expect(allLinesEndWithNewline).toBeTrue
|
||||
expect(stdout).toBe content
|
||||
|
||||
describe "on Windows", ->
|
||||
originalPlatform = null
|
||||
|
||||
beforeEach ->
|
||||
# Prevent any commands from actually running and affecting the host
|
||||
originalSpawn = ChildProcess.spawn
|
||||
spyOn(ChildProcess, 'spawn')
|
||||
originalPlatform = process.platform
|
||||
Object.defineProperty process, 'platform', value: 'win32'
|
||||
|
||||
afterEach ->
|
||||
Object.defineProperty process, 'platform', value: originalPlatform
|
||||
|
||||
describe "when the explorer command is spawned on Windows", ->
|
||||
it "doesn't quote arguments of the form /root,C...", ->
|
||||
new BufferedProcess({command: 'explorer.exe', args: ['/root,C:\\foo']})
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"explorer.exe /root,C:\\foo"'
|
||||
|
||||
it "spawns the command using a cmd.exe wrapper when options.shell is undefined", ->
|
||||
new BufferedProcess({command: 'dir'})
|
||||
expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'cmd.exe'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][0]).toBe '/s'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][1]).toBe '/d'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][2]).toBe '/c'
|
||||
expect(ChildProcess.spawn.argsForCall[0][1][3]).toBe '"dir"'
|
||||
|
||||
@@ -74,6 +74,13 @@ describe "CommandRegistry", ->
|
||||
grandchild.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual ['.foo.bar', '.bar', '.foo']
|
||||
|
||||
it "orders inline listeners by reverse registration order", ->
|
||||
calls = []
|
||||
registry.add child, 'command', -> calls.push('child1')
|
||||
registry.add child, 'command', -> calls.push('child2')
|
||||
child.dispatchEvent(new CustomEvent('command', bubbles: true))
|
||||
expect(calls).toEqual ['child2', 'child1']
|
||||
|
||||
it "stops bubbling through ancestors when .stopPropagation() is called on the event", ->
|
||||
calls = []
|
||||
|
||||
|
||||
85
spec/decoration-manager-spec.coffee
Normal file
85
spec/decoration-manager-spec.coffee
Normal file
@@ -0,0 +1,85 @@
|
||||
DecorationManager = require '../src/decoration-manager'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
describe "DecorationManager", ->
|
||||
[decorationManager, buffer, defaultMarkerLayer] = []
|
||||
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
displayLayer = buffer.addDisplayLayer()
|
||||
defaultMarkerLayer = displayLayer.addMarkerLayer()
|
||||
decorationManager = new DecorationManager(displayLayer, defaultMarkerLayer)
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
afterEach ->
|
||||
decorationManager.destroy()
|
||||
buffer.release()
|
||||
|
||||
describe "decorations", ->
|
||||
[marker, decoration, decorationProperties] = []
|
||||
beforeEach ->
|
||||
marker = defaultMarkerLayer.markBufferRange([[2, 13], [3, 15]])
|
||||
decorationProperties = {type: 'line-number', class: 'one'}
|
||||
decoration = decorationManager.decorateMarker(marker, decorationProperties)
|
||||
|
||||
it "can add decorations associated with markers and remove them", ->
|
||||
expect(decoration).toBeDefined()
|
||||
expect(decoration.getProperties()).toBe decorationProperties
|
||||
expect(decorationManager.decorationForId(decoration.id)).toBe decoration
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)[marker.id][0]).toBe decoration
|
||||
|
||||
decoration.destroy()
|
||||
expect(decorationManager.decorationsForScreenRowRange(2, 3)[marker.id]).not.toBeDefined()
|
||||
expect(decorationManager.decorationForId(decoration.id)).not.toBeDefined()
|
||||
|
||||
it "will not fail if the decoration is removed twice", ->
|
||||
decoration.destroy()
|
||||
decoration.destroy()
|
||||
expect(decorationManager.decorationForId(decoration.id)).not.toBeDefined()
|
||||
|
||||
it "does not allow destroyed markers to be decorated", ->
|
||||
marker.destroy()
|
||||
expect(->
|
||||
decorationManager.decorateMarker(marker, {type: 'overlay', item: document.createElement('div')})
|
||||
).toThrow("Cannot decorate a destroyed marker")
|
||||
expect(decorationManager.getOverlayDecorations()).toEqual []
|
||||
|
||||
describe "when a decoration is updated via Decoration::update()", ->
|
||||
it "emits an 'updated' event containing the new and old params", ->
|
||||
decoration.onDidChangeProperties updatedSpy = jasmine.createSpy()
|
||||
decoration.setProperties type: 'line-number', class: 'two'
|
||||
|
||||
{oldProperties, newProperties} = updatedSpy.mostRecentCall.args[0]
|
||||
expect(oldProperties).toEqual decorationProperties
|
||||
expect(newProperties).toEqual {type: 'line-number', gutterName: 'line-number', class: 'two'}
|
||||
|
||||
describe "::getDecorations(properties)", ->
|
||||
it "returns decorations matching the given optional properties", ->
|
||||
expect(decorationManager.getDecorations()).toEqual [decoration]
|
||||
expect(decorationManager.getDecorations(class: 'two').length).toEqual 0
|
||||
expect(decorationManager.getDecorations(class: 'one').length).toEqual 1
|
||||
|
||||
describe "::decorateMarker", ->
|
||||
describe "when decorating gutters", ->
|
||||
[marker] = []
|
||||
|
||||
beforeEach ->
|
||||
marker = defaultMarkerLayer.markBufferRange([[1, 0], [1, 0]])
|
||||
|
||||
it "creates a decoration that is both of 'line-number' and 'gutter' type when called with the 'line-number' type", ->
|
||||
decorationProperties = {type: 'line-number', class: 'one'}
|
||||
decoration = decorationManager.decorateMarker(marker, decorationProperties)
|
||||
expect(decoration.isType('line-number')).toBe true
|
||||
expect(decoration.isType('gutter')).toBe true
|
||||
expect(decoration.getProperties().gutterName).toBe 'line-number'
|
||||
expect(decoration.getProperties().class).toBe 'one'
|
||||
|
||||
it "creates a decoration that is only of 'gutter' type if called with the 'gutter' type and a 'gutterName'", ->
|
||||
decorationProperties = {type: 'gutter', gutterName: 'test-gutter', class: 'one'}
|
||||
decoration = decorationManager.decorateMarker(marker, decorationProperties)
|
||||
expect(decoration.isType('gutter')).toBe true
|
||||
expect(decoration.isType('line-number')).toBe false
|
||||
expect(decoration.getProperties().gutterName).toBe 'test-gutter'
|
||||
expect(decoration.getProperties().class).toBe 'one'
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,10 @@
|
||||
{Point} = require 'text-buffer'
|
||||
{isPairedCharacter} = require '../src/text-utils'
|
||||
|
||||
module.exports =
|
||||
class FakeLinesYardstick
|
||||
constructor: (@model, @lineTopIndex) ->
|
||||
{@displayLayer} = @model
|
||||
@characterWidthsByScope = {}
|
||||
|
||||
getScopedCharacterWidth: (scopeNames, char) ->
|
||||
@@ -24,31 +26,38 @@ class FakeLinesYardstick
|
||||
|
||||
targetRow = screenPosition.row
|
||||
targetColumn = screenPosition.column
|
||||
baseCharacterWidth = @model.getDefaultCharWidth()
|
||||
|
||||
top = @lineTopIndex.pixelPositionAfterBlocksForRow(targetRow)
|
||||
left = 0
|
||||
column = 0
|
||||
|
||||
iterator = @model.tokenizedLineForScreenRow(targetRow).getTokenIterator()
|
||||
while iterator.next()
|
||||
characterWidths = @getScopedCharacterWidths(iterator.getScopes())
|
||||
scopes = []
|
||||
startIndex = 0
|
||||
{tagCodes, lineText} = @model.screenLineForScreenRow(targetRow)
|
||||
for tagCode in tagCodes
|
||||
if @displayLayer.isOpenTagCode(tagCode)
|
||||
scopes.push(@displayLayer.tagForCode(tagCode))
|
||||
else if @displayLayer.isCloseTagCode(tagCode)
|
||||
scopes.splice(scopes.lastIndexOf(@displayLayer.tagForCode(tagCode)), 1)
|
||||
else
|
||||
text = lineText.substr(startIndex, tagCode)
|
||||
startIndex += tagCode
|
||||
characterWidths = @getScopedCharacterWidths(scopes)
|
||||
|
||||
valueIndex = 0
|
||||
text = iterator.getText()
|
||||
while valueIndex < text.length
|
||||
if iterator.isPairedCharacter()
|
||||
char = text
|
||||
charLength = 2
|
||||
valueIndex += 2
|
||||
else
|
||||
char = text[valueIndex]
|
||||
charLength = 1
|
||||
valueIndex++
|
||||
valueIndex = 0
|
||||
while valueIndex < text.length
|
||||
if isPairedCharacter(text, valueIndex)
|
||||
char = text[valueIndex...valueIndex + 2]
|
||||
charLength = 2
|
||||
valueIndex += 2
|
||||
else
|
||||
char = text[valueIndex]
|
||||
charLength = 1
|
||||
valueIndex++
|
||||
|
||||
break if column is targetColumn
|
||||
break if column is targetColumn
|
||||
|
||||
left += characterWidths[char] ? baseCharacterWidth unless char is '\0'
|
||||
column += charLength
|
||||
left += characterWidths[char] ? @model.getDefaultCharWidth() unless char is '\0'
|
||||
column += charLength
|
||||
|
||||
{top, left}
|
||||
|
||||
3
spec/fixtures/lorem.txt
vendored
Normal file
3
spec/fixtures/lorem.txt
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur ultricies nulla id nibh aliquam, vitae euismod ipsum scelerisque. Vestibulum vulputate facilisis nisi, eu rhoncus turpis pretium ut. Curabitur facilisis urna in diam efficitur, vel maximus tellus consectetur. Suspendisse pulvinar felis sed metus tristique, a posuere dui suscipit. Ut vehicula, tellus ac blandit consequat, libero dui hendrerit elit, non pretium metus odio sed dolor. Vivamus quis volutpat ipsum. In convallis magna nec nunc tristique malesuada. Sed sed hendrerit lacus. Etiam arcu dui, consequat vel neque vitae, iaculis egestas justo. Donec lacinia odio nulla, condimentum porta erat accumsan at. Nunc vulputate nulla vel nunc fermentum egestas.
|
||||
Duis ultricies libero elit, nec facilisis mi rhoncus ornare. Aliquam aliquet libero vitae arcu porttitor mattis. Vestibulum ultricies consectetur arcu, non gravida magna eleifend vel. Phasellus varius mattis ultricies. Vestibulum placerat lacus non consectetur fringilla. Duis congue, arcu iaculis vehicula hendrerit, purus odio faucibus ipsum, et fermentum massa tellus euismod nulla. Vivamus pellentesque blandit massa, sit amet hendrerit turpis congue eu. Suspendisse diam dui, vestibulum nec semper varius, maximus eu nunc. Vivamus facilisis pulvinar viverra. Praesent luctus lectus id est porttitor volutpat. Suspendisse est augue, mattis a tincidunt id, condimentum in turpis. Curabitur at erat commodo orci interdum tincidunt. Sed sodales elit odio, a placerat ipsum luctus nec. Sed maximus, justo ut pharetra pellentesque, orci mi faucibus enim, quis viverra arcu dui sed nisl. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Praesent quis velit libero.
|
||||
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Phasellus a rutrum tortor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce bibendum odio et neque vestibulum rutrum. Vestibulum commodo, nibh non sodales lobortis, dui ex consectetur leo, a finibus libero lectus ac diam. Etiam dui nunc, bibendum a tempor vel, vestibulum lacinia neque. Mauris consectetur odio sit amet maximus pretium. Sed rutrum nunc at ante ullamcorper fermentum. Proin at quam a mauris pellentesque viverra. Nunc pretium pulvinar ipsum. Vestibulum eu nibh ut ex gravida tempus. Praesent ut elit ut ligula tristique dapibus ut sit amet leo. Proin non molestie erat.
|
||||
2
spec/fixtures/shebang
vendored
2
spec/fixtures/shebang
vendored
@@ -1,3 +1,3 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
puts "America – fuck yeah!"
|
||||
puts "Atom fixture test"
|
||||
|
||||
@@ -334,66 +334,56 @@ describe "LanguageMode", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editor.tokenizedLineForScreenRow(4).fold
|
||||
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7]
|
||||
[fold1, fold2, fold3] = languageMode.unfoldAll()
|
||||
expect([fold1.start.row, fold1.end.row]).toEqual [0, 12]
|
||||
expect([fold2.start.row, fold2.end.row]).toEqual [1, 9]
|
||||
expect([fold3.start.row, fold3.end.row]).toEqual [4, 7]
|
||||
|
||||
describe ".foldBufferRow(bufferRow)", ->
|
||||
describe "when bufferRow can be folded", ->
|
||||
it "creates a fold based on the syntactic region starting at the given row", ->
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editor.tokenizedLineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
[fold] = languageMode.unfoldAll()
|
||||
expect([fold.start.row, fold.end.row]).toEqual [1, 9]
|
||||
|
||||
describe "when bufferRow can't be folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", ->
|
||||
languageMode.foldBufferRow(8)
|
||||
fold = editor.tokenizedLineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 9
|
||||
[fold] = languageMode.unfoldAll()
|
||||
expect([fold.start.row, fold.end.row]).toEqual [1, 9]
|
||||
|
||||
describe "when the bufferRow is already folded", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
languageMode.foldBufferRow(2)
|
||||
expect(editor.tokenizedLineForScreenRow(1).fold).toBeDefined()
|
||||
expect(editor.tokenizedLineForScreenRow(0).fold).not.toBeDefined()
|
||||
expect(editor.isFoldedAtBufferRow(0)).toBe(false)
|
||||
expect(editor.isFoldedAtBufferRow(1)).toBe(true)
|
||||
|
||||
languageMode.foldBufferRow(1)
|
||||
expect(editor.tokenizedLineForScreenRow(0).fold).toBeDefined()
|
||||
expect(editor.isFoldedAtBufferRow(0)).toBe(true)
|
||||
|
||||
describe "when the bufferRow is in a multi-line comment", ->
|
||||
it "searches upward and downward for surrounding comment lines and folds them as a single fold", ->
|
||||
buffer.insert([1, 0], " //this is a comment\n // and\n //more docs\n\n//second comment")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editor.tokenizedLineForScreenRow(1).fold
|
||||
expect(fold.getStartRow()).toBe 1
|
||||
expect(fold.getEndRow()).toBe 3
|
||||
[fold] = languageMode.unfoldAll()
|
||||
expect([fold.start.row, fold.end.row]).toEqual [1, 3]
|
||||
|
||||
describe "when the bufferRow is a single-line comment", ->
|
||||
it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", ->
|
||||
buffer.insert([1, 0], " //this is a single line comment\n")
|
||||
languageMode.foldBufferRow(1)
|
||||
fold = editor.tokenizedLineForScreenRow(0).fold
|
||||
expect(fold.getStartRow()).toBe 0
|
||||
expect(fold.getEndRow()).toBe 13
|
||||
[fold] = languageMode.unfoldAll()
|
||||
expect([fold.start.row, fold.end.row]).toEqual [0, 13]
|
||||
|
||||
describe ".foldAllAtIndentLevel(indentLevel)", ->
|
||||
it "folds blocks of text at the given indentation level", ->
|
||||
languageMode.foldAllAtIndentLevel(0)
|
||||
expect(editor.lineTextForScreenRow(0)).toBe "var quicksort = function () {"
|
||||
expect(editor.lineTextForScreenRow(0)).toBe "var quicksort = function () {" + editor.displayLayer.foldCharacter
|
||||
expect(editor.getLastScreenRow()).toBe 0
|
||||
|
||||
languageMode.foldAllAtIndentLevel(1)
|
||||
expect(editor.lineTextForScreenRow(0)).toBe "var quicksort = function () {"
|
||||
expect(editor.lineTextForScreenRow(1)).toBe " var sort = function(items) {"
|
||||
expect(editor.lineTextForScreenRow(1)).toBe " var sort = function(items) {" + editor.displayLayer.foldCharacter
|
||||
expect(editor.getLastScreenRow()).toBe 4
|
||||
|
||||
languageMode.foldAllAtIndentLevel(2)
|
||||
@@ -429,59 +419,35 @@ describe "LanguageMode", ->
|
||||
it "folds every foldable line", ->
|
||||
languageMode.foldAll()
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(1).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 4]
|
||||
|
||||
fold3 = editor.tokenizedLineForScreenRow(2).fold.destroy()
|
||||
|
||||
fold4 = editor.tokenizedLineForScreenRow(3).fold
|
||||
expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8]
|
||||
|
||||
fold5 = editor.tokenizedLineForScreenRow(6).fold
|
||||
expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [11, 16]
|
||||
fold5.destroy()
|
||||
|
||||
fold6 = editor.tokenizedLineForScreenRow(13).fold
|
||||
expect([fold6.getStartRow(), fold6.getEndRow()]).toEqual [21, 22]
|
||||
fold6.destroy()
|
||||
folds = languageMode.unfoldAll()
|
||||
expect(folds.length).toBe 8
|
||||
expect([folds[0].start.row, folds[0].end.row]).toEqual [0, 30]
|
||||
expect([folds[1].start.row, folds[1].end.row]).toEqual [1, 4]
|
||||
expect([folds[2].start.row, folds[2].end.row]).toEqual [5, 27]
|
||||
expect([folds[3].start.row, folds[3].end.row]).toEqual [6, 8]
|
||||
expect([folds[4].start.row, folds[4].end.row]).toEqual [11, 16]
|
||||
expect([folds[5].start.row, folds[5].end.row]).toEqual [17, 20]
|
||||
expect([folds[6].start.row, folds[6].end.row]).toEqual [21, 22]
|
||||
expect([folds[7].start.row, folds[7].end.row]).toEqual [24, 25]
|
||||
|
||||
describe ".foldAllAtIndentLevel()", ->
|
||||
it "folds every foldable range at a given indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(2)
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(6).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [6, 8]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(11).fold
|
||||
expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 16]
|
||||
fold2.destroy()
|
||||
|
||||
fold3 = editor.tokenizedLineForScreenRow(17).fold
|
||||
expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [17, 20]
|
||||
fold3.destroy()
|
||||
|
||||
fold4 = editor.tokenizedLineForScreenRow(21).fold
|
||||
expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [21, 22]
|
||||
fold4.destroy()
|
||||
|
||||
fold5 = editor.tokenizedLineForScreenRow(24).fold
|
||||
expect([fold5.getStartRow(), fold5.getEndRow()]).toEqual [24, 25]
|
||||
fold5.destroy()
|
||||
folds = languageMode.unfoldAll()
|
||||
expect(folds.length).toBe 5
|
||||
expect([folds[0].start.row, folds[0].end.row]).toEqual [6, 8]
|
||||
expect([folds[1].start.row, folds[1].end.row]).toEqual [11, 16]
|
||||
expect([folds[2].start.row, folds[2].end.row]).toEqual [17, 20]
|
||||
expect([folds[3].start.row, folds[3].end.row]).toEqual [21, 22]
|
||||
expect([folds[4].start.row, folds[4].end.row]).toEqual [24, 25]
|
||||
|
||||
it "does not fold anything but the indentLevel", ->
|
||||
languageMode.foldAllAtIndentLevel(0)
|
||||
|
||||
fold1 = editor.tokenizedLineForScreenRow(0).fold
|
||||
expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 30]
|
||||
fold1.destroy()
|
||||
|
||||
fold2 = editor.tokenizedLineForScreenRow(5).fold
|
||||
expect(fold2).toBeFalsy()
|
||||
folds = languageMode.unfoldAll()
|
||||
expect(folds.length).toBe 1
|
||||
expect([folds[0].start.row, folds[0].end.row]).toEqual [0, 30]
|
||||
|
||||
describe ".isFoldableAtBufferRow(bufferRow)", ->
|
||||
it "returns true if the line starts a multi-line comment", ->
|
||||
|
||||
@@ -19,36 +19,45 @@ describe "LinesYardstick", ->
|
||||
screenRowsToMeasure = []
|
||||
|
||||
buildLineNode = (screenRow) ->
|
||||
tokenizedLine = editor.tokenizedLineForScreenRow(screenRow)
|
||||
iterator = tokenizedLine.getTokenIterator()
|
||||
startIndex = 0
|
||||
scopes = []
|
||||
screenLine = editor.screenLineForScreenRow(screenRow)
|
||||
lineNode = document.createElement("div")
|
||||
lineNode.style.whiteSpace = "pre"
|
||||
while iterator.next()
|
||||
span = document.createElement("span")
|
||||
span.className = iterator.getScopes().join(' ').replace(/\.+/g, ' ')
|
||||
span.textContent = iterator.getText()
|
||||
lineNode.appendChild(span)
|
||||
for tagCode in screenLine.tagCodes when tagCode isnt 0
|
||||
if editor.displayLayer.isCloseTagCode(tagCode)
|
||||
scopes.pop()
|
||||
else if editor.displayLayer.isOpenTagCode(tagCode)
|
||||
scopes.push(editor.displayLayer.tagForCode(tagCode))
|
||||
else
|
||||
text = screenLine.lineText.substr(startIndex, tagCode)
|
||||
startIndex += tagCode
|
||||
|
||||
span = document.createElement("span")
|
||||
span.className = scopes.join(' ').replace(/\.+/g, ' ')
|
||||
span.textContent = text
|
||||
lineNode.appendChild(span)
|
||||
jasmine.attachToDOM(lineNode)
|
||||
createdLineNodes.push(lineNode)
|
||||
lineNode
|
||||
|
||||
mockLineNodesProvider =
|
||||
lineNodeForLineIdAndScreenRow: (lineId, screenRow) ->
|
||||
buildLineNode(screenRow)
|
||||
lineNodesById: {}
|
||||
lineIdForScreenRow: (screenRow) ->
|
||||
editor.screenLineForScreenRow(screenRow).id
|
||||
|
||||
textNodesForLineIdAndScreenRow: (lineId, screenRow) ->
|
||||
lineNode = @lineNodeForLineIdAndScreenRow(lineId, screenRow)
|
||||
lineNodeForScreenRow: (screenRow) ->
|
||||
@lineNodesById[@lineIdForScreenRow(screenRow)] ?= buildLineNode(screenRow)
|
||||
|
||||
textNodesForScreenRow: (screenRow) ->
|
||||
lineNode = @lineNodeForScreenRow(screenRow)
|
||||
iterator = document.createNodeIterator(lineNode, NodeFilter.SHOW_TEXT)
|
||||
textNodes = []
|
||||
while textNode = iterator.nextNode()
|
||||
textNodes.push(textNode)
|
||||
textNodes.push(textNode) while textNode = iterator.nextNode()
|
||||
textNodes
|
||||
|
||||
editor.setLineHeightInPixels(14)
|
||||
lineTopIndex = new LineTopIndex({
|
||||
defaultLineHeight: editor.getLineHeightInPixels()
|
||||
})
|
||||
lineTopIndex = new LineTopIndex({defaultLineHeight: editor.getLineHeightInPixels()})
|
||||
linesYardstick = new LinesYardstick(editor, mockLineNodesProvider, lineTopIndex, atom.grammars)
|
||||
|
||||
afterEach ->
|
||||
@@ -69,9 +78,9 @@ describe "LinesYardstick", ->
|
||||
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 0))).toEqual({left: 0, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 1))).toEqual({left: 7, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 5))).toEqual({left: 37.78125, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 6))).toEqual({left: 43.171875, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 9))).toEqual({left: 72.171875, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 5))).toEqual({left: 38, top: 0})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 6))).toEqual({left: 43, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 9))).toEqual({left: 72, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(2, Infinity))).toEqual({left: 287.859375, top: 28})
|
||||
|
||||
it "reuses already computed pixel positions unless it is invalidated", ->
|
||||
@@ -82,9 +91,9 @@ describe "LinesYardstick", ->
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 2))).toEqual({left: 19.203125, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 2))).toEqual({left: 19, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(2, 6))).toEqual({left: 57.609375, top: 28})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 95.609375, top: 70})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 96, top: 70})
|
||||
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
@@ -92,9 +101,9 @@ describe "LinesYardstick", ->
|
||||
}
|
||||
"""
|
||||
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 2))).toEqual({left: 19.203125, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(1, 2))).toEqual({left: 19, top: 14})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(2, 6))).toEqual({left: 57.609375, top: 28})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 95.609375, top: 70})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 96, top: 70})
|
||||
|
||||
linesYardstick.invalidateCache()
|
||||
|
||||
@@ -102,23 +111,6 @@ describe "LinesYardstick", ->
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(2, 6))).toEqual({left: 72, top: 28})
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(5, 10))).toEqual({left: 120, top: 70})
|
||||
|
||||
it "correctly handles RTL characters", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 14px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
editor.setText("السلام عليكم")
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 0)).left).toBe 0
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 1)).left).toBe 8
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 2)).left).toBe 16
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 5)).left).toBe 33
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 7)).left).toBe 50
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 9)).left).toBe 67
|
||||
expect(linesYardstick.pixelPositionForScreenPosition(Point(0, 11)).left).toBe 84
|
||||
|
||||
it "doesn't report a width greater than 0 when the character to measure is at the beginning of a text node", ->
|
||||
# This spec documents what seems to be a bug in Chromium, because we'd
|
||||
# expect that Range(0, 0).getBoundingClientRect().width to always be zero.
|
||||
@@ -163,9 +155,38 @@ describe "LinesYardstick", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 28, left: 100})).toEqual([2, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 32, left: 24.3})).toEqual([2, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 46, left: 66.5})).toEqual([3, 9])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 99.9})).toEqual([5, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 224.2365234375})).toEqual([5, 29])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 80, left: 225})).toEqual([5, 30])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 99.9})).toEqual([5, 14])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 224.2365234375})).toEqual([5, 29])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 70, left: 225})).toEqual([5, 30])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 84, left: 247.1})).toEqual([6, 33])
|
||||
|
||||
it "overshoots to the nearest character when text nodes are not spatially contiguous", ->
|
||||
atom.styles.addStyleSheet """
|
||||
* {
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
}
|
||||
"""
|
||||
|
||||
buildLineNode = (screenRow) ->
|
||||
lineNode = document.createElement("div")
|
||||
lineNode.style.whiteSpace = "pre"
|
||||
lineNode.innerHTML = '<span>foo</span><span style="margin-left: 40px">bar</span>'
|
||||
jasmine.attachToDOM(lineNode)
|
||||
createdLineNodes.push(lineNode)
|
||||
lineNode
|
||||
editor.setText("foobar")
|
||||
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 7})).toEqual([0, 1])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 14})).toEqual([0, 2])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 21})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 30})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 50})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 62})).toEqual([0, 3])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 69})).toEqual([0, 4])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 76})).toEqual([0, 5])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 100})).toEqual([0, 6])
|
||||
expect(linesYardstick.screenPositionForPixelPosition({top: 0, left: 200})).toEqual([0, 6])
|
||||
|
||||
it "clips pixel positions above buffer start", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0]
|
||||
@@ -178,3 +199,7 @@ describe "LinesYardstick", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: Infinity, left: Infinity)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: (editor.getLastScreenRow() + 1) * 14, left: 0)).toEqual [12, 2]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: editor.getLastScreenRow() * 14, left: 0)).toEqual [12, 0]
|
||||
|
||||
it "clips negative horizontal pixel positions", ->
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: 0, left: -10)).toEqual [0, 0]
|
||||
expect(linesYardstick.screenPositionForPixelPosition(top: 1 * 14, left: -10)).toEqual [1, 0]
|
||||
|
||||
@@ -917,6 +917,82 @@ describe "Pane", ->
|
||||
expect(item1.save).not.toHaveBeenCalled()
|
||||
expect(pane.isDestroyed()).toBe false
|
||||
|
||||
describe "when item fails to save", ->
|
||||
[pane, item1, item2] = []
|
||||
|
||||
beforeEach ->
|
||||
pane = new Pane({items: [new Item("A"), new Item("B")], applicationDelegate: atom.applicationDelegate, config: atom.config})
|
||||
[item1, item2] = pane.getItems()
|
||||
|
||||
item1.shouldPromptToSave = -> true
|
||||
item1.getURI = -> "/test/path"
|
||||
|
||||
item1.save = jasmine.createSpy("save").andCallFake ->
|
||||
error = new Error("EACCES, permission denied '/test/path'")
|
||||
error.path = '/test/path'
|
||||
error.code = 'EACCES'
|
||||
throw error
|
||||
|
||||
it "does not destroy the pane if save fails and user clicks cancel", ->
|
||||
confirmations = 0
|
||||
confirm.andCallFake ->
|
||||
confirmations++
|
||||
if confirmations is 1
|
||||
return 0 # click save
|
||||
else
|
||||
return 1 # click cancel
|
||||
|
||||
pane.close()
|
||||
|
||||
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
|
||||
expect(confirmations).toBe(2)
|
||||
expect(item1.save).toHaveBeenCalled()
|
||||
expect(pane.isDestroyed()).toBe false
|
||||
|
||||
it "does destroy the pane if the user saves the file under a new name", ->
|
||||
item1.saveAs = jasmine.createSpy("saveAs").andReturn(true)
|
||||
|
||||
confirmations = 0
|
||||
confirm.andCallFake ->
|
||||
confirmations++
|
||||
return 0 # save and then save as
|
||||
|
||||
showSaveDialog.andReturn("new/path")
|
||||
|
||||
pane.close()
|
||||
|
||||
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
|
||||
expect(confirmations).toBe(2)
|
||||
expect(atom.applicationDelegate.showSaveDialog).toHaveBeenCalled()
|
||||
expect(item1.save).toHaveBeenCalled()
|
||||
expect(item1.saveAs).toHaveBeenCalled()
|
||||
expect(pane.isDestroyed()).toBe true
|
||||
|
||||
it "asks again if the saveAs also fails", ->
|
||||
item1.saveAs = jasmine.createSpy("saveAs").andCallFake ->
|
||||
error = new Error("EACCES, permission denied '/test/path'")
|
||||
error.path = '/test/path'
|
||||
error.code = 'EACCES'
|
||||
throw error
|
||||
|
||||
confirmations = 0
|
||||
confirm.andCallFake ->
|
||||
confirmations++
|
||||
if confirmations < 3
|
||||
return 0 # save, save as, save as
|
||||
return 2 # don't save
|
||||
|
||||
showSaveDialog.andReturn("new/path")
|
||||
|
||||
pane.close()
|
||||
|
||||
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
|
||||
expect(confirmations).toBe(3)
|
||||
expect(atom.applicationDelegate.showSaveDialog).toHaveBeenCalled()
|
||||
expect(item1.save).toHaveBeenCalled()
|
||||
expect(item1.saveAs).toHaveBeenCalled()
|
||||
expect(pane.isDestroyed()).toBe true
|
||||
|
||||
describe "::destroy()", ->
|
||||
[container, pane1, pane2] = []
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ describe "Project", ->
|
||||
|
||||
runs ->
|
||||
bufferA = atom.project.getBuffers()[0]
|
||||
layerA = bufferA.addMarkerLayer(maintainHistory: true)
|
||||
layerA = bufferA.addMarkerLayer(persistent: true)
|
||||
markerA = layerA.markPosition([0, 3])
|
||||
|
||||
notQuittingProject = new Project({notificationManager: atom.notifications, packageManager: atom.packages, confirm: atom.confirm})
|
||||
|
||||
@@ -17,7 +17,7 @@ describe "TextEditor", ->
|
||||
buffer = new TextBuffer
|
||||
editor = atom.workspace.buildTextEditor({buffer})
|
||||
editor.setEditorWidthInChars(80)
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer = editor.tokenizedBuffer
|
||||
steps = []
|
||||
|
||||
times 30, ->
|
||||
@@ -33,8 +33,8 @@ describe "TextEditor", ->
|
||||
logLines()
|
||||
throw new Error("Invalid buffer row #{actualBufferRow} for screen row #{screenRow}", )
|
||||
|
||||
actualScreenLine = editor.tokenizedLineForScreenRow(screenRow)
|
||||
unless actualScreenLine.text is referenceScreenLine.text
|
||||
actualScreenLine = editor.lineTextForScreenRow(screenRow)
|
||||
unless actualScreenLine is referenceScreenLine
|
||||
logLines()
|
||||
throw new Error("Invalid line text at screen row #{screenRow}")
|
||||
|
||||
@@ -84,7 +84,8 @@ describe "TextEditor", ->
|
||||
referenceEditor.setEditorWidthInChars(80)
|
||||
referenceEditor.setText(editor.getText())
|
||||
referenceEditor.setSoftWrapped(editor.isSoftWrapped())
|
||||
screenLines = referenceEditor.tokenizedLinesForScreenRows(0, referenceEditor.getLastScreenRow())
|
||||
|
||||
screenLines = [0..referenceEditor.getLastScreenRow()].map (row) => referenceEditor.lineTextForScreenRow(row)
|
||||
bufferRows = referenceEditor.bufferRowsForScreenRows(0, referenceEditor.getLastScreenRow())
|
||||
|
||||
{screenLines, bufferRows}
|
||||
|
||||
@@ -101,3 +101,22 @@ describe "Selection", ->
|
||||
selection.setBufferRange [[2, 0], [3, 0]]
|
||||
selection.insertText("\r\n", autoIndent: true)
|
||||
expect(buffer.lineForRow(2)).toBe " "
|
||||
|
||||
describe ".fold()", ->
|
||||
it "folds the buffer range spanned by the selection", ->
|
||||
selection.setBufferRange([[0, 3], [1, 6]])
|
||||
selection.fold()
|
||||
|
||||
expect(selection.getScreenRange()).toEqual([[0, 4], [0, 4]])
|
||||
expect(selection.getBufferRange()).toEqual([[1, 6], [1, 6]])
|
||||
expect(editor.lineTextForScreenRow(0)).toBe "var#{editor.displayLayer.foldCharacter}sort = function(items) {"
|
||||
expect(editor.isFoldedAtBufferRow(0)).toBe(true)
|
||||
|
||||
it "doesn't create a fold when the selection is empty", ->
|
||||
selection.setBufferRange([[0, 3], [0, 3]])
|
||||
selection.fold()
|
||||
|
||||
expect(selection.getScreenRange()).toEqual([[0, 3], [0, 3]])
|
||||
expect(selection.getBufferRange()).toEqual([[0, 3], [0, 3]])
|
||||
expect(editor.lineTextForScreenRow(0)).toBe "var quicksort = function () {"
|
||||
expect(editor.isFoldedAtBufferRow(0)).toBe(false)
|
||||
|
||||
@@ -69,13 +69,12 @@ describe('TextEditorComponent', function () {
|
||||
describe('line rendering', async function () {
|
||||
function expectTileContainsRow (tileNode, screenRow, {top}) {
|
||||
let lineNode = tileNode.querySelector('[data-screen-row="' + screenRow + '"]')
|
||||
let tokenizedLine = editor.tokenizedLineForScreenRow(screenRow)
|
||||
|
||||
let text = editor.lineTextForScreenRow(screenRow)
|
||||
expect(lineNode.offsetTop).toBe(top)
|
||||
if (tokenizedLine.text === '') {
|
||||
expect(lineNode.innerHTML).toBe(' ')
|
||||
if (text === '') {
|
||||
expect(lineNode.textContent).toBe(' ')
|
||||
} else {
|
||||
expect(lineNode.textContent).toBe(tokenizedLine.text)
|
||||
expect(lineNode.textContent).toBe(text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,12 +293,12 @@ describe('TextEditorComponent', function () {
|
||||
|
||||
await nextViewUpdatePromise()
|
||||
|
||||
expect(component.lineNodeForScreenRow(3).textContent).toBe(editor.tokenizedLineForScreenRow(3).text)
|
||||
expect(component.lineNodeForScreenRow(3).textContent).toBe(editor.lineTextForScreenRow(3))
|
||||
buffer.delete([[0, 0], [3, 0]])
|
||||
|
||||
await nextViewUpdatePromise()
|
||||
|
||||
expect(component.lineNodeForScreenRow(3).textContent).toBe(editor.tokenizedLineForScreenRow(3).text)
|
||||
expect(component.lineNodeForScreenRow(3).textContent).toBe(editor.lineTextForScreenRow(3))
|
||||
})
|
||||
|
||||
it('updates the top position of lines when the line height changes', async function () {
|
||||
@@ -361,9 +360,9 @@ describe('TextEditorComponent', function () {
|
||||
}
|
||||
})
|
||||
|
||||
it('renders an nbsp on empty lines when no line-ending character is defined', function () {
|
||||
it('renders an placeholder space on empty lines when no line-ending character is defined', function () {
|
||||
atom.config.set('editor.showInvisibles', false)
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe(NBSP)
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe(' ')
|
||||
})
|
||||
|
||||
it('gives the lines and tiles divs the same background color as the editor to improve GPU performance', async function () {
|
||||
@@ -429,13 +428,14 @@ describe('TextEditorComponent', function () {
|
||||
expect(leafNodes[0].classList.contains('leading-whitespace')).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps rebuilding lines when continuous reflow is on', function () {
|
||||
it('keeps rebuilding lines when continuous reflow is on', async function () {
|
||||
wrapperNode.setContinuousReflow(true)
|
||||
let oldLineNode = componentNode.querySelector('.line')
|
||||
let oldLineNode = componentNode.querySelectorAll('.line')[1]
|
||||
|
||||
waitsFor(function () {
|
||||
return componentNode.querySelector('.line') !== oldLineNode
|
||||
})
|
||||
while (true) {
|
||||
await nextViewUpdatePromise()
|
||||
if (componentNode.querySelectorAll('.line')[1] !== oldLineNode) break
|
||||
}
|
||||
})
|
||||
|
||||
describe('when showInvisibles is enabled', function () {
|
||||
@@ -484,7 +484,7 @@ describe('TextEditorComponent', function () {
|
||||
it('displays newlines as their own token outside of the other tokens\' scopeDescriptor', async function () {
|
||||
editor.setText('let\n')
|
||||
await nextViewUpdatePromise()
|
||||
expect(component.lineNodeForScreenRow(0).innerHTML).toBe('<span class="source js"><span class="storage type var js">let</span></span><span class="invisible-character">' + invisibles.eol + '</span>')
|
||||
expect(component.lineNodeForScreenRow(0).innerHTML).toBe('<span class="source js"><span class="storage type var js">let</span><span class="invisible-character eol">' + invisibles.eol + '</span></span>')
|
||||
})
|
||||
|
||||
it('displays trailing carriage returns using a visible, non-empty value', async function () {
|
||||
@@ -497,20 +497,20 @@ describe('TextEditorComponent', function () {
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe(invisibles.eol)
|
||||
})
|
||||
|
||||
it('renders an nbsp on empty lines when the line-ending character is an empty string', async function () {
|
||||
it('renders a placeholder space on empty lines when the line-ending character is an empty string', async function () {
|
||||
atom.config.set('editor.invisibles', {
|
||||
eol: ''
|
||||
})
|
||||
await nextViewUpdatePromise()
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe(NBSP)
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe(' ')
|
||||
})
|
||||
|
||||
it('renders an nbsp on empty lines when the line-ending character is false', async function () {
|
||||
it('renders an placeholder space on empty lines when the line-ending character is false', async function () {
|
||||
atom.config.set('editor.invisibles', {
|
||||
eol: false
|
||||
})
|
||||
await nextViewUpdatePromise()
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe(NBSP)
|
||||
expect(component.lineNodeForScreenRow(10).textContent).toBe(' ')
|
||||
})
|
||||
|
||||
it('interleaves invisible line-ending characters with indent guides on empty lines', async function () {
|
||||
@@ -518,24 +518,25 @@ describe('TextEditorComponent', function () {
|
||||
|
||||
await nextViewUpdatePromise()
|
||||
|
||||
editor.setTabLength(2)
|
||||
editor.setTextInBufferRange([[10, 0], [11, 0]], '\r\n', {
|
||||
normalizeLineEndings: false
|
||||
})
|
||||
await nextViewUpdatePromise()
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe('<span class="source js"><span class="invisible-character eol indent-guide">CE</span></span>')
|
||||
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe('<span class="indent-guide"><span class="invisible-character">C</span><span class="invisible-character">E</span></span>')
|
||||
editor.setTabLength(3)
|
||||
await nextViewUpdatePromise()
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe('<span class="source js"><span class="invisible-character eol indent-guide">CE</span></span>')
|
||||
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe('<span class="indent-guide"><span class="invisible-character">C</span><span class="invisible-character">E</span> </span>')
|
||||
editor.setTabLength(1)
|
||||
await nextViewUpdatePromise()
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe('<span class="source js"><span class="invisible-character eol indent-guide">CE</span></span>')
|
||||
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe('<span class="indent-guide"><span class="invisible-character">C</span></span><span class="indent-guide"><span class="invisible-character">E</span></span>')
|
||||
editor.setTextInBufferRange([[9, 0], [9, Infinity]], ' ')
|
||||
editor.setTextInBufferRange([[11, 0], [11, Infinity]], ' ')
|
||||
await nextViewUpdatePromise()
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe('<span class="indent-guide"><span class="invisible-character">C</span></span><span class="invisible-character">E</span>')
|
||||
expect(component.lineNodeForScreenRow(10).innerHTML).toBe('<span class="source js"><span class="invisible-character eol indent-guide">CE</span></span>')
|
||||
})
|
||||
|
||||
describe('when soft wrapping is enabled', function () {
|
||||
@@ -550,8 +551,8 @@ describe('TextEditorComponent', function () {
|
||||
})
|
||||
|
||||
it('does not show end of line invisibles at the end of wrapped lines', function () {
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe('a line that ')
|
||||
expect(component.lineNodeForScreenRow(1).textContent).toBe('wraps' + invisibles.space + invisibles.eol)
|
||||
expect(component.lineNodeForScreenRow(0).textContent).toBe('a line ')
|
||||
expect(component.lineNodeForScreenRow(1).textContent).toBe('that wraps' + invisibles.space + invisibles.eol)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -986,13 +987,14 @@ describe('TextEditorComponent', function () {
|
||||
expect(component.lineNumberNodeForScreenRow(3) != null).toBe(true)
|
||||
})
|
||||
|
||||
it('keeps rebuilding line numbers when continuous reflow is on', function () {
|
||||
it('keeps rebuilding line numbers when continuous reflow is on', async function () {
|
||||
wrapperNode.setContinuousReflow(true)
|
||||
let oldLineNode = componentNode.querySelectorAll('.line-number')[1]
|
||||
|
||||
waitsFor(function () {
|
||||
return componentNode.querySelectorAll('.line-number')[1] !== oldLineNode
|
||||
})
|
||||
while (true) {
|
||||
await nextViewUpdatePromise()
|
||||
if (componentNode.querySelectorAll('.line-number')[1] !== oldLineNode) break
|
||||
}
|
||||
})
|
||||
|
||||
describe('fold decorations', function () {
|
||||
@@ -1051,7 +1053,7 @@ describe('TextEditorComponent', function () {
|
||||
beforeEach(async function () {
|
||||
editor.setSoftWrapped(true)
|
||||
await nextViewUpdatePromise()
|
||||
componentNode.style.width = 16 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px'
|
||||
componentNode.style.width = 20 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px'
|
||||
component.measureDimensions()
|
||||
await nextViewUpdatePromise()
|
||||
})
|
||||
@@ -1060,6 +1062,14 @@ describe('TextEditorComponent', function () {
|
||||
expect(lineNumberHasClass(0, 'foldable')).toBe(true)
|
||||
expect(lineNumberHasClass(1, 'foldable')).toBe(false)
|
||||
})
|
||||
|
||||
it('does not add the folded class for soft-wrapped lines that contain a fold', async function () {
|
||||
editor.foldBufferRange([[3, 19], [3, 21]])
|
||||
await nextViewUpdatePromise()
|
||||
|
||||
expect(lineNumberHasClass(11, 'folded')).toBe(true)
|
||||
expect(lineNumberHasClass(12, 'folded')).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1082,7 +1092,7 @@ describe('TextEditorComponent', function () {
|
||||
component.destroy()
|
||||
lineNumber = component.lineNumberNodeForScreenRow(1)
|
||||
target = lineNumber.querySelector('.icon-right')
|
||||
return target.dispatchEvent(buildClickEvent(target))
|
||||
target.dispatchEvent(buildClickEvent(target))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1106,6 +1116,37 @@ describe('TextEditorComponent', function () {
|
||||
expect(lineNumberHasClass(1, 'folded')).toBe(false)
|
||||
})
|
||||
|
||||
it('unfolds all the free-form folds intersecting the buffer row when clicked', async function () {
|
||||
expect(lineNumberHasClass(3, 'foldable')).toBe(false)
|
||||
|
||||
editor.foldBufferRange([[3, 4], [5, 4]])
|
||||
editor.foldBufferRange([[5, 5], [8, 10]])
|
||||
await nextViewUpdatePromise()
|
||||
expect(lineNumberHasClass(3, 'folded')).toBe(true)
|
||||
expect(lineNumberHasClass(5, 'folded')).toBe(false)
|
||||
|
||||
let lineNumber = component.lineNumberNodeForScreenRow(3)
|
||||
let target = lineNumber.querySelector('.icon-right')
|
||||
target.dispatchEvent(buildClickEvent(target))
|
||||
await nextViewUpdatePromise()
|
||||
expect(lineNumberHasClass(3, 'folded')).toBe(false)
|
||||
expect(lineNumberHasClass(5, 'folded')).toBe(true)
|
||||
|
||||
editor.setSoftWrapped(true)
|
||||
componentNode.style.width = 20 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px'
|
||||
component.measureDimensions()
|
||||
await nextViewUpdatePromise()
|
||||
editor.foldBufferRange([[3, 19], [3, 21]]) // fold starting on a soft-wrapped portion of the line
|
||||
await nextViewUpdatePromise()
|
||||
expect(lineNumberHasClass(11, 'folded')).toBe(true)
|
||||
|
||||
lineNumber = component.lineNumberNodeForScreenRow(11)
|
||||
target = lineNumber.querySelector('.icon-right')
|
||||
target.dispatchEvent(buildClickEvent(target))
|
||||
await nextViewUpdatePromise()
|
||||
expect(lineNumberHasClass(11, 'folded')).toBe(false)
|
||||
})
|
||||
|
||||
it('does not fold when the line number componentNode is clicked', function () {
|
||||
let lineNumber = component.lineNumberNodeForScreenRow(1)
|
||||
lineNumber.dispatchEvent(buildClickEvent(lineNumber))
|
||||
@@ -1200,7 +1241,7 @@ describe('TextEditorComponent', function () {
|
||||
let cursor = componentNode.querySelector('.cursor')
|
||||
let cursorRect = cursor.getBoundingClientRect()
|
||||
let cursorLocationTextNode = component.lineNodeForScreenRow(0).querySelector('.source.js').childNodes[2]
|
||||
let range = document.createRange()
|
||||
let range = document.createRange(cursorLocationTextNode)
|
||||
range.setStart(cursorLocationTextNode, 0)
|
||||
range.setEnd(cursorLocationTextNode, 1)
|
||||
let rangeRect = range.getBoundingClientRect()
|
||||
@@ -1208,6 +1249,17 @@ describe('TextEditorComponent', function () {
|
||||
expect(cursorRect.width).toBeCloseTo(rangeRect.width, 0)
|
||||
})
|
||||
|
||||
it('positions cursors after the fold-marker when a fold ends the line', async function () {
|
||||
editor.foldBufferRow(0)
|
||||
await nextViewUpdatePromise()
|
||||
editor.setCursorScreenPosition([0, 30])
|
||||
await nextViewUpdatePromise()
|
||||
|
||||
let cursorRect = componentNode.querySelector('.cursor').getBoundingClientRect()
|
||||
let foldMarkerRect = componentNode.querySelector('.fold-marker').getBoundingClientRect()
|
||||
expect(cursorRect.left).toBeCloseTo(foldMarkerRect.right, 0)
|
||||
})
|
||||
|
||||
it('positions cursors correctly after character widths are changed via a stylesheet change', async function () {
|
||||
atom.config.set('editor.fontFamily', 'sans-serif')
|
||||
editor.setCursorScreenPosition([0, 16])
|
||||
@@ -1475,7 +1527,7 @@ describe('TextEditorComponent', function () {
|
||||
component.measureDimensions()
|
||||
await nextViewUpdatePromise()
|
||||
|
||||
let marker2 = editor.displayBuffer.markBufferRange([[9, 0], [9, 0]])
|
||||
let marker2 = editor.markBufferRange([[9, 0], [9, 0]])
|
||||
editor.decorateMarker(marker2, {
|
||||
type: ['line-number', 'line'],
|
||||
'class': 'b'
|
||||
@@ -1887,7 +1939,7 @@ describe('TextEditorComponent', function () {
|
||||
component.measureDimensions()
|
||||
await nextViewUpdatePromise()
|
||||
|
||||
marker = editor.displayBuffer.markBufferRange([[9, 2], [9, 4]], {
|
||||
marker = editor.markBufferRange([[9, 2], [9, 4]], {
|
||||
invalidate: 'inside'
|
||||
})
|
||||
editor.decorateMarker(marker, {
|
||||
@@ -2082,7 +2134,7 @@ describe('TextEditorComponent', function () {
|
||||
|
||||
describe('when the marker is empty', function () {
|
||||
it('renders an overlay decoration when added and removes the overlay when the decoration is destroyed', async function () {
|
||||
let marker = editor.displayBuffer.markBufferRange([[2, 13], [2, 13]], {
|
||||
let marker = editor.markBufferRange([[2, 13], [2, 13]], {
|
||||
invalidate: 'never'
|
||||
})
|
||||
let decoration = editor.decorateMarker(marker, {
|
||||
@@ -2104,7 +2156,7 @@ describe('TextEditorComponent', function () {
|
||||
})
|
||||
|
||||
it('renders the overlay element with the CSS class specified by the decoration', async function () {
|
||||
let marker = editor.displayBuffer.markBufferRange([[2, 13], [2, 13]], {
|
||||
let marker = editor.markBufferRange([[2, 13], [2, 13]], {
|
||||
invalidate: 'never'
|
||||
})
|
||||
let decoration = editor.decorateMarker(marker, {
|
||||
@@ -2125,7 +2177,7 @@ describe('TextEditorComponent', function () {
|
||||
|
||||
describe('when the marker is not empty', function () {
|
||||
it('renders at the head of the marker by default', async function () {
|
||||
let marker = editor.displayBuffer.markBufferRange([[2, 5], [2, 10]], {
|
||||
let marker = editor.markBufferRange([[2, 5], [2, 10]], {
|
||||
invalidate: 'never'
|
||||
})
|
||||
let decoration = editor.decorateMarker(marker, {
|
||||
@@ -2171,7 +2223,7 @@ describe('TextEditorComponent', function () {
|
||||
})
|
||||
|
||||
it('slides horizontally left when near the right edge on #win32 and #darwin', async function () {
|
||||
let marker = editor.displayBuffer.markBufferRange([[0, 26], [0, 26]], {
|
||||
let marker = editor.markBufferRange([[0, 26], [0, 26]], {
|
||||
invalidate: 'never'
|
||||
})
|
||||
let decoration = editor.decorateMarker(marker, {
|
||||
@@ -2753,20 +2805,60 @@ describe('TextEditorComponent', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('when a line is folded', function () {
|
||||
beforeEach(async function () {
|
||||
editor.foldBufferRow(4)
|
||||
describe('when a fold marker is clicked', function () {
|
||||
function clickElementAtPosition (marker, position) {
|
||||
linesNode.dispatchEvent(
|
||||
buildMouseEvent('mousedown', clientCoordinatesForScreenPosition(position), {target: marker})
|
||||
)
|
||||
}
|
||||
|
||||
it('unfolds only the selected fold when other folds are on the same line', async function () {
|
||||
editor.foldBufferRange([[4, 6], [4, 10]])
|
||||
editor.foldBufferRange([[4, 15], [4, 20]])
|
||||
await nextViewUpdatePromise()
|
||||
let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
|
||||
expect(foldMarkers.length).toBe(2)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
|
||||
|
||||
clickElementAtPosition(foldMarkers[0], [4, 6])
|
||||
await nextViewUpdatePromise()
|
||||
foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
|
||||
expect(foldMarkers.length).toBe(1)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
|
||||
|
||||
clickElementAtPosition(foldMarkers[0], [4, 15])
|
||||
await nextViewUpdatePromise()
|
||||
foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
|
||||
expect(foldMarkers.length).toBe(0)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBe(false)
|
||||
})
|
||||
|
||||
describe('when the folded line\'s fold-marker is clicked', function () {
|
||||
it('unfolds the buffer row', function () {
|
||||
let target = component.lineNodeForScreenRow(4).querySelector('.fold-marker')
|
||||
linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([4, 8]), {
|
||||
target: target
|
||||
}))
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBe(false)
|
||||
})
|
||||
it('unfolds only the selected fold when other folds are inside it', async function () {
|
||||
editor.foldBufferRange([[4, 10], [4, 15]])
|
||||
editor.foldBufferRange([[4, 4], [4, 5]])
|
||||
editor.foldBufferRange([[4, 4], [4, 20]])
|
||||
await nextViewUpdatePromise()
|
||||
let foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
|
||||
expect(foldMarkers.length).toBe(1)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
|
||||
|
||||
clickElementAtPosition(foldMarkers[0], [4, 4])
|
||||
await nextViewUpdatePromise()
|
||||
foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
|
||||
expect(foldMarkers.length).toBe(1)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
|
||||
|
||||
clickElementAtPosition(foldMarkers[0], [4, 4])
|
||||
await nextViewUpdatePromise()
|
||||
foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
|
||||
expect(foldMarkers.length).toBe(1)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBe(true)
|
||||
|
||||
clickElementAtPosition(foldMarkers[0], [4, 10])
|
||||
await nextViewUpdatePromise()
|
||||
foldMarkers = component.lineNodeForScreenRow(4).querySelectorAll('.fold-marker')
|
||||
expect(foldMarkers.length).toBe(0)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3101,7 +3193,7 @@ describe('TextEditorComponent', function () {
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(11), {
|
||||
shiftKey: true
|
||||
}))
|
||||
expect(editor.getSelectedScreenRange()).toEqual([[7, 4], [16, 0]])
|
||||
expect(editor.getSelectedScreenRange()).toEqual([[7, 4], [17, 0]])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -3175,7 +3267,7 @@ describe('TextEditorComponent', function () {
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(11), {
|
||||
metaKey: true
|
||||
}))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual([[[7, 4], [7, 6]], [[11, 4], [19, 0]]])
|
||||
expect(editor.getSelectedScreenRanges()).toEqual([[[7, 4], [7, 6]], [[11, 4], [20, 0]]])
|
||||
})
|
||||
|
||||
it('merges overlapping selections on mouseup', async function () {
|
||||
@@ -3189,7 +3281,7 @@ describe('TextEditorComponent', function () {
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenRowInGutter(5), {
|
||||
metaKey: true
|
||||
}))
|
||||
expect(editor.getSelectedScreenRanges()).toEqual([[[5, 0], [19, 0]]])
|
||||
expect(editor.getSelectedScreenRanges()).toEqual([[[5, 0], [20, 0]]])
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -3204,7 +3296,7 @@ describe('TextEditorComponent', function () {
|
||||
}))
|
||||
gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(11)))
|
||||
await nextAnimationFramePromise()
|
||||
expect(editor.getSelectedScreenRange()).toEqual([[1, 4], [11, 14]])
|
||||
expect(editor.getSelectedScreenRange()).toEqual([[1, 4], [11, 5]])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4966,7 +5058,7 @@ describe('TextEditorComponent', function () {
|
||||
|
||||
function lineNumberForBufferRowHasClass (bufferRow, klass) {
|
||||
let screenRow
|
||||
screenRow = editor.displayBuffer.screenRowForBufferRow(bufferRow)
|
||||
screenRow = editor.screenRowForBufferRow(bufferRow)
|
||||
return component.lineNumberNodeForScreenRow(screenRow).classList.contains(klass)
|
||||
}
|
||||
|
||||
|
||||
@@ -1143,53 +1143,6 @@ describe "TextEditorPresenter", ->
|
||||
expectStateUpdate presenter, -> presenter.setScrollLeft(-300)
|
||||
expect(getState(presenter).content.scrollLeft).toBe 0
|
||||
|
||||
describe ".indentGuidesVisible", ->
|
||||
it "is initialized based on the editor.showIndentGuide config setting", ->
|
||||
presenter = buildPresenter()
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe false
|
||||
|
||||
atom.config.set('editor.showIndentGuide', true)
|
||||
presenter = buildPresenter()
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe true
|
||||
|
||||
it "updates when the editor.showIndentGuide config setting changes", ->
|
||||
presenter = buildPresenter()
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe false
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set('editor.showIndentGuide', true)
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe true
|
||||
|
||||
expectStateUpdate presenter, -> atom.config.set('editor.showIndentGuide', false)
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe false
|
||||
|
||||
it "updates when the editor's grammar changes", ->
|
||||
atom.config.set('editor.showIndentGuide', true, scopeSelector: ".source.js")
|
||||
|
||||
presenter = buildPresenter()
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe false
|
||||
|
||||
stateUpdated = false
|
||||
presenter.onDidUpdateState -> stateUpdated = true
|
||||
|
||||
waitsForPromise -> atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
expect(stateUpdated).toBe true
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe true
|
||||
|
||||
expectStateUpdate presenter, -> editor.setGrammar(atom.grammars.selectGrammar('.txt'))
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe false
|
||||
|
||||
it "is always false when the editor is mini", ->
|
||||
atom.config.set('editor.showIndentGuide', true)
|
||||
editor.setMini(true)
|
||||
presenter = buildPresenter()
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe false
|
||||
editor.setMini(false)
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe true
|
||||
editor.setMini(true)
|
||||
expect(getState(presenter).content.indentGuidesVisible).toBe false
|
||||
|
||||
describe ".backgroundColor", ->
|
||||
it "is assigned to ::backgroundColor unless the editor is mini", ->
|
||||
presenter = buildPresenter()
|
||||
@@ -1229,9 +1182,19 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
describe ".tiles", ->
|
||||
lineStateForScreenRow = (presenter, row) ->
|
||||
lineId = presenter.model.tokenizedLineForScreenRow(row).id
|
||||
tileRow = presenter.tileForRow(row)
|
||||
getState(presenter).content.tiles[tileRow]?.lines[lineId]
|
||||
tilesState = getState(presenter).content.tiles
|
||||
lineId = presenter.linesByScreenRow.get(row)?.id
|
||||
tilesState[presenter.tileForRow(row)]?.lines[lineId]
|
||||
|
||||
tagsForCodes = (presenter, tagCodes) ->
|
||||
openTags = []
|
||||
closeTags = []
|
||||
for tagCode in tagCodes when tagCode < 0 # skip text codes
|
||||
if presenter.isOpenTagCode(tagCode)
|
||||
openTags.push(presenter.tagForCode(tagCode))
|
||||
else
|
||||
closeTags.push(presenter.tagForCode(tagCode))
|
||||
{openTags, closeTags}
|
||||
|
||||
tiledContentContract (presenter) -> getState(presenter).content
|
||||
|
||||
@@ -1241,73 +1204,12 @@ describe "TextEditorPresenter", ->
|
||||
presenter.setExplicitHeight(3)
|
||||
|
||||
expect(lineStateForScreenRow(presenter, 2)).toBeUndefined()
|
||||
|
||||
line3 = editor.tokenizedLineForScreenRow(3)
|
||||
expectValues lineStateForScreenRow(presenter, 3), {
|
||||
screenRow: 3
|
||||
text: line3.text
|
||||
tags: line3.tags
|
||||
specialTokens: line3.specialTokens
|
||||
firstNonWhitespaceIndex: line3.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line3.firstTrailingWhitespaceIndex
|
||||
invisibles: line3.invisibles
|
||||
}
|
||||
|
||||
line4 = editor.tokenizedLineForScreenRow(4)
|
||||
expectValues lineStateForScreenRow(presenter, 4), {
|
||||
screenRow: 4
|
||||
text: line4.text
|
||||
tags: line4.tags
|
||||
specialTokens: line4.specialTokens
|
||||
firstNonWhitespaceIndex: line4.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line4.firstTrailingWhitespaceIndex
|
||||
invisibles: line4.invisibles
|
||||
}
|
||||
|
||||
line5 = editor.tokenizedLineForScreenRow(5)
|
||||
expectValues lineStateForScreenRow(presenter, 5), {
|
||||
screenRow: 5
|
||||
text: line5.text
|
||||
tags: line5.tags
|
||||
specialTokens: line5.specialTokens
|
||||
firstNonWhitespaceIndex: line5.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line5.firstTrailingWhitespaceIndex
|
||||
invisibles: line5.invisibles
|
||||
}
|
||||
|
||||
line6 = editor.tokenizedLineForScreenRow(6)
|
||||
expectValues lineStateForScreenRow(presenter, 6), {
|
||||
screenRow: 6
|
||||
text: line6.text
|
||||
tags: line6.tags
|
||||
specialTokens: line6.specialTokens
|
||||
firstNonWhitespaceIndex: line6.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line6.firstTrailingWhitespaceIndex
|
||||
invisibles: line6.invisibles
|
||||
}
|
||||
|
||||
line7 = editor.tokenizedLineForScreenRow(7)
|
||||
expectValues lineStateForScreenRow(presenter, 7), {
|
||||
screenRow: 7
|
||||
text: line7.text
|
||||
tags: line7.tags
|
||||
specialTokens: line7.specialTokens
|
||||
firstNonWhitespaceIndex: line7.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line7.firstTrailingWhitespaceIndex
|
||||
invisibles: line7.invisibles
|
||||
}
|
||||
|
||||
line8 = editor.tokenizedLineForScreenRow(8)
|
||||
expectValues lineStateForScreenRow(presenter, 8), {
|
||||
screenRow: 8
|
||||
text: line8.text
|
||||
tags: line8.tags
|
||||
specialTokens: line8.specialTokens
|
||||
firstNonWhitespaceIndex: line8.firstNonWhitespaceIndex
|
||||
firstTrailingWhitespaceIndex: line8.firstTrailingWhitespaceIndex
|
||||
invisibles: line8.invisibles
|
||||
}
|
||||
|
||||
expectValues lineStateForScreenRow(presenter, 3), {screenRow: 3, tagCodes: editor.screenLineForScreenRow(3).tagCodes}
|
||||
expectValues lineStateForScreenRow(presenter, 4), {screenRow: 4, tagCodes: editor.screenLineForScreenRow(4).tagCodes}
|
||||
expectValues lineStateForScreenRow(presenter, 5), {screenRow: 5, tagCodes: editor.screenLineForScreenRow(5).tagCodes}
|
||||
expectValues lineStateForScreenRow(presenter, 6), {screenRow: 6, tagCodes: editor.screenLineForScreenRow(6).tagCodes}
|
||||
expectValues lineStateForScreenRow(presenter, 7), {screenRow: 7, tagCodes: editor.screenLineForScreenRow(7).tagCodes}
|
||||
expectValues lineStateForScreenRow(presenter, 8), {screenRow: 8, tagCodes: editor.screenLineForScreenRow(8).tagCodes}
|
||||
expect(lineStateForScreenRow(presenter, 9)).toBeUndefined()
|
||||
|
||||
it "updates when the editor's content changes", ->
|
||||
@@ -1315,34 +1217,20 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
expectStateUpdate presenter, -> buffer.insert([2, 0], "hello\nworld\n")
|
||||
|
||||
line1 = editor.tokenizedLineForScreenRow(1)
|
||||
expectValues lineStateForScreenRow(presenter, 1), {
|
||||
text: line1.text
|
||||
tags: line1.tags
|
||||
}
|
||||
|
||||
line2 = editor.tokenizedLineForScreenRow(2)
|
||||
expectValues lineStateForScreenRow(presenter, 2), {
|
||||
text: line2.text
|
||||
tags: line2.tags
|
||||
}
|
||||
|
||||
line3 = editor.tokenizedLineForScreenRow(3)
|
||||
expectValues lineStateForScreenRow(presenter, 3), {
|
||||
text: line3.text
|
||||
tags: line3.tags
|
||||
}
|
||||
expectValues lineStateForScreenRow(presenter, 1), {screenRow: 1, tagCodes: editor.screenLineForScreenRow(1).tagCodes}
|
||||
expectValues lineStateForScreenRow(presenter, 2), {screenRow: 2, tagCodes: editor.screenLineForScreenRow(2).tagCodes}
|
||||
expectValues lineStateForScreenRow(presenter, 3), {screenRow: 3, tagCodes: editor.screenLineForScreenRow(3).tagCodes}
|
||||
|
||||
it "includes the .endOfLineInvisibles if the editor.showInvisibles config option is true", ->
|
||||
editor.setText("hello\nworld\r\n")
|
||||
presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10)
|
||||
expect(lineStateForScreenRow(presenter, 0).endOfLineInvisibles).toBeNull()
|
||||
expect(lineStateForScreenRow(presenter, 1).endOfLineInvisibles).toBeNull()
|
||||
expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 0).tagCodes).openTags).not.toContain('invisible-character eol')
|
||||
expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 1).tagCodes).openTags).not.toContain('invisible-character eol')
|
||||
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
presenter = buildPresenter(explicitHeight: 25, scrollTop: 0, lineHeight: 10)
|
||||
expect(lineStateForScreenRow(presenter, 0).endOfLineInvisibles).toEqual [atom.config.get('editor.invisibles.eol')]
|
||||
expect(lineStateForScreenRow(presenter, 1).endOfLineInvisibles).toEqual [atom.config.get('editor.invisibles.cr'), atom.config.get('editor.invisibles.eol')]
|
||||
expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 0).tagCodes).openTags).toContain('invisible-character eol')
|
||||
expect(tagsForCodes(presenter, lineStateForScreenRow(presenter, 1).tagCodes).openTags).toContain('invisible-character eol')
|
||||
|
||||
describe ".blockDecorations", ->
|
||||
it "contains all block decorations that are present before/after a line, both initially and when decorations change", ->
|
||||
@@ -2905,12 +2793,9 @@ describe "TextEditorPresenter", ->
|
||||
|
||||
describe ".content.tiles", ->
|
||||
lineNumberStateForScreenRow = (presenter, screenRow) ->
|
||||
editor = presenter.model
|
||||
tileRow = presenter.tileForRow(screenRow)
|
||||
line = editor.tokenizedLineForScreenRow(screenRow)
|
||||
|
||||
gutterState = getLineNumberGutterState(presenter)
|
||||
gutterState.content.tiles[tileRow]?.lineNumbers[line?.id]
|
||||
tilesState = getLineNumberGutterState(presenter).content.tiles
|
||||
line = presenter.linesByScreenRow.get(screenRow)
|
||||
tilesState[presenter.tileForRow(screenRow)]?.lineNumbers[line?.id]
|
||||
|
||||
tiledContentContract (presenter) -> getLineNumberGutterState(presenter).content
|
||||
|
||||
@@ -2919,7 +2804,7 @@ describe "TextEditorPresenter", ->
|
||||
editor.foldBufferRow(4)
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setDefaultCharWidth(1)
|
||||
editor.setEditorWidthInChars(50)
|
||||
editor.setEditorWidthInChars(51)
|
||||
presenter = buildPresenter(explicitHeight: 25, scrollTop: 30, lineHeight: 10, tileSize: 2)
|
||||
|
||||
expect(lineNumberStateForScreenRow(presenter, 1)).toBeUndefined()
|
||||
@@ -3184,6 +3069,16 @@ describe "TextEditorPresenter", ->
|
||||
expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'a'
|
||||
expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toContain 'a'
|
||||
|
||||
it "applies the 'folded' decoration only to the initial screen row of a soft-wrapped buffer row", ->
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setDefaultCharWidth(1)
|
||||
editor.setEditorWidthInChars(15)
|
||||
editor.foldBufferRange([[0, 20], [0, 22]])
|
||||
presenter = buildPresenter(explicitHeight: 35, scrollTop: 0, tileSize: 2)
|
||||
|
||||
expect(lineNumberStateForScreenRow(presenter, 0).decorationClasses).toContain 'folded'
|
||||
expect(lineNumberStateForScreenRow(presenter, 1).decorationClasses).toBeNull()
|
||||
|
||||
describe ".foldable", ->
|
||||
it "marks line numbers at the start of a foldable region as foldable", ->
|
||||
presenter = buildPresenter()
|
||||
|
||||
@@ -10,6 +10,7 @@ describe "TextEditorRegistry", ->
|
||||
it "gets added to the list of registered editors", ->
|
||||
editor = {}
|
||||
registry.add(editor)
|
||||
expect(editor.registered).toBe true
|
||||
expect(registry.editors.size).toBe 1
|
||||
expect(registry.editors.has(editor)).toBe(true)
|
||||
|
||||
@@ -19,6 +20,16 @@ describe "TextEditorRegistry", ->
|
||||
expect(registry.editors.size).toBe 1
|
||||
disposable.dispose()
|
||||
expect(registry.editors.size).toBe 0
|
||||
expect(editor.registered).toBe false
|
||||
|
||||
it "can be removed", ->
|
||||
editor = {}
|
||||
registry.add(editor)
|
||||
expect(registry.editors.size).toBe 1
|
||||
success = registry.remove(editor)
|
||||
expect(success).toBe true
|
||||
expect(registry.editors.size).toBe 0
|
||||
expect(editor.registered).toBe false
|
||||
|
||||
describe "when the registry is observed", ->
|
||||
it "calls the callback for current and future editors until unsubscribed", ->
|
||||
|
||||
@@ -39,21 +39,19 @@ describe "TextEditor", ->
|
||||
|
||||
it "preserves the invisibles setting", ->
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
previousInvisibles = editor.tokenizedLineForScreenRow(0).invisibles
|
||||
|
||||
previousLineText = editor.lineTextForScreenRow(0)
|
||||
editor2 = TextEditor.deserialize(editor.serialize(), atom)
|
||||
|
||||
expect(previousInvisibles).toBeDefined()
|
||||
expect(editor2.displayBuffer.tokenizedLineForScreenRow(0).invisibles).toEqual previousInvisibles
|
||||
expect(editor2.lineTextForScreenRow(0)).toBe(previousLineText)
|
||||
|
||||
it "updates invisibles if the settings have changed between serialization and deserialization", ->
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
|
||||
previousLineText = editor.lineTextForScreenRow(0)
|
||||
state = editor.serialize()
|
||||
atom.config.set('editor.invisibles', eol: '?')
|
||||
editor2 = TextEditor.deserialize(state, atom)
|
||||
|
||||
expect(editor.tokenizedLineForScreenRow(0).invisibles.eol).toBe '?'
|
||||
expect(editor2.lineTextForScreenRow(0)).not.toBe(previousLineText)
|
||||
expect(editor2.lineTextForScreenRow(0).endsWith('?')).toBe(true)
|
||||
|
||||
describe "when the editor is constructed with the largeFileMode option set to true", ->
|
||||
it "loads the editor but doesn't tokenize", ->
|
||||
@@ -64,15 +62,14 @@ describe "TextEditor", ->
|
||||
|
||||
runs ->
|
||||
buffer = editor.getBuffer()
|
||||
expect(editor.tokenizedLineForScreenRow(0).text).toBe buffer.lineForRow(0)
|
||||
expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1
|
||||
expect(editor.tokenizedLineForScreenRow(1).tokens.length).toBe 2 # soft tab
|
||||
expect(editor.tokenizedLineForScreenRow(12).text).toBe buffer.lineForRow(12)
|
||||
expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1
|
||||
expect(editor.lineTextForScreenRow(0)).toBe buffer.lineForRow(0)
|
||||
expect(editor.tokensForScreenRow(0).length).toBe 1
|
||||
expect(editor.tokensForScreenRow(1).length).toBe 2 # soft tab
|
||||
expect(editor.lineTextForScreenRow(12)).toBe buffer.lineForRow(12)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
|
||||
editor.insertText('hey"')
|
||||
expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1
|
||||
expect(editor.tokenizedLineForScreenRow(1).tokens.length).toBe 2 # sof tab
|
||||
expect(editor.tokensForScreenRow(0).length).toBe 1
|
||||
expect(editor.tokensForScreenRow(1).length).toBe 2 # soft tab
|
||||
|
||||
describe ".copy()", ->
|
||||
it "returns a different edit session with the same initial state", ->
|
||||
@@ -314,7 +311,7 @@ describe "TextEditor", ->
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setDefaultCharWidth(1)
|
||||
editor.setEditorWidthInChars(50)
|
||||
editor.createFold(2, 3)
|
||||
editor.foldBufferRowRange(2, 3)
|
||||
|
||||
it "positions the cursor at the buffer position that corresponds to the given screen position", ->
|
||||
editor.setCursorScreenPosition([9, 0])
|
||||
@@ -495,7 +492,7 @@ describe "TextEditor", ->
|
||||
it "wraps to the end of the previous line", ->
|
||||
editor.setCursorScreenPosition([4, 4])
|
||||
editor.moveLeft()
|
||||
expect(editor.getCursorScreenPosition()).toEqual [3, 50]
|
||||
expect(editor.getCursorScreenPosition()).toEqual [3, 46]
|
||||
|
||||
describe "when the cursor is on the first line", ->
|
||||
it "remains in the same position (0,0)", ->
|
||||
@@ -683,7 +680,7 @@ describe "TextEditor", ->
|
||||
editor.setCursorScreenPosition([0, 2])
|
||||
editor.moveToEndOfLine()
|
||||
cursor = editor.getLastCursor()
|
||||
expect(cursor.getScreenPosition()).toEqual [3, 4]
|
||||
expect(cursor.getScreenPosition()).toEqual [4, 4]
|
||||
|
||||
describe ".moveToFirstCharacterOfLine()", ->
|
||||
describe "when soft wrap is on", ->
|
||||
@@ -1798,22 +1795,22 @@ describe "TextEditor", ->
|
||||
describe "when the 'preserveFolds' option is false (the default)", ->
|
||||
it "removes folds that contain the selections", ->
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 0]])
|
||||
editor.createFold(1, 4)
|
||||
editor.createFold(2, 3)
|
||||
editor.createFold(6, 8)
|
||||
editor.createFold(10, 11)
|
||||
editor.foldBufferRowRange(1, 4)
|
||||
editor.foldBufferRowRange(2, 3)
|
||||
editor.foldBufferRowRange(6, 8)
|
||||
editor.foldBufferRowRange(10, 11)
|
||||
|
||||
editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 6], [7, 7]]])
|
||||
expect(editor.tokenizedLineForScreenRow(1).fold).toBeUndefined()
|
||||
expect(editor.tokenizedLineForScreenRow(2).fold).toBeUndefined()
|
||||
expect(editor.tokenizedLineForScreenRow(6).fold).toBeUndefined()
|
||||
expect(editor.tokenizedLineForScreenRow(10).fold).toBeDefined()
|
||||
expect(editor.isFoldedAtScreenRow(1)).toBeFalsy()
|
||||
expect(editor.isFoldedAtScreenRow(2)).toBeFalsy()
|
||||
expect(editor.isFoldedAtScreenRow(6)).toBeFalsy()
|
||||
expect(editor.isFoldedAtScreenRow(10)).toBeTruthy()
|
||||
|
||||
describe "when the 'preserveFolds' option is true", ->
|
||||
it "does not remove folds that contain the selections", ->
|
||||
editor.setSelectedBufferRange([[0, 0], [0, 0]])
|
||||
editor.createFold(1, 4)
|
||||
editor.createFold(6, 8)
|
||||
editor.foldBufferRowRange(1, 4)
|
||||
editor.foldBufferRowRange(6, 8)
|
||||
editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 0], [6, 1]]], preserveFolds: true)
|
||||
expect(editor.isFoldedAtBufferRow(1)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(6)).toBeTruthy()
|
||||
@@ -2225,7 +2222,7 @@ describe "TextEditor", ->
|
||||
it "moves the line to the previous row without breaking the fold", ->
|
||||
expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
editor.setSelectedBufferRange([[4, 2], [4, 9]], preserveFolds: true)
|
||||
expect(editor.getSelectedBufferRange()).toEqual [[4, 2], [4, 9]]
|
||||
|
||||
@@ -2253,7 +2250,7 @@ describe "TextEditor", ->
|
||||
expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));"
|
||||
expect(editor.lineTextForBufferRow(9)).toBe " };"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(5)).toBeTruthy()
|
||||
@@ -2291,7 +2288,7 @@ describe "TextEditor", ->
|
||||
it "moves the lines to the previous row without breaking the fold", ->
|
||||
expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
editor.setSelectedBufferRange([[3, 2], [4, 9]], preserveFolds: true)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(3)).toBeFalsy()
|
||||
@@ -2319,7 +2316,7 @@ describe "TextEditor", ->
|
||||
it "moves the lines to the previous row without breaking the fold", ->
|
||||
expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
editor.setSelectedBufferRange([[4, 2], [8, 9]], preserveFolds: true)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
@@ -2363,7 +2360,7 @@ describe "TextEditor", ->
|
||||
expect(editor.lineTextForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));"
|
||||
expect(editor.lineTextForBufferRow(9)).toBe " };"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(5)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(6)).toBeTruthy()
|
||||
@@ -2403,7 +2400,7 @@ describe "TextEditor", ->
|
||||
it "moves the lines to the previous row without breaking the fold", ->
|
||||
expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
editor.setSelectedBufferRanges([
|
||||
[[2, 2], [2, 9]],
|
||||
[[4, 2], [4, 9]]
|
||||
@@ -2441,7 +2438,7 @@ describe "TextEditor", ->
|
||||
|
||||
describe "when there is a fold", ->
|
||||
it "moves all lines that spanned by a selection to preceding row, preserving all folds", ->
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(5)).toBeTruthy()
|
||||
@@ -2468,8 +2465,8 @@ describe "TextEditor", ->
|
||||
|
||||
describe 'and many selections intersects folded rows', ->
|
||||
it 'moves and preserves all the folds', ->
|
||||
editor.createFold(2, 4)
|
||||
editor.createFold(7, 9)
|
||||
editor.foldBufferRowRange(2, 4)
|
||||
editor.foldBufferRowRange(7, 9)
|
||||
|
||||
editor.setSelectedBufferRanges([
|
||||
[[1, 0], [5, 4]],
|
||||
@@ -2553,7 +2550,7 @@ describe "TextEditor", ->
|
||||
it "moves the line to the following row without breaking the fold", ->
|
||||
expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
editor.setSelectedBufferRange([[4, 2], [4, 9]], preserveFolds: true)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
@@ -2579,7 +2576,7 @@ describe "TextEditor", ->
|
||||
expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];"
|
||||
expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(5)).toBeTruthy()
|
||||
@@ -2633,7 +2630,7 @@ describe "TextEditor", ->
|
||||
it "moves the lines to the following row without breaking the fold", ->
|
||||
expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
editor.setSelectedBufferRange([[3, 2], [4, 9]], preserveFolds: true)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(3)).toBeFalsy()
|
||||
@@ -2661,7 +2658,7 @@ describe "TextEditor", ->
|
||||
it "moves the lines to the following row without breaking the fold", ->
|
||||
expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
editor.setSelectedBufferRange([[4, 2], [8, 9]], preserveFolds: true)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
@@ -2691,7 +2688,7 @@ describe "TextEditor", ->
|
||||
expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items;"
|
||||
expect(editor.lineTextForBufferRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(5)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(6)).toBeTruthy()
|
||||
@@ -2733,8 +2730,8 @@ describe "TextEditor", ->
|
||||
|
||||
describe 'and many selections intersects folded rows', ->
|
||||
it 'moves and preserves all the folds', ->
|
||||
editor.createFold(2, 4)
|
||||
editor.createFold(7, 9)
|
||||
editor.foldBufferRowRange(2, 4)
|
||||
editor.foldBufferRowRange(7, 9)
|
||||
|
||||
editor.setSelectedBufferRanges([
|
||||
[[2, 0], [2, 4]],
|
||||
@@ -2763,7 +2760,7 @@ describe "TextEditor", ->
|
||||
|
||||
describe "when there is a fold below one of the selected row", ->
|
||||
it "moves all lines spanned by a selection to the following row, preserving the fold", ->
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(5)).toBeTruthy()
|
||||
@@ -2786,7 +2783,7 @@ describe "TextEditor", ->
|
||||
|
||||
describe "when there is a fold below a group of multiple selections without any lines with no selection in-between", ->
|
||||
it "moves all the lines below the fold, preserving the fold", ->
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
|
||||
expect(editor.isFoldedAtBufferRow(5)).toBeTruthy()
|
||||
@@ -2811,7 +2808,7 @@ describe "TextEditor", ->
|
||||
it "moves the lines to the previous row without breaking the fold", ->
|
||||
expect(editor.lineTextForBufferRow(4)).toBe " while(items.length > 0) {"
|
||||
|
||||
editor.createFold(4, 7)
|
||||
editor.foldBufferRowRange(4, 7)
|
||||
editor.setSelectedBufferRanges([
|
||||
[[2, 2], [2, 9]],
|
||||
[[4, 2], [4, 9]]
|
||||
@@ -2878,6 +2875,13 @@ describe "TextEditor", ->
|
||||
expect(editor.lineTextForBufferRow(1)).toBe "1"
|
||||
expect(editor.lineTextForBufferRow(2)).toBe "2"
|
||||
|
||||
describe "when the line is the last buffer row", ->
|
||||
it "doesn't move it", ->
|
||||
editor.setText("abc\ndef")
|
||||
editor.setCursorBufferPosition([1, 0])
|
||||
editor.moveLineDown()
|
||||
expect(editor.getText()).toBe("abc\ndef")
|
||||
|
||||
describe ".insertText(text)", ->
|
||||
describe "when there is a single selection", ->
|
||||
beforeEach ->
|
||||
@@ -2949,10 +2953,10 @@ describe "TextEditor", ->
|
||||
|
||||
describe "when there is a selection that ends on a folded line", ->
|
||||
it "destroys the selection", ->
|
||||
editor.createFold(2, 4)
|
||||
editor.foldBufferRowRange(2, 4)
|
||||
editor.setSelectedBufferRange([[1, 0], [2, 0]])
|
||||
editor.insertText('holy cow')
|
||||
expect(editor.tokenizedLineForScreenRow(2).fold).toBeUndefined()
|
||||
expect(editor.isFoldedAtScreenRow(2)).toBeFalsy()
|
||||
|
||||
describe "when there are ::onWillInsertText and ::onDidInsertText observers", ->
|
||||
beforeEach ->
|
||||
@@ -3174,7 +3178,7 @@ describe "TextEditor", ->
|
||||
expect(editor.indentationForBufferRow(0)).toBe 1
|
||||
expect(editor.indentationForBufferRow(1)).toBe 1
|
||||
|
||||
it "indents the new line to the correct level when editor.autoIndent is true and using a off-side rule language", ->
|
||||
it "indents the new line to the correct level when editor.autoIndent is true and using an off-side rule language", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
@@ -3246,15 +3250,14 @@ describe "TextEditor", ->
|
||||
editor.setCursorScreenPosition(row: 0, column: 0)
|
||||
editor.backspace()
|
||||
|
||||
describe "when the cursor is on the first column of a line below a fold", ->
|
||||
it "deletes the folded lines", ->
|
||||
editor.setCursorScreenPosition([4, 0])
|
||||
editor.foldCurrentRow()
|
||||
editor.setCursorScreenPosition([5, 0])
|
||||
describe "when the cursor is after a fold", ->
|
||||
it "deletes the folded range", ->
|
||||
editor.foldBufferRange([[4, 7], [5, 8]])
|
||||
editor.setCursorBufferPosition([5, 8])
|
||||
editor.backspace()
|
||||
|
||||
expect(buffer.lineForRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));"
|
||||
expect(buffer.lineForRow(4).fold).toBeUndefined()
|
||||
expect(buffer.lineForRow(4)).toBe " whirrent = items.shift();"
|
||||
expect(editor.isFoldedAtBufferRow(4)).toBe(false)
|
||||
|
||||
describe "when the cursor is in the middle of a line below a fold", ->
|
||||
it "backspaces as normal", ->
|
||||
@@ -3267,14 +3270,13 @@ describe "TextEditor", ->
|
||||
expect(buffer.lineForRow(8)).toBe " eturn sort(left).concat(pivot).concat(sort(right));"
|
||||
|
||||
describe "when the cursor is on a folded screen line", ->
|
||||
it "deletes all of the folded lines along with the fold", ->
|
||||
it "deletes the contents of the fold before the cursor", ->
|
||||
editor.setCursorBufferPosition([3, 0])
|
||||
editor.foldCurrentRow()
|
||||
editor.backspace()
|
||||
|
||||
expect(buffer.lineForRow(1)).toBe ""
|
||||
expect(buffer.lineForRow(2)).toBe " return sort(Array.apply(this, arguments));"
|
||||
expect(editor.getCursorScreenPosition()).toEqual [1, 0]
|
||||
expect(buffer.lineForRow(1)).toBe " var sort = function(items) var pivot = items.shift(), current, left = [], right = [];"
|
||||
expect(editor.getCursorScreenPosition()).toEqual [1, 29]
|
||||
|
||||
describe "when there are multiple cursors", ->
|
||||
describe "when cursors are on the same line", ->
|
||||
@@ -3341,7 +3343,7 @@ describe "TextEditor", ->
|
||||
editor.backspace()
|
||||
|
||||
expect(buffer.lineForRow(3)).toBe " while(items.length > 0) {"
|
||||
expect(editor.tokenizedLineForScreenRow(3).fold).toBeDefined()
|
||||
expect(editor.isFoldedAtScreenRow(3)).toBe(true)
|
||||
|
||||
describe "when there are multiple selections", ->
|
||||
it "removes all selected text", ->
|
||||
@@ -3514,16 +3516,16 @@ describe "TextEditor", ->
|
||||
editor.delete()
|
||||
expect(buffer.lineForRow(12)).toBe '};'
|
||||
|
||||
describe "when the cursor is on the end of a line above a fold", ->
|
||||
describe "when the cursor is before a fold", ->
|
||||
it "only deletes the lines inside the fold", ->
|
||||
editor.foldBufferRow(4)
|
||||
editor.setCursorScreenPosition([3, Infinity])
|
||||
editor.foldBufferRange([[3, 6], [4, 8]])
|
||||
editor.setCursorScreenPosition([3, 6])
|
||||
cursorPositionBefore = editor.getCursorScreenPosition()
|
||||
|
||||
editor.delete()
|
||||
|
||||
expect(buffer.lineForRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];"
|
||||
expect(buffer.lineForRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));"
|
||||
expect(buffer.lineForRow(3)).toBe " vae(items.length > 0) {"
|
||||
expect(buffer.lineForRow(4)).toBe " current = items.shift();"
|
||||
expect(editor.getCursorScreenPosition()).toEqual cursorPositionBefore
|
||||
|
||||
describe "when the cursor is in the middle a line above a fold", ->
|
||||
@@ -3535,20 +3537,21 @@ describe "TextEditor", ->
|
||||
editor.delete()
|
||||
|
||||
expect(buffer.lineForRow(3)).toBe " ar pivot = items.shift(), current, left = [], right = [];"
|
||||
expect(editor.tokenizedLineForScreenRow(4).fold).toBeDefined()
|
||||
expect(editor.isFoldedAtScreenRow(4)).toBe(true)
|
||||
expect(editor.getCursorScreenPosition()).toEqual [3, 4]
|
||||
|
||||
describe "when the cursor is on a folded line", ->
|
||||
it "removes the lines contained by the fold", ->
|
||||
editor.setSelectedBufferRange([[2, 0], [2, 0]])
|
||||
editor.createFold(2, 4)
|
||||
editor.createFold(2, 6)
|
||||
oldLine7 = buffer.lineForRow(7)
|
||||
oldLine8 = buffer.lineForRow(8)
|
||||
describe "when the cursor is inside a fold", ->
|
||||
it "removes the folded content after the cursor", ->
|
||||
editor.foldBufferRange([[2, 6], [6, 21]])
|
||||
editor.setCursorBufferPosition([4, 9])
|
||||
|
||||
editor.delete()
|
||||
expect(editor.tokenizedLineForScreenRow(2).text).toBe oldLine7
|
||||
expect(editor.tokenizedLineForScreenRow(3).text).toBe oldLine8
|
||||
|
||||
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return items;'
|
||||
expect(buffer.lineForRow(3)).toBe ' var pivot = items.shift(), current, left = [], right = [];'
|
||||
expect(buffer.lineForRow(4)).toBe ' while ? left.push(current) : right.push(current);'
|
||||
expect(buffer.lineForRow(5)).toBe ' }'
|
||||
expect(editor.getCursorBufferPosition()).toEqual [4, 9]
|
||||
|
||||
describe "when there are multiple cursors", ->
|
||||
describe "when cursors are on the same line", ->
|
||||
@@ -3805,10 +3808,10 @@ describe "TextEditor", ->
|
||||
it "cuts up to the end of the line", ->
|
||||
editor.setSoftWrapped(true)
|
||||
editor.setDefaultCharWidth(1)
|
||||
editor.setEditorWidthInChars(10)
|
||||
editor.setCursorScreenPosition([2, 2])
|
||||
editor.setEditorWidthInChars(25)
|
||||
editor.setCursorScreenPosition([2, 6])
|
||||
editor.cutToEndOfLine()
|
||||
expect(editor.tokenizedLineForScreenRow(2).text).toBe '= () {'
|
||||
expect(editor.lineTextForScreenRow(2)).toBe ' var function(items) {'
|
||||
|
||||
describe "when soft wrap is off", ->
|
||||
describe "when nothing is selected", ->
|
||||
@@ -4693,7 +4696,8 @@ describe "TextEditor", ->
|
||||
it '.lineTextForScreenRow(row)', ->
|
||||
editor.foldBufferRow(4)
|
||||
expect(editor.lineTextForScreenRow(5)).toEqual ' return sort(left).concat(pivot).concat(sort(right));'
|
||||
expect(editor.lineTextForScreenRow(100)).not.toBeDefined()
|
||||
expect(editor.lineTextForScreenRow(9)).toEqual '};'
|
||||
expect(editor.lineTextForScreenRow(10)).toBeUndefined()
|
||||
|
||||
describe ".deleteLine()", ->
|
||||
it "deletes the first line when the cursor is there", ->
|
||||
@@ -5050,11 +5054,13 @@ describe "TextEditor", ->
|
||||
|
||||
it 'retokenizes when the tab length is updated via .setTabLength()', ->
|
||||
expect(editor.getTabLength()).toBe 2
|
||||
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2
|
||||
leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace'
|
||||
expect(leadingWhitespaceTokens.length).toBe(3)
|
||||
|
||||
editor.setTabLength(6)
|
||||
expect(editor.getTabLength()).toBe 6
|
||||
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6
|
||||
leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace'
|
||||
expect(leadingWhitespaceTokens.length).toBe(1)
|
||||
|
||||
changeHandler = jasmine.createSpy('changeHandler')
|
||||
editor.onDidChange(changeHandler)
|
||||
@@ -5063,21 +5069,25 @@ describe "TextEditor", ->
|
||||
|
||||
it 'retokenizes when the editor.tabLength setting is updated', ->
|
||||
expect(editor.getTabLength()).toBe 2
|
||||
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2
|
||||
leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace'
|
||||
expect(leadingWhitespaceTokens.length).toBe(3)
|
||||
|
||||
atom.config.set 'editor.tabLength', 6, scopeSelector: '.source.js'
|
||||
expect(editor.getTabLength()).toBe 6
|
||||
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6
|
||||
leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace'
|
||||
expect(leadingWhitespaceTokens.length).toBe(1)
|
||||
|
||||
it 'updates the tab length when the grammar changes', ->
|
||||
atom.config.set 'editor.tabLength', 6, scopeSelector: '.source.coffee'
|
||||
|
||||
expect(editor.getTabLength()).toBe 2
|
||||
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2
|
||||
leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace'
|
||||
expect(leadingWhitespaceTokens.length).toBe(3)
|
||||
|
||||
editor.setGrammar(coffeeEditor.getGrammar())
|
||||
expect(editor.getTabLength()).toBe 6
|
||||
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6
|
||||
leadingWhitespaceTokens = editor.tokensForScreenRow(5).filter (token) -> token is 'leading-whitespace'
|
||||
expect(leadingWhitespaceTokens.length).toBe(1)
|
||||
|
||||
describe ".indentLevelForLine(line)", ->
|
||||
it "returns the indent level when the line has only leading whitespace", ->
|
||||
@@ -5113,11 +5123,11 @@ describe "TextEditor", ->
|
||||
|
||||
runs ->
|
||||
expect(editor.getGrammar()).toBe atom.grammars.nullGrammar
|
||||
expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1
|
||||
expect(editor.tokensForScreenRow(0).length).toBe(1)
|
||||
|
||||
atom.grammars.addGrammar(jsGrammar)
|
||||
expect(editor.getGrammar()).toBe jsGrammar
|
||||
expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBeGreaterThan 1
|
||||
expect(editor.tokensForScreenRow(0).length).toBeGreaterThan 1
|
||||
|
||||
describe "editor.autoIndent", ->
|
||||
describe "when editor.autoIndent is false (default)", ->
|
||||
@@ -5153,7 +5163,7 @@ describe "TextEditor", ->
|
||||
expect(editor.indentationForBufferRow(2)).toBe editor.indentationForBufferRow(1) + 1
|
||||
|
||||
describe "when the line preceding the newline does't add a level of indentation", ->
|
||||
it "indents the new line to the same level a as the preceding line", ->
|
||||
it "indents the new line to the same level as the preceding line", ->
|
||||
editor.setCursorBufferPosition([5, 14])
|
||||
editor.insertText('\n')
|
||||
expect(editor.indentationForBufferRow(6)).toBe editor.indentationForBufferRow(5)
|
||||
@@ -5261,7 +5271,7 @@ describe "TextEditor", ->
|
||||
describe ".destroy()", ->
|
||||
it "destroys marker layers associated with the text editor", ->
|
||||
selectionsMarkerLayerId = editor.selectionsMarkerLayer.id
|
||||
foldsMarkerLayerId = editor.displayBuffer.foldsMarkerLayer.id
|
||||
foldsMarkerLayerId = editor.displayLayer.foldsMarkerLayer.id
|
||||
editor.destroy()
|
||||
expect(buffer.getMarkerLayer(selectionsMarkerLayerId)).toBeUndefined()
|
||||
expect(buffer.getMarkerLayer(foldsMarkerLayerId)).toBeUndefined()
|
||||
@@ -5345,10 +5355,10 @@ describe "TextEditor", ->
|
||||
expect(editor.getSelectedBufferRanges()).toEqual [[[3, 5], [3, 5]], [[9, 0], [14, 0]]]
|
||||
|
||||
# folds are also duplicated
|
||||
expect(editor.tokenizedLineForScreenRow(5).fold).toBeDefined()
|
||||
expect(editor.tokenizedLineForScreenRow(7).fold).toBeDefined()
|
||||
expect(editor.tokenizedLineForScreenRow(7).text).toBe " while(items.length > 0) {"
|
||||
expect(editor.tokenizedLineForScreenRow(8).text).toBe " return sort(left).concat(pivot).concat(sort(right));"
|
||||
expect(editor.isFoldedAtScreenRow(5)).toBe(true)
|
||||
expect(editor.isFoldedAtScreenRow(7)).toBe(true)
|
||||
expect(editor.lineTextForScreenRow(7)).toBe " while(items.length > 0) {" + editor.displayLayer.foldCharacter
|
||||
expect(editor.lineTextForScreenRow(8)).toBe " return sort(left).concat(pivot).concat(sort(right));"
|
||||
|
||||
it "duplicates all folded lines for empty selections on folded lines", ->
|
||||
editor.foldBufferRow(4)
|
||||
@@ -5544,17 +5554,15 @@ describe "TextEditor", ->
|
||||
runs ->
|
||||
editor.setText("// http://github.com")
|
||||
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " http://github.com"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
tokens = editor.tokensForScreenRow(0)
|
||||
expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js']
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-hyperlink')
|
||||
|
||||
runs ->
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "http://github.com"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
|
||||
tokens = editor.tokensForScreenRow(0)
|
||||
expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js', 'markup.underline.link.http.hyperlink']
|
||||
|
||||
describe "when the grammar is updated", ->
|
||||
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
|
||||
@@ -5564,25 +5572,22 @@ describe "TextEditor", ->
|
||||
runs ->
|
||||
editor.setText("// SELECT * FROM OCTOCATS")
|
||||
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
tokens = editor.tokensForScreenRow(0)
|
||||
expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js']
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('package-with-injection-selector')
|
||||
|
||||
runs ->
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS"
|
||||
expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"]
|
||||
tokens = editor.tokensForScreenRow(0)
|
||||
expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js']
|
||||
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-sql')
|
||||
|
||||
runs ->
|
||||
{tokens} = editor.tokenizedLineForScreenRow(0)
|
||||
expect(tokens[2].value).toBe "SELECT"
|
||||
expect(tokens[2].scopes).toEqual ["source.js", "comment.line.double-slash.js", "keyword.other.DML.sql"]
|
||||
tokens = editor.tokensForScreenRow(0)
|
||||
expect(tokens).toEqual ['source.js', 'comment.line.double-slash.js', 'punctuation.definition.comment.js', 'keyword.other.DML.sql', 'keyword.operator.star.sql', 'keyword.other.DML.sql']
|
||||
|
||||
describe ".normalizeTabsInBufferRange()", ->
|
||||
it "normalizes tabs depending on the editor's soft tab/tab length settings", ->
|
||||
@@ -5710,6 +5715,19 @@ describe "TextEditor", ->
|
||||
expect(editor.getFirstVisibleScreenRow()).toEqual 89
|
||||
expect(editor.getVisibleRowRange()).toEqual [89, 99]
|
||||
|
||||
describe "::scrollToScreenPosition(position, [options])", ->
|
||||
it "triggers ::onDidRequestAutoscroll with the logical coordinates along with the options", ->
|
||||
scrollSpy = jasmine.createSpy("::onDidRequestAutoscroll")
|
||||
editor.onDidRequestAutoscroll(scrollSpy)
|
||||
|
||||
editor.scrollToScreenPosition([8, 20])
|
||||
editor.scrollToScreenPosition([8, 20], center: true)
|
||||
editor.scrollToScreenPosition([8, 20], center: false, reversed: true)
|
||||
|
||||
expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {})
|
||||
expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: true})
|
||||
expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: false, reversed: true})
|
||||
|
||||
describe '.get/setPlaceholderText()', ->
|
||||
it 'can be created with placeholderText', ->
|
||||
newEditor = atom.workspace.buildTextEditor(
|
||||
@@ -5855,6 +5873,7 @@ describe "TextEditor", ->
|
||||
expect(editor.decorationsStateForScreenRowRange(0, 5)[decoration.id]).toEqual {
|
||||
properties: {type: 'highlight', class: 'foo'}
|
||||
screenRange: marker.getScreenRange(),
|
||||
bufferRange: marker.getBufferRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
@@ -5875,26 +5894,31 @@ describe "TextEditor", ->
|
||||
expect(decorationState["#{layer1Decoration1.id}-#{marker1.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'foo'},
|
||||
screenRange: marker1.getRange(),
|
||||
bufferRange: marker1.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
expect(decorationState["#{layer1Decoration1.id}-#{marker2.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'foo'},
|
||||
screenRange: marker2.getRange(),
|
||||
bufferRange: marker2.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
expect(decorationState["#{layer1Decoration2.id}-#{marker1.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'bar'},
|
||||
screenRange: marker1.getRange(),
|
||||
bufferRange: marker1.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
expect(decorationState["#{layer1Decoration2.id}-#{marker2.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'bar'},
|
||||
screenRange: marker2.getRange(),
|
||||
bufferRange: marker2.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
expect(decorationState["#{layer2Decoration.id}-#{marker3.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'baz'},
|
||||
screenRange: marker3.getRange(),
|
||||
bufferRange: marker3.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
@@ -5906,16 +5930,19 @@ describe "TextEditor", ->
|
||||
expect(decorationState["#{layer1Decoration2.id}-#{marker1.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'bar'},
|
||||
screenRange: marker1.getRange(),
|
||||
bufferRange: marker1.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
expect(decorationState["#{layer1Decoration2.id}-#{marker2.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'bar'},
|
||||
screenRange: marker2.getRange(),
|
||||
bufferRange: marker2.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
expect(decorationState["#{layer2Decoration.id}-#{marker3.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'baz'},
|
||||
screenRange: marker3.getRange(),
|
||||
bufferRange: marker3.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
@@ -5924,6 +5951,7 @@ describe "TextEditor", ->
|
||||
expect(decorationState["#{layer1Decoration2.id}-#{marker1.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'quux'},
|
||||
screenRange: marker1.getRange(),
|
||||
bufferRange: marker1.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
@@ -5932,6 +5960,7 @@ describe "TextEditor", ->
|
||||
expect(decorationState["#{layer1Decoration2.id}-#{marker1.id}"]).toEqual {
|
||||
properties: {type: 'highlight', class: 'bar'},
|
||||
screenRange: marker1.getRange(),
|
||||
bufferRange: marker1.getRange(),
|
||||
rangeIsReversed: false
|
||||
}
|
||||
|
||||
@@ -5943,8 +5972,21 @@ describe "TextEditor", ->
|
||||
|
||||
it "ignores invisibles even if editor.showInvisibles is true", ->
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
invisibles = editor.tokenizedLineForScreenRow(0).invisibles
|
||||
expect(invisibles).toBe(null)
|
||||
expect(editor.lineTextForScreenRow(0).indexOf(atom.config.get('editor.invisibles.eol'))).toBe(-1)
|
||||
|
||||
describe "indent guides", ->
|
||||
it "shows indent guides when `editor.showIndentGuide` is set to true and the editor is not mini", ->
|
||||
editor.setText(" foo")
|
||||
atom.config.set('editor.tabLength', 2)
|
||||
|
||||
atom.config.set('editor.showIndentGuide', false)
|
||||
expect(editor.tokensForScreenRow(0)).toEqual ['source.js', 'leading-whitespace']
|
||||
|
||||
atom.config.set('editor.showIndentGuide', true)
|
||||
expect(editor.tokensForScreenRow(0)).toEqual ['source.js', 'leading-whitespace indent-guide']
|
||||
|
||||
editor.setMini(true)
|
||||
expect(editor.tokensForScreenRow(0)).toEqual ['source.js', 'leading-whitespace']
|
||||
|
||||
describe "when the editor is constructed with the grammar option set", ->
|
||||
beforeEach ->
|
||||
|
||||
@@ -75,22 +75,23 @@ describe 'text utilities', ->
|
||||
|
||||
expect(textUtils.isKoreanCharacter("O")).toBe(false)
|
||||
|
||||
describe ".isCJKCharacter(character)", ->
|
||||
it "returns true when the character is either a korean, half-width or double-width character", ->
|
||||
expect(textUtils.isCJKCharacter("我")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("私")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("B")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter(",")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("¢")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("ハ")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("ヒ")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("ᆲ")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("■")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("우")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("가")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("ㅢ")).toBe(true)
|
||||
expect(textUtils.isCJKCharacter("ㄼ")).toBe(true)
|
||||
describe ".isWrapBoundary(previousCharacter, character)", ->
|
||||
it "returns true when the character is CJK or when the previous character is a space/tab", ->
|
||||
anyCharacter = 'x'
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "我")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "私")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "B")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, ",")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "¢")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "ハ")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "ヒ")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "ᆲ")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "■")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "우")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "가")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "ㅢ")).toBe(true)
|
||||
expect(textUtils.isWrapBoundary(anyCharacter, "ㄼ")).toBe(true)
|
||||
|
||||
expect(textUtils.isDoubleWidthCharacter("a")).toBe(false)
|
||||
expect(textUtils.isDoubleWidthCharacter("O")).toBe(false)
|
||||
expect(textUtils.isDoubleWidthCharacter("z")).toBe(false)
|
||||
expect(textUtils.isWrapBoundary(' ', 'h')).toBe(true)
|
||||
expect(textUtils.isWrapBoundary('\t', 'h')).toBe(true)
|
||||
expect(textUtils.isWrapBoundary('a', 'h')).toBe(false)
|
||||
|
||||
103
spec/tokenized-buffer-iterator-spec.js
Normal file
103
spec/tokenized-buffer-iterator-spec.js
Normal file
@@ -0,0 +1,103 @@
|
||||
/** @babel */
|
||||
|
||||
import TokenizedBufferIterator from '../src/tokenized-buffer-iterator'
|
||||
import {Point} from 'text-buffer'
|
||||
|
||||
describe('TokenizedBufferIterator', () => {
|
||||
it('reports two boundaries at the same position when tags close, open, then close again without a non-negative integer separating them (regression)', () => {
|
||||
const tokenizedBuffer = {
|
||||
tokenizedLineForRow () {
|
||||
return {
|
||||
tags: [-1, -2, -1, -2],
|
||||
text: '',
|
||||
openScopes: []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const grammarRegistry = {
|
||||
scopeForId () {
|
||||
return 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TokenizedBufferIterator(tokenizedBuffer, grammarRegistry)
|
||||
|
||||
iterator.seek(Point(0, 0))
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 0))
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 0))
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
})
|
||||
|
||||
it("reports a boundary at line end if the next line's open scopes don't match the containing tags for the current line", () => {
|
||||
const tokenizedBuffer = {
|
||||
tokenizedLineForRow (row) {
|
||||
if (row === 0) {
|
||||
return {
|
||||
tags: [-1, 3, -2, -3],
|
||||
text: 'bar',
|
||||
openScopes: []
|
||||
}
|
||||
} else if (row === 1) {
|
||||
return {
|
||||
tags: [3],
|
||||
text: 'baz',
|
||||
openScopes: [-1]
|
||||
}
|
||||
} else if (row === 2) {
|
||||
return {
|
||||
tags: [-2],
|
||||
text: '',
|
||||
openScopes: [-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const grammarRegistry = {
|
||||
scopeForId (id) {
|
||||
if (id === -2 || id === -1) {
|
||||
return 'foo'
|
||||
} else if (id === -3) {
|
||||
return 'qux'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const iterator = new TokenizedBufferIterator(tokenizedBuffer, grammarRegistry)
|
||||
|
||||
iterator.seek(Point(0, 0))
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 0))
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 3))
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual(['qux'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 3))
|
||||
expect(iterator.getCloseTags()).toEqual(['qux'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(1, 0))
|
||||
expect(iterator.getCloseTags()).toEqual([])
|
||||
expect(iterator.getOpenTags()).toEqual(['foo'])
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition()).toEqual(Point(2, 0))
|
||||
expect(iterator.getCloseTags()).toEqual(['foo'])
|
||||
expect(iterator.getOpenTags()).toEqual([])
|
||||
})
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
TokenizedBuffer = require '../src/tokenized-buffer'
|
||||
TextBuffer = require 'text-buffer'
|
||||
{Point} = TextBuffer = require 'text-buffer'
|
||||
_ = require 'underscore-plus'
|
||||
|
||||
describe "TokenizedBuffer", ->
|
||||
@@ -134,13 +134,10 @@ describe "TokenizedBuffer", ->
|
||||
describe "on construction", ->
|
||||
it "initially creates un-tokenized screen lines, then tokenizes lines chunk at a time in the background", ->
|
||||
line0 = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
expect(line0.tokens.length).toBe 1
|
||||
expect(line0.tokens[0]).toEqual(value: line0.text, scopes: ['source.js'])
|
||||
expect(line0.tokens).toEqual([value: line0.text, scopes: ['source.js']])
|
||||
|
||||
line11 = tokenizedBuffer.tokenizedLineForRow(11)
|
||||
expect(line11.tokens.length).toBe 2
|
||||
expect(line11.tokens[0]).toEqual(value: " ", scopes: ['source.js'], isAtomic: true)
|
||||
expect(line11.tokens[1]).toEqual(value: "return sort(Array.apply(this, arguments));", scopes: ['source.js'])
|
||||
expect(line11.tokens).toEqual([value: " return sort(Array.apply(this, arguments));", scopes: ['source.js']])
|
||||
|
||||
# background tokenization has not begun
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack).toBeUndefined()
|
||||
@@ -236,7 +233,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.function-call.js', 'meta.arguments.js', 'punctuation.definition.arguments.begin.bracket.round.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.decimal.js'])
|
||||
# line 2 is unchanged
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -283,9 +280,9 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
|
||||
# lines below deleted regions should be shifted upward
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.comparison.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[1]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[1]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.comparison.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -331,7 +328,7 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js'])
|
||||
|
||||
# previous line 3 is pushed down to become line 5
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[3]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.assignment.js'])
|
||||
|
||||
expect(changeHandler).toHaveBeenCalled()
|
||||
[event] = changeHandler.argsForCall[0]
|
||||
@@ -377,32 +374,6 @@ describe "TokenizedBuffer", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeTruthy()
|
||||
|
||||
it "tokenizes leading whitespace based on the new tab length", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " "
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " "
|
||||
|
||||
tokenizedBuffer.setTabLength(4)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " "
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeFalsy()
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " current "
|
||||
|
||||
it "does not tokenize whitespaces followed by combining characters as leading whitespace", ->
|
||||
buffer.setText(" \u030b")
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
{tokens} = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
expect(tokens[0].value).toBe " "
|
||||
expect(tokens[0].hasLeadingWhitespace()).toBe true
|
||||
expect(tokens[1].value).toBe " "
|
||||
expect(tokens[1].hasLeadingWhitespace()).toBe true
|
||||
expect(tokens[2].value).toBe " \u030b"
|
||||
expect(tokens[2].hasLeadingWhitespace()).toBe false
|
||||
|
||||
it "does not break out soft tabs across a scope boundary", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-gfm')
|
||||
@@ -439,132 +410,6 @@ describe "TokenizedBuffer", ->
|
||||
beforeEach ->
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
it "renders each tab as its own atomic token with a value of size tabLength", ->
|
||||
tabAsSpaces = _.multiplyString(' ', tokenizedBuffer.getTabLength())
|
||||
screenLine0 = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
expect(screenLine0.text).toBe "# Econ 101#{tabAsSpaces}"
|
||||
{tokens} = screenLine0
|
||||
|
||||
expect(tokens.length).toBe 3
|
||||
expect(tokens[0].value).toBe "#"
|
||||
expect(tokens[1].value).toBe " Econ 101"
|
||||
expect(tokens[2].value).toBe tabAsSpaces
|
||||
expect(tokens[2].scopes).toEqual tokens[1].scopes
|
||||
expect(tokens[2].isAtomic).toBeTruthy()
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand"
|
||||
|
||||
it "aligns the hard tabs to the correct tab stop column", ->
|
||||
buffer.setText """
|
||||
1\t2 \t3\t4
|
||||
12\t3 \t4\t5
|
||||
123\t4 \t5\t6
|
||||
"""
|
||||
|
||||
tokenizedBuffer.setTabLength(4)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 3
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 2
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1
|
||||
|
||||
tokenizedBuffer.setTabLength(3)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 2
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 3
|
||||
|
||||
tokenizedBuffer.setTabLength(2)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 2
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1
|
||||
|
||||
tokenizedBuffer.setTabLength(1)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 1
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6"
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1
|
||||
|
||||
describe "when the buffer contains UTF-8 surrogate pairs", ->
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-javascript')
|
||||
|
||||
runs ->
|
||||
buffer = atom.project.bufferForPathSync 'sample-with-pairs.js'
|
||||
buffer.setText """
|
||||
'abc\uD835\uDF97def'
|
||||
//\uD835\uDF97xyz
|
||||
"""
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
afterEach ->
|
||||
tokenizedBuffer.destroy()
|
||||
buffer.release()
|
||||
|
||||
it "renders each UTF-8 surrogate pair as its own atomic token", ->
|
||||
screenLine0 = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
expect(screenLine0.text).toBe "'abc\uD835\uDF97def'"
|
||||
{tokens} = screenLine0
|
||||
|
||||
expect(tokens.length).toBe 5
|
||||
expect(tokens[0].value).toBe "'"
|
||||
expect(tokens[1].value).toBe "abc"
|
||||
expect(tokens[2].value).toBe "\uD835\uDF97"
|
||||
expect(tokens[2].isAtomic).toBeTruthy()
|
||||
expect(tokens[3].value).toBe "def"
|
||||
expect(tokens[4].value).toBe "'"
|
||||
|
||||
screenLine1 = tokenizedBuffer.tokenizedLineForRow(1)
|
||||
expect(screenLine1.text).toBe "//\uD835\uDF97xyz"
|
||||
{tokens} = screenLine1
|
||||
|
||||
expect(tokens.length).toBe 4
|
||||
expect(tokens[0].value).toBe '//'
|
||||
expect(tokens[1].value).toBe '\uD835\uDF97'
|
||||
expect(tokens[1].value).toBeTruthy()
|
||||
expect(tokens[2].value).toBe 'xyz'
|
||||
expect(tokens[3].value).toBe ''
|
||||
|
||||
describe "when the grammar is tokenized", ->
|
||||
it "emits the `tokenized` event", ->
|
||||
editor = null
|
||||
@@ -574,7 +419,7 @@ describe "TokenizedBuffer", ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer = editor.tokenizedBuffer
|
||||
tokenizedBuffer.onDidTokenize tokenizedHandler
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedHandler.callCount).toBe(1)
|
||||
@@ -587,7 +432,7 @@ describe "TokenizedBuffer", ->
|
||||
atom.workspace.open('sample.js').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer = editor.tokenizedBuffer
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
tokenizedBuffer.onDidTokenize tokenizedHandler
|
||||
@@ -605,7 +450,7 @@ describe "TokenizedBuffer", ->
|
||||
atom.workspace.open('coffee.coffee').then (o) -> editor = o
|
||||
|
||||
runs ->
|
||||
tokenizedBuffer = editor.displayBuffer.tokenizedBuffer
|
||||
tokenizedBuffer = editor.tokenizedBuffer
|
||||
tokenizedBuffer.onDidTokenize tokenizedHandler
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
tokenizedHandler.reset()
|
||||
@@ -682,132 +527,7 @@ describe "TokenizedBuffer", ->
|
||||
it "returns the range covered by all contigous tokens (within a single line)", ->
|
||||
expect(tokenizedBuffer.bufferRangeForScopeAtPosition('.function', [1, 18])).toEqual [[1, 6], [1, 28]]
|
||||
|
||||
describe "when the editor.tabLength config value changes", ->
|
||||
it "updates the tab length of the tokenized lines", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.setText('\ttest')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 6)
|
||||
expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' '
|
||||
|
||||
it "does not allow the tab length to be less than 1", ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
buffer.setText('\ttest')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 1)
|
||||
expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' '
|
||||
atom.config.set('editor.tabLength', 0)
|
||||
expect(tokenizedBuffer.tokenForPosition([0, 0]).value).toBe ' '
|
||||
|
||||
describe "when the invisibles value changes", ->
|
||||
beforeEach ->
|
||||
|
||||
it "updates the tokens with the appropriate invisible characters", ->
|
||||
buffer = new TextBuffer(text: " \t a line with tabs\tand \tspaces \t ")
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
atom.config.set("editor.invisibles", space: 'S', tab: 'T')
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "SST Sa line with tabsTand T spacesSTS"
|
||||
# Also needs to work for copies
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).copy().text).toBe "SST Sa line with tabsTand T spacesSTS"
|
||||
|
||||
it "assigns endOfLineInvisibles to tokenized lines", ->
|
||||
buffer = new TextBuffer(text: "a line that ends in a carriage-return-line-feed \r\na line that ends in just a line-feed\na line with no ending")
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
|
||||
atom.config.set('editor.showInvisibles', true)
|
||||
atom.config.set("editor.invisibles", cr: 'R', eol: 'N')
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R', 'N']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual ['N']
|
||||
|
||||
# Lines ending in soft wraps get no invisibles
|
||||
[left, right] = tokenizedBuffer.tokenizedLineForRow(0).softWrapAt(20)
|
||||
expect(left.endOfLineInvisibles).toBe null
|
||||
expect(right.endOfLineInvisibles).toEqual ['R', 'N']
|
||||
|
||||
atom.config.set("editor.invisibles", cr: 'R', eol: false)
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R']
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual []
|
||||
|
||||
describe "leading and trailing whitespace", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
it "assigns ::firstNonWhitespaceIndex on tokens that have leading whitespace", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0].firstNonWhitespaceIndex).toBe null
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].firstNonWhitespaceIndex).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].firstNonWhitespaceIndex).toBe null
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].firstNonWhitespaceIndex).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].firstNonWhitespaceIndex).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2].firstNonWhitespaceIndex).toBe null
|
||||
|
||||
# The 4th token *has* leading whitespace, but isn't entirely whitespace
|
||||
buffer.insert([5, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[3].firstNonWhitespaceIndex).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4].firstNonWhitespaceIndex).toBe null
|
||||
|
||||
# Lines that are *only* whitespace are not considered to have leading whitespace
|
||||
buffer.insert([10, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstNonWhitespaceIndex).toBe null
|
||||
|
||||
it "assigns ::firstTrailingWhitespaceIndex on tokens that have trailing whitespace", ->
|
||||
buffer.insert([0, Infinity], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[11].firstTrailingWhitespaceIndex).toBe null
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[12].firstTrailingWhitespaceIndex).toBe 0
|
||||
|
||||
# The last token *has* trailing whitespace, but isn't entirely whitespace
|
||||
buffer.setTextInRange([[2, 39], [2, 40]], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[14].firstTrailingWhitespaceIndex).toBe null
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[15].firstTrailingWhitespaceIndex).toBe 6
|
||||
|
||||
# Lines that are *only* whitespace are considered to have trailing whitespace
|
||||
buffer.insert([10, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstTrailingWhitespaceIndex).toBe 0
|
||||
|
||||
it "only marks trailing whitespace on the last segment of a soft-wrapped line", ->
|
||||
buffer.insert([0, Infinity], ' ')
|
||||
tokenizedLine = tokenizedBuffer.tokenizedLineForRow(0)
|
||||
[segment1, segment2] = tokenizedLine.softWrapAt(16)
|
||||
expect(segment1.tokens[5].value).toBe ' '
|
||||
expect(segment1.tokens[5].firstTrailingWhitespaceIndex).toBe null
|
||||
expect(segment2.tokens[6].value).toBe ' '
|
||||
expect(segment2.tokens[6].firstTrailingWhitespaceIndex).toBe 0
|
||||
|
||||
it "sets leading and trailing whitespace correctly on a line with invisible characters that is copied", ->
|
||||
buffer.setText(" \t a line with tabs\tand \tspaces \t ")
|
||||
|
||||
atom.config.set("editor.showInvisibles", true)
|
||||
atom.config.set("editor.invisibles", space: 'S', tab: 'T')
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
line = tokenizedBuffer.tokenizedLineForRow(0).copy()
|
||||
expect(line.tokens[0].firstNonWhitespaceIndex).toBe 2
|
||||
expect(line.tokens[line.tokens.length - 1].firstTrailingWhitespaceIndex).toBe 0
|
||||
|
||||
describe ".indentLevel on tokenized lines", ->
|
||||
describe ".indentLevelForRow(row)", ->
|
||||
beforeEach ->
|
||||
buffer = atom.project.bufferForPathSync('sample.js')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
@@ -817,43 +537,43 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
describe "when the line is non-empty", ->
|
||||
it "has an indent level based on the leading whitespace on the line", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(0).indentLevel).toBe 0
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 1
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(0)).toBe 0
|
||||
expect(tokenizedBuffer.indentLevelForRow(1)).toBe 1
|
||||
expect(tokenizedBuffer.indentLevelForRow(2)).toBe 2
|
||||
buffer.insert([2, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2.5
|
||||
expect(tokenizedBuffer.indentLevelForRow(2)).toBe 2.5
|
||||
|
||||
describe "when the line is empty", ->
|
||||
it "assumes the indentation level of the first non-empty line below or above if one exists", ->
|
||||
buffer.insert([12, 0], ' ')
|
||||
buffer.insert([12, Infinity], '\n\n')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(14).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(13)).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(14)).toBe 2
|
||||
|
||||
buffer.insert([1, Infinity], '\n\n')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(3).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(2)).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(3)).toBe 2
|
||||
|
||||
buffer.setText('\n\n\n')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 0
|
||||
expect(tokenizedBuffer.indentLevelForRow(1)).toBe 0
|
||||
|
||||
describe "when the changed lines are surrounded by whitespace-only lines", ->
|
||||
it "updates the indentLevel of empty lines that precede the change", ->
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 0
|
||||
expect(tokenizedBuffer.indentLevelForRow(12)).toBe 0
|
||||
|
||||
buffer.insert([12, 0], '\n')
|
||||
buffer.insert([13, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 1
|
||||
expect(tokenizedBuffer.indentLevelForRow(12)).toBe 1
|
||||
|
||||
it "updates empty line indent guides when the empty line is the last line", ->
|
||||
buffer.insert([12, 2], '\n')
|
||||
|
||||
# The newline and the tab need to be in two different operations to surface the bug
|
||||
buffer.insert([12, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 1
|
||||
expect(tokenizedBuffer.indentLevelForRow(13)).toBe 1
|
||||
|
||||
buffer.insert([12, 0], ' ')
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(13)).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(14)).not.toBeDefined()
|
||||
|
||||
it "updates the indentLevel of empty lines surrounding a change that inserts lines", ->
|
||||
@@ -861,24 +581,24 @@ describe "TokenizedBuffer", ->
|
||||
buffer.insert([7, 0], '\n\n')
|
||||
buffer.insert([5, 0], '\n\n')
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 3
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(5)).toBe 3
|
||||
expect(tokenizedBuffer.indentLevelForRow(6)).toBe 3
|
||||
expect(tokenizedBuffer.indentLevelForRow(9)).toBe 3
|
||||
expect(tokenizedBuffer.indentLevelForRow(10)).toBe 3
|
||||
expect(tokenizedBuffer.indentLevelForRow(11)).toBe 2
|
||||
|
||||
tokenizedBuffer.onDidChange changeHandler = jasmine.createSpy('changeHandler')
|
||||
|
||||
buffer.setTextInRange([[7, 0], [8, 65]], ' one\n two\n three\n four')
|
||||
|
||||
delete changeHandler.argsForCall[0][0].bufferChange
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: 2)
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, delta: 2)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 4
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(5)).toBe 4
|
||||
expect(tokenizedBuffer.indentLevelForRow(6)).toBe 4
|
||||
expect(tokenizedBuffer.indentLevelForRow(11)).toBe 4
|
||||
expect(tokenizedBuffer.indentLevelForRow(12)).toBe 4
|
||||
expect(tokenizedBuffer.indentLevelForRow(13)).toBe 2
|
||||
|
||||
it "updates the indentLevel of empty lines surrounding a change that removes lines", ->
|
||||
# create some new lines
|
||||
@@ -890,14 +610,14 @@ describe "TokenizedBuffer", ->
|
||||
buffer.setTextInRange([[7, 0], [8, 65]], ' ok')
|
||||
|
||||
delete changeHandler.argsForCall[0][0].bufferChange
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: -1)
|
||||
expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, delta: -1)
|
||||
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(7).indentLevel).toBe 2 # new text
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(8).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2
|
||||
expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # }
|
||||
expect(tokenizedBuffer.indentLevelForRow(5)).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(6)).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(7)).toBe 2 # new text
|
||||
expect(tokenizedBuffer.indentLevelForRow(8)).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(9)).toBe 2
|
||||
expect(tokenizedBuffer.indentLevelForRow(10)).toBe 2 # }
|
||||
|
||||
describe "::isFoldableAtRow(row)", ->
|
||||
changes = null
|
||||
@@ -1049,3 +769,107 @@ describe "TokenizedBuffer", ->
|
||||
|
||||
runs ->
|
||||
expect(coffeeCalled).toBe true
|
||||
|
||||
describe "text decoration layer API", ->
|
||||
describe "iterator", ->
|
||||
it "iterates over the syntactic scope boundaries", ->
|
||||
buffer = new TextBuffer(text: "var foo = 1 /*\nhello*/var bar = 2\n")
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".js"))
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
iterator = tokenizedBuffer.buildIterator()
|
||||
iterator.seek(Point(0, 0))
|
||||
|
||||
expectedBoundaries = [
|
||||
{position: Point(0, 0), closeTags: [], openTags: ["source.js", "storage.type.var.js"]}
|
||||
{position: Point(0, 3), closeTags: ["storage.type.var.js"], openTags: []}
|
||||
{position: Point(0, 8), closeTags: [], openTags: ["keyword.operator.assignment.js"]}
|
||||
{position: Point(0, 9), closeTags: ["keyword.operator.assignment.js"], openTags: []}
|
||||
{position: Point(0, 10), closeTags: [], openTags: ["constant.numeric.decimal.js"]}
|
||||
{position: Point(0, 11), closeTags: ["constant.numeric.decimal.js"], openTags: []}
|
||||
{position: Point(0, 12), closeTags: [], openTags: ["comment.block.js", "punctuation.definition.comment.js"]}
|
||||
{position: Point(0, 14), closeTags: ["punctuation.definition.comment.js"], openTags: []}
|
||||
{position: Point(1, 5), closeTags: [], openTags: ["punctuation.definition.comment.js"]}
|
||||
{position: Point(1, 7), closeTags: ["punctuation.definition.comment.js", "comment.block.js"], openTags: ["storage.type.var.js"]}
|
||||
{position: Point(1, 10), closeTags: ["storage.type.var.js"], openTags: []}
|
||||
{position: Point(1, 15), closeTags: [], openTags: ["keyword.operator.assignment.js"]}
|
||||
{position: Point(1, 16), closeTags: ["keyword.operator.assignment.js"], openTags: []}
|
||||
{position: Point(1, 17), closeTags: [], openTags: ["constant.numeric.decimal.js"]}
|
||||
{position: Point(1, 18), closeTags: ["constant.numeric.decimal.js"], openTags: []}
|
||||
]
|
||||
|
||||
loop
|
||||
boundary = {
|
||||
position: iterator.getPosition(),
|
||||
closeTags: iterator.getCloseTags(),
|
||||
openTags: iterator.getOpenTags()
|
||||
}
|
||||
|
||||
expect(boundary).toEqual(expectedBoundaries.shift())
|
||||
break unless iterator.moveToSuccessor()
|
||||
|
||||
expect(iterator.seek(Point(0, 1))).toEqual(["source.js", "storage.type.var.js"])
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 3))
|
||||
expect(iterator.seek(Point(0, 8))).toEqual(["source.js"])
|
||||
expect(iterator.getPosition()).toEqual(Point(0, 8))
|
||||
expect(iterator.seek(Point(1, 0))).toEqual(["source.js", "comment.block.js"])
|
||||
expect(iterator.getPosition()).toEqual(Point(1, 5))
|
||||
expect(iterator.seek(Point(1, 18))).toEqual(["source.js", "constant.numeric.decimal.js"])
|
||||
expect(iterator.getPosition()).toEqual(Point(1, 18))
|
||||
|
||||
expect(iterator.seek(Point(2, 0))).toEqual(["source.js"])
|
||||
iterator.moveToSuccessor() # ensure we don't infinitely loop (regression test)
|
||||
|
||||
it "does not report columns beyond the length of the line", ->
|
||||
waitsForPromise ->
|
||||
atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
runs ->
|
||||
buffer = new TextBuffer(text: "# hello\n# world")
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
tokenizedBuffer.setGrammar(atom.grammars.selectGrammar(".coffee"))
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
iterator = tokenizedBuffer.buildIterator()
|
||||
iterator.seek(Point(0, 0))
|
||||
iterator.moveToSuccessor()
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition().column).toBe(7)
|
||||
|
||||
iterator.moveToSuccessor()
|
||||
expect(iterator.getPosition().column).toBe(0)
|
||||
|
||||
iterator.seek(Point(0, 7))
|
||||
expect(iterator.getPosition().column).toBe(7)
|
||||
|
||||
iterator.seek(Point(0, 8))
|
||||
expect(iterator.getPosition().column).toBe(7)
|
||||
|
||||
it "correctly terminates scopes at the beginning of the line (regression)", ->
|
||||
grammar = atom.grammars.createGrammar('test', {
|
||||
'scopeName': 'text.broken'
|
||||
'name': 'Broken grammar'
|
||||
'patterns': [
|
||||
{'begin': 'start', 'end': '(?=end)', 'name': 'blue.broken'},
|
||||
{'match': '.', 'name': 'yellow.broken'}
|
||||
]
|
||||
})
|
||||
|
||||
buffer = new TextBuffer(text: 'start x\nend x\nx')
|
||||
tokenizedBuffer = new TokenizedBuffer({
|
||||
buffer, config: atom.config, grammarRegistry: atom.grammars, packageManager: atom.packages, assert: atom.assert
|
||||
})
|
||||
tokenizedBuffer.setGrammar(grammar)
|
||||
fullyTokenize(tokenizedBuffer)
|
||||
|
||||
iterator = tokenizedBuffer.buildIterator()
|
||||
iterator.seek(Point(1, 0))
|
||||
|
||||
expect(iterator.getPosition()).toEqual([1, 0])
|
||||
expect(iterator.getCloseTags()).toEqual ['blue.broken']
|
||||
expect(iterator.getOpenTags()).toEqual ['yellow.broken']
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
describe "TokenizedLine", ->
|
||||
editor = null
|
||||
|
||||
beforeEach ->
|
||||
waitsForPromise -> atom.packages.activatePackage('language-coffee-script')
|
||||
|
||||
describe "::isOnlyWhitespace()", ->
|
||||
beforeEach ->
|
||||
waitsForPromise ->
|
||||
atom.workspace.open('coffee.coffee').then (o) -> editor = o
|
||||
|
||||
it "returns true when the line is only whitespace", ->
|
||||
expect(editor.tokenizedLineForScreenRow(3).isOnlyWhitespace()).toBe true
|
||||
expect(editor.tokenizedLineForScreenRow(7).isOnlyWhitespace()).toBe true
|
||||
expect(editor.tokenizedLineForScreenRow(23).isOnlyWhitespace()).toBe true
|
||||
|
||||
it "returns false when the line is not only whitespace", ->
|
||||
expect(editor.tokenizedLineForScreenRow(0).isOnlyWhitespace()).toBe false
|
||||
expect(editor.tokenizedLineForScreenRow(2).isOnlyWhitespace()).toBe false
|
||||
@@ -429,7 +429,7 @@ describe "Workspace", ->
|
||||
workspace.open('sample.js').then (e) -> editor = e
|
||||
|
||||
runs ->
|
||||
expect(editor.displayBuffer.largeFileMode).toBe true
|
||||
expect(editor.largeFileMode).toBe true
|
||||
|
||||
describe "when the file is over 20MB", ->
|
||||
it "prompts the user to make sure they want to open a file this big", ->
|
||||
@@ -454,7 +454,7 @@ describe "Workspace", ->
|
||||
|
||||
runs ->
|
||||
expect(atom.applicationDelegate.confirm).toHaveBeenCalled()
|
||||
expect(editor.displayBuffer.largeFileMode).toBe true
|
||||
expect(editor.largeFileMode).toBe true
|
||||
|
||||
describe "when passed a path that matches a custom opener", ->
|
||||
it "returns the resource returned by the custom opener", ->
|
||||
|
||||
Reference in New Issue
Block a user