GCC Code Coverage Report


Directory: src/gate/
File: src/gate/tech/texteditors.c
Date: 2026-09-21 06:26:40
Exec Total Coverage
Lines: 161 214 75.2%
Functions: 15 15 100.0%
Branches: 47 87 54.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 1 gate_result_t gate_texteditor_create(gate_texteditor_t* te, gate_size_t width, gate_size_t height)
33 {
34 1 gate_result_t ret = GATE_RESULT_FAILED;
35 gate_strbuilder_t builder;
36
37 do
38 {
39
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (width == 0) width = 80;
40
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (height == 0) height = 24;
41 1 gate_mem_clear(te, sizeof(gate_texteditor_t));
42
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 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 1 gate_strbuilder_create(&builder, 0);
49 1 gate_slotlist_add(&te->lines, &builder);
50
51 1 te->window_width = width;
52 1 te->window_height = height;
53 1 te->window_start_col = 0;
54 1 te->window_start_row = 0;
55 1 te->cur_col = 0;
56 1 te->cur_row = 0;
57 1 te->state = 0;
58
59 1 ret = GATE_RESULT_OK;
60 } while (0);
61
62
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
63 {
64 gate_texteditor_destroy(te);
65 }
66
67 1 return ret;
68 }
69
70 1 gate_result_t gate_texteditor_clear(gate_texteditor_t* te)
71 {
72 gate_strbuilder_t builder;
73 1 gate_strbuilder_create(&builder, 0);
74 1 gate_slotlist_clear(&te->lines);
75 1 te->window_start_col = 0;
76 1 te->window_start_row = 0;
77 1 te->cur_col = 0;
78 1 te->cur_row = 0;
79
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (NULL == gate_slotlist_add(&te->lines, &builder))
80 {
81 return GATE_RESULT_OUTOFMEMORY;
82 }
83 1 return gate_texteditor_fix_view(te);
84 }
85
86
87 1 gate_result_t gate_texteditor_destroy(gate_texteditor_t* te)
88 {
89 1 gate_slotlist_destroy(&te->lines);
90 1 gate_mem_clear(te, sizeof(gate_texteditor_t));
91 1 return GATE_RESULT_OK;
92 }
93
94 27 static gate_strbuilder_t* gate_texteditor_get_line(gate_texteditor_t* te, gate_size_t line)
95 {
96 static gate_string_t const empty_line = GATE_STRING_INIT_EMPTY;
97
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
27 while (te->cur_row >= gate_texteditor_get_line_count(te))
98 {
99 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
100 if (NULL == gate_slotlist_add(&te->lines, &empty_line))
101 {
102 return NULL;
103 }
104 }
105 27 return (gate_strbuilder_t*)gate_slotlist_at(&te->lines, line);
106 }
107
108
109 20 gate_result_t gate_texteditor_fix_view(gate_texteditor_t* te)
110 {
111 20 gate_result_t ret = GATE_RESULT_OK;
112 gate_strbuilder_t dummy;
113 gate_strbuilder_t* line;
114 gate_size_t linelen;
115
116 20 gate_size_t lines_count = gate_texteditor_get_line_count(te);
117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (lines_count == 0)
118 {
119 gate_strbuilder_create(&dummy, 0);
120 if (NULL == gate_slotlist_add(&te->lines, &dummy))
121 {
122 return GATE_RESULT_FAILED;
123 }
124 ++lines_count;
125 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
126 }
127
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 19 times.
20 if (te->cur_row >= lines_count)
128 {
129 1 te->cur_row = lines_count - 1;
130 1 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
131 }
132
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (te->cur_row < te->window_start_row)
133 {
134 te->window_start_row = te->cur_row;
135 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
136 }
137
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (te->cur_row >= te->window_start_row + te->window_height)
138 {
139 te->window_start_row = te->cur_row + 1 - te->window_height;
140 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
141 }
142
143 20 line = gate_texteditor_get_line(te, te->cur_row);
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (!line)
145 {
146 return GATE_RESULT_FAILED;
147 }
148
149 20 linelen = gate_strbuilder_length(line);
150
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 19 times.
20 if (te->cur_col > linelen)
151 {
152 1 te->cur_col = linelen;
153 1 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
154 }
155
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (te->cur_col < te->window_start_col)
157 {
158 te->window_start_col = te->cur_col;
159 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
160 }
161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 if (te->cur_col >= te->window_start_col + te->window_width)
162 {
163 te->window_start_col = te->cur_col + 1 - te->window_width;
164 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
165 }
166 20 return ret;
167 }
168
169 3 gate_result_t gate_texteditor_insert_char(gate_texteditor_t* te, gate_char32_t chr)
170 {
171 gate_strbuilder_t* line;
172 gate_size_t len;
173 char txt[8];
174 3 gate_size_t txtlen = gate_char_write_utf8(chr, txt, sizeof(txt));
175
176 3 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_LINE_DIRTY);
177
178 3 line = gate_texteditor_get_line(te, te->cur_row);
179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (!line)
180 {
181 return GATE_RESULT_FAILED;
182 }
183
184 3 len = gate_strbuilder_length(line);
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (te->cur_col < len)
186 {
187 txtlen = gate_strbuilder_insert(line, te->cur_col, txt, txtlen);
188 te->cur_col += txtlen;
189 }
190 else
191 {
192 3 gate_strbuilder_append_text(line, txt, txtlen);
193 3 te->cur_col = gate_strbuilder_length(line);
194 }
195 3 return gate_texteditor_fix_view(te);
196 }
197
198 1 gate_result_t gate_texteditor_insert(gate_texteditor_t* te, char const* data, gate_size_t len)
199 {
200 1 gate_strbuilder_t* line = gate_texteditor_get_line(te, te->cur_row);
201
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == line)
202 {
203 return GATE_RESULT_OUTOFBOUNDS;
204 }
205
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 while (te->cur_col > gate_strbuilder_length(line))
206 {
207 gate_strbuilder_append_text(line, " ", 1);
208 }
209
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (0 == gate_strbuilder_insert(line, te->cur_col, data, len))
210 {
211 return GATE_RESULT_OUTOFMEMORY;
212 }
213 1 return gate_texteditor_fix_view(te);
214 }
215
216 1 gate_result_t gate_texteditor_delete_back(gate_texteditor_t* te)
217 {
218 1 gate_result_t ret = gate_texteditor_move(te, GATE_TEXTEDITOR_MOVE_LEFT);
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (GATE_FAILED(ret))
220 {
221 return ret;
222 }
223 1 return gate_texteditor_delete_char(te);
224 }
225
226 2 gate_result_t gate_texteditor_delete_char(gate_texteditor_t* te)
227 {
228 2 gate_strbuilder_t* line = gate_texteditor_get_line(te, te->cur_row);
229 gate_size_t len;
230
231
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (line == NULL)
232 {
233 return GATE_RESULT_OUTOFMEMORY;
234 }
235 2 len = gate_strbuilder_length(line);
236 2 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_LINE_DIRTY);
237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (te->cur_col >= len)
238 {
239 gate_strbuilder_t* nextline;
240 /* merge next line with current one */
241 te->cur_col = len;
242 nextline = gate_slotlist_at(&te->lines, te->cur_row + 1);
243 if (nextline == NULL)
244 {
245 /* at end -> no char to delete */
246 return GATE_RESULT_NOTAVAILABLE;
247 }
248 gate_strbuilder_append_text(line, nextline->ptr_data, nextline->length);
249 gate_slotlist_remove_at(&te->lines, te->cur_row + 1);
250 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
251 }
252 else
253 {
254 /* just delete char at cursor */
255 2 gate_strbuilder_remove(line, te->cur_col, 1);
256 }
257 2 return gate_texteditor_fix_view(te);
258 }
259
260 10 gate_result_t gate_texteditor_move(gate_texteditor_t* te, gate_enumint_t move_direction)
261 {
262 10 gate_result_t ret = GATE_RESULT_FAILED;
263 gate_strbuilder_t* line;
264 gate_size_t length;
265
266
8/9
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✗ Branch 8 not taken.
10 switch (move_direction)
267 {
268 2 case GATE_TEXTEDITOR_MOVE_LEFT:
269 {
270
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (te->cur_col <= 0)
271 {
272 ret = GATE_RESULT_FAILED;
273 }
274 else
275 {
276 2 --te->cur_col;
277 2 ret = gate_texteditor_fix_view(te);
278 }
279 2 break;
280 }
281 1 case GATE_TEXTEDITOR_MOVE_RIGHT:
282 {
283 1 line = gate_slotlist_at(&te->lines, te->cur_row);
284 1 length = gate_strbuilder_length(line);
285
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (te->cur_col >= length)
286 {
287 ret = GATE_RESULT_FAILED;
288 }
289 else
290 {
291 1 ++te->cur_col;
292 1 ret = gate_texteditor_fix_view(te);
293 }
294 1 break;
295 }
296 1 case GATE_TEXTEDITOR_MOVE_UP:
297 {
298
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (te->cur_row <= 0)
299 {
300 ret = GATE_RESULT_FAILED;
301 }
302 else
303 {
304 1 --te->cur_row;
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (te->cur_row < te->window_start_row)
306 {
307 --te->window_start_row;
308 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
309 }
310 1 ret = gate_texteditor_fix_view(te);
311 }
312 1 break;
313 }
314 2 case GATE_TEXTEDITOR_MOVE_DOWN:
315 {
316 2 length = gate_slotlist_length(&te->lines);
317
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (te->cur_row + 1 >= length)
318 {
319 1 return GATE_RESULT_FAILED;
320 }
321 else
322 {
323 1 ++te->cur_row;
324
325
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (te->cur_row >= te->window_start_row + te->window_height)
326 {
327 te->window_start_row = te->cur_row - (te->window_height - 1);
328 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
329 }
330 1 ret = gate_texteditor_fix_view(te);
331 }
332 1 break;
333 }
334 1 case GATE_TEXTEDITOR_MOVE_HOME:
335 {
336 1 te->cur_col = 0;
337 1 ret = gate_texteditor_fix_view(te);
338 1 break;
339 }
340 1 case GATE_TEXTEDITOR_MOVE_END:
341 {
342 1 line = gate_slotlist_at(&te->lines, te->cur_row);
343 1 length = gate_strbuilder_length(line);
344 1 te->cur_col = length;
345 1 ret = gate_texteditor_fix_view(te);
346 1 break;
347 }
348 1 case GATE_TEXTEDITOR_MOVE_PGUP:
349 {
350
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (te->window_start_row >= te->window_height)
351 {
352 te->window_start_row -= te->window_height;
353 }
354 else
355 {
356 1 te->window_start_row = 0;
357 }
358
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (te->cur_row >= te->window_height)
359 {
360 te->cur_row -= te->window_height;
361 }
362 else
363 {
364 1 te->cur_row = 0;
365 }
366 1 ret = gate_texteditor_fix_view(te);
367 1 break;
368 }
369 1 case GATE_TEXTEDITOR_MOVE_PGDOWN:
370 {
371 1 length = gate_slotlist_length(&te->lines);
372
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (te->window_start_row + te->window_height < length)
373 {
374 te->window_start_row += te->window_height;
375 }
376 1 ret = gate_texteditor_fix_view(te);
377 1 break;
378 }
379 }
380 9 return ret;
381 }
382
383 1 gate_result_t gate_texteditor_new_line(gate_texteditor_t* te)
384 {
385 1 gate_result_t ret = GATE_RESULT_FAILED;
386 1 gate_strbuilder_t* line = gate_texteditor_get_line(te, te->cur_row);
387 1 gate_strbuilder_t new_line = GATE_INIT_EMPTY;
388
389 do
390 {
391 gate_strbuilder_t* next_line;
392 gate_size_t line_len;
393
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!line)
394 {
395 ret = GATE_RESULT_FAILED;
396 break;
397 }
398 1 gate_strbuilder_create(&new_line, 0);
399 1 next_line = (gate_strbuilder_t*)gate_slotlist_insert_at(&te->lines, te->cur_row + 1, &new_line);
400 1 gate_strbuilder_release(&new_line);
401
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (NULL == next_line)
402 {
403 ret = GATE_RESULT_FAILED;
404 break;
405 }
406 1 line_len = gate_strbuilder_length(line);
407
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (te->cur_col < line_len)
408 {
409 gate_strbuilder_append_text(next_line, gate_strbuilder_ptr(line, te->cur_col), line_len - te->cur_col);
410 gate_strbuilder_remove(line, te->cur_col, line_len - te->cur_col);
411 }
412 1 te->cur_col = 0;
413 1 ++te->cur_row;
414 1 gate_texteditor_set_state(te, GATE_TEXTEDITOR_STATE_VIEW_DIRTY);
415 1 ret = GATE_RESULT_OK;
416 } while (0);
417
418
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (GATE_SUCCEEDED(ret))
419 {
420 1 return gate_texteditor_fix_view(te);
421 }
422 return ret;
423 }
424
425
426 50 gate_size_t gate_texteditor_get_line_count(gate_texteditor_t const* te)
427 {
428 50 return gate_slotlist_length(&te->lines);
429 }
430
431 3 gate_enumint_t gate_texteditor_get_state(gate_texteditor_t const* te)
432 {
433 3 return te->state;
434 }
435
436 8 void gate_texteditor_set_state(gate_texteditor_t* te, gate_enumint_t state)
437 {
438 8 te->state = state;
439 8 }
440
441 2 gate_size_t gate_texteditor_get_line_view(gate_texteditor_t const* te, gate_size_t line, char const** ptr_text)
442 {
443 gate_strbuilder_t* ptr_line;
444
445 2 ptr_line = (gate_strbuilder_t*)gate_slotlist_at(&te->lines, line);
446
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (ptr_text)
447 {
448 2 *ptr_text = gate_strbuilder_ptr(ptr_line, 0);
449 }
450 2 return gate_strbuilder_length(ptr_line);
451 }
452