From 6c73775961dc2e3935e503cbece5950783a1a395 Mon Sep 17 00:00:00 2001 From: Max Rydahl Andersen Date: Mon, 1 Sep 2025 00:37:42 +0200 Subject: [PATCH] feat: add completion of % and %% magics --- .../main/java/org/dflib/jjava/JavaKernel.java | 31 ++++++++++++++++++- .../jupyter/kernel/magic/registry/Magics.java | 9 ++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/jjava/src/main/java/org/dflib/jjava/JavaKernel.java b/jjava/src/main/java/org/dflib/jjava/JavaKernel.java index 6546309..c03b7bd 100644 --- a/jjava/src/main/java/org/dflib/jjava/JavaKernel.java +++ b/jjava/src/main/java/org/dflib/jjava/JavaKernel.java @@ -398,7 +398,36 @@ public DisplayData inspect(String code, int at, boolean extraDetail) { @Override public ReplacementOptions complete(String code, int at) { int[] replaceStart = new int[1]; // As of now this is always the same as the cursor... - List suggestions = this.evaluator.getShell().sourceCodeAnalysis().completionSuggestions(code, at, replaceStart); + // Check if the cursor is at the end of a line starting with % or %% (cell/line magic) + // and if so, offer completions for all magic aliases and names. + int lineStart = code.lastIndexOf('\n', at - 1) + 1; + String line = code.substring(lineStart, at); + // Match % or %% at start of line, followed by an identifier, and cursor is at end of that identifier + java.util.regex.Matcher magicMatcher = java.util.regex.Pattern + .compile("^(%{1,2})([\\w\\-]*)$") + .matcher(line); + if (magicMatcher.find()) { + String percent = magicMatcher.group(1); + String prefix = magicMatcher.group(2); + + java.util.Set magics = percent.equals("%%") ? this.magics.getAllCellMagicNames() : this.magics.getAllLineMagicNames(); + + // Get all magic names and aliases + + // Filter by prefix if present + java.util.List options = magics.stream() + .filter(name -> name.startsWith(prefix)) + .map(name -> percent + name) + .sorted() + .collect(java.util.stream.Collectors.toList()); + if (!options.isEmpty()) { + int replaceFrom = lineStart; + int replaceTo = at; + return new ReplacementOptions(options, replaceFrom, replaceTo); + } + } + + List suggestions = this.evaluator.getShell().sourceCodeAnalysis().completionSuggestions(code, at, replaceStart); if (suggestions == null || suggestions.isEmpty()) return null; List options = suggestions.stream() diff --git a/jupyter-jvm-basekernel/src/main/java/org/dflib/jjava/jupyter/kernel/magic/registry/Magics.java b/jupyter-jvm-basekernel/src/main/java/org/dflib/jjava/jupyter/kernel/magic/registry/Magics.java index 4a7f6b2..5868c69 100644 --- a/jupyter-jvm-basekernel/src/main/java/org/dflib/jjava/jupyter/kernel/magic/registry/Magics.java +++ b/jupyter-jvm-basekernel/src/main/java/org/dflib/jjava/jupyter/kernel/magic/registry/Magics.java @@ -8,6 +8,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; public class Magics { private final Map> lineMagics; @@ -232,4 +233,12 @@ private void registerCellReflectionMagic(Object instance, Method method, CellMag registerCellMagic(method, cellMagic, new CellReflectionMagicFunction(instance, method)); } + + public Set getAllCellMagicNames() { + return this.cellMagics.keySet(); + } + + public Set getAllLineMagicNames() { + return this.lineMagics.keySet(); + } }