aboutsummaryrefslogtreecommitdiff
path: root/examples/dte/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/dte/misc.c')
-rw-r--r--examples/dte/misc.c764
1 files changed, 0 insertions, 764 deletions
diff --git a/examples/dte/misc.c b/examples/dte/misc.c
deleted file mode 100644
index 4ed640b..0000000
--- a/examples/dte/misc.c
+++ /dev/null
@@ -1,764 +0,0 @@
1#include <stdlib.h>
2#include <string.h>
3#include "misc.h"
4#include "buffer.h"
5#include "change.h"
6#include "indent.h"
7#include "move.h"
8#include "options.h"
9#include "regexp.h"
10#include "selection.h"
11#include "util/debug.h"
12#include "util/macros.h"
13#include "util/string.h"
14#include "util/string-view.h"
15#include "util/utf8.h"
16
17typedef struct {
18 String buf;
19 char *indent;
20 size_t indent_len;
21 size_t indent_width;
22 size_t cur_width;
23 size_t text_width;
24} ParagraphFormatter;
25
26static bool line_has_opening_brace(StringView line)
27{
28 static regex_t re;
29 static bool compiled;
30 if (!compiled) {
31 // TODO: Reimplement without using regex
32 static const char pat[] = "\\{[ \t]*(//.*|/\\*.*\\*/[ \t]*)?$";
33 regexp_compile_or_fatal_error(&re, pat, REG_NEWLINE | REG_NOSUB);
34 compiled = true;
35 }
36
37 regmatch_t m;
38 return regexp_exec(&re, line.data, line.length, 0, &m, 0);
39}
40
41static bool line_has_closing_brace(StringView line)
42{
43 strview_trim_left(&line);
44 return line.length > 0 && line.data[0] == '}';
45}
46
47/*
48 * Stupid { ... } block selector.
49 *
50 * Because braces can be inside strings or comments and writing real
51 * parser for many programming languages does not make sense the rules
52 * for selecting a block are made very simple. Line that matches \{\s*$
53 * starts a block and line that matches ^\s*\} ends it.
54 */
55void select_block(View *view)
56{
57 BlockIter sbi, ebi, bi = view->cursor;
58 StringView line;
59 int level = 0;
60
61 // If current line does not match \{\s*$ but matches ^\s*\} then
62 // cursor is likely at end of the block you want to select
63 fetch_this_line(&bi, &line);
64 if (!line_has_opening_brace(line) && line_has_closing_brace(line)) {
65 block_iter_prev_line(&bi);
66 }
67
68 while (1) {
69 fetch_this_line(&bi, &line);
70 if (line_has_opening_brace(line)) {
71 if (level++ == 0) {
72 sbi = bi;
73 block_iter_next_line(&bi);
74 break;
75 }
76 }
77 if (line_has_closing_brace(line)) {
78 level--;
79 }
80
81 if (!block_iter_prev_line(&bi)) {
82 return;
83 }
84 }
85
86 while (1) {
87 fetch_this_line(&bi, &line);
88 if (line_has_closing_brace(line)) {
89 if (--level == 0) {
90 ebi = bi;
91 break;
92 }
93 }
94 if (line_has_opening_brace(line)) {
95 level++;
96 }
97
98 if (!block_iter_next_line(&bi)) {
99 return;
100 }
101 }
102
103 view->cursor = sbi;
104 view->sel_so = block_iter_get_offset(&ebi);
105 view->sel_eo = SEL_EO_RECALC;
106 view->selection = SELECT_LINES;
107
108 mark_all_lines_changed(view->buffer);
109}
110
111static int get_indent_of_matching_brace(const View *view)
112{
113 const LocalOptions *options = &view->buffer->options;
114 BlockIter bi = view->cursor;
115 StringView line;
116 int level = 0;
117
118 while (block_iter_prev_line(&bi)) {
119 fetch_this_line(&bi, &line);
120 if (line_has_opening_brace(line)) {
121 if (level++ == 0) {
122 return get_indent_width(options, &line);
123 }
124 }
125 if (line_has_closing_brace(line)) {
126 level--;
127 }
128 }
129
130 return -1;
131}
132
133void unselect(View *view)
134{
135 view->select_mode = SELECT_NONE;
136 if (view->selection) {
137 view->selection = SELECT_NONE;
138 mark_all_lines_changed(view->buffer);
139 }
140}
141
142void insert_text(View *view, const char *text, size_t size, bool move_after)
143{
144 size_t del_count = 0;
145 if (view->selection) {
146 del_count = prepare_selection(view);
147 unselect(view);
148 }
149 buffer_replace_bytes(view, del_count, text, size);
150 if (move_after) {
151 block_iter_skip_bytes(&view->cursor, size);
152 }
153}
154
155void delete_ch(View *view)
156{
157 size_t size = 0;
158 if (view->selection) {
159 size = prepare_selection(view);
160 unselect(view);
161 } else {
162 const LocalOptions *options = &view->buffer->options;
163 begin_change(CHANGE_MERGE_DELETE);
164 if (options->emulate_tab) {
165 size = get_indent_level_bytes_right(options, &view->cursor);
166 }
167 if (size == 0) {
168 BlockIter bi = view->cursor;
169 size = block_iter_next_column(&bi);
170 }
171 }
172 buffer_delete_bytes(view, size);
173}
174
175void erase(View *view)
176{
177 size_t size = 0;
178 if (view->selection) {
179 size = prepare_selection(view);
180 unselect(view);
181 } else {
182 const LocalOptions *options = &view->buffer->options;
183 begin_change(CHANGE_MERGE_ERASE);
184 if (options->emulate_tab) {
185 size = get_indent_level_bytes_left(options, &view->cursor);
186 block_iter_back_bytes(&view->cursor, size);
187 }
188 if (size == 0) {
189 CodePoint u;
190 size = block_iter_prev_char(&view->cursor, &u);
191 }
192 }
193 buffer_erase_bytes(view, size);
194}
195
196// Go to beginning of whitespace (tabs and spaces) under cursor and
197// return number of whitespace bytes after cursor after moving cursor
198static size_t goto_beginning_of_whitespace(View *view)
199{
200 BlockIter bi = view->cursor;
201 size_t count = 0;
202 CodePoint u;
203
204 // Count spaces and tabs at or after cursor
205 while (block_iter_next_char(&bi, &u)) {
206 if (u != '\t' && u != ' ') {
207 break;
208 }
209 count++;
210 }
211
212 // Count spaces and tabs before cursor
213 while (block_iter_prev_char(&view->cursor, &u)) {
214 if (u != '\t' && u != ' ') {
215 block_iter_next_char(&view->cursor, &u);
216 break;
217 }
218 count++;
219 }
220 return count;
221}
222
223static bool ws_only(const StringView *line)
224{
225 for (size_t i = 0, n = line->length; i < n; i++) {
226 char ch = line->data[i];
227 if (ch != ' ' && ch != '\t') {
228 return false;
229 }
230 }
231 return true;
232}
233
234// Non-empty line can be used to determine size of indentation for the next line
235static bool find_non_empty_line_bwd(BlockIter *bi)
236{
237 block_iter_bol(bi);
238 do {
239 StringView line;
240 fill_line_ref(bi, &line);
241 if (!ws_only(&line)) {
242 return true;
243 }
244 } while (block_iter_prev_line(bi));
245 return false;
246}
247
248static void insert_nl(View *view)
249{
250 size_t del_count = 0;
251 size_t ins_count = 1;
252 char *ins = NULL;
253
254 // Prepare deleted text (selection or whitespace around cursor)
255 if (view->selection) {
256 del_count = prepare_selection(view);
257 unselect(view);
258 } else {
259 // Trim whitespace around cursor
260 del_count = goto_beginning_of_whitespace(view);
261 }
262
263 // Prepare inserted indentation
264 const LocalOptions *options = &view->buffer->options;
265 if (options->auto_indent) {
266 // Current line will be split at cursor position
267 BlockIter bi = view->cursor;
268 size_t len = block_iter_bol(&bi);
269 StringView line;
270 fill_line_ref(&bi, &line);
271 line.length = len;
272 if (ws_only(&line)) {
273 // This line is (or will become) white space only; find previous,
274 // non whitespace only line
275 if (block_iter_prev_line(&bi) && find_non_empty_line_bwd(&bi)) {
276 fill_line_ref(&bi, &line);
277 ins = get_indent_for_next_line(options, &line);
278 }
279 } else {
280 ins = get_indent_for_next_line(options, &line);
281 }
282 }
283
284 begin_change(CHANGE_MERGE_NONE);
285 if (ins) {
286 // Add newline before indent
287 ins_count = strlen(ins);
288 memmove(ins + 1, ins, ins_count);
289 ins[0] = '\n';
290 ins_count++;
291
292 buffer_replace_bytes(view, del_count, ins, ins_count);
293 free(ins);
294 } else {
295 buffer_replace_bytes(view, del_count, "\n", ins_count);
296 }
297 end_change();
298
299 // Move after inserted text
300 block_iter_skip_bytes(&view->cursor, ins_count);
301}
302
303void insert_ch(View *view, CodePoint ch)
304{
305 if (ch == '\n') {
306 insert_nl(view);
307 return;
308 }
309
310 const Buffer *buffer = view->buffer;
311 const LocalOptions *options = &buffer->options;
312 char buf[8];
313 char *ins = buf;
314 char *alloc = NULL;
315 size_t del_count = 0;
316 size_t ins_count = 0;
317
318 if (view->selection) {
319 // Prepare deleted text (selection)
320 del_count = prepare_selection(view);
321 unselect(view);
322 } else if (options->overwrite) {
323 // Delete character under cursor unless we're at end of line
324 BlockIter bi = view->cursor;
325 del_count = block_iter_is_eol(&bi) ? 0 : block_iter_next_column(&bi);
326 } else if (ch == '}' && options->auto_indent && options->brace_indent) {
327 BlockIter bi = view->cursor;
328 StringView curlr;
329 block_iter_bol(&bi);
330 fill_line_ref(&bi, &curlr);
331 if (ws_only(&curlr)) {
332 int width = get_indent_of_matching_brace(view);
333 if (width >= 0) {
334 // Replace current (ws only) line with some indent + '}'
335 block_iter_bol(&view->cursor);
336 del_count = curlr.length;
337 if (width) {
338 alloc = make_indent(options, width);
339 ins = alloc;
340 ins_count = strlen(ins);
341 // '}' will be replace the terminating NUL
342 }
343 }
344 }
345 }
346
347 // Prepare inserted text
348 if (ch == '\t' && options->expand_tab) {
349 ins_count = options->indent_width;
350 static_assert(sizeof(buf) >= INDENT_WIDTH_MAX);
351 memset(ins, ' ', ins_count);
352 } else {
353 u_set_char_raw(ins, &ins_count, ch);
354 }
355
356 // Record change
357 begin_change(del_count ? CHANGE_MERGE_NONE : CHANGE_MERGE_INSERT);
358 buffer_replace_bytes(view, del_count, ins, ins_count);
359 end_change();
360 free(alloc);
361
362 // Move after inserted text
363 block_iter_skip_bytes(&view->cursor, ins_count);
364}
365
366static void join_selection(View *view)
367{
368 size_t count = prepare_selection(view);
369 size_t len = 0, join = 0;
370 BlockIter bi;
371 CodePoint ch = 0;
372
373 unselect(view);
374 bi = view->cursor;
375
376 begin_change_chain();
377 while (count > 0) {
378 if (!len) {
379 view->cursor = bi;
380 }
381
382 count -= block_iter_next_char(&bi, &ch);
383 if (ch == '\t' || ch == ' ') {
384 len++;
385 } else if (ch == '\n') {
386 len++;
387 join++;
388 } else {
389 if (join) {
390 buffer_replace_bytes(view, len, " ", 1);
391 // Skip the space we inserted and the char we read last
392 block_iter_next_char(&view->cursor, &ch);
393 block_iter_next_char(&view->cursor, &ch);
394 bi = view->cursor;
395 }
396 len = 0;
397 join = 0;
398 }
399 }
400
401 // Don't replace last \n that is at end of the selection
402 if (join && ch == '\n') {
403 join--;
404 len--;
405 }
406
407 if (join) {
408 if (ch == '\n') {
409 // Don't add space to end of line
410 buffer_delete_bytes(view, len);
411 } else {
412 buffer_replace_bytes(view, len, " ", 1);
413 }
414 }
415 end_change_chain(view);
416}
417
418void join_lines(View *view)
419{
420 BlockIter bi = view->cursor;
421
422 if (view->selection) {
423 join_selection(view);
424 return;
425 }
426
427 if (!block_iter_next_line(&bi)) {
428 return;
429 }
430 if (block_iter_is_eof(&bi)) {
431 return;
432 }
433
434 BlockIter next = bi;
435 CodePoint u;
436 size_t count = 1;
437 block_iter_prev_char(&bi, &u);
438 while (block_iter_prev_char(&bi, &u)) {
439 if (u != '\t' && u != ' ') {
440 block_iter_next_char(&bi, &u);
441 break;
442 }
443 count++;
444 }
445 while (block_iter_next_char(&next, &u)) {
446 if (u != '\t' && u != ' ') {
447 break;
448 }
449 count++;
450 }
451
452 view->cursor = bi;
453 if (u == '\n') {
454 buffer_delete_bytes(view, count);
455 } else {
456 buffer_replace_bytes(view, count, " ", 1);
457 }
458}
459
460void clear_lines(View *view, bool auto_indent)
461{
462 char *indent = NULL;
463 if (auto_indent) {
464 BlockIter bi = view->cursor;
465 if (block_iter_prev_line(&bi) && find_non_empty_line_bwd(&bi)) {
466 StringView line;
467 fill_line_ref(&bi, &line);
468 indent = get_indent_for_next_line(&view->buffer->options, &line);
469 }
470 }
471
472 size_t del_count = 0;
473 if (view->selection) {
474 view->selection = SELECT_LINES;
475 del_count = prepare_selection(view);
476 unselect(view);
477 // Don't delete last newline
478 if (del_count) {
479 del_count--;
480 }
481 } else {
482 block_iter_eol(&view->cursor);
483 del_count = block_iter_bol(&view->cursor);
484 }
485
486 if (!indent && !del_count) {
487 return;
488 }
489
490 size_t ins_count = indent ? strlen(indent) : 0;
491 buffer_replace_bytes(view, del_count, indent, ins_count);
492 free(indent);
493 block_iter_skip_bytes(&view->cursor, ins_count);
494}
495
496void delete_lines(View *view)
497{
498 long x = view_get_preferred_x(view);
499 size_t del_count;
500 if (view->selection) {
501 view->selection = SELECT_LINES;
502 del_count = prepare_selection(view);
503 unselect(view);
504 } else {
505 block_iter_bol(&view->cursor);
506 BlockIter tmp = view->cursor;
507 del_count = block_iter_eat_line(&tmp);
508 }
509 buffer_delete_bytes(view, del_count);
510 move_to_preferred_x(view, x);
511}
512
513void new_line(View *view, bool above)
514{
515 if (above && block_iter_prev_line(&view->cursor) == 0) {
516 // Already on first line; insert newline at bof
517 block_iter_bol(&view->cursor);
518 buffer_insert_bytes(view, "\n", 1);
519 return;
520 }
521
522 const LocalOptions *options = &view->buffer->options;
523 char *ins = NULL;
524 block_iter_eol(&view->cursor);
525
526 if (options->auto_indent) {
527 BlockIter bi = view->cursor;
528 if (find_non_empty_line_bwd(&bi)) {
529 StringView line;
530 fill_line_ref(&bi, &line);
531 ins = get_indent_for_next_line(options, &line);
532 }
533 }
534
535 size_t ins_count;
536 if (ins) {
537 ins_count = strlen(ins);
538 memmove(ins + 1, ins, ins_count);
539 ins[0] = '\n';
540 ins_count++;
541 buffer_insert_bytes(view, ins, ins_count);
542 free(ins);
543 } else {
544 ins_count = 1;
545 buffer_insert_bytes(view, "\n", 1);
546 }
547
548 block_iter_skip_bytes(&view->cursor, ins_count);
549}
550
551static void add_word(ParagraphFormatter *pf, const char *word, size_t len)
552{
553 size_t i = 0;
554 size_t word_width = 0;
555 while (i < len) {
556 word_width += u_char_width(u_get_char(word, len, &i));
557 }
558
559 if (pf->cur_width && pf->cur_width + 1 + word_width > pf->text_width) {
560 string_append_byte(&pf->buf, '\n');
561 pf->cur_width = 0;
562 }
563
564 if (pf->cur_width == 0) {
565 if (pf->indent_len) {
566 string_append_buf(&pf->buf, pf->indent, pf->indent_len);
567 }
568 pf->cur_width = pf->indent_width;
569 } else {
570 string_append_byte(&pf->buf, ' ');
571 pf->cur_width++;
572 }
573
574 string_append_buf(&pf->buf, word, len);
575 pf->cur_width += word_width;
576}
577
578static bool is_paragraph_separator(const StringView *line)
579{
580 StringView trimmed = *line;
581 strview_trim(&trimmed);
582
583 return
584 trimmed.length == 0
585 // TODO: make this configurable
586 || strview_equal_cstring(&trimmed, "/*")
587 || strview_equal_cstring(&trimmed, "*/")
588 ;
589}
590
591static bool in_paragraph(const LocalOptions *options, const StringView *line, size_t indent_width)
592{
593 if (get_indent_width(options, line) != indent_width) {
594 return false;
595 }
596 return !is_paragraph_separator(line);
597}
598
599static size_t paragraph_size(View *view)
600{
601 const LocalOptions *options = &view->buffer->options;
602 BlockIter bi = view->cursor;
603 StringView line;
604 block_iter_bol(&bi);
605 fill_line_ref(&bi, &line);
606 if (is_paragraph_separator(&line)) {
607 // Not in paragraph
608 return 0;
609 }
610 size_t indent_width = get_indent_width(options, &line);
611
612 // Go to beginning of paragraph
613 while (block_iter_prev_line(&bi)) {
614 fill_line_ref(&bi, &line);
615 if (!in_paragraph(options, &line, indent_width)) {
616 block_iter_eat_line(&bi);
617 break;
618 }
619 }
620 view->cursor = bi;
621
622 // Get size of paragraph
623 size_t size = 0;
624 do {
625 size_t bytes = block_iter_eat_line(&bi);
626 if (!bytes) {
627 break;
628 }
629 size += bytes;
630 fill_line_ref(&bi, &line);
631 } while (in_paragraph(options, &line, indent_width));
632 return size;
633}
634
635void format_paragraph(View *view, size_t text_width)
636{
637 size_t len;
638 if (view->selection) {
639 view->selection = SELECT_LINES;
640 len = prepare_selection(view);
641 } else {
642 len = paragraph_size(view);
643 }
644 if (!len) {
645 return;
646 }
647
648 const LocalOptions *options = &view->buffer->options;
649 char *sel = block_iter_get_bytes(&view->cursor, len);
650 StringView sv = string_view(sel, len);
651 size_t indent_width = get_indent_width(options, &sv);
652 char *indent = make_indent(options, indent_width);
653
654 ParagraphFormatter pf = {
655 .buf = STRING_INIT,
656 .indent = indent,
657 .indent_len = indent ? strlen(indent) : 0,
658 .indent_width = indent_width,
659 .cur_width = 0,
660 .text_width = text_width
661 };
662
663 for (size_t i = 0; true; ) {
664 while (i < len) {
665 size_t tmp = i;
666 if (!u_is_breakable_whitespace(u_get_char(sel, len, &tmp))) {
667 break;
668 }
669 i = tmp;
670 }
671 if (i == len) {
672 break;
673 }
674
675 size_t start = i;
676 while (i < len) {
677 size_t tmp = i;
678 if (u_is_breakable_whitespace(u_get_char(sel, len, &tmp))) {
679 break;
680 }
681 i = tmp;
682 }
683
684 add_word(&pf, sel + start, i - start);
685 }
686
687 if (pf.buf.len) {
688 string_append_byte(&pf.buf, '\n');
689 }
690 buffer_replace_bytes(view, len, pf.buf.buffer, pf.buf.len);
691 if (pf.buf.len) {
692 block_iter_skip_bytes(&view->cursor, pf.buf.len - 1);
693 }
694 string_free(&pf.buf);
695 free(pf.indent);
696 free(sel);
697
698 unselect(view);
699}
700
701void change_case(View *view, char mode)
702{
703 bool was_selecting = false;
704 bool move = true;
705 size_t text_len;
706 if (view->selection) {
707 SelectionInfo info;
708 init_selection(view, &info);
709 view->cursor = info.si;
710 text_len = info.eo - info.so;
711 unselect(view);
712 was_selecting = true;
713 move = !info.swapped;
714 } else {
715 CodePoint u;
716 if (!block_iter_get_char(&view->cursor, &u)) {
717 return;
718 }
719 text_len = u_char_size(u);
720 }
721
722 String dst = string_new(text_len);
723 char *src = block_iter_get_bytes(&view->cursor, text_len);
724 size_t i = 0;
725 switch (mode) {
726 case 'l':
727 while (i < text_len) {
728 CodePoint u = u_to_lower(u_get_char(src, text_len, &i));
729 string_append_codepoint(&dst, u);
730 }
731 break;
732 case 'u':
733 while (i < text_len) {
734 CodePoint u = u_to_upper(u_get_char(src, text_len, &i));
735 string_append_codepoint(&dst, u);
736 }
737 break;
738 case 't':
739 while (i < text_len) {
740 CodePoint u = u_get_char(src, text_len, &i);
741 u = u_is_upper(u) ? u_to_lower(u) : u_to_upper(u);
742 string_append_codepoint(&dst, u);
743 }
744 break;
745 default:
746 BUG("unhandled case mode");
747 }
748
749 buffer_replace_bytes(view, text_len, dst.buffer, dst.len);
750 free(src);
751
752 if (move && dst.len > 0) {
753 if (was_selecting) {
754 // Move cursor back to where it was
755 size_t idx = dst.len;
756 u_prev_char(dst.buffer, &idx);
757 block_iter_skip_bytes(&view->cursor, idx);
758 } else {
759 block_iter_skip_bytes(&view->cursor, dst.len);
760 }
761 }
762
763 string_free(&dst);
764}