11diff --git a/src/buffer.zig b/src/buffer.zig
2- index 5b1c666..249c012 100644
2+ index f3c72f9..92ff3eb 100644
33--- a/src/buffer.zig
44+++ b/src/buffer.zig
5- @@ -534,6 +534,23 @@ pub const Buffer = struct {
6- }
5+ @@ -564,62 +564,87 @@ pub const Buffer = struct {
6+ log.trace(@This(), "line byte positions: {any}\n", .{self.line_positions.items});
77 }
88
9+ - pub fn updateIndents(self: *Buffer) FatalError!void {
10+ - if (self.ts_state == null or self.ts_state.?.indent == null) return;
11+ - try self.reparse();
12+ -
13+ - self.indents.clearRetainingCapacity();
14+ - for (0..self.line_byte_positions.items.len) |row| {
15+ - try self.indents.append(self.allocator, try self.lineIndent(row));
16+ - }
17+ - log.debug(@This(), "indents: {any}\n", .{self.indents.items});
18+ - }
19+ -
20+ pub fn lineIndent(self: *Buffer, row: usize) FatalError!usize {
21+ const ts_state = if (self.ts_state) |ts_state| ts_state else return 0;
22+ const query = ts_state.indent orelse return 0;
23+ - if (self.lineLength(row) == 0 and row > 0) return self.lineIndent(row - 1);
24+ const root_node = ts.ts.ts_tree_root_node(ts_state.tree);
25+
26+ const line_span = SpanFlat{
27+ .start = if (row == 0) 0 else self.line_byte_positions.items[row - 1],
28+ .end = self.line_byte_positions.items[row],
29+ };
30+ - if (ts.firstSmallestDescendentInSpan(root_node, line_span)) |node| {
31+ - log.trace(@This(), "line {} {}\n", .{ row + 1, line_span });
32+ - var n = node;
33+ - var indent: i32 = 0;
34+ - var processed_rows = std.AutoHashMap(usize, void).init(self.allocator);
35+ - defer processed_rows.deinit();
36+ - while (!ts.ts.ts_node_is_null(n)) {
37+ - const span = SpanFlat{
38+ - .start = ts.ts.ts_node_start_byte(n),
39+ - .end = ts.ts.ts_node_end_byte(n),
40+ - };
41+ - log.trace(@This(), "node {}\n", .{span});
42+ - if (query.captures.get(@intFromPtr(n.id))) |cap| c: {
43+ - const pos = Span.fromSpanFlat(self, span);
44+ + log.trace(@This(), "indent line {} {}\n", .{ row + 1, line_span });
45+
46+ + var n: ?ts.ts.TSNode = null;
47+ + if (line_span.start > 2 and self.lineLength(row) == 0) {
48+ + // assumes no trailing spaces on prev line
49+ + const prev_line_end_span = SpanFlat{ .start = line_span.start - 2, .end = line_span.start - 1 };
50+ + n = ts.firstSmallestDescendentInSpan(root_node, prev_line_end_span);
51+ + if (n) |n_| {
52+ + if (query.captures.get(@intFromPtr(n_.id))) |cap| {
53+ var capture_name_len: u32 = undefined;
54+ const capture_name_buf = ts.ts.ts_query_capture_name_for_id(query.query.?, cap.index, &capture_name_len);
55+ const capture_name = capture_name_buf[0..capture_name_len];
56+ - log.trace(@This(), "capture {s} [{}-{}]\n", .{ capture_name, span.start, span.end });
57+ -
58+ - if (processed_rows.contains(@intCast(pos.start.row))) break :c;
59+ - if (pos.start.row != pos.end.row and pos.start.row != row and std.mem.eql(u8, capture_name, "indent.begin")) {
60+ - indent += 1;
61+ - try processed_rows.put(@intCast(pos.start.row), {});
62+ - } else if (std.mem.eql(u8, capture_name, "indent.end") or std.mem.eql(u8, capture_name, "indent.branch")) {
63+ - indent -= 1;
64+ + if (std.mem.eql(u8, capture_name, "indent.end") or std.mem.eql(u8, capture_name, "indent.branch")) {
65+ + n = ts.firstSmallestDescendentInSpan(root_node, line_span);
66+ }
67+ }
68+ - n = ts.ts.ts_node_parent(n);
69+ }
70+ - return if (indent < 0) 0 else @intCast(indent);
71+ } else {
72+ - log.trace(@This(), "line {}[{}-{}] no node\n", .{ row + 1, line_span.start, line_span.end });
73+ + n = ts.firstSmallestDescendentInSpan(root_node, line_span);
74+ + }
75+ + if (n == null) {
76+ + log.warn(@This(), "no node\n", .{});
77+ return 0;
78+ }
79+ + var node = n.?;
80+ + const indent_row: usize = @intCast(self.posToCursor(ts.ts.ts_node_start_byte(node)).row);
81+ +
82+ + var indent: i32 = 0;
83+ + var processed_rows = std.AutoHashMap(usize, void).init(self.allocator);
84+ + defer processed_rows.deinit();
85+ + while (!ts.ts.ts_node_is_null(node)) {
86+ + const span = SpanFlat{
87+ + .start = ts.ts.ts_node_start_byte(node),
88+ + .end = ts.ts.ts_node_end_byte(node),
89+ + };
90+ + log.trace(@This(), "node {}\n", .{span});
91+ + if (query.captures.get(@intFromPtr(node.id))) |cap| c: {
92+ + const pos = Span.fromSpanFlat(self, span);
93+ +
94+ + var capture_name_len: u32 = undefined;
95+ + const capture_name_buf = ts.ts.ts_query_capture_name_for_id(query.query.?, cap.index, &capture_name_len);
96+ + const capture_name = capture_name_buf[0..capture_name_len];
97+ + log.trace(@This(), "capture {s} [{}-{}]\n", .{ capture_name, span.start, span.end });
98+ +
99+ + if (processed_rows.contains(@intCast(pos.start.row))) break :c;
100+ + if (pos.start.row != pos.end.row and pos.start.row != indent_row and std.mem.eql(u8, capture_name, "indent.begin")) {
101+ + indent += 1;
102+ + try processed_rows.put(@intCast(pos.start.row), {});
103+ + } else if (std.mem.eql(u8, capture_name, "indent.end") or std.mem.eql(u8, capture_name, "indent.branch")) {
104+ + indent -= 1;
105+ + }
106+ + }
107+ + node = ts.ts.ts_node_parent(node);
108+ + }
109+ + return if (indent < 0) 0 else @intCast(indent);
110+ + }
111+ +
9112+ pub fn indentEmptyLine(self: *Buffer) FatalError!void {
10113+ if (self.ts_state == null) return;
11114+ std.debug.assert(self.cursor.col == 0);
115+ + try self.reparse();
12116+ if (self.lineLength(@intCast(self.cursor.row)) != 0) return;
13- + try self.updateIndents();
14117+
15- + const correct_indent: usize = self.indents.items[ @intCast(self.cursor.row)] ;
118+ + const correct_indent: usize = try self.lineIndent( @intCast(self.cursor.row)) ;
16119+ const correct_indent_spaces = correct_indent * self.file_type.indent_spaces;
17120+ if (correct_indent_spaces > 0) {
18121+ const indent_text = try self.allocator.alloc(u21, correct_indent_spaces);
@@ -21,50 +124,38 @@ index 5b1c666..249c012 100644
21124+ var indent_change = try cha.Change.initInsert(self.allocator, self, self.cursor, indent_text);
22125+ try self.appendChange(&indent_change);
23126+ }
24- + }
25- +
127+ }
128+
26129 pub fn undo(self: *Buffer) !void {
27- log.debug(@This(), "undo: {?}/{}\n", .{ self.history_index, self.history.items.len });
28- if (self.history_index) |h_idx| {
29130diff --git a/src/editor.zig b/src/editor.zig
30- index 9e47642..b7d7987 100644
131+ index 8986a02..eaa0bf0 100644
31132--- a/src/editor.zig
32133+++ b/src/editor.zig
33- @@ -19,12 +19,20 @@ const ur = @import("uri.zig");
34-
35- pub const config = Config{
36- .end_of_buffer_char = null,
37- + .indent_newline = false,
38- + .reindent_block_end = false,
39- };
40-
41- pub const Config = struct {
42- /// Char to denote terminal lines after end of buffer
43- /// See vim's :h fillchars -> eob
44- end_of_buffer_char: ?u8,
134+ @@ -24,6 +24,10 @@ pub const Config = struct {
135+ /// Attempt to find matching pair when cursor is over one of these [start, end] chars
136+ /// If start == end, will only check forward until first encountered match
137+ pub const matching_pair_chars: ?[]const [2]u21 = &.{ .{ '{', '}' }, .{ '[', ']' }, .{ '(', ')' }, .{ '<', '>' }, .{ '|', '|' } };
45138+ /// In buffers with TS indent support, autoindent inserted newline
46- + indent_newline: bool,
47- + /// In buffers with TS indent support, reindent current line upon insertion of one of `reindent_block_end_chars`
48- + reindent_block_end: bool,
49- +
50- + pub const reindent_block_end_chars: []const u21 = &.{ '}', ']', ')' };
139+ + pub const indent_newline: bool = false;
140+ + /// In buffers with TS indent support, reindent current line upon insertion of one of these chars
141+ + pub const reindent_block_end_chars: ?[]const u21 = null;
51142 };
52143
53144 pub const Dirty = struct {
54145diff --git a/src/main.zig b/src/main.zig
55- index bab23db..281d62b 100644
146+ index d117195..44b359a 100644
56147--- a/src/main.zig
57148+++ b/src/main.zig
58- @@ -216 ,6 +216 ,17 @@ pub fn startEditor(allocator: std.mem.Allocator) FatalError!void {
149+ @@ -220 ,6 +220 ,17 @@ pub fn startEditor(allocator: std.mem.Allocator) FatalError!void {
59150 if (next_key != null and next_key.?.printable != null) {
60151 try printable.append(allocator, next_key.?.printable.?);
61152 keys_consumed += 1;
62- + if (edi.config .indent_newline and keys_consumed == 1 and next_key.?.printable == '\n') {
153+ + if (edi.Config .indent_newline and keys_consumed == 1 and next_key.?.printable == '\n') {
63154+ try buffer.changeInsertText(try printable.toOwnedSlice(allocator));
64155+ try buffer.indentEmptyLine();
65156+ }
66- + if (edi.config.reindent_block_end and
67- + std.mem.containsAtLeastScalar(u21, edi.Config.reindent_block_end_chars, 1, next_key.?.printable.?))
157+ + if (edi.Config.reindent_block_end_chars != null and
158+ + std.mem.containsAtLeastScalar(u21, edi.Config.reindent_block_end_chars.? , 1, next_key.?.printable.?))
68159+ {
69160+ try buffer.changeInsertText(try printable.toOwnedSlice(allocator));
70161+ log.warn(@This(), "align!!!\n", .{});
@@ -73,13 +164,26 @@ index bab23db..281d62b 100644
73164 } else {
74165 break;
75166 }
76- @@ -359 ,6 +370 ,9 @@ pub fn startEditor(allocator: std.mem.Allocator) FatalError!void {
167+ @@ -427 ,6 +438 ,9 @@ pub fn startEditor(allocator: std.mem.Allocator) FatalError!void {
77168 var change = try cha.Change.initInsert(allocator, buffer, pos, &.{'\n'});
78169 try buffer.appendChange(&change);
79170 if (!below) buffer.moveCursor(.{ .row = row });
80- + if (edi.config .indent_newline) {
171+ + if (edi.Config .indent_newline) {
81172+ try buffer.indentEmptyLine();
82173+ }
83174 } else if (buffer.mode == .normal and eql(u8, key, "J")) {
84175 for (0..@intCast(repeat_or_1)) |_| {
85176 if (buffer.cursor.row + 1 < buffer.line_positions.items.len) {
177+ diff --git a/src/ts.zig b/src/ts.zig
178+ index a7de638..bfd6d5e 100644
179+ --- a/src/ts.zig
180+ +++ b/src/ts.zig
181+ @@ -213,7 +213,7 @@ pub fn firstSmallestDescendentInSpan(node: ts.TSNode, span: SpanFlat) ?ts.TSNode
182+
183+ if (node_end < span.start) return null;
184+ const child_count = ts.ts_node_child_count(node);
185+ - if (child_count == 0 and node_start >= span.start and node_end <= span.end) return node;
186+ + if (child_count == 0 and node_start >= span.start) return node;
187+
188+ for (0..child_count) |child_idx| {
189+ const child_candidate = ts.ts_node_child(node, @intCast(child_idx));
0 commit comments