Basic zsh completion support
- This adds basic zsh support similarly to existing bash completion - New command "completion zsh" - Fix internal recursive command completion model for cases with deep nested commands - Fixes #927
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
* Copyright 2022-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -88,8 +88,9 @@ public abstract class AbstractCompletions {
|
||||
else {
|
||||
commandKey = splitKeys[i];
|
||||
}
|
||||
String desc = i + 1 < splitKeys.length ? null : registration.getDescription();
|
||||
DefaultCommandModelCommand command = commands.computeIfAbsent(commandKey,
|
||||
(fullCommand) -> new DefaultCommandModelCommand(fullCommand, main));
|
||||
(fullCommand) -> new DefaultCommandModelCommand(fullCommand, main, desc));
|
||||
|
||||
// TODO long vs short
|
||||
List<CommandModelOption> options = registration.getOptions().stream()
|
||||
@@ -147,8 +148,13 @@ public abstract class AbstractCompletions {
|
||||
interface CommandModelCommand {
|
||||
|
||||
/**
|
||||
* Gets sub-commands known to this command.
|
||||
* Gets a description of a command.
|
||||
* @return command description
|
||||
*/
|
||||
String getDescription();
|
||||
|
||||
/**
|
||||
* Gets sub-commands known to this command.
|
||||
* @return known sub-commands
|
||||
*/
|
||||
List<CommandModelCommand> getCommands();
|
||||
@@ -240,12 +246,19 @@ public abstract class AbstractCompletions {
|
||||
|
||||
private String fullCommand;
|
||||
private String mainCommand;
|
||||
private String description;
|
||||
private List<CommandModelCommand> commands = new ArrayList<>();
|
||||
private List<CommandModelOption> options = new ArrayList<>();
|
||||
|
||||
DefaultCommandModelCommand(String fullCommand, String mainCommand) {
|
||||
DefaultCommandModelCommand(String fullCommand, String mainCommand, String description) {
|
||||
this.fullCommand = fullCommand;
|
||||
this.mainCommand = mainCommand;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -293,6 +306,9 @@ public abstract class AbstractCompletions {
|
||||
}
|
||||
|
||||
void addCommand(DefaultCommandModelCommand command) {
|
||||
if (commands.contains(command)) {
|
||||
return;
|
||||
}
|
||||
commands.add(command);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.shell.standard.completion;
|
||||
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.shell.command.CommandCatalog;
|
||||
|
||||
/**
|
||||
* Completion script generator for a {@code zsh}.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
public class ZshCompletions extends AbstractCompletions {
|
||||
|
||||
public ZshCompletions(ResourceLoader resourceLoader, CommandCatalog commandCatalog) {
|
||||
super(resourceLoader, commandCatalog);
|
||||
}
|
||||
|
||||
public String generate(String rootCommand) {
|
||||
CommandModel model = generateCommandModel();
|
||||
return builder()
|
||||
.attribute("name", rootCommand)
|
||||
.attribute("model", model)
|
||||
.group("classpath:completion/zsh.stg")
|
||||
.appendGroup("main")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
89
spring-shell-standard/src/main/resources/completion/zsh.stg
Normal file
89
spring-shell-standard/src/main/resources/completion/zsh.stg
Normal file
@@ -0,0 +1,89 @@
|
||||
//
|
||||
// pre content template before commands
|
||||
// needs to escape some > characters
|
||||
//
|
||||
pre(name) ::= <<
|
||||
#compdef _<name> <name>
|
||||
>>
|
||||
|
||||
//
|
||||
// commands section with command and description
|
||||
//
|
||||
cmd_and_desc(command) ::= <<
|
||||
"<command.mainCommand>:<command.description>"
|
||||
>>
|
||||
|
||||
//
|
||||
// case for command to call function
|
||||
//
|
||||
cmd_func(name,command) ::= <<
|
||||
<command.mainCommand>)
|
||||
_<name>_<command.commandParts:{p | <p>}; separator="_">
|
||||
;;
|
||||
>>
|
||||
|
||||
//
|
||||
// recursive sub commands
|
||||
//
|
||||
sub_command(name,command,commands) ::= <<
|
||||
function _<name>_<command.commandParts:{p | <p>}; separator="_"> {
|
||||
local -a commands
|
||||
|
||||
_arguments -C \
|
||||
<command.flags:{f | "<f>" \\}; separator="\n">
|
||||
"1: :->cmnds" \
|
||||
"*::arg:->args"
|
||||
|
||||
case $state in
|
||||
cmnds)
|
||||
commands=(
|
||||
<commands:{c | <cmd_and_desc(c)>}; separator="\n">
|
||||
)
|
||||
_describe "command" commands
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$words[1]" in
|
||||
<commands:{c | <cmd_func(name,c)>}; separator="\n">
|
||||
esac
|
||||
}
|
||||
|
||||
<commands:{c | <sub_command(name,c,c.commands)>}; separator="\n\n">
|
||||
>>
|
||||
|
||||
//
|
||||
// top level commands
|
||||
//
|
||||
top_commands(name,commands) ::= <<
|
||||
function _<name> {
|
||||
local -a commands
|
||||
|
||||
_arguments -C \
|
||||
"1: :->cmnds" \
|
||||
"*::arg:->args"
|
||||
|
||||
case $state in
|
||||
cmnds)
|
||||
commands=(
|
||||
<commands:{c | <cmd_and_desc(c)>}; separator="\n">
|
||||
)
|
||||
_describe "command" commands
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$words[1]" in
|
||||
<commands:{c | <cmd_func(name,c)>}; separator="\n">
|
||||
esac
|
||||
}
|
||||
|
||||
<commands:{c | <sub_command(name,c,c.commands)>}; separator="\n\n">
|
||||
>>
|
||||
|
||||
//
|
||||
// main template to call from render
|
||||
//
|
||||
main(name, model) ::= <<
|
||||
<pre(name)>
|
||||
|
||||
<top_commands(name,model.commands)>
|
||||
>>
|
||||
Reference in New Issue
Block a user