From 95af5608b655259d12cdbc4ca28cf450f9e649c7 Mon Sep 17 00:00:00 2001 From: Alice Koreman Date: Tue, 28 Nov 2023 13:07:24 +0100 Subject: [PATCH] fix: try to scroll inline preview into view (#5400) Currently, when inline preview is used at the bottom of the file, it will be invisible. This changes it to scroll down enough to get the inline preview into view. If the inline preview is bigger than the view, we scroll down so that the cursor is at the top of the screen to get the most possible into view --- src/autocomplete.js | 11 +++++++-- src/autocomplete/inline_test.js | 40 +++++++++++++++++++++++++++++++++ src/virtual_renderer.js | 19 ++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/autocomplete.js b/src/autocomplete.js index f293b618004..ba8458276d5 100644 --- a/src/autocomplete.js +++ b/src/autocomplete.js @@ -90,6 +90,7 @@ class Autocomplete { }.bind(this)); this.tooltipTimer = lang.delayedCall(this.updateDocTooltip.bind(this), 50); + this.popupTimer = lang.delayedCall(this.$updatePopupPosition.bind(this), 50); this.stickySelectionTimer = lang.delayedCall(function() { this.stickySelection = true; @@ -143,6 +144,7 @@ class Autocomplete { } this.hideDocTooltip(); this.stickySelectionTimer.cancel(); + this.popupTimer.cancel(); this.stickySelection = false; } @@ -160,9 +162,14 @@ class Autocomplete { this.tooltipTimer.call(null, null); return; } + + // Update the popup position after a short wait to account for potential scrolling + this.popupTimer.schedule(); + this.tooltipTimer.schedule(); + } else { + this.popupTimer.call(null, null); + this.tooltipTimer.call(null, null); } - this.$updatePopupPosition(); - this.tooltipTimer.call(null, null); } $onPopupShow(hide) { diff --git a/src/autocomplete/inline_test.js b/src/autocomplete/inline_test.js index fc5705b99ba..5e0ee5f5c3a 100644 --- a/src/autocomplete/inline_test.js +++ b/src/autocomplete/inline_test.js @@ -42,6 +42,14 @@ var completions = [ value: "f should not show inline", score: 0, hideInlinePreview: true + }, + { + value: "long\nlong\nlong\nlong\nlong\nlong", + score: 0 + }, + { + value: "long\nlong\nlong\nlong\nlong\nlong".repeat(100), + score: 0 } ]; @@ -261,6 +269,38 @@ module.exports = { done(); }, + "test: should scroll if inline preview outside": function(done) { + // Fill the editor with new lines to get the cursor to the bottom + // of the container + editor.execCommand("insertstring", "\n".repeat(200)); + + var deltaY; + var initialScrollBy = editor.renderer.scrollBy; + editor.renderer.scrollBy = function(varX, varY) { + deltaY = varY; + }; + + inline.show(editor, completions[6], "l"); + editor.renderer.$loop._flush(); + + setTimeout(() => { + // Should scroll 5 lines to get the inline preview into view + assert.strictEqual(deltaY, 50); + + inline.hide(); + editor.renderer.$loop._flush(); + + inline.show(editor, completions[7], "l"); + editor.renderer.$loop._flush(); + + setTimeout(() => { + // Should scroll as much as possbile while keeping the cursor on screen + assert.strictEqual(deltaY, 490); + editor.renderer.scrollBy = initialScrollBy; + done(); + }, 50); + }, 50); + }, tearDown: function() { inline.destroy(); editor.destroy(); diff --git a/src/virtual_renderer.js b/src/virtual_renderer.js index 3947f44f24f..916b800a27a 100644 --- a/src/virtual_renderer.js +++ b/src/virtual_renderer.js @@ -1620,6 +1620,25 @@ class VirtualRenderer { className: "ace_ghost_text" }; this.session.widgetManager.addLineWidget(this.$ghostTextWidget); + + // Check wether the line widget fits in the part of the screen currently in view + var pixelPosition = this.$cursorLayer.getPixelPosition(insertPosition, true); + var el = this.container; + var height = el.getBoundingClientRect().height; + var ghostTextHeight = textLines.length * this.lineHeight; + var fitsY = ghostTextHeight < height - pixelPosition.top; + + // If it fits, no action needed + if (fitsY) return; + + // If it can fully fit in the screen, scroll down until it fits on the screen + // if it cannot fully fit, scroll so that the cursor is at the top of the screen + // to fit as much as possible. + if (ghostTextHeight < height) { + this.scrollBy(0, (textLines.length - 1) * this.lineHeight); + } else { + this.scrollBy(0, pixelPosition.top); + } } }