Skip to content

Commit 76a0732

Browse files
committed
8366691: JShell should support a more convenient completion
Reviewed-by: asotona
1 parent 6df78c4 commit 76a0732

File tree

8 files changed

+881
-162
lines changed

8 files changed

+881
-162
lines changed

src/jdk.jshell/share/classes/jdk/internal/jshell/tool/ConsoleIOContext.java

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -345,18 +345,21 @@ private boolean complete(CompletionState completionState) {
345345
ConsoleIOContextTestSupport.willComputeCompletion();
346346
int[] anchor = new int[] {-1};
347347
List<Suggestion> suggestions;
348-
List<String> doc;
348+
List<AttributedString> doc;
349349
boolean command = prefix.isEmpty() && text.startsWith("/");
350350
if (command) {
351351
suggestions = repl.commandCompletionSuggestions(text, cursor, anchor);
352-
doc = repl.commandDocumentation(text, cursor, true);
352+
doc = repl.commandDocumentation(text, cursor, true)
353+
.stream()
354+
.map(AttributedString::new)
355+
.toList();
353356
} else {
354357
int prefixLength = prefix.length();
355358
suggestions = repl.analysis.completionSuggestions(prefix + text, cursor + prefixLength, anchor);
356359
anchor[0] -= prefixLength;
357360
doc = repl.analysis.documentation(prefix + text, cursor + prefix.length(), false)
358361
.stream()
359-
.map(Documentation::signature)
362+
.map(this::renderSignature)
360363
.toList();
361364
}
362365
long smartCount = suggestions.stream().filter(Suggestion::matchesType).count();
@@ -502,6 +505,41 @@ private boolean complete(CompletionState completionState) {
502505
}
503506
}
504507

508+
private AttributedString renderSignature(Documentation doc) {
509+
int activeParamIndex = doc.activeParameterIndex();
510+
String signature = doc.signature();
511+
512+
if (activeParamIndex == (-1)) {
513+
return new AttributedString(signature);
514+
}
515+
516+
int lparen = signature.indexOf('(');
517+
int rparen = signature.indexOf(')', lparen);
518+
519+
if (lparen == (-1) || rparen == (-1)) {
520+
return new AttributedString(signature);
521+
}
522+
523+
AttributedStringBuilder result = new AttributedStringBuilder();
524+
525+
result.append(signature.substring(0, lparen + 1), AttributedStyle.DEFAULT);
526+
527+
String[] params = signature.substring(lparen + 1, rparen).split(", *");
528+
String sep = "";
529+
530+
for (int i = 0; i < params.length; i++) {
531+
result.append(sep);
532+
result.append(params[i], i == activeParamIndex ? AttributedStyle.BOLD
533+
: AttributedStyle.DEFAULT);
534+
535+
sep = ", ";
536+
}
537+
538+
result.append(signature.substring(rparen), AttributedStyle.DEFAULT);
539+
540+
return result.toAttributedString();
541+
}
542+
505543
private CompletionTask.Result doPrintFullDocumentation(List<CompletionTask> todo, List<String> doc, boolean command) {
506544
if (doc != null && !doc.isEmpty()) {
507545
Terminal term = in.getTerminal();
@@ -722,9 +760,9 @@ private void printColumns(List<? extends CharSequence> candidates) {
722760

723761
private final class CommandSynopsisTask implements CompletionTask {
724762

725-
private final List<String> synopsis;
763+
private final List<AttributedString> synopsis;
726764

727-
public CommandSynopsisTask(List<String> synposis) {
765+
public CommandSynopsisTask(List<AttributedString> synposis) {
728766
this.synopsis = synposis;
729767
}
730768

@@ -738,6 +776,7 @@ public Result perform(String text, int cursor) throws IOException {
738776
// try {
739777
in.getTerminal().writer().println();
740778
in.getTerminal().writer().println(synopsis.stream()
779+
.map(doc -> doc.toAnsi(in.getTerminal()))
741780
.map(l -> l.replaceAll("\n", LINE_SEPARATOR))
742781
.collect(Collectors.joining(LINE_SEPARATORS2)));
743782
// } catch (IOException ex) {
@@ -771,9 +810,9 @@ public Result perform(String text, int cursor) throws IOException {
771810

772811
private final class ExpressionSignaturesTask implements CompletionTask {
773812

774-
private final List<String> doc;
813+
private final List<AttributedString> doc;
775814

776-
public ExpressionSignaturesTask(List<String> doc) {
815+
public ExpressionSignaturesTask(List<AttributedString> doc) {
777816
this.doc = doc;
778817
}
779818

@@ -786,7 +825,9 @@ public String description() {
786825
public Result perform(String text, int cursor) throws IOException {
787826
in.getTerminal().writer().println();
788827
in.getTerminal().writer().println(repl.getResourceString("jshell.console.completion.current.signatures"));
789-
in.getTerminal().writer().println(doc.stream().collect(Collectors.joining(LINE_SEPARATOR)));
828+
in.getTerminal().writer().println(doc.stream()
829+
.map(doc -> doc.toAnsi(in.getTerminal()))
830+
.collect(Collectors.joining(LINE_SEPARATOR)));
790831
return Result.FINISH;
791832
}
792833

src/jdk.jshell/share/classes/jdk/internal/shellsupport/doc/JavadocHelper.java

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public static JavadocHelper create(JavacTask mainTask, Collection<? extends Path
118118
StandardJavaFileManager fm = compiler.getStandardFileManager(null, null, null);
119119
try {
120120
fm.setLocationFromPaths(StandardLocation.SOURCE_PATH, sourceLocations);
121-
return new OnDemandJavadocHelper(mainTask, fm);
121+
return new OnDemandJavadocHelper(mainTask, fm, sourceLocations);
122122
} catch (IOException ex) {
123123
try {
124124
fm.close();
@@ -135,6 +135,21 @@ public Element getSourceElement(Element forElement) throws IOException {
135135
}
136136
@Override
137137
public void close() throws IOException {}
138+
139+
@Override
140+
public String getResolvedDocComment(StoredElement forElement) throws IOException {
141+
return null;
142+
}
143+
144+
@Override
145+
public StoredElement getHandle(Element forElement) {
146+
return null;
147+
}
148+
149+
@Override
150+
public Collection<? extends Path> getSourceLocations() {
151+
return List.of();
152+
}
138153
};
139154
}
140155
}
@@ -147,6 +162,7 @@ public void close() throws IOException {}
147162
* @throws IOException if something goes wrong in the search
148163
*/
149164
public abstract String getResolvedDocComment(Element forElement) throws IOException;
165+
public abstract String getResolvedDocComment(StoredElement forElement) throws IOException;
150166

151167
/**Returns an element representing the same given program element, but the returned element will
152168
* be resolved from source, if it can be found. Returns the original element if the source for
@@ -158,23 +174,30 @@ public void close() throws IOException {}
158174
*/
159175
public abstract Element getSourceElement(Element forElement) throws IOException;
160176

177+
public abstract StoredElement getHandle(Element forElement);
178+
public abstract Collection<? extends Path> getSourceLocations();
179+
161180
/**Closes the helper.
162181
*
163182
* @throws IOException if something foes wrong during the close
164183
*/
165184
@Override
166185
public abstract void close() throws IOException;
167186

187+
public record StoredElement(String module, String binaryName, String handle) {}
188+
168189
private static final class OnDemandJavadocHelper extends JavadocHelper {
169190
private final JavacTask mainTask;
170191
private final JavaFileManager baseFileManager;
171192
private final StandardJavaFileManager fm;
172193
private final Map<String, Pair<JavacTask, TreePath>> signature2Source = new HashMap<>();
194+
private final Collection<? extends Path> sourceLocations;
173195

174-
private OnDemandJavadocHelper(JavacTask mainTask, StandardJavaFileManager fm) {
196+
private OnDemandJavadocHelper(JavacTask mainTask, StandardJavaFileManager fm, Collection<? extends Path> sourceLocations) {
175197
this.mainTask = mainTask;
176198
this.baseFileManager = ((JavacTaskImpl) mainTask).getContext().get(JavaFileManager.class);
177199
this.fm = fm;
200+
this.sourceLocations = sourceLocations;
178201
}
179202

180203
@Override
@@ -187,6 +210,16 @@ public String getResolvedDocComment(Element forElement) throws IOException {
187210
return getResolvedDocComment(sourceElement.fst, sourceElement.snd);
188211
}
189212

213+
@Override
214+
public String getResolvedDocComment(StoredElement forElement) throws IOException {
215+
Pair<JavacTask, TreePath> sourceElement = getSourceElement(forElement);
216+
217+
if (sourceElement == null)
218+
return null;
219+
220+
return getResolvedDocComment(sourceElement.fst, sourceElement.snd);
221+
}
222+
190223
@Override
191224
public Element getSourceElement(Element forElement) throws IOException {
192225
Pair<JavacTask, TreePath> sourceElement = getSourceElement(mainTask, forElement);
@@ -202,7 +235,30 @@ public Element getSourceElement(Element forElement) throws IOException {
202235
return result;
203236
}
204237

205-
private String getResolvedDocComment(JavacTask task, TreePath el) throws IOException {
238+
@Override
239+
public StoredElement getHandle(Element forElement) {
240+
TypeElement type = topLevelType(forElement);
241+
242+
if (type == null)
243+
return null;
244+
245+
Elements elements = mainTask.getElements();
246+
ModuleElement module = elements.getModuleOf(type);
247+
String moduleName = module == null || module.isUnnamed()
248+
? null
249+
: module.getQualifiedName().toString();
250+
String binaryName = elements.getBinaryName(type).toString();
251+
String handle = elementSignature(forElement);
252+
253+
return new StoredElement(moduleName, binaryName, handle);
254+
}
255+
256+
@Override
257+
public Collection<? extends Path> getSourceLocations() {
258+
return sourceLocations;
259+
}
260+
261+
private String getResolvedDocComment(JavacTask task, TreePath el) throws IOException {
206262
DocTrees trees = DocTrees.instance(task);
207263
Element element = trees.getElement(el);
208264
String docComment = trees.getDocComment(el);
@@ -634,7 +690,7 @@ private Stream<ExecutableElement> superMethodsForInheritDoc(JavacTask task,
634690
.filter(supMethod -> task.getElements().overrides(method, supMethod, type));
635691
}
636692

637-
/* Find types from which methods in type may inherit javadoc, in the proper order.*/
693+
/* Find types from which methods in binaryName may inherit javadoc, in the proper order.*/
638694
private Stream<Element> superTypeForInheritDoc(JavacTask task, Element type) {
639695
TypeElement clazz = (TypeElement) type;
640696
Stream<Element> result = interfaces(clazz);
@@ -701,6 +757,35 @@ private String getThrownException(JavacTask task, TreePath rootOn, DocCommentTre
701757
return exc != null ? exc.toString() : null;
702758
}
703759

760+
private Pair<JavacTask, TreePath> getSourceElement(StoredElement el) throws IOException {
761+
if (el == null) {
762+
return null;
763+
}
764+
765+
String handle = el.handle();
766+
Pair<JavacTask, TreePath> cached = signature2Source.get(handle);
767+
768+
if (cached != null) {
769+
return cached.fst != null ? cached : null;
770+
}
771+
772+
Pair<JavacTask, CompilationUnitTree> source = findSource(el.module(), el.binaryName());
773+
774+
if (source == null)
775+
return null;
776+
777+
fillElementCache(source.fst, source.snd);
778+
779+
cached = signature2Source.get(handle);
780+
781+
if (cached != null) {
782+
return cached;
783+
} else {
784+
signature2Source.put(handle, Pair.of(null, null));
785+
return null;
786+
}
787+
}
788+
704789
private Pair<JavacTask, TreePath> getSourceElement(JavacTask origin, Element el) throws IOException {
705790
String handle = elementSignature(el);
706791
Pair<JavacTask, TreePath> cached = signature2Source.get(handle);

0 commit comments

Comments
 (0)