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/edit.h"
20 #include "utils.h"
21
22
delete_char(CmdContext * c,CmdParams * p,gboolean yank)23 static void delete_char(CmdContext *c, CmdParams *p, gboolean yank)
24 {
25 gint end_pos = NTH(p->sci, p->pos, p->num);
26 end_pos = end_pos > p->line_end_pos ? p->line_end_pos : end_pos;
27 if (yank)
28 {
29 c->line_copy = FALSE;
30 SSM(p->sci, SCI_COPYRANGE, p->pos, end_pos);
31 }
32 SSM(p->sci, SCI_DELETERANGE, p->pos, end_pos - p->pos);
33 }
34
35
cmd_delete_char(CmdContext * c,CmdParams * p)36 void cmd_delete_char(CmdContext *c, CmdParams *p)
37 {
38 delete_char(c, p, FALSE);
39 }
40
41
cmd_delete_char_copy(CmdContext * c,CmdParams * p)42 void cmd_delete_char_copy(CmdContext *c, CmdParams *p)
43 {
44 delete_char(c, p, TRUE);
45 }
46
47
delete_char_back(CmdContext * c,CmdParams * p,gboolean yank)48 static void delete_char_back(CmdContext *c, CmdParams *p, gboolean yank)
49 {
50 gint start_pos = NTH(p->sci, p->pos, -p->num);
51 start_pos = start_pos < p->line_start_pos ? p->line_start_pos : start_pos;
52 if (yank)
53 {
54 c->line_copy = FALSE;
55 SSM(p->sci, SCI_COPYRANGE, start_pos, p->pos);
56 }
57 SSM(p->sci, SCI_DELETERANGE, start_pos, p->pos - start_pos);
58 }
59
60
cmd_delete_char_back(CmdContext * c,CmdParams * p)61 void cmd_delete_char_back(CmdContext *c, CmdParams *p)
62 {
63 delete_char_back(c, p, FALSE);
64 }
65
66
cmd_delete_char_back_copy(CmdContext * c,CmdParams * p)67 void cmd_delete_char_back_copy(CmdContext *c, CmdParams *p)
68 {
69 delete_char_back(c, p, TRUE);
70 }
71
72
cmd_clear_right(CmdContext * c,CmdParams * p)73 void cmd_clear_right(CmdContext *c, CmdParams *p)
74 {
75 gint new_line = get_line_number_rel(p->sci, p->num - 1);
76 gint pos = SSM(p->sci, SCI_GETLINEENDPOSITION, new_line, 0);
77
78 c->line_copy = FALSE;
79 SSM(p->sci, SCI_COPYRANGE, p->pos, pos);
80 SSM(p->sci, SCI_DELETERANGE, p->pos, pos - p->pos);
81 }
82
83
insert_eof_nl_if_missing(CmdParams * p)84 static gboolean insert_eof_nl_if_missing(CmdParams *p)
85 {
86 gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0);
87 gint last_line = SSM(p->sci, SCI_GETLINECOUNT, 0, 0);
88 gint line_start_pos = SSM(p->sci, SCI_POSITIONFROMLINE, last_line, 0);
89 gint line_end_pos = SSM(p->sci, SCI_GETLINEENDPOSITION, last_line, 0);
90
91 if (line_start_pos == line_end_pos) {
92 SET_POS(p->sci, line_end_pos, FALSE);
93 SSM(p->sci, SCI_NEWLINE, 0, 0);
94 SET_POS(p->sci, pos, FALSE);
95 return TRUE;
96 }
97 return FALSE;
98 }
99
100
remove_char_from_eof(CmdParams * p)101 static void remove_char_from_eof(CmdParams *p)
102 {
103 gint pos = SSM(p->sci, SCI_GETCURRENTPOS, 0, 0);
104 gint last_line = SSM(p->sci, SCI_GETLINECOUNT, 0, 0);
105 gint line_end_pos = SSM(p->sci, SCI_GETLINEENDPOSITION, last_line, 0);
106
107 SET_POS(p->sci, line_end_pos, FALSE);
108 SSM(p->sci, SCI_DELETEBACK, 0, 0);
109 SET_POS(p->sci, pos, FALSE);
110 }
111
112
cmd_delete_line(CmdContext * c,CmdParams * p)113 void cmd_delete_line(CmdContext *c, CmdParams *p)
114 {
115 gboolean nl_inserted = insert_eof_nl_if_missing(p);
116 gint num = get_line_number_rel(p->sci, p->num);
117 gint end = SSM(p->sci, SCI_POSITIONFROMLINE, num, 0);
118
119 c->line_copy = TRUE;
120 SSM(p->sci, SCI_COPYRANGE, p->line_start_pos, end);
121 SSM(p->sci, SCI_DELETERANGE, p->line_start_pos, end - p->line_start_pos);
122 if (nl_inserted)
123 remove_char_from_eof(p);
124 goto_nonempty(p->sci, GET_CUR_LINE(p->sci), TRUE);
125 }
126
127
cmd_copy_line(CmdContext * c,CmdParams * p)128 void cmd_copy_line(CmdContext *c, CmdParams *p)
129 {
130 gboolean nl_inserted = insert_eof_nl_if_missing(p);
131 gint num = get_line_number_rel(p->sci, p->num);
132 gint end = SSM(p->sci, SCI_POSITIONFROMLINE, num, 0);
133
134 c->line_copy = TRUE;
135 SSM(p->sci, SCI_COPYRANGE, p->line_start_pos, end);
136 if (nl_inserted)
137 remove_char_from_eof(p);
138 }
139
140
cmd_newline(CmdContext * c,CmdParams * p)141 void cmd_newline(CmdContext *c, CmdParams *p)
142 {
143 SSM(p->sci, SCI_NEWLINE, 0, 0);
144 }
145
146
cmd_tab(CmdContext * c,CmdParams * p)147 void cmd_tab(CmdContext *c, CmdParams *p)
148 {
149 SSM(p->sci, SCI_TAB, 0, 0);
150 }
151
152
cmd_del_word_left(CmdContext * c,CmdParams * p)153 void cmd_del_word_left(CmdContext *c, CmdParams *p)
154 {
155 SSM(p->sci, SCI_DELWORDLEFT, 0, 0);
156 }
157
158
cmd_undo(CmdContext * c,CmdParams * p)159 void cmd_undo(CmdContext *c, CmdParams *p)
160 {
161 gint i;
162 for (i = 0; i < p->num; i++)
163 {
164 if (!SSM(p->sci, SCI_CANUNDO, 0, 0))
165 break;
166 SSM(p->sci, SCI_UNDO, 0, 0);
167 }
168 }
169
170
cmd_redo(CmdContext * c,CmdParams * p)171 void cmd_redo(CmdContext *c, CmdParams *p)
172 {
173 gint i;
174 for (i = 0; i < p->num; i++)
175 {
176 if (!SSM(p->sci, SCI_CANREDO, 0, 0))
177 break;
178 SSM(p->sci, SCI_REDO, 0, 0);
179 }
180 }
181
182
paste(CmdContext * c,CmdParams * p,gboolean after)183 static void paste(CmdContext *c, CmdParams *p, gboolean after)
184 {
185 gboolean nl_inserted = FALSE;
186 gint pos;
187 gint i;
188
189 if (c->line_copy)
190 {
191 if (after)
192 {
193 nl_inserted = insert_eof_nl_if_missing(p);
194 pos = SSM(p->sci, SCI_POSITIONFROMLINE, p->line+1, 0);
195 }
196 else
197 pos = p->line_start_pos;
198 }
199 else
200 {
201 pos = p->pos;
202 if (after && pos < p->line_end_pos)
203 pos = NEXT(p->sci, pos);
204 }
205
206 SET_POS(p->sci, pos, TRUE);
207 for (i = 0; i < p->num; i++)
208 SSM(p->sci, SCI_PASTE, 0, 0);
209 if (c->line_copy)
210 {
211 SET_POS(p->sci, pos, TRUE);
212 if (nl_inserted)
213 remove_char_from_eof(p);
214 goto_nonempty(p->sci, GET_CUR_LINE(p->sci), TRUE);
215 }
216 else if (!VI_IS_INSERT(vi_get_mode()))
217 SSM(p->sci, SCI_CHARLEFT, 0, 0);
218 }
219
220
cmd_paste_after(CmdContext * c,CmdParams * p)221 void cmd_paste_after(CmdContext *c, CmdParams *p)
222 {
223 paste(c, p, TRUE);
224 }
225
226
cmd_paste_before(CmdContext * c,CmdParams * p)227 void cmd_paste_before(CmdContext *c, CmdParams *p)
228 {
229 paste(c, p, FALSE);
230 }
231
232
join_lines(CmdContext * c,CmdParams * p,gint line,gint num)233 static void join_lines(CmdContext *c, CmdParams *p, gint line, gint num)
234 {
235 for (int i = 0; i < num; i++)
236 {
237 gint line_start_pos = SSM(p->sci, SCI_POSITIONFROMLINE, line, 0);
238 gint line_end_pos = SSM(p->sci, SCI_GETLINEENDPOSITION, line, 0);
239 gint next_line_end_pos = SSM(p->sci, SCI_GETLINEENDPOSITION, line+1, 0);
240 gint pos = line_end_pos;
241 gint pos_start;
242
243 while (g_ascii_isspace(SSM(p->sci, SCI_GETCHARAT, pos, 0)) && pos > line_start_pos)
244 pos = PREV(p->sci, pos);
245 if (!g_ascii_isspace(SSM(p->sci, SCI_GETCHARAT, pos, 0)))
246 pos = NEXT(p->sci, pos);
247 pos_start = pos;
248
249 pos = line_end_pos;
250 while (g_ascii_isspace(SSM(p->sci, SCI_GETCHARAT, pos, 0)) && pos < next_line_end_pos)
251 pos = NEXT(p->sci, pos);
252
253 SSM(p->sci, SCI_DELETERANGE, pos_start, pos - pos_start);
254 SSM(p->sci, SCI_INSERTTEXT, pos_start, (sptr_t)" ");
255 }
256 }
257
258
cmd_join_lines(CmdContext * c,CmdParams * p)259 void cmd_join_lines(CmdContext *c, CmdParams *p)
260 {
261 gint num = p->num;
262 if (p->num_present && num > 1)
263 num--;
264 join_lines(c, p, p->line, num);
265 }
266
267
cmd_join_lines_sel(CmdContext * c,CmdParams * p)268 void cmd_join_lines_sel(CmdContext *c, CmdParams *p)
269 {
270 join_lines(c, p, p->sel_first_line, p->sel_last_line - p->sel_first_line);
271 vi_set_mode(VI_MODE_COMMAND);
272 }
273
274
replace_char(ScintillaObject * sci,gint pos,gint num,gint line,gboolean force_upper,gboolean force_lower,gunichar repl_char)275 static void replace_char(ScintillaObject *sci, gint pos, gint num, gint line,
276 gboolean force_upper, gboolean force_lower, gunichar repl_char)
277 {
278 gint i;
279 gint max_num;
280 gint last_pos;
281 struct Sci_TextRange tr;
282 gchar *original, *replacement, *repl, *orig;
283
284 max_num = DIFF(sci, pos, SSM(sci, SCI_GETLINEENDPOSITION, line, 0));
285 if (line != -1 && num > max_num)
286 num = max_num;
287
288 max_num = DIFF(sci, pos, SSM(sci, SCI_GETLENGTH, 0, 0));
289 if (num > max_num)
290 num = max_num;
291
292 if (num <= 0)
293 return;
294
295 last_pos = NTH(sci, pos, num);
296 original = g_malloc(last_pos - pos + 1);
297 replacement = g_malloc(6 * num + 1);
298 repl = replacement;
299 orig = original;
300
301 tr.chrg.cpMin = pos;
302 tr.chrg.cpMax = last_pos;
303 tr.lpstrText = orig;
304 SSM(sci, SCI_GETTEXTRANGE, 0, (sptr_t)&tr);
305
306 for (i = 0; i < num; i++)
307 {
308 gunichar c = g_utf8_get_char(orig);
309
310 if (repl_char == 0)
311 {
312 if ((force_upper || g_unichar_islower(c)) && !force_lower)
313 c = g_unichar_toupper(c);
314 else if (force_lower || g_unichar_isupper(c))
315 c = g_unichar_tolower(c);
316 }
317 else
318 {
319 GUnicodeBreakType t = g_unichar_break_type(c);
320 if (t != G_UNICODE_BREAK_CARRIAGE_RETURN &&
321 t != G_UNICODE_BREAK_LINE_FEED)
322 c = repl_char;
323 }
324
325 repl += g_unichar_to_utf8(c, repl);
326 orig = g_utf8_find_next_char(orig, NULL);
327
328 }
329 *repl = '\0';
330
331 SSM(sci, SCI_SETTARGETRANGE, pos, last_pos);
332 SSM(sci, SCI_REPLACETARGET, -1, (sptr_t)replacement);
333
334 if (line != -1)
335 SET_POS(sci, last_pos, TRUE);
336
337 g_free(original);
338 g_free(replacement);
339 }
340
341
cmd_replace_char(CmdContext * c,CmdParams * p)342 void cmd_replace_char(CmdContext *c, CmdParams *p)
343 {
344 gunichar repl = gdk_keyval_to_unicode(p->last_kp->key);
345 replace_char(p->sci, p->pos, p->num, p->line, FALSE, FALSE, repl);
346 }
347
348
cmd_replace_char_sel(CmdContext * c,CmdParams * p)349 void cmd_replace_char_sel(CmdContext *c, CmdParams *p)
350 {
351 gint num = DIFF(p->sci, p->sel_start, p->sel_start + p->sel_len);
352 gunichar repl = gdk_keyval_to_unicode(p->last_kp->key);
353 replace_char(p->sci, p->sel_start, num, -1, FALSE, FALSE, repl);
354 vi_set_mode(VI_MODE_COMMAND);
355 }
356
357
switch_case(CmdContext * c,CmdParams * p,gboolean force_upper,gboolean force_lower)358 static void switch_case(CmdContext *c, CmdParams *p,
359 gboolean force_upper, gboolean force_lower)
360 {
361 if (VI_IS_VISUAL(vi_get_mode()) || p->sel_len > 0)
362 {
363 gint num = DIFF(p->sci, p->sel_start, p->sel_start + p->sel_len);
364 replace_char(p->sci, p->sel_start, num, -1, force_upper, force_lower, 0);
365 vi_set_mode(VI_MODE_COMMAND);
366 }
367 else
368 replace_char(p->sci, p->pos, p->num, p->line, force_upper, force_lower, 0);
369 }
370
371
cmd_switch_case(CmdContext * c,CmdParams * p)372 void cmd_switch_case(CmdContext *c, CmdParams *p)
373 {
374 switch_case(c, p, FALSE, FALSE);
375 }
376
377
cmd_lower_case(CmdContext * c,CmdParams * p)378 void cmd_lower_case(CmdContext *c, CmdParams *p)
379 {
380 switch_case(c, p, FALSE, TRUE);
381 }
382
383
cmd_upper_case(CmdContext * c,CmdParams * p)384 void cmd_upper_case(CmdContext *c, CmdParams *p)
385 {
386 switch_case(c, p, TRUE, FALSE);
387 }
388
389
indent(ScintillaObject * sci,gboolean unindent,gint pos,gint num,gint indent_num)390 static void indent(ScintillaObject *sci, gboolean unindent, gint pos, gint num, gint indent_num)
391 {
392 gint i;
393 gint line_start = SSM(sci, SCI_LINEFROMPOSITION, pos, 0);
394 gint line_count = SSM(sci, SCI_GETLINECOUNT, 0, 0);
395 gint line_end = line_start + num > line_count ? line_count : line_start + num;
396 gint end_pos = SSM(sci, SCI_POSITIONFROMLINE, line_end, 0);
397
398 SSM(sci, SCI_HOME, 0, 0);
399 SSM(sci, SCI_SETSEL, end_pos, pos);
400 for (i = 0; i < indent_num; i++)
401 SSM(sci, unindent ? SCI_BACKTAB : SCI_TAB, 0, 0);
402 goto_nonempty(sci, line_start, TRUE);
403 }
404
405
cmd_indent(CmdContext * c,CmdParams * p)406 void cmd_indent(CmdContext *c, CmdParams *p)
407 {
408 indent(p->sci, FALSE, p->pos, p->num, 1);
409 }
410
411
cmd_unindent(CmdContext * c,CmdParams * p)412 void cmd_unindent(CmdContext *c, CmdParams *p)
413 {
414 indent(p->sci, TRUE, p->pos, p->num, 1);
415 }
416
417
cmd_indent_sel(CmdContext * c,CmdParams * p)418 void cmd_indent_sel(CmdContext *c, CmdParams *p)
419 {
420 indent(p->sci, FALSE, p->sel_start, p->sel_last_line - p->sel_first_line + 1, p->num);
421 vi_set_mode(VI_MODE_COMMAND);
422 }
423
424
cmd_unindent_sel(CmdContext * c,CmdParams * p)425 void cmd_unindent_sel(CmdContext *c, CmdParams *p)
426 {
427 indent(p->sci, TRUE, p->sel_start, p->sel_last_line - p->sel_first_line + 1, p->num);
428 vi_set_mode(VI_MODE_COMMAND);
429 }
430
431
indent_ins(CmdContext * c,CmdParams * p,gboolean indent)432 static void indent_ins(CmdContext *c, CmdParams *p, gboolean indent)
433 {
434 gint delta;
435 SSM(p->sci, SCI_HOME, 0, 0);
436 SSM(p->sci, indent ? SCI_TAB : SCI_BACKTAB, 0, 0);
437 delta = SSM(p->sci, SCI_GETLINEENDPOSITION, p->line, 0) - p->line_end_pos;
438 SET_POS(p->sci, p->pos + delta, TRUE);
439 }
440
441
cmd_indent_ins(CmdContext * c,CmdParams * p)442 void cmd_indent_ins(CmdContext *c, CmdParams *p)
443 {
444 indent_ins(c, p, TRUE);
445 }
446
cmd_unindent_ins(CmdContext * c,CmdParams * p)447 void cmd_unindent_ins(CmdContext *c, CmdParams *p)
448 {
449 indent_ins(c, p, FALSE);
450 }
451
452
copy_char(CmdContext * c,CmdParams * p,gboolean above)453 static void copy_char(CmdContext *c, CmdParams *p, gboolean above)
454 {
455 if ((above && p->line > 0) || (!above && p->line < p->line_num - 1))
456 {
457 gint line = above ? p->line - 1 : p->line + 1;
458 gint col = SSM(p->sci, SCI_GETCOLUMN, p->pos, 0);
459 gint pos = SSM(p->sci, SCI_FINDCOLUMN, line, col);
460 gint line_end = SSM(p->sci, SCI_GETLINEENDPOSITION, line, 0);
461
462 if (pos < line_end)
463 {
464 gchar txt[MAX_CHAR_SIZE];
465 struct Sci_TextRange tr;
466
467 tr.chrg.cpMin = pos;
468 tr.chrg.cpMax = NEXT(p->sci, pos);
469 tr.lpstrText = txt;
470
471 SSM(p->sci, SCI_GETTEXTRANGE, 0, (sptr_t)&tr);
472 SSM(p->sci, SCI_INSERTTEXT, p->pos, (sptr_t)txt);
473 SET_POS(p->sci, NEXT(p->sci, p->pos), TRUE);
474 }
475 }
476 }
477
478
cmd_copy_char_from_above(CmdContext * c,CmdParams * p)479 void cmd_copy_char_from_above(CmdContext *c, CmdParams *p)
480 {
481 copy_char(c, p, TRUE);
482 }
483
484
cmd_copy_char_from_below(CmdContext * c,CmdParams * p)485 void cmd_copy_char_from_below(CmdContext *c, CmdParams *p)
486 {
487 copy_char(c, p, FALSE);
488 }
489
490
cmd_repeat_subst(CmdContext * c,CmdParams * p)491 void cmd_repeat_subst(CmdContext *c, CmdParams *p)
492 {
493 perform_substitute(p->sci, c->substitute_text, p->line, p->line, "");
494 }
495