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.HelpCommand;
|
||||
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.thread.ThreadFactoryBuilder;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.LineReaderBuilder;
|
||||
import org.jline.reader.impl.history.DefaultHistory;
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.jline.terminal.TerminalBuilder;
|
||||
import org.jline.utils.Log;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.bridge.SLF4JBridgeHandler;
|
||||
|
||||
import javax.smartcardio.TerminalFactory;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/**
|
||||
* 启动类
|
||||
@@ -100,6 +98,7 @@ public final class ManagerLite {
|
||||
}
|
||||
LineReader lineReader = LineReaderBuilder.builder()
|
||||
.terminal(terminal)
|
||||
.completer(new CommandCompleter(commandDispatcher))
|
||||
// 如果想记录历史命令,可以配置一个 History
|
||||
.history(new DefaultHistory())
|
||||
.build();
|
||||
@@ -109,21 +108,16 @@ public final class ManagerLite {
|
||||
final String prompt = "client> ";
|
||||
|
||||
InstanceContextHolder.setAdditional("command-process");
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
String cmd = lineReader.readLine(prompt);
|
||||
|
||||
if ("stop".equalsIgnoreCase(cmd)) {
|
||||
break;
|
||||
}
|
||||
commandDispatcher.onCommand(cmd);
|
||||
terminal.flush();
|
||||
} catch (Exception e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,6 +141,8 @@ public final class ManagerLite {
|
||||
commandDispatcher.register(new ReloadCommand(configuration, systemInitializer));
|
||||
// config
|
||||
commandDispatcher.register(new ConfigCommand(configuration));
|
||||
// stop
|
||||
commandDispatcher.register(new StopCommand());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -41,4 +46,24 @@ public interface Command {
|
||||
* 获取该指令的用法
|
||||
*/
|
||||
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.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
|
||||
@@ -33,4 +40,37 @@ public class ConfigCommand extends AbstractCommand {
|
||||
final String value = args[1];
|
||||
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.Command;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -21,7 +26,7 @@ public class HelpCommand extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(String[] args) {
|
||||
final Map<String, Command> commands = CommandDispatcher.getInstance().getCommands();
|
||||
final Map<String, Command> commands = getAllCommands();
|
||||
|
||||
log.info("==========================================");
|
||||
if (hasArgs(args)) {
|
||||
@@ -30,7 +35,7 @@ public class HelpCommand extends AbstractCommand {
|
||||
if (command == null) {
|
||||
log.warn("无法找到指令 {} 的相关信息, 请使用 help 查看可用的指令及帮助!", cmd);
|
||||
} else {
|
||||
log.info("指令:\t{}\t-\t{}\t-\t{}", cmd, command.getDescription(), command.getUsage());
|
||||
log.info("指令:{} - {} - {}", cmd, command.getDescription(), command.getUsage());
|
||||
}
|
||||
} else {
|
||||
commands.forEach((k, v) -> {
|
||||
@@ -38,13 +43,51 @@ public class HelpCommand extends AbstractCommand {
|
||||
if (k.equals(getName())) {
|
||||
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("使用 help <指令> 来查看更详细的帮助信息.");
|
||||
}
|
||||
log.info("==========================================");
|
||||
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