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 |