9 #include "text-motions.h"
10 #include "text-util.h"
20 SYNTAX_SYMBOL_TAB_FILL
,
26 /* A selection is made up of two marks named cursor and anchor.
27 * While the anchor remains fixed the cursor mark follows cursor motions.
28 * For a selection (indicated by []), the marks (^) are placed as follows:
34 * That is the marks point to the *start* of the first and last character
35 * of the selection. In particular for a single character selection (as
36 * depicted on the right above) both marks point to the same location.
38 * The view_selections_{get,set} functions take care of adding/removing
39 * the necessary offset for the last character.
43 Mark cursor
; /* other selection endpoint where it changes */
44 Mark anchor
; /* position where the selection was created */
45 bool anchored
; /* whether anchor remains fixed */
46 size_t pos
; /* in bytes from the start of the file */
47 int row
, col
; /* in terms of zero based screen coordinates */
48 int lastcol
; /* remembered column used when moving across lines */
49 Line
*line
; /* screen line on which cursor currently resides */
50 int generation
; /* used to filter out newly created cursors during iteration */
51 int number
; /* how many cursors are located before this one */
52 View
*view
; /* associated view to which this cursor belongs */
53 Selection
*prev
, *next
; /* previous/next cursors ordered by location at creation time */
57 Text
*text
; /* underlying text management */
58 char *textbuf
; /* scratch buffer used for drawing */
59 UiWin
*ui
; /* corresponding ui window */
60 Cell cell_blank
; /* used for empty/blank cells */
61 int width
, height
; /* size of display area */
62 size_t start
, end
; /* currently displayed area [start, end] in bytes from the start of the file */
63 size_t start_last
; /* previously used start of visible area, used to update the mark */
64 Mark start_mark
; /* mark to keep track of the start of the visible area */
65 size_t lines_size
; /* number of allocated bytes for lines (grows only) */
66 Line
*lines
; /* view->height number of lines representing view content */
67 Line
*topline
; /* top of the view, first line currently shown */
68 Line
*lastline
; /* last currently used line, always <= bottomline */
69 Line
*bottomline
; /* bottom of view, might be unused if lastline < bottomline */
70 Selection
*selection
; /* primary selection, always placed within the visible viewport */
71 Selection
*selection_latest
; /* most recently created cursor */
72 Selection
*selection_dead
; /* primary cursor which was disposed, will be removed when another cursor is created */
73 int selection_count
; /* how many cursors do currently exist */
74 Line
*line
; /* used while drawing view content, line where next char will be drawn */
75 int col
; /* used while drawing view content, column where next char will be drawn */
76 const SyntaxSymbol
*symbols
[SYNTAX_SYMBOL_LAST
]; /* symbols to use for white spaces etc */
77 int tabwidth
; /* how many spaces should be used to display a tab character */
78 Selection
*selections
; /* all cursors currently active */
79 int selection_generation
; /* used to filter out newly created cursors during iteration */
80 bool need_update
; /* whether view has been redrawn */
81 bool large_file
; /* optimize for displaying large files */
85 static const SyntaxSymbol symbols_none
[] = {
86 [SYNTAX_SYMBOL_SPACE
] = { " " },
87 [SYNTAX_SYMBOL_TAB
] = { " " },
88 [SYNTAX_SYMBOL_TAB_FILL
] = { " " },
89 [SYNTAX_SYMBOL_EOL
] = { " " },
90 [SYNTAX_SYMBOL_EOF
] = { " " },
93 static const SyntaxSymbol symbols_default
[] = {
94 [SYNTAX_SYMBOL_SPACE
] = { "·" /* Middle Dot U+00B7 */ },
95 [SYNTAX_SYMBOL_TAB
] = { "›" /* Single Right-Pointing Angle Quotation Mark U+203A */ },
96 [SYNTAX_SYMBOL_TAB_FILL
] = { " " },
97 [SYNTAX_SYMBOL_EOL
] = { "↵" /* Downwards Arrow with Corner Leftwards U+21B5 */ },
98 [SYNTAX_SYMBOL_EOF
] = { "~" },
101 static Cell cell_unused
;
104 /* move visible viewport n-lines up/down, redraws the view but does not change
105 * cursor position which becomes invalid and should be corrected by calling
106 * view_cursor_to. the return value indicates wether the visible area changed.
108 static bool view_viewport_up(View
*view
, int n
);
109 static bool view_viewport_down(View
*view
, int n
);
111 static void view_clear(View
*view
);
112 static bool view_addch(View
*view
, Cell
*cell
);
113 static void selection_free(Selection
*);
114 /* set/move current cursor position to a given (line, column) pair */
115 static size_t cursor_set(Selection
*, Line
*line
, int col
);
117 void view_tabwidth_set(View
*view
, int tabwidth
) {
118 view
->tabwidth
= tabwidth
;
122 /* reset internal view data structures (cell matrix, line offsets etc.) */
123 static void view_clear(View
*view
) {
124 memset(view
->lines
, 0, view
->lines_size
);
125 if (view
->start
!= view
->start_last
) {
126 if (view
->start
== 0)
127 view
->start_mark
= EMARK
;
129 view
->start_mark
= text_mark_set(view
->text
, view
->start
);
132 if (view
->start_mark
== EMARK
)
135 start
= text_mark_get(view
->text
, view
->start_mark
);
140 view
->start_last
= view
->start
;
141 view
->topline
= view
->lines
;
142 view
->topline
->lineno
= view
->large_file
? 1 : text_lineno_by_pos(view
->text
, view
->start
);
143 view
->lastline
= view
->topline
;
145 size_t line_size
= sizeof(Line
) + view
->width
*sizeof(Cell
);
146 size_t end
= view
->height
* line_size
;
148 for (size_t i
= 0; i
< end
; i
+= line_size
) {
149 Line
*line
= (Line
*)(((char*)view
->lines
) + i
);
155 view
->bottomline
= prev
? prev
: view
->topline
;
156 view
->bottomline
->next
= NULL
;
157 view
->line
= view
->topline
;
160 view
->cell_blank
.style
= view
->ui
->style_get(view
->ui
, UI_STYLE_DEFAULT
);
163 Filerange
view_viewport_get(View
*view
) {
164 return (Filerange
){ .start
= view
->start
, .end
= view
->end
};
167 /* try to add another character to the view, return whether there was space left */
168 static bool view_addch(View
*view
, Cell
*cell
) {
173 size_t lineno
= view
->line
->lineno
;
174 unsigned char ch
= (unsigned char)cell
->data
[0];
175 cell
->style
= view
->cell_blank
.style
;
180 width
= view
->tabwidth
- (view
->col
% view
->tabwidth
);
181 for (int w
= 0; w
< width
; w
++) {
182 if (view
->col
+ 1 > view
->width
) {
183 view
->line
= view
->line
->next
;
187 view
->line
->lineno
= lineno
;
190 cell
->len
= w
== 0 ? 1 : 0;
191 int t
= w
== 0 ? SYNTAX_SYMBOL_TAB
: SYNTAX_SYMBOL_TAB_FILL
;
192 strncpy(cell
->data
, view
->symbols
[t
]->symbol
, sizeof(cell
->data
)-1);
193 view
->line
->cells
[view
->col
] = *cell
;
194 view
->line
->len
+= cell
->len
;
195 view
->line
->width
+= cell
->width
;
202 if (view
->col
+ cell
->width
> view
->width
) {
203 view
->line
= view
->line
->next
;
207 view
->line
->lineno
= lineno
;
210 strncpy(cell
->data
, view
->symbols
[SYNTAX_SYMBOL_EOL
]->symbol
, sizeof(cell
->data
)-1);
212 view
->line
->cells
[view
->col
] = *cell
;
213 view
->line
->len
+= cell
->len
;
214 view
->line
->width
+= cell
->width
;
215 for (int i
= view
->col
+ 1; i
< view
->width
; i
++)
216 view
->line
->cells
[i
] = view
->cell_blank
;
218 view
->line
= view
->line
->next
;
220 view
->line
->lineno
= lineno
+ 1;
224 if (ch
< 128 && !isprint(ch
)) {
225 /* non-printable ascii char, represent it as ^(char + 64) */
227 .data
= { '^', ch
== 127 ? '?' : ch
+ 64, '\0' },
230 .style
= cell
->style
,
235 strncpy(cell
->data
, view
->symbols
[SYNTAX_SYMBOL_SPACE
]->symbol
, sizeof(cell
->data
)-1);
239 if (view
->col
+ cell
->width
> view
->width
) {
240 for (int i
= view
->col
; i
< view
->width
; i
++)
241 view
->line
->cells
[i
] = view
->cell_blank
;
242 view
->line
= view
->line
->next
;
247 view
->line
->width
+= cell
->width
;
248 view
->line
->len
+= cell
->len
;
249 view
->line
->lineno
= lineno
;
250 view
->line
->cells
[view
->col
] = *cell
;
252 /* set cells of a character which uses multiple columns */
253 for (int i
= 1; i
< cell
->width
; i
++)
254 view
->line
->cells
[view
->col
++] = cell_unused
;
261 static void cursor_to(Selection
*s
, size_t pos
) {
262 Text
*txt
= s
->view
->text
;
263 s
->cursor
= text_mark_set(txt
, pos
);
265 s
->anchor
= s
->cursor
;
269 if (!view_coord_get(s
->view
, pos
, &s
->line
, &s
->row
, &s
->col
)) {
270 if (s
->view
->selection
== s
) {
271 s
->line
= s
->view
->topline
;
277 // TODO: minimize number of redraws
281 bool view_coord_get(View
*view
, size_t pos
, Line
**retline
, int *retrow
, int *retcol
) {
282 int row
= 0, col
= 0;
283 size_t cur
= view
->start
;
284 Line
*line
= view
->topline
;
286 if (pos
< view
->start
|| pos
> view
->end
) {
287 if (retline
) *retline
= NULL
;
288 if (retrow
) *retrow
= -1;
289 if (retcol
) *retcol
= -1;
293 while (line
&& line
!= view
->lastline
&& cur
< pos
) {
294 if (cur
+ line
->len
> pos
)
302 int max_col
= MIN(view
->width
, line
->width
);
303 while (cur
< pos
&& col
< max_col
) {
304 cur
+= line
->cells
[col
].len
;
305 /* skip over columns occupied by the same character */
306 while (++col
< max_col
&& line
->cells
[col
].len
== 0);
309 line
= view
->bottomline
;
310 row
= view
->height
- 1;
313 if (retline
) *retline
= line
;
314 if (retrow
) *retrow
= row
;
315 if (retcol
) *retcol
= col
;
319 /* move the cursor to the character at pos bytes from the begining of the file.
320 * if pos is not in the current viewport, redraw the view to make it visible */
321 void view_cursor_to(View
*view
, size_t pos
) {
322 view_cursors_to(view
->selection
, pos
);
325 /* redraw the complete with data starting from view->start bytes into the file.
326 * stop once the screen is full, update view->end, view->lastline */
327 void view_draw(View
*view
) {
329 /* read a screenful of text considering each character as 4-byte UTF character*/
330 const size_t size
= view
->width
* view
->height
* 4;
331 /* current buffer to work with */
332 char *text
= view
->textbuf
;
333 /* remaining bytes to process in buffer */
334 size_t rem
= text_bytes_get(view
->text
, view
->start
, size
, text
);
335 /* NUL terminate text section */
337 /* absolute position of character currently being added to display */
338 size_t pos
= view
->start
;
339 /* current position into buffer from which to interpret a character */
341 /* start from known multibyte state */
342 mbstate_t mbstate
= { 0 };
344 Cell cell
= { .data
= "", .len
= 0, .width
= 0, }, prev_cell
= cell
;
348 /* current 'parsed' character' */
351 size_t len
= mbrtowc(&wchar
, cur
, rem
, &mbstate
);
352 if (len
== (size_t)-1 && errno
== EILSEQ
) {
353 /* ok, we encountered an invalid multibyte sequence,
354 * replace it with the Unicode Replacement Character
355 * (FFFD) and skip until the start of the next utf8 char */
356 mbstate
= (mbstate_t){0};
357 for (len
= 1; rem
> len
&& !ISUTF8(cur
[len
]); len
++);
358 cell
= (Cell
){ .data
= "\xEF\xBF\xBD", .len
= len
, .width
= 1 };
359 } else if (len
== (size_t)-2) {
360 /* not enough bytes available to convert to a
361 * wide character. advance file position and read
362 * another junk into buffer.
364 rem
= text_bytes_get(view
->text
, pos
+prev_cell
.len
, size
, text
);
368 } else if (len
== 0) {
369 /* NUL byte encountered, store it and continue */
370 cell
= (Cell
){ .data
= "\x00", .len
= 1, .width
= 2 };
372 if (len
>= sizeof(cell
.data
))
373 len
= sizeof(cell
.data
)-1;
374 for (size_t i
= 0; i
< len
; i
++)
375 cell
.data
[i
] = cur
[i
];
376 cell
.data
[len
] = '\0';
378 cell
.width
= wcwidth(wchar
);
379 if (cell
.width
== -1)
383 if (cell
.width
== 0) {
384 strncat(prev_cell
.data
, cell
.data
, sizeof(prev_cell
.data
)-strlen(prev_cell
.data
)-1);
385 prev_cell
.len
+= cell
.len
;
387 if (prev_cell
.len
&& !view_addch(view
, &prev_cell
))
389 pos
+= prev_cell
.len
;
396 memset(&cell
, 0, sizeof cell
);
399 if (prev_cell
.len
&& view_addch(view
, &prev_cell
))
400 pos
+= prev_cell
.len
;
402 /* set end of viewing region */
405 bool eof
= view
->end
== text_size(view
->text
);
406 if (view
->line
->len
== 0 && eof
&& view
->line
->prev
)
407 view
->lastline
= view
->line
->prev
;
409 view
->lastline
= view
->line
;
411 view
->lastline
= view
->bottomline
;
414 /* clear remaining of line, important to show cursor at end of file */
416 for (int x
= view
->col
; x
< view
->width
; x
++)
417 view
->line
->cells
[x
] = view
->cell_blank
;
420 /* resync position of cursors within visible area */
421 for (Selection
*s
= view
->selections
; s
; s
= s
->next
) {
422 size_t pos
= view_cursors_pos(s
);
423 if (!view_coord_get(view
, pos
, &s
->line
, &s
->row
, &s
->col
) &&
424 s
== view
->selection
) {
425 s
->line
= view
->topline
;
431 view
->need_update
= true;
434 void view_invalidate(View
*view
) {
435 view
->need_update
= true;
438 bool view_update(View
*view
) {
439 if (!view
->need_update
)
441 for (Line
*l
= view
->lastline
->next
; l
; l
= l
->next
) {
442 for (int x
= 0; x
< view
->width
; x
++)
443 l
->cells
[x
] = view
->cell_blank
;
445 view
->need_update
= false;
449 bool view_resize(View
*view
, int width
, int height
) {
454 if (view
->width
== width
&& view
->height
== height
) {
455 view
->need_update
= true;
458 char *textbuf
= malloc(width
* height
* 4 + 1);
461 size_t lines_size
= height
*(sizeof(Line
) + width
*sizeof(Cell
));
462 if (lines_size
> view
->lines_size
) {
463 Line
*lines
= realloc(view
->lines
, lines_size
);
469 view
->lines_size
= lines_size
;
472 view
->textbuf
= textbuf
;
474 view
->height
= height
;
475 memset(view
->lines
, 0, view
->lines_size
);
480 int view_height_get(View
*view
) {
484 int view_width_get(View
*view
) {
488 void view_free(View
*view
) {
491 while (view
->selections
)
492 selection_free(view
->selections
);
498 void view_reload(View
*view
, Text
*text
) {
500 view_selections_clear_all(view
);
501 view_cursor_to(view
, 0);
504 View
*view_new(Text
*text
) {
507 View
*view
= calloc(1, sizeof(View
));
511 if (!view_selections_new(view
, 0)) {
516 view
->cell_blank
= (Cell
) {
522 view_options_set(view
, 0);
524 if (!view_resize(view
, 1, 1)) {
529 view_cursor_to(view
, 0);
534 void view_ui(View
*view
, UiWin
* ui
) {
538 static size_t cursor_set(Selection
*sel
, Line
*line
, int col
) {
540 View
*view
= sel
->view
;
541 size_t pos
= view
->start
;
542 /* get row number and file offset at start of the given line */
543 for (Line
*l
= view
->topline
; l
&& l
!= line
; l
= l
->next
) {
548 /* for characters which use more than 1 column, make sure we are on the left most */
549 while (col
> 0 && line
->cells
[col
].len
== 0)
551 /* calculate offset within the line */
552 for (int i
= 0; i
< col
; i
++)
553 pos
+= line
->cells
[i
].len
;
564 static bool view_viewport_down(View
*view
, int n
) {
566 if (view
->end
>= text_size(view
->text
))
568 if (n
>= view
->height
) {
569 view
->start
= view
->end
;
571 for (line
= view
->topline
; line
&& n
> 0; line
= line
->next
, n
--)
572 view
->start
+= line
->len
;
578 static bool view_viewport_up(View
*view
, int n
) {
579 /* scrolling up is somewhat tricky because we do not yet know where
580 * the lines start, therefore scan backwards but stop at a reasonable
581 * maximum in case we are dealing with a file without any newlines
583 if (view
->start
== 0)
585 size_t max
= view
->width
* view
->height
;
587 Iterator it
= text_iterator_get(view
->text
, view
->start
- 1);
589 if (!text_iterator_byte_get(&it
, &c
))
592 /* skip newlines immediately before display area */
593 if (c
== '\n' && text_iterator_byte_prev(&it
, &c
))
596 if (c
== '\n' && --n
== 0)
600 } while (text_iterator_byte_prev(&it
, &c
));
601 view
->start
-= MIN(view
->start
, off
);
606 void view_redraw_top(View
*view
) {
607 Line
*line
= view
->selection
->line
;
608 for (Line
*cur
= view
->topline
; cur
&& cur
!= line
; cur
= cur
->next
)
609 view
->start
+= cur
->len
;
611 view_cursor_to(view
, view
->selection
->pos
);
614 void view_redraw_center(View
*view
) {
615 int center
= view
->height
/ 2;
616 size_t pos
= view
->selection
->pos
;
617 for (int i
= 0; i
< 2; i
++) {
619 Line
*line
= view
->selection
->line
;
620 for (Line
*cur
= view
->topline
; cur
&& cur
!= line
; cur
= cur
->next
)
622 if (linenr
< center
) {
623 view_slide_down(view
, center
- linenr
);
626 for (Line
*cur
= view
->topline
; cur
&& cur
!= line
&& linenr
> center
; cur
= cur
->next
) {
627 view
->start
+= cur
->len
;
633 view_cursor_to(view
, pos
);
636 void view_redraw_bottom(View
*view
) {
637 size_t pos
= view
->selection
->pos
;
638 view_viewport_up(view
, view
->height
);
639 while (pos
>= view
->end
&& view_viewport_down(view
, 1));
640 cursor_to(view
->selection
, pos
);
643 size_t view_slide_up(View
*view
, int lines
) {
644 Selection
*sel
= view
->selection
;
645 if (view_viewport_down(view
, lines
)) {
646 if (sel
->line
== view
->topline
)
647 cursor_set(sel
, view
->topline
, sel
->col
);
649 view_cursor_to(view
, sel
->pos
);
651 view_screenline_down(sel
);
656 size_t view_slide_down(View
*view
, int lines
) {
657 Selection
*sel
= view
->selection
;
658 bool lastline
= sel
->line
== view
->lastline
;
659 size_t col
= sel
->col
;
660 if (view_viewport_up(view
, lines
)) {
662 cursor_set(sel
, view
->lastline
, col
);
664 view_cursor_to(view
, sel
->pos
);
666 view_screenline_up(sel
);
671 size_t view_scroll_up(View
*view
, int lines
) {
672 Selection
*sel
= view
->selection
;
673 if (view_viewport_up(view
, lines
)) {
674 Line
*line
= sel
->line
< view
->lastline
? sel
->line
: view
->lastline
;
675 cursor_set(sel
, line
, view
->selection
->col
);
677 view_cursor_to(view
, 0);
682 size_t view_scroll_page_up(View
*view
) {
683 Selection
*sel
= view
->selection
;
684 if (view
->start
== 0) {
685 view_cursor_to(view
, 0);
687 view_cursor_to(view
, view
->start
-1);
688 view_redraw_bottom(view
);
689 view_screenline_begin(sel
);
694 size_t view_scroll_page_down(View
*view
) {
695 view_scroll_down(view
, view
->height
);
696 return view_screenline_begin(view
->selection
);
699 size_t view_scroll_halfpage_up(View
*view
) {
700 Selection
*sel
= view
->selection
;
701 if (view
->start
== 0) {
702 view_cursor_to(view
, 0);
704 view_cursor_to(view
, view
->start
-1);
705 view_redraw_center(view
);
706 view_screenline_begin(sel
);
711 size_t view_scroll_halfpage_down(View
*view
) {
712 size_t end
= view
->end
;
713 size_t pos
= view_scroll_down(view
, view
->height
/2);
714 if (pos
< text_size(view
->text
))
715 view_cursor_to(view
, end
);
716 return view
->selection
->pos
;
719 size_t view_scroll_down(View
*view
, int lines
) {
720 Selection
*sel
= view
->selection
;
721 if (view_viewport_down(view
, lines
)) {
722 Line
*line
= sel
->line
> view
->topline
? sel
->line
: view
->topline
;
723 cursor_set(sel
, line
, sel
->col
);
725 view_cursor_to(view
, text_size(view
->text
));
730 size_t view_line_up(Selection
*sel
) {
731 View
*view
= sel
->view
;
732 int lastcol
= sel
->lastcol
;
735 size_t pos
= text_line_up(sel
->view
->text
, sel
->pos
);
736 bool offscreen
= view
->selection
== sel
&& pos
< view
->start
;
737 view_cursors_to(sel
, pos
);
739 view_redraw_top(view
);
741 cursor_set(sel
, sel
->line
, lastcol
);
742 sel
->lastcol
= lastcol
;
746 size_t view_line_down(Selection
*sel
) {
747 View
*view
= sel
->view
;
748 int lastcol
= sel
->lastcol
;
751 size_t pos
= text_line_down(sel
->view
->text
, sel
->pos
);
752 bool offscreen
= view
->selection
== sel
&& pos
> view
->end
;
753 view_cursors_to(sel
, pos
);
755 view_redraw_bottom(view
);
757 cursor_set(sel
, sel
->line
, lastcol
);
758 sel
->lastcol
= lastcol
;
762 size_t view_screenline_up(Selection
*sel
) {
764 return view_line_up(sel
);
765 int lastcol
= sel
->lastcol
;
768 if (!sel
->line
->prev
)
769 view_scroll_up(sel
->view
, 1);
771 cursor_set(sel
, sel
->line
->prev
, lastcol
);
772 sel
->lastcol
= lastcol
;
776 size_t view_screenline_down(Selection
*sel
) {
778 return view_line_down(sel
);
779 int lastcol
= sel
->lastcol
;
782 if (!sel
->line
->next
&& sel
->line
== sel
->view
->bottomline
)
783 view_scroll_down(sel
->view
, 1);
785 cursor_set(sel
, sel
->line
->next
, lastcol
);
786 sel
->lastcol
= lastcol
;
790 size_t view_screenline_begin(Selection
*sel
) {
793 return cursor_set(sel
, sel
->line
, 0);
796 size_t view_screenline_middle(Selection
*sel
) {
799 return cursor_set(sel
, sel
->line
, sel
->line
->width
/ 2);
802 size_t view_screenline_end(Selection
*sel
) {
805 int col
= sel
->line
->width
- 1;
806 return cursor_set(sel
, sel
->line
, col
>= 0 ? col
: 0);
809 size_t view_cursor_get(View
*view
) {
810 return view_cursors_pos(view
->selection
);
813 Line
*view_lines_first(View
*view
) {
814 return view
->topline
;
817 Line
*view_lines_last(View
*view
) {
818 return view
->lastline
;
821 Line
*view_cursors_line_get(Selection
*sel
) {
825 void view_scroll_to(View
*view
, size_t pos
) {
826 view_cursors_scroll_to(view
->selection
, pos
);
829 void view_options_set(View
*view
, enum UiOption options
) {
830 const int mapping
[] = {
831 [SYNTAX_SYMBOL_SPACE
] = UI_OPTION_SYMBOL_SPACE
,
832 [SYNTAX_SYMBOL_TAB
] = UI_OPTION_SYMBOL_TAB
,
833 [SYNTAX_SYMBOL_TAB_FILL
] = UI_OPTION_SYMBOL_TAB_FILL
,
834 [SYNTAX_SYMBOL_EOL
] = UI_OPTION_SYMBOL_EOL
,
835 [SYNTAX_SYMBOL_EOF
] = UI_OPTION_SYMBOL_EOF
,
838 for (int i
= 0; i
< LENGTH(mapping
); i
++) {
839 view
->symbols
[i
] = (options
& mapping
[i
]) ? &symbols_default
[i
] :
843 if (options
& UI_OPTION_LINE_NUMBERS_ABSOLUTE
)
844 options
&= ~UI_OPTION_LARGE_FILE
;
846 view
->large_file
= (options
& UI_OPTION_LARGE_FILE
);
849 view
->ui
->options_set(view
->ui
, options
);
852 enum UiOption
view_options_get(View
*view
) {
853 return view
->ui
? view
->ui
->options_get(view
->ui
) : 0;
856 void view_colorcolumn_set(View
*view
, int col
) {
858 view
->colorcolumn
= col
;
861 int view_colorcolumn_get(View
*view
) {
862 return view
->colorcolumn
;
865 size_t view_screenline_goto(View
*view
, int n
) {
866 size_t pos
= view
->start
;
867 for (Line
*line
= view
->topline
; --n
> 0 && line
!= view
->lastline
; line
= line
->next
)
872 static Selection
*selections_new(View
*view
, size_t pos
, bool force
) {
873 if (pos
> text_size(view
->text
))
875 Selection
*s
= calloc(1, sizeof(*s
));
879 s
->generation
= view
->selection_generation
;
880 if (!view
->selections
) {
882 view
->selection_latest
= s
;
883 view
->selections
= s
;
884 view
->selection_count
= 1;
888 Selection
*prev
= NULL
, *next
= NULL
;
889 Selection
*latest
= view
->selection_latest
? view
->selection_latest
: view
->selection
;
890 size_t cur
= view_cursors_pos(latest
);
894 } else if (pos
> cur
) {
896 for (next
= prev
->next
; next
; prev
= next
, next
= next
->next
) {
897 cur
= view_cursors_pos(next
);
901 } else if (pos
< cur
) {
903 for (prev
= next
->prev
; prev
; next
= prev
, prev
= prev
->prev
) {
904 cur
= view_cursors_pos(prev
);
910 if (pos
== cur
&& !force
)
913 for (Selection
*after
= next
; after
; after
= after
->next
)
922 s
->number
= prev
->number
+ 1;
924 view
->selections
= s
;
926 view
->selection_latest
= s
;
927 view
->selection_count
++;
928 view_selections_dispose(view
->selection_dead
);
929 view_cursors_to(s
, pos
);
936 Selection
*view_selections_new(View
*view
, size_t pos
) {
937 return selections_new(view
, pos
, false);
940 Selection
*view_selections_new_force(View
*view
, size_t pos
) {
941 return selections_new(view
, pos
, true);
944 int view_selections_count(View
*view
) {
945 return view
->selection_count
;
948 int view_selections_number(Selection
*sel
) {
952 int view_selections_column_count(View
*view
) {
953 Text
*txt
= view
->text
;
954 int cpl_max
= 0, cpl
= 0; /* cursors per line */
955 size_t line_prev
= 0;
956 for (Selection
*sel
= view
->selections
; sel
; sel
= sel
->next
) {
957 size_t pos
= view_cursors_pos(sel
);
958 size_t line
= text_lineno_by_pos(txt
, pos
);
959 if (line
== line_prev
)
970 static Selection
*selections_column_next(View
*view
, Selection
*sel
, int column
) {
973 Text
*txt
= view
->text
;
975 size_t pos
= view_cursors_pos(sel
);
976 line_cur
= text_lineno_by_pos(txt
, pos
);
977 column_cur
= INT_MIN
;
979 sel
= view
->selections
;
982 for (; sel
; sel
= sel
->next
) {
983 size_t pos
= view_cursors_pos(sel
);
984 size_t line
= text_lineno_by_pos(txt
, pos
);
985 if (line
!= line_cur
) {
991 if (column
== column_cur
)
997 Selection
*view_selections_column(View
*view
, int column
) {
998 return selections_column_next(view
, NULL
, column
);
1001 Selection
*view_selections_column_next(Selection
*sel
, int column
) {
1002 return selections_column_next(sel
->view
, sel
, column
);
1005 static void selection_free(Selection
*s
) {
1008 for (Selection
*after
= s
->next
; after
; after
= after
->next
)
1011 s
->prev
->next
= s
->next
;
1013 s
->next
->prev
= s
->prev
;
1014 if (s
->view
->selections
== s
)
1015 s
->view
->selections
= s
->next
;
1016 if (s
->view
->selection
== s
)
1017 s
->view
->selection
= s
->next
? s
->next
: s
->prev
;
1018 if (s
->view
->selection_dead
== s
)
1019 s
->view
->selection_dead
= NULL
;
1020 if (s
->view
->selection_latest
== s
)
1021 s
->view
->selection_latest
= s
->prev
? s
->prev
: s
->next
;
1022 s
->view
->selection_count
--;
1026 bool view_selections_dispose(Selection
*sel
) {
1029 View
*view
= sel
->view
;
1030 if (!view
->selections
|| !view
->selections
->next
)
1032 selection_free(sel
);
1033 view_selections_primary_set(view
->selection
);
1037 bool view_selections_dispose_force(Selection
*sel
) {
1038 if (view_selections_dispose(sel
))
1040 View
*view
= sel
->view
;
1041 if (view
->selection_dead
)
1043 view_selection_clear(sel
);
1044 view
->selection_dead
= sel
;
1048 Selection
*view_selection_disposed(View
*view
) {
1049 Selection
*sel
= view
->selection_dead
;
1050 view
->selection_dead
= NULL
;
1054 Selection
*view_selections(View
*view
) {
1055 view
->selection_generation
++;
1056 return view
->selections
;
1059 Selection
*view_selections_primary_get(View
*view
) {
1060 view
->selection_generation
++;
1061 return view
->selection
;
1064 void view_selections_primary_set(Selection
*s
) {
1067 s
->view
->selection
= s
;
1068 Mark anchor
= s
->anchor
;
1069 view_cursors_to(s
, view_cursors_pos(s
));
1073 Selection
*view_selections_prev(Selection
*s
) {
1074 View
*view
= s
->view
;
1075 for (s
= s
->prev
; s
; s
= s
->prev
) {
1076 if (s
->generation
!= view
->selection_generation
)
1079 view
->selection_generation
++;
1083 Selection
*view_selections_next(Selection
*s
) {
1084 View
*view
= s
->view
;
1085 for (s
= s
->next
; s
; s
= s
->next
) {
1086 if (s
->generation
!= view
->selection_generation
)
1089 view
->selection_generation
++;
1093 size_t view_cursors_pos(Selection
*s
) {
1094 return text_mark_get(s
->view
->text
, s
->cursor
);
1097 size_t view_cursors_line(Selection
*s
) {
1098 size_t pos
= view_cursors_pos(s
);
1099 return text_lineno_by_pos(s
->view
->text
, pos
);
1102 size_t view_cursors_col(Selection
*s
) {
1103 size_t pos
= view_cursors_pos(s
);
1104 return text_line_char_get(s
->view
->text
, pos
) + 1;
1107 int view_cursors_cell_get(Selection
*s
) {
1108 return s
->line
? s
->col
: -1;
1111 int view_cursors_cell_set(Selection
*s
, int cell
) {
1112 if (!s
->line
|| cell
< 0)
1114 cursor_set(s
, s
->line
, cell
);
1118 void view_cursors_scroll_to(Selection
*s
, size_t pos
) {
1119 View
*view
= s
->view
;
1120 if (view
->selection
== s
) {
1122 while (pos
< view
->start
&& view_viewport_up(view
, 1));
1123 while (pos
> view
->end
&& view_viewport_down(view
, 1));
1125 view_cursors_to(s
, pos
);
1128 void view_cursors_to(Selection
*s
, size_t pos
) {
1129 View
*view
= s
->view
;
1132 size_t size
= text_size(view
->text
);
1135 if (s
->view
->selection
== s
) {
1136 /* make sure we redraw changes to the very first character of the window */
1137 if (view
->start
== pos
)
1138 view
->start_last
= 0;
1140 if (view
->end
== pos
&& view
->lastline
== view
->bottomline
) {
1141 view
->start
+= view
->topline
->len
;
1145 if (pos
< view
->start
|| pos
> view
->end
) {
1147 view_viewport_up(view
, view
->height
/ 2);
1150 if (pos
<= view
->start
|| pos
> view
->end
) {
1151 view
->start
= text_line_begin(view
->text
, pos
);
1155 if (pos
<= view
->start
|| pos
> view
->end
) {
1164 void view_cursors_place(Selection
*s
, size_t line
, size_t col
) {
1165 Text
*txt
= s
->view
->text
;
1166 size_t pos
= text_pos_by_lineno(txt
, line
);
1167 pos
= text_line_char_set(txt
, pos
, col
> 0 ? col
-1 : col
);
1168 view_cursors_to(s
, pos
);
1171 void view_selections_anchor(Selection
*s
, bool anchored
) {
1172 s
->anchored
= anchored
;
1175 void view_selection_clear(Selection
*s
) {
1176 s
->anchored
= false;
1177 s
->anchor
= s
->cursor
;
1178 s
->view
->need_update
= true;
1181 void view_selections_flip(Selection
*s
) {
1182 Mark temp
= s
->anchor
;
1183 s
->anchor
= s
->cursor
;
1185 view_cursors_to(s
, text_mark_get(s
->view
->text
, s
->cursor
));
1188 bool view_selections_anchored(Selection
*s
) {
1192 void view_selections_clear_all(View
*view
) {
1193 for (Selection
*s
= view
->selections
; s
; s
= s
->next
)
1194 view_selection_clear(s
);
1198 void view_selections_dispose_all(View
*view
) {
1199 for (Selection
*s
= view
->selections
, *next
; s
; s
= next
) {
1201 if (s
!= view
->selection
)
1207 Filerange
view_selection_get(View
*view
) {
1208 return view_selections_get(view
->selection
);
1211 Filerange
view_selections_get(Selection
*s
) {
1213 return text_range_empty();
1214 Text
*txt
= s
->view
->text
;
1215 size_t anchor
= text_mark_get(txt
, s
->anchor
);
1216 size_t cursor
= text_mark_get(txt
, s
->cursor
);
1217 Filerange sel
= text_range_new(anchor
, cursor
);
1218 if (text_range_valid(&sel
))
1219 sel
.end
= text_char_next(txt
, sel
.end
);
1223 bool view_selections_set(Selection
*s
, const Filerange
*r
) {
1224 Text
*txt
= s
->view
->text
;
1225 size_t max
= text_size(txt
);
1226 if (!text_range_valid(r
) || r
->start
>= max
)
1228 size_t anchor
= text_mark_get(txt
, s
->anchor
);
1229 size_t cursor
= text_mark_get(txt
, s
->cursor
);
1230 bool left_extending
= anchor
!= EPOS
&& anchor
> cursor
;
1231 size_t end
= r
->end
> max
? max
: r
->end
;
1232 if (r
->start
!= end
)
1233 end
= text_char_prev(txt
, end
);
1234 view_cursors_to(s
, left_extending
? r
->start
: end
);
1235 s
->anchor
= text_mark_set(txt
, left_extending
? end
: r
->start
);
1239 Filerange
view_regions_restore(View
*view
, SelectionRegion
*s
) {
1240 Text
*txt
= view
->text
;
1241 size_t anchor
= text_mark_get(txt
, s
->anchor
);
1242 size_t cursor
= text_mark_get(txt
, s
->cursor
);
1243 Filerange sel
= text_range_new(anchor
, cursor
);
1244 if (text_range_valid(&sel
))
1245 sel
.end
= text_char_next(txt
, sel
.end
);
1249 bool view_regions_save(View
*view
, Filerange
*r
, SelectionRegion
*s
) {
1250 Text
*txt
= view
->text
;
1251 size_t max
= text_size(txt
);
1252 if (!text_range_valid(r
) || r
->start
>= max
)
1254 size_t end
= r
->end
> max
? max
: r
->end
;
1255 if (r
->start
!= end
)
1256 end
= text_char_prev(txt
, end
);
1257 s
->anchor
= text_mark_set(txt
, r
->start
);
1258 s
->cursor
= text_mark_set(txt
, end
);
1262 void view_selections_set_all(View
*view
, Array
*arr
, bool anchored
) {
1266 for (s
= view
->selections
; s
; s
= s
->next
) {
1267 if (!(r
= array_get(arr
, i
++)) || !view_selections_set(s
, r
)) {
1268 for (Selection
*next
; s
; s
= next
) {
1269 next
= view_selections_next(s
);
1270 if (i
== 1 && s
== view
->selection
)
1271 view_selection_clear(s
);
1273 view_selections_dispose(s
);
1277 s
->anchored
= anchored
;
1279 while ((r
= array_get(arr
, i
++))) {
1280 s
= view_selections_new_force(view
, r
->start
);
1281 if (!s
|| !view_selections_set(s
, r
))
1283 s
->anchored
= anchored
;
1285 view_selections_primary_set(view
->selections
);
1288 Array
view_selections_get_all(View
*view
) {
1290 array_init_sized(&arr
, sizeof(Filerange
));
1291 if (!array_reserve(&arr
, view_selections_count(view
)))
1293 for (Selection
*s
= view
->selections
; s
; s
= s
->next
) {
1294 Filerange r
= view_selections_get(s
);
1295 if (text_range_valid(&r
))
1296 array_add(&arr
, &r
);
1301 void view_selections_normalize(View
*view
) {
1302 Selection
*prev
= NULL
;
1303 Filerange range_prev
= text_range_empty();
1304 for (Selection
*s
= view
->selections
, *next
; s
; s
= next
) {
1306 Filerange range
= view_selections_get(s
);
1307 if (!text_range_valid(&range
)) {
1308 view_selections_dispose(s
);
1309 } else if (prev
&& text_range_overlap(&range_prev
, &range
)) {
1310 range_prev
= text_range_union(&range_prev
, &range
);
1311 view_selections_dispose(s
);
1314 view_selections_set(prev
, &range_prev
);
1320 view_selections_set(prev
, &range_prev
);
1323 Text
*view_text(View
*view
) {
1327 char *view_symbol_eof_get(View
*view
) {
1328 return view
->symbols
[SYNTAX_SYMBOL_EOF
]->symbol
;
1331 bool view_style_define(View
*view
, enum UiStyle id
, const char *style
) {
1332 return view
->ui
->style_define(view
->ui
, id
, style
);
1335 void view_style(View
*view
, enum UiStyle style_id
, size_t start
, size_t end
) {
1336 if (end
< view
->start
|| start
> view
->end
)
1339 CellStyle style
= view
->ui
->style_get(view
->ui
, style_id
);
1340 size_t pos
= view
->start
;
1341 Line
*line
= view
->topline
;
1343 /* skip lines before range to be styled */
1344 while (line
&& pos
+ line
->len
<= start
) {
1352 int col
= 0, width
= view
->width
;
1354 /* skip columns before range to be styled */
1355 while (pos
< start
&& col
< width
)
1356 pos
+= line
->cells
[col
++].len
;
1359 while (pos
<= end
&& col
< width
) {
1360 pos
+= line
->cells
[col
].len
;
1361 line
->cells
[col
++].style
= style
;
1364 } while (pos
<= end
&& (line
= line
->next
));