1 /*
2 * Copyright 2018 Jiri Techet <techet@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include "cmds/motion.h"
20 #include "utils.h"
21
22
cmd_goto_left(CmdContext * c,CmdParams * p)23 void cmd_goto_left(CmdContext *c, CmdParams *p)
24 {
25 gint i;
26 gint start_pos = p->line_start_pos;
27 gint pos = p->pos;
28 for (i = 0; i < p->num && pos > start_pos; i++)
29 pos = PREV(p->sci, pos);
30 SET_POS(p->sci, pos, TRUE);
31 }
32
33
cmd_goto_right(CmdContext * c,CmdParams * p)34 void cmd_goto_right(CmdContext *c, CmdParams *p)
35 {
36 gint i;
37 gint pos = p->pos;
38 for (i = 0; i < p->num && pos < p->line_end_pos; i++)
39 pos = NEXT(p->sci, pos);
40 SET_POS(p->sci, pos, TRUE);
41 }
42
43
cmd_goto_up(CmdContext * c,CmdParams * p)44 void cmd_goto_up(CmdContext *c, CmdParams *p)
45 {
46 gint one_above, pos;
47
48 if (p->line == 0)
49 return;
50
51 /* Calling SCI_LINEUP/SCI_LINEDOWN in a loop for num lines leads to visible
52 * slow scrolling. On the other hand, SCI_LINEUP preserves the value of
53 * SCI_CHOOSECARETX which we cannot read directly from Scintilla and which
54 * we want to keep - perform jump to previous/following line and add
55 * one final SCI_LINEUP/SCI_LINEDOWN which recovers SCI_CHOOSECARETX for us. */
56 one_above = p->line - p->num - 1;
57 if (one_above >= 0)
58 {
59 /* Every case except for the first line - go one line above and perform
60 * SCI_LINEDOWN. This ensures that even with wrapping on, we get the
61 * caret on the first line of the wrapped line */
62 pos = SSM(p->sci, SCI_GETLINEENDPOSITION, one_above, 0);
63 SET_POS_NOX(p->sci, pos, FALSE);
64 SSM(p->sci, SCI_LINEDOWN, 0, 0);
65 }
66 else
67 {
68 /* This is the first line and there is no line above - we need to go to
69 * the following line and do SCI_LINEUP. In addition, when wrapping is
70 * on, we need to repeat SCI_LINEUP to get to the first line of wrapping.
71 * This may lead to visible slow scrolling which is why there's the
72 * fast case above for anything else but the first line. */
73 gint one_below = p->line - p->num + 1;
74 gint wrap_count;
75
76 one_below = one_below > 0 ? one_below : 1;
77 pos = SSM(p->sci, SCI_POSITIONFROMLINE, one_below, 0);
78 SET_POS_NOX(p->sci, pos, FALSE);
79 SSM(p->sci, SCI_LINEUP, 0, 0);
80
81 wrap_count = SSM(p->sci, SCI_WRAPCOUNT, GET_CUR_LINE(p->sci), 0);
82 while (wrap_count > 1)
83 {
84 SSM(p->sci, SCI_LINEUP, 0, 0);
85 wrap_count--;
86 }
87 }
88 }
89
90
cmd_goto_up_nonempty(CmdContext * c,CmdParams * p)91 void cmd_goto_up_nonempty(CmdContext *c, CmdParams *p)
92 {
93 cmd_goto_up(c, p);
94 goto_nonempty(p->sci, GET_CUR_LINE(p->sci), TRUE);
95 }
96
97
goto_down(CmdParams * p,gint num)98 static void goto_down(CmdParams *p, gint num)
99 {
100 gint one_above, pos;
101 gint last_line = p->line_num - 1;
102
103 if (p->line == last_line)
104 return;
105
106 /* see cmd_goto_up() for explanation */
107 one_above = p->line + num - 1;
108 one_above = one_above < last_line ? one_above : last_line - 1;
109 pos = SSM(p->sci, SCI_GETLINEENDPOSITION, one_above, 0);
110 SET_POS_NOX(p->sci, pos, FALSE);
111 SSM(p->sci, SCI_LINEDOWN, 0, 0);
112 }
113
114
cmd_goto_down(CmdContext * c,CmdParams * p)115 void cmd_goto_down(CmdContext *c, CmdParams *p)
116 {
117 goto_down(p, p->num);
118 }
119
120
cmd_goto_down_nonempty(CmdContext * c,CmdParams * p)121 void cmd_goto_down_nonempty(CmdContext *c, CmdParams *p)
122 {
123 goto_down(p, p->num);
124 goto_nonempty(p->sci, GET_CUR_LINE(p->sci), TRUE);
125 }
126
127
cmd_goto_down_one_less_nonempty(CmdContext * c,CmdParams * p)128 void cmd_goto_down_one_less_nonempty(CmdContext *c, CmdParams *p)
129 {
130 if (p->num > 1)
131 goto_down(p, p->num - 1);
132 goto_nonempty(p->sci, GET_CUR_LINE(p->sci), TRUE);
133 }
134
135
cmd_goto_page_up(CmdContext * c,CmdParams * p)136 void cmd_goto_page_up(CmdContext *c, CmdParams *p)
137 {
138 gint shift = p->line_visible_num * p->num;
139 gint new_line = get_line_number_rel(p->sci, -shift);
140 goto_nonempty(p->sci, new_line, TRUE);
141 }
142
143
cmd_goto_page_down(CmdContext * c,CmdParams * p)144 void cmd_goto_page_down(CmdContext *c, CmdParams *p)
145 {
146 gint shift = p->line_visible_num * p->num;
147 gint new_line = get_line_number_rel(p->sci, shift);
148 goto_nonempty(p->sci, new_line, TRUE);
149 }
150
151
cmd_goto_halfpage_up(CmdContext * c,CmdParams * p)152 void cmd_goto_halfpage_up(CmdContext *c, CmdParams *p)
153 {
154 gint shift = p->num_present ? p->num : p->line_visible_num / 2;
155 gint new_line = get_line_number_rel(p->sci, -shift);
156 goto_nonempty(p->sci, new_line, TRUE);
157 }
158
159
cmd_goto_halfpage_down(CmdContext * c,CmdParams * p)160 void cmd_goto_halfpage_down(CmdContext *c, CmdParams *p)
161 {
162 gint shift = p->num_present ? p->num : p->line_visible_num / 2;
163 gint new_line = get_line_number_rel(p->sci, shift);
164 goto_nonempty(p->sci, new_line, TRUE);
165 }
166
167
cmd_goto_line(CmdContext * c,CmdParams * p)168 void cmd_goto_line(CmdContext *c, CmdParams *p)
169 {
170 gint num = p->num > p->line_num ? p->line_num : p->num;
171 goto_nonempty(p->sci, num - 1, TRUE);
172 }
173
174
cmd_goto_line_last(CmdContext * c,CmdParams * p)175 void cmd_goto_line_last(CmdContext *c, CmdParams *p)
176 {
177 gint num = p->num > p->line_num ? p->line_num : p->num;
178 if (!p->num_present)
179 num = p->line_num;
180 goto_nonempty(p->sci, num - 1, TRUE);
181 }
182
183
cmd_goto_screen_top(CmdContext * c,CmdParams * p)184 void cmd_goto_screen_top(CmdContext *c, CmdParams *p)
185 {
186 gint top = p->line_visible_first;
187 gint count = p->line_visible_num;
188 gint line = top + p->num;
189 goto_nonempty(p->sci, line > top + count ? top + count : line, FALSE);
190 }
191
192
cmd_goto_screen_middle(CmdContext * c,CmdParams * p)193 void cmd_goto_screen_middle(CmdContext *c, CmdParams *p)
194 {
195 goto_nonempty(p->sci, p->line_visible_first + p->line_visible_num/2, FALSE);
196 }
197
198
cmd_goto_screen_bottom(CmdContext * c,CmdParams * p)199 void cmd_goto_screen_bottom(CmdContext *c, CmdParams *p)
200 {
201 gint top = p->line_visible_first;
202 gint count = p->line_visible_num;
203 gint line = top + count - p->num;
204 goto_nonempty(p->sci, line < top ? top : line, FALSE);
205 }
206
207
cmd_goto_doc_percentage(CmdContext * c,CmdParams * p)208 void cmd_goto_doc_percentage(CmdContext *c, CmdParams *p)
209 {
210 if (p->num > 100)
211 p->num = 100;
212
213 goto_nonempty(p->sci, (p->line_num * p->num) / 100, TRUE);
214 }
215
216
cmd_goto_line_start(CmdContext * c,CmdParams * p)217 void cmd_goto_line_start(CmdContext *c, CmdParams *p)
218 {
219 SSM(p->sci, SCI_HOME, 0, 0);
220 }
221
222
cmd_goto_line_start_nonempty(CmdContext * c,CmdParams * p)223 void cmd_goto_line_start_nonempty(CmdContext *c, CmdParams *p)
224 {
225 goto_nonempty(p->sci, p->line, TRUE);
226 }
227
228
cmd_goto_line_end(CmdContext * c,CmdParams * p)229 void cmd_goto_line_end(CmdContext *c, CmdParams *p)
230 {
231 if (p->num > 1)
232 goto_down(p, p->num - 1);
233 SSM(p->sci, SCI_LINEEND, 0, 0);
234 }
235
236
cmd_goto_column(CmdContext * c,CmdParams * p)237 void cmd_goto_column(CmdContext *c, CmdParams *p)
238 {
239 gint pos = SSM(p->sci, SCI_FINDCOLUMN, p->line, p->num - 1);
240 SET_POS(p->sci, pos, TRUE);
241 }
242
243
cmd_goto_matching_brace(CmdContext * c,CmdParams * p)244 void cmd_goto_matching_brace(CmdContext *c, CmdParams *p)
245 {
246 gint pos = p->pos;
247 while (pos < p->line_end_pos)
248 {
249 gint matching_pos = SSM(p->sci, SCI_BRACEMATCH, pos, 0);
250 if (matching_pos != -1)
251 {
252 SET_POS(p->sci, matching_pos, TRUE);
253 return;
254 }
255 pos++;
256 }
257 }
258
259
find_char(CmdContext * c,CmdParams * p,gboolean invert)260 static void find_char(CmdContext *c, CmdParams *p, gboolean invert)
261 {
262 struct Sci_TextToFind ttf;
263 gboolean forward;
264 gint pos = p->pos;
265 gint i;
266
267 if (!c->search_char)
268 return;
269
270 forward = c->search_char[0] == 'f' || c->search_char[0] == 't';
271 forward = !forward != !invert;
272 ttf.lpstrText = c->search_char + 1;
273
274 for (i = 0; i < p->num; i++)
275 {
276 gint new_pos;
277
278 if (forward)
279 {
280 ttf.chrg.cpMin = NEXT(p->sci, pos);
281 ttf.chrg.cpMax = p->line_end_pos;
282 }
283 else
284 {
285 ttf.chrg.cpMin = pos;
286 ttf.chrg.cpMax = p->line_start_pos;
287 }
288
289 new_pos = SSM(p->sci, SCI_FINDTEXT, 0, (sptr_t)&ttf);
290 if (new_pos < 0)
291 break;
292 pos = new_pos;
293 }
294
295 if (pos >= 0)
296 {
297 if (c->search_char[0] == 't')
298 pos = PREV(p->sci, pos);
299 else if (c->search_char[0] == 'T')
300 pos = NEXT(p->sci, pos);
301 SET_POS(p->sci, pos, TRUE);
302 }
303 }
304
305
cmd_goto_next_char(CmdContext * c,CmdParams * p)306 void cmd_goto_next_char(CmdContext *c, CmdParams *p)
307 {
308 g_free(c->search_char);
309 c->search_char = g_strconcat("f", kp_to_str(p->last_kp), NULL);
310 find_char(c, p, FALSE);
311 }
312
313
cmd_goto_prev_char(CmdContext * c,CmdParams * p)314 void cmd_goto_prev_char(CmdContext *c, CmdParams *p)
315 {
316 g_free(c->search_char);
317 c->search_char = g_strconcat("F", kp_to_str(p->last_kp), NULL);
318 find_char(c, p, FALSE);
319 }
320
321
cmd_goto_next_char_before(CmdContext * c,CmdParams * p)322 void cmd_goto_next_char_before(CmdContext *c, CmdParams *p)
323 {
324 g_free(c->search_char);
325 c->search_char = g_strconcat("t", kp_to_str(p->last_kp), NULL);
326 find_char(c, p, FALSE);
327 }
328
329
cmd_goto_prev_char_before(CmdContext * c,CmdParams * p)330 void cmd_goto_prev_char_before(CmdContext *c, CmdParams *p)
331 {
332 g_free(c->search_char);
333 c->search_char = g_strconcat("T", kp_to_str(p->last_kp), NULL);
334 find_char(c, p, FALSE);
335 }
336
337
cmd_goto_char_repeat(CmdContext * c,CmdParams * p)338 void cmd_goto_char_repeat(CmdContext *c, CmdParams *p)
339 {
340 find_char(c, p, FALSE);
341 }
342
343
cmd_goto_char_repeat_opposite(CmdContext * c,CmdParams * p)344 void cmd_goto_char_repeat_opposite(CmdContext *c, CmdParams *p)
345 {
346 find_char(c, p, TRUE);
347 }
348
349
cmd_scroll_up(CmdContext * c,CmdParams * p)350 void cmd_scroll_up(CmdContext *c, CmdParams *p)
351 {
352 SSM(p->sci, SCI_LINESCROLL, 0, -p->num);
353 }
354
355
cmd_scroll_down(CmdContext * c,CmdParams * p)356 void cmd_scroll_down(CmdContext *c, CmdParams *p)
357 {
358 SSM(p->sci, SCI_LINESCROLL, 0, p->num);
359 }
360
361
scroll_to_line(CmdParams * p,gint offset,gboolean nonempty)362 static void scroll_to_line(CmdParams *p, gint offset, gboolean nonempty)
363 {
364 gint column = SSM(p->sci, SCI_GETCOLUMN, p->pos, 0);
365 gint line = p->line;
366
367 if (p->num_present)
368 line = p->num - 1;
369 if (nonempty)
370 goto_nonempty(p->sci, line, FALSE);
371 else
372 {
373 gint pos = SSM(p->sci, SCI_FINDCOLUMN, line, column);
374 SET_POS_NOX(p->sci, pos, FALSE);
375 }
376 SSM(p->sci, SCI_SETFIRSTVISIBLELINE, line + offset, 0);
377 }
378
379
cmd_scroll_center(CmdContext * c,CmdParams * p)380 void cmd_scroll_center(CmdContext *c, CmdParams *p)
381 {
382 scroll_to_line(p, - p->line_visible_num / 2, FALSE);
383 }
384
385
cmd_scroll_top(CmdContext * c,CmdParams * p)386 void cmd_scroll_top(CmdContext *c, CmdParams *p)
387 {
388 scroll_to_line(p, 0, FALSE);
389 }
390
391
cmd_scroll_bottom(CmdContext * c,CmdParams * p)392 void cmd_scroll_bottom(CmdContext *c, CmdParams *p)
393 {
394 scroll_to_line(p, - p->line_visible_num + 1, FALSE);
395 }
396
397
cmd_scroll_center_nonempty(CmdContext * c,CmdParams * p)398 void cmd_scroll_center_nonempty(CmdContext *c, CmdParams *p)
399 {
400 scroll_to_line(p, - p->line_visible_num / 2, TRUE);
401 }
402
403
cmd_scroll_top_nonempty(CmdContext * c,CmdParams * p)404 void cmd_scroll_top_nonempty(CmdContext *c, CmdParams *p)
405 {
406 scroll_to_line(p, 0, TRUE);
407 }
408
409
cmd_scroll_top_next_nonempty(CmdContext * c,CmdParams * p)410 void cmd_scroll_top_next_nonempty(CmdContext *c, CmdParams *p)
411 {
412 if (p->num_present)
413 cmd_scroll_top_nonempty(c, p);
414 else
415 {
416 gint line = p->line_visible_first + p->line_visible_num;
417 goto_nonempty(p->sci, line, FALSE);
418 SSM(p->sci, SCI_SETFIRSTVISIBLELINE, line, 0);
419 }
420 }
421
422
cmd_scroll_bottom_nonempty(CmdContext * c,CmdParams * p)423 void cmd_scroll_bottom_nonempty(CmdContext *c, CmdParams *p)
424 {
425 scroll_to_line(p, - p->line_visible_num + 1, TRUE);
426 }
427