1 #ifdef HAVE_CONFIG_H
2 # include "elementary_config.h"
3 #endif
4 
5 #include "Elementary.h"
6 
7 #include "elm_code_widget_private.h"
8 
9 static int
_elm_code_widget_text_line_number_width_get(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd)10 _elm_code_widget_text_line_number_width_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd)
11 {
12    int max;
13 
14    max = elm_code_file_lines_get(pd->code->file);
15 
16    // leave space for 2 digits minimum
17    if (max < 10)
18      max = 10;
19 
20    return floor(log10(max)) + 1;
21 }
22 
23 static int
_elm_code_widget_text_left_gutter_width_get(Eo * obj,Elm_Code_Widget_Data * pd)24 _elm_code_widget_text_left_gutter_width_get(Eo *obj, Elm_Code_Widget_Data *pd)
25 {
26    Elm_Code_Widget *widget;
27    int width = 1; // the status icon, for now
28 
29    widget = obj;
30    if (!widget)
31      return width;
32 
33    if (pd->show_line_numbers)
34      width += _elm_code_widget_text_line_number_width_get(widget, pd);
35 
36    return width;
37 }
38 
39 static char *
_elm_code_widget_text_multi_get(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd,unsigned int start_line,unsigned int start_col,unsigned int end_line,unsigned int end_col)40 _elm_code_widget_text_multi_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd,
41                                 unsigned int start_line, unsigned int start_col,
42                                 unsigned int end_line, unsigned int end_col)
43 {
44    Elm_Code_Line *line;
45    char *first, *last, *ret, *ptr;
46    const char *newline;
47    short newline_len;
48    int ret_len;
49    unsigned int row, start, end;
50 
51    newline = elm_code_file_line_ending_chars_get(pd->code->file, &newline_len);
52 
53    line = elm_code_file_line_get(pd->code->file, start_line);
54    start = elm_code_widget_line_text_position_for_column_get(widget, line, start_col);
55    first = elm_code_line_text_substr(line, start, line->length - start);
56 
57    line = elm_code_file_line_get(pd->code->file, end_line);
58    end = elm_code_widget_line_text_position_for_column_get(widget, line, end_col + 1);
59    last = elm_code_line_text_substr(line, 0, end);
60 
61    ret_len = strlen(first) + strlen(last) + newline_len;
62 
63    for (row = start_line + 1; row < end_line; row++)
64      {
65         line = elm_code_file_line_get(pd->code->file, row);
66         ret_len += line->length + newline_len;
67      }
68 
69    ret = malloc(sizeof(char) * (ret_len + 1));
70    if (!ret) goto end;
71 
72    snprintf(ret, strlen(first) + newline_len + 1, "%s%s", first, newline);
73 
74    ptr = ret;
75    ptr += strlen(first) + newline_len;
76 
77    for (row = start_line + 1; row < end_line; row++)
78      {
79         line = elm_code_file_line_get(pd->code->file, row);
80         if (line->length > 0)
81           snprintf(ptr, line->length + 1, "%s", elm_code_line_text_get(line, NULL));
82 
83         snprintf(ptr + line->length, newline_len + 1, "%s", newline);
84         ptr += line->length + newline_len;
85      }
86    snprintf(ptr, strlen(last) + 1, "%s", last);
87 
88 end:
89    free(first);
90    free(last);
91    return ret;
92 }
93 
94 static char *
_elm_code_widget_text_single_get(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd,unsigned int start_line,unsigned int start_col,unsigned int end_col)95 _elm_code_widget_text_single_get(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd,
96                                            unsigned int start_line, unsigned int start_col,
97                                            unsigned int end_col)
98 {
99    Elm_Code_Line *line;
100    unsigned int start, end;
101 
102    line = elm_code_file_line_get(pd->code->file, start_line);
103    start = elm_code_widget_line_text_position_for_column_get(widget, line, start_col);
104    end = elm_code_widget_line_text_position_for_column_get(widget, line, end_col + 1);
105 
106    return elm_code_line_text_substr(line, start, end - start);
107 }
108 
109 static char *
_elm_code_widget_text_between_positions_get(Eo * widget,Elm_Code_Widget_Data * pd,unsigned int start_line,unsigned int start_col,unsigned int end_line,unsigned int end_col)110 _elm_code_widget_text_between_positions_get(Eo *widget, Elm_Code_Widget_Data *pd,
111                                             unsigned int start_line, unsigned int start_col,
112                                             unsigned int end_line, unsigned int end_col)
113 {
114    if (start_line == end_line)
115      return _elm_code_widget_text_single_get(widget, pd, start_line, start_col, end_col);
116    else
117      return _elm_code_widget_text_multi_get(widget, pd, start_line, start_col, end_line, end_col);
118 }
119 
120 static unsigned int
_elm_code_widget_line_text_column_width_to_position(Eo * obj,Elm_Code_Widget_Data * pd EINA_UNUSED,Elm_Code_Line * line,unsigned int position)121 _elm_code_widget_line_text_column_width_to_position(Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED, Elm_Code_Line *line, unsigned int position)
122 {
123    Eina_Unicode unicode;
124    unsigned int count = 1;
125    int index = 0;
126    const char *chars;
127 
128    if (line->length == 0)
129      return 1;
130 
131    if (line->modified)
132      chars = line->modified;
133    else
134      chars = line->content;
135    if (position > line->length)
136      position = line->length;
137 
138    while ((unsigned int) index < position)
139      {
140         unicode = eina_unicode_utf8_next_get(chars, &index);
141         if (unicode == 0)
142           break;
143 
144         if (unicode == '\t')
145           count += elm_code_widget_text_tabwidth_at_column_get(obj, count);
146         else
147           count++;
148      }
149 
150    return count;
151 }
152 
153 static unsigned int
_elm_code_widget_line_text_column_width_get(Eo * obj,Elm_Code_Widget_Data * pd,Elm_Code_Line * line)154 _elm_code_widget_line_text_column_width_get(Eo *obj, Elm_Code_Widget_Data *pd, Elm_Code_Line *line)
155 {
156    if (!line)
157      return 0;
158 
159    return _elm_code_widget_line_text_column_width_to_position(obj, pd, line, line->length) - 1;
160 }
161 
162 static unsigned int
_elm_code_widget_line_text_position_for_column_get(Eo * obj,Elm_Code_Widget_Data * pd EINA_UNUSED,Elm_Code_Line * line,unsigned int column)163 _elm_code_widget_line_text_position_for_column_get(Eo *obj, Elm_Code_Widget_Data *pd EINA_UNUSED, Elm_Code_Line *line, unsigned int column)
164 {
165    Eina_Unicode unicode;
166    unsigned int count = 1, position = 0;
167    int index = 0;
168    const char *chars;
169 
170    if (!line || line->length == 0 || column == 1)
171      return 0;
172 
173    if (line->modified)
174      chars = line->modified;
175    else
176      chars = line->content;
177 
178    while ((unsigned int) count <= column && index <= (int) line->length)
179      {
180         position = (unsigned int) index;
181         if (index < (int) line->length)
182           unicode = eina_unicode_utf8_next_get(chars, &index);
183         else return line->length;
184 
185         if (unicode == 0)
186           return line->length;
187         else if (unicode == '\t')
188           count += elm_code_widget_text_tabwidth_at_column_get(obj, count);
189         else
190           count++;
191      }
192 
193    return position;
194 }
195 
196 static unsigned int
_elm_code_widget_text_tabwidth_at_column_get(Eo * obj EINA_UNUSED,Elm_Code_Widget_Data * pd,unsigned int column)197 _elm_code_widget_text_tabwidth_at_column_get(Eo *obj EINA_UNUSED, Elm_Code_Widget_Data *pd, unsigned int column)
198 {
199    return pd->tabstop - ((column - 1) % pd->tabstop);
200 }
201 
202 static void
_elm_code_widget_text_insert_single(Elm_Code_Widget * widget,Elm_Code * code,unsigned int col,unsigned int row,const char * text,unsigned int len)203 _elm_code_widget_text_insert_single(Elm_Code_Widget *widget, Elm_Code *code,
204                                     unsigned int col, unsigned int row, const char *text, unsigned int len)
205 {
206    Elm_Code_Widget_Data *pd;
207    Elm_Code_Line *line;
208    unsigned int position, newcol;
209 
210    pd = efl_data_scope_get(widget, ELM_CODE_WIDGET_CLASS);
211    line = elm_code_file_line_get(code->file, row);
212    position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
213    elm_code_line_text_insert(line, position, text, len);
214 
215    newcol = elm_code_widget_line_text_column_width_to_position(widget, line, position + len);
216 
217    // if we are making a line longer than before then we need to resize
218    if (newcol > pd->col_count)
219      _elm_code_widget_resize(widget, line);
220 
221    efl_ui_code_widget_cursor_position_set(widget, row, newcol);
222 }
223 
224 static void
_elm_code_widget_text_insert_multi(Elm_Code_Widget * widget,Elm_Code * code,unsigned int col,unsigned int row,const char * text,unsigned int len)225 _elm_code_widget_text_insert_multi(Elm_Code_Widget *widget, Elm_Code *code,
226                                    unsigned int col, unsigned int row, const char *text, unsigned int len)
227 {
228    Elm_Code_Line *line;
229    unsigned int position, newrow, remain;
230    int nlpos;
231    short nllen;
232    char *ptr;
233 
234    line = elm_code_file_line_get(code->file, row);
235    position = elm_code_widget_line_text_position_for_column_get(widget, line, col);
236    elm_code_line_split_at(line, position);
237 
238    newrow = row;
239    ptr = (char *)text;
240    remain = len;
241    while ((nlpos = elm_code_text_newlinenpos(ptr, remain, &nllen)) != ELM_CODE_TEXT_NOT_FOUND)
242      {
243         if (newrow == row)
244           _elm_code_widget_text_insert_single(widget, code, col, row, text, nlpos);
245         else
246           elm_code_file_line_insert(code->file, newrow, ptr, nlpos, NULL);
247 
248         remain -= nlpos + nllen;
249         ptr += nlpos + nllen;
250         newrow++;
251      }
252 
253    _elm_code_widget_text_insert_single(widget, code, 1, newrow, ptr, len - (ptr - text));
254 }
255 
256 void
_elm_code_widget_text_at_cursor_insert_do(Elm_Code_Widget * widget,const char * text,int length,Eina_Bool undo)257 _elm_code_widget_text_at_cursor_insert_do(Elm_Code_Widget *widget, const char *text, int length, Eina_Bool undo)
258 {
259    Elm_Code *code;
260    Elm_Code_Line *line;
261    Elm_Code_Widget_Change_Info *change;
262    unsigned int row, col, end_row, end_col, curlen, indent;
263    const char *curtext, *indent_text;
264 
265    if (undo)
266      elm_code_widget_selection_delete(widget);
267 
268    code = efl_ui_code_widget_code_get(widget);
269    efl_ui_code_widget_cursor_position_get(widget, &row, &col);
270    line = elm_code_file_line_get(code->file, row);
271    if (line == NULL)
272      {
273         elm_code_file_line_append(code->file, "", 0, NULL);
274         row = elm_code_file_lines_get(code->file);
275         line = elm_code_file_line_get(code->file, row);
276      }
277    if (text[0] == '}')
278      {
279         curtext = elm_code_line_text_get(line, &curlen);
280 
281         if (elm_code_text_is_whitespace(curtext, line->length))
282           {
283              indent_text = elm_code_line_indent_matching_braces_get(line, &indent);
284              elm_code_line_text_leading_whitespace_strip(line);
285 
286              if (indent > 0)
287                elm_code_line_text_insert(line, 0, indent_text, indent);
288 
289              col = elm_code_widget_line_text_column_width_to_position(widget, line, indent + 1);
290              efl_ui_code_widget_cursor_position_set(widget, row, col);
291           }
292      }
293 
294    if (elm_code_text_newlinenpos(text, length, NULL) == ELM_CODE_TEXT_NOT_FOUND)
295      _elm_code_widget_text_insert_single(widget, code, col, row, text, length);
296    else
297      _elm_code_widget_text_insert_multi(widget, code, col, row, text, length);
298    efl_ui_code_widget_cursor_position_get(widget, &end_row, &end_col);
299 
300    efl_event_callback_legacy_call(widget, EFL_UI_CODE_WIDGET_EVENT_CHANGED_USER, NULL);
301 
302    if (undo)
303      {
304         change = _elm_code_widget_change_create(col, row, end_col - 1, end_row, text, length, EINA_TRUE);
305         _elm_code_widget_undo_change_add(widget, change);
306         _elm_code_widget_change_free(change);
307      }
308 }
309 
310 EOLIAN void
_elm_code_widget_text_at_cursor_insert(Elm_Code_Widget * widget,Elm_Code_Widget_Data * pd EINA_UNUSED,const char * text)311 _elm_code_widget_text_at_cursor_insert(Elm_Code_Widget *widget, Elm_Code_Widget_Data *pd EINA_UNUSED, const char *text)
312 {
313    _elm_code_widget_text_at_cursor_insert_do(widget, text, strlen(text), EINA_TRUE);
314 }
315 
316 void
_elm_code_widget_text_at_cursor_insert_no_undo(Elm_Code_Widget * widget,const char * text,unsigned int length)317 _elm_code_widget_text_at_cursor_insert_no_undo(Elm_Code_Widget *widget, const char *text, unsigned int length)
318 {
319    _elm_code_widget_text_at_cursor_insert_do(widget, text, length, EINA_FALSE);
320 }
321