From 31191072f31b8972d16926ad8143e681ab93bbe6 Mon Sep 17 00:00:00 2001 From: nightwing Date: Tue, 27 Aug 2013 00:20:46 +0400 Subject: [PATCH] add first version of line_widgets --- demo/kitchen-sink/demo.js | 69 +++++++++ lib/ace/line_widgets.js | 292 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 361 insertions(+) create mode 100644 lib/ace/line_widgets.js diff --git a/demo/kitchen-sink/demo.js b/demo/kitchen-sink/demo.js index 1b12175e..4d7233be 100644 --- a/demo/kitchen-sink/demo.js +++ b/demo/kitchen-sink/demo.js @@ -563,10 +563,79 @@ event.addListener(container, "drop", function(e) { }); +var LineWidgets = require("ace/line_widgets").LineWidgets; +env.editor.commands.addCommand({ + name: "foo", + bindKey: "F2|F3", + exec: function(editor) { + var Editor = require("ace/editor").Editor + var Renderer = require("ace/virtual_renderer").VirtualRenderer + var split = env.split + var s = editor.session + var inlineEditor = new Editor(new Renderer()) + var splitSession = split.$cloneSession(s) + var row = editor.getCursorPosition().row + if (editor.session.lineWidgets && editor.session.lineWidgets[row]) { + editor.session.lineWidgets[row].destroy(); + return; + } + + + var rowCount = 10; + + var w = { + row: row, + rowCount: rowCount, + fixedWidth: true, + el: inlineEditor.container, + editor: editor + }; + var el = w.el + if (!editor.session.widgetManager) { + editor.session.widgetManager = new LineWidgets(editor.session); + editor.session.widgetManager.attach(editor); + } + + var h = rowCount*editor.renderer.layerConfig.lineHeight - 6; + el.style.height = h - 4 + "px" + el.style.position = "absolute" + el.style.zIndex = "4" + el.style.borderTop = "solid blue 2px" + el.style.borderBottom = "solid blue 2px" + + inlineEditor.setSession(splitSession) + editor.session.widgetManager.addLineWidget(w); + + var kb = { + handleKeyboard:function(_,hashId, keyString) { + if (hashId == 0 && keyString == "esc") { + w.destroy(); + return true; + } + } + } + + w.destroy = function() { + editor.keyBinding.removeKeyboardHandler(kb); + s.widgetManager.removeLineWidget(w); + } + + editor.keyBinding.addKeyboardHandler(kb) + inlineEditor.keyBinding.addKeyboardHandler(kb) + editor.on("changeSession", function(e) { + w.el.parentNode && w.el.parentNode.removeChild(w.el) + }); + inlineEditor.setTheme("ace/theme/solarized_light") + } +}) + +env.editor.commands.addCommand({ + +}) diff --git a/lib/ace/line_widgets.js b/lib/ace/line_widgets.js new file mode 100644 index 00000000..1a5a124e --- /dev/null +++ b/lib/ace/line_widgets.js @@ -0,0 +1,292 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Distributed under the BSD license: + * + * Copyright (c) 2010, Ajax.org B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Ajax.org B.V. nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { +"use strict"; + +var oop = require("./lib/oop"); +var dom = require("./lib/dom"); +var Range = require("./range").Range; + + +function LineWidgets(session) { + this.session = session; + this.session.widgetManager = this; + this.session.getRowLength = this.getRowLength; + this.session.$getWidgetScreenLength = this.$getWidgetScreenLength; + this.updateOnChange = this.updateOnChange.bind(this); + this.renderWidgets = this.renderWidgets.bind(this); + this.measureWidgets = this.measureWidgets.bind(this); + this.session._changedWidgets = []; + this.detach = this.detach.bind(this); + + this.session.on("change", this.updateOnChange); +}; + +(function() { + this.getRowLength = function(row) { + if (this.lineWidgets) + var h = this.lineWidgets[row] && this.lineWidgets[row].rowCount || 0; + else + h = 0; + if (!this.$useWrapMode || !this.$wrapData[row]) { + return 1 + h; + } else { + return this.$wrapData[row].length + 1 + h; + } + }; + + this.$getWidgetScreenLength = function() { + var screenRows = 0; + this.lineWidgets.forEach(function(w){ + if (w && w.rowCount) + screenRows +=w.rowCount; + }); + return screenRows; + }; + + this.attach = function(editor) { + if (editor.widgetManager && editor.widgetManager != this) + editor.widgetManager.detach(); + + if (this.editor == editor) + return; + + this.detach(); + this.editor = editor; + + this.editor.on("changeSession", this.detach); + + editor.widgetManager = this; + + editor.setOption("enableLineWidgets", true); + editor.renderer.on("beforeRender", this.measureWidgets); + editor.renderer.on("afterRender", this.renderWidgets); + }; + this.detach = function(e) { + console.log("detach", this.session.getLength(), e); + if (e && e.session == this.session) + return; // sometimes attach can be called before setSession + var editor = this.editor; + if (!editor) + return; + + editor.off("changeSession", this.detach); + + this.editor = null; + editor.widgetManager = null; + + editor.renderer.off("beforeRender", this.measureWidgets); + editor.renderer.off("afterRender", this.renderWidgets); + this.session.lineWidgets.forEach(function(w) { + if (w && w.el && w.el.parentNode) { + w._inDocument = false; + w.el.parentNode.removeChild(w.el); + } + }); + }; + + this.updateOnChange = function(e) { + var cells = this.session.lineWidget; + if (!cells) return; + + var delta = e.data; + var range = delta.range; + var startRow = range.start.row; + var len = range.end.row - startRow; + + if (len === 0) { + // return + } else if (delta.action == "removeText" || delta.action == "removeLines") { + var removed = cells.splice(startRow + 1, len); + removed.forEach(function(w) { + w && this.removeLineWidget(w); + }, this); + this.$updateRows(); + } else { + var args = Array(len); + args.unshift(startRow + 1, 0); + cells.splice.apply(cells, args); + this.$updateRows(); + } + }; + + this.$updateRows = function() { + var lw = this.session.lineWidgets; + if (!lw) return; + var noWidgets = true; + lw.forEach(function(w, i) { + if (w) { + noWidgets = false; + w.row = i; + } + }); + if (noWidgets) + this.session.lineWidgets = null; + } + + this.addLineWidget = function(w) { + if (!this.session.lineWidgets) + this.session.lineWidgets = Array(this.session.getLength()) + + this.session.lineWidgets[w.row] = w; + + var renderer = this.editor.renderer; + if (w.html && !w.el) { + w.el = dom.createElement("div"); + w.el.innerHTML = w.html; + } + if (w.el) { + dom.addCssClass(w.el, "ace_lineWidgetContainer"); + renderer.container.appendChild(w.el); + w._inDocument = true; + } + + if (!w.coverGutter) { + w.el.style.zIndex = 3; + } + if (!w.pixelHeight) { + w.pixelHeight = w.el.offsetHeight; + } + if (w.rowCount == null) + w.rowCount = Math.ceil(w.pixelHeight / renderer.layerConfig.lineHeight); + + this.session._emit("changeFold", {data:{start:{row: w.row}}}); + + this.$updateRows(); + this.renderWidgets(null, renderer); + return w; + }; + + this.removeLineWidget = function(w) { + w._inDocument = false; + if (w.el && w.el.parentNode) + w.el.parentNode.removeChild(w.el); + if (w.editor && w.editor.destroy) try { + w.editor.destroy(); + } catch(e){} + this.session.lineWidgets[w.row] = undefined; + this.session._emit("changeFold", {data:{start:{row: w.row}}}); + this.$updateRows(); + }; + + this.onWidgetChanged = function(w) { + this.session._changedWidgets.push(w); + this.editor && this.editor.renderer.updateFull(); + }; + + this.measureWidgets = function(e, renderer) { + var ws = this.session._changedWidgets; + var config = renderer.layerConfig; + + if (!ws || !ws.length) return; + var min = Infinity; + for (var i = 0; i < ws.length; i++) { + var w = ws[i].lineWidget; + if (!w._inDocument) { + w._inDocument = true; + renderer.container.appendChild(w.el); + } + + w.h = w.el.offsetHeight; + + if (!w.fixedWidth) { + w.w = w.el.offsetWidth; + w.screenWidth = Math.ceil(w.w / config.characterWidth); + } + + var rowCount = Math.ceil(w.h / config.lineHeight); + if (w.coverLine) + rowCount -= this.session.getRowLineCount(w.row); + + if (w.rowCount != rowCount) { + w.rowCount = rowCount; + if (w.row < min) + min = w.row; + } + } + if (min != Infinity) { + this.session._emit("changeFold", {data:{start:{row: min}}}); + this.session.lineWidgetWidth = null; + } + this.session._changedWidgets = []; + }; + + this.renderWidgets = function(e, renderer) { + var config = renderer.layerConfig; + var ws = this.session.lineWidgets; + if (!ws) + return; + var first = Math.min(this.firstRow, config.firstRow); + var last = Math.max(this.lastRow, config.lastRow, ws.length); + + while (first > 0 && !ws[first]) + first--; + + this.firstRow = config.firstRow; + this.lastRow = config.lastRow; + + renderer.$cursorLayer.config = config; + for (var i = first; i <= last; i++) { + var w = ws[i]; + if (!w || !w.el) continue; + + if (!w._inDocument) { + w._inDocument = true; + renderer.container.appendChild(w.el); + } + var top = renderer.$cursorLayer.getPixelPosition({row: i, column:0}, true).top; + if (!w.coverLine) + top += config.lineHeight * this.session.getRowLineCount(w.row); + w.el.style.top = top - config.offset + "px"; + + var left = w.coverGutter ? 0 : renderer.gutterWidth; + if (!w.fixedWidth) + left -= renderer.scrollLeft; + w.el.style.left = left + "px"; + + if (w.fixedWidth) { + w.el.style.right = renderer.scrollBar.getWidth() + "px"; + } else { + w.el.style.right = ""; + } + } + }; + +}).call(LineWidgets.prototype); + + +exports.LineWidgets = LineWidgets; + +}); + + + +