feat: 新增命令参数补全器; 部分指令调整和适配补全器.
This commit is contained in:
@@ -14,21 +14,19 @@ import com.serliunx.ddns.support.command.CommandDispatcher;
|
|||||||
import com.serliunx.ddns.support.command.target.ConfigCommand;
|
import com.serliunx.ddns.support.command.target.ConfigCommand;
|
||||||
import com.serliunx.ddns.support.command.target.HelpCommand;
|
import com.serliunx.ddns.support.command.target.HelpCommand;
|
||||||
import com.serliunx.ddns.support.command.target.ReloadCommand;
|
import com.serliunx.ddns.support.command.target.ReloadCommand;
|
||||||
|
import com.serliunx.ddns.support.command.CommandCompleter;
|
||||||
|
import com.serliunx.ddns.support.command.target.StopCommand;
|
||||||
import com.serliunx.ddns.support.log.JLineAdaptAppender;
|
import com.serliunx.ddns.support.log.JLineAdaptAppender;
|
||||||
import com.serliunx.ddns.support.thread.ThreadFactoryBuilder;
|
|
||||||
import org.jline.reader.LineReader;
|
import org.jline.reader.LineReader;
|
||||||
import org.jline.reader.LineReaderBuilder;
|
import org.jline.reader.LineReaderBuilder;
|
||||||
import org.jline.reader.impl.history.DefaultHistory;
|
import org.jline.reader.impl.history.DefaultHistory;
|
||||||
import org.jline.terminal.Terminal;
|
import org.jline.terminal.Terminal;
|
||||||
import org.jline.terminal.TerminalBuilder;
|
import org.jline.terminal.TerminalBuilder;
|
||||||
import org.jline.utils.Log;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.slf4j.bridge.SLF4JBridgeHandler;
|
import org.slf4j.bridge.SLF4JBridgeHandler;
|
||||||
|
|
||||||
import javax.smartcardio.TerminalFactory;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.ThreadFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启动类
|
* 启动类
|
||||||
@@ -100,6 +98,7 @@ public final class ManagerLite {
|
|||||||
}
|
}
|
||||||
LineReader lineReader = LineReaderBuilder.builder()
|
LineReader lineReader = LineReaderBuilder.builder()
|
||||||
.terminal(terminal)
|
.terminal(terminal)
|
||||||
|
.completer(new CommandCompleter(commandDispatcher))
|
||||||
// 如果想记录历史命令,可以配置一个 History
|
// 如果想记录历史命令,可以配置一个 History
|
||||||
.history(new DefaultHistory())
|
.history(new DefaultHistory())
|
||||||
.build();
|
.build();
|
||||||
@@ -109,21 +108,16 @@ public final class ManagerLite {
|
|||||||
final String prompt = "client> ";
|
final String prompt = "client> ";
|
||||||
|
|
||||||
InstanceContextHolder.setAdditional("command-process");
|
InstanceContextHolder.setAdditional("command-process");
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
String cmd = lineReader.readLine(prompt);
|
String cmd = lineReader.readLine(prompt);
|
||||||
|
|
||||||
if ("stop".equalsIgnoreCase(cmd)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
commandDispatcher.onCommand(cmd);
|
commandDispatcher.onCommand(cmd);
|
||||||
terminal.flush();
|
terminal.flush();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.exit(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -147,6 +141,8 @@ public final class ManagerLite {
|
|||||||
commandDispatcher.register(new ReloadCommand(configuration, systemInitializer));
|
commandDispatcher.register(new ReloadCommand(configuration, systemInitializer));
|
||||||
// config
|
// config
|
||||||
commandDispatcher.register(new ConfigCommand(configuration));
|
commandDispatcher.register(new ConfigCommand(configuration));
|
||||||
|
// stop
|
||||||
|
commandDispatcher.register(new StopCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package com.serliunx.ddns.support.command;
|
package com.serliunx.ddns.support.command;
|
||||||
|
|
||||||
|
import org.jline.reader.Candidate;
|
||||||
|
import org.jline.reader.LineReader;
|
||||||
|
import org.jline.reader.ParsedLine;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,4 +46,24 @@ public interface Command {
|
|||||||
* 获取该指令的用法
|
* 获取该指令的用法
|
||||||
*/
|
*/
|
||||||
String getUsage();
|
String getUsage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取参数列表
|
||||||
|
*
|
||||||
|
* @return 参数
|
||||||
|
*/
|
||||||
|
default List<String> getArgs() {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令参数补全
|
||||||
|
*
|
||||||
|
* @param reader Jline的LineReader{@link LineReader}
|
||||||
|
* @param line 当前命令行的内容
|
||||||
|
* @param candidates 候选参数列表
|
||||||
|
*/
|
||||||
|
default void onComplete(LineReader reader, ParsedLine line, int index, List<Candidate> candidates) {
|
||||||
|
// do nothing by default.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.serliunx.ddns.support.command;
|
||||||
|
|
||||||
|
import org.jline.reader.Candidate;
|
||||||
|
import org.jline.reader.Completer;
|
||||||
|
import org.jline.reader.LineReader;
|
||||||
|
import org.jline.reader.ParsedLine;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jline 命令补全器
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:serliunx@yeah.net">SerLiunx</a>
|
||||||
|
* @version 1.0.4
|
||||||
|
* @since 2025/2/4
|
||||||
|
*/
|
||||||
|
public class CommandCompleter implements Completer {
|
||||||
|
|
||||||
|
private final CommandDispatcher commandDispatcher;
|
||||||
|
|
||||||
|
public CommandCompleter(CommandDispatcher commandDispatcher) {
|
||||||
|
this.commandDispatcher = commandDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates) {
|
||||||
|
final String currentWord = line.word();
|
||||||
|
Map<String, Command> commands = commandDispatcher.getCommands();
|
||||||
|
if (commands == null ||
|
||||||
|
commands.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第一个参数补全所有指令
|
||||||
|
if (line.wordIndex() == 0) {
|
||||||
|
commands.keySet().forEach(k -> {
|
||||||
|
if (k.startsWith(currentWord)) {
|
||||||
|
candidates.add(new Candidate(k));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else { // 第二个及以后交由具体的指令进行补全逻辑
|
||||||
|
final Command command = commands.get(line.words().get(0));
|
||||||
|
if (command == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
command.onComplete(reader, line, line.wordIndex(), candidates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,13 @@ package com.serliunx.ddns.support.command.target;
|
|||||||
|
|
||||||
import com.serliunx.ddns.config.Configuration;
|
import com.serliunx.ddns.config.Configuration;
|
||||||
import com.serliunx.ddns.support.command.AbstractCommand;
|
import com.serliunx.ddns.support.command.AbstractCommand;
|
||||||
|
import org.jline.reader.Candidate;
|
||||||
|
import org.jline.reader.LineReader;
|
||||||
|
import org.jline.reader.ParsedLine;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 指令: config
|
* 指令: config
|
||||||
@@ -33,4 +40,37 @@ public class ConfigCommand extends AbstractCommand {
|
|||||||
final String value = args[1];
|
final String value = args[1];
|
||||||
return configuration.modify(target, value);
|
return configuration.modify(target, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getArgs() {
|
||||||
|
final Map<String, String> allKeyAndValue;
|
||||||
|
if (configuration == null ||
|
||||||
|
(allKeyAndValue = configuration.getAllKeyAndValue()) == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return new ArrayList<>(allKeyAndValue.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(LineReader reader, ParsedLine line, int index, List<Candidate> candidates) {
|
||||||
|
if (index < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String currentWord = line.word();
|
||||||
|
|
||||||
|
// 补全配置键
|
||||||
|
if (index == 1) {
|
||||||
|
final Map<String, String> allKeyAndValue;
|
||||||
|
if (configuration == null ||
|
||||||
|
(allKeyAndValue = configuration.getAllKeyAndValue()) == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
allKeyAndValue.keySet().forEach(k -> {
|
||||||
|
if (k.startsWith(currentWord)) {
|
||||||
|
candidates.add(new Candidate(k));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,12 @@ package com.serliunx.ddns.support.command.target;
|
|||||||
import com.serliunx.ddns.support.command.AbstractCommand;
|
import com.serliunx.ddns.support.command.AbstractCommand;
|
||||||
import com.serliunx.ddns.support.command.Command;
|
import com.serliunx.ddns.support.command.Command;
|
||||||
import com.serliunx.ddns.support.command.CommandDispatcher;
|
import com.serliunx.ddns.support.command.CommandDispatcher;
|
||||||
|
import org.jline.reader.Candidate;
|
||||||
|
import org.jline.reader.LineReader;
|
||||||
|
import org.jline.reader.ParsedLine;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,7 +26,7 @@ public class HelpCommand extends AbstractCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onCommand(String[] args) {
|
public boolean onCommand(String[] args) {
|
||||||
final Map<String, Command> commands = CommandDispatcher.getInstance().getCommands();
|
final Map<String, Command> commands = getAllCommands();
|
||||||
|
|
||||||
log.info("==========================================");
|
log.info("==========================================");
|
||||||
if (hasArgs(args)) {
|
if (hasArgs(args)) {
|
||||||
@@ -30,7 +35,7 @@ public class HelpCommand extends AbstractCommand {
|
|||||||
if (command == null) {
|
if (command == null) {
|
||||||
log.warn("无法找到指令 {} 的相关信息, 请使用 help 查看可用的指令及帮助!", cmd);
|
log.warn("无法找到指令 {} 的相关信息, 请使用 help 查看可用的指令及帮助!", cmd);
|
||||||
} else {
|
} else {
|
||||||
log.info("指令:\t{}\t-\t{}\t-\t{}", cmd, command.getDescription(), command.getUsage());
|
log.info("指令:{} - {} - {}", cmd, command.getDescription(), command.getUsage());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
commands.forEach((k, v) -> {
|
commands.forEach((k, v) -> {
|
||||||
@@ -38,13 +43,51 @@ public class HelpCommand extends AbstractCommand {
|
|||||||
if (k.equals(getName())) {
|
if (k.equals(getName())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info("{}\t-\t{}\t-\t{}", k, v.getDescription(), v.getUsage());
|
log.info("{} - {} - {}", k, v.getDescription(), v.getUsage());
|
||||||
});
|
});
|
||||||
log.info("exit\t-\t退出程序\t-\tstop");
|
|
||||||
log.info("");
|
log.info("");
|
||||||
log.info("使用 help <指令> 来查看更详细的帮助信息.");
|
log.info("使用 help <指令> 来查看更详细的帮助信息.");
|
||||||
}
|
}
|
||||||
log.info("==========================================");
|
log.info("==========================================");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getArgs() {
|
||||||
|
final Map<String, Command> commands = getAllCommands();
|
||||||
|
if (commands == null ||
|
||||||
|
commands.isEmpty()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return new ArrayList<>(commands.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onComplete(LineReader reader, ParsedLine line, int index, List<Candidate> candidates) {
|
||||||
|
final Map<String, Command> commands = getAllCommands();
|
||||||
|
if (commands == null ||
|
||||||
|
commands.isEmpty() || index < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String currentWord = line.word();
|
||||||
|
|
||||||
|
if (index == 1) {
|
||||||
|
commands.keySet().forEach(k -> {
|
||||||
|
if (k.equals("help")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (k.startsWith(currentWord)) {
|
||||||
|
candidates.add(new Candidate(k));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有指令
|
||||||
|
*/
|
||||||
|
private Map<String, Command> getAllCommands() {
|
||||||
|
return CommandDispatcher.getInstance().getCommands();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.serliunx.ddns.support.command.target;
|
||||||
|
|
||||||
|
import com.serliunx.ddns.support.command.AbstractCommand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指令: stop
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:serliunx@yeah.net">SerLiunx</a>
|
||||||
|
* @version 1.0.4
|
||||||
|
* @since 2025/2/4
|
||||||
|
*/
|
||||||
|
public final class StopCommand extends AbstractCommand {
|
||||||
|
|
||||||
|
public StopCommand() {
|
||||||
|
super("stop", null, "退出程序", "stop");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(String[] args) {
|
||||||
|
System.exit(0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user