Group LSP parameter navigation on language rail

This commit is contained in:
slhx agent
2026-06-21 22:47:29 +02:00
parent accbedbe4e
commit 4789cf79bc
5 changed files with 38 additions and 4 deletions
+5 -2
View File
@@ -327,6 +327,7 @@ Tool operations are first-class commands, not hidden side effects.
| --- | --- |
| `Space l` `h` | hover: compact card first, expandable panel for full text |
| `Space l` `s` | signature help: active parameter first, expandable panel for overloads/docs |
| `Space l` `n` / `Space l` `p` | next/previous parameter or argument placeholder; `g a`/`g A` are attached-keyboard accelerators for the same intent |
| `Space l` `a` | code actions for cursor/selection/diagnostic |
| `Space l` `r` | rename |
| `Space l` `o` | organize imports |
@@ -368,8 +369,10 @@ editor space, and they should not make diagnostics or signature context
unreachable.
Signature help follows the same rule: show the active overload and parameter
first; move between parameters with the parameter navigation grammar; expand only
when the user asks to read all overloads or full docs.
first; move between parameters with the language rail (`Space l n`/`Space l p`)
so hover, signature, and argument navigation are discoverable together. The Go
rail keeps `g a`/`g A` as attached-keyboard accelerators for the same movement.
Expand only when the user asks to read all overloads or full docs.
### Multiple providers
+1
View File
@@ -29,6 +29,7 @@ Rows are redgate TSV requirements: `ring<TAB>id<TAB>summary [tag]`.
1 016 Every attached-keyboard or Ctrl/Esc accelerator that performs a core editing, navigation, recovery, or panel action SHALL have a documented visible mobile fallback or rail path. [mobile]
1 017 Insert and Select mode SHALL return to Normal via Escape and Ctrl-[ as equivalent terminal events while preserving literal Insert-mode Space. [mobile]
1 018 Select mode SHALL keep bare movement keys as selection-extending motions and provide a non-conflicting mobile object-selection rail for word, line, indent, parameter, enclosing form, and diagnostic ranges. [mobile]
1 019 Hover, signature help, and parameter navigation SHALL share a discoverable language rail, with any attached-keyboard accelerator documented as an alias rather than the only path. [mobile]
## ui
+29 -1
View File
@@ -36,6 +36,8 @@ pub const Action = union(enum) {
language_format_policy,
language_organize_imports,
language_code_actions,
language_parameter_next,
language_parameter_previous,
diagnostics_open,
diagnostics_next,
diagnostics_previous,
@@ -106,7 +108,7 @@ pub const Leader = struct {
.rail => "leader: w save q quit Q discard f files P Pi o open p symbols s search r repeat",
.symbol_rail => symbol_mod.rail_status,
.search_rail => "search: f current file p project s symbols",
.language_rail => "language: h hover s sig f fmt o imports a actions (Space path)",
.language_rail => "language: h hover s sig n/p param f fmt o imports a actions",
.diagnostic_rail => "diagnostics: d open n/p next/prev f source (arrows/Enter ok)",
.tool_rail => "tools: l/L lint b build t test c check x cancel j jump y yank",
.open_prompt => "open: type path, Enter opens, Esc cancels",
@@ -239,6 +241,8 @@ pub const Leader = struct {
if (std.mem.eql(u8, text, "F")) return .language_format_policy;
if (std.mem.eql(u8, text, "w")) return .language_format_policy;
if (std.mem.eql(u8, text, "a")) return .language_code_actions;
if (std.mem.eql(u8, text, "n")) return .language_parameter_next;
if (std.mem.eql(u8, text, "p")) return .language_parameter_previous;
self.message = "language action is not built in this profile yet";
return .{ .not_built = .lsp };
},
@@ -594,3 +598,27 @@ test "regular: leader exposes files and Pi panels" {
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
try expectActionTag(.pi_panel, try leader.handleEvent(input.normalize("P")));
}
test "regular: language rail groups hover signature and parameter navigation" {
var leader = Leader.init(std.testing.allocator);
defer leader.deinit();
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
try expectActionTag(.none, try leader.handleEvent(input.normalize("l")));
try std.testing.expect(std.mem.indexOf(u8, leader.status(), "h hover") != null);
try std.testing.expect(std.mem.indexOf(u8, leader.status(), "s sig") != null);
try std.testing.expect(std.mem.indexOf(u8, leader.status(), "n/p param") != null);
try expectActionTag(.hover, try leader.handleEvent(input.normalize("h")));
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
try expectActionTag(.none, try leader.handleEvent(input.normalize("l")));
try expectActionTag(.signature, try leader.handleEvent(input.normalize("s")));
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
try expectActionTag(.none, try leader.handleEvent(input.normalize("l")));
try expectActionTag(.language_parameter_next, try leader.handleEvent(input.normalize("n")));
try expectActionTag(.none, try leader.handleEvent(input.normalize(" ")));
try expectActionTag(.none, try leader.handleEvent(input.normalize("l")));
try expectActionTag(.language_parameter_previous, try leader.handleEvent(input.normalize("p")));
}
+2
View File
@@ -2477,6 +2477,8 @@ pub const Client = struct {
.language_format_policy => try self.showFormatPolicy(),
.language_organize_imports => try self.organizeImports(),
.language_code_actions => try self.openCodeActions(),
.language_parameter_next => try self.moveLspParameter(.next),
.language_parameter_previous => try self.moveLspParameter(.previous),
.diagnostics_open => try self.openDiagnosticsPanel(),
.diagnostics_next => try self.gotoDiagnostic(.next),
.diagnostics_previous => try self.gotoDiagnostic(.previous),
+1 -1
View File
@@ -558,7 +558,7 @@ SCENARIOS = [
Scenario("vertical-motion-preferred-column", 56, 12, "ragged", "attached-keyboard", "motion", "truecolor", "file", setup_ragged, (b"L", b"j", b"j", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "abcdef\nxy\n123456X", ("1│", "3│", "", "mode:normal")),
Scenario("document-half-page-motions", 56, 10, "tall", "attached-keyboard", "motion", "truecolor", "file", setup_tall, (b"G", b"i", b"X", b"\x1b", b"g", b"g", b"\x04", b"i", b"Y", b"\x1b", b"G", b"g", b"u", b"i", b"Z", b"\x1b", b" ", b"w", b" ", b"q"), "l1\nl2\nl3\nl4\nl5\nZl6\nl7\nl8\nl9\nl10\nYl11\nl12\nl13\nl14\nl15\nXl16", ("11│", "16│", "", "mode:normal")),
Scenario("multi-file-coding-loop", 72, 16, "repo-multifile", "attached-keyboard", "multi-file", "truecolor", "file", setup_multifile, (b"A", b"?", b"\x1b", b" ", b"w", b" ", b"o", b"r", b"e", b"p", b"o", b"/", b"a", b"l", b"p", b"h", b"a", b".", b"z", b"i", b"g", b"\r", b"A", b"!", b"\x1b", b" ", b"w", b" ", b"q"), None, ("alpha.zig", "beta", "", "mode:normal"), max_key_events=30, expect_files=(("alpha.zig", "alpha!\n"), ("beta.zig", "beta?"))),
Scenario("lsp-assisted-coding", 60, 12, "lsp-fixture", "attached-keyboard", "lsp", "truecolor", "file", setup_lsp_assist, (b" ", b"l", b"h", b" ", b"l", b"s", b" ", b"d", b"q", b" ", b"d", b"n", b" ", b"l", b"f", b" ", b"w", b" ", b"q"), "ZLS", ("[zls] add(lhs, rhs)", "[zls] add(lhs: i32, rhs: i32) active=rhs", "diag:fresh:zls", "format:zls:applied", "ZLS", "mode:normal"), max_key_events=19, prelude=LSP_ASSIST_PRELUDE),
Scenario("lsp-assisted-coding", 60, 12, "lsp-fixture", "attached-keyboard", "lsp", "truecolor", "file", setup_lsp_assist, (b" ", b"l", b"h", b" ", b"l", b"s", b" ", b"l", b"n", b"q", b" ", b"l", b"p", b"q", b" ", b"d", b"q", b" ", b"d", b"n", b" ", b"l", b"f", b" ", b"w", b" ", b"q"), "ZLS", ("[zls] add(lhs, rhs)", "[zls] add(lhs: i32, rhs: i32) active=rhs", "panel [lsp-parameter]", "diag:fresh:zls", "format:zls:applied", "ZLS", "mode:normal"), max_key_events=27, prelude=LSP_ASSIST_PRELUDE),
Scenario("project-search-panel", 60, 12, "repo-search", "attached-keyboard", "project-search", "truecolor", "file", setup_project_search, (b" ", b"s", b"p", b"f", b"i", b"n", b"d", b"m", b"e", b"\r", b"q", b" ", b"q"), "main\n", ("panel [search]", "target.zig", "findme", "mode:normal"), max_key_events=13),
Scenario("file-and-pi-panels", 60, 12, "repo-multifile", "attached-keyboard", "panels", "truecolor", "file", setup_multifile, (b" ", b"f", b"q", b" ", b"P", b"q", b" ", b"q"), "beta\n", ("panel [files]", "alpha.zig", "panel [pi]", "pi_ask:explain_current_buffer", "mode:normal"), required_attrs=("panel-header", "current-line"), max_key_events=8),
Scenario("job-cancel-output-panel", 60, 12, "job", "attached-keyboard", "job-output", "truecolor", "file", setup_empty, (b" ", b"t", b"x", b"q", b" ", b"q"), "", ("panel [job-output]", "job:profile:build", "job:status:cancelled:user", "mode:normal"), max_key_events=6),