Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
1376485
准备开始实现多级命令行谓词
walterlv Sep 8, 2025
53e6371
AI 测试先行,编写子命令的测试
walterlv Sep 8, 2025
5495cbf
计划使用新的 Attribute
walterlv Sep 8, 2025
b51cde2
计划去掉斜杠分隔子命令
walterlv Sep 8, 2025
dd94178
初步开始支持子命令
walterlv Sep 8, 2025
027b378
各个不同风格的命令行解析时,初步考虑子命令
walterlv Sep 8, 2025
5fca5a5
不再支持 -p a b c 这种把 a b c 都给 p 的解析,现在在所有风格里面,b c 都是位置参数了
walterlv Sep 8, 2025
6388d86
因为目前命令行解析的两步机制,导致不能提前得知 --tags 是什么类型的选项,所以也就无法得知 origin 是位置参数还是选项的值(以…
walterlv Sep 8, 2025
6c5f109
将子命令级数的计算从运行时移到编译时
walterlv Sep 8, 2025
9e91c75
完全匹配 Attribute 的名字和命名空间,避免误识别 + 支持给满足 kebab-case 要求的名字也自动重命名
walterlv Sep 8, 2025
c2c6556
消除所有警告
walterlv Sep 8, 2025
cb9c3fb
添加带有命名法的子命令单元测试
walterlv Sep 8, 2025
356459c
处理通过 default 创建的命令行值,会抛异常的问题
walterlv Sep 8, 2025
302b67d
从程序集收集的命令也能前缀匹配了
walterlv Sep 8, 2025
547ac4f
在代码中消除所有的 Verb 字样
walterlv Sep 8, 2025
0c2e64b
检查命名风格的时候,子命令的名称也要检查
walterlv Sep 8, 2025
dc99962
消除所有的“谓词”文字
walterlv Sep 8, 2025
c0f71c8
消除文档中的 Verb
walterlv Sep 8, 2025
6cd2adc
AI 适配一波 Verb 名称变更
walterlv Sep 8, 2025
e5a05d0
人工再改一波
walterlv Sep 8, 2025
6d8b688
修复错误的转换
walterlv Sep 8, 2025
cd76194
避免将 CommandAttribute 的空格也计入命名风格检测
walterlv Sep 8, 2025
62a53a1
支持直接改名称,而不需要警告
walterlv Sep 8, 2025
7cf8f79
使用警告级别
walterlv Sep 8, 2025
02774c5
修复文件名
walterlv Sep 8, 2025
ae88412
命令和子命令的名称也处理命名风格
walterlv Sep 8, 2025
eb464f4
AI 添加最长匹配的测试
walterlv Sep 8, 2025
7cbe5bd
AI 添加命名转换的测试
walterlv Sep 8, 2025
f62c948
处理单元测试的错误
walterlv Sep 8, 2025
e238334
最长匹配应考虑单词边界
walterlv Sep 9, 2025
d003f31
修复单元测试
walterlv Sep 9, 2025
62a8489
确保编译时源生成器的和运行时的使用相同的命名法生成
walterlv Sep 9, 2025
f1fe965
不同解析器对选项大小写的转换不同
walterlv Sep 9, 2025
19a91cd
修复值长度不正确
walterlv Sep 9, 2025
f66d6c3
修复赋值不正确
walterlv Sep 9, 2025
adfdebe
只有 GNU 才能做到精确拼写
walterlv Sep 9, 2025
3e9bb8b
删除中途用过,但后面不再使用的类
walterlv Sep 9, 2025
9e439e2
添加注释,命名参数,使代码更易懂
walterlv Sep 9, 2025
7a7f18e
避免出现非有意的线性搜索
walterlv Sep 9, 2025
e0011fd
异常类名都变了,不差这点兼容性了
walterlv Sep 10, 2025
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 ApiChanges.3.x-4.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ DotNetCampus.CommandLine 4.0版本带来了全面的架构升级和功能增强

### 破坏性变更和升级指南

1. **命名空间变更**:`OptionAttribute`、`ValueAttribute``VerbAttribute` 的命名空间发生了变化。升级库后,您可能需要借助IDE来修正相关引用。
1. **命名空间变更**:`OptionAttribute`、`ValueAttribute` 的命名空间发生了变化,`VerbAttribute` 名称变为了 `CommandAttribute`。升级库后,您可能需要借助IDE来修正相关引用。

2. **参数解析选项变更**:`CommandLine.Parse(args, xxx)` 的第二个参数已从简单的URL scheme字符串升级为完整的 `CommandLineParsingOptions` 对象:
```csharp
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Advanced features include:
- Support for various data types including collections and dictionaries
- Positional arguments with `ValueAttribute`
- Required properties with C# `required` modifier
- Command handling with verb support
- Command handling with command support
- URL protocol parsing
- High performance thanks to source generators

Expand Down
32 changes: 16 additions & 16 deletions docs/en/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,9 @@ class OptionsBestPractice
}
```

## Command Handling and Verbs
## Command Handling and Commands

You can use the command handler pattern to handle different commands (verbs), similar to `git commit`, `git push`, etc. DotNetCampus.CommandLine provides multiple ways to add command handlers:
You can use the command handler pattern to handle different commands, similar to `git commit`, `git push`, etc. DotNetCampus.CommandLine provides multiple ways to add command handlers:

### 1. Using Delegates to Handle Commands

Expand All @@ -267,17 +267,17 @@ commandLine.AddHandler<AddOptions>(options => { /* Handle the add command */ })
.Run();
```

Use the `Verb` attribute to mark predicates when defining command option classes:
Use the `Command` attribute to mark commands when defining command option classes:

```csharp
[Verb("add")]
[Command("add")]
public class AddOptions
{
[Value(0)]
public string ItemToAdd { get; init; }
}

[Verb("remove")]
[Command("remove")]
public class RemoveOptions
{
[Value(0)]
Expand All @@ -290,7 +290,7 @@ public class RemoveOptions
For more complex command handling logic, you can create classes that implement the `ICommandHandler` interface, encapsulating command options and handling logic together:

```csharp
[Verb("convert")]
[Command("convert")]
internal class ConvertCommandHandler : ICommandHandler
{
[Option('i', "input")]
Expand Down Expand Up @@ -335,10 +335,10 @@ commandLine.AddHandlers<AssemblyCommandHandler>()
.Run();
```

Typically, handler classes need to add the `[Verb]` attribute and implement the `ICommandHandler` interface, and they will be automatically discovered and added:
Typically, handler classes need to add the `[Command]` attribute and implement the `ICommandHandler` interface, and they will be automatically discovered and added:

```csharp
[Verb("sample")]
[Command("sample")]
internal class SampleCommandHandler : ICommandHandler
{
[Option("SampleProperty")]
Expand All @@ -355,10 +355,10 @@ internal class SampleCommandHandler : ICommandHandler
}
```

Additionally, you can create a command handler without the `[Verb]` attribute as the default handler. There can be at most one command handler without the `[Verb]` attribute in the assembly, which will be used when no other commands match:
Additionally, you can create a command handler without the `[Command]` attribute as the default handler. There can be at most one command handler without the `[Command]` attribute in the assembly, which will be used when no other commands match:

```csharp
// Default handler without [Verb] attribute
// Default handler without [Command] attribute
internal class DefaultCommandHandler : ICommandHandler
{
[Option('h', "help")]
Expand Down Expand Up @@ -401,8 +401,8 @@ dotnet-campus://open/document.txt?readOnly=true&mode=Display&silence=true&startu

Features and usage of URL protocol parsing:

1. The URL path part (such as `open/document.txt` in the example) will be parsed as positional arguments or verb plus positional arguments
- The first part of the path can serve as a verb (needs to be marked with the `[Verb]` attribute)
1. The URL path part (such as `open/document.txt` in the example) will be parsed as positional arguments or command plus positional arguments
- The first part of the path can serve as a command (needs to be marked with the `[Command]` attribute)
- The subsequent path parts will be parsed as positional arguments
2. Query parameters (the part after `?`) will be parsed as named options
3. Collection type options can be passed multiple values by repeating parameter names, such as: `tags=csharp&tags=dotnet`
Expand Down Expand Up @@ -482,7 +482,7 @@ Corresponding generated source:
namespace DotNetCampus.Cli.Tests;

/// <summary>
/// Helper for generating command-line options, verbs, or handler functions for <see cref="global::DotNetCampus.Cli.Tests.DotNet03_MixedOptions"/>.
/// Helper for generating command-line options, commands, or handler functions for <see cref="global::DotNetCampus.Cli.Tests.DotNet03_MixedOptions"/>.
/// </summary>
internal sealed class DotNet03_MixedOptionsBuilder
{
Expand Down Expand Up @@ -563,11 +563,11 @@ namespace DotNetCampus.Cli.Tests.Fakes;
/// </summary>
partial class AssemblyCommandHandler : global::DotNetCampus.Cli.Compiler.ICommandHandlerCollection
{
public global::DotNetCampus.Cli.ICommandHandler? TryMatch(string? verb, global::DotNetCampus.Cli.CommandLine cl) => verb switch
public global::DotNetCampus.Cli.ICommandHandler? TryMatch(string? command, global::DotNetCampus.Cli.CommandLine cl) => command switch
{
null => throw new global::DotNetCampus.Cli.Exceptions.CommandVerbAmbiguityException($"Multiple command handlers match the same verb name 'null': AmbiguousOptions, CollectionOptions, ComparedOptions, DefaultVerbCommandHandler, DictionaryOptions, FakeCommandOptions, Options, PrimaryOptions, UnlimitedValueOptions, ValueOptions.", null),
null => throw new global::DotNetCampus.Cli.Exceptions.CommandVerbAmbiguityException($"Multiple command handlers match the same command name 'null': AmbiguousOptions, CollectionOptions, ComparedOptions, DefaultCommandHandler, DictionaryOptions, FakeCommandOptions, Options, PrimaryOptions, UnlimitedValueOptions, ValueOptions.", null),
// Type EditOptions does not implement the ICommandHandler interface, so it cannot be dispatched uniformly and must be called by the developer separately.
"Fake" => (global::DotNetCampus.Cli.ICommandHandler)global::DotNetCampus.Cli.Tests.Fakes.FakeVerbCommandHandlerBuilder.CreateInstance(cl),
"Fake" => (global::DotNetCampus.Cli.ICommandHandler)global::DotNetCampus.Cli.Tests.Fakes.FakeCommandHandlerBuilder.CreateInstance(cl),
// Type PrintOptions does not implement the ICommandHandler interface, so it cannot be dispatched uniformly and must be called by the developer separately.
// Type ShareOptions does not implement the ICommandHandler interface, so it cannot be dispatched uniformly and must be called by the developer separately.
_ => null,
Expand Down
32 changes: 16 additions & 16 deletions docs/zh-hans/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,9 @@ class OptionsBestPractice
}
```

## 命令处理与谓词
## 命令处理与命令

你可以使用命令处理器模式处理不同的命令(谓词),类似于`git commit`、`git push`等。DotNetCampus.CommandLine 提供了多种添加命令处理器的方式:
你可以使用命令处理器模式处理不同的命令,类似于`git commit`、`git push`等。DotNetCampus.CommandLine 提供了多种添加命令处理器的方式:

### 1. 使用委托处理命令

Expand All @@ -267,17 +267,17 @@ commandLine.AddHandler<AddOptions>(options => { /* 处理add命令 */ })
.Run();
```

定义命令选项类时使用`Verb`特性标记谓词
定义命令选项类时使用`Command`特性标记命令

```csharp
[Verb("add")]
[Command("add")]
public class AddOptions
{
[Value(0)]
public string ItemToAdd { get; init; }
}

[Verb("remove")]
[Command("remove")]
public class RemoveOptions
{
[Value(0)]
Expand All @@ -290,7 +290,7 @@ public class RemoveOptions
对于更复杂的命令处理逻辑,你可以创建实现 `ICommandHandler` 接口的类,将命令选项和处理逻辑封装在一起:

```csharp
[Verb("convert")]
[Command("convert")]
internal class ConvertCommandHandler : ICommandHandler
{
[Option('i', "input")]
Expand Down Expand Up @@ -335,10 +335,10 @@ commandLine.AddHandlers<AssemblyCommandHandler>()
.Run();
```

通常,处理器类需要添加 `[Verb]` 特性并实现 `ICommandHandler` 接口,它就会被自动发现和添加:
通常,处理器类需要添加 `[Command]` 特性并实现 `ICommandHandler` 接口,它就会被自动发现和添加:

```csharp
[Verb("sample")]
[Command("sample")]
internal class SampleCommandHandler : ICommandHandler
{
[Option("SampleProperty")]
Expand All @@ -355,10 +355,10 @@ internal class SampleCommandHandler : ICommandHandler
}
```

此外,你也可以创建一个没有 `[Verb]` 特性的命令处理器作为默认处理器。在程序集中最多只能有一个没有 `[Verb]` 特性的命令处理器,它将在没有其他命令匹配时被使用:
此外,你也可以创建一个没有 `[Command]` 特性的命令处理器作为默认处理器。在程序集中最多只能有一个没有 `[Command]` 特性的命令处理器,它将在没有其他命令匹配时被使用:

```csharp
// 没有 [Verb] 特性的默认处理器
// 没有 [Command] 特性的默认处理器
internal class DefaultCommandHandler : ICommandHandler
{
[Option('h', "help")]
Expand Down Expand Up @@ -401,8 +401,8 @@ dotnet-campus://open/document.txt?readOnly=true&mode=Display&silence=true&startu

URL协议解析的特点和用法:

1. URL路径部分(如示例中的 `open/document.txt`)会被解析为位置参数或谓词加位置参数
- 路径的第一部分可作为谓词(需标记 `[Verb]` 特性)
1. URL路径部分(如示例中的 `open/document.txt`)会被解析为位置参数或命令加位置参数
- 路径的第一部分可作为命令(需标记 `[Command]` 特性)
- 随后的路径部分会被解析为位置参数
2. 查询参数(`?` 后的部分)会被解析为命名选项
3. 集合类型选项可通过重复参数名传入多个值,如:`tags=csharp&tags=dotnet`
Expand Down Expand Up @@ -482,7 +482,7 @@ internal record DotNet03_MixedOptions
namespace DotNetCampus.Cli.Tests;

/// <summary>
/// 辅助 <see cref="global::DotNetCampus.Cli.Tests.DotNet03_MixedOptions"/> 生成命令行选项、谓词或处理函数的创建
/// 辅助 <see cref="global::DotNetCampus.Cli.Tests.DotNet03_MixedOptions"/> 生成命令行选项、命令或处理函数的创建
/// </summary>
internal sealed class DotNet03_MixedOptionsBuilder
{
Expand Down Expand Up @@ -563,11 +563,11 @@ namespace DotNetCampus.Cli.Tests.Fakes;
/// </summary>
partial class AssemblyCommandHandler : global::DotNetCampus.Cli.Compiler.ICommandHandlerCollection
{
public global::DotNetCampus.Cli.ICommandHandler? TryMatch(string? verb, global::DotNetCampus.Cli.CommandLine cl) => verb switch
public global::DotNetCampus.Cli.ICommandHandler? TryMatch(string? command, global::DotNetCampus.Cli.CommandLine cl) => command switch
{
null => throw new global::DotNetCampus.Cli.Exceptions.CommandVerbAmbiguityException($"Multiple command handlers match the same verb name 'null': AmbiguousOptions, CollectionOptions, ComparedOptions, DefaultVerbCommandHandler, DictionaryOptions, FakeCommandOptions, Options, PrimaryOptions, UnlimitedValueOptions, ValueOptions.", null),
null => throw new global::DotNetCampus.Cli.Exceptions.CommandVerbAmbiguityException($"Multiple command handlers match the same command name 'null': AmbiguousOptions, CollectionOptions, ComparedOptions, DefaultCommandHandler, DictionaryOptions, FakeCommandOptions, Options, PrimaryOptions, UnlimitedValueOptions, ValueOptions.", null),
// 类型 EditOptions 没有继承 ICommandHandler 接口,因此无法统一调度执行,只能由开发者单独调用。
"Fake" => (global::DotNetCampus.Cli.ICommandHandler)global::DotNetCampus.Cli.Tests.Fakes.FakeVerbCommandHandlerBuilder.CreateInstance(cl),
"Fake" => (global::DotNetCampus.Cli.ICommandHandler)global::DotNetCampus.Cli.Tests.Fakes.FakeCommandHandlerBuilder.CreateInstance(cl),
// 类型 PrintOptions 没有继承 ICommandHandler 接口,因此无法统一调度执行,只能由开发者单独调用。
// 类型 ShareOptions 没有继承 ICommandHandler 接口,因此无法统一调度执行,只能由开发者单独调用。
_ => null,
Expand Down
18 changes: 9 additions & 9 deletions docs/zh-hant/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,17 +268,17 @@ commandLine.AddHandler<AddOptions>(options => { /* 處理add命令 */ })
.Run();
```

定義命令選項類時使用`Verb`特性標記謂詞
定義命令選項類時使用`Command`特性標記命令

```csharp
[Verb("add")]
[Command("add")]
public class AddOptions
{
[Value(0)]
public string ItemToAdd { get; init; }
}

[Verb("remove")]
[Command("remove")]
public class RemoveOptions
{
[Value(0)]
Expand All @@ -291,7 +291,7 @@ public class RemoveOptions
對於更複雜的命令處理邏輯,你可以創建實現 `ICommandHandler` 接口的類,將命令選項和處理邏輯封裝在一起:

```csharp
[Verb("convert")]
[Command("convert")]
internal class ConvertCommandHandler : ICommandHandler
{
[Option('i', "input")]
Expand Down Expand Up @@ -336,10 +336,10 @@ commandLine.AddHandlers<AssemblyCommandHandler>()
.Run();
```

通常,處理器類需要添加 `[Verb]` 特性並實現 `ICommandHandler` 接口,它就會被自動發現和添加:
通常,處理器類需要添加 `[Command]` 特性並實現 `ICommandHandler` 接口,它就會被自動發現和添加:

```csharp
[Verb("sample")]
[Command("sample")]
internal class SampleCommandHandler : ICommandHandler
{
[Option("SampleProperty")]
Expand All @@ -356,10 +356,10 @@ internal class SampleCommandHandler : ICommandHandler
}
```

此外,你也可以創建一個沒有 `[Verb]` 特性的命令處理器作為默認處理器。在程序集中最多只能有一個沒有 `[Verb]` 特性的命令處理器,它將在沒有其他命令匹配時被使用:
此外,你也可以創建一個沒有 `[Command]` 特性的命令處理器作為默認處理器。在程序集中最多只能有一個沒有 `[Command]` 特性的命令處理器,它將在沒有其他命令匹配時被使用:

```csharp
// 沒有 [Verb] 特性的默認處理器
// 沒有 [Command] 特性的默認處理器
internal class DefaultCommandHandler : ICommandHandler
{
[Option('h', "help")]
Expand Down Expand Up @@ -403,7 +403,7 @@ dotnet-campus://open/document.txt?readOnly=true&mode=Display&silence=true&startu
URL協議解析的特點和用法:

1. URL路徑部分(如示例中的 `open/document.txt`)會被解析為位置參數或謂詞加位置參數
- 路徑的第一部分可作為謂詞(需標記 `[Verb]` 特性)
- 路徑的第一部分可作為謂詞(需標記 `[Command]` 特性)
- 隨後的路徑部分會被解析為位置參數
2. 查詢參數(`?` 後的部分)會被解析為命名選項
3. 集合類型選項可通過重複參數名傳入多個值,如:`tags=csharp&tags=dotnet`
Expand Down
16 changes: 8 additions & 8 deletions samples/DotNetCampus.CommandLine.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,37 +141,37 @@ private static void Run4xModule(string[] args)
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void Initialize()
{
// DefaultOptions { VerbName = null }
// DefaultOptions { CommandName = null }
global::DotNetCampus.Cli.CommandRunner.Register<global::DotNetCampus.Cli.DefaultOptions>(
null,
global::DotNetCampus.Cli.DefaultOptionsBuilder.CreateInstance);

// EditOptions { VerbName = "Edit" }
// EditOptions { CommandName = "Edit" }
global::DotNetCampus.Cli.CommandRunner.Register<global::DotNetCampus.Cli.Tests.Fakes.EditOptions>(
"Edit",
global::DotNetCampus.Cli.Tests.Fakes.EditOptionsBuilder.CreateInstance);

// Options { VerbName = null }
// Options { CommandName = null }
global::DotNetCampus.Cli.CommandRunner.Register<global::DotNetCampus.Cli.Tests.Fakes.Options>(
null,
global::DotNetCampus.Cli.Tests.Fakes.OptionsBuilder.CreateInstance);

// PrintOptions { VerbName = "Print" }
// PrintOptions { CommandName = "Print" }
global::DotNetCampus.Cli.CommandRunner.Register<global::DotNetCampus.Cli.Tests.Fakes.PrintOptions>(
"Print",
global::DotNetCampus.Cli.Tests.Fakes.PrintOptionsBuilder.CreateInstance);

// SampleCommandHandler { VerbName = "sample" }
// SampleCommandHandler { CommandName = "sample" }
global::DotNetCampus.Cli.CommandRunner.Register<global::DotNetCampus.Cli.SampleCommandHandler>(
"sample",
global::DotNetCampus.Cli.SampleCommandHandlerBuilder.CreateInstance);

// SampleOptions { VerbName = "sample-options" }
// SampleOptions { CommandName = "sample-options" }
global::DotNetCampus.Cli.CommandRunner.Register<global::DotNetCampus.Cli.SampleOptions>(
"sample-options",
global::DotNetCampus.Cli.SampleOptionsBuilder.CreateInstance);

// ShareOptions { VerbName = "Share" }
// ShareOptions { CommandName = "Share" }
global::DotNetCampus.Cli.CommandRunner.Register<global::DotNetCampus.Cli.Tests.Fakes.ShareOptions>(
"Share",
global::DotNetCampus.Cli.Tests.Fakes.ShareOptionsBuilder.CreateInstance);
Expand All @@ -181,7 +181,7 @@ internal static void Initialize()
// [CollectCommandHandlersFromThisAssembly]
// internal partial class AssemblyCommandHandler;

[Verb("sample")]
[Command("sample")]
internal class SampleCommandHandler : ICommandHandler
{
[Option("SampleProperty")]
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
<data name="SamplePropertyDescription" xml:space="preserve">
<value>Output any text passed to this option.</value>
</data>
<data name="SampleVerbDescription" xml:space="preserve">
<data name="SampleCommandDescription" xml:space="preserve">
<value>Use sample command line action to output some text.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
<data name="SamplePropertyDescription" xml:space="preserve">
<value>输出传入的任何类型的字符串。</value>
</data>
<data name="SampleVerbDescription" xml:space="preserve">
<data name="SampleCommandDescription" xml:space="preserve">
<value>执行示例命令。</value>
</data>
</root>
2 changes: 1 addition & 1 deletion samples/DotNetCampus.CommandLine.Sample/SampleOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

namespace DotNetCampus.Cli;

[Verb("sample-options", LocalizableDescription = nameof(LocalizableStrings.SampleVerbDescription))]
[Command("sample-options", LocalizableDescription = nameof(LocalizableStrings.SampleCommandDescription))]
internal class SampleOptions
{
[Option(LocalizableDescription = nameof(LocalizableStrings.SamplePropertyDescription))]
Expand Down
Loading
Loading