diff --git a/assets/js/hooks/cell_editor/live_editor.js b/assets/js/hooks/cell_editor/live_editor.js index 93dda816f..834c6fb76 100644 --- a/assets/js/hooks/cell_editor/live_editor.js +++ b/assets/js/hooks/cell_editor/live_editor.js @@ -52,6 +52,7 @@ import CollabClient from "./live_editor/collab_client"; import { languages } from "./live_editor/codemirror/languages"; import { exitMulticursor } from "./live_editor/codemirror/commands"; import { highlight } from "./live_editor/highlight"; +import { ancestorNode, closestNode } from "./live_editor/codemirror/tree_utils"; /** * Mounts cell source editor with real-time collaboration mechanism. @@ -513,7 +514,7 @@ export default class LiveEditor { /** @private */ signatureSource({ state, pos }) { - const textUntilCursor = state.doc.sliceString(0, pos); + const textUntilCursor = this.getSignatureHint(state, pos); return this.connection .intellisenseRequest("signature", { @@ -528,6 +529,33 @@ export default class LiveEditor { .catch(() => null); } + /** @private */ + getSignatureHint(state, pos) { + // By default we send all text until cursor as signature hint. + // We use the local AST to limit the hint to the relevanat call + // expression. + + const tree = syntaxTree(state); + const node = tree.resolve(pos); + + if (node && this.language === "elixir") { + let callNode = closestNode(node, [ + "Call", + "FunctionDefinitionCall", + "KernelCall", + ]); + + if (callNode) { + const pipeNode = ancestorNode(callNode, ["Right", "PipeOperator"]); + const boundaryNode = pipeNode || callNode; + + return state.doc.sliceString(boundaryNode.from, pos); + } + } + + return state.doc.sliceString(0, pos); + } + formatterSource(doc) { return this.connection .intellisenseRequest("format", { code: doc.toString() }) @@ -560,15 +588,3 @@ 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; -} diff --git a/assets/js/hooks/cell_editor/live_editor/codemirror/tree_utils.js b/assets/js/hooks/cell_editor/live_editor/codemirror/tree_utils.js new file mode 100644 index 000000000..053a12235 --- /dev/null +++ b/assets/js/hooks/cell_editor/live_editor/codemirror/tree_utils.js @@ -0,0 +1,32 @@ +/** + * Finds the closest node with the given name (parent or self). + */ +export function closestNode(node, names) { + while (node) { + if (names.includes(node.type.name)) return node; + + node = node.parent; + } + + return null; +} + +/** + * Goes up the tree using the given path. + * + * Path is a list of parent node names, from innermost to outermost. + * Returns the alst node on the path, but only if the path matches + * exactly. + */ +export function ancestorNode(node, path) { + let i = 0; + + while (i < path.length && node.parent) { + if (node.parent.type.name !== path[i]) return null; + + node = node.parent; + i++; + } + + return i === path.length && node ? node : null; +}