feat: 新增命令参数补全器; 部分指令调整和适配补全器.

This commit is contained in:
2025-02-04 09:18:25 +08:00
parent 1bb78644d8
commit a8e82b89f8
6 changed files with 191 additions and 14 deletions

View File

@@ -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());
} }
/** /**

View File

@@ -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.
}
} }

View File

@@ -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);
}
}
}

View File

@@ -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));
}
});
}
}
} }

View File

@@ -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();
}
} }

View File

@@ -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;
}
}