aboutsummaryrefslogtreecommitdiff
path: root/editor.c
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2014-09-01 22:01:35 +0200
committerMarc André Tanner <mat@brain-dump.org>2014-09-01 22:01:35 +0200
commit28db11a2c10894d0154786646234b060bc9a6202 (patch)
treeb7c48ca473a287cb9e426ce337dd9c4e9aed1156 /editor.c
parentc72fe77e5d3cf4ecb3b691a5fa582d0e96b5ec1e (diff)
downloadvis-28db11a2c10894d0154786646234b060bc9a6202.tar.gz
vis-28db11a2c10894d0154786646234b060bc9a6202.tar.xz
Refactor frontend code
window.[ch] now contains a somewhat generic editor window which is then enhanced in vis.[ch] with a statusbar.
Diffstat (limited to 'editor.c')
-rw-r--r--editor.c1169
1 files changed, 0 insertions, 1169 deletions
diff --git a/editor.c b/editor.c
deleted file mode 100644
index 85b366f..0000000
--- a/editor.c
+++ /dev/null
@@ -1,1169 +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.
- */
-#define _XOPEN_SOURCE
-#include <string.h>
-#include <stdlib.h>
-#include <wchar.h>
-#include <ctype.h>
-#include <errno.h>
-#include "editor.h"
-#include "text.h"
-#include "text-motions.h"
-#include "text-objects.h"
-#include "util.h"
-
-typedef struct { /* used to calculate the display width of a character */
- char c[7]; /* utf8 encoded bytes */
- size_t len; /* number of bytes of the multibyte sequence */
- wchar_t wchar; /* equivalent converted wide character, needed for wcwidth(3) */
-} Char;
-
-typedef struct {
- unsigned char len; /* number of bytes the character displayed in this cell uses, for
- character which use more than 1 column to display, their lenght
- is stored in the leftmost cell wheras all following cells
- occupied by the same character have a length of 0. */
- char data; /* first byte of the utf8 sequence, used for white space handling */
-} Cell;
-
-typedef struct Line Line;
-struct Line { /* a line on the screen, *not* in the file */
- Line *prev, *next; /* pointer to neighbouring screen lines */
- size_t len; /* line length in terms of bytes */
- size_t lineno; /* line number from start of file */
- int width; /* zero based position of last used column cell */
- Cell cells[]; /* win->width cells storing information about the displayed characters */
-};
-
-typedef struct { /* cursor position */
- Filepos pos; /* in bytes from the start of the file */
- int row, col; /* in terms of zero based screen coordinates */
- Line *line; /* screen line on which cursor currently resides */
-} Cursor;
-
-typedef struct Win Win;
-struct Win { /* window showing part of a file */
- Editor *editor;
- Text *text; /* underlying text management */
- WINDOW *win; /* curses window for the text area */
- WINDOW *statuswin; /* curses window for the statusbar */
- int width, height; /* text area size *not* including the statusbar */
- Filepos start, end; /* currently displayed area [start, end] in bytes from the start of the file */
- Line *lines; /* win->height number of lines representing window content */
- Line *topline; /* top of the window, first line currently shown */
- Line *lastline; /* last currently used line, always <= bottomline */
- Line *bottomline; /* bottom of screen, might be unused if lastline < bottomline */
- Filerange sel; /* selected text range in bytes from start of file */
- Cursor cursor; /* current window cursor position */
-
- Line *line; // TODO: rename to something more descriptive, these are the current drawing pos
- int col;
-
- Syntax *syntax; /* syntax highlighting definitions for this window or NULL */
- int tabwidth; /* how many spaces should be used to display a tab character */
- Win *prev, *next; /* neighbouring windows */
-};
-
-struct Editor {
- int width, height; /* terminal size, available for all windows */
- editor_statusbar_t statusbar;
- Win *windows; /* list of windows */
- Win *win; /* currently active window */
- Syntax *syntaxes; /* NULL terminated array of syntax definitions */
- void (*windows_arrange)(Editor*);
-};
-
-static void editor_windows_arrange(Editor *ed);
-static void editor_windows_arrange_horizontal(Editor *ed);
-static void editor_windows_arrange_vertical(Editor *ed);
-static Filerange window_selection_get(Win *win);
-static void window_statusbar_draw(Win *win);
-static void editor_windows_invalidate(Editor *ed, size_t pos, size_t len);
-static void editor_windows_insert(Editor *ed, size_t pos, const char *c, size_t len);
-static void editor_windows_delete(Editor *ed, size_t pos, size_t len);
-static void editor_search_forward(Editor *ed, Regex *regex);
-static void editor_search_backward(Editor *ed, Regex *regex);
-static void window_selection_clear(Win *win);
-static void window_clear(Win *win);
-static bool window_addch(Win *win, Char *c);
-static size_t window_cursor_update(Win *win);
-static size_t window_line_begin(Win *win);
-static size_t cursor_move_to(Win *win, size_t pos);
-static size_t cursor_move_to_line(Win *win, size_t pos);
-static void window_draw(Win *win);
-static bool window_resize(Win *win, int width, int height);
-static void window_move(Win *win, int x, int y);
-static void window_free(Win *win);
-static Win *window_new(Editor *ed, const char *filename, int x, int y, int w, int h, int pos);
-static size_t pos_by_line(Win *win, Line *line);
-static size_t cursor_offset(Cursor *cursor);
-static bool scroll_line_down(Win *win, int n);
-static bool scroll_line_up(Win *win, int n);
-
-static void window_selection_clear(Win *win) {
- win->sel.start = win->sel.end = (size_t)-1;
- window_draw(win);
- window_cursor_update(win);
-}
-
-static void window_clear(Win *win) {
- size_t line_size = sizeof(Line) + win->width*sizeof(Cell);
- win->topline = win->lines;
- win->topline->lineno = text_lineno_by_pos(win->text, win->start);
- win->lastline = win->topline;
- Line *prev = NULL;
- for (int i = 0; i < win->height; i++) {
- Line *line = (Line*)(((char*)win->lines) + i*line_size);
- line->len = 0;
- line->width = 0;
- line->prev = prev;
- if (prev)
- prev->next = line;
- prev = line;
- }
- win->bottomline = prev;
- win->line = win->topline;
- win->col = 0;
-}
-
-static Filerange window_selection_get(Win *win) {
- Filerange sel = win->sel;
- if (sel.start == (size_t)-1) {
- sel.end = (size_t)-1;
- } else if (sel.end == (size_t)-1) {
- sel.start = (size_t)-1;
- } else if (sel.start > sel.end) {
- size_t tmp = sel.start;
- sel.start = sel.end;
- sel.end = tmp;
- }
- return sel;
-}
-
-/* try to add another character to the window, return whether there was space left */
-static bool window_addch(Win *win, Char *c) {
- if (!win->line)
- return false;
-
- Cell empty = {};
- int width;
- size_t lineno = win->line->lineno;
-
- switch (c->wchar) {
- case '\t':
- width = win->tabwidth - (win->col % win->tabwidth);
- for (int w = 0; w < width; w++) {
- if (win->col + 1 > win->width) {
- win->line = win->line->next;
- win->col = 0;
- if (!win->line)
- return false;
- win->line->lineno = lineno;
- }
- if (w == 0) {
- /* first cell of a tab has a length of 1 */
- win->line->cells[win->col].len = c->len;
- win->line->len += c->len;
- } else {
- /* all remaining ones have a lenght of zero */
- win->line->cells[win->col].len = 0;
- }
- /* but all are marked as part of a tabstop */
- win->line->cells[win->col].data = '\t';
- win->col++;
- win->line->width++;
- waddch(win->win, ' ');
- }
- return true;
- case '\n':
- width = 1;
- if (win->col + width > win->width) {
- win->line = win->line->next;
- win->col = 0;
- if (!win->line)
- return false;
- win->line->lineno = lineno + 1;
- }
- win->line->cells[win->col].len = c->len;
- win->line->len += c->len;
- win->line->cells[win->col].data = '\n';
- for (int i = win->col + 1; i < win->width; i++)
- win->line->cells[i] = empty;
-
- if (win->line == win->bottomline) {
- /* XXX: curses bug? the wclrtoeol(win->win); implied by waddch(win->win, '\n')
- * doesn't seem to work on the last line!?
- *
- * Thus explicitly clear the remaining of the line.
- */
- for (int i = win->col; i < win->width; i++)
- waddch(win->win, ' ');
- } else if (win->line->width == 0) {
- /* add a single space in an otherwise empty line, makes selection cohorent */
- waddch(win->win, ' ');
- }
-
- waddch(win->win, '\n');
- win->line = win->line->next;
- if (win->line)
- win->line->lineno = lineno + 1;
- win->col = 0;
- return true;
- default:
- if (c->wchar < 128 && !isprint(c->wchar)) {
- /* non-printable ascii char, represent it as ^(char + 64) */
- Char s = { .c = "^_", .len = 1 };
- s.c[1] = c->c[0] + 64;
- *c = s;
- width = 2;
- } else {
- if ((width = wcwidth(c->wchar)) == -1) {
- /* this should never happen */
- width = 1;
- }
- }
-
- if (win->col + width > win->width) {
- for (int i = win->col; i < win->width; i++)
- win->line->cells[i] = empty;
- win->line = win->line->next;
- win->col = 0;
- }
-
- if (win->line) {
- win->line->width += width;
- win->line->len += c->len;
- win->line->lineno = lineno;
- win->line->cells[win->col].len = c->len;
- win->line->cells[win->col].data = c->c[0];
- win->col++;
- /* set cells of a character which uses multiple columns */
- for (int i = 1; i < width; i++)
- win->line->cells[win->col++] = empty;
- waddstr(win->win, c->c);
- return true;
- }
- return false;
- }
-}
-
-static void window_statusbar_draw(Win *win) {
- if (!win->editor->statusbar)
- return;
- Cursor *cursor = &win->cursor;
- Line *line = cursor->line;
- size_t lineno = line->lineno;
- int col = cursor->col;
- while (line->prev && line->prev->lineno == lineno) {
- line = line->prev;
- col += line->width;
- }
- win->editor->statusbar(win->statuswin, win->editor->win == win, text_filename(win->text), cursor->line->lineno, col+1);
-}
-
-/* place the cursor according to the screen coordinates in win->{row,col} and
- * update the statusbar. if a selection is active, redraw the window to reflect
- * its changes. */
-static size_t window_cursor_update(Win *win) {
- Cursor *cursor = &win->cursor;
- if (win->sel.start != (size_t)-1) {
- win->sel.end = cursor->pos;
- window_draw(win);
- }
- wmove(win->win, cursor->row, cursor->col);
- window_statusbar_draw(win);
- 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 window to make it visible */
-static size_t cursor_move_to(Win *win, size_t pos) {
- Line *line = win->topline;
- int row = 0;
- int col = 0;
- size_t max = text_size(win->text);
-
- if (pos > max)
- pos = max > 0 ? max - 1 : 0;
-
- if (pos < win->start || pos > win->end) {
- win->start = pos;
- window_draw(win);
- } else {
- size_t cur = win->start;
- while (line && line != win->lastline && cur < pos) {
- if (cur + line->len > pos)
- break;
- cur += line->len;
- line = line->next;
- row++;
- }
-
- if (line) {
- int max_col = MIN(win->width, line->width);
- while (cur < pos && col < max_col) {
- cur += line->cells[col].len;
- col++;
- }
- while (col < max_col && line->cells[col].data == '\t')
- col++;
- } else {
- line = win->bottomline;
- row = win->height - 1;
- }
- }
-
- win->cursor.line = line;
- win->cursor.row = row;
- win->cursor.col = col;
- win->cursor.pos = pos;
- return window_cursor_update(win);
-}
-
-/* move cursor to pos, make sure the whole line is visible */
-static size_t cursor_move_to_line(Win *win, size_t pos) {
- if (pos < win->start || pos > win->end) {
- win->cursor.pos = pos;
- window_line_begin(win);
- }
- return cursor_move_to(win, pos);
-}
-
-/* redraw the complete with data starting from win->start bytes into the file.
- * stop once the screen is full, update win->end, win->lastline */
-static void window_draw(Win *win) {
- window_clear(win);
- wmove(win->win, 0, 0);
- /* current absolute file position */
- size_t pos = win->start;
- /* number of bytes to read in one go */
- // TODO read smaller junks
- size_t text_len = win->width * win->height;
- /* current buffer to work with */
- char text[text_len+1];
- /* remaining bytes to process in buffer*/
- size_t rem = text_bytes_get(win->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 'parsed' character' */
- Char c;
- /* current selection */
- Filerange sel = window_selection_get(win);
- /* matched tokens for each syntax rule */
- regmatch_t match[SYNTAX_REGEX_RULES][1];
- if (win->syntax) {
- for (int i = 0; i < LENGTH(win->syntax->rules); i++) {
- SyntaxRule *rule = &win->syntax->rules[i];
- if (!rule->rule)
- break;
- 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;
- }
- }
- }
-
- while (rem > 0) {
-
- int attrs = COLOR_PAIR(0) | A_NORMAL;
-
- if (win->syntax) {
- size_t off = cur - text; /* number of already processed bytes */
- for (int i = 0; i < LENGTH(win->syntax->rules); i++) {
- SyntaxRule *rule = &win->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 */
- attrs = rule->color.attr;
- }
- }
- }
-
- if (sel.start <= pos && pos < sel.end)
- attrs |= A_REVERSE; // TODO: make configurable
-
- size_t len = mbrtowc(&c.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++);
- c = (Char){ .c = "\xEF\xBF\xBD", .wchar = 0xFFFD, .len = len };
- } 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(win->text, pos, text_len, text);
- text[rem] = '\0';
- cur = text;
- continue;
- } else if (len == 0) {
- /* NUL byte encountered, store it and continue */
- len = 1;
- c = (Char){ .c = "\x00", .wchar = 0x00, .len = len };
- } else {
- for (size_t i = 0; i < len; i++)
- c.c[i] = cur[i];
- c.c[len] = '\0';
- c.len = len;
- }
-
- if (cur[0] == '\n' && rem > 1 && cur[1] == '\r') {
- /* convert windows style newline \n\r into a single char with len = 2 */
- c.len = len = 2;
- }
-
- wattrset(win->win, attrs);
- if (!window_addch(win, &c))
- break;
-
- rem -= len;
- cur += len;
- pos += len;
- }
-
- /* set end of viewing region */
- win->end = pos;
- win->lastline = win->line ? win->line : win->bottomline;
- win->lastline->next = NULL;
- /* and clear the rest of the unused window */
- wclrtobot(win->win);
-}
-
-static bool window_resize(Win *win, int width, int height) {
- height--; // statusbar
- if (wresize(win->win, height, width) == ERR ||
- wresize(win->statuswin, 1, width) == ERR)
- return false;
-
- // TODO: only grow memory area
- win->height = height;
- win->width = width;
- free(win->lines);
- size_t line_size = sizeof(Line) + width*sizeof(Cell);
- if (!(win->lines = calloc(height, line_size)))
- return false;
- window_draw(win);
- cursor_move_to(win, win->cursor.pos);
- return true;
-}
-
-static void window_move(Win *win, int x, int y) {
- mvwin(win->win, y, x);
- mvwin(win->statuswin, y + win->height, x);
-}
-
-static void window_free(Win *win) {
- if (!win)
- return;
- if (win->win)
- delwin(win->win);
- if (win->statuswin)
- delwin(win->statuswin);
- text_free(win->text);
- free(win);
-}
-
-static Win *window_new(Editor *ed, const char *filename, int x, int y, int w, int h, int pos) {
- Win *win = calloc(1, sizeof(Win));
- if (!win)
- return NULL;
- win->editor = ed;
- win->start = pos;
- win->tabwidth = 8; // TODO make configurable
-
- if (filename) {
- for (Syntax *syn = ed->syntaxes; syn && syn->name; syn++) {
- if (!regexec(&syn->file_regex, filename, 0, NULL, 0)) {
- win->syntax = syn;
- break;
- }
- }
- }
-
- if (!(win->text = text_load(filename)) ||
- !(win->win = newwin(h-1, w, y, x)) ||
- !(win->statuswin = newwin(1, w, y+h-1, x)) ||
- !window_resize(win, w, h)) {
- window_free(win);
- return NULL;
- }
-
- window_selection_clear(win);
- cursor_move_to(win, pos);
-
- return win;
-}
-
-Editor *editor_new(int width, int height, editor_statusbar_t statusbar) {
- Editor *ed = calloc(1, sizeof(Editor));
- if (!ed)
- return NULL;
- ed->width = width;
- ed->height = height;
- ed->statusbar = statusbar;
- ed->windows_arrange = editor_windows_arrange_horizontal;
- return ed;
-}
-
-bool editor_load(Editor *ed, const char *filename) {
- Win *win = window_new(ed, filename, 0, 0, ed->width, ed->height, 0);
- if (!win)
- return false;
-
- if (ed->windows)
- ed->windows->prev = win;
- win->next = ed->windows;
- win->prev = NULL;
- ed->windows = win;
- ed->win = win;
- return true;
-}
-
-static size_t pos_by_line(Win *win, Line *line) {
- size_t pos = win->start;
- for (Line *cur = win->topline; cur && cur != line; cur = cur->next)
- pos += cur->len;
- return pos;
-}
-
-size_t editor_char_prev(Editor *ed) {
- Win *win = ed->win;
- Cursor *cursor = &win->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, win->width - 1);
- cursor->row--;
- } else {
- cursor->col--;
- }
- } while (line->cells[cursor->col].len == 0);
-
- cursor->pos -= line->cells[cursor->col].len;
- return window_cursor_update(win);
-}
-
-size_t editor_char_next(Editor *ed) {
- Win *win = ed->win;
- Cursor *cursor = &win->cursor;
- Line *line = cursor->line;
-
- do {
- cursor->pos += line->cells[cursor->col].len;
- if ((line->width == win->width && cursor->col == win->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 window_cursor_update(win);
-}
-
-/* calculate the line offset in bytes of a given cursor position, used after
- * the cursor changes line. this assumes cursor->line already points to the new
- * line, but cursor->col still has the old column position of the previous
- * line based on which the new column position is calculated */
-static size_t cursor_offset(Cursor *cursor) {
- Line *line = cursor->line;
- int col = cursor->col;
- int off = 0;
- /* for characters which use more than 1 column, make sure we are on the left most */
- while (col > 0 && line->cells[col].len == 0 && line->cells[col].data != '\t')
- col--;
- while (col < line->width && line->cells[col].data == '\t')
- col++;
- for (int i = 0; i < col; i++)
- off += line->cells[i].len;
- cursor->col = col;
- return off;
-}
-
-static bool scroll_line_down(Win *win, int n) {
- Line *line;
- if (win->end == text_size(win->text))
- return false;
- for (line = win->topline; line && n > 0; line = line->next, n--)
- win->start += line->len;
- window_draw(win);
- /* try to place the cursor at the same column */
- Cursor *cursor = &win->cursor;
- cursor->pos = win->end - win->lastline->len + cursor_offset(cursor);
- window_cursor_update(win);
- return true;
-}
-
-static bool scroll_line_up(Win *win, 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 (win->start == 0)
- return false;
- size_t max = win->width * win->height / 2;
- char c;
- Iterator it = text_iterator_get(win->text, win->start - 1);
-
- if (!text_iterator_byte_get(&it, &c))
- return false;
- size_t off = 0;
- /* skip newlines immediately before display area */
- if (c == '\r' && text_iterator_byte_prev(&it, &c))
- off++;
- if (c == '\n' && text_iterator_byte_prev(&it, &c))
- off++;
- do {
- if ((c == '\n' || c == '\r') && --n == 0)
- break;
- if (++off > max)
- break;
- } while (text_iterator_byte_prev(&it, &c));
- win->start -= off;
- window_draw(win);
- Cursor *cursor = &win->cursor;
- cursor->pos = win->start + cursor_offset(cursor);
- window_cursor_update(win);
- return true;
-}
-
-size_t editor_file_begin(Editor *ed) {
- return cursor_move_to(ed->win, 0);
-}
-
-size_t editor_file_end(Editor *ed) {
- Win *win = ed->win;
- size_t size = text_size(win->text);
- if (win->end == size)
- return cursor_move_to(win, win->end);
- win->start = size - 1;
- scroll_line_up(win, win->height / 2);
- return cursor_move_to(win, win->end);
-}
-
-size_t editor_page_up(Editor *ed) {
- Win *win = ed->win;
- if (!scroll_line_up(win, win->height))
- cursor_move_to(win, win->start);
- return win->cursor.pos;
-}
-
-size_t editor_page_down(Editor *ed) {
- Win *win = ed->win;
- Cursor *cursor = &win->cursor;
- if (win->end == text_size(win->text))
- return cursor_move_to(win, win->end);
- win->start = win->end;
- window_draw(win);
- int col = cursor->col;
- cursor_move_to(win, win->start);
- cursor->col = col;
- cursor->pos += cursor_offset(cursor);
- return window_cursor_update(win);
-}
-
-size_t editor_line_up(Editor *ed) {
- Win *win = ed->win;
- Cursor *cursor = &win->cursor;
- if (!cursor->line->prev) {
- scroll_line_up(win, 1);
- return cursor->pos;
- }
- cursor->row--;
- cursor->line = cursor->line->prev;
- cursor->pos = pos_by_line(win, cursor->line) + cursor_offset(cursor);
- return window_cursor_update(win);
-}
-
-size_t editor_line_down(Editor *ed) {
- Win *win = ed->win;
- Cursor *cursor = &win->cursor;
- if (!cursor->line->next) {
- if (cursor->line == win->bottomline)
- scroll_line_down(ed->win, 1);
- return cursor->pos;
- }
- cursor->row++;
- cursor->line = cursor->line->next;
- cursor->pos = pos_by_line(win, cursor->line) + cursor_offset(cursor);
- return window_cursor_update(win);
-}
-
-static size_t window_line_begin(Win *win) {
- return cursor_move_to(win, text_line_begin(win->text, win->cursor.pos));
-}
-
-size_t editor_line_begin(Editor *ed) {
- return window_line_begin(ed->win);
-}
-
-size_t editor_line_start(Editor *ed) {
- Win *win = ed->win;
- size_t pos = text_line_start(win->text, win->cursor.pos);
- while (win->end < pos && scroll_line_down(win, 1));
- return cursor_move_to(win, pos);
-}
-
-size_t editor_line_end(Editor *ed) {
- Win *win = ed->win;
- size_t pos = text_line_end(win->text, win->cursor.pos);
- while (win->end < pos && scroll_line_down(win, 1));
- return cursor_move_to(win, pos);
-}
-
-size_t editor_line_finish(Editor *ed) {
- Win *win = ed->win;
- size_t pos = text_line_finish(win->text, win->cursor.pos);
- return cursor_move_to(win, pos);
-}
-
-size_t editor_word_end_prev(Editor *ed) {
- Win *win = ed->win;
- size_t pos = text_word_end_prev(win->text, win->cursor.pos);
- while (win->start > pos && scroll_line_up(win, 1));
- return cursor_move_to(win, pos);
-}
-
-size_t editor_word_end_next(Editor *ed) {
- Win *win = ed->win;
- size_t pos = text_word_end_next(win->text, win->cursor.pos);
- while (win->end < pos && scroll_line_down(win, 1));
- return cursor_move_to(win, pos);
-}
-
-size_t editor_word_start_next(Editor *ed) {
- Win *win = ed->win;
- size_t pos = text_word_start_next(win->text, win->cursor.pos);
- while (win->end < pos && scroll_line_down(win, 1));
- return cursor_move_to(win, pos);
-}
-
-size_t editor_word_start_prev(Editor *ed) {
- Win *win = ed->win;
- size_t pos = text_word_start_prev(win->text, win->cursor.pos);
- while (win->start > pos && scroll_line_up(win, 1));
- return cursor_move_to(win, pos);
-}
-
-size_t editor_sentence_next(Editor *ed) {
- Win *win = ed->win;
- return cursor_move_to_line(win, text_sentence_next(win->text, win->cursor.pos));
-}
-
-size_t editor_paragraph_next(Editor *ed) {
- Win *win = ed->win;
- return cursor_move_to_line(win, text_paragraph_next(win->text, win->cursor.pos));
-}
-
-size_t editor_sentence_prev(Editor *ed) {
- Win *win = ed->win;
- return cursor_move_to_line(win, text_sentence_prev(win->text, win->cursor.pos));
-}
-
-size_t editor_paragraph_prev(Editor *ed) {
- Win *win = ed->win;
- return cursor_move_to_line(win, text_paragraph_prev(win->text, win->cursor.pos));
-}
-
-size_t editor_bracket_match(Editor *ed) {
- Win *win = ed->win;
- return cursor_move_to_line(win, text_bracket_match(win->text, win->cursor.pos));
-}
-
-void editor_draw(Editor *ed) {
- for (Win *win = ed->windows; win; win = win->next) {
- if (ed->win != win) {
- window_draw(win);
- cursor_move_to(win, win->cursor.pos);
- }
- }
- window_draw(ed->win);
- cursor_move_to(ed->win, ed->win->cursor.pos);
-}
-
-void editor_update(Editor *ed) {
- for (Win *win = ed->windows; win; win = win->next) {
- if (ed->win != win) {
- wnoutrefresh(win->statuswin);
- wnoutrefresh(win->win);
- }
- }
-
- wnoutrefresh(ed->win->statuswin);
- wnoutrefresh(ed->win->win);
-}
-
-void editor_free(Editor *ed) {
- if (!ed)
- return;
- window_free(ed->win);
- free(ed);
-}
-
-bool editor_resize(Editor *ed, int width, int height) {
- ed->width = width;
- ed->height = height;
- editor_windows_arrange(ed);
- return true;
-}
-
-bool editor_syntax_load(Editor *ed, Syntax *syntaxes, Color *colors) {
- bool success = true;
- ed->syntaxes = syntaxes;
- for (Syntax *syn = syntaxes; syn && syn->name; syn++) {
- if (regcomp(&syn->file_regex, syn->file, REG_EXTENDED|REG_NOSUB|REG_ICASE|REG_NEWLINE))
- success = false;
- Color *color = colors;
- for (int j = 0; j < LENGTH(syn->rules); j++) {
- SyntaxRule *rule = &syn->rules[j];
- if (!rule->rule)
- break;
- if (rule->color.fg == 0 && color && color->fg != 0)
- rule->color = *color++;
- if (rule->color.attr == 0)
- rule->color.attr = A_NORMAL;
- if (rule->color.fg != 0)
- rule->color.attr |= COLOR_PAIR(editor_color_reserve(rule->color.fg, rule->color.bg));
- if (regcomp(&rule->regex, rule->rule, REG_EXTENDED|rule->cflags))
- success = false;
- }
- }
-
- return success;
-}
-
-void editor_syntax_unload(Editor *ed) {
- for (Syntax *syn = ed->syntaxes; syn && syn->name; syn++) {
- regfree(&syn->file_regex);
- for (int j = 0; j < LENGTH(syn->rules); j++) {
- SyntaxRule *rule = &syn->rules[j];
- if (!rule->rule)
- break;
- regfree(&rule->regex);
- }
- }
-
- ed->syntaxes = NULL;
-}
-
-static void editor_windows_invalidate(Editor *ed, size_t pos, size_t len) {
- size_t end = pos + len;
- for (Win *win = ed->windows; win; win = win->next) {
- if (ed->win != win && ed->win->text == win->text &&
- ((win->start <= pos && pos <= win->end) ||
- (win->start <= end && end <= win->end))) {
- window_draw(win);
- cursor_move_to(win, win->cursor.pos);
- }
- }
-}
-
-static void editor_windows_insert(Editor *ed, size_t pos, const char *c, size_t len) {
- text_insert_raw(ed->win->text, pos, c, len);
- editor_windows_invalidate(ed, pos, len);
-}
-
-static void editor_windows_delete(Editor *ed, size_t pos, size_t len) {
- text_delete(ed->win->text, pos, len);
- editor_windows_invalidate(ed, pos, len);
-}
-
-size_t editor_delete(Editor *ed) {
- Cursor *cursor = &ed->win->cursor;
- Line *line = cursor->line;
- size_t len = line->cells[cursor->col].len;
- editor_windows_delete(ed, cursor->pos, len);
- window_draw(ed->win);
- return cursor_move_to(ed->win, cursor->pos);
-}
-
-size_t editor_backspace(Editor *ed) {
- Win *win = ed->win;
- Cursor *cursor = &win->cursor;
- if (win->start == cursor->pos) {
- if (win->start == 0)
- return cursor->pos;
- /* if we are on the top left most position in the window
- * first scroll up so that the to be deleted character is
- * visible then proceed as normal */
- size_t pos = cursor->pos;
- scroll_line_up(win, 1);
- cursor_move_to(win, pos);
- }
- editor_char_prev(ed);
- size_t pos = cursor->pos;
- size_t len = cursor->line->cells[cursor->col].len;
- editor_windows_delete(ed, pos, len);
- window_draw(win);
- return cursor_move_to(ed->win, pos);
-}
-
-size_t editor_insert(Editor *ed, const char *c, size_t len) {
- Win *win = ed->win;
- size_t pos = win->cursor.pos;
- editor_windows_insert(ed, pos, c, len);
- if (win->cursor.line == win->bottomline && memchr(c, '\n', len))
- scroll_line_down(win, 1);
- else
- window_draw(win);
- return cursor_move_to(win, pos + len);
-}
-
-size_t editor_replace(Editor *ed, const char *c, size_t len) {
- Win *win = ed->win;
- Cursor *cursor = &win->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 != '\n') {
- size_t oldlen = line->cells[cursor->col].len;
- text_delete(win->text, pos, oldlen);
- }
- editor_windows_insert(ed, pos, c, len);
- if (cursor->line == win->bottomline && memchr(c, '\n', len))
- scroll_line_down(win, 1);
- else
- window_draw(win);
- return cursor_move_to(win, pos + len);
-}
-
-size_t editor_cursor_get(Editor *ed) {
- return ed->win->cursor.pos;
-}
-
-Text *editor_text_get(Editor *ed) {
- return ed->win->text;
-}
-
-void editor_scroll_to(Editor *ed, size_t pos) {
- Win *win = ed->win;
- while (pos < win->start && scroll_line_up(win, 1));
- while (pos > win->end && scroll_line_down(win, 1));
- cursor_move_to(win, pos);
-}
-
-void editor_cursor_to(Editor *ed, size_t pos) {
- cursor_move_to(ed->win, pos);
-}
-
-size_t editor_selection_start(Editor *ed) {
- return ed->win->sel.start = editor_cursor_get(ed);
-}
-
-size_t editor_selection_end(Editor *ed) {
- return ed->win->sel.end = editor_cursor_get(ed);
-}
-
-Filerange editor_selection_get(Editor *ed) {
- return window_selection_get(ed->win);
-}
-
-void editor_selection_clear(Editor *ed) {
- window_selection_clear(ed->win);
-}
-
-size_t editor_line_goto(Editor *ed, size_t lineno) {
- size_t pos = text_pos_by_lineno(ed->win->text, lineno);
- return cursor_move_to(ed->win, pos);
-}
-
-static void editor_search_forward(Editor *ed, Regex *regex) {
- Win *win = ed->win;
- Cursor *cursor = &win->cursor;
- int pos = cursor->pos + 1;
- int end = text_size(win->text);
- RegexMatch match[1];
- bool found = false;
- if (text_search_forward(win->text, pos, end - pos, regex, 1, match, 0)) {
- pos = 0;
- end = cursor->pos;
- if (!text_search_forward(win->text, pos, end, regex, 1, match, 0))
- found = true;
- } else {
- found = true;
- }
- if (found)
- cursor_move_to_line(win, match[0].start);
-}
-
-static void editor_search_backward(Editor *ed, Regex *regex) {
- Win *win = ed->win;
- Cursor *cursor = &win->cursor;
- int pos = 0;
- int end = cursor->pos;
- RegexMatch match[1];
- bool found = false;
- if (text_search_backward(win->text, pos, end, regex, 1, match, 0)) {
- pos = cursor->pos + 1;
- end = text_size(win->text);
- if (!text_search_backward(win->text, pos, end - pos, regex, 1, match, 0))
- found = true;
- } else {
- found = true;
- }
- if (found)
- cursor_move_to_line(win, match[0].start);
-}
-
-void editor_search(Editor *ed, const char *s, int direction) {
- Regex *regex = text_regex_new();
- if (!regex)
- return;
- if (!text_regex_compile(regex, s, REG_EXTENDED)) {
- if (direction >= 0)
- editor_search_forward(ed, regex);
- else
- editor_search_backward(ed, regex);
- }
- text_regex_free(regex);
-}
-
-void editor_snapshot(Editor *ed) {
- text_snapshot(ed->win->text);
-}
-
-void editor_undo(Editor *ed) {
- Win *win = ed->win;
- if (text_undo(win->text))
- window_draw(win);
-}
-
-void editor_redo(Editor *ed) {
- Win *win = ed->win;
- if (text_redo(win->text))
- window_draw(win);
-}
-
-static void editor_windows_arrange(Editor *ed) {
- erase();
- wnoutrefresh(stdscr);
- ed->windows_arrange(ed);
-}
-
-static void editor_windows_arrange_horizontal(Editor *ed) {
- int n = 0, x = 0, y = 0;
- for (Win *win = ed->windows; win; win = win->next)
- n++;
- int height = ed->height / n;
- for (Win *win = ed->windows; win; win = win->next) {
- window_resize(win, ed->width, win->next ? height : ed->height - y);
- window_move(win, x, y);
- y += height;
- }
-}
-
-static void editor_windows_arrange_vertical(Editor *ed) {
- int n = 0, x = 0, y = 0;
- for (Win *win = ed->windows; win; win = win->next)
- n++;
- int width = ed->width / n;
- for (Win *win = ed->windows; win; win = win->next) {
- window_resize(win, win->next ? width : ed->width - x, ed->height);
- window_move(win, x, y);
- x += width;
- }
-}
-
-static void editor_window_split_internal(Editor *ed, const char *filename) {
- Win *sel = ed->win;
- editor_load(ed, filename);
- if (sel && !filename) {
- Win *win = ed->win;
- text_free(win->text);
- win->text = sel->text;
- win->start = sel->start;
- win->syntax = sel->syntax;
- win->cursor.pos = sel->cursor.pos;
- // TODO show begin of line instead of cursor->pos
- }
-}
-
-void editor_window_split(Editor *ed, const char *filename) {
- editor_window_split_internal(ed, filename);
- ed->windows_arrange = editor_windows_arrange_horizontal;
- editor_windows_arrange(ed);
-}
-
-void editor_window_vsplit(Editor *ed, const char *filename) {
- editor_window_split_internal(ed, filename);
- ed->windows_arrange = editor_windows_arrange_vertical;
- editor_windows_arrange(ed);
-}
-
-void editor_window_next(Editor *ed) {
- Win *sel = ed->win;
- if (!sel)
- return;
- ed->win = ed->win->next;
- if (!ed->win)
- ed->win = ed->windows;
- window_statusbar_draw(sel);
- window_statusbar_draw(ed->win);
-}
-
-void editor_window_prev(Editor *ed) {
- Win *sel = ed->win;
- if (!sel)
- return;
- ed->win = ed->win->prev;
- if (!ed->win)
- for (ed->win = ed->windows; ed->win->next; ed->win = ed->win->next);
- window_statusbar_draw(sel);
- window_statusbar_draw(ed->win);
-}
-
-void editor_mark_set(Editor *ed, Mark mark, size_t pos) {
- text_mark_set(ed->win->text, mark, pos);
-}
-
-void editor_mark_goto(Editor *ed, Mark mark) {
- Win *win = ed->win;
- size_t pos = text_mark_get(win->text, mark);
- if (pos != (size_t)-1)
- cursor_move_to_line(win, pos);
-}
-
-void editor_mark_clear(Editor *ed, Mark mark) {
- text_mark_clear(ed->win->text, mark);
-}