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
18 changes: 10 additions & 8 deletions src/Handler/SemanticTokens/AstVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@
* Walk the xphp source + AST and emit {@see TokenSpec} entries for the
* PHP-shaped surface (keywords, variables, numbers, strings, comments,
* class / interface / enum / function / method / property names) plus
* the xphp generic forms (Slice 3, not yet implemented).
* the xphp generic forms (`typeParameter` for each `T`).
*
* Two passes:
*
* 1. **Token scan** via PHP's built-in {@see PhpToken::tokenize}. The
* tokens are byte-indexed into the ORIGINAL source (not the stripped
* buffer), so positions feed directly into {@see PositionMap}.
* Emits keywords, variables, numbers, single-quoted strings,
* double-quoted strings (as a single span -- interpolation
* classification is deferred to Slice 4), and comments. Deliberately
* double-quoted strings (as a single span -- finer-grained
* interpolation classification is not yet handled), and comments. Deliberately
* skips T_STRING (identifiers) so the AST pass can classify each
* identifier into its semantic role without overlap.
*
Expand All @@ -51,9 +51,11 @@
* names -> `function`, PropertyItem names -> `property`, Param
* names -> `parameter`.
*
* Slice 3 will extend the AST pass to recognise xphp generic
* `ATTR_GENERIC_PARAMS` / `ATTR_GENERIC_ARGS` decorations and emit
* `typeParameter` for every `T` in the 12 audit forms.
* The token pass classifies identifiers inside `<...>` clauses as
* `typeParameter`, and the AST pass reads the enclosing ClassLike's
* `ATTR_GENERIC_PARAMS` decoration to re-classify reified-`T`
* references (`new T()`, `instanceof T`, `T::class`) the token scan
* can't distinguish from ordinary class names.
*/
final class AstVisitor
{
Expand Down Expand Up @@ -125,7 +127,7 @@ private function collectFromTokens(array &$out, array $reclassifyVariableAt = []
return;
}

// Slice 3: state machine tracks whether we're inside a
// State machine tracks whether we're inside a
// `<...>` generic clause. `<` opens a clause if (a) the
// previous non-trivial token was an identifier (T_STRING),
// and (b) the next non-trivial token is an uppercase-starting
Expand Down Expand Up @@ -478,7 +480,7 @@ public function emit(array &$out, int $originalOffset, int $length, string $type
// Length stays in BYTES at this point -- correct for ASCII-only
// identifiers (the vast majority of PHP source). LSP wants
// UTF-16 code units; for ASCII the two are equal. Non-ASCII
// tokens (e.g. UTF-8 strings) are an edge case Slice 4 covers.
// tokens (e.g. UTF-8 strings) are an edge case not yet handled.
$out[] = new TokenSpec(
line: $line,
startChar: $startChar,
Expand Down
36 changes: 18 additions & 18 deletions src/Handler/XphpCodeActionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,20 @@
* `textDocument/codeAction` handler.
*
* Surfaces lightbulb / Alt+Enter quick-fixes for the cursor's
* range and any diagnostics in it. Currently returns an empty
* action list -- this is scaffolding so the LSP capability is
* advertised correctly (clients suppress the lightbulb UI for
* servers that don't advertise it, even if a future fix is
* available).
* range and any diagnostics in it. Merges three providers:
* - {@see ImportCodeActionProvider} -- "Import class" / "Simplify
* FQN" for the unresolved name under the cursor.
* - {@see DiagnosticCodeActionProvider} -- per-diagnostic fixes
* keyed off `$params->context->diagnostics` (e.g. the
* "Did you mean ...?" null/true/false replacements for the
* undefined-bareword diagnostic).
* - {@see OptimizeImportsCodeActionProvider} -- "Optimize imports"
* removing unused `use` statements.
*
* Concrete quick-fixes will land in follow-up commits, each tied
* to a specific diagnostic code emitted by the analyzer (e.g.
* "Did you mean ...?" for the undefined-bareword diagnostic from
* commit 47a37fa). Each fix will:
* 1. Inspect `$params->context->diagnostics` for codes it handles.
* 2. Build a CodeAction with a WorkspaceEdit (textEdits)
* OR a Command to execute server-side.
* 3. Optionally defer the heavy lookup to
* `codeAction/resolve` via XphpCodeActionResolveHandler.
* Each provider builds its CodeAction with the WorkspaceEdit
* attached eagerly, so the action is applied directly on accept
* (no `codeAction/resolve` round-trip -- see
* {@see XphpCodeActionResolveHandler}).
*
* Available since IntelliJ Platform 2023.2 (the codeAction
* capability itself); 2024.2 for the `resolve` round-trip.
Expand All @@ -63,10 +62,11 @@ public function registerCapabiltiies(ServerCapabilities $capabilities): void
{
$capabilities->codeActionProvider = new CodeActionOptions(
// `resolveProvider: true` opts into the
// `codeAction/resolve` round-trip so quick-fixes
// can emit lightweight items up-front and defer
// the actual WorkspaceEdit construction to the
// moment the user accepts the action.
// `codeAction/resolve` round-trip. Today's providers
// attach their WorkspaceEdit eagerly, so the round-trip
// goes unused (see XphpCodeActionResolveHandler) -- the
// flag is kept as a hook for any future fix expensive
// enough to defer its edit until the user accepts.
resolveProvider: true,
);
}
Expand Down
20 changes: 11 additions & 9 deletions src/Handler/XphpCodeActionResolveHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@
* expensive to do for every potential action the editor's
* lightbulb might render.
*
* Currently scaffolding -- there are no actions to resolve yet
* (see XphpCodeActionHandler). Once specific quick-fixes land,
* each emits a `data` payload identifying its kind + target, and
* this handler dispatches on that payload to construct the
* WorkspaceEdit.
* Currently a no-op: every action {@see XphpCodeActionHandler}
* returns already carries its WorkspaceEdit eagerly, so the client
* applies the fix directly and never round-trips through
* `codeAction/resolve`. The handler stays registered as a hook
* for any future fix expensive enough to emit a lightweight item
* up-front (with a `data` payload identifying its kind + target)
* and defer WorkspaceEdit construction to here.
*
* Available since IntelliJ Platform 2024.2.
*/
Expand All @@ -43,10 +45,10 @@ public function methods(): array
*/
public function resolve(CodeAction $action): Promise
{
// No-op for now -- XphpCodeActionHandler emits an empty
// action list, so this method is never called in
// practice. Future commits will add per-kind dispatch
// here.
// No-op -- XphpCodeActionHandler attaches every action's
// WorkspaceEdit eagerly, so the client never round-trips
// here. Future fixes that defer their edit would add
// per-kind dispatch on `$action->data` at this point.
return new Success($action);
}
}
16 changes: 12 additions & 4 deletions src/Handler/XphpSemanticTokensHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,18 @@
* `vscode-languageclient`) render the tokens with their built-in
* "semantic" coloring -- no per-editor theme work required.
*
* Slice 1 (this commit): handler advertises capability + dispatches
* to {@see AstVisitor}, but the visitor emits no tokens. The
* client receives an empty `data: []` and renders unchanged.
* Subsequent slices extend the visitor.
* The handler advertises the capability and dispatches to
* {@see AstVisitor}, which emits the file's tokens: keywords,
* variables, numbers, strings, comments, declaration names
* (class / interface / enum / method / function / property /
* parameter), and `typeParameter` for every xphp generic `T` --
* both inside `<...>` clauses and reified-`T` uses (`new T()`,
* `instanceof T`, `T::class`) in generic class bodies.
*
* Remaining limitation: token `length` is byte-counted, so a
* multi-byte identifier is off by the UTF-8/UTF-16 delta (LSP
* wants UTF-16 code units). ASCII identifiers -- the vast
* majority -- are exact. See {@see AstVisitor::emit}.
*
* Server-capability shape: an ARRAY value, not a class instance.
* The phpactor JSON serializer null-strips empty options classes,
Expand Down
Loading