diff --git a/demo/demo.js b/demo/demo.js index 491c36ea..49e91f0f 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -376,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)) { @@ -474,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/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/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; }