Mailspring/spec/text-editor-spec.coffee
Ben Gotow 1e8fd46342 fix(drafts): Various improvements and fixes to drafts, draft state management
Summary:
This diff contains a few major changes:

1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario:
   - View thread with draft, edit draft
   - Move to another thread
   - Move back to thread with draft
   - Move to another thread. Notice that one or more messages from thread with draft are still there.

There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great.

2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here:

   - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI.

   - Previously, when you added a contact to To/CC/BCC, this happened:

     <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates

Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state.

To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source!

This diff includes a few minor fixes as well:

1. Draft.state is gone—use Message.object = draft instead
2. String model attributes should never be null
3. Pre-send checks that can cancel draft send
4. Put the entire curl history and task queue into feedback reports
5. Cache localIds for extra speed
6. Move us up to latest React

Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet

Reviewers: evan

Reviewed By: evan

Differential Revision: https://review.inboxapp.com/D1125
2015-02-03 16:24:31 -08:00

4020 lines
173 KiB
CoffeeScript

clipboard = require 'clipboard'
TextEditor = require '../src/text-editor'
describe "TextEditor", ->
[buffer, editor, lineLengths] = []
convertToHardTabs = (buffer) ->
buffer.setText(buffer.getText().replace(/[ ]{2}/g, "\t"))
beforeEach ->
waitsForPromise ->
atom.project.open('sample.js', autoIndent: false).then (o) -> editor = o
runs ->
buffer = editor.buffer
lineLengths = buffer.getLines().map (line) -> line.length
waitsForPromise ->
atom.packages.activatePackage('language-javascript')
describe "when the editor is deserialized", ->
it "restores selections and folds based on markers in the buffer", ->
editor.setSelectedBufferRange([[1, 2], [3, 4]])
editor.addSelectionForBufferRange([[5, 6], [7, 5]], reversed: true)
editor.foldBufferRow(4)
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
editor2 = editor.testSerialization()
expect(editor2.id).toBe editor.id
expect(editor2.getBuffer().getPath()).toBe editor.getBuffer().getPath()
expect(editor2.getSelectedBufferRanges()).toEqual [[[1, 2], [3, 4]], [[5, 6], [7, 5]]]
expect(editor2.getSelections()[1].isReversed()).toBeTruthy()
expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy()
editor2.destroy()
it "preserves the invisibles setting", ->
atom.config.set('editor.showInvisibles', true)
previousInvisibles = editor.displayBuffer.invisibles
editor2 = editor.testSerialization()
expect(editor2.displayBuffer.invisibles).toEqual previousInvisibles
expect(editor2.displayBuffer.tokenizedBuffer.invisibles).toEqual previousInvisibles
it "updates invisibles if the settings have changed between serialization and deserialization", ->
atom.config.set('editor.showInvisibles', true)
previousInvisibles = editor.displayBuffer.invisibles
state = editor.serialize()
atom.config.set('editor.invisibles', eol: '?')
editor2 = TextEditor.deserialize(state)
expect(editor2.displayBuffer.invisibles.eol).toBe '?'
expect(editor2.displayBuffer.tokenizedBuffer.invisibles.eol).toBe '?'
describe "when the editor is constructed with an initialLine option", ->
it "positions the cursor on the specified line", ->
editor = null
waitsForPromise ->
atom.workspace.open('sample.less', initialLine: 5).then (o) -> editor = o
runs ->
expect(editor.getLastCursor().getBufferPosition().row).toEqual 5
expect(editor.getLastCursor().getBufferPosition().column).toEqual 0
describe "when the editor is constructed with an initialColumn option", ->
it "positions the cursor on the specified column", ->
editor = null
waitsForPromise ->
atom.workspace.open('sample.less', initialColumn: 8).then (o) -> editor = o
runs ->
expect(editor.getLastCursor().getBufferPosition().row).toEqual 0
expect(editor.getLastCursor().getBufferPosition().column).toEqual 8
describe "when the editor is reopened with an initialLine option", ->
it "positions the cursor on the specified line", ->
editor = null
waitsForPromise ->
atom.workspace.open('sample.less', initialLine: 5).then (o) -> editor = o
waitsForPromise ->
atom.workspace.open('sample.less', initialLine: 4).then (o) -> editor = o
runs ->
expect(editor.getLastCursor().getBufferPosition().row).toEqual 4
expect(editor.getLastCursor().getBufferPosition().column).toEqual 0
describe "when the editor is reopened with an initialColumn option", ->
it "positions the cursor on the specified column", ->
editor = null
waitsForPromise ->
atom.workspace.open('sample.less', initialColumn: 8).then (o) -> editor = o
waitsForPromise ->
atom.workspace.open('sample.less', initialColumn: 7).then (o) -> editor = o
runs ->
expect(editor.getLastCursor().getBufferPosition().row).toEqual 0
expect(editor.getLastCursor().getBufferPosition().column).toEqual 7
describe ".copy()", ->
it "returns a different edit session with the same initial state", ->
editor.setSelectedBufferRange([[1, 2], [3, 4]])
editor.addSelectionForBufferRange([[5, 6], [7, 8]], reversed: true)
editor.foldBufferRow(4)
expect(editor.isFoldedAtBufferRow(4)).toBeTruthy()
editor2 = editor.copy()
expect(editor2.id).not.toBe editor.id
expect(editor2.getSelectedBufferRanges()).toEqual editor.getSelectedBufferRanges()
expect(editor2.getSelections()[1].isReversed()).toBeTruthy()
expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy()
# editor2 can now diverge from its origin edit session
editor2.getLastSelection().setBufferRange([[2, 1], [4, 3]])
expect(editor2.getSelectedBufferRanges()).not.toEqual editor.getSelectedBufferRanges()
editor2.unfoldBufferRow(4)
expect(editor2.isFoldedAtBufferRow(4)).not.toBe editor.isFoldedAtBufferRow(4)
describe "config defaults", ->
it "uses the `editor.tabLength`, `editor.softWrap`, and `editor.softTabs`, and `core.fileEncoding` config values", ->
editor1 = null
editor2 = null
atom.config.set('editor.tabLength', 4)
atom.config.set('editor.softWrap', true)
atom.config.set('editor.softTabs', false)
atom.config.set('core.fileEncoding', 'utf16le')
waitsForPromise ->
atom.workspace.open('a').then (o) -> editor1 = o
runs ->
expect(editor1.getTabLength()).toBe 4
expect(editor1.isSoftWrapped()).toBe true
expect(editor1.getSoftTabs()).toBe false
expect(editor1.getEncoding()).toBe 'utf16le'
atom.config.set('editor.tabLength', 8)
atom.config.set('editor.softWrap', false)
atom.config.set('editor.softTabs', true)
atom.config.set('core.fileEncoding', 'macroman')
waitsForPromise ->
atom.workspace.open('b').then (o) -> editor2 = o
runs ->
expect(editor2.getTabLength()).toBe 8
expect(editor2.isSoftWrapped()).toBe false
expect(editor2.getSoftTabs()).toBe true
expect(editor2.getEncoding()).toBe 'macroman'
it "uses scoped `core.fileEncoding` values", ->
editor1 = null
editor2 = null
atom.config.set('core.fileEncoding', 'utf16le')
atom.config.set('core.fileEncoding', 'macroman', scopeSelector: '.js')
waitsForPromise ->
atom.workspace.open('a').then (o) -> editor1 = o
runs ->
expect(editor1.getEncoding()).toBe 'utf16le'
waitsForPromise ->
atom.workspace.open('test.js').then (o) -> editor2 = o
runs ->
expect(editor2.getEncoding()).toBe 'macroman'
describe "title", ->
describe ".getTitle()", ->
it "uses the basename of the buffer's path as its title, or 'untitled' if the path is undefined", ->
expect(editor.getTitle()).toBe 'sample.js'
buffer.setPath(undefined)
expect(editor.getTitle()).toBe 'untitled'
describe ".getLongTitle()", ->
it "appends the name of the containing directory to the basename of the file", ->
expect(editor.getLongTitle()).toBe 'sample.js - fixtures'
buffer.setPath(undefined)
expect(editor.getLongTitle()).toBe 'untitled'
it "notifies ::onDidChangeTitle observers when the underlying buffer path changes", ->
observed = []
editor.onDidChangeTitle (title) -> observed.push(title)
buffer.setPath('/foo/bar/baz.txt')
buffer.setPath(undefined)
expect(observed).toEqual ['baz.txt', 'untitled']
describe "path", ->
it "notifies ::onDidChangePath observers when the underlying buffer path changes", ->
observed = []
editor.onDidChangePath (filePath) -> observed.push(filePath)
buffer.setPath(__filename)
buffer.setPath(undefined)
expect(observed).toEqual [__filename, undefined]
describe "encoding", ->
it "notifies ::onDidChangeEncoding observers when the editor encoding changes", ->
observed = []
editor.onDidChangeEncoding (encoding) -> observed.push(encoding)
editor.setEncoding('utf16le')
editor.setEncoding('utf16le')
editor.setEncoding('utf16be')
editor.setEncoding()
editor.setEncoding()
expect(observed).toEqual ['utf16le', 'utf16be', 'utf8']
describe "cursor", ->
describe ".getLastCursor()", ->
it "returns the most recently created cursor", ->
editor.addCursorAtScreenPosition([1, 0])
lastCursor = editor.addCursorAtScreenPosition([2, 0])
expect(editor.getLastCursor()).toBe lastCursor
describe "when the cursor moves", ->
it "clears a goal column established by vertical movement", ->
editor.setText('b')
editor.setCursorBufferPosition([0,0])
editor.insertNewline()
editor.moveUp()
editor.insertText('a')
editor.moveDown()
expect(editor.getCursorBufferPosition()).toEqual [1, 1]
it "emits an event with the old position, new position, and the cursor that moved", ->
cursorCallback = jasmine.createSpy('cursor-changed-position')
editorCallback = jasmine.createSpy('editor-changed-cursor-position')
editor.getLastCursor().onDidChangePosition(cursorCallback)
editor.onDidChangeCursorPosition(editorCallback)
editor.setCursorBufferPosition([2, 4])
expect(editorCallback).toHaveBeenCalled()
expect(cursorCallback).toHaveBeenCalled()
eventObject = editorCallback.mostRecentCall.args[0]
expect(cursorCallback.mostRecentCall.args[0]).toEqual(eventObject)
expect(eventObject.oldBufferPosition).toEqual [0, 0]
expect(eventObject.oldScreenPosition).toEqual [0, 0]
expect(eventObject.newBufferPosition).toEqual [2, 4]
expect(eventObject.newScreenPosition).toEqual [2, 4]
expect(eventObject.cursor).toBe editor.getLastCursor()
describe ".setCursorScreenPosition(screenPosition)", ->
it "clears a goal column established by vertical movement", ->
# set a goal column by moving down
editor.setCursorScreenPosition(row: 3, column: lineLengths[3])
editor.moveDown()
expect(editor.getCursorScreenPosition().column).not.toBe 6
# clear the goal column by explicitly setting the cursor position
editor.setCursorScreenPosition([4,6])
expect(editor.getCursorScreenPosition().column).toBe 6
editor.moveDown()
expect(editor.getCursorScreenPosition().column).toBe 6
it "merges multiple cursors", ->
editor.setCursorScreenPosition([0, 0])
editor.addCursorAtScreenPosition([0, 1])
[cursor1, cursor2] = editor.getCursors()
editor.setCursorScreenPosition([4, 7])
expect(editor.getCursors().length).toBe 1
expect(editor.getCursors()).toEqual [cursor1]
expect(editor.getCursorScreenPosition()).toEqual [4, 7]
describe "when soft-wrap is enabled and code is folded", ->
beforeEach ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(50)
editor.createFold(2, 3)
it "positions the cursor at the buffer position that corresponds to the given screen position", ->
editor.setCursorScreenPosition([9, 0])
expect(editor.getCursorBufferPosition()).toEqual [8, 11]
describe ".moveUp()", ->
it "moves the cursor up", ->
editor.setCursorScreenPosition([2, 2])
editor.moveUp()
expect(editor.getCursorScreenPosition()).toEqual [1, 2]
it "retains the goal column across lines of differing length", ->
expect(lineLengths[6]).toBeGreaterThan(32)
editor.setCursorScreenPosition(row: 6, column: 32)
editor.moveUp()
expect(editor.getCursorScreenPosition().column).toBe lineLengths[5]
editor.moveUp()
expect(editor.getCursorScreenPosition().column).toBe lineLengths[4]
editor.moveUp()
expect(editor.getCursorScreenPosition().column).toBe 32
describe "when the cursor is on the first line", ->
it "moves the cursor to the beginning of the line, but retains the goal column", ->
editor.setCursorScreenPosition([0, 4])
editor.moveUp()
expect(editor.getCursorScreenPosition()).toEqual([0, 0])
editor.moveDown()
expect(editor.getCursorScreenPosition()).toEqual([1, 4])
describe "when there is a selection", ->
beforeEach ->
editor.setSelectedBufferRange([[4, 9],[5, 10]])
it "moves above the selection", ->
cursor = editor.getLastCursor()
editor.moveUp()
expect(cursor.getBufferPosition()).toEqual [3, 9]
it "merges cursors when they overlap", ->
editor.addCursorAtScreenPosition([1, 0])
[cursor1, cursor2] = editor.getCursors()
editor.moveUp()
expect(editor.getCursors()).toEqual [cursor1]
expect(cursor1.getBufferPosition()).toEqual [0,0]
describe ".moveDown()", ->
it "moves the cursor down", ->
editor.setCursorScreenPosition([2, 2])
editor.moveDown()
expect(editor.getCursorScreenPosition()).toEqual [3, 2]
it "retains the goal column across lines of differing length", ->
editor.setCursorScreenPosition(row: 3, column: lineLengths[3])
editor.moveDown()
expect(editor.getCursorScreenPosition().column).toBe lineLengths[4]
editor.moveDown()
expect(editor.getCursorScreenPosition().column).toBe lineLengths[5]
editor.moveDown()
expect(editor.getCursorScreenPosition().column).toBe lineLengths[3]
describe "when the cursor is on the last line", ->
it "moves the cursor to the end of line, but retains the goal column when moving back up", ->
lastLineIndex = buffer.getLines().length - 1
lastLine = buffer.lineForRow(lastLineIndex)
expect(lastLine.length).toBeGreaterThan(0)
editor.setCursorScreenPosition(row: lastLineIndex, column: editor.getTabLength())
editor.moveDown()
expect(editor.getCursorScreenPosition()).toEqual(row: lastLineIndex, column: lastLine.length)
editor.moveUp()
expect(editor.getCursorScreenPosition().column).toBe editor.getTabLength()
it "retains a goal column of 0 when moving back up", ->
lastLineIndex = buffer.getLines().length - 1
lastLine = buffer.lineForRow(lastLineIndex)
expect(lastLine.length).toBeGreaterThan(0)
editor.setCursorScreenPosition(row: lastLineIndex, column: 0)
editor.moveDown()
editor.moveUp()
expect(editor.getCursorScreenPosition().column).toBe 0
describe "when there is a selection", ->
beforeEach ->
editor.setSelectedBufferRange([[4, 9],[5, 10]])
it "moves below the selection", ->
cursor = editor.getLastCursor()
editor.moveDown()
expect(cursor.getBufferPosition()).toEqual [6, 10]
it "merges cursors when they overlap", ->
editor.setCursorScreenPosition([12, 2])
editor.addCursorAtScreenPosition([11, 2])
[cursor1, cursor2] = editor.getCursors()
editor.moveDown()
expect(editor.getCursors()).toEqual [cursor1]
expect(cursor1.getBufferPosition()).toEqual [12,2]
describe ".moveLeft()", ->
it "moves the cursor by one column to the left", ->
editor.setCursorScreenPosition([1, 8])
editor.moveLeft()
expect(editor.getCursorScreenPosition()).toEqual [1, 7]
it "moves the cursor by n columns to the left", ->
editor.setCursorScreenPosition([1, 8])
editor.moveLeft(4)
expect(editor.getCursorScreenPosition()).toEqual [1, 4]
it "moves the cursor by two rows up when the columnCount is longer than an entire line", ->
editor.setCursorScreenPosition([2, 2])
editor.moveLeft(34)
expect(editor.getCursorScreenPosition()).toEqual [0, 29]
it "moves the cursor to the beginning columnCount is longer than the position in the buffer", ->
editor.setCursorScreenPosition([1, 0])
editor.moveLeft(100)
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
describe "when the cursor is in the first column", ->
describe "when there is a previous line", ->
it "wraps to the end of the previous line", ->
editor.setCursorScreenPosition(row: 1, column: 0)
editor.moveLeft()
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: buffer.lineForRow(0).length)
it "moves the cursor by one row up and n columns to the left", ->
editor.setCursorScreenPosition([1, 0])
editor.moveLeft(4)
expect(editor.getCursorScreenPosition()).toEqual [0, 26]
describe "when the next line is empty", ->
it "wraps to the beginning of the previous line", ->
editor.setCursorScreenPosition([11, 0])
editor.moveLeft()
expect(editor.getCursorScreenPosition()).toEqual [10, 0]
describe "when the cursor is on the first line", ->
it "remains in the same position (0,0)", ->
editor.setCursorScreenPosition(row: 0, column: 0)
editor.moveLeft()
expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0)
it "remains in the same position (0,0) when columnCount is specified", ->
editor.setCursorScreenPosition([0, 0])
editor.moveLeft(4)
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
describe "when softTabs is enabled and the cursor is preceded by leading whitespace", ->
it "skips tabLength worth of whitespace at a time", ->
editor.setCursorBufferPosition([5, 6])
editor.moveLeft()
expect(editor.getCursorBufferPosition()).toEqual [5, 4]
describe "when there is a selection", ->
beforeEach ->
editor.setSelectedBufferRange([[5, 22],[5, 27]])
it "moves to the left of the selection", ->
cursor = editor.getLastCursor()
editor.moveLeft()
expect(cursor.getBufferPosition()).toEqual [5, 22]
editor.moveLeft()
expect(cursor.getBufferPosition()).toEqual [5, 21]
it "merges cursors when they overlap", ->
editor.setCursorScreenPosition([0, 0])
editor.addCursorAtScreenPosition([0, 1])
[cursor1, cursor2] = editor.getCursors()
editor.moveLeft()
expect(editor.getCursors()).toEqual [cursor1]
expect(cursor1.getBufferPosition()).toEqual [0,0]
describe ".moveRight()", ->
it "moves the cursor by one column to the right", ->
editor.setCursorScreenPosition([3, 3])
editor.moveRight()
expect(editor.getCursorScreenPosition()).toEqual [3, 4]
it "moves the cursor by n columns to the right", ->
editor.setCursorScreenPosition([3, 7])
editor.moveRight(4)
expect(editor.getCursorScreenPosition()).toEqual [3, 11]
it "moves the cursor by two rows down when the columnCount is longer than an entire line", ->
editor.setCursorScreenPosition([0, 29])
editor.moveRight(34)
expect(editor.getCursorScreenPosition()).toEqual [2, 2]
it "moves the cursor to the end of the buffer when columnCount is longer than the number of characters following the cursor position", ->
editor.setCursorScreenPosition([11, 5])
editor.moveRight(100)
expect(editor.getCursorScreenPosition()).toEqual [12, 2]
describe "when the cursor is on the last column of a line", ->
describe "when there is a subsequent line", ->
it "wraps to the beginning of the next line", ->
editor.setCursorScreenPosition([0, buffer.lineForRow(0).length])
editor.moveRight()
expect(editor.getCursorScreenPosition()).toEqual [1, 0]
it "moves the cursor by one row down and n columns to the right", ->
editor.setCursorScreenPosition([0, buffer.lineForRow(0).length])
editor.moveRight(4)
expect(editor.getCursorScreenPosition()).toEqual [1, 3]
describe "when the next line is empty", ->
it "wraps to the beginning of the next line", ->
editor.setCursorScreenPosition([9, 4])
editor.moveRight()
expect(editor.getCursorScreenPosition()).toEqual [10, 0]
describe "when the cursor is on the last line", ->
it "remains in the same position", ->
lastLineIndex = buffer.getLines().length - 1
lastLine = buffer.lineForRow(lastLineIndex)
expect(lastLine.length).toBeGreaterThan(0)
lastPosition = { row: lastLineIndex, column: lastLine.length }
editor.setCursorScreenPosition(lastPosition)
editor.moveRight()
expect(editor.getCursorScreenPosition()).toEqual(lastPosition)
describe "when there is a selection", ->
beforeEach ->
editor.setSelectedBufferRange([[5, 22],[5, 27]])
it "moves to the left of the selection", ->
cursor = editor.getLastCursor()
editor.moveRight()
expect(cursor.getBufferPosition()).toEqual [5, 27]
editor.moveRight()
expect(cursor.getBufferPosition()).toEqual [5, 28]
it "merges cursors when they overlap", ->
editor.setCursorScreenPosition([12, 2])
editor.addCursorAtScreenPosition([12, 1])
[cursor1, cursor2] = editor.getCursors()
editor.moveRight()
expect(editor.getCursors()).toEqual [cursor1]
expect(cursor1.getBufferPosition()).toEqual [12,2]
describe ".moveToTop()", ->
it "moves the cursor to the top of the buffer", ->
editor.setCursorScreenPosition [11,1]
editor.addCursorAtScreenPosition [12,0]
editor.moveToTop()
expect(editor.getCursors().length).toBe 1
expect(editor.getCursorBufferPosition()).toEqual [0,0]
describe ".moveToBottom()", ->
it "moves the cusor to the bottom of the buffer", ->
editor.setCursorScreenPosition [0,0]
editor.addCursorAtScreenPosition [1,0]
editor.moveToBottom()
expect(editor.getCursors().length).toBe 1
expect(editor.getCursorBufferPosition()).toEqual [12,2]
describe ".moveToBeginningOfScreenLine()", ->
describe "when soft wrap is on", ->
it "moves cursor to the beginning of the screen line", ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(10)
editor.setCursorScreenPosition([1, 2])
editor.moveToBeginningOfScreenLine()
cursor = editor.getLastCursor()
expect(cursor.getScreenPosition()).toEqual [1, 0]
describe "when soft wrap is off", ->
it "moves cursor to the beginning of then line", ->
editor.setCursorScreenPosition [0,5]
editor.addCursorAtScreenPosition [1,7]
editor.moveToBeginningOfScreenLine()
expect(editor.getCursors().length).toBe 2
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [0,0]
expect(cursor2.getBufferPosition()).toEqual [1,0]
describe ".moveToEndOfScreenLine()", ->
describe "when soft wrap is on", ->
it "moves cursor to the beginning of the screen line", ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(10)
editor.setCursorScreenPosition([1, 2])
editor.moveToEndOfScreenLine()
cursor = editor.getLastCursor()
expect(cursor.getScreenPosition()).toEqual [1, 9]
describe "when soft wrap is off", ->
it "moves cursor to the end of line", ->
editor.setCursorScreenPosition [0,0]
editor.addCursorAtScreenPosition [1,0]
editor.moveToEndOfScreenLine()
expect(editor.getCursors().length).toBe 2
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [0,29]
expect(cursor2.getBufferPosition()).toEqual [1,30]
describe ".moveToBeginningOfLine()", ->
it "moves cursor to the beginning of the buffer line", ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(10)
editor.setCursorScreenPosition([1, 2])
editor.moveToBeginningOfLine()
cursor = editor.getLastCursor()
expect(cursor.getScreenPosition()).toEqual [0, 0]
describe ".moveToEndOfLine()", ->
it "moves cursor to the end of the buffer line", ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(10)
editor.setCursorScreenPosition([0, 2])
editor.moveToEndOfLine()
cursor = editor.getLastCursor()
expect(cursor.getScreenPosition()).toEqual [3, 4]
describe ".moveToFirstCharacterOfLine()", ->
describe "when soft wrap is on", ->
it "moves to the first character of the current screen line or the beginning of the screen line if it's already on the first character", ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(10)
editor.setCursorScreenPosition [2,5]
editor.addCursorAtScreenPosition [8,7]
editor.moveToFirstCharacterOfLine()
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getScreenPosition()).toEqual [2,0]
expect(cursor2.getScreenPosition()).toEqual [8,4]
editor.moveToFirstCharacterOfLine()
expect(cursor1.getScreenPosition()).toEqual [2,0]
expect(cursor2.getScreenPosition()).toEqual [8,0]
describe "when soft wrap is off", ->
it "moves to the first character of the current line or the beginning of the line if it's already on the first character", ->
editor.setCursorScreenPosition [0,5]
editor.addCursorAtScreenPosition [1,7]
editor.moveToFirstCharacterOfLine()
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [0,0]
expect(cursor2.getBufferPosition()).toEqual [1,2]
editor.moveToFirstCharacterOfLine()
expect(cursor1.getBufferPosition()).toEqual [0,0]
expect(cursor2.getBufferPosition()).toEqual [1,0]
it "moves to the beginning of the line if it only contains whitespace ", ->
editor.setText("first\n \nthird")
editor.setCursorScreenPosition [1,2]
editor.moveToFirstCharacterOfLine()
cursor = editor.getLastCursor()
expect(cursor.getBufferPosition()).toEqual [1,0]
describe "when invisible characters are enabled with soft tabs", ->
it "moves to the first character of the current line without being confused by the invisible characters", ->
atom.config.set('editor.showInvisibles', true)
editor.setCursorScreenPosition [1,7]
editor.moveToFirstCharacterOfLine()
expect(editor.getCursorBufferPosition()).toEqual [1,2]
editor.moveToFirstCharacterOfLine()
expect(editor.getCursorBufferPosition()).toEqual [1,0]
describe "when invisible characters are enabled with hard tabs", ->
it "moves to the first character of the current line without being confused by the invisible characters", ->
atom.config.set('editor.showInvisibles', true)
buffer.setTextInRange([[1, 0], [1, Infinity]], '\t\t\ta', normalizeLineEndings: false)
editor.setCursorScreenPosition [1,7]
editor.moveToFirstCharacterOfLine()
expect(editor.getCursorBufferPosition()).toEqual [1,3]
editor.moveToFirstCharacterOfLine()
expect(editor.getCursorBufferPosition()).toEqual [1,0]
describe ".moveToBeginningOfWord()", ->
it "moves the cursor to the beginning of the word", ->
editor.setCursorBufferPosition [0, 8]
editor.addCursorAtBufferPosition [1, 12]
editor.addCursorAtBufferPosition [3, 0]
[cursor1, cursor2, cursor3] = editor.getCursors()
editor.moveToBeginningOfWord()
expect(cursor1.getBufferPosition()).toEqual [0, 4]
expect(cursor2.getBufferPosition()).toEqual [1, 11]
expect(cursor3.getBufferPosition()).toEqual [2, 39]
it "does not fail at position [0, 0]", ->
editor.setCursorBufferPosition([0, 0])
editor.moveToBeginningOfWord()
it "treats lines with only whitespace as a word", ->
editor.setCursorBufferPosition([11, 0])
editor.moveToBeginningOfWord()
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
it "works when the current line is blank", ->
editor.setCursorBufferPosition([10, 0])
editor.moveToBeginningOfWord()
expect(editor.getCursorBufferPosition()).toEqual [9, 2]
describe ".moveToPreviousWordBoundary()", ->
it "moves the cursor to the previous word boundary", ->
editor.setCursorBufferPosition [0, 8]
editor.addCursorAtBufferPosition [2, 0]
editor.addCursorAtBufferPosition [2, 4]
editor.addCursorAtBufferPosition [3, 14]
[cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
editor.moveToPreviousWordBoundary()
expect(cursor1.getBufferPosition()).toEqual [0, 4]
expect(cursor2.getBufferPosition()).toEqual [1, 30]
expect(cursor3.getBufferPosition()).toEqual [2, 0]
expect(cursor4.getBufferPosition()).toEqual [3, 13]
describe ".moveToNextWordBoundary()", ->
it "moves the cursor to the previous word boundary", ->
editor.setCursorBufferPosition [0, 8]
editor.addCursorAtBufferPosition [2, 40]
editor.addCursorAtBufferPosition [3, 0]
editor.addCursorAtBufferPosition [3, 30]
[cursor1, cursor2, cursor3, cursor4] = editor.getCursors()
editor.moveToNextWordBoundary()
expect(cursor1.getBufferPosition()).toEqual [0, 13]
expect(cursor2.getBufferPosition()).toEqual [3, 0]
expect(cursor3.getBufferPosition()).toEqual [3, 4]
expect(cursor4.getBufferPosition()).toEqual [3, 31]
describe ".moveToEndOfWord()", ->
it "moves the cursor to the end of the word", ->
editor.setCursorBufferPosition [0, 6]
editor.addCursorAtBufferPosition [1, 10]
editor.addCursorAtBufferPosition [2, 40]
[cursor1, cursor2, cursor3] = editor.getCursors()
editor.moveToEndOfWord()
expect(cursor1.getBufferPosition()).toEqual [0, 13]
expect(cursor2.getBufferPosition()).toEqual [1, 12]
expect(cursor3.getBufferPosition()).toEqual [3, 7]
it "does not blow up when there is no next word", ->
editor.setCursorBufferPosition [Infinity, Infinity]
endPosition = editor.getCursorBufferPosition()
editor.moveToEndOfWord()
expect(editor.getCursorBufferPosition()).toEqual endPosition
it "treats lines with only whitespace as a word", ->
editor.setCursorBufferPosition([9, 4])
editor.moveToEndOfWord()
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
it "works when the current line is blank", ->
editor.setCursorBufferPosition([10, 0])
editor.moveToEndOfWord()
expect(editor.getCursorBufferPosition()).toEqual [11, 8]
describe ".moveToBeginningOfNextWord()", ->
it "moves the cursor before the first character of the next word", ->
editor.setCursorBufferPosition [0,6]
editor.addCursorAtBufferPosition [1,11]
editor.addCursorAtBufferPosition [2,0]
[cursor1, cursor2, cursor3] = editor.getCursors()
editor.moveToBeginningOfNextWord()
expect(cursor1.getBufferPosition()).toEqual [0, 14]
expect(cursor2.getBufferPosition()).toEqual [1, 13]
expect(cursor3.getBufferPosition()).toEqual [2, 4]
# When the cursor is on whitespace
editor.setText("ab cde- ")
editor.setCursorBufferPosition [0,2]
cursor = editor.getLastCursor()
editor.moveToBeginningOfNextWord()
expect(cursor.getBufferPosition()).toEqual [0, 3]
it "does not blow up when there is no next word", ->
editor.setCursorBufferPosition [Infinity, Infinity]
endPosition = editor.getCursorBufferPosition()
editor.moveToBeginningOfNextWord()
expect(editor.getCursorBufferPosition()).toEqual endPosition
it "treats lines with only whitespace as a word", ->
editor.setCursorBufferPosition([9, 4])
editor.moveToBeginningOfNextWord()
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
it "works when the current line is blank", ->
editor.setCursorBufferPosition([10, 0])
editor.moveToBeginningOfNextWord()
expect(editor.getCursorBufferPosition()).toEqual [11, 9]
describe ".moveToBeginningOfNextParagraph()", ->
it "moves the cursor before the first line of the next paragraph", ->
editor.setCursorBufferPosition [0,6]
cursor = editor.getLastCursor()
editor.moveToBeginningOfNextParagraph()
expect(cursor.getBufferPosition()).toEqual { row : 10, column : 0 }
editor.setText("")
editor.setCursorBufferPosition [0,0]
cursor = editor.getLastCursor()
editor.moveToBeginningOfNextParagraph()
expect(cursor.getBufferPosition()).toEqual [0, 0]
describe ".moveToBeginningOfPreviousParagraph()", ->
it "moves the cursor before the first line of the pevious paragraph", ->
editor.setCursorBufferPosition [10,0]
cursor = editor.getLastCursor()
editor.moveToBeginningOfPreviousParagraph()
expect(cursor.getBufferPosition()).toEqual { row : 0, column : 0 }
editor.setText("")
editor.setCursorBufferPosition [0,0]
cursor = editor.getLastCursor()
editor.moveToBeginningOfPreviousParagraph()
expect(cursor.getBufferPosition()).toEqual [0, 0]
describe ".getCurrentParagraphBufferRange()", ->
it "returns the buffer range of the current paragraph, delimited by blank lines or the beginning / end of the file", ->
buffer.setText """
I am the first paragraph,
bordered by the beginning of
the file
#{' '}
I am the second paragraph
with blank lines above and below
me.
I am the last paragraph,
bordered by the end of the file.
"""
# in a paragraph
editor.setCursorBufferPosition([1, 7])
expect(editor.getCurrentParagraphBufferRange()).toEqual [[0, 0], [2, 8]]
editor.setCursorBufferPosition([7, 1])
expect(editor.getCurrentParagraphBufferRange()).toEqual [[5, 0], [7, 3]]
editor.setCursorBufferPosition([9, 10])
expect(editor.getCurrentParagraphBufferRange()).toEqual [[9, 0], [10, 32]]
# between paragraphs
editor.setCursorBufferPosition([3, 1])
expect(editor.getCurrentParagraphBufferRange()).toBeUndefined()
describe "::getCursorScreenPositions()", ->
it "returns the cursor positions in the order they were added", ->
editor.foldBufferRow(4)
cursor1 = editor.addCursorAtBufferPosition([8, 5])
cursor2 = editor.addCursorAtBufferPosition([3, 5])
expect(editor.getCursorScreenPositions()).toEqual [[0, 0], [5, 5], [3, 5]]
describe "::getCursorsOrderedByBufferPosition()", ->
it "returns all cursors ordered by buffer positions", ->
originalCursor = editor.getLastCursor()
cursor1 = editor.addCursorAtBufferPosition([8, 5])
cursor2 = editor.addCursorAtBufferPosition([4, 5])
expect(editor.getCursorsOrderedByBufferPosition()).toEqual [originalCursor, cursor2, cursor1]
describe "addCursorAtScreenPosition(screenPosition)", ->
describe "when a cursor already exists at the position", ->
it "returns the existing cursor", ->
cursor1 = editor.addCursorAtScreenPosition([0,2])
cursor2 = editor.addCursorAtScreenPosition([0,2])
expect(cursor2.marker).toBe cursor1.marker
describe "addCursorAtBufferPosition(bufferPosition)", ->
describe "when a cursor already exists at the position", ->
it "returns the existing cursor", ->
cursor1 = editor.addCursorAtBufferPosition([1,4])
cursor2 = editor.addCursorAtBufferPosition([1,4])
expect(cursor2.marker).toBe cursor1.marker
describe "autoscroll", ->
beforeEach ->
editor.manageScrollPosition = true
editor.setVerticalScrollMargin(2)
editor.setHorizontalScrollMargin(2)
editor.setLineHeightInPixels(10)
editor.setDefaultCharWidth(10)
editor.setHorizontalScrollbarHeight(0)
editor.setHeight(5.5 * 10)
editor.setWidth(5.5 * 10)
it "scrolls down when the last cursor gets closer than ::verticalScrollMargin to the bottom of the editor", ->
expect(editor.getScrollTop()).toBe 0
expect(editor.getScrollBottom()).toBe 5.5 * 10
editor.setCursorScreenPosition([2, 0])
expect(editor.getScrollBottom()).toBe 5.5 * 10
editor.moveDown()
expect(editor.getScrollBottom()).toBe 6 * 10
editor.moveDown()
expect(editor.getScrollBottom()).toBe 7 * 10
it "scrolls up when the last cursor gets closer than ::verticalScrollMargin to the top of the editor", ->
editor.setCursorScreenPosition([11, 0])
editor.setScrollBottom(editor.getScrollHeight())
editor.moveUp()
expect(editor.getScrollBottom()).toBe editor.getScrollHeight()
editor.moveUp()
expect(editor.getScrollTop()).toBe 7 * 10
editor.moveUp()
expect(editor.getScrollTop()).toBe 6 * 10
it "scrolls right when the last cursor gets closer than ::horizontalScrollMargin to the right of the editor", ->
expect(editor.getScrollLeft()).toBe 0
expect(editor.getScrollRight()).toBe 5.5 * 10
editor.setCursorScreenPosition([0, 2])
expect(editor.getScrollRight()).toBe 5.5 * 10
editor.moveRight()
expect(editor.getScrollRight()).toBe 6 * 10
editor.moveRight()
expect(editor.getScrollRight()).toBe 7 * 10
it "scrolls left when the last cursor gets closer than ::horizontalScrollMargin to the left of the editor", ->
editor.setScrollRight(editor.getScrollWidth())
editor.setCursorScreenPosition([6, 62])
expect(editor.getScrollRight()).toBe editor.getScrollWidth()
editor.moveLeft()
expect(editor.getScrollLeft()).toBe 59 * 10
editor.moveLeft()
expect(editor.getScrollLeft()).toBe 58 * 10
it "scrolls down when inserting lines makes the document longer than the editor's height", ->
editor.setCursorScreenPosition([13, Infinity])
editor.insertNewline()
expect(editor.getScrollBottom()).toBe 14 * 10
editor.insertNewline()
expect(editor.getScrollBottom()).toBe 15 * 10
it "autoscrolls to the cursor when it moves due to undo", ->
editor.insertText('abc')
editor.setScrollTop(Infinity)
editor.undo()
expect(editor.getScrollTop()).toBe 0
describe '.logCursorScope()', ->
beforeEach ->
spyOn(atom.notifications, 'addInfo')
it 'opens a notification', ->
editor.logCursorScope()
expect(atom.notifications.addInfo).toHaveBeenCalled()
describe "selection", ->
selection = null
beforeEach ->
selection = editor.getLastSelection()
describe "when the selection range changes", ->
it "emits an event with the old range, new range, and the selection that moved", ->
editor.setSelectedBufferRange([[3, 0], [4, 5]])
editor.onDidChangeSelectionRange rangeChangedHandler = jasmine.createSpy()
editor.selectToBufferPosition([6, 2])
expect(rangeChangedHandler).toHaveBeenCalled()
eventObject = rangeChangedHandler.mostRecentCall.args[0]
expect(eventObject.oldBufferRange).toEqual [[3, 0], [4, 5]]
expect(eventObject.oldScreenRange).toEqual [[3, 0], [4, 5]]
expect(eventObject.newBufferRange).toEqual [[3, 0], [6, 2]]
expect(eventObject.newScreenRange).toEqual [[3, 0], [6, 2]]
expect(eventObject.selection).toBe selection
describe ".selectUp/Down/Left/Right()", ->
it "expands each selection to its cursor's new location", ->
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[3,16], [3,21]]])
[selection1, selection2] = editor.getSelections()
editor.selectRight()
expect(selection1.getBufferRange()).toEqual [[0,9], [0,14]]
expect(selection2.getBufferRange()).toEqual [[3,16], [3,22]]
editor.selectLeft()
editor.selectLeft()
expect(selection1.getBufferRange()).toEqual [[0,9], [0,12]]
expect(selection2.getBufferRange()).toEqual [[3,16], [3,20]]
editor.selectDown()
expect(selection1.getBufferRange()).toEqual [[0,9], [1,12]]
expect(selection2.getBufferRange()).toEqual [[3,16], [4,20]]
editor.selectUp()
expect(selection1.getBufferRange()).toEqual [[0,9], [0,12]]
expect(selection2.getBufferRange()).toEqual [[3,16], [3,20]]
it "merges selections when they intersect when moving down", ->
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[1,10], [1,20]], [[2,15], [3,25]]])
[selection1, selection2, selection3] = editor.getSelections()
editor.selectDown()
expect(editor.getSelections()).toEqual [selection1]
expect(selection1.getScreenRange()).toEqual([[0, 9], [4, 25]])
expect(selection1.isReversed()).toBeFalsy()
it "merges selections when they intersect when moving up", ->
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[1,10], [1,20]]], reversed: true)
[selection1, selection2] = editor.getSelections()
editor.selectUp()
expect(editor.getSelections().length).toBe 1
expect(editor.getSelections()).toEqual [selection1]
expect(selection1.getScreenRange()).toEqual([[0, 0], [1, 20]])
expect(selection1.isReversed()).toBeTruthy()
it "merges selections when they intersect when moving left", ->
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[0,13], [1,20]]], reversed: true)
[selection1, selection2] = editor.getSelections()
editor.selectLeft()
expect(editor.getSelections()).toEqual [selection1]
expect(selection1.getScreenRange()).toEqual([[0, 8], [1, 20]])
expect(selection1.isReversed()).toBeTruthy()
it "merges selections when they intersect when moving right", ->
editor.setSelectedBufferRanges([[[0,9], [0,14]], [[0,14], [1,20]]])
[selection1, selection2] = editor.getSelections()
editor.selectRight()
expect(editor.getSelections()).toEqual [selection1]
expect(selection1.getScreenRange()).toEqual([[0, 9], [1, 21]])
expect(selection1.isReversed()).toBeFalsy()
describe "when counts are passed into the selection functions", ->
it "expands each selection to its cursor's new location", ->
editor.setSelectedBufferRanges([[[0,9], [0,13]], [[3,16], [3,21]]])
[selection1, selection2] = editor.getSelections()
editor.selectRight(2)
expect(selection1.getBufferRange()).toEqual [[0,9], [0,15]]
expect(selection2.getBufferRange()).toEqual [[3,16], [3,23]]
editor.selectLeft(3)
expect(selection1.getBufferRange()).toEqual [[0,9], [0,12]]
expect(selection2.getBufferRange()).toEqual [[3,16], [3,20]]
editor.selectDown(3)
expect(selection1.getBufferRange()).toEqual [[0,9], [3,12]]
expect(selection2.getBufferRange()).toEqual [[3,16], [6,20]]
editor.selectUp(2)
expect(selection1.getBufferRange()).toEqual [[0,9], [1,12]]
expect(selection2.getBufferRange()).toEqual [[3,16], [4,20]]
describe ".selectToBufferPosition(bufferPosition)", ->
it "expands the last selection to the given position", ->
editor.setSelectedBufferRange([[3, 0], [4, 5]])
editor.addCursorAtBufferPosition([5, 6])
editor.selectToBufferPosition([6, 2])
selections = editor.getSelections()
expect(selections.length).toBe 2
[selection1, selection2] = selections
expect(selection1.getBufferRange()).toEqual [[3, 0], [4, 5]]
expect(selection2.getBufferRange()).toEqual [[5, 6], [6, 2]]
describe ".selectToScreenPosition(screenPosition)", ->
it "expands the last selection to the given position", ->
editor.setSelectedBufferRange([[3, 0], [4, 5]])
editor.addCursorAtScreenPosition([5, 6])
editor.selectToScreenPosition([6, 2])
selections = editor.getSelections()
expect(selections.length).toBe 2
[selection1, selection2] = selections
expect(selection1.getScreenRange()).toEqual [[3, 0], [4, 5]]
expect(selection2.getScreenRange()).toEqual [[5, 6], [6, 2]]
describe ".selectToBeginningOfNextParagraph()", ->
it "selects from the cursor to first line of the next paragraph", ->
editor.setSelectedBufferRange([[3, 0], [4, 5]])
editor.addCursorAtScreenPosition([5, 6])
editor.selectToScreenPosition([6, 2])
editor.selectToBeginningOfNextParagraph()
selections = editor.getSelections()
expect(selections.length).toBe 1
expect(selections[0].getScreenRange()).toEqual [[3, 0], [10, 0]]
describe ".selectToBeginningOfPreviousParagraph()", ->
it "selects from the cursor to the first line of the pevious paragraph", ->
editor.setSelectedBufferRange([[3, 0], [4, 5]])
editor.addCursorAtScreenPosition([5, 6])
editor.selectToScreenPosition([6, 2])
editor.selectToBeginningOfPreviousParagraph()
selections = editor.getSelections()
expect(selections.length).toBe 1
expect(selections[0].getScreenRange()).toEqual [[0, 0], [5, 6]]
it "merges selections if they intersect, maintaining the directionality of the last selection", ->
editor.setCursorScreenPosition([4, 10])
editor.selectToScreenPosition([5, 27])
editor.addCursorAtScreenPosition([3, 10])
editor.selectToScreenPosition([6, 27])
selections = editor.getSelections()
expect(selections.length).toBe 1
[selection1] = selections
expect(selection1.getScreenRange()).toEqual [[3, 10], [6, 27]]
expect(selection1.isReversed()).toBeFalsy()
editor.addCursorAtScreenPosition([7, 4])
editor.selectToScreenPosition([4, 11])
selections = editor.getSelections()
expect(selections.length).toBe 1
[selection1] = selections
expect(selection1.getScreenRange()).toEqual [[3, 10], [7, 4]]
expect(selection1.isReversed()).toBeTruthy()
describe ".selectToTop()", ->
it "selects text from cusor position to the top of the buffer", ->
editor.setCursorScreenPosition [11,2]
editor.addCursorAtScreenPosition [10,0]
editor.selectToTop()
expect(editor.getCursors().length).toBe 1
expect(editor.getCursorBufferPosition()).toEqual [0,0]
expect(editor.getLastSelection().getBufferRange()).toEqual [[0,0], [11,2]]
expect(editor.getLastSelection().isReversed()).toBeTruthy()
describe ".selectToBottom()", ->
it "selects text from cusor position to the bottom of the buffer", ->
editor.setCursorScreenPosition [10,0]
editor.addCursorAtScreenPosition [9,3]
editor.selectToBottom()
expect(editor.getCursors().length).toBe 1
expect(editor.getCursorBufferPosition()).toEqual [12,2]
expect(editor.getLastSelection().getBufferRange()).toEqual [[9,3], [12,2]]
expect(editor.getLastSelection().isReversed()).toBeFalsy()
describe ".selectAll()", ->
it "selects the entire buffer", ->
editor.selectAll()
expect(editor.getLastSelection().getBufferRange()).toEqual buffer.getRange()
describe ".selectToBeginningOfLine()", ->
it "selects text from cusor position to beginning of line", ->
editor.setCursorScreenPosition [12,2]
editor.addCursorAtScreenPosition [11,3]
editor.selectToBeginningOfLine()
expect(editor.getCursors().length).toBe 2
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [12,0]
expect(cursor2.getBufferPosition()).toEqual [11,0]
expect(editor.getSelections().length).toBe 2
[selection1, selection2] = editor.getSelections()
expect(selection1.getBufferRange()).toEqual [[12,0], [12,2]]
expect(selection1.isReversed()).toBeTruthy()
expect(selection2.getBufferRange()).toEqual [[11,0], [11,3]]
expect(selection2.isReversed()).toBeTruthy()
describe ".selectToEndOfLine()", ->
it "selects text from cusor position to end of line", ->
editor.setCursorScreenPosition [12,0]
editor.addCursorAtScreenPosition [11,3]
editor.selectToEndOfLine()
expect(editor.getCursors().length).toBe 2
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [12,2]
expect(cursor2.getBufferPosition()).toEqual [11,44]
expect(editor.getSelections().length).toBe 2
[selection1, selection2] = editor.getSelections()
expect(selection1.getBufferRange()).toEqual [[12,0], [12,2]]
expect(selection1.isReversed()).toBeFalsy()
expect(selection2.getBufferRange()).toEqual [[11,3], [11,44]]
expect(selection2.isReversed()).toBeFalsy()
describe ".selectLinesContainingCursors()", ->
it "selects the entire line (including newlines) at given row", ->
editor.setCursorScreenPosition([1, 2])
editor.selectLinesContainingCursors()
expect(editor.getSelectedBufferRange()).toEqual [[1,0], [2,0]]
expect(editor.getSelectedText()).toBe " var sort = function(items) {\n"
editor.setCursorScreenPosition([12, 2])
editor.selectLinesContainingCursors()
expect(editor.getSelectedBufferRange()).toEqual [[12,0], [12,2]]
editor.setCursorBufferPosition([0, 2])
editor.selectLinesContainingCursors()
editor.selectLinesContainingCursors()
expect(editor.getSelectedBufferRange()).toEqual [[0,0], [2,0]]
it "autoscrolls to the selection", ->
editor.manageScrollPosition = true
editor.setLineHeightInPixels(10)
editor.setDefaultCharWidth(10)
editor.setHeight(50)
editor.setWidth(50)
editor.setHorizontalScrollbarHeight(0)
editor.setCursorScreenPosition([5, 6])
editor.scrollToTop()
expect(editor.getScrollTop()).toBe 0
editor.selectLinesContainingCursors()
expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10
describe ".selectToBeginningOfWord()", ->
it "selects text from cusor position to beginning of word", ->
editor.setCursorScreenPosition [0,13]
editor.addCursorAtScreenPosition [3,49]
editor.selectToBeginningOfWord()
expect(editor.getCursors().length).toBe 2
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [0,4]
expect(cursor2.getBufferPosition()).toEqual [3,47]
expect(editor.getSelections().length).toBe 2
[selection1, selection2] = editor.getSelections()
expect(selection1.getBufferRange()).toEqual [[0,4], [0,13]]
expect(selection1.isReversed()).toBeTruthy()
expect(selection2.getBufferRange()).toEqual [[3,47], [3,49]]
expect(selection2.isReversed()).toBeTruthy()
describe ".selectToEndOfWord()", ->
it "selects text from cusor position to end of word", ->
editor.setCursorScreenPosition [0,4]
editor.addCursorAtScreenPosition [3,48]
editor.selectToEndOfWord()
expect(editor.getCursors().length).toBe 2
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [0,13]
expect(cursor2.getBufferPosition()).toEqual [3,50]
expect(editor.getSelections().length).toBe 2
[selection1, selection2] = editor.getSelections()
expect(selection1.getBufferRange()).toEqual [[0,4], [0,13]]
expect(selection1.isReversed()).toBeFalsy()
expect(selection2.getBufferRange()).toEqual [[3,48], [3,50]]
expect(selection2.isReversed()).toBeFalsy()
describe ".selectToBeginningOfNextWord()", ->
it "selects text from cusor position to beginning of next word", ->
editor.setCursorScreenPosition [0,4]
editor.addCursorAtScreenPosition [3,48]
editor.selectToBeginningOfNextWord()
expect(editor.getCursors().length).toBe 2
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [0,14]
expect(cursor2.getBufferPosition()).toEqual [3,51]
expect(editor.getSelections().length).toBe 2
[selection1, selection2] = editor.getSelections()
expect(selection1.getBufferRange()).toEqual [[0,4], [0,14]]
expect(selection1.isReversed()).toBeFalsy()
expect(selection2.getBufferRange()).toEqual [[3,48], [3,51]]
expect(selection2.isReversed()).toBeFalsy()
describe ".selectToPreviousWordBoundary()", ->
it "select to the previous word boundary", ->
editor.setCursorBufferPosition [0, 8]
editor.addCursorAtBufferPosition [2, 0]
editor.addCursorAtBufferPosition [3, 4]
editor.addCursorAtBufferPosition [3, 14]
editor.selectToPreviousWordBoundary()
expect(editor.getSelections().length).toBe 4
[selection1, selection2, selection3, selection4] = editor.getSelections()
expect(selection1.getBufferRange()).toEqual [[0,8], [0,4]]
expect(selection1.isReversed()).toBeTruthy()
expect(selection2.getBufferRange()).toEqual [[2,0], [1,30]]
expect(selection2.isReversed()).toBeTruthy()
expect(selection3.getBufferRange()).toEqual [[3,4], [3,0]]
expect(selection3.isReversed()).toBeTruthy()
expect(selection4.getBufferRange()).toEqual [[3,14], [3,13]]
expect(selection4.isReversed()).toBeTruthy()
describe ".selectToNextWordBoundary()", ->
it "select to the next word boundary", ->
editor.setCursorBufferPosition [0, 8]
editor.addCursorAtBufferPosition [2, 40]
editor.addCursorAtBufferPosition [4, 0]
editor.addCursorAtBufferPosition [3, 30]
editor.selectToNextWordBoundary()
expect(editor.getSelections().length).toBe 4
[selection1, selection2, selection3, selection4] = editor.getSelections()
expect(selection1.getBufferRange()).toEqual [[0,8], [0,13]]
expect(selection1.isReversed()).toBeFalsy()
expect(selection2.getBufferRange()).toEqual [[2,40], [3,0]]
expect(selection2.isReversed()).toBeFalsy()
expect(selection3.getBufferRange()).toEqual [[4,0], [4,4]]
expect(selection3.isReversed()).toBeFalsy()
expect(selection4.getBufferRange()).toEqual [[3,30], [3,31]]
expect(selection4.isReversed()).toBeFalsy()
describe ".selectWordsContainingCursors()", ->
describe "when the cursor is inside a word", ->
it "selects the entire word", ->
editor.setCursorScreenPosition([0, 8])
editor.selectWordsContainingCursors()
expect(editor.getSelectedText()).toBe 'quicksort'
describe "when the cursor is between two words", ->
it "selects the word the cursor is on", ->
editor.setCursorScreenPosition([0, 4])
editor.selectWordsContainingCursors()
expect(editor.getSelectedText()).toBe 'quicksort'
editor.setCursorScreenPosition([0, 3])
editor.selectWordsContainingCursors()
expect(editor.getSelectedText()).toBe 'var'
describe "when the cursor is inside a region of whitespace", ->
it "selects the whitespace region", ->
editor.setCursorScreenPosition([5, 2])
editor.selectWordsContainingCursors()
expect(editor.getSelectedBufferRange()).toEqual [[5, 0], [5, 6]]
editor.setCursorScreenPosition([5, 0])
editor.selectWordsContainingCursors()
expect(editor.getSelectedBufferRange()).toEqual [[5, 0], [5, 6]]
describe "when the cursor is at the end of the text", ->
it "select the previous word", ->
editor.buffer.append 'word'
editor.moveToBottom()
editor.selectWordsContainingCursors()
expect(editor.getSelectedBufferRange()).toEqual [[12, 2], [12, 6]]
describe 'when editor.nonWordCharacters is set scoped to a grammar', ->
coffeeEditor = null
beforeEach ->
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
waitsForPromise ->
atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
it 'selects the correct surrounding word for the given scoped setting', ->
coffeeEditor.setCursorBufferPosition [0, 9] # in the middle of quicksort
coffeeEditor.selectWordsContainingCursors()
expect(coffeeEditor.getSelectedBufferRange()).toEqual [[0, 6], [0, 15]]
atom.config.set 'editor.nonWordCharacters', 'qusort', scopeSelector: '.source.coffee'
coffeeEditor.setCursorBufferPosition [0, 9]
coffeeEditor.selectWordsContainingCursors()
expect(coffeeEditor.getSelectedBufferRange()).toEqual [[0, 8], [0, 11]]
editor.setCursorBufferPosition [0, 7]
editor.selectWordsContainingCursors()
expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [0, 13]]
describe ".selectToFirstCharacterOfLine()", ->
it "moves to the first character of the current line or the beginning of the line if it's already on the first character", ->
editor.setCursorScreenPosition [0,5]
editor.addCursorAtScreenPosition [1,7]
editor.selectToFirstCharacterOfLine()
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [0,0]
expect(cursor2.getBufferPosition()).toEqual [1,2]
expect(editor.getSelections().length).toBe 2
[selection1, selection2] = editor.getSelections()
expect(selection1.getBufferRange()).toEqual [[0,0], [0,5]]
expect(selection1.isReversed()).toBeTruthy()
expect(selection2.getBufferRange()).toEqual [[1,2], [1,7]]
expect(selection2.isReversed()).toBeTruthy()
editor.selectToFirstCharacterOfLine()
[selection1, selection2] = editor.getSelections()
expect(selection1.getBufferRange()).toEqual [[0,0], [0,5]]
expect(selection1.isReversed()).toBeTruthy()
expect(selection2.getBufferRange()).toEqual [[1,0], [1,7]]
expect(selection2.isReversed()).toBeTruthy()
describe ".setSelectedBufferRanges(ranges)", ->
it "clears existing selections and creates selections for each of the given ranges", ->
editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[4, 4], [5, 5]]])
expect(editor.getSelectedBufferRanges()).toEqual [[[2, 2], [3, 3]], [[4, 4], [5, 5]]]
editor.setSelectedBufferRanges([[[5, 5], [6, 6]]])
expect(editor.getSelectedBufferRanges()).toEqual [[[5, 5], [6, 6]]]
it "merges intersecting selections", ->
editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[3, 0], [5, 5]]])
expect(editor.getSelectedBufferRanges()).toEqual [[[2, 2], [5, 5]]]
it "does not merge non-empty adjacent selections", ->
editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[3, 3], [5, 5]]])
expect(editor.getSelectedBufferRanges()).toEqual [[[2, 2], [3, 3]], [[3, 3], [5, 5]]]
it "recyles existing selection instances", ->
selection = editor.getLastSelection()
editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[4, 4], [5, 5]]])
[selection1, selection2] = editor.getSelections()
expect(selection1).toBe selection
expect(selection1.getBufferRange()).toEqual [[2, 2], [3, 3]]
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.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()
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.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 0], [6, 1]]], preserveFolds: true)
expect(editor.isFoldedAtBufferRow(1)).toBeTruthy()
expect(editor.isFoldedAtBufferRow(6)).toBeTruthy()
describe ".setSelectedScreenRanges(ranges)", ->
beforeEach ->
editor.foldBufferRow(4)
it "clears existing selections and creates selections for each of the given ranges", ->
editor.setSelectedScreenRanges([[[3, 4], [3, 7]], [[5, 4], [5, 7]]])
expect(editor.getSelectedBufferRanges()).toEqual [[[3, 4], [3, 7]], [[8, 4], [8, 7]]]
editor.setSelectedScreenRanges([[[6, 2], [6, 4]]])
expect(editor.getSelectedScreenRanges()).toEqual [[[6, 2], [6, 4]]]
it "merges intersecting selections and unfolds the fold", ->
editor.setSelectedScreenRanges([[[2, 2], [3, 3]], [[3, 0], [5, 5]]])
expect(editor.getSelectedScreenRanges()).toEqual [[[2, 2], [8, 5]]]
it "recyles existing selection instances", ->
selection = editor.getLastSelection()
editor.setSelectedScreenRanges([[[2, 2], [3, 4]], [[4, 4], [5, 5]]])
[selection1, selection2] = editor.getSelections()
expect(selection1).toBe selection
expect(selection1.getScreenRange()).toEqual [[2, 2], [3, 4]]
describe ".setSelectedBufferRange(range)", ->
describe "when the 'autoscroll' option is true", ->
it "autoscrolls to the selection", ->
editor.manageScrollPosition = true
editor.setLineHeightInPixels(10)
editor.setDefaultCharWidth(10)
editor.setHeight(50)
editor.setWidth(50)
editor.setHorizontalScrollbarHeight(0)
expect(editor.getScrollTop()).toBe 0
editor.setSelectedBufferRange([[5, 6], [6, 8]], autoscroll: true)
expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10
expect(editor.getScrollRight()).toBe 50
editor.setSelectedBufferRange([[6, 6], [6, 8]], autoscroll: true)
expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10
expect(editor.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10
describe ".selectMarker(marker)", ->
describe "if the marker is valid", ->
it "selects the marker's range and returns the selected range", ->
marker = editor.markBufferRange([[0, 1], [3, 3]])
expect(editor.selectMarker(marker)).toEqual [[0, 1], [3, 3]]
expect(editor.getSelectedBufferRange()).toEqual [[0, 1], [3, 3]]
describe "if the marker is invalid", ->
it "does not change the selection and returns a falsy value", ->
marker = editor.markBufferRange([[0, 1], [3, 3]])
marker.destroy()
expect(editor.selectMarker(marker)).toBeFalsy()
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 0]]
describe ".addSelectionForBufferRange(bufferRange)", ->
it "adds a selection for the specified buffer range", ->
editor.addSelectionForBufferRange([[3, 4], [5, 6]])
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 0]], [[3, 4], [5, 6]]]
it "autoscrolls to the added selection if needed", ->
editor.manageScrollPosition = true
editor.setLineHeightInPixels(10)
editor.setDefaultCharWidth(10)
editor.setHeight(50)
editor.setWidth(50)
editor.addSelectionForBufferRange([[8, 10], [8, 15]])
expect(editor.getScrollTop()).toBe 75
expect(editor.getScrollLeft()).toBe 160
describe ".addSelectionBelow()", ->
describe "when the selection is non-empty", ->
it "selects the same region of the line below current selections if possible", ->
editor.setSelectedBufferRange([[3, 16], [3, 21]])
editor.addSelectionForBufferRange([[3, 25], [3, 34]])
editor.addSelectionBelow()
expect(editor.getSelectedBufferRanges()).toEqual [
[[3, 16], [3, 21]]
[[3, 25], [3, 34]]
[[4, 16], [4, 21]]
[[4, 25], [4, 29]]
]
for cursor in editor.getCursors()
expect(cursor.isVisible()).toBeFalsy()
it "skips lines that are too short to create a non-empty selection", ->
editor.setSelectedBufferRange([[3, 31], [3, 38]])
editor.addSelectionBelow()
expect(editor.getSelectedBufferRanges()).toEqual [
[[3, 31], [3, 38]]
[[6, 31], [6, 38]]
]
it "honors the original selection's range (goal range) when adding across shorter lines", ->
editor.setSelectedBufferRange([[3, 22], [3, 38]])
editor.addSelectionBelow()
editor.addSelectionBelow()
editor.addSelectionBelow()
expect(editor.getSelectedBufferRanges()).toEqual [
[[3, 22], [3, 38]]
[[4, 22], [4, 29]]
[[5, 22], [5, 30]]
[[6, 22], [6, 38]]
]
it "clears selection goal ranges when the selection changes", ->
editor.setSelectedBufferRange([[3, 22], [3, 38]])
editor.addSelectionBelow()
editor.selectLeft()
editor.addSelectionBelow()
expect(editor.getSelectedBufferRanges()).toEqual [
[[3, 22], [3, 37]]
[[4, 22], [4, 29]]
[[5, 22], [5, 28]]
]
# goal range from previous add selection is honored next time
editor.addSelectionBelow()
expect(editor.getSelectedBufferRanges()).toEqual [
[[3, 22], [3, 37]]
[[4, 22], [4, 29]]
[[5, 22], [5, 30]] # select to end of line 5 because line 4's goal range was reset by line 3 previously
[[6, 22], [6, 28]]
]
describe "when the selection is empty", ->
it "does not skip lines that are shorter than the current column", ->
editor.setCursorBufferPosition([3, 36])
editor.addSelectionBelow()
editor.addSelectionBelow()
editor.addSelectionBelow()
expect(editor.getSelectedBufferRanges()).toEqual [
[[3, 36], [3, 36]]
[[4, 29], [4, 29]]
[[5, 30], [5, 30]]
[[6, 36], [6, 36]]
]
it "skips empty lines when the column is non-zero", ->
editor.setCursorBufferPosition([9, 4])
editor.addSelectionBelow()
expect(editor.getSelectedBufferRanges()).toEqual [
[[9, 4], [9, 4]]
[[11, 4], [11, 4]]
]
it "does not skip empty lines when the column is zero", ->
editor.setCursorBufferPosition([9, 0])
editor.addSelectionBelow()
expect(editor.getSelectedBufferRanges()).toEqual [
[[9, 0], [9, 0]]
[[10, 0], [10, 0]]
]
describe ".addSelectionAbove()", ->
describe "when the selection is non-empty", ->
it "selects the same region of the line above current selections if possible", ->
editor.setSelectedBufferRange([[3, 16], [3, 21]])
editor.addSelectionForBufferRange([[3, 37], [3, 44]])
editor.addSelectionAbove()
expect(editor.getSelectedBufferRanges()).toEqual [
[[3, 16], [3, 21]]
[[3, 37], [3, 44]]
[[2, 16], [2, 21]]
[[2, 37], [2, 40]]
]
for cursor in editor.getCursors()
expect(cursor.isVisible()).toBeFalsy()
it "skips lines that are too short to create a non-empty selection", ->
editor.setSelectedBufferRange([[6, 31], [6, 38]])
editor.addSelectionAbove()
expect(editor.getSelectedBufferRanges()).toEqual [
[[6, 31], [6, 38]]
[[3, 31], [3, 38]]
]
it "honors the original selection's range (goal range) when adding across shorter lines", ->
editor.setSelectedBufferRange([[6, 22], [6, 38]])
editor.addSelectionAbove()
editor.addSelectionAbove()
editor.addSelectionAbove()
expect(editor.getSelectedBufferRanges()).toEqual [
[[6, 22], [6, 38]]
[[5, 22], [5, 30]]
[[4, 22], [4, 29]]
[[3, 22], [3, 38]]
]
describe "when the selection is empty", ->
it "does not skip lines that are shorter than the current column", ->
editor.setCursorBufferPosition([6, 36])
editor.addSelectionAbove()
editor.addSelectionAbove()
editor.addSelectionAbove()
expect(editor.getSelectedBufferRanges()).toEqual [
[[6, 36], [6, 36]]
[[5, 30], [5, 30]]
[[4, 29], [4, 29]]
[[3, 36], [3, 36]]
]
it "skips empty lines when the column is non-zero", ->
editor.setCursorBufferPosition([11, 4])
editor.addSelectionAbove()
expect(editor.getSelectedBufferRanges()).toEqual [
[[11, 4], [11, 4]]
[[9, 4], [9, 4]]
]
it "does not skip empty lines when the column is zero", ->
editor.setCursorBufferPosition([10, 0])
editor.addSelectionAbove()
expect(editor.getSelectedBufferRanges()).toEqual [
[[10, 0], [10, 0]]
[[9, 0], [9, 0]]
]
describe ".splitSelectionsIntoLines()", ->
it "splits all multi-line selections into one selection per line", ->
editor.setSelectedBufferRange([[0, 3], [2, 4]])
editor.splitSelectionsIntoLines()
expect(editor.getSelectedBufferRanges()).toEqual [
[[0, 3], [0, 29]]
[[1, 0], [1, 30]]
[[2, 0], [2, 4]]
]
editor.setSelectedBufferRange([[0, 3], [1, 10]])
editor.splitSelectionsIntoLines()
expect(editor.getSelectedBufferRanges()).toEqual [
[[0, 3], [0, 29]]
[[1, 0], [1, 10]]
]
editor.setSelectedBufferRange([[0, 0], [0, 3]])
editor.splitSelectionsIntoLines()
expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]]]
describe ".consolidateSelections()", ->
it "destroys all selections but the most recent, returning true if any selections were destroyed", ->
editor.setSelectedBufferRange([[3, 16], [3, 21]])
selection1 = editor.getLastSelection()
selection2 = editor.addSelectionForBufferRange([[3, 25], [3, 34]])
selection3 = editor.addSelectionForBufferRange([[8, 4], [8, 10]])
expect(editor.getSelections()).toEqual [selection1, selection2, selection3]
expect(editor.consolidateSelections()).toBeTruthy()
expect(editor.getSelections()).toEqual [selection3]
expect(selection3.isEmpty()).toBeFalsy()
expect(editor.consolidateSelections()).toBeFalsy()
expect(editor.getSelections()).toEqual [selection3]
describe "when the cursor is moved while there is a selection", ->
makeSelection = -> selection.setBufferRange [[1, 2], [1, 5]]
it "clears the selection", ->
makeSelection()
editor.moveDown()
expect(selection.isEmpty()).toBeTruthy()
makeSelection()
editor.moveUp()
expect(selection.isEmpty()).toBeTruthy()
makeSelection()
editor.moveLeft()
expect(selection.isEmpty()).toBeTruthy()
makeSelection()
editor.moveRight()
expect(selection.isEmpty()).toBeTruthy()
makeSelection()
editor.setCursorScreenPosition([3, 3])
expect(selection.isEmpty()).toBeTruthy()
it "does not share selections between different edit sessions for the same buffer", ->
editor2 = null
waitsForPromise ->
atom.project.open('sample.js').then (o) -> editor2 = o
runs ->
editor.setSelectedBufferRanges([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
editor2.setSelectedBufferRanges([[[8, 7], [6, 5]], [[4, 3], [2, 1]]])
expect(editor2.getSelectedBufferRanges()).not.toEqual editor.getSelectedBufferRanges()
describe "buffer manipulation", ->
describe ".insertText(text)", ->
describe "when there is a single selection", ->
beforeEach ->
editor.setSelectedBufferRange([[1, 0], [1, 2]])
it "replaces the selection with the given text", ->
range = editor.insertText('xxx')
expect(range).toEqual [ [[1, 0], [1, 3]] ]
expect(buffer.lineForRow(1)).toBe 'xxxvar sort = function(items) {'
describe "when there are multiple empty selections", ->
describe "when the cursors are on the same line", ->
it "inserts the given text at the location of each cursor and moves the cursors to the end of each cursor's inserted text", ->
editor.setCursorScreenPosition([1, 2])
editor.addCursorAtScreenPosition([1, 5])
editor.insertText('xxx')
expect(buffer.lineForRow(1)).toBe ' xxxvarxxx sort = function(items) {'
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [1, 5]
expect(cursor2.getBufferPosition()).toEqual [1, 11]
describe "when the cursors are on different lines", ->
it "inserts the given text at the location of each cursor and moves the cursors to the end of each cursor's inserted text", ->
editor.setCursorScreenPosition([1, 2])
editor.addCursorAtScreenPosition([2, 4])
editor.insertText('xxx')
expect(buffer.lineForRow(1)).toBe ' xxxvar sort = function(items) {'
expect(buffer.lineForRow(2)).toBe ' xxxif (items.length <= 1) return items;'
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [1, 5]
expect(cursor2.getBufferPosition()).toEqual [2, 7]
it "autoscrolls to the last cursor", ->
editor.manageScrollPosition = true
editor.setCursorScreenPosition([1, 2])
editor.addCursorAtScreenPosition([10, 4])
editor.setLineHeightInPixels(10)
editor.setHeight(50)
expect(editor.getScrollTop()).toBe 0
editor.insertText('a')
expect(editor.getScrollTop()).toBe 80
describe "when there are multiple non-empty selections", ->
describe "when the selections are on the same line", ->
it "replaces each selection range with the inserted characters", ->
editor.setSelectedBufferRanges([[[0,4], [0,13]], [[0,22], [0,24]]])
editor.insertText("x")
[cursor1, cursor2] = editor.getCursors()
[selection1, selection2] = editor.getSelections()
expect(cursor1.getScreenPosition()).toEqual [0, 5]
expect(cursor2.getScreenPosition()).toEqual [0, 15]
expect(selection1.isEmpty()).toBeTruthy()
expect(selection2.isEmpty()).toBeTruthy()
expect(editor.lineTextForBufferRow(0)).toBe "var x = functix () {"
describe "when the selections are on different lines", ->
it "replaces each selection with the given text, clears the selections, and places the cursor at the end of each selection's inserted text", ->
editor.setSelectedBufferRanges([[[1, 0], [1, 2]], [[2, 0], [2, 4]]])
editor.insertText('xxx')
expect(buffer.lineForRow(1)).toBe 'xxxvar sort = function(items) {'
expect(buffer.lineForRow(2)).toBe 'xxxif (items.length <= 1) return items;'
[selection1, selection2] = editor.getSelections()
expect(selection1.isEmpty()).toBeTruthy()
expect(selection1.cursor.getBufferPosition()).toEqual [1, 3]
expect(selection2.isEmpty()).toBeTruthy()
expect(selection2.cursor.getBufferPosition()).toEqual [2, 3]
describe "when there is a selection that ends on a folded line", ->
it "destroys the selection", ->
editor.createFold(2,4)
editor.setSelectedBufferRange([[1,0], [2,0]])
editor.insertText('holy cow')
expect(editor.tokenizedLineForScreenRow(2).fold).toBeUndefined()
describe "when there are ::onWillInsertText and ::onDidInsertText observers", ->
beforeEach ->
editor.setSelectedBufferRange([[1, 0], [1, 2]])
it "notifies the observers when inserting text", ->
willInsertSpy = jasmine.createSpy().andCallFake ->
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) {'
didInsertSpy = jasmine.createSpy().andCallFake ->
expect(buffer.lineForRow(1)).toBe 'xxxvar sort = function(items) {'
editor.onWillInsertText(willInsertSpy)
editor.onDidInsertText(didInsertSpy)
expect(editor.insertText('xxx')).toBeTruthy()
expect(buffer.lineForRow(1)).toBe 'xxxvar sort = function(items) {'
expect(willInsertSpy).toHaveBeenCalled()
expect(didInsertSpy).toHaveBeenCalled()
options = willInsertSpy.mostRecentCall.args[0]
expect(options.text).toBe 'xxx'
expect(options.cancel).toBeDefined()
options = didInsertSpy.mostRecentCall.args[0]
expect(options.text).toBe 'xxx'
it "cancels text insertion when an ::onWillInsertText observer calls cancel on an event", ->
willInsertSpy = jasmine.createSpy().andCallFake ({cancel}) ->
cancel()
didInsertSpy = jasmine.createSpy()
editor.onWillInsertText(willInsertSpy)
editor.onDidInsertText(didInsertSpy)
expect(editor.insertText('xxx')).toBe false
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) {'
expect(willInsertSpy).toHaveBeenCalled()
expect(didInsertSpy).not.toHaveBeenCalled()
describe "when the undo option is set to 'skip'", ->
beforeEach ->
editor.setSelectedBufferRange([[1, 2], [1, 2]])
it "does not undo the skipped operation", ->
range = editor.insertText('x')
range = editor.insertText('y', undo: 'skip')
editor.undo()
expect(buffer.lineForRow(1)).toBe ' yvar sort = function(items) {'
describe ".insertNewline()", ->
describe "when there is a single cursor", ->
describe "when the cursor is at the beginning of a line", ->
it "inserts an empty line before it", ->
editor.setCursorScreenPosition(row: 1, column: 0)
editor.insertNewline()
expect(buffer.lineForRow(1)).toBe ''
expect(editor.getCursorScreenPosition()).toEqual(row: 2, column: 0)
describe "when the cursor is in the middle of a line", ->
it "splits the current line to form a new line", ->
editor.setCursorScreenPosition(row: 1, column: 6)
originalLine = buffer.lineForRow(1)
lineBelowOriginalLine = buffer.lineForRow(2)
editor.insertNewline()
expect(buffer.lineForRow(1)).toBe originalLine[0...6]
expect(buffer.lineForRow(2)).toBe originalLine[6..]
expect(buffer.lineForRow(3)).toBe lineBelowOriginalLine
expect(editor.getCursorScreenPosition()).toEqual(row: 2, column: 0)
describe "when the cursor is on the end of a line", ->
it "inserts an empty line after it", ->
editor.setCursorScreenPosition(row: 1, column: buffer.lineForRow(1).length)
editor.insertNewline()
expect(buffer.lineForRow(2)).toBe ''
expect(editor.getCursorScreenPosition()).toEqual(row: 2, column: 0)
describe "when there are multiple cursors", ->
describe "when the cursors are on the same line", ->
it "breaks the line at the cursor locations", ->
editor.setCursorScreenPosition([3, 13])
editor.addCursorAtScreenPosition([3, 38])
editor.insertNewline()
expect(editor.lineTextForBufferRow(3)).toBe " var pivot"
expect(editor.lineTextForBufferRow(4)).toBe " = items.shift(), current"
expect(editor.lineTextForBufferRow(5)).toBe ", left = [], right = [];"
expect(editor.lineTextForBufferRow(6)).toBe " while(items.length > 0) {"
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [4, 0]
expect(cursor2.getBufferPosition()).toEqual [5, 0]
describe "when the cursors are on different lines", ->
it "inserts newlines at each cursor location", ->
editor.setCursorScreenPosition([3, 0])
editor.addCursorAtScreenPosition([6, 0])
editor.insertText("\n")
expect(editor.lineTextForBufferRow(3)).toBe ""
expect(editor.lineTextForBufferRow(4)).toBe " var pivot = items.shift(), current, left = [], right = [];"
expect(editor.lineTextForBufferRow(5)).toBe " while(items.length > 0) {"
expect(editor.lineTextForBufferRow(6)).toBe " current = items.shift();"
expect(editor.lineTextForBufferRow(7)).toBe ""
expect(editor.lineTextForBufferRow(8)).toBe " current < pivot ? left.push(current) : right.push(current);"
expect(editor.lineTextForBufferRow(9)).toBe " }"
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [4,0]
expect(cursor2.getBufferPosition()).toEqual [8,0]
describe ".insertNewlineBelow()", ->
describe "when the operation is undone", ->
it "places the cursor back at the previous location", ->
editor.setCursorBufferPosition([0,2])
editor.insertNewlineBelow()
expect(editor.getCursorBufferPosition()).toEqual [1,0]
editor.undo()
expect(editor.getCursorBufferPosition()).toEqual [0,2]
it "inserts a newline below the cursor's current line, autoindents it, and moves the cursor to the end of the line", ->
atom.config.set("editor.autoIndent", true)
editor.insertNewlineBelow()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe " "
expect(editor.getCursorBufferPosition()).toEqual [1, 2]
describe ".insertNewlineAbove()", ->
describe "when the cursor is on first line", ->
it "inserts a newline on the first line and moves the cursor to the first line", ->
editor.setCursorBufferPosition([0])
editor.insertNewlineAbove()
expect(editor.getCursorBufferPosition()).toEqual [0,0]
expect(editor.lineTextForBufferRow(0)).toBe ''
expect(editor.lineTextForBufferRow(1)).toBe 'var quicksort = function () {'
expect(editor.buffer.getLineCount()).toBe 14
describe "when the cursor is not on the first line", ->
it "inserts a newline above the current line and moves the cursor to the inserted line", ->
editor.setCursorBufferPosition([3,4])
editor.insertNewlineAbove()
expect(editor.getCursorBufferPosition()).toEqual [3,0]
expect(editor.lineTextForBufferRow(3)).toBe ''
expect(editor.lineTextForBufferRow(4)).toBe ' var pivot = items.shift(), current, left = [], right = [];'
expect(editor.buffer.getLineCount()).toBe 14
editor.undo()
expect(editor.getCursorBufferPosition()).toEqual [3,4]
it "indents the new line to the correct level when editor.autoIndent is true", ->
atom.config.set('editor.autoIndent', true)
editor.setText(' var test')
editor.setCursorBufferPosition([0,2])
editor.insertNewlineAbove()
expect(editor.getCursorBufferPosition()).toEqual [0,2]
expect(editor.lineTextForBufferRow(0)).toBe ' '
expect(editor.lineTextForBufferRow(1)).toBe ' var test'
editor.setText('\n var test')
editor.setCursorBufferPosition([1,2])
editor.insertNewlineAbove()
expect(editor.getCursorBufferPosition()).toEqual [1,2]
expect(editor.lineTextForBufferRow(0)).toBe ''
expect(editor.lineTextForBufferRow(1)).toBe ' '
expect(editor.lineTextForBufferRow(2)).toBe ' var test'
editor.setText('function() {\n}')
editor.setCursorBufferPosition([1,1])
editor.insertNewlineAbove()
expect(editor.getCursorBufferPosition()).toEqual [1,2]
expect(editor.lineTextForBufferRow(0)).toBe 'function() {'
expect(editor.lineTextForBufferRow(1)).toBe ' '
expect(editor.lineTextForBufferRow(2)).toBe '}'
describe "when a new line is appended before a closing tag (e.g. by pressing enter before a selection)", ->
it "moves the line down and keeps the indentation level the same when editor.autoIndent is true", ->
atom.config.set('editor.autoIndent', true)
editor.setCursorBufferPosition([9,2])
editor.insertNewline()
expect(editor.lineTextForBufferRow(10)).toBe ' };'
describe ".backspace()", ->
describe "when there is a single cursor", ->
changeScreenRangeHandler = null
beforeEach ->
selection = editor.getLastSelection()
changeScreenRangeHandler = jasmine.createSpy('changeScreenRangeHandler')
selection.onDidChangeRange changeScreenRangeHandler
describe "when the cursor is on the middle of the line", ->
it "removes the character before the cursor", ->
editor.setCursorScreenPosition(row: 1, column: 7)
expect(buffer.lineForRow(1)).toBe " var sort = function(items) {"
editor.backspace()
line = buffer.lineForRow(1)
expect(line).toBe " var ort = function(items) {"
expect(editor.getCursorScreenPosition()).toEqual {row: 1, column: 6}
expect(changeScreenRangeHandler).toHaveBeenCalled()
expect(editor.getLastCursor().isVisible()).toBeTruthy()
describe "when the cursor is at the beginning of a line", ->
it "joins it with the line above", ->
originalLine0 = buffer.lineForRow(0)
expect(originalLine0).toBe "var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe " var sort = function(items) {"
editor.setCursorScreenPosition(row: 1, column: 0)
editor.backspace()
line0 = buffer.lineForRow(0)
line1 = buffer.lineForRow(1)
expect(line0).toBe "var quicksort = function () { var sort = function(items) {"
expect(line1).toBe " if (items.length <= 1) return items;"
expect(editor.getCursorScreenPosition()).toEqual [0, originalLine0.length]
expect(changeScreenRangeHandler).toHaveBeenCalled()
describe "when the cursor is at the first column of the first line", ->
it "does nothing, but doesn't raise an error", ->
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])
editor.backspace()
expect(buffer.lineForRow(4)).toBe " return sort(left).concat(pivot).concat(sort(right));"
expect(buffer.lineForRow(4).fold).toBeUndefined()
describe "when the cursor is in the middle of a line below a fold", ->
it "backspaces as normal", ->
editor.setCursorScreenPosition([4,0])
editor.foldCurrentRow()
editor.setCursorScreenPosition([5,5])
editor.backspace()
expect(buffer.lineForRow(7)).toBe " }"
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", ->
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]
describe "when there are multiple cursors", ->
describe "when cursors are on the same line", ->
it "removes the characters preceding each cursor", ->
editor.setCursorScreenPosition([3, 13])
editor.addCursorAtScreenPosition([3, 38])
editor.backspace()
expect(editor.lineTextForBufferRow(3)).toBe " var pivo = items.shift(), curren, left = [], right = [];"
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [3, 12]
expect(cursor2.getBufferPosition()).toEqual [3, 36]
[selection1, selection2] = editor.getSelections()
expect(selection1.isEmpty()).toBeTruthy()
expect(selection2.isEmpty()).toBeTruthy()
describe "when cursors are on different lines", ->
describe "when the cursors are in the middle of their lines", ->
it "removes the characters preceding each cursor", ->
editor.setCursorScreenPosition([3, 13])
editor.addCursorAtScreenPosition([4, 10])
editor.backspace()
expect(editor.lineTextForBufferRow(3)).toBe " var pivo = items.shift(), current, left = [], right = [];"
expect(editor.lineTextForBufferRow(4)).toBe " whileitems.length > 0) {"
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [3, 12]
expect(cursor2.getBufferPosition()).toEqual [4, 9]
[selection1, selection2] = editor.getSelections()
expect(selection1.isEmpty()).toBeTruthy()
expect(selection2.isEmpty()).toBeTruthy()
describe "when the cursors are on the first column of their lines", ->
it "removes the newlines preceding each cursor", ->
editor.setCursorScreenPosition([3, 0])
editor.addCursorAtScreenPosition([6, 0])
editor.backspace()
expect(editor.lineTextForBufferRow(2)).toBe " if (items.length <= 1) return items; var pivot = items.shift(), current, left = [], right = [];"
expect(editor.lineTextForBufferRow(3)).toBe " while(items.length > 0) {"
expect(editor.lineTextForBufferRow(4)).toBe " current = items.shift(); current < pivot ? left.push(current) : right.push(current);"
expect(editor.lineTextForBufferRow(5)).toBe " }"
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [2,40]
expect(cursor2.getBufferPosition()).toEqual [4,30]
describe "when there is a single selection", ->
it "deletes the selection, but not the character before it", ->
editor.setSelectedBufferRange([[0,5], [0,9]])
editor.backspace()
expect(editor.buffer.lineForRow(0)).toBe 'var qsort = function () {'
describe "when the selection ends on a folded line", ->
it "preserves the fold", ->
editor.setSelectedBufferRange([[3,0], [4,0]])
editor.foldBufferRow(4)
editor.backspace()
expect(buffer.lineForRow(3)).toBe " while(items.length > 0) {"
expect(editor.tokenizedLineForScreenRow(3).fold).toBeDefined()
describe "when there are multiple selections", ->
it "removes all selected text", ->
editor.setSelectedBufferRanges([[[0,4], [0,13]], [[0,16], [0,24]]])
editor.backspace()
expect(editor.lineTextForBufferRow(0)).toBe 'var = () {'
describe ".deleteToBeginningOfWord()", ->
describe "when no text is selected", ->
it "deletes all text between the cursor and the beginning of the word", ->
editor.setCursorBufferPosition([1, 24])
editor.addCursorAtBufferPosition([3, 5])
[cursor1, cursor2] = editor.getCursors()
editor.deleteToBeginningOfWord()
expect(buffer.lineForRow(1)).toBe ' var sort = function(ems) {'
expect(buffer.lineForRow(3)).toBe ' ar pivot = items.shift(), current, left = [], right = [];'
expect(cursor1.getBufferPosition()).toEqual [1, 22]
expect(cursor2.getBufferPosition()).toEqual [3, 4]
editor.deleteToBeginningOfWord()
expect(buffer.lineForRow(1)).toBe ' var sort = functionems) {'
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return itemsar pivot = items.shift(), current, left = [], right = [];'
expect(cursor1.getBufferPosition()).toEqual [1, 21]
expect(cursor2.getBufferPosition()).toEqual [2, 39]
editor.deleteToBeginningOfWord()
expect(buffer.lineForRow(1)).toBe ' var sort = ems) {'
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return ar pivot = items.shift(), current, left = [], right = [];'
expect(cursor1.getBufferPosition()).toEqual [1, 13]
expect(cursor2.getBufferPosition()).toEqual [2, 34]
editor.setText(' var sort')
editor.setCursorBufferPosition([0, 2])
editor.deleteToBeginningOfWord()
expect(buffer.lineForRow(0)).toBe 'var sort'
describe "when text is selected", ->
it "deletes only selected text", ->
editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]])
editor.deleteToBeginningOfWord()
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
expect(buffer.lineForRow(2)).toBe 'if (items.length <= 1) return items;'
describe '.deleteToEndOfLine()', ->
describe 'when no text is selected', ->
it 'deletes all text between the cursor and the end of the line', ->
editor.setCursorBufferPosition([1, 24])
editor.addCursorAtBufferPosition([2, 5])
[cursor1, cursor2] = editor.getCursors()
editor.deleteToEndOfLine()
expect(buffer.lineForRow(1)).toBe ' var sort = function(it'
expect(buffer.lineForRow(2)).toBe ' i'
expect(cursor1.getBufferPosition()).toEqual [1, 24]
expect(cursor2.getBufferPosition()).toEqual [2, 5]
describe 'when at the end of the line', ->
it 'deletes the next newline', ->
editor.setCursorBufferPosition([1, 30])
editor.deleteToEndOfLine()
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) { if (items.length <= 1) return items;'
describe 'when text is selected', ->
it 'deletes only the text in the selection', ->
editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]])
editor.deleteToEndOfLine()
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
expect(buffer.lineForRow(2)).toBe 'if (items.length <= 1) return items;'
describe ".deleteToBeginningOfLine()", ->
describe "when no text is selected", ->
it "deletes all text between the cursor and the beginning of the line", ->
editor.setCursorBufferPosition([1, 24])
editor.addCursorAtBufferPosition([2, 5])
[cursor1, cursor2] = editor.getCursors()
editor.deleteToBeginningOfLine()
expect(buffer.lineForRow(1)).toBe 'ems) {'
expect(buffer.lineForRow(2)).toBe 'f (items.length <= 1) return items;'
expect(cursor1.getBufferPosition()).toEqual [1, 0]
expect(cursor2.getBufferPosition()).toEqual [2, 0]
describe "when at the beginning of the line", ->
it "deletes the newline", ->
editor.setCursorBufferPosition([2])
editor.deleteToBeginningOfLine()
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) { if (items.length <= 1) return items;'
describe "when text is selected", ->
it "still deletes all text to begginning of the line", ->
editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]])
editor.deleteToBeginningOfLine()
expect(buffer.lineForRow(1)).toBe 'ems) {'
expect(buffer.lineForRow(2)).toBe ' if (items.length <= 1) return items;'
describe ".delete()", ->
describe "when there is a single cursor", ->
describe "when the cursor is on the middle of a line", ->
it "deletes the character following the cursor", ->
editor.setCursorScreenPosition([1, 6])
editor.delete()
expect(buffer.lineForRow(1)).toBe ' var ort = function(items) {'
describe "when the cursor is on the end of a line", ->
it "joins the line with the following line", ->
editor.setCursorScreenPosition([1, buffer.lineForRow(1).length])
editor.delete()
expect(buffer.lineForRow(1)).toBe ' var sort = function(items) { if (items.length <= 1) return items;'
describe "when the cursor is on the last column of the last line", ->
it "does nothing, but doesn't raise an error", ->
editor.setCursorScreenPosition([12, buffer.lineForRow(12).length])
editor.delete()
expect(buffer.lineForRow(12)).toBe '};'
describe "when the cursor is on the end of a line above a fold", ->
it "only deletes the lines inside the fold", ->
editor.foldBufferRow(4)
editor.setCursorScreenPosition([3, Infinity])
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(editor.getCursorScreenPosition()).toEqual cursorPositionBefore
describe "when the cursor is in the middle a line above a fold", ->
it "deletes as normal", ->
editor.foldBufferRow(4)
editor.setCursorScreenPosition([3, 4])
cursorPositionBefore = editor.getCursorScreenPosition()
editor.delete()
expect(buffer.lineForRow(3)).toBe " ar pivot = items.shift(), current, left = [], right = [];"
expect(editor.tokenizedLineForScreenRow(4).fold).toBeDefined()
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)
editor.delete()
expect(editor.tokenizedLineForScreenRow(2).text).toBe oldLine7
expect(editor.tokenizedLineForScreenRow(3).text).toBe oldLine8
describe "when there are multiple cursors", ->
describe "when cursors are on the same line", ->
it "removes the characters following each cursor", ->
editor.setCursorScreenPosition([3, 13])
editor.addCursorAtScreenPosition([3, 38])
editor.delete()
expect(editor.lineTextForBufferRow(3)).toBe " var pivot= items.shift(), current left = [], right = [];"
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [3, 13]
expect(cursor2.getBufferPosition()).toEqual [3, 37]
[selection1, selection2] = editor.getSelections()
expect(selection1.isEmpty()).toBeTruthy()
expect(selection2.isEmpty()).toBeTruthy()
describe "when cursors are on different lines", ->
describe "when the cursors are in the middle of the lines", ->
it "removes the characters following each cursor", ->
editor.setCursorScreenPosition([3, 13])
editor.addCursorAtScreenPosition([4, 10])
editor.delete()
expect(editor.lineTextForBufferRow(3)).toBe " var pivot= items.shift(), current, left = [], right = [];"
expect(editor.lineTextForBufferRow(4)).toBe " while(tems.length > 0) {"
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [3, 13]
expect(cursor2.getBufferPosition()).toEqual [4, 10]
[selection1, selection2] = editor.getSelections()
expect(selection1.isEmpty()).toBeTruthy()
expect(selection2.isEmpty()).toBeTruthy()
describe "when the cursors are at the end of their lines", ->
it "removes the newlines following each cursor", ->
editor.setCursorScreenPosition([0, 29])
editor.addCursorAtScreenPosition([1, 30])
editor.delete()
expect(editor.lineTextForBufferRow(0)).toBe "var quicksort = function () { var sort = function(items) { if (items.length <= 1) return items;"
[cursor1, cursor2] = editor.getCursors()
expect(cursor1.getBufferPosition()).toEqual [0,29]
expect(cursor2.getBufferPosition()).toEqual [0,59]
describe "when there is a single selection", ->
it "deletes the selection, but not the character following it", ->
editor.setSelectedBufferRanges([[[1, 24], [1, 27]], [[2, 0], [2, 4]]])
editor.delete()
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
expect(buffer.lineForRow(2)).toBe 'if (items.length <= 1) return items;'
expect(editor.getLastSelection().isEmpty()).toBeTruthy()
describe "when there are multiple selections", ->
describe "when selections are on the same line", ->
it "removes all selected text", ->
editor.setSelectedBufferRanges([[[0,4], [0,13]], [[0,16], [0,24]]])
editor.delete()
expect(editor.lineTextForBufferRow(0)).toBe 'var = () {'
describe ".deleteToEndOfWord()", ->
describe "when no text is selected", ->
it "deletes to the end of the word", ->
editor.setCursorBufferPosition([1, 24])
editor.addCursorAtBufferPosition([2, 5])
[cursor1, cursor2] = editor.getCursors()
editor.deleteToEndOfWord()
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
expect(buffer.lineForRow(2)).toBe ' i (items.length <= 1) return items;'
expect(cursor1.getBufferPosition()).toEqual [1, 24]
expect(cursor2.getBufferPosition()).toEqual [2, 5]
editor.deleteToEndOfWord()
expect(buffer.lineForRow(1)).toBe ' var sort = function(it {'
expect(buffer.lineForRow(2)).toBe ' iitems.length <= 1) return items;'
expect(cursor1.getBufferPosition()).toEqual [1, 24]
expect(cursor2.getBufferPosition()).toEqual [2, 5]
describe "when text is selected", ->
it "deletes only selected text", ->
editor.setSelectedBufferRange([[1, 24], [1, 27]])
editor.deleteToEndOfWord()
expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {'
describe ".indent()", ->
describe "when the selection is empty", ->
describe "when autoIndent is disabled", ->
describe "if 'softTabs' is true (the default)", ->
it "inserts 'tabLength' spaces into the buffer", ->
tabRegex = new RegExp("^[ ]{#{editor.getTabLength()}}")
expect(buffer.lineForRow(0)).not.toMatch(tabRegex)
editor.indent()
expect(buffer.lineForRow(0)).toMatch(tabRegex)
it "respects the tab stops when cursor is in the middle of a tab", ->
editor.setTabLength(4)
buffer.insert([12, 2], "\n ")
editor.setCursorBufferPosition [13, 1]
editor.indent()
expect(buffer.lineForRow(13)).toMatch /^\s+$/
expect(buffer.lineForRow(13).length).toBe 4
expect(editor.getCursorBufferPosition()).toEqual [13, 4]
buffer.insert([13, 0], " ")
editor.setCursorBufferPosition [13, 6]
editor.indent()
expect(buffer.lineForRow(13).length).toBe 8
describe "if 'softTabs' is false", ->
it "insert a \t into the buffer", ->
editor.setSoftTabs(false)
expect(buffer.lineForRow(0)).not.toMatch(/^\t/)
editor.indent()
expect(buffer.lineForRow(0)).toMatch(/^\t/)
describe "when autoIndent is enabled", ->
describe "when the cursor's column is less than the suggested level of indentation", ->
describe "when 'softTabs' is true (the default)", ->
it "moves the cursor to the end of the leading whitespace and inserts enough whitespace to bring the line to the suggested level of indentaion", ->
buffer.insert([5, 0], " \n")
editor.setCursorBufferPosition [5, 0]
editor.indent(autoIndent: true)
expect(buffer.lineForRow(5)).toMatch /^\s+$/
expect(buffer.lineForRow(5).length).toBe 6
expect(editor.getCursorBufferPosition()).toEqual [5, 6]
it "respects the tab stops when cursor is in the middle of a tab", ->
editor.setTabLength(4)
buffer.insert([12, 2], "\n ")
editor.setCursorBufferPosition [13, 1]
editor.indent(autoIndent: true)
expect(buffer.lineForRow(13)).toMatch /^\s+$/
expect(buffer.lineForRow(13).length).toBe 4
expect(editor.getCursorBufferPosition()).toEqual [13, 4]
buffer.insert([13, 0], " ")
editor.setCursorBufferPosition [13, 6]
editor.indent(autoIndent: true)
expect(buffer.lineForRow(13).length).toBe 8
describe "when 'softTabs' is false", ->
it "moves the cursor to the end of the leading whitespace and inserts enough tabs to bring the line to the suggested level of indentaion", ->
convertToHardTabs(buffer)
editor.setSoftTabs(false)
buffer.insert([5, 0], "\t\n")
editor.setCursorBufferPosition [5, 0]
editor.indent(autoIndent: true)
expect(buffer.lineForRow(5)).toMatch /^\t\t\t$/
expect(editor.getCursorBufferPosition()).toEqual [5, 3]
describe "when the difference between the suggested level of indentation and the current level of indentation is greater than 0 but less than 1", ->
it "inserts one tab", ->
editor.setSoftTabs(false)
buffer.setText(" \ntest")
editor.setCursorBufferPosition [1, 0]
editor.indent(autoIndent: true)
expect(buffer.lineForRow(1)).toBe '\ttest'
expect(editor.getCursorBufferPosition()).toEqual [1, 1]
describe "when the line's indent level is greater than the suggested level of indentation", ->
describe "when 'softTabs' is true (the default)", ->
it "moves the cursor to the end of the leading whitespace and inserts 'tabLength' spaces into the buffer", ->
buffer.insert([7, 0], " \n")
editor.setCursorBufferPosition [7, 2]
editor.indent(autoIndent: true)
expect(buffer.lineForRow(7)).toMatch /^\s+$/
expect(buffer.lineForRow(7).length).toBe 8
expect(editor.getCursorBufferPosition()).toEqual [7, 8]
describe "when 'softTabs' is false", ->
it "moves the cursor to the end of the leading whitespace and inserts \t into the buffer", ->
convertToHardTabs(buffer)
editor.setSoftTabs(false)
buffer.insert([7, 0], "\t\t\t\n")
editor.setCursorBufferPosition [7, 1]
editor.indent(autoIndent: true)
expect(buffer.lineForRow(7)).toMatch /^\t\t\t\t$/
expect(editor.getCursorBufferPosition()).toEqual [7, 4]
describe "when the selection is not empty", ->
it "indents the selected lines", ->
editor.setSelectedBufferRange([[0, 0], [10, 0]])
selection = editor.getLastSelection()
spyOn(selection, "indentSelectedRows")
editor.indent()
expect(selection.indentSelectedRows).toHaveBeenCalled()
describe "if editor.softTabs is false", ->
it "inserts a tab character into the buffer", ->
editor.setSoftTabs(false)
expect(buffer.lineForRow(0)).not.toMatch(/^\t/)
editor.indent()
expect(buffer.lineForRow(0)).toMatch(/^\t/)
expect(editor.getCursorBufferPosition()).toEqual [0, 1]
expect(editor.getCursorScreenPosition()).toEqual [0, editor.getTabLength()]
editor.indent()
expect(buffer.lineForRow(0)).toMatch(/^\t\t/)
expect(editor.getCursorBufferPosition()).toEqual [0, 2]
expect(editor.getCursorScreenPosition()).toEqual [0, editor.getTabLength() * 2]
describe "clipboard operations", ->
describe ".cutSelectedText()", ->
it "removes the selected text from the buffer and places it on the clipboard", ->
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]])
editor.cutSelectedText()
expect(buffer.lineForRow(0)).toBe "var = function () {"
expect(buffer.lineForRow(1)).toBe " var = function(items) {"
expect(clipboard.readText()).toBe 'quicksort\nsort'
describe "when no text is selected", ->
beforeEach ->
editor.setSelectedBufferRanges([
[[0, 0], [0, 0]],
[[5, 0], [5, 0]],
])
it "cuts the lines on which there are cursors", ->
editor.cutSelectedText()
expect(buffer.getLineCount()).toBe(11)
expect(buffer.lineForRow(1)).toBe(" if (items.length <= 1) return items;")
expect(buffer.lineForRow(4)).toBe(" current < pivot ? left.push(current) : right.push(current);")
expect(atom.clipboard.read()).toEqual """
var quicksort = function () {
current = items.shift();
"""
describe ".cutToEndOfLine()", ->
describe "when soft wrap is on", ->
it "cuts up to the end of the line", ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(10)
editor.setCursorScreenPosition([2, 2])
editor.cutToEndOfLine()
expect(editor.tokenizedLineForScreenRow(2).text).toBe '= () {'
describe "when soft wrap is off", ->
describe "when nothing is selected", ->
it "cuts up to the end of the line", ->
editor.setCursorBufferPosition([2, 20])
editor.addCursorAtBufferPosition([3, 20])
editor.cutToEndOfLine()
expect(buffer.lineForRow(2)).toBe ' if (items.length'
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
expect(atom.clipboard.read()).toBe ' <= 1) return items;\ns.shift(), current, left = [], right = [];'
describe "when text is selected", ->
it "only cuts the selected text, not to the end of the line", ->
editor.setSelectedBufferRanges([[[2,20], [2, 30]], [[3, 20], [3, 20]]])
editor.cutToEndOfLine()
expect(buffer.lineForRow(2)).toBe ' if (items.lengthurn items;'
expect(buffer.lineForRow(3)).toBe ' var pivot = item'
expect(atom.clipboard.read()).toBe ' <= 1) ret\ns.shift(), current, left = [], right = [];'
describe ".copySelectedText()", ->
it "copies selected text onto the clipboard", ->
editor.setSelectedBufferRanges([[[0,4], [0,13]], [[1,6], [1, 10]], [[2,8], [2, 13]]])
editor.copySelectedText()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe " var sort = function(items) {"
expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items;"
expect(clipboard.readText()).toBe 'quicksort\nsort\nitems'
expect(atom.clipboard.read()).toEqual """
quicksort
sort
items
"""
describe "when no text is selected", ->
beforeEach ->
editor.setSelectedBufferRanges([
[[1, 5], [1, 5]],
[[5, 8], [5, 8]]
])
it "copies the lines on which there are cursors", ->
editor.copySelectedText()
expect(atom.clipboard.read()).toEqual([
" var sort = function(items) {\n"
" current = items.shift();\n"
].join("\n"))
expect(editor.getSelectedBufferRanges()).toEqual([
[[1, 5], [1, 5]],
[[5, 8], [5, 8]]
])
describe ".pasteText()", ->
copyText = (text, {startColumn, textEditor}={}) ->
startColumn ?= 0
textEditor ?= editor
textEditor.setCursorBufferPosition([0, 0])
textEditor.insertText(text)
numberOfNewlines = text.match(/\n/g)?.length
endColumn = text.match(/[^\n]*$/)[0]?.length
textEditor.getLastSelection().setBufferRange([[0,startColumn], [numberOfNewlines,endColumn]])
textEditor.cutSelectedText()
it "pastes text into the buffer", ->
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]])
atom.clipboard.write('first')
editor.pasteText()
expect(editor.lineTextForBufferRow(0)).toBe "var first = function () {"
expect(editor.lineTextForBufferRow(1)).toBe " var first = function(items) {"
describe "when `autoIndentOnPaste` is true", ->
beforeEach ->
atom.config.set("editor.autoIndentOnPaste", true)
describe "when only whitespace precedes the cursor", ->
it "auto-indents the lines spanned by the pasted text, based on the first pasted line", ->
expect(editor.indentationForBufferRow(5)).toBe(3)
atom.clipboard.write("a(x);\n b(x);\n c(x);\n", indentBasis: 0)
editor.setCursorBufferPosition([5, 0])
editor.pasteText()
# Adjust the indentation of the pasted block
expect(editor.indentationForBufferRow(5)).toBe(3)
expect(editor.indentationForBufferRow(6)).toBe(4)
expect(editor.indentationForBufferRow(7)).toBe(5)
# Preserve the indentation of the next row
expect(editor.indentationForBufferRow(8)).toBe(3)
describe "when non-whitespace characters precede the cursor", ->
it "does not auto-indent the first line being pasted", ->
editor.setText """
if (x) {
y();
}
"""
atom.clipboard.write(" z();")
editor.setCursorBufferPosition([1, Infinity])
editor.pasteText()
expect(editor.lineTextForBufferRow(1)).toBe(" y(); z();")
describe "when `autoIndentOnPaste` is false", ->
beforeEach ->
atom.config.set('editor.autoIndentOnPaste', false)
describe "when the cursor is indented further than the original copied text", ->
it "increases the indentation of the copied lines to match", ->
editor.setSelectedBufferRange([[1, 2], [3, 0]])
editor.copySelectedText()
editor.setCursorBufferPosition([5, 6])
editor.pasteText()
expect(editor.lineTextForBufferRow(5)).toBe " var sort = function(items) {"
expect(editor.lineTextForBufferRow(6)).toBe " if (items.length <= 1) return items;"
describe "when the cursor is indented less far than the original copied text", ->
it "decreases the indentation of the copied lines to match", ->
editor.setSelectedBufferRange([[6, 6], [8, 0]])
editor.copySelectedText()
editor.setCursorBufferPosition([1, 2])
editor.pasteText()
expect(editor.lineTextForBufferRow(1)).toBe " current < pivot ? left.push(current) : right.push(current);"
expect(editor.lineTextForBufferRow(2)).toBe "}"
describe "when the first copied line has leading whitespace", ->
it "preserves the line's leading whitespace", ->
editor.setSelectedBufferRange([[4, 0], [6, 0]])
editor.copySelectedText()
editor.setCursorBufferPosition([0, 0])
editor.pasteText()
expect(editor.lineTextForBufferRow(0)).toBe " while(items.length > 0) {"
expect(editor.lineTextForBufferRow(1)).toBe " current = items.shift();"
describe 'when the clipboard has many selections', ->
beforeEach ->
atom.config.set("editor.autoIndentOnPaste", false)
editor.setSelectedBufferRanges([[[0, 4], [0, 13]], [[1, 6], [1, 10]]])
editor.copySelectedText()
it "pastes each selection separately into the buffer", ->
editor.copySelectedText()
editor.moveRight()
editor.insertText("_")
editor.pasteText()
expect(editor.lineTextForBufferRow(0)).toBe "var quicksort_quicksort = function () {"
expect(editor.lineTextForBufferRow(1)).toBe " var sort_sort = function(items) {"
describe 'and the selections count does not match', ->
beforeEach ->
editor.setSelectedBufferRanges([[[0, 4], [0, 13]]])
it "pastes the whole text into the buffer", ->
editor.pasteText()
expect(editor.lineTextForBufferRow(0)).toBe "var quicksort"
expect(editor.lineTextForBufferRow(1)).toBe "sort = function () {"
describe "when a full line was cut", ->
beforeEach ->
editor.setCursorBufferPosition([2, 13])
editor.cutSelectedText()
editor.setCursorBufferPosition([2, 13])
it "pastes the line above the cursor and retains the cursor's column", ->
editor.pasteText()
expect(editor.lineTextForBufferRow(2)).toBe(" if (items.length <= 1) return items;")
expect(editor.lineTextForBufferRow(3)).toBe(" var pivot = items.shift(), current, left = [], right = [];")
expect(editor.getCursorBufferPosition()).toEqual([3, 13])
describe "when a full line was copied", ->
beforeEach ->
editor.setCursorBufferPosition([2, 13])
editor.copySelectedText()
describe "when there is a selection", ->
it "overwrites the selection as with any copied text", ->
editor.setSelectedBufferRange([[1, 2], [1, Infinity]])
editor.pasteText()
expect(editor.lineTextForBufferRow(1)).toBe(" if (items.length <= 1) return items;")
expect(editor.lineTextForBufferRow(2)).toBe("")
expect(editor.lineTextForBufferRow(3)).toBe(" if (items.length <= 1) return items;")
expect(editor.getCursorBufferPosition()).toEqual([2, 0])
describe "when there is no selection", ->
it "pastes the line above the cursor and retains the cursor's column", ->
editor.pasteText()
expect(editor.lineTextForBufferRow(2)).toBe(" if (items.length <= 1) return items;")
expect(editor.lineTextForBufferRow(3)).toBe(" if (items.length <= 1) return items;")
expect(editor.getCursorBufferPosition()).toEqual([3, 13])
describe ".indentSelectedRows()", ->
describe "when nothing is selected", ->
describe "when softTabs is enabled", ->
it "indents line and retains selection", ->
editor.setSelectedBufferRange([[0,3], [0,3]])
editor.indentSelectedRows()
expect(buffer.lineForRow(0)).toBe " var quicksort = function () {"
expect(editor.getSelectedBufferRange()).toEqual [[0, 3 + editor.getTabLength()], [0, 3 + editor.getTabLength()]]
describe "when softTabs is disabled", ->
it "indents line and retains selection", ->
convertToHardTabs(buffer)
editor.setSoftTabs(false)
editor.setSelectedBufferRange([[0,3], [0,3]])
editor.indentSelectedRows()
expect(buffer.lineForRow(0)).toBe "\tvar quicksort = function () {"
expect(editor.getSelectedBufferRange()).toEqual [[0, 3 + 1], [0, 3 + 1]]
describe "when one line is selected", ->
describe "when softTabs is enabled", ->
it "indents line and retains selection", ->
editor.setSelectedBufferRange([[0,4], [0,14]])
editor.indentSelectedRows()
expect(buffer.lineForRow(0)).toBe "#{editor.getTabText()}var quicksort = function () {"
expect(editor.getSelectedBufferRange()).toEqual [[0, 4 + editor.getTabLength()], [0, 14 + editor.getTabLength()]]
describe "when softTabs is disabled", ->
it "indents line and retains selection", ->
convertToHardTabs(buffer)
editor.setSoftTabs(false)
editor.setSelectedBufferRange([[0,4], [0,14]])
editor.indentSelectedRows()
expect(buffer.lineForRow(0)).toBe "\tvar quicksort = function () {"
expect(editor.getSelectedBufferRange()).toEqual [[0, 4 + 1], [0, 14 + 1]]
describe "when multiple lines are selected", ->
describe "when softTabs is enabled", ->
it "indents selected lines (that are not empty) and retains selection", ->
editor.setSelectedBufferRange([[9,1], [11,15]])
editor.indentSelectedRows()
expect(buffer.lineForRow(9)).toBe " };"
expect(buffer.lineForRow(10)).toBe ""
expect(buffer.lineForRow(11)).toBe " return sort(Array.apply(this, arguments));"
expect(editor.getSelectedBufferRange()).toEqual [[9, 1 + editor.getTabLength()], [11, 15 + editor.getTabLength()]]
it "does not indent the last row if the selection ends at column 0", ->
editor.setSelectedBufferRange([[9,1], [11,0]])
editor.indentSelectedRows()
expect(buffer.lineForRow(9)).toBe " };"
expect(buffer.lineForRow(10)).toBe ""
expect(buffer.lineForRow(11)).toBe " return sort(Array.apply(this, arguments));"
expect(editor.getSelectedBufferRange()).toEqual [[9, 1 + editor.getTabLength()], [11, 0]]
describe "when softTabs is disabled", ->
it "indents selected lines (that are not empty) and retains selection", ->
convertToHardTabs(buffer)
editor.setSoftTabs(false)
editor.setSelectedBufferRange([[9,1], [11,15]])
editor.indentSelectedRows()
expect(buffer.lineForRow(9)).toBe "\t\t};"
expect(buffer.lineForRow(10)).toBe ""
expect(buffer.lineForRow(11)).toBe "\t\treturn sort(Array.apply(this, arguments));"
expect(editor.getSelectedBufferRange()).toEqual [[9, 1 + 1], [11, 15 + 1]]
describe ".outdentSelectedRows()", ->
describe "when nothing is selected", ->
it "outdents line and retains selection", ->
editor.setSelectedBufferRange([[1,3], [1,3]])
editor.outdentSelectedRows()
expect(buffer.lineForRow(1)).toBe "var sort = function(items) {"
expect(editor.getSelectedBufferRange()).toEqual [[1, 3 - editor.getTabLength()], [1, 3 - editor.getTabLength()]]
it "outdents when indent is less than a tab length", ->
editor.insertText(' ')
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
it "outdents a single hard tab when indent is multiple hard tabs and and the session is using soft tabs", ->
editor.insertText('\t\t')
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe "\tvar quicksort = function () {"
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
it "outdents when a mix of hard tabs and soft tabs are used", ->
editor.insertText('\t ')
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe " var quicksort = function () {"
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe " var quicksort = function () {"
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
it "outdents only up to the first non-space non-tab character", ->
editor.insertText(' \tfoo\t ')
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe "\tfoo\t var quicksort = function () {"
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe "foo\t var quicksort = function () {"
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe "foo\t var quicksort = function () {"
describe "when one line is selected", ->
it "outdents line and retains editor", ->
editor.setSelectedBufferRange([[1,4], [1,14]])
editor.outdentSelectedRows()
expect(buffer.lineForRow(1)).toBe "var sort = function(items) {"
expect(editor.getSelectedBufferRange()).toEqual [[1, 4 - editor.getTabLength()], [1, 14 - editor.getTabLength()]]
describe "when multiple lines are selected", ->
it "outdents selected lines and retains editor", ->
editor.setSelectedBufferRange([[0,1], [3,15]])
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe "var sort = function(items) {"
expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items;"
expect(buffer.lineForRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];"
expect(editor.getSelectedBufferRange()).toEqual [[0, 1], [3, 15 - editor.getTabLength()]]
it "does not outdent the last line of the selection if it ends at column 0", ->
editor.setSelectedBufferRange([[0,1], [3,0]])
editor.outdentSelectedRows()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe "var sort = function(items) {"
expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items;"
expect(buffer.lineForRow(3)).toBe " var pivot = items.shift(), current, left = [], right = [];"
expect(editor.getSelectedBufferRange()).toEqual [[0, 1], [3, 0]]
describe ".autoIndentSelectedRows", ->
it "auto-indents the selection", ->
editor.setCursorBufferPosition([2, 0])
editor.insertText("function() {\ninside=true\n}\n i=1\n")
editor.getLastSelection().setBufferRange([[2,0], [6,0]])
editor.autoIndentSelectedRows()
expect(editor.lineTextForBufferRow(2)).toBe " function() {"
expect(editor.lineTextForBufferRow(3)).toBe " inside=true"
expect(editor.lineTextForBufferRow(4)).toBe " }"
expect(editor.lineTextForBufferRow(5)).toBe " i=1"
describe ".toggleLineCommentsInSelection()", ->
it "toggles comments on the selected lines", ->
editor.setSelectedBufferRange([[4, 5], [7, 5]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {"
expect(buffer.lineForRow(5)).toBe " // current = items.shift();"
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
expect(buffer.lineForRow(7)).toBe " // }"
expect(editor.getSelectedBufferRange()).toEqual [[4, 8], [7, 8]]
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
expect(buffer.lineForRow(5)).toBe " current = items.shift();"
expect(buffer.lineForRow(6)).toBe " current < pivot ? left.push(current) : right.push(current);"
expect(buffer.lineForRow(7)).toBe " }"
it "does not comment the last line of a non-empty selection if it ends at column 0", ->
editor.setSelectedBufferRange([[4, 5], [7, 0]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(4)).toBe " // while(items.length > 0) {"
expect(buffer.lineForRow(5)).toBe " // current = items.shift();"
expect(buffer.lineForRow(6)).toBe " // current < pivot ? left.push(current) : right.push(current);"
expect(buffer.lineForRow(7)).toBe " }"
it "uncomments lines if all lines match the comment regex", ->
editor.setSelectedBufferRange([[0, 0], [0, 1]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "// var quicksort = function () {"
editor.setSelectedBufferRange([[0, 0], [2, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "// // var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe "// var sort = function(items) {"
expect(buffer.lineForRow(2)).toBe "// if (items.length <= 1) return items;"
editor.setSelectedBufferRange([[0, 0], [2, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "// var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe " var sort = function(items) {"
expect(buffer.lineForRow(2)).toBe " if (items.length <= 1) return items;"
editor.setSelectedBufferRange([[0, 0], [0, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
it "uncomments commented lines separated by an empty line", ->
editor.setSelectedBufferRange([[0, 0], [1, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "// var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe "// var sort = function(items) {"
buffer.insert([0, Infinity], '\n')
editor.setSelectedBufferRange([[0, 0], [2, Infinity]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(0)).toBe "var quicksort = function () {"
expect(buffer.lineForRow(1)).toBe ""
expect(buffer.lineForRow(2)).toBe " var sort = function(items) {"
it "preserves selection emptiness", ->
editor.setCursorBufferPosition([4, 0])
editor.toggleLineCommentsInSelection()
expect(editor.getLastSelection().isEmpty()).toBeTruthy()
it "does not explode if the current language mode has no comment regex", ->
editor.destroy()
waitsForPromise ->
atom.workspace.open(null, autoIndent: false).then (o) -> editor = o
runs ->
editor.setSelectedBufferRange([[4, 5], [4, 5]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(4)).toBe " while(items.length > 0) {"
it "uncomments when the line lacks the trailing whitespace in the comment regex", ->
editor.setCursorBufferPosition([10, 0])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(10)).toBe "// "
expect(editor.getSelectedBufferRange()).toEqual [[10, 3], [10, 3]]
editor.backspace()
expect(buffer.lineForRow(10)).toBe "//"
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(10)).toBe ""
expect(editor.getSelectedBufferRange()).toEqual [[10, 0], [10, 0]]
it "uncomments when the line has leading whitespace", ->
editor.setCursorBufferPosition([10, 0])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(10)).toBe "// "
editor.moveToBeginningOfLine()
editor.insertText(" ")
editor.setSelectedBufferRange([[10, 0], [10, 0]])
editor.toggleLineCommentsInSelection()
expect(buffer.lineForRow(10)).toBe " "
describe ".undo() and .redo()", ->
it "undoes/redoes the last change", ->
editor.insertText("foo")
editor.undo()
expect(buffer.lineForRow(0)).not.toContain "foo"
editor.redo()
expect(buffer.lineForRow(0)).toContain "foo"
it "batches the undo / redo of changes caused by multiple cursors", ->
editor.setCursorScreenPosition([0, 0])
editor.addCursorAtScreenPosition([1, 0])
editor.insertText("foo")
editor.backspace()
expect(buffer.lineForRow(0)).toContain "fovar"
expect(buffer.lineForRow(1)).toContain "fo "
editor.undo()
expect(buffer.lineForRow(0)).toContain "foo"
expect(buffer.lineForRow(1)).toContain "foo"
editor.redo()
expect(buffer.lineForRow(0)).not.toContain "foo"
expect(buffer.lineForRow(0)).toContain "fovar"
it "restores the selected ranges after undo and redo", ->
editor.setSelectedBufferRanges([[[1, 6], [1, 10]], [[1, 22], [1, 27]]])
editor.delete()
editor.delete()
selections = editor.getSelections()
expect(buffer.lineForRow(1)).toBe ' var = function( {'
expect(editor.getSelectedBufferRanges()).toEqual [[[1, 6], [1, 6]], [[1, 17], [1, 17]]]
editor.undo()
expect(editor.getSelectedBufferRanges()).toEqual [[[1, 6], [1, 6]], [[1, 18], [1, 18]]]
editor.undo()
expect(editor.getSelectedBufferRanges()).toEqual [[[1, 6], [1, 10]], [[1, 22], [1, 27]]]
editor.redo()
expect(editor.getSelectedBufferRanges()).toEqual [[[1, 6], [1, 6]], [[1, 18], [1, 18]]]
xit "restores folds after undo and redo", ->
editor.foldBufferRow(1)
editor.setSelectedBufferRange([[1, 0], [10, Infinity]], preserveFolds: true)
expect(editor.isFoldedAtBufferRow(1)).toBeTruthy()
editor.insertText """
\ // testing
function foo() {
return 1 + 2;
}
"""
expect(editor.isFoldedAtBufferRow(1)).toBeFalsy()
editor.foldBufferRow(2)
editor.undo()
expect(editor.isFoldedAtBufferRow(1)).toBeTruthy()
expect(editor.isFoldedAtBufferRow(9)).toBeTruthy()
expect(editor.isFoldedAtBufferRow(10)).toBeFalsy()
editor.redo()
expect(editor.isFoldedAtBufferRow(1)).toBeFalsy()
expect(editor.isFoldedAtBufferRow(2)).toBeTruthy()
describe "::transact", ->
it "restores the selection when the transaction is undone/redone", ->
buffer.setText('1234')
editor.setSelectedBufferRange([[0, 1], [0, 3]])
editor.transact ->
editor.delete()
editor.moveToEndOfLine()
editor.insertText('5')
expect(buffer.getText()).toBe '145'
editor.undo()
expect(buffer.getText()).toBe '1234'
expect(editor.getSelectedBufferRange()).toEqual [[0, 1], [0, 3]]
editor.redo()
expect(buffer.getText()).toBe '145'
expect(editor.getSelectedBufferRange()).toEqual [[0, 3], [0, 3]]
describe "when the buffer is changed (via its direct api, rather than via than edit session)", ->
it "moves the cursor so it is in the same relative position of the buffer", ->
expect(editor.getCursorScreenPosition()).toEqual [0, 0]
editor.addCursorAtScreenPosition([0, 5])
editor.addCursorAtScreenPosition([1, 0])
[cursor1, cursor2, cursor3] = editor.getCursors()
buffer.insert([0, 1], 'abc')
expect(cursor1.getScreenPosition()).toEqual [0, 0]
expect(cursor2.getScreenPosition()).toEqual [0, 8]
expect(cursor3.getScreenPosition()).toEqual [1, 0]
it "does not destroy cursors or selections when a change encompasses them", ->
cursor = editor.getLastCursor()
cursor.setBufferPosition [3, 3]
editor.buffer.delete([[3, 1], [3, 5]])
expect(cursor.getBufferPosition()).toEqual [3, 1]
expect(editor.getCursors().indexOf(cursor)).not.toBe -1
selection = editor.getLastSelection()
selection.setBufferRange [[3, 5], [3, 10]]
editor.buffer.delete [[3, 3], [3, 8]]
expect(selection.getBufferRange()).toEqual [[3, 3], [3, 5]]
expect(editor.getSelections().indexOf(selection)).not.toBe -1
it "merges cursors when the change causes them to overlap", ->
editor.setCursorScreenPosition([0, 0])
editor.addCursorAtScreenPosition([0, 2])
editor.addCursorAtScreenPosition([1, 2])
[cursor1, cursor2, cursor3] = editor.getCursors()
expect(editor.getCursors().length).toBe 3
buffer.delete([[0, 0], [0, 2]])
expect(editor.getCursors().length).toBe 2
expect(editor.getCursors()).toEqual [cursor1, cursor3]
expect(cursor1.getBufferPosition()).toEqual [0,0]
expect(cursor3.getBufferPosition()).toEqual [1,2]
describe 'reading text', ->
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()
describe ".deleteLine()", ->
it "deletes the first line when the cursor is there", ->
editor.getLastCursor().moveToTop()
line1 = buffer.lineForRow(1)
count = buffer.getLineCount()
expect(buffer.lineForRow(0)).not.toBe(line1)
editor.deleteLine()
expect(buffer.lineForRow(0)).toBe(line1)
expect(buffer.getLineCount()).toBe(count - 1)
it "deletes the last line when the cursor is there", ->
count = buffer.getLineCount()
secondToLastLine = buffer.lineForRow(count - 2)
expect(buffer.lineForRow(count - 1)).not.toBe(secondToLastLine)
editor.getLastCursor().moveToBottom()
editor.deleteLine()
newCount = buffer.getLineCount()
expect(buffer.lineForRow(newCount - 1)).toBe(secondToLastLine)
expect(newCount).toBe(count - 1)
it "deletes whole lines when partial lines are selected", ->
editor.setSelectedBufferRange([[0, 2], [1, 2]])
line2 = buffer.lineForRow(2)
count = buffer.getLineCount()
expect(buffer.lineForRow(0)).not.toBe(line2)
expect(buffer.lineForRow(1)).not.toBe(line2)
editor.deleteLine()
expect(buffer.lineForRow(0)).toBe(line2)
expect(buffer.getLineCount()).toBe(count - 2)
it "only deletes first line if only newline is selected on second line", ->
editor.setSelectedBufferRange([[0, 2], [1, 0]])
line1 = buffer.lineForRow(1)
count = buffer.getLineCount()
expect(buffer.lineForRow(0)).not.toBe(line1)
editor.deleteLine()
expect(buffer.lineForRow(0)).toBe(line1)
expect(buffer.getLineCount()).toBe(count - 1)
it "deletes the entire region when invoke on a folded region", ->
editor.foldBufferRow(1)
editor.getLastCursor().moveToTop()
editor.getLastCursor().moveDown()
expect(buffer.getLineCount()).toBe(13)
editor.deleteLine()
expect(buffer.getLineCount()).toBe(4)
it "deletes the entire file from the bottom up", ->
count = buffer.getLineCount()
expect(count).toBeGreaterThan(0)
for line in [0...count]
editor.getLastCursor().moveToBottom()
editor.deleteLine()
expect(buffer.getLineCount()).toBe(1)
expect(buffer.getText()).toBe('')
it "deletes the entire file from the top down", ->
count = buffer.getLineCount()
expect(count).toBeGreaterThan(0)
for line in [0...count]
editor.getLastCursor().moveToTop()
editor.deleteLine()
expect(buffer.getLineCount()).toBe(1)
expect(buffer.getText()).toBe('')
describe "when soft wrap is enabled", ->
it "deletes the entire line that the cursor is on", ->
editor.setSoftWrapped(true)
editor.setEditorWidthInChars(10)
editor.setCursorBufferPosition([6])
line7 = buffer.lineForRow(7)
count = buffer.getLineCount()
expect(buffer.lineForRow(6)).not.toBe(line7)
editor.deleteLine()
expect(buffer.lineForRow(6)).toBe(line7)
expect(buffer.getLineCount()).toBe(count - 1)
describe "when the line being deleted preceeds a fold, and the command is undone", ->
it "restores the line and preserves the fold", ->
editor.setCursorBufferPosition([4])
editor.foldCurrentRow()
expect(editor.isFoldedAtScreenRow(4)).toBeTruthy()
editor.setCursorBufferPosition([3])
editor.deleteLine()
expect(editor.isFoldedAtScreenRow(3)).toBeTruthy()
expect(buffer.lineForRow(3)).toBe ' while(items.length > 0) {'
editor.undo()
expect(editor.isFoldedAtScreenRow(4)).toBeTruthy()
expect(buffer.lineForRow(3)).toBe ' var pivot = items.shift(), current, left = [], right = [];'
describe ".replaceSelectedText(options, fn)", ->
describe "when no text is selected", ->
it "inserts the text returned from the function at the cursor position", ->
editor.replaceSelectedText {}, -> '123'
expect(buffer.lineForRow(0)).toBe '123var quicksort = function () {'
editor.replaceSelectedText {selectWordIfEmpty: true}, -> 'var'
editor.setCursorBufferPosition([0])
expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {'
editor.setCursorBufferPosition([10])
editor.replaceSelectedText null, -> ''
expect(buffer.lineForRow(10)).toBe ''
describe "when text is selected", ->
it "replaces the selected text with the text returned from the function", ->
editor.setSelectedBufferRange([[0, 1], [0, 3]])
editor.replaceSelectedText {}, -> 'ia'
expect(buffer.lineForRow(0)).toBe 'via quicksort = function () {'
describe ".transpose()", ->
it "swaps two characters", ->
editor.buffer.setText("abc")
editor.setCursorScreenPosition([0, 1])
editor.transpose()
expect(editor.lineTextForBufferRow(0)).toBe 'bac'
it "reverses a selection", ->
editor.buffer.setText("xabcz")
editor.setSelectedBufferRange([[0, 1], [0, 4]])
editor.transpose()
expect(editor.lineTextForBufferRow(0)).toBe 'xcbaz'
describe ".upperCase()", ->
describe "when there is no selection", ->
it "upper cases the current word", ->
editor.buffer.setText("aBc")
editor.setCursorScreenPosition([0, 1])
editor.upperCase()
expect(editor.lineTextForBufferRow(0)).toBe 'ABC'
expect(editor.getSelectedBufferRange()).toEqual [[0, 1], [0, 1]]
describe "when there is a selection", ->
it "upper cases the current selection", ->
editor.buffer.setText("abc")
editor.setSelectedBufferRange([[0,0], [0,2]])
editor.upperCase()
expect(editor.lineTextForBufferRow(0)).toBe 'ABc'
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 2]]
describe ".lowerCase()", ->
describe "when there is no selection", ->
it "lower cases the current word", ->
editor.buffer.setText("aBC")
editor.setCursorScreenPosition([0, 1])
editor.lowerCase()
expect(editor.lineTextForBufferRow(0)).toBe 'abc'
expect(editor.getSelectedBufferRange()).toEqual [[0, 1], [0, 1]]
describe "when there is a selection", ->
it "lower cases the current selection", ->
editor.buffer.setText("ABC")
editor.setSelectedBufferRange([[0,0], [0,2]])
editor.lowerCase()
expect(editor.lineTextForBufferRow(0)).toBe 'abC'
expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 2]]
describe "soft-tabs detection", ->
it "assigns soft / hard tabs based on the contents of the buffer, or uses the default if unknown", ->
waitsForPromise ->
atom.workspace.open('sample.js', softTabs: false).then (editor) ->
expect(editor.getSoftTabs()).toBeTruthy()
waitsForPromise ->
atom.workspace.open('sample-with-tabs.coffee', softTabs: true).then (editor) ->
expect(editor.getSoftTabs()).toBeFalsy()
waitsForPromise ->
atom.workspace.open('sample-with-tabs-and-initial-comment.js', softTabs: true).then (editor) ->
expect(editor.getSoftTabs()).toBeFalsy()
waitsForPromise ->
atom.workspace.open(null, softTabs: false).then (editor) ->
expect(editor.getSoftTabs()).toBeFalsy()
describe '.getTabLength()', ->
describe 'when scoped settings are used', ->
coffeeEditor = null
beforeEach ->
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
waitsForPromise ->
atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
afterEach: ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
it 'returns correct values based on the scope of the set grammars', ->
atom.config.set 'editor.tabLength', 6, scopeSelector: '.source.coffee'
expect(editor.getTabLength()).toBe 2
expect(coffeeEditor.getTabLength()).toBe 6
it 'retokenizes when the tab length is updated via .setTabLength()', ->
expect(editor.getTabLength()).toBe 2
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2
editor.setTabLength(6)
expect(editor.getTabLength()).toBe 6
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6
changeHandler = jasmine.createSpy('changeHandler')
editor.onDidChange(changeHandler)
editor.setTabLength(6)
expect(changeHandler).not.toHaveBeenCalled()
it 'retokenizes when the editor.tabLength setting is updated', ->
expect(editor.getTabLength()).toBe 2
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2
atom.config.set 'editor.tabLength', 6, scopeSelector: '.source.js'
expect(editor.getTabLength()).toBe 6
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6
it 'updates the tab length when the grammar changes', ->
atom.config.set 'editor.tabLength', 6, scopeSelector: '.source.coffee'
expect(editor.getTabLength()).toBe 2
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 2
editor.setGrammar(coffeeEditor.getGrammar())
expect(editor.getTabLength()).toBe 6
expect(editor.tokenizedLineForScreenRow(5).tokens[0].firstNonWhitespaceIndex).toBe 6
describe ".indentLevelForLine(line)", ->
it "returns the indent level when the line has only leading whitespace", ->
expect(editor.indentLevelForLine(" hello")).toBe(2)
expect(editor.indentLevelForLine(" hello")).toBe(1.5)
it "returns the indent level when the line has only leading tabs", ->
expect(editor.indentLevelForLine("\t\thello")).toBe(2)
it "returns the indent level when the line has mixed leading whitespace and tabs", ->
expect(editor.indentLevelForLine("\t hello")).toBe(2)
expect(editor.indentLevelForLine(" \thello")).toBe(2)
expect(editor.indentLevelForLine(" \t hello")).toBe(2.5)
expect(editor.indentLevelForLine(" \t \thello")).toBe(3.5)
describe "when the buffer is reloaded", ->
it "preserves the current cursor position", ->
editor.setCursorScreenPosition([0, 1])
editor.buffer.reload()
expect(editor.getCursorScreenPosition()).toEqual [0,1]
describe "when a better-matched grammar is added to syntax", ->
it "switches to the better-matched grammar and re-tokenizes the buffer", ->
editor.destroy()
jsGrammar = atom.grammars.selectGrammar('a.js')
atom.grammars.removeGrammar(jsGrammar)
waitsForPromise ->
atom.workspace.open('sample.js', autoIndent: false).then (o) -> editor = o
runs ->
expect(editor.getGrammar()).toBe atom.grammars.nullGrammar
expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1
atom.grammars.addGrammar(jsGrammar)
expect(editor.getGrammar()).toBe jsGrammar
expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBeGreaterThan 1
describe "editor.autoIndent", ->
describe "when editor.autoIndent is false (default)", ->
describe "when `indent` is triggered", ->
it "does not auto-indent the line", ->
editor.setCursorBufferPosition([1, 30])
editor.insertText("\n ")
expect(editor.lineTextForBufferRow(2)).toBe " "
atom.config.set("editor.autoIndent", false)
editor.indent()
expect(editor.lineTextForBufferRow(2)).toBe " "
describe "when editor.autoIndent is true", ->
beforeEach ->
atom.config.set("editor.autoIndent", true)
describe "when `indent` is triggered", ->
it "auto-indents the line", ->
editor.setCursorBufferPosition([1, 30])
editor.insertText("\n ")
expect(editor.lineTextForBufferRow(2)).toBe " "
atom.config.set("editor.autoIndent", true)
editor.indent()
expect(editor.lineTextForBufferRow(2)).toBe " "
describe "when a newline is added", ->
describe "when the line preceding the newline adds a new level of indentation", ->
it "indents the newline to one additional level of indentation beyond the preceding line", ->
editor.setCursorBufferPosition([1, Infinity])
editor.insertText('\n')
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", ->
editor.setCursorBufferPosition([5, 14])
editor.insertText('\n')
expect(editor.indentationForBufferRow(6)).toBe editor.indentationForBufferRow(5)
describe "when the line preceding the newline is a comment", ->
it "maintains the indent of the commented line", ->
editor.setCursorBufferPosition([0, 0])
editor.insertText(' //')
editor.setCursorBufferPosition([0, Infinity])
editor.insertText('\n')
expect(editor.indentationForBufferRow(1)).toBe 2
describe "when the line preceding the newline contains only whitespace", ->
it "bases the new line's indentation on only the preceding line", ->
editor.setCursorBufferPosition([6, Infinity])
editor.insertText("\n ")
expect(editor.getCursorBufferPosition()).toEqual([7, 2])
editor.insertNewline()
expect(editor.lineTextForBufferRow(8)).toBe(" ")
it "does not indent the line preceding the newline", ->
editor.setCursorBufferPosition([2, 0])
editor.insertText(' var this-line-should-be-indented-more\n')
expect(editor.indentationForBufferRow(1)).toBe 1
atom.config.set("editor.autoIndent", true)
editor.setCursorBufferPosition([2, Infinity])
editor.insertText('\n')
expect(editor.indentationForBufferRow(1)).toBe 1
expect(editor.indentationForBufferRow(2)).toBe 1
describe "when the cursor is before whitespace", ->
it "retains the whitespace following the cursor on the new line", ->
editor.setText(" var sort = function() {}")
editor.setCursorScreenPosition([0, 12])
editor.insertNewline()
expect(buffer.lineForRow(0)).toBe ' var sort ='
expect(buffer.lineForRow(1)).toBe ' function() {}'
expect(editor.getCursorScreenPosition()).toEqual [1, 2]
describe "when inserted text matches a decrease indent pattern", ->
describe "when the preceding line matches an increase indent pattern", ->
it "decreases the indentation to match that of the preceding line", ->
editor.setCursorBufferPosition([1, Infinity])
editor.insertText('\n')
expect(editor.indentationForBufferRow(2)).toBe editor.indentationForBufferRow(1) + 1
editor.insertText('}')
expect(editor.indentationForBufferRow(2)).toBe editor.indentationForBufferRow(1)
describe "when the preceding line doesn't match an increase indent pattern", ->
it "decreases the indentation to be one level below that of the preceding line", ->
editor.setCursorBufferPosition([3, Infinity])
editor.insertText('\n ')
expect(editor.indentationForBufferRow(4)).toBe editor.indentationForBufferRow(3)
editor.insertText('}')
expect(editor.indentationForBufferRow(4)).toBe editor.indentationForBufferRow(3) - 1
it "doesn't break when decreasing the indentation on a row that has no indentation", ->
editor.setCursorBufferPosition([12, Infinity])
editor.insertText("\n}; # too many closing brackets!")
expect(editor.lineTextForBufferRow(13)).toBe "}; # too many closing brackets!"
describe "when inserted text does not match a decrease indent pattern", ->
it "does not decrease the indentation", ->
editor.setCursorBufferPosition([12, 0])
editor.insertText(' ')
expect(editor.lineTextForBufferRow(12)).toBe ' };'
editor.insertText('\t\t')
expect(editor.lineTextForBufferRow(12)).toBe ' \t\t};'
describe "when the current line does not match a decrease indent pattern", ->
it "leaves the line unchanged", ->
editor.setCursorBufferPosition([2, 4])
expect(editor.indentationForBufferRow(2)).toBe editor.indentationForBufferRow(1) + 1
editor.insertText('foo')
expect(editor.indentationForBufferRow(2)).toBe editor.indentationForBufferRow(1) + 1
describe 'when scoped settings are used', ->
coffeeEditor = null
beforeEach ->
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
waitsForPromise ->
atom.project.open('coffee.coffee', autoIndent: false).then (o) -> coffeeEditor = o
runs ->
atom.config.set('editor.autoIndent', true, scopeSelector: '.source.js')
atom.config.set('editor.autoIndent', false, scopeSelector: '.source.coffee')
afterEach: ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
it "does not auto-indent the line for javascript files", ->
editor.setCursorBufferPosition([1, 30])
editor.insertText("\n")
expect(editor.lineTextForBufferRow(2)).toBe " "
coffeeEditor.setCursorBufferPosition([1, 18])
coffeeEditor.insertText("\n")
expect(coffeeEditor.lineTextForBufferRow(2)).toBe ""
describe "soft and hard tabs", ->
afterEach ->
atom.packages.deactivatePackages()
atom.packages.unloadPackages()
it "resets the tab style when tokenization is complete", ->
editor.destroy()
waitsForPromise ->
atom.project.open('sample-with-tabs-and-leading-comment.coffee').then (o) -> editor = o
runs ->
expect(editor.softTabs).toBe true
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
runs ->
expect(editor.softTabs).toBe false
it "uses hard tabs in Makefile files", ->
# FIXME remove once this is handled by a scoped setting in the
# language-make package
waitsForPromise ->
atom.packages.activatePackage('language-make')
waitsForPromise ->
atom.project.open('Makefile').then (o) -> editor = o
runs ->
expect(editor.softTabs).toBe false
describe ".destroy()", ->
it "destroys all markers associated with the edit session", ->
expect(buffer.getMarkerCount()).toBeGreaterThan 0
editor.destroy()
expect(buffer.getMarkerCount()).toBe 0
it "notifies ::onDidDestroy observers when the editor is destroyed", ->
destroyObserverCalled = false
editor.onDidDestroy -> destroyObserverCalled = true
editor.destroy()
expect(destroyObserverCalled).toBe true
describe ".joinLines()", ->
describe "when no text is selected", ->
describe "when the line below isn't empty", ->
it "joins the line below with the current line separated by a space and moves the cursor to the start of line that was moved up", ->
editor.setCursorBufferPosition([0, Infinity])
editor.insertText(' ')
editor.setCursorBufferPosition([0])
editor.joinLines()
expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () { var sort = function(items) {'
expect(editor.getCursorBufferPosition()).toEqual [0, 29]
describe "when the line below is empty", ->
it "deletes the line below and moves the cursor to the end of the line", ->
editor.setCursorBufferPosition([9])
editor.joinLines()
expect(editor.lineTextForBufferRow(9)).toBe ' };'
expect(editor.lineTextForBufferRow(10)).toBe ' return sort(Array.apply(this, arguments));'
expect(editor.getCursorBufferPosition()).toEqual [9, 4]
describe "when the cursor is on the last row", ->
it "does nothing", ->
editor.setCursorBufferPosition([Infinity, Infinity])
editor.joinLines()
expect(editor.lineTextForBufferRow(12)).toBe '};'
describe "when the line is empty", ->
it "joins the line below with the current line with no added space", ->
editor.setCursorBufferPosition([10])
editor.joinLines()
expect(editor.lineTextForBufferRow(10)).toBe 'return sort(Array.apply(this, arguments));'
expect(editor.getCursorBufferPosition()).toEqual [10, 0]
describe "when text is selected", ->
describe "when the selection does not span multiple lines", ->
it "joins the line below with the current line separated by a space and retains the selected text", ->
editor.setSelectedBufferRange([[0, 1], [0, 3]])
editor.joinLines()
expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () { var sort = function(items) {'
expect(editor.getSelectedBufferRange()).toEqual [[0, 1], [0, 3]]
describe "when the selection spans multiple lines", ->
it "joins all selected lines separated by a space and retains the selected text", ->
editor.setSelectedBufferRange([[9, 3], [12, 1]])
editor.joinLines()
expect(editor.lineTextForBufferRow(9)).toBe ' }; return sort(Array.apply(this, arguments)); };'
expect(editor.getSelectedBufferRange()).toEqual [[9, 3], [9, 49]]
describe ".duplicateLines()", ->
it "for each selection, duplicates all buffer lines intersected by the selection", ->
editor.foldBufferRow(4)
editor.setCursorBufferPosition([2, 5])
editor.addSelectionForBufferRange([[3, 0], [8, 0]], preserveFolds: true)
editor.duplicateLines()
expect(editor.getTextInBufferRange([[2, 0], [13, 5]])).toBe """
\ if (items.length <= 1) return items;
if (items.length <= 1) return items;
var pivot = items.shift(), current, left = [], right = [];
while(items.length > 0) {
current = items.shift();
current < pivot ? left.push(current) : right.push(current);
}
var pivot = items.shift(), current, left = [], right = [];
while(items.length > 0) {
current = items.shift();
current < pivot ? left.push(current) : right.push(current);
}
"""
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));"
it "duplicates all folded lines for empty selections on folded lines", ->
editor.foldBufferRow(4)
editor.setCursorBufferPosition([4, 0])
editor.duplicateLines()
expect(editor.getTextInBufferRange([[2, 0], [11, 5]])).toBe """
\ if (items.length <= 1) return items;
var pivot = items.shift(), current, left = [], right = [];
while(items.length > 0) {
current = items.shift();
current < pivot ? left.push(current) : right.push(current);
}
while(items.length > 0) {
current = items.shift();
current < pivot ? left.push(current) : right.push(current);
}
"""
expect(editor.getSelectedBufferRange()).toEqual [[8, 0], [8, 0]]
it "can duplicate the last line of the buffer", ->
editor.setSelectedBufferRange([[11, 0], [12, 2]])
editor.duplicateLines()
expect(editor.getTextInBufferRange([[11, 0], [14, 2]])).toBe """
\ return sort(Array.apply(this, arguments));
};
return sort(Array.apply(this, arguments));
};
"""
expect(editor.getSelectedBufferRange()).toEqual [[13, 0], [14, 2]]
describe ".shouldPromptToSave()", ->
it "returns false when an edit session's buffer is in use by more than one session", ->
jasmine.unspy(editor, 'shouldPromptToSave')
expect(editor.shouldPromptToSave()).toBeFalsy()
buffer.setText('changed')
expect(editor.shouldPromptToSave()).toBeTruthy()
editor2 = null
waitsForPromise ->
atom.project.open('sample.js', autoIndent: false).then (o) -> editor2 = o
runs ->
expect(editor.shouldPromptToSave()).toBeFalsy()
editor2.destroy()
expect(editor.shouldPromptToSave()).toBeTruthy()
describe "when the editor contains surrogate pair characters", ->
it "correctly backspaces over them", ->
editor.setText('\uD835\uDF97\uD835\uDF97\uD835\uDF97')
editor.moveToBottom()
editor.backspace()
expect(editor.getText()).toBe '\uD835\uDF97\uD835\uDF97'
editor.backspace()
expect(editor.getText()).toBe '\uD835\uDF97'
editor.backspace()
expect(editor.getText()).toBe ''
it "correctly deletes over them", ->
editor.setText('\uD835\uDF97\uD835\uDF97\uD835\uDF97')
editor.moveToTop()
editor.delete()
expect(editor.getText()).toBe '\uD835\uDF97\uD835\uDF97'
editor.delete()
expect(editor.getText()).toBe '\uD835\uDF97'
editor.delete()
expect(editor.getText()).toBe ''
it "correctly moves over them", ->
editor.setText('\uD835\uDF97\uD835\uDF97\uD835\uDF97\n')
editor.moveToTop()
editor.moveRight()
expect(editor.getCursorBufferPosition()).toEqual [0, 2]
editor.moveRight()
expect(editor.getCursorBufferPosition()).toEqual [0, 4]
editor.moveRight()
expect(editor.getCursorBufferPosition()).toEqual [0, 6]
editor.moveRight()
expect(editor.getCursorBufferPosition()).toEqual [1, 0]
editor.moveLeft()
expect(editor.getCursorBufferPosition()).toEqual [0, 6]
editor.moveLeft()
expect(editor.getCursorBufferPosition()).toEqual [0, 4]
editor.moveLeft()
expect(editor.getCursorBufferPosition()).toEqual [0, 2]
editor.moveLeft()
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
describe "when the editor contains variation sequence character pairs", ->
it "correctly backspaces over them", ->
editor.setText('\u2714\uFE0E\u2714\uFE0E\u2714\uFE0E')
editor.moveToBottom()
editor.backspace()
expect(editor.getText()).toBe '\u2714\uFE0E\u2714\uFE0E'
editor.backspace()
expect(editor.getText()).toBe '\u2714\uFE0E'
editor.backspace()
expect(editor.getText()).toBe ''
it "correctly deletes over them", ->
editor.setText('\u2714\uFE0E\u2714\uFE0E\u2714\uFE0E')
editor.moveToTop()
editor.delete()
expect(editor.getText()).toBe '\u2714\uFE0E\u2714\uFE0E'
editor.delete()
expect(editor.getText()).toBe '\u2714\uFE0E'
editor.delete()
expect(editor.getText()).toBe ''
it "correctly moves over them", ->
editor.setText('\u2714\uFE0E\u2714\uFE0E\u2714\uFE0E\n')
editor.moveToTop()
editor.moveRight()
expect(editor.getCursorBufferPosition()).toEqual [0, 2]
editor.moveRight()
expect(editor.getCursorBufferPosition()).toEqual [0, 4]
editor.moveRight()
expect(editor.getCursorBufferPosition()).toEqual [0, 6]
editor.moveRight()
expect(editor.getCursorBufferPosition()).toEqual [1, 0]
editor.moveLeft()
expect(editor.getCursorBufferPosition()).toEqual [0, 6]
editor.moveLeft()
expect(editor.getCursorBufferPosition()).toEqual [0, 4]
editor.moveLeft()
expect(editor.getCursorBufferPosition()).toEqual [0, 2]
editor.moveLeft()
expect(editor.getCursorBufferPosition()).toEqual [0, 0]
describe ".setIndentationForBufferRow", ->
describe "when the editor uses soft tabs but the row has hard tabs", ->
it "only replaces whitespace characters", ->
editor.setSoftWrapped(true)
editor.setText("\t1\n\t2")
editor.setCursorBufferPosition([0, 0])
editor.setIndentationForBufferRow(0, 2)
expect(editor.getText()).toBe(" 1\n\t2")
describe "when the indentation level is a non-integer", ->
it "does not throw an exception", ->
editor.setSoftWrapped(true)
editor.setText("\t1\n\t2")
editor.setCursorBufferPosition([0, 0])
editor.setIndentationForBufferRow(0, 2.1)
expect(editor.getText()).toBe(" 1\n\t2")
describe ".reloadGrammar()", ->
beforeEach ->
waitsForPromise ->
atom.packages.activatePackage('language-coffee-script')
it "updates the grammar based on grammar overrides", ->
expect(editor.getGrammar().name).toBe 'JavaScript'
atom.grammars.setGrammarOverrideForPath(editor.getPath(), 'source.coffee')
callback = jasmine.createSpy('callback')
editor.onDidChangeGrammar(callback)
editor.reloadGrammar()
expect(editor.getGrammar().name).toBe 'CoffeeScript'
expect(callback.callCount).toBe 1
expect(callback.argsForCall[0][0]).toBe atom.grammars.grammarForScopeName('source.coffee')
describe "when the editor's grammar has an injection selector", ->
beforeEach ->
waitsForPromise ->
atom.packages.activatePackage('language-text')
waitsForPromise ->
atom.packages.activatePackage('language-javascript')
it "includes the grammar's patterns when the selector matches the current scope in other grammars", ->
waitsForPromise ->
atom.packages.activatePackage('language-hyperlink')
runs ->
grammar = atom.grammars.selectGrammar("text.js")
{tokens} = grammar.tokenizeLine("var i; // http://github.com")
expect(tokens[0].value).toBe "var"
expect(tokens[0].scopes).toEqual ["source.js", "storage.modifier.js"]
expect(tokens[6].value).toBe "http://github.com"
expect(tokens[6].scopes).toEqual ["source.js", "comment.line.double-slash.js", "markup.underline.link.http.hyperlink"]
describe "when the grammar is added", ->
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
waitsForPromise ->
atom.workspace.open('sample.js').then (o) -> editor = o
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"]
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"]
describe "when the grammar is updated", ->
it "retokenizes existing buffers that contain tokens that match the injection selector", ->
waitsForPromise ->
atom.workspace.open('sample.js').then (o) -> editor = o
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"]
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"]
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"]
describe ".normalizeTabsInBufferRange()", ->
it "normalizes tabs depending on the editor's soft tab/tab length settings", ->
editor.setTabLength(1)
editor.setSoftTabs(true)
editor.setText('\t\t\t')
editor.normalizeTabsInBufferRange([[0, 0], [0, 1]])
expect(editor.getText()).toBe ' \t\t'
editor.setTabLength(2)
editor.normalizeTabsInBufferRange([[0, 0], [Infinity, Infinity]])
expect(editor.getText()).toBe ' '
editor.setSoftTabs(false)
editor.normalizeTabsInBufferRange([[0, 0], [Infinity, Infinity]])
expect(editor.getText()).toBe ' '
describe ".scrollToCursorPosition()", ->
it "scrolls the last cursor into view, centering around the cursor if possible and the 'center' option isn't false", ->
editor.setCursorScreenPosition([8, 8])
editor.setLineHeightInPixels(10)
editor.setDefaultCharWidth(10)
editor.setHeight(60)
editor.setWidth(50)
editor.setHorizontalScrollbarHeight(0)
expect(editor.getScrollTop()).toBe 0
expect(editor.getScrollLeft()).toBe 0
editor.scrollToCursorPosition()
expect(editor.getScrollTop()).toBe (8.5 * 10) - 30
expect(editor.getScrollBottom()).toBe (8.5 * 10) + 30
expect(editor.getScrollRight()).toBe (9 + editor.getHorizontalScrollMargin()) * 10
editor.setScrollTop(0)
editor.scrollToCursorPosition(center: false)
expect(editor.getScrollBottom()).toBe (9 + editor.getVerticalScrollMargin()) * 10
describe ".pageUp/Down()", ->
it "scrolls one screen height up or down and moves the cursor one page length", ->
editor.manageScrollPosition = true
editor.setLineHeightInPixels(10)
editor.setHeight(50)
expect(editor.getScrollHeight()).toBe 130
expect(editor.getCursorBufferPosition().row).toBe 0
editor.pageDown()
expect(editor.getScrollTop()).toBe 50
expect(editor.getCursorBufferPosition().row).toBe 5
editor.pageDown()
expect(editor.getScrollTop()).toBe 80
expect(editor.getCursorBufferPosition().row).toBe 10
editor.pageUp()
expect(editor.getScrollTop()).toBe 30
expect(editor.getCursorBufferPosition().row).toBe 5
editor.pageUp()
expect(editor.getScrollTop()).toBe 0
expect(editor.getCursorBufferPosition().row).toBe 0
describe ".selectPageUp/Down()", ->
it "selects one screen height of text up or down", ->
editor.manageScrollPosition = true
editor.setLineHeightInPixels(10)
editor.setHeight(50)
expect(editor.getScrollHeight()).toBe 130
expect(editor.getCursorBufferPosition().row).toBe 0
editor.selectPageDown()
expect(editor.getScrollTop()).toBe 30
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [5,0]]]
editor.selectPageDown()
expect(editor.getScrollTop()).toBe 80
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [10,0]]]
editor.selectPageDown()
expect(editor.getScrollTop()).toBe 80
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [12,2]]]
editor.moveToBottom()
editor.selectPageUp()
expect(editor.getScrollTop()).toBe 50
expect(editor.getSelectedBufferRanges()).toEqual [[[7,0], [12,2]]]
editor.selectPageUp()
expect(editor.getScrollTop()).toBe 0
expect(editor.getSelectedBufferRanges()).toEqual [[[2,0], [12,2]]]
editor.selectPageUp()
expect(editor.getScrollTop()).toBe 0
expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [12,2]]]
describe '.get/setPlaceholderText()', ->
it 'can be created with placeholderText', ->
TextBuffer = require 'text-buffer'
newEditor = new TextEditor
buffer: new TextBuffer
mini: true
placeholderText: 'yep'
expect(newEditor.getPlaceholderText()).toBe 'yep'
it 'models placeholderText and emits an event when changed', ->
editor.onDidChangePlaceholderText handler = jasmine.createSpy()
expect(editor.getPlaceholderText()).toBeUndefined()
editor.setPlaceholderText('OK')
expect(handler).toHaveBeenCalledWith 'OK'
expect(editor.getPlaceholderText()).toBe 'OK'