Migrate docs to antora

- This is basically copy from main branch minus all
  terminal ui things.
- Relates #971
This commit is contained in:
Janne Valkealahti
2024-01-12 14:02:10 +00:00
parent 09d4bfed51
commit 814ed4958f
108 changed files with 637 additions and 1037 deletions

View File

@@ -1,5 +0,0 @@
[appendix]
[#appendix-debugging]
== Debugging
Please find more info about debugging from https://github.com/spring-projects/spring-shell/wiki/Debugging[project wiki].

View File

@@ -1,43 +0,0 @@
=== Command Catalog
The `CommandCatalog` interface defines how command registrations exist in
a shell application. It is possible to dynamically register and de-register
commands, which gives flexibility for use cases where possible commands
come and go, depending on a shell's state. Consider the following example:
====
[source, java, indent=0]
----
include::{snippets}/CommandCatalogSnippets.java[tag=snippet1]
----
====
==== Command Resolver
You can implement the `CommandResolver` interface and define a bean to dynamically
resolve mappings from a command's name to its `CommandRegistration` instances. Consider
the following example:
====
[source, java, indent=0]
----
include::{snippets}/CommandCatalogSnippets.java[tag=snippet2]
----
====
IMPORTANT: A current limitation of a `CommandResolver` is that it is used every time commands are resolved.
Thus, we advise not using it if a command resolution call takes a long time, as it would
make the shell feel sluggish.
==== Command Catalog Customizer
You can use the `CommandCatalogCustomizer` interface to customize a `CommandCatalog`.
Its main use is to modify a catalog. Also, within `spring-shell` auto-configuration, this
interface is used to register existing `CommandRegistration` beans into a catalog.
Consider the following example:
====
[source, java, indent=0]
----
include::{snippets}/CommandCatalogSnippets.java[tag=snippet3]
----
====
You can create a `CommandCatalogCustomizer` as a bean, and Spring Shell handles the rest.

View File

@@ -1,20 +0,0 @@
=== Command Context
The `CommandContext` interface gives access to a currently running
context. You can use it to get access to options:
====
[source, java, indent=0]
----
include::{snippets}/CommandContextSnippets.java[tag=snippet1]
----
====
If you need to print something into a shell, you can get a `Terminal`
and use its writer to print something:
====
[source, java, indent=0]
----
include::{snippets}/CommandContextSnippets.java[tag=snippet2]
----
====

View File

@@ -1,3 +0,0 @@
=== Command Execution
When command parsing has done its job and command registration has been resolved, command execution
does the hard work of running the code.

View File

@@ -1,3 +0,0 @@
=== Command Parser
Before a command can be executed, we need to parse the command and whatever options the user may have provided. Parsing
comes between command registration and command execution.

View File

@@ -1,105 +0,0 @@
[#appendix-tech-intro-registration]
=== Command Registration
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Defining a command registration is a first step to introducing the structure of a command and its options
and parameters. This is loosely decoupled from what happens later, such as parsing command-line input and running
actual target code. Essentially, it is the definition of a command API that is shown to a user.
==== Commands
A command in a `spring-shell` structure is defined as an array of commands. This yields a
structure similar to the following example:
====
[source, bash]
----
command1 sub1
command2 sub1 subsub1
command2 sub2 subsub1
command2 sub2 subsub2
----
====
NOTE: We do not currently support mapping commands to an explicit parent if sub-commands are defined.
For example, `command1 sub1` and `command1 sub1 subsub1` cannot both be registered.
==== Interaction Mode
Spring Shell has been designed to work on two modes: interactive (which essentially
is a `REPL` where you have an active shell instance throughout a series of commands) and
non-interactive (where commands are executed one by one from a command line).
Differentation between these modes is mostly around limitations about what can be done
in each mode. For example, it would not be feasible to show what was a previous stacktrace
of a command if the shell is no longer active. Generally, whether the shell is still active
dictates the available information.
Also, being on an active `REPL` session may provide more information about what the user has been
doing within an active session.
==== Options
Options can be defined as long and short, where the prefixing is `--` and `-`, respectively.
The following examples show long and short options:
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationSnippets.java[tag=snippet1]
----
====
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationSnippets.java[tag=snippet2]
----
====
==== Target
The target defines the execution target of a command. It can be a method in a POJO,
a `Consumer`, or a `Function`.
===== Method
Using a `Method` in an existing POJO is one way to define a target.
Consider the following class:
====
[source, java, indent=0]
----
include::{snippets}/CommandTargetSnippets.java[tag=snippet11]
----
====
Given the existing class shown in the preceding listing, you can then register its method:
====
[source, java, indent=0]
----
include::{snippets}/CommandTargetSnippets.java[tag=snippet12]
----
====
===== Function
Using a `Function` as a target gives a lot of flexibility to handle what
happens in a command execution, because you can handle many things manually by using
a `CommandContext` given to a `Function`. The return type from a `Function` is
then what gets printed into the shell as a result. Consider the following example:
====
[source, java, indent=0]
----
include::{snippets}/CommandTargetSnippets.java[tag=snippet2]
----
====
===== Consumer
Using a `Consumer` is basically the same as using a `Function`, with the difference being
that there is no return type. If you need to print something into a shell,
you can get a reference to a `Terminal` from a context and print something
through it. Consider the following example:
====
[source, java, indent=0]
----
include::{snippets}/CommandTargetSnippets.java[tag=snippet3]
----
====

View File

@@ -1,65 +0,0 @@
[#appendix-tech-intro-searchalgorithm]
=== Search Algorithms
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
`SearchMatch` is an interface to match _text_ with a _pattern_. Match
results are in a returned value `SearchMatchResult`. Match result
contains info about match positions and overall score of a match.
https://github.com/junegunn/fzf[fzf].
==== Implementations
*FuzzyMatchV2Search*
Port of _fzf FuzzyMatchV2Search_ algorithm. Does a fast fuzzy search and is good
quickly finding paths.
*ExactMatchNaive*
Port of _fzf ExactMatchNaive_ algorithm. Simple exact match works more accurately
if you know what to search.
==== SearchMatch
Algorithms and default syntax are hidden inside package protected classes
as we don't want to fully open these until we know API's are good to go
for longer support. You need to construct `SearchMatch` via its
build-in builder.
====
[source, java, indent=0]
----
include::{snippets}/SearchAlgorithmsSnippets.java[tag=builder]
----
====
It's possible to configure _case sensitivity_, on what _direction_ search
happens or if text should be _normilized_ before search happens. Normalization
is handy when different languages have sligh variation for same type
of characters.
Search algorithm is selected based on a search syntax shown in
below table.
.Search syntax
|===
|Token |Match type |Description
|`hell`
|fuzzy-match
|Items that match `hello`
|`'stuff`
|exact-match
|Items that include `stuff`
|===
==== Examples
====
[source, java, indent=0]
----
include::{snippets}/SearchAlgorithmsSnippets.java[tag=simple]
----
====

View File

@@ -1,60 +0,0 @@
[#appendix-tech-intro-theming]
=== Theming
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Styling in a theming is provided by a use of a _AttributedString_ from `JLine`.
Unfortunately styling in `JLine` is mostly undocumented but we try to go through
some of its features here.
In `JLine` a style spec is a string having a special format. Spec can be given
multiple times if separated by a comma. A spec will either define a color for
foreground, background or its mode. Special format `<spec>:=<spec>` allows to
define a default within latter spec if former for some reason is invalid.
If spec contains a colon its former part indicates either foreground or background
and possible values are `foreground`, `fg`, `f`, `background`, `bg`, `b`, `foreground-rgb`,
`fg-rgb`, `f-rgb`, `background-rgb`, `bg-rgb` or `b-rgb`. Without rbg a color value
is name from an allowable colors `black`, `red`, `green`, `yellow`, `blue`, `magenta`,
`cyan` or `white`. Colors have their short formats `k`, `r`, `g`, `y`, `b`, `m`, `c` and `w`
respectively. If color is prefixed with either `!` or `bright-`, bright mode is automatically
applied. Prefixing with `~` will resolve from JLine internal bsd color table.
If rgb format is expected and prefixed with either `x` or `#` a normal
hex format is used.
====
[source, text]
----
fg-red
fg-r
fg-rgb:red
fg-rgb:xff3333
fg-rgb:#ff3333
----
====
If spec contains special names `default`, `bold`, `faint`, `italic`, `underline`, `blink`,
`inverse`, `inverse-neg`, `inverseneg`, `conceal`, `crossed-out`, `crossedout` or `hidden`
a style is changed accordingly with an existing color.
====
[source, text]
----
bold
bold,fg:red
----
====
If spec is a number or numbers separated with semicolon, format is a plain part of an ansi
ascii codes.
====
[source, text]
----
31
31;1
----
====
NOTE: JLine special mapping format which would resolve spec starting with dot can't be
used as we don't yet map those into Spring Shell styling names.

View File

@@ -1,19 +0,0 @@
[appendix]
[#appendix-tech-intro]
== Techical Introduction
This appendix contains information for developers and others who would like to know more about how Spring Shell
works internally and what its design decisions are.
include::appendices-techical-intro-registration.adoc[]
include::appendices-techical-intro-parser.adoc[]
include::appendices-techical-intro-execution.adoc[]
include::appendices-techical-intro-commandcontext.adoc[]
include::appendices-techical-intro-commandcatalog.adoc[]
include::appendices-techical-intro-theming.adoc[]
include::appendices-techical-intro-searchalgorithm.adoc[]

View File

@@ -1,3 +0,0 @@
include::appendices-techical-intro.adoc[]
include::appendices-debugging.adoc[]

View File

@@ -1,35 +0,0 @@
{"version": 2, "width": 85, "height": 8, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[1.590847, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
[2.774407, "o", "\r\n"]
[4.560309, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[5.138757, "o", "\u001b[31mc\u001b[0m"]
[5.267214, "o", "\u001b[31mo\u001b[0m"]
[5.467126, "o", "\u001b[31mm\u001b[0m"]
[5.674049, "o", "\u001b[31mp\u001b[0m"]
[5.752663, "o", "\u001b[31mo\u001b[0m"]
[5.917741, "o", "\u001b[31mn\u001b[0m"]
[6.040844, "o", "\u001b[31me\u001b[0m"]
[6.123068, "o", "\u001b[31mn\u001b[0m"]
[6.277935, "o", "\u001b[31mt\u001b[0m"]
[7.050283, "o", "\u001b[31m \u001b[0m"]
[7.413917, "o", "\u001b[31mc\u001b[0m"]
[7.568894, "o", "\u001b[31mo\u001b[0m"]
[7.664856, "o", "\u001b[31mn\u001b[0m"]
[7.853492, "o", "\u001b[13D\u001b[1mcomponent confirmation\u001b[0m \u001b[K"]
[8.698242, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
[8.745928, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[8.803411, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[2m(Y/n)\u001b[0m\r"]
[13.769588, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
[13.779021, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34mtrue\u001b[0m\r\n"]
[13.779319, "o", "Got value true\r\n"]
[13.781245, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[15.955396, "o", "\u001b[1mcomponent confirmation\u001b[0m"]
[16.743244, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l"]
[16.743555, "o", "\u001b[?2004l"]
[16.748638, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[16.754861, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[2m(Y/n)\u001b[0m\r"]
[19.347907, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
[19.355493, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34mfalse\u001b[0m\r\n"]
[19.356618, "o", "Got value false\r\n"]
[19.3586, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[21.3586, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]

View File

@@ -1,40 +0,0 @@
{"version": 2, "width": 85, "height": 11, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[1.590847, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
[5.030349, "o", "\r\n"]
[7.108752, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[7.604731, "o", "\u001b[31mf\u001b[0m"]
[7.754184, "o", "\u001b[31ml\u001b[0m"]
[7.98951, "o", "\u001b[31mo\u001b[0m"]
[8.405578, "o", "\u001b[31mw\u001b[0m"]
[8.763007, "o", "\u001b[31m \u001b[0m"]
[8.963478, "o", "\u001b[31mc\u001b[0m"]
[9.036017, "o", "\u001b[31mo\u001b[0m"]
[9.353476, "o", "\u001b[31mn\u001b[0m"]
[9.517639, "o", "\u001b[8D\u001b[1mflow conditional\u001b[0m \u001b[K"]
[10.036978, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
[10.115908, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[10.176519, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mSingle1\u001b[0m [Use arrows to move], type to filter\u001b[97C \u001b[96;1m>\u001b[0m\u001b[96;1m Field1\u001b[0m\u001b[135C Field2\u001b[2A\r"]
[11.824714, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\u001b[2A"]
[11.835781, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mSingle1\u001b[0m \u001b[34mfield1\u001b[0m\r\n"]
[11.838786, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[11.844824, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mField1\u001b[0m \u001b[34m[Default defaultField1Value]\u001b[0m\r"]
[12.734384, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
[12.740754, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mField1\u001b[0m \u001b[34mdefaultField1Value\u001b[0m\r\n"]
[12.74362, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[13.547149, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[14.091367, "o", "\u001b[1mflow conditional\u001b[0m"]
[14.419251, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
[14.424986, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[14.432116, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mSingle1\u001b[0m [Use arrows to move], type to filter\u001b[97C \u001b[96;1m>\u001b[0m\u001b[96;1m Field1\u001b[0m\u001b[135C Field2\u001b[2A\r"]
[14.995304, "o", "\r\r\n Field1\r\r\n\u001b[96;1m> Field2\u001b[0m\u001b[2A\r"]
[15.409104, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\u001b[2A"]
[15.416729, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mSingle1\u001b[0m \u001b[34mfield2\u001b[0m\r\n"]
[15.41833, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[15.424318, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mField2\u001b[0m \u001b[34m[Default defaultField2Value]\u001b[0m\r"]
[16.002129, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
[16.007474, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mField2\u001b[0m \u001b[34mdefaultField2Value\u001b[0m\r\n"]
[16.010197, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[19.865275, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l"]
[19.865497, "o", "\u001b[?2004l"]
[19.868634, "o", "\u001b[31morg.jline.reader.EndOfFileException\u001b[0m\r\n\u001b[31mDetails of the error have been omitted. You can use the \u001b[1mstacktrace\u001b[22m command to print the full stacktrace.\u001b[0m\r\n"]
[19.870354, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]

View File

@@ -1,42 +0,0 @@
{"version": 2, "width": 85, "height": 15, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[1.590847, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
[5.968022, "o", "\r\n"]
[8.099727, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[11.261894, "o", "\u001b[1mflow showcase\u001b[0m"]
[12.6451, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
[12.721206, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[12.780227, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mField1\u001b[0m \u001b[34m[Default defaultField1Value]\u001b[0m\r"]
[14.703281, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
[14.711232, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mField1\u001b[0m \u001b[34mdefaultField1Value\u001b[0m\r\n"]
[14.713588, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[14.720745, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mField2\u001b[0m \r"]
[16.084492, "o", "\u001b[9Ch\r"]
[16.176942, "o", "\u001b[10Ci\r"]
[16.620009, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
[16.625711, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mField2\u001b[0m \u001b[34mhi\u001b[0m\r\n"]
[16.628041, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[16.633919, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mConfirmation1\u001b[0m \u001b[2m(Y/n)\u001b[0m\r"]
[20.090558, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
[20.098654, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mConfirmation1\u001b[0m \u001b[34mtrue\u001b[0m\r\n"]
[20.101548, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[20.105099, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mPath1\u001b[0m \r"]
[21.53213, "o", "\u001b[8Cp\u001b[134C \u001b[32m>\u001b[0m \u001b[32mPath ok\u001b[0m\u001b[A\r"]
[21.626297, "o", "\u001b[9Ca\r"]
[21.896442, "o", "\u001b[10Ct\r"]
[21.941151, "o", "\u001b[11Ch\r"]
[23.041717, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\u001b[A"]
[23.044506, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mPath1\u001b[0m \u001b[34mpath\u001b[0m\r\n"]
[23.049225, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[23.05459, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mSingle1\u001b[0m [Use arrows to move], type to filter\u001b[97C \u001b[96;1m>\u001b[0m\u001b[96;1m key1\u001b[0m\u001b[137C key2\u001b[2A\r"]
[24.520415, "o", "\r\r\n key1\r\r\n\u001b[96;1m> key2\u001b[0m\u001b[2A\r"]
[25.431855, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\u001b[2A"]
[25.438619, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mSingle1\u001b[0m \u001b[34mvalue2\u001b[0m\r\n"]
[25.441975, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[25.446547, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mMulti1\u001b[0m [Use arrows to move], type to filter\u001b[98C \u001b[96;1m>\u001b[0m\u001b[96;1m \u001b[39m[ ]\u001b[0m key1\u001b[133C \u001b[1m[ ]\u001b[0m key2\u001b[133C \u001b[1m[ ]\u001b[0m key3\u001b[3A\r"]
[26.698294, "o", "\r\r\n\u001b[2C\u001b[32m[x]\u001b[0m\u001b[A\r"]
[28.022787, "o", "\r\r\n \r\r\n\u001b[96;1m> \u001b[0m\u001b[2A\r"]
[28.110824, "o", "\r\r\n\r\n\u001b[2C\u001b[32m[x]\u001b[0m\u001b[2A\r"]
[29.250629, "o", "\r\r\n\r\n\u001b[2C\u001b[1m[ ]\u001b[0m\u001b[2A\r"]
[30.360368, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\u001b[3A"]
[30.368273, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mMulti1\u001b[0m \u001b[34mvalue1\u001b[0m\r\n"]
[30.370638, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]

View File

@@ -1,42 +0,0 @@
{"version": 2, "width": 85, "height": 9, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[1.746447, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
[2.881033, "o", "\r\n"]
[4.603396, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[5.105471, "o", "\u001b[31mc\u001b[0m"]
[5.202878, "o", "\u001b[31mo\u001b[0m"]
[5.428705, "o", "\u001b[31mm\u001b[0m"]
[5.630033, "o", "\u001b[31mp\u001b[0m"]
[5.692044, "o", "\u001b[31mo\u001b[0m"]
[5.86652, "o", "\u001b[31mn\u001b[0m"]
[6.018765, "o", "\u001b[31me\u001b[0m"]
[6.089776, "o", "\u001b[31mn\u001b[0m"]
[6.251781, "o", "\u001b[31mt\u001b[0m"]
[7.126694, "o", "\u001b[31m \u001b[0m"]
[7.319912, "o", "\u001b[31mm\u001b[0m"]
[7.550772, "o", "\u001b[31mu\u001b[0m"]
[7.80087, "o", "\u001b[12D\u001b[1mcomponent multi\u001b[0m \u001b[K"]
[8.291497, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
[8.338879, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[8.398742, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mtestSimple\u001b[0m [Use arrows to move], type to filter\u001b[54C \u001b[96;1m>\u001b[0m\u001b[96;1m \u001b[39m[ ]\u001b[0m key1\u001b[93C \u001b[2m[ ]\u001b[0m \u001b[2mkey2\u001b[0m\u001b[93C \u001b[1m[ ]\u001b[0m key3\u001b[3A\r"]
[12.265649, "o", "\u001b[35Cfiltering 'k'\u001b[K\r"]
[12.507991, "o", "\u001b[47C\u001b[1@e\r"]
[12.945812, "o", "\u001b[48C\u001b[1@y\r"]
[13.35949, "o", "\u001b[49C\u001b[1@1\r\r\n\u001b[103C \u001b[K\r\r\r\n\u001b[K\r\r\n\u001b[K\u001b[3A"]
[14.187299, "o", "\u001b[49C\u001b[P\r\r\n\u001b[103C \u001b[2m[ ]\u001b[0m \u001b[2mkey2\u001b[0m\u001b[93C \u001b[1m[ ]\u001b[0m key3\u001b[3A\r"]
[14.356992, "o", "\u001b[48C\u001b[P\r"]
[14.495579, "o", "\u001b[47C\u001b[P\r"]
[14.642467, "o", "\u001b[35Ctype to filter\u001b[K\r"]
[15.763904, "o", "\r\r\n\u001b[2C\u001b[32m[x]\u001b[0m\u001b[A\r"]
[16.496138, "o", "\r\r\n \r\r\n\u001b[96;1m> \u001b[0m\u001b[2A\r"]
[16.678991, "o", "\r\r\n\r\n \r\r\n\u001b[96;1m> \u001b[0m\u001b[3A\r"]
[17.979077, "o", "\r\r\n\r\n\r\n\u001b[2C\u001b[32m[x]\u001b[0m\u001b[3A\r"]
[18.809587, "o", "\r\r\n\r\n\r\n\u001b[2C\u001b[1m[ ]\u001b[0m\u001b[3A\r"]
[20.452942, "o", "\r\r\n\r\n\u001b[96;1m> \u001b[0m\r\r\n \u001b[3A\r"]
[21.705426, "o", "\r\r\n\r\n \r\r\n\u001b[96;1m> \u001b[0m\u001b[3A\r"]
[22.207898, "o", "\r\r\n\r\n\r\n\u001b[2C\u001b[32m[x]\u001b[0m\u001b[3A\r"]
[23.30633, "o", "\r\r\n\r\n\r\n\u001b[2C\u001b[1m[ ]\u001b[0m\u001b[3A\r"]
[24.642701, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\u001b[3A"]
[24.64692, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mtestSimple\u001b[0m \u001b[34mvalue1\u001b[0m\r\n"]
[24.647546, "o", "Got value value1\r\n"]
[24.649365, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[26.649365, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]

View File

@@ -1,35 +0,0 @@
{"version": 2, "width": 85, "height": 6, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[1.276071, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
[1.975169, "o", "\r\n"]
[3.730642, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[4.157171, "o", "\u001b[31mc\u001b[0m"]
[4.259876, "o", "\u001b[31mo\u001b[0m"]
[4.480644, "o", "\u001b[31mm\u001b[0m"]
[4.675743, "o", "\u001b[31mp\u001b[0m"]
[4.746389, "o", "\u001b[31mo\u001b[0m"]
[4.899817, "o", "\u001b[31mn\u001b[0m"]
[5.064803, "o", "\u001b[31me\u001b[0m"]
[5.151957, "o", "\u001b[31mn\u001b[0m"]
[5.295573, "o", "\u001b[31mt\u001b[0m"]
[5.626209, "o", "\u001b[31m \u001b[0m"]
[5.949778, "o", "\u001b[31mp\u001b[0m"]
[6.052068, "o", "\u001b[31ma\u001b[0m"]
[6.270888, "o", "\u001b[31mt\u001b[0m"]
[6.342701, "o", "\u001b[13D\u001b[1mcomponent path\u001b[0m\u001b[K"]
[6.866598, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
[6.918336, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[6.979593, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \r"]
[8.74751, "o", "\u001b[14C/\u001b[88C \u001b[31m>\u001b[0m\u001b[31m>>\u001b[0m \u001b[31mDirectory exists\u001b[0m\u001b[A\r"]
[10.825765, "o", "\u001b[15Ct\r\r\n\u001b[32m>\u001b[0m \u001b[32mPath ok\u001b[0m\u001b[K\u001b[A\r"]
[10.96141, "o", "\u001b[16Cm\r"]
[11.050615, "o", "\u001b[17Cp\r\r\n\u001b[31m>>>\u001b[0m \u001b[31mDirectory exists\u001b[0m\u001b[K\u001b[A\r"]
[11.812886, "o", "\u001b[18C/\r"]
[11.930461, "o", "\u001b[19Cd\r\r\n\u001b[32m>\u001b[0m \u001b[32mPath ok\u001b[0m\u001b[K\u001b[A\r"]
[12.128981, "o", "\u001b[20Ce\r"]
[12.176119, "o", "\u001b[21Cm\r"]
[12.28765, "o", "\u001b[22Co\r"]
[13.464196, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\u001b[A"]
[13.471408, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34m/tmp/demo\u001b[0m\r\n"]
[13.471875, "o", "Got value /tmp/demo\r\n"]
[13.475016, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[15.475016, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]

View File

@@ -1,23 +0,0 @@
{"version": 2, "width": 85, "height": 10, "timestamp": 1666162443, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[1.881363, "o", "java -jar spring-shell-samples/build/libs/spring-shell-samples.jar"]
[2.630692, "o", "\r\n"]
[6.50287, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[8.347165, "o", "\u001b[1mcomponent path search\u001b[0m"]
[9.422581, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
[9.524556, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[9.612905, "o", "\u001b[32;1m?\u001b[0m \u001b[1mEnter value\u001b[0m \u001b[70C \u001b[32m\u001b[0m \u001b[32mType '<path> <pattern>' to search\u001b[0m\u001b[A\r"]
[10.889157, "o", "\u001b[14C.\r\r\n\u001b[35C\u001b[32m, 20/27\u001b[0m\u001b[42C \u001b[96;1m\u001b[0m\u001b[96;1m buildSrc/src\u001b[0m\u001b[70C \u001b[96;1mbuildSrc/src/main\u001b[0m\u001b[65C \u001b[96;1mbuildSrc/build.gradle\u001b[0m\u001b[61C \u001b[96;1mbuildSrc/src/main/java\u001b[0m\u001b[60C \u001b[96;1mbuildSrc/src/main/java/org\u001b[0m\u001b[6A\r"]
[12.272435, "o", "\u001b[15C \r"]
[12.486882, "o", "\u001b[16Cg\r\r\n\r\n\u001b[14C\u001b[96;1m/main/java/org/springframework/shell/\u001b[33;22mg\u001b[96;1mradle\u001b[0m\r\r\n\u001b[19C\u001b[96;1m/java/org/springframework/shell/\u001b[33;22mg\u001b[96;1mradle/BomPlugin.java\u001b[0m\r\r\n\u001b[11C\u001b[96;1msrc/main/java/org/springframework/shell/\u001b[33;22mg\u001b[96;1mradle/DocsPlugin.java\u001b[0m\u001b[K\r\r\n\u001b[24C\u001b[96;1m/org/springframework/shell/\u001b[33;22mg\u001b[96;1mradle/RootPlugin.java\u001b[0m\r\r\n\u001b[28C\u001b[96;1m/springframework/shell/\u001b[33;22mg\u001b[96;1mradle/ModulePlugin.java\u001b[0m\u001b[6A\r"]
[12.707385, "o", "\u001b[17Cr\r\r\n\r\n\u001b[52C\u001b[33mr\u001b[0m\r\r\n\u001b[52C\u001b[33mr\u001b[0m\r\r\n\u001b[52C\u001b[33mr\u001b[96;1madle/Root\u001b[0m\r\r\n\u001b[52C\u001b[33mr\u001b[96;1madle/Docs\u001b[0m\r\r\n\u001b[52C\u001b[33mr\u001b[96;1madle/Samp\u001b[0m\u001b[6A\r"]
[12.758464, "o", "\u001b[18Ca\r\r\n\r\n\u001b[53C\u001b[33ma\u001b[0m\r\r\n\u001b[53C\u001b[33ma\u001b[0m\r\r\n\u001b[53C\u001b[33ma\u001b[0m\r\r\n\u001b[53C\u001b[33ma\u001b[0m\r\r\n\u001b[53C\u001b[33ma\u001b[96;1mdle/Modu\u001b[0m\u001b[6A\r"]
[13.035614, "o", "\u001b[19Cd\r\r\n\u001b[37C\u001b[32m19\u001b[0m\r\r\n\u001b[54C\u001b[33md\u001b[0m\r\r\n\u001b[54C\u001b[33md\u001b[0m\r\r\n\u001b[54C\u001b[33md\u001b[96;1mle/Docs\u001b[0m\r\r\n\u001b[54C\u001b[33md\u001b[96;1mle/Module\u001b[0m\u001b[K\u001b[96;1mPlugin.java\u001b[0m\r\r\n\u001b[54C\u001b[33md\u001b[96;1mle/Samp\u001b[0m\u001b[6A\r"]
[13.132591, "o", "\u001b[20Cl\r\r\n\r\n\u001b[55C\u001b[33ml\u001b[0m\r\r\n\u001b[55C\u001b[33ml\u001b[0m\r\r\n\u001b[55C\u001b[33ml\u001b[0m\r\r\n\u001b[55C\u001b[33ml\u001b[96;1me/Samp\u001b[0m\r\r\n\u001b[55C\u001b[33ml\u001b[96;1me/Modu\u001b[0m\u001b[6A\r"]
[13.226949, "o", "\u001b[21Ce\r\r\n\r\n\u001b[56C\u001b[33me\u001b[0m\r\r\n\u001b[56C\u001b[33me\u001b[0m\r\r\n\u001b[56C\u001b[33me\u001b[0m\r\r\n\u001b[56C\u001b[33me\u001b[0m\r\r\n\u001b[56C\u001b[33me\u001b[0m\u001b[6A\r"]
[14.514777, "o", "\r\r\n\r\n \r\r\n\u001b[96;1m \u001b[0m\u001b[3A\r"]
[15.008775, "o", "\r\r\n\r\n\u001b[96;1m \u001b[0m\r\r\n \u001b[3A\r"]
[15.547301, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\u001b[6A"]
[15.552903, "o", "\u001b[32;1m?\u001b[0m \u001b[1mEnter value\u001b[0m \u001b[34mbuildSrc/src/main/java/org/springframework/shell/gradle\u001b[0m\r\n"]
[15.55352, "o", "Got value buildSrc/src/main/java/org/springframework/shell/gradle\r\n"]
[16.475016, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[18.475016, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]

View File

@@ -1,38 +0,0 @@
{"version": 2, "width": 85, "height": 8, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[2.193321, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
[3.11524, "o", "\r\n"]
[4.904714, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[5.372714, "o", "\u001b[31mc\u001b[0m"]
[5.484943, "o", "\u001b[31mo\u001b[0m"]
[5.70511, "o", "\u001b[31mm\u001b[0m"]
[5.913092, "o", "\u001b[31mp\u001b[0m"]
[5.992808, "o", "\u001b[31mo\u001b[0m"]
[6.159092, "o", "\u001b[31mn\u001b[0m"]
[6.281094, "o", "\u001b[31me\u001b[0m"]
[6.353925, "o", "\u001b[31mn\u001b[0m"]
[6.49373, "o", "\u001b[31mt\u001b[0m"]
[6.677988, "o", "\u001b[31m \u001b[0m"]
[6.825018, "o", "\u001b[31ms\u001b[0m"]
[6.95602, "o", "\u001b[31mi\u001b[0m"]
[7.121719, "o", "\u001b[31mn\u001b[0m"]
[7.309007, "o", "\u001b[13D\u001b[1mcomponent single\u001b[0m \u001b[K"]
[7.972392, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
[8.019885, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[8.080156, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mtestSimple\u001b[0m [Use arrows to move], type to filter\u001b[54C \u001b[96;1m>\u001b[0m\u001b[96;1m key1\u001b[0m\u001b[97C key2\u001b[2A\r"]
[9.181662, "o", "\r\r\n key1\r\r\n\u001b[96;1m> key2\u001b[0m\u001b[2A\r"]
[9.701431, "o", "\r\r\n\u001b[96;1m> key1\u001b[0m\r\r\n key2\u001b[2A\r"]
[10.380557, "o", "\r\r\n key1\r\r\n\u001b[96;1m> key2\u001b[0m\u001b[2A\r"]
[12.157001, "o", "\u001b[35Cfiltering 'k'\u001b[K\r"]
[14.605973, "o", "\u001b[47C\u001b[1@e\r\r\n\u001b[96;1m> key1\u001b[0m\r\r\n key2\u001b[2A\r"]
[14.92268, "o", "\u001b[48C\u001b[1@y\r"]
[15.320008, "o", "\u001b[49C\u001b[1@1\r\r\n\u001b[103C \u001b[K\r\r\r\n\u001b[K\u001b[2A"]
[16.215448, "o", "\u001b[49C\u001b[P\r\r\n\u001b[103C key2\u001b[2A\r"]
[16.559377, "o", "\u001b[48C\u001b[P\r"]
[16.784486, "o", "\u001b[47C\u001b[P\r"]
[17.004921, "o", "\u001b[35Ctype to filter\u001b[K\r"]
[17.790542, "o", "\r\r\n key1\r\r\n\u001b[96;1m> key2\u001b[0m\u001b[2A\r"]
[18.340142, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\u001b[2A"]
[18.347511, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mtestSimple\u001b[0m \u001b[34mvalue2\u001b[0m\r\n"]
[18.34852, "o", "Got value value2\r\n"]
[18.35067, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[20.35067, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]

View File

@@ -1,30 +0,0 @@
{"version": 2, "width": 85, "height": 6, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
[1.262977, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
[2.045105, "o", "\r\n"]
[3.846992, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[4.717927, "o", "\u001b[31mc\u001b[0m"]
[4.824399, "o", "\u001b[31mo\u001b[0m"]
[5.014061, "o", "\u001b[31mm\u001b[0m"]
[5.703137, "o", "\u001b[31mp\u001b[0m"]
[5.802386, "o", "\u001b[31mo\u001b[0m"]
[6.434988, "o", "\u001b[31mn\u001b[0m"]
[6.653321, "o", "\u001b[31me\u001b[0m"]
[6.756821, "o", "\u001b[31mn\u001b[0m"]
[7.293975, "o", "\u001b[31mt\u001b[0m"]
[7.409984, "o", "\u001b[31m \u001b[0m"]
[7.604973, "o", "\u001b[31ms\u001b[0m"]
[7.775599, "o", "\u001b[31mt\u001b[0m"]
[7.928792, "o", "\u001b[31mr\u001b[0m"]
[7.979401, "o", "\u001b[31mi\u001b[0m"]
[8.051232, "o", "\u001b[31mn\u001b[0m"]
[8.15451, "o", "\u001b[15D\u001b[1mcomponent string\u001b[0m\u001b[K"]
[8.892861, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
[8.951185, "o", "\u001b[?1h\u001b=\u001b[?25l"]
[9.013964, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34m[Default myvalue]\u001b[0m\r"]
[10.304694, "o", "\u001b[14Ch\u001b[K\r"]
[10.533832, "o", "\u001b[15Ci\r"]
[11.088255, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
[11.097044, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34mhi\u001b[0m\r\n"]
[11.097492, "o", "Got value hi\r\n"]
[11.100226, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
[13.100226, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]

View File

@@ -1,27 +0,0 @@
$ $JAVA_HOME/bin/java -jar demo-0.0.1-SNAPSHOT.jar
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v{spring-boot-version})
2022-09-13T18:42:12.818+01:00 INFO 12644 --- [ main] com.example.demo.DemoApplication
: Starting DemoApplication using Java 17.0.4 on ...
2022-09-13T18:42:12.821+01:00 INFO 12644 --- [ main] com.example.demo.DemoApplication
: No active profile set, falling back to 1 default profile: "default"
2022-09-13T18:42:13.606+01:00 INFO 12644 --- [ main] com.example.demo.DemoApplication
: Started DemoApplication in 1.145 seconds (process running for 1.578)
shell:>help
AVAILABLE COMMANDS
Built-In Commands
help: Display help about available commands
stacktrace: Display the full stacktrace of the last error.
clear: Clear the shell screen.
quit, exit: Exit the shell.
history: Display or save the history of previously run commands
version: Show version info
script: Read and execute commands from a file.

View File

@@ -1,26 +0,0 @@
$JAVA_HOME/bin/java -jar demo-0.0.1-SNAPSHOT.jar help
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v{spring-boot-version})
2022-09-13T18:42:12.818+01:00 INFO 12644 --- [ main] com.example.demo.DemoApplication
: Starting DemoApplication using Java 17.0.4 on ...
2022-09-13T18:42:12.821+01:00 INFO 12644 --- [ main] com.example.demo.DemoApplication
: No active profile set, falling back to 1 default profile: "default"
2022-09-13T18:42:13.606+01:00 INFO 12644 --- [ main] com.example.demo.DemoApplication
: Started DemoApplication in 1.145 seconds (process running for 1.578)
AVAILABLE COMMANDS
Built-In Commands
help: Display help about available commands
stacktrace: Display the full stacktrace of the last error.
clear: Clear the shell screen.
quit, exit: Exit the shell.
history: Display or save the history of previously run commands
version: Show version info
script: Read and execute commands from a file.

View File

@@ -1,155 +0,0 @@
== Getting Started
To see what Spring Shell has to offer, we can write a trivial _hello world_
shell application that has a simple argument.
IMPORTANT: _Spring Shell_ is based on _Spring Boot_ {spring-boot-version} and
_Spring Framework_ {spring-version} and thus requires _JDK 17_.
=== Creating a Project
For the purpose of this tutorial, we create a simple Spring Boot application by
using https://start.spring.io where you can choose _Spring Shell_ dependency.
This minimal application depends only on `spring-boot-starter` and
`spring-shell-starter`.
NOTE: _Spring Shell_ version on `start.spring.io` is usually latest release.
With _maven_ you're expected to have something like:
====
[source, xml, subs=attributes+]
----
<properties>
<spring-shell.version>{project-version}</spring-shell.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-starter</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.shell</groupId>
<artifactId>spring-shell-dependencies</artifactId>
<version>${spring-shell.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
====
With _gradle_ you're expected to have something like:
====
[source, groovy, subs=attributes+]
----
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.shell:spring-shell-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.shell:spring-shell-dependencies:{project-version}"
}
}
----
====
CAUTION: Given that Spring Shell starts the REPL (Read-Eval-Print-Loop) because this
dependency is present, you need to either skip tests when you build (`-DskipTests`)
throughout this tutorial or remove the sample integration test that was generated
by https://start.spring.io. If you do not remove it, the integration test creates
the Spring `ApplicationContext` and, depending on your build tool, stays stuck in
the eval loop or crashes with a NPE.
Once compiled it can be run either in interactive mode:
====
[source, text, subs=attributes+]
----
include::code/getting-started-run-interactive.out[]
----
====
Or in non-interactive mode:
====
[source, text, subs=attributes+]
----
include::code/getting-started-run-noninteractive.out[]
----
====
TIP: Check out <<using-shell-customization-logging>> making logging to work
better with shell apps.
[[using-spring-shell-your-first-command]]
=== Your First Command
Now we can add our first command. To do so, create a new class (named whatever you want) and
annotate it with `@ShellComponent` which is a variation of `@Component` that is used to restrict
the set of classes that are scanned for candidate commands.
Then we can create a `helloWorld` method that takes `String` as an argument and
returns it with "Hello world". Add `@ShellMethod` and optionally change command name
using `key` parameter. You can use `@ShellOption` to define argument default value
if it's not given when running a command.
====
[source, java]
----
package com.example.demo;
import org.springframework.shell.standard.ShellComponent;
import org.springframework.shell.standard.ShellMethod;
import org.springframework.shell.standard.ShellOption;
@ShellComponent
public class MyCommands {
@ShellMethod(key = "hello-world")
public String helloWorld(
@ShellOption(defaultValue = "spring") String arg
) {
return "Hello world " + arg;
}
}
----
====
New _hello-world_ command becomes visible to _help_:
====
[source, text]
----
My Commands
hello-world:
----
====
And you can run it:
====
[source, text]
----
shell:>hello-world
Hello world spring
shell:>hello-world --arg boot
Hello world boot
----
====
The rest of this document delves deeper into the whole Spring Shell programming model.

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -1,24 +0,0 @@
= Spring Shell Reference Documentation
Eric Bottard; Janne Valkealahti; Jay Bryant; Corneil du Plessis
:doctype: book
:hide-uri-scheme:
:icons: font
:experimental: // For kbd: macro
:spring-shell-starter: spring-shell-starter
*{project-version}*
(C) 2017 - 2023 VMware, Inc.
_Copies of this document may be made for your own use and for distribution to
others, provided that you do not charge any fee for such copies and further
provided that each copy contains this Copyright Notice, whether distributed in
print or electronically._
include::introduction.adoc[]
include::getting-started.adoc[]
include::using-shell.adoc[]
include::appendices.adoc[]

View File

@@ -1,14 +0,0 @@
== What is Spring Shell?
Not all applications need a fancy web user interface.
Sometimes, interacting with an application through an interactive terminal is
the most appropriate way to get things done.
Spring Shell lets you create such a runnable application, where the
user enters textual commands that are run until the program terminates.
The Spring Shell project provides the infrastructure to create such a REPL (Read, Eval,
Print Loop) application, letting you concentrate on implementing commands by using
the familiar Spring programming model.
Spring Shell includes advanced features (such as parsing, tab completion, colorization of
output, fancy ASCII-art table display, input conversion, and validation), freeing you
to focus on core command logic.

View File

@@ -1,18 +0,0 @@
[[using-shell-basics-reading]]
=== Reading Docs
Throughout this documentation, we make references to configuring something by using
annotations or programmatic examples.
NOTE: There are two annotation models, <<commands-registration-annotation, annotations>>
referred to new annotation model, <<commands-registration-legacyannotation, legacy annotations>>
referred to old legacy annotation model.
Old legacy annotation model mostly relates to use of `@ShellMethod` and `@ShellOption` and
new annotation model relates to use of `@Command`.
The programmatic model is how things are actually registered, even if you use annotations.
NOTE: Currently whole documentation structure is in transit to provide better
structure how things can be used using different ways to provide configurations.
So pardon a for little confusion now and there during a transit.

View File

@@ -1,16 +0,0 @@
[[using-shell-basics]]
== Basics
This section covers the basics of Spring Shell. Before going on to define actual commands and options,
we need to go through some of the fundamental concepts of Spring Shell.
Essentially, a few things needs to happen before you have a working Spring Shell application:
- Create a Spring Boot application.
- Define commands and options.
- Package the application.
- Run the application, either interactively or non-interactively.
You can get a full working Spring Shell application without defining any user-level commands
as some basic built-in commands (such as `help` and `history`) are provided.
include::using-shell-basics-reading.adoc[]

View File

@@ -1,75 +0,0 @@
[[using-shell-building]]
== Building
This section covers how to build a Spring Shell application.
[[native]]
=== Native Support
Support for compiling _Spring Shell_ application into a _GraalVM_ binary
mostly comes from _Spring Framework_ and _Spring Boot_ where feature is
called _AOT_. Ahead of Time means that application context is prepared
during the compilation time to being ready for _GraalVM_ generation.
Building atop of _AOT_ features from a framework _Spring Shell_ has its
own _GraalVM_ configuration providing hints what should exist in
a binary. Usually trouble comes from a 3rd party libraries which doesn't
yet contain _GraalVM_ related configurations or those configurations
are incomplete.
IMPORTANT: It is requred to use _GraalVM Reachability Metadata Repository_ which
provides some missing hints for 3rd party libraries. Also you need to have
_GraalVM_ installed and `JAVA_HOME` pointing to that.
For _gradle_ add graalvm's native plugin and configure metadata repository.
====
[source, groovy, subs=attributes+]
----
plugins {
id 'org.graalvm.buildtools.native' version '0.9.16'
}
graalvmNative {
metadataRepository {
enabled = true
}
}
----
====
When gradle build is run with `./gradlew nativeCompile` you should get binary
under `build/native/nativeCompile` directory.
For `maven` use `spring-boot-starter-parent` as parent and you'll get `native`
profile which can be used to do a compilation. You need to configure metadata repository
====
[source, xml, subs=attributes+]
----
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<configuration>
<metadataRepository>
<enabled>true</enabled>
</metadataRepository>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
----
====
NOTE: If you rely on `spring-boot-starter-parent` it manages `native-maven-plugin`
version which is kept up to date.
When maven build is run with `./mvnw package -Pnative` you should get binary
under `target` directory.
If everything went well this binary can be run as is instead of executing
boot application jar via jvm.

View File

@@ -1,147 +0,0 @@
[[dynamic-command-availability]]
=== Dynamic Command Availability
Registered commands do not always make sense, due to the internal state of the application.
For example, there may be a `download` command, but it only works once the user has used `connect` on a remote
server. Now, if the user tries to use the `download` command, the shell should explain that
the command exists but that it is not available at the time.
Spring Shell lets you do that, even letting you provide a short explanation of the reason for
the command not being available.
There are three possible ways for a command to indicate availability.
They all use a no-arg method that returns an instance of `Availability`.
Consider the following example:
====
[source, java]
----
@ShellComponent
public class MyCommands {
private boolean connected;
@ShellMethod("Connect to the server.")
public void connect(String user, String password) {
[...]
connected = true;
}
@ShellMethod("Download the nuclear codes.")
public void download() {
[...]
}
public Availability downloadAvailability() {
return connected
? Availability.available()
: Availability.unavailable("you are not connected");
}
}
----
====
The `connect` method is used to connect to the server (details omitted), altering the state
of the command through the `connected` boolean when done.
The `download` command as marked as unavailable until the user has connected, thanks to the presence
of a method named exactly as the `download` command method with the `Availability` suffix in its name.
The method returns an instance of `Availability`, constructed with one of the two factory methods.
If the command is not available, an explanation has to be provided.
Now, if the user tries to invoke the command while not being connected, here is what happens:
====
[source]
----
shell:>download
Command 'download' exists but is not currently available because you are not connected.
Details of the error have been omitted. You can use the stacktrace command to print the full stacktrace.
----
====
Information about currently unavailable commands is also used in the integrated help. See <<built-in-commands-help>>.
[TIP]
====
The reason provided when the command is not available should read nicely if appended after "`Because`".
You should not start the sentence with a capital or add a final period
====
If naming the availability method after the name of the command method does not suit you, you
can provide an explicit name by using the `@ShellMethodAvailability` annotation:
====
[source, java]
----
@ShellMethod("Download the nuclear codes.")
@ShellMethodAvailability("availabilityCheck") // <1>
public void download() {
[...]
}
public Availability availabilityCheck() { // <1>
return connected
? Availability.available()
: Availability.unavailable("you are not connected");
}
----
<1> the names have to match
====
Finally, it is often the case that several commands in the same class share the same internal state and, thus,
should all be available or unavailable as a group. Instead of having to stick the `@ShellMethodAvailability`
on all command methods, Spring Shell lets you flip things around and put the `@ShellMethodAvailabilty`
annotation on the availability method, specifying the names of the commands that it controls:
====
[source, java]
----
@ShellMethod("Download the nuclear codes.")
public void download() {
[...]
}
@ShellMethod("Disconnect from the server.")
public void disconnect() {
[...]
}
@ShellMethodAvailability({"download", "disconnect"})
public Availability availabilityCheck() {
return connected
? Availability.available()
: Availability.unavailable("you are not connected");
}
----
====
[TIP]
=====
The default value for the `@ShellMethodAvailability.value()` attribute is `*`. This special
wildcard matches all command names. This makes it easy to turn all commands of a single class on or off
with a single availability method:
====
[source,java]
----
@ShellComponent
public class Toggles {
@ShellMethodAvailability
public Availability availabilityOnWeekdays() {
return Calendar.getInstance().get(DAY_OF_WEEK) == SUNDAY
? Availability.available()
: Availability.unavailable("today is not Sunday");
}
@ShellMethod
public void foo() {}
@ShellMethod
public void bar() {}
}
----
====
=====
TIP: Spring Shell does not impose many constraints on how to write commands and how to organize classes.
However, it is often good practice to put related commands in the same class, and the availability indicators
can benefit from that.

View File

@@ -1,4 +0,0 @@
[[built-in-commands-clear]]
==== Clear
The `clear` command does what you would expect and clears the screen, resetting the prompt
in the top left corner.

View File

@@ -1,8 +0,0 @@
[[built-in-commands-completion]]
==== Completion
The `completion` command set lets you create script files that can be used
with am OS shell implementations to provide completion. This is very useful when
working with non-interactive mode.
Currently, the only implementation is for bash, which works with `bash` sub-command.

View File

@@ -1,6 +0,0 @@
[[built-in-commands-exit]]
==== Exit
The `quit` command (also aliased as `exit`) requests the shell to quit, gracefully
closing the Spring application context. If not overridden, a JLine `History` bean writes a history of all
commands to disk, so that they are available again on the next launch.

View File

@@ -1,178 +0,0 @@
[[built-in-commands-help]]
==== Help
Running a shell application often implies that the user is in a graphically limited
environment. Also, while we are nearly always connected in the era of mobile phones,
accessing a web browser or any other rich UI application (such as a PDF viewer) may not always
be possible. This is why it is important that the shell commands are correctly self-documented, and this is where the `help`
command comes in.
Typing `help` + `ENTER` lists all the commands known to the shell (including <<dynamic-command-availability,unavailable>> commands)
and a short description of what they do, similar to the following:
====
[source, bash]
----
my-shell:>help
AVAILABLE COMMANDS
Built-In Commands
exit: Exit the shell.
help: Display help about available commands
stacktrace: Display the full stacktrace of the last error.
clear: Clear the shell screen.
quit: Exit the shell.
history: Display or save the history of previously run commands
completion bash: Generate bash completion script
version: Show version info
script: Read and execute commands from a file.
----
====
Typing `help <command>` shows more detailed information about a command, including the available parameters, their
type, whether they are mandatory or not, and other details.
The following listing shows the `help` command applied to itself:
====
[source, bash]
----
my-shell:>help help
NAME
help - Display help about available commands
SYNOPSIS
help --command String
OPTIONS
--command or -C String
The command to obtain help for.
[Optional]
----
====
Help is templated and can be customized if needed. Settings are under `spring.shell.command.help` where you can use
`enabled` to disable command, `grouping-mode` taking `group` or `flat` if you want to hide groups by flattening
a structure, `command-template` to define your template for output of a command help, `commands-template` to define
output of a command list.
If `spring.shell.command.help.grouping-mode=flat` is set, then help would show:
====
[source, bash]
----
my-shell:>help help
AVAILABLE COMMANDS
exit: Exit the shell.
help: Display help about available commands
stacktrace: Display the full stacktrace of the last error.
clear: Clear the shell screen.
quit: Exit the shell.
history: Display or save the history of previously run commands
completion bash: Generate bash completion script
version: Show version info
script: Read and execute commands from a file.
----
====
Output from `help` and `help <commmand>` are both templated with a default implementation
which can be changed.
Option `spring.shell.command.help.commands-template` defaults to
`classpath:template/help-commands-default.stg` and is passed `GroupsInfoModel`
as a model.
Option `spring.shell.command.help.command-template` defaults to
`classpath:template/help-command-default.stg` and is passed `CommandInfoModel`
as a model.
[[groupsinfomodel-variables]]
.GroupsInfoModel Variables
|===
|Key |Description
|`showGroups`
|`true` if showing groups is enabled. Otherwise, false.
|`groups`
|The commands variables (see <<groupcommandinfomodel-variables>>).
|`commands`
|The commands variables (see <<commandinfomodel-variables>>).
|`hasUnavailableCommands`
|`true` if there is unavailable commands. Otherwise, false.
|===
[[groupcommandinfomodel-variables]]
.GroupCommandInfoModel Variables
|===
|Key |Description
|`group`
|The name of a group, if set. Otherwise, empty.
|`commands`
|The commands, if set. Otherwise, empty. Type is a multi value, see <<commandinfomodel-variables>>.
|===
[[commandinfomodel-variables]]
.CommandInfoModel Variables
|===
|Key |Description
|`name`
|The name of a command, if set. Otherwise, null. Type is string and contains full command.
|`names`
|The names of a command, if set. Otherwise, null. Type is multi value essentially `name` splitted.
|`aliases`
|The possible aliases, if set. Type is multi value with strings.
|`description`
|The description of a command, if set. Otherwise, null.
|`parameters`
|The parameters variables, if set. Otherwise empty. Type is a multi value, see <<commandparameterinfomodel-variables>>.
|`availability`
|The availability variables (see <<commandavailabilityinfomodel-variables>>).
|===
[[commandparameterinfomodel-variables]]
.CommandParameterInfoModel Variables
|===
|Key |Description
|`type`
|The type of a parameter if set. Otherwise, null.
|`arguments`
|The arguments, if set. Otherwise, null. Type is multi value with strings.
|`required`
|`true` if required. Otherwise, false.
|`description`
|The description of a parameter, if set. Otherwise, null.
|`defaultValue`
|The default value of a parameter, if set. Otherwise, null.
|`hasDefaultValue`
|`true` if defaultValue exists. Otherwise, false.
|===
[[commandavailabilityinfomodel-variables]]
.CommandAvailabilityInfoModel Variables
|===
|Key |Description
|`available`
|`true` if available. Otherwise, false.
|`reason`
|The reason if not available if set. Otherwise, null.
|===

View File

@@ -1,16 +0,0 @@
[[built-in-commands-history]]
==== History
The `history` command shows the history of commands that has been executed.
There are a few configuration options that you can use to configure behavior
of a history. History is kept in a log file, which is enabled by default and can
be turned off by setting `spring.shell.history.enabled`. The name of a log file
is resolved from `spring.application.name` and defaults to `spring-shell.log`,
which you can change by setting `spring.shell.history.name`.
By default, a log file is generated to a current working directory, which you can dictate
by setting `spring.shell.config.location`. This property can contain
a placeholder (`{userconfig}`), which resolves to a common shared config directory.
TIP: Run the Spring Shell application to see how the sample application works as it uses these options.

View File

@@ -1,7 +0,0 @@
[[built-in-commands-script]]
==== Script
The `script` command accepts a local file as an argument and replays commands found there, one at a time.
Reading from the file behaves exactly like inside the interactive shell, so lines starting with `//` are considered
to be comments and are ignored, while lines ending with `\` trigger line continuation.

View File

@@ -1,9 +0,0 @@
[[built-in-commands-stacktrace]]
==== Stacktrace
When an exception occurs inside command code, it is caught by the shell and a simple, one-line message is displayed
so as not to overflow the user with too much information.
There are cases, though, when understanding what exactly happened is important (especially if the exception has a nested cause).
To this end, Spring Shell remembers the last exception that occurred, and the user can later use the `stacktrace`
command to print all the details on the console.

View File

@@ -1,37 +0,0 @@
[[built-in-commands-version]]
==== Version
The `version` command shows existing build and git info by integrating into
Boot's `BuildProperties` and `GitProperties` if those exist in the shell application.
By default, only version information is shown, and you can enable other information through configuration
options.
The relevant settings are under `spring.shell.command.version`, where you can use `enabled` to
disable a command and, optionally, define your own template with `template`. You can use the
`show-build-artifact`, `show-build-group`, `show-build-name`, `show-build-time`,
`show-build-version`, `show-git-branch`, `show-git-commit-id`,
`show-git-short-commit-id` and `show-git-commit-time` commands to control
fields in a default template.
The template defaults to `classpath:template/version-default.st`, and you can define
your own, as the following example shows:
====
[source]
----
<buildVersion>
----
====
This setting would output something like the following:
====
[source]
----
X.X.X
----
====
You can add the following attributes to the default template rendering: `buildVersion`, `buildGroup`,
`buildGroup`, `buildName`, `buildTime`, `gitShortCommitId`, `gitCommitId`,
`gitBranch`, and `gitCommitTime`.

View File

@@ -1,18 +0,0 @@
[[built-in-commands]]
=== Built-In Commands
include::using-shell-commands-builtin-help.adoc[]
include::using-shell-commands-builtin-clear.adoc[]
include::using-shell-commands-builtin-exit.adoc[]
include::using-shell-commands-builtin-stacktrace.adoc[]
include::using-shell-commands-builtin-script.adoc[]
include::using-shell-commands-builtin-history.adoc[]
include::using-shell-commands-builtin-completion.adoc[]
include::using-shell-commands-builtin-version.adoc[]

View File

@@ -1,87 +0,0 @@
[[dynamic-command-exitcode-annotation]]
==== @ExceptionResolver
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
`@ShellComponent` classes can have `@ExceptionResolver` methods to handle exceptions from component
methods. These are meant for annotated methods.
The exception may match against a top-level exception being propagated (e.g. a direct IOException
being thrown) or against a nested cause within a wrapper exception (e.g. an IOException wrapped
inside an IllegalStateException). This can match at arbitrary cause levels.
For matching exception types, preferably declare the target exception as a method argument, as
the preceding example(s) shows. When multiple exception methods match, a root exception match is
generally preferred to a cause exception match. More specifically, the ExceptionDepthComparator
is used to sort exceptions based on their depth from the thrown exception type.
Alternatively, the annotation declaration may narrow the exception types to match, as the
following example shows:
====
[source, java, indent=0]
----
include::{snippets}/ErrorHandlingSnippets.java[tag=exception-resolver-with-type-in-annotation]
----
====
====
[source, java, indent=0]
----
include::{snippets}/ErrorHandlingSnippets.java[tag=exception-resolver-with-type-in-method]
----
====
`@ExceptionResolver` can also return `String` which is used as an output to console. You can
use `@ExitCode` annotation to define return code.
====
[source, java, indent=0]
----
include::{snippets}/ErrorHandlingSnippets.java[tag=exception-resolver-with-exitcode-annotation]
----
====
`@ExceptionResolver` with `void` return type is automatically handled as handled exception.
You can then also define `@ExitCode` and use `Terminal` if you need to write something
into console.
====
[source, java, indent=0]
----
include::{snippets}/ErrorHandlingSnippets.java[tag=exception-resolver-with-void]
----
====
===== Method Arguments
`@ExceptionResolver` methods support the following arguments:
[Attributes]
|===
|Method argument |Description
|Exception type
|For access to the raised exception. This is any type of `Exception` or `Throwable`.
|Terminal
|For access to underlying `JLine` terminal to i.e. get its terminal writer.
|===
===== Return Values
`@ExceptionResolver` methods support the following return values:
[Attributes]
|===
|Return value |Description
|String
|Plain text to return to a shell. Exit code 1 is used in this case.
|CommandHandlingResult
|Plain `CommandHandlingResult` having message and exit code.
|void
|A method with a void return type is considered to have fully handled the exception. Usually
you would define `Terminal` as a method argument and write response using _terminal writer_
from it. As exception is fully handled, Exit code 0 is used in this case.
|===

View File

@@ -1,34 +0,0 @@
[[dynamic-command-exitcode-mappings]]
==== Exit Code Mappings
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Default behaviour of an exit codes is as:
- Errors from a command option parsing will result code of `2`
- Any generic error will result result code of `1`
- Obviously in any other case result code is `0`
Every `CommandRegistration` can define its own mappings between _Exception_ and _exit code_.
Essentially we're bound to functionality in `Spring Boot` regarding _exit code_ and simply
integrate into that.
Assuming there is an exception show below which would be thrown from a command:
====
[source, java, indent=0]
----
include::{snippets}/ExitCodeSnippets.java[tag=my-exception-class]
----
====
It is possible to define a mapping function between `Throwable` and exit code. You can also
just configure a _class_ to _exit code_ which is just a syntactic sugar within configurations.
====
[source, java, indent=0]
----
include::{snippets}/ExitCodeSnippets.java[tag=example1]
----
====
NOTE: Exit codes cannot be customized with annotation based configuration

View File

@@ -1,55 +0,0 @@
[[dynamic-command-exitcode-resolving]]
==== Exception Resolving
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Unhandled exceptions will bubble up into shell's `ResultHandlerService` and then eventually
handled by some instance of `ResultHandler`. Chain of `ExceptionResolver` implementations
can be used to resolve exceptions and gives you flexibility to return message to get written
into console together with exit code which are wrapped within `CommandHandlingResult`.
`CommandHandlingResult` may contain a _message_ and/or _exit code_.
====
[source, java, indent=0]
----
include::{snippets}/ErrorHandlingSnippets.java[tag=my-exception-resolver-class]
----
====
`CommandExceptionResolver` implementations can be defined globally as bean.
====
[source, java, indent=0]
----
include::{snippets}/ErrorHandlingSnippets.java[tag=my-exception-resolver-class-as-bean]
----
====
or defined per `CommandRegistration` if it's applicable only for a particular command itself.
====
[source, java, indent=0]
----
include::{snippets}/ErrorHandlingSnippets.java[tag=example1]
----
====
NOTE: Resolvers defined with a command are handled before global resolvers.
Use you own exception types which can also be an instance of boot's `ExitCodeGenerator` if
you want to define exit code there.
====
[source, java, indent=0]
----
include::{snippets}/ErrorHandlingSnippets.java[tag=my-exception-class]
----
====
Some build in `CommandExceptionResolver` beans are registered to handle common
exceptions thrown from command parsing. These are registered with _order_
presedence defined in `CommandExceptionResolver.DEFAULT_PRECEDENCE`.
As these beans are used in a given order, `@Order` annotation or `Ordered`
interface from can be used just like in any other spring app. This
is generally useful if you need to control your own beans to get used
either before or after a defaults.

View File

@@ -1,19 +0,0 @@
[[dynamic-command-exitcode]]
=== Exception Handling
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Exceptions happen from a user code wether it is intentional or not. This section describes
how `spring-shell` handles exceptions and gives instructions and best practices how to
work with it.
Many command line applications when applicable return an _exit code_ which running environment
can use to differentiate if command has been executed successfully or not. In a `spring-shell`
this mostly relates when a command is run on a non-interactive mode meaning one command
is always executed once with an instance of a `spring-shell`. Take a note that _exit code_
always relates to non-interactive shell.
include::using-shell-commands-exceptionhandling-resolving.adoc[]
include::using-shell-commands-exceptionhandling-mappings.adoc[]
include::using-shell-commands-exceptionhandling-annotation.adoc[]

View File

@@ -1,41 +0,0 @@
[[commands-helpoptions]]
=== Help Options
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
_Spring Shell_ has a build-in `help` command but not all favour getting command help
from it as you always need to call it with arguments for target command. It's
common in many cli frameworks for every command having options _--help_ and _-h_
to print out command help.
Default functionality is that every command will get modified to have options
_--help_ and _-h_, which if present in a given command will automatically
short circuit command execution into a existing `help` command regardless
what other command-line options is typed.
Below example shows its default settings.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationHelpOptionsSnippets.java[tag=defaults]
----
====
It is possible to change default behaviour via configuration options.
====
[source, yaml]
----
spring:
shell:
help:
enabled: true
long-names: help
short-names: h
command: help
----
====
NOTE: Commands defined programmationally or via annotations will automatically add
help options. With annotation model you can only turn things off globally, programmatic
model gives option to modify settings per command.

View File

@@ -1,27 +0,0 @@
[[commands-hidden]]
=== Hidden Command
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
It is possible to _hide_ a command which is convenient in cases where it is not yet ready for
prime time, is meant for debugging purposes or you have any other reason you dont want to
advertise its presense.
Hidden command can be executed if you know it and its options. It is effectively removed
from:
* Help listing
* Help page for command return "unknown command"
* Command completion in interactive mode
* Bash completion
Below is an example how to define command as _hidden_. It shows available builder methods
to define _hidden_ state.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationHiddenSnippets.java[tag=snippet1]
----
====
NOTE: Defining hidden commands is not supported with annotation based configuration

View File

@@ -1,24 +0,0 @@
[[commands-interactionmode]]
=== Interaction Mode
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Command registration can define `InteractionMode` which is used to hide commands
depending which mode shell is executing. More about that in <<using-shell-execution-interactionmode>>.
You can define it with `CommandRegisration`.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationInteractionModeSnippets.java[tag=snippet1]
----
====
Or with `@ShellMethod`.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationInteractionModeSnippets.java[tag=snippet2]
----
====

View File

@@ -1,54 +0,0 @@
[[organizing-commands]]
=== Organizing Commands
When your shell starts to provide a lot of functionality, you may end up
with a lot of commands, which could be confusing for your users. By typing `help`,
they would see a daunting list of commands, organized in alphabetical order,
which may not always be the best way to show the available commands.
To alleviate this possible confusion, Spring Shell provides the ability to group commands together,
with reasonable defaults. Related commands would then end up in the same group (for example, `User Management Commands`)
and be displayed together in the help screen and other places.
By default, commands are grouped according to the class they are implemented in,
turning the camelCase class name into separate words (so `URLRelatedCommands` becomes `URL Related Commands`).
This is a sensible default, as related commands are often already in the class anyway,
because they need to use the same collaborating objects.
If, however, this behavior does not suit you, you can override the group for a
command in the following ways, in order of priority:
. Specify a `group()` in the `@ShellMethod` annotation.
. Place a `@ShellCommandGroup` on the class in which the command is defined. This applies
the group for all commands defined in that class (unless overridden, as explained earlier).
. Place a `@ShellCommandGroup` on the package (through `package-info.java`)
in which the command is defined. This applies to all the commands defined in the
package (unless overridden at the method or class level, as explained earlier).
The following listing shows an example:
====
[source,java]
----
public class UserCommands {
@ShellMethod(value = "This command ends up in the 'User Commands' group")
public void foo() {}
@ShellMethod(value = "This command ends up in the 'Other Commands' group",
group = "Other Commands")
public void bar() {}
}
...
@ShellCommandGroup("Other Commands")
public class SomeCommands {
@ShellMethod(value = "This one is in 'Other Commands'")
public void wizz() {}
@ShellMethod(value = "And this one is 'Yet Another Group'",
group = "Yet Another Group")
public void last() {}
}
----
====

View File

@@ -1,52 +0,0 @@
[[commands-registration-annotation]]
==== Annotation
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
`@Command` annotation when used on a method marks it as a candidate for command registration.
In below example a command `example` is defined.
====
[source, java, indent=0]
----
include::{snippets}/CommandAnnotationSnippets.java[tag=command-anno-in-method]
----
====
`@Command` annotation can be placed on a class which either defines defaults or shared settings
for `@Command` methods defined in a same class. In below example a command `parent example` is
defined.
====
[source, java, indent=0]
----
include::{snippets}/CommandAnnotationSnippets.java[tag=command-anno-in-class]
----
====
Using a `@Command` will not automatically register command targets, instead it is required to use
`@EnableCommand` and/or `@CommandScan` annotations. This model is familiar from other parts
of Spring umbrella and provides better flexibility for a user being inclusive rather than exclusive
for command targets.
You can define target classes using `@EnableCommand`. It will get picked from all _Configuration_
classes.
====
[source, java, indent=0]
----
include::{snippets}/CommandAnnotationSnippets.java[tag=enablecommand-with-class]
----
====
You can define target classes using `@CommandScan`. It will get picked from all _Configuration_
classes.
TIP: Define `@CommandScan` in Spring Boot `App` class on a top level and it will automatically
scan all command targets from all packages and classes under `App`.
====
[source, java, indent=0]
----
include::{snippets}/CommandAnnotationSnippets.java[tag=commandscan-no-args]
----
====

View File

@@ -1,51 +0,0 @@
[[commands-registration-legacyannotation]]
==== Legacy Annotation
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
When you use the standard API, methods on beans are turned into executable commands, provided that:
* The bean class bears the `@ShellComponent` annotation. (This is used to restrict the set of beans
that are considered.)
* The method bears the `@ShellMethod` annotation.
[TIP]
====
The `@ShellComponent` is a stereotype annotation that is itself meta-annotated with `@Component`. As a result,
you can use it in addition to the filtering mechanism to declare beans (for example, by using `@ComponentScan`).
You can customize the name of the created bean by using the `value` attribute of the annotation.
====
====
[source, java, indent=0]
----
include::{snippets}/AnnotationRegistrationSnippets.java[tag=snippet1]
----
====
The only required attribute of the `@ShellMethod` annotation is its `value` attribute, which should have
a short, one-sentence, description of what the command does. This lets your users
get consistent help about your commands without having to leave the shell (see <<built-in-commands-help>>).
NOTE: The description of your command should be short -- no more than one or two sentences. For better
consistency, it should start with a capital letter and end with a period.
By default, you need not specify the key for your command (that is, the word(s) that should be used
to invoke it in the shell). The name of the method is used as the command key, turning camelCase names into
dashed, gnu-style, names (for example, `sayHello()` becomes `say-hello`).
You can, however, explicitly set the command key, by using the `key` attribute of the annotation:
====
[source, java, indent=0]
----
include::{snippets}/AnnotationRegistrationSnippets.java[tag=snippet2]
----
====
NOTE: The `key` attribute accepts multiple values.
If you set multiple keys for a single method, the command is registered with those different aliases.
TIP: The command key can contain pretty much any character, including spaces. When coming up with names though,
keep in mind that consistency is often appreciated by users. That is, you should avoid mixing dashed-names with
spaced names and other inconsistencies.

View File

@@ -1,41 +0,0 @@
[[commands-registration-programmatic]]
==== Programmatic
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
In the programmatic model, `CommandRegistration` can be defined as a `@Bean`
and it will be automatically registered.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=plain]
----
====
If all your commands have something in common, an instance of
a _CommandRegistration.BuilderSupplier_ is created which can
be autowired. Default implementation of this supplier returns
a new builder so you don't need to worry about its internal state.
IMPORTANT: Commands registered programmatically automatically
add _help options_ mentioned in <<commands-helpoptions>>.
If bean of this supplier type is defined then auto-configuration
will back off giving you an option to redefine default functionality.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=fromsupplier]
----
====
`CommandRegistrationCustomizer` beans can be defined if you want to centrally
modify builder instance given you by supplier mentioned above.
====
[source, java, indent=0]
----
include::{snippets}/CommandRegistrationBeanSnippets.java[tag=customizer]
----
====

View File

@@ -1,21 +0,0 @@
=== Registration
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
There are two different ways to define a command: through an annotation model and
through a programmatic model. In the annotation model, you define your methods
in a class and annotate the class and the methods with specific annotations.
In the programmatic model, you use a more low level approach, defining command
registrations (either as beans or by dynamically registering with a command catalog).
Starting from _3.1.x_ a better support for defining commands using
<<commands-registration-annotation, annotations>> were added. Firstly because eventually standard
package providing <<commands-registration-legacyannotation, legacy annotations>> will get deprecated
and removed. Secondly so that we're able to provide same set of features than using underlying
`CommandRegistration`. Creating new a annotation model allows us to rethink and modernise that
part without breaking existing applications.
include::using-shell-commands-registration-programmatic.adoc[]
include::using-shell-commands-registration-annotation.adoc[]
include::using-shell-commands-registration-legacyannotation.adoc[]

View File

@@ -1,27 +0,0 @@
=== Writing
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
When something needs to get written into your console you can always
use JDK's `System.out` which then goes directly into JDK's own streams.
Other recommended way is to use JLine's `Terminal` and get _writer_
instance from there.
If using target endpoints, i.e. _consumer_ which is not expected
to return anything given `CommandContext` contains reference to
`Terminal` and writer can be accessed from there.
====
[source, java, indent=0]
----
include::{snippets}/WritingSnippets.java[tag=reg-terminal-writer]
----
====
It's possible to autowire `Terminal` to get access to its writer.
====
[source, java, indent=0]
----
include::{snippets}/WritingSnippets.java[tag=anno-terminal-writer]
----
====

View File

@@ -1,24 +0,0 @@
== Commands
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
In this section, we go through an actual command registration and leave command options
and execution for later in a documentation. You can find more detailed info in
<<appendix-tech-intro-registration>>.
include::using-shell-commands-registration.adoc[]
include::using-shell-commands-organize.adoc[]
include::using-shell-commands-availability.adoc[]
include::using-shell-commands-exceptionhandling.adoc[]
include::using-shell-commands-hidden.adoc[]
include::using-shell-commands-helpoptions.adoc[]
include::using-shell-commands-interactionmode.adoc[]
include::using-shell-commands-builtin.adoc[]
include::using-shell-commands-writing.adoc[]

View File

@@ -1,64 +0,0 @@
== Completion
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Spring Shell can provide completion proposals for both interactive shell
and a command-line. There are differences however as when shell is in
interactive mode we have an active instance of a shell meaning it's
easier to provide more programmatic ways to provide completion hints.
When shell is purely run as a command-line tool a completion can only
be accomplished with integration into OS level shell's like _bash_.
=== Interactive
Hints for completions are calculated with _function_ or _interface_ style
methods which takes `CompletionContext` and returns a list of
`CompletionProposal` instances. `CompletionContext` gives you various
information about a current context like command registration and option.
NOTE: Generic resolvers can be registered as a beans if those are useful
for all commands and scenarious. For example existing completion
implementation `RegistrationOptionsCompletionResolver` handles completions
for a option names.
====
[source, java, indent=0]
----
include::{snippets}/CompletionSnippets.java[tag=resolver-1]
----
====
Option values with builder based command registration can be
defined per option.
====
[source, java, indent=0]
----
include::{snippets}/CompletionSnippets.java[tag=builder-1]
----
====
Option values with annotation based command registration are handled
via `ValueProvider` interface which can be defined with `@ShellOption`
annotation.
====
[source, java, indent=0]
----
include::{snippets}/CompletionSnippets.java[tag=provider-1]
----
====
Actual `ValueProvider` with annotation based command needs to be
registered as a _Bean_.
====
[source, java, indent=0]
----
include::{snippets}/CompletionSnippets.java[tag=anno-method]
----
====
=== Command-Line
Command-line completion currently only support _bash_ and is documented
in a built-in `completion` command <<built-in-commands-completion>>.

View File

@@ -1,37 +0,0 @@
[[using-shell-components-flow]]
=== Flow
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
When you use <<using-shell-components-ui>> to build something that involves
use of a multiple components, your implementation may become a bit cluttered.
To ease these use cases, we added a
`ComponentFlow` that can hook multiple component executions together
as a "`flow`".
The following listings show examples of flows and their output in a shell:
====
[source, java, indent=0]
----
include::{snippets}/FlowComponentSnippets.java[tag=snippet1]
----
====
image::images/component-flow-showcase-1.svg[text input]
Normal execution order of a components is same as defined with a builder. It's
possible to conditionally choose where to jump in a flow by using a `next`
function and returning target _component id_. If this returned id is aither _null_
or doesn't exist flow is essentially stopped right there.
====
[source, java, indent=0]
----
include::{snippets}/FlowComponentSnippets.java[tag=snippet2]
----
====
image::images/component-flow-conditional-1.svg[text input]
TIP: The result from running a flow returns `ComponentFlowResult`, which you can
use to do further actions.

View File

@@ -1,31 +0,0 @@
[[using-shell-components-ui-confirmation]]
==== Confirmation
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
The confirmation component asks a user for a simple confirmation. It is essentially a
yes-or-no question.
====
[source, java, indent=0]
----
include::{snippets}/UiComponentSnippets.java[tag=snippet5]
----
====
The following image shows the typical output from a confirmation component:
image::images/component-confirmation-1.svg[text input]
The context object is `ConfirmationInputContext`. The following table describes its context variables:
[[confirmationinputcontext-template-variables]]
.ConfirmationInputContext Template Variables
|===
|Key |Description
|`defaultValue`
|The default value -- either `true` or `false`.
|`model`
|The parent context variables (see <<textcomponentcontext-template-variables>>).
|===

View File

@@ -1,34 +0,0 @@
[[using-shell-components-ui-multiselect]]
==== Multi Select
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
The multi select component asks a user to select multiple items from a list.
The following listing shows an example:
====
[source, java, indent=0]
----
include::{snippets}/UiComponentSnippets.java[tag=snippet7]
----
====
The following image shows a typical multi-select component:
image::images/component-multi-select-1.svg[text input]
The context object is `MultiItemSelectorContext`. The following table describes its context variables:
[[multiitemselectorcontext-template-variables]]
.MultiItemSelectorContext Template Variables
|===
|Key |Description
|`values`
|The values returned when the component exists.
|`rows`
|The visible items, where rows contain maps of name, selected, on-row, and enabled items.
|`model`
|The parent context variables (see <<selectorcomponentcontext-template-variables>>).
|===

View File

@@ -1,27 +0,0 @@
[[using-shell-components-ui-pathinput]]
==== Path Input
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
The path input component asks a user for a `Path` and gives additional information about a path itself.
====
[source, java, indent=0]
----
include::{snippets}/UiComponentSnippets.java[tag=snippet4]
----
====
The following image shows typical output from a path input component:
image::images/component-path-input-1.svg[text input]
The context object is `PathInputContext`. The following table describes its context variables:
[[pathinputcontext-template-variables]]
.PathInputContext Template Variables
|===
|Key |Description
|`model`
|The parent context variables (see <<textcomponentcontext-template-variables>>).
|===

View File

@@ -1,35 +0,0 @@
[[using-shell-components-ui-pathsearch]]
==== Path Search
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
The path search component asks base directory for scan and optional search expression.
Results are shown in a single select list where user can pick a path.
`PathSearchConfig` can be used to customise component behaviour.
====
[source, java, indent=0]
----
include::{snippets}/UiComponentSnippets.java[tag=snippet9]
----
====
NOTE: Logic for search is passed as is into algorithms documented
in <<appendix-tech-intro-searchalgorithm>>.
The following image shows typical output from a path search component:
image::images/component-path-search-1.svg[text input]
The context object is `PathSearchContext`. The following table describes its context variables:
[[pathsearchcontext-template-variables]]
.PathSearchContext Template Variables
|===
|Key |Description
|`pathViewItems`
|The items available for rendering search results.
|`model`
|The parent context variables (see <<textcomponentcontext-template-variables>>).
|===

View File

@@ -1,102 +0,0 @@
[[using-shell-components-ui-render]]
==== Component Render
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
You can implement component rendering in either of two ways: fully
programmatically or by using a _ANTLR Stringtemplate_.
Strictly speaking, there is a simple `Function` renderer interface
that takes `Context` as an input and outputs a list of `AttributedString`.
This lets you choose between templating and code.
Templating is a good choice if you do not need to do anything complex or
you just want to slightly modify existing component layouts. Rendering
through code then gives you flexibility to do whatever you need.
The programmatic way to render is to create a `Function`:
====
[source, java, indent=0]
----
include::{snippets}/UiComponentSnippets.java[tag=snippet1]
----
====
Then you can hook it to a component:
====
[source, java, indent=0]
----
include::{snippets}/UiComponentSnippets.java[tag=snippet2]
----
====
Components have their own context but usually share some functionality
from a parent component types. The following tables show those context variables:
[[textcomponentcontext-template-variables]]
.TextComponentContext Template Variables
|===
|Key |Description
|`resultValue`
|The value after a component renders its result.
|`name`
|The name of a component -- that is, its title.
|`message`
|The possible message set for a component.
|`messageLevel`
|The level of a message -- one of `INFO`, `WARN`, or `ERROR`.
|`hasMessageLevelInfo`
|Return `true` if level is `INFO`. Otherwise, false.
|`hasMessageLevelWarn`
|Return `true` if level is `WARN`. Otherwise, false.
|`hasMessageLevelError`
|Return `true` if level is `ERROR`. Otherwise, false.
|`input`
|The raw user input.
|===
[[selectorcomponentcontext-template-variables]]
.SelectorComponentContext Template Variables
|===
|Key |Description
|`name`
|The name of a component -- that is, its title.
|`input`
|The raw user input -- mostly used for filtering.
|`itemStates`
|The full list of item states.
|`itemStateView`
|The visible list of item states.
|`isResult`
|Return `true` if the context is in a result mode.
|`cursorRow`
|The current cursor row in a selector.
|===
[[componentcontext-template-variables]]
.ComponentContext Template Variables
|===
|Key |Description
|`terminalWidth`
|The width of terminal, type is _Integer_ and defaults to _NULL_ if not set.
|===

View File

@@ -1,45 +0,0 @@
[[using-shell-components-ui-singleselect]]
==== Single Select
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
A single select component asks a user to choose one item from a list. It is similar to a simple
dropbox implementation. The following listing shows an example:
====
[source, java, indent=0]
----
include::{snippets}/UiComponentSnippets.java[tag=snippet6]
----
====
The following image shows typical output for a single select component:
image::images/component-single-select-1.svg[text input]
The context object is `SingleItemSelectorContext`. The following table describes its context variables:
[[singleitemselectorcontext-template-variables]]
.SingleItemSelectorContext Template Variables
|===
|Key |Description
|`value`
|The returned value when the component exists.
|`rows`
|The visible items, where rows contains maps of name and selected items.
|`model`
|The parent context variables (see <<selectorcomponentcontext-template-variables>>).
|===
You can pre-select an item by defining it to get exposed. This is
useful if you know the default and lets the user merely press `Enter` to make a choice.
The following listing sets a default:
====
[source, java, indent=0]
----
include::{snippets}/UiComponentSnippets.java[tag=snippet8]
----
====

View File

@@ -1,43 +0,0 @@
[[using-shell-components-ui-stringinput]]
==== String Input
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
The string input component asks a user for simple text input, optionally masking values
if the content contains something sensitive. The following listing shows an example:
====
[source, java, indent=0]
----
include::{snippets}/UiComponentSnippets.java[tag=snippet3]
----
====
The following image shows typical output from a string input component:
image::images/component-text-input-1.svg[text input]
The context object is `StringInputContext`. The following table lists its context variables:
[[stringinputcontext-template-variables]]
.StringInputContext Template Variables
|===
|Key |Description
|`defaultValue`
|The default value, if set. Otherwise, null.
|`maskedInput`
|The masked input value
|`maskedResultValue`
|The masked result value
|`maskCharacter`
|The mask character, if set. Otherwise, null.
|`hasMaskCharacter`
|`true` if a mask character is set. Otherwise, false.
|`model`
|The parent context variables (see <<textcomponentcontext-template-variables>>).
|===

View File

@@ -1,36 +0,0 @@
[[using-shell-components-ui]]
=== Flow Components
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Starting from version 2.1.x, a new component model provides an
easier way to create higher-level user interaction for the usual use cases,
such as asking for input in various forms. These usually are just plain text
input or choosing something from a list.
Templates for built-in components are in the
`org/springframework/shell/component` classpath.
Built-in components generally follow this logic:
. Enter a run loop for user input.
. Generate component-related context.
. Render the runtime status of a component state.
. Exit.
. Render the final status of a component state.
NOTE: <<using-shell-components-flow>> gives better interface for defining the flow of
components that are better suited for defining interactive command flows.
include::using-shell-components-ui-render.adoc[]
include::using-shell-components-ui-stringinput.adoc[]
include::using-shell-components-ui-pathinput.adoc[]
include::using-shell-components-ui-pathsearch.adoc[]
include::using-shell-components-ui-confirmation.adoc[]
include::using-shell-components-ui-singleselect.adoc[]
include::using-shell-components-ui-multiselect.adoc[]

View File

@@ -1,11 +0,0 @@
[[using-shell-components]]
== Components
Components are a set of features which are either build-in or something
you can re-use or extend for your own needs. Components in question are
either built-in _commands_ or UI side components providing higher level
features within commands itself.
include::using-shell-components-flow.adoc[]
include::using-shell-components-ui.adoc[]

View File

@@ -1,44 +0,0 @@
[[using-shell-customization-commandnotfound]]
=== Command Not Found
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
On default a missing command is handled via `CommandNotFoundResultHandler`
and outputs a simple message:
====
[source, text]
----
shell:>missing
No command found for 'missing'
----
====
Internally `CommandNotFoundResultHandler` is using `CommandNotFoundMessageProvider`
which is a simple function taking a `ProviderContext` and returning a text
message. Below is an example what a custom message provider might look like.
====
[source, java, indent=0]
----
include::{snippets}/CommandNotFoundSnippets.java[tag=custom-provider]
----
====
It's possible to change this implementation by defining it as a bean.
====
[source, java, indent=0]
----
include::{snippets}/CommandNotFoundSnippets.java[tag=provider-bean-1]
----
====
`CommandNotFoundResultHandler` is a functional interface so it can
be writter as a lambda.
====
[source, java, indent=0]
----
include::{snippets}/CommandNotFoundSnippets.java[tag=provider-bean-2]
----
====

View File

@@ -1,23 +0,0 @@
[[using-shell-customization-contextclose]]
=== Context Close
Command execution logic happens via Spring Boot's `ApplicationRunner` beans.
Normally Spring `ApplicationContext` closes automatically after these runner
beans has been processed unless there is something what keeps it alive like
use of `@EnableScheduling` or generally speaking there are threads which
don't die automatically.
It is possible to add configuration property `spring.shell.context.close`
which registers `ApplicationListener` for `ApplicationReadyEvent` and requests
context close after shell has completed its execution logic.
[source, yaml]
----
spring:
shell:
context:
close: true
----
NOTE: This setting is not enabled by default.

View File

@@ -1,55 +0,0 @@
[[using-shell-customization-logging]]
=== Logging
On default a _Spring Boot_ application will log messages into a console which
at minimum is annoying and may also mix output from a shell commands.
Fortunately there is a simple way to instruct logging changes via boot properties.
Completely silence console logging by defining its pattern as an empty value.
====
[source, yaml]
----
logging:
pattern:
console:
----
====
If you need log from a shell then write those into a file.
====
[source, yaml]
----
logging:
file:
name: shell.log
----
====
If you need different log levels.
====
[source, yaml]
----
logging:
level:
org:
springframework:
shell: debug
----
====
Passing contiguration properties as command line options is not supported but
you can use any other ways supported by boot, for example.
====
[source, bash]
----
$ java -Dlogging.level.root=debug -jar demo.jar
$ LOGGING_LEVEL_ROOT=debug java -jar demo.jar
----
====
NOTE: In a GraalVM image settings are locked during compilation which means
you can't change log levels at runtime.

View File

@@ -1,23 +0,0 @@
[[using-shell-customization-singlecommand]]
=== Single Command
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
If your shell application is made for exactly a single purpose having only one
command it may be beneficial to configure it for this. Property
`spring.shell.noninteractive.primary-command` if defined will disable all other
runners than `NonInteractiveShellRunner` and configures it to use
defined _Primary Command_.
====
[source, yaml]
----
spring:
shell:
noninteractive:
primary-command: mycommand
----
====
For example if you have a command `mycommand` with option `arg`
it had to be executed with `<shellapp> mycommand --arg hi`, but with above
setting it can be executed with `<shellapp> --arg hi`.

View File

@@ -1,68 +0,0 @@
[[theming]]
=== Theming
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Current terminal implementations are rich in features and can usually show
something else that just plain text. For example a text can be styled to be
_bold_ or have different colors. It's also common for terminals to be able
to show various characters from an unicode table like emoji's which are usually
used to make shell output more pretty.
Spring Shell supports these via it's theming framework which contains two parts,
firstly _styling_ can be used to change text type and secondly _figures_ how
some characters are shown. These two are then combined together as a _theme_.
More about _theming_ internals, see <<appendix-tech-intro-theming>>.
NOTE: Default theme is named `default` but can be change using property
`spring.shell.theme.name`. Other built-in theme named `dump` uses
no styling for colors and tries to not use any special figures.
Modify existing style by overriding settings.
====
[source, java, indent=0]
----
include::{snippets}/ThemingSnippets.java[tag=custom-style-class]
----
====
Modify existing figures by overriding settings.
====
[source, java, indent=0]
----
include::{snippets}/ThemingSnippets.java[tag=custom-figure-class]
----
====
To create a new theme, create a `ThemeSettings` and provide your own _style_
and _figure_ implementations.
====
[source, java, indent=0]
----
include::{snippets}/ThemingSnippets.java[tag=custom-theme-class]
----
====
Register a new bean `Theme` where you can return your custom `ThemeSettings`
and a _theme_ name.
====
[source, java, indent=0]
----
include::{snippets}/ThemingSnippets.java[tag=custom-theme-config]
----
====
You can use `ThemeResolver` to resolve _styles_ if you want to create
JLine-styled strings programmatically and _figures_ if you want to
theme characters for being more pretty.
====
[source, java, indent=0]
----
include::{snippets}/ThemingSnippets.java[tag=using-theme-resolver]
----
====

View File

@@ -1,14 +0,0 @@
[[using-shell-customization]]
== Customization
This section describes how you can customize the shell.
include::using-shell-customization-styling.adoc[]
include::using-shell-customization-logging.adoc[]
include::using-shell-customization-commandnotfound.adoc[]
include::using-shell-customization-singlecommand.adoc[]
include::using-shell-customization-contextclose.adoc[]

View File

@@ -1,35 +0,0 @@
[[using-shell-execution]]
== Execution
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
This section describes how to set up a Spring Shell to work in interactive mode.
[[using-shell-execution-interactionmode]]
=== Interaction Mode
Version 2.1.x introduced built-in support to distinguish between interactive
and non-interactive modes. This makes it easier to use the shell as a
simple command-line tool without requiring customization.
Currently, interactive mode is entered if any command line options are passed when starting
or running a shell from a command line. This works especially well when a shell application
is compiled with <<native>>.
Some commands may not have any useful meanings when they run in interactive mode
or (conversely) in non-interactive mode. For example, a built-in `exit` command would
have no meaning in non-interactive mode, because it is used to exit interactive mode.
The `@ShellMethod` annotation has a field called `interactionMode` that you can use to inform
shell about when a particular command is available.
[[using-shell-execution-shellrunner]]
=== Shell Runners
`ShellApplicationRunner` is a main interface where Boot's `ApplicationArguments` are passed
and its default implementation makes a choice which `ShellRunner` is used. There can be
only one `ShellApplicationRunner` but it can be redefined if needed for some reason.
Three `ShellRunner` implementation exists, named `InteractiveShellRunner`,
`NonInteractiveShellRunner` and `ScriptShellRunner`. These are enabled on default but
can be disable if needed using properties `spring.shell.interactive.enabled`,
`spring.shell.noninteractive.enabled` and `spring.shell.script.enabled` respecively.

View File

@@ -1,91 +0,0 @@
[[using-shell-options-arity]]
=== Arity
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Arity defines how many parameters option parsing takes.
NOTE: There are limitations in a `legacy annotation` compared to `annotation`
and `programmatic` use of arity settings. These are mentioned in notes in
below samples.
[source,java,indent=0,role="primary"]
.Programmatic
----
include::{snippets}/OptionSnippets.java[tag=option-registration-zeroorone-programmatic]
----
[source,java,indent=0,role="secondary"]
.Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-registration-zeroorone-annotation]
----
[source,java,indent=0,role="secondary"]
.Legacy Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-registration-zeroorone-legacyannotation]
----
[[using-shell-options-arity-optionarity-table]]
.OptionArity
|===
|Value |min/max
|ZERO
|0 / 0
|ZERO_OR_ONE
|0 / 1
|EXACTLY_ONE
|1 / 1
|ZERO_OR_MORE
| 0 / Integer MAX
|ONE_OR_MORE
|1 / Integer MAX
|===
NOTE: `legacy annotation` doesn't support defining minimum arity.
[source,java,indent=0,role="primary"]
.Programmatic
----
include::{snippets}/OptionSnippets.java[tag=option-registration-zerooronewithminmax-programmatic]
----
[source,java,indent=0,role="secondary"]
.Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-registration-zerooronewithminmax-annotation]
----
[source,java,indent=0,role="secondary"]
.Legacy Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-registration-zerooronewithminmax-legacyannotation]
----
In below example we have option _arg1_ and it's defined as type _String[]_. Arity
defines that it needs at least 1 parameter and not more that 2. As seen in below
spesific exceptions _TooManyArgumentsOptionException_ and
_NotEnoughArgumentsOptionException_ are thrown to indicate arity mismatch.
====
[source, bash]
----
shell:>e2e reg arity-errors --arg1
Not enough arguments --arg1 requires at least 1.
shell:>e2e reg arity-errors --arg1 one
Hello [one]
shell:>e2e reg arity-errors --arg1 one two
Hello [one, two]
shell:>e2e reg arity-errors --arg1 one two three
Too many arguments --arg1 requires at most 2.
----
====

View File

@@ -1,13 +0,0 @@
[[using-shell-options-basics-annotation]]
==== Annotation
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
`Option` annotation can be used to define an option name if you
don't want it to be same as argument name.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-with-option-annotation]
----
====

View File

@@ -1,33 +0,0 @@
[[using-shell-options-basics-legacyannotation]]
==== Legacy Annotation
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Having a target method with argument is automatically registered with a matching
argument name.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-without-annotation]
----
====
`@ShellOption` annotation can be used to define an option name if you
don't want it to be same as argument name.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-with-annotation]
----
====
If option name is defined without prefix, either `-` or `--`, it is discovered
from _ShellMethod#prefix_.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-with-annotation-without-prefix]
----
====

View File

@@ -1,19 +0,0 @@
[[using-shell-options-basics-registration]]
[[using-shell-options-basics-programmatic]]
==== Programmatic
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Programmatic way with `CommandRegistration` is to use `withOption` to define
an option.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-longarg]
----
====
`CommandRegistration` can be defined as a bean or manually registered
with a `CommandCatalog`.
NOTE: Check below sections for other option types, i.e. short format.

View File

@@ -1,13 +0,0 @@
[[using-shell-options-basics]]
=== Basics
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
This section gives a generic idea how an option can be defined. Following
sections, beyond basics, discuss more about how various option behaviour
can be accomplished for a particular use case.
include::using-shell-options-basics-programmatic.adoc[]
include::using-shell-options-basics-annotation.adoc[]
include::using-shell-options-basics-legacyannotation.adoc[]

View File

@@ -1,26 +0,0 @@
[[using-shell-options-default]]
=== Default Value
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Having a default value for an option is somewhat related to
<<using-shell-options-optional>>, as there are cases where you
may want to know if the user defined an option and change behavior
based on a default value:
[source,java,indent=0,role="primary"]
.Programmatic
----
include::{snippets}/OptionSnippets.java[tag=option-default-programmatic]
----
[source,java,indent=0,role="secondary"]
.Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-default-annotation]
----
[source,java,indent=0,role="secondary"]
.Legacy Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-default-legacyannotation]
----

View File

@@ -1,359 +0,0 @@
==== Invoking your Commands
This section addresses how you can control the way in which your commands are invoked.
===== By Name Versus Positional Parameters
As seen <<documenting-the-command,earlier>>, decorating a method with `@ShellMethod` is the sole requirement for creating a command.
The user can set the value of all the method parameters in either of two ways:
* By using a parameter key (for example, `--arg value`). This approach is called "`by name parameters.`"
* Without a key, by setting parameter values in the order in which they appear in the method signature (called "`positional parameters`").
These two approaches can be mixed and matched, with named parameters always taking precedence (as they are less
prone to ambiguity). Consider the following command definition:
====
[source, java]
----
@ShellMethod("Display stuff.")
public String echo(int a, int b, int c) {
return String.format("You said a=%d, b=%d, c=%d", a, b, c);
}
----
====
Given the preceding command definiton, the following invocations are all equivalent, as shown in the output:
====
[source, bash]
----
shell:>echo 1 2 3 <1>
You said a=1, b=2, c=3
shell:>echo --a 1 --b 2 --c 3 <2>
You said a=1, b=2, c=3
shell:>echo --b 2 --c 3 --a 1 <3>
You said a=1, b=2, c=3
shell:>echo --a 1 2 3 <4>
You said a=1, b=2, c=3
shell:>echo 1 --c 3 2 <5>
You said a=1, b=2, c=3
----
<1> This uses positional parameters.
<2> This is an example of full by-name parameters.
<3> By-name parameters can be reordered as desired.
<4> You can use a mix of the two approaches.
<5> The non by-name parameters are resolved in the order in which they appear.
====
====== Customizing the Named Parameter Keys
As seen <<your-first-command,earlier>>, the default strategy for deriving the key for a named parameter is to use the Java
name of the method signature and prefix it with two dashes (`--`). You can customize this in two ways:
* Use the `prefix()` attribute of the `@ShellMethod` annotation to change the default prefix for the whole method.
* Annotate the parameter with the `@ShellOption` annotation to override the entire key in a per-parameter fashion.
Consider the following example:
====
[source, java]
----
@ShellMethod(value = "Display stuff.", prefix="-")
public String echo(int a, int b, @ShellOption("--third") int c) {
return String.format("You said a=%d, b=%d, c=%d", a, b, c);
}
----
====
For such a setup, the possible parameter keys are `-a`, `-b` and `--third`.
[TIP]
=====
You can specify several keys for a single parameter. If you do so, these keys are mutually exclusive (only one of them can be used) ways
to specify the same parameter. The following example shows the signature of the
built-in <<built-in-commands-help,`help`>> command:
====
[source, java]
----
@ShellMethod("Describe a command.")
public String help(@ShellOption({"-C", "--command"}) String command) {
...
}
----
====
=====
[[optional-parameters-default-values]]
===== Optional Parameters and Default Values
Spring Shell provides the ability to give parameters default values, which lets users omit
those parameters. Consider the following command definition:
====
[source, java]
----
@ShellMethod("Say hello.")
public String greet(@ShellOption(defaultValue="World") String who) {
return "Hello " + who;
}
----
====
With the preceding definition, the `greet` command can still be invoked as `greet Mother` (or `greet --who Mother`), but the following
is also possible:
====
[source]
----
shell:>greet
Hello World
----
====
===== Parameter Arity
Up to now, it has always been assumed that each parameter maps to a single word entered by the user.
Situations may arise, though, when a parameter value should be multi-valued. This is driven by the `arity()`
attribute of the `@ShellOption` annotation. You can use a collection or array for the parameter type and specify how
many values are expected:
====
[source, java]
----
@ShellMethod("Add Numbers.")
public float add(@ShellOption(arity=3) float[] numbers) {
return numbers[0] + numbers[1] + numbers[2];
}
----
====
The users can then invoke the command by using any of the following syntax:
====
[source]
----
shell:>add 1 2 3.3
6.3
shell:>add --numbers 1 2 3.3
6.3
----
====
[WARNING]
=====
When using the _by-name_ parameter approach, the key should *not* be repeated. The following does *not* work:
====
[source]
----
shell:>add --numbers 1 --numbers 2 --numbers 3.3
----
====
=====
====== Varying Amount Arity
The above example demonstrates requiring a known, constant arity for a parameter, three in this case. Allowing any number of multiple values of a parameter can be achieved by leaving `arity` unspecified and using Spring's built-in comma separated value parsing for collections and/or arrays:
[source, java]
----
@ShellMethod("Add a Varying Amount of Numbers.")
public double add(@ShellOption double[] numbers) {
return Arrays.stream(numbers).sum();
}
----
The command may then be invoked with any amount of `numbers`:
====
[source]
----
shell:>add 1,2,3.3
6.3
shell:>add --numbers 42
42.0
shell:>add --numbers 1,2,3.3,4,5
15.3
----
====
====== Special Handling of Boolean Parameters
When it comes to parameter arity, one kind of parameter receives a special treatment by default, as
is often the case in command-line utilities.
Boolean (that is, `boolean` as well as `java.lang.Boolean`) parameters behave like they have an `arity()` of `0` by default, allowing users to set their values by using a "`flag`" approach.
Consider the following command definition:
====
[source, java]
----
@ShellMethod("Terminate the system.")
public String shutdown(boolean force) {
return "You said " + force;
}
----
====
This preceding command definition allows the following invocations:
====
[source]
----
shell:>shutdown
You said false
shell:>shutdown --force
You said true
----
====
TIP: This special treatment plays well with the <<optional-parameters-default-values,default value>> specification. Although the default
for boolean parameters is to have their default value be `false`, you can specify otherwise (that is,
`@ShellOption(defaultValue="true")`), and the behavior is inverted (that is, not specifying the parameter
results in the value being `true`, and specifying the flag results in the value being `false`)
[WARNING]
=====
Having this behavior of implicit `arity()=0` prevents the user from specifying a value (for example, `shutdown --force true`).
If you would like to allow this behavior (and forego the flag approach), then force an arity of `1` by using the annotation as follows:
====
[source, java]
----
@ShellMethod("Terminate the system.")
public String shutdown(@ShellOption(arity=1, defaultValue="false") boolean force) {
return "You said " + force;
}
----
====
=====
[[quotes-handling]]
===== Quotes Handling
Spring Shell takes user input and tokenizes it into words, splitting on space characters.
If the user wants to provide a parameter value that contains spaces, that value needs to be quoted.
Both single (`'`) and double (`"`) quotes are supported, and those quotes are not part of the value:
Consider the following command definition:
====
[source, java]
----
@ShellMethod("Prints what has been entered.")
public String echo(String what) {
return "You said " + what;
}
----
====
The following commands all invoke the preceding command definition:
====
[source]
----
shell:>echo Hello
You said Hello
shell:>echo 'Hello'
You said Hello
shell:>echo 'Hello World'
You said Hello World
shell:>echo "Hello World"
You said Hello World
----
====
Supporting both single and double quotes lets the user embed one type of quotes into
a value:
====
[source]
----
shell:>echo "I'm here!"
You said I'm here!
shell:>echo 'He said "Hi!"'
You said He said "Hi!"
----
====
That way, the user can use a single quote as an apostrophe in a message.
Should the user need to embed the same kind of quote that was used to quote the whole parameter,
the escape sequence uses the backslash (`\`) character:
====
[source]
----
shell:>echo 'I\'m here!'
You said I'm here!
shell:>echo "He said \"Hi!\""
You said He said "Hi!"
shell:>echo I\'m here!
You said I'm here!
----
====
It is also possible to escape space characters when not using enclosing quotes:
====
[source]
----
shell:>echo This\ is\ a\ single\ value
You said This is a single value
----
====
[[interacting-with-the-shell]]
===== Interacting with the Shell
The Spring Shell project builds on top of the https://github.com/jline/jline3[JLine] library and, as a result, brings
a lot of nice interactive features, some of which are detailed in this section.
First and foremost, Spring Shell supports tab completion almost everywhere possible. So, if there
is an `echo` command and the user types `ec` and presses `TAB`, `echo` appears.
Should there be several commands that start with `ec`, then the user is prompted to choose (using `TAB` or
`Shift + TAB` to navigate and `ENTER` to select.)
But completion does not stop at command keys. It also works for parameter keys (`--arg`) and even
parameter values, if the application developer registered the appropriate beans (see <<providing-tab-completion>>).
Another nice feature of Spring Shell applications is support for line continuation. If a command and its parameters
is too long and does not fit nicely on the screen, a user can chunk it by ending a line with a backslash
(`\`) character, pressing `ENTER`, and continuing on the next line. Upon submission of the whole command, this is
parsed as if the user entered a single space on line breaks. The following listing shows an example of this behavior:
====
[source]
----
shell:>register module --type source --name foo \ <1>
> --uri file:///tmp/bar
Successfully registered module 'source:foo'
----
<1> command continues on next line
====
Line continuation also automatically triggers if the user has opened a quote (see <<quotes-handling>>)
and presses `ENTER` while still in the quotes:
====
[source]
----
shell:>echo "Hello <1>
dquote> World"
You said Hello World
----
<1> The user pressed `ENTER` here.
====
Finally, Spring Shell applications benefit from a lot of keyboard shortcuts (borrowed from Emacs) with which you may
already be familiar from working with your regular OS Shell. Notable shortcuts include `Ctrl+r` to perform
a reverse search, `Ctrl+a`] and `Ctrl+e` to move to the beginning and the end of the current line (respectively),
and `Esc f` and `Esc b` to move forward or backward (respectively) one word at a time.
[[providing-tab-completion]]
// ===== Providing TAB Completion Proposals
// TBD

View File

@@ -1,40 +0,0 @@
[[using-shell-options-label]]
=== Label
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
_Option Label_ has no functional behaviour within a shell itself other than
what a default `help` command outputs. Within a command documentation
a type of an option is documented but this is not always super useful. Thus
you may want to give better descriptive word for an option.
NOTE: Label is not supported with `legacy annotation`.
[source,java,indent=0,role="primary"]
.Programmatic
----
include::{snippets}/OptionSnippets.java[tag=option-label-programmatic]
----
[source,java,indent=0,role="secondary"]
.Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-label-annotation]
----
Defining label is then shown in `help`.
====
[source, bash]
----
my-shell:>help labelOption
NAME
labelOption -
SYNOPSIS
labelOption --arg MYLABEL
OPTIONS
--arg MYLABEL
[Optional]
----
====

View File

@@ -1,113 +0,0 @@
[[using-shell-options-naming]]
=== Naming
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
If there is a need to modify option long names that can be done
using `OptionNameModifier` interface which is a simple
`Function<String, String>`. In this interface original option
name goes in and modified name comes out.
Modifier can be defined per `OptionSpec` in `CommandRegistration`,
defaulting globally as bean or via configuration properties.
Modifier defined manually in `OptionSpec` takes takes precedence
over one defined globally. There is no global modifier defined
on default.
You can define one with an option in `CommandRegistration`.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-naming-case-req]
----
====
Add one _singleton bean_ as type `OptionNameModifier` and that becomes
a global default.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-naming-case-bean]
----
====
It's also possible to just add configuration property with
`spring.shell.option.naming.case-type` which auto-configures
one based on a type defined.
`noop` is to do nothing, `camel`, `snake`, `kebab`, `pascal`
activates build-in modifiers for `camelCase`, `snake_case`,
`kebab-case` or `PascalCase` respectively.
NOTE: If creating `CommandRegistration` beans directly, global
default via configuration properies only work if using
pre-configured `Builder` instance. See more
<<using-shell-commands-programmaticmodel>>.
====
[source, yaml]
----
spring:
shell:
option:
naming:
case-type: noop
# case-type: camel
# case-type: snake
# case-type: kebab
# case-type: pascal
----
====
For example options defined in an annotated method like this.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-naming-case-sample1]
----
====
On default `help` for that command shows names coming
directly from `@ShellOption`.
====
[source, bash]
----
OPTIONS
--from_snake String
[Mandatory]
--fromCamel String
[Mandatory]
--from-kebab String
[Mandatory]
--FromPascal String
[Mandatory]
----
====
Define `spring.shell.option.naming.case-type=kebab` and default
modifier is added and option names then look like.
====
[source, bash]
----
OPTIONS
--from-snake String
[Mandatory]
--from-camel String
[Mandatory]
--from-kebab String
[Mandatory]
--from-pascal String
[Mandatory]
----
====

View File

@@ -1,46 +0,0 @@
[[using-shell-options-optional]]
=== Optional Value
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
An option is either required or not and, generally speaking, how it behaves depends on
a command target.
Making option optional.
[source,java,indent=0,role="primary"]
.Programmatic
----
include::{snippets}/OptionSnippets.java[tag=option-optional-programmatic]
----
[source,java,indent=0,role="secondary"]
.Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-optional-annotation]
----
[source,java,indent=0,role="secondary"]
.Legacy Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-optional-legacyannotation]
----
Making option mandatory.
[source,java,indent=0,role="primary"]
.Programmatic
----
include::{snippets}/OptionSnippets.java[tag=option-mandatory-programmatic]
----
[source,java,indent=0,role="secondary"]
.Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-mandatory-annotation]
----
[source,java,indent=0,role="secondary"]
.Legacy Annotation
----
include::{snippets}/OptionSnippets.java[tag=option-mandatory-legacyannotation]
----

View File

@@ -1,70 +0,0 @@
[[using-shell-options-positional]]
=== Positional
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Positional information is mostly related to a command target method:
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-positional]
----
====
NOTE: Be careful with positional parameters as it may soon
become confusing which options those are mapped to.
Usually arguments are mapped to an option when those are defined in a
command line whether it's a long or short option. Generally speaking
there are _options_, _option arguments_ and _arguments_ where latter
are the ones which are not mapped to any spesific option.
Unrecognised arguments can then have a secondary mapping logic where
positional information is important. With option position you're
essentially telling command parsing how to interpret plain raw
ambiguous arguments.
Let's look what happens when we don't define a position.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-aritystrings-noposition]
----
====
Option _arg1_ is required and there is no info what to do with argument
`one` resulting error for missing option.
====
[source, bash]
----
shell:>arity-strings-1 one
Missing mandatory option --arg1.
----
====
Now let's define a position `0`.
====
[source, java, indent=0]
----
include::{snippets}/OptionSnippets.java[tag=option-registration-aritystrings-position]
----
====
Arguments are processed until we get up to 2 arguments.
====
[source, bash]
----
shell:>arity-strings-2 one
Hello [one]
shell:>arity-strings-2 one two
Hello [one, two]
shell:>arity-strings-2 one two three
Hello [one, two]
----
====

View File

@@ -1,46 +0,0 @@
[[using-shell-options-short]]
=== Short Format
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Short style _POSIX_ option is usually just a synonym to long format. As
shown below option `--arg` is equal to `-a`.
[source,java,indent=0,role="primary"]
.Programmatic
----
include::{snippets}/ShortOptionSnippets.java[tag=option-type-string-programmatic]
----
[source,java,indent=0,role="secondary"]
.Annotation
----
include::{snippets}/ShortOptionSnippets.java[tag=option-type-string-annotation]
----
[source,java,indent=0,role="secondary"]
.Legacy Annotation
----
include::{snippets}/ShortOptionSnippets.java[tag=option-type-string-legacyannotation]
----
Short option with combined format is powerful if type is defined as a flag
which means type is a _boolean_. That way you can define a presence of a flags
as `-abc`, `-abc true` or `-abc false`.
[source,java,indent=0,role="primary"]
.Programmatic
----
include::{snippets}/ShortOptionSnippets.java[tag=option-type-multiple-booleans-programmatic]
----
[source,java,indent=0,role="secondary"]
.Annotation
----
include::{snippets}/ShortOptionSnippets.java[tag=option-type-multiple-booleans-annotation]
----
[source,java,indent=0,role="secondary"]
.Legacy Annotation
----
include::{snippets}/ShortOptionSnippets.java[tag=option-type-multiple-booleans-legacyannotation]
----

View File

@@ -1,137 +0,0 @@
[[using-shell-options-types]]
=== Types
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
This section talks about how particular data type is used as an option value.
==== String
`String` is a most simplest type as there's no conversion involved as what's
coming in from a user is always a string.
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-string-anno]
----
====
While it's not strictly required to define type as a `String` it's always
adviced to do so.
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-string-reg]
----
====
==== Boolean
Using boolean types is a bit more involved as there are `boolean` and
`Boolean` where latter can be _null_. Boolean types are usually used as
flags meaning argument value may not be needed.
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-boolean-anno]
----
====
====
[source, bash]
----
shell:>example
arg1=false arg2=true arg3=false arg4=false arg5=true arg6=false
shell:>example --arg4
arg1=false arg2=true arg3=false arg4=true arg5=true arg6=false
shell:>example --arg4 false
arg1=false arg2=true arg3=false arg4=false arg5=true arg6=false
----
====
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-boolean-reg]
----
====
====
[source, bash]
----
shell:>example
arg1=false arg2=true arg3=false arg4=null arg5=true arg6=false
shell:>example --arg4
arg1=false arg2=true arg3=false arg4=true arg5=true arg6=false
shell:>example --arg4 false
arg1=false arg2=true arg3=false arg4=false arg5=true arg6=false
----
====
==== Number
Numbers are converted as is.
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-integer-anno]
----
====
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-integer-reg]
----
====
==== Enum
Conversion to enums is possible if given value is exactly matching enum itself.
Currently you can convert assuming case insensitivity.
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-enum-class]
----
====
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-enum-anno]
----
====
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-enum-reg]
----
====
==== Array
Arrays can be used as is with strings and primitive types.
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-string-array-anno]
----
====
====
[source, java, indent=0]
----
include::{snippets}/OptionTypesSnippets.java[tag=option-type-string-array-reg]
----
====

View File

@@ -1,28 +0,0 @@
[[validating-command-arguments]]
=== Validation
Spring Shell integrates with the https://beanvalidation.org/[Bean Validation API] to support
automatic and self-documenting constraints on command parameters.
Annotations found on command parameters and annotations at the method level are
honored and trigger validation prior to the command executing. Consider the following command:
====
[source, java]
----
@ShellMethod("Change password.")
public String changePassword(@Size(min = 8, max = 40) String password) {
return "Password successfully set to " + password;
}
----
====
From the preceding example, you get the following behavior for free:
====
----
shell:>change-password hello
The following constraints were not met:
--password string : size must be between 8 and 40 (You passed 'hello')
----
====

View File

@@ -1,38 +0,0 @@
[[using-shell-options]]
== Options
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Command line arguments can be separated into options and positional parameters.
Following sections describes features how options are defined and used. We first
go through some basics about using options and then go into details about
various ways how options and arguments works.
Generally speaking an _option_ is something after a commands prefixed with
either `-` or `--`. An _option_ can either have a value or not depending
on its context.
Options can be defined with a target method using annotations with a method
arguments or with programmatically using `CommandRegistration`.
NOTE: In below sections `@ShellOption` refer to a _legacy annotation model_
and `@Option` refer to an _annotation model_.
include::using-shell-options-basics.adoc[]
include::using-shell-options-short.adoc[]
include::using-shell-options-arity.adoc[]
include::using-shell-options-positional.adoc[]
include::using-shell-options-optional.adoc[]
include::using-shell-options-default.adoc[]
include::using-shell-options-validation.adoc[]
include::using-shell-options-label.adoc[]
include::using-shell-options-types.adoc[]
include::using-shell-options-naming.adoc[]

View File

@@ -1,25 +0,0 @@
[[using-shell-testing-basics]]
=== Basics
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Spring Shell provides a number of utilities and annotations to help when testing your application.
Test support is provided by two modules: `spring-shell-test` contains core items, and
`spring-shell-test-autoconfigure` supports auto-configuration for tests.
To test _interactive_ commands.
====
[source, java, indent=0]
----
include::{snippets}/TestingSnippets.java[tag=testing-shelltest-interactive]
----
====
To test _non-interactive_ commands.
====
[source, java, indent=0]
----
include::{snippets}/TestingSnippets.java[tag=testing-shelltest-noninteractive]
----
====

View File

@@ -1,27 +0,0 @@
[[using-shell-testing-settings]]
=== Settings
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Built in emulation uses terminal width 80 and height 24 on default.
Changing dimensions is useful if output would span into multiple
lines and you don't want to handle those cases in a tests.
These can be changed using properties `spring.shell.test.terminal-width`
or `spring.shell.test.terminal-height`.
====
[source, java, indent=0]
----
include::{snippets}/TestingSnippets.java[tag=testing-shelltest-dimensions-props]
----
====
`ShellTest` annotation have fields `terminalWidth` and `terminalHeight`
which can also be used to change dimensions.
====
[source, java, indent=0]
----
include::{snippets}/TestingSnippets.java[tag=testing-shelltest-dimensions-field]
----
====

View File

@@ -1,20 +0,0 @@
[[using-shell-testing]]
== Testing
ifndef::snippets[:snippets: ../../test/java/org/springframework/shell/docs]
Testing cli application is difficult due to various reasons:
- There are differences between OS's.
- Within OS there may be different shell implementations in use.
- What goes into a shell and comes out from a shell my be totally
different what you see in shell itself due to control characters.
- Shell may feel syncronous but most likely it is not meaning when
someting is written into it, you can't assume next update in
in it is not final.
NOTE: Testing support is currently under development and will be
unstable for various parts.
include::using-shell-testing-basics.adoc[]
include::using-shell-testing-settings.adoc[]

View File

@@ -1,17 +0,0 @@
include::using-shell-basics.adoc[]
include::using-shell-commands.adoc[]
include::using-shell-options.adoc[]
include::using-shell-completion.adoc[]
include::using-shell-building.adoc[]
include::using-shell-components.adoc[]
include::using-shell-customization.adoc[]
include::using-shell-execution.adoc[]
include::using-shell-testing.adoc[]