dte test coverage


Directory: ./
File: src/selection.c
Date: 2025-09-07 23:01:39
Exec Total Coverage
Lines: 60 105 57.1%
Functions: 7 9 77.8%
Branches: 24 56 42.9%

Line Branch Exec Source
1 #include "selection.h"
2 #include "editor.h"
3 #include "regexp.h"
4 #include "util/unicode.h"
5
6 7 static bool include_cursor_char_in_selection(const View *view)
7 {
8 7 const EditorState *e = view->window->editor;
9
1/2
✓ Branch 0 (2→3) taken 7 times.
✗ Branch 1 (2→8) not taken.
7 if (!e->options.select_cursor_char) {
10 return false;
11 }
12
13 7 bool overwrite = view->buffer->options.overwrite;
14
1/2
✓ Branch 0 (3→4) taken 7 times.
✗ Branch 1 (3→5) not taken.
7 CursorInputMode mode = overwrite ? CURSOR_MODE_OVERWRITE : CURSOR_MODE_INSERT;
15 7 TermCursorType type = e->cursor_styles[mode].type;
16
1/2
✓ Branch 0 (5→6) taken 7 times.
✗ Branch 1 (5→7) not taken.
7 if (type == CURSOR_KEEP) {
17 7 type = e->cursor_styles[CURSOR_MODE_DEFAULT].type;
18 }
19
20 // If "select-cursor-char" option is true, include character under cursor
21 // in selections for any cursor type except bars (where it makes no sense
22 // to do so)
23 7 return !(type == CURSOR_STEADY_BAR || type == CURSOR_BLINKING_BAR);
24 }
25
26 22 SelectionInfo init_selection(const View *view)
27 {
28 22 size_t so = view->sel_so;
29 22 size_t eo = block_iter_get_offset(&view->cursor);
30 22 bool swapped = (so > eo);
31
32 66 SelectionInfo info = {
33 .si = view->cursor,
34
2/2
✓ Branch 0 (3→4) taken 8 times.
✓ Branch 1 (3→5) taken 14 times.
22 .so = swapped ? eo : so,
35
2/2
✓ Branch 0 (5→6) taken 8 times.
✓ Branch 1 (5→7) taken 14 times.
22 .eo = swapped ? so : eo,
36 .swapped = swapped,
37 };
38
39
2/2
✓ Branch 0 (7→8) taken 8 times.
✓ Branch 1 (7→9) taken 14 times.
22 if (!swapped) {
40 8 block_iter_goto_offset(&info.si, so);
41 }
42
43 22 BlockIter ei = info.si;
44 22 block_iter_skip_bytes(&ei, info.eo - info.so);
45
2/2
✓ Branch 0 (10→11) taken 1 times.
✓ Branch 1 (10→15) taken 21 times.
22 if (block_iter_is_eof(&ei)) {
46
1/2
✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 1 times.
1 if (info.so == info.eo) {
47 return info;
48 }
49 1 CodePoint u;
50 1 info.eo -= block_iter_prev_char(&ei, &u);
51 }
52
53
2/2
✓ Branch 0 (15→16) taken 15 times.
✓ Branch 1 (15→19) taken 7 times.
22 if (view->selection == SELECT_LINES) {
54 15 info.so -= block_iter_bol(&info.si);
55 15 info.eo += block_iter_eat_line(&ei);
56
1/2
✓ Branch 0 (19→20) taken 7 times.
✗ Branch 1 (19→22) not taken.
7 } else if (include_cursor_char_in_selection(view)) {
57 7 info.eo += block_iter_next_column(&ei);
58 }
59
60 22 return info;
61 }
62
63 11 size_t prepare_selection(View *view)
64 {
65 11 SelectionInfo info = init_selection(view);
66 11 view->cursor = info.si;
67 11 return info.eo - info.so;
68 }
69
70 9 size_t get_nr_selected_lines(const SelectionInfo *info)
71 {
72 9 BlockIter bi = info->si;
73 9 size_t nr_lines = 0;
74
75
2/2
✓ Branch 0 (8→3) taken 45 times.
✓ Branch 1 (8→9) taken 9 times.
54 for (size_t pos = info->so, eo = info->eo; pos < eo; nr_lines++) {
76 45 pos += block_iter_eat_line(&bi);
77 45 BUG_ON(block_iter_is_eof(&bi) && pos != info->eo);
78 }
79
80 9 return nr_lines;
81 }
82
83 size_t get_nr_selected_chars(const SelectionInfo *info)
84 {
85 BlockIter bi = info->si;
86 size_t nr_chars = 0;
87 CodePoint u;
88
89 for (size_t pos = info->so, eo = info->eo; pos < eo; nr_chars++) {
90 pos += block_iter_next_char(&bi, &u);
91 }
92
93 return nr_chars;
94 }
95
96 4 bool line_has_opening_brace(StringView line)
97 {
98 // TODO: Reimplement without using regex
99 4 static const regex_t *re;
100
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→5) taken 3 times.
4 if (!re) {
101 1 re = regexp_compile_or_fatal_error("\\{[ \t]*(//.*|/\\*.*\\*/[ \t]*)?$");
102 }
103
104 4 return regexp_exec(re, line.data, line.length, 0, NULL, 0);
105 }
106
107 3 bool line_has_closing_brace(StringView line)
108 {
109 3 strview_trim_left(&line);
110
2/4
✓ Branch 0 (3→4) taken 3 times.
✗ Branch 1 (3→5) not taken.
✓ Branch 2 (4→5) taken 3 times.
✗ Branch 3 (4→6) not taken.
3 return line.length > 0 && line.data[0] == '}';
111 }
112
113 /*
114 * Stupid { ... } block selector.
115 *
116 * Because braces can be inside strings or comments and writing real
117 * parser for many programming languages does not make sense the rules
118 * for selecting a block are made very simple. Line that matches \{\s*$
119 * starts a block and line that matches ^\s*\} ends it.
120 */
121 void select_block(View *view)
122 {
123 BlockIter bi = view->cursor;
124 StringView line = get_current_line(&bi);
125
126 // If current line does not match \{\s*$ but matches ^\s*\} then
127 // cursor is likely at end of the block you want to select
128 if (!line_has_opening_brace(line) && line_has_closing_brace(line)) {
129 block_iter_prev_line(&bi);
130 }
131
132 BlockIter sbi;
133 int level = 0;
134 while (1) {
135 line = get_current_line(&bi);
136 if (line_has_opening_brace(line)) {
137 if (level++ == 0) {
138 sbi = bi;
139 block_iter_next_line(&bi);
140 break;
141 }
142 }
143 if (line_has_closing_brace(line)) {
144 level--;
145 }
146
147 if (!block_iter_prev_line(&bi)) {
148 return;
149 }
150 }
151
152 BlockIter ebi;
153 while (1) {
154 line = get_current_line(&bi);
155 if (line_has_closing_brace(line)) {
156 if (--level == 0) {
157 ebi = bi;
158 break;
159 }
160 }
161 if (line_has_opening_brace(line)) {
162 level++;
163 }
164
165 if (!block_iter_next_line(&bi)) {
166 return;
167 }
168 }
169
170 view->cursor = sbi;
171 view->sel_so = block_iter_get_offset(&ebi);
172 view->sel_eo = SEL_EO_RECALC;
173 view->selection = SELECT_LINES;
174 mark_all_lines_changed(view->buffer);
175 }
176
177 16 void view_do_set_selection_type(View *view, SelectionType sel)
178 {
179 // Should only be called from view_set_selection_type()
180 16 BUG_ON(sel == view->selection);
181
182
2/2
✓ Branch 0 (4→5) taken 1 times.
✓ Branch 1 (4→7) taken 15 times.
16 if (sel == SELECT_NONE) {
183 1 unselect(view);
184 1 return;
185 }
186
187
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→11) taken 15 times.
15 if (view->selection) {
188 if (view->selection != sel) {
189 view->selection = sel;
190 // TODO: be less brute force about this; only the first/last
191 // line of the selection can change in this case
192 mark_all_lines_changed(view->buffer);
193 }
194 return;
195 }
196
197 15 view->sel_so = block_iter_get_offset(&view->cursor);
198 15 view->sel_eo = SEL_EO_RECALC;
199 15 view->selection = sel;
200
201 // Need to mark current line changed because cursor might
202 // move up or down before screen is updated
203 15 view_update_cursor_y(view);
204 15 buffer_mark_lines_changed(view->buffer, view->cy, view->cy);
205 }
206