diff --git a/benchmark/benchmark-suite.coffee b/benchmark/benchmark-suite.coffee index 99f5c60d8..c60811985 100644 --- a/benchmark/benchmark-suite.coffee +++ b/benchmark/benchmark-suite.coffee @@ -53,7 +53,7 @@ describe "editorView.", -> describe "at-end.", -> beforeEach -> - editorView.moveCursorToBottom() + editorView.moveToBottom() benchmark "insert-delete", -> editorView.insertText('"') @@ -103,7 +103,7 @@ describe "editorView.", -> benchmark "cache-entire-visible-area", 100, -> for i in [firstRow..lastRow] line = editorView.lineElementForScreenRow(i)[0] - editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.lineLengthForBufferRow(i))) + editorView.positionLeftForLineAndColumn(line, i, Math.max(0, editorView.getModel().lineTextForBufferRow(i).length)) describe "text-rendering.", -> beforeEach -> @@ -178,7 +178,7 @@ describe "editorView.", -> atom.workspaceView.openSync('huge.js') benchmark "moving-to-eof.", 1, -> - editorView.moveCursorToBottom() + editorView.moveToBottom() describe "on-first-line.", -> benchmark "inserting-newline", 5, -> @@ -195,11 +195,11 @@ describe "editorView.", -> endPosition = null beforeEach -> - editorView.moveCursorToBottom() + editorView.moveToBottom() endPosition = editorView.getCursorScreenPosition() benchmark "move-to-beginning-of-word", -> - editorView.moveCursorToBeginningOfWord() + editorView.moveToBeginningOfWord() editorView.setCursorScreenPosition(endPosition) benchmark "insert", -> diff --git a/docs/advanced/keymaps.md b/docs/advanced/keymaps.md index 24e76f2b1..cd05f0b95 100644 --- a/docs/advanced/keymaps.md +++ b/docs/advanced/keymaps.md @@ -56,7 +56,7 @@ character of the current line: class EditorView listenForEvents: -> @command 'editor:move-to-first-character-of-line', => - @editor.moveCursorToFirstCharacterOfLine() + @editor.moveToFirstCharacterOfLine() ``` The `::command` method is basically an enhanced version of jQuery's `::on` diff --git a/docs/your-first-package.md b/docs/your-first-package.md index 4fc41d044..fe9550bd9 100644 --- a/docs/your-first-package.md +++ b/docs/your-first-package.md @@ -53,7 +53,7 @@ module.exports = convert: -> # This assumes the active pane item is an editor - editor = atom.workspace.activePaneItem + editor = atom.workspace.getActivePaneItem() editor.insertText('Hello, World!') ``` @@ -131,8 +131,8 @@ inserting 'Hello, World!' convert the selected text to ASCII art. ```coffeescript convert: -> # This assumes the active pane item is an editor - editor = atom.workspace.activePaneItem - selection = editor.getSelection() + editor = atom.workspace.getActivePaneItem() + selection = editor.getLastSelection() figlet = require 'figlet' figlet selection.getText(), {font: "Larry 3D 2"}, (error, asciiArt) -> diff --git a/package.json b/package.json index 2f92b4967..6dc56fa54 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "coffee-script": "1.7.0", "coffeestack": "0.7.0", "delegato": "^1", - "emissary": "^1.2.2", + "emissary": "^1.3.1", + "event-kit": "0.5.0", "first-mate": "^2.0.5", "fs-plus": "^2.2.6", "fstream": "0.1.24", @@ -56,7 +57,7 @@ "serializable": "^1", "space-pen": "3.4.6", "temp": "0.7.0", - "text-buffer": "^3.0.4", + "text-buffer": "^3.1.0", "theorist": "^1.0.2", "underscore-plus": "^1.5.1", "vm-compatibility-layer": "0.1.0" @@ -78,11 +79,11 @@ "bookmarks": "0.28.0", "bracket-matcher": "0.54.0", "command-palette": "0.24.0", - "deprecation-cop": "0.9.0", + "deprecation-cop": "0.10.0", "dev-live-reload": "0.34.0", "exception-reporting": "0.20.0", "feedback": "0.33.0", - "find-and-replace": "0.131.1", + "find-and-replace": "0.132.0", "fuzzy-finder": "0.57.0", "git-diff": "0.39.0", "go-to-line": "0.25.0", @@ -91,7 +92,7 @@ "incompatible-packages": "0.9.0", "keybinding-resolver": "0.19.0", "link": "0.25.0", - "markdown-preview": "0.100.0", + "markdown-preview": "0.101.0", "metrics": "0.33.0", "open-on-github": "0.30.0", "package-generator": "0.31.0", @@ -104,12 +105,11 @@ "symbols-view": "0.63.0", "tabs": "0.50.0", "timecop": "0.22.0", - "tree-view": "0.113.0", + "tree-view": "0.114.0", "update-package-dependencies": "0.6.0", "welcome": "0.18.0", "whitespace": "0.25.0", "wrap-guide": "0.21.0", - "language-c": "0.28.0", "language-coffee-script": "0.30.0", "language-css": "0.17.0", @@ -121,24 +121,24 @@ "language-java": "0.11.0", "language-javascript": "0.39.0", "language-json": "0.8.0", - "language-less": "0.14.0", + "language-less": "0.15.0", "language-make": "0.12.0", "language-mustache": "0.10.0", "language-objective-c": "0.11.0", "language-perl": "0.9.0", "language-php": "0.15.0", "language-property-list": "0.7.0", - "language-python": "0.18.0", + "language-python": "0.19.0", "language-ruby": "0.35.0", "language-ruby-on-rails": "0.18.0", - "language-sass": "0.20.0", + "language-sass": "0.21.0", "language-shellscript": "0.8.0", "language-source": "0.8.0", "language-sql": "0.10.0", "language-text": "0.6.0", - "language-todo": "0.10.0", + "language-todo": "0.11.0", "language-toml": "0.12.0", - "language-xml": "0.18.0", + "language-xml": "0.19.0", "language-yaml": "0.17.0" }, "private": true, diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index 34b75f54f..75e906376 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -67,48 +67,48 @@ describe "DisplayBuffer", -> it "uses the preferred line length as the soft wrap column when it is less than the configured soft wrap column", -> atom.config.set('editor.preferredLineLength', 100) atom.config.set('editor.softWrapAtPreferredLineLength', true) - expect(displayBuffer.lineForRow(10).text).toBe ' return ' + expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return ' atom.config.set('editor.preferredLineLength', 5) - expect(displayBuffer.lineForRow(10).text).toBe 'funct' + expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe 'funct' atom.config.set('editor.softWrapAtPreferredLineLength', false) - expect(displayBuffer.lineForRow(10).text).toBe ' return ' + expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return ' describe "when the line is shorter than the max line length", -> it "renders the line unchanged", -> - expect(displayBuffer.lineForRow(0).text).toBe buffer.lineForRow(0) + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe buffer.lineForRow(0) describe "when the line is empty", -> it "renders the empty line", -> - expect(displayBuffer.lineForRow(13).text).toBe '' + expect(displayBuffer.tokenizedLineForScreenRow(13).text).toBe '' describe "when there is a non-whitespace character at the max length boundary", -> describe "when there is whitespace before the boundary", -> it "wraps the line at the end of the first whitespace preceding the boundary", -> - expect(displayBuffer.lineForRow(10).text).toBe ' return ' - expect(displayBuffer.lineForRow(11).text).toBe 'sort(left).concat(pivot).concat(sort(right));' + expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' return ' + expect(displayBuffer.tokenizedLineForScreenRow(11).text).toBe 'sort(left).concat(pivot).concat(sort(right));' describe "when there is no whitespace before the boundary", -> it "wraps the line exactly at the boundary since there's no more graceful place to wrap it", -> buffer.setTextInRange([[0, 0], [1, 0]], 'abcdefghijklmnopqrstuvwxyz\n') displayBuffer.setEditorWidthInChars(10) - expect(displayBuffer.lineForRow(0).text).toBe 'abcdefghij' - expect(displayBuffer.lineForRow(1).text).toBe 'klmnopqrst' - expect(displayBuffer.lineForRow(2).text).toBe 'uvwxyz' + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe 'abcdefghij' + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe 'klmnopqrst' + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe 'uvwxyz' describe "when there is a whitespace character at the max length boundary", -> it "wraps the line at the first non-whitespace character following the boundary", -> - expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items.shift(), current, left = [], ' - expect(displayBuffer.lineForRow(4).text).toBe 'right = [];' + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe ' var pivot = items.shift(), current, left = [], ' + expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe 'right = [];' describe "when there are hard tabs", -> beforeEach -> buffer.setText(buffer.getText().replace(new RegExp(' ', 'g'), '\t')) it "correctly tokenizes the hard tabs", -> - expect(displayBuffer.lineForRow(3).tokens[0].isHardTab).toBeTruthy() - expect(displayBuffer.lineForRow(3).tokens[1].isHardTab).toBeTruthy() + expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[0].isHardTab).toBeTruthy() + expect(displayBuffer.tokenizedLineForScreenRow(3).tokens[1].isHardTab).toBeTruthy() describe "when the buffer changes", -> describe "when buffer lines are updated", -> @@ -121,8 +121,8 @@ describe "DisplayBuffer", -> describe "when the update makes a soft-wrapped line shorter than the max line length", -> it "rewraps the line and emits a change event", -> buffer.delete([[6, 24], [6, 42]]) - expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot ? : right.push(current);' - expect(displayBuffer.lineForRow(8).text).toBe ' }' + expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot ? : right.push(current);' + expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe ' }' expect(changeHandler).toHaveBeenCalled() [[event]]= changeHandler.argsForCall @@ -132,30 +132,30 @@ describe "DisplayBuffer", -> describe "when the update causes a line to softwrap an additional time", -> it "rewraps the line and emits a change event", -> buffer.insert([6, 28], '1234567890') - expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot ? ' - expect(displayBuffer.lineForRow(8).text).toBe 'left1234567890.push(current) : ' - expect(displayBuffer.lineForRow(9).text).toBe 'right.push(current);' - expect(displayBuffer.lineForRow(10).text).toBe ' }' + expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot ? ' + expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe 'left1234567890.push(current) : ' + expect(displayBuffer.tokenizedLineForScreenRow(9).text).toBe 'right.push(current);' + expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe ' }' expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, screenDelta: 1, bufferDelta: 0) describe "when buffer lines are inserted", -> it "inserts / updates wrapped lines and emits a change event", -> buffer.insert([6, 21], '1234567890 abcdefghij 1234567890\nabcdefghij') - expect(displayBuffer.lineForRow(7).text).toBe ' current < pivot1234567890 abcdefghij ' - expect(displayBuffer.lineForRow(8).text).toBe '1234567890' - expect(displayBuffer.lineForRow(9).text).toBe 'abcdefghij ? left.push(current) : ' - expect(displayBuffer.lineForRow(10).text).toBe 'right.push(current);' + expect(displayBuffer.tokenizedLineForScreenRow(7).text).toBe ' current < pivot1234567890 abcdefghij ' + expect(displayBuffer.tokenizedLineForScreenRow(8).text).toBe '1234567890' + expect(displayBuffer.tokenizedLineForScreenRow(9).text).toBe 'abcdefghij ? left.push(current) : ' + expect(displayBuffer.tokenizedLineForScreenRow(10).text).toBe 'right.push(current);' expect(changeHandler).toHaveBeenCalledWith(start: 7, end: 8, screenDelta: 2, bufferDelta: 1) describe "when buffer lines are removed", -> it "removes lines and emits a change event", -> buffer.setTextInRange([[3, 21], [7, 5]], ';') - expect(displayBuffer.lineForRow(3).text).toBe ' var pivot = items;' - expect(displayBuffer.lineForRow(4).text).toBe ' return ' - expect(displayBuffer.lineForRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));' - expect(displayBuffer.lineForRow(6).text).toBe ' };' + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe ' var pivot = items;' + expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe ' return ' + expect(displayBuffer.tokenizedLineForScreenRow(5).text).toBe 'sort(left).concat(pivot).concat(sort(right));' + expect(displayBuffer.tokenizedLineForScreenRow(6).text).toBe ' };' expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 9, screenDelta: -6, bufferDelta: -4) @@ -169,9 +169,9 @@ describe "DisplayBuffer", -> buffer.delete([[0, Infinity], [1, 0]]) buffer.insert([0, Infinity], '\n') - expect(displayBuffer.lineForRow(0).text).toBe "the quick brown fox jumps over " - expect(displayBuffer.lineForRow(1).text).toBe "the lazy dog." - expect(displayBuffer.lineForRow(2).text).toBe "" + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "the quick brown fox jumps over " + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "the lazy dog." + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "" describe "position translation", -> it "translates positions accounting for wrapped lines", -> @@ -204,9 +204,9 @@ describe "DisplayBuffer", -> describe ".setEditorWidthInChars(length)", -> it "changes the length at which lines are wrapped and emits a change event for all screen lines", -> displayBuffer.setEditorWidthInChars(40) - expect(tokensText displayBuffer.lineForRow(4).tokens).toBe 'left = [], right = [];' - expect(tokensText displayBuffer.lineForRow(5).tokens).toBe ' while(items.length > 0) {' - expect(tokensText displayBuffer.lineForRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig' + expect(tokensText displayBuffer.tokenizedLineForScreenRow(4).tokens).toBe 'left = [], right = [];' + expect(tokensText displayBuffer.tokenizedLineForScreenRow(5).tokens).toBe ' while(items.length > 0) {' + expect(tokensText displayBuffer.tokenizedLineForScreenRow(12).tokens).toBe 'sort(left).concat(pivot).concat(sort(rig' expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 15, screenDelta: 3, bufferDelta: 0) it "only allows positive widths to be assigned", -> @@ -242,7 +242,7 @@ describe "DisplayBuffer", -> fold = displayBuffer.createFold(4, 7) expect(fold).toBeDefined() - [line4, line5] = displayBuffer.linesForRows(4, 5) + [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) expect(line4.fold).toBe fold expect(line4.text).toMatch /^4-+/ expect(line5.text).toBe '8' @@ -251,7 +251,7 @@ describe "DisplayBuffer", -> changeHandler.reset() fold.destroy() - [line4, line5] = displayBuffer.linesForRows(4, 5) + [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) expect(line4.fold).toBeUndefined() expect(line4.text).toMatch /^4-+/ expect(line5.text).toBe '5' @@ -262,7 +262,7 @@ describe "DisplayBuffer", -> it "renders a fold placeholder for the folded line but does not skip any lines", -> fold = displayBuffer.createFold(4, 4) - [line4, line5] = displayBuffer.linesForRows(4, 5) + [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) expect(line4.fold).toBe fold expect(line4.text).toMatch /^4-+/ expect(line5.text).toBe '5' @@ -275,7 +275,7 @@ describe "DisplayBuffer", -> fold.destroy() - [line4, line5] = displayBuffer.linesForRows(4, 5) + [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) expect(line4.fold).toBeUndefined() expect(line4.text).toMatch /^4-+/ expect(line5.text).toBe '5' @@ -287,13 +287,13 @@ describe "DisplayBuffer", -> innerFold = displayBuffer.createFold(6, 7) outerFold = displayBuffer.createFold(4, 8) - [line4, line5] = displayBuffer.linesForRows(4, 5) + [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) expect(line4.fold).toBe outerFold expect(line4.text).toMatch /4-+/ expect(line5.text).toMatch /9-+/ outerFold.destroy() - [line4, line5, line6, line7] = displayBuffer.linesForRows(4, 7) + [line4, line5, line6, line7] = displayBuffer.tokenizedLinesForScreenRows(4, 7) expect(line4.fold).toBeUndefined() expect(line4.text).toMatch /^4-+/ expect(line5.text).toBe '5' @@ -305,7 +305,7 @@ describe "DisplayBuffer", -> innerFold = displayBuffer.createFold(4, 6) outerFold = displayBuffer.createFold(4, 8) - [line4, line5] = displayBuffer.linesForRows(4, 5) + [line4, line5] = displayBuffer.tokenizedLinesForScreenRows(4, 5) expect(line4.fold).toBe outerFold expect(line4.text).toMatch /4-+/ expect(line5.text).toMatch /9-+/ @@ -326,14 +326,14 @@ describe "DisplayBuffer", -> innerFold = displayBuffer.createFold(2, 5) expect(changeHandler).not.toHaveBeenCalled() - [line0, line1] = displayBuffer.linesForRows(0, 1) + [line0, line1] = displayBuffer.tokenizedLinesForScreenRows(0, 1) expect(line0.fold).toBe outerFold expect(line1.fold).toBeUndefined() changeHandler.reset() innerFold.destroy() expect(changeHandler).not.toHaveBeenCalled() - [line0, line1] = displayBuffer.linesForRows(0, 1) + [line0, line1] = displayBuffer.tokenizedLinesForScreenRows(0, 1) expect(line0.fold).toBe outerFold expect(line1.fold).toBeUndefined() @@ -342,8 +342,8 @@ describe "DisplayBuffer", -> fold2 = displayBuffer.createFold(4, 9) fold1 = displayBuffer.createFold(0, 4) - expect(displayBuffer.lineForRow(0).text).toMatch /^0/ - expect(displayBuffer.lineForRow(1).text).toMatch /^10/ + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toMatch /^0/ + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toMatch /^10/ describe "when there is another display buffer pointing to the same buffer", -> it "does not create folds in the other display buffer", -> @@ -363,19 +363,19 @@ describe "DisplayBuffer", -> buffer.setTextInRange([[1, 0], [5, 1]], 'party!') it "removes the fold and replaces the selection with the new text", -> - expect(displayBuffer.lineForRow(0).text).toBe "0" - expect(displayBuffer.lineForRow(1).text).toBe "party!" - expect(displayBuffer.lineForRow(2).fold).toBe fold2 - expect(displayBuffer.lineForRow(3).text).toMatch /^9-+/ + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "0" + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "party!" + expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold2 + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -4) describe "when the changes is subsequently undone", -> xit "restores destroyed folds", -> buffer.undo() - expect(displayBuffer.lineForRow(2).text).toBe '2' - expect(displayBuffer.lineForRow(2).fold).toBe fold1 - expect(displayBuffer.lineForRow(3).text).toBe '5' + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2' + expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe '5' describe "when the old range surrounds two nested folds", -> it "removes both folds and replaces the selection with the new text", -> @@ -384,9 +384,9 @@ describe "DisplayBuffer", -> buffer.setTextInRange([[1, 0], [10, 0]], 'goodbye') - expect(displayBuffer.lineForRow(0).text).toBe "0" - expect(displayBuffer.lineForRow(1).text).toBe "goodbye10" - expect(displayBuffer.lineForRow(2).text).toBe "11" + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "0" + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "goodbye10" + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "11" expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 3, screenDelta: -2, bufferDelta: -9) @@ -403,42 +403,42 @@ describe "DisplayBuffer", -> it "updates the buffer and re-positions subsequent folds", -> buffer.setTextInRange([[0, 0], [1, 1]], 'abc') - expect(displayBuffer.lineForRow(0).text).toBe "abc" - expect(displayBuffer.lineForRow(1).fold).toBe fold1 - expect(displayBuffer.lineForRow(2).text).toBe "5" - expect(displayBuffer.lineForRow(3).fold).toBe fold2 - expect(displayBuffer.lineForRow(4).text).toMatch /^9-+/ + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "abc" + expect(displayBuffer.tokenizedLineForScreenRow(1).fold).toBe fold1 + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "5" + expect(displayBuffer.tokenizedLineForScreenRow(3).fold).toBe fold2 + expect(displayBuffer.tokenizedLineForScreenRow(4).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 1, screenDelta: -1, bufferDelta: -1) changeHandler.reset() fold1.destroy() - expect(displayBuffer.lineForRow(0).text).toBe "abc" - expect(displayBuffer.lineForRow(1).text).toBe "2" - expect(displayBuffer.lineForRow(3).text).toMatch /^4-+/ - expect(displayBuffer.lineForRow(4).text).toBe "5" - expect(displayBuffer.lineForRow(5).fold).toBe fold2 - expect(displayBuffer.lineForRow(6).text).toMatch /^9-+/ + expect(displayBuffer.tokenizedLineForScreenRow(0).text).toBe "abc" + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "2" + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch /^4-+/ + expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe "5" + expect(displayBuffer.tokenizedLineForScreenRow(5).fold).toBe fold2 + expect(displayBuffer.tokenizedLineForScreenRow(6).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalledWith(start: 1, end: 1, screenDelta: 2, bufferDelta: 0) describe "when the old range straddles the beginning of a fold", -> it "destroys the fold", -> buffer.setTextInRange([[1, 1], [3, 0]], "a\nb\nc\nd\n") - expect(displayBuffer.lineForRow(1).text).toBe '1a' - expect(displayBuffer.lineForRow(2).text).toBe 'b' - expect(displayBuffer.lineForRow(2).fold).toBeUndefined() - expect(displayBuffer.lineForRow(3).text).toBe 'c' + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1a' + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe 'b' + expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBeUndefined() + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe 'c' describe "when the old range follows a fold", -> it "re-positions the screen ranges for the change event based on the preceding fold", -> buffer.setTextInRange([[10, 0], [11, 0]], 'abc') - expect(displayBuffer.lineForRow(1).text).toBe "1" - expect(displayBuffer.lineForRow(2).fold).toBe fold1 - expect(displayBuffer.lineForRow(3).text).toBe "5" - expect(displayBuffer.lineForRow(4).fold).toBe fold2 - expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/ + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1" + expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe "5" + expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2 + expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalledWith(start: 6, end: 7, screenDelta: -1, bufferDelta: -1) @@ -449,12 +449,12 @@ describe "DisplayBuffer", -> expect(fold1.getStartRow()).toBe 2 expect(fold1.getEndRow()).toBe 5 - expect(displayBuffer.lineForRow(1).text).toBe "1" - expect(displayBuffer.lineForRow(2).text).toBe "2" - expect(displayBuffer.lineForRow(2).fold).toBe fold1 - expect(displayBuffer.lineForRow(3).text).toMatch "5" - expect(displayBuffer.lineForRow(4).fold).toBe fold2 - expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/ + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1" + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "2" + expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "5" + expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2 + expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 2, screenDelta: 0, bufferDelta: 1) @@ -464,12 +464,12 @@ describe "DisplayBuffer", -> expect(fold1.getStartRow()).toBe 2 expect(fold1.getEndRow()).toBe 7 - expect(displayBuffer.lineForRow(1).text).toBe "1" - expect(displayBuffer.lineForRow(2).text).toBe "2" - expect(displayBuffer.lineForRow(2).fold).toBe fold1 - expect(displayBuffer.lineForRow(3).text).toMatch "5" - expect(displayBuffer.lineForRow(4).fold).toBe fold2 - expect(displayBuffer.lineForRow(5).text).toMatch /^9-+/ + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1" + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe "2" + expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "5" + expect(displayBuffer.tokenizedLineForScreenRow(4).fold).toBe fold2 + expect(displayBuffer.tokenizedLineForScreenRow(5).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalledWith(start: 2, end: 2, screenDelta: 0, bufferDelta: 3) @@ -478,21 +478,21 @@ describe "DisplayBuffer", -> it "destroys the fold", -> fold2.destroy() buffer.setTextInRange([[3, 0], [6, 0]], 'a\n') - expect(displayBuffer.lineForRow(2).text).toBe '2' - expect(displayBuffer.lineForRow(2).fold).toBeUndefined() - expect(displayBuffer.lineForRow(3).text).toBe 'a' - expect(displayBuffer.lineForRow(4).text).toBe '6' + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2' + expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBeUndefined() + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toBe 'a' + expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe '6' describe "when the old range is contained to a single line in-between two folds", -> it "re-renders the line with the placeholder and re-positions the second fold", -> buffer.insert([5, 0], 'abc\n') - expect(displayBuffer.lineForRow(1).text).toBe "1" - expect(displayBuffer.lineForRow(2).fold).toBe fold1 - expect(displayBuffer.lineForRow(3).text).toMatch "abc" - expect(displayBuffer.lineForRow(4).text).toBe "5" - expect(displayBuffer.lineForRow(5).fold).toBe fold2 - expect(displayBuffer.lineForRow(6).text).toMatch /^9-+/ + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe "1" + expect(displayBuffer.tokenizedLineForScreenRow(2).fold).toBe fold1 + expect(displayBuffer.tokenizedLineForScreenRow(3).text).toMatch "abc" + expect(displayBuffer.tokenizedLineForScreenRow(4).text).toBe "5" + expect(displayBuffer.tokenizedLineForScreenRow(5).fold).toBe fold2 + expect(displayBuffer.tokenizedLineForScreenRow(6).text).toMatch /^9-+/ expect(changeHandler).toHaveBeenCalledWith(start: 3, end: 3, screenDelta: 1, bufferDelta: 1) @@ -545,15 +545,15 @@ describe "DisplayBuffer", -> displayBuffer.createFold(1, 9) displayBuffer.createFold(11, 12) - expect(displayBuffer.lineForRow(1).text).toBe '1' - expect(displayBuffer.lineForRow(2).text).toBe '10' + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1' + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '10' displayBuffer.unfoldBufferRow(2) - expect(displayBuffer.lineForRow(1).text).toBe '1' - expect(displayBuffer.lineForRow(2).text).toBe '2' - expect(displayBuffer.lineForRow(7).fold).toBeDefined() - expect(displayBuffer.lineForRow(8).text).toMatch /^9-+/ - expect(displayBuffer.lineForRow(10).fold).toBeDefined() + expect(displayBuffer.tokenizedLineForScreenRow(1).text).toBe '1' + expect(displayBuffer.tokenizedLineForScreenRow(2).text).toBe '2' + expect(displayBuffer.tokenizedLineForScreenRow(7).fold).toBeDefined() + expect(displayBuffer.tokenizedLineForScreenRow(8).text).toMatch /^9-+/ + expect(displayBuffer.tokenizedLineForScreenRow(10).fold).toBeDefined() describe ".outermostFoldsInBufferRowRange(startRow, endRow)", -> it "returns the outermost folds entirely contained in the given row range, exclusive of end row", -> diff --git a/spec/editor-component-spec.coffee b/spec/editor-component-spec.coffee index 411b39241..2ea61d36c 100644 --- a/spec/editor-component-spec.coffee +++ b/spec/editor-component-spec.coffee @@ -65,9 +65,9 @@ describe "EditorComponent", -> linesNode = componentNode.querySelector('.lines') expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, 0px, 0px)" expect(componentNode.querySelectorAll('.line').length).toBe 6 + 2 # no margin above - expect(component.lineNodeForScreenRow(0).textContent).toBe editor.lineForScreenRow(0).text + expect(component.lineNodeForScreenRow(0).textContent).toBe editor.tokenizedLineForScreenRow(0).text expect(component.lineNodeForScreenRow(0).offsetTop).toBe 0 - expect(component.lineNodeForScreenRow(5).textContent).toBe editor.lineForScreenRow(5).text + expect(component.lineNodeForScreenRow(5).textContent).toBe editor.tokenizedLineForScreenRow(5).text expect(component.lineNodeForScreenRow(5).offsetTop).toBe 5 * lineHeightInPixels verticalScrollbarNode.scrollTop = 4.5 * lineHeightInPixels @@ -77,9 +77,9 @@ describe "EditorComponent", -> expect(linesNode.style['-webkit-transform']).toBe "translate3d(0px, #{-4.5 * lineHeightInPixels}px, 0px)" expect(componentNode.querySelectorAll('.line').length).toBe 6 + 4 # margin above and below expect(component.lineNodeForScreenRow(2).offsetTop).toBe 2 * lineHeightInPixels - expect(component.lineNodeForScreenRow(2).textContent).toBe editor.lineForScreenRow(2).text + expect(component.lineNodeForScreenRow(2).textContent).toBe editor.tokenizedLineForScreenRow(2).text expect(component.lineNodeForScreenRow(9).offsetTop).toBe 9 * lineHeightInPixels - expect(component.lineNodeForScreenRow(9).textContent).toBe editor.lineForScreenRow(9).text + expect(component.lineNodeForScreenRow(9).textContent).toBe editor.tokenizedLineForScreenRow(9).text it "updates the top position of subsequent lines when lines are inserted or removed", -> editor.getBuffer().deleteRows(0, 1) @@ -111,11 +111,11 @@ describe "EditorComponent", -> buffer.insert([0, 0], '\n\n') nextAnimationFrame() - expect(component.lineNodeForScreenRow(3).textContent).toBe editor.lineForScreenRow(3).text + expect(component.lineNodeForScreenRow(3).textContent).toBe editor.tokenizedLineForScreenRow(3).text buffer.delete([[0, 0], [3, 0]]) nextAnimationFrame() - expect(component.lineNodeForScreenRow(3).textContent).toBe editor.lineForScreenRow(3).text + expect(component.lineNodeForScreenRow(3).textContent).toBe editor.tokenizedLineForScreenRow(3).text it "updates the top position of lines when the line height changes", -> initialLineHeightInPixels = editor.getLineHeightInPixels() @@ -601,7 +601,7 @@ describe "EditorComponent", -> describe "cursor rendering", -> it "renders the currently visible cursors, translated relative to the scroll position", -> - cursor1 = editor.getCursor() + cursor1 = editor.getLastCursor() cursor1.setScreenPosition([0, 5]) wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' @@ -710,7 +710,7 @@ describe "EditorComponent", -> expect(cursorsNode.classList.contains('blink-off')).toBe false # Stop blinking after moving the cursor - editor.moveCursorRight() + editor.moveRight() expect(cursorsNode.classList.contains('blink-off')).toBe false advanceClock(component.props.cursorBlinkResumeDelay) @@ -814,8 +814,8 @@ describe "EditorComponent", -> it "does not render empty selections", -> editor.addSelectionForBufferRange([[2, 2], [2, 2]]) nextAnimationFrame() - expect(editor.getSelection(0).isEmpty()).toBe true - expect(editor.getSelection(1).isEmpty()).toBe true + expect(editor.getSelections()[0].isEmpty()).toBe true + expect(editor.getSelections()[1].isEmpty()).toBe true expect(componentNode.querySelectorAll('.selection').length).toBe 0 @@ -1481,7 +1481,7 @@ describe "EditorComponent", -> cursor = null beforeEach -> - cursor = editor.getCursor() + cursor = editor.getLastCursor() cursor.setScreenPosition([0, 0]) it "adds the 'has-selection' class to the editor when there is a selection", -> @@ -1827,28 +1827,28 @@ describe "EditorComponent", -> it "inserts the newest character in the input's value into the buffer", -> componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode)) nextAnimationFrame() - expect(editor.lineForBufferRow(0)).toBe 'xvar quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'xvar quicksort = function () {' componentNode.dispatchEvent(buildTextInputEvent(data: 'y', target: inputNode)) nextAnimationFrame() - expect(editor.lineForBufferRow(0)).toBe 'xyvar quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'xyvar quicksort = function () {' it "replaces the last character if the length of the input's value doesn't increase, as occurs with the accented character menu", -> componentNode.dispatchEvent(buildTextInputEvent(data: 'u', target: inputNode)) nextAnimationFrame() - expect(editor.lineForBufferRow(0)).toBe 'uvar quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'uvar quicksort = function () {' # simulate the accented character suggestion's selection of the previous character inputNode.setSelectionRange(0, 1) componentNode.dispatchEvent(buildTextInputEvent(data: 'ü', target: inputNode)) nextAnimationFrame() - expect(editor.lineForBufferRow(0)).toBe 'üvar quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'üvar quicksort = function () {' it "does not handle input events when input is disabled", -> component.setInputEnabled(false) componentNode.dispatchEvent(buildTextInputEvent(data: 'x', target: inputNode)) expect(nextAnimationFrame).toBe noAnimationFrame - expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () {' describe "when IME composition is used to insert international characters", -> inputNode = null @@ -1866,46 +1866,46 @@ describe "EditorComponent", -> it "inserts the chosen completion", -> componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'svar quicksort = function () {' componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'sdvar quicksort = function () {' componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) componentNode.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe '速度var quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe '速度var quicksort = function () {' it "reverts back to the original text when the completion helper is dismissed", -> componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'svar quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'svar quicksort = function () {' componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'sdvar quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'sdvar quicksort = function () {' componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () {' it "allows multiple accented character to be inserted with the ' on a US international layout", -> inputNode.value = "'" inputNode.setSelectionRange(0, 1) componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe "'var quicksort = function () {" + expect(editor.lineTextForBufferRow(0)).toBe "'var quicksort = function () {" componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) componentNode.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe "ávar quicksort = function () {" + expect(editor.lineTextForBufferRow(0)).toBe "ávar quicksort = function () {" inputNode.value = "'" inputNode.setSelectionRange(0, 1) componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: "'", target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe "á'var quicksort = function () {" + expect(editor.lineTextForBufferRow(0)).toBe "á'var quicksort = function () {" componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) componentNode.dispatchEvent(buildTextInputEvent(data: 'á', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe "áávar quicksort = function () {" + expect(editor.lineTextForBufferRow(0)).toBe "áávar quicksort = function () {" describe "when a string is selected", -> beforeEach -> @@ -1914,25 +1914,25 @@ describe "EditorComponent", -> it "inserts the chosen completion", -> componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var ssort = function () {' componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var sdsort = function () {' componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) componentNode.dispatchEvent(buildTextInputEvent(data: '速度', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'var 速度sort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var 速度sort = function () {' it "reverts back to the original text when the completion helper is dismissed", -> componentNode.dispatchEvent(buildIMECompositionEvent('compositionstart', target: inputNode)) componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 's', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'var ssort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var ssort = function () {' componentNode.dispatchEvent(buildIMECompositionEvent('compositionupdate', data: 'sd', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'var sdsort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var sdsort = function () {' componentNode.dispatchEvent(buildIMECompositionEvent('compositionend', target: inputNode)) - expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () {' describe "commands", -> describe "editor:consolidate-selections", -> diff --git a/spec/editor-spec.coffee b/spec/editor-spec.coffee index 07f88bea1..5b0af428c 100644 --- a/spec/editor-spec.coffee +++ b/spec/editor-spec.coffee @@ -30,7 +30,7 @@ describe "Editor", -> 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.getSelection(1).isReversed()).toBeTruthy() + expect(editor2.getSelections()[1].isReversed()).toBeTruthy() expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy() editor2.destroy() @@ -63,8 +63,8 @@ describe "Editor", -> runs -> buffer = editor.buffer - expect(editor.getCursor().getBufferPosition().row).toEqual 5 - expect(editor.getCursor().getBufferPosition().column).toEqual 0 + 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", -> @@ -75,8 +75,8 @@ describe "Editor", -> runs -> buffer = editor.buffer - expect(editor.getCursor().getBufferPosition().row).toEqual 0 - expect(editor.getCursor().getBufferPosition().column).toEqual 8 + expect(editor.getLastCursor().getBufferPosition().row).toEqual 0 + expect(editor.getLastCursor().getBufferPosition().column).toEqual 8 describe ".copy()", -> it "returns a different edit session with the same initial state", -> @@ -88,11 +88,11 @@ describe "Editor", -> editor2 = editor.copy() expect(editor2.id).not.toBe editor.id expect(editor2.getSelectedBufferRanges()).toEqual editor.getSelectedBufferRanges() - expect(editor2.getSelection(1).isReversed()).toBeTruthy() + expect(editor2.getSelections()[1].isReversed()).toBeTruthy() expect(editor2.isFoldedAtBufferRow(4)).toBeTruthy() # editor2 can now diverge from its origin edit session - editor2.getSelection().setBufferRange([[2, 1], [4, 3]]) + 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) @@ -147,49 +147,49 @@ describe "Editor", -> expect(titleChangedHandler.callCount).toBe 2 describe "cursor", -> - describe ".getCursor()", -> + describe ".getLastCursor()", -> it "returns the most recently created cursor", -> editor.addCursorAtScreenPosition([1, 0]) lastCursor = editor.addCursorAtScreenPosition([2, 0]) - expect(editor.getCursor()).toBe lastCursor + 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.moveCursorUp() + editor.moveUp() editor.insertText('a') - editor.moveCursorDown() + editor.moveDown() expect(editor.getCursorBufferPosition()).toEqual [1, 1] it "emits a single 'cursors-moved' event for all moved cursors", -> editor.on 'cursors-moved', cursorsMovedHandler = jasmine.createSpy("cursorsMovedHandler") - editor.moveCursorDown() + editor.moveDown() expect(cursorsMovedHandler.callCount).toBe 1 cursorsMovedHandler.reset() editor.addCursorAtScreenPosition([3, 0]) - editor.moveCursorDown() + editor.moveDown() expect(cursorsMovedHandler.callCount).toBe 1 cursorsMovedHandler.reset() - editor.getCursor().moveDown() + editor.getLastCursor().moveDown() expect(cursorsMovedHandler.callCount).toBe 1 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.moveCursorDown() + 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.moveCursorDown() + editor.moveDown() expect(editor.getCursorScreenPosition().column).toBe 6 it "merges multiple cursors", -> @@ -211,32 +211,32 @@ describe "Editor", -> editor.setCursorScreenPosition([9, 0]) expect(editor.getCursorBufferPosition()).toEqual [8, 11] - describe ".moveCursorUp()", -> + describe ".moveUp()", -> it "moves the cursor up", -> editor.setCursorScreenPosition([2, 2]) - editor.moveCursorUp() + 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.moveCursorUp() + editor.moveUp() expect(editor.getCursorScreenPosition().column).toBe lineLengths[5] - editor.moveCursorUp() + editor.moveUp() expect(editor.getCursorScreenPosition().column).toBe lineLengths[4] - editor.moveCursorUp() + 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.moveCursorUp() + editor.moveUp() expect(editor.getCursorScreenPosition()).toEqual([0, 0]) - editor.moveCursorDown() + editor.moveDown() expect(editor.getCursorScreenPosition()).toEqual([1, 4]) describe "when there is a selection", -> @@ -244,34 +244,34 @@ describe "Editor", -> editor.setSelectedBufferRange([[4, 9],[5, 10]]) it "moves above the selection", -> - cursor = editor.getCursor() - editor.moveCursorUp() + 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.moveCursorUp() + editor.moveUp() expect(editor.getCursors()).toEqual [cursor1] expect(cursor1.getBufferPosition()).toEqual [0,0] - describe ".moveCursorDown()", -> + describe ".moveDown()", -> it "moves the cursor down", -> editor.setCursorScreenPosition([2, 2]) - editor.moveCursorDown() + 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.moveCursorDown() + editor.moveDown() expect(editor.getCursorScreenPosition().column).toBe lineLengths[4] - editor.moveCursorDown() + editor.moveDown() expect(editor.getCursorScreenPosition().column).toBe lineLengths[5] - editor.moveCursorDown() + editor.moveDown() expect(editor.getCursorScreenPosition().column).toBe lineLengths[3] describe "when the cursor is on the last line", -> @@ -281,10 +281,10 @@ describe "Editor", -> expect(lastLine.length).toBeGreaterThan(0) editor.setCursorScreenPosition(row: lastLineIndex, column: editor.getTabLength()) - editor.moveCursorDown() + editor.moveDown() expect(editor.getCursorScreenPosition()).toEqual(row: lastLineIndex, column: lastLine.length) - editor.moveCursorUp() + editor.moveUp() expect(editor.getCursorScreenPosition().column).toBe editor.getTabLength() it "retains a goal column of 0 when moving back up", -> @@ -293,8 +293,8 @@ describe "Editor", -> expect(lastLine.length).toBeGreaterThan(0) editor.setCursorScreenPosition(row: lastLineIndex, column: 0) - editor.moveCursorDown() - editor.moveCursorUp() + editor.moveDown() + editor.moveUp() expect(editor.getCursorScreenPosition().column).toBe 0 describe "when there is a selection", -> @@ -302,8 +302,8 @@ describe "Editor", -> editor.setSelectedBufferRange([[4, 9],[5, 10]]) it "moves below the selection", -> - cursor = editor.getCursor() - editor.moveCursorDown() + cursor = editor.getLastCursor() + editor.moveDown() expect(cursor.getBufferPosition()).toEqual [6, 10] it "merges cursors when they overlap", -> @@ -311,34 +311,65 @@ describe "Editor", -> editor.addCursorAtScreenPosition([11, 2]) [cursor1, cursor2] = editor.getCursors() - editor.moveCursorDown() + editor.moveDown() expect(editor.getCursors()).toEqual [cursor1] expect(cursor1.getBufferPosition()).toEqual [12,2] - describe ".moveCursorLeft()", -> + describe ".moveLeft()", -> it "moves the cursor by one column to the left", -> editor.setCursorScreenPosition([1, 8]) - editor.moveCursorLeft() + 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.moveCursorLeft() + 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.moveCursorLeft() + 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.moveCursorLeft() + editor.moveLeft() expect(editor.getCursorBufferPosition()).toEqual [5, 4] describe "when there is a selection", -> @@ -346,11 +377,11 @@ describe "Editor", -> editor.setSelectedBufferRange([[5, 22],[5, 27]]) it "moves to the left of the selection", -> - cursor = editor.getCursor() - editor.moveCursorLeft() + cursor = editor.getLastCursor() + editor.moveLeft() expect(cursor.getBufferPosition()).toEqual [5, 22] - editor.moveCursorLeft() + editor.moveLeft() expect(cursor.getBufferPosition()).toEqual [5, 21] it "merges cursors when they overlap", -> @@ -358,23 +389,49 @@ describe "Editor", -> editor.addCursorAtScreenPosition([0, 1]) [cursor1, cursor2] = editor.getCursors() - editor.moveCursorLeft() + editor.moveLeft() expect(editor.getCursors()).toEqual [cursor1] expect(cursor1.getBufferPosition()).toEqual [0,0] - describe ".moveCursorRight()", -> + describe ".moveRight()", -> it "moves the cursor by one column to the right", -> editor.setCursorScreenPosition([3, 3]) - editor.moveCursorRight() + 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.moveCursorRight() + 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 @@ -383,7 +440,7 @@ describe "Editor", -> lastPosition = { row: lastLineIndex, column: lastLine.length } editor.setCursorScreenPosition(lastPosition) - editor.moveCursorRight() + editor.moveRight() expect(editor.getCursorScreenPosition()).toEqual(lastPosition) @@ -392,11 +449,11 @@ describe "Editor", -> editor.setSelectedBufferRange([[5, 22],[5, 27]]) it "moves to the left of the selection", -> - cursor = editor.getCursor() - editor.moveCursorRight() + cursor = editor.getLastCursor() + editor.moveRight() expect(cursor.getBufferPosition()).toEqual [5, 27] - editor.moveCursorRight() + editor.moveRight() expect(cursor.getBufferPosition()).toEqual [5, 28] it "merges cursors when they overlap", -> @@ -404,85 +461,85 @@ describe "Editor", -> editor.addCursorAtScreenPosition([12, 1]) [cursor1, cursor2] = editor.getCursors() - editor.moveCursorRight() + editor.moveRight() expect(editor.getCursors()).toEqual [cursor1] expect(cursor1.getBufferPosition()).toEqual [12,2] - describe ".moveCursorToTop()", -> + describe ".moveToTop()", -> it "moves the cursor to the top of the buffer", -> editor.setCursorScreenPosition [11,1] editor.addCursorAtScreenPosition [12,0] - editor.moveCursorToTop() + editor.moveToTop() expect(editor.getCursors().length).toBe 1 expect(editor.getCursorBufferPosition()).toEqual [0,0] - describe ".moveCursorToBottom()", -> + describe ".moveToBottom()", -> it "moves the cusor to the bottom of the buffer", -> editor.setCursorScreenPosition [0,0] editor.addCursorAtScreenPosition [1,0] - editor.moveCursorToBottom() + editor.moveToBottom() expect(editor.getCursors().length).toBe 1 expect(editor.getCursorBufferPosition()).toEqual [12,2] - describe ".moveCursorToBeginningOfScreenLine()", -> + describe ".moveToBeginningOfScreenLine()", -> describe "when soft wrap is on", -> it "moves cursor to the beginning of the screen line", -> editor.setSoftWrap(true) editor.setEditorWidthInChars(10) editor.setCursorScreenPosition([1, 2]) - editor.moveCursorToBeginningOfScreenLine() - cursor = editor.getCursor() + 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.moveCursorToBeginningOfScreenLine() + 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 ".moveCursorToEndOfScreenLine()", -> + describe ".moveToEndOfScreenLine()", -> describe "when soft wrap is on", -> it "moves cursor to the beginning of the screen line", -> editor.setSoftWrap(true) editor.setEditorWidthInChars(10) editor.setCursorScreenPosition([1, 2]) - editor.moveCursorToEndOfScreenLine() - cursor = editor.getCursor() + 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.moveCursorToEndOfScreenLine() + 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 ".moveCursorToBeginningOfLine()", -> + describe ".moveToBeginningOfLine()", -> it "moves cursor to the beginning of the buffer line", -> editor.setSoftWrap(true) editor.setEditorWidthInChars(10) editor.setCursorScreenPosition([1, 2]) - editor.moveCursorToBeginningOfLine() - cursor = editor.getCursor() + editor.moveToBeginningOfLine() + cursor = editor.getLastCursor() expect(cursor.getScreenPosition()).toEqual [0, 0] - describe ".moveCursorToEndOfLine()", -> + describe ".moveToEndOfLine()", -> it "moves cursor to the end of the buffer line", -> editor.setSoftWrap(true) editor.setEditorWidthInChars(10) editor.setCursorScreenPosition([0, 2]) - editor.moveCursorToEndOfLine() - cursor = editor.getCursor() + editor.moveToEndOfLine() + cursor = editor.getLastCursor() expect(cursor.getScreenPosition()).toEqual [3, 4] - describe ".moveCursorToFirstCharacterOfLine()", -> + 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.setSoftWrap(true) @@ -490,12 +547,12 @@ describe "Editor", -> editor.setCursorScreenPosition [2,5] editor.addCursorAtScreenPosition [8,7] - editor.moveCursorToFirstCharacterOfLine() + editor.moveToFirstCharacterOfLine() [cursor1, cursor2] = editor.getCursors() expect(cursor1.getScreenPosition()).toEqual [2,0] expect(cursor2.getScreenPosition()).toEqual [8,4] - editor.moveCursorToFirstCharacterOfLine() + editor.moveToFirstCharacterOfLine() expect(cursor1.getScreenPosition()).toEqual [2,0] expect(cursor2.getScreenPosition()).toEqual [8,0] @@ -504,29 +561,29 @@ describe "Editor", -> editor.setCursorScreenPosition [0,5] editor.addCursorAtScreenPosition [1,7] - editor.moveCursorToFirstCharacterOfLine() + editor.moveToFirstCharacterOfLine() [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual [0,0] expect(cursor2.getBufferPosition()).toEqual [1,2] - editor.moveCursorToFirstCharacterOfLine() + 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.moveCursorToFirstCharacterOfLine() - cursor = editor.getCursor() + 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.moveCursorToFirstCharacterOfLine() + editor.moveToFirstCharacterOfLine() expect(editor.getCursorBufferPosition()).toEqual [1,2] - editor.moveCursorToFirstCharacterOfLine() + editor.moveToFirstCharacterOfLine() expect(editor.getCursorBufferPosition()).toEqual [1,0] describe "when invisible characters are enabled with hard tabs", -> @@ -535,19 +592,19 @@ describe "Editor", -> buffer.setTextInRange([[1, 0], [1, Infinity]], '\t\t\ta', false) editor.setCursorScreenPosition [1,7] - editor.moveCursorToFirstCharacterOfLine() + editor.moveToFirstCharacterOfLine() expect(editor.getCursorBufferPosition()).toEqual [1,3] - editor.moveCursorToFirstCharacterOfLine() + editor.moveToFirstCharacterOfLine() expect(editor.getCursorBufferPosition()).toEqual [1,0] - describe ".moveCursorToBeginningOfWord()", -> + 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.moveCursorToBeginningOfWord() + editor.moveToBeginningOfWord() expect(cursor1.getBufferPosition()).toEqual [0, 4] expect(cursor2.getBufferPosition()).toEqual [1, 11] @@ -555,19 +612,19 @@ describe "Editor", -> it "does not fail at position [0, 0]", -> editor.setCursorBufferPosition([0, 0]) - editor.moveCursorToBeginningOfWord() + editor.moveToBeginningOfWord() it "treats lines with only whitespace as a word", -> editor.setCursorBufferPosition([11, 0]) - editor.moveCursorToBeginningOfWord() + editor.moveToBeginningOfWord() expect(editor.getCursorBufferPosition()).toEqual [10, 0] it "works when the current line is blank", -> editor.setCursorBufferPosition([10, 0]) - editor.moveCursorToBeginningOfWord() + editor.moveToBeginningOfWord() expect(editor.getCursorBufferPosition()).toEqual [9, 2] - describe ".moveCursorToPreviousWordBoundary()", -> + describe ".moveToPreviousWordBoundary()", -> it "moves the cursor to the previous word boundary", -> editor.setCursorBufferPosition [0, 8] editor.addCursorAtBufferPosition [2, 0] @@ -575,14 +632,14 @@ describe "Editor", -> editor.addCursorAtBufferPosition [3, 14] [cursor1, cursor2, cursor3, cursor4] = editor.getCursors() - editor.moveCursorToPreviousWordBoundary() + 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 ".moveCursorToNextWordBoundary()", -> + describe ".moveToNextWordBoundary()", -> it "moves the cursor to the previous word boundary", -> editor.setCursorBufferPosition [0, 8] editor.addCursorAtBufferPosition [2, 40] @@ -590,21 +647,21 @@ describe "Editor", -> editor.addCursorAtBufferPosition [3, 30] [cursor1, cursor2, cursor3, cursor4] = editor.getCursors() - editor.moveCursorToNextWordBoundary() + 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 ".moveCursorToEndOfWord()", -> + 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.moveCursorToEndOfWord() + editor.moveToEndOfWord() expect(cursor1.getBufferPosition()).toEqual [0, 13] expect(cursor2.getBufferPosition()).toEqual [1, 12] @@ -613,27 +670,27 @@ describe "Editor", -> it "does not blow up when there is no next word", -> editor.setCursorBufferPosition [Infinity, Infinity] endPosition = editor.getCursorBufferPosition() - editor.moveCursorToEndOfWord() + editor.moveToEndOfWord() expect(editor.getCursorBufferPosition()).toEqual endPosition it "treats lines with only whitespace as a word", -> editor.setCursorBufferPosition([9, 4]) - editor.moveCursorToEndOfWord() + editor.moveToEndOfWord() expect(editor.getCursorBufferPosition()).toEqual [10, 0] it "works when the current line is blank", -> editor.setCursorBufferPosition([10, 0]) - editor.moveCursorToEndOfWord() + editor.moveToEndOfWord() expect(editor.getCursorBufferPosition()).toEqual [11, 8] - describe ".moveCursorToBeginningOfNextWord()", -> + 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.moveCursorToBeginningOfNextWord() + editor.moveToBeginningOfNextWord() expect(cursor1.getBufferPosition()).toEqual [0, 14] expect(cursor2.getBufferPosition()).toEqual [1, 13] @@ -642,56 +699,56 @@ describe "Editor", -> # When the cursor is on whitespace editor.setText("ab cde- ") editor.setCursorBufferPosition [0,2] - cursor = editor.getCursor() - editor.moveCursorToBeginningOfNextWord() + 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.moveCursorToBeginningOfNextWord() + editor.moveToBeginningOfNextWord() expect(editor.getCursorBufferPosition()).toEqual endPosition it "treats lines with only whitespace as a word", -> editor.setCursorBufferPosition([9, 4]) - editor.moveCursorToBeginningOfNextWord() + editor.moveToBeginningOfNextWord() expect(editor.getCursorBufferPosition()).toEqual [10, 0] it "works when the current line is blank", -> editor.setCursorBufferPosition([10, 0]) - editor.moveCursorToBeginningOfNextWord() + editor.moveToBeginningOfNextWord() expect(editor.getCursorBufferPosition()).toEqual [11, 9] - describe ".moveCursorToBeginningOfNextParagraph()", -> + describe ".moveToBeginningOfNextParagraph()", -> it "moves the cursor before the first line of the next paragraph", -> editor.setCursorBufferPosition [0,6] - cursor = editor.getCursor() + cursor = editor.getLastCursor() - editor.moveCursorToBeginningOfNextParagraph() + editor.moveToBeginningOfNextParagraph() expect(cursor.getBufferPosition()).toEqual { row : 10, column : 0 } editor.setText("") editor.setCursorBufferPosition [0,0] - cursor = editor.getCursor() - editor.moveCursorToBeginningOfNextParagraph() + cursor = editor.getLastCursor() + editor.moveToBeginningOfNextParagraph() expect(cursor.getBufferPosition()).toEqual [0, 0] - describe ".moveCursorToBeginningOfPreviousParagraph()", -> + describe ".moveToBeginningOfPreviousParagraph()", -> it "moves the cursor before the first line of the pevious paragraph", -> editor.setCursorBufferPosition [10,0] - cursor = editor.getCursor() + cursor = editor.getLastCursor() - editor.moveCursorToBeginningOfPreviousParagraph() + editor.moveToBeginningOfPreviousParagraph() expect(cursor.getBufferPosition()).toEqual { row : 0, column : 0 } editor.setText("") editor.setCursorBufferPosition [0,0] - cursor = editor.getCursor() - editor.moveCursorToBeginningOfPreviousParagraph() + cursor = editor.getLastCursor() + editor.moveToBeginningOfPreviousParagraph() expect(cursor.getBufferPosition()).toEqual [0, 0] @@ -750,6 +807,26 @@ describe "Editor", -> buffer.insert([8, 0], '...') expect(cursorMovedHandler).not.toHaveBeenCalled() + describe "::getCursorBufferPositions()", -> + it "returns the cursor positions in the order they were added", -> + cursor1 = editor.addCursorAtBufferPosition([8, 5]) + cursor2 = editor.addCursorAtBufferPosition([4, 5]) + expect(editor.getCursorBufferPositions()).toEqual [[0, 0], [8, 5], [4, 5]] + + 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", -> @@ -782,23 +859,23 @@ describe "Editor", -> editor.setCursorScreenPosition([2, 0]) expect(editor.getScrollBottom()).toBe 5.5 * 10 - editor.moveCursorDown() + editor.moveDown() expect(editor.getScrollBottom()).toBe 6 * 10 - editor.moveCursorDown() + 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.moveCursorUp() + editor.moveUp() expect(editor.getScrollBottom()).toBe editor.getScrollHeight() - editor.moveCursorUp() + editor.moveUp() expect(editor.getScrollTop()).toBe 7 * 10 - editor.moveCursorUp() + 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", -> @@ -808,10 +885,10 @@ describe "Editor", -> editor.setCursorScreenPosition([0, 2]) expect(editor.getScrollRight()).toBe 5.5 * 10 - editor.moveCursorRight() + editor.moveRight() expect(editor.getScrollRight()).toBe 6 * 10 - editor.moveCursorRight() + 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", -> @@ -820,10 +897,10 @@ describe "Editor", -> expect(editor.getScrollRight()).toBe editor.getScrollWidth() - editor.moveCursorLeft() + editor.moveLeft() expect(editor.getScrollLeft()).toBe 59 * 10 - editor.moveCursorLeft() + editor.moveLeft() expect(editor.getScrollLeft()).toBe 58 * 10 it "scrolls down when inserting lines makes the document longer than the editor's height", -> @@ -843,7 +920,7 @@ describe "Editor", -> selection = null beforeEach -> - selection = editor.getSelection() + selection = editor.getLastSelection() describe ".selectUp/Down/Left/Right()", -> it "expands each selection to its cursor's new location", -> @@ -881,14 +958,13 @@ describe "Editor", -> [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,14], [1,20]]], reversed: true) + editor.setSelectedBufferRanges([[[0,9], [0,13]], [[0,13], [1,20]]], reversed: true) [selection1, selection2] = editor.getSelections() editor.selectLeft() @@ -897,7 +973,7 @@ describe "Editor", -> expect(selection1.isReversed()).toBeTruthy() it "merges selections when they intersect when moving right", -> - editor.setSelectedBufferRanges([[[0,9], [0,13]], [[0,14], [1,20]]]) + editor.setSelectedBufferRanges([[[0,9], [0,14]], [[0,14], [1,20]]]) [selection1, selection2] = editor.getSelections() editor.selectRight() @@ -905,6 +981,39 @@ describe "Editor", -> 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]]) @@ -969,8 +1078,8 @@ describe "Editor", -> editor.selectToTop() expect(editor.getCursors().length).toBe 1 expect(editor.getCursorBufferPosition()).toEqual [0,0] - expect(editor.getSelection().getBufferRange()).toEqual [[0,0], [11,2]] - expect(editor.getSelection().isReversed()).toBeTruthy() + 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", -> @@ -979,13 +1088,13 @@ describe "Editor", -> editor.selectToBottom() expect(editor.getCursors().length).toBe 1 expect(editor.getCursorBufferPosition()).toEqual [12,2] - expect(editor.getSelection().getBufferRange()).toEqual [[9,3], [12,2]] - expect(editor.getSelection().isReversed()).toBeFalsy() + 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.getSelection().getBufferRange()).toEqual buffer.getRange() + expect(editor.getLastSelection().getBufferRange()).toEqual buffer.getRange() describe ".selectToBeginningOfLine()", -> it "selects text from cusor position to beginning of line", -> @@ -1025,20 +1134,20 @@ describe "Editor", -> expect(selection2.getBufferRange()).toEqual [[11,3], [11,44]] expect(selection2.isReversed()).toBeFalsy() - describe ".selectLine()", -> + describe ".selectLinesContainingCursors()", -> it "selects the entire line (including newlines) at given row", -> editor.setCursorScreenPosition([1, 2]) - editor.selectLine() + editor.selectLinesContainingCursors() expect(editor.getSelectedBufferRange()).toEqual [[1,0], [2,0]] expect(editor.getSelectedText()).toBe " var sort = function(items) {\n" editor.setCursorScreenPosition([12, 2]) - editor.selectLine() + editor.selectLinesContainingCursors() expect(editor.getSelectedBufferRange()).toEqual [[12,0], [12,2]] editor.setCursorBufferPosition([0, 2]) - editor.selectLine() - editor.selectLine() + editor.selectLinesContainingCursors() + editor.selectLinesContainingCursors() expect(editor.getSelectedBufferRange()).toEqual [[0,0], [2,0]] describe ".selectToBeginningOfWord()", -> @@ -1138,39 +1247,39 @@ describe "Editor", -> expect(selection4.getBufferRange()).toEqual [[3,30], [3,31]] expect(selection4.isReversed()).toBeFalsy() - describe ".selectWord()", -> + describe ".selectWordsContainingCursors()", -> describe "when the cursor is inside a word", -> it "selects the entire word", -> editor.setCursorScreenPosition([0, 8]) - editor.selectWord() + 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.selectWord() + editor.selectWordsContainingCursors() expect(editor.getSelectedText()).toBe 'quicksort' editor.setCursorScreenPosition([0, 3]) - editor.selectWord() + 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.selectWord() + editor.selectWordsContainingCursors() expect(editor.getSelectedBufferRange()).toEqual [[5, 0], [5, 6]] editor.setCursorScreenPosition([5, 0]) - editor.selectWord() + 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.moveCursorToBottom() - editor.selectWord() + editor.moveToBottom() + editor.selectWordsContainingCursors() expect(editor.getSelectedBufferRange()).toEqual [[12, 2], [12, 6]] describe ".selectToFirstCharacterOfLine()", -> @@ -1210,8 +1319,12 @@ describe "Editor", -> 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.getSelection() + selection = editor.getLastSelection() editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[4, 4], [5, 5]]]) [selection1, selection2] = editor.getSelections() @@ -1227,10 +1340,10 @@ describe "Editor", -> editor.createFold(10, 11) editor.setSelectedBufferRanges([[[2, 2], [3, 3]], [[6, 6], [7, 7]]]) - expect(editor.lineForScreenRow(1).fold).toBeUndefined() - expect(editor.lineForScreenRow(2).fold).toBeUndefined() - expect(editor.lineForScreenRow(6).fold).toBeUndefined() - expect(editor.lineForScreenRow(10).fold).toBeDefined() + 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", -> @@ -1241,6 +1354,29 @@ describe "Editor", -> 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", -> @@ -1383,10 +1519,10 @@ describe "Editor", -> editor.addSelectionForBufferRange([[3, 37], [3, 44]]) editor.addSelectionAbove() expect(editor.getSelectedBufferRanges()).toEqual [ - [[2, 16], [2, 21]] - [[2, 37], [2, 40]] [[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() @@ -1395,8 +1531,8 @@ describe "Editor", -> editor.setSelectedBufferRange([[6, 31], [6, 38]]) editor.addSelectionAbove() expect(editor.getSelectedBufferRanges()).toEqual [ - [[3, 31], [3, 38]] [[6, 31], [6, 38]] + [[3, 31], [3, 38]] ] it "honors the original selection's range (goal range) when adding across shorter lines", -> @@ -1405,10 +1541,10 @@ describe "Editor", -> editor.addSelectionAbove() editor.addSelectionAbove() expect(editor.getSelectedBufferRanges()).toEqual [ - [[3, 22], [3, 38]] - [[4, 22], [4, 29]] - [[5, 22], [5, 30]] [[6, 22], [6, 38]] + [[5, 22], [5, 30]] + [[4, 22], [4, 29]] + [[3, 22], [3, 38]] ] describe "when the selection is empty", -> @@ -1418,26 +1554,26 @@ describe "Editor", -> editor.addSelectionAbove() editor.addSelectionAbove() expect(editor.getSelectedBufferRanges()).toEqual [ - [[3, 36], [3, 36]] - [[4, 29], [4, 29]] - [[5, 30], [5, 30]] [[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 [ - [[9, 4], [9, 4]] [[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 [ - [[9, 0], [9, 0]] [[10, 0], [10, 0]] + [[9, 0], [9, 0]] ] describe ".splitSelectionsIntoLines()", -> @@ -1464,7 +1600,7 @@ describe "Editor", -> 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.getSelection() + selection1 = editor.getLastSelection() selection2 = editor.addSelectionForBufferRange([[3, 25], [3, 34]]) selection3 = editor.addSelectionForBufferRange([[8, 4], [8, 10]]) @@ -1480,19 +1616,19 @@ describe "Editor", -> it "clears the selection", -> makeSelection() - editor.moveCursorDown() + editor.moveDown() expect(selection.isEmpty()).toBeTruthy() makeSelection() - editor.moveCursorUp() + editor.moveUp() expect(selection.isEmpty()).toBeTruthy() makeSelection() - editor.moveCursorLeft() + editor.moveLeft() expect(selection.isEmpty()).toBeTruthy() makeSelection() - editor.moveCursorRight() + editor.moveRight() expect(selection.isEmpty()).toBeTruthy() makeSelection() @@ -1573,7 +1709,7 @@ describe "Editor", -> expect(selection1.isEmpty()).toBeTruthy() expect(selection2.isEmpty()).toBeTruthy() - expect(editor.lineForBufferRow(0)).toBe "var x = functix () {" + 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", -> @@ -1595,7 +1731,7 @@ describe "Editor", -> editor.createFold(2,4) editor.setSelectedBufferRange([[1,0], [2,0]]) editor.insertText('holy cow') - expect(editor.lineForScreenRow(2).fold).toBeUndefined() + expect(editor.tokenizedLineForScreenRow(2).fold).toBeUndefined() describe "when will-insert-text and did-insert-text events are used", -> beforeEach -> @@ -1680,10 +1816,10 @@ describe "Editor", -> editor.insertNewline() - expect(editor.lineForBufferRow(3)).toBe " var pivot" - expect(editor.lineForBufferRow(4)).toBe " = items.shift(), current" - expect(editor.lineForBufferRow(5)).toBe ", left = [], right = [];" - expect(editor.lineForBufferRow(6)).toBe " while(items.length > 0) {" + 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] @@ -1695,13 +1831,13 @@ describe "Editor", -> editor.addCursorAtScreenPosition([6, 0]) editor.insertText("\n") - expect(editor.lineForBufferRow(3)).toBe "" - expect(editor.lineForBufferRow(4)).toBe " var pivot = items.shift(), current, left = [], right = [];" - expect(editor.lineForBufferRow(5)).toBe " while(items.length > 0) {" - expect(editor.lineForBufferRow(6)).toBe " current = items.shift();" - expect(editor.lineForBufferRow(7)).toBe "" - expect(editor.lineForBufferRow(8)).toBe " current < pivot ? left.push(current) : right.push(current);" - expect(editor.lineForBufferRow(9)).toBe " }" + 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] @@ -1729,8 +1865,8 @@ describe "Editor", -> editor.setCursorBufferPosition([0]) editor.insertNewlineAbove() expect(editor.getCursorBufferPosition()).toEqual [0,0] - expect(editor.lineForBufferRow(0)).toBe '' - expect(editor.lineForBufferRow(1)).toBe 'var quicksort = function () {' + 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", -> @@ -1738,8 +1874,8 @@ describe "Editor", -> editor.setCursorBufferPosition([3,4]) editor.insertNewlineAbove() expect(editor.getCursorBufferPosition()).toEqual [3,0] - expect(editor.lineForBufferRow(3)).toBe '' - expect(editor.lineForBufferRow(4)).toBe ' var pivot = items.shift(), current, left = [], right = [];' + 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() @@ -1753,33 +1889,33 @@ describe "Editor", -> editor.insertNewlineAbove() expect(editor.getCursorBufferPosition()).toEqual [0,2] - expect(editor.lineForBufferRow(0)).toBe ' ' - expect(editor.lineForBufferRow(1)).toBe ' var test' + 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.lineForBufferRow(0)).toBe '' - expect(editor.lineForBufferRow(1)).toBe ' ' - expect(editor.lineForBufferRow(2)).toBe ' var test' + 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.lineForBufferRow(0)).toBe 'function() {' - expect(editor.lineForBufferRow(1)).toBe ' ' - expect(editor.lineForBufferRow(2)).toBe '}' + 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.lineForBufferRow(10)).toBe ' };' + expect(editor.lineTextForBufferRow(10)).toBe ' };' describe ".backspace()", -> describe "when there is a single cursor", -> @@ -1801,7 +1937,7 @@ describe "Editor", -> expect(line).toBe " var ort = function(items) {" expect(editor.getCursorScreenPosition()).toEqual {row: 1, column: 6} expect(changeScreenRangeHandler).toHaveBeenCalled() - expect(editor.getCursor().isVisible()).toBeTruthy() + expect(editor.getLastCursor().isVisible()).toBeTruthy() describe "when the cursor is at the beginning of a line", -> it "joins it with the line above", -> @@ -1863,7 +1999,7 @@ describe "Editor", -> editor.backspace() - expect(editor.lineForBufferRow(3)).toBe " var pivo = items.shift(), curren, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " var pivo = items.shift(), curren, left = [], right = [];" [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual [3, 12] @@ -1881,8 +2017,8 @@ describe "Editor", -> editor.backspace() - expect(editor.lineForBufferRow(3)).toBe " var pivo = items.shift(), current, left = [], right = [];" - expect(editor.lineForBufferRow(4)).toBe " whileitems.length > 0) {" + 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] @@ -1898,10 +2034,10 @@ describe "Editor", -> editor.addCursorAtScreenPosition([6, 0]) editor.backspace() - expect(editor.lineForBufferRow(2)).toBe " if (items.length <= 1) return items; var pivot = items.shift(), current, left = [], right = [];" - expect(editor.lineForBufferRow(3)).toBe " while(items.length > 0) {" - expect(editor.lineForBufferRow(4)).toBe " current = items.shift(); current < pivot ? left.push(current) : right.push(current);" - expect(editor.lineForBufferRow(5)).toBe " }" + 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] @@ -1920,13 +2056,13 @@ describe "Editor", -> editor.backspace() expect(buffer.lineForRow(3)).toBe " while(items.length > 0) {" - expect(editor.lineForScreenRow(3).fold).toBeDefined() + 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.lineForBufferRow(0)).toBe 'var = () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var = () {' describe ".deleteToBeginningOfWord()", -> describe "when no text is selected", -> @@ -2058,7 +2194,7 @@ describe "Editor", -> editor.delete() expect(buffer.lineForRow(3)).toBe " ar pivot = items.shift(), current, left = [], right = [];" - expect(editor.lineForScreenRow(4).fold).toBeDefined() + expect(editor.tokenizedLineForScreenRow(4).fold).toBeDefined() expect(editor.getCursorScreenPosition()).toEqual [3, 4] describe "when the cursor is on a folded line", -> @@ -2070,8 +2206,8 @@ describe "Editor", -> oldLine8 = buffer.lineForRow(8) editor.delete() - expect(editor.lineForScreenRow(2).text).toBe oldLine7 - expect(editor.lineForScreenRow(3).text).toBe oldLine8 + 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", -> @@ -2081,7 +2217,7 @@ describe "Editor", -> editor.delete() - expect(editor.lineForBufferRow(3)).toBe " var pivot= items.shift(), current left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " var pivot= items.shift(), current left = [], right = [];" [cursor1, cursor2] = editor.getCursors() expect(cursor1.getBufferPosition()).toEqual [3, 13] @@ -2099,8 +2235,8 @@ describe "Editor", -> editor.delete() - expect(editor.lineForBufferRow(3)).toBe " var pivot= items.shift(), current, left = [], right = [];" - expect(editor.lineForBufferRow(4)).toBe " while(tems.length > 0) {" + 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] @@ -2117,7 +2253,7 @@ describe "Editor", -> editor.delete() - expect(editor.lineForBufferRow(0)).toBe "var quicksort = function () { var sort = function(items) { if (items.length <= 1) return items;" + 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] @@ -2129,14 +2265,14 @@ describe "Editor", -> editor.delete() expect(buffer.lineForRow(1)).toBe ' var sort = function(it) {' expect(buffer.lineForRow(2)).toBe 'if (items.length <= 1) return items;' - expect(editor.getSelection().isEmpty()).toBeTruthy() + 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.lineForBufferRow(0)).toBe 'var = () {' + expect(editor.lineTextForBufferRow(0)).toBe 'var = () {' describe ".deleteToEndOfWord()", -> describe "when no text is selected", -> @@ -2252,7 +2388,7 @@ describe "Editor", -> describe "when the selection is not empty", -> it "indents the selected lines", -> editor.setSelectedBufferRange([[0, 0], [10, 0]]) - selection = editor.getSelection() + selection = editor.getLastSelection() spyOn(selection, "indentSelectedRows") editor.indent() expect(selection.indentSelectedRows).toHaveBeenCalled() @@ -2290,7 +2426,7 @@ describe "Editor", -> editor.setEditorWidthInChars(10) editor.setCursorScreenPosition([2, 2]) editor.cutToEndOfLine() - expect(editor.lineForScreenRow(2).text).toBe '= () {' + expect(editor.tokenizedLineForScreenRow(2).text).toBe '= () {' describe "when soft wrap is off", -> describe "when nothing is selected", -> @@ -2331,27 +2467,27 @@ describe "Editor", -> it "pastes text into the buffer", -> atom.clipboard.write('first') editor.pasteText() - expect(editor.lineForBufferRow(0)).toBe "var first = function () {" - expect(editor.lineForBufferRow(1)).toBe " var first = function(items) {" + expect(editor.lineTextForBufferRow(0)).toBe "var first = function () {" + expect(editor.lineTextForBufferRow(1)).toBe " var first = function(items) {" describe 'when the clipboard has many selections', -> it "pastes each selection separately into the buffer", -> atom.clipboard.write('first\nsecond', {selections: ['first', 'second'] }) editor.pasteText() - expect(editor.lineForBufferRow(0)).toBe "var first = function () {" - expect(editor.lineForBufferRow(1)).toBe " var second = function(items) {" + expect(editor.lineTextForBufferRow(0)).toBe "var first = function () {" + expect(editor.lineTextForBufferRow(1)).toBe " var second = function(items) {" describe 'and the selections count does not match', -> it "pastes the whole text into the buffer", -> atom.clipboard.write('first\nsecond\nthird', {selections: ['first', 'second', 'third'] }) editor.pasteText() - expect(editor.lineForBufferRow(0)).toBe "var first" - expect(editor.lineForBufferRow(1)).toBe "second" - expect(editor.lineForBufferRow(2)).toBe "third = function () {" + expect(editor.lineTextForBufferRow(0)).toBe "var first" + expect(editor.lineTextForBufferRow(1)).toBe "second" + expect(editor.lineTextForBufferRow(2)).toBe "third = function () {" - expect(editor.lineForBufferRow(3)).toBe " var first" - expect(editor.lineForBufferRow(4)).toBe "second" - expect(editor.lineForBufferRow(5)).toBe "third = function(items) {" + expect(editor.lineTextForBufferRow(3)).toBe " var first" + expect(editor.lineTextForBufferRow(4)).toBe "second" + expect(editor.lineTextForBufferRow(5)).toBe "third = function(items) {" describe ".indentSelectedRows()", -> describe "when nothing is selected", -> @@ -2545,7 +2681,7 @@ describe "Editor", -> it "preserves selection emptiness", -> editor.setCursorBufferPosition([4, 0]) editor.toggleLineCommentsInSelection() - expect(editor.getSelection().isEmpty()).toBeTruthy() + expect(editor.getLastSelection().isEmpty()).toBeTruthy() it "does not explode if the current language mode has no comment regex", -> editor.destroy() @@ -2576,7 +2712,7 @@ describe "Editor", -> editor.toggleLineCommentsInSelection() expect(buffer.lineForRow(10)).toBe "// " - editor.moveCursorToBeginningOfLine() + editor.moveToBeginningOfLine() editor.insertText(" ") editor.setSelectedBufferRange([[10, 0], [10, 0]]) editor.toggleLineCommentsInSelection() @@ -2660,7 +2796,7 @@ describe "Editor", -> editor.beginTransaction() editor.delete() - editor.moveCursorToEndOfLine() + editor.moveToEndOfLine() editor.insertText('5') expect(buffer.getText()).toBe '145' @@ -2688,7 +2824,7 @@ describe "Editor", -> expect(cursor3.getScreenPosition()).toEqual [1, 0] it "does not destroy cursors or selections when a change encompasses them", -> - cursor = editor.getCursor() + cursor = editor.getLastCursor() cursor.setBufferPosition [3, 3] editor.buffer.delete([[3, 1], [3, 5]]) expect(cursor.getBufferPosition()).toEqual [3, 1] @@ -2715,9 +2851,15 @@ describe "Editor", -> 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.getCursor().moveToTop() + editor.getLastCursor().moveToTop() line1 = buffer.lineForRow(1) count = buffer.getLineCount() expect(buffer.lineForRow(0)).not.toBe(line1) @@ -2729,7 +2871,7 @@ describe "Editor", -> count = buffer.getLineCount() secondToLastLine = buffer.lineForRow(count - 2) expect(buffer.lineForRow(count - 1)).not.toBe(secondToLastLine) - editor.getCursor().moveToBottom() + editor.getLastCursor().moveToBottom() editor.deleteLine() newCount = buffer.getLineCount() expect(buffer.lineForRow(newCount - 1)).toBe(secondToLastLine) @@ -2756,8 +2898,8 @@ describe "Editor", -> it "deletes the entire region when invoke on a folded region", -> editor.foldBufferRow(1) - editor.getCursor().moveToTop() - editor.getCursor().moveDown() + editor.getLastCursor().moveToTop() + editor.getLastCursor().moveDown() expect(buffer.getLineCount()).toBe(13) editor.deleteLine() expect(buffer.getLineCount()).toBe(4) @@ -2766,7 +2908,7 @@ describe "Editor", -> count = buffer.getLineCount() expect(count).toBeGreaterThan(0) for line in [0...count] - editor.getCursor().moveToBottom() + editor.getLastCursor().moveToBottom() editor.deleteLine() expect(buffer.getLineCount()).toBe(1) expect(buffer.getText()).toBe('') @@ -2775,7 +2917,7 @@ describe "Editor", -> count = buffer.getLineCount() expect(count).toBeGreaterThan(0) for line in [0...count] - editor.getCursor().moveToTop() + editor.getLastCursor().moveToTop() editor.deleteLine() expect(buffer.getLineCount()).toBe(1) expect(buffer.getText()).toBe('') @@ -2831,13 +2973,13 @@ describe "Editor", -> editor.buffer.setText("abc") editor.setCursorScreenPosition([0, 1]) editor.transpose() - expect(editor.lineForBufferRow(0)).toBe 'bac' + expect(editor.lineTextForBufferRow(0)).toBe 'bac' it "reverses a selection", -> editor.buffer.setText("xabcz") editor.setSelectedBufferRange([[0, 1], [0, 4]]) editor.transpose() - expect(editor.lineForBufferRow(0)).toBe 'xcbaz' + expect(editor.lineTextForBufferRow(0)).toBe 'xcbaz' describe ".upperCase()", -> describe "when there is no selection", -> @@ -2845,7 +2987,7 @@ describe "Editor", -> editor.buffer.setText("aBc") editor.setCursorScreenPosition([0, 1]) editor.upperCase() - expect(editor.lineForBufferRow(0)).toBe 'ABC' + expect(editor.lineTextForBufferRow(0)).toBe 'ABC' expect(editor.getSelectedBufferRange()).toEqual [[0, 1], [0, 1]] describe "when there is a selection", -> @@ -2853,7 +2995,7 @@ describe "Editor", -> editor.buffer.setText("abc") editor.setSelectedBufferRange([[0,0], [0,2]]) editor.upperCase() - expect(editor.lineForBufferRow(0)).toBe 'ABc' + expect(editor.lineTextForBufferRow(0)).toBe 'ABc' expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 2]] describe ".lowerCase()", -> @@ -2862,7 +3004,7 @@ describe "Editor", -> editor.buffer.setText("aBC") editor.setCursorScreenPosition([0, 1]) editor.lowerCase() - expect(editor.lineForBufferRow(0)).toBe 'abc' + expect(editor.lineTextForBufferRow(0)).toBe 'abc' expect(editor.getSelectedBufferRange()).toEqual [[0, 1], [0, 1]] describe "when there is a selection", -> @@ -2870,7 +3012,7 @@ describe "Editor", -> editor.buffer.setText("ABC") editor.setSelectedBufferRange([[0,0], [0,2]]) editor.lowerCase() - expect(editor.lineForBufferRow(0)).toBe 'abC' + expect(editor.lineTextForBufferRow(0)).toBe 'abC' expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 2]] describe "soft-tabs detection", -> @@ -2923,11 +3065,11 @@ describe "Editor", -> runs -> expect(editor.getGrammar()).toBe atom.syntax.nullGrammar - expect(editor.lineForScreenRow(0).tokens.length).toBe 1 + expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBe 1 atom.syntax.addGrammar(jsGrammar) expect(editor.getGrammar()).toBe jsGrammar - expect(editor.lineForScreenRow(0).tokens.length).toBeGreaterThan 1 + expect(editor.tokenizedLineForScreenRow(0).tokens.length).toBeGreaterThan 1 describe "auto-indent", -> copyText = (text, {startColumn}={}) -> @@ -2936,7 +3078,7 @@ describe "Editor", -> editor.insertText(text) numberOfNewlines = text.match(/\n/g)?.length endColumn = text.match(/[^\n]*$/)[0]?.length - editor.getSelection().setBufferRange([[0,startColumn], [numberOfNewlines,endColumn]]) + editor.getLastSelection().setBufferRange([[0,startColumn], [numberOfNewlines,endColumn]]) editor.cutSelectedText() describe "editor.autoIndent", -> @@ -2945,11 +3087,11 @@ describe "Editor", -> it "does not auto-indent the line", -> editor.setCursorBufferPosition([1, 30]) editor.insertText("\n ") - expect(editor.lineForBufferRow(2)).toBe " " + expect(editor.lineTextForBufferRow(2)).toBe " " atom.config.set("editor.autoIndent", false) editor.indent() - expect(editor.lineForBufferRow(2)).toBe " " + expect(editor.lineTextForBufferRow(2)).toBe " " describe "when editor.autoIndent is true", -> beforeEach -> @@ -2959,11 +3101,11 @@ describe "Editor", -> it "auto-indents the line", -> editor.setCursorBufferPosition([1, 30]) editor.insertText("\n ") - expect(editor.lineForBufferRow(2)).toBe " " + expect(editor.lineTextForBufferRow(2)).toBe " " atom.config.set("editor.autoIndent", true) editor.indent() - expect(editor.lineForBufferRow(2)).toBe " " + expect(editor.lineTextForBufferRow(2)).toBe " " describe "when a newline is added", -> describe "when the line preceding the newline adds a new level of indentation", -> @@ -3027,15 +3169,15 @@ describe "Editor", -> 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.lineForBufferRow(13)).toBe "}; # 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.lineForBufferRow(12)).toBe ' };' + expect(editor.lineTextForBufferRow(12)).toBe ' };' editor.insertText('\t\t') - expect(editor.lineForBufferRow(12)).toBe ' \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", -> @@ -3053,22 +3195,22 @@ describe "Editor", -> atom.config.set('editor.normalizeIndentOnPaste', false) editor.setCursorBufferPosition([5, 2]) editor.pasteText() - expect(editor.lineForBufferRow(5)).toBe " function() {" - expect(editor.lineForBufferRow(6)).toBe "var cool = 1;" - expect(editor.lineForBufferRow(7)).toBe " }" + expect(editor.lineTextForBufferRow(5)).toBe " function() {" + expect(editor.lineTextForBufferRow(6)).toBe "var cool = 1;" + expect(editor.lineTextForBufferRow(7)).toBe " }" describe "when the inserted text contains no newlines", -> it "does not adjust the indentation level of the text", -> editor.setCursorBufferPosition([5, 2]) editor.insertText("foo", indentBasis: 5) - expect(editor.lineForBufferRow(5)).toBe " foo current = items.shift();" + expect(editor.lineTextForBufferRow(5)).toBe " foo current = items.shift();" it "does not adjust the whitespace if there are preceding characters", -> copyText(" foo") editor.setCursorBufferPosition([5, 30]) editor.pasteText() - expect(editor.lineForBufferRow(5)).toBe " current = items.shift(); foo" + expect(editor.lineTextForBufferRow(5)).toBe " current = items.shift(); foo" describe "when the inserted text contains newlines", -> describe "when the cursor is preceded only by whitespace characters", -> @@ -3077,10 +3219,10 @@ describe "Editor", -> editor.setCursorBufferPosition([3, 4]) editor.pasteText() - expect(editor.lineForBufferRow(3)).toBe " while (true) {" - expect(editor.lineForBufferRow(4)).toBe " foo();" - expect(editor.lineForBufferRow(5)).toBe " }" - expect(editor.lineForBufferRow(6)).toBe "var pivot = items.shift(), current, left = [], right = [];" + expect(editor.lineTextForBufferRow(3)).toBe " while (true) {" + expect(editor.lineTextForBufferRow(4)).toBe " foo();" + expect(editor.lineTextForBufferRow(5)).toBe " }" + expect(editor.lineTextForBufferRow(6)).toBe "var pivot = items.shift(), current, left = [], right = [];" describe "when the cursor is preceded by non-whitespace characters", -> it "normalizes the indentation level of all lines based on the level of the existing first line", -> @@ -3088,21 +3230,21 @@ describe "Editor", -> editor.setCursorBufferPosition([1, Infinity]) editor.pasteText() - expect(editor.lineForBufferRow(1)).toBe " var sort = function(items) {while (true) {" - expect(editor.lineForBufferRow(2)).toBe " foo();" - expect(editor.lineForBufferRow(3)).toBe " }" - expect(editor.lineForBufferRow(4)).toBe "" + expect(editor.lineTextForBufferRow(1)).toBe " var sort = function(items) {while (true) {" + expect(editor.lineTextForBufferRow(2)).toBe " foo();" + expect(editor.lineTextForBufferRow(3)).toBe " }" + expect(editor.lineTextForBufferRow(4)).toBe "" it "autoIndentSelectedRows auto-indents the selection", -> editor.setCursorBufferPosition([2, 0]) editor.insertText("function() {\ninside=true\n}\n i=1\n") - editor.getSelection().setBufferRange([[2,0], [6,0]]) + editor.getLastSelection().setBufferRange([[2,0], [6,0]]) editor.autoIndentSelectedRows() - expect(editor.lineForBufferRow(2)).toBe " function() {" - expect(editor.lineForBufferRow(3)).toBe " inside=true" - expect(editor.lineForBufferRow(4)).toBe " }" - expect(editor.lineForBufferRow(5)).toBe " i=1" + 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 "soft and hard tabs", -> it "resets the tab style when tokenization is complete", -> @@ -3127,36 +3269,36 @@ describe "Editor", -> 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.joinLines() - expect(editor.lineForBufferRow(0)).toBe 'var quicksort = function () { var sort = function(items) {' + expect(editor.lineTextForBufferRow(0)).toBe 'var quicksort = function () { var sort = function(items) {' expect(editor.getCursorBufferPosition()).toEqual [0, 30] 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.lineForBufferRow(9)).toBe ' };' - expect(editor.lineForBufferRow(10)).toBe ' return sort(Array.apply(this, arguments));' + 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.lineForBufferRow(12)).toBe '};' + expect(editor.lineTextForBufferRow(12)).toBe '};' 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.lineForBufferRow(0)).toBe 'var quicksort = function () { var sort = function(items) {' + 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.lineForBufferRow(9)).toBe ' }; return sort(Array.apply(this, arguments)); };' + expect(editor.lineTextForBufferRow(9)).toBe ' }; return sort(Array.apply(this, arguments)); };' expect(editor.getSelectedBufferRange()).toEqual [[9, 3], [9, 49]] describe ".duplicateLines()", -> @@ -3184,10 +3326,10 @@ describe "Editor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[3, 5], [3, 5]], [[9, 0], [14, 0]]] # folds are also duplicated - expect(editor.lineForScreenRow(5).fold).toBeDefined() - expect(editor.lineForScreenRow(7).fold).toBeDefined() - expect(editor.lineForScreenRow(7).text).toBe " while(items.length > 0) {" - expect(editor.lineForScreenRow(8).text).toBe " return sort(left).concat(pivot).concat(sort(right));" + 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) @@ -3239,7 +3381,7 @@ describe "Editor", -> describe "when the edit session contains surrogate pair characters", -> it "correctly backspaces over them", -> editor.setText('\uD835\uDF97\uD835\uDF97\uD835\uDF97') - editor.moveCursorToBottom() + editor.moveToBottom() editor.backspace() expect(editor.getText()).toBe '\uD835\uDF97\uD835\uDF97' editor.backspace() @@ -3249,7 +3391,7 @@ describe "Editor", -> it "correctly deletes over them", -> editor.setText('\uD835\uDF97\uD835\uDF97\uD835\uDF97') - editor.moveCursorToTop() + editor.moveToTop() editor.delete() expect(editor.getText()).toBe '\uD835\uDF97\uD835\uDF97' editor.delete() @@ -3259,22 +3401,22 @@ describe "Editor", -> it "correctly moves over them", -> editor.setText('\uD835\uDF97\uD835\uDF97\uD835\uDF97\n') - editor.moveCursorToTop() - editor.moveCursorRight() + editor.moveToTop() + editor.moveRight() expect(editor.getCursorBufferPosition()).toEqual [0, 2] - editor.moveCursorRight() + editor.moveRight() expect(editor.getCursorBufferPosition()).toEqual [0, 4] - editor.moveCursorRight() + editor.moveRight() expect(editor.getCursorBufferPosition()).toEqual [0, 6] - editor.moveCursorRight() + editor.moveRight() expect(editor.getCursorBufferPosition()).toEqual [1, 0] - editor.moveCursorLeft() + editor.moveLeft() expect(editor.getCursorBufferPosition()).toEqual [0, 6] - editor.moveCursorLeft() + editor.moveLeft() expect(editor.getCursorBufferPosition()).toEqual [0, 4] - editor.moveCursorLeft() + editor.moveLeft() expect(editor.getCursorBufferPosition()).toEqual [0, 2] - editor.moveCursorLeft() + editor.moveLeft() expect(editor.getCursorBufferPosition()).toEqual [0, 0] describe ".setIndentationForBufferRow", -> @@ -3336,7 +3478,7 @@ describe "Editor", -> runs -> editor.setText("// http://github.com") - {tokens} = editor.lineForScreenRow(0) + {tokens} = editor.tokenizedLineForScreenRow(0) expect(tokens[1].value).toBe " http://github.com" expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"] @@ -3344,7 +3486,7 @@ describe "Editor", -> atom.packages.activatePackage('language-hyperlink') runs -> - {tokens} = editor.lineForScreenRow(0) + {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"] @@ -3356,7 +3498,7 @@ describe "Editor", -> runs -> editor.setText("// SELECT * FROM OCTOCATS") - {tokens} = editor.lineForScreenRow(0) + {tokens} = editor.tokenizedLineForScreenRow(0) expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS" expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"] @@ -3364,7 +3506,7 @@ describe "Editor", -> atom.packages.activatePackage('package-with-injection-selector') runs -> - {tokens} = editor.lineForScreenRow(0) + {tokens} = editor.tokenizedLineForScreenRow(0) expect(tokens[1].value).toBe " SELECT * FROM OCTOCATS" expect(tokens[1].scopes).toEqual ["source.js", "comment.line.double-slash.js"] @@ -3372,7 +3514,7 @@ describe "Editor", -> atom.packages.activatePackage('language-sql') runs -> - {tokens} = editor.lineForScreenRow(0) + {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"] @@ -3458,7 +3600,7 @@ describe "Editor", -> expect(editor.getScrollTop()).toBe 80 expect(editor.getSelectedBufferRanges()).toEqual [[[0,0], [12,2]]] - editor.moveCursorToBottom() + editor.moveToBottom() editor.selectPageUp() expect(editor.getScrollTop()).toBe 50 expect(editor.getSelectedBufferRanges()).toEqual [[[7,0], [12,2]]] diff --git a/spec/language-mode-spec.coffee b/spec/language-mode-spec.coffee index b20ded983..fd3a4a478 100644 --- a/spec/language-mode-spec.coffee +++ b/spec/language-mode-spec.coffee @@ -275,46 +275,46 @@ describe "LanguageMode", -> it "folds every foldable line", -> languageMode.foldAll() - fold1 = editor.lineForScreenRow(0).fold + fold1 = editor.tokenizedLineForScreenRow(0).fold expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 12] fold1.destroy() - fold2 = editor.lineForScreenRow(1).fold + fold2 = editor.tokenizedLineForScreenRow(1).fold expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 9] fold2.destroy() - fold3 = editor.lineForScreenRow(4).fold + fold3 = editor.tokenizedLineForScreenRow(4).fold expect([fold3.getStartRow(), fold3.getEndRow()]).toEqual [4, 7] describe ".foldBufferRow(bufferRow)", -> describe "when bufferRow can be folded", -> it "creates a fold based on the syntactic region starting at the given row", -> languageMode.foldBufferRow(1) - fold = editor.lineForScreenRow(1).fold + fold = editor.tokenizedLineForScreenRow(1).fold expect(fold.getStartRow()).toBe 1 expect(fold.getEndRow()).toBe 9 describe "when bufferRow can't be folded", -> it "searches upward for the first row that begins a syntatic region containing the given buffer row (and folds it)", -> languageMode.foldBufferRow(8) - fold = editor.lineForScreenRow(1).fold + fold = editor.tokenizedLineForScreenRow(1).fold expect(fold.getStartRow()).toBe 1 expect(fold.getEndRow()).toBe 9 describe "when the bufferRow is already folded", -> it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", -> languageMode.foldBufferRow(2) - expect(editor.lineForScreenRow(1).fold).toBeDefined() - expect(editor.lineForScreenRow(0).fold).not.toBeDefined() + expect(editor.tokenizedLineForScreenRow(1).fold).toBeDefined() + expect(editor.tokenizedLineForScreenRow(0).fold).not.toBeDefined() languageMode.foldBufferRow(1) - expect(editor.lineForScreenRow(0).fold).toBeDefined() + expect(editor.tokenizedLineForScreenRow(0).fold).toBeDefined() describe "when the bufferRow is in a multi-line comment", -> it "searches upward and downward for surrounding comment lines and folds them as a single fold", -> buffer.insert([1,0], " //this is a comment\n // and\n //more docs\n\n//second comment") languageMode.foldBufferRow(1) - fold = editor.lineForScreenRow(1).fold + fold = editor.tokenizedLineForScreenRow(1).fold expect(fold.getStartRow()).toBe 1 expect(fold.getEndRow()).toBe 3 @@ -322,7 +322,7 @@ describe "LanguageMode", -> it "searches upward for the first row that begins a syntatic region containing the folded row (and folds it)", -> buffer.insert([1,0], " //this is a single line comment\n") languageMode.foldBufferRow(1) - fold = editor.lineForScreenRow(0).fold + fold = editor.tokenizedLineForScreenRow(0).fold expect(fold.getStartRow()).toBe 0 expect(fold.getEndRow()).toBe 13 @@ -357,38 +357,38 @@ describe "LanguageMode", -> it "folds every foldable line", -> languageMode.foldAll() - fold1 = editor.lineForScreenRow(0).fold + fold1 = editor.tokenizedLineForScreenRow(0).fold expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19] fold1.destroy() - fold2 = editor.lineForScreenRow(1).fold + fold2 = editor.tokenizedLineForScreenRow(1).fold expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [1, 4] - fold3 = editor.lineForScreenRow(2).fold.destroy() + fold3 = editor.tokenizedLineForScreenRow(2).fold.destroy() - fold4 = editor.lineForScreenRow(3).fold + fold4 = editor.tokenizedLineForScreenRow(3).fold expect([fold4.getStartRow(), fold4.getEndRow()]).toEqual [6, 8] describe ".foldAllAtIndentLevel()", -> it "folds every foldable range at a given indentLevel", -> languageMode.foldAllAtIndentLevel(2) - fold1 = editor.lineForScreenRow(6).fold + fold1 = editor.tokenizedLineForScreenRow(6).fold expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [6, 8] fold1.destroy() - fold2 = editor.lineForScreenRow(11).fold + fold2 = editor.tokenizedLineForScreenRow(11).fold expect([fold2.getStartRow(), fold2.getEndRow()]).toEqual [11, 14] fold2.destroy() it "does not fold anything but the indentLevel", -> languageMode.foldAllAtIndentLevel(0) - fold1 = editor.lineForScreenRow(0).fold + fold1 = editor.tokenizedLineForScreenRow(0).fold expect([fold1.getStartRow(), fold1.getEndRow()]).toEqual [0, 19] fold1.destroy() - fold2 = editor.lineForScreenRow(5).fold + fold2 = editor.tokenizedLineForScreenRow(5).fold expect(fold2).toBeFalsy() describe ".isFoldableAtBufferRow(bufferRow)", -> diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index df9921e69..1701e3f03 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -27,42 +27,91 @@ describe "PaneContainer", -> it "preserves the active pane across serialization, independent of focus", -> pane3A.activate() - expect(containerA.activePane).toBe pane3A + expect(containerA.getActivePane()).toBe pane3A containerB = containerA.testSerialization() [pane1B, pane2B, pane3B] = containerB.getPanes() - expect(containerB.activePane).toBe pane3B + expect(containerB.getActivePane()).toBe pane3B - describe "::activePane", -> + it "does not allow the root pane to be destroyed", -> + container = new PaneContainer + container.getRoot().destroy() + expect(container.getRoot()).toBeDefined() + expect(container.getRoot().isDestroyed()).toBe false + + describe "::getActivePane()", -> [container, pane1, pane2] = [] beforeEach -> container = new PaneContainer - pane1 = container.root + pane1 = container.getRoot() - it "references the first pane if no pane has been made active", -> - expect(container.activePane).toBe pane1 - expect(pane1.active).toBe true + it "returns the first pane if no pane has been made active", -> + expect(container.getActivePane()).toBe pane1 + expect(pane1.isActive()).toBe true - it "references the most pane on which ::activate was most recently called", -> + it "returns the most pane on which ::activate() was most recently called", -> pane2 = pane1.splitRight() pane2.activate() - expect(container.activePane).toBe pane2 - expect(pane1.active).toBe false - expect(pane2.active).toBe true + expect(container.getActivePane()).toBe pane2 + expect(pane1.isActive()).toBe false + expect(pane2.isActive()).toBe true pane1.activate() - expect(container.activePane).toBe pane1 - expect(pane1.active).toBe true - expect(pane2.active).toBe false + expect(container.getActivePane()).toBe pane1 + expect(pane1.isActive()).toBe true + expect(pane2.isActive()).toBe false - it "is reassigned to the next pane if the current active pane is destroyed", -> + it "returns the next pane if the current active pane is destroyed", -> pane2 = pane1.splitRight() pane2.activate() pane2.destroy() - expect(container.activePane).toBe pane1 - expect(pane1.active).toBe true + expect(container.getActivePane()).toBe pane1 + expect(pane1.isActive()).toBe true - it "does not allow the root pane to be destroyed", -> - pane1.destroy() - expect(container.root).toBe pane1 - expect(pane1.isDestroyed()).toBe false + describe "::onDidChangeActivePaneItem()", -> + [container, pane1, pane2, observed] = [] + + beforeEach -> + container = new PaneContainer(root: new Pane(items: [new Object, new Object])) + container.getRoot().splitRight(items: [new Object, new Object]) + [pane1, pane2] = container.getPanes() + + observed = [] + container.onDidChangeActivePaneItem (item) -> observed.push(item) + + it "invokes observers when the active item of the active pane changes", -> + pane2.activateNextItem() + pane2.activateNextItem() + expect(observed).toEqual [pane2.itemAtIndex(1), pane2.itemAtIndex(0)] + + it "invokes observers when the active pane changes", -> + pane1.activate() + pane2.activate() + expect(observed).toEqual [pane1.itemAtIndex(0), pane2.itemAtIndex(0)] + + describe "::observePanes()", -> + it "invokes observers with all current and future panes", -> + container = new PaneContainer + container.getRoot().splitRight() + [pane1, pane2] = container.getPanes() + + observed = [] + container.observePanes (pane) -> observed.push(pane) + + pane3 = pane2.splitDown() + pane4 = pane2.splitRight() + + expect(observed).toEqual [pane1, pane2, pane3, pane4] + + describe "::observePaneItems()", -> + it "invokes observers with all current and future pane items", -> + container = new PaneContainer(root: new Pane(items: [new Object, new Object])) + container.getRoot().splitRight(items: [new Object]) + [pane1, pane2] = container.getPanes() + observed = [] + container.observePaneItems (pane) -> observed.push(pane) + + pane3 = pane2.splitDown(items: [new Object]) + pane3.addItems([new Object, new Object]) + + expect(observed).toEqual container.getPaneItems() diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 04b1c2474..c66b88850 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -21,39 +21,83 @@ describe "Pane", -> describe "construction", -> it "sets the active item to the first item", -> pane = new Pane(items: [new Item("A"), new Item("B")]) - expect(pane.activeItem).toBe pane.items[0] + expect(pane.getActiveItem()).toBe pane.itemAtIndex(0) it "compacts the items array", -> pane = new Pane(items: [undefined, new Item("A"), null, new Item("B")]) - expect(pane.items.length).toBe 2 - expect(pane.activeItem).toBe pane.items[0] + expect(pane.getItems().length).toBe 2 + expect(pane.getActiveItem()).toBe pane.itemAtIndex(0) + + describe "::activate()", -> + [container, pane1, pane2] = [] + + beforeEach -> + container = new PaneContainer(root: new Pane) + container.getRoot().splitRight() + [pane1, pane2] = container.getPanes() + + it "changes the active pane on the container", -> + expect(container.getActivePane()).toBe pane2 + pane1.activate() + expect(container.getActivePane()).toBe pane1 + pane2.activate() + expect(container.getActivePane()).toBe pane2 + + it "invokes ::onDidChangeActivePane observers on the container", -> + observed = [] + container.onDidChangeActivePane (activePane) -> observed.push(activePane) + + pane1.activate() + pane1.activate() + pane2.activate() + pane1.activate() + expect(observed).toEqual [pane1, pane2, pane1] + + it "invokes ::onDidChangeActive observers on the relevant panes", -> + observed = [] + pane1.onDidChangeActive (active) -> observed.push(active) + pane1.activate() + pane2.activate() + expect(observed).toEqual [true, false] + + it "invokes ::onDidActivate() observers", -> + eventCount = 0 + pane1.onDidActivate -> eventCount++ + pane1.activate() + pane1.activate() + pane2.activate() + expect(eventCount).toBe 2 describe "::addItem(item, index)", -> it "adds the item at the given index", -> pane = new Pane(items: [new Item("A"), new Item("B")]) - [item1, item2] = pane.items + [item1, item2] = pane.getItems() item3 = new Item("C") pane.addItem(item3, 1) - expect(pane.items).toEqual [item1, item3, item2] + expect(pane.getItems()).toEqual [item1, item3, item2] - it "adds the item after the active item ", -> + it "adds the item after the active item if no index is provided", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() pane.activateItem(item2) item4 = new Item("D") pane.addItem(item4) - expect(pane.items).toEqual [item1, item2, item4, item3] + expect(pane.getItems()).toEqual [item1, item2, item4, item3] it "sets the active item after adding the first item", -> pane = new Pane item = new Item("A") - events = [] - pane.on 'item-added', -> events.push('item-added') - pane.$activeItem.changes.onValue -> events.push('active-item-changed') - pane.addItem(item) - expect(pane.activeItem).toBe item - expect(events).toEqual ['item-added', 'active-item-changed'] + expect(pane.getActiveItem()).toBe item + + it "invokes ::onDidAddItem() observers", -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + events = [] + pane.onDidAddItem (event) -> events.push(event) + + item = new Item("C") + pane.addItem(item, 1) + expect(events).toEqual [{item, index: 1}] describe "::activateItem(item)", -> pane = null @@ -62,83 +106,102 @@ describe "Pane", -> pane = new Pane(items: [new Item("A"), new Item("B")]) it "changes the active item to the current item", -> - expect(pane.activeItem).toBe pane.items[0] - pane.activateItem(pane.items[1]) - expect(pane.activeItem).toBe pane.items[1] + expect(pane.getActiveItem()).toBe pane.itemAtIndex(0) + pane.activateItem(pane.itemAtIndex(1)) + expect(pane.getActiveItem()).toBe pane.itemAtIndex(1) it "adds the given item if it isn't present in ::items", -> item = new Item("C") pane.activateItem(item) - expect(item in pane.items).toBe true - expect(pane.activeItem).toBe item + expect(item in pane.getItems()).toBe true + expect(pane.getActiveItem()).toBe item + + it "invokes ::onDidChangeActiveItem() observers", -> + observed = [] + pane.onDidChangeActiveItem (item) -> observed.push(item) + pane.activateItem(pane.itemAtIndex(1)) + expect(observed).toEqual [pane.itemAtIndex(1)] describe "::activateNextItem() and ::activatePreviousItem()", -> it "sets the active item to the next/previous item, looping around at either end", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 pane.activatePreviousItem() - expect(pane.activeItem).toBe item3 + expect(pane.getActiveItem()).toBe item3 pane.activatePreviousItem() - expect(pane.activeItem).toBe item2 + expect(pane.getActiveItem()).toBe item2 pane.activateNextItem() - expect(pane.activeItem).toBe item3 + expect(pane.getActiveItem()).toBe item3 pane.activateNextItem() - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 describe "::activateItemAtIndex(index)", -> it "activates the item at the given index", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() pane.activateItemAtIndex(2) - expect(pane.activeItem).toBe item3 + expect(pane.getActiveItem()).toBe item3 pane.activateItemAtIndex(1) - expect(pane.activeItem).toBe item2 + expect(pane.getActiveItem()).toBe item2 pane.activateItemAtIndex(0) - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 # Doesn't fail with out-of-bounds indices pane.activateItemAtIndex(100) - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 pane.activateItemAtIndex(-1) - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 describe "::destroyItem(item)", -> [pane, item1, item2, item3] = [] beforeEach -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() - it "removes the item from the items list", -> - expect(pane.activeItem).toBe item1 + it "removes the item from the items list and destroyes it", -> + expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item2) - expect(item2 in pane.items).toBe false - expect(pane.activeItem).toBe item1 + expect(item2 in pane.getItems()).toBe false + expect(item2.isDestroyed()).toBe true + expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item1) - expect(item1 in pane.items).toBe false + expect(item1 in pane.getItems()).toBe false + expect(item1.isDestroyed()).toBe true + + it "invokes ::onWillDestroyItem() observers before destroying the item", -> + events = [] + pane.onWillDestroyItem (event) -> + expect(item2.isDestroyed()).toBe false + events.push(event) + + pane.destroyItem(item2) + expect(item2.isDestroyed()).toBe true + expect(events).toEqual [{item: item2, index: 1}] + + it "invokes ::onDidRemoveItem() observers", -> + events = [] + pane.onDidRemoveItem (event) -> events.push(event) + pane.destroyItem(item2) + expect(events).toEqual [{item: item2, index: 1, destroyed: true}] describe "when the destroyed item is the active item and is the first item", -> it "activates the next item", -> - expect(pane.activeItem).toBe item1 + expect(pane.getActiveItem()).toBe item1 pane.destroyItem(item1) - expect(pane.activeItem).toBe item2 + expect(pane.getActiveItem()).toBe item2 describe "when the destroyed item is the active item and is not the first item", -> beforeEach -> pane.activateItem(item2) it "activates the previous item", -> - expect(pane.activeItem).toBe item2 + expect(pane.getActiveItem()).toBe item2 pane.destroyItem(item2) - expect(pane.activeItem).toBe item1 - - it "emits 'item-removed' with the item, its index, and true indicating the item is being destroyed", -> - pane.on 'item-removed', itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") - pane.destroyItem(item2) - expect(itemRemovedHandler).toHaveBeenCalledWith(item2, 1, true) + expect(pane.getActiveItem()).toBe item1 describe "if the item is modified", -> itemUri = null @@ -157,7 +220,7 @@ describe "Pane", -> pane.destroyItem(item1) expect(item1.save).toHaveBeenCalled() - expect(item1 in pane.items).toBe false + expect(item1 in pane.getItems()).toBe false expect(item1.isDestroyed()).toBe true describe "when the item has no uri", -> @@ -170,7 +233,7 @@ describe "Pane", -> expect(atom.showSaveDialogSync).toHaveBeenCalled() expect(item1.saveAs).toHaveBeenCalledWith("/selected/path") - expect(item1 in pane.items).toBe false + expect(item1 in pane.getItems()).toBe false expect(item1.isDestroyed()).toBe true describe "if the [Don't Save] option is selected", -> @@ -179,7 +242,7 @@ describe "Pane", -> pane.destroyItem(item1) expect(item1.save).not.toHaveBeenCalled() - expect(item1 in pane.items).toBe false + expect(item1 in pane.getItems()).toBe false expect(item1.isDestroyed()).toBe true describe "if the [Cancel] option is selected", -> @@ -188,7 +251,7 @@ describe "Pane", -> pane.destroyItem(item1) expect(item1.save).not.toHaveBeenCalled() - expect(item1 in pane.items).toBe true + expect(item1 in pane.getItems()).toBe true expect(item1.isDestroyed()).toBe false describe "when the last item is destroyed", -> @@ -197,7 +260,7 @@ describe "Pane", -> expect(atom.config.get('core.destroyEmptyPanes')).toBe false pane.destroyItem(item) for item in pane.getItems() expect(pane.isDestroyed()).toBe false - expect(pane.activeItem).toBeUndefined() + expect(pane.getActiveItem()).toBeUndefined() expect(-> pane.saveActiveItem()).not.toThrow() expect(-> pane.saveActiveItemAs()).not.toThrow() @@ -210,10 +273,10 @@ describe "Pane", -> describe "::destroyActiveItem()", -> it "destroys the active item", -> pane = new Pane(items: [new Item("A"), new Item("B")]) - activeItem = pane.activeItem + activeItem = pane.getActiveItem() pane.destroyActiveItem() expect(activeItem.isDestroyed()).toBe true - expect(activeItem in pane.items).toBe false + expect(activeItem in pane.getItems()).toBe false it "does not throw an exception if there are no more items", -> pane = new Pane @@ -222,27 +285,40 @@ describe "Pane", -> describe "::destroyItems()", -> it "destroys all items", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() pane.destroyItems() expect(item1.isDestroyed()).toBe true expect(item2.isDestroyed()).toBe true expect(item3.isDestroyed()).toBe true - expect(pane.items).toEqual [] + expect(pane.getItems()).toEqual [] + + describe "::observeItems()", -> + it "invokes the observer with all current and future items", -> + pane = new Pane(items: [new Item, new Item]) + [item1, item2] = pane.getItems() + + observed = [] + pane.observeItems (item) -> observed.push(item) + + item3 = new Item + pane.addItem(item3) + + expect(observed).toEqual [item1, item2, item3] describe "when an item emits a destroyed event", -> it "removes it from the list of items", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items - pane.items[1].destroy() - expect(pane.items).toEqual [item1, item3] + [item1, item2, item3] = pane.getItems() + pane.itemAtIndex(1).destroy() + expect(pane.getItems()).toEqual [item1, item3] describe "::destroyInactiveItems()", -> it "destroys all items but the active item", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() pane.activateItem(item2) pane.destroyInactiveItems() - expect(pane.items).toEqual [item2] + expect(pane.getItems()).toEqual [item2] describe "::saveActiveItem()", -> pane = null @@ -253,30 +329,30 @@ describe "Pane", -> describe "when the active item has a uri", -> beforeEach -> - pane.activeItem.uri = "test" + pane.getActiveItem().uri = "test" describe "when the active item has a save method", -> it "saves the current item", -> - pane.activeItem.save = jasmine.createSpy("save") + pane.getActiveItem().save = jasmine.createSpy("save") pane.saveActiveItem() - expect(pane.activeItem.save).toHaveBeenCalled() + expect(pane.getActiveItem().save).toHaveBeenCalled() describe "when the current item has no save method", -> it "does nothing", -> - expect(pane.activeItem.save).toBeUndefined() + expect(pane.getActiveItem().save).toBeUndefined() pane.saveActiveItem() describe "when the current item has no uri", -> describe "when the current item has a saveAs method", -> it "opens a save dialog and saves the current item as the selected path", -> - pane.activeItem.saveAs = jasmine.createSpy("saveAs") + pane.getActiveItem().saveAs = jasmine.createSpy("saveAs") pane.saveActiveItem() expect(atom.showSaveDialogSync).toHaveBeenCalled() - expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path') + expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path') describe "when the current item has no saveAs method", -> it "does nothing", -> - expect(pane.activeItem.saveAs).toBeUndefined() + expect(pane.getActiveItem().saveAs).toBeUndefined() pane.saveActiveItem() expect(atom.showSaveDialogSync).not.toHaveBeenCalled() @@ -289,22 +365,22 @@ describe "Pane", -> describe "when the current item has a saveAs method", -> it "opens the save dialog and calls saveAs on the item with the selected path", -> - pane.activeItem.path = __filename - pane.activeItem.saveAs = jasmine.createSpy("saveAs") + pane.getActiveItem().path = __filename + pane.getActiveItem().saveAs = jasmine.createSpy("saveAs") pane.saveActiveItemAs() expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__filename) - expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path') + expect(pane.getActiveItem().saveAs).toHaveBeenCalledWith('/selected/path') describe "when the current item does not have a saveAs method", -> it "does nothing", -> - expect(pane.activeItem.saveAs).toBeUndefined() + expect(pane.getActiveItem().saveAs).toBeUndefined() pane.saveActiveItemAs() expect(atom.showSaveDialogSync).not.toHaveBeenCalled() describe "::itemForUri(uri)", -> it "returns the item for which a call to .getUri() returns the given uri", -> pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) - [item1, item2, item3] = pane.items + [item1, item2, item3] = pane.getItems() item1.uri = "a" item2.uri = "b" expect(pane.itemForUri("a")).toBe item1 @@ -312,24 +388,32 @@ describe "Pane", -> expect(pane.itemForUri("bogus")).toBeUndefined() describe "::moveItem(item, index)", -> - it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", -> - pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) - [item1, item2, item3, item4] = pane.items - pane.on 'item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler") + [pane, item1, item2, item3, item4] = [] + beforeEach -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) + [item1, item2, item3, item4] = pane.getItems() + + it "moves the item to the given index and invokes ::onDidMoveItem observers", -> pane.moveItem(item1, 2) expect(pane.getItems()).toEqual [item2, item3, item1, item4] - expect(itemMovedHandler).toHaveBeenCalledWith(item1, 2) - itemMovedHandler.reset() pane.moveItem(item2, 3) expect(pane.getItems()).toEqual [item3, item1, item4, item2] - expect(itemMovedHandler).toHaveBeenCalledWith(item2, 3) - itemMovedHandler.reset() pane.moveItem(item2, 1) expect(pane.getItems()).toEqual [item3, item2, item1, item4] - expect(itemMovedHandler).toHaveBeenCalledWith(item2, 1) + + it "invokes ::onDidMoveItem() observers", -> + events = [] + pane.onDidMoveItem (event) -> events.push(event) + + pane.moveItem(item1, 2) + pane.moveItem(item2, 3) + expect(events).toEqual [ + {item: item1, oldIndex: 0, newIndex: 2} + {item: item2, oldIndex: 0, newIndex: 3} + ] describe "::moveItemToPane(item, pane, index)", -> [container, pane1, pane2] = [] @@ -339,13 +423,20 @@ describe "Pane", -> pane1 = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) container = new PaneContainer(root: pane1) pane2 = pane1.splitRight(items: [new Item("D"), new Item("E")]) - [item1, item2, item3] = pane1.items - [item4, item5] = pane2.items + [item1, item2, item3] = pane1.getItems() + [item4, item5] = pane2.getItems() it "moves the item to the given pane at the given index", -> pane1.moveItemToPane(item2, pane2, 1) - expect(pane1.items).toEqual [item1, item3] - expect(pane2.items).toEqual [item4, item2, item5] + expect(pane1.getItems()).toEqual [item1, item3] + expect(pane2.getItems()).toEqual [item4, item2, item5] + + it "invokes ::onDidRemoveItem() observers", -> + events = [] + pane1.onDidRemoveItem (event) -> events.push(event) + pane1.moveItemToPane(item2, pane2, 1) + + expect(events).toEqual [{item: item2, index: 1, destroyed: false}] describe "when the moved item the last item in the source pane", -> beforeEach -> @@ -455,7 +546,7 @@ describe "Pane", -> pane2 = pane1.splitRight() it "destroys the pane's destroyable items", -> - [item1, item2] = pane1.items + [item1, item2] = pane1.getItems() pane1.destroy() expect(item1.isDestroyed()).toBe true expect(item2.isDestroyed()).toBe true @@ -493,12 +584,12 @@ describe "Pane", -> it "can serialize and deserialize the pane and all its items", -> newPane = pane.testSerialization() - expect(newPane.items).toEqual pane.items + expect(newPane.getItems()).toEqual pane.getItems() it "restores the active item on deserialization", -> pane.activateItemAtIndex(1) newPane = pane.testSerialization() - expect(newPane.activeItem).toEqual newPane.items[1] + expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1) it "does not include items that cannot be deserialized", -> spyOn(console, 'warn') @@ -506,8 +597,8 @@ describe "Pane", -> pane.activateItem(unserializable) newPane = pane.testSerialization() - expect(newPane.activeItem).toEqual pane.items[0] - expect(newPane.items.length).toBe pane.items.length - 1 + expect(newPane.getActiveItem()).toEqual pane.itemAtIndex(0) + expect(newPane.getItems().length).toBe pane.getItems().length - 1 it "includes the pane's focus state in the serialized state", -> pane.focus() diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 9f73884b5..4862e639b 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -37,7 +37,7 @@ describe "PaneView", -> describe "when the active pane item changes", -> it "hides all item views except the active one", -> - expect(pane.activeItem).toBe view1 + expect(pane.getActiveItem()).toBe view1 expect(view1.css('display')).not.toBe 'none' pane.activateItem(view2) @@ -48,7 +48,7 @@ describe "PaneView", -> itemChangedHandler = jasmine.createSpy("itemChangedHandler") container.on 'pane:active-item-changed', itemChangedHandler - expect(pane.activeItem).toBe view1 + expect(pane.getActiveItem()).toBe view1 paneModel.activateItem(view2) paneModel.activateItem(view2) @@ -149,7 +149,7 @@ describe "PaneView", -> activeItemTitleChangedHandler = jasmine.createSpy("activeItemTitleChangedHandler") pane.on 'pane:active-item-title-changed', activeItemTitleChangedHandler - expect(pane.activeItem).toBe view1 + expect(pane.getActiveItem()).toBe view1 view2.trigger 'title-changed' expect(activeItemTitleChangedHandler).not.toHaveBeenCalled() @@ -246,7 +246,7 @@ describe "PaneView", -> it "transfers focus to the active view", -> focusHandler = jasmine.createSpy("focusHandler") - pane.activeItem.on 'focus', focusHandler + pane.getActiveItem().on 'focus', focusHandler pane.focus() expect(focusHandler).toHaveBeenCalled() diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 09ad8fb91..06e3ee29d 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -190,6 +190,11 @@ describe "Project", -> expect(atom.project.getPath()?).toBeFalsy() expect(atom.project.getRootDirectory()?).toBeFalsy() + it "normalizes the path to remove consecutive slashes, ., and .. segments", -> + atom.project.setPath("#{require.resolve('./fixtures/dir/a')}#{path.sep}b#{path.sep}#{path.sep}..") + expect(atom.project.getPath()).toEqual path.dirname(require.resolve('./fixtures/dir/a')) + expect(atom.project.getRootDirectory().path).toEqual path.dirname(require.resolve('./fixtures/dir/a')) + describe ".replace()", -> [filePath, commentFilePath, sampleContent, sampleCommentContent] = [] diff --git a/spec/random-editor-spec.coffee b/spec/random-editor-spec.coffee index 1f19ec5c7..33d941115 100644 --- a/spec/random-editor-spec.coffee +++ b/spec/random-editor-spec.coffee @@ -35,7 +35,7 @@ describe "Editor", -> logLines() throw new Error("Invalid buffer row #{actualBufferRow} for screen row #{screenRow}", ) - actualScreenLine = editor.lineForScreenRow(screenRow) + actualScreenLine = editor.tokenizedLineForScreenRow(screenRow) unless actualScreenLine.text is referenceScreenLine.text logLines() throw new Error("Invalid line text at screen row #{screenRow}") @@ -83,7 +83,7 @@ describe "Editor", -> screenLines = [] bufferRows = [] for bufferRow in [0..tokenizedBuffer.getLastRow()] - for screenLine in softWrapLine(tokenizedBuffer.lineForScreenRow(bufferRow)) + for screenLine in softWrapLine(tokenizedBuffer.tokenizedLineForRow(bufferRow)) screenLines.push(screenLine) bufferRows.push(bufferRow) else diff --git a/spec/selection-spec.coffee b/spec/selection-spec.coffee index 2448cbc02..020dffa86 100644 --- a/spec/selection-spec.coffee +++ b/spec/selection-spec.coffee @@ -6,7 +6,7 @@ describe "Selection", -> beforeEach -> buffer = atom.project.bufferForPathSync('sample.js') editor = new Editor(buffer: buffer, tabLength: 2) - selection = editor.getSelection() + selection = editor.getLastSelection() afterEach -> buffer.destroy() diff --git a/spec/tokenized-buffer-spec.coffee b/spec/tokenized-buffer-spec.coffee index 063f75ba1..ad90c4ded 100644 --- a/spec/tokenized-buffer-spec.coffee +++ b/spec/tokenized-buffer-spec.coffee @@ -49,38 +49,38 @@ describe "TokenizedBuffer", -> describe "on construction", -> it "initially creates un-tokenized screen lines, then tokenizes lines chunk at a time in the background", -> - line0 = tokenizedBuffer.lineForScreenRow(0) + line0 = tokenizedBuffer.tokenizedLineForRow(0) expect(line0.tokens.length).toBe 1 expect(line0.tokens[0]).toEqual(value: line0.text, scopes: ['source.js']) - line11 = tokenizedBuffer.lineForScreenRow(11) + line11 = tokenizedBuffer.tokenizedLineForRow(11) expect(line11.tokens.length).toBe 2 expect(line11.tokens[0]).toEqual(value: " ", scopes: ['source.js'], isAtomic: true) expect(line11.tokens[1]).toEqual(value: "return sort(Array.apply(this, arguments));", scopes: ['source.js']) # background tokenization has not begun - expect(tokenizedBuffer.lineForScreenRow(0).ruleStack).toBeUndefined() + expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack).toBeUndefined() # tokenize chunk 1 advanceClock() - expect(tokenizedBuffer.lineForScreenRow(0).ruleStack?).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(4).ruleStack?).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeFalsy() + expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy() expect(changeHandler).toHaveBeenCalledWith(start: 0, end: 4, delta: 0) changeHandler.reset() # tokenize chunk 2 advanceClock() - expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(9).ruleStack?).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(10).ruleStack?).toBeFalsy() + expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(9).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeFalsy() expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 9, delta: 0) changeHandler.reset() # tokenize last chunk advanceClock() - expect(tokenizedBuffer.lineForScreenRow(10).ruleStack?).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(12).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(10).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(12).ruleStack?).toBeTruthy() expect(changeHandler).toHaveBeenCalledWith(start: 10, end: 12, delta: 0) describe "when the buffer is partially tokenized", -> @@ -134,8 +134,8 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.firstInvalidRow()).toBe 5 buffer.setTextInRange([[6, 0], [7, 0]], "\n\n\n") - expect(tokenizedBuffer.lineForScreenRow(6).ruleStack?).toBeFalsy() - expect(tokenizedBuffer.lineForScreenRow(7).ruleStack?).toBeFalsy() + expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeFalsy() + expect(tokenizedBuffer.tokenizedLineForRow(7).ruleStack?).toBeFalsy() changeHandler.reset() expect(tokenizedBuffer.firstInvalidRow()).toBe 5 @@ -149,10 +149,10 @@ describe "TokenizedBuffer", -> it "updates tokens to reflect the change", -> buffer.setTextInRange([[0, 0], [2, 0]], "foo()\n7\n") - expect(tokenizedBuffer.lineForScreenRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.brace.round.js']) - expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.js']) + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1]).toEqual(value: '(', scopes: ['source.js', 'meta.brace.round.js']) + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: '7', scopes: ['source.js', 'constant.numeric.js']) # line 2 is unchanged - expect(tokenizedBuffer.lineForScreenRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js']) + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js']) expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] @@ -164,7 +164,7 @@ describe "TokenizedBuffer", -> buffer.insert([5, 30], '/* */') changeHandler.reset() buffer.insert([2, 0], '/*') - expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js'] + expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js'] expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange @@ -172,9 +172,9 @@ describe "TokenizedBuffer", -> changeHandler.reset() advanceClock() - expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] - expect(tokenizedBuffer.lineForScreenRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] - expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange @@ -185,23 +185,23 @@ describe "TokenizedBuffer", -> buffer.insert([5, 0], '*/') buffer.insert([1, 0], 'var ') - expect(tokenizedBuffer.lineForScreenRow(1).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] describe "when lines are both updated and removed", -> it "updates tokens to reflect the change", -> buffer.setTextInRange([[1, 0], [3, 0]], "foo()") # previous line 0 remains - expect(tokenizedBuffer.lineForScreenRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js']) + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js']) # previous line 3 should be combined with input to form line 1 - expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js']) - expect(tokenizedBuffer.lineForScreenRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js']) + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js']) + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[6]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js']) # lines below deleted regions should be shifted upward - expect(tokenizedBuffer.lineForScreenRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js']) - expect(tokenizedBuffer.lineForScreenRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js']) - expect(tokenizedBuffer.lineForScreenRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js']) + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2]).toEqual(value: 'while', scopes: ['source.js', 'keyword.control.js']) + expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js']) + expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: '<', scopes: ['source.js', 'keyword.operator.js']) expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] @@ -214,8 +214,8 @@ describe "TokenizedBuffer", -> changeHandler.reset() buffer.setTextInRange([[2, 0], [3, 0]], '/*') - expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js'] - expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js'] + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js'] + expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js'] expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange @@ -223,8 +223,8 @@ describe "TokenizedBuffer", -> changeHandler.reset() advanceClock() - expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] - expect(tokenizedBuffer.lineForScreenRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] delete event.bufferChange @@ -235,19 +235,19 @@ describe "TokenizedBuffer", -> buffer.setTextInRange([[1, 0], [2, 0]], "foo()\nbar()\nbaz()\nquux()") # previous line 0 remains - expect(tokenizedBuffer.lineForScreenRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js']) + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0]).toEqual( value: 'var', scopes: ['source.js', 'storage.modifier.js']) # 3 new lines inserted - expect(tokenizedBuffer.lineForScreenRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js']) - expect(tokenizedBuffer.lineForScreenRow(2).tokens[0]).toEqual(value: 'bar', scopes: ['source.js']) - expect(tokenizedBuffer.lineForScreenRow(3).tokens[0]).toEqual(value: 'baz', scopes: ['source.js']) + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0]).toEqual(value: 'foo', scopes: ['source.js']) + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0]).toEqual(value: 'bar', scopes: ['source.js']) + expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0]).toEqual(value: 'baz', scopes: ['source.js']) # previous line 2 is joined with quux() on line 4 - expect(tokenizedBuffer.lineForScreenRow(4).tokens[0]).toEqual(value: 'quux', scopes: ['source.js']) - expect(tokenizedBuffer.lineForScreenRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js']) + expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0]).toEqual(value: 'quux', scopes: ['source.js']) + expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[4]).toEqual(value: 'if', scopes: ['source.js', 'keyword.control.js']) # previous line 3 is pushed down to become line 5 - expect(tokenizedBuffer.lineForScreenRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js']) + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4]).toEqual(value: '=', scopes: ['source.js', 'keyword.operator.js']) expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] @@ -264,17 +264,17 @@ describe "TokenizedBuffer", -> [event] = changeHandler.argsForCall[0] delete event.bufferChange expect(event).toEqual(start: 2, end: 2, delta: 2) - expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js'] - expect(tokenizedBuffer.lineForScreenRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] - expect(tokenizedBuffer.lineForScreenRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] - expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].scopes).toEqual ['source.js'] + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].scopes).toEqual ['source.js', 'comment.block.js', 'punctuation.definition.comment.js'] + expect(tokenizedBuffer.tokenizedLineForRow(3).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(4).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js'] changeHandler.reset() advanceClock() # tokenize invalidated lines in background - expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] - expect(tokenizedBuffer.lineForScreenRow(6).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] - expect(tokenizedBuffer.lineForScreenRow(7).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] - expect(tokenizedBuffer.lineForScreenRow(8).tokens[0].scopes).not.toBe ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(6).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(7).tokens[0].scopes).toEqual ['source.js', 'comment.block.js'] + expect(tokenizedBuffer.tokenizedLineForRow(8).tokens[0].scopes).not.toBe ['source.js', 'comment.block.js'] expect(changeHandler).toHaveBeenCalled() [event] = changeHandler.argsForCall[0] @@ -285,13 +285,13 @@ describe "TokenizedBuffer", -> it "tokenizes the initial chunk synchronously, then tokenizes the remaining lines in the background", -> commentBlock = _.multiplyString("// a comment\n", tokenizedBuffer.chunkSize + 2) buffer.insert([0,0], commentBlock) - expect(tokenizedBuffer.lineForScreenRow(0).ruleStack?).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(4).ruleStack?).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeFalsy() + expect(tokenizedBuffer.tokenizedLineForRow(0).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(4).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeFalsy() advanceClock() - expect(tokenizedBuffer.lineForScreenRow(5).ruleStack?).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(6).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(5).ruleStack?).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(6).ruleStack?).toBeTruthy() describe ".findOpeningBracket(closingBufferPosition)", -> it "returns the position of the matching bracket, skipping any nested brackets", -> @@ -302,18 +302,18 @@ describe "TokenizedBuffer", -> expect(tokenizedBuffer.findClosingBracket([1, 29])).toEqual [9, 2] it "tokenizes leading whitespace based on the new tab length", -> - expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].isAtomic).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].value).toBe " " - expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].isAtomic).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].value).toBe " " + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " " + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " " tokenizedBuffer.setTabLength(4) fullyTokenize(tokenizedBuffer) - expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].isAtomic).toBeTruthy() - expect(tokenizedBuffer.lineForScreenRow(5).tokens[0].value).toBe " " - expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].isAtomic).toBeFalsy() - expect(tokenizedBuffer.lineForScreenRow(5).tokens[1].value).toBe " current " + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].isAtomic).toBeTruthy() + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[0].value).toBe " " + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].isAtomic).toBeFalsy() + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[1].value).toBe " current " describe "when the buffer contains hard-tabs", -> beforeEach -> @@ -335,7 +335,7 @@ describe "TokenizedBuffer", -> it "renders each tab as its own atomic token with a value of size tabLength", -> tabAsSpaces = _.multiplyString(' ', tokenizedBuffer.getTabLength()) - screenLine0 = tokenizedBuffer.lineForScreenRow(0) + screenLine0 = tokenizedBuffer.tokenizedLineForRow(0) expect(screenLine0.text).toBe "# Econ 101#{tabAsSpaces}" { tokens } = screenLine0 @@ -347,7 +347,7 @@ describe "TokenizedBuffer", -> expect(tokens[2].isAtomic).toBeTruthy() expect(tokens[3].value).toBe "" - expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand" + expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "#{tabAsSpaces} buy()#{tabAsSpaces}while supply > demand" it "aligns the hard tabs to the correct tab stop column", -> buffer.setText """ @@ -359,62 +359,62 @@ describe "TokenizedBuffer", -> tokenizedBuffer.setTabLength(4) fullyTokenize(tokenizedBuffer) - expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4" - expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 3 + expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4" + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 3 - expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5" - expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5" + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6" - expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6" + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1 tokenizedBuffer.setTabLength(3) fullyTokenize(tokenizedBuffer) - expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4" - expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4" + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5" - expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5" + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6" - expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 3 + expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6" + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 3 tokenizedBuffer.setTabLength(2) fullyTokenize(tokenizedBuffer) - expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4" - expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4" + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5" - expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5" + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6" - expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6" + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1 tokenizedBuffer.setTabLength(1) fullyTokenize(tokenizedBuffer) - expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "1 2 3 4" - expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(0).tokens[1].screenDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "1 2 3 4" + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[1].screenDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(1).text).toBe "12 3 4 5" - expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].screenDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(1).text).toBe "12 3 4 5" + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].screenDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(2).text).toBe "123 4 5 6" - expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].bufferDelta).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].screenDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(2).text).toBe "123 4 5 6" + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].bufferDelta).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].screenDelta).toBe 1 describe "when the buffer contains UTF-8 surrogate pairs", -> beforeEach -> @@ -435,7 +435,7 @@ describe "TokenizedBuffer", -> buffer.release() it "renders each UTF-8 surrogate pair as its own atomic token", -> - screenLine0 = tokenizedBuffer.lineForScreenRow(0) + screenLine0 = tokenizedBuffer.tokenizedLineForRow(0) expect(screenLine0.text).toBe "'abc\uD835\uDF97def'" { tokens } = screenLine0 @@ -447,7 +447,7 @@ describe "TokenizedBuffer", -> expect(tokens[3].value).toBe "def" expect(tokens[4].value).toBe "'" - screenLine1 = tokenizedBuffer.lineForScreenRow(1) + screenLine1 = tokenizedBuffer.tokenizedLineForRow(1) expect(screenLine1.text).toBe "//\uD835\uDF97xyz" { tokens } = screenLine1 @@ -525,7 +525,7 @@ describe "TokenizedBuffer", -> tokenizedBuffer.setGrammar(atom.syntax.selectGrammar('test.erb')) fullyTokenize(tokenizedBuffer) - {tokens} = tokenizedBuffer.lineForScreenRow(0) + {tokens} = tokenizedBuffer.tokenizedLineForRow(0) expect(tokens[0]).toEqual value: "
", scopes: ["text.html.ruby"] waitsForPromise -> @@ -533,7 +533,7 @@ describe "TokenizedBuffer", -> runs -> fullyTokenize(tokenizedBuffer) - {tokens} = tokenizedBuffer.lineForScreenRow(0) + {tokens} = tokenizedBuffer.tokenizedLineForRow(0) expect(tokens[0]).toEqual value: '<', scopes: ["text.html.ruby","meta.tag.block.any.html","punctuation.definition.tag.begin.html"] describe ".tokenForPosition(position)", -> @@ -599,9 +599,9 @@ describe "TokenizedBuffer", -> tokenizedBuffer.setInvisibles(space: 'S', tab: 'T') fullyTokenize(tokenizedBuffer) - expect(tokenizedBuffer.lineForScreenRow(0).text).toBe "SST Sa line with tabsTand T spacesSTS" + expect(tokenizedBuffer.tokenizedLineForRow(0).text).toBe "SST Sa line with tabsTand T spacesSTS" # Also needs to work for copies - expect(tokenizedBuffer.lineForScreenRow(0).copy().text).toBe "SST Sa line with tabsTand T spacesSTS" + expect(tokenizedBuffer.tokenizedLineForRow(0).copy().text).toBe "SST Sa line with tabsTand T spacesSTS" it "assigns endOfLineInvisibles to tokenized lines", -> buffer = new TextBuffer(text: "a line that ends in a carriage-return-line-feed \r\na line that ends in just a line-feed\na line with no ending") @@ -611,17 +611,17 @@ describe "TokenizedBuffer", -> tokenizedBuffer.setInvisibles(cr: 'R', eol: 'N') fullyTokenize(tokenizedBuffer) - expect(tokenizedBuffer.lineForScreenRow(0).endOfLineInvisibles).toEqual ['R', 'N'] - expect(tokenizedBuffer.lineForScreenRow(1).endOfLineInvisibles).toEqual ['N'] + expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R', 'N'] + expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual ['N'] # Lines ending in soft wraps get no invisibles - [left, right] = tokenizedBuffer.lineForScreenRow(0).softWrapAt(20) + [left, right] = tokenizedBuffer.tokenizedLineForRow(0).softWrapAt(20) expect(left.endOfLineInvisibles).toBe null expect(right.endOfLineInvisibles).toEqual ['R', 'N'] tokenizedBuffer.setInvisibles(cr: 'R', eol: false) - expect(tokenizedBuffer.lineForScreenRow(0).endOfLineInvisibles).toEqual ['R'] - expect(tokenizedBuffer.lineForScreenRow(1).endOfLineInvisibles).toEqual [] + expect(tokenizedBuffer.tokenizedLineForRow(0).endOfLineInvisibles).toEqual ['R'] + expect(tokenizedBuffer.tokenizedLineForRow(1).endOfLineInvisibles).toEqual [] describe "leading and trailing whitespace", -> beforeEach -> @@ -630,41 +630,40 @@ describe "TokenizedBuffer", -> fullyTokenize(tokenizedBuffer) it "assigns ::firstNonWhitespaceIndex on tokens that have leading whitespace", -> - expect(tokenizedBuffer.lineForScreenRow(0).tokens[0].firstNonWhitespaceIndex).toBe null - expect(tokenizedBuffer.lineForScreenRow(1).tokens[0].firstNonWhitespaceIndex).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(1).tokens[1].firstNonWhitespaceIndex).toBe null + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[0].firstNonWhitespaceIndex).toBe null + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[0].firstNonWhitespaceIndex).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(1).tokens[1].firstNonWhitespaceIndex).toBe null - expect(tokenizedBuffer.lineForScreenRow(2).tokens[0].firstNonWhitespaceIndex).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(2).tokens[1].firstNonWhitespaceIndex).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(2).tokens[2].firstNonWhitespaceIndex).toBe null + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[0].firstNonWhitespaceIndex).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[1].firstNonWhitespaceIndex).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[2].firstNonWhitespaceIndex).toBe null # The 4th token *has* leading whitespace, but isn't entirely whitespace buffer.insert([5, 0], ' ') - expect(tokenizedBuffer.lineForScreenRow(5).tokens[3].firstNonWhitespaceIndex).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(5).tokens[4].firstNonWhitespaceIndex).toBe null + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[3].firstNonWhitespaceIndex).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(5).tokens[4].firstNonWhitespaceIndex).toBe null # Lines that are *only* whitespace are not considered to have leading whitespace buffer.insert([10, 0], ' ') - expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].firstNonWhitespaceIndex).toBe null + expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstNonWhitespaceIndex).toBe null it "assigns ::firstTrailingWhitespaceIndex on tokens that have trailing whitespace", -> buffer.insert([0, Infinity], ' ') - expect(tokenizedBuffer.lineForScreenRow(0).tokens[11].firstTrailingWhitespaceIndex).toBe null - expect(tokenizedBuffer.lineForScreenRow(0).tokens[12].firstTrailingWhitespaceIndex).toBe 0 + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[11].firstTrailingWhitespaceIndex).toBe null + expect(tokenizedBuffer.tokenizedLineForRow(0).tokens[12].firstTrailingWhitespaceIndex).toBe 0 # The last token *has* trailing whitespace, but isn't entirely whitespace buffer.setTextInRange([[2, 39], [2, 40]], ' ') - expect(tokenizedBuffer.lineForScreenRow(2).tokens[14].firstTrailingWhitespaceIndex).toBe null - console.log tokenizedBuffer.lineForScreenRow(2).tokens[15] - expect(tokenizedBuffer.lineForScreenRow(2).tokens[15].firstTrailingWhitespaceIndex).toBe 6 + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[14].firstTrailingWhitespaceIndex).toBe null + expect(tokenizedBuffer.tokenizedLineForRow(2).tokens[15].firstTrailingWhitespaceIndex).toBe 6 # Lines that are *only* whitespace are considered to have trailing whitespace buffer.insert([10, 0], ' ') - expect(tokenizedBuffer.lineForScreenRow(10).tokens[0].firstTrailingWhitespaceIndex).toBe 0 + expect(tokenizedBuffer.tokenizedLineForRow(10).tokens[0].firstTrailingWhitespaceIndex).toBe 0 it "only marks trailing whitespace on the last segment of a soft-wrapped line", -> buffer.insert([0, Infinity], ' ') - tokenizedLine = tokenizedBuffer.lineForScreenRow(0) + tokenizedLine = tokenizedBuffer.tokenizedLineForRow(0) [segment1, segment2] = tokenizedLine.softWrapAt(16) expect(segment1.tokens[5].value).toBe ' ' expect(segment1.tokens[5].firstTrailingWhitespaceIndex).toBe null @@ -677,7 +676,7 @@ describe "TokenizedBuffer", -> tokenizedBuffer.setInvisibles(space: 'S', tab: 'T') fullyTokenize(tokenizedBuffer) - line = tokenizedBuffer.lineForScreenRow(0).copy() + line = tokenizedBuffer.tokenizedLineForRow(0).copy() expect(line.tokens[0].firstNonWhitespaceIndex).toBe 2 expect(line.tokens[line.tokens.length - 1].firstTrailingWhitespaceIndex).toBe 0 @@ -704,55 +703,55 @@ describe "TokenizedBuffer", -> describe "when the line is non-empty", -> it "has an indent level based on the leading whitespace on the line", -> - expect(tokenizedBuffer.lineForScreenRow(0).indentLevel).toBe 0 - expect(tokenizedBuffer.lineForScreenRow(1).indentLevel).toBe 1 - expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(0).indentLevel).toBe 0 + expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2 buffer.insert([2, 0], ' ') - expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2.5 + expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2.5 describe "when the line is empty", -> it "assumes the indentation level of the first non-empty line below or above if one exists", -> buffer.insert([12, 0], ' ') buffer.insert([12, Infinity], '\n\n') - expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(14).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(14).indentLevel).toBe 2 buffer.insert([1, Infinity], '\n\n') - expect(tokenizedBuffer.lineForScreenRow(2).indentLevel).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(3).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(2).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(3).indentLevel).toBe 2 buffer.setText('\n\n\n') - expect(tokenizedBuffer.lineForScreenRow(1).indentLevel).toBe 0 + expect(tokenizedBuffer.tokenizedLineForRow(1).indentLevel).toBe 0 describe "when the changed lines are surrounded by whitespace-only lines", -> it "updates the indentLevel of empty lines that precede the change", -> - expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 0 + expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 0 buffer.insert([12, 0], '\n') buffer.insert([13, 0], ' ') - expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 1 it "updates empty line indent guides when the empty line is the last line", -> buffer.insert([12, 2], '\n') # The newline and he tab need to be in two different operations to surface the bug buffer.insert([12, 0], ' ') - expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 1 + expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 1 buffer.insert([12, 0], ' ') - expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(14)).not.toBeDefined() + expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(14)).not.toBeDefined() it "updates the indentLevel of empty lines surrounding a change that inserts lines", -> # create some new lines buffer.insert([7, 0], '\n\n') buffer.insert([5, 0], '\n\n') - expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 3 - expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 3 - expect(tokenizedBuffer.lineForScreenRow(9).indentLevel).toBe 3 - expect(tokenizedBuffer.lineForScreenRow(10).indentLevel).toBe 3 - expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 3 + expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 3 + expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 3 + expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 3 + expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 2 tokenizedBuffer.on "changed", changeHandler = jasmine.createSpy('changeHandler') @@ -761,11 +760,11 @@ describe "TokenizedBuffer", -> delete changeHandler.argsForCall[0][0].bufferChange expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: 2) - expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 4 - expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 4 - expect(tokenizedBuffer.lineForScreenRow(11).indentLevel).toBe 4 - expect(tokenizedBuffer.lineForScreenRow(12).indentLevel).toBe 4 - expect(tokenizedBuffer.lineForScreenRow(13).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 4 + expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 4 + expect(tokenizedBuffer.tokenizedLineForRow(11).indentLevel).toBe 4 + expect(tokenizedBuffer.tokenizedLineForRow(12).indentLevel).toBe 4 + expect(tokenizedBuffer.tokenizedLineForRow(13).indentLevel).toBe 2 it "updates the indentLevel of empty lines surrounding a change that removes lines", -> # create some new lines @@ -779,9 +778,9 @@ describe "TokenizedBuffer", -> delete changeHandler.argsForCall[0][0].bufferChange expect(changeHandler).toHaveBeenCalledWith(start: 5, end: 10, delta: -1) - expect(tokenizedBuffer.lineForScreenRow(5).indentLevel).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(6).indentLevel).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(7).indentLevel).toBe 2 # new text - expect(tokenizedBuffer.lineForScreenRow(8).indentLevel).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(9).indentLevel).toBe 2 - expect(tokenizedBuffer.lineForScreenRow(10).indentLevel).toBe 2 # } + expect(tokenizedBuffer.tokenizedLineForRow(5).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(6).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(7).indentLevel).toBe 2 # new text + expect(tokenizedBuffer.tokenizedLineForRow(8).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(9).indentLevel).toBe 2 + expect(tokenizedBuffer.tokenizedLineForRow(10).indentLevel).toBe 2 # } diff --git a/spec/tokenized-line-spec.coffee b/spec/tokenized-line-spec.coffee index 9e1507468..ec03524be 100644 --- a/spec/tokenized-line-spec.coffee +++ b/spec/tokenized-line-spec.coffee @@ -10,13 +10,13 @@ describe "TokenizedLine", -> atom.project.open('coffee.coffee').then (o) -> editor = o it "returns true when the line is only whitespace", -> - expect(editor.lineForScreenRow(3).isOnlyWhitespace()).toBe true - expect(editor.lineForScreenRow(7).isOnlyWhitespace()).toBe true - expect(editor.lineForScreenRow(23).isOnlyWhitespace()).toBe true + expect(editor.tokenizedLineForScreenRow(3).isOnlyWhitespace()).toBe true + expect(editor.tokenizedLineForScreenRow(7).isOnlyWhitespace()).toBe true + expect(editor.tokenizedLineForScreenRow(23).isOnlyWhitespace()).toBe true it "returns false when the line is not only whitespace", -> - expect(editor.lineForScreenRow(0).isOnlyWhitespace()).toBe false - expect(editor.lineForScreenRow(2).isOnlyWhitespace()).toBe false + expect(editor.tokenizedLineForScreenRow(0).isOnlyWhitespace()).toBe false + expect(editor.tokenizedLineForScreenRow(2).isOnlyWhitespace()).toBe false describe "::getScopeTree()", -> it "returns a tree whose inner nodes are scopes and whose leaf nodes are tokens in those scopes", -> @@ -35,6 +35,6 @@ describe "TokenizedLine", -> runs -> tokenIndex = 0 - tokens = editor.lineForScreenRow(1).tokens - scopeTree = editor.lineForScreenRow(1).getScopeTree() + tokens = editor.tokenizedLineForScreenRow(1).tokens + scopeTree = editor.tokenizedLineForScreenRow(1).getScopeTree() ensureValidScopeTree(scopeTree) diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index a75896582..88bcdf49c 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -8,8 +8,12 @@ describe "Workspace", -> atom.workspace = workspace = new Workspace describe "::open(uri, options)", -> + openEvents = null + beforeEach -> - spyOn(workspace.activePane, 'activate').andCallThrough() + openEvents = [] + workspace.onDidOpen (event) -> openEvents.push(event) + spyOn(workspace.getActivePane(), 'activate').andCallThrough() describe "when the 'searchAllPanes' option is false (default)", -> describe "when called without a uri", -> @@ -21,18 +25,21 @@ describe "Workspace", -> runs -> expect(editor1.getPath()).toBeUndefined() - expect(workspace.activePane.items).toEqual [editor1] - expect(workspace.activePaneItem).toBe editor1 - expect(workspace.activePane.activate).toHaveBeenCalled() + expect(workspace.getActivePane().items).toEqual [editor1] + expect(workspace.getActivePaneItem()).toBe editor1 + expect(workspace.getActivePane().activate).toHaveBeenCalled() + expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor1, index: 0}] + openEvents = [] waitsForPromise -> workspace.open().then (editor) -> editor2 = editor runs -> expect(editor2.getPath()).toBeUndefined() - expect(workspace.activePane.items).toEqual [editor1, editor2] - expect(workspace.activePaneItem).toBe editor2 - expect(workspace.activePane.activate).toHaveBeenCalled() + expect(workspace.getActivePane().items).toEqual [editor1, editor2] + expect(workspace.getActivePaneItem()).toBe editor2 + expect(workspace.getActivePane().activate).toHaveBeenCalled() + expect(openEvents).toEqual [{uri: undefined, pane: workspace.getActivePane(), item: editor2, index: 1}] describe "when called with a uri", -> describe "when the active pane already has an editor for the given uri", -> @@ -51,8 +58,29 @@ describe "Workspace", -> runs -> expect(editor).toBe editor1 - expect(workspace.activePaneItem).toBe editor - expect(workspace.activePane.activate).toHaveBeenCalled() + expect(workspace.getActivePaneItem()).toBe editor + expect(workspace.getActivePane().activate).toHaveBeenCalled() + + expect(openEvents).toEqual [ + { + uri: atom.project.resolve('a') + item: editor1 + pane: atom.workspace.getActivePane() + index: 0 + } + { + uri: atom.project.resolve('b') + item: editor2 + pane: atom.workspace.getActivePane() + index: 1 + } + { + uri: atom.project.resolve('a') + item: editor1 + pane: atom.workspace.getActivePane() + index: 0 + } + ] describe "when the active pane does not have an editor for the given uri", -> it "adds and activates a new editor for the given path on the active pane", -> @@ -62,9 +90,9 @@ describe "Workspace", -> runs -> expect(editor.getUri()).toBe atom.project.resolve('a') - expect(workspace.activePaneItem).toBe editor - expect(workspace.activePane.items).toEqual [editor] - expect(workspace.activePane.activate).toHaveBeenCalled() + expect(workspace.getActivePaneItem()).toBe editor + expect(workspace.getActivePane().items).toEqual [editor] + expect(workspace.getActivePane().activate).toHaveBeenCalled() describe "when the 'searchAllPanes' option is true", -> describe "when an editor for the given uri is already open on an inactive pane", -> @@ -83,14 +111,14 @@ describe "Workspace", -> workspace.open('b').then (o) -> editor2 = o runs -> - expect(workspace.activePaneItem).toBe editor2 + expect(workspace.getActivePaneItem()).toBe editor2 waitsForPromise -> workspace.open('a', searchAllPanes: true) runs -> - expect(workspace.activePane).toBe pane1 - expect(workspace.activePaneItem).toBe editor1 + expect(workspace.getActivePane()).toBe pane1 + expect(workspace.getActivePaneItem()).toBe editor1 describe "when no editor for the given uri is open in any pane", -> it "opens an editor for the given uri in the active pane", -> @@ -99,21 +127,21 @@ describe "Workspace", -> workspace.open('a', searchAllPanes: true).then (o) -> editor = o runs -> - expect(workspace.activePaneItem).toBe editor + expect(workspace.getActivePaneItem()).toBe editor describe "when the 'split' option is set", -> describe "when the 'split' option is 'left'", -> it "opens the editor in the leftmost pane of the current pane axis", -> - pane1 = workspace.activePane + pane1 = workspace.getActivePane() pane2 = pane1.splitRight() - expect(workspace.activePane).toBe pane2 + expect(workspace.getActivePane()).toBe pane2 editor = null waitsForPromise -> workspace.open('a', split: 'left').then (o) -> editor = o runs -> - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 expect(pane1.items).toEqual [editor] expect(pane2.items).toEqual [] @@ -123,37 +151,37 @@ describe "Workspace", -> workspace.open('a', split: 'left').then (o) -> editor = o runs -> - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 expect(pane1.items).toEqual [editor] expect(pane2.items).toEqual [] describe "when a pane axis is the leftmost sibling of the current pane", -> it "opens the new item in the current pane", -> editor = null - pane1 = workspace.activePane + pane1 = workspace.getActivePane() pane2 = pane1.splitLeft() pane3 = pane2.splitDown() pane1.activate() - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 waitsForPromise -> workspace.open('a', split: 'left').then (o) -> editor = o runs -> - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 expect(pane1.items).toEqual [editor] describe "when the 'split' option is 'right'", -> it "opens the editor in the rightmost pane of the current pane axis", -> editor = null - pane1 = workspace.activePane + pane1 = workspace.getActivePane() pane2 = null waitsForPromise -> workspace.open('a', split: 'right').then (o) -> editor = o runs -> pane2 = workspace.getPanes().filter((p) -> p != pane1)[0] - expect(workspace.activePane).toBe pane2 + expect(workspace.getActivePane()).toBe pane2 expect(pane1.items).toEqual [] expect(pane2.items).toEqual [editor] @@ -163,18 +191,18 @@ describe "Workspace", -> workspace.open('a', split: 'right').then (o) -> editor = o runs -> - expect(workspace.activePane).toBe pane2 + expect(workspace.getActivePane()).toBe pane2 expect(pane1.items).toEqual [] expect(pane2.items).toEqual [editor] describe "when a pane axis is the rightmost sibling of the current pane", -> it "opens the new item in a new pane split to the right of the current pane", -> editor = null - pane1 = workspace.activePane + pane1 = workspace.getActivePane() pane2 = pane1.splitRight() pane3 = pane2.splitDown() pane1.activate() - expect(workspace.activePane).toBe pane1 + expect(workspace.getActivePane()).toBe pane1 pane4 = null waitsForPromise -> @@ -182,7 +210,7 @@ describe "Workspace", -> runs -> pane4 = workspace.getPanes().filter((p) -> p != pane1)[0] - expect(workspace.activePane).toBe pane4 + expect(workspace.getActivePane()).toBe pane4 expect(pane4.items).toEqual [editor] expect(workspace.paneContainer.root.children[0]).toBe pane1 expect(workspace.paneContainer.root.children[1]).toBe pane4 @@ -203,21 +231,21 @@ describe "Workspace", -> workspace.open("bar://baz").then (item) -> expect(item).toEqual { bar: "bar://baz" } - it "emits an 'editor-created' event", -> + it "notifies ::onDidAddTextEditor observers", -> absolutePath = require.resolve('./fixtures/dir/a') newEditorHandler = jasmine.createSpy('newEditorHandler') - workspace.on 'editor-created', newEditorHandler + workspace.onDidAddTextEditor newEditorHandler editor = null waitsForPromise -> workspace.open(absolutePath).then (e) -> editor = e runs -> - expect(newEditorHandler).toHaveBeenCalledWith editor + expect(newEditorHandler.argsForCall[0][0].textEditor).toBe editor describe "::reopenItem()", -> it "opens the uri associated with the last closed pane that isn't currently open", -> - pane = workspace.activePane + pane = workspace.getActivePane() waitsForPromise -> workspace.open('a').then -> workspace.open('b').then -> @@ -226,44 +254,44 @@ describe "Workspace", -> runs -> # does not reopen items with no uri - expect(workspace.activePaneItem.getUri()).toBeUndefined() + expect(workspace.getActivePaneItem().getUri()).toBeUndefined() pane.destroyActiveItem() waitsForPromise -> workspace.reopenItem() runs -> - expect(workspace.activePaneItem.getUri()).not.toBeUndefined() + expect(workspace.getActivePaneItem().getUri()).not.toBeUndefined() # destroy all items - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1') pane.destroyActiveItem() - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b') pane.destroyActiveItem() - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a') pane.destroyActiveItem() # reopens items with uris - expect(workspace.activePaneItem).toBeUndefined() + expect(workspace.getActivePaneItem()).toBeUndefined() waitsForPromise -> workspace.reopenItem() runs -> - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('a') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('a') # does not reopen items that are already open waitsForPromise -> workspace.open('b') runs -> - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('b') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('b') waitsForPromise -> workspace.reopenItem() runs -> - expect(workspace.activePaneItem.getUri()).toBe atom.project.resolve('file1') + expect(workspace.getActivePaneItem().getUri()).toBe atom.project.resolve('file1') describe "::increase/decreaseFontSize()", -> it "increases/decreases the font size without going below 1", -> @@ -282,7 +310,22 @@ describe "Workspace", -> describe "::openLicense()", -> it "opens the license as plain-text in a buffer", -> waitsForPromise -> workspace.openLicense() - runs -> expect(workspace.activePaneItem.getText()).toMatch /Copyright/ + runs -> expect(workspace.getActivePaneItem().getText()).toMatch /Copyright/ + + describe "::observeTextEditors()", -> + it "invokes the observer with current and future text editors", -> + observed = [] + + waitsForPromise -> workspace.open() + waitsForPromise -> workspace.open() + waitsForPromise -> workspace.openLicense() + + runs -> + workspace.observeTextEditors (editor) -> observed.push(editor) + + waitsForPromise -> workspace.open() + + expect(observed).toEqual workspace.getTextEditors() describe "when an editor is destroyed", -> it "removes the editor", -> @@ -292,23 +335,9 @@ describe "Workspace", -> workspace.open("a").then (e) -> editor = e runs -> - expect(workspace.getEditors()).toHaveLength 1 + expect(workspace.getTextEditors()).toHaveLength 1 editor.destroy() - expect(workspace.getEditors()).toHaveLength 0 - - describe "when an editor is copied", -> - it "emits an 'editor-created' event", -> - editor = null - handler = jasmine.createSpy('editorCreatedHandler') - workspace.on 'editor-created', handler - - waitsForPromise -> - workspace.open("a").then (o) -> editor = o - - runs -> - expect(handler.callCount).toBe 1 - editorCopy = editor.copy() - expect(handler.callCount).toBe 2 + expect(workspace.getTextEditors()).toHaveLength 0 it "stores the active grammars used by all the open editors", -> waitsForPromise -> diff --git a/src/atom.coffee b/src/atom.coffee index 47bbbaa98..4fb113302 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -24,7 +24,7 @@ class Atom extends Model # Public: Load or create the Atom environment in the given mode. # # * `mode` A {String} mode that is either 'editor' or 'spec' depending on the - # kind of environment you want to build. + # kind of environment you want to build. # # Returns an Atom instance, fully initialized @loadOrCreate: (mode) -> @@ -321,8 +321,9 @@ class Atom extends Model # Call this method when establishing a real application window. startEditorWindow: -> + {resourcePath, safeMode} = @getLoadSettings() + CommandInstaller = require './command-installer' - resourcePath = atom.getLoadSettings().resourcePath CommandInstaller.installAtomCommand resourcePath, false, (error) -> console.warn error.message if error? CommandInstaller.installApmCommand resourcePath, false, (error) -> @@ -341,7 +342,7 @@ class Atom extends Model @packages.activate() @keymaps.loadUserKeymap() - @requireUserInitScript() + @requireUserInitScript() unless safeMode @menu.update() maximize = dimensions?.maximized and process.platform isnt 'darwin' diff --git a/src/clipboard.coffee b/src/clipboard.coffee index 03d53d574..48bfb8dd5 100644 --- a/src/clipboard.coffee +++ b/src/clipboard.coffee @@ -47,8 +47,8 @@ class Clipboard # associated metadata. # # Returns an {Object} with the following keys: - # * `text` The {String} clipboard text. - # * `metadata` The metadata stored by an earlier call to {::write}. + # * `text` The {String} clipboard text. + # * `metadata` The metadata stored by an earlier call to {::write}. readWithMetadata: -> text = @read() if @signatureForMetadata is @md5(text) diff --git a/src/cursor.coffee b/src/cursor.coffee index 968b965dd..68018a72c 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -157,7 +157,7 @@ class Cursor extends Model # # Returns a {Boolean}. isLastCursor: -> - this == @editor.getCursor() + this == @editor.getLastCursor() # Public: Identifies if the cursor is surrounded by whitespace. # @@ -222,10 +222,15 @@ class Cursor extends Model # Public: Returns the cursor's current buffer row of text excluding its line # ending. getCurrentBufferLine: -> - @editor.lineForBufferRow(@getBufferRow()) + @editor.lineTextForBufferRow(@getBufferRow()) # Public: Moves the cursor up one screen row. - moveUp: (rowCount = 1, {moveToEndOfSelection}={}) -> + # + # * `rowCount` (optional) {Number} number of rows to move (default: 1) + # * `options` (optional) {Object} with the following keys: + # * `moveToEndOfSelection` if true, move to the left of the selection if a + # selection exists. + moveUp: (rowCount=1, {moveToEndOfSelection}={}) -> range = @marker.getScreenRange() if moveToEndOfSelection and not range.isEmpty() { row, column } = range.start @@ -237,7 +242,12 @@ class Cursor extends Model @goalColumn = column # Public: Moves the cursor down one screen row. - moveDown: (rowCount = 1, {moveToEndOfSelection}={}) -> + # + # * `rowCount` (optional) {Number} number of rows to move (default: 1) + # * `options` (optional) {Object} with the following keys: + # * `moveToEndOfSelection` if true, move to the left of the selection if a + # selection exists. + moveDown: (rowCount=1, {moveToEndOfSelection}={}) -> range = @marker.getScreenRange() if moveToEndOfSelection and not range.isEmpty() { row, column } = range.end @@ -250,30 +260,51 @@ class Cursor extends Model # Public: Moves the cursor left one screen column. # + # * `columnCount` (optional) {Number} number of columns to move (default: 1) # * `options` (optional) {Object} with the following keys: # * `moveToEndOfSelection` if true, move to the left of the selection if a # selection exists. - moveLeft: ({moveToEndOfSelection}={}) -> + moveLeft: (columnCount=1, {moveToEndOfSelection}={}) -> range = @marker.getScreenRange() if moveToEndOfSelection and not range.isEmpty() @setScreenPosition(range.start) else {row, column} = @getScreenPosition() - [row, column] = if column > 0 then [row, column - 1] else [row - 1, Infinity] + + while columnCount > column and row > 0 + columnCount -= column + column = @editor.lineTextForScreenRow(--row).length + columnCount-- # subtract 1 for the row move + + column = column - columnCount @setScreenPosition({row, column}) # Public: Moves the cursor right one screen column. # + # * `columnCount` (optional) {Number} number of columns to move (default: 1) # * `options` (optional) {Object} with the following keys: # * `moveToEndOfSelection` if true, move to the right of the selection if a # selection exists. - moveRight: ({moveToEndOfSelection}={}) -> + moveRight: (columnCount=1, {moveToEndOfSelection}={}) -> range = @marker.getScreenRange() if moveToEndOfSelection and not range.isEmpty() @setScreenPosition(range.end) else { row, column } = @getScreenPosition() - @setScreenPosition([row, column + 1], skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true) + maxLines = @editor.getScreenLineCount() + rowLength = @editor.lineTextForScreenRow(row).length + columnsRemainingInLine = rowLength - column + + while columnCount > columnsRemainingInLine and row < maxLines - 1 + columnCount -= columnsRemainingInLine + columnCount-- # subtract 1 for the row move + + column = 0 + rowLength = @editor.lineTextForScreenRow(++row).length + columnsRemainingInLine = rowLength + + column = column + columnCount + @setScreenPosition({row, column}, skipAtomicTokens: true, wrapBeyondNewlines: true, wrapAtSoftNewlines: true) # Public: Moves the cursor to the top of the buffer. moveToTop: -> @@ -309,17 +340,6 @@ class Cursor extends Model @setBufferPosition([lineBufferRange.start.row, targetBufferColumn]) - # Public: Moves the cursor to the beginning of the buffer line, skipping all - # whitespace. - skipLeadingWhitespace: -> - position = @getBufferPosition() - scanRange = @getCurrentLineBufferRange() - endOfLeadingWhitespace = null - @editor.scanInBufferRange /^[ \t]*/, scanRange, ({range}) -> - endOfLeadingWhitespace = range.end - - @setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position) - # Public: Moves the cursor to the end of the line. moveToEndOfScreenLine: -> @setScreenPosition([@getScreenRow(), Infinity]) @@ -352,6 +372,17 @@ class Cursor extends Model if position = @getMoveNextWordBoundaryBufferPosition() @setBufferPosition(position) + # Public: Moves the cursor to the beginning of the buffer line, skipping all + # whitespace. + skipLeadingWhitespace: -> + position = @getBufferPosition() + scanRange = @getCurrentLineBufferRange() + endOfLeadingWhitespace = null + @editor.scanInBufferRange /^[ \t]*/, scanRange, ({range}) -> + endOfLeadingWhitespace = range.end + + @setBufferPosition(endOfLeadingWhitespace) if endOfLeadingWhitespace.isGreaterThan(position) + # Public: Retrieves the buffer position of where the current word starts. # # * `options` (optional) An {Object} with the following keys: @@ -562,10 +593,18 @@ class Cursor extends Model # its current position. hasPrecedingCharactersOnLine: -> bufferPosition = @getBufferPosition() - line = @editor.lineForBufferRow(bufferPosition.row) + line = @editor.lineTextForBufferRow(bufferPosition.row) firstCharacterColumn = line.search(/\S/) if firstCharacterColumn is -1 false else bufferPosition.column > firstCharacterColumn + + # Public: Compare this cursor's buffer position to another cursor's buffer position. + # + # See {Point::compare} for more details. + # + # * `otherCursor`{Cursor} to compare against + compare: (otherCursor) -> + @getBufferPosition().compare(otherCursor.getBufferPosition()) diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 0b7121acc..5146f1816 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -377,25 +377,25 @@ class DisplayBuffer extends Model # Gets the screen line for the given screen row. # - # screenRow - A {Number} indicating the screen row. + # * `screenRow` - A {Number} indicating the screen row. # - # Returns a {ScreenLine}. - lineForRow: (row) -> - @screenLines[row] + # Returns {TokenizedLine} + tokenizedLineForScreenRow: (screenRow) -> + @screenLines[screenRow] # Gets the screen lines for the given screen row range. # # startRow - A {Number} indicating the beginning screen row. # endRow - A {Number} indicating the ending screen row. # - # Returns an {Array} of {ScreenLine}s. - linesForRows: (startRow, endRow) -> + # Returns an {Array} of {TokenizedLine}s. + tokenizedLinesForScreenRows: (startRow, endRow) -> @screenLines[startRow..endRow] # Gets all the screen lines. # - # Returns an {Array} of {ScreenLines}s. - getLines: -> + # Returns an {Array} of {TokenizedLine}s. + getTokenizedLines: -> new Array(@screenLines...) indentLevelForLine: (line) -> @@ -555,7 +555,7 @@ class DisplayBuffer extends Model top = targetRow * @lineHeightInPixels left = 0 column = 0 - for token in @lineForRow(targetRow).tokens + for token in @tokenizedLineForScreenRow(targetRow).tokens charWidths = @getScopedCharWidths(token.scopes) for char in token.value return {top, left} if column is targetColumn @@ -573,7 +573,7 @@ class DisplayBuffer extends Model left = 0 column = 0 - for token in @lineForRow(row).tokens + for token in @tokenizedLineForScreenRow(row).tokens charWidths = @getScopedCharWidths(token.scopes) for char in token.value charWidth = charWidths[char] ? defaultCharWidth @@ -983,7 +983,7 @@ class DisplayBuffer extends Model logLines: (start=0, end=@getLastRow()) -> for row in [start..end] - line = @lineForRow(row).text + line = @tokenizedLineForScreenRow(row).text console.log row, @bufferRowForScreenRow(row), line, line.length handleTokenizedBufferChange: (tokenizedBufferChange) => @@ -1024,7 +1024,7 @@ class DisplayBuffer extends Model bufferRow = startBufferRow while bufferRow < endBufferRow - tokenizedLine = @tokenizedBuffer.lineForScreenRow(bufferRow) + tokenizedLine = @tokenizedBuffer.tokenizedLineForRow(bufferRow) if fold = @largestFoldStartingAtBufferRow(bufferRow) foldLine = tokenizedLine.copy() diff --git a/src/editor-component.coffee b/src/editor-component.coffee index cc248c69a..056c29fe7 100644 --- a/src/editor-component.coffee +++ b/src/editor-component.coffee @@ -52,7 +52,7 @@ EditorComponent = React.createClass {focused, showIndentGuide, showLineNumbers, visible} = @state {editor, mini, cursorBlinkPeriod, cursorBlinkResumeDelay} = @props maxLineNumberDigits = editor.getLineCount().toString().length - hasSelection = editor.getSelection()? and !editor.getSelection().isEmpty() + hasSelection = editor.getLastSelection()? and !editor.getLastSelection().isEmpty() style = {} if @performedInitialMeasurement @@ -60,7 +60,7 @@ EditorComponent = React.createClass [renderedStartRow, renderedEndRow] = renderedRowRange cursorPixelRects = @getCursorPixelRects(renderedRowRange) - tokenizedLines = editor.linesForScreenRows(renderedStartRow, renderedEndRow - 1) + tokenizedLines = editor.tokenizedLinesForScreenRows(renderedStartRow, renderedEndRow - 1) decorations = editor.decorationsForScreenRowRange(renderedStartRow, renderedEndRow) highlightDecorations = @getHighlightDecorations(decorations) @@ -184,12 +184,13 @@ EditorComponent = React.createClass @checkForVisibilityChange() componentWillUnmount: -> - @props.parentView.trigger 'editor:will-be-removed', [@props.parentView] + {editor, parentView} = @props + + parentView.trigger 'editor:will-be-removed', [parentView] @unsubscribe() window.removeEventListener 'resize', @requestHeightAndWidthMeasurement clearInterval(@domPollingIntervalId) @domPollingIntervalId = null - @props.editor.destroy() componentWillReceiveProps: (newProps) -> @props.editor.setMini(newProps.mini) @@ -259,9 +260,9 @@ EditorComponent = React.createClass getHiddenInputPosition: -> {editor} = @props {focused} = @state - return {top: 0, left: 0} unless @isMounted() and focused and editor.getCursor()? + return {top: 0, left: 0} unless @isMounted() and focused and editor.getLastCursor()? - {top, left, height, width} = editor.getCursor().getPixelRect() + {top, left, height, width} = editor.getLastCursor().getPixelRect() width = 2 if width is 0 # Prevent autoscroll at the end of longest line top -= editor.getScrollTop() left -= editor.getScrollLeft() @@ -404,8 +405,8 @@ EditorComponent = React.createClass {parentView, editor, mini} = @props @addCommandListeners - 'core:move-left': -> editor.moveCursorLeft() - 'core:move-right': -> editor.moveCursorRight() + 'core:move-left': -> editor.moveLeft() + 'core:move-right': -> editor.moveRight() 'core:select-left': -> editor.selectLeft() 'core:select-right': -> editor.selectRight() 'core:select-all': -> editor.selectAll() @@ -416,8 +417,8 @@ EditorComponent = React.createClass 'core:cut': -> editor.cutSelectedText() 'core:copy': -> editor.copySelectedText() 'core:paste': -> editor.pasteText() - 'editor:move-to-previous-word': -> editor.moveCursorToPreviousWord() - 'editor:select-word': -> editor.selectWord() + 'editor:move-to-previous-word': -> editor.moveToPreviousWord() + 'editor:select-word': -> editor.selectWordsContainingCursors() 'editor:consolidate-selections': @consolidateSelections 'editor:delete-to-beginning-of-word': -> editor.deleteToBeginningOfWord() 'editor:delete-to-beginning-of-line': -> editor.deleteToBeginningOfLine() @@ -425,18 +426,18 @@ EditorComponent = React.createClass 'editor:delete-to-end-of-word': -> editor.deleteToEndOfWord() 'editor:delete-line': -> editor.deleteLine() 'editor:cut-to-end-of-line': -> editor.cutToEndOfLine() - 'editor:move-to-beginning-of-next-paragraph': -> editor.moveCursorToBeginningOfNextParagraph() - 'editor:move-to-beginning-of-previous-paragraph': -> editor.moveCursorToBeginningOfPreviousParagraph() - 'editor:move-to-beginning-of-screen-line': -> editor.moveCursorToBeginningOfScreenLine() - 'editor:move-to-beginning-of-line': -> editor.moveCursorToBeginningOfLine() - 'editor:move-to-end-of-screen-line': -> editor.moveCursorToEndOfScreenLine() - 'editor:move-to-end-of-line': -> editor.moveCursorToEndOfLine() - 'editor:move-to-first-character-of-line': -> editor.moveCursorToFirstCharacterOfLine() - 'editor:move-to-beginning-of-word': -> editor.moveCursorToBeginningOfWord() - 'editor:move-to-end-of-word': -> editor.moveCursorToEndOfWord() - 'editor:move-to-beginning-of-next-word': -> editor.moveCursorToBeginningOfNextWord() - 'editor:move-to-previous-word-boundary': -> editor.moveCursorToPreviousWordBoundary() - 'editor:move-to-next-word-boundary': -> editor.moveCursorToNextWordBoundary() + 'editor:move-to-beginning-of-next-paragraph': -> editor.moveToBeginningOfNextParagraph() + 'editor:move-to-beginning-of-previous-paragraph': -> editor.moveToBeginningOfPreviousParagraph() + 'editor:move-to-beginning-of-screen-line': -> editor.moveToBeginningOfScreenLine() + 'editor:move-to-beginning-of-line': -> editor.moveToBeginningOfLine() + 'editor:move-to-end-of-screen-line': -> editor.moveToEndOfScreenLine() + 'editor:move-to-end-of-line': -> editor.moveToEndOfLine() + 'editor:move-to-first-character-of-line': -> editor.moveToFirstCharacterOfLine() + 'editor:move-to-beginning-of-word': -> editor.moveToBeginningOfWord() + 'editor:move-to-end-of-word': -> editor.moveToEndOfWord() + 'editor:move-to-beginning-of-next-word': -> editor.moveToBeginningOfNextWord() + 'editor:move-to-previous-word-boundary': -> editor.moveToPreviousWordBoundary() + 'editor:move-to-next-word-boundary': -> editor.moveToNextWordBoundary() 'editor:select-to-beginning-of-next-paragraph': -> editor.selectToBeginningOfNextParagraph() 'editor:select-to-beginning-of-previous-paragraph': -> editor.selectToBeginningOfPreviousParagraph() 'editor:select-to-end-of-line': -> editor.selectToEndOfLine() @@ -447,17 +448,17 @@ EditorComponent = React.createClass 'editor:select-to-next-word-boundary': -> editor.selectToNextWordBoundary() 'editor:select-to-previous-word-boundary': -> editor.selectToPreviousWordBoundary() 'editor:select-to-first-character-of-line': -> editor.selectToFirstCharacterOfLine() - 'editor:select-line': -> editor.selectLine() + 'editor:select-line': -> editor.selectLinesContainingCursors() 'editor:transpose': -> editor.transpose() 'editor:upper-case': -> editor.upperCase() 'editor:lower-case': -> editor.lowerCase() unless mini @addCommandListeners - 'core:move-up': -> editor.moveCursorUp() - 'core:move-down': -> editor.moveCursorDown() - 'core:move-to-top': -> editor.moveCursorToTop() - 'core:move-to-bottom': -> editor.moveCursorToBottom() + 'core:move-up': -> editor.moveUp() + 'core:move-down': -> editor.moveDown() + 'core:move-to-top': -> editor.moveToTop() + 'core:move-to-bottom': -> editor.moveToBottom() 'core:page-up': -> editor.pageUp() 'core:page-down': -> editor.pageDown() 'core:select-up': -> editor.selectUp() @@ -615,6 +616,9 @@ EditorComponent = React.createClass # CTRL+click brings up the context menu on OSX, so don't handle those either return if ctrlKey and process.platform is 'darwin' + # Prevent focusout event on hidden input if editor is already focused + event.preventDefault() if @state.focused + screenPosition = @screenPositionForMouseEvent(event) if event.target?.classList.contains('fold-marker') @@ -690,7 +694,7 @@ EditorComponent = React.createClass onGutterShiftClick: (event) -> {editor} = @props clickedRow = @screenPositionForMouseEvent(event).row - tailPosition = editor.getSelection().getTailScreenPosition() + tailPosition = editor.getLastSelection().getTailScreenPosition() if clickedRow < tailPosition.row editor.selectToScreenPosition([clickedRow, 0]) diff --git a/src/editor-view.coffee b/src/editor-view.coffee index cc18f7e9e..2f945f458 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -215,12 +215,22 @@ class EditorView extends View view.css('z-index', 1) @find('.lines').prepend(view) + detach: -> + return unless @attached + super + @attached = false + @unmountComponent() + beforeRemove: -> return unless @attached @attached = false - React.unmountComponentAtNode(@element) if @component.isMounted() + @unmountComponent() + @editor.destroy() @trigger 'editor:detached', this + unmountComponent: -> + React.unmountComponentAtNode(@element) if @component.isMounted() + # Public: Split the editor view left. splitLeft: -> pane = @getPane() diff --git a/src/editor.coffee b/src/editor.coffee index 629955d3f..0bb39a2ac 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -24,12 +24,12 @@ TextMateScopeSelector = require('first-mate').ScopeSelector # ## Accessing Editor Instances # # The easiest way to get hold of `Editor` objects is by registering a callback -# with `::eachEditor` on the `atom.workspace` global. Your callback will then -# be called with all current editor instances and also when any editor is +# with `::observeTextEditors` on the `atom.workspace` global. Your callback will +# then be called with all current editor instances and also when any editor is # created in the future. # # ```coffee -# atom.workspace.eachEditor (editor) -> +# atom.workspace.observeTextEditors (editor) -> # editor.insertText('Hello World') # ``` # @@ -412,20 +412,41 @@ class Editor extends Model # Public: Returns a {String} representing the contents of the line at the # given buffer row. # - # * `row` A {Number} representing a zero-indexed buffer row. - lineForBufferRow: (row) -> @buffer.lineForRow(row) + # * `bufferRow` A {Number} representing a zero-indexed buffer row. + lineTextForBufferRow: (bufferRow) -> @buffer.lineForRow(bufferRow) + lineForBufferRow: (bufferRow) -> + deprecate 'Use Editor::lineTextForBufferRow(bufferRow) instead' + @lineTextForBufferRow(bufferRow) - # {Delegates to: DisplayBuffer.lineForRow} - lineForScreenRow: (row) -> @displayBuffer.lineForRow(row) + # Public: Returns a {String} representing the contents of the line at the + # given screen row. + # + # * `screenRow` A {Number} representing a zero-indexed screen row. + lineTextForScreenRow: (screenRow) -> @displayBuffer.tokenizedLineForScreenRow(screenRow)?.text - # {Delegates to: DisplayBuffer.linesForRows} - linesForScreenRows: (start, end) -> @displayBuffer.linesForRows(start, end) + # Gets the screen line for the given screen row. + # + # * `screenRow` - A {Number} indicating the screen row. + # + # Returns {TokenizedLine} + tokenizedLineForScreenRow: (screenRow) -> @displayBuffer.tokenizedLineForScreenRow(screenRow) + lineForScreenRow: (screenRow) -> + deprecate "Editor::tokenizedLineForScreenRow(bufferRow) is the new name. But it's private. Try to use Editor::lineTextForScreenRow instead" + @tokenizedLineForScreenRow(screenRow) - # Public: Returns a {Number} representing the line length for the given + # {Delegates to: DisplayBuffer.tokenizedLinesForScreenRows} + tokenizedLinesForScreenRows: (start, end) -> @displayBuffer.tokenizedLinesForScreenRows(start, end) + linesForScreenRows: (start, end) -> + deprecate "Use Editor::tokenizedLinesForScreenRows instead" + @tokenizedLinesForScreenRows(start, end) + + # Returns a {Number} representing the line length for the given # buffer row, exclusive of its line-ending character(s). # # * `row` A {Number} indicating the buffer row. - lineLengthForBufferRow: (row) -> @buffer.lineLengthForRow(row) + lineLengthForBufferRow: (row) -> + deprecate "Use editor.lineTextForBufferRow(row).length instead" + @lineTextForBufferRow(row).length bufferRowForScreenRow: (row) -> @displayBuffer.bufferRowForScreenRow(row) @@ -462,7 +483,7 @@ class Editor extends Model # # Returns a {Range}. getCurrentParagraphBufferRange: -> - @getCursor().getCurrentParagraphBufferRange() + @getLastCursor().getCurrentParagraphBufferRange() ### @@ -642,7 +663,7 @@ class Editor extends Model selection.insertText(fn(text)) selection.setBufferRange(range) - # Public: Split multi-line selections into one selection per line. + # Split multi-line selections into one selection per line. # # Operates on all selections. This method breaks apart all multi-line # selections to create multiple single-line selections that cumulatively cover @@ -733,7 +754,7 @@ class Editor extends Model # Public: For each cursor, insert a newline at beginning the following line. insertNewlineBelow: -> @transact => - @moveCursorToEndOfLine() + @moveToEndOfLine() @insertNewline() # Public: For each cursor, insert a newline at the end of the preceding line. @@ -743,16 +764,16 @@ class Editor extends Model indentLevel = @indentationForBufferRow(bufferRow) onFirstLine = bufferRow is 0 - @moveCursorToBeginningOfLine() - @moveCursorLeft() + @moveToBeginningOfLine() + @moveLeft() @insertNewline() if @shouldAutoIndent() and @indentationForBufferRow(bufferRow) < indentLevel @setIndentationForBufferRow(bufferRow, indentLevel) if onFirstLine - @moveCursorUp() - @moveCursorToEndOfLine() + @moveUp() + @moveToEndOfLine() ### Section: Removing Text @@ -834,7 +855,7 @@ class Editor extends Model # whitespace. usesSoftTabs: -> for bufferRow in [0..@buffer.getLastRow()] - continue if @displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment() + continue if @displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() line = @buffer.lineForRow(bufferRow) return true if line[0] is ' ' @@ -911,7 +932,7 @@ class Editor extends Model # # Returns a {Number}. indentationForBufferRow: (bufferRow) -> - @indentLevelForLine(@lineForBufferRow(bufferRow)) + @indentLevelForLine(@lineTextForBufferRow(bufferRow)) # Public: Set the indentation level for the given buffer row. # @@ -929,7 +950,7 @@ class Editor extends Model if preserveLeadingWhitespace endColumn = 0 else - endColumn = @lineForBufferRow(bufferRow).match(/^\s*/)[0].length + endColumn = @lineTextForBufferRow(bufferRow).match(/^\s*/)[0].length newIndentString = @buildIndentString(newLevel) @buffer.setTextInRange([[bufferRow, 0], [bufferRow, endColumn]], newIndentString) @@ -979,12 +1000,12 @@ class Editor extends Model # Public: Undo the last change. undo: -> - @getCursor().needsAutoscroll = true + @getLastCursor().needsAutoscroll = true @buffer.undo(this) # Public: Redo the last change. redo: -> - @getCursor().needsAutoscroll = true + @getLastCursor().needsAutoscroll = true @buffer.redo(this) ### @@ -1047,11 +1068,15 @@ class Editor extends Model # Public: Convert a range in buffer-coordinates to screen-coordinates. # + # * `bufferRange` {Range} in buffer coordinates to translate into screen coordinates. + # # Returns a {Range}. screenRangeForBufferRange: (bufferRange) -> @displayBuffer.screenRangeForBufferRange(bufferRange) # Public: Convert a range in screen-coordinates to buffer-coordinates. # + # * `screenRange` {Range} in screen coordinates to translate into buffer coordinates. + # # Returns a {Range}. bufferRangeForScreenRange: (screenRange) -> @displayBuffer.bufferRangeForScreenRange(screenRange) @@ -1100,7 +1125,11 @@ class Editor extends Model # editor.clipScreenPosition([2, Infinity]) # -> `[2, 10]` # ``` # - # * `bufferPosition` The {Point} representing the position to clip. + # * `screenPosition` The {Point} representing the position to clip. + # * `options` (optional) {Object} + # * `wrapBeyondNewlines` {Boolean} if `true`, continues wrapping past newlines + # * `wrapAtSoftNewlines` {Boolean} if `true`, continues wrapping past soft newlines + # * `screenLine` {Boolean} if `true`, indicates that you're using a line number, not a row number # # Returns a {Point}. clipScreenPosition: (screenPosition, options) -> @displayBuffer.clipScreenPosition(screenPosition, options) @@ -1160,7 +1189,7 @@ class Editor extends Model # position. See {::scopesForBufferPosition} for more information. # # Returns an {Array} of {String}s. - getCursorScopes: -> @getCursor().getScopes() + getCursorScopes: -> @getLastCursor().getScopes() logCursorScope: -> console.log @getCursorScopes() @@ -1168,7 +1197,7 @@ class Editor extends Model # Public: Determine if the given row is entirely a comment isBufferRowCommented: (bufferRow) -> - if match = @lineForBufferRow(bufferRow).match(/\S/) + if match = @lineTextForBufferRow(bufferRow).match(/\S/) scopes = @tokenForBufferPosition([bufferRow, match.index]).scopes new TextMateScopeSelector('comment.*').matches(scopes) @@ -1218,7 +1247,7 @@ class Editor extends Model return else if atom.config.get("editor.normalizeIndentOnPaste") and metadata?.indentBasis? - if !@getCursor().hasPrecedingCharactersOnLine() or containsNewlines + if !@getLastCursor().hasPrecedingCharactersOnLine() or containsNewlines options.indentBasis ?= metadata.indentBasis @insertText(text, options) @@ -1331,7 +1360,7 @@ class Editor extends Model # # Returns a {Boolean}. isFoldedAtCursorRow: -> - @isFoldedAtScreenRow(@getCursorScreenRow()) + @isFoldedAtScreenRow(@getCursorScreenPosition().row) # Public: Determine whether the given row in buffer coordinates is folded. # @@ -1511,30 +1540,248 @@ class Editor extends Model Section: Cursors ### - # Public: Determine if there are multiple cursors. - hasMultipleCursors: -> - @getCursors().length > 1 + # Essential: Get the position of the most recently added cursor in buffer + # coordinates. + # + # Returns a {Point} + getCursorBufferPosition: -> + @getLastCursor().getBufferPosition() - # Public: Get an Array of all {Cursor}s. - getCursors: -> new Array(@cursors...) + # Essential: Get the position of all the cursor positions in buffer coordinates. + # + # Returns {Array} of {Point}s in the order they were added + getCursorBufferPositions: -> + cursor.getBufferPosition() for cursor in @getCursors() - # Public: Get the most recently added {Cursor}. - getCursor: -> - _.last(@cursors) + # Essential: Move the cursor to the given position in buffer coordinates. + # + # If there are multiple cursors, they will be consolidated to a single cursor. + # + # * `position` A {Point} or {Array} of `[row, column]` + # * `options` (optional) An {Object} combining options for {::clipScreenPosition} with: + # * `autoscroll` Determines whether the editor scrolls to the new cursor's + # position. Defaults to true. + setCursorBufferPosition: (position, options) -> + @moveCursors (cursor) -> cursor.setBufferPosition(position, options) - # Public: Add a cursor at the position in screen coordinates. + # Essential: Get the position of the most recently added cursor in screen + # coordinates. + # + # Returns a {Point}. + getCursorScreenPosition: -> + @getLastCursor().getScreenPosition() + + # Essential: Get the position of all the cursor positions in screen coordinates. + # + # Returns {Array} of {Point}s in the order the cursors were added + getCursorScreenPositions: -> + cursor.getScreenPosition() for cursor in @getCursors() + + # Get the row of the most recently added cursor in screen coordinates. + # + # Returns the screen row {Number}. + getCursorScreenRow: -> + deprecate('Use `editor.getCursorScreenPosition().row` instead') + @getCursorScreenPosition().row + + # Essential: Move the cursor to the given position in screen coordinates. + # + # If there are multiple cursors, they will be consolidated to a single cursor. + # + # * `position` A {Point} or {Array} of `[row, column]` + # * `options` (optional) An {Object} combining options for {::clipScreenPosition} with: + # * `autoscroll` Determines whether the editor scrolls to the new cursor's + # position. Defaults to true. + setCursorScreenPosition: (position, options) -> + @moveCursors (cursor) -> cursor.setScreenPosition(position, options) + + # Essential: Add a cursor at the given position in buffer coordinates. + # + # * `bufferPosition` A {Point} or {Array} of `[row, column]` + # + # Returns a {Cursor}. + addCursorAtBufferPosition: (bufferPosition) -> + @markBufferPosition(bufferPosition, @getSelectionMarkerAttributes()) + @getLastSelection().cursor + + # Essential: Add a cursor at the position in screen coordinates. + # + # * `screenPosition` A {Point} or {Array} of `[row, column]` # # Returns a {Cursor}. addCursorAtScreenPosition: (screenPosition) -> @markScreenPosition(screenPosition, @getSelectionMarkerAttributes()) @getLastSelection().cursor - # Public: Add a cursor at the given position in buffer coordinates. + # Essential: Returns {Boolean} indicating whether or not there are multiple cursors. + hasMultipleCursors: -> + @getCursors().length > 1 + + # Essential: Move every cursor up one row in screen coordinates. # - # Returns a {Cursor}. - addCursorAtBufferPosition: (bufferPosition) -> - @markBufferPosition(bufferPosition, @getSelectionMarkerAttributes()) - @getLastSelection().cursor + # * `lineCount` (optional) {Number} number of lines to move + moveUp: (lineCount) -> + @moveCursors (cursor) -> cursor.moveUp(lineCount, moveToEndOfSelection: true) + moveCursorUp: (lineCount) -> + deprecate("Use Editor::moveUp() instead") + @moveUp(lineCount) + + # Essential: Move every cursor down one row in screen coordinates. + # + # * `lineCount` (optional) {Number} number of lines to move + moveDown: (lineCount) -> + @moveCursors (cursor) -> cursor.moveDown(lineCount, moveToEndOfSelection: true) + moveCursorDown: (lineCount) -> + deprecate("Use Editor::moveDown() instead") + @moveDown(lineCount) + + # Essential: Move every cursor left one column. + # + # * `columnCount` (optional) {Number} number of columns to move (default: 1) + moveLeft: (columnCount) -> + @moveCursors (cursor) -> cursor.moveLeft(columnCount, moveToEndOfSelection: true) + moveCursorLeft: -> + deprecate("Use Editor::moveLeft() instead") + @moveLeft() + + # Essential: Move every cursor right one column. + # + # * `columnCount` (optional) {Number} number of columns to move (default: 1) + moveRight: (columnCount) -> + @moveCursors (cursor) -> cursor.moveRight(columnCount, moveToEndOfSelection: true) + moveCursorRight: -> + deprecate("Use Editor::moveRight() instead") + @moveRight() + + # Essential: Move every cursor to the beginning of its line in buffer coordinates. + moveToBeginningOfLine: -> + @moveCursors (cursor) -> cursor.moveToBeginningOfLine() + moveCursorToBeginningOfLine: -> + deprecate("Use Editor::moveToBeginningOfLine() instead") + @moveToBeginningOfLine() + + # Essential: Move every cursor to the beginning of its line in screen coordinates. + moveToBeginningOfScreenLine: -> + @moveCursors (cursor) -> cursor.moveToBeginningOfScreenLine() + moveCursorToBeginningOfScreenLine: -> + deprecate("Use Editor::moveToBeginningOfScreenLine() instead") + @moveToBeginningOfScreenLine() + + # Essential: Move every cursor to the first non-whitespace character of its line. + moveToFirstCharacterOfLine: -> + @moveCursors (cursor) -> cursor.moveToFirstCharacterOfLine() + moveCursorToFirstCharacterOfLine: -> + deprecate("Use Editor::moveToFirstCharacterOfLine() instead") + @moveToFirstCharacterOfLine() + + # Essential: Move every cursor to the end of its line in buffer coordinates. + moveToEndOfLine: -> + @moveCursors (cursor) -> cursor.moveToEndOfLine() + moveCursorToEndOfLine: -> + deprecate("Use Editor::moveToEndOfLine() instead") + @moveToEndOfLine() + + # Essential: Move every cursor to the end of its line in screen coordinates. + moveToEndOfScreenLine: -> + @moveCursors (cursor) -> cursor.moveToEndOfScreenLine() + moveCursorToEndOfScreenLine: -> + deprecate("Use Editor::moveToEndOfScreenLine() instead") + @moveToEndOfScreenLine() + + # Essential: Move every cursor to the beginning of its surrounding word. + moveToBeginningOfWord: -> + @moveCursors (cursor) -> cursor.moveToBeginningOfWord() + moveCursorToBeginningOfWord: -> + deprecate("Use Editor::moveToBeginningOfWord() instead") + @moveToBeginningOfWord() + + # Essential: Move every cursor to the end of its surrounding word. + moveToEndOfWord: -> + @moveCursors (cursor) -> cursor.moveToEndOfWord() + moveCursorToEndOfWord: -> + deprecate("Use Editor::moveToEndOfWord() instead") + @moveToEndOfWord() + + # Cursor Extended + + # Extended: Move every cursor to the top of the buffer. + # + # If there are multiple cursors, they will be merged into a single cursor. + moveToTop: -> + @moveCursors (cursor) -> cursor.moveToTop() + moveCursorToTop: -> + deprecate("Use Editor::moveToTop() instead") + @moveToTop() + + # Extended: Move every cursor to the bottom of the buffer. + # + # If there are multiple cursors, they will be merged into a single cursor. + moveToBottom: -> + @moveCursors (cursor) -> cursor.moveToBottom() + moveCursorToBottom: -> + deprecate("Use Editor::moveToBottom() instead") + @moveToBottom() + + # Extended: Move every cursor to the beginning of the next word. + moveToBeginningOfNextWord: -> + @moveCursors (cursor) -> cursor.moveToBeginningOfNextWord() + moveCursorToBeginningOfNextWord: -> + deprecate("Use Editor::moveToBeginningOfNextWord() instead") + @moveToBeginningOfNextWord() + + # Extended: Move every cursor to the previous word boundary. + moveToPreviousWordBoundary: -> + @moveCursors (cursor) -> cursor.moveToPreviousWordBoundary() + moveCursorToPreviousWordBoundary: -> + deprecate("Use Editor::moveToPreviousWordBoundary() instead") + @moveToPreviousWordBoundary() + + # Extended: Move every cursor to the next word boundary. + moveToNextWordBoundary: -> + @moveCursors (cursor) -> cursor.moveToNextWordBoundary() + moveCursorToNextWordBoundary: -> + deprecate("Use Editor::moveToNextWordBoundary() instead") + @moveToNextWordBoundary() + + # Extended: Move every cursor to the beginning of the next paragraph. + moveToBeginningOfNextParagraph: -> + @moveCursors (cursor) -> cursor.moveToBeginningOfNextParagraph() + moveCursorToBeginningOfNextParagraph: -> + deprecate("Use Editor::moveToBeginningOfNextParagraph() instead") + @moveToBeginningOfNextParagraph() + + # Extended: Move every cursor to the beginning of the previous paragraph. + moveToBeginningOfPreviousParagraph: -> + @moveCursors (cursor) -> cursor.moveToBeginningOfPreviousParagraph() + moveCursorToBeginningOfPreviousParagraph: -> + deprecate("Use Editor::moveToBeginningOfPreviousParagraph() instead") + @moveToBeginningOfPreviousParagraph() + + # Extended: Returns the most recently added {Cursor} + getLastCursor: -> + _.last(@cursors) + + # Deprecated: + getCursor: -> + deprecate("Use Editor::getLastCursor() instead") + @getLastCursor() + + # Extended: Returns the word surrounding the most recently added cursor. + # + # * `options` (optional) See {Cursor::getBeginningOfCurrentWordBufferPosition}. + getWordUnderCursor: (options) -> + @getTextInBufferRange(@getLastCursor().getCurrentWordBufferRange(options)) + + # Extended: Get an Array of all {Cursor}s. + getCursors: -> + cursor for cursor in @cursors + + # Extended: Get all {Cursors}s, ordered by their position in the buffer + # instead of the order in which they were added. + # + # Returns an {Array} of {Selection}s. + getCursorsOrderedByBufferPosition: -> + @getCursors().sort (a, b) -> a.compare(b) # Add a cursor based on the given {DisplayBufferMarker}. addCursor: (marker) -> @@ -1551,130 +1798,6 @@ class Editor extends Model _.remove(@cursors, cursor) @emit 'cursor-removed', cursor - # Public: Move the cursor to the given position in screen coordinates. - # - # If there are multiple cursors, they will be consolidated to a single cursor. - # - # * `position` A {Point} or {Array} of `[row, column]` - # * `options` (optional) An {Object} combining options for {::clipScreenPosition} with: - # * `autoscroll` Determines whether the editor scrolls to the new cursor's - # position. Defaults to true. - setCursorScreenPosition: (position, options) -> - @moveCursors (cursor) -> cursor.setScreenPosition(position, options) - - # Public: Get the position of the most recently added cursor in screen - # coordinates. - # - # Returns a {Point}. - getCursorScreenPosition: -> - @getCursor().getScreenPosition() - - # Public: Get the row of the most recently added cursor in screen coordinates. - # - # Returns the screen row {Number}. - getCursorScreenRow: -> - @getCursor().getScreenRow() - - # Public: Move the cursor to the given position in buffer coordinates. - # - # If there are multiple cursors, they will be consolidated to a single cursor. - # - # * `position` A {Point} or {Array} of `[row, column]` - # * `options` (optional) An {Object} combining options for {::clipScreenPosition} with: - # * `autoscroll` Determines whether the editor scrolls to the new cursor's - # position. Defaults to true. - setCursorBufferPosition: (position, options) -> - @moveCursors (cursor) -> cursor.setBufferPosition(position, options) - - # Public: Get the position of the most recently added cursor in buffer - # coordinates. - # - # Returns a {Point}. - getCursorBufferPosition: -> - @getCursor().getBufferPosition() - - # Public: Returns the word surrounding the most recently added cursor. - # - # * `options` (optional) See {Cursor::getBeginningOfCurrentWordBufferPosition}. - getWordUnderCursor: (options) -> - @getTextInBufferRange(@getCursor().getCurrentWordBufferRange(options)) - - # Public: Move every cursor up one row in screen coordinates. - moveCursorUp: (lineCount) -> - @moveCursors (cursor) -> cursor.moveUp(lineCount, moveToEndOfSelection: true) - - # Public: Move every cursor down one row in screen coordinates. - moveCursorDown: (lineCount) -> - @moveCursors (cursor) -> cursor.moveDown(lineCount, moveToEndOfSelection: true) - - # Public: Move every cursor left one column. - moveCursorLeft: -> - @moveCursors (cursor) -> cursor.moveLeft(moveToEndOfSelection: true) - - # Public: Move every cursor right one column. - moveCursorRight: -> - @moveCursors (cursor) -> cursor.moveRight(moveToEndOfSelection: true) - - # Public: Move every cursor to the top of the buffer. - # - # If there are multiple cursors, they will be merged into a single cursor. - moveCursorToTop: -> - @moveCursors (cursor) -> cursor.moveToTop() - - # Public: Move every cursor to the bottom of the buffer. - # - # If there are multiple cursors, they will be merged into a single cursor. - moveCursorToBottom: -> - @moveCursors (cursor) -> cursor.moveToBottom() - - # Public: Move every cursor to the beginning of its line in screen coordinates. - moveCursorToBeginningOfScreenLine: -> - @moveCursors (cursor) -> cursor.moveToBeginningOfScreenLine() - - # Public: Move every cursor to the beginning of its line in buffer coordinates. - moveCursorToBeginningOfLine: -> - @moveCursors (cursor) -> cursor.moveToBeginningOfLine() - - # Public: Move every cursor to the first non-whitespace character of its line. - moveCursorToFirstCharacterOfLine: -> - @moveCursors (cursor) -> cursor.moveToFirstCharacterOfLine() - - # Public: Move every cursor to the end of its line in screen coordinates. - moveCursorToEndOfScreenLine: -> - @moveCursors (cursor) -> cursor.moveToEndOfScreenLine() - - # Public: Move every cursor to the end of its line in buffer coordinates. - moveCursorToEndOfLine: -> - @moveCursors (cursor) -> cursor.moveToEndOfLine() - - # Public: Move every cursor to the beginning of its surrounding word. - moveCursorToBeginningOfWord: -> - @moveCursors (cursor) -> cursor.moveToBeginningOfWord() - - # Public: Move every cursor to the end of its surrounding word. - moveCursorToEndOfWord: -> - @moveCursors (cursor) -> cursor.moveToEndOfWord() - - # Public: Move every cursor to the beginning of the next word. - moveCursorToBeginningOfNextWord: -> - @moveCursors (cursor) -> cursor.moveToBeginningOfNextWord() - - # Public: Move every cursor to the previous word boundary. - moveCursorToPreviousWordBoundary: -> - @moveCursors (cursor) -> cursor.moveToPreviousWordBoundary() - - # Public: Move every cursor to the next word boundary. - moveCursorToNextWordBoundary: -> - @moveCursors (cursor) -> cursor.moveToNextWordBoundary() - - # Public: Move every cursor to the beginning of the next paragraph. - moveCursorToBeginningOfNextParagraph: -> - @moveCursors (cursor) -> cursor.moveToBeginningOfNextParagraph() - - # Public: Move every cursor to the beginning of the previous paragraph. - moveCursorToBeginningOfPreviousParagraph: -> - @moveCursors (cursor) -> cursor.moveToBeginningOfPreviousParagraph() - moveCursors: (fn) -> @movingCursors = true fn(cursor) for cursor in @getCursors() @@ -1704,11 +1827,422 @@ class Editor extends Model @setCursorBufferPosition(cursorPosition) if cursorPosition cursorPosition = null - ### Section: Selections ### + # Essential: Get the selected text of the most recently added selection. + # + # Returns a {String}. + getSelectedText: -> + @getLastSelection().getText() + + # Essential: Get the {Range} of the most recently added selection in buffer + # coordinates. + # + # Returns a {Range}. + getSelectedBufferRange: -> + @getLastSelection().getBufferRange() + + # Essential: Get the {Range}s of all selections in buffer coordinates. + # + # The ranges are sorted by their position in the buffer. + # + # Returns an {Array} of {Range}s. + getSelectedBufferRanges: -> + selection.getBufferRange() for selection in @getSelections() + + # Essential: Set the selected range in buffer coordinates. If there are multiple + # selections, they are reduced to a single selection with the given range. + # + # * `bufferRange` A {Range} or range-compatible {Array}. + # * `options` (optional) An options {Object}: + # * `reversed` A {Boolean} indicating whether to create the selection in a + # reversed orientation. + setSelectedBufferRange: (bufferRange, options) -> + @setSelectedBufferRanges([bufferRange], options) + + # Essential: Set the selected ranges in buffer coordinates. If there are multiple + # selections, they are replaced by new selections with the given ranges. + # + # * `bufferRanges` An {Array} of {Range}s or range-compatible {Array}s. + # * `options` (optional) An options {Object}: + # * `reversed` A {Boolean} indicating whether to create the selection in a + # reversed orientation. + setSelectedBufferRanges: (bufferRanges, options={}) -> + throw new Error("Passed an empty array to setSelectedBufferRanges") unless bufferRanges.length + + selections = @getSelections() + selection.destroy() for selection in selections[bufferRanges.length...] + + @mergeIntersectingSelections options, => + for bufferRange, i in bufferRanges + bufferRange = Range.fromObject(bufferRange) + if selections[i] + selections[i].setBufferRange(bufferRange, options) + else + @addSelectionForBufferRange(bufferRange, options) + + # Essential: Get the {Range} of the most recently added selection in screen + # coordinates. + # + # Returns a {Range}. + getSelectedScreenRange: -> + @getLastSelection().getScreenRange() + + # Essential: Get the {Range}s of all selections in screen coordinates. + # + # The ranges are sorted by their position in the buffer. + # + # Returns an {Array} of {Range}s. + getSelectedScreenRanges: -> + selection.getScreenRange() for selection in @getSelections() + + # Essential: Set the selected range in screen coordinates. If there are multiple + # selections, they are reduced to a single selection with the given range. + # + # * `screenRange` A {Range} or range-compatible {Array}. + # * `options` (optional) An options {Object}: + # * `reversed` A {Boolean} indicating whether to create the selection in a + # reversed orientation. + setSelectedScreenRange: (screenRange, options) -> + @setSelectedBufferRange(@bufferRangeForScreenRange(screenRange, options), options) + + # Essential: Set the selected ranges in screen coordinates. If there are multiple + # selections, they are replaced by new selections with the given ranges. + # + # * `screenRanges` An {Array} of {Range}s or range-compatible {Array}s. + # * `options` (optional) An options {Object}: + # * `reversed` A {Boolean} indicating whether to create the selection in a + # reversed orientation. + setSelectedScreenRanges: (screenRanges, options={}) -> + throw new Error("Passed an empty array to setSelectedScreenRanges") unless screenRanges.length + + selections = @getSelections() + selection.destroy() for selection in selections[screenRanges.length...] + + @mergeIntersectingSelections options, => + for screenRange, i in screenRanges + screenRange = Range.fromObject(screenRange) + if selections[i] + selections[i].setScreenRange(screenRange, options) + else + @addSelectionForScreenRange(screenRange, options) + + # Essential: Add a selection for the given range in buffer coordinates. + # + # * `bufferRange` A {Range} + # * `options` (optional) An options {Object}: + # * `reversed` A {Boolean} indicating whether to create the selection in a + # reversed orientation. + # + # Returns the added {Selection}. + addSelectionForBufferRange: (bufferRange, options={}) -> + @markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options)) + selection = @getLastSelection() + selection.autoscroll() if @manageScrollPosition + selection + + # Essential: Add a selection for the given range in screen coordinates. + # + # * `screenRange` A {Range} + # * `options` (optional) An options {Object}: + # * `reversed` A {Boolean} indicating whether to create the selection in a + # reversed orientation. + # + # Returns the added {Selection}. + addSelectionForScreenRange: (screenRange, options={}) -> + @markScreenRange(screenRange, _.defaults(@getSelectionMarkerAttributes(), options)) + selection = @getLastSelection() + selection.autoscroll() if @manageScrollPosition + selection + + # Essential: Select from the current cursor position to the given position in + # buffer coordinates. + # + # This method may merge selections that end up intesecting. + # + # * `position` An instance of {Point}, with a given `row` and `column`. + selectToBufferPosition: (position) -> + lastSelection = @getLastSelection() + lastSelection.selectToBufferPosition(position) + @mergeIntersectingSelections(reversed: lastSelection.isReversed()) + + # Essential: Select from the current cursor position to the given position in + # screen coordinates. + # + # This method may merge selections that end up intesecting. + # + # * `position` An instance of {Point}, with a given `row` and `column`. + selectToScreenPosition: (position) -> + lastSelection = @getLastSelection() + lastSelection.selectToScreenPosition(position) + @mergeIntersectingSelections(reversed: lastSelection.isReversed()) + + # Essential: Move the cursor of each selection one character upward while + # preserving the selection's tail position. + # + # * `rowCount` (optional) {Number} number of rows to select (default: 1) + # + # This method may merge selections that end up intesecting. + selectUp: (rowCount) -> + @expandSelectionsBackward (selection) -> selection.selectUp(rowCount) + + # Essential: Move the cursor of each selection one character downward while + # preserving the selection's tail position. + # + # * `rowCount` (optional) {Number} number of rows to select (default: 1) + # + # This method may merge selections that end up intesecting. + selectDown: (rowCount) -> + @expandSelectionsForward (selection) -> selection.selectDown(rowCount) + + # Essential: Move the cursor of each selection one character leftward while + # preserving the selection's tail position. + # + # * `columnCount` (optional) {Number} number of columns to select (default: 1) + # + # This method may merge selections that end up intesecting. + selectLeft: (columnCount) -> + @expandSelectionsBackward (selection) -> selection.selectLeft(columnCount) + + # Essential: Move the cursor of each selection one character rightward while + # preserving the selection's tail position. + # + # * `columnCount` (optional) {Number} number of columns to select (default: 1) + # + # This method may merge selections that end up intesecting. + selectRight: (columnCount) -> + @expandSelectionsForward (selection) -> selection.selectRight(columnCount) + + # Essential: Select from the top of the buffer to the end of the last selection + # in the buffer. + # + # This method merges multiple selections into a single selection. + selectToTop: -> + @expandSelectionsBackward (selection) -> selection.selectToTop() + + # Essential: Selects from the top of the first selection in the buffer to the end + # of the buffer. + # + # This method merges multiple selections into a single selection. + selectToBottom: -> + @expandSelectionsForward (selection) -> selection.selectToBottom() + + # Essential: Select all text in the buffer. + # + # This method merges multiple selections into a single selection. + selectAll: -> + @expandSelectionsForward (selection) -> selection.selectAll() + + # Essential: Move the cursor of each selection to the beginning of its line + # while preserving the selection's tail position. + # + # This method may merge selections that end up intesecting. + selectToBeginningOfLine: -> + @expandSelectionsBackward (selection) -> selection.selectToBeginningOfLine() + + # Essential: Move the cursor of each selection to the first non-whitespace + # character of its line while preserving the selection's tail position. If the + # cursor is already on the first character of the line, move it to the + # beginning of the line. + # + # This method may merge selections that end up intersecting. + selectToFirstCharacterOfLine: -> + @expandSelectionsBackward (selection) -> selection.selectToFirstCharacterOfLine() + + # Essential: Move the cursor of each selection to the end of its line while + # preserving the selection's tail position. + # + # This method may merge selections that end up intersecting. + selectToEndOfLine: -> + @expandSelectionsForward (selection) -> selection.selectToEndOfLine() + + # Essential: Expand selections to the beginning of their containing word. + # + # Operates on all selections. Moves the cursor to the beginning of the + # containing word while preserving the selection's tail position. + selectToBeginningOfWord: -> + @expandSelectionsBackward (selection) -> selection.selectToBeginningOfWord() + + # Essential: Expand selections to the end of their containing word. + # + # Operates on all selections. Moves the cursor to the end of the containing + # word while preserving the selection's tail position. + selectToEndOfWord: -> + @expandSelectionsForward (selection) -> selection.selectToEndOfWord() + + # Essential: For each cursor, select the containing line. + # + # This method merges selections on successive lines. + selectLinesContainingCursors: -> + @expandSelectionsForward (selection) -> selection.selectLine() + selectLine: -> + deprecate('Use Editor::selectLinesContainingCursors instead') + @selectLinesContainingCursors() + + # Essential: Select the word surrounding each cursor. + selectWordsContainingCursors: -> + @expandSelectionsForward (selection) -> selection.selectWord() + selectWord: -> + deprecate('Use Editor::selectWordsContainingCursors instead') + @selectWordsContainingCursors() + + # Selection Extended + + # Extended: For each selection, move its cursor to the preceding word boundary + # while maintaining the selection's tail position. + # + # This method may merge selections that end up intersecting. + selectToPreviousWordBoundary: -> + @expandSelectionsBackward (selection) -> selection.selectToPreviousWordBoundary() + + # Extended: For each selection, move its cursor to the next word boundary while + # maintaining the selection's tail position. + # + # This method may merge selections that end up intersecting. + selectToNextWordBoundary: -> + @expandSelectionsForward (selection) -> selection.selectToNextWordBoundary() + + # Extended: Expand selections to the beginning of the next word. + # + # Operates on all selections. Moves the cursor to the beginning of the next + # word while preserving the selection's tail position. + selectToBeginningOfNextWord: -> + @expandSelectionsForward (selection) -> selection.selectToBeginningOfNextWord() + + # Extended: Expand selections to the beginning of the next paragraph. + # + # Operates on all selections. Moves the cursor to the beginning of the next + # paragraph while preserving the selection's tail position. + selectToBeginningOfNextParagraph: -> + @expandSelectionsForward (selection) -> selection.selectToBeginningOfNextParagraph() + + # Extended: Expand selections to the beginning of the next paragraph. + # + # Operates on all selections. Moves the cursor to the beginning of the next + # paragraph while preserving the selection's tail position. + selectToBeginningOfPreviousParagraph: -> + @expandSelectionsBackward (selection) -> selection.selectToBeginningOfPreviousParagraph() + + # Extended: Select the range of the given marker if it is valid. + # + # * `marker` A {DisplayBufferMarker} + # + # Returns the selected {Range} or `undefined` if the marker is invalid. + selectMarker: (marker) -> + if marker.isValid() + range = marker.getBufferRange() + @setSelectedBufferRange(range) + range + + # Extended: Get the most recently added {Selection}. + # + # Returns a {Selection}. + getLastSelection: -> + _.last(@selections) + + # Deprecated: + getSelection: (index) -> + if index? + deprecate("Use Editor::getSelections()[index] instead when getting a specific selection") + @getSelections()[index] + else + deprecate("Use Editor::getLastSelection() instead") + @getLastSelection() + + # Extended: Get current {Selection}s. + # + # Returns: An {Array} of {Selection}s. + getSelections: -> + selection for selection in @selections + + # Extended: Get all {Selection}s, ordered by their position in the buffer + # instead of the order in which they were added. + # + # Returns an {Array} of {Selection}s. + getSelectionsOrderedByBufferPosition: -> + @getSelections().sort (a, b) -> a.compare(b) + + # Extended: Determine if a given range in buffer coordinates intersects a + # selection. + # + # * `bufferRange` A {Range} or range-compatible {Array}. + # + # Returns a {Boolean}. + selectionIntersectsBufferRange: (bufferRange) -> + _.any @getSelections(), (selection) -> + selection.intersectsBufferRange(bufferRange) + + # Selections Private + + # Add a similarly-shaped selection to the next eligible line below + # each selection. + # + # Operates on all selections. If the selection is empty, adds an empty + # selection to the next following non-empty line as close to the current + # selection's column as possible. If the selection is non-empty, adds a + # selection to the next line that is long enough for a non-empty selection + # starting at the same column as the current selection to be added to it. + addSelectionBelow: -> + @expandSelectionsForward (selection) -> selection.addSelectionBelow() + + # Add a similarly-shaped selection to the next eligible line above + # each selection. + # + # Operates on all selections. If the selection is empty, adds an empty + # selection to the next preceding non-empty line as close to the current + # selection's column as possible. If the selection is non-empty, adds a + # selection to the next line that is long enough for a non-empty selection + # starting at the same column as the current selection to be added to it. + addSelectionAbove: -> + @expandSelectionsBackward (selection) -> selection.addSelectionAbove() + + # Calls the given function with each selection, then merges selections + expandSelectionsForward: (fn) -> + @mergeIntersectingSelections => + fn(selection) for selection in @getSelections() + + # Calls the given function with each selection, then merges selections in the + # reversed orientation + expandSelectionsBackward: (fn) -> + @mergeIntersectingSelections reversed: true, => + fn(selection) for selection in @getSelections() + + finalizeSelections: -> + selection.finalize() for selection in @getSelections() + + selectionsForScreenRows: (startRow, endRow) -> + @getSelections().filter (selection) -> selection.intersectsScreenRowRange(startRow, endRow) + + # Merges intersecting selections. If passed a function, it executes + # the function with merging suppressed, then merges intersecting selections + # afterward. + mergeIntersectingSelections: (args...) -> + fn = args.pop() if _.isFunction(_.last(args)) + options = args.pop() ? {} + + return fn?() if @suppressSelectionMerging + + if fn? + @suppressSelectionMerging = true + result = fn() + @suppressSelectionMerging = false + + reducer = (disjointSelections, selection) -> + intersectingSelection = _.find disjointSelections, (otherSelection) -> + exclusive = not selection.isEmpty() and not otherSelection.isEmpty() + intersects = otherSelection.intersectsWith(selection, exclusive) + intersects + + if intersectingSelection? + intersectingSelection.merge(selection, options) + disjointSelections + else + disjointSelections.concat([selection]) + + _.reduce(@getSelections(), reducer, []) + # Add a {Selection} based on the given {DisplayBufferMarker}. # # * `marker` The {DisplayBufferMarker} to highlight @@ -1731,61 +2265,6 @@ class Editor extends Model @emit 'selection-added', selection selection - # Public: Add a selection for the given range in buffer coordinates. - # - # * `bufferRange` A {Range} - # * `options` (optional) An options {Object}: - # * `reversed` A {Boolean} indicating whether to create the selection in a - # reversed orientation. - # - # Returns the added {Selection}. - addSelectionForBufferRange: (bufferRange, options={}) -> - @markBufferRange(bufferRange, _.defaults(@getSelectionMarkerAttributes(), options)) - selection = @getLastSelection() - selection.autoscroll() if @manageScrollPosition - selection - - # Public: Set the selected range in buffer coordinates. If there are multiple - # selections, they are reduced to a single selection with the given range. - # - # * `bufferRange` A {Range} or range-compatible {Array}. - # * `options` (optional) An options {Object}: - # * `reversed` A {Boolean} indicating whether to create the selection in a - # reversed orientation. - setSelectedBufferRange: (bufferRange, options) -> - @setSelectedBufferRanges([bufferRange], options) - - # Public: Set the selected range in screen coordinates. If there are multiple - # selections, they are reduced to a single selection with the given range. - # - # * `screenRange` A {Range} or range-compatible {Array}. - # * `options` (optional) An options {Object}: - # * `reversed` A {Boolean} indicating whether to create the selection in a - # reversed orientation. - setSelectedScreenRange: (screenRange, options) -> - @setSelectedBufferRange(@bufferRangeForScreenRange(screenRange, options), options) - - # Public: Set the selected ranges in buffer coordinates. If there are multiple - # selections, they are replaced by new selections with the given ranges. - # - # * `bufferRanges` An {Array} of {Range}s or range-compatible {Array}s. - # * `options` (optional) An options {Object}: - # * `reversed` A {Boolean} indicating whether to create the selection in a - # reversed orientation. - setSelectedBufferRanges: (bufferRanges, options={}) -> - throw new Error("Passed an empty array to setSelectedBufferRanges") unless bufferRanges.length - - selections = @getSelections() - selection.destroy() for selection in selections[bufferRanges.length...] - - @mergeIntersectingSelections options, => - for bufferRange, i in bufferRanges - bufferRange = Range.fromObject(bufferRange) - if selections[i] - selections[i].setBufferRange(bufferRange, options) - else - @addSelectionForBufferRange(bufferRange, options) - # Remove the given selection. removeSelection: (selection) -> _.remove(@selections, selection) @@ -1795,7 +2274,7 @@ class Editor extends Model # recently added cursor. clearSelections: -> @consolidateSelections() - @getSelection().clear() + @getLastSelection().clear() # Reduce multiple selections to the most recently added selection. consolidateSelections: -> @@ -1809,304 +2288,6 @@ class Editor extends Model selectionScreenRangeChanged: (selection) -> @emit 'selection-screen-range-changed', selection - # Public: Get current {Selection}s. - # - # Returns: An {Array} of {Selection}s. - getSelections: -> new Array(@selections...) - - selectionsForScreenRows: (startRow, endRow) -> - @getSelections().filter (selection) -> selection.intersectsScreenRowRange(startRow, endRow) - - # Public: Get the most recent {Selection} or the selection at the given - # index. - # - # * `index` (optional) The index of the selection to return, based on the order - # in which the selections were added. - # - # Returns a {Selection}. - # or the at the specified index. - getSelection: (index) -> - index ?= @selections.length - 1 - @selections[index] - - # Public: Get the most recently added {Selection}. - # - # Returns a {Selection}. - getLastSelection: -> - _.last(@selections) - - # Public: Get all {Selection}s, ordered by their position in the buffer - # instead of the order in which they were added. - # - # Returns an {Array} of {Selection}s. - getSelectionsOrderedByBufferPosition: -> - @getSelections().sort (a, b) -> a.compare(b) - - # Public: Get the last {Selection} based on its position in the buffer. - # - # Returns a {Selection}. - getLastSelectionInBuffer: -> - _.last(@getSelectionsOrderedByBufferPosition()) - - # Public: Determine if a given range in buffer coordinates intersects a - # selection. - # - # * `bufferRange` A {Range} or range-compatible {Array}. - # - # Returns a {Boolean}. - selectionIntersectsBufferRange: (bufferRange) -> - _.any @getSelections(), (selection) -> - selection.intersectsBufferRange(bufferRange) - - # Public: Get the {Range} of the most recently added selection in screen - # coordinates. - # - # Returns a {Range}. - getSelectedScreenRange: -> - @getLastSelection().getScreenRange() - - # Public: Get the {Range} of the most recently added selection in buffer - # coordinates. - # - # Returns a {Range}. - getSelectedBufferRange: -> - @getLastSelection().getBufferRange() - - # Public: Get the {Range}s of all selections in buffer coordinates. - # - # The ranges are sorted by their position in the buffer. - # - # Returns an {Array} of {Range}s. - getSelectedBufferRanges: -> - selection.getBufferRange() for selection in @getSelectionsOrderedByBufferPosition() - - # Public: Get the {Range}s of all selections in screen coordinates. - # - # The ranges are sorted by their position in the buffer. - # - # Returns an {Array} of {Range}s. - getSelectedScreenRanges: -> - selection.getScreenRange() for selection in @getSelectionsOrderedByBufferPosition() - - # Public: Get the selected text of the most recently added selection. - # - # Returns a {String}. - getSelectedText: -> - @getLastSelection().getText() - - # Public: Select from the current cursor position to the given position in - # screen coordinates. - # - # This method may merge selections that end up intesecting. - # - # * `position` An instance of {Point}, with a given `row` and `column`. - selectToScreenPosition: (position) -> - lastSelection = @getLastSelection() - lastSelection.selectToScreenPosition(position) - @mergeIntersectingSelections(reversed: lastSelection.isReversed()) - - # Public: Move the cursor of each selection one character rightward while - # preserving the selection's tail position. - # - # This method may merge selections that end up intesecting. - selectRight: -> - @expandSelectionsForward (selection) -> selection.selectRight() - - # Public: Move the cursor of each selection one character leftward while - # preserving the selection's tail position. - # - # This method may merge selections that end up intesecting. - selectLeft: -> - @expandSelectionsBackward (selection) -> selection.selectLeft() - - # Public: Move the cursor of each selection one character upward while - # preserving the selection's tail position. - # - # This method may merge selections that end up intesecting. - selectUp: (rowCount) -> - @expandSelectionsBackward (selection) -> selection.selectUp(rowCount) - - # Public: Move the cursor of each selection one character downward while - # preserving the selection's tail position. - # - # This method may merge selections that end up intesecting. - selectDown: (rowCount) -> - @expandSelectionsForward (selection) -> selection.selectDown(rowCount) - - # Public: Select from the top of the buffer to the end of the last selection - # in the buffer. - # - # This method merges multiple selections into a single selection. - selectToTop: -> - @expandSelectionsBackward (selection) -> selection.selectToTop() - - # Public: Select all text in the buffer. - # - # This method merges multiple selections into a single selection. - selectAll: -> - @expandSelectionsForward (selection) -> selection.selectAll() - - # Public: Selects from the top of the first selection in the buffer to the end - # of the buffer. - # - # This method merges multiple selections into a single selection. - selectToBottom: -> - @expandSelectionsForward (selection) -> selection.selectToBottom() - - # Public: Move the cursor of each selection to the beginning of its line - # while preserving the selection's tail position. - # - # This method may merge selections that end up intesecting. - selectToBeginningOfLine: -> - @expandSelectionsBackward (selection) -> selection.selectToBeginningOfLine() - - # Public: Move the cursor of each selection to the first non-whitespace - # character of its line while preserving the selection's tail position. If the - # cursor is already on the first character of the line, move it to the - # beginning of the line. - # - # This method may merge selections that end up intersecting. - selectToFirstCharacterOfLine: -> - @expandSelectionsBackward (selection) -> selection.selectToFirstCharacterOfLine() - - # Public: Move the cursor of each selection to the end of its line while - # preserving the selection's tail position. - # - # This method may merge selections that end up intersecting. - selectToEndOfLine: -> - @expandSelectionsForward (selection) -> selection.selectToEndOfLine() - - # Public: For each selection, move its cursor to the preceding word boundary - # while maintaining the selection's tail position. - # - # This method may merge selections that end up intersecting. - selectToPreviousWordBoundary: -> - @expandSelectionsBackward (selection) -> selection.selectToPreviousWordBoundary() - - # Public: For each selection, move its cursor to the next word boundary while - # maintaining the selection's tail position. - # - # This method may merge selections that end up intersecting. - selectToNextWordBoundary: -> - @expandSelectionsForward (selection) -> selection.selectToNextWordBoundary() - - # Public: For each cursor, select the containing line. - # - # This method merges selections on successive lines. - selectLine: -> - @expandSelectionsForward (selection) -> selection.selectLine() - - # Public: Add a similarly-shaped selection to the next eligible line below - # each selection. - # - # Operates on all selections. If the selection is empty, adds an empty - # selection to the next following non-empty line as close to the current - # selection's column as possible. If the selection is non-empty, adds a - # selection to the next line that is long enough for a non-empty selection - # starting at the same column as the current selection to be added to it. - addSelectionBelow: -> - @expandSelectionsForward (selection) -> selection.addSelectionBelow() - - # Public: Add a similarly-shaped selection to the next eligible line above - # each selection. - # - # Operates on all selections. If the selection is empty, adds an empty - # selection to the next preceding non-empty line as close to the current - # selection's column as possible. If the selection is non-empty, adds a - # selection to the next line that is long enough for a non-empty selection - # starting at the same column as the current selection to be added to it. - addSelectionAbove: -> - @expandSelectionsBackward (selection) -> selection.addSelectionAbove() - - # Public: Expand selections to the beginning of their containing word. - # - # Operates on all selections. Moves the cursor to the beginning of the - # containing word while preserving the selection's tail position. - selectToBeginningOfWord: -> - @expandSelectionsBackward (selection) -> selection.selectToBeginningOfWord() - - # Public: Expand selections to the end of their containing word. - # - # Operates on all selections. Moves the cursor to the end of the containing - # word while preserving the selection's tail position. - selectToEndOfWord: -> - @expandSelectionsForward (selection) -> selection.selectToEndOfWord() - - # Public: Expand selections to the beginning of the next word. - # - # Operates on all selections. Moves the cursor to the beginning of the next - # word while preserving the selection's tail position. - selectToBeginningOfNextWord: -> - @expandSelectionsForward (selection) -> selection.selectToBeginningOfNextWord() - - # Public: Select the word containing each cursor. - selectWord: -> - @expandSelectionsForward (selection) -> selection.selectWord() - - # Public: Expand selections to the beginning of the next paragraph. - # - # Operates on all selections. Moves the cursor to the beginning of the next - # paragraph while preserving the selection's tail position. - selectToBeginningOfNextParagraph: -> - @expandSelectionsForward (selection) -> selection.selectToBeginningOfNextParagraph() - - # Public: Expand selections to the beginning of the next paragraph. - # - # Operates on all selections. Moves the cursor to the beginning of the next - # paragraph while preserving the selection's tail position. - selectToBeginningOfPreviousParagraph: -> - @expandSelectionsBackward (selection) -> selection.selectToBeginningOfPreviousParagraph() - - # Public: Select the range of the given marker if it is valid. - # - # * `marker` A {DisplayBufferMarker} - # - # Returns the selected {Range} or `undefined` if the marker is invalid. - selectMarker: (marker) -> - if marker.isValid() - range = marker.getBufferRange() - @setSelectedBufferRange(range) - range - - # Calls the given function with each selection, then merges selections - expandSelectionsForward: (fn) -> - @mergeIntersectingSelections => - fn(selection) for selection in @getSelections() - - # Calls the given function with each selection, then merges selections in the - # reversed orientation - expandSelectionsBackward: (fn) -> - @mergeIntersectingSelections reversed: true, => - fn(selection) for selection in @getSelections() - - finalizeSelections: -> - selection.finalize() for selection in @getSelections() - - # Merges intersecting selections. If passed a function, it executes - # the function with merging suppressed, then merges intersecting selections - # afterward. - mergeIntersectingSelections: (args...) -> - fn = args.pop() if _.isFunction(_.last(args)) - options = args.pop() ? {} - - return fn?() if @suppressSelectionMerging - - if fn? - @suppressSelectionMerging = true - result = fn() - @suppressSelectionMerging = false - - reducer = (disjointSelections, selection) -> - intersectingSelection = _.find(disjointSelections, (s) -> s.intersectsWith(selection)) - if intersectingSelection? - intersectingSelection.merge(selection, options) - disjointSelections - else - disjointSelections.concat([selection]) - - _.reduce(@getSelections(), reducer, []) - - ### Section: Scrolling the Editor @@ -2118,16 +2299,16 @@ class Editor extends Model # * `options` (optional) {Object} # * `center` Center the editor around the cursor if possible. Defauls to true. scrollToCursorPosition: (options) -> - @getCursor().autoscroll(center: options?.center ? true) + @getLastCursor().autoscroll(center: options?.center ? true) pageUp: -> newScrollTop = @getScrollTop() - @getHeight() - @moveCursorUp(@getRowsPerPage()) + @moveUp(@getRowsPerPage()) @setScrollTop(newScrollTop) pageDown: -> newScrollTop = @getScrollTop() + @getHeight() - @moveCursorDown(@getRowsPerPage()) + @moveDown(@getRowsPerPage()) @setScrollTop(newScrollTop) selectPageUp: -> diff --git a/src/language-mode.coffee b/src/language-mode.coffee index 4bbffcc73..de1a5b928 100644 --- a/src/language-mode.coffee +++ b/src/language-mode.coffee @@ -145,17 +145,17 @@ class LanguageMode rowRange rowRangeForCommentAtBufferRow: (bufferRow) -> - return unless @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment() + return unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() startRow = bufferRow for currentRow in [bufferRow-1..0] break if @buffer.isRowBlank(currentRow) - break unless @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment() + break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() startRow = currentRow endRow = bufferRow for currentRow in [bufferRow+1..@buffer.getLastRow()] break if @buffer.isRowBlank(currentRow) - break unless @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(currentRow).isComment() + break unless @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(currentRow).isComment() endRow = currentRow return [startRow, endRow] if startRow isnt endRow @@ -168,7 +168,7 @@ class LanguageMode continue if @editor.isBufferRowBlank(row) indentation = @editor.indentationForBufferRow(row) if indentation <= startIndentLevel - includeRowInFold = indentation == startIndentLevel and @foldEndRegexForScopes(scopes)?.searchSync(@editor.lineForBufferRow(row)) + includeRowInFold = indentation == startIndentLevel and @foldEndRegexForScopes(scopes)?.searchSync(@editor.lineTextForBufferRow(row)) foldEndRow = row if includeRowInFold break @@ -201,13 +201,13 @@ class LanguageMode # row is a comment. isLineCommentedAtBufferRow: (bufferRow) -> return false unless 0 <= bufferRow <= @editor.getLastBufferRow() - @editor.displayBuffer.tokenizedBuffer.lineForScreenRow(bufferRow).isComment() + @editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() # Find a row range for a 'paragraph' around specified bufferRow. # Right now, a paragraph is a block of text bounded by and empty line or a # block of text that is not the same type (comments next to source code). rowRangeForParagraphAtBufferRow: (bufferRow) -> - return unless /\w/.test(@editor.lineForBufferRow(bufferRow)) + return unless /\w/.test(@editor.lineTextForBufferRow(bufferRow)) if @isLineCommentedAtBufferRow(bufferRow) isOriginalRowComment = true @@ -220,17 +220,17 @@ class LanguageMode startRow = bufferRow while startRow > firstRow break if @isLineCommentedAtBufferRow(startRow - 1) != isOriginalRowComment - break unless /\w/.test(@editor.lineForBufferRow(startRow - 1)) + break unless /\w/.test(@editor.lineTextForBufferRow(startRow - 1)) startRow-- endRow = bufferRow lastRow = @editor.getLastBufferRow() while endRow < lastRow break if @isLineCommentedAtBufferRow(endRow + 1) != isOriginalRowComment - break unless /\w/.test(@editor.lineForBufferRow(endRow + 1)) + break unless /\w/.test(@editor.lineTextForBufferRow(endRow + 1)) endRow++ - new Range([startRow, 0], [endRow, @editor.lineLengthForBufferRow(endRow)]) + new Range([startRow, 0], [endRow, @editor.lineTextForBufferRow(endRow).length]) # Given a buffer row, this returns a suggested indentation level. # diff --git a/src/pane-axis-view.coffee b/src/pane-axis-view.coffee index 3039600c0..0018ee6b1 100644 --- a/src/pane-axis-view.coffee +++ b/src/pane-axis-view.coffee @@ -1,11 +1,17 @@ +{CompositeDisposable} = require 'event-kit' {View} = require './space-pen-extensions' PaneView = null module.exports = class PaneAxisView extends View initialize: (@model) -> - @onChildAdded(child) for child in @model.children - @subscribe @model.children, 'changed', @onChildrenChanged + @subscriptions = new CompositeDisposable + + @onChildAdded({child, index}) for child, index in @model.getChildren() + + @subscriptions.add @model.onDidAddChild(@onChildAdded) + @subscriptions.add @model.onDidRemoveChild(@onChildRemoved) + @subscriptions.add @model.onDidReplaceChild(@onChildReplaced) afterAttach: -> @container = @closest('.panes').view() @@ -14,19 +20,22 @@ class PaneAxisView extends View viewClass = model.getViewClass() model._view ?= new viewClass(model) - onChildrenChanged: ({index, removedValues, insertedValues}) => + onChildReplaced: ({index, oldChild, newChild}) => focusedElement = document.activeElement if @hasFocus() - @onChildRemoved(child, index) for child in removedValues - @onChildAdded(child, index + i) for child, i in insertedValues + @onChildRemoved({child: oldChild, index}) + @onChildAdded({child: newChild, index}) focusedElement?.focus() if document.activeElement is document.body - onChildAdded: (child, index) => + onChildAdded: ({child, index}) => view = @viewForModel(child) @insertAt(index, view) - onChildRemoved: (child) => + onChildRemoved: ({child}) => view = @viewForModel(child) view.detach() PaneView ?= require './pane-view' if view instanceof PaneView and view.model.isDestroyed() @container?.trigger 'pane:removed', [view] + + beforeRemove: -> + @subscriptions.dispose() diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 39657fcf7..9ef95bc8c 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -1,4 +1,5 @@ -{Model, Sequence} = require 'theorist' +{Model} = require 'theorist' +{Emitter, CompositeDisposable} = require 'event-kit' {flatten} = require 'underscore-plus' Serializable = require 'serializable' @@ -10,18 +11,17 @@ class PaneAxis extends Model atom.deserializers.add(this) Serializable.includeInto(this) + parent: null + container: null + orientation: null + constructor: ({@container, @orientation, children}) -> - @children = Sequence.fromArray(children ? []) - - @subscribe @children.onEach (child) => - child.parent = this - child.container = @container - @subscribe child, 'destroyed', => @removeChild(child) - - @subscribe @children.onRemoval (child) => @unsubscribe(child) - - @when @children.$length.becomesLessThan(2), 'reparentLastChild' - @when @children.$length.becomesLessThan(1), 'destroy' + @emitter = new Emitter + @subscriptionsByChild = new WeakMap + @subscriptions = new CompositeDisposable + @children = [] + if children? + @addChild(child) for child in children deserializeParams: (params) -> {container} = params @@ -32,35 +32,93 @@ class PaneAxis extends Model children: @children.map (child) -> child.serialize() orientation: @orientation + getParent: -> @parent + + setParent: (@parent) -> @parent + + getContainer: -> @container + + setContainer: (@container) -> @container + getViewClass: -> if @orientation is 'vertical' PaneColumnView ?= require './pane-column-view' else PaneRowView ?= require './pane-row-view' + getChildren: -> @children.slice() + getPanes: -> flatten(@children.map (child) -> child.getPanes()) - addChild: (child, index=@children.length) -> - @children.splice(index, 0, child) + getItems: -> + flatten(@children.map (child) -> child.getItems()) - removeChild: (child) -> + onDidAddChild: (fn) -> + @emitter.on 'did-add-child', fn + + onDidRemoveChild: (fn) -> + @emitter.on 'did-remove-child', fn + + onDidReplaceChild: (fn) -> + @emitter.on 'did-replace-child', fn + + onDidDestroy: (fn) -> + @emitter.on 'did-destroy', fn + + addChild: (child, index=@children.length) -> + child.setParent(this) + child.setContainer(@container) + + @subscribeToChild(child) + + @children.splice(index, 0, child) + @emitter.emit 'did-add-child', {child, index} + + removeChild: (child, replacing=false) -> index = @children.indexOf(child) throw new Error("Removing non-existent child") if index is -1 + + @unsubscribeFromChild(child) + @children.splice(index, 1) + @emitter.emit 'did-remove-child', {child, index} + @reparentLastChild() if not replacing and @children.length < 2 replaceChild: (oldChild, newChild) -> + @unsubscribeFromChild(oldChild) + @subscribeToChild(newChild) + + newChild.setParent(this) + newChild.setContainer(@container) + index = @children.indexOf(oldChild) - throw new Error("Replacing non-existent child") if index is -1 @children.splice(index, 1, newChild) + @emitter.emit 'did-replace-child', {oldChild, newChild, index} insertChildBefore: (currentChild, newChild) -> index = @children.indexOf(currentChild) - @children.splice(index, 0, newChild) + @addChild(newChild, index) insertChildAfter: (currentChild, newChild) -> index = @children.indexOf(currentChild) - @children.splice(index + 1, 0, newChild) + @addChild(newChild, index + 1) reparentLastChild: -> @parent.replaceChild(this, @children[0]) + @destroy() + + subscribeToChild: (child) -> + subscription = child.onDidDestroy => @removeChild(child) + @subscriptionsByChild.set(child, subscription) + @subscriptions.add(subscription) + + unsubscribeFromChild: (child) -> + subscription = @subscriptionsByChild.get(child) + @subscriptions.remove(subscription) + subscription.dispose() + + destroyed: -> + @subscriptions.dispose() + @emitter.emit 'did-destroy' + @emitter.dispose() diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index 77be49420..09a890d74 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -1,5 +1,6 @@ {deprecate} = require 'grim' Delegator = require 'delegato' +{CompositeDisposable} = require 'event-kit' {$, View} = require './space-pen-extensions' PaneView = require './pane-view' PaneContainer = require './pane-container' @@ -15,13 +16,15 @@ class PaneContainerView extends View @div class: 'panes' initialize: (params) -> + @subscriptions = new CompositeDisposable + if params instanceof PaneContainer @model = params else @model = new PaneContainer({root: params?.root?.model}) - @subscribe @model.$root, @onRootChanged - @subscribe @model.$activePaneItem.changes, @onActivePaneItemChanged + @subscriptions.add @model.observeRoot(@onRootChanged) + @subscriptions.add @model.onDidChangeActivePaneItem(@onActivePaneItemChanged) viewForModel: (model) -> if model? @@ -88,7 +91,7 @@ class PaneContainerView extends View @viewForModel(@model.activePane) getActivePaneItem: -> - @model.activePaneItem + @model.getActivePaneItem() getActiveView: -> @getActivePaneView()?.activeView @@ -153,3 +156,6 @@ class PaneContainerView extends View getPanes: -> deprecate("Use PaneContainerView::getPaneViews() instead") @getPaneViews() + + beforeRemove: -> + @subscriptions.dispose() diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 07083263a..14eeae077 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -1,5 +1,6 @@ -{find} = require 'underscore-plus' +{find, flatten} = require 'underscore-plus' {Model} = require 'theorist' +{Emitter, CompositeDisposable} = require 'event-kit' Serializable = require 'serializable' Pane = require './pane' @@ -11,10 +12,9 @@ class PaneContainer extends Model @version: 1 @properties - root: -> new Pane activePane: null - previousRoot: null + root: null @behavior 'activePaneItem', -> @$activePane @@ -23,9 +23,16 @@ class PaneContainer extends Model constructor: (params) -> super - @subscribe @$root, @onRootChanged + + @emitter = new Emitter + @subscriptions = new CompositeDisposable + + @setRoot(params?.root ? new Pane) @destroyEmptyPanes() if params?.destroyEmptyPanes + @monitorActivePaneItem() + @monitorPaneItems() + deserializeParams: (params) -> params.root = atom.deserializers.deserialize(params.root, container: this) params.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes') @@ -36,16 +43,75 @@ class PaneContainer extends Model root: @root?.serialize() activePaneId: @activePane.id + onDidChangeRoot: (fn) -> + @emitter.on 'did-change-root', fn + + observeRoot: (fn) -> + fn(@getRoot()) + @onDidChangeRoot(fn) + + onDidAddPane: (fn) -> + @emitter.on 'did-add-pane', fn + + observePanes: (fn) -> + fn(pane) for pane in @getPanes() + @onDidAddPane ({pane}) -> fn(pane) + + onDidChangeActivePane: (fn) -> + @emitter.on 'did-change-active-pane', fn + + observeActivePane: (fn) -> + fn(@getActivePane()) + @onDidChangeActivePane(fn) + + onDidAddPaneItem: (fn) -> + @emitter.on 'did-add-pane-item', fn + + observePaneItems: (fn) -> + fn(item) for item in @getPaneItems() + @onDidAddPaneItem ({item}) -> fn(item) + + onDidChangeActivePaneItem: (fn) -> + @emitter.on 'did-change-active-pane-item', fn + + observeActivePaneItem: (fn) -> + fn(@getActivePaneItem()) + @onDidChangeActivePaneItem(fn) + + onDidDestroyPaneItem: (fn) -> + @emitter.on 'did-destroy-pane-item', fn + + getRoot: -> @root + + setRoot: (@root) -> + @root.setParent(this) + @root.setContainer(this) + @emitter.emit 'did-change-root', @root + if not @getActivePane()? and @root instanceof Pane + @setActivePane(@root) + replaceChild: (oldChild, newChild) -> throw new Error("Replacing non-existent child") if oldChild isnt @root - @root = newChild + @setRoot(newChild) getPanes: -> - @root?.getPanes() ? [] + @getRoot().getPanes() + + getPaneItems: -> + @getRoot().getItems() getActivePane: -> @activePane + setActivePane: (activePane) -> + if activePane isnt @activePane + @activePane = activePane + @emitter.emit 'did-change-active-pane', @activePane + @activePane + + getActivePaneItem: -> + @getActivePane().getActiveItem() + paneForUri: (uri) -> find @getPanes(), (pane) -> pane.itemForUri(uri)? @@ -73,26 +139,37 @@ class PaneContainer extends Model else false - onRootChanged: (root) => - @unsubscribe(@previousRoot) if @previousRoot? - @previousRoot = root - - unless root? - @activePane = null - return - - root.parent = this - root.container = this - - - @activePane ?= root if root instanceof Pane - destroyEmptyPanes: -> pane.destroy() for pane in @getPanes() when pane.items.length is 0 - itemDestroyed: (item) -> - @emit 'item-destroyed', item + paneItemDestroyed: (item) -> + @emitter.emit 'did-destroy-pane-item', item + + didAddPane: (pane) -> + @emitter.emit 'did-add-pane', pane # Called by Model superclass when destroyed destroyed: -> pane.destroy() for pane in @getPanes() + @subscriptions.dispose() + @emitter.dispose() + + monitorActivePaneItem: -> + childSubscription = null + @subscriptions.add @observeActivePane (activePane) => + if childSubscription? + @subscriptions.remove(childSubscription) + childSubscription.dispose() + + childSubscription = activePane.observeActiveItem (activeItem) => + @emitter.emit 'did-change-active-pane-item', activeItem + + @subscriptions.add(childSubscription) + + monitorPaneItems: -> + @subscriptions.add @observePanes (pane) => + for item, index in pane.getItems() + @emitter.emit 'did-add-pane-item', {item, pane, index} + + pane.onDidAddItem ({item, index}) => + @emitter.emit 'did-add-pane-item', {item, pane, index} diff --git a/src/pane-view.coffee b/src/pane-view.coffee index 0d641582e..ac79faaba 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -1,6 +1,7 @@ {$, View} = require './space-pen-extensions' Delegator = require 'delegato' {deprecate} = require 'grim' +{CompositeDisposable} = require 'event-kit' PropertyAccessors = require 'property-accessors' Pane = require './pane' @@ -33,6 +34,8 @@ class PaneView extends View previousActiveItem: null initialize: (args...) -> + @subscriptions = new CompositeDisposable + if args[0] instanceof Pane @model = args[0] else @@ -44,13 +47,13 @@ class PaneView extends View @handleEvents() handleEvents: -> - @subscribe @model.$activeItem, @onActiveItemChanged - @subscribe @model, 'item-added', @onItemAdded - @subscribe @model, 'item-removed', @onItemRemoved - @subscribe @model, 'item-moved', @onItemMoved - @subscribe @model, 'before-item-destroyed', @onBeforeItemDestroyed - @subscribe @model, 'activated', @onActivated - @subscribe @model.$active, @onActiveStatusChanged + @subscriptions.add @model.observeActiveItem(@onActiveItemChanged) + @subscriptions.add @model.onDidAddItem(@onItemAdded) + @subscriptions.add @model.onDidRemoveItem(@onItemRemoved) + @subscriptions.add @model.onDidMoveItem(@onItemMoved) + @subscriptions.add @model.onWillDestroyItem(@onBeforeItemDestroyed) + @subscriptions.add @model.onDidActivate(@onActivated) + @subscriptions.add @model.observeActive(@onActiveStatusChanged) @subscribe this, 'focusin', => @model.focus() @subscribe this, 'focusout', => @model.blur() @@ -160,10 +163,10 @@ class PaneView extends View @trigger 'pane:active-item-changed', [item] - onItemAdded: (item, index) => + onItemAdded: ({item, index}) => @trigger 'pane:item-added', [item, index] - onItemRemoved: (item, index, destroyed) => + onItemRemoved: ({item, index, destroyed}) => if item instanceof $ viewToRemove = item else if viewToRemove = @viewsByItem.get(item) @@ -177,7 +180,7 @@ class PaneView extends View @trigger 'pane:item-removed', [item, index] - onItemMoved: (item, newIndex) => + onItemMoved: ({item, newIndex}) => @trigger 'pane:item-moved', [item, newIndex] onBeforeItemDestroyed: (item) => @@ -219,6 +222,7 @@ class PaneView extends View @closest('.panes').view() beforeRemove: -> + @subscriptions.dispose() @model.destroy() unless @model.isDestroyed() remove: (selector, keepData) -> diff --git a/src/pane.coffee b/src/pane.coffee index 678e2156e..d56ae007b 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -1,47 +1,16 @@ {find, compact, extend, last} = require 'underscore-plus' -{Model, Sequence} = require 'theorist' +{Model} = require 'theorist' +{Emitter} = require 'event-kit' Serializable = require 'serializable' +Grim = require 'grim' PaneAxis = require './pane-axis' Editor = require './editor' PaneView = null -# Extended: A container for multiple items, one of which is *active* at a given -# time. With the default packages, a tab is displayed for each item and the -# active item's view is displayed. -# -# ## Events -# ### activated -# -# Extended: Emit when this pane as been activated -# -# ### item-added -# -# Extended: Emit when an item was added to the pane -# -# * `item` The pane item that has been added -# * `index` {Number} Index in the pane -# -# ### before-item-destroyed -# -# Extended: Emit before the item is destroyed -# -# * `item` The pane item that will be destoryed -# -# ### item-removed -# -# Extended: Emit when the item was removed from the pane -# -# * `item` The pane item that was removed -# * `index` {Number} Index in the pane -# * `destroying` {Boolean} `true` when the item is being removed because of destruction -# -# ### item-moved -# -# Extended: Emit when an item was moved within the pane -# -# * `item` The pane item that was moved -# * `newIndex` {Number} Index that the item was moved to -# +# Extended: A container for presenting content in the center of the workspace. +# Panes can contain multiple items, one of which is *active* at a given time. +# The view corresponding to the active item is displayed in the interface. In +# the default configuration, tabs are also displayed for each item. module.exports = class Pane extends Model atom.deserializers.add(this) @@ -64,15 +33,11 @@ class Pane extends Model constructor: (params) -> super - @items = Sequence.fromArray(compact(params?.items ? [])) - @activeItem ?= @items[0] + @emitter = new Emitter + @items = [] - @subscribe @items.onEach (item) => - if typeof item.on is 'function' - @subscribe item, 'destroyed', => @removeItem(item, true) - - @subscribe @items.onRemoval (item, index) => - @unsubscribe item if typeof item.on is 'function' + @addItems(compact(params?.items ? [])) + @setActiveItem(@items[0]) unless @getActiveItem()? # Called by the Serializable mixin during serialization. serializeParams: -> @@ -91,7 +56,174 @@ class Pane extends Model # Called by the view layer to construct a view for this model. getViewClass: -> PaneView ?= require './pane-view' - isActive: -> @active + getParent: -> @parent + + setParent: (@parent) -> @parent + + getContainer: -> @container + + setContainer: (container) -> + container.didAddPane({pane: this}) unless container is @container + @container = container + + ### + Section: Event Subscription + ### + + # Public: Invoke the given callback when the pane is activated. + # + # The given callback will be invoked whenever {::activate} is called on the + # pane, even if it is already active at the time. + # + # * `callback` {Function} to be called when the pane is activated. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidActivate: (callback) -> + @emitter.on 'did-activate', callback + + # Public: Invoke the given callback when the pane is destroyed. + # + # * `callback` {Function} to be called when the pane is destroyed. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidDestroy: (callback) -> + @emitter.on 'did-destroy', callback + + # Public: Invoke the given callback when the value of the {::isActive} + # property changes. + # + # * `callback` {Function} to be called when the value of the {::isActive} + # property changes. + # * `active` {Boolean} indicating whether the pane is active. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangeActive: (callback) -> + @container.onDidChangeActivePane (activePane) => + callback(this is activePane) + + # Public: Invoke the given callback with the current and future values of the + # {::isActive} property. + # + # * `callback` {Function} to be called with the current and future values of + # the {::isActive} property. + # * `active` {Boolean} indicating whether the pane is active. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeActive: (callback) -> + callback(@isActive()) + @onDidChangeActive(callback) + + # Public: Invoke the given callback when an item is added to the pane. + # + # * `callback` {Function} to be called with when items are added. + # * `event` {Object} with the following keys: + # * `item` The added pane item. + # * `index` {Number} indicating where the item is located. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidAddItem: (callback) -> + @emitter.on 'did-add-item', callback + + # Public: Invoke the given callback when an item is removed from the pane. + # + # * `callback` {Function} to be called with when items are removed. + # * `event` {Object} with the following keys: + # * `item` The removed pane item. + # * `index` {Number} indicating where the item was located. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidRemoveItem: (callback) -> + @emitter.on 'did-remove-item', callback + + # Public: Invoke the given callback when an item is moved within the pane. + # + # * `callback` {Function} to be called with when items are moved. + # * `event` {Object} with the following keys: + # * `item` The removed pane item. + # * `oldIndex` {Number} indicating where the item was located. + # * `newIndex` {Number} indicating where the item is now located. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidMoveItem: (callback) -> + @emitter.on 'did-move-item', callback + + # Public: Invoke the given callback with all current and future items. + # + # * `callback` {Function} to be called with current and future items. + # * `item` An item that is present in {::getItems} at the time of + # subscription or that is added at some later time. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeItems: (callback) -> + callback(item) for item in @getItems() + @onDidAddItem ({item}) -> callback(item) + + # Public: Invoke the given callback when the value of {::getActiveItem} + # changes. + # + # * `callback` {Function} to be called with when the active item changes. + # * `activeItem` The current active item. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidChangeActiveItem: (callback) -> + @emitter.on 'did-change-active-item', callback + + # Public: Invoke the given callback with the current and future values of + # {::getActiveItem}. + # + # * `callback` {Function} to be called with the current and future active + # items. + # * `activeItem` The current active item. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeActiveItem: (callback) -> + callback(@getActiveItem()) + @onDidChangeActiveItem(callback) + + # Public: Invoke the given callback before items are destroyed. + # + # * `callback` {Function} to be called before items are destroyed. + # * `event` {Object} with the following keys: + # * `item` The item that will be destroyed. + # * `index` The location of the item. + # + # Returns a {Disposable} on which `.dispose()` can be called to + # unsubscribe. + onWillDestroyItem: (callback) -> + @emitter.on 'will-destroy-item', callback + + on: (eventName) -> + switch eventName + when 'activated' + Grim.deprecate("Use Pane::onDidActivate instead") + when 'destroyed' + Grim.deprecate("Use Pane::onDidDestroy instead") + when 'item-added' + Grim.deprecate("Use Pane::onDidAddItem instead") + when 'item-removed' + Grim.deprecate("Use Pane::onDidRemoveItem instead") + when 'item-moved' + Grim.deprecate("Use Pane::onDidMoveItem instead") + when 'before-item-destroyed' + Grim.deprecate("Use Pane::onWillDestroyItem instead") + else + Grim.deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.") + super + + behavior: (behaviorName) -> + switch behaviorName + when 'active' + Grim.deprecate("The $active behavior property is deprecated. Use ::observeActive or ::onDidChangeActive instead.") + when 'container' + Grim.deprecate("The $container behavior property is deprecated.") + when 'activeItem' + Grim.deprecate("The $activeItem behavior property is deprecated. Use ::observeActiveItem or ::onDidChangeActiveItem instead.") + when 'focused' + Grim.deprecate("The $focused behavior property is deprecated.") + else + Grim.deprecate("Pane::behavior is deprecated. Use event subscription methods instead.") + + super # Called by the view layer to indicate that the pane has gained focus. focus: -> @@ -103,14 +235,12 @@ class Pane extends Model @focused = false true # if this is called from an event handler, don't cancel it - # Public: Makes this pane the *active* pane, causing it to gain focus - # immediately. - activate: -> - @container?.activePane = this - @emit 'activated' - getPanes: -> [this] + ### + Section: Items + ### + # Public: Get the items in this pane. # # Returns an {Array} of items. @@ -120,15 +250,23 @@ class Pane extends Model # Public: Get the active pane item in this pane. # # Returns a pane item. - getActiveItem: -> + getActiveItem: -> @activeItem + + setActiveItem: (activeItem) -> + unless activeItem is @activeItem + @activeItem = activeItem + @emitter.emit 'did-change-active-item', @activeItem @activeItem - # Public: Returns an {Editor} if the pane item is an {Editor}, or null - # otherwise. + # Return an {Editor} if the pane item is an {Editor}, or null otherwise. getActiveEditor: -> @activeItem if @activeItem instanceof Editor - # Public: Returns the item at the specified index. + # Public: Return the item at the given index. + # + # * `index` {Number} + # + # Returns an item or `null` if no item exists at the given index. itemAtIndex: (index) -> @items[index] @@ -148,86 +286,115 @@ class Pane extends Model else @activateItemAtIndex(@items.length - 1) - # Returns the index of the current active item. + # Public: Get the index of the active item. + # + # Returns a {Number}. getActiveItemIndex: -> @items.indexOf(@activeItem) - # Makes the item at the given index active. + # Public: Activate the item at the given index. + # + # * `index` {Number} activateItemAtIndex: (index) -> @activateItem(@itemAtIndex(index)) - # Makes the given item active, adding the item if necessary. + # Public: Make the given item *active*, causing it to be displayed by + # the pane's view. activateItem: (item) -> if item? @addItem(item) - @activeItem = item + @setActiveItem(item) - # Public: Adds the item to the pane. + # Public: Add the given item to the pane. # - # * `item` The item to add. It can be a model with an associated view or a view. - # * `index` (optional) {Number} at which to add the item. If omitted, the item is - # added after the current active item. + # * `item` The item to add. It can be a model with an associated view or a + # view. + # * `index` (optional) {Number} indicating the index at which to add the item. + # If omitted, the item is added after the current active item. # - # Returns the added item + # Returns the added item. addItem: (item, index=@getActiveItemIndex() + 1) -> return if item in @items + if typeof item.on is 'function' + @subscribe item, 'destroyed', => @removeItem(item, true) + @items.splice(index, 0, item) @emit 'item-added', item, index - @activeItem ?= item + @emitter.emit 'did-add-item', {item, index} + @setActiveItem(item) unless @getActiveItem()? item - # Public: Adds the given items to the pane. + # Public: Add the given items to the pane. # - # * `items` An {Array} of items to add. Items can be models with associated - # views or views. Any items that are already present in items will - # not be added. - # * `index` (optional) {Number} index at which to add the item. If omitted, the item is - # added after the current active item. + # * `items` An {Array} of items to add. Items can be views or models with + # associated views. Any objects that are already present in the pane's + # current items will not be added again. + # * `index` (optional) {Number} index at which to add the items. If omitted, + # the item is # added after the current active item. # - # Returns an {Array} of the added items + # Returns an {Array} of added items. addItems: (items, index=@getActiveItemIndex() + 1) -> items = items.filter (item) => not (item in @items) @addItem(item, index + i) for item, i in items items - removeItem: (item, destroying) -> + removeItem: (item, destroyed=false) -> index = @items.indexOf(item) return if index is -1 + + if typeof item.on is 'function' + @unsubscribe item + if item is @activeItem if @items.length is 1 - @activeItem = undefined + @setActiveItem(undefined) else if index is 0 @activateNextItem() else @activatePreviousItem() @items.splice(index, 1) - @emit 'item-removed', item, index, destroying - @container?.itemDestroyed(item) if destroying + @emit 'item-removed', item, index, destroyed + @emitter.emit 'did-remove-item', {item, index, destroyed} + @container?.paneItemDestroyed(item) if destroyed @destroy() if @items.length is 0 and atom.config.get('core.destroyEmptyPanes') - # Public: Moves the given item to the specified index. + # Public: Move the given item to the given index. + # + # * `item` The item to move. + # * `index` {Number} indicating the index to which to move the item. moveItem: (item, newIndex) -> oldIndex = @items.indexOf(item) @items.splice(oldIndex, 1) @items.splice(newIndex, 0, item) @emit 'item-moved', item, newIndex + @emitter.emit 'did-move-item', {item, oldIndex, newIndex} - # Public: Moves the given item to the given index at another pane. + # Public: Move the given item to the given index on another pane. + # + # * `item` The item to move. + # * `pane` {Pane} to which to move the item. + # * `index` {Number} indicating the index to which to move the item in the + # given pane. moveItemToPane: (item, pane, index) -> pane.addItem(item, index) @removeItem(item) - # Public: Destroys the currently active item and make the next item active. + # Public: Destroy the active item and activate the next item. destroyActiveItem: -> @destroyItem(@activeItem) false - # Public: Destroys the given item. If it is the active item, activate the next - # one. If this is the last item, also destroys the pane. + # Public: Destroy the given item. + # + # If the item is active, the next item will be activated. If the item is the + # last item, the pane will be destroyed if the `core.destroyEmptyPanes` config + # setting is `true`. destroyItem: (item) -> - if item? + index = @items.indexOf(item) + if index isnt -1 @emit 'before-item-destroyed', item + @emitter.emit 'will-destroy-item', {item, index} if @promptToSaveItem(item) @removeItem(item, true) item.destroy?() @@ -235,27 +402,14 @@ class Pane extends Model else false - # Public: Destroys all items and destroys the pane. + # Public: Destroy all items. destroyItems: -> @destroyItem(item) for item in @getItems() - # Public: Destroys all items but the active one. + # Public: Destroy all items except for the active item. destroyInactiveItems: -> @destroyItem(item) for item in @getItems() when item isnt @activeItem - destroy: -> - if @container?.isAlive() and @container.getPanes().length is 1 - @destroyItems() - else - super - - # Called by model superclass. - destroyed: -> - @container.activateNextPane() if @isActive() - item.destroy?() for item in @items.slice() - - # Public: Prompts the user to save the given item if it can be saved and is - # currently unsaved. promptToSaveItem: (item) -> return true unless item.shouldPromptToSave?() @@ -270,18 +424,23 @@ class Pane extends Model when 1 then false when 2 then true - # Public: Saves the active item. - saveActiveItem: -> - @saveItem(@activeItem) + # Public: Save the active item. + saveActiveItem: (nextAction) -> + @saveItem(@getActiveItem(), nextAction) - # Public: Saves the active item at a prompted-for location. - saveActiveItemAs: -> - @saveItemAs(@activeItem) + # Public: Prompt the user for a location and save the active item with the + # path they select. + # + # * `nextAction` (optional) {Function} which will be called after the item is + # successfully saved. + saveActiveItemAs: (nextAction) -> + @saveItemAs(@getActiveItem(), nextAction) - # Public: Saves the specified item. + # Public: Save the given item. # # * `item` The item to save. - # * `nextAction` (optional) {Function} which will be called after the item is saved. + # * `nextAction` (optional) {Function} which will be called after the item is + # successfully saved. saveItem: (item, nextAction) -> if item?.getUri?() item.save?() @@ -289,10 +448,12 @@ class Pane extends Model else @saveItemAs(item, nextAction) - # Public: Saves the given item at a prompted-for location. + # Public: Prompt the user for a location and save the active item with the + # path they select. # # * `item` The item to save. - # * `nextAction` (optional) {Function} which will be called after the item is saved. + # * `nextAction` (optional) {Function} which will be called after the item is + # successfully saved. saveItemAs: (item, nextAction) -> return unless item?.saveAs? @@ -302,17 +463,20 @@ class Pane extends Model item.saveAs(newItemPath) nextAction?() - # Public: Saves all items. + # Public: Save all items. saveItems: -> @saveItem(item) for item in @getItems() - # Public: Returns the first item that matches the given URI or undefined if + # Public: Return the first item that matches the given URI or undefined if # none exists. + # + # * `uri` {String} containing a URI. itemForUri: (uri) -> find @items, (item) -> item.getUri?() is uri - # Public: Activates the first item that matches the given URI. Returns a - # boolean indicating whether a matching item was found. + # Public: Activate the first item that matches the given URI. + # + # Returns a {Boolean} indicating whether an item matching the URI was found. activateItemForUri: (uri) -> if item = @itemForUri(uri) @activateItem(item) @@ -324,19 +488,55 @@ class Pane extends Model if @activeItem? @activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize()) - # Public: Creates a new pane to the left of the receiver. + ### + Section: Lifecycle + ### + + # Public: Determine whether the pane is active. # - # * `params` {Object} with keys - # * `items` (optional) {Array} of items with which to construct the new pane. + # Returns a {Boolean}. + isActive: -> + @container?.getActivePane() is this + + # Public: Makes this pane the *active* pane, causing it to gain focus. + activate: -> + @container?.setActivePane(this) + @emit 'activated' + @emitter.emit 'did-activate' + + # Public: Close the pane and destroy all its items. + # + # If this is the last pane, all the items will be destroyed but the pane + # itself will not be destroyed. + destroy: -> + if @container?.isAlive() and @container.getPanes().length is 1 + @destroyItems() + else + super + + # Called by model superclass. + destroyed: -> + @container.activateNextPane() if @isActive() + @emitter.emit 'did-destroy' + item.destroy?() for item in @items.slice() + + ### + Section: Splitting + ### + + # Public: Create a new pane to the left of this pane. + # + # * `params` (optional) {Object} with the following keys: + # * `items` (optional) {Array} of items to add to the new pane. # # Returns the new {Pane}. splitLeft: (params) -> @split('horizontal', 'before', params) - # Public: Creates a new pane to the right of the receiver. + # Public: Create a new pane to the right of this pane. # - # * `params` {Object} with keys: - # * `items` (optional) {Array} of items with which to construct the new pane. + # * `params` (optional) {Object} with the following keys: + # * `items` (optional) {Array} of items to add to the new pane. # # Returns the new {Pane}. splitRight: (params) -> @@ -344,8 +544,8 @@ class Pane extends Model # Public: Creates a new pane above the receiver. # - # * `params` {Object} with keys: - # * `items` (optional) {Array} of items with which to construct the new pane. + # * `params` (optional) {Object} with the following keys: + # * `items` (optional) {Array} of items to add to the new pane. # # Returns the new {Pane}. splitUp: (params) -> @@ -353,8 +553,8 @@ class Pane extends Model # Public: Creates a new pane below the receiver. # - # * `params` {Object} with keys: - # * `items` (optional) {Array} of items with which to construct the new pane. + # * `params` (optional) {Object} with the following keys: + # * `items` (optional) {Array} of items to add to the new pane. # # Returns the new {Pane}. splitDown: (params) -> diff --git a/src/project.coffee b/src/project.coffee index c0b81ddd0..bf2adf2c3 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -84,6 +84,7 @@ class Project extends Model # # * `projectPath` {String} path setPath: (projectPath) -> + projectPath = path.normalize(projectPath) if projectPath @path = projectPath @rootDirectory?.off() diff --git a/src/selection.coffee b/src/selection.coffee index efbb40592..38198fef4 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -198,18 +198,26 @@ class Selection extends Model @modifySelection => @cursor.setBufferPosition(position) # Public: Selects the text one position right of the cursor. - selectRight: -> - @modifySelection => @cursor.moveRight() + # + # * `columnCount` (optional) {Number} number of columns to select (default: 1) + selectRight: (columnCount) -> + @modifySelection => @cursor.moveRight(columnCount) # Public: Selects the text one position left of the cursor. - selectLeft: -> - @modifySelection => @cursor.moveLeft() + # + # * `columnCount` (optional) {Number} number of columns to select (default: 1) + selectLeft: (columnCount) -> + @modifySelection => @cursor.moveLeft(columnCount) # Public: Selects all the text one position above the cursor. + # + # * `rowCount` (optional) {Number} number of rows to select (default: 1) selectUp: (rowCount) -> @modifySelection => @cursor.moveUp(rowCount) # Public: Selects all the text one position below the cursor. + # + # * `rowCount` (optional) {Number} number of rows to select (default: 1) selectDown: (rowCount) -> @modifySelection => @cursor.moveDown(rowCount) @@ -629,8 +637,8 @@ class Selection extends Model # * `otherSelection` A {Selection} to check against. # # Returns a {Boolean} - intersectsWith: (otherSelection) -> - @getBufferRange().intersectsWith(otherSelection.getBufferRange()) + intersectsWith: (otherSelection, exclusive) -> + @getBufferRange().intersectsWith(otherSelection.getBufferRange(), exclusive) # Public: Combines the given selection into this selection and then destroys # the given selection. diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 89abf98d5..79dcba5f7 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -221,25 +221,18 @@ class TokenizedBuffer extends Model {tokens, ruleStack} = @grammar.tokenizeLine(line, ruleStack, row is 0) new TokenizedLine({tokens, ruleStack, tabLength, lineEnding, indentLevel, @invisibles}) - # FIXME: benogle says: These are actually buffer rows as all buffer rows are - # accounted for in @tokenizedLines - lineForScreenRow: (row) -> - @linesForScreenRows(row, row)[0] + tokenizedLineForRow: (bufferRow) -> + @tokenizedLines[bufferRow] - # FIXME: benogle says: These are actually buffer rows as all buffer rows are - # accounted for in @tokenizedLines - linesForScreenRows: (startRow, endRow) -> - @tokenizedLines[startRow..endRow] + stackForRow: (bufferRow) -> + @tokenizedLines[bufferRow]?.ruleStack - stackForRow: (row) -> - @tokenizedLines[row]?.ruleStack - - indentLevelForRow: (row) -> - line = @buffer.lineForRow(row) + indentLevelForRow: (bufferRow) -> + line = @buffer.lineForRow(bufferRow) indentLevel = 0 if line is '' - nextRow = row + 1 + nextRow = bufferRow + 1 lineCount = @getLineCount() while nextRow < lineCount nextLine = @buffer.lineForRow(nextRow) @@ -248,7 +241,7 @@ class TokenizedBuffer extends Model break nextRow++ - previousRow = row - 1 + previousRow = bufferRow - 1 while previousRow >= 0 previousLine = @buffer.lineForRow(previousRow) unless previousLine is '' @@ -373,5 +366,5 @@ class TokenizedBuffer extends Model logLines: (start=0, end=@buffer.getLastRow()) -> for row in [start..end] - line = @lineForScreenRow(row).text + line = @tokenizedLineForRow(row).text console.log row, line, line.length diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index 9da0d7b90..b4ae47318 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -84,7 +84,7 @@ class WorkspaceView extends View @panes.replaceWith(panes) @panes = panes - @subscribe @model, 'uri-opened', => @trigger 'uri-opened' + @subscribe @model.onDidOpen => @trigger 'uri-opened' @subscribe scrollbarStyle, (style) => @removeClass('scrollbars-visible-always scrollbars-visible-when-scrolling') @@ -409,4 +409,4 @@ class WorkspaceView extends View # Deprecated: Call {Workspace::getActivePaneItem} instead. getActivePaneItem: -> deprecate("Use Workspace::getActivePaneItem instead") - @model.activePaneItem + @model.getActivePaneItem() diff --git a/src/workspace.coffee b/src/workspace.coffee index 85870ef1b..97fff29e1 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -5,6 +5,7 @@ _ = require 'underscore-plus' Q = require 'q' Serializable = require 'serializable' Delegator = require 'delegato' +{Emitter} = require 'event-kit' Editor = require './editor' PaneContainer = require './pane-container' Pane = require './pane' @@ -16,17 +17,6 @@ Pane = require './pane' # editors, and manipulate panes. To add panels, you'll need to use the # {WorkspaceView} class for now until we establish APIs at the model layer. # -# ## Events -# -# ### uri-opened -# -# Extended: Emit when something has been opened. This can be anything, from an -# editor to the settings view. You can get the new item via {::getActivePaneItem} -# -# ### editor-created -# -# Extended: Emit when an editor is created (a file opened). -# # * `editor` {Editor} the new editor # module.exports = @@ -44,9 +34,11 @@ class Workspace extends Model constructor: -> super + @emitter = new Emitter @openers = [] - @subscribe @paneContainer, 'item-destroyed', @onPaneItemDestroyed + @paneContainer.onDidDestroyPaneItem(@onPaneItemDestroyed) + @registerOpener (filePath) => switch filePath when 'atom://.atom/stylesheet' @@ -83,34 +75,130 @@ class Workspace extends Model for scopeName in includedGrammarScopes ? [] addGrammar(atom.syntax.grammarForScopeName(scopeName)) - addGrammar(editor.getGrammar()) for editor in @getEditors() + addGrammar(editor.getGrammar()) for editor in @getTextEditors() _.uniq(packageNames) editorAdded: (editor) -> @emit 'editor-created', editor - # Public: Register a function to be called for every current and future - # {Editor} in the workspace. + ### + Section: Event Subscription + ### + + # Extended: Invoke the given callback when a pane is added to the workspace. # - # * `callback` A {Function} with an {Editor} as its only argument. + # * `callback` {Function} to be called panes are added. + # * `event` {Object} with the following keys: + # * `pane` The added pane. # - # Returns a subscription object with an `.off` method that you can call to - # unregister the callback. + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidAddPane: (callback) -> @paneContainer.onDidAddPane(callback) + + # Extended: Invoke the given callback with all current and future panes in the + # workspace. + # + # * `callback` {Function} to be called with current and future panes. + # * `pane` A {Pane} that is present in {::getPanes} at the time of + # subscription or that is added at some later time. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observePanes: (callback) -> @paneContainer.observePanes(callback) + + # Extended: Invoke the given callback when a pane item is added to the + # workspace. + # + # * `callback` {Function} to be called panes are added. + # * `event` {Object} with the following keys: + # * `item` The added pane item. + # * `pane` {Pane} containing the added item. + # * `index` {Number} indicating the index of the added item in its pane. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidAddPaneItem: (callback) -> @paneContainer.onDidAddPaneItem(callback) + + # Extended: Invoke the given callback with all current and future panes items in + # the workspace. + # + # * `callback` {Function} to be called with current and future pane items. + # * `item` An item that is present in {::getPaneItems} at the time of + # subscription or that is added at some later time. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observePaneItems: (callback) -> @paneContainer.observePaneItems(callback) + + # Extended: Invoke the given callback when a text editor is added to the + # workspace. + # + # * `callback` {Function} to be called panes are added. + # * `event` {Object} with the following keys: + # * `textEditor` {Editor} that was added. + # * `pane` {Pane} containing the added text editor. + # * `index` {Number} indicating the index of the added text editor in its + # pane. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidAddTextEditor: (callback) -> + @onDidAddPaneItem ({item, pane, index}) -> + callback({textEditor: item, pane, index}) if item instanceof Editor + + # Essential: Invoke the given callback with all current and future text + # editors in the workspace. + # + # * `callback` {Function} to be called with current and future text editors. + # * `editor` An {Editor} that is present in {::getTextEditors} at the time + # of subscription or that is added at some later time. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + observeTextEditors: (callback) -> + callback(textEditor) for textEditor in @getTextEditors() + @onDidAddTextEditor ({textEditor}) -> callback(textEditor) + + # Essential: Invoke the given callback whenever an item is opened. Unlike + # ::onDidAddPaneItem, observers will be notified for items that are already + # present in the workspace when they are reopened. + # + # * `callback` {Function} to be called whenever an item is opened. + # * `event` {Object} with the following keys: + # * `uri` {String} representing the opened URI. Could be `undefined`. + # * `item` The opened item. + # * `pane` The pane in which the item was opened. + # * `index` The index of the opened item on its pane. + # + # Returns a {Disposable} on which `.dispose()` can be called to unsubscribe. + onDidOpen: (callback) -> + @emitter.on 'did-open', callback + eachEditor: (callback) -> + deprecate("Use Workspace::observeTextEditors instead") + callback(editor) for editor in @getEditors() @subscribe this, 'editor-created', (editor) -> callback(editor) - # Public: Get all current editors in the workspace. - # - # Returns an {Array} of {Editor}s. getEditors: -> + deprecate("Use Workspace::getTextEditors instead") + editors = [] for pane in @paneContainer.getPanes() editors.push(item) for item in pane.getItems() when item instanceof Editor editors - # Public: Open a given a URI in Atom asynchronously. + on: (eventName) -> + switch eventName + when 'editor-created' + deprecate("Use Workspace::onDidAddTextEditor or Workspace::observeTextEditors instead.") + when 'uri-opened' + deprecate("Use Workspace::onDidAddPaneItem instead.") + else + deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.") + + super + + ### + Section: Opening + ### + + # Essential: Open a given a URI in Atom asynchronously. # # * `uri` A {String} containing a URI. # * `options` (optional) {Object} @@ -137,11 +225,11 @@ class Workspace extends Model pane = @paneContainer.paneForUri(uri) if searchAllPanes pane ?= switch split when 'left' - @activePane.findLeftmostSibling() + @getActivePane().findLeftmostSibling() when 'right' - @activePane.findOrCreateRightmostSibling() + @getActivePane().findOrCreateRightmostSibling() else - @activePane + @getActivePane() @openUriInPane(uri, pane, options) @@ -195,12 +283,14 @@ class Workspace extends Model @itemOpened(item) pane.activateItem(item) pane.activate() if changeFocus + index = pane.getActiveItemIndex() @emit "uri-opened" + @emitter.emit 'did-open', {uri, pane, item, index} item .catch (error) -> console.error(error.stack ? error) - # Public: Asynchronously reopens the last-closed item's URI if it hasn't already been + # Extended: Asynchronously reopens the last-closed item's URI if it hasn't already been # reopened. # # Returns a promise that is resolved when the item is opened @@ -216,7 +306,7 @@ class Workspace extends Model if uri = @destroyedItemUris.pop() @openSync(uri) - # Public: Register an opener for a uri. + # Extended: Register an opener for a uri. # # An {Editor} will be used if no openers return a value. # @@ -232,52 +322,52 @@ class Workspace extends Model registerOpener: (opener) -> @openers.push(opener) - # Public: Unregister an opener registered with {::registerOpener}. + # Extended: Unregister an opener registered with {::registerOpener}. unregisterOpener: (opener) -> _.remove(@openers, opener) getOpeners: -> @openers - # Public: Get the active {Pane}. + ### + Section: Pane Items + ### + + # Essential: Get all pane items in the workspace. # - # Returns a {Pane}. - getActivePane: -> - @paneContainer.activePane + # Returns an {Array} of items. + getPaneItems: -> + @paneContainer.getPaneItems() - # Public: Get all {Pane}s. - # - # Returns an {Array} of {Pane}s. - getPanes: -> - @paneContainer.getPanes() - - # Public: Save all pane items. - saveAll: -> - @paneContainer.saveAll() - - # Public: Make the next pane active. - activateNextPane: -> - @paneContainer.activateNextPane() - - # Public: Make the previous pane active. - activatePreviousPane: -> - @paneContainer.activatePreviousPane() - - # Public: Get the first pane {Pane} with an item for the given URI. - # - # * `uri` {String} uri - # - # Returns a {Pane} or `undefined` if no pane exists for the given URI. - paneForUri: (uri) -> - @paneContainer.paneForUri(uri) - - # Public: Get the active {Pane}'s active item. + # Essential: Get the active {Pane}'s active item. # # Returns an pane item {Object}. getActivePaneItem: -> - @paneContainer.getActivePane().getActiveItem() + @paneContainer.getActivePaneItem() - # Public: Save the active pane item. + # Essential: Get all text editors in the workspace. + # + # Returns an {Array} of {Editor}s. + getTextEditors: -> + @getPaneItems().filter (item) -> item instanceof Editor + + # Essential: Get the active item if it is an {Editor}. + # + # Returns an {Editor} or `undefined` if the current active item is not an + # {Editor}. + getActiveTextEditor: -> + activeItem = @getActiveItem() + activeItem if activeItem instanceof Editor + + # Deprecated: + getActiveEditor: -> + @activePane?.getActiveEditor() + + # Extended: Save all pane items. + saveAll: -> + @paneContainer.saveAll() + + # Save the active pane item. # # If the active pane item currently has a URI according to the item's # `.getUri` method, calls `.save` on the item. Otherwise @@ -286,7 +376,7 @@ class Workspace extends Model saveActivePaneItem: -> @activePane?.saveActiveItem() - # Public: Prompt the user for a path and save the active pane item to it. + # Prompt the user for a path and save the active pane item to it. # # Opens a native dialog where the user selects a path on disk, then calls # `.saveAs` on the item with the selected path. This method does nothing if @@ -294,34 +384,59 @@ class Workspace extends Model saveActivePaneItemAs: -> @activePane?.saveActiveItemAs() - # Public: Destroy (close) the active pane item. + # Destroy (close) the active pane item. # # Removes the active pane item and calls the `.destroy` method on it if one is # defined. destroyActivePaneItem: -> @activePane?.destroyActiveItem() - # Public: Destroy (close) the active pane. + ### + Section: Panes + ### + + # Extended: Get all panes in the workspace. + # + # Returns an {Array} of {Pane}s. + getPanes: -> + @paneContainer.getPanes() + + # Extended: Get the active {Pane}. + # + # Returns a {Pane}. + getActivePane: -> + @paneContainer.getActivePane() + + # Extended: Make the next pane active. + activateNextPane: -> + @paneContainer.activateNextPane() + + # Extended: Make the previous pane active. + activatePreviousPane: -> + @paneContainer.activatePreviousPane() + + # Extended: Get the first pane {Pane} with an item for the given URI. + # + # * `uri` {String} uri + # + # Returns a {Pane} or `undefined` if no pane exists for the given URI. + paneForUri: (uri) -> + @paneContainer.paneForUri(uri) + + # Destroy (close) the active pane. destroyActivePane: -> @activePane?.destroy() - # Public: Get the active item if it is an {Editor}. - # - # Returns an {Editor} or `undefined` if the current active item is not an - # {Editor}. - getActiveEditor: -> - @activePane?.getActiveEditor() - - # Public: Increase the editor font size by 1px. + # Increase the editor font size by 1px. increaseFontSize: -> atom.config.set("editor.fontSize", atom.config.get("editor.fontSize") + 1) - # Public: Decrease the editor font size by 1px. + # Decrease the editor font size by 1px. decreaseFontSize: -> fontSize = atom.config.get("editor.fontSize") atom.config.set("editor.fontSize", fontSize - 1) if fontSize > 1 - # Public: Restore to a default editor font size. + # Restore to a default editor font size. resetFontSize: -> atom.config.restoreDefault("editor.fontSize")