Skip to content

imp(ioc): add some command line features to scope generator#2612

Merged
LinQingYuu merged 3 commits intodevfrom
imp/cmdline
Mar 16, 2026
Merged

imp(ioc): add some command line features to scope generator#2612
LinQingYuu merged 3 commits intodevfrom
imp/cmdline

Conversation

@ruattd
Copy link
Copy Markdown
Contributor

@ruattd ruattd commented Mar 16, 2026

如果还有什么问题丢 comment,一块做了

Summary by Sourcery

扩展生命周期作用域源代码生成器,以支持更多命令处理程序参数,并与更新后的 IoC 命名空间保持一致。

新功能:

  • 允许生命周期命令处理方法声明一个可选的布尔参数 isCallback,该参数会由生成的代码透传。

改进:

  • 更新生成器常量和生成的 using 语句以使用新的 PCL.Core.AppPCL.Core.App.IoC 命名空间。
  • 调整生成的依赖注入调用,改为直接调用 DependencyGroups,而不是通过 IoC 的静态访问入口进行调用。
Original summary in English

Summary by Sourcery

Extend the lifecycle scope source generator to support additional command handler parameters and align it with updated IoC namespaces.

New Features:

  • Allow lifecycle command handler methods to declare an optional boolean isCallback parameter that is passed through by the generated code.

Enhancements:

  • Update generator constants and generated usings to use the new PCL.Core.App and PCL.Core.App.IoC namespaces.
  • Adjust generated dependency injection calls to invoke DependencyGroups directly instead of via the IoC static access point.

@ruattd ruattd requested a review from a team March 16, 2026 10:12
@pcl-ce-automation pcl-ce-automation bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 size: M PR 大小评估:中型 labels Mar 16, 2026
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai bot commented Mar 16, 2026

Reviewer's Guide

优化生命周期作用域源生成器以使用新的 IoC/App 命名空间,并扩展命令处理程序生成逻辑,使生成的处理程序可以在命令模型之后可选地接收一个 isCallback 布尔值,同时相应更新依赖注入调用方式。

带可选 isCallback 的生成命令处理程序时序图

sequenceDiagram
    actor User
    participant CliCommandLine as CommandLine
    participant StartupService
    participant GeneratedScope as GeneratedScopeHandler
    participant CommandHandler as UserCommandHandler

    User->>CliCommandLine: enter command
    CliCommandLine->>StartupService: parsed command text
    StartupService->>GeneratedScope: TryHandleCommand(command, handler)
    note over StartupService,GeneratedScope: handler signature: (model, isCallback)
    StartupService->>GeneratedScope: invoke handler(model, isCallback)
    alt HasCommandModelArg and HasIsCallbackArg
        GeneratedScope->>CommandHandler: Handle(model, isCallback, otherArgs)
    else HasCommandModelArg only
        GeneratedScope->>CommandHandler: Handle(model, otherArgs)
    else HasIsCallbackArg only
        GeneratedScope->>CommandHandler: Handle(isCallback, otherArgs)
    else No special args
        GeneratedScope->>CommandHandler: Handle(otherArgs)
    end
    CommandHandler-->>StartupService: completion
Loading

更新后依赖注入调用的时序图

sequenceDiagram
    participant GeneratedScope as GeneratedScopeHandler
    participant DependencyGroups
    participant Service as InjectedService

    GeneratedScope->>DependencyGroups: InvokeInjection(action, identifier, targets)
    DependencyGroups->>InjectedService: resolve and invoke action(parameter)
    InjectedService-->>DependencyGroups: completion
    DependencyGroups-->>GeneratedScope: result
Loading

更新后的生命周期作用域生成器模型类图

classDiagram
    class LifecycleScopeGenerator {
        +const string AppNamespace
        +const string IocNamespace
        +const string ScopeAttributeType
        +const string StartMethodAttributeType
        +const string StopMethodAttributeType
        +const string CommandHandlerMethodAttributeType
        +const string DependencyInjectionMethodAttributeType
        +void Initialize(IncrementalGeneratorInitializationContext context)
        -static void _CollectSources(SourceProductionContext spc, ImmutableArray~ScopeModel~ scopes)
        -static string _GenerateScopeSource(ScopeModel model)
        -static IEnumerable~string~ _EmitMethod(ScopeMethodModel model)
    }

    class ScopeModel {
        +string Namespace
        +string ClassName
        +ImmutableArray~ScopeMethodModel~ Methods
    }

    class ScopeMethodModel {
        +string MethodName
        +bool Awaitable
    }

    class StartMethodModel {
    }

    class StopMethodModel {
    }

    class CommandHandlerMethodModel {
        +string Command
        +bool HasCommandModelArg
        +bool HasIsCallbackArg
        +(string Name, string TypeName, bool HasDefaultValue, object DefaultValue)[] SplitArgs
    }

    class DependencyInjectionMethodModel {
        +string Identifier
        +string ParameterType
        +int Targets
    }

    class StartupService {
        +static void TryHandleCommand(string command, Action~CommandLine, bool~ handler)
    }

    class DependencyGroups {
        +static void InvokeInjection(Delegate action, string identifier, AttributeTargets targets)
    }

    ScopeMethodModel <|-- StartMethodModel
    ScopeMethodModel <|-- StopMethodModel
    ScopeMethodModel <|-- CommandHandlerMethodModel
    ScopeMethodModel <|-- DependencyInjectionMethodModel

    LifecycleScopeGenerator --> ScopeModel
    ScopeModel --> ScopeMethodModel
    LifecycleScopeGenerator ..> StartupService : generates calls to
    LifecycleScopeGenerator ..> DependencyGroups : generates calls to
Loading

File-Level Changes

Change Details Files
更新 lifecycle/IoC 类型及 using 引用以反映新的 App/IoC 命名空间布局。
  • 引入 AppNamespaceIocNamespace 常量,并基于它们推导作用域/方法特性类型名称。
  • 调整 Start/Stop/CommandHandler/DependencyInjection 特性类型字符串以使用 IoC 命名空间。
  • 将生成代码中的 using 指令从旧的 Lifecycle 命名空间更改为新的 App 和 IoC 命名空间。
PCL.Core.SourceGenerators/LifecycleScopeGenerator.cs
增强命令处理程序模型及生成逻辑,以支持在生成的处理程序中添加可选的 isCallback 布尔参数。
  • CommandHandlerMethodModel 增加 HasIsCallbackArg 标志。
  • 根据是否存在 CommandLine 模型参数来检测可选 isCallback 布尔参数的位置。
  • 在构造 CommandHandlerMethodModel 时传入 HasIsCallbackArg 并调整构造器调用。
  • 更新生成的 TryHandleCommand lambda,以按条件绑定 modelisCallback 参数,并在存在时进行转发。
PCL.Core.SourceGenerators/LifecycleScopeGenerator.cs
调整依赖注入调用以使用更新后的 DependencyGroups 位置。
  • 将生成代码中依赖注入方法的调用从 IoC.DependencyGroups.InvokeInjection 更改为 DependencyGroups.InvokeInjection
PCL.Core.SourceGenerators/LifecycleScopeGenerator.cs

Tips and commands

Interacting with Sourcery

  • 触发新审查: 在 pull request 上评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的审查评论。
  • 从审查评论生成 GitHub issue: 在审查评论下回复,要求 Sourcery 从该评论创建 issue。你也可以在审查评论下回复 @sourcery-ai issue 来从该评论创建 issue。
  • 生成 pull request 标题: 在 pull request 标题的任意位置写上 @sourcery-ai 以在任意时间生成标题。你也可以在 pull request 上评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文的任意位置写上 @sourcery-ai summary,即可在该位置生成 PR 摘要。你也可以在 pull request 上评论 @sourcery-ai summary 来在任意时间(重新)生成摘要。
  • 生成审查者指南: 在 pull request 上评论 @sourcery-ai guide 来(重新)生成审查者指南。
  • 一次性解决所有 Sourcery 评论: 在 pull request 上评论 @sourcery-ai resolve,即可标记解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,这会非常有用。
  • 撤销所有 Sourcery 审查: 在 pull request 上评论 @sourcery-ai dismiss,即可撤销所有现有的 Sourcery 审查。尤其适用于你想从头开始新的审查时——别忘了再评论 @sourcery-ai review 来触发新审查!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或禁用诸如 Sourcery 生成的 pull request 摘要、审查者指南等审查功能。
  • 更改审查语言。
  • 添加、移除或编辑自定义审查说明。
  • 调整其他审查设置。

Getting Help

Original review guide in English

Reviewer's Guide

Refines the lifecycle scope source generator to use new IoC/App namespaces and extends command handler generation so that generated handlers can optionally accept an isCallback boolean along with the command model, while updating dependency injection invocation accordingly.

Sequence diagram for generated command handler with optional isCallback

sequenceDiagram
    actor User
    participant CliCommandLine as CommandLine
    participant StartupService
    participant GeneratedScope as GeneratedScopeHandler
    participant CommandHandler as UserCommandHandler

    User->>CliCommandLine: enter command
    CliCommandLine->>StartupService: parsed command text
    StartupService->>GeneratedScope: TryHandleCommand(command, handler)
    note over StartupService,GeneratedScope: handler signature: (model, isCallback)
    StartupService->>GeneratedScope: invoke handler(model, isCallback)
    alt HasCommandModelArg and HasIsCallbackArg
        GeneratedScope->>CommandHandler: Handle(model, isCallback, otherArgs)
    else HasCommandModelArg only
        GeneratedScope->>CommandHandler: Handle(model, otherArgs)
    else HasIsCallbackArg only
        GeneratedScope->>CommandHandler: Handle(isCallback, otherArgs)
    else No special args
        GeneratedScope->>CommandHandler: Handle(otherArgs)
    end
    CommandHandler-->>StartupService: completion
Loading

Sequence diagram for updated dependency injection invocation

sequenceDiagram
    participant GeneratedScope as GeneratedScopeHandler
    participant DependencyGroups
    participant Service as InjectedService

    GeneratedScope->>DependencyGroups: InvokeInjection(action, identifier, targets)
    DependencyGroups->>InjectedService: resolve and invoke action(parameter)
    InjectedService-->>DependencyGroups: completion
    DependencyGroups-->>GeneratedScope: result
Loading

Class diagram for updated lifecycle scope generator models

classDiagram
    class LifecycleScopeGenerator {
        +const string AppNamespace
        +const string IocNamespace
        +const string ScopeAttributeType
        +const string StartMethodAttributeType
        +const string StopMethodAttributeType
        +const string CommandHandlerMethodAttributeType
        +const string DependencyInjectionMethodAttributeType
        +void Initialize(IncrementalGeneratorInitializationContext context)
        -static void _CollectSources(SourceProductionContext spc, ImmutableArray~ScopeModel~ scopes)
        -static string _GenerateScopeSource(ScopeModel model)
        -static IEnumerable~string~ _EmitMethod(ScopeMethodModel model)
    }

    class ScopeModel {
        +string Namespace
        +string ClassName
        +ImmutableArray~ScopeMethodModel~ Methods
    }

    class ScopeMethodModel {
        +string MethodName
        +bool Awaitable
    }

    class StartMethodModel {
    }

    class StopMethodModel {
    }

    class CommandHandlerMethodModel {
        +string Command
        +bool HasCommandModelArg
        +bool HasIsCallbackArg
        +(string Name, string TypeName, bool HasDefaultValue, object DefaultValue)[] SplitArgs
    }

    class DependencyInjectionMethodModel {
        +string Identifier
        +string ParameterType
        +int Targets
    }

    class StartupService {
        +static void TryHandleCommand(string command, Action~CommandLine, bool~ handler)
    }

    class DependencyGroups {
        +static void InvokeInjection(Delegate action, string identifier, AttributeTargets targets)
    }

    ScopeMethodModel <|-- StartMethodModel
    ScopeMethodModel <|-- StopMethodModel
    ScopeMethodModel <|-- CommandHandlerMethodModel
    ScopeMethodModel <|-- DependencyInjectionMethodModel

    LifecycleScopeGenerator --> ScopeModel
    ScopeModel --> ScopeMethodModel
    LifecycleScopeGenerator ..> StartupService : generates calls to
    LifecycleScopeGenerator ..> DependencyGroups : generates calls to
Loading

File-Level Changes

Change Details Files
Update lifecycle/IoC type and using references to reflect new App/IoC namespace layout.
  • Introduce AppNamespace and IocNamespace constants and derive Scope/Method attribute type names from them.
  • Adjust Start/Stop/CommandHandler/DependencyInjection attribute type strings to use the IoC namespace.
  • Change generated using directives from the old Lifecycle namespace to the new App and IoC namespaces.
PCL.Core.SourceGenerators/LifecycleScopeGenerator.cs
Enhance command handler model and emission to support an optional isCallback boolean parameter in generated handlers.
  • Extend CommandHandlerMethodModel with a HasIsCallbackArg flag.
  • Detect optional isCallback bool parameter position based on whether a CommandLine model argument exists.
  • Pass HasIsCallbackArg into CommandHandlerMethodModel and adjust constructor calls.
  • Update emitted TryHandleCommand lambda to bind model and isCallback parameters conditionally and to forward them when present.
PCL.Core.SourceGenerators/LifecycleScopeGenerator.cs
Adjust dependency injection invocation to use the updated DependencyGroups location.
  • Change generated code to call DependencyGroups.InvokeInjection instead of IoC.DependencyGroups.InvokeInjection for dependency injection methods.
PCL.Core.SourceGenerators/LifecycleScopeGenerator.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我发现了 1 个问题,并给出了一些整体性反馈:

  • 在命令处理程序的模型收集逻辑中,当 hasCommandModelArg 为 true 时,splitArgs 仍然只跳过第一个参数,因此当存在 isCallback 时,它会被错误地当作 SplitArgs 的一部分;请调整 Skip(...) 的逻辑,在同时存在命令模型参数和 isCallback 时跳过这两个参数。
  • hasIsCallbackArgIndex 的逻辑依赖参数名等于 "isCallback";可以考虑将该参数名集中管理(例如提取为常量)或使用 nameof,以降低在其它地方修改参数名后出现不匹配的风险。
面向 AI Agent 的提示(Prompt)
Please address the comments from this code review:

## Overall Comments
- In the command handler model collection, `splitArgs` still skips only the first parameter when `hasCommandModelArg` is true, so when `isCallback` is present it will be incorrectly treated as part of `SplitArgs`; adjust the `Skip(...)` logic to skip both the command model and `isCallback` when both are present.
- The `hasIsCallbackArgIndex` logic depends on parameter name equality to `"isCallback"`; consider centralizing the parameter name (e.g., as a constant) or using `nameof` to reduce the chance of mismatches if the parameter name changes elsewhere.

## Individual Comments

### Comment 1
<location path="PCL.Core.SourceGenerators/LifecycleScopeGenerator.cs" line_range="130-132" />
<code_context>
+                    var hasIsCallbackArg = paraArray.Length > hasIsCallbackArgIndex
+                        && paraArray[hasIsCallbackArgIndex].Type.SpecialType == SpecialType.System_Boolean
+                        && paraArray[hasIsCallbackArgIndex].Name == "isCallback";
                     var splitArgs = (
                         from para in hasCommandModelArg ? paraArray.Skip(1) : paraArray
                         let name = para.Name
</code_context>
<issue_to_address>
**issue (bug_risk):** `isCallback` 参数已被检测到,但仍然被包含在 `splitArgs` 中,这很可能会导致生成的处理程序参数错位。

由于 `hasIsCallbackArg` 表示的是一个特殊的框架开关,而不是 CLI 参数,将其包含在 `splitArgs` 中(不管 `hasIsCallbackArg` 是否为 true,都通过 `paraArray.Skip(1)`)会使 `isCallback` 作为第一项 `SplitArgs` 条目暴露出来。这与 `_EmitMethod` 中将 `isCallback` 单独传递的做法不一致。请调整跳过逻辑,使在存在命令模型参数和 `isCallback` 时,两者都被排除在外(例如使用 `Skip(hasCommandModelArg ? (hasIsCallbackArg ? 2 : 1) : (hasIsCallbackArg ? 1 : 0))`),从而确保只有真正的载荷参数出现在 `SplitArgs` 中。
</issue_to_address>

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请对每条评论点 👍 或 👎,我会根据你的反馈改进后续评审。
Original comment in English

Hey - I've found 1 issue, and left some high level feedback:

  • In the command handler model collection, splitArgs still skips only the first parameter when hasCommandModelArg is true, so when isCallback is present it will be incorrectly treated as part of SplitArgs; adjust the Skip(...) logic to skip both the command model and isCallback when both are present.
  • The hasIsCallbackArgIndex logic depends on parameter name equality to "isCallback"; consider centralizing the parameter name (e.g., as a constant) or using nameof to reduce the chance of mismatches if the parameter name changes elsewhere.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the command handler model collection, `splitArgs` still skips only the first parameter when `hasCommandModelArg` is true, so when `isCallback` is present it will be incorrectly treated as part of `SplitArgs`; adjust the `Skip(...)` logic to skip both the command model and `isCallback` when both are present.
- The `hasIsCallbackArgIndex` logic depends on parameter name equality to `"isCallback"`; consider centralizing the parameter name (e.g., as a constant) or using `nameof` to reduce the chance of mismatches if the parameter name changes elsewhere.

## Individual Comments

### Comment 1
<location path="PCL.Core.SourceGenerators/LifecycleScopeGenerator.cs" line_range="130-132" />
<code_context>
+                    var hasIsCallbackArg = paraArray.Length > hasIsCallbackArgIndex
+                        && paraArray[hasIsCallbackArgIndex].Type.SpecialType == SpecialType.System_Boolean
+                        && paraArray[hasIsCallbackArgIndex].Name == "isCallback";
                     var splitArgs = (
                         from para in hasCommandModelArg ? paraArray.Skip(1) : paraArray
                         let name = para.Name
</code_context>
<issue_to_address>
**issue (bug_risk):** The `isCallback` parameter is detected but still included in `splitArgs`, which likely misaligns generated handler arguments.

Since `hasIsCallbackArg` indicates a special framework flag rather than a CLI parameter, including it in `splitArgs` (via `paraArray.Skip(1)` regardless of `hasIsCallbackArg`) will expose `isCallback` as the first `SplitArgs` entry. This diverges from `_EmitMethod`, where `isCallback` is passed separately. Adjust the skip logic so both the command model and `isCallback` are excluded when present (e.g., `Skip(hasCommandModelArg ? (hasIsCallbackArg ? 2 : 1) : (hasIsCallbackArg ? 1 : 0))`), ensuring only real payload parameters appear in `SplitArgs`.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@pcl-ce-automation pcl-ce-automation bot added 🕑 等待合并 已处理完毕,正在等待代码合并入主分支 and removed 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 labels Mar 16, 2026
@LinQingYuu LinQingYuu merged commit cf8e70e into dev Mar 16, 2026
3 checks passed
@pcl-ce-automation pcl-ce-automation bot added 👌 完成 相关问题已修复或功能已实现,计划在下次版本更新时正式上线 and removed 🕑 等待合并 已处理完毕,正在等待代码合并入主分支 labels Mar 16, 2026
@ruattd ruattd deleted the imp/cmdline branch March 16, 2026 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: M PR 大小评估:中型 👌 完成 相关问题已修复或功能已实现,计划在下次版本更新时正式上线

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants