summaryrefslogtreecommitdiff
path: root/src/Row.zig
diff options
context:
space:
mode:
authorGravatar Uko Kokņevičs2021-12-21 05:56:41 +0200
committerGravatar Uko Kokņevičs2021-12-21 05:56:41 +0200
commit2d2278364b6186c6cdf0f0497b0498431dfe7dd1 (patch)
tree8f2329afbc90817c855f5c5154a547a58a9458aa /src/Row.zig
downloades-2d2278364b6186c6cdf0f0497b0498431dfe7dd1.tar.gz
es-2d2278364b6186c6cdf0f0497b0498431dfe7dd1.tar.xz
es-2d2278364b6186c6cdf0f0497b0498431dfe7dd1.zip
Initial config
Diffstat (limited to 'src/Row.zig')
-rw-r--r--src/Row.zig273
1 files changed, 273 insertions, 0 deletions
diff --git a/src/Row.zig b/src/Row.zig
new file mode 100644
index 0000000..633e338
--- /dev/null
+++ b/src/Row.zig
@@ -0,0 +1,273 @@
1const std = @import("std");
2
3const Allocator = std.mem.Allocator;
4const ArrayList = std.ArrayList;
5const Buffer = @import("Buffer.zig");
6const Config = @import("Config.zig");
7const Highlight = @import("highlight.zig").Highlight;
8const Row = @This();
9const StringBuilder = @import("StringBuilder.zig");
10
11allocator: Allocator,
12
13idx: usize,
14
15data: ArrayList(u8),
16rdata: ArrayList(u8),
17hldata: ArrayList(Highlight),
18
19ends_with_open_comment: bool,
20
21pub fn init(allocator: Allocator, idx: usize, data: []const u8) !Row {
22 var self = Row{
23 .allocator = allocator,
24
25 .idx = idx,
26
27 .data = ArrayList(u8).init(allocator),
28 .rdata = ArrayList(u8).init(allocator),
29 .hldata = ArrayList(Highlight).init(allocator),
30
31 .ends_with_open_comment = false,
32 };
33 errdefer self.deinit();
34
35 try self.data.appendSlice(data);
36 return self;
37}
38
39pub fn deinit(self: Row) void {
40 self.data.deinit();
41 self.rdata.deinit();
42 self.hldata.deinit();
43}
44
45pub fn appendString(self: *Row, buf: *Buffer, str: []const u8) !void {
46 try self.data.appendSlice(str);
47 return self.update(buf);
48}
49
50pub fn cleanWhiteSpace(self: *Row, buf: *Buffer) !void {
51 const orig_len = self.data.items.len;
52 while (self.data.items.len > 0) {
53 if (std.ascii.isBlank(self.data.items[self.data.items.len - 1])) {
54 _ = self.data.pop();
55 } else {
56 break;
57 }
58 }
59
60 if (orig_len != self.data.items.len) {
61 buf.dirty = true;
62 try self.update(buf);
63 }
64}
65
66pub fn cxToRx(self: Row, config: Config, cx: usize) usize {
67 var rx: usize = 0;
68 var i: usize = 0;
69 while (i < cx) : (i += 1) {
70 if (self.data.items[i] == '\t') {
71 rx += config.tab_stop - (rx % config.tab_stop);
72 } else {
73 rx += 1;
74 }
75 }
76
77 return rx;
78}
79
80pub fn deleteChar(self: *Row, buf: *Buffer, at: usize) !void {
81 _ = self.data.orderedRemove(at);
82 try self.update(buf);
83}
84
85pub fn indentation(self: Row, allocator: Allocator) ![]u8 {
86 var str = ArrayList(u8).init(allocator);
87 defer str.deinit();
88
89 var idx: usize = 0;
90 while (idx < self.data.items.len) : (idx += 1) {
91 if (!std.ascii.isBlank(self.data.items[idx])) {
92 break;
93 }
94 }
95
96 try str.appendSlice(self.data.items[0..idx]);
97 return str.toOwnedSlice();
98}
99
100pub fn insertChar(self: *Row, buf: *Buffer, at: usize, char: u8) !void {
101 try self.data.insert(at, char);
102 try self.update(buf);
103}
104
105pub fn rxToCx(self: Row, config: Config, rx: usize) usize {
106 if (rx == 0) {
107 return 0;
108 }
109
110 var cur_rx: usize = 0;
111 for (self.data.items) |char, cx| {
112 if (char == '\t') {
113 cur_rx += config.tab_stop - (cur_rx % config.tab_stop);
114 } else {
115 cur_rx += 1;
116 }
117
118 if (cur_rx >= rx) {
119 return cx + 1;
120 }
121 }
122
123 return self.data.items.len;
124}
125
126// TODO: I don't like that this is modifying both row and buffer (parent of row)
127pub fn update(self: *Row, buf: *Buffer) !void {
128 self.rdata.clearRetainingCapacity();
129
130 for (self.data.items) |char| {
131 if (char == '\t') {
132 const len = buf.config.tab_stop - self.rdata.items.len % buf.config.tab_stop;
133 try self.rdata.appendNTimes(' ', len);
134 } else {
135 try self.rdata.append(char);
136 }
137 }
138
139 try self.updateSyntax(buf);
140}
141
142const UpdateSyntaxError = std.mem.Allocator.Error;
143pub fn updateSyntax(self: *Row, buf: *Buffer) UpdateSyntaxError!void {
144 try self.hldata.resize(self.rdata.items.len);
145 std.mem.set(Highlight, self.hldata.items, .normal);
146 if (buf.syntax == null) {
147 return;
148 }
149
150 const syntax = buf.syntax.?;
151 const kw1 = syntax.keywords1;
152 const kw2 = syntax.keywords2;
153
154 var after_sep = true;
155 var in_comment = if (self.idx > 0) buf.rows.items[self.idx - 1].ends_with_open_comment else false;
156 var curr_str_quote: ?u8 = null;
157
158 var i: usize = 0;
159 main_loop: while (i < self.rdata.items.len) {
160 const prev_hl = if (i > 0) self.hldata.items[i - 1] else .normal;
161
162 if (syntax.singleline_comment_start) |scs| {
163 std.debug.assert(scs.len != 0);
164 if (curr_str_quote == null and !in_comment and scs.len + i <= self.rdata.items.len) {
165 if (std.mem.eql(u8, scs, self.rdata.items[i .. (i + scs.len)])) {
166 std.mem.set(Highlight, self.hldata.items[i..], .comment);
167 break;
168 }
169 }
170 }
171
172 if (syntax.multiline_comment_start) |mcs| {
173 std.debug.assert(mcs.len != 0);
174 if (syntax.multiline_comment_end) |mce| {
175 std.debug.assert(mce.len != 0);
176 if (curr_str_quote == null) {
177 if (in_comment) {
178 self.hldata.items[i] = .comment_ml;
179 if (mce.len + i <= self.rdata.items.len
180 and std.mem.eql(u8, mce, self.rdata.items[i .. (i + mce.len)])
181 ) {
182 std.mem.set(Highlight, self.hldata.items[i .. (i + mce.len)], .comment_ml);
183 i += mce.len;
184 in_comment = false;
185 after_sep = true;
186 } else {
187 i += 1;
188 continue;
189 }
190 } else if (mcs.len + i <= self.rdata.items.len
191 and std.mem.eql(u8, mcs, self.rdata.items[i .. (i + mcs.len)])) {
192 std.mem.set(Highlight, self.hldata.items[i .. (i + mcs.len)], .comment_ml);
193 i += mcs.len;
194 in_comment = true;
195 continue;
196 }
197 }
198
199 }
200 }
201
202 const c = self.rdata.items[i];
203
204 if (syntax.flags.hl_strings) {
205 if (curr_str_quote) |quote| {
206 self.hldata.items[i] = .string;
207 i += 1;
208 // Pretty dumb way of detecting \" or \' but it works \shrug/
209 if (c == '\\' and i < self.rdata.items.len) {
210 self.hldata.items[i] = .string;
211 i += 1;
212 } else if (c == quote) {
213 curr_str_quote = null;
214 after_sep = true;
215 }
216
217 continue;
218 } else {
219 // TODO: Move this to syntax struct
220 if (c == '"' or c == '\'') {
221 curr_str_quote = c;
222 self.hldata.items[i] = .string;
223 i += 1;
224 continue;
225 }
226 }
227 }
228
229 if (syntax.flags.hl_numbers) {
230 if ((std.ascii.isDigit(c) and (after_sep or prev_hl == .number))
231 or (c == '.' and prev_hl == .number)) {
232 after_sep = false;
233 self.hldata.items[i] = .number;
234 i += 1;
235 continue;
236 }
237 }
238
239 if (after_sep) {
240 for (kw1) |kw| {
241 if (i + kw.len <= self.rdata.items.len
242 and std.mem.eql(u8, kw, self.rdata.items[i .. (i + kw.len)])
243 and (i + kw.len == self.rdata.items.len or syntax.isSeparator(self.rdata.items[i + kw.len]))) {
244 std.mem.set(Highlight, self.hldata.items[i .. (i + kw.len)], .keyword1);
245 i += kw.len;
246 after_sep = false;
247 continue :main_loop;
248 }
249 }
250
251 for (kw2) |kw| {
252 if (i + kw.len <= self.rdata.items.len
253 and std.mem.eql(u8, kw, self.rdata.items[i .. (i + kw.len)])
254 and (i + kw.len == self.rdata.items.len or syntax.isSeparator(self.rdata.items[i + kw.len]))) {
255 std.mem.set(Highlight, self.hldata.items[i .. (i + kw.len)], .keyword2);
256 i += kw.len;
257 after_sep = false;
258 continue :main_loop;
259 }
260 }
261 }
262
263 after_sep = syntax.isSeparator(c);
264 i += 1;
265 }
266
267 if (in_comment != self.ends_with_open_comment) {
268 self.ends_with_open_comment = in_comment;
269 if (self.idx + 1 < buf.rows.items.len) {
270 try buf.rows.items[self.idx + 1].updateSyntax(buf);
271 }
272 }
273}