diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2015-04-22 12:45:52 +0200 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2015-04-22 12:45:52 +0200 |
| commit | 625244ab41640ec43d2621465ace81b4c242385b (patch) | |
| tree | c3130b06d1fcb2994465f9bf9de4368cbb691363 /window.c | |
| parent | acb864cdc5b24e54b0b2b08e06d706ccaa3fa4c1 (diff) | |
| download | vis-625244ab41640ec43d2621465ace81b4c242385b.tar.gz vis-625244ab41640ec43d2621465ace81b4c242385b.tar.xz | |
Rename window.[ch] to view.[ch]
Diffstat (limited to 'window.c')
| -rw-r--r-- | window.c | 898 |
1 files changed, 0 insertions, 898 deletions
diff --git a/window.c b/window.c deleted file mode 100644 index 000e42d..0000000 --- a/window.c +++ /dev/null @@ -1,898 +0,0 @@ -/* - * Copyright (c) 2014 Marc André Tanner <mat at brain-dump.org> - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include <string.h> -#include <stdlib.h> -#include <wchar.h> -#include <ctype.h> -#include <errno.h> -#include <regex.h> -#include "editor.h" -#include "window.h" -#include "syntax.h" -#include "text.h" -#include "text-motions.h" -#include "util.h" - -typedef struct { /* cursor position */ - Filepos pos; /* in bytes from the start of the file */ - Filepos lastpos; /* previous cursor position */ - int row, col; /* in terms of zero based screen coordinates */ - int lastcol; /* remembered column used when moving across lines */ - Line *line; /* screen line on which cursor currently resides */ - bool highlighted; /* true e.g. when cursor is on a bracket */ -} Cursor; - -struct View { /* viewable area, showing part of a file */ - Text *text; /* underlying text management */ - UiWin *ui; - ViewEvent *events; - int width, height; /* size of display area */ - Filepos start, end; /* currently displayed area [start, end] in bytes from the start of the file */ - size_t lines_size; /* number of allocated bytes for lines (grows only) */ - Line *lines; /* view->height number of lines representing view content */ - Line *topline; /* top of the view, first line currently shown */ - Line *lastline; /* last currently used line, always <= bottomline */ - Line *bottomline; /* bottom of view, might be unused if lastline < bottomline */ - Filerange sel; /* selected text range in bytes from start of file */ - Cursor cursor; /* current cursor position within view */ - Line *line; /* used while drawing view content, line where next char will be drawn */ - int col; /* used while drawing view content, column where next char will be drawn */ - Syntax *syntax; /* syntax highlighting definitions for this view or NULL */ - int tabwidth; /* how many spaces should be used to display a tab character */ -}; - -static void view_clear(View *view); -static bool view_addch(View *view, Cell *cell); -static size_t view_cursor_update(View *view); -/* set/move current cursor position to a given (line, column) pair */ -static size_t view_cursor_set(View *view, Line *line, int col); -/* move visible viewport n-lines up/down, redraws the view but does not change - * cursor position which becomes invalid and should be corrected by either: - * - * - view_cursor_to - * - view_cursor_set - * - * the return value indicates wether the visible area changed. - */ -static bool view_viewport_up(View *view, int n); -static bool view_viewport_down(View *view, int n); - -void view_tabwidth_set(View *view, int tabwidth) { - view->tabwidth = tabwidth; - view_draw(view); -} - -void view_selection_clear(View *view) { - view->sel = text_range_empty(); - view_draw(view); - view_cursor_update(view); -} - -/* reset internal view data structures (cell matrix, line offsets etc.) */ -static void view_clear(View *view) { - /* calculate line number of first line */ - // TODO move elsewhere - view->topline = view->lines; - view->topline->lineno = text_lineno_by_pos(view->text, view->start); - view->lastline = view->topline; - - /* reset all other lines */ - size_t line_size = sizeof(Line) + view->width*sizeof(Cell); - size_t end = view->height * line_size; - Line *prev = NULL; - for (size_t i = 0; i < end; i += line_size) { - Line *line = (Line*)(((char*)view->lines) + i); - line->width = 0; - line->len = 0; - line->prev = prev; - if (prev) - prev->next = line; - prev = line; - } - view->bottomline = prev ? prev : view->topline; - view->bottomline->next = NULL; - view->line = view->topline; - view->col = 0; -} - -Filerange view_selection_get(View *view) { - Filerange sel = view->sel; - if (sel.start > sel.end) { - size_t tmp = sel.start; - sel.start = sel.end; - sel.end = tmp; - } - if (!text_range_valid(&sel)) - return text_range_empty(); - sel.end = text_char_next(view->text, sel.end); - return sel; -} - -void view_selection_set(View *view, Filerange *sel) { - Cursor *cursor = &view->cursor; - view->sel = *sel; - view_draw(view); - if (view->ui) - view->ui->cursor_to(view->ui, cursor->col, cursor->row); -} - -Filerange view_viewport_get(View *view) { - return (Filerange){ .start = view->start, .end = view->end }; -} - -/* try to add another character to the view, return whether there was space left */ -static bool view_addch(View *view, Cell *cell) { - if (!view->line) - return false; - - int width; - static Cell empty; - size_t lineno = view->line->lineno; - - switch (cell->data[0]) { - case '\t': - width = view->tabwidth - (view->col % view->tabwidth); - for (int w = 0; w < width; w++) { - if (view->col + 1 > view->width) { - view->line = view->line->next; - view->col = 0; - if (!view->line) - return false; - view->line->lineno = lineno; - } - if (w == 0) { - /* first cell of a tab has a length of 1 */ - view->line->cells[view->col].len = cell->len; - view->line->len += cell->len; - } else { - /* all remaining ones have a lenght of zero */ - view->line->cells[view->col].len = 0; - } - /* but all are marked as part of a tabstop */ - view->line->cells[view->col].width = 1; - view->line->cells[view->col].data[0] = ' '; - view->line->cells[view->col].data[1] = '\0'; - view->line->cells[view->col].istab = true; - view->line->cells[view->col].attr = cell->attr; - view->line->width++; - view->col++; - } - return true; - case '\n': - cell->width = 1; - if (view->col + cell->width > view->width) { - view->line = view->line->next; - view->col = 0; - if (!view->line) - return false; - view->line->lineno = lineno; - } - view->line->cells[view->col] = *cell; - view->line->len += cell->len; - view->line->width += cell->width; - for (int i = view->col + 1; i < view->width; i++) - view->line->cells[i] = empty; - - view->line = view->line->next; - if (view->line) - view->line->lineno = lineno + 1; - view->col = 0; - return true; - default: - if ((unsigned char)cell->data[0] < 128 && !isprint((unsigned char)cell->data[0])) { - /* non-printable ascii char, represent it as ^(char + 64) */ - *cell = (Cell) { - .data = { '^', cell->data[0] + 64, '\0' }, - .len = 1, - .width = 2, - .istab = false, - .attr = cell->attr, - }; - } - - if (view->col + cell->width > view->width) { - for (int i = view->col; i < view->width; i++) - view->line->cells[i] = empty; - view->line = view->line->next; - view->col = 0; - } - - if (view->line) { - view->line->width += cell->width; - view->line->len += cell->len; - view->line->lineno = lineno; - view->line->cells[view->col] = *cell; - view->col++; - /* set cells of a character which uses multiple columns */ - for (int i = 1; i < cell->width; i++) - view->line->cells[view->col++] = empty; - return true; - } - return false; - } -} - -CursorPos view_cursor_getpos(View *view) { - Cursor *cursor = &view->cursor; - Line *line = cursor->line; - CursorPos pos = { .line = line->lineno, .col = cursor->col }; - while (line->prev && line->prev->lineno == pos.line) { - line = line->prev; - pos.col += line->width; - } - pos.col++; - return pos; -} - -/* snyc current cursor position with internal Line/Cell structures */ -static void view_cursor_sync(View *view) { - int row = 0, col = 0; - size_t cur = view->start, pos = view->cursor.pos; - Line *line = view->topline; - - while (line && line != view->lastline && cur < pos) { - if (cur + line->len > pos) - break; - cur += line->len; - line = line->next; - row++; - } - - if (line) { - int max_col = MIN(view->width, line->width); - while (cur < pos && col < max_col) { - cur += line->cells[col].len; - /* skip over columns occupied by the same character */ - while (++col < max_col && line->cells[col].len == 0); - } - } else { - line = view->bottomline; - row = view->height - 1; - } - - view->cursor.line = line; - view->cursor.row = row; - view->cursor.col = col; -} - -/* place the cursor according to the screen coordinates in view->{row,col} and - * fire user callback. if a selection is active, redraw the view to reflect - * its changes. */ -static size_t view_cursor_update(View *view) { - Cursor *cursor = &view->cursor; - if (view->sel.start != EPOS) { - view->sel.end = cursor->pos; - view_draw(view); - } else if (view->ui && view->syntax) { - size_t pos = cursor->pos; - size_t pos_match = text_bracket_match_except(view->text, pos, "<>"); - if (pos != pos_match && view->start <= pos_match && pos_match < view->end) { - if (cursor->highlighted) - view_draw(view); /* clear active highlighting */ - cursor->pos = pos_match; - view_cursor_sync(view); - cursor->line->cells[cursor->col].attr |= A_REVERSE; - cursor->pos = pos; - view_cursor_sync(view); - view->ui->draw_text(view->ui, view->topline); - cursor->highlighted = true; - } else if (cursor->highlighted) { - cursor->highlighted = false; - view_draw(view); - } - } - if (cursor->pos != cursor->lastpos) - cursor->lastcol = 0; - cursor->lastpos = cursor->pos; - if (view->ui) - view->ui->cursor_to(view->ui, cursor->col, cursor->row); - return cursor->pos; -} - -/* move the cursor to the character at pos bytes from the begining of the file. - * if pos is not in the current viewport, redraw the view to make it visible */ -void view_cursor_to(View *view, size_t pos) { - size_t max = text_size(view->text); - - if (pos > max) - pos = max > 0 ? max - 1 : 0; - - if (pos == max && view->end != max) { - /* do not display an empty screen when shoviewg the end of the file */ - view->start = max - 1; - view_viewport_up(view, view->height / 2); - } else { - /* set the start of the viewable region to the start of the line on which - * the cursor should be placed. if this line requires more space than - * available in the view then simply start displaying text at the new - * cursor position */ - for (int i = 0; i < 2 && (pos < view->start || pos > view->end); i++) { - view->start = i == 0 ? text_line_begin(view->text, pos) : pos; - view_draw(view); - } - } - - view->cursor.pos = pos; - view_cursor_sync(view); - view_cursor_update(view); -} - -/* redraw the complete with data starting from view->start bytes into the file. - * stop once the screen is full, update view->end, view->lastline */ -void view_draw(View *view) { - view_clear(view); - /* current absolute file position */ - size_t pos = view->start; - /* number of bytes to read in one go */ - size_t text_len = view->width * view->height; - /* current buffer to work with */ - char text[text_len+1]; - /* remaining bytes to process in buffer*/ - size_t rem = text_bytes_get(view->text, pos, text_len, text); - /* NUL terminate because regex(3) function expect it */ - text[rem] = '\0'; - /* current position into buffer from which to interpret a character */ - char *cur = text; - /* current selection */ - Filerange sel = view_selection_get(view); - /* syntax definition to use */ - Syntax *syntax = view->syntax; - /* matched tokens for each syntax rule */ - regmatch_t match[syntax ? LENGTH(syntax->rules) : 1][1], *matched = NULL; - memset(match, 0, sizeof match); - /* default and current curses attributes to use */ - int default_attrs = COLOR_PAIR(0) | A_NORMAL, attrs = default_attrs; - - while (rem > 0) { - - /* current 'parsed' character' */ - wchar_t wchar; - Cell cell; - memset(&cell, 0, sizeof cell); - - if (syntax) { - if (matched && cur >= text + matched->rm_eo) { - /* end of current match */ - matched = NULL; - attrs = default_attrs; - for (int i = 0; i < LENGTH(syntax->rules); i++) { - if (match[i][0].rm_so == -1) - continue; /* no match on whole text */ - /* reset matches which overlap with matched */ - if (text + match[i][0].rm_so <= cur && cur < text + match[i][0].rm_eo) { - match[i][0].rm_so = 0; - match[i][0].rm_eo = 0; - } - } - } - - if (!matched) { - size_t off = cur - text; /* number of already processed bytes */ - for (int i = 0; i < LENGTH(syntax->rules); i++) { - SyntaxRule *rule = &syntax->rules[i]; - if (!rule->rule) - break; - if (match[i][0].rm_so == -1) - continue; /* no match on whole text */ - if (off >= (size_t)match[i][0].rm_eo) { - /* past match, continue search from current position */ - if (regexec(&rule->regex, cur, 1, match[i], 0) || - match[i][0].rm_so == match[i][0].rm_eo) { - match[i][0].rm_so = -1; - match[i][0].rm_eo = -1; - continue; - } - match[i][0].rm_so += off; - match[i][0].rm_eo += off; - } - - if (text + match[i][0].rm_so <= cur && cur < text + match[i][0].rm_eo) { - /* within matched expression */ - matched = &match[i][0]; - attrs = rule->color->attr; - break; /* first match views */ - } - } - } - } - - size_t len = mbrtowc(&wchar, cur, rem, NULL); - if (len == (size_t)-1 && errno == EILSEQ) { - /* ok, we encountered an invalid multibyte sequence, - * replace it with the Unicode Replacement Character - * (FFFD) and skip until the start of the next utf8 char */ - for (len = 1; rem > len && !ISUTF8(cur[len]); len++); - cell = (Cell){ .data = "\xEF\xBF\xBD", .len = len, .width = 1, .istab = false }; - } else if (len == (size_t)-2) { - /* not enough bytes available to convert to a - * wide character. advance file position and read - * another junk into buffer. - */ - rem = text_bytes_get(view->text, pos, text_len, text); - text[rem] = '\0'; - cur = text; - continue; - } else if (len == 0) { - /* NUL byte encountered, store it and continue */ - cell = (Cell){ .data = "\x00", .len = 1, .width = 0, .istab = false }; - } else { - for (size_t i = 0; i < len; i++) - cell.data[i] = cur[i]; - cell.data[len] = '\0'; - cell.istab = false; - cell.len = len; - cell.width = wcwidth(wchar); - if (cell.width == -1) - cell.width = 1; - } - - if (cur[0] == '\r' && rem > 1 && cur[1] == '\n') { - /* convert views style newline \r\n into a single char with len = 2 */ - cell = (Cell){ .data = "\n", .len = 2, .width = 1, .istab = false }; - } - - cell.attr = attrs; - if (sel.start <= pos && pos < sel.end) - cell.attr |= A_REVERSE; - if (!view_addch(view, &cell)) - break; - - rem -= cell.len; - cur += cell.len; - pos += cell.len; - } - - /* set end of vieviewg region */ - view->end = pos; - view->lastline = view->line ? view->line : view->bottomline; - view->lastline->next = NULL; - view_cursor_sync(view); - if (view->ui) - view->ui->draw_text(view->ui, view->topline); - if (sel.start != EPOS && view->events && view->events->selection) - view->events->selection(view->events->data, &sel); -} - -bool view_resize(View *view, int width, int height) { - size_t lines_size = height*(sizeof(Line) + width*sizeof(Cell)); - if (lines_size > view->lines_size) { - Line *lines = realloc(view->lines, lines_size); - if (!lines) - return false; - view->lines = lines; - view->lines_size = lines_size; - } - view->width = width; - view->height = height; - if (view->lines) - memset(view->lines, 0, view->lines_size); - view_draw(view); - return true; -} - -int view_height_get(View *view) { - return view->height; -} - -void view_free(View *view) { - if (!view) - return; - free(view->lines); - free(view); -} - -void view_reload(View *view, Text *text) { - view->text = text; - view_selection_clear(view); - view_cursor_to(view, 0); - if (view->ui) - view->ui->reload(view->ui, text); -} - -View *view_new(Text *text, ViewEvent *events) { - if (!text) - return NULL; - View *view = calloc(1, sizeof(View)); - if (!view) - return NULL; - view->text = text; - view->events = events; - view->tabwidth = 8; - - if (!view_resize(view, 1, 1)) { - view_free(view); - return NULL; - } - view_selection_clear(view); - view_cursor_to(view, 0); - - return view; -} - -void view_ui(View *view, UiWin* ui) { - view->ui = ui; -} -size_t view_char_prev(View *view) { - Cursor *cursor = &view->cursor; - Line *line = cursor->line; - - do { - if (cursor->col == 0) { - if (!line->prev) - return cursor->pos; - cursor->line = line = line->prev; - cursor->col = MIN(line->width, view->width - 1); - cursor->row--; - } else { - cursor->col--; - } - } while (line->cells[cursor->col].len == 0); - - cursor->pos -= line->cells[cursor->col].len; - return view_cursor_update(view); -} - -size_t view_char_next(View *view) { - Cursor *cursor = &view->cursor; - Line *line = cursor->line; - - do { - cursor->pos += line->cells[cursor->col].len; - if ((line->width == view->width && cursor->col == view->width - 1) || - cursor->col == line->width) { - if (!line->next) - return cursor->pos; - cursor->line = line = line->next; - cursor->row++; - cursor->col = 0; - } else { - cursor->col++; - } - } while (line->cells[cursor->col].len == 0); - - return view_cursor_update(view); -} - -static size_t view_cursor_set(View *view, Line *line, int col) { - int row = 0; - size_t pos = view->start; - Cursor *cursor = &view->cursor; - /* get row number and file offset at start of the given line */ - for (Line *cur = view->topline; cur && cur != line; cur = cur->next) { - pos += cur->len; - row++; - } - - /* for characters which use more than 1 column, make sure we are on the left most */ - while (col > 0 && line->cells[col].len == 0) - col--; - while (col < line->width && line->cells[col].istab) - col++; - - /* calculate offset within the line */ - for (int i = 0; i < col; i++) - pos += line->cells[i].len; - - cursor->col = col; - cursor->row = row; - cursor->pos = pos; - cursor->line = line; - - view_cursor_update(view); - - return pos; -} - -static bool view_viewport_down(View *view, int n) { - Line *line; - if (view->end == text_size(view->text)) - return false; - if (n >= view->height) { - view->start = view->end; - } else { - for (line = view->topline; line && n > 0; line = line->next, n--) - view->start += line->len; - } - view_draw(view); - return true; -} - -static bool view_viewport_up(View *view, int n) { - /* scrolling up is somewhat tricky because we do not yet know where - * the lines start, therefore scan backwards but stop at a reasonable - * maximum in case we are dealing with a file without any newlines - */ - if (view->start == 0) - return false; - size_t max = view->width * view->height; - char c; - Iterator it = text_iterator_get(view->text, view->start - 1); - - if (!text_iterator_byte_get(&it, &c)) - return false; - size_t off = 0; - /* skip newlines immediately before display area */ - if (c == '\n' && text_iterator_byte_prev(&it, &c)) - off++; - if (c == '\r' && text_iterator_byte_prev(&it, &c)) - off++; - do { - if (c == '\n' && --n == 0) - break; - if (++off > max) - break; - } while (text_iterator_byte_prev(&it, &c)); - if (c == '\r') - off++; - view->start -= off; - view_draw(view); - return true; -} - -void view_redraw_top(View *view) { - Line *line = view->cursor.line; - for (Line *cur = view->topline; cur && cur != line; cur = cur->next) - view->start += cur->len; - view_draw(view); - view_cursor_to(view, view->cursor.pos); -} - -void view_redraw_center(View *view) { - int center = view->height / 2; - size_t pos = view->cursor.pos; - for (int i = 0; i < 2; i++) { - int linenr = 0; - Line *line = view->cursor.line; - for (Line *cur = view->topline; cur && cur != line; cur = cur->next) - linenr++; - if (linenr < center) { - view_slide_down(view, center - linenr); - continue; - } - for (Line *cur = view->topline; cur && cur != line && linenr > center; cur = cur->next) { - view->start += cur->len; - linenr--; - } - break; - } - view_draw(view); - view_cursor_to(view, pos); -} - -void view_redraw_bottom(View *view) { - Line *line = view->cursor.line; - if (line == view->lastline) - return; - int linenr = 0; - size_t pos = view->cursor.pos; - for (Line *cur = view->topline; cur && cur != line; cur = cur->next) - linenr++; - view_slide_down(view, view->height - linenr - 1); - view_cursor_to(view, pos); -} - -size_t view_slide_up(View *view, int lines) { - Cursor *cursor = &view->cursor; - if (view_viewport_down(view, lines)) { - if (cursor->line == view->topline) - view_cursor_set(view, view->topline, cursor->col); - else - view_cursor_to(view, cursor->pos); - } else { - view_screenline_down(view); - } - return cursor->pos; -} - -size_t view_slide_down(View *view, int lines) { - Cursor *cursor = &view->cursor; - if (view_viewport_up(view, lines)) { - if (cursor->line == view->lastline) - view_cursor_set(view, view->lastline, cursor->col); - else - view_cursor_to(view, cursor->pos); - } else { - view_screenline_up(view); - } - return cursor->pos; -} - -size_t view_scroll_up(View *view, int lines) { - Cursor *cursor = &view->cursor; - if (view_viewport_up(view, lines)) { - Line *line = cursor->line < view->lastline ? cursor->line : view->lastline; - view_cursor_set(view, line, view->cursor.col); - } else { - view_cursor_to(view, 0); - } - return cursor->pos; -} - -size_t view_scroll_down(View *view, int lines) { - Cursor *cursor = &view->cursor; - if (view_viewport_down(view, lines)) { - Line *line = cursor->line > view->topline ? cursor->line : view->topline; - view_cursor_set(view, line, cursor->col); - } else { - view_cursor_to(view, text_size(view->text)); - } - return cursor->pos; -} - -size_t view_line_up(View *view) { - Cursor *cursor = &view->cursor; - if (cursor->line->prev && cursor->line->prev->prev && - cursor->line->lineno != cursor->line->prev->lineno && - cursor->line->prev->lineno != cursor->line->prev->prev->lineno) - return view_screenline_up(view); - size_t bol = text_line_begin(view->text, cursor->pos); - size_t prev = text_line_prev(view->text, bol); - size_t pos = text_line_offset(view->text, prev, cursor->pos - bol); - view_cursor_to(view, pos); - return cursor->pos; -} - -size_t view_line_down(View *view) { - Cursor *cursor = &view->cursor; - if (!cursor->line->next || cursor->line->next->lineno != cursor->line->lineno) - return view_screenline_down(view); - size_t bol = text_line_begin(view->text, cursor->pos); - size_t next = text_line_next(view->text, bol); - size_t pos = text_line_offset(view->text, next, cursor->pos - bol); - view_cursor_to(view, pos); - return cursor->pos; -} - -size_t view_screenline_up(View *view) { - Cursor *cursor = &view->cursor; - int lastcol = cursor->lastcol; - if (!lastcol) - lastcol = cursor->col; - if (!cursor->line->prev) - view_scroll_up(view, 1); - if (cursor->line->prev) - view_cursor_set(view, cursor->line->prev, lastcol); - cursor->lastcol = lastcol; - return cursor->pos; -} - -size_t view_screenline_down(View *view) { - Cursor *cursor = &view->cursor; - int lastcol = cursor->lastcol; - if (!lastcol) - lastcol = cursor->col; - if (!cursor->line->next && cursor->line == view->bottomline) - view_scroll_down(view, 1); - if (cursor->line->next) - view_cursor_set(view, cursor->line->next, lastcol); - cursor->lastcol = lastcol; - return cursor->pos; -} - -size_t view_screenline_begin(View *view) { - return view_cursor_set(view, view->cursor.line, 0); -} - -size_t view_screenline_middle(View *view) { - Cursor *cursor = &view->cursor; - return view_cursor_set(view, cursor->line, cursor->line->width / 2); -} - -size_t view_screenline_end(View *view) { - Cursor *cursor = &view->cursor; - int col = cursor->line->width - 1; - return view_cursor_set(view, cursor->line, col >= 0 ? col : 0); -} - -size_t view_delete_key(View *view) { - Cursor *cursor = &view->cursor; - Line *line = cursor->line; - size_t len = line->cells[cursor->col].len; - text_delete(view->text, cursor->pos, len); - view_draw(view); - view_cursor_to(view, cursor->pos); - return cursor->pos; -} - -size_t view_backspace_key(View *view) { - Cursor *cursor = &view->cursor; - if (view->start == cursor->pos) { - if (view->start == 0) - return cursor->pos; - /* if we are on the top left most position in the view - * first scroll up so that the to be deleted character is - * visible then proceed as normal */ - size_t pos = cursor->pos; - view_viewport_up(view, 1); - view_cursor_to(view, pos); - } - view_char_prev(view); - size_t pos = cursor->pos; - size_t len = cursor->line->cells[cursor->col].len; - text_delete(view->text, pos, len); - view_draw(view); - view_cursor_to(view, pos); - return pos; -} - -size_t view_insert_key(View *view, const char *c, size_t len) { - size_t pos = view->cursor.pos; - text_insert(view->text, pos, c, len); - if (view->cursor.line == view->bottomline && memchr(c, '\n', len)) - view_viewport_down(view, 1); - else - view_draw(view); - pos += len; - view_cursor_to(view, pos); - return pos; -} - -size_t view_replace_key(View *view, const char *c, size_t len) { - Cursor *cursor = &view->cursor; - Line *line = cursor->line; - size_t pos = cursor->pos; - /* do not overwrite new line which would merge the two lines */ - if (line->cells[cursor->col].data[0] != '\n') { - size_t oldlen = line->cells[cursor->col].len; - text_delete(view->text, pos, oldlen); - } - text_insert(view->text, pos, c, len); - if (cursor->line == view->bottomline && memchr(c, '\n', len)) - view_viewport_down(view, 1); - else - view_draw(view); - pos += len; - view_cursor_to(view, pos); - return pos; -} - -size_t view_cursor_get(View *view) { - return view->cursor.pos; -} - -const Line *view_lines_get(View *view) { - return view->topline; -} - -void view_scroll_to(View *view, size_t pos) { - while (pos < view->start && view_viewport_up(view, 1)); - while (pos > view->end && view_viewport_down(view, 1)); - view_cursor_to(view, pos); -} - -void view_selection_start(View *view) { - if (view->sel.start != EPOS && view->sel.end != EPOS) - return; - size_t pos = view_cursor_get(view); - view->sel.start = view->sel.end = pos; - view_draw(view); - view_cursor_to(view, pos); -} - -void view_syntax_set(View *view, Syntax *syntax) { - view->syntax = syntax; -} - -Syntax *view_syntax_get(View *view) { - return view->syntax; -} - -size_t view_screenline_goto(View *view, int n) { - size_t pos = view->start; - for (Line *line = view->topline; --n > 0 && line != view->lastline; line = line->next) - pos += line->len; - return pos; -} |
