Cleanup: Reduce indentation by returning early
[blender.git] / source / blender / blenkernel / intern / text.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bke
22  */
23
24 #include <stdlib.h> /* abort */
25 #include <string.h> /* strstr */
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <wctype.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_fileops.h"
33 #include "BLI_listbase.h"
34 #include "BLI_path_util.h"
35 #include "BLI_string.h"
36 #include "BLI_string_cursor_utf8.h"
37 #include "BLI_string_utf8.h"
38 #include "BLI_utildefines.h"
39
40 #include "BLT_translation.h"
41
42 #include "DNA_constraint_types.h"
43 #include "DNA_material_types.h"
44 #include "DNA_node_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_scene_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_space_types.h"
49 #include "DNA_text_types.h"
50 #include "DNA_userdef_types.h"
51
52 #include "BKE_idtype.h"
53 #include "BKE_lib_id.h"
54 #include "BKE_main.h"
55 #include "BKE_node.h"
56 #include "BKE_text.h"
57
58 #include "BLO_read_write.h"
59
60 #ifdef WITH_PYTHON
61 #  include "BPY_extern.h"
62 #endif
63
64 /* -------------------------------------------------------------------- */
65 /** \name Prototypes
66  * \{ */
67
68 static void txt_pop_first(Text *text);
69 static void txt_pop_last(Text *text);
70 static void txt_delete_line(Text *text, TextLine *line);
71 static void txt_delete_sel(Text *text);
72 static void txt_make_dirty(Text *text);
73
74 /** \} */
75
76 /* -------------------------------------------------------------------- */
77 /** \name Text Data-Block
78  * \{ */
79
80 static void text_init_data(ID *id)
81 {
82   Text *text = (Text *)id;
83   TextLine *tmp;
84
85   BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(text, id));
86
87   text->filepath = NULL;
88
89   text->flags = TXT_ISDIRTY | TXT_ISMEM;
90   if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0) {
91     text->flags |= TXT_TABSTOSPACES;
92   }
93
94   BLI_listbase_clear(&text->lines);
95
96   tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
97   tmp->line = (char *)MEM_mallocN(1, "textline_string");
98   tmp->format = NULL;
99
100   tmp->line[0] = 0;
101   tmp->len = 0;
102
103   tmp->next = NULL;
104   tmp->prev = NULL;
105
106   BLI_addhead(&text->lines, tmp);
107
108   text->curl = text->lines.first;
109   text->curc = 0;
110   text->sell = text->lines.first;
111   text->selc = 0;
112 }
113
114 /**
115  * Only copy internal data of Text ID from source
116  * to already allocated/initialized destination.
117  * You probably never want to use that directly,
118  * use #BKE_id_copy or #BKE_id_copy_ex for typical needs.
119  *
120  * WARNING! This function will not handle ID user count!
121  *
122  * \param flag: Copying options (see BKE_lib_id.h's LIB_ID_COPY_... flags for more).
123  */
124 static void text_copy_data(Main *UNUSED(bmain),
125                            ID *id_dst,
126                            const ID *id_src,
127                            const int UNUSED(flag))
128 {
129   Text *text_dst = (Text *)id_dst;
130   const Text *text_src = (Text *)id_src;
131
132   /* File name can be NULL. */
133   if (text_src->filepath) {
134     text_dst->filepath = BLI_strdup(text_src->filepath);
135   }
136
137   text_dst->flags |= TXT_ISDIRTY;
138
139   BLI_listbase_clear(&text_dst->lines);
140   text_dst->curl = text_dst->sell = NULL;
141   text_dst->compiled = NULL;
142
143   /* Walk down, reconstructing. */
144   LISTBASE_FOREACH (TextLine *, line_src, &text_src->lines) {
145     TextLine *line_dst = MEM_mallocN(sizeof(*line_dst), __func__);
146
147     line_dst->line = BLI_strdup(line_src->line);
148     line_dst->format = NULL;
149     line_dst->len = line_src->len;
150
151     BLI_addtail(&text_dst->lines, line_dst);
152   }
153
154   text_dst->curl = text_dst->sell = text_dst->lines.first;
155   text_dst->curc = text_dst->selc = 0;
156 }
157
158 /** Free (or release) any data used by this text (does not free the text itself). */
159 static void text_free_data(ID *id)
160 {
161   /* No animdata here. */
162   Text *text = (Text *)id;
163
164   BKE_text_free_lines(text);
165
166   MEM_SAFE_FREE(text->filepath);
167 #ifdef WITH_PYTHON
168   BPY_text_free_code(text);
169 #endif
170 }
171
172 static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address)
173 {
174   Text *text = (Text *)id;
175
176   /* Note: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */
177   if ((text->flags & TXT_ISMEM) && (text->flags & TXT_ISEXT)) {
178     text->flags &= ~TXT_ISEXT;
179   }
180
181   /* Clean up, important in undo case to reduce false detection of changed datablocks. */
182   text->compiled = NULL;
183
184   /* write LibData */
185   BLO_write_id_struct(writer, Text, id_address, &text->id);
186   BKE_id_blend_write(writer, &text->id);
187
188   if (text->filepath) {
189     BLO_write_string(writer, text->filepath);
190   }
191
192   if (!(text->flags & TXT_ISEXT)) {
193     /* now write the text data, in two steps for optimization in the readfunction */
194     LISTBASE_FOREACH (TextLine *, tmp, &text->lines) {
195       BLO_write_struct(writer, TextLine, tmp);
196     }
197
198     LISTBASE_FOREACH (TextLine *, tmp, &text->lines) {
199       BLO_write_raw(writer, tmp->len + 1, tmp->line);
200     }
201   }
202 }
203
204 static void text_blend_read_data(BlendDataReader *reader, ID *id)
205 {
206   Text *text = (Text *)id;
207   BLO_read_data_address(reader, &text->filepath);
208
209   text->compiled = NULL;
210
211 #if 0
212   if (text->flags & TXT_ISEXT) {
213     BKE_text_reload(text);
214   }
215   /* else { */
216 #endif
217
218   BLO_read_list(reader, &text->lines);
219
220   BLO_read_data_address(reader, &text->curl);
221   BLO_read_data_address(reader, &text->sell);
222
223   LISTBASE_FOREACH (TextLine *, ln, &text->lines) {
224     BLO_read_data_address(reader, &ln->line);
225     ln->format = NULL;
226
227     if (ln->len != (int)strlen(ln->line)) {
228       printf("Error loading text, line lengths differ\n");
229       ln->len = strlen(ln->line);
230     }
231   }
232
233   text->flags = (text->flags) & ~TXT_ISEXT;
234
235   id_us_ensure_real(&text->id);
236 }
237
238 IDTypeInfo IDType_ID_TXT = {
239     .id_code = ID_TXT,
240     .id_filter = FILTER_ID_TXT,
241     .main_listbase_index = INDEX_ID_TXT,
242     .struct_size = sizeof(Text),
243     .name = "Text",
244     .name_plural = "texts",
245     .translation_context = BLT_I18NCONTEXT_ID_TEXT,
246     .flags = 0,
247
248     .init_data = text_init_data,
249     .copy_data = text_copy_data,
250     .free_data = text_free_data,
251     .make_local = NULL,
252     .foreach_id = NULL,
253     .foreach_cache = NULL,
254
255     .blend_write = text_blend_write,
256     .blend_read_data = text_blend_read_data,
257     .blend_read_lib = NULL,
258     .blend_read_expand = NULL,
259 };
260
261 /** \} */
262
263 /* -------------------------------------------------------------------- */
264 /** \name Text Add, Free, Validation
265  * \{ */
266
267 /**
268  * \note caller must handle `compiled` member.
269  */
270 void BKE_text_free_lines(Text *text)
271 {
272   for (TextLine *tmp = text->lines.first, *tmp_next; tmp; tmp = tmp_next) {
273     tmp_next = tmp->next;
274     MEM_freeN(tmp->line);
275     if (tmp->format) {
276       MEM_freeN(tmp->format);
277     }
278     MEM_freeN(tmp);
279   }
280
281   BLI_listbase_clear(&text->lines);
282
283   text->curl = text->sell = NULL;
284 }
285
286 Text *BKE_text_add(Main *bmain, const char *name)
287 {
288   Text *ta;
289
290   ta = BKE_libblock_alloc(bmain, ID_TXT, name, 0);
291   /* Texts always have 'real' user (see also read code). */
292   id_us_ensure_real(&ta->id);
293
294   text_init_data(&ta->id);
295
296   return ta;
297 }
298
299 /* this function replaces extended ascii characters */
300 /* to a valid utf-8 sequences */
301 int txt_extended_ascii_as_utf8(char **str)
302 {
303   ptrdiff_t bad_char, i = 0;
304   const ptrdiff_t length = (ptrdiff_t)strlen(*str);
305   int added = 0;
306
307   while ((*str)[i]) {
308     if ((bad_char = BLI_utf8_invalid_byte(*str + i, length - i)) == -1) {
309       break;
310     }
311
312     added++;
313     i += bad_char + 1;
314   }
315
316   if (added != 0) {
317     char *newstr = MEM_mallocN(length + added + 1, "text_line");
318     ptrdiff_t mi = 0;
319     i = 0;
320
321     while ((*str)[i]) {
322       if ((bad_char = BLI_utf8_invalid_byte((*str) + i, length - i)) == -1) {
323         memcpy(newstr + mi, (*str) + i, length - i + 1);
324         break;
325       }
326
327       memcpy(newstr + mi, (*str) + i, bad_char);
328
329       BLI_str_utf8_from_unicode((*str)[i + bad_char], newstr + mi + bad_char);
330       i += bad_char + 1;
331       mi += bad_char + 2;
332     }
333     newstr[length + added] = '\0';
334     MEM_freeN(*str);
335     *str = newstr;
336   }
337
338   return added;
339 }
340
341 // this function removes any control characters from
342 // a textline and fixes invalid utf-8 sequences
343
344 static void cleanup_textline(TextLine *tl)
345 {
346   int i;
347
348   for (i = 0; i < tl->len; i++) {
349     if (tl->line[i] < ' ' && tl->line[i] != '\t') {
350       memmove(tl->line + i, tl->line + i + 1, tl->len - i);
351       tl->len--;
352       i--;
353     }
354   }
355   tl->len += txt_extended_ascii_as_utf8(&tl->line);
356 }
357
358 /**
359  * used for load and reload (unlike txt_insert_buf)
360  * assumes all fields are empty
361  */
362 static void text_from_buf(Text *text, const unsigned char *buffer, const int len)
363 {
364   int i, llen, lines_count;
365
366   BLI_assert(BLI_listbase_is_empty(&text->lines));
367
368   llen = 0;
369   lines_count = 0;
370   for (i = 0; i < len; i++) {
371     if (buffer[i] == '\n') {
372       TextLine *tmp;
373
374       tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
375       tmp->line = (char *)MEM_mallocN(llen + 1, "textline_string");
376       tmp->format = NULL;
377
378       if (llen) {
379         memcpy(tmp->line, &buffer[i - llen], llen);
380       }
381       tmp->line[llen] = 0;
382       tmp->len = llen;
383
384       cleanup_textline(tmp);
385
386       BLI_addtail(&text->lines, tmp);
387       lines_count += 1;
388
389       llen = 0;
390       continue;
391     }
392     llen++;
393   }
394
395   /* create new line in cases:
396    * - rest of line (if last line in file hasn't got \n terminator).
397    *   in this case content of such line would be used to fill text line buffer
398    * - file is empty. in this case new line is needed to start editing from.
399    * - last character in buffer is \n. in this case new line is needed to
400    *   deal with newline at end of file. (see [#28087]) (sergey) */
401   if (llen != 0 || lines_count == 0 || buffer[len - 1] == '\n') {
402     TextLine *tmp;
403
404     tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
405     tmp->line = (char *)MEM_mallocN(llen + 1, "textline_string");
406     tmp->format = NULL;
407
408     if (llen) {
409       memcpy(tmp->line, &buffer[i - llen], llen);
410     }
411
412     tmp->line[llen] = 0;
413     tmp->len = llen;
414
415     cleanup_textline(tmp);
416
417     BLI_addtail(&text->lines, tmp);
418     /* lines_count += 1; */ /* UNUSED */
419   }
420
421   text->curl = text->sell = text->lines.first;
422   text->curc = text->selc = 0;
423 }
424
425 bool BKE_text_reload(Text *text)
426 {
427   unsigned char *buffer;
428   size_t buffer_len;
429   char filepath_abs[FILE_MAX];
430   BLI_stat_t st;
431
432   if (!text->filepath) {
433     return false;
434   }
435
436   BLI_strncpy(filepath_abs, text->filepath, FILE_MAX);
437   BLI_path_abs(filepath_abs, ID_BLEND_PATH_FROM_GLOBAL(&text->id));
438
439   buffer = BLI_file_read_text_as_mem(filepath_abs, 0, &buffer_len);
440   if (buffer == NULL) {
441     return false;
442   }
443
444   /* free memory: */
445   BKE_text_free_lines(text);
446   txt_make_dirty(text);
447
448   /* clear undo buffer */
449   if (BLI_stat(filepath_abs, &st) != -1) {
450     text->mtime = st.st_mtime;
451   }
452   else {
453     text->mtime = 0;
454   }
455
456   text_from_buf(text, buffer, buffer_len);
457
458   MEM_freeN(buffer);
459   return true;
460 }
461
462 Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const bool is_internal)
463 {
464   unsigned char *buffer;
465   size_t buffer_len;
466   Text *ta;
467   char filepath_abs[FILE_MAX];
468   BLI_stat_t st;
469
470   BLI_strncpy(filepath_abs, file, FILE_MAX);
471   if (relpath) { /* can be NULL (bg mode) */
472     BLI_path_abs(filepath_abs, relpath);
473   }
474
475   buffer = BLI_file_read_text_as_mem(filepath_abs, 0, &buffer_len);
476   if (buffer == NULL) {
477     return NULL;
478   }
479
480   ta = BKE_libblock_alloc(bmain, ID_TXT, BLI_path_basename(filepath_abs), 0);
481   /* Texts always have 'real' user (see also read code). */
482   id_us_ensure_real(&ta->id);
483
484   BLI_listbase_clear(&ta->lines);
485   ta->curl = ta->sell = NULL;
486
487   if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0) {
488     ta->flags = TXT_TABSTOSPACES;
489   }
490
491   if (is_internal == false) {
492     ta->filepath = MEM_mallocN(strlen(file) + 1, "text_name");
493     strcpy(ta->filepath, file);
494   }
495   else {
496     ta->flags |= TXT_ISMEM | TXT_ISDIRTY;
497   }
498
499   /* clear undo buffer */
500   if (BLI_stat(filepath_abs, &st) != -1) {
501     ta->mtime = st.st_mtime;
502   }
503   else {
504     ta->mtime = 0;
505   }
506
507   text_from_buf(ta, buffer, buffer_len);
508
509   MEM_freeN(buffer);
510
511   return ta;
512 }
513
514 Text *BKE_text_load(Main *bmain, const char *file, const char *relpath)
515 {
516   return BKE_text_load_ex(bmain, file, relpath, false);
517 }
518
519 Text *BKE_text_copy(Main *bmain, const Text *ta)
520 {
521   Text *ta_copy;
522   BKE_id_copy(bmain, &ta->id, (ID **)&ta_copy);
523   return ta_copy;
524 }
525
526 void BKE_text_clear(Text *text) /* called directly from rna */
527 {
528   txt_sel_all(text);
529   txt_delete_sel(text);
530   txt_make_dirty(text);
531 }
532
533 void BKE_text_write(Text *text, const char *str) /* called directly from rna */
534 {
535   txt_insert_buf(text, str);
536   txt_move_eof(text, 0);
537   txt_make_dirty(text);
538 }
539
540 /* returns 0 if file on disk is the same or Text is in memory only
541  * returns 1 if file has been modified on disk since last local edit
542  * returns 2 if file on disk has been deleted
543  * -1 is returned if an error occurs */
544
545 int BKE_text_file_modified_check(Text *text)
546 {
547   BLI_stat_t st;
548   int result;
549   char file[FILE_MAX];
550
551   if (!text->filepath) {
552     return 0;
553   }
554
555   BLI_strncpy(file, text->filepath, FILE_MAX);
556   BLI_path_abs(file, ID_BLEND_PATH_FROM_GLOBAL(&text->id));
557
558   if (!BLI_exists(file)) {
559     return 2;
560   }
561
562   result = BLI_stat(file, &st);
563
564   if (result == -1) {
565     return -1;
566   }
567
568   if ((st.st_mode & S_IFMT) != S_IFREG) {
569     return -1;
570   }
571
572   if (st.st_mtime > text->mtime) {
573     return 1;
574   }
575
576   return 0;
577 }
578
579 void BKE_text_file_modified_ignore(Text *text)
580 {
581   BLI_stat_t st;
582   int result;
583   char file[FILE_MAX];
584
585   if (!text->filepath) {
586     return;
587   }
588
589   BLI_strncpy(file, text->filepath, FILE_MAX);
590   BLI_path_abs(file, ID_BLEND_PATH_FROM_GLOBAL(&text->id));
591
592   if (!BLI_exists(file)) {
593     return;
594   }
595
596   result = BLI_stat(file, &st);
597
598   if (result == -1 || (st.st_mode & S_IFMT) != S_IFREG) {
599     return;
600   }
601
602   text->mtime = st.st_mtime;
603 }
604
605 /** \} */
606
607 /* -------------------------------------------------------------------- */
608 /** \name Editing Utility Functions
609  * \{ */
610
611 static void make_new_line(TextLine *line, char *newline)
612 {
613   if (line->line) {
614     MEM_freeN(line->line);
615   }
616   if (line->format) {
617     MEM_freeN(line->format);
618   }
619
620   line->line = newline;
621   line->len = strlen(newline);
622   line->format = NULL;
623 }
624
625 static TextLine *txt_new_line(const char *str)
626 {
627   TextLine *tmp;
628
629   if (!str) {
630     str = "";
631   }
632
633   tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
634   tmp->line = MEM_mallocN(strlen(str) + 1, "textline_string");
635   tmp->format = NULL;
636
637   strcpy(tmp->line, str);
638
639   tmp->len = strlen(str);
640   tmp->next = tmp->prev = NULL;
641
642   return tmp;
643 }
644
645 static TextLine *txt_new_linen(const char *str, int n)
646 {
647   TextLine *tmp;
648
649   tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
650   tmp->line = MEM_mallocN(n + 1, "textline_string");
651   tmp->format = NULL;
652
653   BLI_strncpy(tmp->line, (str) ? str : "", n + 1);
654
655   tmp->len = strlen(tmp->line);
656   tmp->next = tmp->prev = NULL;
657
658   return tmp;
659 }
660
661 void txt_clean_text(Text *text)
662 {
663   TextLine **top, **bot;
664
665   if (!text->lines.first) {
666     if (text->lines.last) {
667       text->lines.first = text->lines.last;
668     }
669     else {
670       text->lines.first = text->lines.last = txt_new_line(NULL);
671     }
672   }
673
674   if (!text->lines.last) {
675     text->lines.last = text->lines.first;
676   }
677
678   top = (TextLine **)&text->lines.first;
679   bot = (TextLine **)&text->lines.last;
680
681   while ((*top)->prev) {
682     *top = (*top)->prev;
683   }
684   while ((*bot)->next) {
685     *bot = (*bot)->next;
686   }
687
688   if (!text->curl) {
689     if (text->sell) {
690       text->curl = text->sell;
691     }
692     else {
693       text->curl = text->lines.first;
694     }
695     text->curc = 0;
696   }
697
698   if (!text->sell) {
699     text->sell = text->curl;
700     text->selc = 0;
701   }
702 }
703
704 int txt_get_span(TextLine *from, TextLine *to)
705 {
706   int ret = 0;
707   TextLine *tmp = from;
708
709   if (!to || !from) {
710     return 0;
711   }
712   if (from == to) {
713     return 0;
714   }
715
716   /* Look forwards */
717   while (tmp) {
718     if (tmp == to) {
719       return ret;
720     }
721     ret++;
722     tmp = tmp->next;
723   }
724
725   /* Look backwards */
726   if (!tmp) {
727     tmp = from;
728     ret = 0;
729     while (tmp) {
730       if (tmp == to) {
731         break;
732       }
733       ret--;
734       tmp = tmp->prev;
735     }
736     if (!tmp) {
737       ret = 0;
738     }
739   }
740
741   return ret;
742 }
743
744 static void txt_make_dirty(Text *text)
745 {
746   text->flags |= TXT_ISDIRTY;
747 #ifdef WITH_PYTHON
748   if (text->compiled) {
749     BPY_text_free_code(text);
750   }
751 #endif
752 }
753
754 /** \} */
755
756 /* -------------------------------------------------------------------- */
757 /** \name Cursor Utility Functions
758  * \{ */
759
760 static void txt_curs_cur(Text *text, TextLine ***linep, int **charp)
761 {
762   *linep = &text->curl;
763   *charp = &text->curc;
764 }
765
766 static void txt_curs_sel(Text *text, TextLine ***linep, int **charp)
767 {
768   *linep = &text->sell;
769   *charp = &text->selc;
770 }
771
772 bool txt_cursor_is_line_start(Text *text)
773 {
774   return (text->selc == 0);
775 }
776
777 bool txt_cursor_is_line_end(Text *text)
778 {
779   return (text->selc == text->sell->len);
780 }
781
782 /** \} */
783
784 /* -------------------------------------------------------------------- */
785 /** \name Cursor Movement Functions
786  *
787  * \note If the user moves the cursor the space containing that cursor should be popped
788  * See #txt_pop_first, #txt_pop_last
789  * Other space-types retain their own top location.
790  * \{ */
791
792 void txt_move_up(Text *text, const bool sel)
793 {
794   TextLine **linep;
795   int *charp;
796
797   if (sel) {
798     txt_curs_sel(text, &linep, &charp);
799   }
800   else {
801     txt_pop_first(text);
802     txt_curs_cur(text, &linep, &charp);
803   }
804   if (!*linep) {
805     return;
806   }
807
808   if ((*linep)->prev) {
809     int column = BLI_str_utf8_offset_to_column((*linep)->line, *charp);
810     *linep = (*linep)->prev;
811     *charp = BLI_str_utf8_offset_from_column((*linep)->line, column);
812   }
813   else {
814     txt_move_bol(text, sel);
815   }
816
817   if (!sel) {
818     txt_pop_sel(text);
819   }
820 }
821
822 void txt_move_down(Text *text, const bool sel)
823 {
824   TextLine **linep;
825   int *charp;
826
827   if (sel) {
828     txt_curs_sel(text, &linep, &charp);
829   }
830   else {
831     txt_pop_last(text);
832     txt_curs_cur(text, &linep, &charp);
833   }
834   if (!*linep) {
835     return;
836   }
837
838   if ((*linep)->next) {
839     int column = BLI_str_utf8_offset_to_column((*linep)->line, *charp);
840     *linep = (*linep)->next;
841     *charp = BLI_str_utf8_offset_from_column((*linep)->line, column);
842   }
843   else {
844     txt_move_eol(text, sel);
845   }
846
847   if (!sel) {
848     txt_pop_sel(text);
849   }
850 }
851
852 int txt_calc_tab_left(TextLine *tl, int ch)
853 {
854   /* do nice left only if there are only spaces */
855
856   int tabsize = (ch < TXT_TABSIZE) ? ch : TXT_TABSIZE;
857
858   for (int i = 0; i < ch; i++) {
859     if (tl->line[i] != ' ') {
860       tabsize = 0;
861       break;
862     }
863   }
864
865   /* if in the middle of the space-tab */
866   if (tabsize && ch % TXT_TABSIZE != 0) {
867     tabsize = (ch % TXT_TABSIZE);
868   }
869   return tabsize;
870 }
871
872 int txt_calc_tab_right(TextLine *tl, int ch)
873 {
874   if (tl->line[ch] == ' ') {
875     int i;
876     for (i = 0; i < ch; i++) {
877       if (tl->line[i] != ' ') {
878         return 0;
879       }
880     }
881
882     int tabsize = (ch) % TXT_TABSIZE + 1;
883     for (i = ch + 1; tl->line[i] == ' ' && tabsize < TXT_TABSIZE; i++) {
884       tabsize++;
885     }
886
887     return i - ch;
888   }
889
890   return 0;
891 }
892
893 void txt_move_left(Text *text, const bool sel)
894 {
895   TextLine **linep;
896   int *charp;
897   int tabsize = 0;
898
899   if (sel) {
900     txt_curs_sel(text, &linep, &charp);
901   }
902   else {
903     txt_pop_first(text);
904     txt_curs_cur(text, &linep, &charp);
905   }
906   if (!*linep) {
907     return;
908   }
909
910   if (*charp == 0) {
911     if ((*linep)->prev) {
912       txt_move_up(text, sel);
913       *charp = (*linep)->len;
914     }
915   }
916   else {
917     /* do nice left only if there are only spaces */
918     // TXT_TABSIZE hardcoded in DNA_text_types.h
919     if (text->flags & TXT_TABSTOSPACES) {
920       tabsize = txt_calc_tab_left(*linep, *charp);
921     }
922
923     if (tabsize) {
924       (*charp) -= tabsize;
925     }
926     else {
927       const char *prev = BLI_str_prev_char_utf8((*linep)->line + *charp);
928       *charp = prev - (*linep)->line;
929     }
930   }
931
932   if (!sel) {
933     txt_pop_sel(text);
934   }
935 }
936
937 void txt_move_right(Text *text, const bool sel)
938 {
939   TextLine **linep;
940   int *charp;
941
942   if (sel) {
943     txt_curs_sel(text, &linep, &charp);
944   }
945   else {
946     txt_pop_last(text);
947     txt_curs_cur(text, &linep, &charp);
948   }
949   if (!*linep) {
950     return;
951   }
952
953   if (*charp == (*linep)->len) {
954     if ((*linep)->next) {
955       txt_move_down(text, sel);
956       *charp = 0;
957     }
958   }
959   else {
960     /* do nice right only if there are only spaces */
961     /* spaces hardcoded in DNA_text_types.h */
962     int tabsize = 0;
963
964     if (text->flags & TXT_TABSTOSPACES) {
965       tabsize = txt_calc_tab_right(*linep, *charp);
966     }
967
968     if (tabsize) {
969       (*charp) += tabsize;
970     }
971     else {
972       (*charp) += BLI_str_utf8_size((*linep)->line + *charp);
973     }
974   }
975
976   if (!sel) {
977     txt_pop_sel(text);
978   }
979 }
980
981 void txt_jump_left(Text *text, const bool sel, const bool use_init_step)
982 {
983   TextLine **linep;
984   int *charp;
985
986   if (sel) {
987     txt_curs_sel(text, &linep, &charp);
988   }
989   else {
990     txt_pop_first(text);
991     txt_curs_cur(text, &linep, &charp);
992   }
993   if (!*linep) {
994     return;
995   }
996
997   BLI_str_cursor_step_utf8(
998       (*linep)->line, (*linep)->len, charp, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, use_init_step);
999
1000   if (!sel) {
1001     txt_pop_sel(text);
1002   }
1003 }
1004
1005 void txt_jump_right(Text *text, const bool sel, const bool use_init_step)
1006 {
1007   TextLine **linep;
1008   int *charp;
1009
1010   if (sel) {
1011     txt_curs_sel(text, &linep, &charp);
1012   }
1013   else {
1014     txt_pop_last(text);
1015     txt_curs_cur(text, &linep, &charp);
1016   }
1017   if (!*linep) {
1018     return;
1019   }
1020
1021   BLI_str_cursor_step_utf8(
1022       (*linep)->line, (*linep)->len, charp, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, use_init_step);
1023
1024   if (!sel) {
1025     txt_pop_sel(text);
1026   }
1027 }
1028
1029 void txt_move_bol(Text *text, const bool sel)
1030 {
1031   TextLine **linep;
1032   int *charp;
1033
1034   if (sel) {
1035     txt_curs_sel(text, &linep, &charp);
1036   }
1037   else {
1038     txt_curs_cur(text, &linep, &charp);
1039   }
1040   if (!*linep) {
1041     return;
1042   }
1043
1044   *charp = 0;
1045
1046   if (!sel) {
1047     txt_pop_sel(text);
1048   }
1049 }
1050
1051 void txt_move_eol(Text *text, const bool sel)
1052 {
1053   TextLine **linep;
1054   int *charp;
1055
1056   if (sel) {
1057     txt_curs_sel(text, &linep, &charp);
1058   }
1059   else {
1060     txt_curs_cur(text, &linep, &charp);
1061   }
1062   if (!*linep) {
1063     return;
1064   }
1065
1066   *charp = (*linep)->len;
1067
1068   if (!sel) {
1069     txt_pop_sel(text);
1070   }
1071 }
1072
1073 void txt_move_bof(Text *text, const bool sel)
1074 {
1075   TextLine **linep;
1076   int *charp;
1077
1078   if (sel) {
1079     txt_curs_sel(text, &linep, &charp);
1080   }
1081   else {
1082     txt_curs_cur(text, &linep, &charp);
1083   }
1084   if (!*linep) {
1085     return;
1086   }
1087
1088   *linep = text->lines.first;
1089   *charp = 0;
1090
1091   if (!sel) {
1092     txt_pop_sel(text);
1093   }
1094 }
1095
1096 void txt_move_eof(Text *text, const bool sel)
1097 {
1098   TextLine **linep;
1099   int *charp;
1100
1101   if (sel) {
1102     txt_curs_sel(text, &linep, &charp);
1103   }
1104   else {
1105     txt_curs_cur(text, &linep, &charp);
1106   }
1107   if (!*linep) {
1108     return;
1109   }
1110
1111   *linep = text->lines.last;
1112   *charp = (*linep)->len;
1113
1114   if (!sel) {
1115     txt_pop_sel(text);
1116   }
1117 }
1118
1119 void txt_move_toline(Text *text, unsigned int line, const bool sel)
1120 {
1121   txt_move_to(text, line, 0, sel);
1122 }
1123
1124 /* Moves to a certain byte in a line, not a certain utf8-character! */
1125 void txt_move_to(Text *text, unsigned int line, unsigned int ch, const bool sel)
1126 {
1127   TextLine **linep;
1128   int *charp;
1129   unsigned int i;
1130
1131   if (sel) {
1132     txt_curs_sel(text, &linep, &charp);
1133   }
1134   else {
1135     txt_curs_cur(text, &linep, &charp);
1136   }
1137   if (!*linep) {
1138     return;
1139   }
1140
1141   *linep = text->lines.first;
1142   for (i = 0; i < line; i++) {
1143     if ((*linep)->next) {
1144       *linep = (*linep)->next;
1145     }
1146     else {
1147       break;
1148     }
1149   }
1150   if (ch > (unsigned int)((*linep)->len)) {
1151     ch = (unsigned int)((*linep)->len);
1152   }
1153   *charp = ch;
1154
1155   if (!sel) {
1156     txt_pop_sel(text);
1157   }
1158 }
1159
1160 /** \} */
1161
1162 /* -------------------------------------------------------------------- */
1163 /** \name Text Selection Functions
1164  * \{ */
1165
1166 static void txt_curs_swap(Text *text)
1167 {
1168   TextLine *tmpl;
1169   int tmpc;
1170
1171   tmpl = text->curl;
1172   text->curl = text->sell;
1173   text->sell = tmpl;
1174
1175   tmpc = text->curc;
1176   text->curc = text->selc;
1177   text->selc = tmpc;
1178 }
1179
1180 static void txt_pop_first(Text *text)
1181 {
1182   if (txt_get_span(text->curl, text->sell) < 0 ||
1183       (text->curl == text->sell && text->curc > text->selc)) {
1184     txt_curs_swap(text);
1185   }
1186
1187   txt_pop_sel(text);
1188 }
1189
1190 static void txt_pop_last(Text *text)
1191 {
1192   if (txt_get_span(text->curl, text->sell) > 0 ||
1193       (text->curl == text->sell && text->curc < text->selc)) {
1194     txt_curs_swap(text);
1195   }
1196
1197   txt_pop_sel(text);
1198 }
1199
1200 void txt_pop_sel(Text *text)
1201 {
1202   text->sell = text->curl;
1203   text->selc = text->curc;
1204 }
1205
1206 void txt_order_cursors(Text *text, const bool reverse)
1207 {
1208   if (!text->curl) {
1209     return;
1210   }
1211   if (!text->sell) {
1212     return;
1213   }
1214
1215   /* Flip so text->curl is before/after text->sell */
1216   if (reverse == false) {
1217     if ((txt_get_span(text->curl, text->sell) < 0) ||
1218         (text->curl == text->sell && text->curc > text->selc)) {
1219       txt_curs_swap(text);
1220     }
1221   }
1222   else {
1223     if ((txt_get_span(text->curl, text->sell) > 0) ||
1224         (text->curl == text->sell && text->curc < text->selc)) {
1225       txt_curs_swap(text);
1226     }
1227   }
1228 }
1229
1230 bool txt_has_sel(Text *text)
1231 {
1232   return ((text->curl != text->sell) || (text->curc != text->selc));
1233 }
1234
1235 static void txt_delete_sel(Text *text)
1236 {
1237   TextLine *tmpl;
1238   char *buf;
1239
1240   if (!text->curl) {
1241     return;
1242   }
1243   if (!text->sell) {
1244     return;
1245   }
1246
1247   if (!txt_has_sel(text)) {
1248     return;
1249   }
1250
1251   txt_order_cursors(text, false);
1252
1253   buf = MEM_mallocN(text->curc + (text->sell->len - text->selc) + 1, "textline_string");
1254
1255   strncpy(buf, text->curl->line, text->curc);
1256   strcpy(buf + text->curc, text->sell->line + text->selc);
1257   buf[text->curc + (text->sell->len - text->selc)] = 0;
1258
1259   make_new_line(text->curl, buf);
1260
1261   tmpl = text->sell;
1262   while (tmpl != text->curl) {
1263     tmpl = tmpl->prev;
1264     if (!tmpl) {
1265       break;
1266     }
1267
1268     txt_delete_line(text, tmpl->next);
1269   }
1270
1271   text->sell = text->curl;
1272   text->selc = text->curc;
1273 }
1274
1275 void txt_sel_all(Text *text)
1276 {
1277   text->curl = text->lines.first;
1278   text->curc = 0;
1279
1280   text->sell = text->lines.last;
1281   text->selc = text->sell->len;
1282 }
1283
1284 /**
1285  * Reverse of #txt_pop_sel
1286  * Clears the selection and ensures the cursor is located
1287  * at the selection (where the cursor is visually while editing).
1288  */
1289 void txt_sel_clear(Text *text)
1290 {
1291   if (text->sell) {
1292     text->curl = text->sell;
1293     text->curc = text->selc;
1294   }
1295 }
1296
1297 void txt_sel_line(Text *text)
1298 {
1299   if (!text->curl) {
1300     return;
1301   }
1302
1303   text->curc = 0;
1304   text->sell = text->curl;
1305   text->selc = text->sell->len;
1306 }
1307
1308 void txt_sel_set(Text *text, int startl, int startc, int endl, int endc)
1309 {
1310   TextLine *froml, *tol;
1311   int fromllen, tollen;
1312
1313   /* Support negative indices. */
1314   if (startl < 0 || endl < 0) {
1315     int end = BLI_listbase_count(&text->lines) - 1;
1316     if (startl < 0) {
1317       startl = end + startl + 1;
1318     }
1319     if (endl < 0) {
1320       endl = end + endl + 1;
1321     }
1322   }
1323   CLAMP_MIN(startl, 0);
1324   CLAMP_MIN(endl, 0);
1325
1326   froml = BLI_findlink(&text->lines, startl);
1327   if (froml == NULL) {
1328     froml = text->lines.last;
1329   }
1330   if (startl == endl) {
1331     tol = froml;
1332   }
1333   else {
1334     tol = BLI_findlink(&text->lines, endl);
1335     if (tol == NULL) {
1336       tol = text->lines.last;
1337     }
1338   }
1339
1340   fromllen = BLI_strlen_utf8(froml->line);
1341   tollen = BLI_strlen_utf8(tol->line);
1342
1343   /* Support negative indices. */
1344   if (startc < 0) {
1345     startc = fromllen + startc + 1;
1346   }
1347   if (endc < 0) {
1348     endc = tollen + endc + 1;
1349   }
1350
1351   CLAMP(startc, 0, fromllen);
1352   CLAMP(endc, 0, tollen);
1353
1354   text->curl = froml;
1355   text->curc = BLI_str_utf8_offset_from_index(froml->line, startc);
1356   text->sell = tol;
1357   text->selc = BLI_str_utf8_offset_from_index(tol->line, endc);
1358 }
1359
1360 /** \} */
1361
1362 /* -------------------------------------------------------------------- */
1363 /** \name Buffer Conversion for Undo/Redo
1364  *
1365  * Buffer conversion functions that rely on the buffer already being validated.
1366  *
1367  * The only requirement for these functions is that they're reverse-able,
1368  * the undo logic doesn't inspect their content.
1369  *
1370  * Currently buffers:
1371  * - Always ends with a new-line.
1372  * - Are not null terminated.
1373  * \{ */
1374
1375 /**
1376  * Create a buffer, the only requirement is #txt_from_buf_for_undo can decode it.
1377  */
1378 char *txt_to_buf_for_undo(Text *text, int *r_buf_len)
1379 {
1380   int buf_len = 0;
1381   LISTBASE_FOREACH (const TextLine *, l, &text->lines) {
1382     buf_len += l->len + 1;
1383   }
1384   char *buf = MEM_mallocN(buf_len, __func__);
1385   char *buf_step = buf;
1386   LISTBASE_FOREACH (const TextLine *, l, &text->lines) {
1387     memcpy(buf_step, l->line, l->len);
1388     buf_step += l->len;
1389     *buf_step++ = '\n';
1390   }
1391   *r_buf_len = buf_len;
1392   return buf;
1393 }
1394
1395 /**
1396  * Decode a buffer from #txt_to_buf_for_undo.
1397  */
1398 void txt_from_buf_for_undo(Text *text, const char *buf, int buf_len)
1399 {
1400   const char *buf_end = buf + buf_len;
1401   const char *buf_step = buf;
1402
1403   /* First re-use existing lines.
1404    * Good for undo since it means in practice many operations re-use all
1405    * except for the modified line. */
1406   TextLine *l_src = text->lines.first;
1407   BLI_listbase_clear(&text->lines);
1408   while (buf_step != buf_end && l_src) {
1409     /* New lines are ensured by #txt_to_buf_for_undo. */
1410     const char *buf_step_next = strchr(buf_step, '\n');
1411     const int len = buf_step_next - buf_step;
1412
1413     TextLine *l = l_src;
1414     l_src = l_src->next;
1415     if (l->len != len) {
1416       l->line = MEM_reallocN(l->line, len + 1);
1417       l->len = len;
1418     }
1419     MEM_SAFE_FREE(l->format);
1420
1421     memcpy(l->line, buf_step, len);
1422     l->line[len] = '\0';
1423     BLI_addtail(&text->lines, l);
1424     buf_step = buf_step_next + 1;
1425   }
1426
1427   /* If we have extra lines. */
1428   while (l_src != NULL) {
1429     TextLine *l_src_next = l_src->next;
1430     MEM_freeN(l_src->line);
1431     if (l_src->format) {
1432       MEM_freeN(l_src->format);
1433     }
1434     MEM_freeN(l_src);
1435     l_src = l_src_next;
1436   }
1437
1438   while (buf_step != buf_end) {
1439     /* New lines are ensured by #txt_to_buf_for_undo. */
1440     const char *buf_step_next = strchr(buf_step, '\n');
1441     const int len = buf_step_next - buf_step;
1442
1443     TextLine *l = MEM_mallocN(sizeof(TextLine), "textline");
1444     l->line = MEM_mallocN(len + 1, "textline_string");
1445     l->len = len;
1446     l->format = NULL;
1447
1448     memcpy(l->line, buf_step, len);
1449     l->line[len] = '\0';
1450     BLI_addtail(&text->lines, l);
1451     buf_step = buf_step_next + 1;
1452   }
1453
1454   text->curl = text->sell = text->lines.first;
1455   text->curc = text->selc = 0;
1456
1457   txt_make_dirty(text);
1458 }
1459
1460 /** \} */
1461
1462 /* -------------------------------------------------------------------- */
1463 /** \name Cut and Paste Functions
1464  * \{ */
1465
1466 char *txt_to_buf(Text *text, int *r_buf_strlen)
1467 {
1468   int length;
1469   TextLine *tmp, *linef, *linel;
1470   int charf, charl;
1471   char *buf;
1472
1473   if (r_buf_strlen) {
1474     *r_buf_strlen = 0;
1475   }
1476
1477   if (!text->curl) {
1478     return NULL;
1479   }
1480   if (!text->sell) {
1481     return NULL;
1482   }
1483   if (!text->lines.first) {
1484     return NULL;
1485   }
1486
1487   linef = text->lines.first;
1488   charf = 0;
1489
1490   linel = text->lines.last;
1491   charl = linel->len;
1492
1493   if (linef == text->lines.last) {
1494     length = charl - charf;
1495
1496     buf = MEM_mallocN(length + 2, "text buffer");
1497
1498     BLI_strncpy(buf, linef->line + charf, length + 1);
1499     buf[length] = 0;
1500   }
1501   else {
1502     length = linef->len - charf;
1503     length += charl;
1504     length += 2; /* For the 2 '\n' */
1505
1506     tmp = linef->next;
1507     while (tmp && tmp != linel) {
1508       length += tmp->len + 1;
1509       tmp = tmp->next;
1510     }
1511
1512     buf = MEM_mallocN(length + 1, "cut buffer");
1513
1514     strncpy(buf, linef->line + charf, linef->len - charf);
1515     length = linef->len - charf;
1516
1517     buf[length++] = '\n';
1518
1519     tmp = linef->next;
1520     while (tmp && tmp != linel) {
1521       strncpy(buf + length, tmp->line, tmp->len);
1522       length += tmp->len;
1523
1524       buf[length++] = '\n';
1525
1526       tmp = tmp->next;
1527     }
1528     strncpy(buf + length, linel->line, charl);
1529     length += charl;
1530
1531     /* python compiler wants an empty end line */
1532     buf[length++] = '\n';
1533     buf[length] = 0;
1534   }
1535
1536   if (r_buf_strlen) {
1537     *r_buf_strlen = length;
1538   }
1539
1540   return buf;
1541 }
1542
1543 char *txt_sel_to_buf(Text *text, int *r_buf_strlen)
1544 {
1545   char *buf;
1546   int length = 0;
1547   TextLine *tmp, *linef, *linel;
1548   int charf, charl;
1549
1550   if (r_buf_strlen) {
1551     *r_buf_strlen = 0;
1552   }
1553
1554   if (!text->curl) {
1555     return NULL;
1556   }
1557   if (!text->sell) {
1558     return NULL;
1559   }
1560
1561   if (text->curl == text->sell) {
1562     linef = linel = text->curl;
1563
1564     if (text->curc < text->selc) {
1565       charf = text->curc;
1566       charl = text->selc;
1567     }
1568     else {
1569       charf = text->selc;
1570       charl = text->curc;
1571     }
1572   }
1573   else if (txt_get_span(text->curl, text->sell) < 0) {
1574     linef = text->sell;
1575     linel = text->curl;
1576
1577     charf = text->selc;
1578     charl = text->curc;
1579   }
1580   else {
1581     linef = text->curl;
1582     linel = text->sell;
1583
1584     charf = text->curc;
1585     charl = text->selc;
1586   }
1587
1588   if (linef == linel) {
1589     length = charl - charf;
1590
1591     buf = MEM_mallocN(length + 1, "sel buffer");
1592
1593     BLI_strncpy(buf, linef->line + charf, length + 1);
1594   }
1595   else {
1596     length += linef->len - charf;
1597     length += charl;
1598     length++; /* For the '\n' */
1599
1600     tmp = linef->next;
1601     while (tmp && tmp != linel) {
1602       length += tmp->len + 1;
1603       tmp = tmp->next;
1604     }
1605
1606     buf = MEM_mallocN(length + 1, "sel buffer");
1607
1608     strncpy(buf, linef->line + charf, linef->len - charf);
1609     length = linef->len - charf;
1610
1611     buf[length++] = '\n';
1612
1613     tmp = linef->next;
1614     while (tmp && tmp != linel) {
1615       strncpy(buf + length, tmp->line, tmp->len);
1616       length += tmp->len;
1617
1618       buf[length++] = '\n';
1619
1620       tmp = tmp->next;
1621     }
1622     strncpy(buf + length, linel->line, charl);
1623     length += charl;
1624
1625     buf[length] = 0;
1626   }
1627
1628   if (r_buf_strlen) {
1629     *r_buf_strlen = length;
1630   }
1631
1632   return buf;
1633 }
1634
1635 void txt_insert_buf(Text *text, const char *in_buffer)
1636 {
1637   int l = 0, len;
1638   size_t i = 0, j;
1639   TextLine *add;
1640   char *buffer;
1641
1642   if (!in_buffer) {
1643     return;
1644   }
1645
1646   txt_delete_sel(text);
1647
1648   len = strlen(in_buffer);
1649   buffer = BLI_strdupn(in_buffer, len);
1650   len += txt_extended_ascii_as_utf8(&buffer);
1651
1652   /* Read the first line (or as close as possible */
1653   while (buffer[i] && buffer[i] != '\n') {
1654     txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &i));
1655   }
1656
1657   if (buffer[i] == '\n') {
1658     txt_split_curline(text);
1659     i++;
1660
1661     while (i < len) {
1662       l = 0;
1663
1664       while (buffer[i] && buffer[i] != '\n') {
1665         i++;
1666         l++;
1667       }
1668
1669       if (buffer[i] == '\n') {
1670         add = txt_new_linen(buffer + (i - l), l);
1671         BLI_insertlinkbefore(&text->lines, text->curl, add);
1672         i++;
1673       }
1674       else {
1675         for (j = i - l; j < i && j < len;) {
1676           txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &j));
1677         }
1678         break;
1679       }
1680     }
1681   }
1682
1683   MEM_freeN(buffer);
1684 }
1685
1686 /** \} */
1687
1688 /* -------------------------------------------------------------------- */
1689 /** \name Find String in Text
1690  * \{ */
1691
1692 int txt_find_string(Text *text, const char *findstr, int wrap, int match_case)
1693 {
1694   TextLine *tl, *startl;
1695   const char *s = NULL;
1696
1697   if (!text->curl || !text->sell) {
1698     return 0;
1699   }
1700
1701   txt_order_cursors(text, false);
1702
1703   tl = startl = text->sell;
1704
1705   if (match_case) {
1706     s = strstr(&tl->line[text->selc], findstr);
1707   }
1708   else {
1709     s = BLI_strcasestr(&tl->line[text->selc], findstr);
1710   }
1711   while (!s) {
1712     tl = tl->next;
1713     if (!tl) {
1714       if (wrap) {
1715         tl = text->lines.first;
1716       }
1717       else {
1718         break;
1719       }
1720     }
1721
1722     if (match_case) {
1723       s = strstr(tl->line, findstr);
1724     }
1725     else {
1726       s = BLI_strcasestr(tl->line, findstr);
1727     }
1728     if (tl == startl) {
1729       break;
1730     }
1731   }
1732
1733   if (s) {
1734     int newl = txt_get_span(text->lines.first, tl);
1735     int newc = (int)(s - tl->line);
1736     txt_move_to(text, newl, newc, 0);
1737     txt_move_to(text, newl, newc + strlen(findstr), 1);
1738     return 1;
1739   }
1740
1741   return 0;
1742 }
1743
1744 /** \} */
1745
1746 /* -------------------------------------------------------------------- */
1747 /** \name Line Editing Functions
1748  * \{ */
1749
1750 void txt_split_curline(Text *text)
1751 {
1752   TextLine *ins;
1753   char *left, *right;
1754
1755   if (!text->curl) {
1756     return;
1757   }
1758
1759   txt_delete_sel(text);
1760
1761   /* Make the two half strings */
1762
1763   left = MEM_mallocN(text->curc + 1, "textline_string");
1764   if (text->curc) {
1765     memcpy(left, text->curl->line, text->curc);
1766   }
1767   left[text->curc] = 0;
1768
1769   right = MEM_mallocN(text->curl->len - text->curc + 1, "textline_string");
1770   memcpy(right, text->curl->line + text->curc, text->curl->len - text->curc + 1);
1771
1772   MEM_freeN(text->curl->line);
1773   if (text->curl->format) {
1774     MEM_freeN(text->curl->format);
1775   }
1776
1777   /* Make the new TextLine */
1778
1779   ins = MEM_mallocN(sizeof(TextLine), "textline");
1780   ins->line = left;
1781   ins->format = NULL;
1782   ins->len = text->curc;
1783
1784   text->curl->line = right;
1785   text->curl->format = NULL;
1786   text->curl->len = text->curl->len - text->curc;
1787
1788   BLI_insertlinkbefore(&text->lines, text->curl, ins);
1789
1790   text->curc = 0;
1791
1792   txt_make_dirty(text);
1793   txt_clean_text(text);
1794
1795   txt_pop_sel(text);
1796 }
1797
1798 static void txt_delete_line(Text *text, TextLine *line)
1799 {
1800   if (!text->curl) {
1801     return;
1802   }
1803
1804   BLI_remlink(&text->lines, line);
1805
1806   if (line->line) {
1807     MEM_freeN(line->line);
1808   }
1809   if (line->format) {
1810     MEM_freeN(line->format);
1811   }
1812
1813   MEM_freeN(line);
1814
1815   txt_make_dirty(text);
1816   txt_clean_text(text);
1817 }
1818
1819 static void txt_combine_lines(Text *text, TextLine *linea, TextLine *lineb)
1820 {
1821   char *tmp, *s;
1822
1823   if (!linea || !lineb) {
1824     return;
1825   }
1826
1827   tmp = MEM_mallocN(linea->len + lineb->len + 1, "textline_string");
1828
1829   s = tmp;
1830   s += BLI_strcpy_rlen(s, linea->line);
1831   s += BLI_strcpy_rlen(s, lineb->line);
1832   (void)s;
1833
1834   make_new_line(linea, tmp);
1835
1836   txt_delete_line(text, lineb);
1837
1838   txt_make_dirty(text);
1839   txt_clean_text(text);
1840 }
1841
1842 void txt_duplicate_line(Text *text)
1843 {
1844   TextLine *textline;
1845
1846   if (!text->curl) {
1847     return;
1848   }
1849
1850   if (text->curl == text->sell) {
1851     textline = txt_new_line(text->curl->line);
1852     BLI_insertlinkafter(&text->lines, text->curl, textline);
1853
1854     txt_make_dirty(text);
1855     txt_clean_text(text);
1856   }
1857 }
1858
1859 void txt_delete_char(Text *text)
1860 {
1861   unsigned int c = '\n';
1862
1863   if (!text->curl) {
1864     return;
1865   }
1866
1867   if (txt_has_sel(text)) { /* deleting a selection */
1868     txt_delete_sel(text);
1869     txt_make_dirty(text);
1870     return;
1871   }
1872   if (text->curc == text->curl->len) { /* Appending two lines */
1873     if (text->curl->next) {
1874       txt_combine_lines(text, text->curl, text->curl->next);
1875       txt_pop_sel(text);
1876     }
1877     else {
1878       return;
1879     }
1880   }
1881   else { /* Just deleting a char */
1882     size_t c_len = 0;
1883     c = BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &c_len);
1884     UNUSED_VARS(c);
1885
1886     memmove(text->curl->line + text->curc,
1887             text->curl->line + text->curc + c_len,
1888             text->curl->len - text->curc - c_len + 1);
1889
1890     text->curl->len -= c_len;
1891
1892     txt_pop_sel(text);
1893   }
1894
1895   txt_make_dirty(text);
1896   txt_clean_text(text);
1897 }
1898
1899 void txt_delete_word(Text *text)
1900 {
1901   txt_jump_right(text, true, true);
1902   txt_delete_sel(text);
1903   txt_make_dirty(text);
1904 }
1905
1906 void txt_backspace_char(Text *text)
1907 {
1908   unsigned int c = '\n';
1909
1910   if (!text->curl) {
1911     return;
1912   }
1913
1914   if (txt_has_sel(text)) { /* deleting a selection */
1915     txt_delete_sel(text);
1916     txt_make_dirty(text);
1917     return;
1918   }
1919   if (text->curc == 0) { /* Appending two lines */
1920     if (!text->curl->prev) {
1921       return;
1922     }
1923
1924     text->curl = text->curl->prev;
1925     text->curc = text->curl->len;
1926
1927     txt_combine_lines(text, text->curl, text->curl->next);
1928     txt_pop_sel(text);
1929   }
1930   else { /* Just backspacing a char */
1931     size_t c_len = 0;
1932     const char *prev = BLI_str_prev_char_utf8(text->curl->line + text->curc);
1933     c = BLI_str_utf8_as_unicode_and_size(prev, &c_len);
1934     UNUSED_VARS(c);
1935
1936     /* source and destination overlap, don't use memcpy() */
1937     memmove(text->curl->line + text->curc - c_len,
1938             text->curl->line + text->curc,
1939             text->curl->len - text->curc + 1);
1940
1941     text->curl->len -= c_len;
1942     text->curc -= c_len;
1943
1944     txt_pop_sel(text);
1945   }
1946
1947   txt_make_dirty(text);
1948   txt_clean_text(text);
1949 }
1950
1951 void txt_backspace_word(Text *text)
1952 {
1953   txt_jump_left(text, true, true);
1954   txt_delete_sel(text);
1955   txt_make_dirty(text);
1956 }
1957
1958 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
1959  * Used by txt_convert_tab_to_spaces, indent and unindent.
1960  * Remember to change this string according to max tab size */
1961 static char tab_to_spaces[] = "    ";
1962
1963 static void txt_convert_tab_to_spaces(Text *text)
1964 {
1965   /* sb aims to pad adjust the tab-width needed so that the right number of spaces
1966    * is added so that the indention of the line is the right width (i.e. aligned
1967    * to multiples of TXT_TABSIZE)
1968    */
1969   const char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
1970   txt_insert_buf(text, sb);
1971 }
1972
1973 static bool txt_add_char_intern(Text *text, unsigned int add, bool replace_tabs)
1974 {
1975   char *tmp, ch[BLI_UTF8_MAX];
1976   size_t add_len;
1977
1978   if (!text->curl) {
1979     return 0;
1980   }
1981
1982   if (add == '\n') {
1983     txt_split_curline(text);
1984     return true;
1985   }
1986
1987   /* insert spaces rather than tabs */
1988   if (add == '\t' && replace_tabs) {
1989     txt_convert_tab_to_spaces(text);
1990     return true;
1991   }
1992
1993   txt_delete_sel(text);
1994
1995   add_len = BLI_str_utf8_from_unicode(add, ch);
1996
1997   tmp = MEM_mallocN(text->curl->len + add_len + 1, "textline_string");
1998
1999   memcpy(tmp, text->curl->line, text->curc);
2000   memcpy(tmp + text->curc, ch, add_len);
2001   memcpy(
2002       tmp + text->curc + add_len, text->curl->line + text->curc, text->curl->len - text->curc + 1);
2003
2004   make_new_line(text->curl, tmp);
2005
2006   text->curc += add_len;
2007
2008   txt_pop_sel(text);
2009
2010   txt_make_dirty(text);
2011   txt_clean_text(text);
2012
2013   return 1;
2014 }
2015
2016 bool txt_add_char(Text *text, unsigned int add)
2017 {
2018   return txt_add_char_intern(text, add, (text->flags & TXT_TABSTOSPACES) != 0);
2019 }
2020
2021 bool txt_add_raw_char(Text *text, unsigned int add)
2022 {
2023   return txt_add_char_intern(text, add, 0);
2024 }
2025
2026 void txt_delete_selected(Text *text)
2027 {
2028   txt_delete_sel(text);
2029   txt_make_dirty(text);
2030 }
2031
2032 bool txt_replace_char(Text *text, unsigned int add)
2033 {
2034   unsigned int del;
2035   size_t del_size = 0, add_size;
2036   char ch[BLI_UTF8_MAX];
2037
2038   if (!text->curl) {
2039     return false;
2040   }
2041
2042   /* If text is selected or we're at the end of the line just use txt_add_char */
2043   if (text->curc == text->curl->len || txt_has_sel(text) || add == '\n') {
2044     return txt_add_char(text, add);
2045   }
2046
2047   del = BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &del_size);
2048   UNUSED_VARS(del);
2049   add_size = BLI_str_utf8_from_unicode(add, ch);
2050
2051   if (add_size > del_size) {
2052     char *tmp = MEM_mallocN(text->curl->len + add_size - del_size + 1, "textline_string");
2053     memcpy(tmp, text->curl->line, text->curc);
2054     memcpy(tmp + text->curc + add_size,
2055            text->curl->line + text->curc + del_size,
2056            text->curl->len - text->curc - del_size + 1);
2057     MEM_freeN(text->curl->line);
2058     text->curl->line = tmp;
2059   }
2060   else if (add_size < del_size) {
2061     char *tmp = text->curl->line;
2062     memmove(tmp + text->curc + add_size,
2063             tmp + text->curc + del_size,
2064             text->curl->len - text->curc - del_size + 1);
2065   }
2066
2067   memcpy(text->curl->line + text->curc, ch, add_size);
2068   text->curc += add_size;
2069   text->curl->len += add_size - del_size;
2070
2071   txt_pop_sel(text);
2072   txt_make_dirty(text);
2073   txt_clean_text(text);
2074   return true;
2075 }
2076
2077 /**
2078  * Generic prefix operation, use for comment & indent.
2079  *
2080  * \note caller must handle undo.
2081  */
2082 static void txt_select_prefix(Text *text, const char *add, bool skip_blank_lines)
2083 {
2084   int len, num, curc_old, selc_old;
2085   char *tmp;
2086
2087   const int indentlen = strlen(add);
2088
2089   BLI_assert(!ELEM(NULL, text->curl, text->sell));
2090
2091   curc_old = text->curc;
2092   selc_old = text->selc;
2093
2094   num = 0;
2095   while (true) {
2096
2097     /* don't indent blank lines */
2098     if ((text->curl->len != 0) || (skip_blank_lines == 0)) {
2099       tmp = MEM_mallocN(text->curl->len + indentlen + 1, "textline_string");
2100
2101       text->curc = 0;
2102       if (text->curc) {
2103         memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
2104       }
2105       memcpy(tmp + text->curc, add, indentlen);
2106
2107       len = text->curl->len - text->curc;
2108       if (len > 0) {
2109         memcpy(tmp + text->curc + indentlen, text->curl->line + text->curc, len);
2110       }
2111       tmp[text->curl->len + indentlen] = 0;
2112
2113       make_new_line(text->curl, tmp);
2114
2115       text->curc += indentlen;
2116
2117       txt_make_dirty(text);
2118       txt_clean_text(text);
2119     }
2120
2121     if (text->curl == text->sell) {
2122       if (text->curl->len != 0) {
2123         text->selc += indentlen;
2124       }
2125       break;
2126     }
2127
2128     text->curl = text->curl->next;
2129     num++;
2130   }
2131
2132   while (num > 0) {
2133     text->curl = text->curl->prev;
2134     num--;
2135   }
2136
2137   /* Keep the cursor left aligned if we don't have a selection. */
2138   if (curc_old == 0 && !(text->curl == text->sell && curc_old == selc_old)) {
2139     if (text->curl == text->sell) {
2140       if (text->curc == text->selc) {
2141         text->selc = 0;
2142       }
2143     }
2144     text->curc = 0;
2145   }
2146   else {
2147     if (text->curl->len != 0) {
2148       text->curc = curc_old + indentlen;
2149     }
2150   }
2151 }
2152
2153 /**
2154  * Generic un-prefix operation, use for comment & indent.
2155  *
2156  * \param require_all: When true, all non-empty lines must have this prefix.
2157  * Needed for comments where we might want to un-comment a block which contains some comments.
2158  *
2159  * \note caller must handle undo.
2160  */
2161 static bool txt_select_unprefix(Text *text, const char *remove, const bool require_all)
2162 {
2163   int num = 0;
2164   const int indentlen = strlen(remove);
2165   bool unindented_first = false;
2166   bool changed_any = false;
2167
2168   BLI_assert(!ELEM(NULL, text->curl, text->sell));
2169
2170   if (require_all) {
2171     /* Check all non-empty lines use this 'remove',
2172      * so the operation is applied equally or not at all. */
2173     TextLine *l = text->curl;
2174     while (true) {
2175       if (STREQLEN(l->line, remove, indentlen)) {
2176         /* pass */
2177       }
2178       else {
2179         /* Blank lines or whitespace can be skipped. */
2180         for (int i = 0; i < l->len; i++) {
2181           if (!ELEM(l->line[i], '\t', ' ')) {
2182             return false;
2183           }
2184         }
2185       }
2186       if (l == text->sell) {
2187         break;
2188       }
2189       l = l->next;
2190     }
2191   }
2192
2193   while (true) {
2194     bool changed = false;
2195     if (STREQLEN(text->curl->line, remove, indentlen)) {
2196       if (num == 0) {
2197         unindented_first = true;
2198       }
2199       text->curl->len -= indentlen;
2200       memmove(text->curl->line, text->curl->line + indentlen, text->curl->len + 1);
2201       changed = true;
2202       changed_any = true;
2203     }
2204
2205     txt_make_dirty(text);
2206     txt_clean_text(text);
2207
2208     if (text->curl == text->sell) {
2209       if (changed) {
2210         text->selc = MAX2(text->selc - indentlen, 0);
2211       }
2212       break;
2213     }
2214
2215     text->curl = text->curl->next;
2216     num++;
2217   }
2218
2219   if (unindented_first) {
2220     text->curc = MAX2(text->curc - indentlen, 0);
2221   }
2222
2223   while (num > 0) {
2224     text->curl = text->curl->prev;
2225     num--;
2226   }
2227
2228   /* caller must handle undo */
2229   return changed_any;
2230 }
2231
2232 void txt_comment(Text *text)
2233 {
2234   const char *prefix = "#";
2235
2236   if (ELEM(NULL, text->curl, text->sell)) {
2237     return;
2238   }
2239
2240   const bool skip_blank_lines = txt_has_sel(text);
2241   txt_select_prefix(text, prefix, skip_blank_lines);
2242 }
2243
2244 bool txt_uncomment(Text *text)
2245 {
2246   const char *prefix = "#";
2247
2248   if (ELEM(NULL, text->curl, text->sell)) {
2249     return false;
2250   }
2251
2252   return txt_select_unprefix(text, prefix, true);
2253 }
2254
2255 void txt_indent(Text *text)
2256 {
2257   const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
2258
2259   if (ELEM(NULL, text->curl, text->sell)) {
2260     return;
2261   }
2262
2263   txt_select_prefix(text, prefix, true);
2264 }
2265
2266 bool txt_unindent(Text *text)
2267 {
2268   const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
2269
2270   if (ELEM(NULL, text->curl, text->sell)) {
2271     return false;
2272   }
2273
2274   return txt_select_unprefix(text, prefix, false);
2275 }
2276
2277 void txt_move_lines(struct Text *text, const int direction)
2278 {
2279   TextLine *line_other;
2280
2281   BLI_assert(ELEM(direction, TXT_MOVE_LINE_UP, TXT_MOVE_LINE_DOWN));
2282
2283   if (!text->curl || !text->sell) {
2284     return;
2285   }
2286
2287   txt_order_cursors(text, false);
2288
2289   line_other = (direction == TXT_MOVE_LINE_DOWN) ? text->sell->next : text->curl->prev;
2290
2291   if (!line_other) {
2292     return;
2293   }
2294
2295   BLI_remlink(&text->lines, line_other);
2296
2297   if (direction == TXT_MOVE_LINE_DOWN) {
2298     BLI_insertlinkbefore(&text->lines, text->curl, line_other);
2299   }
2300   else {
2301     BLI_insertlinkafter(&text->lines, text->sell, line_other);
2302   }
2303
2304   txt_make_dirty(text);
2305   txt_clean_text(text);
2306 }
2307
2308 int txt_setcurr_tab_spaces(Text *text, int space)
2309 {
2310   int i = 0;
2311   int test = 0;
2312   const char *word = ":";
2313   const char *comm = "#";
2314   const char indent = (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
2315   static const char *back_words[] = {"return", "break", "continue", "pass", "yield", NULL};
2316
2317   if (!text->curl) {
2318     return 0;
2319   }
2320
2321   while (text->curl->line[i] == indent) {
2322     // we only count those tabs/spaces that are before any text or before the curs;
2323     if (i == text->curc) {
2324       return i;
2325     }
2326
2327     i++;
2328   }
2329   if (strstr(text->curl->line, word)) {
2330     /* if we find a ':' on this line, then add a tab but not if it is:
2331      * 1) in a comment
2332      * 2) within an identifier
2333      * 3) after the cursor (text->curc), i.e. when creating space before a function def [#25414]
2334      */
2335     int a;
2336     bool is_indent = false;
2337     for (a = 0; (a < text->curc) && (text->curl->line[a] != '\0'); a++) {
2338       char ch = text->curl->line[a];
2339       if (ch == '#') {
2340         break;
2341       }
2342       if (ch == ':') {
2343         is_indent = 1;
2344       }
2345       else if (ch != ' ' && ch != '\t') {
2346         is_indent = 0;
2347       }
2348     }
2349     if (is_indent) {
2350       i += space;
2351     }
2352   }
2353
2354   for (test = 0; back_words[test]; test++) {
2355     /* if there are these key words then remove a tab because we are done with the block */
2356     if (strstr(text->curl->line, back_words[test]) && i > 0) {
2357       if (strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm)) {
2358         i -= space;
2359       }
2360     }
2361   }
2362   return i;
2363 }
2364
2365 /** \} */
2366
2367 /* -------------------------------------------------------------------- */
2368 /** \name Character Queries
2369  * \{ */
2370
2371 int text_check_bracket(const char ch)
2372 {
2373   int a;
2374   char opens[] = "([{";
2375   char close[] = ")]}";
2376
2377   for (a = 0; a < (sizeof(opens) - 1); a++) {
2378     if (ch == opens[a]) {
2379       return a + 1;
2380     }
2381     if (ch == close[a]) {
2382       return -(a + 1);
2383     }
2384   }
2385   return 0;
2386 }
2387
2388 /* TODO, have a function for operators -
2389  * http://docs.python.org/py3k/reference/lexical_analysis.html#operators */
2390 bool text_check_delim(const char ch)
2391 {
2392   int a;
2393   char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,@";
2394
2395   for (a = 0; a < (sizeof(delims) - 1); a++) {
2396     if (ch == delims[a]) {
2397       return true;
2398     }
2399   }
2400   return false;
2401 }
2402
2403 bool text_check_digit(const char ch)
2404 {
2405   if (ch < '0') {
2406     return false;
2407   }
2408   if (ch <= '9') {
2409     return true;
2410   }
2411   return false;
2412 }
2413
2414 bool text_check_identifier(const char ch)
2415 {
2416   if (ch < '0') {
2417     return false;
2418   }
2419   if (ch <= '9') {
2420     return true;
2421   }
2422   if (ch < 'A') {
2423     return false;
2424   }
2425   if (ch <= 'Z' || ch == '_') {
2426     return true;
2427   }
2428   if (ch < 'a') {
2429     return false;
2430   }
2431   if (ch <= 'z') {
2432     return true;
2433   }
2434   return false;
2435 }
2436
2437 bool text_check_identifier_nodigit(const char ch)
2438 {
2439   if (ch <= '9') {
2440     return false;
2441   }
2442   if (ch < 'A') {
2443     return false;
2444   }
2445   if (ch <= 'Z' || ch == '_') {
2446     return true;
2447   }
2448   if (ch < 'a') {
2449     return false;
2450   }
2451   if (ch <= 'z') {
2452     return true;
2453   }
2454   return false;
2455 }
2456
2457 #ifndef WITH_PYTHON
2458 int text_check_identifier_unicode(const unsigned int ch)
2459 {
2460   return (ch < 255 && text_check_identifier((unsigned int)ch));
2461 }
2462
2463 int text_check_identifier_nodigit_unicode(const unsigned int ch)
2464 {
2465   return (ch < 255 && text_check_identifier_nodigit((char)ch));
2466 }
2467 #endif /* WITH_PYTHON */
2468
2469 bool text_check_whitespace(const char ch)
2470 {
2471   if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
2472     return true;
2473   }
2474   return false;
2475 }
2476
2477 int text_find_identifier_start(const char *str, int i)
2478 {
2479   if (UNLIKELY(i <= 0)) {
2480     return 0;
2481   }
2482
2483   while (i--) {
2484     if (!text_check_identifier(str[i])) {
2485       break;
2486     }
2487   }
2488   i++;
2489   return i;
2490 }
2491
2492 /** \} */