diff options
Diffstat (limited to 'editor.c')
| -rw-r--r-- | editor.c | 1169 |
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); -} |
