附录 A:技术介绍
本附录包含面向开发人员和其他希望详细了解 Spring Shell 如何操作 在内部工作,以及它的设计决策是什么。
A.1. 命令注册
定义命令注册是介绍命令结构及其选项的第一步 和参数。这与后面发生的事情松散地解耦,例如解析命令行输入和运行 实际目标代码。从本质上讲,它是向用户显示的命令 API 的定义。
A.1.1. 命令
结构中的命令定义为命令数组。这将产生一个
结构,类似于以下示例:spring-shell
command1 sub1
command2 sub1 subsub1
command2 sub2 subsub1
command2 sub2 subsub2
如果定义了子命令,我们目前不支持将命令映射到显式父级。
例如,不能同时注册 和 。command1 sub1 command1 sub1 subsub1 |
A.1.2. 交互模式
Spring Shell 被设计为在两种模式下工作:交互式(本质上是
是您在一系列命令中拥有活动 shell 实例的位置)和
非交互式 (命令从命令行逐个执行)。REPL
这些模式之间的差异主要在于对可以执行的操作的限制 在每种模式下。例如,显示以前的 stacktrace 是不可行的 如果 shell 不再处于活动状态,则命令的命令。通常,shell 是否仍处于活动状态 指示可用信息。
此外,处于活动会话中可能会提供有关用户所经历的更多信息
在活动会话中执行。REPL
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 中的一个方法,
a 或 .Consumer
Function
方法
在现有 POJO 中使用 a 是定义目标的一种方法。
请考虑以下类: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();
功能
使用 a 作为目标可以灵活地处理
发生在命令执行中,因为您可以使用
a 给定为 a .的返回类型为
然后是打印到 shell 中的内容。请考虑以下示例:Function
CommandContext
Function
Function
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();
消费者
使用 a 与使用 基本相同,区别在于
没有 return 类型。如果你需要将某些东西打印到 shell 中,
您可以从上下文中获取对 a 的引用并打印一些内容
通过它。请考虑以下示例:Consumer
Function
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. 命令上下文
该接口允许访问当前正在运行的
上下文。您可以使用它来访问选项:CommandContext
String arg = ctx.getOptionValue("arg");
如果你需要将某些内容打印到 shell 中,你可以获取 a 并使用它的 writer 来打印一些内容:Terminal
ctx.getTerminal().writer().println("hi");
A.5. 命令目录
该接口定义命令注册在
一个 shell 应用程序。可以动态注册和取消注册
commands,为可能的用例提供了灵活性 命令
来来去去,具体取决于 shell 的状态。请考虑以下示例:CommandCatalog
CommandRegistration registration = CommandRegistration.builder().build();
catalog.register(registration);
A.5.1. 命令解析器
您可以实现该接口并定义一个 Bean 以动态
解析从命令名称到其实例的映射。考虑
以下示例:CommandResolver
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;
}
}
a 的当前限制是每次解析命令时都会使用它。
因此,如果命令解析调用需要很长时间,我们建议不要使用它,因为它会
使 shell 感觉迟钝。CommandResolver |
A.5.2. 命令目录定制器
您可以使用该界面自定义 .
它的主要用途是修改目录。此外,在自动配置中,此
接口用于将现有 bean 注册到目录中。
请考虑以下示例:CommandCatalogCustomizer
CommandCatalog
spring-shell
CommandRegistration
static class CustomCommandCatalogCustomizer implements CommandCatalogCustomizer {
@Override
public void customize(CommandCatalog commandCatalog) {
CommandRegistration registration = CommandRegistration.builder()
.command("resolve command")
.build();
commandCatalog.register(registration);
}
}
你可以创建一个 as bean,Spring Shell 会处理其余的工作。CommandCatalogCustomizer
A.6. 主题
主题中的样式是通过使用 AttributedString from 来提供的。
不幸的是,样式设置大多没有记录,但我们尝试通过
它的一些功能在这里。JLine
JLine
在样式规范中,是一个具有特殊格式的字符串。可以给出 spec
如果用逗号分隔,则多次。规范将为
foreground、background 或其模式。特殊格式允许
如果前者由于某种原因无效,则在后一个规范中定义一个默认值。JLine
<spec>:=<spec>
如果 spec 包含冒号,则其前部分表示前景或背景
可能的值为 、 或 。没有 rbg 的 color 值
是允许的颜色 、 、 或 的名称。颜色的短格式分别为 、 、 、 、 和 。如果 color 以 或 为前缀,则亮色模式会自动
应用的。前缀 with 将从 JLine 内部 bsd 颜色表中解析。foreground
fg
f
background
bg
b
foreground-rgb
fg-rgb
f-rgb
background-rgb
bg-rgb
b-rgb
black
red
green
yellow
blue
magenta
cyan
white
k
r
g
y
b
m
c
w
!
bright-
~
如果 rgb 格式是预期的,并且前缀为 either 或 normal
使用十六进制格式。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
是将文本与模式匹配的界面。火柴
results 位于 return value 中。比赛结果
包含有关对战位置和对战总分的信息。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