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