dte test coverage


Directory: ./
File: src/wrap.c
Date: 2024-12-21 16:03:22
Exec Total Coverage
Lines: 0 81 0.0%
Functions: 0 6 0.0%
Branches: 0 52 0.0%

Line Branch Exec Source
1 #include <stdbool.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "wrap.h"
5 #include "buffer.h"
6 #include "indent.h"
7 #include "misc.h"
8 #include "selection.h"
9 #include "util/string-view.h"
10 #include "util/string.h"
11 #include "util/unicode.h"
12 #include "util/utf8.h"
13
14 typedef struct {
15 String buf;
16 char *indent;
17 size_t indent_len;
18 size_t indent_width;
19 size_t cur_width;
20 size_t text_width;
21 } ParagraphFormatter;
22
23 static void add_word(ParagraphFormatter *pf, const char *word, size_t len)
24 {
25 size_t i = 0;
26 size_t word_width = 0;
27 while (i < len) {
28 word_width += u_char_width(u_get_char(word, len, &i));
29 }
30
31 if (pf->cur_width && pf->cur_width + 1 + word_width > pf->text_width) {
32 string_append_byte(&pf->buf, '\n');
33 pf->cur_width = 0;
34 }
35
36 if (pf->cur_width == 0) {
37 if (pf->indent_len) {
38 string_append_buf(&pf->buf, pf->indent, pf->indent_len);
39 }
40 pf->cur_width = pf->indent_width;
41 } else {
42 string_append_byte(&pf->buf, ' ');
43 pf->cur_width++;
44 }
45
46 string_append_buf(&pf->buf, word, len);
47 pf->cur_width += word_width;
48 }
49
50 static bool is_long_comment_delim(const StringView *sv)
51 {
52 // TODO: make this configurable
53 return strview_equal_cstring(sv, "/*") || strview_equal_cstring(sv, "*/");
54 }
55
56 static bool is_paragraph_separator(const StringView *line)
57 {
58 StringView trimmed = *line;
59 strview_trim(&trimmed);
60 return (trimmed.length == 0) || is_long_comment_delim(&trimmed);
61 }
62
63 static bool in_paragraph (
64 const StringView *line,
65 size_t para_indent_width,
66 unsigned int tab_width
67 ) {
68 size_t w = get_indent_width(line, tab_width);
69 return (w == para_indent_width) && !is_paragraph_separator(line);
70 }
71
72 static size_t paragraph_size(View *view)
73 {
74 BlockIter bi = view->cursor;
75 block_iter_bol(&bi);
76 StringView line = block_iter_get_line(&bi);
77 if (is_paragraph_separator(&line)) {
78 // Not in paragraph
79 return 0;
80 }
81
82 unsigned int tab_width = view->buffer->options.tab_width;
83 size_t para_indent_width = get_indent_width(&line, tab_width);
84
85 // Go to beginning of paragraph
86 while (block_iter_prev_line(&bi)) {
87 line = block_iter_get_line(&bi);
88 if (!in_paragraph(&line, para_indent_width, tab_width)) {
89 block_iter_eat_line(&bi);
90 break;
91 }
92 }
93 view->cursor = bi;
94
95 // Get size of paragraph
96 size_t size = 0;
97 do {
98 size_t bytes = block_iter_eat_line(&bi);
99 if (!bytes) {
100 break;
101 }
102 size += bytes;
103 line = block_iter_get_line(&bi);
104 } while (in_paragraph(&line, para_indent_width, tab_width));
105 return size;
106 }
107
108 void wrap_paragraph(View *view, size_t text_width)
109 {
110 size_t len;
111 if (view->selection) {
112 view->selection = SELECT_LINES;
113 len = prepare_selection(view);
114 } else {
115 len = paragraph_size(view);
116 }
117 if (!len) {
118 return;
119 }
120
121 const LocalOptions *options = &view->buffer->options;
122 char *sel = block_iter_get_bytes(&view->cursor, len);
123 StringView sv = string_view(sel, len);
124 size_t indent_width = get_indent_width(&sv, options->tab_width);
125 char *indent = make_indent(options, indent_width);
126
127 ParagraphFormatter pf = {
128 .buf = STRING_INIT,
129 .indent = indent,
130 .indent_len = indent ? strlen(indent) : 0,
131 .indent_width = indent_width,
132 .cur_width = 0,
133 .text_width = text_width
134 };
135
136 for (size_t i = 0; true; ) {
137 while (i < len) {
138 size_t tmp = i;
139 if (!u_is_breakable_whitespace(u_get_char(sel, len, &tmp))) {
140 break;
141 }
142 i = tmp;
143 }
144 if (i == len) {
145 break;
146 }
147
148 size_t start = i;
149 while (i < len) {
150 size_t tmp = i;
151 if (u_is_breakable_whitespace(u_get_char(sel, len, &tmp))) {
152 break;
153 }
154 i = tmp;
155 }
156
157 add_word(&pf, sel + start, i - start);
158 }
159
160 if (pf.buf.len) {
161 string_append_byte(&pf.buf, '\n');
162 }
163 buffer_replace_bytes(view, len, pf.buf.buffer, pf.buf.len);
164 if (pf.buf.len) {
165 block_iter_skip_bytes(&view->cursor, pf.buf.len - 1);
166 }
167
168 string_free(&pf.buf);
169 free(pf.indent);
170 free(sel);
171 unselect(view);
172 }
173