From 92d1b7559b901b16641ecaff01c34d68d0fa0614 Mon Sep 17 00:00:00 2001 From: Lee Sing Jie Date: Tue, 29 Nov 2011 21:25:25 +0800 Subject: [PATCH 1/4] Fix contextMenu when editor require scrolling When editor is not at the top of the page (requires scrolling), textarea right-click context menu will fail to appear. Use clientX and clientY which returns the relative coordinates instead of pageX and pageY. --- lib/ace/mouse/default_handlers.js | 2 +- lib/ace/mouse/mouse_event.js | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/ace/mouse/default_handlers.js b/lib/ace/mouse/default_handlers.js index a574e46a..db5800ad 100644 --- a/lib/ace/mouse/default_handlers.js +++ b/lib/ace/mouse/default_handlers.js @@ -96,7 +96,7 @@ function DefaultHandlers(editor) { editor.moveCursorToPosition(pos); } if(button == 2) { - editor.textInput.onContextMenu({x: pageX, y: pageY}, selectionEmpty); + editor.textInput.onContextMenu({x: ev.clientX, y: ev.clientY}, selectionEmpty); event.capture(editor.container, function(){}, editor.textInput.onContextMenuClose); } return; diff --git a/lib/ace/mouse/mouse_event.js b/lib/ace/mouse/mouse_event.js index b8ffcf5c..63e3998d 100644 --- a/lib/ace/mouse/mouse_event.js +++ b/lib/ace/mouse/mouse_event.js @@ -51,6 +51,9 @@ var MouseEvent = exports.MouseEvent = function(domEvent, editor) { this.pageX = event.getDocumentX(domEvent); this.pageY = event.getDocumentY(domEvent); + this.clientX = domEvent.clientX; + this.clientY = domEvent.clientY; + this.$pos = null; this.$inSelection = null; @@ -130,4 +133,4 @@ var MouseEvent = exports.MouseEvent = function(domEvent, editor) { }).call(MouseEvent.prototype); -}); \ No newline at end of file +}); From 3beed6da5b63d9485f899d67286c38a169fe73ec Mon Sep 17 00:00:00 2001 From: Zef Hemel Date: Thu, 1 Dec 2011 16:29:27 +0100 Subject: [PATCH 2/4] Added ability to cancel the placeholder related changes + test. --- lib/ace/placeholder.js | 12 ++++++++++++ lib/ace/placeholder_test.js | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/ace/placeholder.js b/lib/ace/placeholder.js index df6a5f98..f89eedac 100644 --- a/lib/ace/placeholder.js +++ b/lib/ace/placeholder.js @@ -58,6 +58,8 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) }; this.$pos = pos; + // Used for reset + this.$undoStackDepth = session.getUndoManager().$undoStack ? session.getUndoManager().$undoStack.length : -1; this.setup(); session.selection.on("changeCursor", this.$onCursorChange); @@ -188,6 +190,16 @@ var PlaceHolder = function(session, length, pos, others, mainClass, othersClass) this.others[i].detach(); } }; + + this.cancel = function() { + if(this.$undoStackDepth === -1) + throw Error("Canceling placeholders only supported with undo manager attached to session."); + var undoManager = this.session.getUndoManager(); + var undosRequired = undoManager.$undoStack.length - this.$undoStackDepth; + for (var i = 0; i < undosRequired; i++) { + undoManager.undo(true); + } + }; }).call(PlaceHolder.prototype); diff --git a/lib/ace/placeholder_test.js b/lib/ace/placeholder_test.js index 6130aa3f..c8611e9b 100644 --- a/lib/ace/placeholder_test.js +++ b/lib/ace/placeholder_test.js @@ -49,6 +49,7 @@ var MockRenderer = require("./test/mockrenderer").MockRenderer; var assert = require("./test/assertions"); var JavaScriptMode = require("./mode/javascript").Mode; var PlaceHolder = require('./placeholder').PlaceHolder; +var UndoManager = require('./undomanager').UndoManager; module.exports = { @@ -133,6 +134,25 @@ module.exports = { editor.moveCursorTo(1, 0); p.onCursorChange(); // Have to do this by hand because moveCursorTo doesn't trigger the event assert.ok(left); + }, + + "test: cancel": function(next) { + var session = new EditSession("var a = 10;\nconsole.log(a, a);", new JavaScriptMode()); + session.setUndoManager(new UndoManager()); + var editor = new Editor(new MockRenderer(), session); + var p = new PlaceHolder(session, 1, {row: 0, column: 4}, [{row: 1, column: 12}, {row: 1, column: 15}]); + + editor.moveCursorTo(0, 5); + editor.insert('b'); + editor.insert('cd'); + editor.remove('left'); + assert.equal(session.doc.getValue(), "var abc = 10;\nconsole.log(abc, abc);"); + // Wait a little for the changes to enter the undo stack + setTimeout(function() { + p.cancel(); + assert.equal(session.doc.getValue(), "var a = 10;\nconsole.log(a, a);"); + next(); + }, 80); } }; From d9f3eff921361c5d9e77bdff287e576621c22729 Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 1 Dec 2011 23:19:42 +0400 Subject: [PATCH 3/4] fix caching of foldWidgets --- lib/ace/layer/gutter.js | 3 ++- lib/ace/mode/folding/fold_mode.js | 1 + lib/ace/mode/folding/xml.js | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 503cc93a..2a8ea96e 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -129,7 +129,8 @@ var Gutter = function(parentEl) { if (foldWidgets) { var c = foldWidgets[i]; - if (!c) + // check if cached value is invalidated and we need to recompute + if (c == null) c = foldWidgets[i] = this.session.getFoldWidget(i); if (c) html.push( diff --git a/lib/ace/mode/folding/fold_mode.js b/lib/ace/mode/folding/fold_mode.js index 1f941c00..d231a35f 100644 --- a/lib/ace/mode/folding/fold_mode.js +++ b/lib/ace/mode/folding/fold_mode.js @@ -48,6 +48,7 @@ var FoldMode = exports.FoldMode = function() {}; this.foldingStartMarker = null; this.foldingStopMarker = null; + // must return "" if there's no fold, to enable caching this.getFoldWidget = function(session, row) { if (this.foldingStartMarker) { if (this.foldingStopMarker) diff --git a/lib/ace/mode/folding/xml.js b/lib/ace/mode/folding/xml.js index 06aa3167..0e5802a5 100644 --- a/lib/ace/mode/folding/xml.js +++ b/lib/ace/mode/folding/xml.js @@ -66,13 +66,13 @@ oop.inherits(FoldMode, BaseFoldMode); var fold = tags[0]; if (!fold || this.voidElements[fold]) - return; + return ""; if (fold.charAt(0) == "/") return "end"; if (tags.indexOf("/" + fold) !== -1) - return; + return ""; return "start"; }; From f02c04965b9a07363da549b2c47c5039ddc680cc Mon Sep 17 00:00:00 2001 From: nightwing Date: Thu, 1 Dec 2011 23:58:16 +0400 Subject: [PATCH 4/4] shift+click must fold all subfolds as well --- lib/ace/edit_session/folding.js | 48 +++++++++++++++++++++------------ lib/ace/mode/folding/cstyle.js | 8 ------ 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 207e5436..5c8c5af2 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -570,10 +570,10 @@ function Folding() { var token = iterator.getCurrentToken(); if (token && /^comment|string/.test(token.type)) { var range = new Range(); - var t; + var re = new RegExp(token.type.replace(/\..*/, "\\.")); do { - t = iterator.stepBackward(); - } while(t && t.type == token.type); + token = iterator.stepBackward(); + } while(token && re.test(token.type)) iterator.stepForward(); range.start.row = iterator.getCurrentTokenRow(); @@ -582,29 +582,33 @@ function Folding() { var iterator = new TokenIterator(this, row, column); do { - t = iterator.stepForward(); - } while(t && t.type == token.type); - t = iterator.stepBackward(); + token = iterator.stepForward(); + } while(token && re.test(token.type)) + token = iterator.stepBackward(); range.end.row = iterator.getCurrentTokenRow(); - range.end.column = iterator.getCurrentTokenColumn() + t.value.length - 1; - return range; + range.end.column = iterator.getCurrentTokenColumn() + token.value.length - 1; + return range } }; - this.foldAll = function() { + this.foldAll = function(startRow, endRow) { var foldWidgets = this.foldWidgets; - for (var row = foldWidgets.length; row--; ) { + endRow = endRow || foldWidgets.length; + for (var row = startRow || 0; row < endRow; row++) { if (foldWidgets[row] == null) foldWidgets[row] = this.getFoldWidget(row); if (foldWidgets[row] != "start") continue; var range = this.getFoldWidgetRange(row); - if (range) + // sometimes range can be incompatible with existing fold + // wouldn't it be better for addFold to return null istead of throwing? + if (range && range.end.row < endRow) try { this.addFold("...", range); + } catch(e) {} } - }; + } // structured folding this.$setFolding = function(foldMode) { @@ -628,9 +632,11 @@ function Folding() { this.on('change', this.$updateFoldWidgets); }; - this.onFoldWidgetClick = function(row, htmlEvent) { + this.onFoldWidgetClick = function(row, e) { var type = this.getFoldWidget(row); var line = this.getLine(row); + var onlySubfolds = e.shiftKey; + var addSubfolds = onlySubfolds || e.ctrlKey || e.altKey || e.metaKey; var fold; if (type == "end") @@ -639,13 +645,21 @@ function Folding() { fold = this.getFoldAt(row, line.length, 1); if (fold) { - this.expandFold(fold); + if (addSubfolds) + this.removeFold(fold); + else + this.expandFold(fold); return; } var range = this.getFoldWidgetRange(row); - if (range) - this.addFold("...", range); + if (range) { + if (!onlySubfolds) + this.addFold("...", range); + + if (addSubfolds) + this.foldAll(range.start.row + 1, range.end.row); + } }; this.updateFoldWidgets = function(e) { @@ -669,4 +683,4 @@ function Folding() { exports.Folding = Folding; -}); \ No newline at end of file +}); diff --git a/lib/ace/mode/folding/cstyle.js b/lib/ace/mode/folding/cstyle.js index 7b06fe35..d44a7de6 100644 --- a/lib/ace/mode/folding/cstyle.js +++ b/lib/ace/mode/folding/cstyle.js @@ -69,9 +69,6 @@ oop.inherits(FoldMode, BaseFoldMode); end.row --; end.column = session.getLine(end.row).length; } - - } else { - end = {row: session.getLength(), column: 0}; } return Range.fromPoints(start, end); @@ -90,11 +87,6 @@ oop.inherits(FoldMode, BaseFoldMode); if (start) { start.column++; end.column--; - } else { - start = { - row: 0, - column: session.getLine(0).length - }; } return Range.fromPoints(start, end);