From 22edc9127a1eeaa1cc4d078c63995c83b238dce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonatan=20K=C5=82osko?= Date: Fri, 16 Feb 2024 20:32:01 +0100 Subject: [PATCH] Send expanded completion hint for maps and bitstrings (#2488) --- assets/js/hooks/cell_editor/live_editor.js | 42 +++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/assets/js/hooks/cell_editor/live_editor.js b/assets/js/hooks/cell_editor/live_editor.js index cfcb44e09..93dda816f 100644 --- a/assets/js/hooks/cell_editor/live_editor.js +++ b/assets/js/hooks/cell_editor/live_editor.js @@ -18,6 +18,7 @@ import { foldGutter, LanguageDescription, codeFolding, + syntaxTree, } from "@codemirror/language"; import { history } from "@codemirror/commands"; import { highlightSelectionMatches } from "@codemirror/search"; @@ -380,15 +381,16 @@ export default class LiveEditor { // Trigger completion implicitly only for identifiers and members const triggerBeforeCursor = context.matchBefore(/[\w?!.]$/); - const lineUntilCursor = context.matchBefore(/^.*/); if (!triggerBeforeCursor && !context.explicit) { return null; } + const textUntilCursor = this.getCompletionHint(context); + return this.connection .intellisenseRequest("completion", { - hint: lineUntilCursor.text, + hint: textUntilCursor, editor_auto_completion: settings.editor_auto_completion, }) .then((response) => { @@ -405,12 +407,12 @@ export default class LiveEditor { }); const replaceLength = replacedSuffixLength( - lineUntilCursor.text, + textUntilCursor, response.items[0].insert_text, ); return { - from: lineUntilCursor.to - replaceLength, + from: context.pos - replaceLength, options: completions, validFor: /^\w*[!?]?$/, }; @@ -418,6 +420,26 @@ export default class LiveEditor { .catch(() => null); } + /** @private */ + getCompletionHint(context) { + // By default we only send the current line content until cursor + // as completion hint. We use the local AST to send more context + // for multiline expressions where we know it's relevant. + + const tree = syntaxTree(context.state); + const node = tree.resolve(context.pos); + + if (node && this.language === "elixir") { + const boundaryNode = closestNode(node, ["Map", "Bitstring"]); + + if (boundaryNode) { + return context.state.doc.sliceString(boundaryNode.from, context.pos); + } + } + + return context.matchBefore(/^.*/).text; + } + /** @private */ completionItemToCompletions(item) { const completion = { @@ -538,3 +560,15 @@ export default class LiveEditor { this.initialWidgets = {}; } } + +function closestNode(node, names) { + while (node) { + if (names.includes(node.type.name)) { + return node; + } + + node = node.parent; + } + + return null; +}