附录 A:技术介绍
本附录包含面向开发人员和其他希望详细了解 Spring Shell 如何作 在内部工作,以及它的设计决策是什么。
A.1. 命令注册
定义命令注册是介绍命令结构及其选项的第一步 和参数。这与后面发生的事情松散地解耦,例如解析命令行输入和运行 实际目标代码。从本质上讲,它是向用户显示的命令 API 的定义。
A.1.1. 命令
命令中的spring-shellstructure 定义为命令数组。这将产生一个
结构,类似于以下示例:
command1 sub1
command2 sub1 subsub1
command2 sub2 subsub1
command2 sub2 subsub2
如果定义了子命令,我们目前不支持将命令映射到显式父级。
例如command1 sub1和command1 sub1 subsub1不能同时注册。 |
A.1.2. 交互模式
Spring Shell 被设计为在两种模式下工作:交互式(本质上是
是一个REPL其中,您在一系列命令中有一个活动的 shell 实例),并且
非交互式 (命令从命令行逐个执行)。
这些模式之间的差异主要在于对可以执行的作的限制 在每种模式下。例如,显示以前的 stacktrace 是不可行的 如果 shell 不再处于活动状态,则命令的命令。通常,shell 是否仍处于活动状态 指示可用信息。
此外,处于活跃状态REPLsession 可能会提供有关用户所经历的更多信息
在活动会话中执行。
A.1.3. 选项
选项可以定义为 long 和 short,其中前缀分别为 和 。
以下示例显示了 long 和 short 选项:---
CommandRegistration.builder()
.withOption()
.longNames("myopt")
.and()
.build();
CommandRegistration.builder()
.withOption()
.shortNames('s')
.and()
.build();
A.1.4. 目标
目标定义命令的执行目标。它可以是 POJO 中的一个方法,
一个Consumer或Function.
方法
使用Method是定义目标的一种方法。
请考虑以下类:
public static class CommandPojo {
String command(String arg) {
return arg;
}
}
给定前面清单中显示的现有类,然后你可以注册它的方法:
CommandPojo pojo = new CommandPojo();
CommandRegistration.builder()
.command("command")
.withTarget()
.method(pojo, "command")
.and()
.withOption()
.longNames("arg")
.and()
.build();
功能
使用Function作为 Target 提供了很大的灵活性来处理
发生在命令执行中,因为您可以使用
一个CommandContextgiven给一个Function.来自Function是
然后是打印到 shell 中的内容。请考虑以下示例:
CommandRegistration.builder()
.command("command")
.withTarget()
.function(ctx -> {
String arg = ctx.getOptionValue("arg");
return String.format("hi, arg value is '%s'", arg);
})
.and()
.withOption()
.longNames("arg")
.and()
.build();
消费者
使用Consumer与使用Function,区别在于
没有 return 类型。如果你需要将某些东西打印到 shell 中,
您可以获取对Terminal从上下文中打印一些东西
通过它。请考虑以下示例:
CommandRegistration.builder()
.command("command")
.withTarget()
.consumer(ctx -> {
String arg = ctx.getOptionValue("arg");
ctx.getTerminal().writer()
.println(String.format("hi, arg value is '%s'", arg));
})
.and()
.withOption()
.longNames("arg")
.and()
.build();
A.4. 命令上下文
这CommandContextinterface 允许访问当前正在运行的
上下文。您可以使用它来访问选项:
String arg = ctx.getOptionValue("arg");
如果你需要将某些东西打印到 shell 中,你可以获取一个Terminal并使用它的 writer 打印一些东西:
ctx.getTerminal().writer().println("hi");
A.5. 命令目录
这CommandCataloginterface 定义命令注册在
一个 shell 应用程序。可以动态注册和取消注册
commands,为可能的用例提供了灵活性 命令
来来去去,具体取决于 shell 的状态。请考虑以下示例:
CommandRegistration registration = CommandRegistration.builder().build();
catalog.register(registration);
A.5.1. 命令解析器
您可以实现CommandResolver接口并定义一个 Bean 以动态
解析从命令名称到其CommandRegistration实例。考虑
以下示例:
static class CustomCommandResolver implements CommandResolver {
List<CommandRegistration> registrations = new ArrayList<>();
CustomCommandResolver() {
CommandRegistration resolved = CommandRegistration.builder()
.command("resolve command")
.build();
registrations.add(resolved);
}
@Override
public List<CommandRegistration> resolve() {
return registrations;
}
}
当前CommandResolver是每次解析命令时都会使用它。
因此,如果命令解析调用需要很长时间,我们建议不要使用它,因为它会
使 shell 感觉迟钝。 |
A.5.2. 命令目录定制器
您可以使用CommandCatalogCustomizerinterface 自定义CommandCatalog.
它的主要用途是修改目录。此外,在spring-shellauto-configuration,则
interface 用于注册现有的CommandRegistrationbean 导入到目录中。
请考虑以下示例:
static class CustomCommandCatalogCustomizer implements CommandCatalogCustomizer {
@Override
public void customize(CommandCatalog commandCatalog) {
CommandRegistration registration = CommandRegistration.builder()
.command("resolve command")
.build();
commandCatalog.register(registration);
}
}
您可以创建一个CommandCatalogCustomizer作为 bean,而 Spring Shell 会处理其余部分。
A.6. 主题
主题设置中的样式是通过使用 AttributedString 从JLine.
不幸的是,样式JLine大部分是无记录的,但我们尝试通过
它的一些功能在这里。
在JLine样式规范是具有特殊格式的字符串。可以给出 spec
如果用逗号分隔,则多次。规范将为
foreground、background 或其模式。特殊格式<spec>:=<spec>允许
如果前者由于某种原因无效,则在后一个规范中定义一个默认值。
如果 spec 包含冒号,则其前部分表示前景或背景
和可能的值为foreground,fg,f,background,bg,b,foreground-rgb,fg-rgb,f-rgb,background-rgb,bg-rgb或b-rgb.没有 rbg 的 color 值
是允许颜色的名称black,red,green,yellow,blue,magenta,cyan或white.颜色有其简短的格式k,r,g,y,b,m,c和w分别。如果 color 以!或bright-,则亮度模式会自动
应用的。前缀 with 将从 JLine 内部 bsd 颜色表中解析。~
如果 rgb 格式为预期,并且前缀为x或正常
使用十六进制格式。#
fg-red
fg-r
fg-rgb:red
fg-rgb:xff3333
fg-rgb:#ff3333
如果 spec 包含特殊名称default,bold,faint,italic,underline,blink,inverse,inverse-neg,inverseneg,conceal,crossed-out,crossedout或hidden样式会使用现有颜色进行相应更改。
bold
bold,fg:red
如果 spec 是一个或多个用分号分隔的数字,则 format 是 ansi 的纯部分 ASCII 代码。
31
31;1
| 解析以 dot 开头的 spec 的 JLine 特殊映射格式不能是 使用,因为我们还没有将它们映射到 Spring Shell 样式名称中。 |
A.7. 搜索算法
SearchMatch是将文本与模式匹配的界面。火柴
结果位于返回值中SearchMatchResult.比赛结果
包含有关对战位置和对战总分的信息。
FZF 的。
A.7.1. 实现
FuzzyMatchV2搜索
fzf FuzzyMatchV2Search 算法的端口。进行快速模糊搜索,效果很好 快速找到路径。
ExactMatchNaive (精确匹配幼稚)
fzf ExactMatchNaive 算法的端口。简单精确匹配效果更准确 如果您知道要搜索什么。
A.7.2. 搜索匹配
算法和默认语法隐藏在包保护的类中
因为我们不想完全打开这些,直到我们知道 API 可以正常使用
以获得更长的支持。您需要构造SearchMatch通过其
内置构建器。
SearchMatch searchMatch = SearchMatch.builder()
.caseSensitive(false)
.normalize(false)
.forward(true)
.build();
可以配置区分大小写,搜索方向 发生,或者是否应该在搜索发生之前对文本进行规范化。正常化 当不同的语言对同一类型有轻微的变体时,很方便 字符。
根据 中显示的搜索语法选择搜索算法 下表。
| 令 牌 | 比赛类型 | 描述 |
|---|---|---|
|
模糊匹配 |
匹配的项 |
|
精确匹配 |
包含 |
A.7.3. 示例
SearchMatch searchMatch = SearchMatch.builder()
.caseSensitive(false)
.normalize(false)
.forward(true)
.build();
SearchMatchResult result = searchMatch.match("foo bar baz", "fbb");
result.getStart();
// 0 - start position inclusive
result.getEnd();
// 9 - end position exclusive
result.getPositions();
// 0,4,8 - positions, inclusive
result.getScore();
// 112 - score
result.getAlgorithm();
// FuzzyMatchV2SearchMatchAlgorithm - resolved algo