Add select object rail

This commit is contained in:
slhx agent
2026-06-21 22:39:56 +02:00
parent d7bf5edc1f
commit accbedbe4e
4 changed files with 33 additions and 12 deletions
+13 -12
View File
@@ -134,12 +134,12 @@ Selection-building mode. Escape or Ctrl-[ returns to Normal. Movement extends th
active selection by default. Vertical movement preserves a preferred cursor active selection by default. Vertical movement preserves a preferred cursor
column across ragged lines, column across ragged lines,
clamping only to the current line length until horizontal/editing motion resets clamping only to the current line length until horizontal/editing motion resets
that preference. Semantic object selections live behind the match/object rail that preference. Semantic object selections live behind the select-object rail
(`m w`, `m l`, `m i`, `m p`, etc.) so bare `h/j/k/l`, word motions, arrows, and (`s` then `o w`, `o l`, `o i`, `o p`, etc.) so bare `h/j/k/l`, word motions,
mobile line aliases keep behaving as selection-extending motions. Line object arrows, and mobile line aliases keep behaving as selection-extending motions.
selection includes the line's newline boundary when present, matching Vim-like Line object selection includes the line's newline boundary when present, matching
linewise edit semantics. Select mode can apply operations such as delete, Vim-like linewise edit semantics. Select mode can apply operations such as
replace, copy, format, code action, or explain to the selection. delete, replace, copy, format, code action, or explain to the selection.
### Panel mode ### Panel mode
@@ -254,12 +254,13 @@ Objects are shared by Normal and Select mode.
| `m` `{` | jump/select `{}` pair, depending on mode | | `m` `{` | jump/select `{}` pair, depending on mode |
| `m` `[` | jump/select `[]` pair, depending on mode | | `m` `[` | jump/select `[]` pair, depending on mode |
| `m` `"` / `m` `'` / `m` `` ` `` | jump/select quoted string where parseable | | `m` `"` / `m` `'` / `m` `` ` `` | jump/select quoted string where parseable |
| `s` `w` | select word | | `s` then motion | enter Select mode and extend the selection with that motion |
| `s` `l` | select line | | `s` `o` `w` | select word |
| `s` `i` | select current indent block | | `s` `o` `l` | select line |
| `s` `p` | select current parameter/argument | | `s` `o` `i` | select current indent block |
| `s` `f` | select current function or syntactic enclosing form where available | | `s` `o` `p` | select current parameter/argument |
| `s` `d` | select current diagnostic range | | `s` `o` `f` | select current function or syntactic enclosing form where available |
| `s` `o` `d` | select current diagnostic range |
Physical keyboards may expose `%` as an alias for `m m`. The mobile canonical Physical keyboards may expose `%` as an alias for `m m`. The mobile canonical
path stays `m m` because `%` is awkward on software keyboards and QWERTZ. path stays `m m` because `%` is awkward on software keyboards and QWERTZ.
+1
View File
@@ -28,6 +28,7 @@ Rows are redgate TSV requirements: `ring<TAB>id<TAB>summary [tag]`.
1 015 The keymap SHALL classify bindings into core mobile, mobile SSH accelerator, and attached-keyboard tiers so Esc/Ctrl/arrow-capable clients can be used without making those keys mandatory for core commands. [mobile] 1 015 The keymap SHALL classify bindings into core mobile, mobile SSH accelerator, and attached-keyboard tiers so Esc/Ctrl/arrow-capable clients can be used without making those keys mandatory for core commands. [mobile]
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 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 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]
## ui ## ui
+18
View File
@@ -33,6 +33,7 @@ const PrefixRail = enum {
none, none,
insert_space, insert_space,
match, match,
select_object,
go, go,
repeat, repeat,
delete, delete,
@@ -568,6 +569,7 @@ pub const Client = struct {
.none => if (self.pending_count != 0) "count pending" else "", .none => if (self.pending_count != 0) "count pending" else "",
.insert_space => "insert-space: n normal Space literal text commits space+text", .insert_space => "insert-space: n normal Space literal text commits space+text",
.match => "match: m jump s inside a around ( { [ quotes", .match => "match: m jump s inside a around ( { [ quotes",
.select_object => "select-object: w word l line i indent p param f form d diagnostic",
.go => "go: d definition r references e diagnostic a parameter", .go => "go: d definition r references e diagnostic a parameter",
.repeat => "repeat: digits count . repeat-last", .repeat => "repeat: digits count . repeat-last",
.delete => "delete: d line h previous-char l char", .delete => "delete: d line h previous-char l char",
@@ -1198,6 +1200,7 @@ pub const Client = struct {
if (std.mem.eql(u8, text, "b")) return self.repeatSelectionProtocol("command move_word_back", self.takeRepeat()); if (std.mem.eql(u8, text, "b")) return self.repeatSelectionProtocol("command move_word_back", self.takeRepeat());
if (std.mem.eql(u8, text, "e")) return self.repeatSelectionProtocol("command move_word_end", self.takeRepeat()); if (std.mem.eql(u8, text, "e")) return self.repeatSelectionProtocol("command move_word_end", self.takeRepeat());
if (std.mem.eql(u8, text, "m")) return self.openPrefix(.match); if (std.mem.eql(u8, text, "m")) return self.openPrefix(.match);
if (std.mem.eql(u8, text, "o")) return self.openPrefix(.select_object);
if (std.mem.eql(u8, text, "g")) return self.openPrefix(.go); if (std.mem.eql(u8, text, "g")) return self.openPrefix(.go);
if (text.len == 1) { if (text.len == 1) {
if (self.objectRangeForKey(text[0])) |range| { if (self.objectRangeForKey(text[0])) |range| {
@@ -1256,6 +1259,9 @@ pub const Client = struct {
.match => self.applyMatchRail(event) catch |err| { .match => self.applyMatchRail(event) catch |err| {
self.message = @errorName(err); self.message = @errorName(err);
}, },
.select_object => self.applySelectObjectRail(event) catch |err| {
self.message = @errorName(err);
},
.go => try self.applyGoRail(event), .go => try self.applyGoRail(event),
.repeat => self.applyKnownRailOrMessage(event, "repeat rail ready"), .repeat => self.applyKnownRailOrMessage(event, "repeat rail ready"),
.delete => try self.applyDeleteRail(event), .delete => try self.applyDeleteRail(event),
@@ -1878,6 +1884,18 @@ pub const Client = struct {
self.applyKnownRailOrMessage(event, "go rail ready"); self.applyKnownRailOrMessage(event, "go rail ready");
} }
fn applySelectObjectRail(self: *Client, event: input.Event) !void {
const text = eventText(event) orelse return self.unknownPrefixOrInput("select object");
if (text.len == 1) {
const range = try self.objectRangeForKey(text[0]);
try self.session.selectRange(range.start, range.end);
self.mode = .select;
self.message = "selected object";
return;
}
self.unknownPrefixOrInput("select object");
}
const ParameterDirection = enum { next, previous }; const ParameterDirection = enum { next, previous };
fn moveParameter(self: *Client, direction: ParameterDirection) !void { fn moveParameter(self: *Client, direction: ParameterDirection) !void {
+1
View File
@@ -552,6 +552,7 @@ SCENARIOS = [
Scenario("counted-move-inserts-at-count", 56, 12, "short", "attached-keyboard", "counts", "truecolor", "file", setup_short, (b"3", b"l", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "abcXdef", ("1│", "")), Scenario("counted-move-inserts-at-count", 56, 12, "short", "attached-keyboard", "counts", "truecolor", "file", setup_short, (b"3", b"l", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "abcXdef", ("1│", "")),
Scenario("percent-match-jump", 56, 12, "brackets", "attached-keyboard", "match-jump", "truecolor", "file", setup_brackets, (b"%", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "(abX)", ("1│", "")), Scenario("percent-match-jump", 56, 12, "brackets", "attached-keyboard", "match-jump", "truecolor", "file", setup_brackets, (b"%", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), "(abX)", ("1│", "")),
Scenario("select-mode-motions", 56, 12, "short", "attached-keyboard", "select", "truecolor", "file", setup_short, (b"L", b"s", b"H", b"L", b"n", b" ", b"q"), "abcdef\n", ("attr:selection", "", "mode:normal")), Scenario("select-mode-motions", 56, 12, "short", "attached-keyboard", "select", "truecolor", "file", setup_short, (b"L", b"s", b"H", b"L", b"n", b" ", b"q"), "abcdef\n", ("attr:selection", "", "mode:normal")),
Scenario("select-object-word-rail", 56, 12, "short", "attached-keyboard", "select-object", "truecolor", "file", setup_short, (b"s", b"o", b"w", b"n", b" ", b"q"), "abcdef\n", ("attr:selection", "mode:normal")),
Scenario("first-nonblank-line-motion", 56, 12, "indented", "attached-keyboard", "line-edges", "truecolor", "file", setup_indented, (b"L", b"^", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), " Xitem", ("1│", "", "mode:normal")), Scenario("first-nonblank-line-motion", 56, 12, "indented", "attached-keyboard", "line-edges", "truecolor", "file", setup_indented, (b"L", b"^", b"i", b"X", b"\x1b", b" ", b"w", b" ", b"q"), " Xitem", ("1│", "", "mode:normal")),
Scenario("horizontal-cursor-scroll", 24, 8, "wide-line", "attached-keyboard", "horizontal-scroll", "truecolor", "file", setup_long_line, (b"L", b" ", b"q"), "abcdefghijklmnopqrstuvwxyz0123456789\n", ("0123456789", "", "mode:normal")), Scenario("horizontal-cursor-scroll", 24, 8, "wide-line", "attached-keyboard", "horizontal-scroll", "truecolor", "file", setup_long_line, (b"L", b" ", b"q"), "abcdefghijklmnopqrstuvwxyz0123456789\n", ("0123456789", "", "mode:normal")),
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("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")),