Skip to content

Commit dcfcc54

Browse files
gselzerclaude
andcommitted
Combine Jython's analysis and jedi in one server
code-lsp no longer merges several plugins' servers. JythonServerPlugin now gives JythonLanguageServer a PythonCompanion (jedi, if offered): Jython's completions first, then jedi's for other names; jedi's signatures and hover where Jython has none; Jython's problems only. Stubs are written as scripts open and change, rather than whenever where they run was asked; and jedi's environment is the stubs alone, so interpreters share the editor's jedi. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 9413b08 commit dcfcc54

5 files changed

Lines changed: 426 additions & 200 deletions

File tree

‎src/main/java/org/scijava/plugins/scripting/jython/JythonLanguageServer.java‎

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,23 @@
3636
import java.util.List;
3737
import java.util.Map;
3838
import java.util.Set;
39+
import java.util.concurrent.CompletableFuture;
3940

4041
import javax.script.Bindings;
4142
import javax.script.ScriptContext;
4243

4344
import org.eclipse.lsp4j.CompletionItem;
4445
import org.eclipse.lsp4j.CompletionItemKind;
4546
import org.eclipse.lsp4j.CompletionList;
47+
import org.eclipse.lsp4j.CompletionParams;
48+
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
49+
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
50+
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
51+
import org.eclipse.lsp4j.Hover;
52+
import org.eclipse.lsp4j.HoverParams;
4653
import org.eclipse.lsp4j.SignatureHelp;
54+
import org.eclipse.lsp4j.SignatureHelpParams;
55+
import org.eclipse.lsp4j.jsonrpc.messages.Either;
4756
import org.python.core.PyJavaType;
4857
import org.python.core.PyModule;
4958
import org.python.core.PyObject;
@@ -62,6 +71,11 @@
6271
* a live interpreter, its variables. In a call's arguments, with nothing
6372
* typed, the variables of the parameter's type come first. Signature help
6473
* rates the overloads against the arguments typed.
74+
* <p>
75+
* If a Python language server is offered (see {@link PythonCompanion}), its
76+
* completions follow Jython's own; and its signatures and hover serve where
77+
* Jython has none.
78+
* </p>
6579
*
6680
* @author Albert Cardona
6781
* @author Curtis Rueden
@@ -80,17 +94,96 @@ public class JythonLanguageServer extends ScriptLanguageServer {
8094
private final LanguageServerService servers;
8195
private final JythonAutoCompletions engine = new JythonAutoCompletions();
8296

97+
/** A Python language server beside Jython's analysis, or null if none. */
98+
private final PythonCompanion python;
99+
83100
/**
84101
* @param servers Reads the scripts' parameters.
85102
* @param environment Where the scripts run (e.g. a live interpreter).
86103
*/
87104
public JythonLanguageServer(final LanguageServerService servers,
88105
final Environment environment)
106+
{
107+
this(servers, environment, null);
108+
}
109+
110+
/**
111+
* @param servers Reads the scripts' parameters.
112+
* @param environment Where the scripts run (e.g. a live interpreter).
113+
* @param python A Python language server beside Jython's analysis, or null.
114+
*/
115+
JythonLanguageServer(final LanguageServerService servers,
116+
final Environment environment, final PythonCompanion python)
89117
{
90118
super(environment);
91119
this.servers = servers;
120+
this.python = python;
92121
}
93122

123+
// -- LanguageServer, with the Python server beside --
124+
125+
@Override
126+
public void didOpen(final DidOpenTextDocumentParams params) {
127+
super.didOpen(params);
128+
if (python != null) python.didOpen(params);
129+
}
130+
131+
@Override
132+
public void didChange(final DidChangeTextDocumentParams params) {
133+
super.didChange(params);
134+
if (python != null) python.didChange(params);
135+
}
136+
137+
@Override
138+
public void didClose(final DidCloseTextDocumentParams params) {
139+
super.didClose(params);
140+
if (python != null) python.didClose(params);
141+
}
142+
143+
@Override
144+
public CompletableFuture<Either<List<CompletionItem>, CompletionList>>
145+
completion(final CompletionParams params)
146+
{
147+
final CompletableFuture<Either<List<CompletionItem>, CompletionList>> own =
148+
super.completion(params);
149+
if (python == null) return own;
150+
return own.thenCompose(answer -> python.completion(params, answer
151+
.getRight()));
152+
}
153+
154+
@Override
155+
public CompletableFuture<CompletionItem> resolveCompletionItem(
156+
final CompletionItem item)
157+
{
158+
if (python == null) return super.resolveCompletionItem(item);
159+
return python.resolveCompletionItem(item,
160+
super::resolveCompletionItem);
161+
}
162+
163+
@Override
164+
public CompletableFuture<SignatureHelp> signatureHelp(
165+
final SignatureHelpParams params)
166+
{
167+
final CompletableFuture<SignatureHelp> own = super.signatureHelp(params);
168+
if (python == null) return own;
169+
return own.thenCompose(help -> python.signatureHelp(params, help));
170+
}
171+
172+
@Override
173+
public CompletableFuture<Hover> hover(final HoverParams params) {
174+
final CompletableFuture<Hover> own = super.hover(params);
175+
if (python == null) return own;
176+
return own.thenCompose(h -> python.hover(params, h));
177+
}
178+
179+
@Override
180+
public CompletableFuture<Object> shutdown() {
181+
if (python != null) python.shutdown();
182+
return super.shutdown();
183+
}
184+
185+
// -- ScriptLanguageServer --
186+
94187
@Override
95188
protected CompletionList complete(final Document doc, final int caret) {
96189
final String text = doc.text();

‎src/main/java/org/scijava/plugins/scripting/jython/JythonPythonServerPlugin.java‎

Lines changed: 0 additions & 180 deletions
This file was deleted.

‎src/main/java/org/scijava/plugins/scripting/jython/JythonServerPlugin.java‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
*/
2929
package org.scijava.plugins.scripting.jython;
3030

31+
import java.io.File;
32+
3133
import org.eclipse.lsp4j.services.LanguageServer;
3234
import org.scijava.Priority;
3335
import org.scijava.code.lsp.LanguageServerService;
@@ -39,7 +41,8 @@
3941

4042
/**
4143
* Offers Jython's in-process language server (see
42-
* {@link JythonLanguageServer}).
44+
* {@link JythonLanguageServer}), with a Python language server beside it if
45+
* one is offered (see {@link PythonCompanion}).
4346
*
4447
* @author Gabriel Selzer
4548
*/
@@ -50,14 +53,33 @@ public class JythonServerPlugin implements LanguageServerPlugin {
5053
@Parameter(required = false)
5154
private LanguageServerService servers;
5255

56+
private JavaStubs stubs;
57+
5358
@Override
5459
public boolean supports(final ScriptLanguage language) {
5560
return isJython(language);
5661
}
5762

5863
@Override
5964
public LanguageServer create(final Environment environment) {
60-
return new JythonLanguageServer(servers, environment);
65+
return new JythonLanguageServer(servers, environment, PythonCompanion
66+
.launch(servers, stubs()));
67+
}
68+
69+
/** Sets the stubs, e.g. to write them elsewhere in tests. */
70+
synchronized void setStubs(final JavaStubs stubs) {
71+
this.stubs = stubs;
72+
}
73+
74+
/** Gets the stubs of Java packages, creating them on first use. */
75+
synchronized JavaStubs stubs() {
76+
if (stubs != null) return stubs;
77+
final String dir = System.getProperty("scijava.jython.stubs");
78+
stubs = new JavaStubs(dir != null ? new File(dir) : new File(System
79+
.getProperty("user.home"), ".cache" + File.separator + "scijava" +
80+
File.separator + "jython-stubs"), Thread.currentThread()
81+
.getContextClassLoader());
82+
return stubs;
6183
}
6284

6385
/** Matches "Jython" and "Python (Jython)", without claiming CPython. */

0 commit comments

Comments
 (0)