dte test coverage


Directory: ./
File: src/selection.c
Date: 2025-02-14 16:55:22
Exec Total Coverage
Lines: 53 93 57.0%
Functions: 6 8 75.0%
Branches: 21 50 42.0%

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 4 static regex_t re;
99 4 static bool compiled;
100
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→5) taken 3 times.
4 if (!compiled) {
101 // TODO: Reimplement without using regex
102 1 static const char pat[] = "\\{[ \t]*(//.*|/\\*.*\\*/[ \t]*)?$";
103 1 regexp_compile_or_fatal_error(&re, pat, REG_NEWLINE | REG_NOSUB);
104 1 compiled = true;
105 }
106
107 4 regmatch_t m;
108 4 return regexp_exec(&re, line.data, line.length, 0, &m, 0);
109 }
110
111 3 bool line_has_closing_brace(StringView line)
112 {
113 3 strview_trim_left(&line);
114
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] == '}';
115 }
116
117 /*
118 * Stupid { ... } block selector.
119 *
120 * Because braces can be inside strings or comments and writing real
121 * parser for many programming languages does not make sense the rules
122 * for selecting a block are made very simple. Line that matches \{\s*$
123 * starts a block and line that matches ^\s*\} ends it.
124 */
125 void select_block(View *view)
126 {
127 BlockIter bi = view->cursor;
128 StringView line;
129 fetch_this_line(&bi, &line);
130
131 // If current line does not match \{\s*$ but matches ^\s*\} then
132 // cursor is likely at end of the block you want to select
133 if (!line_has_opening_brace(line) && line_has_closing_brace(line)) {
134 block_iter_prev_line(&bi);
135 }
136
137 BlockIter sbi;
138 int level = 0;
139 while (1) {
140 fetch_this_line(&bi, &line);
141 if (line_has_opening_brace(line)) {
142 if (level++ == 0) {
143 sbi = bi;
144 block_iter_next_line(&bi);
145 break;
146 }
147 }
148 if (line_has_closing_brace(line)) {
149 level--;
150 }
151
152 if (!block_iter_prev_line(&bi)) {
153 return;
154 }
155 }
156
157 BlockIter ebi;
158 while (1) {
159 fetch_this_line(&bi, &line);
160 if (line_has_closing_brace(line)) {
161 if (--level == 0) {
162 ebi = bi;
163 break;
164 }
165 }
166 if (line_has_opening_brace(line)) {
167 level++;
168 }
169
170 if (!block_iter_next_line(&bi)) {
171 return;
172 }
173 }
174
175 view->cursor = sbi;
176 view->sel_so = block_iter_get_offset(&ebi);
177 view->sel_eo = SEL_EO_RECALC;
178 view->selection = SELECT_LINES;
179 mark_all_lines_changed(view->buffer);
180 }
181