Skip to content

Commit 2711b87

Browse files
gselzerclaude
andcommitted
Follow scijava-code-lsp's ScriptDialect.header
The dialect writes its imports and declarations in one header line, and names the packages it imports only for them, to hide from completion. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 6a9c499 commit 2711b87

2 files changed

Lines changed: 68 additions & 42 deletions

File tree

‎src/main/java/org/scijava/plugins/scripting/jython/completion/JythonDialect.java‎

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.ArrayList;
3333
import java.util.Collection;
3434
import java.util.List;
35+
import java.util.Map;
3536
import java.util.Set;
3637
import java.util.TreeSet;
3738

@@ -41,10 +42,11 @@
4142
import org.scijava.script.ScriptLanguage;
4243

4344
/**
44-
* How Jython scripts see their script parameters: as the Python values Jython
45-
* converts them to (e.g. {@code str} for strings, {@code int} for integers,
46-
* {@code array.array} for arrays), or else as the Java objects themselves
47-
* (e.g. {@code net.imglib2.img.Img}).
45+
* Describes how to turn Jython scripts into plain Jython code. Scripts see
46+
* their parameters as the Python values Jython converts them to (e.g.
47+
* {@code str} for strings, {@code int} for integers, {@code array.array} for
48+
* arrays), or else as the Java objects themselves (e.g.
49+
* {@code net.imglib2.img.Img}).
4850
* <p>
4951
* Each is declared as an instance of its type, in code both Jython's own
5052
* analysis (Python 2) and a Python language server knowing the Java packages
@@ -68,7 +70,36 @@ public String languageId() {
6870
}
6971

7072
@Override
71-
public String parameterType(final Class<?> type) {
73+
public Header header(final Map<String, Class<?>> parameters) {
74+
// Declare each parameter as an instance of its type, for type reasoning
75+
final List<String> types = new ArrayList<>();
76+
final List<String> declarations = new ArrayList<>();
77+
parameters.forEach((name, type) -> {
78+
final String t = parameterType(type);
79+
types.add(t);
80+
declarations.add(name + " = " + t.replace('$', '.') + "()");
81+
});
82+
// And import the packages of those types, before the declarations
83+
final List<String> code = new ArrayList<>();
84+
final Set<String> hidden = new TreeSet<>();
85+
for (final String p : packages(types)) {
86+
code.add("import " + p);
87+
// NB: "import a.b" binds a.
88+
hidden.add(p.split("\\.")[0]);
89+
}
90+
code.addAll(declarations);
91+
// Combine into a Header
92+
return new Header(String.join("; ", code), hidden);
93+
}
94+
95+
/**
96+
* @param type the Java type (of a SciJava Script parameter) declared in the
97+
* script
98+
* @return the type of the object given to the script by Jython: a Python
99+
* type for the values Jython converts (e.g. {@code str}), or else
100+
* the Java type itself
101+
*/
102+
static String parameterType(final Class<?> type) {
72103
final Class<?> t = boxed(type);
73104
// NB: As Jython converts them (Py.java2py).
74105
if (t == String.class || t == Character.class) return "str";
@@ -80,25 +111,6 @@ public String parameterType(final Class<?> type) {
80111
return t.getName();
81112
}
82113

83-
@Override
84-
public String declare(final String name, final String type) {
85-
return name + " = " + type.replace('$', '.') + "()";
86-
}
87-
88-
@Override
89-
public List<String> imports(final Collection<String> types) {
90-
final List<String> imports = new ArrayList<>();
91-
for (final String p : packages(types)) imports.add("import " + p);
92-
return imports;
93-
}
94-
95-
@Override
96-
public String imported(final String statement) {
97-
// NB: "import a.b" binds a.
98-
return statement.startsWith("import ") ? statement.substring(7).split(
99-
"[.\\s]")[0] : null;
100-
}
101-
102114
/**
103115
* The packages or modules of the given types that a declaration refers to
104116
* (none for builtins, e.g. {@code str}).

‎src/test/java/org/scijava/plugins/scripting/jython/completion/JythonDialectTest.java‎

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
import static org.junit.Assert.assertTrue;
3434

3535
import java.util.Arrays;
36+
import java.util.LinkedHashMap;
3637
import java.util.List;
38+
import java.util.Map;
3739
import java.util.concurrent.TimeUnit;
3840
import java.util.stream.Collectors;
3941

@@ -43,6 +45,7 @@
4345
import org.junit.Test;
4446
import org.scijava.Context;
4547
import org.scijava.code.lsp.LanguageServerService;
48+
import org.scijava.code.lsp.ScriptDialect.Header;
4649
import org.scijava.code.lsp.ScriptSession;
4750
import org.scijava.script.ScriptLanguage;
4851
import org.scijava.script.ScriptService;
@@ -80,26 +83,37 @@ public void testScriptsAreServed() {
8083
}
8184

8285
@Test
83-
public void testDeclarations() {
84-
final JythonDialect dialect = new JythonDialect();
85-
assertEquals("img = net.imglib2.img.Img()", dialect.declare("img",
86-
"net.imglib2.img.Img"));
86+
public void testParameterType() {
8787
// As Jython converts them: e.g. strings and numbers to Python's.
88-
assertEquals("str", dialect.parameterType(String.class));
89-
assertEquals("int", dialect.parameterType(int.class));
90-
assertEquals("int", dialect.parameterType(Long.class));
91-
assertEquals("float", dialect.parameterType(double.class));
92-
assertEquals("bool", dialect.parameterType(boolean.class));
93-
assertEquals("array.array", dialect.parameterType(double[].class));
94-
assertEquals("java.math.BigDecimal", dialect.parameterType(
88+
assertEquals("str", JythonDialect.parameterType(String.class));
89+
assertEquals("int", JythonDialect.parameterType(int.class));
90+
assertEquals("int", JythonDialect.parameterType(Long.class));
91+
assertEquals("float", JythonDialect.parameterType(double.class));
92+
assertEquals("bool", JythonDialect.parameterType(boolean.class));
93+
assertEquals("array.array", JythonDialect.parameterType(
94+
double[].class));
95+
// Or else as the Java objects themselves.
96+
assertEquals("java.math.BigDecimal", JythonDialect.parameterType(
9597
java.math.BigDecimal.class));
96-
assertEquals("n = int()", dialect.declare("n", "int"));
97-
assertEquals("e = java.util.Map.Entry()", dialect.declare("e",
98-
"java.util.Map$Entry"));
99-
assertEquals(Arrays.asList("import array", "import java.util",
100-
"import net.imglib2.img"), dialect.imports(Arrays.asList(
101-
"net.imglib2.img.Img", "str", "java.util.Map$Entry", "array.array")));
102-
assertEquals("net", dialect.imported("import net.imglib2.img"));
98+
}
99+
100+
@Test
101+
public void testHeader() {
102+
// Each parameter is declared as an instance of its type, whose package
103+
// is imported.
104+
final Map<String, Class<?>> parameters = new LinkedHashMap<>();
105+
parameters.put("d", java.math.BigDecimal.class);
106+
parameters.put("n", int.class);
107+
parameters.put("e", Map.Entry.class);
108+
parameters.put("a", double[].class);
109+
final Header header = new JythonDialect().header(parameters);
110+
assertEquals("import array; import java.math; import java.util; " +
111+
"d = java.math.BigDecimal(); n = int(); " +
112+
"e = java.util.Map.Entry(); a = array.array()", header.code());
113+
// The names the imports bind ("import a.b" binds a) are the header's
114+
// alone.
115+
assertEquals(Arrays.asList("array", "java"), Arrays.asList(header
116+
.hiddenNames().toArray()));
103117
}
104118

105119
@Test

0 commit comments

Comments
 (0)