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:
M | src/main.zig | | | 168 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
A | tabs.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