diff --git a/demo/demo.js b/demo/demo.js index cd70a622..49e91f0f 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -153,6 +153,7 @@ exports.launch = function(env) { docs.textile.setUndoManager(new UndoManager()); var container = document.getElementById("editor"); + var cockpitInput = document.getElementById("cockpitInput"); env.editor = new Editor(new Renderer(container, theme)); var modes = { @@ -336,7 +337,9 @@ exports.launch = function(env) { } function onResize() { - container.style.width = (document.documentElement.clientWidth - 180) + "px"; + var width = (document.documentElement.clientWidth - 300); + container.style.width = width + "px"; + cockpitInput.style.width = width + "px"; container.style.height = (document.documentElement.clientHeight - 22) + "px"; env.editor.resize(); }; @@ -373,8 +376,8 @@ exports.launch = function(env) { mode = "python"; } else if (/^.*\.php$/i.test(file.name)) { mode = "php"; - } else if (/^.*\.cs$/i.test(file.name)) { - mode = "csharp"; + } else if (/^.*\.cs$/i.test(file.name)) { + mode = "csharp"; } else if (/^.*\.java$/i.test(file.name)) { mode = "java"; } else if (/^.*\.rb$/i.test(file.name)) { @@ -471,28 +474,58 @@ exports.launch = function(env) { mac: "Alt-L", sender: "editor" }, - exec: function() { - var session = env.editor.session, - range = env.editor.selection.getRange(), - placeHolder = session.getTextRange(range).substring(0, 3) + "..."; - - session.addFold(placeHolder, range); + exec: function(env) { + toggleFold(env, false) } }); canon.addCommand({ - name: "undfold", + name: "unfold", bindKey: { win: "Alt-Shift-L", mac: "Alt-Shift-L", sender: "editor" }, - exec: function() { - var session = env.editor.session, - range = env.editor.selection.getRange(); - session.expandFolds(session.getFoldsInRange(range)); + exec: function(env) { + toggleFold(env, true) } }); + + function toggleFold(env, tryToUnfold) { + var session = env.editor.session, + selection = env.editor.selection, + range = selection.getRange(), addFold; + + if(range.isEmpty()) { + var br = session.findMatchingBracket(range.start); + var fold = session.getFoldAt(range.start.row, range.start.column) + if(fold) { + session.expandFold(fold); + selection.setSelectionRange(fold.range) + } else if(br) { + if(range.compare(br.row,br.column) == 1) + range.end = br; + else + range.start = br; + addFold = true; + } + } else { + var folds = session.getFoldsInRange(range); + if(tryToUnfold && folds.length) + session.expandFolds(folds); + else if(folds.length == 1 && folds[0].range.compare(range) == 0) + session.expandFolds(folds); + else + addFold = true; + } + if(addFold) { + var placeHolder = session.getTextRange(range); + if(placeHolder.length < 3) + return; + placeHolder = placeHolder.trim().substring(0, 3).replace(' ','','g') + "..."; + session.addFold(placeHolder, range); + } + } }; }); diff --git a/demo/styles.css b/demo/styles.css index 6b774db3..4be33b19 100644 --- a/demo/styles.css +++ b/demo/styles.css @@ -1,5 +1,6 @@ html { height: 100%; + width: 100%; overflow: hidden; } @@ -18,7 +19,7 @@ body { #editor { position: absolute; top: 0px; - left: 180px; + left: 300px; bottom: 0px; right: 0px; background: white; @@ -28,9 +29,17 @@ body { padding: 5px; } +#controls td { + text-align: right; +} + +#controls td + td { + text-align: left; +} + #cockpitInput { position: absolute; - left: 180px; + left: 300px; right: 0px; bottom: 0; diff --git a/index.html b/index.html index 01b9ae1e..59752466 100644 --- a/index.html +++ b/index.html @@ -9,15 +9,16 @@ - - -
+ + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index 9b300747..7a40b316 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -152,7 +152,7 @@ canon.addCommand({ }); canon.addCommand({ name: "selecttostart", - bindKey: bindKey("Alt-Shift-Up", "Command-Shift-Up"), + bindKey: bindKey("Ctrl-Shift-Home|Alt-Shift-Up", "Command-Shift-Up"), exec: function(env, args, request) { env.editor.getSelection().selectFileStart(); } }); canon.addCommand({ @@ -182,7 +182,7 @@ canon.addCommand({ }); canon.addCommand({ name: "selecttoend", - bindKey: bindKey("Alt-Shift-Down", "Command-Shift-Down"), + bindKey: bindKey("Ctrl-Shift-End|Alt-Shift-Down", "Command-Shift-Down"), exec: function(env, args, request) { env.editor.getSelection().selectFileEnd(); } }); canon.addCommand({ diff --git a/lib/ace/css/editor.css b/lib/ace/css/editor.css index 06bc87b9..075c317e 100644 --- a/lib/ace/css/editor.css +++ b/lib/ace/css/editor.css @@ -123,6 +123,7 @@ .ace_marker-layer { cursor: text; + pointer-events: none; } .ace_marker-layer .ace_step { diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index fc4a3fcf..44a83ff1 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -691,7 +691,7 @@ var EditSession = function(text, mode) { if (delta.group == "doc") { this.doc.revertDeltas(delta.deltas); lastUndoRange = - this.$setUndoSelection(delta.deltas, true, lastUndoRange); + this.$getUndoSelection(delta.deltas, true, lastUndoRange); } else { delta.deltas.forEach(function(foldDelta) { this.addFolds(foldDelta.folds); @@ -699,6 +699,7 @@ var EditSession = function(text, mode) { } } this.$fromUndo = false; + lastUndoRange && this.selection.setSelectionRange(lastUndoRange); }, this.redoChanges = function(deltas) { @@ -712,13 +713,14 @@ var EditSession = function(text, mode) { if (delta.group == "doc") { this.doc.applyDeltas(delta.deltas); lastUndoRange = - this.$setUndoSelection(delta.deltas, false, lastUndoRange); + this.$getUndoSelection(delta.deltas, false, lastUndoRange); } } this.$fromUndo = false; + lastUndoRange && this.selection.setSelectionRange(lastUndoRange); }, - this.$setUndoSelection = function(deltas, isUndo, lastUndoRange) { + this.$getUndoSelection = function(deltas, isUndo, lastUndoRange) { function isInsert(delta) { var insert = delta.action == "insertText" || delta.action == "insertLines"; @@ -726,7 +728,7 @@ var EditSession = function(text, mode) { } var delta = deltas[0]; - var range; + var range, point; var lastDeltaIsInsert = false; if (isInsert(delta)) { range = delta.range.clone(); @@ -739,15 +741,18 @@ var EditSession = function(text, mode) { for (var i = 1; i < deltas.length; i++) { delta = deltas[i]; if (isInsert(delta)) { - if (range.compare(delta.range.start) == -1) { + point = delta.range.start; + if (range.compare(point.row, point.column) == -1) { range.setStart(delta.range.start); } - if (range.compare(delta.range.end) == 1) { + point = delta.range.end; + if (range.compare(point.row, point.column) == 1) { range.setEnd(delta.range.end); } lastDeltaIsInsert = true; } else { - if (range.compare(delta.range.start) == -1) { + point = delta.range.start; + if (range.compare(point.row, point.column) == -1) { range = Range.fromPoints(delta.range.start, delta.range.start); } @@ -766,11 +771,6 @@ var EditSession = function(text, mode) { } } - if (!range.isEmpty()) { - this.selection.setSelectionRange(range); - } else { - this.selection.moveCursorToPosition(range.start); - } return range; }, @@ -1389,12 +1389,10 @@ var EditSession = function(text, mode) { var docRow = 0; var docColumn = 0; var column; - var foldLine; var foldLineRowLength; var row = 0; var rowLength = 0; var splits = null; - var split = 0; var rowCache = this.$rowCache; var doCache = !rowCache.length; @@ -1406,6 +1404,11 @@ var EditSession = function(text, mode) { } } var docRowCacheLast = docRow; + // clamp row before clamping column, for selection on last line + var maxRow = this.getLength() - 1; + + var foldLine = this.getNextFold(docRow); + var foldStart = foldLine ?foldLine.start.row :Infinity; while (row <= screenRow) { if (doCache @@ -1417,30 +1420,41 @@ var EditSession = function(text, mode) { docRowCacheLast = docRow; } rowLength = this.getRowLength(docRow); - if (row + rowLength - 1 >= screenRow) { + if (row + rowLength - 1 >= screenRow || docRow >= maxRow) { break; } else { row += rowLength; - docRow = this.getRowFoldEnd(docRow) + 1; + docRow++; + if(docRow > foldStart) { + docRow = foldLine.end.row+1; + foldLine = this.getNextFold(docRow); + foldStart = foldLine ?foldLine.start.row :Infinity; + } } } - splits = this.$wrapData[docRow] || []; - foldLine = this.getFoldLine(docRow); - line = foldLine - ? this.getFoldDisplayLine(foldLine) - : this.getLine(docRow); + if (foldLine && foldLine.start.row <= docRow) + line = this.getFoldDisplayLine(foldLine); + else { + line = this.getLine(docRow); + foldLine = null; + } if (this.$useWrapMode) { - docColumn = split = splits[screenRow - row - 1] || 0; - line = line.substring(split); + splits = this.$wrapData[docRow]; + if (splits) { + column = splits[screenRow - row] + if(screenRow > row && splits.length) { + docColumn = splits[screenRow - row - 1] || splits[splits.length - 1]; + line = line.substring(docColumn); + } + } } docColumn += this.$getStringScreenWidth(line, screenColumn)[1]; // Need to do some clamping action here. if (this.$useWrapMode) { - column = splits[screenRow - row] if (docColumn >= column) { // We remove one character at the end such that the docColumn // position returned is not associated to the next row on the diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index 0e3fb889..3a2e6121 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -191,7 +191,11 @@ function Folding() { this.getFoldLine = function(docRow, startFoldLine) { var foldData = this.$foldData; - var i = Math.max(foldData.indexOf(startFoldLine), 0); + var i = 0; + if(startFoldLine) + i = foldData.indexOf(startFoldLine); + if(i == -1) + i = 0; for (i; i < foldData.length; i++) { var foldLine = foldData[i]; if (foldLine.start.row <= docRow && foldLine.end.row >= docRow) { @@ -203,6 +207,47 @@ function Folding() { return null; } + // returns the fold which starts after or contains docRow + this.getNextFold = function(docRow, startFoldLine) { + var foldData = this.$foldData, ans; + var i = 0; + if(startFoldLine) + i = foldData.indexOf(startFoldLine); + if(i == -1) + i = 0; + for (i; i < foldData.length; i++) { + var foldLine = foldData[i]; + if (foldLine.end.row >= docRow) { + return foldLine; + } + } + return null; + } + + this.getFoldedRowCount = function(first, last) { + var foldData = this.$foldData, rowCount = last-first+1; + for (var i = 0; i < foldData.length; i++) { + var foldLine = foldData[i], + end = foldLine.end.row, + start = foldLine.start.row; + if(end >= last) { + if(start < last) { + if(start >= first) + rowCount -= last-start; + else + rowCount = 0;//in one fold + } + break; + } else if(end >= first){ + if (start >= first) //fold inside range + rowCount -= end-start; + else + rowCount -= end-first+1; + } + } + return rowCount; + } + this.$addFoldLine = function(foldLine) { this.$foldData.push(foldLine); this.$foldData.sort(function(a, b) { @@ -428,6 +473,7 @@ function Folding() { }; this.getRowFoldEnd = function(docRow, startFoldRow) { + //console.trace() var foldLine = this.getFoldLine(docRow, startFoldRow); return (foldLine ? foldLine.end.row @@ -484,4 +530,4 @@ function Folding() { exports.Folding = Folding; -}); +}); \ No newline at end of file diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 5fc99c51..ef9b0e8a 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -348,8 +348,6 @@ var Editor =function(renderer, session) { session.$selectionMarker = session.addMarker(range, "ace_selection", style); } - this.onCursorChange(e); - if (this.$highlightSelectedWord) this.session.getMode().highlightSelection(this); }; diff --git a/lib/ace/layer/gutter.js b/lib/ace/layer/gutter.js index 84a36ca2..344018ec 100644 --- a/lib/ace/layer/gutter.js +++ b/lib/ace/layer/gutter.js @@ -99,12 +99,22 @@ var Gutter = function(parentEl) { this.update = function(config) { this.$config = config; + var emptyAnno = {className: "", text: []}; var html = []; - for ( var i = config.firstRow; i <= config.lastRow; i++) { - var annotation = this.$annotations[i] || { - className: "", - text: [] - }; + var i = config.firstRow, lastRow = config.lastRow + fold = this.session.getNextFold(i), + foldStart = fold ?fold.start.row :Infinity; + + while (true) { + if(i > foldStart) { + i = fold.end.row+1; + fold = this.session.getNextFold(i); + foldStart = fold ?fold.start.row :Infinity; + } + if(i > lastRow) + break; + + var annotation = this.$annotations[i] || emptyAnno; html.push("
", (i+1), "
"); - - i = this.session.getRowFoldEnd(i); + i++; } - this.element = dom.setInnerHtml(this.element, html.join("")); + this.element = dom.setInnerHtml(this.element, html.join("")); this.element.style.height = config.minHeight + "px"; }; diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index 01ed9770..243e2371 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -230,13 +230,12 @@ var Text = function(parentEl) { return this.update(config); var el = this.element; - if (oldConfig.firstRow < config.firstRow) - for (var row=oldConfig.firstRow; row0; row--) el.removeChild(el.firstChild); if (oldConfig.lastRow > config.lastRow) - for (var row=config.lastRow+1; row<=oldConfig.lastRow; row = this.session.getRowFoldEnd(row) + 1) + for (var row=this.session.getFoldedRowCount(config.lastRow + 1, oldConfig.lastRow); row>0; row--) el.removeChild(el.lastChild); if (config.firstRow < oldConfig.firstRow) { @@ -254,8 +253,20 @@ var Text = function(parentEl) { }; this.$renderLinesFragment = function(config, firstRow, lastRow) { - var fragment = document.createDocumentFragment(); - for (var row=firstRow; row<=lastRow; row++) { + var fragment = document.createDocumentFragment(), + row = firstRow, + fold = this.session.getNextFold(row), + foldStart = fold ?fold.start.row :Infinity; + + while (true) { + if(row > foldStart) { + row = fold.end.row+1; + fold = this.session.getNextFold(row); + foldStart = fold ?fold.start.row :Infinity; + } + if(row > lastRow) + break; + var lineEl = dom.createElement("div"); lineEl.className = "ace_line"; @@ -276,7 +287,7 @@ var Text = function(parentEl) { lineEl.innerHTML = html.join(""); fragment.appendChild(lineEl); - row = this.session.getRowFoldEnd(row); + row++; } return fragment; }; @@ -286,12 +297,36 @@ var Text = function(parentEl) { this.config = config; var html = []; - var tokens = this.session.getTokens(config.firstRow, config.lastRow) - var fragment = this.$renderLinesFragment(config, config.firstRow, config.lastRow); + var firstRow = config.firstRow, lastRow = config.lastRow; - // Clear the current content of the element and add the rendered fragment. - this.element.innerHTML = ""; - this.element.appendChild(fragment); + var row = firstRow, + fold = this.session.getNextFold(row), + foldStart = fold ?fold.start.row :Infinity; + + while (true) { + if(row > foldStart) { + row = fold.end.row+1; + fold = this.session.getNextFold(row); + foldStart = fold ?fold.start.row :Infinity; + } + if(row > lastRow) + break; + + html.push("
" + ) + // Get the tokens per line as there might be some lines in between + // beeing folded. + // OPTIMIZE: If there is a long block of unfolded lines, just make + // this call once for that big block of unfolded lines. + var tokens = this.session.getTokens(row, row); + if (tokens.length == 1) + this.$renderLine(html, row, tokens[0].tokens); + html.push("
") + + row++; + } + this.element = dom.setInnerHtml(this.element, html.join("")); }; this.$textToken = { diff --git a/lib/ace/mode/css_highlight_rules.js b/lib/ace/mode/css_highlight_rules.js index a312ee0c..83f44860 100644 --- a/lib/ace/mode/css_highlight_rules.js +++ b/lib/ace/mode/css_highlight_rules.js @@ -97,7 +97,7 @@ var CssHighlightRules = function() { "vertical-ideographic|vertical-text|visible|w-resize|wait|whitespace|" + "zero").split("|") ); - + var colors = lang.arrayToMap( ("aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|" + "purple|red|silver|teal|white|yellow").split("|") @@ -122,12 +122,12 @@ var CssHighlightRules = function() { return re.join(""); } - this.$rules = { - "start" : [ { + var base_ruleset = [ + { token : "comment", // multi line comment regex : "\\/\\*", - next : "comment" - }, { + next : "ruleset_comment" + },{ token : "string", // single line regex : '["](?:(?:\\\\.)|(?:[^"\\\\]))*?["]' }, { @@ -190,12 +190,6 @@ var CssHighlightRules = function() { }, { token : "constant.numeric", // hex3 color regex : "#[a-fA-F0-9]{3}" - }, { - token : "lparen", - regex : "\{" - }, { - token : "rparen", - regex : "\}" }, { token : function(value) { if (properties.hasOwnProperty(value.toLowerCase())) { @@ -215,15 +209,121 @@ var CssHighlightRules = function() { } }, regex : "\\-?[a-zA-Z_][a-zA-Z0-9_\\-]*" - }], - "comment" : [{ - token : "comment", // closing comment - regex : ".*?\\*\\/", - next : "start" + } + ]; + + var ruleset = lang.copyArray( base_ruleset ); + ruleset.unshift( + { + token : "rparen", + regex : "\}", + next: "start" + } + ); + + var media_ruleset = lang.copyArray( base_ruleset ); + media_ruleset.unshift( + { + token : "rparen", + regex : "\}", + next: "media" + } + ); + + var base_comment = [ + { + token : "comment", // comment spanning whole line + regex : ".+" + } + ]; + + var comment = lang.copyArray( base_comment ); + comment.unshift( + { + token : "comment", // closing comment + regex : ".*?\\*\\/", + next : "start" + } + ); + + var media_comment = lang.copyArray( base_comment ); + media_comment.unshift( + { + token : "comment", // closing comment + regex : ".*?\\*\\/", + next : "media" + } + ); + + var ruleset_comment = lang.copyArray( base_comment ); + ruleset_comment.unshift( + { + token : "comment", // closing comment + regex : ".*?\\*\\/", + next : "ruleset" + } + ); + + this.$rules = { + "start" : [ { + token : "comment", // multi line comment + regex : "\\/\\*", + next : "comment" }, { - token : "comment", // comment spanning whole line - regex : ".+" - }] + token: "lparen", + regex: "{", + next: "ruleset" + }, { + token: "string", + regex: "@media.*?{", + next: "media" + },{ + token: "keyword", + regex: "#[a-zA-Z0-9-_]+" + },{ + token: "variable", + regex: "\\.[a-zA-Z0-9-_]+" + },{ + token: "string", + regex: ":[a-zA-Z0-9-_]+" + },{ + token: "constant", + regex: "[a-zA-Z0-9-_]+" + }], + + "media" : [ { + token : "comment", // multi line comment + regex : "\\/\\*", + next : "media_comment" + }, { + token: "lparen", + regex: "{", + next: "media_ruleset" + },{ + token: "string", + regex: "}", + next: "start" + },{ + token: "keyword", + regex: "#[a-zA-Z0-9-_]+" + },{ + token: "variable", + regex: "\\.[a-zA-Z0-9-_]+" + },{ + token: "string", + regex: ":[a-zA-Z0-9-_]+" + },{ + token: "constant", + regex: "[a-zA-Z0-9-_]+" + }], + + "comment" : comment, + + "ruleset" : ruleset, + "ruleset_comment" : ruleset_comment, + + "media_ruleset" : media_ruleset, + "media_comment" : media_comment }; }; diff --git a/lib/ace/mouse_handler.js b/lib/ace/mouse_handler.js index 1805e247..8c722431 100644 --- a/lib/ace/mouse_handler.js +++ b/lib/ace/mouse_handler.js @@ -123,8 +123,6 @@ var MouseHandler = function(editor) { onStartSelect(pos); } - editor.renderer.scrollCursorIntoView(); - var mousePageX, mousePageY; var overwrite = editor.getOverwrite(); var mousedownTime = (new Date()).getTime(); diff --git a/lib/ace/renderloop.js b/lib/ace/renderloop.js index 95a6b2c4..afe998c3 100644 --- a/lib/ace/renderloop.js +++ b/lib/ace/renderloop.js @@ -63,7 +63,16 @@ var RenderLoop = function(onRender) { } }; - if (window.postMessage) { + this.setTimeoutZero = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame; + + if (this.setTimeoutZero) { + + this.setTimeoutZero = this.setTimeoutZero.bind(window) + } else if (window.postMessage) { this.messageName = "zero-timeout-message"; diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 9c813ddc..913158aa 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -410,7 +410,6 @@ var VirtualRenderer = function(container, theme) { this.$markerFront.update(this.layerConfig); this.$cursorLayer.update(this.layerConfig); this.$updateScrollBar(); - this.scrollCursorIntoView(); return; }
- + + +
- - -
- - -
- - -
- - -
- - -
- - -
- + +
- + + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ +
- + +
+
+
- + +
+