From 8c1d415bddb80faceb0820a0220d9d402d78c1c7 Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 25 Nov 2011 22:47:07 +0400 Subject: [PATCH 1/6] make text layer full length --- lib/ace/css/editor.css | 3 +++ lib/ace/layer/text.js | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index 8de99943..f884f35c 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -105,6 +105,9 @@ white-space: nowrap; height: 100%; width: 100%; + box-sizing: border-box; + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; } .ace_text-layer { diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index 98670b02..b7612d17 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -50,7 +50,6 @@ var EventEmitter = require("../lib/event_emitter").EventEmitter; var Text = function(parentEl) { this.element = dom.createElement("div"); this.element.className = "ace_layer ace_text-layer"; - this.element.style.width = "auto"; parentEl.appendChild(this.element); this.$characterSize = this.$measureSizes() || {width: 0, height: 0}; From 3412f89007362d4d7ea4d2a1acedac6be5b29dfe Mon Sep 17 00:00:00 2001 From: nightwing Date: Fri, 25 Nov 2011 23:58:13 +0400 Subject: [PATCH 2/6] add simple foldWidgets --- lib/ace/css/editor.css | 38 ++++++++++++++++++++++++++++++++- lib/ace/edit_session.js | 2 ++ lib/ace/edit_session/folding.js | 8 +++++++ lib/ace/editor.js | 13 +++++++++++ lib/ace/layer/gutter.js | 27 +++++++++++++++++++++++ lib/ace/virtual_renderer.js | 4 ++-- 6 files changed, 89 insertions(+), 3 deletions(-) diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index f884f35c..e88cb4fe 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -185,5 +185,41 @@ } .ace_dragging .ace_content { - cursor: move; + cursor: move; } + +.ace_folding-enabled .ace_gutter-cell { + padding-right: 9px!important; +} + +.ace_fold-widget { + margin-right: -9px; + display: inline-block; + height: 9px; + width: 9px; + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAZdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41Ljg3O4BdAAAAbUlEQVQoU2NgoBqIiIibCcRn0tPzbufmlt4uKam+XVXVdLuhoeN2UVHlGSCeCbYsMTHjdlxcSjOyzSB+a2vvbbhYXV2rGsgkoIQvSBBEg0wCiaM4GSQAsg5kAsg6DAUw1SAJkJtwKkBWSFaoAADKrzXSD2pEpgAAAABJRU5ErkJggg==") no-repeat; + background-origin: content-box; + padding: 1px 0; +} + +.ace_fold-widget.end{ + transform: scaleY(-1); + -moz-transform: scaleY(-1); + -webkit-transform: scaleY(-1); + opacity:0.8; +} + +.ace_fold-widget.closed{ + -moz-transform: none; + -webkit-transform: none; + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJBAMAAAASvxsjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAC1QTFRF////fn6FXl5kXl5kWFhecnJ5WFhecnJ5YWFoZ2dubW11dHR7enqCgICIhYWNjO3uwQAAAA90Uk5TACZNg5mZzMzb29vb29vbP7t0EwAAACtJREFUCFtjYAhgAIE6ARB5+SKIPKAD4mxg3ggkF2iB2JMngsQzwGocgBgA2zEHmb0961QAAAAASUVORK5CYII="); +} + +.ace_fold-widget:hover { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAZdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41Ljg3O4BdAAAASklEQVQoU2NgoBqQWHFlJhD/lzny/TY6BomD5MGWgSQPvPj5H4bXP4GwQeJw1wA5augKoaaqoTgZWSFWBTDVMIUgGq+nCSrApRsAuCZYT+KbmI0AAAAASUVORK5CYII="); +} + +.ace_fold-widget.closed:hover { + background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJBAMAAAASvxsjAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABVQTFRF////HMT3GKjUHMT3GKjUr+T5wOj5rFcpYAAAAAR0Uk5TACaZ2z7RZscAAAAkSURBVAhbY2BQYAABZwEQaWYIIk2TQRyzNBDHDMI2RKgBqQcAaNgD0/oixWYAAAAASUVORK5CYII="); +} + diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 544a635c..864cedad 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -515,6 +515,8 @@ var EditSession = function(text, mode) { this.tokenRe = mode.tokenRe; this.nonTokenRe = mode.nonTokenRe; + this.$setFolding(mode); + this._dispatchEvent("changeMode"); }; diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 32dd00c4..e05c7056 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -591,7 +591,15 @@ function Folding() { this.addFold(placeholder, range); }; + + // structured folding + this.$setFolding = function(mode) { + }; + + this.onFoldWidgetClick = function(e) { + }; } + exports.Folding = Folding; }); \ No newline at end of file diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 9b34b9b0..4953f3d3 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -637,6 +637,19 @@ var Editor = function(renderer, session) { return this.$modeBehaviours; }; + this.setShowFoldWidgets = function(show) { + var gutter = this.renderer.$gutterLayer; + if (gutter.getShowFoldWidgets() == show) + return; + + this.renderer.$gutterLayer.setShowFoldWidgets(show); + this.$showFoldWidgets = show; + this.renderer.updateFull(); + }; + this.getShowFoldWidgets = function() { + return this.renderer.$gutterLayer.getShowFoldWidgets(); + }; + this.remove = function(dir) { if (this.selection.isEmpty()){ if(dir == "left") diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index f5f527f3..ae3e8530 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -45,6 +45,7 @@ var Gutter = function(parentEl) { this.element = dom.createElement("div"); this.element.className = "ace_layer ace_gutter-layer"; parentEl.appendChild(this.element); + this.setShowFoldWidgets(this.$showFoldWidgets); this.$breakpoints = []; this.$annotations = []; @@ -105,6 +106,7 @@ var Gutter = function(parentEl) { var lastRow = config.lastRow; var fold = this.session.getNextFoldLine(i); var foldStart = fold ? fold.start.row : Infinity; + var foldWidgets = this.$showFoldWidgets && this.session.foldWidgets; while (true) { if(i > foldStart) { @@ -123,6 +125,18 @@ var Gutter = function(parentEl) { "' title='", annotation.text.join("\n"), "' style='height:", config.lineHeight, "px;'>", (i+1)); + if (foldWidgets) { + var c = foldWidgets[i]; + if (c == null) + c = foldWidgets[i] = this.session.getFoldWidget(i); + if(c) + html.push( + "" + ); + } + var wrappedRowLength = this.session.getRowLength(i) - 1; while (wrappedRowLength--) { html.push("
\xA6"); @@ -136,6 +150,19 @@ var Gutter = function(parentEl) { this.element.style.height = config.minHeight + "px"; }; + this.$showFoldWidgets = true; + this.setShowFoldWidgets = function(show) { + if (show) + dom.addCssClass(this.element, "ace_folding-enabled"); + else + dom.removeCssClass(this.element, "ace_folding-enabled"); + + this.$showFoldWidgets = show; + }; + this.getShowFoldWidgets = function() { + return this.$showFoldWidgets; + }; + }).call(Gutter.prototype); exports.Gutter = Gutter; diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index b491106a..b3ac15b3 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -264,11 +264,11 @@ var VirtualRenderer = function(container, theme) { }; this.$onGutterClick = function(e) { - var pageX = event.getDocumentX(e); var pageY = event.getDocumentY(e); + var row = this.screenToTextCoordinates(0, pageY).row; this._dispatchEvent("gutter" + e.type, { - row: this.screenToTextCoordinates(pageX, pageY).row, + row: row, htmlEvent: e }); }; From 77dfd0b29c2f7302a585cb50cdfb08797160fba8 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 28 Nov 2011 00:20:33 +0400 Subject: [PATCH 3/6] addFold must not throw if new fold is inside the existing one --- lib/ace/edit_session.js | 30 ++++++++++++++++++++++++++ lib/ace/edit_session/fold.js | 36 +++++++++++++++++++++++++++++++ lib/ace/edit_session/fold_line.js | 2 +- lib/ace/edit_session/folding.js | 29 +++++++++---------------- lib/ace/edit_session_test.js | 36 ++++++++++++++++++------------- lib/ace/range.js | 6 ++++++ lib/ace/virtual_renderer.js | 4 ++-- 7 files changed, 106 insertions(+), 37 deletions(-) diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 864cedad..658174e6 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -837,6 +837,12 @@ var EditSession = function(text, mode) { return Math.max(0, Math.min(row, this.doc.getLength()-1)); }; + this.$clipColumnToRow = function(row, column) { + if (column < 0) + return 0; + return Math.min(this.doc.getLine(row).length, column); + }; + this.$clipPositionToDocument = function(row, column) { column = Math.max(0, column); @@ -859,6 +865,30 @@ var EditSession = function(text, mode) { }; }; + this.$clipRangeToDocument = function(range) { + if (range.start.row < 0) { + range.start.row = 0; + range.start.column = 0 + } else { + range.start.column = this.$clipColumnToRow( + range.start.row, + range.start.column + ); + } + + var len = this.doc.getLength() - 1; + if (range.end.row > len) { + range.end.row = len; + range.end.column = this.doc.getLine(len).length; + } else { + range.end.column = this.$clipColumnToRow( + range.end.row, + range.end.column + ); + } + return range; + }; + // WRAPMODE this.$wrapLimit = 80; this.$useWrapMode = false; diff --git a/lib/ace/edit_session/fold.js b/lib/ace/edit_session/fold.js index bb9d98e7..fcf32dc7 100644 --- a/lib/ace/edit_session/fold.js +++ b/lib/ace/edit_session/fold.js @@ -74,6 +74,42 @@ var Fold = exports.Fold = function(range, placeholder) { return fold; }; + this.addSubFold = function(fold) { + if (this.range.isEequal(fold)) + return this; + + if (!this.range.containsRange(fold)) + throw "A fold can't intersect already existing fold" + fold.range + this.range; + + var row = fold.range.start.row, column = fold.range.start.column; + for (var i = 0, cmp = -1; i < this.subFolds.length; i++) { + cmp = this.subFolds[i].range.compare(row, column); + if (cmp != 1) + break; + } + var afterStart = this.subFolds[i]; + + if (cmp == 0) + return afterStart.addSubFold(fold) + + // cmp == -1 + var row = fold.range.end.row, column = fold.range.end.column; + for (var j = i, cmp = -1; j < this.subFolds.length; j++) { + cmp = this.subFolds[j].range.compare(row, column); + if (cmp != 1) + break; + } + var afterEnd = this.subFolds[j]; + + if (cmp == 0) + throw "A fold can't intersect already existing fold" + fold.range + this.range; + + var consumedFolds = this.subFolds.splice(i, j - i, fold) + fold.setFoldLine(this.foldLine); + + return fold; + } + }).call(Fold.prototype); }); \ No newline at end of file diff --git a/lib/ace/edit_session/fold_line.js b/lib/ace/edit_session/fold_line.js index 03017b9a..54e72a87 100644 --- a/lib/ace/edit_session/fold_line.js +++ b/lib/ace/edit_session/fold_line.js @@ -41,7 +41,7 @@ define(function(require, exports, module) { var Range = require("../range").Range; /** - * If the an array is passed in, the folds are expected to be sorted already. + * If an array is passed in, the folds are expected to be sorted already. */ function FoldLine(foldData, folds) { this.foldData = foldData; diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index e05c7056..dd942fcc 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -247,6 +247,8 @@ function Folding() { else fold = new Fold(range, placeholder); + this.$clipRangeToDocument(fold.range); + var startRow = fold.start.row; var startColumn = fold.start.column; var endRow = fold.end.row; @@ -259,29 +261,18 @@ function Folding() { if (startRow == endRow && endColumn - startColumn < 2) throw "The range has to be at least 2 characters width"; - var existingFold = this.getFoldAt(startRow, startColumn, 1); + var startFold = this.getFoldAt(startRow, startColumn, 1); + var endFold = this.getFoldAt(endRow, endColumn, -1); + if (startFold && endFold == startFold) + return startFold.addSubFold(fold); + if ( - existingFold - && existingFold.range.isEnd(endRow, endColumn) - && existingFold.range.isStart(startRow, startColumn) + (startFold && !startFold.range.isStart(startRow, startColumn)) + || (endFold && !endFold.range.isEnd(endRow, endColumn)) ) { - return fold; + throw "A fold can't intersect already existing fold" + fold.range + startFold.range; } - existingFold = this.getFoldAt(startRow, startColumn, 1); - if (existingFold && !existingFold.range.isStart(startRow, startColumn)) - throw "A fold can't start inside of an already existing fold"; - - existingFold = this.getFoldAt(endRow, endColumn, -1); - if (existingFold && !existingFold.range.isEnd(endRow, endColumn)) - throw "A fold can't end inside of an already existing fold"; - - if (endRow >= this.doc.getLength()) - throw "End of fold is outside of the document."; - - if (endColumn > this.getLine(endRow).length || startColumn > this.getLine(startRow).length) - throw "End of fold is outside of the document."; - // Check if there are folds in the range we create the new fold for. var folds = this.getFoldsInRange(fold.range); if (folds.length > 0) { diff --git a/lib/ace/edit_session_test.js b/lib/ace/edit_session_test.js index aeb2cef6..cdba3444 100644 --- a/lib/ace/edit_session_test.js +++ b/lib/ace/edit_session_test.js @@ -109,10 +109,10 @@ module.exports = { assert.position(session.findMatchingBracket({row: 6, column: 20}), 1, 15); assert.position(session.findMatchingBracket({row: 1, column: 22}), 1, 20); assert.position(session.findMatchingBracket({row: 3, column: 31}), 3, 21); - assert.position(session.findMatchingBracket({row: 4, column: 24}), 4, 19); + assert.position(session.findMatchingBracket({row: 4, column: 24}), 4, 19); assert.equal(session.findMatchingBracket({row: 0, column: 1}), null); }, - + "test: find matching closing bracket in JavaScript mode" : function() { var lines = [ "function foo() {", @@ -131,7 +131,7 @@ module.exports = { assert.position(session.findMatchingBracket({row: 1, column: 16}), 6, 19); assert.position(session.findMatchingBracket({row: 1, column: 21}), 1, 21); assert.position(session.findMatchingBracket({row: 3, column: 22}), 3, 30); - assert.position(session.findMatchingBracket({row: 4, column: 20}), 4, 23); + assert.position(session.findMatchingBracket({row: 4, column: 20}), 4, 23); }, "test: handle unbalanced brackets in JavaScript mode" : function() { @@ -223,7 +223,7 @@ module.exports = { "12\t\t34", "ぁぁa" ]); - + assert.equal(session.getScreenLastRowColumn(0), 4); assert.equal(session.getScreenLastRowColumn(1), 10); assert.equal(session.getScreenLastRowColumn(2), 5); @@ -267,13 +267,13 @@ module.exports = { "12\t\t34", "ぁぁa" ]); - + assert.position(session.documentToScreenPosition(0, 3), 0, 3); assert.position(session.documentToScreenPosition(1, 3), 1, 4); assert.position(session.documentToScreenPosition(1, 4), 1, 8); assert.position(session.documentToScreenPosition(2, 2), 2, 4); }, - + "test: documentToScreen with soft wrap": function() { var session = new EditSession(["foo bar foo bar"]); session.setUseWrapMode(true); @@ -283,7 +283,7 @@ module.exports = { assert.position(session.documentToScreenPosition(0, 11), 0, 11); assert.position(session.documentToScreenPosition(0, 12), 1, 0); }, - + "test: documentToScreen with soft wrap and multibyte characters": function() { session = new EditSession(["ぁぁa"]); @@ -298,7 +298,7 @@ module.exports = { "test: documentToScreen should clip position to the document boundaries": function() { var session = new EditSession("foo bar\njuhu kinners"); - + assert.position(session.documentToScreenPosition(-1, 4), 0, 0); assert.position(session.documentToScreenPosition(3, 0), 1, 12); }, @@ -340,7 +340,7 @@ module.exports = { assert.position(session.screenToDocumentPosition(0, 12), 0, 11); assert.position(session.screenToDocumentPosition(0, 20), 0, 11); }, - + "test: screenToDocument with soft wrap and multi byte characters": function() { session = new EditSession(["ぁ a"]); session.setUseWrapMode(true); @@ -352,10 +352,10 @@ module.exports = { assert.position(session.screenToDocumentPosition(0, 4), 0, 3); assert.position(session.screenToDocumentPosition(0, 5), 0, 3); }, - + "test: screenToDocument should clip position to the document boundaries": function() { var session = new EditSession("foo bar\njuhu kinners"); - + assert.position(session.screenToDocumentPosition(-1, 4), 0, 0); assert.position(session.screenToDocumentPosition(0, -1), 0, 0); assert.position(session.screenToDocumentPosition(0, 30), 0, 7); @@ -649,7 +649,7 @@ module.exports = { var session = createFoldTestSession(); var undoManager = session.getUndoManager(); var foldLines = session.$foldData; - + function insert(row, column, text) { session.insert({row: row, column: column}, text); @@ -930,7 +930,7 @@ module.exports = { } } - tryAddFold("foo", new Range(0, 13, 0, 17), true); + tryAddFold("foo", new Range(0, 13, 0, 17), false); tryAddFold("foo", new Range(0, 14, 0, 18), true); tryAddFold("foo", new Range(0, 13, 0, 18), false); assert.equal(session.$foldData[0].folds.length, 1); @@ -940,9 +940,9 @@ module.exports = { assert.equal(session.$foldData[0].folds.length, 2); session.removeFold(fold); - tryAddFold("foo", new Range(0, 18, 0, 22), true); + tryAddFold("foo", new Range(0, 18, 0, 22), false); tryAddFold("foo", new Range(0, 18, 0, 19), true); - tryAddFold("foo", new Range(0, 22, 1, 10), true); + tryAddFold("foo", new Range(0, 22, 1, 10), false); }, "test add subfolds": function() { @@ -972,6 +972,12 @@ module.exports = { assert.equal(foldData[0].folds.length, 1); assert.equal(foldData[0].folds[0], oldFold); assert.equal(fold.subFolds.length, 0); + + session.unfold(null, true); + fold = session.addFold("fold0", new Range(0, 0, 0, 21)); + session.addFold("fold0", new Range(0, 1, 0, 5)); + session.addFold("fold0", new Range(0, 6, 0, 8)); + assert.equal(fold.subFolds.length, 2); } }; diff --git a/lib/ace/range.js b/lib/ace/range.js index 596f3f57..0121f8aa 100644 --- a/lib/ace/range.js +++ b/lib/ace/range.js @@ -50,6 +50,12 @@ var Range = function(startRow, startColumn, endRow, endColumn) { }; (function() { + this.isEequal = function(range) { + return this.start.row == range.start.row && + this.end.row == range.end.row && + this.start.column == range.start.column && + this.end.column == range.end.column + }; this.toString = function() { return ("Range: [" + this.start.row + "/" + this.start.column + diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index b3ac15b3..b491106a 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -264,11 +264,11 @@ var VirtualRenderer = function(container, theme) { }; this.$onGutterClick = function(e) { + var pageX = event.getDocumentX(e); var pageY = event.getDocumentY(e); - var row = this.screenToTextCoordinates(0, pageY).row; this._dispatchEvent("gutter" + e.type, { - row: row, + row: this.screenToTextCoordinates(pageX, pageY).row, htmlEvent: e }); }; From 2424261f7a3054cbe27fbc5897e485b07d72ba36 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 28 Nov 2011 00:53:42 +0400 Subject: [PATCH 4/6] use tokenIterator to find comment range --- lib/ace/edit_session/folding.js | 37 ++++++++++++++++++--------------- lib/ace/virtual_renderer.js | 4 ++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index dd942fcc..79a28786 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -41,6 +41,7 @@ define(function(require, exports, module) { var Range = require("../range").Range; var FoldLine = require("./fold_line").FoldLine; var Fold = require("./fold").Fold; +var TokenIterator = require("../token_iterator").TokenIterator; function Folding() { /** @@ -533,24 +534,26 @@ function Folding() { range.start.column++; } else { - var token = this.getTokenAt(cursor.row, cursor.column); + var iterator = new TokenIterator(this, cursor.row, cursor.column); + var token = iterator.getCurrentToken(); if (token && /^comment|string/.test(token.type)) { - var startRow = cursor.row; - var endRow = cursor.row; - var t = token; - while ((t = this.getTokenAt(startRow - 1)) && t.type == token.type) { - startRow --; - token = t; - } - range.start.row = startRow; - range.start.column = token.start + 2; - - while ((t = this.getTokenAt(endRow + 1, 0)) && t.type == token.type) { - endRow ++; - token = t; - } - range.end.row = endRow; - range.end.column = token.start + token.value.length - 1; + do { + t = iterator.stepBackward(); + } while(t && t.type == token.type) + + iterator.stepForward(); + range.start.row = iterator.getCurrentTokenRow(); + range.start.column = iterator.getCurrentTokenColumn() + 2; + + var iterator = new TokenIterator(this, cursor.row, cursor.column); + + do { + t = iterator.stepForward(); + } while(t && t.type == token.type) + t = iterator.stepBackward(); + + range.end.row = iterator.getCurrentTokenRow(); + range.end.column = iterator.getCurrentTokenColumn() + t.value.length - 1; } } } else { diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index b491106a..b3ac15b3 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -264,11 +264,11 @@ var VirtualRenderer = function(container, theme) { }; this.$onGutterClick = function(e) { - var pageX = event.getDocumentX(e); var pageY = event.getDocumentY(e); + var row = this.screenToTextCoordinates(0, pageY).row; this._dispatchEvent("gutter" + e.type, { - row: this.screenToTextCoordinates(pageX, pageY).row, + row: row, htmlEvent: e }); }; From aa9ff0d7635c249dd8cb346bd4baf469a7497f19 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 28 Nov 2011 01:21:23 +0400 Subject: [PATCH 5/6] ctrl+dblClick must remove fold and it's subfolds --- lib/ace/mouse/default_handlers.js | 5 ++++- lib/ace/mouse/mouse_event.js | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/ace/mouse/default_handlers.js b/lib/ace/mouse/default_handlers.js index a574e46a..85ce5e5c 100644 --- a/lib/ace/mouse/default_handlers.js +++ b/lib/ace/mouse/default_handlers.js @@ -259,7 +259,10 @@ function DefaultHandlers(editor) { // If the user dclicked on a fold, then expand it. var fold = editor.session.getFoldAt(pos.row, pos.column, 1); if (fold) { - editor.session.expandFold(fold); + if (ev.getAccelKey()) + editor.session.removeFold(fold); + else + editor.session.expandFold(fold); } else { editor.moveCursorToPosition(pos); diff --git a/lib/ace/mouse/mouse_event.js b/lib/ace/mouse/mouse_event.js index b8ffcf5c..bc66a9da 100644 --- a/lib/ace/mouse/mouse_event.js +++ b/lib/ace/mouse/mouse_event.js @@ -128,6 +128,10 @@ var MouseEvent = exports.MouseEvent = function(domEvent, editor) { return this.domEvent.shiftKey; }; + this.getAccelKey = function() { + return this.domEvent.ctrlKey || this.domEvent.metaKey ; + }; + }).call(MouseEvent.prototype); }); \ No newline at end of file From 4677db58d86fbf17bbbf86f73b8f7be133b218d4 Mon Sep 17 00:00:00 2001 From: nightwing Date: Mon, 28 Nov 2011 18:09:39 +0400 Subject: [PATCH 6/6] add fold detection rules for python and c style languages --- lib/ace/edit_session/folding.js | 229 ++++++++++++++++++++++++++++---- lib/ace/mode/c_cpp.js | 2 + lib/ace/mode/csharp.js | 54 ++++---- lib/ace/mode/css.js | 2 + lib/ace/mode/java.js | 2 + lib/ace/mode/javascript.js | 2 + lib/ace/mode/json.js | 2 + lib/ace/mode/python.js | 5 + lib/ace/virtual_renderer.js | 3 + 9 files changed, 250 insertions(+), 51 deletions(-) diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 79a28786..73cef50a 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -534,27 +534,7 @@ function Folding() { range.start.column++; } else { - var iterator = new TokenIterator(this, cursor.row, cursor.column); - var token = iterator.getCurrentToken(); - if (token && /^comment|string/.test(token.type)) { - do { - t = iterator.stepBackward(); - } while(t && t.type == token.type) - - iterator.stepForward(); - range.start.row = iterator.getCurrentTokenRow(); - range.start.column = iterator.getCurrentTokenColumn() + 2; - - var iterator = new TokenIterator(this, cursor.row, cursor.column); - - do { - t = iterator.stepForward(); - } while(t && t.type == token.type) - t = iterator.stepBackward(); - - range.end.row = iterator.getCurrentTokenRow(); - range.end.column = iterator.getCurrentTokenColumn() + t.value.length - 1; - } + range = this.getCommentFoldRange(cursor.row, cursor.column) || range; } } else { var folds = this.getFoldsInRange(range); @@ -574,7 +554,6 @@ function Folding() { return } - var placeholder = "..."; if (!range.isMultiLine()) { placeholder = this.getTextRange(range); @@ -586,12 +565,212 @@ function Folding() { this.addFold(placeholder, range); }; - // structured folding - this.$setFolding = function(mode) { + this.getCommentFoldRange = function(row, column) { + var iterator = new TokenIterator(this, row, column); + var token = iterator.getCurrentToken(); + if (token && /^comment|string/.test(token.type)) { + var range = new Range; + do { + t = iterator.stepBackward(); + } while(t && t.type == token.type) + + iterator.stepForward(); + range.start.row = iterator.getCurrentTokenRow(); + range.start.column = iterator.getCurrentTokenColumn() + 2; + + var iterator = new TokenIterator(this, row, column); + + do { + t = iterator.stepForward(); + } while(t && t.type == token.type) + t = iterator.stepBackward(); + + range.end.row = iterator.getCurrentTokenRow(); + range.end.column = iterator.getCurrentTokenColumn() + t.value.length - 1; + return range + } }; - this.onFoldWidgetClick = function(e) { + this.foldAll = function() { + var foldWidgets = this.foldWidgets + for (var row = foldWidgets.length; row--; ) { + if (foldWidgets[row] == null) + foldWidgets[row] = this.getFoldWidget(row) + if (foldWidgets[row] != "start") + continue + + var range = this.getFoldWidgetRange(row); + console.log(row, range) + if (range) + this.addFold("...", range) + } + } + + // structured folding + this.$setFolding = function(mode) { + mode = mode && mode.foldingRules; + var foldRules = Folding.commonFoldingRules + if (typeof mode == "string") + mode = foldRules[mode]; + + if (mode) { + this.foldWidgets = []; + this.removeListener('change', this.$updateFoldWidgets); + + if (mode.getFoldWidget) + this.getFoldWidget = mode.getFoldWidget; + else if (mode.foldingStopMarker) + this.getFoldWidget = foldRules.$testBoth; + else + this.getFoldWidget = foldRules.$testStart; + + this.foldingStopMarker = mode.foldingStopMarker; + this.foldingStartMarker = mode.foldingStartMarker; + + if (typeof mode.getFoldWidgetRange == "string") + this.getFoldWidgetRange = foldRules[mode.getFoldWidgetRange]; + else + this.getFoldWidgetRange = mode.getFoldWidgetRange; + + + this.$updateFoldWidgets = (mode.onChange || foldRules.onChange).bind(this); + + this.on('change', this.$updateFoldWidgets); + } else { + this.foldWidgets = null; + this.removeListener('change', this.$updateFoldWidgets); + } }; + + this.onFoldWidgetClick = function(row, htmlEvent) { + var type = this.getFoldWidget(row); + var line = this.getLine(row); + + if (type == "end") + var fold = this.getFoldAt(row, 0, -1); + else + var fold = this.getFoldAt(row, line.length, 1); + + if (fold) { + this.expandFold(fold); + return; + } + + range = this.getFoldWidgetRange(row); + if (range) + this.addFold("...", range); + } +} + +Folding.commonFoldingRules = { + $testStart: function(row) { + if(this.foldingStartMarker.test(this.getLine(row))) + return "start"; + return ""; + }, + $testBoth: function(row) { + var line = this.getLine(row); + if(this.foldingStartMarker.test(line)) + return "start"; + if(this.foldingStopMarker.test(line)) + return "end"; + return ""; + }, + onChange: function(e) { + var delta = e.data; + var range = delta.range; + var firstRow = range.start.row; + var len = range.end.row - firstRow; + + if (len == 0) { + this.foldWidgets[firstRow] = null; + } else if (delta.action == "removeText" || delta.action == "removeLines") { + this.foldWidgets.splice(firstRow, len + 1, null); + } else { + var args = Array(len + 1); + args.unshift(firstRow, 1) + this.foldWidgets.splice.apply(this.foldWidgets, args); + } + }, + + indentationBlock: function(row) { + var re = /^\s*/; + var startRow = row, endRow = row; + var line = this.getLine(row); + var startColumn = line.length - 1; + var startLevel = line.match(re)[0].length; + + while (line = this.getLine(++row)) { + var level = line.match(re)[0].length; + + if (level == line.length) + continue; + + if (level <= startLevel) + break; + + endRow = row; + } + + if (endRow > startRow) { + endColumn = this.getLine(endRow).length; + return new Range(startRow, startColumn, endRow, endColumn); + } + }, + + "cStyle": { + foldingStartMarker : /(\{|\[)[^\}\]]*$|^\s*(\/\*)/, + foldingStopMarker : /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/, + getFoldWidgetRange: function(row) { + var line = this.getLine(row); + var match = line.match(this.foldingStartMarker); + if (match) { + var i = match.index; + + if (match[2]) + return this.getCommentFoldRange(row, i + match[0].length); + + var start = {row: row, column: i+1}; + var end = this.$findClosingBracket(match[1], start); + if (end) { + var fw = this.foldWidgets[end.row]; + if (fw == null) + fw = this.getFoldWidget(end.row); + + if (fw == "start"){ + end.row --; + end.column = this.getLine(end.row).length; + } + + } else { + end = {row: this.getLength(), column: 0}; + } + + return Range.fromPoints(start, end); + } + + var match = line.match(this.foldingStopMarker); + if (match) { + var i = match.index + match[0].length; + + if (match[2]) + return this.getCommentFoldRange(row, i); + + var end = {row: row, column: i}; + var start = this.$findOpeningBracket(match[1], end) + if (start){ + start.column++; + end.column--; + } else { + start = {row: 0, column: this.getLine(0).length} + } + + return Range.fromPoints(start, end); + } + } + }, + // TODO: folding based only on indentation + "indentation": null } exports.Folding = Folding; diff --git a/lib/ace/mode/c_cpp.js b/lib/ace/mode/c_cpp.js index 0d141904..9ff9a67f 100644 --- a/lib/ace/mode/c_cpp.js +++ b/lib/ace/mode/c_cpp.js @@ -55,6 +55,8 @@ oop.inherits(Mode, TextMode); (function() { + this.foldingRules = "cStyle"; + this.toggleCommentLines = function(state, doc, startRow, endRow) { var outdent = true; var outentedRows = []; diff --git a/lib/ace/mode/csharp.js b/lib/ace/mode/csharp.js index a5a8c27b..e5332cef 100644 --- a/lib/ace/mode/csharp.js +++ b/lib/ace/mode/csharp.js @@ -16,34 +16,36 @@ oop.inherits(Mode, TextMode); (function() { - this.getNextLineIndent = function(state, line, tab) { - var indent = this.$getIndent(line); + this.foldingRules = "cStyle"; - var tokenizedLine = this.$tokenizer.getLineTokens(line, state); - var tokens = tokenizedLine.tokens; - var endState = tokenizedLine.state; + this.getNextLineIndent = function(state, line, tab) { + var indent = this.$getIndent(line); + + var tokenizedLine = this.$tokenizer.getLineTokens(line, state); + var tokens = tokenizedLine.tokens; + var endState = tokenizedLine.state; + + if (tokens.length && tokens[tokens.length-1].type == "comment") { + return indent; + } + + if (state == "start") { + var match = line.match(/^.*[\{\(\[]\s*$/); + if (match) { + indent += tab; + } + } + + return indent; + }; - if (tokens.length && tokens[tokens.length-1].type == "comment") { - return indent; - } - - if (state == "start") { - var match = line.match(/^.*[\{\(\[]\s*$/); - if (match) { - indent += tab; - } - } - - return indent; - }; - - this.checkOutdent = function(state, line, input) { - return this.$outdent.checkOutdent(line, input); - }; - - this.autoOutdent = function(state, doc, row) { - this.$outdent.autoOutdent(doc, row); - }; + this.checkOutdent = function(state, line, input) { + return this.$outdent.checkOutdent(line, input); + }; + + this.autoOutdent = function(state, doc, row) { + this.$outdent.autoOutdent(doc, row); + }; this.createWorker = function(session) { diff --git a/lib/ace/mode/css.js b/lib/ace/mode/css.js index 6c8267db..02ea953f 100644 --- a/lib/ace/mode/css.js +++ b/lib/ace/mode/css.js @@ -52,6 +52,8 @@ oop.inherits(Mode, TextMode); (function() { + this.foldingRules = "cStyle"; + this.getNextLineIndent = function(state, line, tab) { var indent = this.$getIndent(line); diff --git a/lib/ace/mode/java.js b/lib/ace/mode/java.js index d8f5a241..f69bb8ad 100644 --- a/lib/ace/mode/java.js +++ b/lib/ace/mode/java.js @@ -16,6 +16,8 @@ oop.inherits(Mode, JavaScriptMode); (function() { + this.foldingRules = "cStyle"; + this.createWorker = function(session) { return null; }; diff --git a/lib/ace/mode/javascript.js b/lib/ace/mode/javascript.js index 38553837..5524c874 100644 --- a/lib/ace/mode/javascript.js +++ b/lib/ace/mode/javascript.js @@ -55,6 +55,8 @@ oop.inherits(Mode, TextMode); (function() { + this.foldingRules = "cStyle"; + this.toggleCommentLines = function(state, doc, startRow, endRow) { var outdent = true; var outentedRows = []; diff --git a/lib/ace/mode/json.js b/lib/ace/mode/json.js index e908d53c..d725a22f 100644 --- a/lib/ace/mode/json.js +++ b/lib/ace/mode/json.js @@ -54,6 +54,8 @@ oop.inherits(Mode, TextMode); (function() { + this.foldingRules = "cStyle"; + this.getNextLineIndent = function(state, line, tab) { var indent = this.$getIndent(line); diff --git a/lib/ace/mode/python.js b/lib/ace/mode/python.js index 5d95012c..caeee70b 100644 --- a/lib/ace/mode/python.js +++ b/lib/ace/mode/python.js @@ -52,6 +52,11 @@ oop.inherits(Mode, TextMode); (function() { + this.foldingRules = { + foldingStartMarker: /\:(:?\s*)?(:?#.*)?$/, + getFoldWidgetRange: "indentationBlock" + }; + this.toggleCommentLines = function(state, doc, startRow, endRow) { var outdent = true; var outentedRows = []; diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index b3ac15b3..1013384a 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -267,6 +267,9 @@ var VirtualRenderer = function(container, theme) { var pageY = event.getDocumentY(e); var row = this.screenToTextCoordinates(0, pageY).row; + if (e.target.className.indexOf('ace_fold-widget') != -1) + return this.session.onFoldWidgetClick(row, e); + this._dispatchEvent("gutter" + e.type, { row: row, htmlEvent: e