/
githubmirror
/
brackets
Обзор
Документация
Войти
/
githubmirror
/
brackets
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/spec/Editor-test.js
2 130 строк
130 KB
Martin Zagora
fix unit tests because of new sticky flag
22 фев 2017, 04:22
22 фев 2017, 04:22
c590f02
Код
Авторство
О чём код?
/* * Copyright (c) 2012 - present Adobe Systems Incorporated. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * */ /*global describe, it, expect, beforeEach, afterEach, jasmine, spyOn */ define(function (require, exports, module) { 'use strict'; var Editor = require("editor/Editor").Editor, LanguageManager = require("language/LanguageManager"), PreferencesManager = require("preferences/PreferencesManager"), SpecRunnerUtils = require("spec/SpecRunnerUtils"); var langNames = { css: {mode: "css", langName: "CSS"}, javascript: {mode: "javascript", langName: "JavaScript"}, html: {mode: "html", langName: "HTML"}, unknown: {mode: null, langName: "Text"} }; function compareMode(expected, actual) { if (typeof actual === "string") { return actual === expected; } else if (actual === null) { return expected === null; } return actual === expected; } function expectModeAndLang(editor, lang) { expect(editor.getModeForSelection()).toSpecifyModeNamed(lang.mode); expect(editor.getLanguageForSelection().getName()).toBe(lang.langName); } describe("Editor", function () { var defaultContent = "Brackets is going to be awesome!\n"; var myDocument, myEditor; function createTestEditor(content, languageId) { // create dummy Document and Editor var mocks = SpecRunnerUtils.createMockEditor(content, languageId); myDocument = mocks.doc; myEditor = mocks.editor; } beforeEach(function () { this.addMatchers({ toSpecifyModeNamed: function (expected) { return compareMode(expected, this.actual); } }); }); afterEach(function () { if (myEditor) { SpecRunnerUtils.destroyMockEditor(myDocument); myEditor = null; myDocument = null; } }); // Helper functions for testing cursor position / selection range function fixPos(pos) { if (!("sticky" in pos)) { pos.sticky = null; } return pos; } function fixSel(sel) { fixPos(sel.start); fixPos(sel.end); if (!("reversed" in sel)) { sel.reversed = false; } return sel; } function fixSels(sels) { sels.forEach(function (sel) { fixSel(sel); }); return sels; } function expectCursorAt(pos) { var selection = myEditor.getSelection(); expect(selection.start).toEqual(selection.end); expect(fixPos(selection.start)).toEqual(fixPos(pos)); } function expectSelection(sel) { expect(fixSel(myEditor.getSelection())).toEqual(fixSel(sel)); } function expectSelections(sels) { expect(fixSels(myEditor.getSelections())).toEqual(fixSels(sels)); } describe("Editor wrapper", function () { beforeEach(function () { createTestEditor(defaultContent, ""); }); it("should initialize with content", function () { // verify editor content expect(myEditor._codeMirror.getValue()).toEqual(defaultContent); }); // FUTURE: this should really be in a Document unit test, but there's no "official" // way to create the model for a Document without manually creating an Editor, so we're // testing this here for now until we get a real central model. it("should trigger a synchronous Document change event when an edit is performed", function () { var changeFired = false; function changeHandler(event, doc, changeList) { myDocument.off("change", changeHandler); changeFired = true; expect(doc).toBe(myDocument); expect(changeList.length).toBe(1); expect(changeList[0].from.line).toEqual(0); expect(changeList[0].from.ch).toEqual(0); expect(changeList[0].to.line).toEqual(1); expect(changeList[0].to.ch).toEqual(0); expect(changeList[0].text).toEqual(["new content"]); } myDocument.on("change", changeHandler); myEditor._codeMirror.setValue("new content"); expect(changeFired).toBe(true); }); it("should send an array of multiple change records for an operation", function () { var cm = myEditor._codeMirror, changeHandler = jasmine.createSpy(); myDocument.on("change", changeHandler); cm.operation(function () { cm.replaceRange("inserted", {line: 1, ch: 0}); cm.replaceRange("", {line: 0, ch: 0}, {line: 0, ch: 4}); }); expect(changeHandler.callCount).toBe(1); var args = changeHandler.mostRecentCall.args; expect(args[1]).toBe(myDocument); expect(args[2][0].text).toEqual(["inserted"]); expect(args[2][0].from.line).toEqual(1); expect(args[2][0].from.ch).toEqual(0); expect(args[2][0].to.line).toEqual(1); expect(args[2][0].to.ch).toEqual(0); expect(args[2][1].text).toEqual([""]); expect(args[2][1].from.line).toEqual(0); expect(args[2][1].from.ch).toEqual(0); expect(args[2][1].to.line).toEqual(0); expect(args[2][1].to.ch).toEqual(4); }); it("should set mode based on Document language", function () { createTestEditor(defaultContent, "html"); var htmlLanguage = LanguageManager.getLanguage("html"); expect(myEditor.getModeForDocument()).toBe(htmlLanguage.getMode()); }); }); describe("Focus", function () { beforeEach(function () { createTestEditor(defaultContent, ""); }); it("should not have focus until explicitly set", function () { expect(myEditor.hasFocus()).toBe(false); }); it("should be able to detect when it has focus", function () { /* * @note: This test really just ensures that * calling the editor's focus method * will call the codeMirror focus method * This is due to the fact that focusing the editor * may not actually focus the editor if the app * doesn't have the keyboard focus which changed * with cef 2171 * @todo: This will need to be replaced with * checks to see that the editor is the "active" element * rather than have the input focus after focusing * the editor * @see: https://github.com/adobe/brackets/issues/9972 */ spyOn(myEditor._codeMirror, "focus").andCallThrough(); myEditor.focus(); expect(myEditor._codeMirror.focus).toHaveBeenCalled(); }); }); describe("getModeForSelection()", function () { var jsContent = "var foo;"; var htmlContent = "<html><head>\n" + " <script>\n" + " var bar;\n" + " </script>\n" + "</head><body>\n" + " <p>Hello</p>\n" + "</body></html>"; it("should get mode in homogeneous file", function () { createTestEditor(jsContent, langNames.javascript.mode); // Mode at point myEditor.setCursorPos(0, 0); // first char in text expectModeAndLang(myEditor, langNames.javascript); myEditor.setCursorPos(0, 8); // last char in text expectModeAndLang(myEditor, langNames.javascript); myEditor.setCursorPos(0, 3); // middle of text expectModeAndLang(myEditor, langNames.javascript); // Mode for range myEditor.setSelection({line: 0, ch: 4}, {line: 0, ch: 7}); expectModeAndLang(myEditor, langNames.javascript); myEditor.setSelection({line: 0, ch: 0}, {line: 0, ch: 8}); // select all expectModeAndLang(myEditor, langNames.javascript); // Mode for multiple cursors/selections myEditor.setSelections([{start: {line: 0, ch: 0}, end: {line: 0, ch: 0}}, {start: {line: 0, ch: 5}, end: {line: 0, ch: 5}}]); expectModeAndLang(myEditor, langNames.javascript); myEditor.setSelections([{start: {line: 0, ch: 0}, end: {line: 0, ch: 3}}, {start: {line: 0, ch: 5}, end: {line: 0, ch: 7}}]); expectModeAndLang(myEditor, langNames.javascript); }); it("should get mode in HTML file", function () { createTestEditor(htmlContent, "html"); // Mode at point myEditor.setCursorPos(0, 0); // first char in text expectModeAndLang(myEditor, langNames.html); myEditor.setCursorPos(6, 14); // last char in text expectModeAndLang(myEditor, langNames.html); myEditor.setCursorPos(5, 7); // middle of text - html expectModeAndLang(myEditor, langNames.html); myEditor.setCursorPos(2, 7); // middle of text - js expectModeAndLang(myEditor, langNames.javascript); // Mode for range - homogeneous mode myEditor.setSelection({line: 5, ch: 2}, {line: 5, ch: 14}); expectModeAndLang(myEditor, langNames.html); myEditor.setSelection({line: 5, ch: 0}, {line: 6, ch: 0}); // whole line expectModeAndLang(myEditor, langNames.html); myEditor.setSelection({line: 2, ch: 4}, {line: 2, ch: 12}); expectModeAndLang(myEditor, langNames.javascript); myEditor.setSelection({line: 2, ch: 0}, {line: 3, ch: 0}); // whole line expectModeAndLang(myEditor, langNames.javascript); // Mode for multiple cursors/selections - homogeneous mode myEditor.setSelections([{start: {line: 2, ch: 0}, end: {line: 2, ch: 0}}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}}]); expectModeAndLang(myEditor, langNames.javascript); myEditor.setSelections([{start: {line: 2, ch: 0}, end: {line: 2, ch: 2}}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 6}}]); expectModeAndLang(myEditor, langNames.javascript); myEditor.setSelections([{start: {line: 0, ch: 0}, end: {line: 0, ch: 0}}, {start: {line: 5, ch: 7}, end: {line: 5, ch: 7}}, {start: {line: 6, ch: 14}, end: {line: 6, ch: 14}}]); expectModeAndLang(myEditor, langNames.html); myEditor.setSelections([{start: {line: 0, ch: 0}, end: {line: 0, ch: 2}}, {start: {line: 5, ch: 7}, end: {line: 5, ch: 9}}, {start: {line: 6, ch: 12}, end: {line: 6, ch: 14}}]); expectModeAndLang(myEditor, langNames.html); // Mode for range - mix of modes myEditor.setSelection({line: 2, ch: 4}, {line: 3, ch: 7}); expectModeAndLang(myEditor, langNames.unknown); // Mode for multiple cursors/selections - mix of modes myEditor.setSelections([{start: {line: 0, ch: 0}, end: {line: 0, ch: 0}}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}}, {start: {line: 6, ch: 14}, end: {line: 6, ch: 14}}]); expectModeAndLang(myEditor, langNames.unknown); myEditor.setSelections([{start: {line: 0, ch: 0}, end: {line: 0, ch: 2}}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 7}}, {start: {line: 6, ch: 12}, end: {line: 6, ch: 14}}]); expectModeAndLang(myEditor, langNames.unknown); myEditor.setSelections([{start: {line: 0, ch: 0}, end: {line: 2, ch: 0}}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 7}}, {start: {line: 6, ch: 12}, end: {line: 6, ch: 14}}]); expectModeAndLang(myEditor, langNames.unknown); myEditor.setSelections([{start: {line: 0, ch: 0}, end: {line: 0, ch: 2}}, {start: {line: 2, ch: 4}, end: {line: 5, ch: 3}}, {start: {line: 6, ch: 12}, end: {line: 6, ch: 14}}]); expectModeAndLang(myEditor, langNames.unknown); // Mode for range - mix of modes where start & endpoints are same mode // Known limitation of getModeForSelection() that it does not spot where the mode // differs in mid-selection myEditor.setSelection({line: 0, ch: 0}, {line: 6, ch: 14}); // select all expectModeAndLang(myEditor, langNames.html); }); }); describe("Column/ch conversion", function () { it("should get mode in HTML file", function () { var content = "foo () {\n" + " one;\n" + "\ttwo;\n" + "}\n" + "\n" + "\tA\tB"; createTestEditor(content, "javascript"); // Tab size 4 expect(myEditor.getColOffset({line: 1, ch: 0})).toBe(0); expect(myEditor.getColOffset({line: 1, ch: 1})).toBe(1); expect(myEditor.getColOffset({line: 1, ch: 2})).toBe(2); expect(myEditor.getColOffset({line: 1, ch: 3})).toBe(3); expect(myEditor.getColOffset({line: 1, ch: 4})).toBe(4); expect(myEditor.getColOffset({line: 1, ch: 5})).toBe(5); expect(myEditor.getColOffset({line: 2, ch: 0})).toBe(0); expect(myEditor.getColOffset({line: 2, ch: 1})).toBe(4); expect(myEditor.getColOffset({line: 2, ch: 2})).toBe(5); expect(myEditor.getColOffset({line: 4, ch: 0})).toBe(0); expect(myEditor.getColOffset({line: 5, ch: 1})).toBe(4); expect(myEditor.getColOffset({line: 5, ch: 2})).toBe(5); expect(myEditor.getColOffset({line: 5, ch: 3})).toBe(8); expect(myEditor.getColOffset({line: 5, ch: 4})).toBe(9); // Tab size 2 var defaultTabSize = Editor.getTabSize(); expect(defaultTabSize).toBe(4); Editor.setTabSize(2); expect(myEditor.getColOffset({line: 1, ch: 0})).toBe(0); // first line is all spaces: should be unchanged expect(myEditor.getColOffset({line: 1, ch: 1})).toBe(1); expect(myEditor.getColOffset({line: 1, ch: 2})).toBe(2); expect(myEditor.getColOffset({line: 1, ch: 3})).toBe(3); expect(myEditor.getColOffset({line: 1, ch: 4})).toBe(4); expect(myEditor.getColOffset({line: 1, ch: 5})).toBe(5); expect(myEditor.getColOffset({line: 2, ch: 0})).toBe(0); // but line with a tab shows different behavior expect(myEditor.getColOffset({line: 2, ch: 1})).toBe(2); expect(myEditor.getColOffset({line: 2, ch: 2})).toBe(3); expect(myEditor.getColOffset({line: 4, ch: 0})).toBe(0); expect(myEditor.getColOffset({line: 5, ch: 1})).toBe(2); // same here expect(myEditor.getColOffset({line: 5, ch: 2})).toBe(3); expect(myEditor.getColOffset({line: 5, ch: 3})).toBe(4); expect(myEditor.getColOffset({line: 5, ch: 4})).toBe(5); // Restore default Editor.setTabSize(defaultTabSize); }); }); function makeDummyLines(num) { var content = [], i; for (i = 0; i < num; i++) { content.push("this is line " + i); } return content; } describe("Selections", function () { beforeEach(function () { createTestEditor(makeDummyLines(10).join("\n"), "unknown"); }); describe("hasSelection", function () { it("should return false for a single cursor", function () { myEditor._codeMirror.setCursor(0, 2); expect(myEditor.hasSelection()).toBe(false); }); it("should return true for a single selection", function () { myEditor._codeMirror.setSelection({line: 0, ch: 1}, {line: 0, ch: 5}); expect(myEditor.hasSelection()).toBe(true); }); it("should return false for multiple cursors", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 2); expect(myEditor.hasSelection()).toBe(false); }); it("should return true for multiple selections", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 2); expect(myEditor.hasSelection()).toBe(true); }); it("should return true for mixed cursors and selections", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 2); expect(myEditor.hasSelection()).toBe(true); }); }); describe("getCursorPos", function () { it("should return a single cursor", function () { myEditor._codeMirror.setCursor(0, 2); expect(myEditor.getCursorPos().line).toEqual(0); expect(myEditor.getCursorPos().ch).toEqual(2); expect(myEditor.getCursorPos(false, "start").line).toEqual(0); expect(myEditor.getCursorPos(false, "start").ch).toEqual(2); expect(myEditor.getCursorPos(false, "anchor").line).toEqual(0); expect(myEditor.getCursorPos(false, "anchor").ch).toEqual(2); expect(myEditor.getCursorPos(false, "end").line).toEqual(0); expect(myEditor.getCursorPos(false, "end").ch).toEqual(2); expect(myEditor.getCursorPos(false, "head").line).toEqual(0); expect(myEditor.getCursorPos(false, "head").ch).toEqual(2); }); it("should return the correct ends of a single selection", function () { myEditor._codeMirror.setSelection({line: 0, ch: 1}, {line: 0, ch: 5}); expect(myEditor.getCursorPos().line).toEqual(0); expect(myEditor.getCursorPos().ch).toEqual(5); expect(myEditor.getCursorPos(false, "start").line).toEqual(0); expect(myEditor.getCursorPos(false, "start").ch).toEqual(1); expect(myEditor.getCursorPos(false, "anchor").line).toEqual(0); expect(myEditor.getCursorPos(false, "anchor").ch).toEqual(1); expect(myEditor.getCursorPos(false, "end").line).toEqual(0); expect(myEditor.getCursorPos(false, "end").ch).toEqual(5); expect(myEditor.getCursorPos(false, "head").line).toEqual(0); expect(myEditor.getCursorPos(false, "head").ch).toEqual(5); }); it("should return the default primary cursor in a multiple cursor selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 2); expect(myEditor.getCursorPos().line).toEqual(2); expect(myEditor.getCursorPos().ch).toEqual(1); expect(myEditor.getCursorPos(false, "start").line).toEqual(2); expect(myEditor.getCursorPos(false, "start").ch).toEqual(1); expect(myEditor.getCursorPos(false, "anchor").line).toEqual(2); expect(myEditor.getCursorPos(false, "anchor").ch).toEqual(1); expect(myEditor.getCursorPos(false, "end").line).toEqual(2); expect(myEditor.getCursorPos(false, "end").ch).toEqual(1); expect(myEditor.getCursorPos(false, "head").line).toEqual(2); expect(myEditor.getCursorPos(false, "head").ch).toEqual(1); }); it("should return the specific primary cursor in a multiple cursor selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 1); expect(myEditor.getCursorPos().line).toEqual(1); expect(myEditor.getCursorPos().ch).toEqual(1); expect(myEditor.getCursorPos(false, "start").line).toEqual(1); expect(myEditor.getCursorPos(false, "start").ch).toEqual(1); expect(myEditor.getCursorPos(false, "anchor").line).toEqual(1); expect(myEditor.getCursorPos(false, "anchor").ch).toEqual(1); expect(myEditor.getCursorPos(false, "end").line).toEqual(1); expect(myEditor.getCursorPos(false, "end").ch).toEqual(1); expect(myEditor.getCursorPos(false, "head").line).toEqual(1); expect(myEditor.getCursorPos(false, "head").ch).toEqual(1); }); it("should return the correct ends of the default primary selection in a multiple selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 2); expect(myEditor.getCursorPos().line).toEqual(2); expect(myEditor.getCursorPos().ch).toEqual(4); expect(myEditor.getCursorPos(false, "start").line).toEqual(2); expect(myEditor.getCursorPos(false, "start").ch).toEqual(1); expect(myEditor.getCursorPos(false, "anchor").line).toEqual(2); expect(myEditor.getCursorPos(false, "anchor").ch).toEqual(1); expect(myEditor.getCursorPos(false, "end").line).toEqual(2); expect(myEditor.getCursorPos(false, "end").ch).toEqual(4); expect(myEditor.getCursorPos(false, "head").line).toEqual(2); expect(myEditor.getCursorPos(false, "head").ch).toEqual(4); }); it("should return the correct ends of a specific primary selection in a multiple selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 1); expect(myEditor.getCursorPos().line).toEqual(1); expect(myEditor.getCursorPos().ch).toEqual(4); expect(myEditor.getCursorPos(false, "start").line).toEqual(1); expect(myEditor.getCursorPos(false, "start").ch).toEqual(1); expect(myEditor.getCursorPos(false, "anchor").line).toEqual(1); expect(myEditor.getCursorPos(false, "anchor").ch).toEqual(1); expect(myEditor.getCursorPos(false, "end").line).toEqual(1); expect(myEditor.getCursorPos(false, "end").ch).toEqual(4); expect(myEditor.getCursorPos(false, "head").line).toEqual(1); expect(myEditor.getCursorPos(false, "head").ch).toEqual(4); }); }); describe("setCursorPos", function () { it("should replace an existing single cursor", function () { myEditor._codeMirror.setCursor(0, 2); myEditor.setCursorPos(1, 3); expect(myEditor.getCursorPos().line).toEqual(1); expect(myEditor.getCursorPos().ch).toEqual(3); }); it("should replace an existing single selection", function () { myEditor._codeMirror.setSelection({line: 0, ch: 1}, {line: 0, ch: 5}); myEditor.setCursorPos(1, 3); expect(myEditor.getCursorPos().line).toEqual(1); expect(myEditor.getCursorPos().ch).toEqual(3); }); it("should replace existing multiple cursors", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 2); myEditor.setCursorPos(1, 3); expect(myEditor.getCursorPos().line).toEqual(1); expect(myEditor.getCursorPos().ch).toEqual(3); }); it("should replace existing multiple selections", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 2); myEditor.setCursorPos(1, 3); expect(myEditor.getCursorPos().line).toEqual(1); expect(myEditor.getCursorPos().ch).toEqual(3); }); }); describe("getSelection", function () { it("should return a single cursor", function () { myEditor._codeMirror.setCursor(0, 2); expectSelection({start: {line: 0, ch: 2}, end: {line: 0, ch: 2}, reversed: false}); }); it("should return a single selection", function () { myEditor._codeMirror.setSelection({line: 0, ch: 1}, {line: 0, ch: 5}); expectSelection({start: {line: 0, ch: 1}, end: {line: 0, ch: 5}, reversed: false}); }); it("should return a multiline selection", function () { myEditor._codeMirror.setSelection({line: 0, ch: 5}, {line: 1, ch: 3}); expectSelection({start: {line: 0, ch: 5}, end: {line: 1, ch: 3}, reversed: false}); }); it("should return a single selection in the proper order when reversed", function () { myEditor._codeMirror.setSelection({line: 0, ch: 5}, {line: 0, ch: 1}); expectSelection({start: {line: 0, ch: 1}, end: {line: 0, ch: 5}, reversed: true}); }); it("should return a multiline selection in the proper order when reversed", function () { myEditor._codeMirror.setSelection({line: 1, ch: 3}, {line: 0, ch: 5}); expectSelection({start: {line: 0, ch: 5}, end: {line: 1, ch: 3}, reversed: true}); }); it("should return the default primary cursor in a multiple cursor selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 2); expectSelection({start: {line: 2, ch: 1}, end: {line: 2, ch: 1}, reversed: false}); }); it("should return the specific primary cursor in a multiple cursor selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 1); expectSelection({start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, reversed: false}); }); it("should return the default primary selection in a multiple selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 2); expectSelection({start: {line: 2, ch: 1}, end: {line: 2, ch: 4}, reversed: false}); }); it("should return the default primary selection in the proper order when reversed", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 4}, head: {line: 2, ch: 1}} ], 2); expectSelection({start: {line: 2, ch: 1}, end: {line: 2, ch: 4}, reversed: true}); }); it("should return the specific primary selection in a multiple selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 1); expectSelection({start: {line: 1, ch: 1}, end: {line: 1, ch: 4}, reversed: false}); }); it("should return the specific primary selection in the proper order when reversed", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 4}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 1); expectSelection({start: {line: 1, ch: 1}, end: {line: 1, ch: 4}, reversed: true}); }); }); describe("getSelections", function () { it("should return a single cursor", function () { myEditor._codeMirror.setCursor(0, 2); expectSelections([{start: {line: 0, ch: 2}, end: {line: 0, ch: 2}, reversed: false, primary: true}]); }); it("should return a single selection", function () { myEditor._codeMirror.setSelection({line: 0, ch: 1}, {line: 0, ch: 5}); expectSelections([{start: {line: 0, ch: 1}, end: {line: 0, ch: 5}, reversed: false, primary: true}]); }); it("should properly reverse a single selection whose head is before its anchor", function () { myEditor._codeMirror.setSelection({line: 0, ch: 5}, {line: 0, ch: 1}); expectSelections([{start: {line: 0, ch: 1}, end: {line: 0, ch: 5}, reversed: true, primary: true}]); }); it("should return multiple cursors", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 2); expectSelections([{start: {line: 0, ch: 1}, end: {line: 0, ch: 1}, reversed: false, primary: false}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, reversed: false, primary: false}, {start: {line: 2, ch: 1}, end: {line: 2, ch: 1}, reversed: false, primary: true} ]); }); it("should return a multiple selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 2); expectSelections([{start: {line: 0, ch: 1}, end: {line: 0, ch: 4}, reversed: false, primary: false}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 4}, reversed: false, primary: false}, {start: {line: 2, ch: 1}, end: {line: 2, ch: 4}, reversed: false, primary: true} ]); }); it("should properly reverse selections whose heads are before their anchors in a multiple selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 4}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 4}, head: {line: 2, ch: 1}} ], 2); expectSelections([{start: {line: 0, ch: 1}, end: {line: 0, ch: 4}, reversed: true, primary: false}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 4}, reversed: false, primary: false}, {start: {line: 2, ch: 1}, end: {line: 2, ch: 4}, reversed: true, primary: true} ]); }); it("should properly reverse multiline selections whose heads are before their anchors in a multiple selection", function () { myEditor._codeMirror.setSelections([{anchor: {line: 1, ch: 3}, head: {line: 0, ch: 5}}, {anchor: {line: 4, ch: 4}, head: {line: 3, ch: 1}} ], 1); expectSelections([{start: {line: 0, ch: 5}, end: {line: 1, ch: 3}, reversed: true, primary: false}, {start: {line: 3, ch: 1}, end: {line: 4, ch: 4}, reversed: true, primary: true} ]); }); }); describe("getSelectedText", function () { it("should return empty string for a cursor", function () { myEditor._codeMirror.setCursor(0, 2); expect(myEditor.getSelectedText()).toEqual(""); expect(myEditor.getSelectedText(true)).toEqual(""); }); it("should return the contents of a single selection", function () { myEditor._codeMirror.setSelection({line: 0, ch: 8}, {line: 0, ch: 14}); expect(myEditor.getSelectedText()).toEqual("line 0"); expect(myEditor.getSelectedText(true)).toEqual("line 0"); }); it("should return the primary selection by default, but concatenate contents if allSelections is true", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 8}, head: {line: 0, ch: 14}}, {anchor: {line: 1, ch: 8}, head: {line: 1, ch: 14}}, {anchor: {line: 2, ch: 8}, head: {line: 2, ch: 14}} ], 2); expect(myEditor.getSelectedText()).toEqual("line 2"); expect(myEditor.getSelectedText(true)).toEqual("line 0\nline 1\nline 2"); }); it("should return a primary selection other than the last", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 8}, head: {line: 0, ch: 14}}, {anchor: {line: 1, ch: 8}, head: {line: 1, ch: 14}}, {anchor: {line: 2, ch: 8}, head: {line: 2, ch: 14}} ], 1); expect(myEditor.getSelectedText()).toEqual("line 1"); expect(myEditor.getSelectedText(true)).toEqual("line 0\nline 1\nline 2"); }); it("should return the contents of a multiple selection when some selections are reversed", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 14}, head: {line: 0, ch: 8}}, {anchor: {line: 1, ch: 8}, head: {line: 1, ch: 14}}, {anchor: {line: 2, ch: 14}, head: {line: 2, ch: 8}} ], 2); expect(myEditor.getSelectedText()).toEqual("line 2"); expect(myEditor.getSelectedText(true)).toEqual("line 0\nline 1\nline 2"); }); }); describe("setSelection", function () { it("should replace an existing single cursor", function () { myEditor._codeMirror.setCursor(0, 2); myEditor.setSelection({line: 1, ch: 3}, {line: 2, ch: 5}); expectSelection({start: {line: 1, ch: 3}, end: {line: 2, ch: 5}, reversed: false}); }); it("should replace an existing single selection", function () { myEditor._codeMirror.setSelection({line: 0, ch: 1}, {line: 0, ch: 5}); myEditor.setSelection({line: 1, ch: 3}, {line: 2, ch: 5}); expectSelection({start: {line: 1, ch: 3}, end: {line: 2, ch: 5}, reversed: false}); }); it("should allow implicit end", function () { myEditor.setSelection({line: 1, ch: 3}); expectSelection({start: {line: 1, ch: 3}, end: {line: 1, ch: 3}, reversed: false}); }); it("should replace existing multiple cursors", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 2); myEditor.setSelection({line: 1, ch: 3}, {line: 2, ch: 5}); expectSelection({start: {line: 1, ch: 3}, end: {line: 2, ch: 5}, reversed: false}); }); it("should replace existing multiple selections", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 2); myEditor.setSelection({line: 1, ch: 3}, {line: 2, ch: 5}); expectSelection({start: {line: 1, ch: 3}, end: {line: 2, ch: 5}, reversed: false}); }); }); describe("setSelections", function () { it("should replace an existing single cursor", function () { myEditor._codeMirror.setCursor(0, 2); myEditor.setSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}}]); expectSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}, reversed: false, primary: false}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false, primary: true}]); expectSelection({start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false}); }); it("should replace an existing single selection", function () { myEditor._codeMirror.setSelection({line: 0, ch: 1}, {line: 0, ch: 5}); myEditor.setSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}}]); expectSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}, reversed: false, primary: false}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false, primary: true}]); expectSelection({start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false}); }); it("should replace existing multiple cursors", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 1}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 1}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 1}} ], 2); myEditor.setSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}}]); expectSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}, reversed: false, primary: false}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false, primary: true}]); expectSelection({start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false}); }); it("should replace existing multiple selections", function () { myEditor._codeMirror.setSelections([{anchor: {line: 0, ch: 1}, head: {line: 0, ch: 4}}, {anchor: {line: 1, ch: 1}, head: {line: 1, ch: 4}}, {anchor: {line: 2, ch: 1}, head: {line: 2, ch: 4}} ], 2); myEditor.setSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}}]); expectSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}, reversed: false, primary: false}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false, primary: true}]); expectSelection({start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false}); }); it("should specify non-default primary selection", function () { myEditor.setSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}, primary: true}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}}]); expectSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}, reversed: false, primary: true}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false, primary: false}]); expectSelection({start: {line: 0, ch: 1}, end: {line: 1, ch: 3}, reversed: false}); }); it("should sort and merge overlapping selections", function () { myEditor.setSelections([{start: {line: 2, ch: 4}, end: {line: 3, ch: 0}}, {start: {line: 2, ch: 3}, end: {line: 2, ch: 6}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 4}}]); expectSelections([{start: {line: 1, ch: 1}, end: {line: 1, ch: 4}, reversed: false, primary: true}, {start: {line: 2, ch: 3}, end: {line: 3, ch: 0}, reversed: false, primary: false}]); expectSelection({start: {line: 1, ch: 1}, end: {line: 1, ch: 4}, reversed: false}); }); it("should properly set reversed selections", function () { myEditor.setSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}, reversed: true}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}}]); expectSelections([{start: {line: 0, ch: 1}, end: {line: 1, ch: 3}, reversed: true, primary: false}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 5}, reversed: false, primary: true}]); }); }); describe("convertToLineSelections", function () { it("should expand a cursor to a line selection, keeping original selection for tracking", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(1); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(1); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(1); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); }); it("should expand a range within a line to a line selection, keeping original selection for tracking", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 0, ch: 8}}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(1); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(1); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(1); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); }); it("should expand a range that spans multiple lines to a line selection", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 1, ch: 8}}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(1); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(2); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(1); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); }); it("should preserve the reversed attribute on a tracked range", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 0, ch: 8}, reversed: true}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(1); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(1); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(1); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); }); it("should keep a line selection the same if expandEndAtStartOfLine is not set", function () { var origSelections = [{start: {line: 0, ch: 0}, end: {line: 1, ch: 0}}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(1); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(1); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(1); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); }); it("should expand a line selection if expandEndAtStartOfLine is set", function () { var origSelections = [{start: {line: 0, ch: 0}, end: {line: 1, ch: 0}}], result = myEditor.convertToLineSelections(origSelections, {expandEndAtStartOfLine: true}); expect(result.length).toBe(1); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(2); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(1); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); }); it("should process a discontiguous mix of cursor, range, and line selections separately, preserving the primary tracked selection", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 8}, primary: true}, {start: {line: 4, ch: 4}, end: {line: 5, ch: 8}}, {start: {line: 7, ch: 0}, end: {line: 8, ch: 0}}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(4); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(1); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(1); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); expect(result[1].selectionForEdit.start.line).toEqual(2); expect(result[1].selectionForEdit.start.ch).toEqual(0); expect(result[1].selectionForEdit.end.line).toEqual(3); expect(result[1].selectionForEdit.end.ch).toEqual(0); expect(result[1].selectionsToTrack.length).toBe(1); expect(result[1].selectionsToTrack[0]).toEqual(origSelections[1]); expect(result[2].selectionForEdit.start.line).toEqual(4); expect(result[2].selectionForEdit.start.ch).toEqual(0); expect(result[2].selectionForEdit.end.line).toEqual(6); expect(result[2].selectionForEdit.end.ch).toEqual(0); expect(result[2].selectionsToTrack.length).toBe(1); expect(result[2].selectionsToTrack[0]).toEqual(origSelections[2]); expect(result[3].selectionForEdit.start.line).toEqual(7); expect(result[3].selectionForEdit.start.ch).toEqual(0); expect(result[3].selectionForEdit.end.line).toEqual(8); expect(result[3].selectionForEdit.end.ch).toEqual(0); // not expanded since expandEndAtStartOfLine is false expect(result[3].selectionsToTrack.length).toBe(1); expect(result[3].selectionsToTrack[0]).toEqual(origSelections[3]); }); it("should merge selections on same line, preserving primary/reversed info on subsumed selections", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}}, {start: {line: 0, ch: 8}, end: {line: 1, ch: 8}, primary: true, reversed: true}, {start: {line: 4, ch: 0}, end: {line: 5, ch: 0}}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(2); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(2); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(2); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); expect(result[0].selectionsToTrack[1]).toEqual(origSelections[1]); expect(result[1].selectionForEdit.start.line).toEqual(4); expect(result[1].selectionForEdit.start.ch).toEqual(0); expect(result[1].selectionForEdit.end.line).toEqual(5); expect(result[1].selectionForEdit.end.ch).toEqual(0); expect(result[1].selectionsToTrack.length).toBe(1); expect(result[1].selectionsToTrack[0]).toEqual(origSelections[2]); }); it("should merge selections on adjacent lines by default", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}}, {start: {line: 1, ch: 4}, end: {line: 1, ch: 4}, primary: true}, {start: {line: 4, ch: 0}, end: {line: 5, ch: 0}}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(2); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(2); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(2); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); expect(result[0].selectionsToTrack[1]).toEqual(origSelections[1]); expect(result[1].selectionForEdit.start.line).toEqual(4); expect(result[1].selectionForEdit.start.ch).toEqual(0); expect(result[1].selectionForEdit.end.line).toEqual(5); expect(result[1].selectionForEdit.end.ch).toEqual(0); expect(result[1].selectionsToTrack.length).toBe(1); expect(result[1].selectionsToTrack[0]).toEqual(origSelections[2]); }); it("should merge adjacent multiline selections where the first selection ends on the same line where the second selection starts", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 1, ch: 4}, primary: true}, {start: {line: 1, ch: 8}, end: {line: 2, ch: 8}}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(1); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(3); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(2); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); expect(result[0].selectionsToTrack[1]).toEqual(origSelections[1]); }); it("should not merge selections on adjacent lines if mergeAdjacent is false", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}}, {start: {line: 1, ch: 4}, end: {line: 1, ch: 4}, primary: true}, {start: {line: 4, ch: 0}, end: {line: 5, ch: 0}}], result = myEditor.convertToLineSelections(origSelections, {mergeAdjacent: false}); expect(result.length).toBe(3); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(1); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(1); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); expect(result[1].selectionForEdit.start.line).toEqual(1); expect(result[1].selectionForEdit.start.ch).toEqual(0); expect(result[1].selectionForEdit.end.line).toEqual(2); expect(result[1].selectionForEdit.end.ch).toEqual(0); expect(result[1].selectionsToTrack.length).toBe(1); expect(result[1].selectionsToTrack[0]).toEqual(origSelections[1]); expect(result[2].selectionForEdit.start.line).toEqual(4); expect(result[2].selectionForEdit.start.ch).toEqual(0); expect(result[2].selectionForEdit.end.line).toEqual(5); expect(result[2].selectionForEdit.end.ch).toEqual(0); // not expanded since expandEndAtStartOfLine not set expect(result[2].selectionsToTrack.length).toBe(1); expect(result[2].selectionsToTrack[0]).toEqual(origSelections[2]); }); it("should merge line selections separated by a one-line gap by default if expandEndAtStartOfLine is true", function () { var origSelections = [{start: {line: 0, ch: 0}, end: {line: 1, ch: 0}, primary: true}, {start: {line: 2, ch: 0}, end: {line: 3, ch: 0}}], result = myEditor.convertToLineSelections(origSelections, {expandEndAtStartOfLine: true}); expect(result.length).toBe(1); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(4); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(2); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); expect(result[0].selectionsToTrack[1]).toEqual(origSelections[1]); }); it("should not merge line selections separated by a one-line gap if expandEndAtStartOfLine is true but mergeAdjacent is false", function () { // Note that in this case, if you were to actually set this as a multiple selection, CodeMirror would // merge the adjacent selections at that point. But while processing an edit you might not want that. var origSelections = [{start: {line: 0, ch: 0}, end: {line: 1, ch: 0}, primary: true}, {start: {line: 2, ch: 0}, end: {line: 3, ch: 0}}], result = myEditor.convertToLineSelections(origSelections, {expandEndAtStartOfLine: true, mergeAdjacent: false}); expect(result.length).toBe(2); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(2); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(1); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); expect(result[1].selectionForEdit.start.line).toEqual(2); expect(result[1].selectionForEdit.start.ch).toEqual(0); expect(result[1].selectionForEdit.end.line).toEqual(4); expect(result[1].selectionForEdit.end.ch).toEqual(0); expect(result[1].selectionsToTrack.length).toBe(1); expect(result[1].selectionsToTrack[0]).toEqual(origSelections[1]); }); it("should merge multiple adjacent/overlapping selections together", function () { var origSelections = [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}}, {start: {line: 1, ch: 4}, end: {line: 2, ch: 4}, primary: true, reversed: true}, {start: {line: 2, ch: 8}, end: {line: 5, ch: 0}}], result = myEditor.convertToLineSelections(origSelections); expect(result.length).toBe(1); expect(result[0].selectionForEdit.start.line).toEqual(0); expect(result[0].selectionForEdit.start.ch).toEqual(0); expect(result[0].selectionForEdit.end.line).toEqual(5); expect(result[0].selectionForEdit.end.ch).toEqual(0); expect(result[0].selectionsToTrack.length).toBe(3); expect(result[0].selectionsToTrack[0]).toEqual(origSelections[0]); expect(result[0].selectionsToTrack[1]).toEqual(origSelections[1]); expect(result[0].selectionsToTrack[2]).toEqual(origSelections[2]); }); }); }); describe("Soft tabs", function () { beforeEach(function () { // Configure the editor's CM instance for 4-space soft tabs, regardless of prefs. createTestEditor("", "unknown"); var instance = myEditor._codeMirror; instance.setOption("indentWithTabs", false); instance.setOption("indentUnit", 4); }); function checkSoftTab(sel, dir, command, expectedSel, expectedText) { expectedText = expectedText || myEditor.document.getText(); if (Array.isArray(sel)) { myEditor.setSelections(sel); } else { myEditor.setCursorPos(sel); } myEditor._handleSoftTabNavigation(dir, command); if (Array.isArray(expectedSel)) { expectSelections(expectedSel); } else { expectCursorAt(expectedSel); } expect(myEditor.document.getText()).toEqual(expectedText); } it("should move left by a soft tab if cursor is immediately after 1 indent level worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 4}, -1, "moveH", {line: 0, ch: 0}); }); it("should backspace by a soft tab if cursor is immediately after 1 indent level worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 4}, -1, "deleteH", {line: 0, ch: 0}, "content"); }); it("should move right by a soft tab if cursor is immediately before 1 indent level worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 0}, 1, "moveH", {line: 0, ch: 4}); }); it("should delete right by a soft tab if cursor is immediately before 1 indent level worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 0}, 1, "deleteH", {line: 0, ch: 0}, "content"); }); it("should move left by a soft tab if cursor is immediately after 2 indent levels worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 8}, -1, "moveH", {line: 0, ch: 4}); }); it("should backspace by a soft tab if cursor is immediately after 2 indent levels worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 8}, -1, "deleteH", {line: 0, ch: 4}, " content"); }); it("should move right by a soft tab if cursor is immediately before 2 indent levels worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 0}, 1, "moveH", {line: 0, ch: 4}); }); it("should delete right by a soft tab if cursor is immediately before 2 indent levels worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 0}, 1, "deleteH", {line: 0, ch: 0}, " content"); }); it("should move left by a soft tab if cursor is exactly between 2 indent levels worth of spaces", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 4}, -1, "moveH", {line: 0, ch: 0}); }); it("should backspace by a soft tab if cursor is exactly between 2 indent levels worth of spaces", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 4}, -1, "deleteH", {line: 0, ch: 0}, " content"); }); it("should move right by a soft tab if cursor is exactly between 2 indent levels worth of spaces", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 4}, 1, "moveH", {line: 0, ch: 8}); }); it("should delete right by a soft tab if cursor is exactly between 2 indent levels worth of spaces", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 4}, 1, "deleteH", {line: 0, ch: 4}, " content"); }); it("should move left to tab stop if cursor is 1 char after it", function () { myEditor.document.setText(" "); checkSoftTab({line: 0, ch: 5}, -1, "moveH", {line: 0, ch: 4}); }); it("should backspace to tab stop if cursor is 1 char after it", function () { myEditor.document.setText(" "); checkSoftTab({line: 0, ch: 5}, -1, "deleteH", {line: 0, ch: 4}, " "); }); it("should move right to tab stop if cursor is 1 char before it", function () { myEditor.document.setText(" "); checkSoftTab({line: 0, ch: 3}, 1, "moveH", {line: 0, ch: 4}); }); it("should delete right to tab stop if cursor is 1 char before it", function () { myEditor.document.setText(" "); checkSoftTab({line: 0, ch: 3}, 1, "deleteH", {line: 0, ch: 3}, " "); }); it("should move left to tab stop if cursor is 2 chars after it", function () { myEditor.document.setText(" "); checkSoftTab({line: 0, ch: 6}, -1, "moveH", {line: 0, ch: 4}); }); it("should backspace to tab stop if cursor is 2 chars after it", function () { myEditor.document.setText(" "); checkSoftTab({line: 0, ch: 6}, -1, "deleteH", {line: 0, ch: 4}, " "); }); it("should move right to tab stop if cursor is 2 chars before it", function () { myEditor.document.setText(" "); checkSoftTab({line: 0, ch: 2}, 1, "moveH", {line: 0, ch: 4}); }); it("should delete right to tab stop if cursor is 2 chars before it", function () { myEditor.document.setText(" "); checkSoftTab({line: 0, ch: 2}, 1, "deleteH", {line: 0, ch: 2}, " "); }); it("should not handle soft tab if moving left after non-whitespace content", function () { myEditor.document.setText("start content"); checkSoftTab({line: 0, ch: 8}, -1, "moveH", {line: 0, ch: 7}); }); it("should not handle soft tab if moving right after non-whitespace content", function () { myEditor.document.setText("start content"); checkSoftTab({line: 0, ch: 5}, 1, "moveH", {line: 0, ch: 6}); }); it("should not handle soft tab if moving left at beginning of line", function () { myEditor.document.setText("foo\n content"); checkSoftTab({line: 1, ch: 0}, -1, "moveH", {line: 0, ch: 3}); }); it("should not handle soft tab if moving right at end of line", function () { myEditor.document.setText(" content\nfoo"); checkSoftTab({line: 0, ch: 11}, 1, "moveH", {line: 1, ch: 0}); }); it("should not handle soft tab if moving right at end of line would cause a jump past end of line", function () { myEditor.document.setText(" four "); checkSoftTab({line: 0, ch: 8}, 1, "moveH", {line: 0, ch: 9}); }); describe("with soft tabs preference off", function () { beforeEach(function () { // Disable soft tabs PreferencesManager.set("softTabs", false); }); afterEach(function () { // Re-enable soft tabs PreferencesManager.set("softTabs", true); }); it("should move left by 1 space if cursor is immediately after 1 indent level worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 4}, -1, "moveH", {line: 0, ch: 3}); }); it("should backspace by 1 space if cursor is immediately after 1 indent level worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 4}, -1, "deleteH", {line: 0, ch: 3}, " content"); }); it("should move right by 1 space if cursor is immediately before 1 indent level worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 0}, 1, "moveH", {line: 0, ch: 1}); }); it("should delete right by 1 space if cursor is immediately before 1 indent level worth of spaces at beginning of line", function () { myEditor.document.setText(" content"); checkSoftTab({line: 0, ch: 0}, 1, "deleteH", {line: 0, ch: 0}, " content"); }); }); describe("with multiple selections", function () { it("should move left over a soft tab from multiple aligned cursors", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}}, {start: {line: 1, ch: 4}, end: {line: 1, ch: 4}}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}}], -1, "moveH", [{start: {line: 0, ch: 0}, end: {line: 0, ch: 0}, primary: false, reversed: false}, {start: {line: 1, ch: 0}, end: {line: 1, ch: 0}, primary: false, reversed: false}, {start: {line: 2, ch: 0}, end: {line: 2, ch: 0}, primary: true, reversed: false}]); }); it("should move right over a soft tab from multiple aligned cursors", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 0}, end: {line: 0, ch: 0}}, {start: {line: 1, ch: 0}, end: {line: 1, ch: 0}}, {start: {line: 2, ch: 0}, end: {line: 2, ch: 0}}], 1, "moveH", [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}, primary: false, reversed: false}, {start: {line: 1, ch: 4}, end: {line: 1, ch: 4}, primary: false, reversed: false}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}, primary: true, reversed: false}]); }); it("should backspace over a soft tab from multiple aligned cursors", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}}, {start: {line: 1, ch: 4}, end: {line: 1, ch: 4}}, {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}}], -1, "deleteH", [{start: {line: 0, ch: 0}, end: {line: 0, ch: 0}, primary: false, reversed: false}, {start: {line: 1, ch: 0}, end: {line: 1, ch: 0}, primary: false, reversed: false}, {start: {line: 2, ch: 0}, end: {line: 2, ch: 0}, primary: true, reversed: false}], "one\ntwo\nthree\n"); }); it("should delete right over a soft tab from multiple aligned cursors", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 0}, end: {line: 0, ch: 0}}, {start: {line: 1, ch: 0}, end: {line: 1, ch: 0}}, {start: {line: 2, ch: 0}, end: {line: 2, ch: 0}}], 1, "deleteH", [{start: {line: 0, ch: 0}, end: {line: 0, ch: 0}, primary: false, reversed: false}, {start: {line: 1, ch: 0}, end: {line: 1, ch: 0}, primary: false, reversed: false}, {start: {line: 2, ch: 0}, end: {line: 2, ch: 0}, primary: true, reversed: false}], "one\ntwo\nthree\n"); }); it("should move left to next soft tab from multiple cursors at same distance from tab stops", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}}, {start: {line: 1, ch: 2}, end: {line: 1, ch: 2}}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}}], -1, "moveH", [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}, primary: false, reversed: false}, {start: {line: 1, ch: 0}, end: {line: 1, ch: 0}, primary: false, reversed: false}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, primary: true, reversed: false}]); }); it("should move right to next soft tab from multiple cursors at same distance from tab stops", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 9}, end: {line: 2, ch: 9}}], 1, "moveH", [{start: {line: 0, ch: 8}, end: {line: 0, ch: 8}, primary: false, reversed: false}, {start: {line: 1, ch: 4}, end: {line: 1, ch: 4}, primary: false, reversed: false}, {start: {line: 2, ch: 12}, end: {line: 2, ch: 12}, primary: true, reversed: false}]); }); it("should backspace to next soft tab from multiple cursors at same distance from tab stops", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}}, {start: {line: 1, ch: 2}, end: {line: 1, ch: 2}}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}}], -1, "deleteH", [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}, primary: false, reversed: false}, {start: {line: 1, ch: 0}, end: {line: 1, ch: 0}, primary: false, reversed: false}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, primary: true, reversed: false}], " one\n two\n three\n"); }); it("should delete right to next soft tab from multiple cursors at same distance from tab stops", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 9}, end: {line: 2, ch: 9}}], 1, "deleteH", [{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}, primary: false, reversed: false}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, primary: false, reversed: false}, {start: {line: 2, ch: 9}, end: {line: 2, ch: 9}, primary: true, reversed: false}], " one\n two\n three\n" ); }); it("should do default move left from multiple cursors at different distances from tab stops", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}}], -1, "moveH", [{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}, primary: false, reversed: false}, {start: {line: 1, ch: 0}, end: {line: 1, ch: 0}, primary: false, reversed: false}, {start: {line: 2, ch: 9}, end: {line: 2, ch: 9}, primary: true, reversed: false}]); }); it("should do default move right from multiple cursors at different distances from tab stops", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}}], 1, "moveH", [{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}, primary: false, reversed: false}, {start: {line: 1, ch: 2}, end: {line: 1, ch: 2}, primary: false, reversed: false}, {start: {line: 2, ch: 11}, end: {line: 2, ch: 11}, primary: true, reversed: false}]); }); it("should do default backspace from multiple cursors at different distances from tab stops", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}}, {start: {line: 1, ch: 2}, end: {line: 1, ch: 2}}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}}], -1, "deleteH", [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}, primary: false, reversed: false}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, primary: false, reversed: false}, {start: {line: 2, ch: 9}, end: {line: 2, ch: 9}, primary: true, reversed: false}], " one\n two\n three\n"); }); it("should do default delete right from multiple cursors at different distances from tab stops", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 8}}], 1, "deleteH", [{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}, primary: false, reversed: false}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, primary: false, reversed: false}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, primary: true, reversed: false}], " one\n two\n three\n" ); }); it("should do default move left from multiple cursors if one is inside content", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}}, {start: {line: 1, ch: 9}, end: {line: 1, ch: 9}}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}}], -1, "moveH", [{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}, primary: false, reversed: false}, {start: {line: 1, ch: 8}, end: {line: 1, ch: 8}, primary: false, reversed: false}, {start: {line: 2, ch: 9}, end: {line: 2, ch: 9}, primary: true, reversed: false}]); }); it("should do default move right from multiple cursors if one is inside content", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 14}, end: {line: 2, ch: 14}}], 1, "moveH", [{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}, primary: false, reversed: false}, {start: {line: 1, ch: 2}, end: {line: 1, ch: 2}, primary: false, reversed: false}, {start: {line: 2, ch: 15}, end: {line: 2, ch: 15}, primary: true, reversed: false}]); }); it("should do default backspace from multiple cursors if one is inside content", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 10}, end: {line: 0, ch: 10}}, {start: {line: 1, ch: 2}, end: {line: 1, ch: 2}}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}}], -1, "deleteH", [{start: {line: 0, ch: 9}, end: {line: 0, ch: 9}, primary: false, reversed: false}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, primary: false, reversed: false}, {start: {line: 2, ch: 9}, end: {line: 2, ch: 9}, primary: true, reversed: false}], " oe\n two\n three\n"); }); it("should do default delete right from multiple cursors if one is inside content", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 15}, end: {line: 2, ch: 15}}], 1, "deleteH", [{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}, primary: false, reversed: false}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, primary: false, reversed: false}, {start: {line: 2, ch: 15}, end: {line: 2, ch: 15}, primary: true, reversed: false}], " one\n two\n thre\n" ); }); it("should collapse ranges and handle other consistent soft tabs when moving left", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}}, {start: {line: 1, ch: 2}, end: {line: 1, ch: 6}}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}}], -1, "moveH", [{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}, primary: false, reversed: false}, {start: {line: 1, ch: 2}, end: {line: 1, ch: 2}, primary: false, reversed: false}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, primary: true, reversed: false}]); }); it("should collapse ranges and handle other consistent soft tabs when moving right", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 5}, end: {line: 0, ch: 9}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 9}, end: {line: 2, ch: 9}}], 1, "moveH", [{start: {line: 0, ch: 9}, end: {line: 0, ch: 9}, primary: false, reversed: false}, {start: {line: 1, ch: 4}, end: {line: 1, ch: 4}, primary: false, reversed: false}, {start: {line: 2, ch: 12}, end: {line: 2, ch: 12}, primary: true, reversed: false}]); }); it("should delete ranges and do nothing with cursors when backspacing", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}}, {start: {line: 1, ch: 8}, end: {line: 1, ch: 10}}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}}], -1, "deleteH", [{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}, primary: false, reversed: false}, {start: {line: 1, ch: 8}, end: {line: 1, ch: 8}, primary: false, reversed: false}, {start: {line: 2, ch: 10}, end: {line: 2, ch: 10}, primary: true, reversed: false}], " one\n o\n three\n"); }); it("should delete ranges and do nothing with cursors when deleting right", function () { myEditor.document.setText(" one\n two\n three\n"); checkSoftTab([{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 14}, end: {line: 2, ch: 16}}], 1, "deleteH", [{start: {line: 0, ch: 5}, end: {line: 0, ch: 5}, primary: false, reversed: false}, {start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, primary: false, reversed: false}, {start: {line: 2, ch: 14}, end: {line: 2, ch: 14}, primary: true, reversed: false}], " one\n two\n the\n" ); }); }); }); describe("Smart Tab handling", function () { function makeEditor(content, useTabs) { createTestEditor(content, "javascript"); var instance = myEditor._codeMirror; instance.setOption("indentWithTabs", useTabs); instance.setOption("indentUnit", 4); } it("should indent and move cursor to correct position if at beginning of an empty line - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + "\n" + " }\n" + "}"; makeEditor(content); myEditor.setCursorPos({line: 2, ch: 0}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, reversed: false}); var lines = content.split("\n"); lines[2] = " "; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should indent and move cursor to correct position if at beginning of an empty line - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setCursorPos({line: 2, ch: 0}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 2}, end: {line: 2, ch: 2}, reversed: false}); var lines = content.split("\n"); lines[2] = "\t\t"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should move cursor to end of whitespace (without adding more) if at beginning of a line with correct amount of whitespace - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " \n" + " }\n" + "}"; makeEditor(content); myEditor.setCursorPos({line: 2, ch: 0}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, reversed: false}); expect(myEditor.document.getText()).toEqual(content); }); it("should move cursor to end of whitespace (without adding more) if at beginning of a line with correct amount of whitespace - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\t\t\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setCursorPos({line: 2, ch: 0}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 2}, end: {line: 2, ch: 2}, reversed: false}); expect(myEditor.document.getText()).toEqual(content); }); it("should add another indent whitespace if already past correct indent level on an all whitespace line - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " \n" + " }\n" + "}"; makeEditor(content); myEditor.setCursorPos({line: 2, ch: 12}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 16}, end: {line: 2, ch: 16}, reversed: false}); var lines = content.split("\n"); lines[2] = " " + lines[2]; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add another indent whitespace if already past correct indent level on an all whitespace line - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\t\t\t\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setCursorPos({line: 2, ch: 3}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 4}, end: {line: 2, ch: 4}, reversed: false}); var lines = content.split("\n"); lines[2] = "\t" + lines[2]; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should indent improperly indented line to proper level and move cursor to beginning of content if cursor is in whitespace before content - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setCursorPos({line: 2, ch: 2}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, reversed: false}); var lines = content.split("\n"); lines[2] = " indentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should indent improperly indented line to proper level and move cursor to beginning of content if cursor is in whitespace before content - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setCursorPos({line: 2, ch: 0}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 2}, end: {line: 2, ch: 2}, reversed: false}); var lines = content.split("\n"); lines[2] = "\t\tindentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add one indent level (not autoindent) if cursor is immediately before content - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setCursorPos({line: 1, ch: 8}); myEditor._handleTabKey(); expectSelection({start: {line: 1, ch: 12}, end: {line: 1, ch: 12}, reversed: false}); var lines = content.split("\n"); lines[1] = " if (bar) {"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add one indent level (not autoindent) if cursor is immediately before content - tabs", function () { var content = "function foo() {\n" + "\t\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setCursorPos({line: 1, ch: 2}); myEditor._handleTabKey(); expectSelection({start: {line: 1, ch: 3}, end: {line: 1, ch: 3}, reversed: false}); var lines = content.split("\n"); lines[1] = "\t\t\tif (bar) {"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should move cursor and not indent further if cursor is in whitespace before properly indented line - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setCursorPos({line: 2, ch: 4}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, reversed: false}); expect(myEditor.document.getText()).toEqual(content); }); it("should move cursor and not indent further if cursor is in whitespace before properly indented line - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\t\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setCursorPos({line: 2, ch: 1}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 2}, end: {line: 2, ch: 2}, reversed: false}); expect(myEditor.document.getText()).toEqual(content); }); it("should add an indent level if cursor is immediately before content on properly indented line - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setCursorPos({line: 2, ch: 8}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 12}, end: {line: 2, ch: 12}, reversed: false}); var lines = content.split("\n"); lines[2] = " indentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add an indent level if cursor is immediately before content on properly indented line - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\t\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setCursorPos({line: 2, ch: 2}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 3}, end: {line: 2, ch: 3}, reversed: false}); var lines = content.split("\n"); lines[2] = "\t\t\tindentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add an indent level to each line (regardless of existing indentation) if selection spans multiple lines - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}", i; makeEditor(content); myEditor.setSelection({line: 1, ch: 6}, {line: 3, ch: 3}); myEditor._handleTabKey(); expectSelection({start: {line: 1, ch: 10}, end: {line: 3, ch: 8}, reversed: false}); var lines = content.split("\n"); for (i = 1; i <= 3; i++) { lines[i] = " " + lines[i]; } expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add an indent level to each line (regardless of existing indentation) if selection spans multiple lines - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}", i; makeEditor(content, true); myEditor.setSelection({line: 1, ch: 0}, {line: 3, ch: 1}); myEditor._handleTabKey(); expectSelection({start: {line: 1, ch: 0}, end: {line: 3, ch: 2}, reversed: false}); var lines = content.split("\n"); for (i = 1; i <= 3; i++) { lines[i] = "\t" + lines[i]; } expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add spaces to indent to the next soft tab stop if cursor is in the middle of a line after non-whitespace content - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setSelection({line: 2, ch: 9}, {line: 2, ch: 9}); // should add three spaces to get to column 12 myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 12}, end: {line: 2, ch: 12}, reversed: false}); var lines = content.split("\n"); lines[2] = " inden tme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should insert a tab if cursor is in the middle of a line after non-whitespace content - tab", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setSelection({line: 2, ch: 5}, {line: 2, ch: 5}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 6}, end: {line: 2, ch: 6}, reversed: false}); var lines = content.split("\n"); lines[2] = "\tinde\tntme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add spaces to next soft tab before the beginning of the selection if it's a range in the middle of a line after non-whitespace content - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setSelection({line: 2, ch: 9}, {line: 2, ch: 14}); // should add three spaces to get to column 12 myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 12}, end: {line: 2, ch: 17}, reversed: false}); var lines = content.split("\n"); lines[2] = " inden tme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add a tab before the beginning of the selection if it's a range in the middle of a line after non-whitespace content - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setSelection({line: 2, ch: 5}, {line: 2, ch: 8}); myEditor._handleTabKey(); expectSelection({start: {line: 2, ch: 6}, end: {line: 2, ch: 9}, reversed: false}); var lines = content.split("\n"); lines[2] = "\tinde\tntme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); describe("with multiple selections", function () { // In some of these tests we force a selection other than the last to be primary if the last selection is the // one that triggers the behavior - so our code can't just cheat and rely on the primary selection. // Note that a side-effect of the way CM adds indent levels is that ends of ranges that are within the // whitespace at the beginning of the line get pushed to the first non-whitespace character on the line, // so in tests below that fall back to "add one indent level before each line", the selections might change // more than you would expect by just adding a single indent level. it("should add one indent level before all selected lines if any of the selections is multiline - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setSelections([{start: {line: 0, ch: 9}, end: {line: 0, ch: 9}, primary: true}, {start: {line: 2, ch: 6}, end: {line: 3, ch: 3}}]); myEditor._handleTabKey(); expectSelections([{start: {line: 0, ch: 13}, end: {line: 0, ch: 13}, primary: true, reversed: false}, {start: {line: 2, ch: 10}, end: {line: 3, ch: 8}, primary: false, reversed: false}]); var lines = content.split("\n"); lines[0] = " " + lines[0]; lines[2] = " " + lines[2]; lines[3] = " " + lines[3]; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add one indent level before all selected lines if any of the selections is multiline - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setSelections([{start: {line: 0, ch: 6}, end: {line: 0, ch: 6}, primary: true}, {start: {line: 2, ch: 3}, end: {line: 3, ch: 1}}]); myEditor._handleTabKey(); expectSelections([{start: {line: 0, ch: 7}, end: {line: 0, ch: 7}, primary: true, reversed: false}, {start: {line: 2, ch: 4}, end: {line: 3, ch: 2}, primary: false, reversed: false}]); var lines = content.split("\n"); lines[0] = "\t" + lines[0]; lines[2] = "\t" + lines[2]; lines[3] = "\t" + lines[3]; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add spaces before each cursor to get to next tab stop if any selection is after first non-whitespace character in its line - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setSelections([{start: {line: 0, ch: 3}, end: {line: 0, ch: 3}}, {start: {line: 2, ch: 6}, end: {line: 2, ch: 6}}, {start: {line: 3, ch: 2}, end: {line: 3, ch: 2}}]); myEditor._handleTabKey(); expectSelections([{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}, primary: false, reversed: false}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, primary: false, reversed: false}, {start: {line: 3, ch: 4}, end: {line: 3, ch: 4}, primary: true, reversed: false}]); var lines = content.split("\n"); lines[0] = "fun ction foo() {"; lines[2] = " in dentme();"; lines[3] = " }"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add a tab before each cursor if any selection is after first non-whitespace character in its line - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setSelections([{start: {line: 0, ch: 3}, end: {line: 0, ch: 3}}, {start: {line: 2, ch: 6}, end: {line: 2, ch: 6}}, {start: {line: 3, ch: 1}, end: {line: 3, ch: 1}}]); myEditor._handleTabKey(); expectSelections([{start: {line: 0, ch: 4}, end: {line: 0, ch: 4}, primary: false, reversed: false}, {start: {line: 2, ch: 7}, end: {line: 2, ch: 7}, primary: false, reversed: false}, {start: {line: 3, ch: 2}, end: {line: 3, ch: 2}, primary: true, reversed: false}]); var lines = content.split("\n"); lines[0] = "fun\tction foo() {"; lines[2] = "\tinden\ttme();"; lines[3] = "\t\t}"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add spaces before beginning of each range to get to next tab stop if any selection is after first non-whitespace character in its line - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setSelections([{start: {line: 0, ch: 3}, end: {line: 0, ch: 6}}, {start: {line: 2, ch: 6}, end: {line: 2, ch: 9}}, {start: {line: 3, ch: 2}, end: {line: 3, ch: 4}}]); myEditor._handleTabKey(); expectSelections([{start: {line: 0, ch: 4}, end: {line: 0, ch: 7}, primary: false, reversed: false}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 11}, primary: false, reversed: false}, {start: {line: 3, ch: 4}, end: {line: 3, ch: 6}, primary: true, reversed: false}]); var lines = content.split("\n"); lines[0] = "fun ction foo() {"; lines[2] = " in dentme();"; lines[3] = " }"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add a tab before beginning of each range if any selection is after first non-whitespace character in its line - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setSelections([{start: {line: 0, ch: 3}, end: {line: 0, ch: 6}}, {start: {line: 2, ch: 6}, end: {line: 2, ch: 9}}, {start: {line: 3, ch: 1}, end: {line: 3, ch: 2}}]); myEditor._handleTabKey(); expectSelections([{start: {line: 0, ch: 4}, end: {line: 0, ch: 7}, primary: false, reversed: false}, {start: {line: 2, ch: 7}, end: {line: 2, ch: 10}, primary: false, reversed: false}, {start: {line: 3, ch: 2}, end: {line: 3, ch: 3}, primary: true, reversed: false}]); var lines = content.split("\n"); lines[0] = "fun\tction foo() {"; lines[2] = "\tinden\ttme();"; lines[3] = "\t\t}"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add spaces before each cursor to get to next tab stop (not autoindent) if any selection is exactly before the first non-whitespace character on the line - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setSelections([{start: {line: 1, ch: 4}, end: {line: 1, ch: 4}, primary: true}, // should not move {start: {line: 2, ch: 4}, end: {line: 2, ch: 4}}]); // should get indented and move myEditor._handleTabKey(); expectSelections([{start: {line: 1, ch: 8}, end: {line: 1, ch: 8}, primary: true, reversed: false}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, primary: false, reversed: false}]); var lines = content.split("\n"); lines[1] = " if (bar) {"; lines[2] = " indentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should add a tab at each cursor (not autoindent) if any selection is exactly before the first non-whitespace character on the line - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setSelections([{start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, primary: true}, // should not move {start: {line: 2, ch: 1}, end: {line: 2, ch: 1}}]); // should get indented and move myEditor._handleTabKey(); expectSelections([{start: {line: 1, ch: 2}, end: {line: 1, ch: 2}, primary: true, reversed: false}, {start: {line: 2, ch: 2}, end: {line: 2, ch: 2}, primary: false, reversed: false}]); var lines = content.split("\n"); lines[1] = "\t\tif (bar) {"; lines[2] = "\t\tindentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should try to autoindent each line if all cursors are in start-of-line whitespace, and if at least one cursor changed position or indent was added, do nothing further - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + " }\n" + "}"; makeEditor(content); myEditor.setSelections([{start: {line: 1, ch: 2}, end: {line: 1, ch: 2}, primary: true}, // should not move {start: {line: 2, ch: 2}, end: {line: 2, ch: 2}}]); // should get indented and move myEditor._handleTabKey(); expectSelections([{start: {line: 1, ch: 4}, end: {line: 1, ch: 4}, primary: true, reversed: false}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 8}, primary: false, reversed: false}]); var lines = content.split("\n"); lines[2] = " indentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should try to autoindent each line if all cursors are in start-of-line whitespace, and if at least one cursor changed position or indent was added, do nothing further - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\tindentme();\n" + "\t}\n" + "}"; makeEditor(content, true); myEditor.setSelections([{start: {line: 1, ch: 0}, end: {line: 1, ch: 0}, primary: true}, // should not move {start: {line: 2, ch: 0}, end: {line: 2, ch: 0}}]); // should get indented and move myEditor._handleTabKey(); expectSelections([{start: {line: 1, ch: 1}, end: {line: 1, ch: 1}, primary: true, reversed: false}, {start: {line: 2, ch: 2}, end: {line: 2, ch: 2}, primary: false, reversed: false}]); var lines = content.split("\n"); lines[2] = "\t\tindentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should try to autoindent each line if all cursors are in start-of-line whitespace, but if no cursors changed position or added indent, add an indent to the beginning of each line - spaces", function () { var content = "function foo() {\n" + " if (bar) {\n" + " indentme();\n" + // indent already correct " }\n" + "}"; makeEditor(content); myEditor.setSelections([{start: {line: 1, ch: 4}, end: {line: 1, ch: 4}}, {start: {line: 2, ch: 8}, end: {line: 2, ch: 8}}]); myEditor._handleTabKey(); expectSelections([{start: {line: 1, ch: 8}, end: {line: 1, ch: 8}, primary: false, reversed: false}, {start: {line: 2, ch: 12}, end: {line: 2, ch: 12}, primary: true, reversed: false}]); var lines = content.split("\n"); lines[1] = " if (bar) {"; lines[2] = " indentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); it("should try to autoindent each line if all cursors are in start-of-line whitespace, but if no cursors changed position or added indent, add an indent to the beginning of each line - tabs", function () { var content = "function foo() {\n" + "\tif (bar) {\n" + "\t\tindentme();\n" + // indent already correct "\t}\n" + "}"; makeEditor(content, true); myEditor.setSelections([{start: {line: 1, ch: 1}, end: {line: 1, ch: 1}}, {start: {line: 2, ch: 2}, end: {line: 2, ch: 2}}]); myEditor._handleTabKey(); expectSelections([{start: {line: 1, ch: 2}, end: {line: 1, ch: 2}, primary: false, reversed: false}, {start: {line: 2, ch: 3}, end: {line: 2, ch: 3}, primary: true, reversed: false}]); var lines = content.split("\n"); lines[1] = "\t\tif (bar) {"; lines[2] = "\t\t\tindentme();"; expect(myEditor.document.getText()).toEqual(lines.join("\n")); }); }); }); describe("Gutter APIs", function () { var leftGutter = "left", rightGutter = "right", lineNumberGutter = "CodeMirror-linenumbers"; beforeEach(function () { createTestEditor(defaultContent, "javascript"); Editor.registerGutter(leftGutter, 1); Editor.registerGutter(rightGutter, 101); }); afterEach(function () { var nonLineNumberGutters = Editor.getRegisteredGutters().map(function (gutter) { return gutter.name; }); nonLineNumberGutters.forEach(function (gutter) { if (gutter !== lineNumberGutter) { Editor.unregisterGutter(gutter); } }); }); it("should register multiple gutters in the correct order", function () { var expectedGutters = [leftGutter, lineNumberGutter, rightGutter]; var gutters = myEditor._codeMirror.getOption("gutters"); var registeredGutters = Editor.getRegisteredGutters().map(function (gutter) { return gutter.name; }); expect(gutters).toEqual(expectedGutters); expect(registeredGutters).toEqual(expectedGutters); }); it("should return gutters registered with the same priority in insertion order", function () { var secondRightGutter = "second-right"; Editor.registerGutter(secondRightGutter, 101); var expectedGutters = [leftGutter, lineNumberGutter, rightGutter, secondRightGutter]; var gutters = myEditor._codeMirror.getOption("gutters"); var registeredGutters = Editor.getRegisteredGutters().map(function (gutter) { return gutter.name; }); expect(gutters).toEqual(expectedGutters); expect(registeredGutters).toEqual(expectedGutters); }); it("should have only gutters registered with the intended languageIds ", function () { var lessOnlyGutter = "less-only-gutter"; Editor.registerGutter(lessOnlyGutter, 101, ["less"]); var expectedGutters = [leftGutter, lineNumberGutter, rightGutter]; var expectedRegisteredGutters = [leftGutter, lineNumberGutter, rightGutter, lessOnlyGutter]; var gutters = myEditor._codeMirror.getOption("gutters"); var registeredGutters = Editor.getRegisteredGutters().map(function (gutter) { return gutter.name; }); expect(gutters).toEqual(expectedGutters); expect(registeredGutters).toEqual(expectedRegisteredGutters); }); it("should unregister gutters correctly", function () { Editor.unregisterGutter(leftGutter); Editor.unregisterGutter(rightGutter); Editor.registerGutter(leftGutter, 1); var expectedGutters = [leftGutter, lineNumberGutter]; var gutters = myEditor._codeMirror.getOption("gutters"); var registeredGutters = Editor.getRegisteredGutters().map(function (gutter) { return gutter.name; }); expect(gutters).toEqual(expectedGutters); expect(registeredGutters).toEqual(expectedGutters); }); it("should set gutter marker correctly", function () { var marker = window.document.createElement("div"); myEditor.setGutterMarker(1, leftGutter, marker); var lineInfo = myEditor._codeMirror.lineInfo(1); expect(lineInfo.gutterMarkers[leftGutter], marker); }); }); }); });