Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit 444bd1f

Browse files
committed
Merge pull request #1932 from adobe/nj/issue-1764
Fix Delete Line to avoid exceptions and work intuitively in inline editors
2 parents 7cc28ce + 87c579c commit 444bd1f

4 files changed

Lines changed: 161 additions & 23 deletions

File tree

src/editor/Editor.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -537,15 +537,6 @@ define(function (require, exports, module) {
537537
// note: this change might have been a real edit made by the user, OR this might have
538538
// been a change synced from another editor
539539

540-
if (this._visibleRange) {
541-
// _visibleRange has already updated via its own Document listener, when we pushed our
542-
// change into the Document above (_masterEditor._applyChanges()). But changes due to our
543-
// own edits should never cause the range to lose sync - verify that.
544-
if (this._visibleRange.startLine === null || this._visibleRange.endLine === null) {
545-
throw new Error("ERROR: Typing in Editor should not destroy its own _visibleRange");
546-
}
547-
}
548-
549540
CodeHintManager.handleChange(this);
550541
};
551542

src/editor/EditorCommandHandlers.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -170,17 +170,25 @@ define(function (require, exports, module) {
170170
return;
171171
}
172172

173-
var from, to, sel, doc;
173+
var from,
174+
to,
175+
sel = editor.getSelection(),
176+
doc = editor.document;
174177

175-
doc = editor._codeMirror;
176-
sel = doc.getCursor(true);
177-
from = {line: sel.line, ch: 0};
178-
sel = doc.getCursor(false);
179-
to = {line: sel.line + 1, ch: 0};
180-
if (doc.lineCount() === to.line && from.line > 0) {
181-
from.line -= 1;
182-
from.ch = doc.getLine(from.line).length;
178+
from = {line: sel.start.line, ch: 0};
179+
to = {line: sel.end.line + 1, ch: 0};
180+
if (to.line === editor.getLastVisibleLine() + 1) {
181+
// Instead of deleting the newline after the last line, delete the newline
182+
// before the first line--unless this is the entire visible content of the editor,
183+
// in which case just delete the line content.
184+
if (from.line > editor.getFirstVisibleLine()) {
185+
from.line -= 1;
186+
from.ch = doc.getLine(from.line).length;
187+
}
188+
to.line -= 1;
189+
to.ch = doc.getLine(to.line).length;
183190
}
191+
184192
doc.replaceRange("", from, to);
185193
}
186194

test/spec/EditorCommandHandlers-test.js

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,15 @@ define(function (require, exports, module) {
4747
"}";
4848

4949
var myDocument, myEditor;
50-
beforeEach(function () {
50+
51+
function setupFullEditor() {
5152
// create dummy Document and Editor
5253
var mocks = SpecRunnerUtils.createMockEditor(defaultContent, "javascript");
5354
myDocument = mocks.doc;
5455
myEditor = mocks.editor;
5556

5657
myEditor.focus();
57-
});
58+
}
5859

5960
afterEach(function () {
6061
SpecRunnerUtils.destroyMockEditor(myDocument);
@@ -75,7 +76,8 @@ define(function (require, exports, module) {
7576

7677

7778
describe("Line comment/uncomment", function () {
78-
79+
beforeEach(setupFullEditor);
80+
7981
it("should comment/uncomment a single line, cursor at start", function () {
8082
myEditor.setCursorPos(3, 0);
8183

@@ -352,6 +354,8 @@ define(function (require, exports, module) {
352354

353355

354356
describe("Duplicate", function () {
357+
beforeEach(setupFullEditor);
358+
355359
it("should duplicate whole line if no selection", function () {
356360
// place cursor in middle of line 1
357361
myEditor.setCursorPos(1, 10);
@@ -498,6 +502,7 @@ define(function (require, exports, module) {
498502
});
499503

500504
describe("Move Lines Up/Down", function () {
505+
beforeEach(setupFullEditor);
501506

502507
it("should move whole line up if no selection", function () {
503508
// place cursor in middle of line 1
@@ -805,6 +810,140 @@ define(function (require, exports, module) {
805810
});
806811
});
807812

813+
describe("Delete Line", function () {
814+
beforeEach(setupFullEditor);
815+
816+
it("should delete the first line when selection is an IP in that line", function () {
817+
myEditor.setSelection({line: 0, ch: 5}, {line: 0, ch: 5});
818+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
819+
820+
var expectedText = defaultContent.split("\n").slice(1).join("\n");
821+
expect(myDocument.getText()).toEqual(expectedText);
822+
});
823+
824+
it("should delete the first line when selection is a range in that line", function () {
825+
myEditor.setSelection({line: 0, ch: 5}, {line: 0, ch: 8});
826+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
827+
828+
var expectedText = defaultContent.split("\n").slice(1).join("\n");
829+
expect(myDocument.getText()).toEqual(expectedText);
830+
});
831+
832+
it("should delete a middle line when selection is an IP in that line", function () {
833+
myEditor.setSelection({line: 2, ch: 5}, {line: 2, ch: 5});
834+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
835+
836+
var lines = defaultContent.split("\n");
837+
lines.splice(2, 1);
838+
expect(myDocument.getText()).toEqual(lines.join("\n"));
839+
});
840+
841+
it("should delete a middle line when selection is a range in that line", function () {
842+
myEditor.setSelection({line: 2, ch: 5}, {line: 2, ch: 8});
843+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
844+
845+
var lines = defaultContent.split("\n");
846+
lines.splice(2, 1);
847+
expect(myDocument.getText()).toEqual(lines.join("\n"));
848+
});
849+
850+
it("should delete the last line when selection is an IP in that line", function () {
851+
myEditor.setSelection({line: 7, ch: 0}, {line: 7, ch: 0});
852+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
853+
854+
var expectedText = defaultContent.split("\n").slice(0, 7).join("\n");
855+
expect(myDocument.getText()).toEqual(expectedText);
856+
});
857+
858+
it("should delete the last line when selection is a range in that line", function () {
859+
myEditor.setSelection({line: 7, ch: 0}, {line: 7, ch: 1});
860+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
861+
862+
var expectedText = defaultContent.split("\n").slice(0, 7).join("\n");
863+
expect(myDocument.getText()).toEqual(expectedText);
864+
});
865+
866+
it("should delete multiple lines starting at the top when selection spans them", function () {
867+
myEditor.setSelection({line: 0, ch: 5}, {line: 2, ch: 4});
868+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
869+
870+
var expectedText = defaultContent.split("\n").slice(3).join("\n");
871+
expect(myDocument.getText()).toEqual(expectedText);
872+
});
873+
874+
it("should delete multiple lines ending at the bottom when selection spans them", function () {
875+
myEditor.setSelection({line: 5, ch: 5}, {line: 7, ch: 0});
876+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
877+
878+
var expectedText = defaultContent.split("\n").slice(0, 5).join("\n");
879+
expect(myDocument.getText()).toEqual(expectedText);
880+
});
881+
882+
it("should leave empty text when all lines are selected", function () {
883+
myEditor.setSelection({line: 0, ch: 4}, {line: 7, ch: 1});
884+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
885+
expect(myDocument.getText()).toEqual("");
886+
});
887+
});
888+
889+
describe("Delete Line - editor with visible range", function () {
890+
function makeEditorWithRange(range) {
891+
// create editor with a visible range
892+
var mocks = SpecRunnerUtils.createMockEditor(defaultContent, "javascript", range);
893+
myDocument = mocks.doc;
894+
myEditor = mocks.editor;
895+
896+
myEditor.focus();
897+
}
898+
899+
it("should delete the top line of the visible range", function () {
900+
makeEditorWithRange({startLine: 1, endLine: 5});
901+
myEditor.setSelection({line: 1, ch: 5}, {line: 1, ch: 5});
902+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
903+
904+
var lines = defaultContent.split("\n");
905+
lines.splice(1, 1);
906+
expect(myDocument.getText()).toEqual(lines.join("\n"));
907+
expect(myEditor._visibleRange.startLine).toNotBe(null);
908+
expect(myEditor._visibleRange.endLine).toNotBe(null);
909+
});
910+
911+
it("should delete the bottom line of the visible range", function () {
912+
makeEditorWithRange({startLine: 1, endLine: 5});
913+
myEditor.setSelection({line: 5, ch: 2}, {line: 5, ch: 2});
914+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
915+
916+
var lines = defaultContent.split("\n");
917+
lines.splice(5, 1);
918+
expect(myDocument.getText()).toEqual(lines.join("\n"));
919+
expect(myEditor._visibleRange.startLine).toNotBe(null);
920+
expect(myEditor._visibleRange.endLine).toNotBe(null);
921+
});
922+
923+
it("should leave a single newline when all visible lines are selected", function () {
924+
makeEditorWithRange({startLine: 1, endLine: 5});
925+
myEditor.setSelection({line: 1, ch: 5}, {line: 5, ch: 2});
926+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
927+
928+
var lines = defaultContent.split("\n");
929+
lines.splice(1, 5, "");
930+
expect(myDocument.getText()).toEqual(lines.join("\n"));
931+
expect(myEditor._visibleRange.startLine).toNotBe(null);
932+
expect(myEditor._visibleRange.endLine).toNotBe(null);
933+
});
934+
935+
it("should leave a single newline when only one line is visible", function () {
936+
makeEditorWithRange({startLine: 3, endLine: 3});
937+
myEditor.setSelection({line: 3, ch: 4}, {line: 3, ch: 4});
938+
CommandManager.execute(Commands.EDIT_DELETE_LINES, myEditor);
939+
940+
var lines = defaultContent.split("\n");
941+
lines.splice(3, 1, "");
942+
expect(myDocument.getText()).toEqual(lines.join("\n"));
943+
expect(myEditor._visibleRange.startLine).toNotBe(null);
944+
expect(myEditor._visibleRange.endLine).toNotBe(null);
945+
});
946+
});
808947

809948
});
810949
});

test/spec/SpecRunnerUtils.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ define(function (require, exports, module) {
129129
* currentDocument or added to the working set.
130130
* @return {!{doc:{Document}, editor:{Editor}}}
131131
*/
132-
function createMockEditor(initialContent, mode) {
132+
function createMockEditor(initialContent, mode, visibleRange) {
133133
mode = mode || "";
134134

135135
// Initialize EditorManager
@@ -142,7 +142,7 @@ define(function (require, exports, module) {
142142
var doc = createMockDocument(initialContent);
143143

144144
// create Editor instance
145-
var editor = new Editor(doc, true, mode, $editorHolder.get(0), {});
145+
var editor = new Editor(doc, true, mode, $editorHolder.get(0), {}, visibleRange);
146146

147147
return { doc: doc, editor: editor };
148148
}

0 commit comments

Comments
 (0)