diff --git a/demo/kitchen-sink/docs/c9search.c9search_results b/demo/kitchen-sink/docs/c9search.c9search_results index bf0fdb87..0ad9ccd4 100644 --- a/demo/kitchen-sink/docs/c9search.c9search_results +++ b/demo/kitchen-sink/docs/c9search.c9search_results @@ -1,4 +1,4 @@ -Searching for 'var' in /workspace/configs +Searching for var in/.c9/metadata/workspace/pluginsregexp, case sensitive, whole word configs/default.js: 1: var fs = require("fs"); diff --git a/lib/ace/autocomplete/popup.js b/lib/ace/autocomplete/popup.js index 891cf1e8..76deb274 100644 --- a/lib/ace/autocomplete/popup.js +++ b/lib/ace/autocomplete/popup.js @@ -209,6 +209,9 @@ var AcePopup = function(parentNode) { } // public + popup.isOpen = false; + popup.isTopdown = false; + popup.data = []; popup.setData = function(list) { popup.data = list || []; @@ -233,26 +236,33 @@ var AcePopup = function(parentNode) { popup._signal("select"); } }; + + popup.on("changeSelection", function() { + if (popup.isOpen) + popup.setRow(popup.selection.lead.row); + }); popup.hide = function() { this.container.style.display = "none"; this._signal("hide"); popup.isOpen = false; }; - popup.show = function(pos, lineHeight) { + popup.show = function(pos, lineHeight, topdownOnly) { var el = this.container; var screenHeight = window.innerHeight; var renderer = this.renderer; // var maxLines = Math.min(renderer.$maxLines, this.session.getLength()); var maxH = renderer.$maxLines * lineHeight * 1.4; var top = pos.top + this.$borderSize; - if (top + maxH > screenHeight - lineHeight) { + if (top + maxH > screenHeight - lineHeight && !topdownOnly) { el.style.top = ""; el.style.bottom = screenHeight - top + "px"; + popup.isTopdown = false; } else { top += lineHeight; el.style.top = top + "px"; el.style.bottom = ""; + popup.isTopdown = true; } el.style.left = pos.left + "px"; diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index 9c663d18..0ba9956c 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -35,12 +35,13 @@ var lang = require("../lib/lang"); var config = require("../config"); function bindKey(win, mac) { - return { - win: win, - mac: mac - }; + return {win: win, mac: mac}; } +/* + multiSelectAction: "forEach"|"forEachLine"|function|undefined, + scrollIntoView: true|"cursor"|"center"|"selectionPart" +*/ exports.commands = [{ name: "showSettingsMenu", bindKey: bindKey("Ctrl-,", "Command-,"), @@ -75,21 +76,46 @@ exports.commands = [{ name: "fold", bindKey: bindKey("Alt-L|Ctrl-F1", "Command-Alt-L|Command-F1"), exec: function(editor) { editor.session.toggleFold(false); }, + scrollIntoView: "center", readOnly: true }, { name: "unfold", bindKey: bindKey("Alt-Shift-L|Ctrl-Shift-F1", "Command-Alt-Shift-L|Command-Shift-F1"), exec: function(editor) { editor.session.toggleFold(true); }, + scrollIntoView: "center", + readOnly: true +}, { + name: "toggleFoldWidget", + bindKey: bindKey("F2", "F2"), + exec: function(editor) { editor.session.toggleFoldWidget(); }, + scrollIntoView: "center", + readOnly: true +}, { + name: "toggleParentFoldWidget", + bindKey: bindKey("Alt-F2", "Alt-F2"), + exec: function(editor) { editor.session.toggleFoldWidget(true); }, + scrollIntoView: "center", readOnly: true }, { name: "foldall", - bindKey: bindKey("Alt-0", "Command-Option-0"), + bindKey: bindKey("Ctrl-Alt-0", "Ctrl-Command-Option-0"), exec: function(editor) { editor.session.foldAll(); }, + scrollIntoView: "center", + readOnly: true +}, { + name: "foldOther", + bindKey: bindKey("Alt-0", "Command-Option-0"), + exec: function(editor) { + editor.session.foldAll(); + editor.session.unfold(editor.selection.getAllRanges()); + }, + scrollIntoView: "center", readOnly: true }, { name: "unfoldall", bindKey: bindKey("Alt-Shift-0", "Command-Option-Shift-0"), exec: function(editor) { editor.session.unfold(); }, + scrollIntoView: "center", readOnly: true }, { name: "findnext", @@ -345,7 +371,8 @@ exports.commands = [{ name: "togglecomment", bindKey: bindKey("Ctrl-/", "Command-/"), exec: function(editor) { editor.toggleCommentLines(); }, - multiSelectAction: "forEachLine" + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" }, { name: "toggleBlockComment", bindKey: bindKey("Ctrl-Shift-/", "Command-Shift-/"), @@ -439,26 +466,31 @@ exports.commands = [{ name: "outdent", bindKey: bindKey("Shift-Tab", "Shift-Tab"), exec: function(editor) { editor.blockOutdent(); }, - multiSelectAction: "forEach" + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" }, { name: "indent", bindKey: bindKey("Tab", "Tab"), exec: function(editor) { editor.indent(); }, - multiSelectAction: "forEach" -},{ + multiSelectAction: "forEach", + scrollIntoView: "selectionPart" +}, { name: "blockoutdent", bindKey: bindKey("Ctrl-[", "Ctrl-["), exec: function(editor) { editor.blockOutdent(); }, - multiSelectAction: "forEachLine" -},{ + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" +}, { name: "blockindent", bindKey: bindKey("Ctrl-]", "Ctrl-]"), exec: function(editor) { editor.blockIndent(); }, - multiSelectAction: "forEachLine" + multiSelectAction: "forEachLine", + scrollIntoView: "selectionPart" }, { name: "insertstring", exec: function(editor, str) { editor.insert(str); }, - multiSelectAction: "forEach" + multiSelectAction: "forEach", + scrollIntoView: "cursor" }, { name: "inserttext", exec: function(editor, args) { diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index b291c11f..65d80d3d 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -158,7 +158,7 @@ z-index: 1; position: absolute; overflow: hidden; - white-space: nowrap; + white-space: pre; height: 100%; width: 100%; -moz-box-sizing: border-box; diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index a9b2ddd1..e6725a51 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -108,6 +108,18 @@ function Folding() { return foundFolds; }; + + this.getFoldsInRangeList = function(ranges) { + if (Array.isArray(ranges)) { + var folds = []; + ranges.forEach(function(range) { + folds = folds.concat(this.getFoldsInRange(range)); + }, this); + } else { + var folds = this.getFoldsInRange(ranges); + } + return folds; + } /* * Returns all folds in the document @@ -413,7 +425,7 @@ function Folding() { }; this.expandFold = function(fold) { - this.removeFold(fold); + this.removeFold(fold); fold.subFolds.forEach(function(subFold) { fold.restoreRange(subFold); this.addFold(subFold); @@ -441,8 +453,8 @@ function Folding() { range = Range.fromPoints(location, location); else range = location; - - folds = this.getFoldsInRange(range); + + folds = this.getFoldsInRangeList(range); if (expandInner) { this.removeFolds(folds); } else { @@ -450,7 +462,7 @@ function Folding() { // expandFolds several times. while (folds.length) { this.expandFolds(folds); - folds = this.getFoldsInRange(range); + folds = this.getFoldsInRangeList(range); } } }; @@ -636,17 +648,16 @@ function Folding() { continue; var range = this.getFoldWidgetRange(row); - var rangeEndRow = range.end.row; // sometimes range can be incompatible with existing fold // TODO change addFold to return null istead of throwing if (range && range.isMultiLine() - && rangeEndRow <= endRow + && range.end.row <= endRow && range.start.row >= startRow ) try { var fold = this.addFold("...", range); fold.collapseChildren = depth; // addFold can change the range - row = rangeEndRow; + row = range.end.row; } catch(e) {} } }; @@ -727,25 +738,34 @@ function Folding() { } this.onFoldWidgetClick = function(row, e) { + e = e.domEvent; + var options = { + children: e.shiftKey, + all: e.ctrlKey || e.metaKey, + siblings: e.altKey + }; + + var range = this.$toggleFoldWidget(row, options); + if (!range) + (e.target || e.srcElement).className += " ace_invalid"; + }; + + this.$toggleFoldWidget = function(row, options) { var type = this.getFoldWidget(row); var line = this.getLine(row); - e = e.domEvent; - var children = e.shiftKey; - var all = e.ctrlKey || e.metaKey; - var siblings = e.altKey; var dir = type === "end" ? -1 : 1; var fold = this.getFoldAt(row, dir === -1 ? 0 : line.length, dir); if (fold) { - if (children || all) + if (options.children || options.all) this.removeFold(fold); else this.expandFold(fold); return; } - var range = this.getFoldWidgetRange(row); + var range = this.getFoldWidgetRange(row, true); // sometimes singleline folds can be missed by the code above if (range && !range.isMultiLine()) { fold = this.getFoldAt(range.start.row, range.start.column, 1); @@ -755,24 +775,48 @@ function Folding() { } } - if (siblings) { + if (options.siblings) { var data = this.getParentFoldRangeData(row); if (data.range) { var startRow = data.range.start.row + 1; var endRow = data.range.end.row; } - this.foldAll(startRow, endRow, all ? 10000 : 0); - } else if (children) { - var endRow = range ? range.end.row : this.getLength(); - this.foldAll(row + 1, range.end.row, all ? 10000 : 0); + this.foldAll(startRow, endRow, options.all ? 10000 : 0); + } else if (options.children) { + endRow = range ? range.end.row : this.getLength(); + this.foldAll(row + 1, range.end.row, options.all ? 10000 : 0); } else if (range) { - if (all) + if (options.all) range.collapseChildren = 10000; this.addFold("...", range); } - if (!range) - (e.target || e.srcElement).className += " ace_invalid" + return range; + }; + + + + this.toggleFoldWidget = function(toggleParent) { + var row = this.selection.getCursor().row; + row = this.getRowFoldStart(row); + var range = this.$toggleFoldWidget(row, {}); + + if (range) + return; + // handle toggleParent + var data = this.getParentFoldRangeData(row, true); + range = data.range || data.firstRange; + + if (range) { + row = range.start.row; + var fold = this.getFoldAt(row, this.getLine(row).length, 1); + + if (fold) { + this.removeFold(fold); + } else { + this.addFold("...", range); + } + } }; this.updateFoldWidgets = function(e) { diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 4ff568c9..5ce8135a 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -170,12 +170,33 @@ var Editor = function(renderer, session) { command: commadEvent.command || {}, args: commadEvent.args }; + + var command = this.curOp.command; + if (command && command.scrollIntoView) + this.$blockScrolling++; this.selections.push(this.selection.toJSON()); }; this.endOperation = function() { if (this.curOp) { + var command = this.curOp.command; + if (command && command.scrollIntoView) { + this.$blockScrolling--; + switch (command.scrollIntoView) { + case "center": + this.renderer.scrollCursorIntoView(null, 0.5); + break; + case "cursor": + this.renderer.scrollCursorIntoView(); + break; + case "selectionPart": + this.renderer.scrollCursorIntoView(); + break; + default: + break; + } + } this.prevOp = this.curOp; this.curOp = null; } @@ -1643,8 +1664,8 @@ var Editor = function(renderer, session) { var range = this.getSelectionRange().collapseRows(); return { - first: range.start.row, - last: range.end.row + first: this.session.getRowFoldStart(range.start.row), + last: this.session.getRowFoldEnd(range.end.row) }; }; diff --git a/lib/ace/keyboard/hash_handler.js b/lib/ace/keyboard/hash_handler.js index baa88158..cb02f170 100644 --- a/lib/ace/keyboard/hash_handler.js +++ b/lib/ace/keyboard/hash_handler.js @@ -119,6 +119,9 @@ function HashHandler(config, platform) { if (typeof command === "function") command = { exec: command }; + if (typeof command !== "object") + return; + if (!command.name) command.name = name; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index c1130d8f..cd8622d8 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -75,8 +75,7 @@ var Gutter = function(parentEl) { this.setAnnotations = function(annotations) { // iterate over sparse array - this.$annotations = [] - var rowInfo, row; + this.$annotations = []; for (var i = 0; i < annotations.length; i++) { var annotation = annotations[i]; var row = annotation.row; @@ -112,7 +111,7 @@ var Gutter = function(parentEl) { } else if (delta.action == "removeText" || delta.action == "removeLines") { this.$annotations.splice(firstRow, len + 1, null); } else { - var args = Array(len + 1); + var args = new Array(len + 1); args.unshift(firstRow, 1); this.$annotations.splice.apply(this.$annotations, args); } @@ -121,13 +120,16 @@ var Gutter = function(parentEl) { this.update = function(config) { var firstRow = config.firstRow; var lastRow = config.lastRow; - var fold = this.session.getNextFoldLine(firstRow); + var session = this.session; + var fold = session.getNextFoldLine(firstRow); var foldStart = fold ? fold.start.row : Infinity; - var foldWidgets = this.$showFoldWidgets && this.session.foldWidgets; - var breakpoints = this.session.$breakpoints; - var decorations = this.session.$decorations; - var firstLineNumber = this.session.$firstLineNumber; + var foldWidgets = this.$showFoldWidgets && session.foldWidgets; + var breakpoints = session.$breakpoints; + var decorations = session.$decorations; + var firstLineNumber = session.$firstLineNumber; var lastLineNumber = 0; + + var gutterRenderer = session.gutterRenderer; var cell = null; var index = -1; @@ -135,7 +137,7 @@ var Gutter = function(parentEl) { while (true) { if (row > foldStart) { row = fold.end.row + 1; - fold = this.session.getNextFoldLine(row, fold); + fold = session.getNextFoldLine(row, fold); foldStart = fold ? fold.start.row : Infinity; } if (row > lastRow) { @@ -166,19 +168,15 @@ var Gutter = function(parentEl) { if (cell.element.className != className) cell.element.className = className; - var height = this.session.getRowLength(row) * config.lineHeight + "px"; + var height = session.getRowLength(row) * config.lineHeight + "px"; if (height != cell.element.style.height) cell.element.style.height = height; - var text = lastLineNumber = row + firstLineNumber; - if (text != cell.textNode.data) - cell.textNode.data = text; - if (foldWidgets) { var c = foldWidgets[row]; // check if cached value is invalidated and we need to recompute if (c == null) - c = foldWidgets[row] = this.session.getFoldWidget(row); + c = foldWidgets[row] = session.getFoldWidget(row); } if (c) { @@ -198,21 +196,30 @@ var Gutter = function(parentEl) { if (cell.foldWidget.style.height != height) cell.foldWidget.style.height = height; } else { - if (cell.foldWidget != null) { + if (cell.foldWidget) { cell.element.removeChild(cell.foldWidget); cell.foldWidget = null; } } + + var text = lastLineNumber = gutterRenderer + ? gutterRenderer.getText(session, row) + : row + firstLineNumber; + if (text != cell.textNode.data) + cell.textNode.data = text; row++; } this.element.style.height = config.minHeight + "px"; - if (this.$fixedWidth || this.session.$useWrapMode) - lastLineNumber = this.session.getLength(); + if (this.$fixedWidth || session.$useWrapMode) + lastLineNumber = session.getLength(); - var gutterWidth = lastLineNumber.toString().length * config.characterWidth; + var gutterWidth = gutterRenderer + ? gutterRenderer.getWidth(session, lastLineNumber, config) + : lastLineNumber.toString().length * config.characterWidth; + var padding = this.$padding || this.$computePadding(); gutterWidth += padding.left + padding.right; if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) { diff --git a/lib/ace/lib/lang.js b/lib/ace/lib/lang.js index 4e6ebf2c..4405b5c2 100644 --- a/lib/ace/lib/lang.js +++ b/lib/ace/lib/lang.js @@ -78,14 +78,16 @@ exports.copyArray = function(array){ }; exports.deepCopy = function (obj) { - if (typeof obj != "object") { + if (typeof obj !== "object" || !obj) + return obj; + var cons = obj.constructor; + if (cons === RegExp) return obj; - } - var copy = obj.constructor(); + var copy = cons(); for (var key in obj) { - if (typeof obj[key] == "object") { - copy[key] = this.deepCopy(obj[key]); + if (typeof obj[key] === "object") { + copy[key] = exports.deepCopy(obj[key]); } else { copy[key] = obj[key]; } @@ -170,6 +172,10 @@ exports.deferredCall = function(fcn) { timer = null; return deferred; }; + + deferred.isPending = function() { + return timer; + }; return deferred; }; @@ -183,15 +189,15 @@ exports.delayedCall = function(fcn, defaultTimeout) { }; var _self = function(timeout) { + if (timer == null) + timer = setTimeout(callback, timeout || defaultTimeout); + }; + + _self.delay = function(timeout) { timer && clearTimeout(timer); timer = setTimeout(callback, timeout || defaultTimeout); }; - - _self.delay = _self; - _self.schedule = function(timeout) { - if (timer == null) - timer = setTimeout(callback, timeout || 0); - }; + _self.schedule = _self; _self.call = function() { this.cancel(); diff --git a/lib/ace/mode/_test/highlight_rules_test.js b/lib/ace/mode/_test/highlight_rules_test.js index be6656d8..c9d2aa15 100644 --- a/lib/ace/mode/_test/highlight_rules_test.js +++ b/lib/ace/mode/_test/highlight_rules_test.js @@ -50,9 +50,13 @@ function generateTestData() { var data = tokenizer.getLineTokens(line, state); var tmp = []; tmp.push(JSON.stringify(data.state)); + var tokenizedLine = ""; data.tokens.forEach(function(x) { + tokenizedLine += x.value; tmp.push(JSON.stringify([x.type, x.value])); }); + if (tokenizedLine != line) + tmp.push(JSON.stringify(line)); state = data.state; return tmp.join(",\n "); }); @@ -85,12 +89,15 @@ function testMode(modeName, i) { lineData.values = []; lineData.types = []; lineData.state = lineData.shift(); + var line = null; + if (typeof lineData[lineData.length - 1] == "string") + line = lineData.pop() lineData.forEach(function(x) { lineData.types.push(x[0]); lineData.values.push(x[1]); }); - - var line = lineData.values.join(""); + if (typeof line != "string") + line = lineData.values.join(""); var tokens = tokenizer.getLineTokens(line, state); var values = tokens.tokens.map(function(x) {return x.value;}); @@ -98,7 +105,7 @@ function testMode(modeName, i) { var success = true; var err = testEqual([ - lineData.state, tokens.state, + JSON.stringify(lineData.state), JSON.stringify(tokens.state), lineData.types, types, lineData.values, values]); @@ -107,7 +114,7 @@ function testMode(modeName, i) { throw "error"; } - state = lineData.state; + state = tokens.state; }); } function testEqual(a) { diff --git a/lib/ace/mode/_test/tokens_c9search.json b/lib/ace/mode/_test/tokens_c9search.json index 98052746..902d8dce 100644 --- a/lib/ace/mode/_test/tokens_c9search.json +++ b/lib/ace/mode/_test/tokens_c9search.json @@ -1,104 +1,131 @@ [[ - "start", - ["text","Searching for 'var' in /workspace/configs"] + ["start",{}], + ["text","Searching for '"], + ["text","var"], + ["text","' in"], + ["text"," /.c9/metadata/workspace/plugins "], + ["text","(regexp, case sensitive, whole word)"], + "Searching for \u0001var\u0001 in\u0001/.c9/metadata/workspace/plugins\u0001\u0001regexp, case sensitive, whole word\u0001" ],[ - "start" + ["start",{}] ],[ - "start", + ["start",{}], ["string","configs/default.js"], ["text",":"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric"," 1"], ["c9searchresults.text",": "], - ["c9searchresults.text","var fs = require(\"fs\");"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," fs = require(\"fs\");"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t2"], ["c9searchresults.text",": "], - ["c9searchresults.text","var argv = require('optimist').argv;"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," argv = require('optimist').argv;"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t3"], ["c9searchresults.text",": "], - ["c9searchresults.text","var path = require(\"path\");"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," path = require(\"path\");"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t5"], ["c9searchresults.text",": "], - ["c9searchresults.text","var clientExtensions = {};"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," clientExtensions = {};"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t6"], ["c9searchresults.text",": "], - ["c9searchresults.text","var clientDirs = fs.readdirSync(__dirname + \"/../plugins-client\");"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," clientDirs = fs.readdirSync(__dirname + \"/../plugins-client\");"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t7"], ["c9searchresults.text",": "], - ["c9searchresults.text","for (var i = 0; i < clientDirs.length; i++) {"] + ["c9searchresults.text","for ("], + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," i = 0; i < clientDirs.length; i++) {"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t8"], - ["c9searchresults.text",": "], - ["c9searchresults.text","var dir = clientDirs[i];"] + ["c9searchresults.text",": "], + ["c9searchresults.text"," "], + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," dir = clientDirs[i];"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t12"], - ["c9searchresults.text",": "], - ["c9searchresults.text","var name = dir.split(\".\")[1];"] + ["c9searchresults.text",": "], + ["c9searchresults.text"," "], + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," name = dir.split(\".\")[1];"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t16"], ["c9searchresults.text",": "], - ["c9searchresults.text","var projectDir = (argv.w && path.resolve(process.cwd(), argv.w)) || process.cwd();"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," projectDir = (argv.w && path.resolve(process.cwd(), argv.w)) || process.cwd();"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t17"], ["c9searchresults.text",": "], - ["c9searchresults.text","var fsUrl = \"/workspace\";"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," fsUrl = \"/workspace\";"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t19"], ["c9searchresults.text",": "], - ["c9searchresults.text","var port = argv.p || process.env.PORT || 3131;"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," port = argv.p || process.env.PORT || 3131;"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t20"], ["c9searchresults.text",": "], - ["c9searchresults.text","var host = argv.l || \"localhost\";"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," host = argv.l || \"localhost\";"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t22"], ["c9searchresults.text",": "], - ["c9searchresults.text","var config = {"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," config = {"] ],[ - "start" + ["start",{}] ],[ - "start", + ["start",{}], ["string","configs/local.js"], ["text",":"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t2"], ["c9searchresults.text",": "], - ["c9searchresults.text","var config = require(\"./default\");"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," config = require(\"./default\");"] ],[ - "start" + ["start",{}] ],[ - "start", + ["start",{}], ["string","configs/packed.js"], ["text",":"] ],[ - "start", + ["start",{}], ["c9searchresults.constant.numeric","\t1"], ["c9searchresults.text",": "], - ["c9searchresults.text","var config = require(\"./default\");"] + ["c9searchresults.keyword","var"], + ["c9searchresults.text"," config = require(\"./default\");"] ],[ - "start" + ["start",{}] ],[ - "start" + ["start",{}] ],[ - "start", - ["text","Found 15 matches in 3 files"] + ["start",{}], + ["text","Found "], + ["constant.numeric","15"], + ["text"," matches in "], + ["constant.numeric","3"], + ["text"," files"] ]] \ No newline at end of file diff --git a/lib/ace/mode/c9search_highlight_rules.js b/lib/ace/mode/c9search_highlight_rules.js index df31112b..07273bbb 100644 --- a/lib/ace/mode/c9search_highlight_rules.js +++ b/lib/ace/mode/c9search_highlight_rules.js @@ -32,8 +32,15 @@ define(function(require, exports, module) { "use strict"; var oop = require("../lib/oop"); +var lang = require("../lib/lang"); var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; +function safeCreateRegexp(source, flag) { + try { + return new RegExp(source, flag); + } catch(e) {} +} + var C9SearchHighlightRules = function() { // regexp must not have capturing parentheses. Use (?:) instead. @@ -41,12 +48,120 @@ var C9SearchHighlightRules = function() { this.$rules = { "start" : [ { - token : ["c9searchresults.constant.numeric", "c9searchresults.text", "c9searchresults.text"], - regex : "(^\\s+[0-9]+)(:\\s*)(.+)" + tokenNames : ["c9searchresults.constant.numeric", "c9searchresults.text", "c9searchresults.text", "c9searchresults.keyword"], + regex : "(^\\s+[0-9]+)(:\\s)(.+)", + onMatch : function(val, state, stack) { + var values = this.splitRegex.exec(val); + var types = this.tokenNames; + var tokens = [{ + type: types[0], + value: values[1] + },{ + type: types[1], + value: values[2] + }]; + + var regex = stack[1]; + var str = values[3]; + if (regex && str) + values = str.split(regex); // this doesn't work on ie8 but we don't care:) + else + values = [str]; + + for (var i = 0, l = values.length; i < l; i+=2) { + if (values[i]) + tokens.push({ + type: types[2], + value: values[i] + }); + if (values[i+1]) + tokens.push({ + type: types[3], + value: values[i + 1] + }); + } + return tokens; + } }, { token : ["string", "text"], // single line - regex : "(.+)(:$)" + regex : "(\\S.*)(:$)" + }, + { + regex : "Searching for .*$", + onMatch: function(val, state, stack) { + var parts = val.split("\x01"); + var search = parts[1]; + if (parts.length < 3) + return "text"; + var options = parts[2] == " in" ? parts[5] : parts[6]; + + if (!/regex/.test(options)) + search = lang.escapeRegExp(search); + if (/whole/.test(options)) + search = "\\b" + search + "\\b"; + var regex = safeCreateRegexp( + "(" + search + ")", + / sensitive/.test(options) ? "" : "i" + ); + if (regex) { + stack[0] = state; + stack[1] = regex; + } + + var i = 0; + var tokens = [ + { + value: parts[i++] + "'", + type: "text" + }, + { + value: parts[i++], + type: "text" // "c9searchresults.keyword" + }, + { + value: "'" + parts[i++], + type: "text" + } + ]; + + // replaced + if (parts[2] !== " in") { + tokens.push({ + value: "'" + parts[i++] + "'", + type: "text" + }, { + value: parts[i++], + type: "text" + }); + } + // path + tokens.push({ + value: " " + parts[i++] + " ", + type: "text" + }); + // options + if (parts[i+1]) { + tokens.push({ + value: "(" + parts[i+1] + ")", + type: "text" + }); + i += 1; + } else { + i -= 1; + } + while (i++ < parts.length) + parts[i] && tokens.push({ + value: parts[i], + type: "text" + }); + + return tokens; + } + }, + { + regex : "\\d+", + token: "constant.numeric" } ] }; @@ -56,4 +171,4 @@ oop.inherits(C9SearchHighlightRules, TextHighlightRules); exports.C9SearchHighlightRules = C9SearchHighlightRules; -}); +}); \ No newline at end of file diff --git a/lib/ace/mode/folding/cstyle.js b/lib/ace/mode/folding/cstyle.js index 8be20d27..dd151e24 100644 --- a/lib/ace/mode/folding/cstyle.js +++ b/lib/ace/mode/folding/cstyle.js @@ -52,7 +52,7 @@ oop.inherits(FoldMode, BaseFoldMode); this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/; this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/; - this.getFoldWidgetRange = function(session, foldStyle, row) { + this.getFoldWidgetRange = function(session, foldStyle, row, forceMultiline) { var line = session.getLine(row); var match = line.match(this.foldingStartMarker); if (match) { @@ -60,11 +60,20 @@ oop.inherits(FoldMode, BaseFoldMode); if (match[1]) return this.openingBracketBlock(session, match[1], row, i); - - return session.getCommentFoldRange(row, i + match[0].length, 1); + + var range = session.getCommentFoldRange(row, i + match[0].length, 1); + + if (range && !range.isMultiLine()) { + if (forceMultiline) { + range = this.getSectionRange(session, row); + } else if (foldStyle != "all") + range = null; + } + + return range; } - if (foldStyle !== "markbeginend") + if (foldStyle === "markbegin") return; var match = line.match(this.foldingStopMarker); @@ -77,6 +86,38 @@ oop.inherits(FoldMode, BaseFoldMode); return session.getCommentFoldRange(row, i, -1); } }; + + this.getSectionRange = function(session, row) { + var line = session.getLine(row); + var startIndent = line.search(/\S/); + var startRow = row; + var startColumn = line.length; + row = row + 1; + var endRow = row; + var maxRow = session.getLength(); + while (++row < maxRow) { + line = session.getLine(row); + var indent = line.search(/\S/); + if (indent === -1) + continue; + if (startIndent > indent) + break; + var subRange = this.getFoldWidgetRange(session, "all", row); + + if (subRange) { + if (subRange.start.row <= startRow) { + break; + } else if (subRange.isMultiLine()) { + row = subRange.end.row; + } else if (startIndent == indent) { + break; + } + } + endRow = row; + } + + return new Range(startRow, startColumn, endRow, session.getLine(endRow).length); + }; }).call(FoldMode.prototype); diff --git a/lib/ace/mode/folding/cstyle_test.js b/lib/ace/mode/folding/cstyle_test.js index e007c792..f199c916 100644 --- a/lib/ace/mode/folding/cstyle_test.js +++ b/lib/ace/mode/folding/cstyle_test.js @@ -76,6 +76,30 @@ module.exports = { assert.range(session.getFoldWidgetRange(0), 0, 2, 2, 7); assert.range(session.getFoldWidgetRange(2), 0, 2, 2, 7); + }, + + "test: fold sections": function() { + var session = new EditSession([ + '/* section0 */', + '{', + ' /* section1 */', + ' stuff', + ' ', + ' /* section2 */', + ' ', + ' stuff', + ' ', + ' }', + 'foo' + ]); + + var mode = new JavaScriptMode(); + session.setFoldStyle("markbegin"); + session.setMode(mode); + + assert.range(session.getFoldWidgetRange(0, true), 0, 14, 10, 3); + assert.range(session.getFoldWidgetRange(2, true), 2, 18, 3, 9); + assert.range(session.getFoldWidgetRange(5, true), 5, 18, 7, 9); } }; diff --git a/lib/ace/mode/folding/fold_mode.js b/lib/ace/mode/folding/fold_mode.js index 9272cdcd..7a6e9ec8 100644 --- a/lib/ace/mode/folding/fold_mode.js +++ b/lib/ace/mode/folding/fold_mode.js @@ -94,7 +94,7 @@ var FoldMode = exports.FoldMode = function() {}; var fw = session.foldWidgets[end.row]; if (fw == null) - fw = this.getFoldWidget(session, end.row); + fw = session.getFoldWidget(end.row); if (fw == "start" && end.row > start.row) { end.row --; diff --git a/lib/ace/mode/sh.js b/lib/ace/mode/sh.js index c66c2619..7620deee 100644 --- a/lib/ace/mode/sh.js +++ b/lib/ace/mode/sh.js @@ -36,9 +36,11 @@ var TextMode = require("./text").Mode; var Tokenizer = require("../tokenizer").Tokenizer; var ShHighlightRules = require("./sh_highlight_rules").ShHighlightRules; var Range = require("../range").Range; +var CStyleFoldMode = require("./folding/cstyle").FoldMode; var Mode = function() { this.HighlightRules = ShHighlightRules; + this.foldingRules = new CStyleFoldMode(); }; oop.inherits(Mode, TextMode); diff --git a/lib/ace/mouse/fold_handler.js b/lib/ace/mouse/fold_handler.js index d47f6d53..45aae96f 100644 --- a/lib/ace/mouse/fold_handler.js +++ b/lib/ace/mouse/fold_handler.js @@ -73,7 +73,7 @@ function FoldHandler(editor) { var range = data.range || data.firstRange; if (range) { - var row = range.start.row; + row = range.start.row; var fold = session.getFoldAt(row, session.getLine(row).length, 1); if (fold) { diff --git a/lib/ace/multi_select.js b/lib/ace/multi_select.js index a811b6be..2152275e 100644 --- a/lib/ace/multi_select.js +++ b/lib/ace/multi_select.js @@ -693,7 +693,7 @@ var Editor = require("./editor").Editor; var range = sel.toOrientedRange(); if (range.isEmpty()) { - var range = session.getWordRange(range.start.row, range.start.column); + range = session.getWordRange(range.start.row, range.start.column); range.cursor = dir == -1 ? range.start : range.end; this.multiSelect.addRange(range); return; @@ -703,7 +703,11 @@ var Editor = require("./editor").Editor; var newRange = find(session, needle, dir); if (newRange) { newRange.cursor = dir == -1 ? newRange.start : newRange.end; + this.$blockScrolling += 1; + this.session.unfold(newRange); this.multiSelect.addRange(newRange); + this.$blockScrolling -= 1; + this.renderer.scrollCursorIntoView(null, 0.5); } if (skip) this.multiSelect.substractPoint(range.cursor); @@ -721,11 +725,27 @@ var Editor = require("./editor").Editor; if (!ranges.length) { var range = this.selection.getRange(); var fr = range.start.row, lr = range.end.row; + var guessRange = fr == lr; + if (guessRange) { + var max = this.session.getLength(); + var line; + do { + line = this.session.getLine(lr); + } while (/[=:]/.test(line) && ++lr < max); + do { + line = this.session.getLine(fr); + } while (/[=:]/.test(line) && --fr > 0); + + if (fr < 0) fr = 0; + if (lr >= max) lr = max - 1; + } var lines = this.session.doc.removeLines(fr, lr); - lines = this.$reAlignText(lines); - this.session.doc.insertLines(fr, lines); - range.start.column = 0; - range.end.column = lines[lines.length - 1].length; + lines = this.$reAlignText(lines, guessRange); + this.session.doc.insert({row: fr, column: 0}, lines.join("\n") + "\n"); + if (!guessRange) { + range.start.column = 0; + range.end.column = lines[lines.length - 1].length; + } this.selection.setRange(range); } else { // filter out ranges on same row @@ -771,7 +791,7 @@ var Editor = require("./editor").Editor; } }; - this.$reAlignText = function(lines) { + this.$reAlignText = function(lines, forceLeft) { var isLeftAligned = true, isRightAligned = true; var startW, textW, endW; @@ -800,7 +820,8 @@ var Editor = require("./editor").Editor; endW = m[3].length; return m; - }).map(isLeftAligned ? isRightAligned ? alignRight : alignLeft : unAlign); + }).map(forceLeft ? alignLeft : + isLeftAligned ? isRightAligned ? alignRight : alignLeft : unAlign); function spaces(n) { return lang.stringRepeat(" ", n); diff --git a/lib/ace/theme/tomorrow_night_bright.css b/lib/ace/theme/tomorrow_night_bright.css index f092a44f..76c9a760 100644 --- a/lib/ace/theme/tomorrow_night_bright.css +++ b/lib/ace/theme/tomorrow_night_bright.css @@ -32,7 +32,14 @@ .ace-tomorrow-night-bright .ace_marker-layer .ace_bracket { margin: -1px 0 0 -1px; - border: 1px solid #343434 + border: 1px solid #888888 +} +.ace-tomorrow-night-bright .ace_marker-layer .ace_highlight { + border: 1px solid rgb(110, 119, 0); + border-bottom: 0; + box-shadow: inset 0 -1px rgb(110, 119, 0); + margin: -1px 0 0 -1px; + background: rgba(255, 235, 0, 0.1); } .ace-tomorrow-night-bright .ace_marker-layer .ace_active-line { @@ -48,7 +55,7 @@ } .ace-tomorrow-night-bright .ace_marker-layer .ace_selected-word { - border: 1px solid #424242 + border: 1px solid #888888 } .ace-tomorrow-night-bright .ace_invisible { @@ -123,6 +130,10 @@ color: #969896 } +.ace-tomorrow-night-bright .ace_c9searchresults.ace_keyword { + color: #C2C280; +} + .ace-tomorrow-night-bright .ace_indent-guide { background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYFBXV/8PAAJoAXX4kT2EAAAAAElFTkSuQmCC) right repeat-y; } \ No newline at end of file diff --git a/lib/ace/tokenizer.js b/lib/ace/tokenizer.js index 6d63b075..5a9aaad4 100644 --- a/lib/ace/tokenizer.js +++ b/lib/ace/tokenizer.js @@ -310,7 +310,7 @@ var Tokenizer = function(rules) { token = { value: line.substring(lastIndex, lastIndex += 2000), type: "overflow" - } + }; } currentState = "start"; stack = []; diff --git a/lib/ace/undomanager.js b/lib/ace/undomanager.js index 12aaa01d..304dac23 100644 --- a/lib/ace/undomanager.js +++ b/lib/ace/undomanager.js @@ -52,14 +52,14 @@ var UndoManager = function() { (function() { /** - * Provides a means for implementing your own undo manager. `options` has one property, `args`, an [[Array `Array`]], with two elements: - * - * - `args[0]` is an array of deltas - * - `args[1]` is the document to associate with - * - * @param {Object} options Contains additional properties - * - **/ + * Provides a means for implementing your own undo manager. `options` has one property, `args`, an [[Array `Array`]], with two elements: + * + * - `args[0]` is an array of deltas + * - `args[1]` is the document to associate with + * + * @param {Object} options Contains additional properties + * + **/ this.execute = function(options) { var deltas = options.args[0]; this.$doc = options.args[1]; @@ -78,12 +78,12 @@ var UndoManager = function() { }; /** - * [Perform an undo operation on the document, reverting the last change.]{: #UndoManager.undo} - * @param {Boolean} dontSelect {:dontSelect} - * - * - * @returns {Range} The range of the undo. - **/ + * [Perform an undo operation on the document, reverting the last change.]{: #UndoManager.undo} + * @param {Boolean} dontSelect {:dontSelect} + * + * + * @returns {Range} The range of the undo. + **/ this.undo = function(dontSelect) { var deltas = this.$undoStack.pop(); var undoSelectionRange = null; @@ -98,11 +98,11 @@ var UndoManager = function() { }; /** - * [Perform a redo operation on the document, reimplementing the last change.]{: #UndoManager.redo} - * @param {Boolean} dontSelect {:dontSelect} - * - * - **/ + * [Perform a redo operation on the document, reimplementing the last change.]{: #UndoManager.redo} + * @param {Boolean} dontSelect {:dontSelect} + * + * + **/ this.redo = function(dontSelect) { var deltas = this.$redoStack.pop(); var redoSelectionRange = null; @@ -117,9 +117,9 @@ var UndoManager = function() { }; /** - * - * Destroys the stack of undo and redo redo operations. - **/ + * + * Destroys the stack of undo and redo redo operations. + **/ this.reset = function() { this.$undoStack = []; this.$redoStack = []; @@ -127,36 +127,36 @@ var UndoManager = function() { }; /** - * - * Returns `true` if there are undo operations left to perform. - * @returns {Boolean} - **/ + * + * Returns `true` if there are undo operations left to perform. + * @returns {Boolean} + **/ this.hasUndo = function() { return this.$undoStack.length > 0; }; /** - * - * Returns `true` if there are redo operations left to perform. - * @returns {Boolean} - **/ + * + * Returns `true` if there are redo operations left to perform. + * @returns {Boolean} + **/ this.hasRedo = function() { return this.$redoStack.length > 0; }; /** - * - * Marks the current status clean - **/ + * + * Marks the current status clean + **/ this.markClean = function() { this.dirtyCounter = 0; }; /** - * - * Returns if the current status is clean - * @returns {Boolean} - **/ + * + * Returns if the current status is clean + * @returns {Boolean} + **/ this.isClean = function() { return this.dirtyCounter === 0; }; diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index b2d64a5c..f08e24cb 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -134,7 +134,8 @@ var VirtualRenderer = function(container, theme) { width: 0, height: 0, scrollerHeight: 0, - scrollerWidth: 0 + scrollerWidth: 0, + $dirty: true }; this.layerConfig = { @@ -223,6 +224,9 @@ var VirtualRenderer = function(container, theme) { **/ this.setSession = function(session) { this.session = session; + + if (this.scrollMargin.top && session.getScrollTop() <= 0) + session.setScrollTop(-this.scrollMargin.top); this.scroller.className = "ace_scroller"; @@ -232,7 +236,6 @@ var VirtualRenderer = function(container, theme) { this.$gutterLayer.setSession(session); this.$textLayer.setSession(session); this.$loop.schedule(this.CHANGE_FULL); - }; /** @@ -300,6 +303,12 @@ var VirtualRenderer = function(container, theme) { }; this.$changes = 0; + this.$updateSizeAsync = function() { + if (this.$loop.pending) + this.$size.$dirty = true; + else + this.onResize(); + }; /** * [Triggers a resize of the editor.]{: #VirtualRenderer.onResize} * @param {Boolean} force If `true`, recomputes the size, even if the height and width haven't changed @@ -325,7 +334,6 @@ var VirtualRenderer = function(container, theme) { height = el.clientHeight || el.scrollHeight; if (!width) width = el.clientWidth || el.scrollWidth; - var changes = this.$updateCachedSize(force, gutterWidth, width, height); // console.log("resizing to", width, height, JSON.stringify(this.$size)) @@ -342,7 +350,7 @@ var VirtualRenderer = function(container, theme) { this.$gutterLayer.$padding = null; if (force) - this.$renderChanges(changes, true); + this.$renderChanges(changes | this.$changes, true); else this.$loop.schedule(changes | this.$changes); @@ -371,7 +379,7 @@ var VirtualRenderer = function(container, theme) { this.scrollBarV.element.style.bottom = this.scrollBarH.getHeight() + "px"; if (this.session) { - this.session.setScrollTop(this.getScrollTop()); + //this.session.setScrollTop(this.getScrollTop()); changes = changes | this.CHANGE_SCROLL; } } @@ -399,6 +407,9 @@ var VirtualRenderer = function(container, theme) { changes = changes | this.CHANGE_FULL; } + if (size.$dirty) + size.$dirty = !width && !height; + if (changes) this._signal("resize", oldSize); @@ -410,9 +421,11 @@ var VirtualRenderer = function(container, theme) { if (gutterWidth != this.gutterWidth) this.$changes |= this.$updateCachedSize(true, gutterWidth, this.$size.width, this.$size.height); - if (this.session.getUseWrapMode() && this.adjustWrapLimit()) + if (this.session.getUseWrapMode() && this.adjustWrapLimit()) { this.$loop.schedule(this.CHANGE_FULL); - else { + } else if (this.$size.$dirty) { + this.$loop.schedule(this.CHANGE_FULL); + } else { this.$computeLayerConfig(); this.$loop.schedule(this.CHANGE_MARKER); } @@ -692,6 +705,8 @@ var VirtualRenderer = function(container, theme) { sm.left = left|0; sm.v = sm.top + sm.bottom; sm.h = sm.left + sm.right; + if (sm.top && this.scrollTop <= 0 && this.session) + this.session.setScrollTop(sm.top); this.updateFull(); }; @@ -744,7 +759,7 @@ var VirtualRenderer = function(container, theme) { this.$changes |= changes; return; } - if (!this.$size.width) { + if (this.$size.$dirty) { this.$changes |= changes; return this.onResize(true); } @@ -972,6 +987,7 @@ var VirtualRenderer = function(container, theme) { this.content.style.height = minHeight + "px"; return changes; + return changes; }; this.$updateLines = function() { diff --git a/lib/ace/worker/mirror.js b/lib/ace/worker/mirror.js index c521f8fd..7a3318fb 100644 --- a/lib/ace/worker/mirror.js +++ b/lib/ace/worker/mirror.js @@ -13,7 +13,9 @@ var Mirror = exports.Mirror = function(sender) { var _self = this; sender.on("change", function(e) { doc.applyDeltas(e.data); - deferredUpdate.schedule(_self.$timeout); + if (_self.$timeout) + return deferredUpdate.schedule(_self.$timeout); + _self.onUpdate(); }); }; @@ -38,6 +40,10 @@ var Mirror = exports.Mirror = function(sender) { // abstract method }; + this.isPending = function() { + return this.deferredUpdate.isPending(); + }; + }).call(Mirror.prototype); }); diff --git a/lib/ace/worker/worker.js b/lib/ace/worker/worker.js index 2f250afc..04296156 100644 --- a/lib/ace/worker/worker.js +++ b/lib/ace/worker/worker.js @@ -16,6 +16,10 @@ window.console.trace = window.console; window.window = window; window.ace = window; +window.onerror = function(message, file, line, col, err) { + console.error("Worker " + err.stack); +}; + window.normalizeModule = function(parentId, moduleName) { // normalize plugin requires if (moduleName.indexOf("!") !== -1) { diff --git a/lib/ace/worker/worker_client.js b/lib/ace/worker/worker_client.js index 3411d509..798f1b4b 100644 --- a/lib/ace/worker/worker_client.js +++ b/lib/ace/worker/worker_client.js @@ -39,7 +39,6 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) { this.$sendDeltaQueue = this.$sendDeltaQueue.bind(this); this.changeListener = this.changeListener.bind(this); this.onMessage = this.onMessage.bind(this); - this.onError = this.onError.bind(this); // nameToUrl is renamed to toUrl in requirejs 2 if (require.nameToUrl && !require.toUrl) @@ -69,7 +68,6 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) { this.callbackId = 1; this.callbacks = {}; - this.$worker.onerror = this.onError; this.$worker.onmessage = this.onMessage; }; @@ -77,11 +75,6 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) { oop.implement(this, EventEmitter); - this.onError = function(e) { - window.console && console.log && console.log(e); - throw e; - }; - this.onMessage = function(e) { var msg = e.data; switch(msg.type) { @@ -157,7 +150,7 @@ var WorkerClient = function(topLevelNamespaces, mod, classname) { this.changeListener = function(e) { if (!this.deltaQueue) { this.deltaQueue = [e.data]; - setTimeout(this.$sendDeltaQueue, 1); + setTimeout(this.$sendDeltaQueue, 0); } else this.deltaQueue.push(e.data); };