Remember Column during navigation

This commit is contained in:
Fabian Jakobs 2010-05-17 17:58:26 +02:00
commit 1d7ed0d3dd
6 changed files with 174 additions and 126 deletions

View file

@ -562,4 +562,51 @@ ace.Document = function(text, mode) {
return Math.max(0, Math.min(row, this.lines.length-1)); return Math.max(0, Math.min(row, this.lines.length-1));
}; };
this.documentToScreenColumn = function(row, docColumn) {
var tabSize = this.getTabSize();
var screenColumn = 0;
var remaining = docColumn;
var line = this.getLine(row).split("\t");
for (var i=0; i<line.length; i++) {
var len = line[i].length;
if (remaining > len) {
remaining -= (len + 1);
screenColumn += len + tabSize;
}
else {
screenColumn += remaining;
break;
}
}
return screenColumn;
};
this.screenToDocumentColumn = function(row, screenColumn) {
var tabSize = this.getTabSize();
var docColumn = 0;
var remaining = screenColumn;
var line = this.getLine(row).split("\t");
for (var i=0; i<line.length; i++) {
var len = line[i].length;
if (remaining >= len + tabSize) {
remaining -= (len + tabSize);
docColumn += (len + 1);
}
else if (remaining > len){
docColumn += len;
break;
}
else {
docColumn += remaining;
break;
}
}
return docColumn;
};
}).call(ace.Document.prototype); }).call(ace.Document.prototype);

View file

@ -93,6 +93,7 @@ ace.Editor = function(renderer, doc) {
this.doc.addEventListener("changeBreakpoint", this.$onDocumentChangeBreakpoint); this.doc.addEventListener("changeBreakpoint", this.$onDocumentChangeBreakpoint);
this.selection = doc.getSelection(); this.selection = doc.getSelection();
this.$desiredColumn = 0;
this.$onCursorChange = ace.bind(this.onCursorChange, this); this.$onCursorChange = ace.bind(this.onCursorChange, this);
this.selection.addEventListener("changeCursor", this.$onCursorChange); this.selection.addEventListener("changeCursor", this.$onCursorChange);
@ -279,10 +280,12 @@ ace.Editor = function(renderer, doc) {
this.onMouseDoubleClick = function(e) { this.onMouseDoubleClick = function(e) {
this.selection.selectWord(); this.selection.selectWord();
this.$updateDesiredColumn();
}; };
this.onMouseTripleClick = function(e) { this.onMouseTripleClick = function(e) {
this.selection.selectLine(); this.selection.selectLine();
this.$updateDesiredColumn();
}; };
this.onMouseWheel = function(e) { this.onMouseWheel = function(e) {
@ -467,6 +470,7 @@ ace.Editor = function(renderer, doc) {
var addedColumns = this.doc.indentRows(this.getSelectionRange(), indentString); var addedColumns = this.doc.indentRows(this.getSelectionRange(), indentString);
this.selection.shiftSelection(addedColumns); this.selection.shiftSelection(addedColumns);
this.$updateDesiredColumn();
}; };
this.blockOutdent = function(indentString) { this.blockOutdent = function(indentString) {
@ -477,6 +481,7 @@ ace.Editor = function(renderer, doc) {
var addedColumns = this.doc.outdentRows(this.getSelectionRange(), indentString); var addedColumns = this.doc.outdentRows(this.getSelectionRange(), indentString);
this.selection.shiftSelection(addedColumns); this.selection.shiftSelection(addedColumns);
this.$updateDesiredColumn();
}; };
this.toggleCommentLines = function() { this.toggleCommentLines = function() {
@ -656,14 +661,17 @@ ace.Editor = function(renderer, doc) {
this.clearSelection = function() { this.clearSelection = function() {
this.selection.clearSelection(); this.selection.clearSelection();
this.$updateDesiredColumn();
}; };
this.moveCursorTo = function(row, column) { this.moveCursorTo = function(row, column) {
this.selection.moveCursorTo(row, column); this.selection.moveCursorTo(row, column);
this.$updateDesiredColumn();
}; };
this.moveCursorToPosition = function(pos) { this.moveCursorToPosition = function(pos) {
this.selection.moveCursorToPosition(pos); this.selection.moveCursorToPosition(pos);
this.$updateDesiredColumn();
}; };
@ -680,16 +688,34 @@ ace.Editor = function(renderer, doc) {
this.navigateTo = function(row, column) { this.navigateTo = function(row, column) {
this.clearSelection(); this.clearSelection();
this.moveCursorTo(row, column); this.moveCursorTo(row, column);
this.$updateDesiredColumn(column);
}; };
this.navigateUp = function() { this.navigateUp = function() {
this.clearSelection(); this.selection.clearSelection();
this.selection.moveCursorUp(); this.selection.moveCursorBy(-1, 0);
if (this.$desiredColumn) {
var cursor = this.getCursorPosition();
var column = this.doc.screenToDocumentColumn(cursor.row, this.$desiredColumn);
this.selection.moveCursorTo(cursor.row, column);
}
}; };
this.navigateDown = function() { this.navigateDown = function() {
this.clearSelection(); this.selection.clearSelection();
this.selection.moveCursorDown(); this.selection.moveCursorBy(1, 0);
if (this.$desiredColumn) {
var cursor = this.getCursorPosition();
var column = this.doc.screenToDocumentColumn(cursor.row, this.$desiredColumn);
this.selection.moveCursorTo(cursor.row, column);
}
};
this.$updateDesiredColumn = function() {
var cursor = this.getCursorPosition();
this.$desiredColumn = this.doc.documentToScreenColumn(cursor.row, cursor.column);
}; };
this.navigateLeft = function() { this.navigateLeft = function() {
@ -715,39 +741,40 @@ ace.Editor = function(renderer, doc) {
}; };
this.navigateLineStart = function() { this.navigateLineStart = function() {
this.clearSelection();
this.selection.moveCursorLineStart(); this.selection.moveCursorLineStart();
this.clearSelection();
}; };
this.navigateLineEnd = function() { this.navigateLineEnd = function() {
this.clearSelection();
this.selection.moveCursorLineEnd(); this.selection.moveCursorLineEnd();
this.clearSelection();
}; };
this.navigateFileEnd = function() { this.navigateFileEnd = function() {
this.clearSelection();
this.selection.moveCursorFileEnd(); this.selection.moveCursorFileEnd();
this.clearSelection();
}; };
this.navigateFileStart = function() { this.navigateFileStart = function() {
this.clearSelection();
this.selection.moveCursorFileStart(); this.selection.moveCursorFileStart();
this.clearSelection();
}; };
this.navigateWordRight = function() { this.navigateWordRight = function() {
this.clearSelection();
this.selection.moveCursorWordRight(); this.selection.moveCursorWordRight();
this.clearSelection();
}; };
this.navigateWordLeft = function() { this.navigateWordLeft = function() {
this.clearSelection();
this.selection.moveCursorWordLeft(); this.selection.moveCursorWordLeft();
this.clearSelection();
}; };
this.replace = function(replacement) { this.replace = function(replacement) {
var range = this.$tryReplace(this.getSelectionRange(), replacement); var range = this.$tryReplace(this.getSelectionRange(), replacement);
if (range !== null) if (range !== null)
this.selection.setSelectionRange(range); this.selection.setSelectionRange(range);
this.$updateDesiredColumn();
}, },
this.replaceAll = function(replacement) { this.replaceAll = function(replacement) {
@ -763,6 +790,7 @@ ace.Editor = function(renderer, doc) {
this.$tryReplace(range, replacement); this.$tryReplace(range, replacement);
} }
this.selection.setSelectionRange(range); this.selection.setSelectionRange(range);
this.$updateDesiredColumn();
}, },
this.$tryReplace = function(range, replacement) { this.$tryReplace = function(range, replacement) {
@ -800,8 +828,10 @@ ace.Editor = function(renderer, doc) {
}); });
var range = this.$search.find(this.doc); var range = this.$search.find(this.doc);
if (range) if (range) {
this.selection.setSelectionRange(range); this.selection.setSelectionRange(range);
this.$updateDesiredColumn();
}
}; };
this.undo = function() { this.undo = function() {

View file

@ -250,57 +250,10 @@ ace.VirtualRenderer = function(container) {
this.$documentToScreenPosition = function(pos) { this.$documentToScreenPosition = function(pos) {
return { return {
row: pos.row, row: pos.row,
column: this.$documentToScreenColumn(pos.row, pos.column) column: this.doc.documentToScreenColumn(pos.row, pos.column)
}; };
}; };
this.$documentToScreenColumn = function(row, docColumn) {
var tabSize = this.doc.getTabSize();
var screenColumn = 0;
var remaining = docColumn;
var line = this.doc.getLine(row).split("\t");
for (var i=0; i<line.length; i++) {
var len = line[i].length;
if (remaining > len) {
remaining -= (len + 1);
screenColumn += len + tabSize;
}
else {
screenColumn += remaining;
break;
}
}
return screenColumn;
};
this.$screenToDocumentColumn = function(row, screenColumn) {
var tabSize = this.doc.getTabSize();
var docColumn = 0;
var remaining = screenColumn;
var line = this.doc.getLine(row).split("\t");
for (var i=0; i<line.length; i++) {
var len = line[i].length;
if (remaining >= len + tabSize) {
remaining -= (len + tabSize);
docColumn += (len + 1);
}
else if (remaining > len){
docColumn += len;
break;
}
else {
docColumn += remaining;
break;
}
}
return docColumn;
};
this.hideCursor = function() { this.hideCursor = function() {
this.$cursorLayer.hideCursor(); this.$cursorLayer.hideCursor();
}; };
@ -369,7 +322,7 @@ ace.VirtualRenderer = function(container) {
return { return {
row : row, row : row,
column : this.$screenToDocumentColumn(Math.max(0, Math.min(row, this.doc.getLength()-1)), col) column : this.doc.screenToDocumentColumn(Math.max(0, Math.min(row, this.doc.getLength()-1)), col)
}; };
}; };

View file

@ -162,5 +162,51 @@ var TextDocumentTest = new TestCase("TextDocumentTest", {
undoManager.undo(); undoManager.undo();
doc.$informUndoManager.call(); doc.$informUndoManager.call();
assertEquals(initialText, doc.toString()); assertEquals(initialText, doc.toString());
},
"test: convert document to screen coordinates" : function() {
var doc = new ace.Document("01234\t567890\t1234");
doc.setTabSize(4);
assertEquals(0, doc.documentToScreenColumn(0, 0));
assertEquals(4, doc.documentToScreenColumn(0, 4));
assertEquals(5, doc.documentToScreenColumn(0, 5));
assertEquals(9, doc.documentToScreenColumn(0, 6));
assertEquals(15, doc.documentToScreenColumn(0, 12));
assertEquals(19, doc.documentToScreenColumn(0, 13));
doc.setTabSize(2);
assertEquals(0, doc.documentToScreenColumn(0, 0));
assertEquals(4, doc.documentToScreenColumn(0, 4));
assertEquals(5, doc.documentToScreenColumn(0, 5));
assertEquals(7, doc.documentToScreenColumn(0, 6));
assertEquals(13, doc.documentToScreenColumn(0, 12));
assertEquals(15, doc.documentToScreenColumn(0, 13));
},
"test: convert document to scrren coordinates with leading tabs": function() {
var doc = new ace.Document("\t\t123");
doc.setTabSize(4);
assertEquals(0, doc.documentToScreenColumn(0, 0));
assertEquals(4, doc.documentToScreenColumn(0, 1));
assertEquals(8, doc.documentToScreenColumn(0, 2));
assertEquals(9, doc.documentToScreenColumn(0, 3));
},
"test: convert screen to document coordinates" : function() {
var doc = new ace.Document("01234\t567890\t1234");
doc.setTabSize(4);
assertEquals(0, doc.screenToDocumentColumn(0, 0));
assertEquals(4, doc.screenToDocumentColumn(0, 4));
assertEquals(5, doc.screenToDocumentColumn(0, 5));
assertEquals(5, doc.screenToDocumentColumn(0, 6));
assertEquals(5, doc.screenToDocumentColumn(0, 7));
assertEquals(5, doc.screenToDocumentColumn(0, 8));
assertEquals(6, doc.screenToDocumentColumn(0, 9));
assertEquals(12, doc.screenToDocumentColumn(0, 15));
assertEquals(13, doc.screenToDocumentColumn(0, 19));
} }
}); });

View file

@ -11,7 +11,7 @@ var NavigationTest = TestCase("NavigationTest",
var editor = new ace.Editor(new MockRenderer(), doc); var editor = new ace.Editor(new MockRenderer(), doc);
editor.navigateFileEnd(); editor.navigateFileEnd();
var cursor = editor.getSelection().getCursor(); var cursor = editor.getCursorPosition();
assertTrue(editor.getFirstVisibleRow() <= cursor.row); assertTrue(editor.getFirstVisibleRow() <= cursor.row);
assertTrue(editor.getLastVisibleRow() >= cursor.row); assertTrue(editor.getLastVisibleRow() >= cursor.row);
@ -32,32 +32,32 @@ var NavigationTest = TestCase("NavigationTest",
editor.navigateTo(0, 0); editor.navigateTo(0, 0);
editor.gotoLine(101); editor.gotoLine(101);
assertPosition(100, 0, editor.getSelection().getCursor()); assertPosition(100, 0, editor.getCursorPosition());
assertEquals(90, editor.getFirstVisibleRow()); assertEquals(90, editor.getFirstVisibleRow());
editor.navigateTo(100, 0); editor.navigateTo(100, 0);
editor.gotoLine(11); editor.gotoLine(11);
assertPosition(10, 0, editor.getSelection().getCursor()); assertPosition(10, 0, editor.getCursorPosition());
assertEquals(0, editor.getFirstVisibleRow()); assertEquals(0, editor.getFirstVisibleRow());
editor.navigateTo(100, 0); editor.navigateTo(100, 0);
editor.gotoLine(6); editor.gotoLine(6);
assertPosition(5, 0, editor.getSelection().getCursor()); assertPosition(5, 0, editor.getCursorPosition());
assertEquals(0, editor.getFirstVisibleRow()); assertEquals(0, editor.getFirstVisibleRow());
editor.navigateTo(100, 0); editor.navigateTo(100, 0);
editor.gotoLine(1); editor.gotoLine(1);
assertPosition(0, 0, editor.getSelection().getCursor()); assertPosition(0, 0, editor.getCursorPosition());
assertEquals(0, editor.getFirstVisibleRow()); assertEquals(0, editor.getFirstVisibleRow());
editor.navigateTo(0, 0); editor.navigateTo(0, 0);
editor.gotoLine(191); editor.gotoLine(191);
assertPosition(190, 0, editor.getSelection().getCursor()); assertPosition(190, 0, editor.getCursorPosition());
assertEquals(180, editor.getFirstVisibleRow()); assertEquals(180, editor.getFirstVisibleRow());
editor.navigateTo(0, 0); editor.navigateTo(0, 0);
editor.gotoLine(196); editor.gotoLine(196);
assertPosition(195, 0, editor.getSelection().getCursor()); assertPosition(195, 0, editor.getCursorPosition());
assertEquals(180, editor.getFirstVisibleRow()); assertEquals(180, editor.getFirstVisibleRow());
}, },
@ -66,12 +66,41 @@ var NavigationTest = TestCase("NavigationTest",
editor.navigateTo(0, 0); editor.navigateTo(0, 0);
editor.gotoLine(12); editor.gotoLine(12);
assertPosition(11, 0, editor.getSelection().getCursor()); assertPosition(11, 0, editor.getCursorPosition());
assertEquals(0, editor.getFirstVisibleRow()); assertEquals(0, editor.getFirstVisibleRow());
editor.navigateTo(30, 0); editor.navigateTo(30, 0);
editor.gotoLine(33); editor.gotoLine(33);
assertPosition(32, 0, editor.getSelection().getCursor()); assertPosition(32, 0, editor.getCursorPosition());
assertEquals(30, editor.getFirstVisibleRow()); assertEquals(30, editor.getFirstVisibleRow());
},
"test: navigate from the end of a long line down to a short line and back should maintain the curser column": function() {
var editor = new ace.Editor(new MockRenderer(), new ace.Document(["123456", "1"]));
editor.navigateTo(0, 6);
assertPosition(0, 6, editor.getCursorPosition());
editor.navigateDown();
assertPosition(1, 1, editor.getCursorPosition());
editor.navigateUp();
assertPosition(0, 6, editor.getCursorPosition());
},
"test: reset desired column on navigate left or right": function() {
var editor = new ace.Editor(new MockRenderer(), new ace.Document(["123456", "12"]));
editor.navigateTo(0, 6);
assertPosition(0, 6, editor.getCursorPosition());
editor.navigateDown();
assertPosition(1, 2, editor.getCursorPosition());
editor.navigateLeft();
assertPosition(1, 1, editor.getCursorPosition());
editor.navigateUp();
assertPosition(0, 1, editor.getCursorPosition());
} }
}); });

View file

@ -1,62 +1,5 @@
var VirtualRendererTest = new TestCase("VirtualRendererTest", { var VirtualRendererTest = new TestCase("VirtualRendererTest", {
"test: convert document to screen coordinates" : function() {
var el = document.createElement("div");
var renderer = new ace.VirtualRenderer(el);
var doc = new ace.Document("01234\t567890\t1234");
doc.setTabSize(4);
renderer.setDocument(doc);
assertEquals(0, renderer.$documentToScreenColumn(0, 0));
assertEquals(4, renderer.$documentToScreenColumn(0, 4));
assertEquals(5, renderer.$documentToScreenColumn(0, 5));
assertEquals(9, renderer.$documentToScreenColumn(0, 6));
assertEquals(15, renderer.$documentToScreenColumn(0, 12));
assertEquals(19, renderer.$documentToScreenColumn(0, 13));
doc.setTabSize(2);
assertEquals(0, renderer.$documentToScreenColumn(0, 0));
assertEquals(4, renderer.$documentToScreenColumn(0, 4));
assertEquals(5, renderer.$documentToScreenColumn(0, 5));
assertEquals(7, renderer.$documentToScreenColumn(0, 6));
assertEquals(13, renderer.$documentToScreenColumn(0, 12));
assertEquals(15, renderer.$documentToScreenColumn(0, 13));
},
"test: convert document to scrren coordinates with leading tabs": function() {
var el = document.createElement("div");
var renderer = new ace.VirtualRenderer(el);
var doc = new ace.Document("\t\t123");
doc.setTabSize(4);
renderer.setDocument(doc);
assertEquals(0, renderer.$documentToScreenColumn(0, 0));
assertEquals(4, renderer.$documentToScreenColumn(0, 1));
assertEquals(8, renderer.$documentToScreenColumn(0, 2));
assertEquals(9, renderer.$documentToScreenColumn(0, 3));
},
"test: convert screen to document coordinates" : function() {
var el = document.createElement("div");
var renderer = new ace.VirtualRenderer(el);
var doc = new ace.Document("01234\t567890\t1234");
doc.setTabSize(4);
renderer.setDocument(doc);
assertEquals(0, renderer.$screenToDocumentColumn(0, 0));
assertEquals(4, renderer.$screenToDocumentColumn(0, 4));
assertEquals(5, renderer.$screenToDocumentColumn(0, 5));
assertEquals(5, renderer.$screenToDocumentColumn(0, 6));
assertEquals(5, renderer.$screenToDocumentColumn(0, 7));
assertEquals(5, renderer.$screenToDocumentColumn(0, 8));
assertEquals(6, renderer.$screenToDocumentColumn(0, 9));
assertEquals(12, renderer.$screenToDocumentColumn(0, 15));
assertEquals(13, renderer.$screenToDocumentColumn(0, 19));
},
"test: screen2text the column should be rounded to the next character edge" : function() { "test: screen2text the column should be rounded to the next character edge" : function() {
var el = document.createElement("div"); var el = document.createElement("div");
el.style.left = "0px"; el.style.left = "0px";