diff options
Diffstat (limited to 'src/Editor.zig')
| -rw-r--r-- | src/Editor.zig | 150 |
1 files changed, 6 insertions, 144 deletions
diff --git a/src/Editor.zig b/src/Editor.zig index 4b563ad..99ec932 100644 --- a/src/Editor.zig +++ b/src/Editor.zig | |||
| @@ -6,6 +6,7 @@ const ArrayList = std.ArrayList; | |||
| 6 | const Buffer = es.Buffer; | 6 | const Buffer = es.Buffer; |
| 7 | const Editor = @This(); | 7 | const Editor = @This(); |
| 8 | const Key = es.Key; | 8 | const Key = es.Key; |
| 9 | const KeyReader = es.KeyReader; | ||
| 9 | const KeyState = es.key_state.KeyState; | 10 | const KeyState = es.key_state.KeyState; |
| 10 | const StringBuilder = es.StringBuilder; | 11 | const StringBuilder = es.StringBuilder; |
| 11 | const StringHashMap = std.StringHashMap; | 12 | const StringHashMap = std.StringHashMap; |
| @@ -23,7 +24,7 @@ statusmsg_time: i64, | |||
| 23 | 24 | ||
| 24 | current_state: KeyState, | 25 | current_state: KeyState, |
| 25 | 26 | ||
| 26 | key_buffer: ArrayList(Key), | 27 | key_reader: KeyReader, |
| 27 | should_exit: bool, | 28 | should_exit: bool, |
| 28 | 29 | ||
| 29 | pub fn init(allocator: Allocator) !Editor { | 30 | pub fn init(allocator: Allocator) !Editor { |
| @@ -41,7 +42,7 @@ pub fn init(allocator: Allocator) !Editor { | |||
| 41 | 42 | ||
| 42 | .current_state = es.key_state.defaultState, | 43 | .current_state = es.key_state.defaultState, |
| 43 | 44 | ||
| 44 | .key_buffer = ArrayList(Key).init(allocator), | 45 | .key_reader = KeyReader.init(allocator), |
| 45 | .should_exit = false, | 46 | .should_exit = false, |
| 46 | }; | 47 | }; |
| 47 | errdefer self.deinit(); | 48 | errdefer self.deinit(); |
| @@ -66,7 +67,7 @@ pub fn deinit(self: *Editor) void { | |||
| 66 | self.allocator.free(statusmsg); | 67 | self.allocator.free(statusmsg); |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | self.key_buffer.deinit(); | 70 | self.key_reader.deinit(); |
| 70 | 71 | ||
| 71 | self.* = undefined; | 72 | self.* = undefined; |
| 72 | } | 73 | } |
| @@ -195,7 +196,7 @@ pub fn openFile(self: *Editor) !void { | |||
| 195 | } | 196 | } |
| 196 | 197 | ||
| 197 | pub fn processKeypress(self: *Editor) !void { | 198 | pub fn processKeypress(self: *Editor) !void { |
| 198 | const key = try self.readKey(); | 199 | const key = try self.key_reader.readKey(); |
| 199 | try self.current_state(self, self.buffer, key); | 200 | try self.current_state(self, self.buffer, key); |
| 200 | } | 201 | } |
| 201 | 202 | ||
| @@ -220,7 +221,7 @@ pub fn promptEx( | |||
| 220 | 221 | ||
| 221 | // TODO: Navigation | 222 | // TODO: Navigation |
| 222 | // TODO: Draw the cursor | 223 | // TODO: Draw the cursor |
| 223 | const key = try self.readKey(); | 224 | const key = try self.key_reader.readKey(); |
| 224 | switch (key) { | 225 | switch (key) { |
| 225 | Key.delete, Key.backspace => _ = buf.popOrNull(), | 226 | Key.delete, Key.backspace => _ = buf.popOrNull(), |
| 226 | Key.ctrl('g') => { | 227 | Key.ctrl('g') => { |
| @@ -430,142 +431,3 @@ fn parseUnsignedOptDefault(comptime T: type, buf_opt: ?[]const u8, radix: u8, de | |||
| 430 | return default; | 431 | return default; |
| 431 | } | 432 | } |
| 432 | } | 433 | } |
| 433 | |||
| 434 | fn readByte(reader: std.fs.File) !?u8 { | ||
| 435 | var buf = [_]u8{undefined}; | ||
| 436 | if (1 != try reader.read(&buf)) { | ||
| 437 | return null; | ||
| 438 | } else { | ||
| 439 | return buf[0]; | ||
| 440 | } | ||
| 441 | } | ||
| 442 | |||
| 443 | fn readByteBlocking(reader: std.fs.File) !u8 { | ||
| 444 | // No we do not care about possible EOF on stdin, don't run the editor with redirected stdin. | ||
| 445 | var char = try readByte(reader); | ||
| 446 | while (char == null) : (char = try readByte(reader)) { | ||
| 447 | std.os.sched_yield() catch {}; // :) | ||
| 448 | } | ||
| 449 | |||
| 450 | return char.?; | ||
| 451 | } | ||
| 452 | |||
| 453 | fn readKey(self: *Editor) !Key { | ||
| 454 | if (self.key_buffer.items.len > 0) { | ||
| 455 | return self.key_buffer.pop(); | ||
| 456 | } | ||
| 457 | |||
| 458 | const std_in = std.io.getStdIn(); | ||
| 459 | |||
| 460 | const char1 = try readByteBlocking(std_in); | ||
| 461 | if (char1 != '\x1b') { | ||
| 462 | return Key.char(char1); | ||
| 463 | } | ||
| 464 | |||
| 465 | // TODO: This is a bad way of parsing. | ||
| 466 | const char2 = (try readByte(std_in)) orelse { return Key.escape; }; | ||
| 467 | if (char2 == '[') { | ||
| 468 | const char3 = (try readByte(std_in)) orelse { return Key.meta('['); }; | ||
| 469 | if (char3 >= '0' and char3 <= '9') { | ||
| 470 | const char4 = (try readByte(std_in)) orelse { | ||
| 471 | std.log.err("Unknown terminal sequence '^[[{c}'", .{char3}); | ||
| 472 | try self.key_buffer.append(Key.char(char3)); | ||
| 473 | return Key.meta('['); | ||
| 474 | }; | ||
| 475 | |||
| 476 | if (char4 == '~') { | ||
| 477 | return switch (char3) { | ||
| 478 | '1' => Key.home, | ||
| 479 | '3' => Key.delete, | ||
| 480 | '4' => Key.end, | ||
| 481 | '5' => Key.page_up, | ||
| 482 | '6' => Key.page_down, | ||
| 483 | '7' => Key.home, | ||
| 484 | '8' => Key.end, | ||
| 485 | else => { | ||
| 486 | std.log.err("Unknown terminal sequence '^[[{c}~'", .{char3}); | ||
| 487 | try self.key_buffer.append(Key.char('~')); | ||
| 488 | try self.key_buffer.append(Key.char(char3)); | ||
| 489 | return Key.meta('['); | ||
| 490 | }, | ||
| 491 | }; | ||
| 492 | } else if (char4 == ';' and char3 == '1') { | ||
| 493 | const char5 = (try readByte(std_in)) orelse { | ||
| 494 | std.log.err("Unknown terminal sequence '^[[1;'", .{}); | ||
| 495 | try self.key_buffer.append(Key.char(';')); | ||
| 496 | try self.key_buffer.append(Key.char('1')); | ||
| 497 | return Key.meta('['); | ||
| 498 | }; | ||
| 499 | |||
| 500 | if (char5 == '5') { | ||
| 501 | const char6 = (try readByte(std_in)) orelse { | ||
| 502 | std.log.err("Unknown terminal sequence '^[[1;5'", .{}); | ||
| 503 | try self.key_buffer.append(Key.char('5')); | ||
| 504 | try self.key_buffer.append(Key.char(';')); | ||
| 505 | try self.key_buffer.append(Key.char('1')); | ||
| 506 | return Key.meta('['); | ||
| 507 | }; | ||
| 508 | |||
| 509 | return switch (char6) { | ||
| 510 | 'A' => Key.ctrl_up, | ||
| 511 | 'B' => Key.ctrl_down, | ||
| 512 | 'C' => Key.ctrl_right, | ||
| 513 | 'D' => Key.ctrl_left, | ||
| 514 | 'F' => Key.ctrl_end, | ||
| 515 | 'H' => Key.ctrl_home, | ||
| 516 | else => { | ||
| 517 | std.log.err("Unknown terminal sequence '^[[1;5{c}'", .{char6}); | ||
| 518 | try self.key_buffer.append(Key.char(char6)); | ||
| 519 | try self.key_buffer.append(Key.char('5')); | ||
| 520 | try self.key_buffer.append(Key.char(';')); | ||
| 521 | try self.key_buffer.append(Key.char('1')); | ||
| 522 | return Key.meta('['); | ||
| 523 | }, | ||
| 524 | }; | ||
| 525 | } else { | ||
| 526 | std.log.err("Unknown terminal sequence '^[[1;{c}'", .{char5}); | ||
| 527 | try self.key_buffer.append(Key.char(char5)); | ||
| 528 | try self.key_buffer.append(Key.char(';')); | ||
| 529 | try self.key_buffer.append(Key.char('1')); | ||
| 530 | return Key.meta('['); | ||
| 531 | } | ||
| 532 | } else { | ||
| 533 | std.log.err("Unknown terminal sequence '^[[{c}{c}'", .{char3, char4}); | ||
| 534 | try self.key_buffer.append(Key.char(char4)); | ||
| 535 | try self.key_buffer.append(Key.char(char3)); | ||
| 536 | return Key.meta('['); | ||
| 537 | } | ||
| 538 | } else { | ||
| 539 | return switch (char3) { | ||
| 540 | 'A' => Key.up, | ||
| 541 | 'B' => Key.down, | ||
| 542 | 'C' => Key.right, | ||
| 543 | 'D' => Key.left, | ||
| 544 | 'F' => Key.end, | ||
| 545 | 'H' => Key.home, | ||
| 546 | else => { | ||
| 547 | std.log.err("Unknown terminal sequence '^[[{c}'", .{char3}); | ||
| 548 | try self.key_buffer.append(Key.char(char3)); | ||
| 549 | return Key.meta('['); | ||
| 550 | }, | ||
| 551 | }; | ||
| 552 | } | ||
| 553 | } else if (char2 == 'O') { | ||
| 554 | const char3 = (try readByte(std_in)) orelse { return Key.meta('O'); }; | ||
| 555 | return switch (char3) { | ||
| 556 | 'F' => Key.end, | ||
| 557 | 'H' => Key.home, | ||
| 558 | else => { | ||
| 559 | std.log.err("Unknown terminal sequence '^[O{c}'", .{char3}); | ||
| 560 | try self.key_buffer.append(Key.char(char3)); | ||
| 561 | return Key.meta('O'); | ||
| 562 | }, | ||
| 563 | }; | ||
| 564 | } else { | ||
| 565 | return Key.meta(char2); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | // C-<page up> = ^[[5;5~ | ||
| 569 | // C-<page down> = ^[[6;5 | ||
| 570 | |||
| 571 | // S- adds ;2, M- adds ;3, S-M- adds ;4, C- adds ;5, S-C- adds ;6, M-C- adds ;7, S-M-C- adds ;8 | ||