Skip to content

L33. Artisan command and signature strings#274

Open
shuvroroy wants to merge 29 commits into
PHPantom-dev:mainfrom
shuvroroy:feat/artisan-command-and-signature-strings
Open

L33. Artisan command and signature strings#274
shuvroroy wants to merge 29 commits into
PHPantom-dev:mainfrom
shuvroroy:feat/artisan-command-and-signature-strings

Conversation

@shuvroroy

@shuvroroy shuvroroy commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements L33 — Artisan command and signature strings: PHPantom now statically recovers Artisan command names and parses command $signature grammar, bringing completion, go-to-definition, hover, and diagnostics to command-name string literals and to a command's own arguments/options.

What it does

Command names — recovered from a command class's $signature, $name, or #[AsCommand] attribute, scanned across project and vendor command classes into a LaravelCommandIndex (built on initialized, refreshed incrementally on edits). Wherever a command name appears as a string literal it now:

  • completesArtisan::call('|'), Artisan::queue('|'), Schedule::command('|'), and $this->call('|') / callSilently('|') inside a command;
  • navigates — go-to-definition jumps to the declaring command class;
  • hovers — shows the class plus its arguments and options;
  • validates — unknown names are flagged (invalid_laravel_command).

Own arguments & options — the $signature grammar ({user}, {user?}, {--queue=}, arrays *, defaults, shortcut|name, : descriptions) is parsed once so that inside a command class:

  • $this->argument('|') / $this->option('|') complete against the enclosing command's own signature;
  • hover shows the parameter's description and shape (array / optional / takes-value / default / shortcut);
  • unknown names are flagged (invalid_command_parameter);
  • the parameter array of Artisan::call('cmd', ['|' => …]) completes argument names and --option keys.

Both diagnostic codes work with the existing [[diagnostics.ignore]] mechanism; no new config.

Scope / limitations

  • Naming convention for discovery. The vendor/project scan treats a class as a command when it extends a Command-suffixed class (or lives under Console/Commands/, or carries #[AsCommand]). This matches the near-universal Laravel/Symfony convention but would miss a command with an unconventional class name.
  • No false positives when discovery is incomplete. Unknown-command diagnostics are suppressed entirely when the index is empty, and $this->call/callSilently is recognized only inside a syntactic console-command class (so ordinary ->call(), e.g. $this->call('GET', '/uri') in HTTP tests, is never touched).
  • Session-only command diagnostics. The command index is built in the LSP initialized handler, not the headless analyze pipeline, so unknown-command flagging is an editor-session feature. Own-parameter validation is offset-based and works in both.
  • Static only. Names built dynamically (variables, concatenation) and commands configured via configure()/getArguments() rather than $signature/$name/#[AsCommand] are not recovered.

Testing

  • Unit — signature parser (name, args, options, arrays, defaults, shortcuts, descriptions, multiline), source scanner (all three declaration surfaces + non-command rejection), index dedup/lookup, and both completion-context detectors (including $this->call vs. non-$this receivers).
  • Integration (tests/integration/laravel_commands.rs) — command-name completion in Artisan::call, go-to-definition to the declaring class, unknown-command diagnostic, own-option completion, and unknown-own-argument diagnostic, all driven through the real initializeddid_open → request flow.
  • Full suite green.

Example

Added to the examples/laravel demo project:

  • app/Console/Commands/SyncBakeryCommand.php$signature with an argument, two options, and inline descriptions; handle() exercises $this->argument/$this->option/$this->call.
  • app/Console/Commands/GenerateReportCommand.php — the #[AsCommand(name: 'reports:generate')] path.
  • Demo::artisanCommands() — command-name completion/navigation/hover, an unknown name, and Artisan::call('bakery:sync', [...]) array-key completion, with a numbered "Try:" walkthrough.
// Demo::artisanCommands()
Artisan::call('bakery:sync');                    // completes, Ctrl+Click → SyncBakeryCommand, hover shows args/options
Schedule::command('bakery:sync')->daily();
Artisan::call('does:not-exist');                 // flagged: Unknown command
Artisan::call('bakery:sync', [
    'bakery' => 1,                               // array keys complete from the target signature
    '--fresh' => true,
    '--since' => '2024-01-01',
]);

// inside SyncBakeryCommand (signature: bakery:sync {bakery} {--fresh} {--since=})
$this->argument('bakery');   // completes/validates/hovers against this signature
$this->option('since');      // hover: "takes a value"

shuvroroy added 29 commits July 23, 2026 23:05
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 83.76623% with 150 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/hover/mod.rs 0.00% 67 Missing ⚠️
src/virtual_members/laravel/commands.rs 91.06% 37 Missing ⚠️
src/completion/command_params.rs 86.40% 28 Missing ⚠️
src/symbol_map/extraction.rs 88.78% 12 Missing ⚠️
src/diagnostics/mod.rs 93.44% 4 Missing ⚠️
src/definition/resolve.rs 0.00% 1 Missing ⚠️
src/references/dispatch.rs 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants