From d4a46aa46771d25c81d6a215b6ab56ce9282c96a Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Fri, 18 Mar 2011 22:25:39 +0100 Subject: [PATCH 01/26] Use getStringScreenWidth in Session.computeWidth --- lib/ace/edit_session.js | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 6c33c449..6ccb6a91 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -348,16 +348,16 @@ var EditSession = function(text, mode) { this.setUseWorker = function(useWorker) { if (this.$useWorker == useWorker) return; - + if (useWorker && !this.$worker && window.Worker) this.$worker = mode.createWorker(this); - + if (!useWorker && this.$worker) { this.$worker.terminate(); this.$worker = null; } }; - + this.getUseWorker = function() { return this.$useWorker; }; @@ -417,14 +417,11 @@ var EditSession = function(text, mode) { var tabSize = this.getTabSize(); for ( var i = 0; i < lines.length; i++) { - var len = lines[i].length; + var line = lines[i], + len = line.length, + screenLen = this.$getStringScreenWidth(line); longestLine = Math.max(longestLine, len); - - lines[i].replace(/\t/g, function(m) { - len += tabSize-1; - return m; - }); - longestScreenLine = Math.max(longestScreenLine, len); + longestScreenLine = Math.max(longestScreenLine, screenLen); } this.width = longestLine; @@ -601,7 +598,7 @@ var EditSession = function(text, mode) { var actions = [{}]; - + // collapse insert and remove operations for (var i=0; i Date: Fri, 18 Mar 2011 23:30:25 +0100 Subject: [PATCH 02/26] First iteration of implementing real real tabs --- lib/ace/edit_session.js | 19 +++++++++++-- lib/ace/layer/text.js | 63 ++++++++++++++++++++++++++++++----------- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 6ccb6a91..ed84f009 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -944,13 +944,14 @@ var EditSession = function(text, mode) { this.$getDisplayTokens = function(str) { var arr = []; - var tabSize = this.getTabSize(); + var tabSize; for (var i = 0; i < str.length; i++) { var c = str.charCodeAt(i); // Tab if (c == 9) { arr.push(TAB); + tabSize = this.getScreenTabSize(arr.length); for (var n = 1; n < tabSize; n++) { arr.push(TAB_SPACE); } @@ -990,7 +991,7 @@ var EditSession = function(text, mode) { var c = str.charCodeAt(i); // tab if (c == 9) { - screenColumn += tabSize; + screenColumn += this.getScreenTabSize(screenColumn); } // CJK characters else if ( @@ -1103,6 +1104,13 @@ var EditSession = function(text, mode) { return this.screenToDocumentPosition(screenRow, screenColumn).column; }; + /** + * Returns the width of a tab character at screenColumn. + */ + this.getScreenTabSize = function(screenColumn) { + return this.$tabSize - screenColumn % this.$tabSize; + }; + this.screenToDocumentPosition = function(row, column) { var line; var docRow; @@ -1131,7 +1139,8 @@ var EditSession = function(text, mode) { line = this.getLine(docRow).substring(docColumn); } - var tabSize = this.getTabSize(); + var tabSize, + screenColumn = 0; for(var i = 0; i < line.length; i++) { var c = line.charCodeAt(i); @@ -1139,8 +1148,10 @@ var EditSession = function(text, mode) { docColumn += 1; // tab if (c == 9) { + tabSize = this.getScreenTabSize(screenColumn); if (remaining >= tabSize) { remaining -= tabSize; + screenColumn += tabSize; } else { remaining = 0; docColumn -= 1; @@ -1154,6 +1165,7 @@ var EditSession = function(text, mode) { c >= 0xF900 && c <= 0xFAFF || c >= 0x3400 && c <= 0x4DBF ) { + screenColumn += 2; if (remaining >= 2) { remaining -= 2; } else { @@ -1161,6 +1173,7 @@ var EditSession = function(text, mode) { docColumn -= 1; } } else { + screenColumn += 2; remaining -= 1; } } else { diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index 43f24baa..7c22548f 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -160,18 +160,21 @@ var Text = function(parentEl) { return true; }; + this.$tabStrings = []; this.$computeTabString = function() { var tabSize = this.session.getTabSize(); - if (this.showInvisibles) { - var halfTab = (tabSize) / 2; - this.$tabString = "" - + new Array(Math.floor(halfTab)).join(" ") - + this.TAB_CHAR - + new Array(Math.ceil(halfTab)+1).join(" ") - + ""; - } else { - this.$tabString = new Array(tabSize+1).join(" "); + var tabStr = this.$tabStrings = [0]; + for (var i = 1; i < tabSize + 1; i++) { + if (this.showInvisibles) { + tabStr.push("" + + this.TAB_CHAR + + new Array(i).join(" ") + + ""); + } else { + tabStr.push(new Array(i+1).join(" ")); + } } + }; this.updateLines = function(config, firstRow, lastRow) { @@ -295,17 +298,41 @@ var Text = function(parentEl) { var spaceReplace = " "; } - var _self = this; - var characterWidth = this.config.characterWidth; + var _self = this, + characterWidth = this.config.characterWidth, + screenColumn = 0; + function addToken(token, value) { var output = value - .replace(/&/g, "&") - .replace(/" + c + "" + } + }) +// .replace(/\t/g, function(c, idx) { +// idx += screenColumnOld; +// var tabSize = _self.session.getScreenTabSize(idx); +// screenColumn += tabSize - 1; +// return _self.$tabStrings[tabSize]; +// }) +// .replace(/&/g, "&") +// .replace(/" + c + "" - }); +// .replace(/[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF]/g, function(c) { +// screenColumn += 1; +// return "" + c + "" +// }); + screenColumn += value.length; if (!_self.$textToken[token.type]) { var classes = "ace_" + token.type.replace(/\./g, " ace_"); @@ -344,7 +371,9 @@ var Text = function(parentEl) { "
"); + split ++; + screenColumn = 0; splitChars = splits[split] || Number.MAX_VALUE; } if (value.length != 0) { From d1bfdba34b7587af0092f3a3bee1a9e989de4ad9 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Sat, 19 Mar 2011 00:34:35 +0100 Subject: [PATCH 03/26] Fixed some bugs and unit tests pass again --- lib/ace/edit_session.js | 10 ++++---- lib/ace/selection.js | 33 +++++++++++---------------- lib/ace/test/edit_session_test.js | 38 +++++++++++++++++++------------ 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index ed84f009..536d9784 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -896,9 +896,9 @@ var EditSession = function(text, mode) { // and multipleWidth characters. var len = displayed.length; displayed.join(""). - // Get all the tabs. - replace(/4/g, function(m) { - len -= tabSize - 1; + // Get all the tabs spaces. + replace(/5/g, function(m) { + len -= 1; }). // Get all the multipleWidth characters. replace(/2/g, function(m) { @@ -950,8 +950,8 @@ var EditSession = function(text, mode) { var c = str.charCodeAt(i); // Tab if (c == 9) { - arr.push(TAB); tabSize = this.getScreenTabSize(arr.length); + arr.push(TAB); for (var n = 1; n < tabSize; n++) { arr.push(TAB_SPACE); } @@ -1173,7 +1173,7 @@ var EditSession = function(text, mode) { docColumn -= 1; } } else { - screenColumn += 2; + screenColumn += 1; remaining -= 1; } } else { diff --git a/lib/ace/selection.js b/lib/ace/selection.js index 5c2a45bb..1be40c41 100644 --- a/lib/ace/selection.js +++ b/lib/ace/selection.js @@ -50,7 +50,7 @@ var Selection = function(session) { this.clearSelection(); this.selectionLead = this.doc.createAnchor(0, 0); this.selectionAnchor = this.doc.createAnchor(0, 0); - + var _self = this; this.selectionLead.on("change", function(e) { _self._dispatchEvent("changeCursor"); @@ -59,7 +59,7 @@ var Selection = function(session) { if (e.old.row == e.value.row) _self.$updateDesiredColumn(); }); - + this.selectionAnchor.on("change", function() { if (!_self.$isEmpty) _self._dispatchEvent("changeSelection"); @@ -382,19 +382,14 @@ var Selection = function(session) { }; this.moveCursorBy = function(rows, chars) { - if (this.session.getUseWrapMode()) { - var screenPos = this.session.documentToScreenPosition( - this.selectionLead.row, - this.selectionLead.column - ); - var screenCol = (chars == 0 && this.$desiredColumn) || screenPos.column; - var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenCol); - - this.moveCursorTo(docPos.row, docPos.column + chars, chars == 0); - } else { - var docColumn = (chars == 0 && this.$desiredColumn) || this.selectionLead.column; - this.moveCursorTo(this.selectionLead.row + rows, docColumn + chars, chars == 0); - } + var screenPos = this.session.documentToScreenPosition( + this.selectionLead.row, + this.selectionLead.column + ); + var screenCol = (chars == 0 && this.$desiredColumn) || screenPos.column; + var docPos = this.session.screenToDocumentPosition(screenPos.row + rows, screenCol); + + this.moveCursorTo(docPos.row, docPos.column + chars, chars == 0); }; this.moveCursorToPosition = function(position) { @@ -408,11 +403,9 @@ var Selection = function(session) { }; this.moveCursorToScreen = function(row, column, preventUpdateDesiredColumn) { - if (this.session.getUseWrapMode()) { - var pos = this.session.screenToDocumentPosition(row, column); - row = pos.row; - column = pos.column; - } + var pos = this.session.screenToDocumentPosition(row, column); + row = pos.row; + column = pos.column; this.moveCursorTo(row, column, preventUpdateDesiredColumn); }; diff --git a/lib/ace/test/edit_session_test.js b/lib/ace/test/edit_session_test.js index 3cb061bb..0e6f4663 100644 --- a/lib/ace/test/edit_session_test.js +++ b/lib/ace/test/edit_session_test.js @@ -140,18 +140,19 @@ var Test = { assert.equal(session.documentToScreenColumn(0, 0), 0); assert.equal(session.documentToScreenColumn(0, 4), 4); assert.equal(session.documentToScreenColumn(0, 5), 5); - assert.equal(session.documentToScreenColumn(0, 6), 9); - assert.equal(session.documentToScreenColumn(0, 12), 15); - assert.equal(session.documentToScreenColumn(0, 13), 19); + assert.equal(session.documentToScreenColumn(0, 6), 8); + assert.equal(session.documentToScreenColumn(0, 12), 14); + assert.equal(session.documentToScreenColumn(0, 13), 16); session.setTabSize(2); assert.equal(session.documentToScreenColumn(0, 0), 0); assert.equal(session.documentToScreenColumn(0, 4), 4); assert.equal(session.documentToScreenColumn(0, 5), 5); - assert.equal(session.documentToScreenColumn(0, 6), 7); - assert.equal(session.documentToScreenColumn(0, 12), 13); - assert.equal(session.documentToScreenColumn(0, 13), 15); + assert.equal(session.documentToScreenColumn(0, 6), 6); + assert.equal(session.documentToScreenColumn(0, 7), 7); + assert.equal(session.documentToScreenColumn(0, 12), 12); + assert.equal(session.documentToScreenColumn(0, 13), 14); }, "test: convert document to screen coordinates with leading tabs": function() { @@ -179,7 +180,7 @@ var Test = { session.setUseWrapMode(true); session.setWrapLimitRange(2, 2); session.adjustWrapLimit(80); - + assert.position(session.documentToScreenPosition(0, 1), 1, 0); assert.position(session.documentToScreenPosition(0, 2), 2, 0); assert.position(session.documentToScreenPosition(0, 4), 2, 1); @@ -194,10 +195,20 @@ var Test = { assert.equal(session.screenToDocumentColumn(0, 5), 5); assert.equal(session.screenToDocumentColumn(0, 6), 5); assert.equal(session.screenToDocumentColumn(0, 7), 5); - assert.equal(session.screenToDocumentColumn(0, 8), 5); - assert.equal(session.screenToDocumentColumn(0, 9), 6); + assert.equal(session.screenToDocumentColumn(0, 8), 6); + assert.equal(session.screenToDocumentColumn(0, 9), 7); assert.equal(session.screenToDocumentColumn(0, 15), 12); - assert.equal(session.screenToDocumentColumn(0, 19), 13); + assert.equal(session.screenToDocumentColumn(0, 19), 16); + + session.setTabSize(2); + + assert.equal(session.screenToDocumentColumn(0, 0), 0); + assert.equal(session.screenToDocumentColumn(0, 4), 4); + assert.equal(session.screenToDocumentColumn(0, 5), 5); + assert.equal(session.screenToDocumentColumn(0, 6), 6); + assert.equal(session.screenToDocumentColumn(0, 12), 12); + assert.equal(session.screenToDocumentColumn(0, 13), 12); + assert.equal(session.screenToDocumentColumn(0, 14), 13); }, "test: screenToDocument with soft wrap and multi byte characters": function() { @@ -217,7 +228,7 @@ var Test = { session = new EditSession(["ぁ a"]); session.setUseWrapMode(true); session.adjustWrapLimit(80); - + assert.position(session.screenToDocumentPosition(0, 1), 0, 0); assert.position(session.screenToDocumentPosition(0, 2), 0, 1); assert.position(session.screenToDocumentPosition(0, 3), 0, 2); @@ -227,13 +238,12 @@ var Test = { "test: wrapLine split function" : function() { var splits; - var computeWrapSplits = EditSession.prototype.$computeWrapSplits; var c = 0; function computeAndAssert(line, assertEqual, wrapLimit, tabSize) { wrapLimit = wrapLimit || 12; tabSize = tabSize || 4; - splits = computeWrapSplits.call(EditSession.prototype, line, wrapLimit, tabSize); + var splits = EditSession.prototype.$computeWrapSplits(line, wrapLimit, tabSize); // console.log("String:", line, "Result:", splits, "Expected:", assertEqual); assert.ok(splits.length == assertEqual.length); for (var i = 0; i < splits.length; i++) { @@ -297,7 +307,7 @@ var Test = { assert.equal(session.$getDisplayTokens("\t").length, 4); assert.equal(session.$getDisplayTokens("abc").length, 3); - assert.equal(session.$getDisplayTokens("abc\t").length, 7); + assert.equal(session.$getDisplayTokens("abc\t").length, 4); }, "test issue 83": function() { From a8a1d9483b36ceed3715d23c55836a88aae4672b Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Sat, 19 Mar 2011 00:54:35 +0100 Subject: [PATCH 04/26] Moving replacing of spaces into big replace function to avoid replacements of tab --- lib/ace/layer/text.js | 48 ++++++++++++------------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index 7c22548f..7d4f845b 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -280,58 +280,36 @@ var Text = function(parentEl) { }; this.$renderLine = function(stringBuilder, row, tokens) { - if (this.showInvisibles) { - var self = this; - var spaceRe = /( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])/g; - var spaceReplace = function(space) { - if (space.charCodeAt(0) == 32) - return new Array(space.length+1).join(" "); - else { - var space = new Array(space.length+1).join(self.SPACE_CHAR); - return "" + space + ""; - } - - }; - } - else { - var spaceRe = /[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/g; - var spaceReplace = " "; - } - var _self = this, characterWidth = this.config.characterWidth, screenColumn = 0; function addToken(token, value) { var output = value - .replace(/\t|&|<|[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF]/g, function(c, idx) { - if (c == "\t") { + .replace(/\t|&|<|( +)|([\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000])|[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FFF\uF900-\uFAFF\u3400-\u4DBF]/g, function(c, a, b, tabIdx, idx4) { + if (c.charCodeAt(0) == 32) { + return new Array(c.length+1).join(" "); + } else if (c == "\t") { var tabSize = _self.session. - getScreenTabSize(screenColumn + idx); + getScreenTabSize(screenColumn + tabIdx); screenColumn += tabSize - 1; return _self.$tabStrings[tabSize]; } else if (c == "&") { return "&"; } else if (c == "<") { return "<"; + } else if (c.match(/[\v\f \u00a0\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u200b\u2028\u2029\u3000]/)) { + if (this.showInvisibles) { + var space = new Array(c.length+1).join(self.SPACE_CHAR); + return "" + space + ""; + } else { + return " " + } } else { screenColumn += 1; return "" + c + "" } - }) -// .replace(/\t/g, function(c, idx) { -// idx += screenColumnOld; -// var tabSize = _self.session.getScreenTabSize(idx); -// screenColumn += tabSize - 1; -// return _self.$tabStrings[tabSize]; -// }) -// .replace(/&/g, "&") -// .replace(/" + c + "" -// }); + }); screenColumn += value.length; if (!_self.$textToken[token.type]) { From 63d7ba443982759c2090601ca14bfb52babec675 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Sat, 19 Mar 2011 14:14:06 +0100 Subject: [PATCH 05/26] Add 'softTab' option to demo --- demo/demo.js | 20 ++++++++++++-------- index.html | 28 ++++++++++++++++------------ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/demo/demo.js b/demo/demo.js index b2bc257a..2655a4c1 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -139,7 +139,7 @@ exports.launch = function(env) { docs.svg = new EditSession(document.getElementById("svgtext").innerHTML.replace("<", "<")); docs.svg.setMode(new SvgMode()); docs.svg.setUndoManager(new UndoManager()); - + docs.textile = new EditSession(document.getElementById("textiletext").innerHTML); docs.textile.setMode(new TextileMode()); docs.textile.setUndoManager(new UndoManager()); @@ -301,6 +301,10 @@ exports.launch = function(env) { env.editor.renderer.setHScrollBarAlwaysVisible(checked); }); + bindCheckbox("soft_tab", function(checked) { + env.editor.getSession().setUseSoftTabs(checked); + }); + function bindCheckbox(id, callback) { var el = document.getElementById(id); var onCheck = function() { @@ -383,11 +387,11 @@ exports.launch = function(env) { }); window.env = env; - + /** * This demonstrates how you can define commands and bind shortcuts to them. */ - + // Command to focus the command line from the editor. canon.addCommand({ name: "focuscli", @@ -400,7 +404,7 @@ exports.launch = function(env) { env.cli.cliView.element.focus(); } }); - + // Command to focus the editor line from the command line. canon.addCommand({ name: "focuseditor", @@ -413,7 +417,7 @@ exports.launch = function(env) { env.editor.focus(); } }); - + // Fake-Save, works from the editor and the command line. canon.addCommand({ name: "save", @@ -426,7 +430,7 @@ exports.launch = function(env) { alert("Fake Save File"); } }); - + // Fake-Print with custom lookup-sender-match function. canon.addCommand({ name: "save", @@ -435,9 +439,9 @@ exports.launch = function(env) { mac: "Command-P", sender: function(env, sender, hashId, keyString) { if (sender == "editor") { - return true; + return true; } else { - alert("Sorry, can only print from the editor"); + alert("Sorry, can only print from the editor"); } } }, diff --git a/index.html b/index.html index 60703dd5..4cdac4cd 100644 --- a/index.html +++ b/index.html @@ -71,7 +71,7 @@ - + @@ -128,6 +128,10 @@ + + + + @@ -144,7 +148,7 @@ @@ -329,8 +333,8 @@ print "\n"; - @@ -329,8 +333,8 @@ print "\n"; -