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
column across ragged lines,
clamping only to the current line length until horizontal/editing motion resets
that preference. Semantic object selections live behind the match/object rail
(`m w`, `m l`, `m i`, `m p`, etc.) so bare `h/j/k/l`, word motions, arrows, and
mobile line aliases keep behaving as selection-extending motions. Line object
selection includes the line's newline boundary when present, matching Vim-like
linewise edit semantics. Select mode can apply operations such as delete,
replace, copy, format, code action, or explain to the selection.
that preference. Semantic object selections live behind the select-object rail
(`s` then `o w`, `o l`, `o i`, `o p`, etc.) so bare `h/j/k/l`, word motions,
arrows, and mobile line aliases keep behaving as selection-extending motions.
Line object selection includes the line's newline boundary when present, matching
Vim-like linewise edit semantics. Select mode can apply operations such as
delete, replace, copy, format, code action, or explain to the selection.
### 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` `"` / `m` `'` / `m` `` ` `` | jump/select quoted string where parseable |
| `s` `w` | select word |
| `s` `l` | select line |
| `s` `i` | select current indent block |
| `s` `p` | select current parameter/argument |
| `s` `f` | select current function or syntactic enclosing form where available |
| `s` `d` | select current diagnostic range |
| `s` then motion | enter Select mode and extend the selection with that motion |
| `s` `o` `w` | select word |
| `s` `o` `l` | select line |
| `s` `o` `i` | select current indent block |
| `s` `o` `p` | select current parameter/argument |
| `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
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 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]
## ui
+18
View File
@@ -33,6 +33,7 @@ const PrefixRail = enum {
none,
insert_space,
match,
select_object,
go,
repeat,
delete,
@@ -568,6 +569,7 @@ pub const Client = struct {
.none => if (self.pending_count != 0) "count pending" else "",
.insert_space => "insert-space: n normal Space literal text commits space+text",
.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",
.repeat => "repeat: digits count . repeat-last",
.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, "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, "o")) return self.openPrefix(.select_object);
if (std.mem.eql(u8, text, "g")) return self.openPrefix(.go);
if (text.len == 1) {
if (self.objectRangeForKey(text[0])) |range| {
@@ -1256,6 +1259,9 @@ pub const Client = struct {
.match => self.applyMatchRail(event) catch |err| {
self.message = @errorName(err);
},
.select_object => self.applySelectObjectRail(event) catch |err| {
self.message = @errorName(err);
},
.go => try self.applyGoRail(event),
.repeat => self.applyKnownRailOrMessage(event, "repeat rail ready"),
.delete => try self.applyDeleteRail(event),
@@ -1878,6 +1884,18 @@ pub const Client = struct {
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 };
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("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-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("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")),