mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-20 11:04:34 +02:00
8146138: jshell tool: add /help <command>
8147495: jshell tool: correctly handle arguments on /seteditor command 8147886: jshell tool: commands don't allow reference to start-up or explicit id of dropped/failed snippets 8147887: jshell tool: /list start -- fails 8147898: jshell tool: /reload quiet -- should quiet echo Reviewed-by: jlahoda
This commit is contained in:
parent
eb64b90d89
commit
50e679eda2
4 changed files with 367 additions and 99 deletions
|
@ -33,6 +33,7 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.WatchKey;
|
import java.nio.file.WatchKey;
|
||||||
import java.nio.file.WatchService;
|
import java.nio.file.WatchService;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
|
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
|
||||||
|
@ -58,7 +59,7 @@ public class ExternalEditor {
|
||||||
this.input = input;
|
this.input = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void edit(String cmd, String initialText) {
|
private void edit(String[] cmd, String initialText) {
|
||||||
try {
|
try {
|
||||||
setupWatch(initialText);
|
setupWatch(initialText);
|
||||||
launch(cmd);
|
launch(cmd);
|
||||||
|
@ -106,8 +107,10 @@ public class ExternalEditor {
|
||||||
watchedThread.start();
|
watchedThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launch(String cmd) throws IOException {
|
private void launch(String[] cmd) throws IOException {
|
||||||
ProcessBuilder pb = new ProcessBuilder(cmd, tmpfile.toString());
|
String[] params = Arrays.copyOf(cmd, cmd.length + 1);
|
||||||
|
params[cmd.length] = tmpfile.toString();
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(params);
|
||||||
pb = pb.inheritIO();
|
pb = pb.inheritIO();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -139,7 +142,7 @@ public class ExternalEditor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void edit(String cmd, Consumer<String> errorHandler, String initialText,
|
static void edit(String[] cmd, Consumer<String> errorHandler, String initialText,
|
||||||
Consumer<String> saveHandler, IOContext input) {
|
Consumer<String> saveHandler, IOContext input) {
|
||||||
ExternalEditor ed = new ExternalEditor(errorHandler, saveHandler, input);
|
ExternalEditor ed = new ExternalEditor(errorHandler, saveHandler, input);
|
||||||
ed.edit(cmd, initialText);
|
ed.edit(cmd, initialText);
|
||||||
|
|
|
@ -35,6 +35,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.io.StreamTokenizer;
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.AccessDeniedException;
|
import java.nio.file.AccessDeniedException;
|
||||||
|
@ -152,7 +153,7 @@ public class JShellTool {
|
||||||
private Feedback feedback = Feedback.Default;
|
private Feedback feedback = Feedback.Default;
|
||||||
private String cmdlineClasspath = null;
|
private String cmdlineClasspath = null;
|
||||||
private String cmdlineStartup = null;
|
private String cmdlineStartup = null;
|
||||||
private String editor = null;
|
private String[] editor = null;
|
||||||
|
|
||||||
// Commands and snippets which should be replayed
|
// Commands and snippets which should be replayed
|
||||||
private List<String> replayableHistory;
|
private List<String> replayableHistory;
|
||||||
|
@ -537,7 +538,7 @@ public class JShellTool {
|
||||||
arg = cmd.substring(idx + 1).trim();
|
arg = cmd.substring(idx + 1).trim();
|
||||||
cmd = cmd.substring(0, idx);
|
cmd = cmd.substring(0, idx);
|
||||||
}
|
}
|
||||||
Command[] candidates = findCommand(cmd, c -> c.kind != CommandKind.HELP_ONLY);
|
Command[] candidates = findCommand(cmd, c -> c.kind.isRealCommand);
|
||||||
if (candidates.length == 0) {
|
if (candidates.length == 0) {
|
||||||
if (!rerunHistoryEntryById(cmd.substring(1))) {
|
if (!rerunHistoryEntryById(cmd.substring(1))) {
|
||||||
hard("No such command or snippet id: %s", cmd);
|
hard("No such command or snippet id: %s", cmd);
|
||||||
|
@ -579,18 +580,33 @@ public class JShellTool {
|
||||||
public final String command;
|
public final String command;
|
||||||
public final String params;
|
public final String params;
|
||||||
public final String description;
|
public final String description;
|
||||||
|
public final String help;
|
||||||
public final Function<String,Boolean> run;
|
public final Function<String,Boolean> run;
|
||||||
public final CompletionProvider completions;
|
public final CompletionProvider completions;
|
||||||
public final CommandKind kind;
|
public final CommandKind kind;
|
||||||
|
|
||||||
public Command(String command, String params, String description, Function<String,Boolean> run, CompletionProvider completions) {
|
// NORMAL Commands
|
||||||
this(command, params, description, run, completions, CommandKind.NORMAL);
|
public Command(String command, String params, String description, String help,
|
||||||
|
Function<String,Boolean> run, CompletionProvider completions) {
|
||||||
|
this(command, params, description, help,
|
||||||
|
run, completions, CommandKind.NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Command(String command, String params, String description, Function<String,Boolean> run, CompletionProvider completions, CommandKind kind) {
|
// Documentation pseudo-commands
|
||||||
|
public Command(String command, String description, String help,
|
||||||
|
CommandKind kind) {
|
||||||
|
this(command, null, description, help,
|
||||||
|
arg -> { throw new IllegalStateException(); },
|
||||||
|
EMPTY_COMPLETION_PROVIDER,
|
||||||
|
kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Command(String command, String params, String description, String help,
|
||||||
|
Function<String,Boolean> run, CompletionProvider completions, CommandKind kind) {
|
||||||
this.command = command;
|
this.command = command;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
this.help = help;
|
||||||
this.run = run;
|
this.run = run;
|
||||||
this.completions = completions;
|
this.completions = completions;
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
|
@ -603,10 +619,59 @@ public class JShellTool {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum CommandKind {
|
enum CommandKind {
|
||||||
NORMAL,
|
NORMAL(true, true, true),
|
||||||
REPLAY,
|
REPLAY(true, true, true),
|
||||||
HIDDEN,
|
HIDDEN(true, false, false),
|
||||||
HELP_ONLY;
|
HELP_ONLY(false, true, false),
|
||||||
|
HELP_SUBJECT(false, false, false);
|
||||||
|
|
||||||
|
final boolean isRealCommand;
|
||||||
|
final boolean showInHelp;
|
||||||
|
final boolean shouldSuggestCompletions;
|
||||||
|
private CommandKind(boolean isRealCommand, boolean showInHelp, boolean shouldSuggestCompletions) {
|
||||||
|
this.isRealCommand = isRealCommand;
|
||||||
|
this.showInHelp = showInHelp;
|
||||||
|
this.shouldSuggestCompletions = shouldSuggestCompletions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArgTokenizer extends StreamTokenizer {
|
||||||
|
|
||||||
|
ArgTokenizer(String arg) {
|
||||||
|
super(new StringReader(arg));
|
||||||
|
resetSyntax();
|
||||||
|
wordChars(0x00, 0xFF);
|
||||||
|
quoteChar('"');
|
||||||
|
quoteChar('\'');
|
||||||
|
|
||||||
|
whitespaceChars(0x09, 0x0D);
|
||||||
|
whitespaceChars(0x1C, 0x20);
|
||||||
|
whitespaceChars(0x85, 0x85);
|
||||||
|
whitespaceChars(0xA0, 0xA0);
|
||||||
|
whitespaceChars(0x1680, 0x1680);
|
||||||
|
whitespaceChars(0x180E, 0x180E);
|
||||||
|
whitespaceChars(0x2000, 0x200A);
|
||||||
|
whitespaceChars(0x202F, 0x202F);
|
||||||
|
whitespaceChars(0x205F, 0x205F);
|
||||||
|
whitespaceChars(0x3000, 0x3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
String next() {
|
||||||
|
try {
|
||||||
|
nextToken();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return sval;
|
||||||
|
}
|
||||||
|
|
||||||
|
String val() {
|
||||||
|
return sval;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isQuoted() {
|
||||||
|
return ttype == '\'' || ttype == '"';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class FixedCompletionProvider implements CompletionProvider {
|
static final class FixedCompletionProvider implements CompletionProvider {
|
||||||
|
@ -722,86 +787,215 @@ public class JShellTool {
|
||||||
// Table of commands -- with command forms, argument kinds, help message, implementation, ...
|
// Table of commands -- with command forms, argument kinds, help message, implementation, ...
|
||||||
|
|
||||||
{
|
{
|
||||||
registerCommand(new Command("/list", "[all|start|history|<name or id>]", "list the source you have typed",
|
registerCommand(new Command("/list", "[all|start|<name or id>]", "list the source you have typed",
|
||||||
arg -> cmdList(arg),
|
"Show the source of snippets, prefaced with the snippet id.\n\n" +
|
||||||
editKeywordCompletion()));
|
"/list\n" +
|
||||||
registerCommand(new Command("/seteditor", "<executable>", "set the external editor command to use",
|
" -- List the currently active snippets of code that you typed or read with /open\n" +
|
||||||
arg -> cmdSetEditor(arg),
|
"/list start\n" +
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
" -- List the automatically evaluated start-up snippets\n" +
|
||||||
|
"/list all\n" +
|
||||||
|
" -- List all snippets including failed, overwritten, dropped, and start-up\n" +
|
||||||
|
"/list <name>\n" +
|
||||||
|
" -- List snippets with the specified name (preference for active snippets)\n" +
|
||||||
|
"/list <id>\n" +
|
||||||
|
" -- List the snippet with the specified snippet id\n",
|
||||||
|
arg -> cmdList(arg),
|
||||||
|
editKeywordCompletion()));
|
||||||
|
registerCommand(new Command("/seteditor", "<command>", "set the external editor command to use",
|
||||||
|
"Specify the command to launch for the /edit command.\n" +
|
||||||
|
"The command is an operating system dependent string.\n" +
|
||||||
|
"The command may include space-separated arguments (such as flags).\n" +
|
||||||
|
"When /edit is used, temporary file to edit will be appended as the last argument.\n",
|
||||||
|
arg -> cmdSetEditor(arg),
|
||||||
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
registerCommand(new Command("/edit", "<name or id>", "edit a source entry referenced by name or id",
|
registerCommand(new Command("/edit", "<name or id>", "edit a source entry referenced by name or id",
|
||||||
arg -> cmdEdit(arg),
|
"Edit a snippet or snippets of source in an external editor.\n" +
|
||||||
editCompletion()));
|
"The editor to use is set with /seteditor.\n" +
|
||||||
|
"If no editor has been set, a simple editor will be launched.\n\n" +
|
||||||
|
"/edit <name>\n" +
|
||||||
|
" -- Edit the snippet or snippets with the specified name (preference for active snippets)\n" +
|
||||||
|
"/edit <id>\n" +
|
||||||
|
" -- Edit the snippet with the specified snippet id\n" +
|
||||||
|
"/edit\n" +
|
||||||
|
" -- Edit the currently active snippets of code that you typed or read with /open\n",
|
||||||
|
arg -> cmdEdit(arg),
|
||||||
|
editCompletion()));
|
||||||
registerCommand(new Command("/drop", "<name or id>", "delete a source entry referenced by name or id",
|
registerCommand(new Command("/drop", "<name or id>", "delete a source entry referenced by name or id",
|
||||||
arg -> cmdDrop(arg),
|
"Drop a snippet -- making it inactive.\n\n" +
|
||||||
editCompletion(),
|
"/drop <name>\n" +
|
||||||
CommandKind.REPLAY));
|
" -- Drop the snippet with the specified name\n" +
|
||||||
registerCommand(new Command("/save", "[all|history|start] <file>", "save: <none> - current source;\n" +
|
"/drop <id>\n" +
|
||||||
" all - source including overwritten, failed, and start-up code;\n" +
|
" -- Drop the snippet with the specified snippet id\n",
|
||||||
" history - editing history;\n" +
|
arg -> cmdDrop(arg),
|
||||||
" start - default start-up definitions",
|
editCompletion(),
|
||||||
arg -> cmdSave(arg),
|
CommandKind.REPLAY));
|
||||||
saveCompletion()));
|
registerCommand(new Command("/save", "[all|history|start] <file>", "Save snippet source to a file.",
|
||||||
|
"Save the specified snippets and/or commands to the specified file.\n\n" +
|
||||||
|
"/save <file>\n" +
|
||||||
|
" -- Save the source of current active snippets to the file\n" +
|
||||||
|
"/save all <file>\n" +
|
||||||
|
" -- Save the source of all snippets to the file\n" +
|
||||||
|
" Includes source including overwritten, failed, and start-up code\n" +
|
||||||
|
"/save history <file>\n" +
|
||||||
|
" -- Save the sequential history of all commands and snippets entered since jshell was launched\n" +
|
||||||
|
"/save start <file>\n" +
|
||||||
|
" -- Save the default start-up definitions to the file\n",
|
||||||
|
arg -> cmdSave(arg),
|
||||||
|
saveCompletion()));
|
||||||
registerCommand(new Command("/open", "<file>", "open a file as source input",
|
registerCommand(new Command("/open", "<file>", "open a file as source input",
|
||||||
arg -> cmdOpen(arg),
|
"Open a file and read its contents as snippets and commands.\n\n" +
|
||||||
FILE_COMPLETION_PROVIDER));
|
"/open <file>\n" +
|
||||||
|
" -- Read the specified file as jshell input.\n",
|
||||||
|
arg -> cmdOpen(arg),
|
||||||
|
FILE_COMPLETION_PROVIDER));
|
||||||
registerCommand(new Command("/vars", null, "list the declared variables and their values",
|
registerCommand(new Command("/vars", null, "list the declared variables and their values",
|
||||||
arg -> cmdVars(),
|
"List the type, name, and value of the current active jshell variables.\n",
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
arg -> cmdVars(),
|
||||||
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
registerCommand(new Command("/methods", null, "list the declared methods and their signatures",
|
registerCommand(new Command("/methods", null, "list the declared methods and their signatures",
|
||||||
arg -> cmdMethods(),
|
"List the name, parameter types, and return type of the current active jshell methods.\n",
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
arg -> cmdMethods(),
|
||||||
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
registerCommand(new Command("/classes", null, "list the declared classes",
|
registerCommand(new Command("/classes", null, "list the declared classes",
|
||||||
arg -> cmdClasses(),
|
"List the current active jshell classes, interfaces, and enums.\n",
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
arg -> cmdClasses(),
|
||||||
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
registerCommand(new Command("/imports", null, "list the imported items",
|
registerCommand(new Command("/imports", null, "list the imported items",
|
||||||
arg -> cmdImports(),
|
"List the current active jshell imports.\n",
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
arg -> cmdImports(),
|
||||||
registerCommand(new Command("/exit", null, "exit the REPL",
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
arg -> cmdExit(),
|
registerCommand(new Command("/exit", null, "exit jshell",
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
"Leave the jshell tool. No work is saved.\n" +
|
||||||
registerCommand(new Command("/reset", null, "reset everything in the REPL",
|
"Save any work before using this command\n",
|
||||||
arg -> cmdReset(),
|
arg -> cmdExit(),
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
|
registerCommand(new Command("/reset", null, "reset jshell",
|
||||||
|
"Reset the jshell tool code and execution state:\n" +
|
||||||
|
" * All entered code is lost.\n" +
|
||||||
|
" * Start-up code is re-executed.\n" +
|
||||||
|
" * The execution state is restarted.\n" +
|
||||||
|
" * The classpath is cleared.\n" +
|
||||||
|
"Tool settings are maintained: /feedback, /prompt, and /seteditor\n" +
|
||||||
|
"Save any work before using this command\n",
|
||||||
|
arg -> cmdReset(),
|
||||||
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
registerCommand(new Command("/reload", "[restore] [quiet]", "reset and replay relevant history -- current or previous (restore)",
|
registerCommand(new Command("/reload", "[restore] [quiet]", "reset and replay relevant history -- current or previous (restore)",
|
||||||
arg -> cmdReload(arg),
|
"Reset the jshell tool code and execution state then replay each\n" +
|
||||||
reloadCompletion()));
|
"jshell valid command and valid snippet in the order they were entered.\n\n" +
|
||||||
|
"/reload\n" +
|
||||||
|
" -- Reset and replay the valid history since jshell was entered, or\n" +
|
||||||
|
" a /reset, or /reload command was executed -- whichever is most\n" +
|
||||||
|
" recent.\n" +
|
||||||
|
"/reload restore\n" +
|
||||||
|
" -- Reset and replay the valid history between the previous and most\n" +
|
||||||
|
" recent time that jshell was entered, or a /reset, or /reload\n" +
|
||||||
|
" command was executed. This can thus be used to restore a previous\n" +
|
||||||
|
" jshell tool sesson.\n" +
|
||||||
|
"/reload [restore] quiet\n" +
|
||||||
|
" -- With the 'quiet' argument the replay is not shown. Errors will display.\n",
|
||||||
|
arg -> cmdReload(arg),
|
||||||
|
reloadCompletion()));
|
||||||
registerCommand(new Command("/feedback", "<level>", "feedback information: off, concise, normal, verbose, default, or ?",
|
registerCommand(new Command("/feedback", "<level>", "feedback information: off, concise, normal, verbose, default, or ?",
|
||||||
arg -> cmdFeedback(arg),
|
"Set the level of feedback describing the effect of commands and snippets.\n\n" +
|
||||||
new FixedCompletionProvider("off", "concise", "normal", "verbose", "default", "?")));
|
"/feedback off\n" +
|
||||||
|
" -- Give no feedback\n" +
|
||||||
|
"/feedback concise\n" +
|
||||||
|
" -- Brief and generally symbolic feedback\n" +
|
||||||
|
"/feedback normal\n" +
|
||||||
|
" -- Give a natural language description of the actions\n" +
|
||||||
|
"/feedback verbose\n" +
|
||||||
|
" -- Like normal but with side-effects described\n" +
|
||||||
|
"/feedback default\n" +
|
||||||
|
" -- Same as normal for user input, off for input from a file\n",
|
||||||
|
arg -> cmdFeedback(arg),
|
||||||
|
new FixedCompletionProvider("off", "concise", "normal", "verbose", "default", "?")));
|
||||||
registerCommand(new Command("/prompt", null, "toggle display of a prompt",
|
registerCommand(new Command("/prompt", null, "toggle display of a prompt",
|
||||||
arg -> cmdPrompt(),
|
"Toggle between displaying an input prompt and not displaying a prompt.\n" +
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
"Particularly useful when pasting large amounts of text.\n",
|
||||||
|
arg -> cmdPrompt(),
|
||||||
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
registerCommand(new Command("/classpath", "<path>", "add a path to the classpath",
|
registerCommand(new Command("/classpath", "<path>", "add a path to the classpath",
|
||||||
arg -> cmdClasspath(arg),
|
"Append a additional path to the classpath.\n",
|
||||||
classPathCompletion(),
|
arg -> cmdClasspath(arg),
|
||||||
CommandKind.REPLAY));
|
classPathCompletion(),
|
||||||
|
CommandKind.REPLAY));
|
||||||
registerCommand(new Command("/history", null, "history of what you have typed",
|
registerCommand(new Command("/history", null, "history of what you have typed",
|
||||||
arg -> cmdHistory(),
|
"Display the history of snippet and command input since this jshell was launched.\n",
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
arg -> cmdHistory(),
|
||||||
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
registerCommand(new Command("/setstart", "<file>", "read file and set as the new start-up definitions",
|
registerCommand(new Command("/setstart", "<file>", "read file and set as the new start-up definitions",
|
||||||
arg -> cmdSetStart(arg),
|
"The contents of the specified file become the default start-up snippets and commands.\n",
|
||||||
FILE_COMPLETION_PROVIDER));
|
arg -> cmdSetStart(arg),
|
||||||
registerCommand(new Command("/debug", "", "toggle debugging of the REPL",
|
FILE_COMPLETION_PROVIDER));
|
||||||
arg -> cmdDebug(arg),
|
registerCommand(new Command("/debug", null, "toggle debugging of the jshell",
|
||||||
EMPTY_COMPLETION_PROVIDER,
|
"Display debugging information for the jshelll implementation.\n" +
|
||||||
CommandKind.HIDDEN));
|
"0: Debugging off\n" +
|
||||||
registerCommand(new Command("/help", "", "this help message",
|
"r: Debugging on\n" +
|
||||||
arg -> cmdHelp(),
|
"g: General debugging on\n" +
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
"f: File manager debugging on\n" +
|
||||||
registerCommand(new Command("/?", "", "this help message",
|
"c': Completion analysis debugging on\n" +
|
||||||
arg -> cmdHelp(),
|
"d': Dependency debugging on\n" +
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
"e': Event debugging on\n",
|
||||||
|
arg -> cmdDebug(arg),
|
||||||
|
EMPTY_COMPLETION_PROVIDER,
|
||||||
|
CommandKind.HIDDEN));
|
||||||
|
registerCommand(new Command("/help", "[<command>|<subject>]", "get information about jshell",
|
||||||
|
"Display information about jshell.\n" +
|
||||||
|
"/help\n" +
|
||||||
|
" -- List the jshell commands and help subjects.\n" +
|
||||||
|
"/help <command>\n" +
|
||||||
|
" -- Display information about the specified comand. The slash must be included.\n" +
|
||||||
|
" Only the first few letters of the command are needed -- if more than one\n" +
|
||||||
|
" each will be displayed. Example: /help /li\n" +
|
||||||
|
"/help <subject>\n" +
|
||||||
|
" -- Display information about the specified help subject. Example: /help intro\n",
|
||||||
|
arg -> cmdHelp(arg),
|
||||||
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
|
registerCommand(new Command("/?", "", "get information about jshell",
|
||||||
|
"Display information about jshell (abbreviation for /help).\n" +
|
||||||
|
"/?\n" +
|
||||||
|
" -- Display list of commands and help subjects.\n" +
|
||||||
|
"/? <command>\n" +
|
||||||
|
" -- Display information about the specified comand. The slash must be included.\n" +
|
||||||
|
" Only the first few letters of the command are needed -- if more than one\n" +
|
||||||
|
" match, each will be displayed. Example: /? /li\n" +
|
||||||
|
"/? <subject>\n" +
|
||||||
|
" -- Display information about the specified help subject. Example: /? intro\n",
|
||||||
|
arg -> cmdHelp(arg),
|
||||||
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
registerCommand(new Command("/!", "", "re-run last snippet",
|
registerCommand(new Command("/!", "", "re-run last snippet",
|
||||||
arg -> cmdUseHistoryEntry(-1),
|
"Reevaluate the most recently entered snippet.\n",
|
||||||
EMPTY_COMPLETION_PROVIDER));
|
arg -> cmdUseHistoryEntry(-1),
|
||||||
registerCommand(new Command("/<id>", "", "re-run snippet by id",
|
EMPTY_COMPLETION_PROVIDER));
|
||||||
arg -> { throw new IllegalStateException(); },
|
|
||||||
EMPTY_COMPLETION_PROVIDER,
|
// Documentation pseudo-commands
|
||||||
CommandKind.HELP_ONLY));
|
|
||||||
registerCommand(new Command("/-<n>", "", "re-run n-th previous snippet",
|
registerCommand(new Command("/<id>", "re-run snippet by id",
|
||||||
arg -> { throw new IllegalStateException(); },
|
"",
|
||||||
EMPTY_COMPLETION_PROVIDER,
|
CommandKind.HELP_ONLY));
|
||||||
CommandKind.HELP_ONLY));
|
registerCommand(new Command("/-<n>", "re-run n-th previous snippet",
|
||||||
|
"",
|
||||||
|
CommandKind.HELP_ONLY));
|
||||||
|
registerCommand(new Command("intro", "An introduction to the jshell tool",
|
||||||
|
"The jshell tool allows you to execute Java code, getting immediate results.\n" +
|
||||||
|
"You can enter a Java definition (variable, method, class, etc), like: int x = 8\n" +
|
||||||
|
"or a Java expression, like: x + x\n" +
|
||||||
|
"or a Java statement or import.\n" +
|
||||||
|
"These little chunks of Java code are called 'snippets'.\n\n" +
|
||||||
|
"There are also jshell commands that allow you to understand and\n" +
|
||||||
|
"control what you are doing, like: /list\n\n" +
|
||||||
|
"For a list of commands: /help",
|
||||||
|
CommandKind.HELP_SUBJECT));
|
||||||
|
registerCommand(new Command("shortcuts", "Describe shortcuts",
|
||||||
|
"Supported shortcuts include:\n\n" +
|
||||||
|
"<tab> -- After entering the first few letters of a Java identifier,\n" +
|
||||||
|
" a jshell command, or, in some cases, a jshell command argument,\n" +
|
||||||
|
" press the <tab> key to complete the input.\n" +
|
||||||
|
" If there is more than one completion, show possible completions.\n" +
|
||||||
|
"Shift-<tab> -- After the name and open parenthesis of a method or constructor invocation,\n" +
|
||||||
|
" hold the <shift> key and press the <tab> to see a synopsis of all\n" +
|
||||||
|
" matching methods/constructors.\n",
|
||||||
|
CommandKind.HELP_SUBJECT));
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Suggestion> commandCompletionSuggestions(String code, int cursor, int[] anchor) {
|
public List<Suggestion> commandCompletionSuggestions(String code, int cursor, int[] anchor) {
|
||||||
|
@ -813,7 +1007,7 @@ public class JShellTool {
|
||||||
result = commands.values()
|
result = commands.values()
|
||||||
.stream()
|
.stream()
|
||||||
.distinct()
|
.distinct()
|
||||||
.filter(cmd -> cmd.kind != CommandKind.HIDDEN && cmd.kind != CommandKind.HELP_ONLY)
|
.filter(cmd -> cmd.kind.shouldSuggestCompletions)
|
||||||
.map(cmd -> cmd.command)
|
.map(cmd -> cmd.command)
|
||||||
.filter(key -> key.startsWith(prefix))
|
.filter(key -> key.startsWith(prefix))
|
||||||
.map(key -> new Suggestion(key + " ", false));
|
.map(key -> new Suggestion(key + " ", false));
|
||||||
|
@ -856,7 +1050,11 @@ public class JShellTool {
|
||||||
hard("/seteditor requires a path argument");
|
hard("/seteditor requires a path argument");
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
editor = arg;
|
List<String> ed = new ArrayList<>();
|
||||||
|
ArgTokenizer at = new ArgTokenizer(arg);
|
||||||
|
String n;
|
||||||
|
while ((n = at.next()) != null) ed.add(n);
|
||||||
|
editor = ed.toArray(new String[ed.size()]);
|
||||||
fluff("Editor set to: %s", arg);
|
fluff("Editor set to: %s", arg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -971,11 +1169,28 @@ public class JShellTool {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean cmdHelp() {
|
boolean cmdHelp(String arg) {
|
||||||
|
if (!arg.isEmpty()) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
commands.values().stream()
|
||||||
|
.filter(c -> c.command.startsWith(arg))
|
||||||
|
.forEach(c -> {
|
||||||
|
sb.append("\n");
|
||||||
|
sb.append(c.command);
|
||||||
|
sb.append("\n\n");
|
||||||
|
sb.append(c.help);
|
||||||
|
sb.append("\n");
|
||||||
|
});
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
cmdout.print(sb);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cmdout.printf("No commands or subjects start with the provided argument: %s\n\n", arg);
|
||||||
|
}
|
||||||
int synopsisLen = 0;
|
int synopsisLen = 0;
|
||||||
Map<String, String> synopsis2Description = new LinkedHashMap<>();
|
Map<String, String> synopsis2Description = new LinkedHashMap<>();
|
||||||
for (Command cmd : new LinkedHashSet<>(commands.values())) {
|
for (Command cmd : new LinkedHashSet<>(commands.values())) {
|
||||||
if (cmd.kind == CommandKind.HIDDEN)
|
if (!cmd.kind.showInHelp)
|
||||||
continue;
|
continue;
|
||||||
StringBuilder synopsis = new StringBuilder();
|
StringBuilder synopsis = new StringBuilder();
|
||||||
synopsis.append(cmd.command);
|
synopsis.append(cmd.command);
|
||||||
|
@ -994,9 +1209,13 @@ public class JShellTool {
|
||||||
cmdout.println(e.getValue().replace("\n", indentedNewLine));
|
cmdout.println(e.getValue().replace("\n", indentedNewLine));
|
||||||
}
|
}
|
||||||
cmdout.println();
|
cmdout.println();
|
||||||
cmdout.println("Supported shortcuts include:");
|
cmdout.println("For more information type '/help' followed by the name of command or a subject.");
|
||||||
cmdout.println("<tab> -- show possible completions for the current text");
|
cmdout.println("For example '/help /list' or '/help intro'. Subjects:\n");
|
||||||
cmdout.println("Shift-<tab> -- for current method or constructor invocation, show a synopsis of the method/constructor");
|
commands.values().stream()
|
||||||
|
.filter(c -> c.kind == CommandKind.HELP_SUBJECT)
|
||||||
|
.forEach(c -> {
|
||||||
|
cmdout.printf("%-12s -- %s\n", c.command, c.description);
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1033,8 +1252,16 @@ public class JShellTool {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean inStartUp(Snippet sn) {
|
||||||
|
return mapSnippet.get(sn).space == startNamespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isActive(Snippet sn) {
|
||||||
|
return state.status(sn).isActive;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean mainActive(Snippet sn) {
|
private boolean mainActive(Snippet sn) {
|
||||||
return notInStartUp(sn) && state.status(sn).isActive;
|
return !inStartUp(sn) && isActive(sn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean matchingDeclaration(Snippet sn, String name) {
|
private boolean matchingDeclaration(Snippet sn, String name) {
|
||||||
|
@ -1054,6 +1281,10 @@ public class JShellTool {
|
||||||
if (allowAll && arg.equals("all")) {
|
if (allowAll && arg.equals("all")) {
|
||||||
// all snippets including start-up, failed, and overwritten
|
// all snippets including start-up, failed, and overwritten
|
||||||
return snippets.stream();
|
return snippets.stream();
|
||||||
|
} else if (allowAll && arg.equals("start")) {
|
||||||
|
// start-up snippets
|
||||||
|
return snippets.stream()
|
||||||
|
.filter(this::inStartUp);
|
||||||
} else if (arg.isEmpty()) {
|
} else if (arg.isEmpty()) {
|
||||||
// Default is all active user snippets
|
// Default is all active user snippets
|
||||||
return snippets.stream()
|
return snippets.stream()
|
||||||
|
@ -1063,7 +1294,7 @@ public class JShellTool {
|
||||||
nonEmptyStream(
|
nonEmptyStream(
|
||||||
() -> snippets.stream(),
|
() -> snippets.stream(),
|
||||||
// look for active user declarations matching the name
|
// look for active user declarations matching the name
|
||||||
sn -> mainActive(sn) && matchingDeclaration(sn, arg),
|
sn -> isActive(sn) && matchingDeclaration(sn, arg),
|
||||||
// else, look for any declarations matching the name
|
// else, look for any declarations matching the name
|
||||||
sn -> matchingDeclaration(sn, arg),
|
sn -> matchingDeclaration(sn, arg),
|
||||||
// else, look for an id of this name
|
// else, look for an id of this name
|
||||||
|
@ -1839,10 +2070,6 @@ public class JShellTool {
|
||||||
return feedback;
|
return feedback;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean notInStartUp(Snippet sn) {
|
|
||||||
return mapSnippet.get(sn).space != startNamespace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The current version number as a string.
|
/** The current version number as a string.
|
||||||
*/
|
*/
|
||||||
static String version() {
|
static String version() {
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8143037 8142447 8144095 8140265 8144906
|
* @bug 8143037 8142447 8144095 8140265 8144906 8146138 8147887 8147886
|
||||||
* @requires os.family != "solaris"
|
* @requires os.family != "solaris"
|
||||||
* @summary Tests for Basic tests for REPL tool
|
* @summary Tests for Basic tests for REPL tool
|
||||||
* @library /tools/lib
|
* @library /tools/lib
|
||||||
|
@ -306,19 +306,38 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testHelp() {
|
public void testHelpLength() {
|
||||||
Consumer<String> testOutput = (s) -> {
|
Consumer<String> testOutput = (s) -> {
|
||||||
List<String> ss = Stream.of(s.split("\n"))
|
List<String> ss = Stream.of(s.split("\n"))
|
||||||
.filter(l -> !l.isEmpty())
|
.filter(l -> !l.isEmpty())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
assertTrue(ss.size() >= 5, "Help does not print enough lines:\n" + s);
|
assertTrue(ss.size() >= 10, "Help does not print enough lines:\n" + s);
|
||||||
};
|
};
|
||||||
test(
|
test(
|
||||||
(a) -> assertCommandCheckOutput(a, "/?", testOutput),
|
(a) -> assertCommandCheckOutput(a, "/?", testOutput),
|
||||||
(a) -> assertCommandCheckOutput(a, "/help", testOutput)
|
(a) -> assertCommandCheckOutput(a, "/help", testOutput),
|
||||||
|
(a) -> assertCommandCheckOutput(a, "/help /list", testOutput)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testHelp() {
|
||||||
|
test(
|
||||||
|
(a) -> assertHelp(a, "/?", "/list", "/help", "/exit", "intro"),
|
||||||
|
(a) -> assertHelp(a, "/help", "/list", "/help", "/exit", "intro"),
|
||||||
|
(a) -> assertHelp(a, "/help short", "shortcuts", "<tab>"),
|
||||||
|
(a) -> assertHelp(a, "/? /li", "/list all", "snippets"),
|
||||||
|
(a) -> assertHelp(a, "/help /help", "/help <command>")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertHelp(boolean a, String command, String... find) {
|
||||||
|
assertCommandCheckOutput(a, command, s -> {
|
||||||
|
for (String f : find) {
|
||||||
|
assertTrue(s.contains(f), "Expected output of " + command + " to contain: " + f);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void oneLineOfError() {
|
public void oneLineOfError() {
|
||||||
test(
|
test(
|
||||||
(a) -> assertCommand(a, "12+", null),
|
(a) -> assertCommand(a, "12+", null),
|
||||||
|
@ -682,10 +701,12 @@ public class ToolBasicTest extends ReplToolTesting {
|
||||||
a -> assertVariable(a, "int", "aardvark"),
|
a -> assertVariable(a, "int", "aardvark"),
|
||||||
a -> assertCommandCheckOutput(a, "/list aardvark",
|
a -> assertCommandCheckOutput(a, "/list aardvark",
|
||||||
s -> assertTrue(s.contains("aardvark"))),
|
s -> assertTrue(s.contains("aardvark"))),
|
||||||
|
a -> assertCommandCheckOutput(a, "/list start",
|
||||||
|
s -> checkLineToList(s, START_UP)),
|
||||||
a -> assertCommandCheckOutput(a, "/list all",
|
a -> assertCommandCheckOutput(a, "/list all",
|
||||||
s -> checkLineToList(s, startVarList)),
|
s -> checkLineToList(s, startVarList)),
|
||||||
a -> assertCommandCheckOutput(a, "/list history",
|
a -> assertCommandCheckOutput(a, "/list printf",
|
||||||
s -> assertTrue(s.split("\n").length >= 4, s)),
|
s -> assertTrue(s.contains("void printf"))),
|
||||||
a -> assertCommandCheckOutput(a, "/list " + arg,
|
a -> assertCommandCheckOutput(a, "/list " + arg,
|
||||||
s -> assertEquals(s, "| No definition or id named " + arg +
|
s -> assertEquals(s, "| No definition or id named " + arg +
|
||||||
" found. Try /list without arguments.\n"))
|
" found. Try /list without arguments.\n"))
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 8081845
|
* @bug 8081845 8147898
|
||||||
* @summary Tests for /reload in JShell tool
|
* @summary Tests for /reload in JShell tool
|
||||||
* @modules jdk.compiler/com.sun.tools.javac.api
|
* @modules jdk.compiler/com.sun.tools.javac.api
|
||||||
* jdk.compiler/com.sun.tools.javac.main
|
* jdk.compiler/com.sun.tools.javac.main
|
||||||
|
@ -110,6 +110,23 @@ public class ToolReloadTest extends ReplToolTesting {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testReloadQuiet() {
|
||||||
|
test(false, new String[]{"-nostartup"},
|
||||||
|
a -> assertVariable(a, "int", "a"),
|
||||||
|
a -> dropVariable(a, "/dr 1", "int a = 0"),
|
||||||
|
a -> assertMethod(a, "int b() { return 0; }", "()I", "b"),
|
||||||
|
a -> dropMethod(a, "/drop b", "b ()I"),
|
||||||
|
a -> assertClass(a, "class A {}", "class", "A"),
|
||||||
|
a -> dropClass(a, "/dr A", "class A"),
|
||||||
|
a -> assertCommand(a, "/reload quiet",
|
||||||
|
"| Restarting and restoring state.\n"),
|
||||||
|
a -> assertCommandCheckOutput(a, "/vars", assertVariables()),
|
||||||
|
a -> assertCommandCheckOutput(a, "/methods", assertMethods()),
|
||||||
|
a -> assertCommandCheckOutput(a, "/classes", assertClasses()),
|
||||||
|
a -> assertCommandCheckOutput(a, "/imports", assertImports())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public void testReloadRepeat() {
|
public void testReloadRepeat() {
|
||||||
test(false, new String[]{"-nostartup"},
|
test(false, new String[]{"-nostartup"},
|
||||||
(a) -> assertVariable(a, "int", "c", "7", "7"),
|
(a) -> assertVariable(a, "int", "c", "7", "7"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue