also looking at this
feat(line-editor): support bracketed paste mode
#55
2 files
+108
-14
| @@ -14,6 +14,8 @@ pub const EscapeSequence = enum { | ||
| 14 | 14 | delete, |
| 15 | 15 | page_up, |
| 16 | 16 | page_down, |
| 17 | paste_start, // ESC[200~ (bracketed paste begin) | |
| 18 | paste_end, // ESC[201~ (bracketed paste end) | |
| 17 | 19 | unknown, |
| 18 | 20 | |
| 19 | 21 | /// Parse escape sequence from input |
| @@ -36,23 +38,42 @@ pub const EscapeSequence = enum { | ||
| 36 | 38 | else => {}, |
| 37 | 39 | } |
| 38 | 40 | |
| 39 | // Multi-character sequences | |
| 40 | if (bytes.len >= 4 and bytes[3] == '~') { | |
| 41 | switch (bytes[2]) { | |
| 42 | '3' => return .delete, | |
| 43 | '5' => return .page_up, | |
| 44 | '6' => return .page_down, | |
| 45 | else => {}, | |
| 41 | // Numeric-parameter sequences: ESC[<digits>~ (delete, paste, ...) and | |
| 42 | // ESC[1;5C/D (ctrl arrows). bytes[2] is the first parameter digit. | |
| 43 | // Return null while the parameter is still arriving so multi-digit | |
| 44 | // codes like 200/201 (bracketed paste) are never truncated to .unknown. | |
| 45 | if (bytes[2] >= '0' and bytes[2] <= '9') { | |
| 46 | var idx: usize = 2; | |
| 47 | while (idx < bytes.len and bytes[idx] >= '0' and bytes[idx] <= '9') : (idx += 1) {} | |
| 48 | if (idx >= bytes.len) return null; // still reading digits | |
| 49 | ||
| 50 | // ESC[<num>~ | |
| 51 | if (bytes[idx] == '~') { | |
| 52 | var num: u32 = 0; | |
| 53 | for (bytes[2..idx]) |d| num = num * 10 + (d - '0'); | |
| 54 | return switch (num) { | |
| 55 | 1 => .home, | |
| 56 | 3 => .delete, | |
| 57 | 4 => .end_key, | |
| 58 | 5 => .page_up, | |
| 59 | 6 => .page_down, | |
| 60 | 200 => .paste_start, | |
| 61 | 201 => .paste_end, | |
| 62 | else => .unknown, | |
| 63 | }; | |
| 46 | 64 | } |
| 47 | } | |
| 48 | 65 | |
| 49 | // Ctrl+Arrow sequences: ESC[1;5C (right) or ESC[1;5D (left) | |
| 50 | if (bytes.len >= 6 and bytes[2] == '1' and bytes[3] == ';' and bytes[4] == '5') { | |
| 51 | switch (bytes[5]) { | |
| 52 | 'C' => return .ctrl_right, | |
| 53 | 'D' => return .ctrl_left, | |
| 54 | else => {}, | |
| 66 | // ESC[1;5C / ESC[1;5D (ctrl arrows) | |
| 67 | if (bytes[idx] == ';') { | |
| 68 | if (bytes.len < idx + 3) return null; // need ";5C"/";5D" | |
| 69 | return switch (bytes[idx + 2]) { | |
| 70 | 'C' => .ctrl_right, | |
| 71 | 'D' => .ctrl_left, | |
| 72 | else => .unknown, | |
| 73 | }; | |
| 55 | 74 | } |
| 75 | ||
| 76 | return .unknown; | |
| 56 | 77 | } |
| 57 | 78 | } |
| 58 | 79 | |