Add xterm-256color as a valid terminal.
[eco.git] / buffer.c
blobcb5d5d53f324165f65e36cd0b9972a8c7333620e
1 /*
2 * Copyright (C) 2008 - 2012 Diego Hernan Borghetti.
3 * Eco
4 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
10 #include "debug.h"
11 #include "term.h"
12 #include "screen.h"
13 #include "buffer.h"
14 #include "view.h"
15 #include "eco.h"
16 #include "kill.h"
17 #include "file.h"
20 E_Line *e_line_alloc(void)
22 E_Line *ln;
24 ln= (E_Line *)malloc(sizeof(E_Line));
25 ln->next= NULL;
26 ln->prev= NULL;
27 ln->flag= 0;
28 ln->size= 16;
29 ln->used= 0;
30 ln->text= (char *)malloc(16);
31 return(ln);
34 void e_line_realloc(E_Line *ln)
36 ln->size+= 16;
37 ln->text= (char *)realloc((void *)ln->text, ln->size);
40 void e_line_free(E_Line *ln)
42 free((void *)ln->text);
43 free((void *)ln);
46 void e_line_add(E_Buffer *bf, E_Line *ln)
48 ln->next= bf->line->next;
49 ln->prev= bf->line;
51 if (ln->next)
52 ln->next->prev= ln;
53 bf->line->next= ln;
54 bf->line= ln;
55 bf->nlines++;
58 void e_line_add_first(E_Buffer *bf, E_Line *ln)
60 ln->next= bf->lines;
61 ln->prev= NULL;
63 if (ln->next)
64 ln->next->prev= ln;
66 bf->lines= ln;
67 bf->first= ln; /* auto-reframe. */
68 bf->nlines++;
71 void e_line_remove(E_Buffer *bf, E_Line *ln)
73 if (ln->next)
74 ln->next->prev= ln->prev;
75 if (ln->prev)
76 ln->prev->next= ln->next;
78 if (bf->lines == ln)
79 bf->lines= ln->next;
81 if (bf->first == ln) {
82 if (bf->first->prev)
83 bf->first= bf->first->prev;
84 else
85 bf->first= bf->first->next;
88 if (bf->line == ln) {
89 if (bf->line->next)
90 bf->line= bf->line->next;
91 else
92 bf->line= bf->line->prev;
95 ln->next= NULL;
96 ln->prev= NULL;
97 bf->nlines--;
100 E_Buffer *e_buffer_new(char *file)
102 E_Buffer *bf;
104 bf= (E_Buffer *)malloc(sizeof(E_Buffer));
105 bf->next= NULL;
106 bf->paths= e_file_get_paths(file);
108 if (!bf->paths) {
109 free((void *)bf);
110 return(NULL);
113 if (!bf->paths->path) {
114 free((void *)bf->paths);
115 free((void *)bf);
116 return(NULL);
119 if (!bf->paths->file) {
120 free((void *)bf->paths->path);
121 free((void *)bf->paths);
122 return(NULL);
125 /* remove the full path, only show the name. */
126 bf->name= strrchr(bf->paths->file, '/');
127 if (!bf->name)
128 bf->name= bf->paths->file;
129 else
130 bf->name++;
132 e_debug_printf("path: %s\n", bf->paths->path);
133 e_debug_printf("file: %s\n", bf->paths->file);
134 e_debug_printf("name: %s\n", bf->name);
136 bf->lines= e_line_alloc();
137 bf->first= bf->lines;
138 bf->line= bf->lines;
139 bf->dot= 0;
140 bf->dot_pad= 0;
141 bf->flag= 0;
142 bf->nlines= 1;
143 bf->nl= 1;
144 return(bf);
147 void e_buffer_free(E_Buffer *bf)
149 E_Line *p;
151 /* only remove the lock when we free the buffer. */
152 e_file_lock_rem(bf->paths);
154 free((void *)bf->paths->path);
155 free((void *)bf->paths->file);
156 free((void *)bf->paths->lock_file);
157 free((void *)bf->paths);
159 while (bf->lines) {
160 p= bf->lines->next;
161 e_line_free(bf->lines);
162 bf->lines= p;
164 free((void *)bf);
167 void e_buffer_recalc(E_Buffer *bf)
169 E_Line *p;
171 bf->nl= 1;
172 p= bf->lines;
173 while (p) {
174 if (p == bf->line)
175 break;
176 bf->nl++;
177 p= p->next;
181 /* Add a new line at the current position in the buffer. */
182 void e_buffer_newline(E_Buffer *bf)
184 E_Line *ln, *lnp;
186 BUFFER_SET(bf, BUFFER_FLUSH);
188 /* save the current line. */
189 lnp= bf->line;
191 ln= e_line_alloc();
192 e_line_add(bf, ln);
194 /* Always clean the current position, the new line don't
195 * have any text..
197 bf->dot= 0;
198 bf->nl++;
200 BUFFER_SET(bf, BUFFER_DOWN);
202 if (!(bf->flag & BUFFER_NOIDENT)) {
203 int i, tot;
205 /* Check if we have tabs on the current line
206 * to insert in the next.
208 for (i= 0, tot= 0; i < lnp->used; i++) {
209 /* If the line don't have any tab at
210 * the start there is nothing to do.
212 if (i == 0 && lnp->text[i] != '\t')
213 break;
214 else if (lnp->text[i] == '\t')
215 tot++;
216 else
217 break;
220 if (tot) {
221 e_buffer_insert(bf, '\t', tot);
223 /* If the last line finish with a { add an extra tab. */
224 if (lnp->text[lnp->used-1] == '{')
225 e_buffer_insert(bf, '\t', 1);
227 /* If the last line was empty, remove all the tabs. */
228 if (tot == lnp->used)
229 lnp->used= 0;
234 /* Add a new line at the begin of the buffer. */
235 void e_buffer_newline_first(E_Buffer *bf)
237 E_Line *ln;
239 BUFFER_SET(bf, BUFFER_FLUSH);
241 ln= e_line_alloc();
242 e_line_add_first(bf, ln);
243 bf->dot= 0;
244 bf->nl++;
247 /* Split the current line in two line, the starting
248 * point for split is the current cursor position.
250 void e_buffer_splitline(E_Buffer *bf)
252 E_Line *ln;
253 int i, dot;
255 /* save the current line. */
256 ln= bf->line;
258 /* and the cursor position. */
259 dot= bf->dot;
261 /* insert a new line and add all the character
262 * from the current position, to the end.
264 e_buffer_newline(bf);
265 for (i= dot; i < ln->used; i++)
266 e_buffer_insert(bf, ln->text[i], 1);
268 ln->used= dot;
270 /* Always go to the begin of the line. */
271 bf->dot= 0;
274 /* Join two line, the source line is the current line and
275 * the dest line is the prev.
277 void e_buffer_joinline(E_Buffer *bf)
279 E_Line *ln;
280 int i, dot;
282 /* if we are at the begin, return. */
283 if (!bf->line->prev)
284 return;
286 /* save the current line. */
287 ln= bf->line;
289 /* move to the previous. */
290 e_buffer_up(bf);
292 /* put the cursor to the end of the line. */
293 bf->dot= bf->line->used;
295 /* and save the position. */
296 dot= bf->dot;
298 /* insert all the characters. */
299 for (i= 0; i < ln->used; i++)
300 e_buffer_insert(bf, ln->text[i], 1);
302 /* remove the unused line. */
303 e_line_remove(bf, ln);
304 e_line_free(ln);
306 /* restore the original cursor position. */
307 bf->dot= dot;
308 e_buffer_recalc(bf);
311 /* Remove the current line from the buffer, this also
312 * can save the line in the cut-buffer.
314 * If 'cut' is equal to two, only copy the line to the
315 * cut-buffer.
317 void e_buffer_killline(E_Buffer *bf, int cut)
319 E_Line *ln;
321 if (cut) {
322 e_kill_cut(bf->line);
323 if (cut == 2) {
324 if (bf->line->used)
325 e_kill_cut(NULL);
326 return;
330 if ((!bf->line->prev) && (!bf->line->next)) {
331 /* If we don't have previous or next line,
332 * the only thing to do is clean the
333 * current line.
335 e_buffer_cleanline(bf);
336 return;
338 else if (!bf->line->next) {
339 /* If we don't have next line, only clean. */
340 e_buffer_cleanline(bf);
341 return;
344 if (bf->line->used) {
345 /* This work like emacs, if the line have text
346 * first clean the text.
348 e_buffer_cleanline(bf);
349 return;
352 /* And if the line don't have text, remove it. */
353 BUFFER_SET(bf, BUFFER_FLUSH);
355 ln= bf->line;
356 e_buffer_down(bf);
357 e_line_remove(bf, ln);
358 e_line_free(ln);
359 e_buffer_recalc(bf);
362 /* Insert a character in the current line at the current
363 * cursor position.
365 void e_buffer_insert(E_Buffer *bf, int c, int repeat)
367 int i, j;
369 BUFFER_SET(bf, BUFFER_FLUSH);
371 if (!(bf->flag & BUFFER_NOIDENT)) {
372 if (bf->line->text[bf->dot] == ')' && c == ')') {
373 if (bf->dot < bf->line->used)
374 bf->dot++;
375 return;
377 else if (bf->line->text[bf->dot] == ';' && c == ';') {
378 if (bf->dot < bf->line->used)
379 bf->dot++;
380 return;
384 for (j= 0; j < repeat; j++) {
385 /* check if we need reallocate the line. */
386 if ((bf->line->used+1) >= bf->line->size)
387 e_line_realloc(bf->line);
389 /* if the cursor is not at the end of the line,
390 * we need scroll the text to the right.
392 if (bf->dot != bf->line->used) {
393 for (i= bf->line->used; i > bf->dot; i--)
394 bf->line->text[i]= bf->line->text[i-1];
397 /* insert the new character. */
398 bf->line->text[bf->dot]= c;
399 bf->dot++;
400 bf->line->used++;
403 if (!(bf->flag & BUFFER_NOIDENT)) {
404 if (c == '(') {
405 /* save the current position. */
406 i= bf->dot;
408 /* insert the second part. */
409 e_buffer_insert(bf, ')', 1);
411 /* and move back the cursor. */
412 bf->dot= i;
418 * Insert multiples characters in the current line at
419 * the current cursor position ('\n' are valid here!).
421 void e_buffer_insert_str(E_Buffer *bf, char *str, int len)
423 char *p;
424 int i;
426 BUFFER_SET(bf, BUFFER_FLUSH);
428 p= str;
429 while (len > 0) {
430 if (*p == '\n')
431 e_buffer_newline(bf);
432 else {
433 /* check if we need reallocate the line. */
434 if ((bf->line->used+1) >= bf->line->size)
435 e_line_realloc(bf->line);
437 /* if the cursor is not at the end of the line,
438 * we need scroll the text to the right.
440 if (bf->dot != bf->line->used) {
441 for (i= bf->line->used; i > bf->dot; i--)
442 bf->line->text[i]= bf->line->text[i-1];
445 /* insert the new character. */
446 bf->line->text[bf->dot]= *p;
447 bf->dot++;
448 bf->line->used++;
450 p++;
451 len--;
455 /* Just that, insert a backspace ;) */
456 void e_buffer_backspace(E_Buffer *bf)
458 int i;
460 /* The only case that we have here is if the cursor position
461 * is at the begin of the line, in that case we need join
462 * the two lines.
464 if (bf->dot > 0) {
465 BUFFER_SET(bf, BUFFER_FLUSH);
467 /* If the cursor is not at the end of the line,
468 * we need scroll the text.
470 for (i= bf->dot; i < bf->line->used; i++)
471 bf->line->text[i-1]= bf->line->text[i];
472 bf->dot--;
473 bf->line->used--;
475 else
476 e_buffer_joinline(bf);
479 void e_buffer_del(E_Buffer *bf)
481 e_buffer_right(bf);
482 e_buffer_backspace(bf);
485 void e_buffer_up(E_Buffer *bf)
487 if (bf->line->prev) {
488 bf->line= bf->line->prev;
489 if (bf->dot > bf->line->used)
490 bf->dot= bf->line->used;
491 bf->nl--;
492 BUFFER_SET(bf, BUFFER_UP);
496 void e_buffer_down(E_Buffer *bf)
498 if (bf->line->next) {
499 bf->line= bf->line->next;
500 if (bf->dot > bf->line->used)
501 bf->dot= bf->line->used;
502 bf->nl++;
503 BUFFER_SET(bf, BUFFER_DOWN);
507 void e_buffer_left(E_Buffer *bf)
509 if (bf->dot > 0)
510 bf->dot--;
511 else {
512 e_buffer_up(bf);
513 e_buffer_eol(bf);
517 void e_buffer_right(E_Buffer *bf)
519 if (bf->dot < bf->line->used)
520 bf->dot++;
521 else {
522 e_buffer_down(bf);
523 e_buffer_bol(bf);
527 void e_buffer_goto_begin(E_Buffer *bf)
529 bf->line= bf->lines;
530 bf->dot= 0;
531 bf->nl= 1;
534 void e_buffer_goto_end(E_Buffer *bf)
536 E_Line *p;
538 p= bf->line;
539 while (p->next)
540 p= p->next;
542 bf->line= p;
543 bf->dot= 0;
544 bf->nl= bf->nlines+1; /* zero */
547 void e_buffer_bol(E_Buffer *bf)
549 bf->dot= 0;
552 void e_buffer_eol(E_Buffer *bf)
554 bf->dot= bf->line->used;
557 void e_buffer_scroll(E_Buffer *bf, int nline, int dir)
559 int i;
561 for(i= 0; i < nline; i++) {
562 if (dir) {
563 if (!bf->first->prev)
564 break;
565 bf->first= bf->first->prev;
566 if (bf->line->prev)
567 bf->line= bf->line->prev;
569 else {
570 if (!bf->first->next)
571 break;
572 bf->first= bf->first->next;
573 if (bf->line->next)
574 bf->line= bf->line->next;
577 if (bf->dot > bf->line->used)
578 bf->dot= bf->line->used;
579 e_buffer_recalc(bf);
582 void e_buffer_goto(E_Buffer *bf, int line)
584 E_Line *p;
585 int num;
587 num= 1;
588 p= bf->lines;
589 while (p) {
590 if (num == line)
591 break;
592 num++;
593 p= p->next;
596 if (!p)
597 return;
599 bf->nl= num;
600 bf->line= p;
601 if (bf->dot > bf->line->used)
602 bf->dot= 0;
605 void e_buffer_cleanline(E_Buffer *bf)
607 BUFFER_SET(bf, BUFFER_FLUSH);
609 bf->line->used= 0;
610 bf->dot= 0;
613 static E_Line *__e_buffer_search(E_Buffer *bf, char *pattern, int *dot_found, int dir)
615 E_Line *ln;
616 int i, e, a, len, dot, found;
618 len= strlen(pattern);
619 found= 0;
620 dot= 0;
622 /* I know.. this is not the best backward.. but work. */
623 if (dir == BUFFER_SEARCH_FORWARD)
624 ln= bf->line;
625 else
626 ln= bf->line->prev;
628 while (ln) {
629 if (dir == BUFFER_SEARCH_FORWARD && ln == bf->line)
630 i= bf->dot;
631 else
632 i= 0;
634 for (; i < ln->used; i++) {
635 if (ln->text[i] == pattern[0]) {
636 /* save the current position of the cursor. */
637 a= i;
638 dot= i;
639 found= 1;
641 for (e= 0; e < len; e++) {
642 if (ln->text[i] != pattern[e]) {
643 found= 0;
644 break;
647 i++;
648 if (i > ln->used) {
649 found= 0;
650 break;
654 i= a;
657 if (found)
658 break;
661 if (found)
662 break;
664 if (dir == BUFFER_SEARCH_FORWARD)
665 ln= ln->next;
666 else
667 ln= ln->prev;
670 if (dir == BUFFER_SEARCH_FORWARD)
671 *dot_found= (dot+len);
672 else
673 *dot_found= dot;
675 return(ln);
678 void e_buffer_search(E_Buffer *bf, char *pattern, int dir)
680 E_Line *ln;
681 int dot;
683 ln= __e_buffer_search(bf, pattern, &dot, dir);
684 if (ln) {
685 bf->first= ln;
686 bf->line= ln;
687 bf->dot= dot;
688 e_buffer_recalc(bf);
692 int e_buffer_replace(E_Buffer *bf, char *pattern, char *replace)
694 E_Line *ln;
695 int i, c, len, ncount, dot;
697 /* Avoid identation during the replace. */
698 bf->flag |= BUFFER_NOIDENT;
700 c= strlen(pattern);
701 len= strlen(replace);
702 ncount= 0;
704 ln= __e_buffer_search(bf, pattern, &dot, BUFFER_SEARCH_FORWARD);
705 while (ln) {
706 /* first set the line, so all the next call
707 * work fine.
709 bf->first= ln;
710 bf->line= ln;
711 bf->dot= dot;
712 e_buffer_recalc(bf);
714 /* now remove the pattern. */
715 for (i= 0; i < c; i++)
716 e_buffer_backspace(bf);
718 /* and now insert the new string. */
719 for (i= 0; i < len; i++)
720 e_buffer_insert(bf, replace[i], 1);
722 ncount++;
723 ln= __e_buffer_search(bf, pattern, &dot, BUFFER_SEARCH_FORWARD);
726 /* and remove the flag. */
727 bf->flag &= ~BUFFER_NOIDENT;
729 return(ncount);
732 void e_buffer_rem_empty_lines(E_Buffer *bf)
734 E_Line *ln;
735 int i, found, tot;
737 ln= bf->lines;
738 tot= 0;
739 while (ln) {
740 found= 1;
741 for (i= 0; i < ln->used; i++) {
742 if (ln->text[i] != ' ' && ln->text[i] != '\t') {
743 found= 0;
744 break;
748 /* If the line is empty, remove any ' ' or '\t' character. */
749 if (found) {
750 ln->used= 0;
751 tot++;
754 ln= ln->next;
757 if (tot)
758 BUFFER_SET(bf, BUFFER_FLUSH);