Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dotnet-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x

- name: Build
run: dotnet build -c release
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/nuget-tag-publish.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: NuGet Package

on:
on:
push:
tags:
- '*'
Expand All @@ -18,12 +18,12 @@ jobs:
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x

- name: Install dotnet tool
run: dotnet tool install -g dotnetCampus.TagToVersion

- name: Set tag to version
- name: Set tag to version
run: dotnet TagToVersion -t ${{ github.ref }}

- name: Build with dotnet
Expand Down
3 changes: 2 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<PackageVersion Include="DotNetCampus.CodeAnalysisUtils" Version="0.0.1-alpha.3" />
<PackageVersion Include="DotNetCampus.CommandLine.Temp40" Version="4.0.1-benchmark.1" />
<PackageVersion Include="DotNetCampus.LatestCSharpFeatures" Version="13.0.1" />
<PackageVersion Include="dotnetCampus.SourceLocalizations" Version="0.1.1-alpha.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0" />
Expand All @@ -17,4 +18,4 @@
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
<PackageVersion Include="System.Memory" Version="4.5.5" />
</ItemGroup>
</Project>
</Project>
115 changes: 115 additions & 0 deletions docs/en/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,121 @@ commandLine
3. If multiple handlers match the same command, `CommandNameAmbiguityException` is thrown.
4. If any handler is asynchronous, you must use `RunAsync` instead of `Run` (otherwise compilation fails).

## Help Information

DotNetCampus.CommandLine has a built-in help message generation mechanism. When users pass help flags such as `--help`, `-h`, or `/?`, the program automatically outputs help information and exits.

### Enabling Help

Call `AddHelpHandler()` to enable help (it is recommended to place it at the end of the handler chain for consistency, but it can actually be placed at any position):

```csharp
await CommandLine.Parse(args)
.AddHandler<DefaultOptions>(o => o.Run())
.AddHandler<ConvertHandler>()
.AddHandler<EditHandler>()
.AddHelpHandler()
.RunAsync();
```

### Adding Descriptions to Options and Commands

Use the `Description` property to add description text to commands, options, and positional arguments:

```csharp
[Command("convert", Description = "Convert input values and demonstrate type parsing.")]
internal class ConvertHandler : ICommandHandler
{
[Value(0, Description = "The input file to convert.")]
public required string InputFile { get; init; }

[Option('f', "format", Description = "Output format.")]
public OutputFormat Format { get; init; } = OutputFormat.Text;

[Option('n', "count", Description = "Maximum number of records to convert.")]
public int? Count { get; init; }

public Task<int> RunAsync() { /* ... */ }
}
```

`OptionAttribute` also has a `ValueName` property for customizing the value placeholder displayed in help:

```csharp
[Option(Description = "Pass any directory into this option.", ValueName = "directory_path")]
public string? DefaultDirectory { get; set; }
```

This displays as `--default-directory <directory_path>` in help output instead of the default `--default-directory <value>`.

When `ValueName` is not set, the value placeholder is automatically generated based on the option type:

| Option Type | Default Placeholder |
| ----------------- | ------------------- |
| Boolean option | (none) |
| Regular option | `<value>` |
| Collection option | `<value>...` |
| Dictionary option | `<key>=<value>...` |

After setting `ValueName`, the `value` in the placeholder is replaced with your specified name (collections and dictionaries still retain the `...` suffix).

### Automatic Detection of Help Flags

The library automatically detects help flags based on the current command line style:

| Style | Supported Help Flags |
| ----------------- | ---------------------------------------- |
| Flexible (default)| `--help` `-h` `/help` `/h` `/?` `-?` |
| DotNet / Gnu | `--help` `-h` |
| Posix | `-h` |
| Windows | `/help` `/h` `/?` `-help` `-h` `-?` |
| URL | Help detection not supported |

When users pass a subcommand + help flag (e.g., `myapp convert --help`), only the help for that subcommand is displayed; when a help flag is passed directly (e.g., `myapp --help`), global help is displayed, including the list of all subcommands.

After help output completes, the program returns exit code `0` and does not continue executing command handlers.

### Customizing Help Behavior

Use `HelpConfigurations` to customize various aspects of help:

```csharp
.AddHelpHandler(new HelpConfigurations
{
// Maximum width of the option/command name column (default 30); items exceeding this width have their description on the next line
MaxColumnWidth = 40,

// Localization: use the Description value as a key and return localized text
HelpTextLocalizer = key => MyResources.ResourceManager.GetString(key) ?? key,

// Custom output target (defaults to Console.Out)
HelpMessageWriter = text => File.WriteAllText("help.txt", text),

// Fully custom help handler (implement IHelpHandler interface)
HelpHandler = new MyCustomHelpHandler(),
})
```

#### Help Text Localization

If your program needs multilingual support, you can set `Description` to a resource key and use the `HelpTextLocalizer` delegate for translation:

```csharp
// Use a resource key as Description when defining a command
[Command(Description = nameof(LocalizableStrings.SampleCommandDescription))]
internal class DefaultOptions
{
[Option(Description = nameof(LocalizableStrings.SamplePropertyDescription))]
public string? DefaultText { get; set; }
}

// Provide the localization delegate when enabling help
.AddHelpHandler(new HelpConfigurations
{
HelpTextLocalizer = key => LocalizableStrings.ResourceManager.GetString(key) ?? key,
})
```

## URL Protocol Support

DotNetCampus.CommandLine can parse a URL protocol string:
Expand Down
115 changes: 115 additions & 0 deletions docs/zh-hans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,121 @@ commandLine
1. 如果多个命令处理器匹配同一个命令,会抛出 `CommandNameAmbiguityException`。
1. 命令处理器中,有任何一个是异步时,你将必须使用 `RunAsync` 替代 `Run`,否则会编译不通过。

## 帮助信息

DotNetCampus.CommandLine 内置了帮助信息生成机制。当用户传入 `--help`、`-h`、`/?` 等帮助标志时,程序会自动输出帮助信息并退出。

### 启用帮助

调用 `AddHelpHandler()` 即可启用帮助(推荐放在处理器链末尾以保持格式统一,但实际上放在任意位置均可):

```csharp
await CommandLine.Parse(args)
.AddHandler<DefaultOptions>(o => o.Run())
.AddHandler<ConvertHandler>()
.AddHandler<EditHandler>()
.AddHelpHandler()
.RunAsync();
```

### 为选项和命令添加描述

通过 `Description` 属性为命令、选项和位置参数添加描述文本:

```csharp
[Command("convert", Description = "Convert input values and demonstrate type parsing.")]
internal class ConvertHandler : ICommandHandler
{
[Value(0, Description = "The input file to convert.")]
public required string InputFile { get; init; }

[Option('f', "format", Description = "Output format.")]
public OutputFormat Format { get; init; } = OutputFormat.Text;

[Option('n', "count", Description = "Maximum number of records to convert.")]
public int? Count { get; init; }

public Task<int> RunAsync() { /* ... */ }
}
```

`OptionAttribute` 还有一个 `ValueName` 属性,用于在帮助中显示值占位符:

```csharp
[Option(Description = "Pass any directory into this option.", ValueName = "directory_path")]
public string? DefaultDirectory { get; set; }
```

这会在帮助输出中显示为 `--default-directory <directory_path>` 而非默认的 `--default-directory <value>`。

未设置 `ValueName` 时,帮助中的值占位符根据选项类型自动生成:

| 选项类型 | 默认占位符 |
| -------- | ------------------ |
| 布尔选项 | (无) |
| 普通选项 | `<value>` |
| 集合选项 | `<value>...` |
| 字典选项 | `<key>=<value>...` |

设置 `ValueName` 后,占位符中的 `value` 会被替换为你指定的名称(集合和字典仍保留 `...` 后缀)。

### 帮助标志的自动检测

库会根据当前的命令行风格自动检测对应的帮助标志:

| 风格 | 支持的帮助标志 |
| ----------------- | ---------------------------------------- |
| Flexible(默认) | `--help` `-h` `/help` `/h` `/?` `-?` |
| DotNet / Gnu | `--help` `-h` |
| Posix | `-h` |
| Windows | `/help` `/h` `/?` `-help` `-h` `-?` |
| URL | 不支持帮助检测 |

当用户传入子命令 + 帮助标志时(如 `myapp convert --help`),只显示该子命令的帮助;当直接传入帮助标志时(如 `myapp --help`),则显示全局帮助,包含所有子命令列表。

帮助输出完成后程序会返回退出代码 `0`,不会继续执行命令处理器。

### 自定义帮助行为

通过 `HelpConfigurations` 可以自定义帮助的各个方面:

```csharp
.AddHelpHandler(new HelpConfigurations
{
// 选项/命令名称列的最大宽度(默认 30),超过此宽度的项其描述换行显示
MaxColumnWidth = 40,

// 本地化:以 Description 的值为键,返回本地化文本
HelpTextLocalizer = key => MyResources.ResourceManager.GetString(key) ?? key,

// 自定义输出目标(默认写入 Console.Out)
HelpMessageWriter = text => File.WriteAllText("help.txt", text),

// 完全自定义帮助处理器(实现 IHelpHandler 接口)
HelpHandler = new MyCustomHelpHandler(),
})
```

#### 帮助文本本地化

如果你的程序需要多语言支持,可以将 `Description` 设置为资源键,然后通过 `HelpTextLocalizer` 委托进行翻译:

```csharp
// 定义命令时使用资源键作为 Description
[Command(Description = nameof(LocalizableStrings.SampleCommandDescription))]
internal class DefaultOptions
{
[Option(Description = nameof(LocalizableStrings.SamplePropertyDescription))]
public string? DefaultText { get; set; }
}

// 启用帮助时提供本地化委托
.AddHelpHandler(new HelpConfigurations
{
HelpTextLocalizer = key => LocalizableStrings.ResourceManager.GetString(key) ?? key,
})
```

## URL协议支持

DotNetCampus.CommandLine 支持解析 URL 协议字符串,格式如下:
Expand Down
Loading
Loading