feat: add a normalized :Forge parser/dispatcher with strict bang validation #143

Closed
opened 2026-04-11 19:02:20 +00:00 by barrettruth · 1 comment
barrettruth commented 2026-04-11 19:02:20 +00:00

Prerequisites

  • I have searched existing issues.

Problem

Issue #142 proposes a canonical Ex-style :Forge command language, but the current implementation in plugin/forge.lua is still an ad hoc positional parser.

That parser currently:

  • special-cases families independently
  • duplicates argument handling across PR/issue/CI/release commands
  • cannot cleanly validate command-specific modifiers
  • cannot enforce subcommand-specific bang legality
  • makes it harder to normalize old sugar into a canonical command object

Without a normalized parser/dispatcher, the new CLI will stay brittle.

Proposed solution

Add a dedicated command parser/dispatcher layer for :Forge.

Responsibilities

  1. Parse a command line into a normalized command object:

    • family
    • verb
    • subjects
    • modifiers
    • bang
  2. Validate:

    • unknown families/verbs
    • missing required subjects
    • unknown modifiers
    • duplicate modifiers
    • mutually exclusive modifiers
    • invalid bang usage
  3. Dispatch the normalized command to the existing operation layer.

Example normalized forms

:Forge pr list state=closed repo=upstream ->

{
  bang = false,
  family = 'pr',
  verb = 'list',
  subjects = {},
  modifiers = {
    state = 'closed',
    repo = 'upstream',
  },
}

:Forge! release delete v1.2.3 repo=upstream ->

{
  bang = true,
  family = 'release',
  verb = 'delete',
  subjects = { 'v1.2.3' },
  modifiers = {
    repo = 'upstream',
  },
}

Bang behavior

Use -bang on the top-level user command, but enforce legality after parsing.

If ! is not supported by the resolved subcommand, abort with:

  • E477: No ! allowed

No side effects should occur before that validation.

Suggested implementation split

  • lua/forge/cmd.lua for tokenizer/parser/validator/dispatcher
  • plugin/forge.lua reduced to user-command entrypoint + completion hookup

Acceptance criteria

  • normalized parser exists as a dedicated module
  • subcommand-specific validation is table-driven
  • unsupported bang produces E477: No ! allowed
  • no side effects happen before validation finishes
  • legacy sugar can be normalized or rejected explicitly rather than by accidental positional parsing
  • tests cover valid and invalid command forms

Alternatives considered

  • Continue extending the existing family-by-family parser. This is simpler in the short term but guarantees duplicated logic and inconsistent validation.
  • Push parsing down into each family module. That spreads grammar concerns across the codebase and makes completion harder.
## Prerequisites - [x] I have searched existing issues. ## Problem Issue #142 proposes a canonical Ex-style `:Forge` command language, but the current implementation in `plugin/forge.lua` is still an ad hoc positional parser. That parser currently: - special-cases families independently - duplicates argument handling across PR/issue/CI/release commands - cannot cleanly validate command-specific modifiers - cannot enforce subcommand-specific bang legality - makes it harder to normalize old sugar into a canonical command object Without a normalized parser/dispatcher, the new CLI will stay brittle. ## Proposed solution Add a dedicated command parser/dispatcher layer for `:Forge`. ### Responsibilities 1. Parse a command line into a normalized command object: - `family` - `verb` - `subjects` - `modifiers` - `bang` 2. Validate: - unknown families/verbs - missing required subjects - unknown modifiers - duplicate modifiers - mutually exclusive modifiers - invalid bang usage 3. Dispatch the normalized command to the existing operation layer. ### Example normalized forms ` :Forge pr list state=closed repo=upstream ` -> ```lua { bang = false, family = 'pr', verb = 'list', subjects = {}, modifiers = { state = 'closed', repo = 'upstream', }, } ``` ` :Forge! release delete v1.2.3 repo=upstream ` -> ```lua { bang = true, family = 'release', verb = 'delete', subjects = { 'v1.2.3' }, modifiers = { repo = 'upstream', }, } ``` ### Bang behavior Use `-bang` on the top-level user command, but enforce legality after parsing. If `!` is not supported by the resolved subcommand, abort with: - `E477: No ! allowed` No side effects should occur before that validation. ### Suggested implementation split - `lua/forge/cmd.lua` for tokenizer/parser/validator/dispatcher - `plugin/forge.lua` reduced to user-command entrypoint + completion hookup ### Acceptance criteria - [ ] normalized parser exists as a dedicated module - [ ] subcommand-specific validation is table-driven - [ ] unsupported bang produces `E477: No ! allowed` - [ ] no side effects happen before validation finishes - [ ] legacy sugar can be normalized or rejected explicitly rather than by accidental positional parsing - [ ] tests cover valid and invalid command forms ## Alternatives considered - Continue extending the existing family-by-family parser. This is simpler in the short term but guarantees duplicated logic and inconsistent validation. - Push parsing down into each family module. That spreads grammar concerns across the codebase and makes completion harder.
barrettruth commented 2026-04-11 20:54:33 +00:00

Implemented in #149.

Implemented in #149.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
barrettruth/forge.nvim#143
No description provided.