Skip to content

Commit d795dcd

Browse files
committed
Added docs for sub-commands
1 parent 1e809a2 commit d795dcd

9 files changed

Lines changed: 898 additions & 9 deletions

File tree

content/docs/aesh/_index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ That's it! Æsh automatically:
5858
### Command Hierarchies
5959
- **Group commands** - Create command trees like `git remote add` or `docker container start`
6060
- **Subcommands** - Unlimited nesting depth for complex CLIs
61+
- **Sub-command mode** - Interactive contexts where subcommands share parent options
62+
- **Inherited options** - Parent options automatically available to subcommands
6163
- **Dynamic registration** - Add and remove commands at runtime
6264

6365
### User Experience

content/docs/aesh/arguments.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The `@Argument` annotation defines positional command-line arguments.
1616
| `defaultValue` | `String[]` | `{}` | Default values |
1717
| `askIfNotSet` | `boolean` | `false` | Prompt user if not set |
1818
| `overrideRequired` | `boolean` | `false` | Override required validation |
19+
| `inherited` | `boolean` | `false` | Make argument available to subcommands in sub-command mode |
1920
| `converter` | `Class<? extends Converter>` | `NullConverter.class` | Custom value converter |
2021
| `completer` | `Class<? extends OptionCompleter>` | `NullOptionCompleter.class` | Custom completer |
2122
| `validator` | `Class<? extends OptionValidator>` | `NullValidator.class` | Custom validator |
@@ -104,3 +105,52 @@ public class CopyCommand implements Command<CommandInvocation> {
104105
```
105106

106107
Usage: `copy myfile.txt -d /tmp -v`
108+
109+
## Inherited Arguments
110+
111+
Inherited arguments are automatically available to all subcommands when using [sub-command mode](/docs/aesh/sub-command-mode). Mark an argument with `inherited = true` on a group command, and subcommands with matching field names will have the value auto-populated.
112+
113+
```java
114+
@GroupCommandDefinition(
115+
name = "project",
116+
groupCommands = {BuildCommand.class, TestCommand.class}
117+
)
118+
public class ProjectCommand implements Command<CommandInvocation> {
119+
120+
@Argument(description = "Project name", inherited = true)
121+
private String projectName;
122+
123+
@Override
124+
public CommandResult execute(CommandInvocation invocation) {
125+
invocation.enterSubCommandMode(this);
126+
return CommandResult.SUCCESS;
127+
}
128+
}
129+
130+
@CommandDefinition(name = "build")
131+
public class BuildCommand implements Command<CommandInvocation> {
132+
133+
// This field is auto-populated from parent's inherited argument
134+
@Argument(description = "Project name")
135+
private String projectName;
136+
137+
@Override
138+
public CommandResult execute(CommandInvocation invocation) {
139+
invocation.println("Building " + projectName);
140+
return CommandResult.SUCCESS;
141+
}
142+
}
143+
```
144+
145+
You can also access inherited values programmatically:
146+
147+
```java
148+
@Override
149+
public CommandResult execute(CommandInvocation invocation) {
150+
String name = invocation.getInheritedValue("projectName", String.class);
151+
invocation.println("Project: " + name);
152+
return CommandResult.SUCCESS;
153+
}
154+
```
155+
156+
See [Sub-Command Mode](/docs/aesh/sub-command-mode) for complete documentation on inherited options and arguments.

content/docs/aesh/command-invocation.md

Lines changed: 249 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,29 +28,39 @@ public class ExampleCommand implements Command<CommandInvocation> {
2828

2929
```java
3030
public interface CommandInvocation {
31-
31+
3232
// Output methods
3333
void print(String message);
3434
void println(String message);
35-
35+
3636
// Shell access
3737
Shell getShell();
38-
38+
3939
// Control methods
4040
void stop();
41-
41+
4242
// Help system
4343
String getHelpInfo();
4444
String getHelpInfo(String commandName);
45-
45+
4646
// Configuration access
4747
CommandInvocationConfiguration getConfiguration();
48-
48+
4949
// Operator support
5050
Operator getOperator();
51-
51+
5252
// Piped input
5353
String getInputLine() throws InterruptedException;
54+
55+
// Sub-command mode
56+
boolean enterSubCommandMode(Command<?> command);
57+
void exitSubCommandMode();
58+
boolean isInSubCommandMode();
59+
CommandContext getCommandContext();
60+
<T> T getParentValue(String name, Class<T> type);
61+
<T> T getParentValue(String name, Class<T> type, T defaultValue);
62+
<T> T getInheritedValue(String name, Class<T> type);
63+
<T> T getInheritedValue(String name, Class<T> type, T defaultValue);
5464
}
5565
```
5666

@@ -324,6 +334,238 @@ public class UppercaseCommand implements Command<CommandInvocation> {
324334

325335
**Output:** `HELLO WORLD`
326336

337+
## Sub-Command Mode Methods
338+
339+
These methods support [Sub-Command Mode](/docs/aesh/sub-command-mode), allowing group commands to create interactive contexts where subcommands can access parent values.
340+
341+
### enterSubCommandMode(Command)
342+
343+
Enters sub-command mode for the current group command. Returns `true` if successful, `false` if sub-command mode is disabled.
344+
345+
```java
346+
@GroupCommandDefinition(name = "project", groupCommands = {BuildCommand.class, TestCommand.class})
347+
public class ProjectCommand implements Command<CommandInvocation> {
348+
349+
@Option(name = "name", required = true)
350+
private String projectName;
351+
352+
@Override
353+
public CommandResult execute(CommandInvocation invocation) {
354+
invocation.println("Project: " + projectName);
355+
356+
// Enter sub-command mode - prompt changes to "project[myapp]>"
357+
if (invocation.enterSubCommandMode(this)) {
358+
invocation.println("Available commands: build, test");
359+
}
360+
361+
return CommandResult.SUCCESS;
362+
}
363+
}
364+
```
365+
366+
### exitSubCommandMode()
367+
368+
Exits the current sub-command mode level. If nested, returns to the parent context. If at the top level, returns to the main prompt.
369+
370+
```java
371+
@CommandDefinition(name = "done", description = "Exit project mode")
372+
public class DoneCommand implements Command<CommandInvocation> {
373+
374+
@Override
375+
public CommandResult execute(CommandInvocation invocation) {
376+
invocation.exitSubCommandMode();
377+
return CommandResult.SUCCESS;
378+
}
379+
}
380+
```
381+
382+
### isInSubCommandMode()
383+
384+
Returns `true` if currently in sub-command mode.
385+
386+
```java
387+
@Override
388+
public CommandResult execute(CommandInvocation invocation) {
389+
if (invocation.isInSubCommandMode()) {
390+
invocation.println("Currently in sub-command mode");
391+
} else {
392+
invocation.println("Running at top level");
393+
}
394+
return CommandResult.SUCCESS;
395+
}
396+
```
397+
398+
### getParentValue(String name, Class&lt;T&gt; type)
399+
400+
Retrieves a value from the parent command by field name. Returns `null` if not found or not in sub-command mode.
401+
402+
```java
403+
@CommandDefinition(name = "build", description = "Build the project")
404+
public class BuildCommand implements Command<CommandInvocation> {
405+
406+
@Override
407+
public CommandResult execute(CommandInvocation invocation) {
408+
// Get parent's projectName field value
409+
String projectName = invocation.getParentValue("projectName", String.class);
410+
411+
if (projectName != null) {
412+
invocation.println("Building " + projectName);
413+
} else {
414+
invocation.println("Error: No project name available");
415+
}
416+
417+
return CommandResult.SUCCESS;
418+
}
419+
}
420+
```
421+
422+
### getParentValue(String name, Class&lt;T&gt; type, T defaultValue)
423+
424+
Same as above, but returns a default value if the parent value is not found.
425+
426+
```java
427+
@Override
428+
public CommandResult execute(CommandInvocation invocation) {
429+
// Get parent value with default
430+
Boolean verbose = invocation.getParentValue("verbose", Boolean.class, false);
431+
432+
if (verbose) {
433+
invocation.println("[VERBOSE] Starting build process...");
434+
}
435+
436+
return CommandResult.SUCCESS;
437+
}
438+
```
439+
440+
### getInheritedValue(String name, Class&lt;T&gt; type)
441+
442+
Retrieves a value from inherited options (options marked with `inherited = true` on the parent). Returns `null` if not found.
443+
444+
```java
445+
@Override
446+
public CommandResult execute(CommandInvocation invocation) {
447+
// Get inherited option value
448+
Boolean verbose = invocation.getInheritedValue("verbose", Boolean.class);
449+
450+
if (verbose != null && verbose) {
451+
invocation.println("[VERBOSE] Detailed output enabled");
452+
}
453+
454+
return CommandResult.SUCCESS;
455+
}
456+
```
457+
458+
### getInheritedValue(String name, Class&lt;T&gt; type, T defaultValue)
459+
460+
Same as above, but returns a default value if the inherited value is not found.
461+
462+
```java
463+
@Override
464+
public CommandResult execute(CommandInvocation invocation) {
465+
// Get inherited value with default
466+
String logLevel = invocation.getInheritedValue("logLevel", String.class, "INFO");
467+
invocation.println("Log level: " + logLevel);
468+
469+
return CommandResult.SUCCESS;
470+
}
471+
```
472+
473+
### getCommandContext()
474+
475+
Returns the `CommandContext` object for advanced context manipulation. Most use cases are covered by the methods above.
476+
477+
```java
478+
@Override
479+
public CommandResult execute(CommandInvocation invocation) {
480+
CommandContext context = invocation.getCommandContext();
481+
482+
// Check context depth (nesting level)
483+
int depth = context.depth();
484+
invocation.println("Context depth: " + depth);
485+
486+
// Get the context path (e.g., "module:project")
487+
String path = context.getContextPath();
488+
invocation.println("Context path: " + path);
489+
490+
return CommandResult.SUCCESS;
491+
}
492+
```
493+
494+
### Sub-Command Mode Example
495+
496+
Here's a complete example showing how subcommands access parent values:
497+
498+
```java
499+
@GroupCommandDefinition(
500+
name = "project",
501+
description = "Project management",
502+
groupCommands = {BuildCommand.class, StatusCommand.class}
503+
)
504+
public class ProjectCommand implements Command<CommandInvocation> {
505+
506+
@Option(name = "name", required = true)
507+
private String projectName;
508+
509+
@Option(name = "verbose", hasValue = false, inherited = true)
510+
private boolean verbose;
511+
512+
public String getProjectName() { return projectName; }
513+
public boolean isVerbose() { return verbose; }
514+
515+
@Override
516+
public CommandResult execute(CommandInvocation invocation) {
517+
invocation.println("Project: " + projectName);
518+
invocation.enterSubCommandMode(this);
519+
return CommandResult.SUCCESS;
520+
}
521+
}
522+
523+
@CommandDefinition(name = "build", description = "Build the project")
524+
public class BuildCommand implements Command<CommandInvocation> {
525+
526+
@ParentCommand
527+
private ProjectCommand parent; // Direct injection
528+
529+
@Option(name = "verbose", hasValue = false)
530+
private boolean verbose; // Auto-populated from parent's inherited option
531+
532+
@Override
533+
public CommandResult execute(CommandInvocation invocation) {
534+
// Method 1: Use @ParentCommand annotation
535+
String name = parent.getProjectName();
536+
537+
// Method 2: Use getParentValue()
538+
String name2 = invocation.getParentValue("projectName", String.class);
539+
540+
// Method 3: Check inherited values
541+
Boolean inheritedVerbose = invocation.getInheritedValue("verbose", Boolean.class);
542+
543+
invocation.println("Building " + name);
544+
if (verbose) {
545+
invocation.println("[VERBOSE] Compiling sources...");
546+
}
547+
548+
return CommandResult.SUCCESS;
549+
}
550+
}
551+
```
552+
553+
**Usage:**
554+
```
555+
[myapp]$ project --name=myapp --verbose
556+
Project: myapp
557+
Entering project mode.
558+
559+
project[myapp]> build
560+
Building myapp
561+
[VERBOSE] Compiling sources...
562+
563+
project[myapp]> exit
564+
[myapp]$
565+
```
566+
567+
See [Sub-Command Mode](/docs/aesh/sub-command-mode) for complete documentation.
568+
327569
## Complete Example
328570

329571
```java

0 commit comments

Comments
 (0)