From 3c082814298a718ae3624672abd80b685b033e31 Mon Sep 17 00:00:00 2001 From: Andrew Nester Date: Tue, 24 Jan 2023 18:33:08 +0100 Subject: [PATCH] feat: Added Editor API to set the ghost text --- ace.d.ts | 4 ++++ src/css/editor.css.js | 5 +++++ src/editor.js | 26 +++++++++++++++++++++++ src/line_widgets.js | 7 ++++++ src/virtual_renderer.js | 41 +++++++++++++++++++++++++++++++++++- src/virtual_renderer_test.js | 29 +++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 1 deletion(-) diff --git a/ace.d.ts b/ace.d.ts index 186c3af4731..e025e0b2943 100644 --- a/ace.d.ts +++ b/ace.d.ts @@ -665,6 +665,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; @@ -833,6 +835,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 c1e6c809aff..9ad8a41158c 100644 --- a/src/css/editor.css.js +++ b/src/css/editor.css.js @@ -576,4 +576,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 351aea03f7c..1fb6ae25d70 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"); @@ -1531,6 +1532,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 0410051ba5d..56463a60a39 100644 --- a/src/virtual_renderer.js +++ b/src/virtual_renderer.js @@ -1595,7 +1595,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 24b484c0557..68c3d8b2932 100644 --- a/src/virtual_renderer_test.js +++ b/src/virtual_renderer_test.js @@ -298,6 +298,35 @@ module.exports = { {x: 6, y: 6, color: "rgba(0, 0, 0, 0)"} ]; 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"); } // change tab size after setDocument (for text layer)