diff --git a/ace.d.ts b/ace.d.ts index 0e6ec5a42ba..a1774d518d8 100644 --- a/ace.d.ts +++ b/ace.d.ts @@ -675,6 +675,8 @@ export namespace Ace { showComposition(position: number): void; setCompositionText(text: string): void; hideComposition(): void; + setGhostText(text: string, position: Point): void; + removeGhostText(): void; setTheme(theme: string, callback?: () => void): void; getTheme(): string; setStyle(style: string, include?: boolean): void; @@ -843,6 +845,8 @@ export namespace Ace { removeWordLeft(): void; removeLineToEnd(): void; splitLine(): void; + setGhostText(text: string, position: Point): void; + removeGhostText(): void; transposeLetters(): void; toLowerCase(): void; toUpperCase(): void; diff --git a/src/css/editor.css.js b/src/css/editor.css.js index 9495bd70ad5..967cf5d7d41 100644 --- a/src/css/editor.css.js +++ b/src/css/editor.css.js @@ -583,4 +583,9 @@ styles.join("\\n") white-space: pre; opacity: 0.7; margin: 0 10px; +} + +.ace_ghost_text { + opacity: 0.5; + font-style: italic; }`; diff --git a/src/editor.js b/src/editor.js index 36902347bc2..83c8589e024 100644 --- a/src/editor.js +++ b/src/editor.js @@ -16,6 +16,7 @@ var CommandManager = require("./commands/command_manager").CommandManager; var defaultCommands = require("./commands/default_commands").commands; var config = require("./config"); var TokenIterator = require("./token_iterator").TokenIterator; +var LineWidgets = require("./line_widgets").LineWidgets; var clipboard = require("./clipboard"); @@ -1425,6 +1426,31 @@ Editor.$uid = 0; this.moveCursorToPosition(cursor); }; + /** + * Set the "ghost" text in provided position. "Ghost" text is a kind of + * preview text inside the editor which can be used to preview some code + * inline in the editor such as, for example, code completions. + * + * @param {String} text Text to be inserted as "ghost" text + * @param {object} position Position to insert text to + */ + this.setGhostText = function(text, position) { + if (!this.session.widgetManager) { + this.session.widgetManager = new LineWidgets(this.session); + this.session.widgetManager.attach(this); + } + this.renderer.setGhostText(text, position); + }; + + /** + * Removes "ghost" text currently displayed in the editor. + */ + this.removeGhostText = function() { + if (!this.session.widgetManager) return; + + this.renderer.removeGhostText(); + }; + /** * Transposes current line. **/ diff --git a/src/line_widgets.js b/src/line_widgets.js index e0009a7b7e5..f512d9ec13e 100644 --- a/src/line_widgets.js +++ b/src/line_widgets.js @@ -182,8 +182,15 @@ function LineWidgets(session) { w.el = dom.createElement("div"); w.el.innerHTML = w.html; } + if (w.text && !w.el) { + w.el = dom.createElement("div"); + w.el.textContent = w.text; + } if (w.el) { dom.addCssClass(w.el, "ace_lineWidgetContainer"); + if (w.className) { + dom.addCssClass(w.el, w.className); + } w.el.style.position = "absolute"; w.el.style.zIndex = 5; renderer.container.appendChild(w.el); diff --git a/src/virtual_renderer.js b/src/virtual_renderer.js index 58d214a525c..5a9b442025b 100644 --- a/src/virtual_renderer.js +++ b/src/virtual_renderer.js @@ -1617,7 +1617,46 @@ var VirtualRenderer = function(container, theme) { this.$composition = null; this.$cursorLayer.element.style.display = ""; }; - + + this.setGhostText = function(text, position) { + var cursor = this.session.selection.cursor; + var insertPosition = position || { row: cursor.row, column: cursor.column }; + + this.removeGhostText(); + + var textLines = text.split("\n"); + this.addToken(textLines[0], "ghost_text", insertPosition.row, insertPosition.column); + this.$ghostText = { + text: text, + position: { + row: insertPosition.row, + column: insertPosition. column + } + }; + if (textLines.length > 1) { + this.$ghostTextWidget = { + text: textLines.slice(1).join("\n"), + row: insertPosition.row, + column: insertPosition.column, + className: "ace_ghost_text" + }; + this.session.widgetManager.addLineWidget(this.$ghostTextWidget); + } + + }; + + this.removeGhostText = function() { + if (!this.$ghostText) return; + + var position = this.$ghostText.position; + this.removeExtraToken(position.row, position.column); + if (this.$ghostTextWidget) { + this.session.widgetManager.removeLineWidget(this.$ghostTextWidget); + this.$ghostTextWidget = null; + } + this.$ghostText = null; + }; + this.addToken = function(text, type, row, column) { var session = this.session; session.bgTokenizer.lines[row] = null; diff --git a/src/virtual_renderer_test.js b/src/virtual_renderer_test.js index e307fa1b685..d0754ab169e 100644 --- a/src/virtual_renderer_test.js +++ b/src/virtual_renderer_test.js @@ -300,6 +300,35 @@ module.exports = { ]; assertCoordsColor(values, imageData.data); }, + "test ghost text": function() { + editor.session.setValue("abcdef"); + editor.setGhostText("Ghost"); + + editor.renderer.$loop._flush(); + assert.equal(editor.renderer.content.textContent, "Ghostabcdef"); + + editor.setGhostText("Ghost", {row: 0, column: 3}); + + editor.renderer.$loop._flush(); + assert.equal(editor.renderer.content.textContent, "abcGhostdef"); + + editor.setGhostText("Ghost", {row: 0, column: 6}); + + editor.renderer.$loop._flush(); + assert.equal(editor.renderer.content.textContent, "abcdefGhost"); + }, + + "test multiline ghost text": function() { + editor.session.setValue("abcdef"); + editor.renderer.$loop._flush(); + + editor.setGhostText("Ghost1\nGhost2\nGhost3", {row: 0, column: 6}); + + editor.renderer.$loop._flush(); + assert.equal(editor.renderer.content.textContent, "abcdefGhost1"); + + assert.equal(editor.session.lineWidgets[0].el.textContent, "Ghost2\nGhost3"); + }, "test: brackets highlighting": function (done) { var renderer = editor.renderer; editor.session.setValue(