diff --git a/demo/demo.js b/demo/demo.js index 7854cce9..dea6137c 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -152,9 +152,23 @@ exports.launch = function(env) { docs.textile.setMode(new TextileMode()); docs.textile.setUndoManager(new UndoManager()); + // Add a "name" property to all docs + for (doc in docs) { + docs[doc].name = doc; + } + var container = document.getElementById("editor"); var cockpitInput = document.getElementById("cockpitInput"); - env.editor = new Editor(new Renderer(container, theme)); + + // Splitting. + var Split = require("ace/split").Split; + var split = new Split(container, theme, 1); + env.editor = split.getEditor(0); + split.on("focus", function(editor) { + env.editor = editor; + updateUIEditorOptions(); + }); + env.split = split; window.env = env; window.ace = env.editor; @@ -181,14 +195,36 @@ exports.launch = function(env) { return modes[modeEl.value]; } + var docEl = document.getElementById("doc"); var modeEl = document.getElementById("mode"); var wrapModeEl = document.getElementById("soft_wrap"); + var themeEl = document.getElementById("theme"); + var selectStyleEl = document.getElementById("select_style"); + var highlightActiveEl = document.getElementById("highlight_active"); + var showHiddenEl = document.getElementById("show_hidden"); + var showGutterEl = document.getElementById("show_gutter"); + var showPrintMarginEl = document.getElementById("show_print_margin"); + var highlightSelectedWordE = document.getElementById("highlight_selected_word"); + var showHScrollEl = document.getElementById("show_hscroll"); + var softTabEl = document.getElementById("soft_tab"); bindDropdown("doc", function(value) { var doc = docs[value]; - env.editor.setSession(doc); + var session = env.split.setSession(doc); + session.name = doc.name; - var mode = doc.getMode(); + updateUIEditorOptions(); + + env.editor.focus(); + }); + + function updateUIEditorOptions() { + var editor = env.editor; + var session = editor.session; + + docEl.value = session.name; + + var mode = session.getMode(); if (mode instanceof JavaScriptMode) { modeEl.value = "javascript"; } @@ -238,13 +274,22 @@ exports.launch = function(env) { modeEl.value = "text"; } - if (!doc.getUseWrapMode()) { + if (!session.getUseWrapMode()) { wrapModeEl.value = "off"; } else { - wrapModeEl.value = doc.getWrapLimitRange().min || "free"; + wrapModeEl.value = session.getWrapLimitRange().min || "free"; } - env.editor.focus(); - }); + + selectStyleEl.checked = editor.getSelectionStyle() == "line" + themeEl.value = editor.getTheme(); + highlightActiveEl.checked = editor.getHighlightActiveLine(); + showHiddenEl.checked = editor.getShowInvisibles(); + showGutterEl.checked = editor.renderer.getShowGutter(); + showPrintMarginEl.checked = editor.renderer.getShowPrintMargin(); + highlightSelectedWordE.checked = editor.getHighlightSelectedWord(); + showHScrollEl.checked = editor.renderer.getHScrollBarAlwaysVisible(); + softTabEl.checked = session.getUseSoftTabs(); + } bindDropdown("mode", function(value) { env.editor.getSession().setMode(modes[value] || modes.text); @@ -259,7 +304,7 @@ exports.launch = function(env) { }); bindDropdown("fontsize", function(value) { - document.getElementById("editor").style.fontSize = value; + env.split.setFontSize(value); }); bindDropdown("soft_wrap", function(value) { @@ -320,6 +365,31 @@ exports.launch = function(env) { env.editor.getSession().setUseSoftTabs(checked); }); + var secondSession = null; + bindDropdown("split", function(value) { + var sp = env.split; + if (value == "none") { + if (sp.getSplits() == 2) { + secondSession = sp.getEditor(1).session; + } + sp.setSplits(1); + } else { + var newEditor = (sp.getSplits() == 1); + if (value == "below") { + sp.setOriantation(sp.BELOW); + } else { + sp.setOriantation(sp.BESIDE); + } + sp.setSplits(2); + + if (newEditor) { + var session = secondSession || sp.getEditor(0).session; + var newSession = sp.setSession(session, 1); + newSession.name = session.name; + } + } + }); + function bindCheckbox(id, callback) { var el = document.getElementById(id); var onCheck = function() { @@ -343,7 +413,8 @@ exports.launch = function(env) { container.style.width = width + "px"; cockpitInput.style.width = width + "px"; container.style.height = (document.documentElement.clientHeight - 22) + "px"; - env.editor.resize(); + env.split.resize(); +// env.editor.resize(); }; window.onresize = onResize; diff --git a/index.html b/index.html index 13068165..c381fee9 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,17 @@ + + + @@ -115,21 +126,21 @@ diff --git a/lib/ace/edit_session.js b/lib/ace/edit_session.js index 14d195a8..606121e9 100644 --- a/lib/ace/edit_session.js +++ b/lib/ace/edit_session.js @@ -694,7 +694,7 @@ var EditSession = function(text, mode) { return this.doc.remove(range); }; - this.undoChanges = function(deltas) { + this.undoChanges = function(deltas, dontSelect) { if (!deltas.length) return; @@ -713,10 +713,13 @@ var EditSession = function(text, mode) { } } this.$fromUndo = false; - lastUndoRange && this.selection.setSelectionRange(lastUndoRange); + lastUndoRange && + !dontSelect && + this.selection.setSelectionRange(lastUndoRange); + return lastUndoRange; }, - this.redoChanges = function(deltas) { + this.redoChanges = function(deltas, dontSelect) { if (!deltas.length) return; @@ -731,7 +734,10 @@ var EditSession = function(text, mode) { } } this.$fromUndo = false; - lastUndoRange && this.selection.setSelectionRange(lastUndoRange); + lastUndoRange && + !dontSelect && + this.selection.setSelectionRange(lastUndoRange); + return lastUndoRange; }, this.$getUndoSelection = function(deltas, isUndo, lastUndoRange) { diff --git a/lib/ace/edit_session/fold_line.js b/lib/ace/edit_session/fold_line.js index 51024142..7e017a8c 100644 --- a/lib/ace/edit_session/fold_line.js +++ b/lib/ace/edit_session/fold_line.js @@ -58,7 +58,7 @@ function FoldLine(foldData, folds) { this.end = this.range.end; this.folds.forEach(function(fold) { - fold.foldLine = this; + fold.setFoldLine(this); }, this); } diff --git a/lib/ace/edit_session/folding.js b/lib/ace/edit_session/folding.js index d03de3f6..904e0fb5 100644 --- a/lib/ace/edit_session/folding.js +++ b/lib/ace/edit_session/folding.js @@ -59,6 +59,22 @@ Fold.prototype.toString = function() { return '"' + this.placeholder + '" ' + this.range.toString(); } +Fold.prototype.setFoldLine = function(foldLine) { + this.foldLine = foldLine; + this.subFolds.forEach(function(fold) { + fold.setFoldLine(foldLine); + }); +} + +Fold.prototype.clone = function() { + var range = this.range.clone(); + var fold = new Fold(range, this.placeholder); + this.subFolds.forEach(function(subFold) { + fold.subFolds.push(subFold.clone()); + }); + return fold; +} + function Folding() { /** * Looks up a fold at a given row/column. Possible values for side: @@ -526,6 +542,19 @@ function Folding() { foldLine, row, endColumn, startRow, startColumn); } }; + + this.$cloneFoldData = function() { + var foldData = this.$foldData; + var fd = []; + fd = this.$foldData.map(function(foldLine) { + var folds = foldLine.folds.map(function(fold) { + return fold.clone(); + }); + return new FoldLine(fd, folds); + }); + + return fd; + }; } exports.Folding = Folding; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 9ec189e9..20330582 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -217,6 +217,10 @@ var Editor =function(renderer, session) { this.renderer.setTheme(theme); }; + this.getTheme = function() { + return this.renderer.getTheme(); + } + this.setStyle = function(style) { this.renderer.setStyle(style) }; @@ -1124,6 +1128,10 @@ var Editor =function(renderer, session) { this.session.getUndoManager().redo(); }; + this.destroy = function() { + this.renderer.destroy(); + } + }).call(Editor.prototype); diff --git a/lib/ace/layer/cursor.js b/lib/ace/layer/cursor.js index a1cd4698..224b638f 100644 --- a/lib/ace/layer/cursor.js +++ b/lib/ace/layer/cursor.js @@ -123,16 +123,20 @@ var Cursor = function(parentEl) { if (this.isVisible) { this.element.appendChild(this.cursor); } - + if (this.session.getOverwrite()) { dom.addCssClass(this.cursor, "ace_overwrite"); } else { dom.removeCssClass(this.cursor, "ace_overwrite"); } - + this.restartTimer(); }; + this.destroy = function() { + clearInterval(this.blinkId); + } + }).call(Cursor.prototype); exports.Cursor = Cursor; diff --git a/lib/ace/layer/text.js b/lib/ace/layer/text.js index 243e2371..88a4e1bf 100644 --- a/lib/ace/layer/text.js +++ b/lib/ace/layer/text.js @@ -82,7 +82,7 @@ var Text = function(parentEl) { this.$pollSizeChanges = function() { var self = this; - setInterval(function() { + this.$pollSizeChangesTimer = setInterval(function() { self.checkForSizeChanges(); }, 500); }; @@ -517,6 +517,10 @@ var Text = function(parentEl) { this.$renderLineCore(stringBuilder, row, renderTokens, splits); }; + this.destroy = function() { + clearInterval(this.$pollSizeChangesTimer); + }; + }).call(Text.prototype); exports.Text = Text; diff --git a/lib/ace/split.js b/lib/ace/split.js new file mode 100644 index 00000000..e6c671ca --- /dev/null +++ b/lib/ace/split.js @@ -0,0 +1,295 @@ +/* vim:ts=4:sts=4:sw=4: + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Julian Viereck + * + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +var oop = require("pilot/oop"); +var lang = require("pilot/lang"); +var EventEmitter = require("pilot/event_emitter").EventEmitter; + +var Editor = require("ace/editor").Editor; +var Renderer = require("ace/virtual_renderer").VirtualRenderer; +var EditSession = require("ace/edit_session").EditSession; + +var Split = function(container, theme, splits) { + this.BELOW = 1; + this.BESIDE = 0; + + this.$container = container; + this.$theme = theme; + this.$splits = 0; + this.$editorCSS = ""; + this.$editors = []; + this.$oriantation = this.BESIDE; + + this.setSplits(splits || 1); + this.$cEditor = this.$editors[0]; + + + this.on("focus", function(editor) { + this.$cEditor = editor; + }.bind(this)); +}; + +(function(){ + + oop.implement(this, EventEmitter); + + this.$createEditor = function() { + var dom = document.createElement("div"); + dom.className = this.$editorCSS; + dom.style = "position: absolute; top:0px; bottom:0px"; + this.$container.appendChild(dom); + var session = new EditSession(""); + var editor = new Editor(new Renderer(dom, this.$theme)); + + editor.on("focus", function() { + this._emit("focus", editor); + }.bind(this)); + + this.$editors.push(editor); + editor.container.style.fontSize = this.$fontSize; + return editor; + } + + this.setSplits = function(splits) { + var editor; + if (splits < 1) { + throw "The number of splits have to be > 0!"; + } + + if (splits == this.$splits) { + return; + } else if (splits > this.$splits) { + while (this.$splits < this.$editors.length && this.$splits < splits) { + var editor = this.$editors[this.$splits]; + this.$container.appendChild(editor.container); + editor.container.style.fontSize = this.$fontSize; + this.$splits ++; + } + while (this.$splits < splits) { + this.$createEditor(); + this.$splits ++; + } + } else { + while (this.$splits > splits) { + editor = this.$editors[this.$splits - 1]; + this.$container.removeChild(editor.container); + this.$splits --; + } + } + this.resize(); + } + + this.getSplits = function() { + return this.$splits; + } + + this.getEditor = function(idx) { + return this.$editors[idx]; + } + + this.getCurrentEditor = function() { + return this.$cEditor; + } + + this.focus = function() { + this.$cEditor.focus(); + } + + this.blur = function() { + this.$cEditor.blur(); + } + + this.setTheme = function(theme) { + this.$editors.forEach(function(editor) { + editor.setTheme(theme); + }); + } + + this.setKeyboardHandler = function(keybinding) { + this.$editors.forEach(function(editor) { + editor.setKeyboardHandler(keybinding); + }); + } + + this.forEach = function(callback, scope) { + this.$editors.forEach(callback, scope); + } + + this.setFontSize = function(size) { + this.$fontSize = size; + this.forEach(function(editor) { + editor.container.style.fontSize = size; + }); + } + + this.$cloneSession = function(session) { + var s = new EditSession(session.getDocument(), session.getMode()); + + var undoManager = session.getUndoManager(); + if (undoManager) { + var undoManagerProxy = new UndoManagerProxy(undoManager, s); + s.setUndoManager(undoManagerProxy); + } + + // Overwrite the default $informUndoManager function such that new delas + // aren't added to the undo manager from the new and the old session. + s.$informUndoManager = lang.deferredCall(function() { s.$deltas = []; }); + + // Copy over 'settings' from the session. + s.setTabSize(session.getTabSize()); + s.setUseSoftTabs(session.getUseSoftTabs()); + s.setOverwrite(session.getOverwrite()); + s.setBreakpoints(session.getBreakpoints()); + s.setUseWrapMode(session.getUseWrapMode()); + s.setUseWorker(session.getUseWorker()); + s.setWrapLimitRange(session.$wrapLimitRange.min, + session.$wrapLimitRange.max); + s.$foldData = session.$cloneFoldData(); + + return s; + } + + this.setSession = function(session, idx) { + var editor + if (idx == null) { + editor = this.$cEditor; + } else { + editor = this.$editors[idx]; + } + + // Check if the session is used already by any of the editors in the + // split. If it is, we have to clone the session as two editors using + // the same session can cause terrible side effects (e.g. UndoQueue goes + // wrong). This also gives the user of Split the possibility to treat + // each session on each split editor different. + var isUsed = this.$editors.some(function(editor) { + return editor.session === session; + }); + + if (isUsed) { + session = this.$cloneSession(session); + } + editor.setSession(session); + + // Return the session set on the editor. This might be a cloned one. + return session; + } + + this.getOriantation = function() { + return this.$oriantation; + } + + this.setOriantation = function(oriantation) { + if (this.$oriantation == oriantation) { + return; + } + this.$oriantation = oriantation; + this.resize(); + } + + this.resize = function() { + var width = this.$container.clientWidth; + var height = this.$container.clientHeight; + var editor; + + if (this.$oriantation == this.BESIDE) { + var editorWidth = width / this.$splits; + for (var i = 0; i < this.$splits; i++) { + editor = this.$editors[i]; + editor.container.style.width = editorWidth + "px"; + editor.container.style.top = "0px"; + editor.container.style.left = i * editorWidth + "px"; + editor.container.style.height = height + "px"; + editor.resize(); + } + } else { + var editorHeight = height / this.$splits; + for (var i = 0; i < this.$splits; i++) { + editor = this.$editors[i]; + editor.container.style.width = width + "px"; + editor.container.style.top = i * editorHeight + "px" + editor.container.style.left = "0px"; + editor.container.style.height = editorHeight + "px"; + editor.resize(); + } + } + } + +}).call(Split.prototype); + +function UndoManagerProxy(undoManager, session) { + this.$u = undoManager; + this.$doc = session; +} + +(function() { + this.execute = function(options) { + this.$u.execute(options); + } + + this.undo = function() { + var selectionRange = this.$u.undo(true); + if (selectionRange) { + this.$doc.selection.setSelectionRange(selectionRange); + } + } + + this.redo = function() { + var selectionRange = this.$u.redo(true); + if (selectionRange) { + this.$doc.selection.setSelectionRange(selectionRange); + } + } + + this.reset = function() { + this.$u.reset(); + } + + this.hasUndo = function() { + return this.$u.hasUndo(); + } + + this.hasRedo = function() { + return this.$u.hasRedo(); + } +}).call(UndoManagerProxy.prototype); + +exports.Split = Split; +}); diff --git a/lib/ace/undomanager.js b/lib/ace/undomanager.js index 66debe14..48c1ee85 100644 --- a/lib/ace/undomanager.js +++ b/lib/ace/undomanager.js @@ -52,20 +52,26 @@ var UndoManager = function() { this.$redoStack = []; }; - this.undo = function() { + this.undo = function(dontSelect) { var deltas = this.$undoStack.pop(); + var undoSelectionRange = null; if (deltas) { - this.$doc.undoChanges(deltas); + undoSelectionRange = + this.$doc.undoChanges(deltas, dontSelect); this.$redoStack.push(deltas); } + return undoSelectionRange; }; - this.redo = function() { + this.redo = function(dontSelect) { var deltas = this.$redoStack.pop(); + var redoSelectionRange = null; if (deltas) { - this.$doc.redoChanges(deltas); + redoSelectionRange = + this.$doc.redoChanges(deltas, dontSelect); this.$undoStack.push(deltas); } + return redoSelectionRange; }; this.reset = function() { diff --git a/lib/ace/virtual_renderer.js b/lib/ace/virtual_renderer.js index 913158aa..a9687660 100644 --- a/lib/ace/virtual_renderer.js +++ b/lib/ace/virtual_renderer.js @@ -750,6 +750,7 @@ var VirtualRenderer = function(container, theme) { this.setTheme = function(theme) { var _self = this; + this.$themeValue = theme; if (!theme || typeof theme == "string") { theme = theme || "ace/theme/textmate"; require([theme], function(theme) { @@ -777,6 +778,10 @@ var VirtualRenderer = function(container, theme) { } }; + this.getTheme = function() { + return this.$themeValue; + } + // Methods allows to add / remove CSS classnames to the editor element. // This feature can be used by plug-ins to provide a visual indication of // a certain mode that editor is in. @@ -789,6 +794,11 @@ var VirtualRenderer = function(container, theme) { dom.removeCssClass(this.container, style) }; + this.destroy = function() { + this.$textLayer.destroy(); + this.$cursorLayer.destroy(); + } + }).call(VirtualRenderer.prototype); exports.VirtualRenderer = VirtualRenderer;
+ + + +
@@ -101,7 +112,7 @@
- +
- +
- +
- +
- +
- +
- +
- +
- +