From 1ce0f187b3fe7a278a50b855c4d51f71842f5f19 Mon Sep 17 00:00:00 2001 From: Mihai Sucan Date: Wed, 16 Feb 2011 05:23:23 +0800 Subject: [PATCH 01/13] test for issue 57. not working yet. --- lib/ace/test/all.js | 3 +- lib/ace/test/all_browser.js | 3 +- lib/ace/test/issue57_test.js | 119 +++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 lib/ace/test/issue57_test.js diff --git a/lib/ace/test/all.js b/lib/ace/test/all.js index 81140835..4582ff11 100644 --- a/lib/ace/test/all.js +++ b/lib/ace/test/all.js @@ -59,5 +59,6 @@ async.concat( require("./mode/javascript_tokenizer_test"), require("./mode/text_test"), require("./mode/xml_test"), - require("./mode/xml_tokenizer_test") + require("./mode/xml_tokenizer_test"), + require("./issue57_test") ).exec(); diff --git a/lib/ace/test/all_browser.js b/lib/ace/test/all_browser.js index b3976fb4..c11076ca 100644 --- a/lib/ace/test/all_browser.js +++ b/lib/ace/test/all_browser.js @@ -20,6 +20,7 @@ async.concat( require("./selection_test"), require("./text_edit_test"), require("./virtual_renderer_test"), + require("./issue57_test"), require("./mode/css_test"), require("./mode/css_tokenizer_test"), require("./mode/html_test"), @@ -72,4 +73,4 @@ async.concat( ].join("") }) -}); \ No newline at end of file +}); diff --git a/lib/ace/test/issue57_test.js b/lib/ace/test/issue57_test.js new file mode 100644 index 00000000..1f58282e --- /dev/null +++ b/lib/ace/test/issue57_test.js @@ -0,0 +1,119 @@ +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Mihai Sucan. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mihai Sucan + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +var EditSession = require("ace/edit_session").EditSession, + Editor = require("../editor").Editor, + MockRenderer = require("./mockrenderer"), + TextMode = require("ace/mode/text").Mode, + assert = require("./assertions"), + async = require("asyncjs"); + +var lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + + "Mauris at arcu mi, eu lobortis mauris. Quisque ut libero eget " + + "diam congue vehicula. Quisque ut odio ut mi aliquam tincidunt. " + + "Duis lacinia aliquam lorem eget eleifend. Morbi eget felis mi. " + + "Duis quam ligula, consequat vitae convallis volutpat, blandit " + + "nec neque. Nulla facilisi. Etiam suscipit lorem ac justo " + + "sollicitudin tristique. Phasellus ut posuere nunc. Aliquam " + + "scelerisque mollis felis non gravida. Vestibulum lacus sem, " + + "posuere non bibendum id, luctus non dolor. Aenean id metus " + + "lorem, vel dapibus est. Donec gravida feugiat augue nec " + + "accumsan.Lorem ipsum dolor sit amet, consectetur adipiscing " + + "elit. Nulla vulputate, velit vitae tincidunt congue, nunc " + + "augue accumsan velit, eu consequat turpis lectus ac orci. " + + "Pellentesque ornare dolor feugiat dui auctor eu varius nulla " + + "fermentum. Sed aliquam odio at velit lacinia vel fermentum " + + "felis sodales. In dignissim magna eget nunc lobortis non " + + "fringilla nibh ullamcorper. Donec facilisis malesuada elit " + + "at egestas. Etiam bibendum, diam vitae tempor aliquet, dui " + + "libero vehicula odio, eget bibendum mauris velit eu lorem."; + +var Test = { + setUp: function() { + this.session = new EditSession(lipsum); + this.editor = new Editor(new MockRenderer(), this.session); + this.selection = this.session.getSelection(); + }, + + "issue 57: highlight selected words by default": function() { + assert.equal(this.editor.getHighlightSelectedWord(), true); + }, + + "issue 57: higlight a word": function() { + this.selection.moveCursorTo(0, 9); + this.selection.selectWord(); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "ipsum"); + assert.equal(this.session.$selectionOccurrences.length, 1); + }, + + "issue 57: higlight another word": function() { + this.selection.moveCursorTo(0, 14); + this.selection.selectWord(); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "dolor"); + assert.equal(this.session.$selectionOccurrences.length, 3); + }, + + "issue 57: no selection, no highlight": function() { + this.selection.clearSelection(); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "issue 57: select a word, no highlight": function() { + this.editor.setHighlightSelectedWord(false); + this.selection.moveCursorTo(0, 14); + this.selection.selectWord(); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "dolor"); + assert.equal(this.session.$selectionOccurrences.length, 0); + } +}; + +module.exports = require("asyncjs/test").testcase(Test); +}); + +if (typeof module !== "undefined" && module === require.main) { + require("../../../support/paths"); + exports.exec() +} From bea0289a77afa22e80c9874594aae23e5dc79afa Mon Sep 17 00:00:00 2001 From: Mihai Sucan Date: Thu, 17 Feb 2011 01:06:00 +0800 Subject: [PATCH 02/13] bug fixes and tests --- lib/ace/mode/text.js | 14 ++- lib/ace/search.js | 15 ++- lib/ace/test/all.js | 4 +- lib/ace/test/all_browser.js | 2 +- ...est.js => highlight_selected_word_test.js} | 109 ++++++++++++++++-- lib/ace/test/search_test.js | 48 +++++++- 6 files changed, 174 insertions(+), 18 deletions(-) rename lib/ace/test/{issue57_test.js => highlight_selected_word_test.js} (58%) diff --git a/lib/ace/mode/text.js b/lib/ace/mode/text.js index d052fda8..4b4a6b4f 100644 --- a/lib/ace/mode/text.js +++ b/lib/ace/mode/text.js @@ -94,22 +94,25 @@ var Mode = function() { var startOuter = selection.start.column - 1; var endOuter = selection.end.column + 1; var line = session.getLine(selection.start.row); - var lineCols = line.length - 1; + var lineCols = line.length; var needle = line.substring(Math.max(startOuter, 0), Math.min(endOuter, lineCols)); // Make sure the outer characters are not part of the word. - if ((startOuter >= 0 && !/[^\w\d]/.test(needle.charAt(0))) || - (endOuter <= lineCols && !/[^\w\d]/.test(needle.charAt(needle.length - 1)))) + if ((startOuter >= 0 && /^[\w\d]/.test(needle)) || + (endOuter <= lineCols && /[\w\d]$/.test(needle))) return; needle = line.substring(selection.start.column, selection.end.column); if (!/^[\w\d]+$/.test(needle)) return; + var cursor = editor.getCursorPosition(); + var newOptions = { wrap: true, wholeWord: true, + caseSensitive: true, needle: needle }; @@ -117,9 +120,8 @@ var Mode = function() { editor.$search.set(newOptions); var ranges = editor.$search.findAll(session); - session.$selectionOccurrences = []; ranges.forEach(function(range) { - if (!range.contains(selection.start.row, selection.start.column)) { + if (!range.contains(cursor.row, cursor.column)) { var marker = session.addMarker(range, "ace_selected_word"); session.$selectionOccurrences.push(marker); } @@ -135,6 +137,8 @@ var Mode = function() { editor.session.$selectionOccurrences.forEach(function(marker) { editor.session.removeMarker(marker); }); + + editor.session.$selectionOccurrences = []; }; }).call(Mode.prototype); diff --git a/lib/ace/search.js b/lib/ace/search.js index aed0a8c5..9df4f6f8 100644 --- a/lib/ace/search.js +++ b/lib/ace/search.js @@ -1,4 +1,5 @@ -/* ***** BEGIN LICENSE BLOCK ***** +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version @@ -20,6 +21,7 @@ * * Contributor(s): * Fabian Jakobs + * Mihai Sucan * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -219,12 +221,16 @@ Search.SELECTION = 2; var lastRow = searchSelection ? range.end.row : session.getLength() - 1; var wrap = this.$options.wrap; + var inWrap = false; function getLine(row) { var line = session.getLine(row); if (searchSelection && row == range.end.row) { line = line.substring(0, range.end.column); } + if (inWrap && row == start.row) { + line = line.substring(0, start.column); + } return line; } @@ -236,6 +242,7 @@ Search.SELECTION = 2; var startIndex = start.column; var stop = false; + inWrap = false; while (!callback(line, startIndex, row)) { @@ -250,6 +257,7 @@ Search.SELECTION = 2; if (wrap) { row = firstRow; startIndex = firstColumn; + inWrap = true; } else { return; } @@ -283,6 +291,7 @@ Search.SELECTION = 2; var line = session.getLine(row).substring(0, start.column); var startIndex = 0; var stop = false; + var inWrap = false; while (!callback(line, startIndex, row)) { @@ -295,6 +304,7 @@ Search.SELECTION = 2; if (row < firstRow) { if (wrap) { row = lastRow; + inWrap = true; } else { return; } @@ -310,6 +320,9 @@ Search.SELECTION = 2; else if (row == lastRow) line = line.substring(0, range.end.column); } + + if (inWrap && row == start.row) + startIndex = start.column; } } }; diff --git a/lib/ace/test/all.js b/lib/ace/test/all.js index 4582ff11..19101373 100644 --- a/lib/ace/test/all.js +++ b/lib/ace/test/all.js @@ -51,6 +51,7 @@ async.concat( require("./search_test"), require("./selection_test"), require("./text_edit_test"), + require("./highlight_selected_word_test"), require("./mode/css_test"), require("./mode/css_tokenizer_test"), require("./mode/html_test"), @@ -59,6 +60,5 @@ async.concat( require("./mode/javascript_tokenizer_test"), require("./mode/text_test"), require("./mode/xml_test"), - require("./mode/xml_tokenizer_test"), - require("./issue57_test") + require("./mode/xml_tokenizer_test") ).exec(); diff --git a/lib/ace/test/all_browser.js b/lib/ace/test/all_browser.js index c11076ca..f3d5638e 100644 --- a/lib/ace/test/all_browser.js +++ b/lib/ace/test/all_browser.js @@ -20,7 +20,7 @@ async.concat( require("./selection_test"), require("./text_edit_test"), require("./virtual_renderer_test"), - require("./issue57_test"), + require("./highlight_selected_word_test"), require("./mode/css_test"), require("./mode/css_tokenizer_test"), require("./mode/html_test"), diff --git a/lib/ace/test/issue57_test.js b/lib/ace/test/highlight_selected_word_test.js similarity index 58% rename from lib/ace/test/issue57_test.js rename to lib/ace/test/highlight_selected_word_test.js index 1f58282e..34526333 100644 --- a/lib/ace/test/issue57_test.js +++ b/lib/ace/test/highlight_selected_word_test.js @@ -63,21 +63,23 @@ var lipsum = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "felis sodales. In dignissim magna eget nunc lobortis non " + "fringilla nibh ullamcorper. Donec facilisis malesuada elit " + "at egestas. Etiam bibendum, diam vitae tempor aliquet, dui " + - "libero vehicula odio, eget bibendum mauris velit eu lorem."; + "libero vehicula odio, eget bibendum mauris velit eu lorem.\n" + + "consectetur"; var Test = { setUp: function() { this.session = new EditSession(lipsum); this.editor = new Editor(new MockRenderer(), this.session); this.selection = this.session.getSelection(); + this.search = this.editor.$search; }, - "issue 57: highlight selected words by default": function() { + "test: highlight selected words by default": function() { assert.equal(this.editor.getHighlightSelectedWord(), true); }, - "issue 57: higlight a word": function() { - this.selection.moveCursorTo(0, 9); + "test: highlight a word": function() { + this.editor.moveCursorTo(0, 9); this.selection.selectWord(); var range = this.selection.getRange(); @@ -85,7 +87,19 @@ var Test = { assert.equal(this.session.$selectionOccurrences.length, 1); }, - "issue 57: higlight another word": function() { + "test: highlight a word and clear highlight": function() { + this.editor.moveCursorTo(0, 8); + this.selection.selectWord(); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "ipsum"); + assert.equal(this.session.$selectionOccurrences.length, 1); + + this.session.getMode().clearSelectionHighlight(this.editor); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: highlight another word": function() { this.selection.moveCursorTo(0, 14); this.selection.selectWord(); @@ -94,12 +108,12 @@ var Test = { assert.equal(this.session.$selectionOccurrences.length, 3); }, - "issue 57: no selection, no highlight": function() { + "test: no selection, no highlight": function() { this.selection.clearSelection(); assert.equal(this.session.$selectionOccurrences.length, 0); }, - "issue 57: select a word, no highlight": function() { + "test: select a word, no highlight": function() { this.editor.setHighlightSelectedWord(false); this.selection.moveCursorTo(0, 14); this.selection.selectWord(); @@ -107,7 +121,86 @@ var Test = { var range = this.selection.getRange(); assert.equal(this.session.getTextRange(range), "dolor"); assert.equal(this.session.$selectionOccurrences.length, 0); - } + }, + + "test: select a word with no matches": function() { + this.editor.setHighlightSelectedWord(true); + + var currentOptions = this.search.getOptions(); + var newOptions = { + wrap: true, + wholeWord: true, + caseSensitive: true, + needle: "Mauris" + }; + this.search.set(newOptions); + + var match = this.search.find(this.session); + assert.notEqual(match, null, "found a match for 'Mauris'"); + + this.search.set(currentOptions); + + this.selection.setSelectionRange(match); + + assert.equal(this.session.getTextRange(match), "Mauris"); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: partial word selection 1": function() { + this.selection.moveCursorTo(0, 14); + this.selection.selectWord(); + this.selection.selectLeft(); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "dolo"); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: partial word selection 2": function() { + this.selection.moveCursorTo(0, 13); + this.selection.selectWord(); + this.selection.selectRight(); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "dolor "); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: partial word selection 3": function() { + this.selection.moveCursorTo(0, 14); + this.selection.selectWord(); + this.selection.selectLeft(); + this.selection.shiftSelection(1); + + var range = this.selection.getRange(); + assert.equal(this.session.getTextRange(range), "olor"); + assert.equal(this.session.$selectionOccurrences.length, 0); + }, + + "test: select last word": function() { + this.selection.moveCursorTo(0, 1); + + var currentOptions = this.search.getOptions(); + var newOptions = { + wrap: true, + wholeWord: true, + caseSensitive: true, + backwards: true, + needle: "consectetur" + }; + this.search.set(newOptions); + + var match = this.search.find(this.session); + assert.notEqual(match, null, "found a match for 'consectetur'"); + assert.position(match.start, 1, 0); + + this.search.set(currentOptions); + + this.selection.setSelectionRange(match); + + assert.equal(this.session.getTextRange(match), "consectetur"); + assert.equal(this.session.$selectionOccurrences.length, 2); + }, }; module.exports = require("asyncjs/test").testcase(Test); diff --git a/lib/ace/test/search_test.js b/lib/ace/test/search_test.js index 3bd0a05f..0f9c89e9 100644 --- a/lib/ace/test/search_test.js +++ b/lib/ace/test/search_test.js @@ -20,6 +20,7 @@ * * Contributor(s): * Fabian Jakobs + * Mihai Sucan * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -341,7 +342,52 @@ var Test = { assert.equal(search.replace("ab12", "cd$1"), "cd12"); assert.equal(search.replace("ab12", "-$&-"), "-ab12-"); assert.equal(search.replace("ab12", "$$"), "$"); - } + }, + + "test: find all matches in a line" : function() { + var session = new EditSession("foo bar foo baz foobar foo"); + + var search = new Search().set({ + needle: "foo", + wrap: true, + wholeWord: true, + }); + + session.getSelection().moveCursorTo(0, 4); + + var ranges = search.findAll(session); + + assert.equal(ranges.length, 3); + assert.position(ranges[0].start, 0, 8); + assert.position(ranges[0].end, 0, 11); + assert.position(ranges[1].start, 0, 23); + assert.position(ranges[1].end, 0, 26); + assert.position(ranges[2].start, 0, 0); + assert.position(ranges[2].end, 0, 3); + }, + + "test: find all matches in a line backwards" : function() { + var session = new EditSession("foo bar foo baz foobar foo"); + + var search = new Search().set({ + needle: "foo", + wrap: true, + wholeWord: true, + backwards: true, + }); + + session.getSelection().moveCursorTo(0, 13); + + var ranges = search.findAll(session); + + assert.equal(ranges.length, 3); + assert.position(ranges[0].start, 0, 8); + assert.position(ranges[0].end, 0, 11); + assert.position(ranges[1].start, 0, 0); + assert.position(ranges[1].end, 0, 3); + assert.position(ranges[2].start, 0, 23); + assert.position(ranges[2].end, 0, 26); + }, }; module.exports = require("asyncjs/test").testcase(Test) From e3e3ec93b499476ed147727c8c882459dfe569df Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Thu, 17 Feb 2011 08:12:27 +0100 Subject: [PATCH 03/13] update textmate theme conversion script --- tool/theme.tmpl.js | 45 +++++++++++++++++++++++++++++++++++++++++---- tool/tmtheme.js | 8 ++++---- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/tool/theme.tmpl.js b/tool/theme.tmpl.js index 38a23553..3c965dac 100644 --- a/tool/theme.tmpl.js +++ b/tool/theme.tmpl.js @@ -1,11 +1,48 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Fabian Jakobs + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + define(function(require, exports, module) { + var dom = require("pilot/dom"); + var cssText = %css%; // import CSS once dom.importCssString(cssText); - return { - cssClass: "%cssClass%" - }; -}) \ No newline at end of file + exports.cssClass = "%cssClass%"; +}); \ No newline at end of file diff --git a/tool/tmtheme.js b/tool/tmtheme.js index fcb7b121..018594eb 100644 --- a/tool/tmtheme.js +++ b/tool/tmtheme.js @@ -159,17 +159,17 @@ function fillTemplate(template, replacements) { } function createTheme(name, styles, cssTemplate, jsTemplate) { - styles.cssClass = "ace" + hyphenate(name); + styles.cssClass = "ace-" + hyphenate(name); var css = fillTemplate(cssTemplate, styles); return fillTemplate(jsTemplate, { name: name, css: '"' + css.replace(/\\/, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\\n") + '"', - cssClass: "ace" + hyphenate(name) + cssClass: "ace-" + hyphenate(name) }); }; function hyphenate(str) { - return str.replace(/([A-Z])/g, "-$1").toLowerCase(); + return str.replace(/([A-Z])/g, "-$1").replace("_", "-").toLowerCase(); } var cssTemplate = fs.readFileSync(__dirname + "/Theme.tmpl.css", "utf8"); @@ -192,5 +192,5 @@ for (var name in themes) { var tmTheme = fs.readFileSync(__dirname + "/tmthemes/" + themes[name] + ".tmTheme", "utf8"); var styles = extractStyles(parseTheme(tmTheme)); - fs.writeFileSync(__dirname + "/../src/ace/theme/" + name + ".js", createTheme(name, styles, cssTemplate, jsTemplate)); + fs.writeFileSync(__dirname + "/../lib/ace/theme/" + name + ".js", createTheme(name, styles, cssTemplate, jsTemplate)); } From cc32cee6fc0afdb271e3a17c5c98e90bce7b3ce7 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Thu, 17 Feb 2011 08:34:56 +0100 Subject: [PATCH 04/13] update pilot --- support/pilot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/support/pilot b/support/pilot index 3028dbab..6e1fbe1d 160000 --- a/support/pilot +++ b/support/pilot @@ -1 +1 @@ -Subproject commit 3028dbabffa2f16cbfd45cd5709baa1c34e0ba4d +Subproject commit 6e1fbe1dfdff64020f15cd9e23ea72b7803cf406 From 831ef0745ac1726a7046f7eb1b1cd98b2b463a1b Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Thu, 17 Feb 2011 01:33:20 -0500 Subject: [PATCH 05/13] Simplify document.insert --- lib/ace/document.js | 41 ++++++++----------------------- lib/ace/test/edit_session_test.js | 14 +++++++++++ 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/lib/ace/document.js b/lib/ace/document.js index 490ee225..491cdd51 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -174,39 +174,18 @@ var Document = function(text) { if (this.getLength() <= 1) this.$detectNewLine(text); - var newLines = this.$split(text); + var lines = this.$split(text); + var firstLine = lines.splice(0, 1)[0]; + var lastLine = lines.length == 0 ? null + : lines.splice(lines.length - 1, 1)[0]; - if (this.isNewLine(text)) { - var end = this.insertNewLine(position); + position = this.insertInLine(position, firstLine); + if (lastLine !== null) { + position = this.insertNewLine(position); // terminate first line + position = this.insertLines(position.row, lines); + position = this.insertInLine(position, lastLine || ""); } - else if (newLines.length == 1) { - var end = this.insertInLine(position, text); - } - else { - if (newLines[0].length > 0) { - var end = this.insertInLine(position, newLines[0]); - this.insertNewLine(end); - } - // If we are inserting at the end of the document, we don't need to - // use insertInLine (concorde depends on this optimization!) - if (position.row + 1 == this.getLength()) { - this.insertLines(position.row + 1, - newLines.slice(1, newLines.length)); - var end = { - row: position.row + newLines.length - 1, - column: position.column + newLines[newLines.length - 1].length - }; - } else { - if (newLines.length > 2) - this.insertLines(position.row + 1, - newLines.slice(1, newLines.length - 1)); - var end = this.insertInLine({ - row: position.row + newLines.length - 1, - column: 0 - }, newLines[newLines.length - 1]); - } - } - return end; + return position; }; this.insertLines = function(row, lines) { diff --git a/lib/ace/test/edit_session_test.js b/lib/ace/test/edit_session_test.js index f5b72140..3cb061bb 100644 --- a/lib/ace/test/edit_session_test.js +++ b/lib/ace/test/edit_session_test.js @@ -325,6 +325,20 @@ var Test = { assert.equal(session.$wrapData.length, 2); assert.equal(session.$wrapData[0].length, 1); assert.equal(session.$wrapData[1].length, 1); + }, + + "test first line blank with wrap": function() { + var session = new EditSession("\nfoo"); + session.setUseWrapMode(true); + assert.equal(session.doc.getValue(), ["", "foo"].join("\n")); + }, + + "test first line blank with wrap 2" : function() { + var session = new EditSession(""); + session.setUseWrapMode(true); + session.setValue("\nfoo"); + + assert.equal(session.doc.getValue(), ["", "foo"].join("\n")); } }; From bf88abd8d93ff0f42e67f07a68aab3751cdc7f17 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Thu, 17 Feb 2011 10:37:20 +0100 Subject: [PATCH 06/13] take into account that undos can have inserts and removes mixed --- lib/ace/edit_session.js | 68 +++++++++++++++++++++++++++++------------ 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index a16b5ba4..539ca1f2 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -536,15 +536,7 @@ var EditSession = function(text, mode) { this.doc.revertDeltas(deltas); this.$fromUndo = false; - // update the selection - var firstDelta = deltas[0]; - var lastDelta = deltas[deltas.length-1]; - - this.selection.clearSelection(); - if (firstDelta.action == "insertText" || firstDelta.action == "insertLines") - this.selection.moveCursorToPosition(firstDelta.range.start); - if (firstDelta.action == "removeText" || firstDelta.action == "removeLines") - this.selection.setSelectionRange(Range.fromPoints(lastDelta.range.start, firstDelta.range.end)); + this.$setUndoSelection(deltas, true); }, this.redoChanges = function(deltas) { @@ -555,17 +547,55 @@ var EditSession = function(text, mode) { this.doc.applyDeltas(deltas); this.$fromUndo = false; - // update the selection - var firstDelta = deltas[0]; - var lastDelta = deltas[deltas.length-1]; - - this.selection.clearSelection(); - if (firstDelta.action == "insertText" || firstDelta.action == "insertLines") - this.selection.setSelectionRange(Range.fromPoints(firstDelta.range.start, lastDelta.range.end)); - if (firstDelta.action == "removeText" || firstDelta.action == "removeLines") - this.selection.moveCursorToPosition(lastDelta.range.start); + this.$setUndoSelection(deltas, false); }, + this.$setUndoSelection = function(deltas, isUndo) { + // invert deltas is they are an undo + if (isUndo) + deltas = deltas.map(function(delta) { + var d = { + range: delta.range + } + if (delta.action == "insertText" || delta.action == "insertLines") + d.action = "removeText" + else + d.action = "insertText" + return d; + }).reverse(); + + + var actions = [{}]; + + // collapse insert and remove operations + for (var i=0; i Date: Thu, 17 Feb 2011 10:37:35 +0100 Subject: [PATCH 07/13] disable auto indent for pasted code --- lib/ace/editor.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 957f8448..69d5f5d9 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -430,8 +430,10 @@ var Editor =function(renderer, session) { this.moveCursorToPosition(end); var lineState = this.bgTokenizer.getState(cursor.row); - // multi line insert - if (cursor.row !== end.row) { + // TODO disabled multiline auto indent + // possibly doing the indent before inserting the text + // if (cursor.row !== end.row) { + if (this.session.getDocument().isNewLine(text)) { var size = this.session.getTabSize(), minIndent = Number.MAX_VALUE; From 4d8e4b29ab0250b547556ec6d5c86be9a1f52b30 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Thu, 17 Feb 2011 10:11:00 +0800 Subject: [PATCH 08/13] Fix scrollbar in Safari 4 --- lib/ace/scrollbar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ace/scrollbar.js b/lib/ace/scrollbar.js index dfc3e71e..3cb59b02 100644 --- a/lib/ace/scrollbar.js +++ b/lib/ace/scrollbar.js @@ -52,7 +52,7 @@ var ScrollBar = function(parent) { parent.appendChild(this.element); this.width = dom.scrollbarWidth(); - this.element.style.width = this.width; + this.element.style.width = this.width + "px"; event.addListener(this.element, "scroll", this.onScroll.bind(this)); }; From 14ebde8b24fa493c5570e0f6b48be87946bf4029 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Mon, 14 Feb 2011 21:34:20 +0800 Subject: [PATCH 09/13] Show horizontal scrollbar only when needed --- lib/ace/scrollbar.js | 2 +- lib/ace/virtual_renderer.js | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/ace/scrollbar.js b/lib/ace/scrollbar.js index 3cb59b02..8862af86 100644 --- a/lib/ace/scrollbar.js +++ b/lib/ace/scrollbar.js @@ -69,7 +69,7 @@ var ScrollBar = function(parent) { }; this.setHeight = function(height) { - this.element.style.height = Math.max(0, height - this.width) + "px"; + this.element.style.height = height + "px"; }; this.setInnerHeight = function(height) { diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 952a20a4..b1540089 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -88,6 +88,10 @@ var VirtualRenderer = function(container, theme) { this.$cursorLayer = new CursorLayer(this.content); this.$cursorPadding = 8; + // Indicates whether the horizontal scrollbar is visible + this.$horizScroll = true; + this.$horizScrollAlwaysVisible = true; + this.scrollBar = new ScrollBar(container); this.scrollBar.addEventListener("scroll", this.onScroll.bind(this)); @@ -199,7 +203,7 @@ var VirtualRenderer = function(container, theme) { this.$size.height = height; this.scroller.style.height = height + "px"; - this.scrollBar.setHeight(height); + this.scrollBar.setHeight(this.scroller.clientHeight); if (this.session) { this.scrollToY(this.getScrollTop()); @@ -361,6 +365,14 @@ var VirtualRenderer = function(container, theme) { this.$updatePrintMargin(); }; + this.setHScrollBarAlwaysVisible = function(alwaysVisible) { + if (this.$horizScrollAlwaysVisible != alwaysVisible) { + this.$horizScrollAlwaysVisible = alwaysVisible; + if (!this.$horizScrollAlwaysVisible || !this.$horizScroll) + this.$loop.schedule(this.CHANGE_SCROLL); + } + } + this.onScroll = function(e) { this.scrollToY(e.data); }; @@ -446,6 +458,12 @@ var VirtualRenderer = function(container, theme) { var longestLine = this.$getLongestLine(); var widthChanged = !this.layerConfig ? true : (this.layerConfig.width != longestLine); + var horizScroll = this.$horizScrollAlwaysVisible || this.$size.scrollerWidth - longestLine < 0; + var horizScrollChanged = this.$horizScroll !== horizScroll; + this.$horizScroll = horizScroll; + if (horizScrollChanged) + this.scroller.style.overflowX = horizScroll ? "scroll" : "hidden"; + var lineCount = Math.ceil(minHeight / this.lineHeight) - 1; var firstRow = Math.max(0, Math.round((this.scrollTop - offset) / this.lineHeight)); var lastRow = firstRow + lineCount; @@ -480,6 +498,11 @@ var VirtualRenderer = function(container, theme) { this.content.style.marginTop = (-offset) + "px"; this.content.style.width = longestLine + "px"; this.content.style.height = minHeight + "px"; + + // Horizontal scrollbar visibility may have changed, which changes + // the client height of the scroller + if (horizScrollChanged) + this.onResize(true); }; this.$updateLines = function() { From 9e162f91b974b1b05fd97a4c5440bb0314d70750 Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Wed, 16 Feb 2011 09:32:00 +0800 Subject: [PATCH 10/13] Add "Persistent Horizontal Scroll" setting to editor demo --- demo/demo.js | 3 +++ index.html | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/demo/demo.js b/demo/demo.js index 2e09a43c..56c6b81e 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -268,6 +268,9 @@ exports.launch = function(env) { bindCheckbox("highlight_selected_word", function(checked) { env.editor.setHighlightSelectedWord(checked); + + bindCheckbox("show_hscroll", function(checked) { + env.editor.renderer.setHScrollBarAlwaysVisible(checked); }); function bindCheckbox(id, callback) { diff --git a/index.html b/index.html index 7d0557ce..ede48739 100644 --- a/index.html +++ b/index.html @@ -62,6 +62,10 @@ + + + + From 95d3a2ada724bc85e50cc71b5a01f6d9121e7c8a Mon Sep 17 00:00:00 2001 From: Joe Cheng Date: Thu, 17 Feb 2011 14:33:20 +0800 Subject: [PATCH 11/13] Fix error with setValue("\nfoo") when wrap mode is on --- lib/ace/document.js | 7 +++++++ lib/ace/test/edit_session_test.js | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/ace/document.js b/lib/ace/document.js index 490ee225..b0e7d2d8 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -183,10 +183,17 @@ var Document = function(text) { var end = this.insertInLine(position, text); } else { + // Insert the first line if (newLines[0].length > 0) { var end = this.insertInLine(position, newLines[0]); this.insertNewLine(end); } + else { + this.insertNewLine(position); + } + + // Now insert remaining lines + // If we are inserting at the end of the document, we don't need to // use insertInLine (concorde depends on this optimization!) if (position.row + 1 == this.getLength()) { diff --git a/lib/ace/test/edit_session_test.js b/lib/ace/test/edit_session_test.js index f5b72140..3cb061bb 100644 --- a/lib/ace/test/edit_session_test.js +++ b/lib/ace/test/edit_session_test.js @@ -325,6 +325,20 @@ var Test = { assert.equal(session.$wrapData.length, 2); assert.equal(session.$wrapData[0].length, 1); assert.equal(session.$wrapData[1].length, 1); + }, + + "test first line blank with wrap": function() { + var session = new EditSession("\nfoo"); + session.setUseWrapMode(true); + assert.equal(session.doc.getValue(), ["", "foo"].join("\n")); + }, + + "test first line blank with wrap 2" : function() { + var session = new EditSession(""); + session.setUseWrapMode(true); + session.setValue("\nfoo"); + + assert.equal(session.doc.getValue(), ["", "foo"].join("\n")); } }; From e99e4cc6323129a6de8156d3f565c4594d0fbba3 Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Thu, 17 Feb 2011 11:05:11 +0100 Subject: [PATCH 12/13] fix scroll cursor into view if the editor is not visible --- lib/ace/virtual_renderer.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index b1540089..2f1ab138 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -582,6 +582,10 @@ var VirtualRenderer = function(container, theme) { }; this.scrollCursorIntoView = function() { + // the editor is not visible + if (this.$size.scrollerHeight === 0) + return; + var pos = this.$cursorLayer.getPixelPosition(); var left = pos.left + this.$padding; From e89045d9bb7f50d3f001c2b091645a2afff56bce Mon Sep 17 00:00:00 2001 From: Fabian Jakobs Date: Thu, 17 Feb 2011 11:15:06 +0100 Subject: [PATCH 13/13] fix demo --- demo/demo.js | 1 + 1 file changed, 1 insertion(+) diff --git a/demo/demo.js b/demo/demo.js index 56c6b81e..ee13653e 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -268,6 +268,7 @@ exports.launch = function(env) { bindCheckbox("highlight_selected_word", function(checked) { env.editor.setHighlightSelectedWord(checked); + }); bindCheckbox("show_hscroll", function(checked) { env.editor.renderer.setHScrollBarAlwaysVisible(checked);