kiloz

Following through https://viewsourcecode.org/snaptoken/kilo/index.html in Zig
git clone git://code.mfashby.net:/kiloz
Log | Files | Refs | README

commit e269f5a8edf2893d962b771996069bc4b7ab1693
parent 461803ad76e2698b9bd468aa81ed0ad93f099669
Author: Martin Ashby <martin@ashbysoft.com>
Date:   Sat, 13 Jan 2024 22:22:09 +0000

Step 89 tabs.
Also added a deferred debug logging facility

Diffstat:
Msrc/main.zig | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Atabs.txt | 3+++
2 files changed, 124 insertions(+), 47 deletions(-)

diff --git a/src/main.zig b/src/main.zig @@ -1,7 +1,27 @@ const std = @import("std"); const version = @import("options").version; -const quit: editorKey = .{.char = 17}; // ctrl+q +const quit: editorKey = .{ .char = 17 }; // ctrl+q +const tabstop = 8; + +var E: ?*EditorState = null; // ONLY use this for the log function... +pub const std_options = struct { + pub fn logFn( + comptime message_level: std.log.Level, + comptime scope: @Type(.EnumLiteral), + comptime format: []const u8, + args: anytype, + ) void { + const level_txt = comptime message_level.asText(); + const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; + if (E) |es| { + nosuspend es.dbglog.writer().print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; + } else { + nosuspend std.io.getStdErr().writer().print(level_txt ++ prefix2 ++ format ++ "\n", args) catch return; + } + } +}; +const log = std.log.scoped(.kiloz); pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; @@ -9,6 +29,7 @@ pub fn main() !void { const a = gpa.allocator(); var es = EditorState.init(a); defer es.deinit(); + E = &es; es.termios_orig = try enterRawMode(); defer disableRawMode(&es.termios_orig); defer clearScreen(); @@ -19,7 +40,7 @@ pub fn main() !void { if (args.len > 1) { try editorOpen(&es, args[1]); } - + while (true) { try editorRefreshScreen(&es); @@ -41,23 +62,36 @@ const EditorState = struct { cx: usize = 0, cx_t: usize = 0, cy: usize = 0, + rx: usize = 0, rowoff: usize = 0, coloff: usize = 0, erow: []ERow = &[_]ERow{}, + dbglog: std.ArrayList(u8), fn init(a: std.mem.Allocator) EditorState { - return .{ .a = a }; + return .{ + .a = a, + .dbglog = std.ArrayList(u8).init(a), + }; } fn deinit(self: *EditorState) void { self.screen_buf.deinit(self.a); for (self.erow) |erow| { - self.a.free(erow); + self.a.free(erow.chars); + self.a.free(erow.render); } self.a.free(self.erow); + + const wtr = std.io.getStdOut().writer(); + wtr.writeAll(self.dbglog.items) catch {}; + self.dbglog.deinit(); } }; -const ERow = []u8; +const ERow = struct { + chars: []u8, + render: []u8, +}; //// Terminal handling @@ -85,16 +119,16 @@ fn disableRawMode(t: *const std.os.linux.termios) void { const editorKey = union(enum) { char: u8, virt: enum { - ARROW_LEFT, - ARROW_RIGHT, - ARROW_UP, - ARROW_DOWN, + ARROW_LEFT, + ARROW_RIGHT, + ARROW_UP, + ARROW_DOWN, PAGE_UP, PAGE_DOWN, HOME, END, DEL, - }, + }, }; fn editorReadKey() !editorKey { @@ -106,53 +140,53 @@ fn editorReadKey() !editorKey { }; if (ch == '\x1b') { const ch1 = rdr.readByte() catch |e| switch (e) { - error.EndOfStream => return .{.char = '\x1b'}, + error.EndOfStream => return .{ .char = '\x1b' }, else => return e, }; const ch2 = rdr.readByte() catch |e| switch (e) { - error.EndOfStream => return .{.char = '\x1b'}, + error.EndOfStream => return .{ .char = '\x1b' }, else => return e, }; if (ch1 == '[') { if (std.ascii.isDigit(ch2)) { const ch3 = rdr.readByte() catch |e| switch (e) { - error.EndOfStream => return .{.char = '\x1b'}, + error.EndOfStream => return .{ .char = '\x1b' }, else => return e, }; if (ch3 == '~') { switch (ch2) { - '1', '7' => return .{.virt = .HOME}, - '4', '8' => return .{.virt = .END}, - '5' => return .{.virt = .PAGE_UP}, - '6' => return .{.virt = .PAGE_DOWN}, - '3' => return .{.virt = .DEL}, - else => return .{.char = '\x1b'}, + '1', '7' => return .{ .virt = .HOME }, + '4', '8' => return .{ .virt = .END }, + '5' => return .{ .virt = .PAGE_UP }, + '6' => return .{ .virt = .PAGE_DOWN }, + '3' => return .{ .virt = .DEL }, + else => return .{ .char = '\x1b' }, } } else { - return .{.char = '\x1b'}; + return .{ .char = '\x1b' }; } } else { switch (ch2) { - 'A' => return .{.virt = .ARROW_UP}, - 'B' => return .{.virt = .ARROW_DOWN}, - 'C' => return .{.virt = .ARROW_RIGHT}, - 'D' => return .{.virt = .ARROW_LEFT}, - 'F' => return .{.virt = .END}, - 'H' => return .{.virt = .HOME}, - else => return .{.char = '\x1b'}, + 'A' => return .{ .virt = .ARROW_UP }, + 'B' => return .{ .virt = .ARROW_DOWN }, + 'C' => return .{ .virt = .ARROW_RIGHT }, + 'D' => return .{ .virt = .ARROW_LEFT }, + 'F' => return .{ .virt = .END }, + 'H' => return .{ .virt = .HOME }, + else => return .{ .char = '\x1b' }, } } } else if (ch1 == 'O') { switch (ch2) { - 'F' => return .{.virt = .END}, - 'H' => return .{.virt = .HOME}, - else => return .{.char = '\x1b'}, + 'F' => return .{ .virt = .END }, + 'H' => return .{ .virt = .HOME }, + else => return .{ .char = '\x1b' }, } } else { - return .{.char = '\x1b'}; + return .{ .char = '\x1b' }; } } - return .{.char = ch}; + return .{ .char = ch }; } } @@ -197,7 +231,39 @@ fn getWindowSize() !Size { fn editorAppendRow(es: *EditorState, line: []u8) !void { const numrows = es.erow.len; es.erow = try es.a.realloc(es.erow, numrows + 1); - es.erow[numrows] = line; + es.erow[numrows] = .{ + .chars = line, + .render = "", + }; + try editorUpdateRow(es, &es.erow[numrows]); +} + +fn editorUpdateRow(es: *EditorState, erow: *ERow) !void { + es.a.free(erow.render); + var al = std.ArrayList(u8).init(es.a); + defer al.deinit(); + for (erow.chars) |c| { + if (c == '\t') { + var tabs = tabstop - @mod(al.items.len, tabstop); + if (tabs == 0) tabs = tabstop; + try al.appendNTimes(' ', tabs); + } else { + try al.append(c); + } + } + erow.render = try al.toOwnedSlice(); +} + +fn editorRowCxToRx(erow: *ERow, cx: usize) usize { + var rx: usize = 0; + for (0..cx) |j| { + if (erow.chars[j] == '\t') { + rx += (tabstop - 1) - (@mod(rx, tabstop)); + } else { + rx += 1; + } + } + return rx; } //// File i/o @@ -240,7 +306,10 @@ fn editorRefreshScreen(es: *EditorState) !void { //try wtr.writeAll("\x1b[2J"); // J clear 2 whole screen try wtr.writeAll("\x1b[H"); // Reset cursor (to 1:1) try editorDrawRows(es, wtr); - try std.fmt.format(wtr, "\x1b[{};{}H", .{ es.cy - es.rowoff + 1, es.cx - es.coloff + 1 }); // Move the cursor to our stored position + try std.fmt.format(wtr, "\x1b[{};{}H", .{ + es.cy - es.rowoff + 1, + es.rx - es.coloff + 1, + }); // Move the cursor to our stored position try wtr.writeAll("\x1b[?25h"); // Show the cursor again try std.io.getStdOut().writeAll(es.screen_buf.items); } @@ -265,7 +334,7 @@ fn editorDrawRows(es: *const EditorState, wtr: anytype) !void { try wtr2.writeAll("~"); } } else { - var row = es.erow[filerow]; + var row = es.erow[filerow].render; if (row.len < es.coloff) { row = ""; } else { @@ -274,24 +343,29 @@ fn editorDrawRows(es: *const EditorState, wtr: anytype) !void { try wtr2.writeAll(row); } - if (y < es.screenrows-1) { + if (y < es.screenrows - 1) { try wtr.writeAll("\r\n"); } } } fn editorScroll(es: *EditorState) void { + es.rx = 0; + if (es.cy < es.erow.len) { + es.rx = editorRowCxToRx(&es.erow[es.cy], es.cx); + } + if (es.cy < es.rowoff) { es.rowoff = es.cy; } if (es.cy >= es.rowoff + es.screenrows) { es.rowoff = es.cy - es.screenrows + 1; } - if (es.cx < es.coloff) { - es.coloff = es.cx; + if (es.rx < es.coloff) { + es.coloff = es.rx; } - if (es.cx >= es.coloff + es.screencols) { - es.coloff = es.cx - es.screencols + 1; + if (es.rx >= es.coloff + es.screencols) { + es.coloff = es.rx - es.screencols + 1; } } @@ -315,10 +389,10 @@ fn editorProcessKeyPress(es: *EditorState) !void { } } -fn editorVMove(dir: enum{up,down}, n: usize, es: *EditorState) void { +fn editorVMove(dir: enum { up, down }, n: usize, es: *EditorState) void { es.cy = switch (dir) { .up => std.math.sub(usize, es.cy, n) catch 0, - .down => std.math.clamp(es.cy+n, 0, es.erow.len-1), + .down => std.math.clamp(es.cy + n, 0, es.erow.len - 1), }; } @@ -334,19 +408,19 @@ fn editorMoveCursor(key: editorKey, es: *EditorState) void { if (es.cx_t == 0) { if (es.cy > 0) { es.cy -= 1; - es.cx_t = es.erow[es.cy].len; + es.cx_t = es.erow[es.cy].chars.len; } } else { if (es.cx_t == std.math.maxInt(usize)) { - es.cx_t = es.erow[es.cy].len-1; + es.cx_t = es.erow[es.cy].chars.len - 1; } else { es.cx_t -= 1; } } }, .ARROW_RIGHT => { - if (es.cx_t >= es.erow[es.cy].len) { - if (es.cy < (es.erow.len-1)) { + if (es.cx_t >= es.erow[es.cy].chars.len) { + if (es.cy < (es.erow.len - 1)) { es.cy += 1; es.cx_t = 0; } @@ -369,7 +443,7 @@ fn editorMoveCursor(key: editorKey, es: *EditorState) void { else => {}, } - es.cx = std.math.clamp(es.cx_t, 0, es.erow[es.cy].len); + es.cx = std.math.clamp(es.cx_t, 0, es.erow[es.cy].chars.len); } //// Init diff --git a/tabs.txt b/tabs.txt @@ -0,0 +1,3 @@ + hi there I have a tab +I don't +I have a tab after some space