GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/texteditors.c
Date: 2026-06-15 21:53:51
Exec Total Coverage
Lines: 0 213 0.0%
Functions: 0 15 0.0%
Branches: 0 85 0.0%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright(c) 2018-2026, Stefan Meislinger |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/tech/texteditors.h"
30 #include "gate/results.h"
31
32 ✗ gate_result_t gate_texteditor_create(gate_texteditor_t* te, gate_size_t width, gate_size_t height)
33 {
34 ✗ gate_result_t ret = GATE_RESULT_FAILED;
35 gate_strbuilder_t builder;
36
37 do
38 {
39 ✗ if (width == 0) width = 80;
40 ✗ if (height == 0) height = 24;
41 ✗ gate_mem_clear(te, sizeof(gate_texteditor_t));
42 ✗ if (NULL == gate_slotlist_create(&te->lines, sizeof(gate_strbuilder_t), &gate_strbuilder_copy_constructor, &gate_strbuilder_destructor))
43 {
44 ✗ ret = GATE_RESULT_OUTOFMEMORY;
45 ✗ break;
46 }
47
48 ✗ gate_strbuilder_create(&builder, 0);
49 ✗ gate_slotlist_add(&te->lines, &builder);
50
51 ✗ te->window_width = width;
52 ✗ te->window_height = height;
53 ✗ te->window_start_col = 0;
54 ✗ te->window_start_row = 0;
55 ✗ te->cur_col = 0;
56 ✗ te->cur_row = 0;
57 ✗ te->state = 0;
58
59 ✗ ret = GATE_RESULT_OK;
60 } while (0);
61
62 ✗ if (GATE_FAILED(ret))
63 {
64 ✗ gate_texteditor_destroy(te);
65 }
66
67 ✗ return ret;
68 }
69
70 ✗ gate_result_t gate_texteditor_clear(gate_texteditor_t* te)
71 {
72 gate_strbuilder_t builder;
73 ✗ gate_strbuilder_create(&builder, 0);
74 ✗ gate_slotlist_clear(&te->lines);
75 ✗ te->window_start_col = 0;
76 ✗ te->window_start_row = 0;
77 ✗ te->cur_col = 0;
78 ✗ te->cur_row = 0;
79 ✗ gate_slotlist_add(&te->lines, &builder);
80 ✗ return gate_texteditor_fix_view(te);
81 }
82
83
84 ✗ gate_result_t gate_texteditor_destroy(gate_texteditor_t* te)
85 {
86 ✗ gate_slotlist_destroy(&te->lines);
87 ✗ gate_mem_clear(te, sizeof(gate_texteditor_t));
88 ✗ return GATE_RESULT_OK;
89 }
90
91 ✗ static gate_strbuilder_t* gate_texteditor_get_line(gate_texteditor_t* te, gate_size_t line)
92 {
93 static gate_string_t const empty_line = GATE_STRING_INIT_EMPTY;
94 ✗ while (te->cur_row >= gate_texteditor_get_line_count(te))
95 {
96 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
97 ✗ if (NULL == gate_slotlist_add(&te->lines, &empty_line))
98 {
99 ✗ return NULL;
100 }
101 }
102 ✗ return (gate_strbuilder_t*)gate_slotlist_at(&te->lines, line);
103 }
104
105
106 ✗ gate_result_t gate_texteditor_fix_view(gate_texteditor_t* te)
107 {
108 ✗ gate_result_t ret = GATE_RESULT_OK;
109 gate_strbuilder_t dummy;
110 gate_strbuilder_t* line;
111 gate_size_t linelen;
112
113 ✗ gate_size_t lines_count = gate_texteditor_get_line_count(te);
114 ✗ if (lines_count == 0)
115 {
116 ✗ gate_strbuilder_create(&dummy, 0);
117 ✗ if (NULL == gate_slotlist_add(&te->lines, &dummy))
118 {
119 ✗ return GATE_RESULT_FAILED;
120 }
121 ✗ ++lines_count;
122 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
123 }
124 ✗ if (te->cur_row >= lines_count)
125 {
126 ✗ te->cur_row = lines_count - 1;
127 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
128 }
129 ✗ if (te->cur_row < te->window_start_row)
130 {
131 ✗ te->window_start_row = te->cur_row;
132 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
133 }
134 ✗ if (te->cur_row >= te->window_start_row + te->window_height)
135 {
136 ✗ te->window_start_row = te->cur_row + 1 - te->window_height;
137 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
138 }
139
140 ✗ line = gate_texteditor_get_line(te, te->cur_row);
141 ✗ if (!line)
142 {
143 ✗ return GATE_RESULT_FAILED;
144 }
145
146 ✗ linelen = gate_strbuilder_length(line);
147 ✗ if (te->cur_col > linelen)
148 {
149 ✗ te->cur_col = linelen;
150 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
151 }
152
153 ✗ if (te->cur_col < te->window_start_col)
154 {
155 ✗ te->window_start_col = te->cur_col;
156 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
157 }
158 ✗ if (te->cur_col >= te->window_start_col + te->window_width)
159 {
160 ✗ te->window_start_col = te->cur_col + 1 - te->window_width;
161 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
162 }
163 ✗ return ret;
164 }
165
166 ✗ gate_result_t gate_texteditor_insert_char(gate_texteditor_t* te, gate_char32_t chr)
167 {
168 gate_strbuilder_t* line;
169 gate_size_t len;
170 char txt[8];
171 ✗ gate_size_t txtlen = gate_char_write_utf8(chr, txt, sizeof(txt));
172
173 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_LINE_DIRTY);
174
175 ✗ line = gate_texteditor_get_line(te, te->cur_row);
176 ✗ if (!line)
177 {
178 ✗ return GATE_RESULT_FAILED;
179 }
180
181 ✗ len = gate_strbuilder_length(line);
182 ✗ if (te->cur_col < len)
183 {
184 ✗ txtlen = gate_strbuilder_insert(line, te->cur_col, txt, txtlen);
185 ✗ te->cur_col += txtlen;
186 }
187 else
188 {
189 ✗ gate_strbuilder_append_text(line, txt, txtlen);
190 ✗ te->cur_col = gate_strbuilder_length(line);
191 }
192 ✗ return gate_texteditor_fix_view(te);
193 }
194
195 ✗ gate_result_t gate_texteditor_insert(gate_texteditor_t* te, char const* data, gate_size_t len)
196 {
197 ✗ gate_strbuilder_t* line = gate_texteditor_get_line(te, te->cur_row);
198 ✗ if (NULL == line)
199 {
200 ✗ return GATE_RESULT_OUTOFBOUNDS;
201 }
202 ✗ while (te->cur_col > gate_strbuilder_length(line))
203 {
204 ✗ gate_strbuilder_append_text(line, " ", 1);
205 }
206 ✗ if (0 == gate_strbuilder_insert(line, te->cur_col, data, len))
207 {
208 ✗ return GATE_RESULT_OUTOFMEMORY;
209 }
210 ✗ return gate_texteditor_fix_view(te);
211 }
212
213 ✗ gate_result_t gate_texteditor_delete_back(gate_texteditor_t* te)
214 {
215 ✗ gate_result_t ret = gate_texteditor_move(te, GATE_TEXTEDITOR_MOVE_LEFT);
216 ✗ if (GATE_FAILED(ret))
217 {
218 ✗ return ret;
219 }
220 ✗ return gate_texteditor_delete_char(te);
221 }
222
223 ✗ gate_result_t gate_texteditor_delete_char(gate_texteditor_t* te)
224 {
225 ✗ gate_strbuilder_t* line = gate_texteditor_get_line(te, te->cur_row);
226 gate_size_t len;
227
228 ✗ if (line == NULL)
229 {
230 ✗ return GATE_RESULT_OUTOFMEMORY;
231 }
232 ✗ len = gate_strbuilder_length(line);
233 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_LINE_DIRTY);
234 ✗ if (te->cur_col >= len)
235 {
236 gate_strbuilder_t* nextline;
237 /* merge next line with current one */
238 ✗ te->cur_col = len;
239 ✗ nextline = gate_slotlist_at(&te->lines, te->cur_row + 1);
240 ✗ if (nextline == NULL)
241 {
242 /* at end -> no char to delete */
243 ✗ return GATE_RESULT_NOTAVAILABLE;
244 }
245 ✗ gate_strbuilder_append_text(line, nextline->ptr_data, nextline->length);
246 ✗ gate_slotlist_remove_at(&te->lines, te->cur_row + 1);
247 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
248 }
249 else
250 {
251 /* just delete char at cursor */
252 ✗ gate_strbuilder_remove(line, te->cur_col, 1);
253 }
254 ✗ return gate_texteditor_fix_view(te);
255 }
256
257 ✗ gate_result_t gate_texteditor_move(gate_texteditor_t* te, unsigned move_direction)
258 {
259 ✗ gate_result_t ret = GATE_RESULT_FAILED;
260 gate_strbuilder_t* line;
261 gate_size_t length;
262
263 ✗ switch (move_direction)
264 {
265 ✗ case GATE_TEXTEDITOR_MOVE_LEFT:
266 {
267 ✗ if (te->cur_col <= 0)
268 {
269 ✗ ret = GATE_RESULT_FAILED;
270 }
271 else
272 {
273 ✗ --te->cur_col;
274 ✗ ret = gate_texteditor_fix_view(te);
275 }
276 ✗ break;
277 }
278 ✗ case GATE_TEXTEDITOR_MOVE_RIGHT:
279 {
280 ✗ line = gate_slotlist_at(&te->lines, te->cur_row);
281 ✗ length = gate_strbuilder_length(line);
282 ✗ if (te->cur_col >= length)
283 {
284 ✗ ret = GATE_RESULT_FAILED;
285 }
286 else
287 {
288 ✗ ++te->cur_col;
289 ✗ ret = gate_texteditor_fix_view(te);
290 }
291 ✗ break;
292 }
293 ✗ case GATE_TEXTEDITOR_MOVE_UP:
294 {
295 ✗ if (te->cur_row <= 0)
296 {
297 ✗ ret = GATE_RESULT_FAILED;
298 }
299 else
300 {
301 ✗ --te->cur_row;
302 ✗ if (te->cur_row < te->window_start_row)
303 {
304 ✗ --te->window_start_row;
305 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
306 }
307 ✗ ret = gate_texteditor_fix_view(te);
308 }
309 ✗ break;
310 }
311 ✗ case GATE_TEXTEDITOR_MOVE_DOWN:
312 {
313 ✗ length = gate_slotlist_length(&te->lines);
314 ✗ if (te->cur_row + 1 >= length)
315 {
316 ✗ return GATE_RESULT_FAILED;
317 }
318 else
319 {
320 ✗ ++te->cur_row;
321
322 ✗ if (te->cur_row >= te->window_start_row + te->window_height)
323 {
324 ✗ te->window_start_row = te->cur_row - (te->window_height - 1);
325 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
326 }
327 ✗ ret = gate_texteditor_fix_view(te);
328 }
329 ✗ break;
330 }
331 ✗ case GATE_TEXTEDITOR_MOVE_HOME:
332 {
333 ✗ te->cur_col = 0;
334 ✗ ret = gate_texteditor_fix_view(te);
335 ✗ break;
336 }
337 ✗ case GATE_TEXTEDITOR_MOVE_END:
338 {
339 ✗ line = gate_slotlist_at(&te->lines, te->cur_row);
340 ✗ length = gate_strbuilder_length(line);
341 ✗ te->cur_col = length;
342 ✗ ret = gate_texteditor_fix_view(te);
343 ✗ break;
344 }
345 ✗ case GATE_TEXTEDITOR_MOVE_PGUP:
346 {
347 ✗ if (te->window_start_row >= te->window_height)
348 {
349 ✗ te->window_start_row -= te->window_height;
350 }
351 else
352 {
353 ✗ te->window_start_row = 0;
354 }
355 ✗ if (te->cur_row >= te->window_height)
356 {
357 ✗ te->cur_row -= te->window_height;
358 }
359 else
360 {
361 ✗ te->cur_row = 0;
362 }
363 ✗ ret = gate_texteditor_fix_view(te);
364 ✗ break;
365 }
366 ✗ case GATE_TEXTEDITOR_MOVE_PGDOWN:
367 {
368 ✗ length = gate_slotlist_length(&te->lines);
369 ✗ if (te->window_start_row + te->window_height < length)
370 {
371 ✗ te->window_start_row += te->window_height;
372 ✗ ret = gate_texteditor_fix_view(te);
373 }
374 ✗ break;
375 }
376 }
377 ✗ return ret;
378 }
379
380 ✗ gate_result_t gate_texteditor_new_line(gate_texteditor_t* te)
381 {
382 ✗ gate_result_t ret = GATE_RESULT_FAILED;
383 ✗ gate_strbuilder_t* line = gate_texteditor_get_line(te, te->cur_row);
384 ✗ gate_strbuilder_t new_line = GATE_INIT_EMPTY;
385
386 do
387 {
388 gate_strbuilder_t* next_line;
389 gate_size_t line_len;
390 ✗ if (!line)
391 {
392 ✗ ret = GATE_RESULT_FAILED;
393 ✗ break;
394 }
395 ✗ gate_strbuilder_create(&new_line, 0);
396 ✗ next_line = (gate_strbuilder_t*)gate_slotlist_insert_at(&te->lines, te->cur_row + 1, &new_line);
397 ✗ gate_strbuilder_release(&new_line);
398 ✗ if (NULL == next_line)
399 {
400 ✗ ret = GATE_RESULT_FAILED;
401 ✗ break;
402 }
403 ✗ line_len = gate_strbuilder_length(line);
404 ✗ if (te->cur_col < line_len)
405 {
406 ✗ gate_strbuilder_append_text(next_line, gate_strbuilder_ptr(line, te->cur_col), line_len - te->cur_col);
407 ✗ gate_strbuilder_remove(line, te->cur_col, line_len - te->cur_col);
408 }
409 ✗ te->cur_col = 0;
410 ✗ ++te->cur_row;
411 ✗ gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
412 ✗ ret = GATE_RESULT_OK;
413 } while (0);
414
415 ✗ if (GATE_SUCCEEDED(ret))
416 {
417 ✗ return gate_texteditor_fix_view(te);
418 }
419 ✗ return ret;
420 }
421
422
423 ✗ gate_size_t gate_texteditor_get_line_count(gate_texteditor_t* te)
424 {
425 ✗ return gate_slotlist_length(&te->lines);
426 }
427
428 ✗ unsigned gate_texteditor_get_state(gate_texteditor_t* te)
429 {
430 ✗ return te->state;
431 }
432
433 ✗ void gate_texteditor_set_state(gate_texteditor_t* te, unsigned state)
434 {
435 ✗ te->state = state;
436 ✗ }
437
438 ✗ gate_size_t gate_texteditor_get_line_view(gate_texteditor_t* te, gate_size_t line, char const** ptr_text)
439 {
440 gate_strbuilder_t* ptr_line;
441
442 ✗ ptr_line = (gate_strbuilder_t*)gate_slotlist_at(&te->lines, line);
443 ✗ if (ptr_text)
444 {
445 ✗ *ptr_text = gate_strbuilder_ptr(ptr_line, 0);
446 }
447 ✗ return gate_strbuilder_length(ptr_line);
448 }
449