diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2014-09-01 22:01:35 +0200 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2014-09-01 22:01:35 +0200 |
| commit | 28db11a2c10894d0154786646234b060bc9a6202 (patch) | |
| tree | b7c48ca473a287cb9e426ce337dd9c4e9aed1156 | |
| parent | c72fe77e5d3cf4ecb3b691a5fa582d0e96b5ec1e (diff) | |
| download | vis-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.
| -rw-r--r-- | Makefile | 7 | ||||
| -rw-r--r-- | colors.c | 12 | ||||
| -rw-r--r-- | config.def.h | 157 | ||||
| -rw-r--r-- | editor.h | 109 | ||||
| -rw-r--r-- | main.c | 234 | ||||
| -rw-r--r-- | vis.c | 516 | ||||
| -rw-r--r-- | vis.h | 170 | ||||
| -rw-r--r-- | window.c (renamed from editor.c) | 654 | ||||
| -rw-r--r-- | window.h | 47 |
9 files changed, 996 insertions, 910 deletions
@@ -1,9 +1,10 @@ include config.mk -SRC += vis.c colors.c editor.c text.c text-motions.c text-objects.c register.c -ALL = ${SRC} util.h editor.h text.h text-motions.h text-objects.h register.h \ - config.def.h config.mk Makefile LICENSE README vis.1 +SRC += vis.c colors.c window.c text.c text-motions.c text-objects.c register.c +HDR = ${SRC:.c=.h} util.h config.def.h +SRC += main.c OBJ = ${SRC:.c=.o} +ALL = ${SRC} ${HDR} config.mk Makefile LICENSE README vis.1 all: clean options vis @@ -2,7 +2,7 @@ #include <stdlib.h> #include <curses.h> -#include "editor.h" +#include "vis.h" #include "util.h" #ifdef NCURSES_VERSION @@ -30,7 +30,7 @@ static unsigned int color_hash(short fg, short bg) return fg * (COLORS + 2) + bg; } -short editor_color_get(short fg, short bg) +short vis_color_get(short fg, short bg) { if (fg >= COLORS) fg = default_fg; @@ -68,10 +68,10 @@ short editor_color_get(short fg, short bg) return color_pair >= 0 ? color_pair : -color_pair; } -short editor_color_reserve(short fg, short bg) +short vis_color_reserve(short fg, short bg) { if (!color2palette) - editor_init(); + vis_init(); if (!color2palette || fg >= COLORS || bg >= COLORS) return 0; if (!has_default_colors && fg == -1) @@ -89,7 +89,7 @@ short editor_color_reserve(short fg, short bg) return color_pair >= 0 ? color_pair : -color_pair; } -void editor_init(void) +void vis_init(void) { if (color2palette) return; @@ -102,5 +102,5 @@ void editor_init(void) color_pairs_max = MIN(COLOR_PAIRS, MAX_COLOR_PAIRS); if (COLORS) color2palette = calloc((COLORS + 2) * (COLORS + 2), sizeof(short)); - editor_color_reserve(COLOR_WHITE, COLOR_BLACK); + vis_color_reserve(COLOR_WHITE, COLOR_BLACK); } diff --git a/config.def.h b/config.def.h index 66e3044..c6ef3f5 100644 --- a/config.def.h +++ b/config.def.h @@ -32,10 +32,10 @@ void op_delete(OperatorContext *c) { if (c->range.start == (size_t)-1) return; size_t len = c->range.end - c->range.start; - text_delete(editor_text_get(editor), c->range.start, len); + vis_delete(vis, c->range.start, len); if (c->pos > c->range.start) - editor_cursor_to(editor, c->range.start); - editor_draw(editor); + window_cursor_to(vis->win->win, c->range.start); + vis_draw(vis); } void op_change(OperatorContext *c) { @@ -74,10 +74,10 @@ enum { }; static Movement moves[] = { - [MOVE_CHAR_PREV] = { .win = editor_char_prev }, - [MOVE_CHAR_NEXT] = { .win = editor_char_next }, - [MOVE_LINE_UP] = { .win = editor_line_up }, - [MOVE_LINE_DOWN] = { .win = editor_line_down }, + [MOVE_CHAR_PREV] = { .win = window_char_prev }, + [MOVE_CHAR_NEXT] = { .win = window_char_next }, + [MOVE_LINE_UP] = { .win = window_line_up }, + [MOVE_LINE_DOWN] = { .win = window_line_down }, [MOVE_LINE_BEGIN] = { .txt = text_line_begin, .type = LINEWISE }, [MOVE_LINE_START] = { .txt = text_line_start, .type = LINEWISE }, [MOVE_LINE_FINISH] = { .txt = text_line_finish, .type = LINEWISE }, @@ -136,7 +136,7 @@ static TextObject textobjs[] = { }; /* draw a statubar, do whatever you want with the given curses window */ -static void statusbar(WINDOW *win, bool active, const char *filename, int line, int col) { +static void statusbar(WINDOW *win, bool active, const char *filename, size_t line, size_t col) { int width, height; getmaxyx(win, height, width); (void)height; @@ -157,15 +157,15 @@ void quit(const Arg *arg) { } static void split(const Arg *arg) { - editor_window_split(editor, arg->s); + vis_window_split(vis, arg->s); } static void mark_set(const Arg *arg) { - editor_mark_set(editor, arg->i, editor_cursor_get(editor)); + vis_mark_set(vis, arg->i, window_cursor_get(vis->win->win)); } static void mark_goto(const Arg *arg) { - editor_mark_goto(editor, arg->i); + vis_mark_goto(vis, arg->i); } static Action action; @@ -203,9 +203,10 @@ void action_reset(Action *a) { } void action_do(Action *a) { - Text *txt = editor_text_get(editor); + Text *txt = vis->win->text; + Win *win = vis->win->win; OperatorContext c; - size_t pos = editor_cursor_get(editor); + size_t pos = window_cursor_get(win); c.pos = pos; if (a->count == 0) a->count = 1; @@ -215,15 +216,15 @@ void action_do(Action *a) { if (a->movement->txt) pos = a->movement->txt(txt, pos); else - pos = a->movement->win(editor); + pos = a->movement->win(win); } c.range.start = MIN(start, pos); c.range.end = MAX(start, pos); if (!a->op) { if (a->movement->type & CHARWISE) - editor_scroll_to(editor, pos); + window_scroll_to(win, pos); else - editor_cursor_to(editor, pos); + window_cursor_to(win, pos); } else if (a->movement->type & INCLUSIVE) { Iterator it = text_iterator_get(txt, c.range.end); text_iterator_char_next(&it, NULL); @@ -254,8 +255,8 @@ static KeyBinding basic_movement[] = { { { KEY(RIGHT) }, movement, { .i = MOVE_CHAR_NEXT } }, { { KEY(UP) }, movement, { .i = MOVE_LINE_UP } }, { { KEY(DOWN) }, movement, { .i = MOVE_LINE_DOWN } }, - { { KEY(PPAGE) }, cursor, { .m = editor_page_up } }, - { { KEY(NPAGE) }, cursor, { .m = editor_page_down } }, + { { KEY(PPAGE) }, cursor, { .m = window_page_up } }, + { { KEY(NPAGE) }, cursor, { .m = window_page_down } }, { { KEY(HOME) }, movement, { .i = MOVE_LINE_START } }, { { KEY(END) }, movement, { .i = MOVE_LINE_FINISH } }, // temporary until we have a way to enter user commands @@ -370,19 +371,19 @@ static KeyBinding vis_marks[] = { /* {a-zA-Z} */ static KeyBinding vis_normal[] = { { { CONTROL('w'), NONE('c') }, split, { .s = NULL } }, - { { CONTROL('w'), NONE('j') }, call, { .f = editor_window_next } }, - { { CONTROL('w'), NONE('k') }, call, { .f = editor_window_prev } }, - { { CONTROL('F') }, cursor, { .m = editor_page_up } }, - { { CONTROL('B') }, cursor, { .m = editor_page_down } }, + { { CONTROL('w'), NONE('j') }, call, { .f = vis_window_next } }, + { { CONTROL('w'), NONE('k') }, call, { .f = vis_window_prev } }, + { { CONTROL('F') }, cursor, { .m = window_page_up } }, + { { CONTROL('B') }, cursor, { .m = window_page_down } }, { { NONE('n') }, find_forward, { .s = "if" } }, { { NONE('p') }, find_backward, { .s = "if" } }, - { { NONE('x') }, cursor, { .m = editor_delete } }, + { { NONE('x') }, cursor, { .f = vis_delete_key } }, { { NONE('i') }, switchmode, { .i = VIS_MODE_INSERT } }, { { NONE('v') }, switchmode, { .i = VIS_MODE_VISUAL } }, { { NONE('R') }, switchmode, { .i = VIS_MODE_REPLACE} }, - { { NONE('u') }, call, { .f = editor_undo } }, - { { CONTROL('R') }, call, { .f = editor_redo } }, - { { CONTROL('L') }, call, { .f = editor_draw } }, + { { NONE('u') }, call, { .f = vis_undo } }, + { { CONTROL('R') }, call, { .f = vis_redo } }, + { { CONTROL('L') }, call, { .f = vis_draw } }, { /* empty last element, array terminator */ }, }; @@ -392,22 +393,22 @@ static KeyBinding vis_visual[] = { }; static void vis_visual_enter(void) { - editor_selection_start(editor); + window_selection_start(vis->win->win); } static void vis_visual_leave(void) { - editor_selection_clear(editor); + window_selection_clear(vis->win->win); } -static KeyBinding vis_insert[] = { +static KeyBinding vis_insert_mode[] = { { { NONE(ESC) }, switchmode, { .i = VIS_MODE_NORMAL } }, - { { CONTROL('D') }, cursor, { .m = editor_delete } }, - BACKSPACE( cursor, m, editor_backspace ), + { { CONTROL('D') }, cursor, { .f = vis_delete_key } }, + BACKSPACE( cursor, f, vis_backspace_key ), { /* empty last element, array terminator */ }, }; static bool vis_insert_input(const char *str, size_t len) { - editor_insert(editor, str, len); + vis_insert_key(vis, str, len); return true; } @@ -417,47 +418,47 @@ static KeyBinding vis_replace[] = { }; static bool vis_replace_input(const char *str, size_t len) { - editor_replace(editor, str, len); + vis_replace_key(vis, str, len); return true; } -static Mode vis[] = { +static Mode vis_modes[] = { [VIS_MODE_BASIC] = { .parent = NULL, .bindings = basic_movement, }, [VIS_MODE_MOVE] = { - .parent = &vis[VIS_MODE_BASIC], + .parent = &vis_modes[VIS_MODE_BASIC], .bindings = vis_movements, }, [VIS_MODE_TEXTOBJ] = { - .parent = &vis[VIS_MODE_MOVE], + .parent = &vis_modes[VIS_MODE_MOVE], .bindings = vis_textobjs, }, [VIS_MODE_OPERATOR] = { - .parent = &vis[VIS_MODE_MOVE], + .parent = &vis_modes[VIS_MODE_MOVE], .bindings = vis_operators, }, [VIS_MODE_NORMAL] = { - .parent = &vis[VIS_MODE_OPERATOR], + .parent = &vis_modes[VIS_MODE_OPERATOR], .bindings = vis_normal, }, [VIS_MODE_VISUAL] = { .name = "VISUAL", - .parent = &vis[VIS_MODE_OPERATOR], + .parent = &vis_modes[VIS_MODE_OPERATOR], .bindings = vis_visual, .enter = vis_visual_enter, .leave = vis_visual_leave, }, [VIS_MODE_INSERT] = { .name = "INSERT", - .parent = &vis[VIS_MODE_BASIC], - .bindings = vis_insert, + .parent = &vis_modes[VIS_MODE_BASIC], + .bindings = vis_insert_mode, .input = vis_insert_input, }, [VIS_MODE_REPLACE] = { .name = "REPLACE", - .parent = &vis[VIS_MODE_INSERT], + .parent = &vis_modes[VIS_MODE_INSERT], .bindings = vis_replace, .input = vis_replace_input, }, @@ -466,7 +467,7 @@ static Mode vis[] = { static void switchmode(const Arg *arg) { if (mode->leave) mode->leave(); - mode = &vis[arg->i]; + mode = &vis_modes[arg->i]; if (mode->enter) mode->enter(); // TODO display mode name somewhere? @@ -498,55 +499,55 @@ XXX: CONTROL(' ') = 0, ^Space Go forward one word */ /* key binding configuration */ -#if 1 static KeyBinding nano_keys[] = { - { { CONTROL('D') }, cursor, { .m = editor_delete } }, - BACKSPACE( cursor, m, editor_backspace ), - { { KEY(LEFT) }, cursor, { .m = editor_char_prev } }, - { { KEY(RIGHT) }, cursor, { .m = editor_char_next } }, - { { CONTROL('F') }, cursor, { .m = editor_char_next } }, - { { KEY(UP) }, cursor, { .m = editor_line_up } }, - { { CONTROL('P') }, cursor, { .m = editor_line_up } }, - { { KEY(DOWN) }, cursor, { .m = editor_line_down } }, - { { CONTROL('N') }, cursor, { .m = editor_line_down } }, - { { KEY(PPAGE) }, cursor, { .m = editor_page_up } }, - { { CONTROL('Y') }, cursor, { .m = editor_page_up } }, - { { KEY(F(7)) }, cursor, { .m = editor_page_up } }, - { { KEY(NPAGE) }, cursor, { .m = editor_page_down } }, - { { CONTROL('V') }, cursor, { .m = editor_page_down } }, - { { KEY(F(8)) }, cursor, { .m = editor_page_down } }, -// { { CONTROL(' ') }, cursor, { .m = editor_word_start_next } }, - { { META(' ') }, cursor, { .m = editor_word_start_prev } }, - { { CONTROL('A') }, cursor, { .m = editor_line_start } }, - { { CONTROL('E') }, cursor, { .m = editor_line_end } }, - { { META(']') }, cursor, { .m = editor_bracket_match } }, - { { META(')') }, cursor, { .m = editor_paragraph_next } }, - { { META('(') }, cursor, { .m = editor_paragraph_prev } }, - { { META('\\') }, cursor, { .m = editor_file_begin } }, - { { META('|') }, cursor, { .m = editor_file_begin } }, - { { META('/') }, cursor, { .m = editor_file_end } }, - { { META('?') }, cursor, { .m = editor_file_end } }, - { { META('U') }, call, { .f = editor_undo } }, - { { META('E') }, call, { .f = editor_redo } }, +#if 0 + { { CONTROL('D') }, cursor, { .m = vis_delete } }, + BACKSPACE( cursor, m, vis_backspace ), + { { KEY(LEFT) }, cursor, { .m = vis_char_prev } }, + { { KEY(RIGHT) }, cursor, { .m = vis_char_next } }, + { { CONTROL('F') }, cursor, { .m = vis_char_next } }, + { { KEY(UP) }, cursor, { .m = vis_line_up } }, + { { CONTROL('P') }, cursor, { .m = vis_line_up } }, + { { KEY(DOWN) }, cursor, { .m = vis_line_down } }, + { { CONTROL('N') }, cursor, { .m = vis_line_down } }, + { { KEY(PPAGE) }, cursor, { .m = vis_page_up } }, + { { CONTROL('Y') }, cursor, { .m = vis_page_up } }, + { { KEY(F(7)) }, cursor, { .m = vis_page_up } }, + { { KEY(NPAGE) }, cursor, { .m = vis_page_down } }, + { { CONTROL('V') }, cursor, { .m = vis_page_down } }, + { { KEY(F(8)) }, cursor, { .m = vis_page_down } }, +// { { CONTROL(' ') }, cursor, { .m = vis_word_start_next } }, + { { META(' ') }, cursor, { .m = vis_word_start_prev } }, + { { CONTROL('A') }, cursor, { .m = vis_line_start } }, + { { CONTROL('E') }, cursor, { .m = vis_line_end } }, + { { META(']') }, cursor, { .m = vis_bracket_match } }, + { { META(')') }, cursor, { .m = vis_paragraph_next } }, + { { META('(') }, cursor, { .m = vis_paragraph_prev } }, + { { META('\\') }, cursor, { .m = vis_file_begin } }, + { { META('|') }, cursor, { .m = vis_file_begin } }, + { { META('/') }, cursor, { .m = vis_file_end } }, + { { META('?') }, cursor, { .m = vis_file_end } }, + { { META('U') }, call, { .f = vis_undo } }, + { { META('E') }, call, { .f = vis_redo } }, { { CONTROL('I') }, insert, { .s = "\t" } }, - /* TODO: handle this in editor to insert \n\r when appriopriate */ + /* TODO: handle this in vis to insert \n\r when appriopriate */ { { CONTROL('M') }, insert, { .s = "\n" } }, - { { CONTROL('L') }, call, { .f = editor_draw } }, + { { CONTROL('L') }, call, { .f = vis_draw } }, +#endif { /* empty last element, array terminator */ }, }; -#endif static Mode nano[] = { { .parent = NULL, .bindings = basic_movement, }, { .parent = &nano[0], .bindings = nano_keys, .input = vis_insert_input, }, }; -/* list of editor configurations, first entry is default. name is matched with +/* list of vis configurations, first entry is default. name is matched with * argv[0] i.e. program name upon execution */ static Config editors[] = { - { .name = "vis", .mode = &vis[VIS_MODE_NORMAL] }, - { .name = "nano", .mode = &nano[1] }, + { .name = "vis", .mode = &vis_modes[VIS_MODE_NORMAL], .statusbar = statusbar }, + { .name = "nano", .mode = &nano[1], .statusbar = statusbar }, }; /* Color definitions, by default the i-th color is used for the i-th syntax diff --git a/editor.h b/editor.h deleted file mode 100644 index b6e52bc..0000000 --- a/editor.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef EDITOR_H -#define EDITOR_H - -#include <curses.h> -#include <regex.h> -#include "text.h" - -typedef struct { - short fg, bg; /* fore and background color */ - int attr; /* curses attributes */ -} Color; - -typedef struct { - char *rule; /* regex to search for */ - int cflags; /* compilation flags (REG_*) used when compiling */ - Color color; /* settings to apply in case of a match */ - regex_t regex; /* compiled form of the above rule */ -} SyntaxRule; - -#define SYNTAX_REGEX_RULES 10 - -typedef struct { /* a syntax definition */ - char *name; /* syntax name */ - char *file; /* apply to files matching this regex */ - regex_t file_regex; /* compiled file name regex */ - SyntaxRule rules[SYNTAX_REGEX_RULES]; /* all rules for this file type */ -} Syntax; - -typedef struct Editor Editor; -typedef size_t Filepos; - -typedef void (*editor_statusbar_t)(WINDOW *win, bool active, const char *filename, int line, int col); - -/* initialize a new editor with the available screen size */ -Editor *editor_new(int width, int height, editor_statusbar_t); -/* loads the given file and displays it in a new window. passing NULL as - * filename will open a new empty buffer */ -bool editor_load(Editor*, const char *filename); -size_t editor_insert(Editor*, const char *c, size_t len); -size_t editor_replace(Editor *ed, const char *c, size_t len); -size_t editor_backspace(Editor*); -size_t editor_delete(Editor*); -bool editor_resize(Editor*, int width, int height); -void editor_snapshot(Editor*); -void editor_undo(Editor*); -void editor_redo(Editor*); -void editor_draw(Editor *editor); -/* flush all changes made to the ncurses windows to the screen */ -void editor_update(Editor *editor); -void editor_free(Editor*); - -/* cursor movements, also updates selection if one is active, returns new cursor postion */ -size_t editor_file_begin(Editor *ed); -size_t editor_file_end(Editor *ed); -size_t editor_page_down(Editor *ed); -size_t editor_page_up(Editor *ed); -size_t editor_char_next(Editor*); -size_t editor_char_prev(Editor*); -size_t editor_line_goto(Editor*, size_t lineno); -size_t editor_line_down(Editor*); -size_t editor_line_up(Editor*); -size_t editor_line_begin(Editor*); -size_t editor_line_start(Editor*); -size_t editor_line_finish(Editor*); -size_t editor_line_end(Editor*); -size_t editor_word_end_next(Editor*); -size_t editor_word_end_prev(Editor *ed); -size_t editor_word_start_next(Editor*); -size_t editor_word_start_prev(Editor *ed); -size_t editor_sentence_next(Editor *ed); -size_t editor_sentence_prev(Editor *ed); -size_t editor_paragraph_next(Editor *ed); -size_t editor_paragraph_prev(Editor *ed); -size_t editor_bracket_match(Editor *ed); - -// mark handling -typedef int Mark; -void editor_mark_set(Editor*, Mark, size_t pos); -void editor_mark_goto(Editor*, Mark); -void editor_mark_clear(Editor*, Mark); - -// TODO comment -bool editor_syntax_load(Editor*, Syntax *syntaxes, Color *colors); -void editor_syntax_unload(Editor*); - -size_t editor_cursor_get(Editor*); -void editor_scroll_to(Editor*, size_t pos); -void editor_cursor_to(Editor*, size_t pos); -Text *editor_text_get(Editor*); -// TODO void return type? -size_t editor_selection_start(Editor*); -size_t editor_selection_end(Editor*); -Filerange editor_selection_get(Editor*); -void editor_selection_clear(Editor*); - -void editor_search(Editor *ed, const char *regex, int direction); - -void editor_window_split(Editor *ed, const char *filename); -void editor_window_vsplit(Editor *ed, const char *filename); -void editor_window_next(Editor *ed); -void editor_window_prev(Editor *ed); - -/* library initialization code, should be run at startup */ -void editor_init(void); -// TODO: comment -short editor_color_reserve(short fg, short bg); -short editor_color_get(short fg, short bg); - -#endif @@ -0,0 +1,234 @@ +#define _POSIX_SOURCE +#include <locale.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <sys/select.h> +#include <sys/ioctl.h> + +#include "vis.h" +#include "util.h" + +#ifdef PDCURSES +int ESCDELAY; +#endif +#ifndef NCURSES_REENTRANT +# define set_escdelay(d) (ESCDELAY = (d)) +#endif + +static void cursor(const Arg *arg); +static void call(const Arg *arg); +static void insert(const Arg *arg); +static void line(const Arg *arg); +static void find_forward(const Arg *arg); +static void find_backward(const Arg *arg); + +static Mode *mode; +static Vis *vis; + +#include "config.h" + +static Config *config = &editors[0]; + +static void cursor(const Arg *arg) { + arg->m(vis->win->win); +} + +static void call(const Arg *arg) { + arg->f(vis); +} + +static void line(const Arg *arg) { + vis_line_goto(vis, arg->i); +} + +static void find_forward(const Arg *arg) { + vis_search(vis, arg->s, 1); +} + +static void find_backward(const Arg *arg) { + vis_search(vis, arg->s, -1); +} + +static void insert(const Arg *arg) { + //vis_insert(vis, arg->s, strlen(arg->s)); +} + +typedef struct Screen Screen; +static struct Screen { + int w, h; + bool need_resize; +} screen = { .need_resize = true }; + +static void sigwinch_handler(int sig) { + screen.need_resize = true; +} + +static void resize_screen(Screen *screen) { + struct winsize ws; + + if (ioctl(0, TIOCGWINSZ, &ws) == -1) { + getmaxyx(stdscr, screen->h, screen->w); + } else { + screen->w = ws.ws_col; + screen->h = ws.ws_row; + } + + resizeterm(screen->h, screen->w); + wresize(stdscr, screen->h, screen->w); + screen->need_resize = false; +} + +static void setup() { + setlocale(LC_CTYPE, ""); + if (!getenv("ESCDELAY")) + set_escdelay(50); + initscr(); + start_color(); + raw(); + noecho(); + keypad(stdscr, TRUE); + meta(stdscr, TRUE); + resize_screen(&screen); + /* needed because we use getch() which implicitly calls refresh() which + would clear the screen (overwrite it with an empty / unused stdscr */ + refresh(); + + struct sigaction sa; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigwinch_handler; + sigaction(SIGWINCH, &sa, NULL); +} + +static void cleanup() { + vis_free(vis); + endwin(); +} + +static bool keymatch(Key *key0, Key *key1) { + return (key0->str[0] && memcmp(key0->str, key1->str, sizeof(key1->str)) == 0) || + (key0->code && key0->code == key1->code); +} + +static KeyBinding *keybinding(Mode *mode, Key *key0, Key *key1) { + for (; mode; mode = mode->parent) { + for (KeyBinding *kb = mode->bindings; kb && (kb->key[0].code || kb->key[0].str[0]); kb++) { + if (keymatch(key0, &kb->key[0]) && (!key1 || keymatch(key1, &kb->key[1]))) + return kb; + } + if (mode->unknown && !mode->unknown(key0, key1)) + break; + } + return NULL; +} + +int main(int argc, char *argv[]) { + /* decide which key configuration to use based on argv[0] */ + char *arg0 = argv[0]; + while (*arg0 && (*arg0 == '.' || *arg0 == '/')) + arg0++; + for (int i = 0; i < LENGTH(editors); i++) { + if (editors[i].name[0] == arg0[0]) { + config = &editors[i]; + break; + } + } + mode = config->mode; + + setup(); + if (!(vis = vis_new(screen.w, screen.h))) + return 1; + if (!vis_syntax_load(vis, syntaxes, colors)) + return 1; + vis_statusbar_set(vis, config->statusbar); + char *filename = argc > 1 ? argv[1] : NULL; + if (!vis_window_new(vis, filename)) + return 1; + + struct timeval idle = { .tv_usec = 0 }, *timeout = NULL; + Key key, key_prev, *key_mod = NULL; + + for (;;) { + if (screen.need_resize) { + resize_screen(&screen); + vis_resize(vis, screen.w, screen.h); + } + + fd_set fds; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + + vis_update(vis); + doupdate(); + idle.tv_sec = 3; + int r = select(1, &fds, NULL, NULL, timeout); + if (r == -1 && errno == EINTR) + continue; + + if (r < 0) { + perror("select()"); + exit(EXIT_FAILURE); + } + + if (!FD_ISSET(STDIN_FILENO, &fds)) { + vis_snapshot(vis); + timeout = NULL; + continue; + } + + int keycode = getch(); + if (keycode == ERR) + continue; + + // TODO verbatim insert mode + int len = 0; + if (keycode >= KEY_MIN) { + key.code = keycode; + key.str[0] = '\0'; + } else { + char keychar = keycode; + key.str[len++] = keychar; + key.code = 0; + + if (!ISASCII(keychar) || keychar == '\e') { + nodelay(stdscr, TRUE); + for (int t; len < LENGTH(key.str) && (t = getch()) != ERR; len++) + key.str[len] = t; + nodelay(stdscr, FALSE); + } + } + + for (size_t i = len; i < LENGTH(key.str); i++) + key.str[i] = '\0'; + + KeyBinding *action = keybinding(mode, key_mod ? key_mod : &key, key_mod ? &key : NULL); + if (!action && key_mod) { + /* second char of a combination was invalid, search again without the prefix */ + action = keybinding(mode, &key, NULL); + key_mod = NULL; + } + if (action) { + /* check if it is the first part of a combination */ + if (!key_mod && (action->key[1].code || action->key[1].str[0])) { + key_prev = key; + key_mod = &key_prev; + continue; + } + action->func(&action->arg); + key_mod = NULL; + continue; + } + + if (keycode >= KEY_MIN) + continue; + + if (mode->input && mode->input(key.str, len)) + timeout = &idle; + } + + cleanup(); + return 0; +} @@ -1,234 +1,400 @@ -#define _POSIX_SOURCE -#include <locale.h> #include <stdlib.h> -#include <unistd.h> -#include <string.h> -#include <signal.h> -#include <errno.h> -#include <sys/select.h> -#include <sys/ioctl.h> - -#include "editor.h" #include "vis.h" #include "util.h" -#ifdef PDCURSES -int ESCDELAY; -#endif -#ifndef NCURSES_REENTRANT -# define set_escdelay(d) (ESCDELAY = (d)) -#endif +static VisWin *vis_window_new_text(Vis *vis, Text *text); +static void vis_window_free(VisWin *win); +static void vis_window_split_internal(Vis *vis, const char *filename); +static void vis_windows_invalidate(Vis *vis, size_t start, size_t end); +static void vis_window_draw(VisWin *win); +static void vis_search_forward(Vis *vis, Regex *regex); +static void vis_search_backward(Vis *vis, Regex *regex); +static void vis_windows_arrange_horizontal(Vis *vis); +static void vis_windows_arrange_vertical(Vis *vis); + + +static void vis_window_resize(VisWin *win, int width, int height) { + window_resize(win->win, width, height - 1); + wresize(win->statuswin, 1, width); + win->width = width; + win->height = height; +} -static void cursor(const Arg *arg); -static void call(const Arg *arg); -static void insert(const Arg *arg); -static void line(const Arg *arg); -static void find_forward(const Arg *arg); -static void find_backward(const Arg *arg); +static void vis_window_move(VisWin *win, int x, int y) { + window_move(win->win, x, y); + mvwin(win->statuswin, y + win->height - 1, x); +} -static Mode *mode; -static Editor *editor; +static void vis_window_statusbar_draw(VisWin *win) { + size_t line, col; + if (win->vis->statusbar) { + window_cursor_getxy(win->win, &line, &col); + win->vis->statusbar(win->statuswin, win->vis->win == win, + text_filename(win->text), line, col); + } +} -#include "config.h" +static void vis_window_cursor_moved(Win *win, void *data) { + vis_window_statusbar_draw(data); +} -static Config *config = &editors[0]; +void vis_statusbar_set(Vis *vis, vis_statusbar_t statusbar) { + vis->statusbar = statusbar; +} -static void cursor(const Arg *arg) { - arg->m(editor); +size_t vis_line_goto(Vis *vis, size_t lineno) { + size_t pos = text_pos_by_lineno(vis->win->text, lineno); + window_cursor_to(vis->win->win, pos); + return pos; } -static void call(const Arg *arg) { - arg->f(editor); +static void vis_search_forward(Vis *vis, Regex *regex) { + VisWin *win = vis->win; + int pos = window_cursor_get(win->win) + 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 = window_cursor_get(win->win); + if (!text_search_forward(win->text, pos, end, regex, 1, match, 0)) + found = true; + } else { + found = true; + } + if (found) + window_cursor_to(win->win, match[0].start); } -static void line(const Arg *arg) { - editor_line_goto(editor, arg->i); +static void vis_search_backward(Vis *vis, Regex *regex) { + VisWin *win = vis->win; + int pos = 0; + int end = window_cursor_get(win->win); + RegexMatch match[1]; + bool found = false; + if (text_search_backward(win->text, pos, end, regex, 1, match, 0)) { + pos = window_cursor_get(win->win) + 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) + window_cursor_to(win->win, match[0].start); } -static void find_forward(const Arg *arg) { - editor_search(editor, arg->s, 1); +void vis_search(Vis *vis, const char *s, int direction) { + Regex *regex = text_regex_new(); + if (!regex) + return; + if (!text_regex_compile(regex, s, REG_EXTENDED)) { + if (direction >= 0) + vis_search_forward(vis, regex); + else + vis_search_backward(vis, regex); + } + text_regex_free(regex); } -static void find_backward(const Arg *arg) { - editor_search(editor, arg->s, -1); +void vis_snapshot(Vis *vis) { + text_snapshot(vis->win->text); } -static void insert(const Arg *arg) { - editor_insert(editor, arg->s, strlen(arg->s)); +void vis_undo(Vis *vis) { + VisWin *win = vis->win; + // TODO window invalidation + if (text_undo(win->text)) + window_draw(win->win); } -typedef struct Screen Screen; -static struct Screen { - int w, h; - bool need_resize; -} screen = { .need_resize = true }; +void vis_redo(Vis *vis) { + VisWin *win = vis->win; + // TODO window invalidation + if (text_redo(win->text)) + window_draw(win->win); +} -static void sigwinch_handler(int sig) { - screen.need_resize = true; +static void vis_windows_arrange_horizontal(Vis *vis) { + int n = 0, x = 0, y = 0; + for (VisWin *win = vis->windows; win; win = win->next) + n++; + int height = vis->height / n; + for (VisWin *win = vis->windows; win; win = win->next) { + vis_window_resize(win, vis->width, win->next ? height : vis->height - y); + vis_window_move(win, x, y); + y += height; + } } -static void resize_screen(Screen *screen) { - struct winsize ws; +static void vis_windows_arrange_vertical(Vis *vis) { + int n = 0, x = 0, y = 0; + for (VisWin *win = vis->windows; win; win = win->next) + n++; + int width = vis->width / n; + for (VisWin *win = vis->windows; win; win = win->next) { + vis_window_resize(win, win->next ? width : vis->width - x, vis->height); + vis_window_move(win, x, y); + x += width; + } +} - if (ioctl(0, TIOCGWINSZ, &ws) == -1) { - getmaxyx(stdscr, screen->h, screen->w); - } else { - screen->w = ws.ws_col; - screen->h = ws.ws_row; +static void vis_window_split_internal(Vis *vis, const char *filename) { + VisWin *sel = vis->win; + if (filename) { + // TODO check that this file isn't already open + vis_window_new(vis, filename); + } else if (sel) { + VisWin *win = vis_window_new_text(vis, sel->text); + win->text = sel->text; + window_syntax_set(win->win, window_syntax_get(sel->win)); + window_cursor_to(win->win, window_cursor_get(sel->win)); } +} + +void vis_window_split(Vis *vis, const char *filename) { + vis_window_split_internal(vis, filename); + vis->windows_arrange = vis_windows_arrange_horizontal; + vis_draw(vis); +} + +void vis_window_vsplit(Vis *vis, const char *filename) { + vis_window_split_internal(vis, filename); + vis->windows_arrange = vis_windows_arrange_vertical; + vis_draw(vis); +} + +void vis_resize(Vis *vis, int width, int height) { + vis->width = width; + vis->height = height; + vis_draw(vis); +} - resizeterm(screen->h, screen->w); - wresize(stdscr, screen->h, screen->w); - screen->need_resize = false; +void vis_window_next(Vis *vis) { + VisWin *sel = vis->win; + if (!sel) + return; + vis->win = vis->win->next; + if (!vis->win) + vis->win = vis->windows; + vis_window_statusbar_draw(sel); + vis_window_statusbar_draw(vis->win); } -static void setup() { - setlocale(LC_CTYPE, ""); - if (!getenv("ESCDELAY")) - set_escdelay(50); - initscr(); - start_color(); - raw(); - noecho(); - keypad(stdscr, TRUE); - meta(stdscr, TRUE); - resize_screen(&screen); - /* needed because we use getch() which implicitly calls refresh() which - would clear the screen (overwrite it with an empty / unused stdscr */ - refresh(); +void vis_window_prev(Vis *vis) { + VisWin *sel = vis->win; + if (!sel) + return; + vis->win = vis->win->prev; + if (!vis->win) + for (vis->win = vis->windows; vis->win->next; vis->win = vis->win->next); + vis_window_statusbar_draw(sel); + vis_window_statusbar_draw(vis->win); +} - struct sigaction sa; - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sa.sa_handler = sigwinch_handler; - sigaction(SIGWINCH, &sa, NULL); +void vis_mark_set(Vis *vis, Mark mark, size_t pos) { + text_mark_set(vis->win->text, mark, pos); } -static void cleanup() { - editor_free(editor); - endwin(); +void vis_mark_goto(Vis *vis, Mark mark) { + VisWin *win = vis->win; + size_t pos = text_mark_get(win->text, mark); + if (pos != (size_t)-1) + window_cursor_to(win->win, pos); } -static bool keymatch(Key *key0, Key *key1) { - return (key0->str[0] && memcmp(key0->str, key1->str, sizeof(key1->str)) == 0) || - (key0->code && key0->code == key1->code); +void vis_mark_clear(Vis *vis, Mark mark) { + text_mark_clear(vis->win->text, mark); } -static KeyBinding *keybinding(Mode *mode, Key *key0, Key *key1) { - for (; mode; mode = mode->parent) { - for (KeyBinding *kb = mode->bindings; kb && (kb->key[0].code || kb->key[0].str[0]); kb++) { - if (keymatch(key0, &kb->key[0]) && (!key1 || keymatch(key1, &kb->key[1]))) - return kb; +static void vis_windows_invalidate(Vis *vis, size_t start, size_t end) { + for (VisWin *win = vis->windows; win; win = win->next) { + if (vis->win != win && vis->win->text == win->text) { + Filerange view = window_viewport_get(win->win); + if ((view.start <= start && start <= view.end) || + (view.start <= end && end <= view.end)) + vis_window_draw(win); } - if (mode->unknown && !mode->unknown(key0, key1)) - break; } - return NULL; } -int main(int argc, char *argv[]) { - /* decide which key configuration to use based on argv[0] */ - char *arg0 = argv[0]; - while (*arg0 && (*arg0 == '.' || *arg0 == '/')) - arg0++; - for (int i = 0; i < LENGTH(editors); i++) { - if (editors[i].name[0] == arg0[0]) { - config = &editors[i]; - break; + +bool vis_syntax_load(Vis *vis, Syntax *syntaxes, Color *colors) { + bool success = true; + vis->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(vis_color_reserve(rule->color.fg, rule->color.bg)); + if (regcomp(&rule->regex, rule->rule, REG_EXTENDED|rule->cflags)) + success = false; } } - mode = config->mode; - - setup(); - if (!(editor = editor_new(screen.w, screen.h, statusbar))) - return 1; - if (!editor_syntax_load(editor, syntaxes, colors)) - return 1; - char *filename = argc > 1 ? argv[1] : NULL; - if (!editor_load(editor, filename)) - return 1; - struct timeval idle = { .tv_usec = 0 }, *timeout = NULL; - Key key, key_prev, *key_mod = NULL; + return success; +} - for (;;) { - if (screen.need_resize) { - resize_screen(&screen); - editor_resize(editor, screen.w, screen.h); +void vis_syntax_unload(Vis *vis) { + for (Syntax *syn = vis->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); } + } - fd_set fds; - FD_ZERO(&fds); - FD_SET(STDIN_FILENO, &fds); + vis->syntaxes = NULL; +} - editor_update(editor); - doupdate(); - idle.tv_sec = 3; - int r = select(1, &fds, NULL, NULL, timeout); - if (r == -1 && errno == EINTR) - continue; +static void vis_window_draw(VisWin *win) { + // TODO window_draw draw should restore cursor position + window_draw(win->win); + window_cursor_to(win->win, window_cursor_get(win->win)); +} - if (r < 0) { - perror("select()"); - exit(EXIT_FAILURE); - } +void vis_draw(Vis *vis) { + erase(); + wnoutrefresh(stdscr); + vis->windows_arrange(vis); + for (VisWin *win = vis->windows; win; win = win->next) { + if (vis->win != win) + vis_window_draw(win); + } + vis_window_draw(vis->win); +} - if (!FD_ISSET(STDIN_FILENO, &fds)) { - editor_snapshot(editor); - timeout = NULL; - continue; +void vis_update(Vis *vis) { + for (VisWin *win = vis->windows; win; win = win->next) { + if (vis->win != win) { + wnoutrefresh(win->statuswin); + window_update(win->win); } + } - int keycode = getch(); - if (keycode == ERR) - continue; - - // TODO verbatim insert mode - int len = 0; - if (keycode >= KEY_MIN) { - key.code = keycode; - key.str[0] = '\0'; - } else { - char keychar = keycode; - key.str[len++] = keychar; - key.code = 0; - - if (!ISASCII(keychar) || keychar == '\e') { - nodelay(stdscr, TRUE); - for (int t; len < LENGTH(key.str) && (t = getch()) != ERR; len++) - key.str[len] = t; - nodelay(stdscr, FALSE); - } - } + wnoutrefresh(vis->win->statuswin); + window_update(vis->win->win); +} - for (size_t i = len; i < LENGTH(key.str); i++) - key.str[i] = '\0'; +static void vis_window_free(VisWin *win) { + if (!win) + return; + window_free(win->win); + if (win->statuswin) + delwin(win->statuswin); + // XXX: open in other windows + text_free(win->text); + free(win); +} - KeyBinding *action = keybinding(mode, key_mod ? key_mod : &key, key_mod ? &key : NULL); - if (!action && key_mod) { - /* second char of a combination was invalid, search again without the prefix */ - action = keybinding(mode, &key, NULL); - key_mod = NULL; - } - if (action) { - /* check if it is the first part of a combination */ - if (!key_mod && (action->key[1].code || action->key[1].str[0])) { - key_prev = key; - key_mod = &key_prev; - continue; +static VisWin *vis_window_new_text(Vis *vis, Text *text) { + VisWin *win = calloc(1, sizeof(VisWin)); + if (!win) + return NULL; + win->vis = vis; + win->text = text; + win->win = window_new(win->text); + win->statuswin = newwin(1, vis->width, 0, 0); + if (!win->win || !win->statuswin) { + vis_window_free(win); + return NULL; + } + window_cursor_watch(win->win, vis_window_cursor_moved, win); + if (vis->windows) + vis->windows->prev = win; + win->next = vis->windows; + win->prev = NULL; + vis->windows = win; + vis->win = win; + return win; +} + +bool vis_window_new(Vis *vis, const char *filename) { + Text *text = text_load(filename); + if (!text) + return false; + + VisWin *win = vis_window_new_text(vis, text); + if (!win) { + text_free(text); + return false; + } + + if (filename) { + for (Syntax *syn = vis->syntaxes; syn && syn->name; syn++) { + if (!regexec(&syn->file_regex, filename, 0, NULL, 0)) { + window_syntax_set(win->win, syn); + break; } - action->func(&action->arg); - key_mod = NULL; - continue; } + } + + vis_draw(vis); - if (keycode >= KEY_MIN) - continue; - - if (mode->input && mode->input(key.str, len)) - timeout = &idle; + return true; +} + +Vis *vis_new(int width, int height) { + Vis *vis = calloc(1, sizeof(Vis)); + if (!vis) + return NULL; + vis->width = width; + vis->height = height; + vis->windows_arrange = vis_windows_arrange_horizontal; + return vis; +} + +void vis_free(Vis *vis) { + for (VisWin *next, *win = vis->windows; win; win = next) { + next = win->next; + vis_window_free(win); } + free(vis); +} + +void vis_insert_key(Vis *vis, const char *c, size_t len) { + Win *win = vis->win->win; + size_t start = window_cursor_get(win); + window_insert_key(win, c, len); + vis_windows_invalidate(vis, start, start + len); +} + +void vis_replace_key(Vis *vis, const char *c, size_t len) { + Win *win = vis->win->win; + size_t start = window_cursor_get(win); + window_replace_key(win, c, len); + vis_windows_invalidate(vis, start, start + 6); +} + +void vis_backspace_key(Vis *vis) { + Win *win = vis->win->win; + size_t end = window_cursor_get(win); + size_t start = window_backspace_key(win); + vis_windows_invalidate(vis, start, end); +} + +void vis_delete_key(Vis *vis) { + size_t start = window_delete_key(vis->win->win); + vis_windows_invalidate(vis, start, start + 6); +} + +void vis_insert(Vis *vis, size_t pos, const char *c, size_t len) { + text_insert_raw(vis->win->text, pos, c, len); + vis_windows_invalidate(vis, pos, pos + len); +} - cleanup(); - return 0; +void vis_delete(Vis *vis, size_t pos, size_t len) { + text_delete(vis->win->text, pos, len); + vis_windows_invalidate(vis, pos, pos + len); } @@ -0,0 +1,170 @@ +#ifndef VIS_H +#define VIS_H + +#include <stddef.h> +#include <regex.h> +#include "text-motions.h" +#include "text-objects.h" +#include "window.h" +#include "register.h" + +typedef struct Vis Vis; +typedef struct VisWin VisWin; + +struct VisWin { + Vis *vis; /* editor instance to which this window belongs */ + Text *text; /* underlying text management */ + Win *win; /* vis window for the text area */ + WINDOW *statuswin; /* curses window for the statusbar */ + int width, height; /* window size including the statusbar */ + VisWin *prev, *next; /* neighbouring windows */ +}; + +typedef void (*vis_statusbar_t)(WINDOW *win, bool active, const char *filename, size_t line, size_t col); + +struct Vis { + int width, height; /* terminal size, available for all windows */ + VisWin *windows; /* list of windows */ + VisWin *win; /* currently active window */ + Syntax *syntaxes; /* NULL terminated array of syntax definitions */ + void (*windows_arrange)(Vis*); /* current layout which places the windows */ + vis_statusbar_t statusbar; /* configurable user hook to draw statusbar */ +}; + +typedef union { + size_t i; + const char *s; + size_t (*m)(Win*); + void (*f)(Vis*); +} Arg; + +typedef struct { + char str[6]; + int code; +} Key; + +typedef struct { + Key key[2]; + void (*func)(const Arg *arg); + const Arg arg; +} KeyBinding; + +typedef struct Mode Mode; +struct Mode { + Mode *parent; + KeyBinding *bindings; + const char *name; + void (*enter)(void); + void (*leave)(void); + bool (*unknown)(Key *key0, Key *key1); /* unknown key for this mode, return value determines whether parent modes will be checked */ + bool (*input)(const char *str, size_t len); /* unknown key for this an all parent modes */ +}; + +typedef struct { + char *name; + Mode *mode; + vis_statusbar_t statusbar; +} Config; + +typedef struct { + int count; + Register *reg; + Filerange range; + size_t pos; +} OperatorContext; + +typedef void (Operator)(OperatorContext*); + +typedef struct { + size_t (*win)(Win*); + size_t (*txt)(Text*, size_t pos); + enum { + LINEWISE = 1 << 0, + CHARWISE = 1 << 1, + INCLUSIVE = 1 << 2, + EXCLUSIVE = 1 << 3, + } type; + int count; +} Movement; + +typedef struct { + Filerange (*range)(Text*, size_t pos); + enum { + INNER, + OUTER, + } type; +} TextObject; + +typedef struct { + int count; + Operator *op; + Movement *movement; + TextObject *textobj; + Register *reg; +} Action; + +typedef struct { + short fg, bg; /* fore and background color */ + int attr; /* curses attributes */ +} Color; + +typedef struct { + char *rule; /* regex to search for */ + int cflags; /* compilation flags (REG_*) used when compiling */ + Color color; /* settings to apply in case of a match */ + regex_t regex; /* compiled form of the above rule */ +} SyntaxRule; + +#define SYNTAX_REGEX_RULES 10 + +typedef struct Syntax Syntax; + +struct Syntax { /* a syntax definition */ + char *name; /* syntax name */ + char *file; /* apply to files matching this regex */ + regex_t file_regex; /* compiled file name regex */ + SyntaxRule rules[SYNTAX_REGEX_RULES]; /* all rules for this file type */ +}; + +Vis *vis_new(int width, int height); +void vis_free(Vis*); +void vis_resize(Vis*, int width, int height); +void vis_snapshot(Vis*); +void vis_undo(Vis*); +void vis_redo(Vis*); +void vis_draw(Vis*); +void vis_update(Vis*); +void vis_insert_key(Vis*, const char *c, size_t len); +void vis_replace_key(Vis*, const char *c, size_t len); +void vis_backspace_key(Vis*); +void vis_delete_key(Vis*); +void vis_insert(Vis*, size_t pos, const char *data, size_t len); +void vis_delete(Vis*, size_t pos, size_t len); + +// mark handling +typedef int Mark; +void vis_mark_set(Vis*, Mark, size_t pos); +void vis_mark_goto(Vis*, Mark); +void vis_mark_clear(Vis*, Mark); + +// TODO comment +bool vis_syntax_load(Vis*, Syntax *syntaxes, Color *colors); +void vis_syntax_unload(Vis*); + +void vis_search(Vis *ed, const char *regex, int direction); +size_t vis_line_goto(Vis *vis, size_t lineno); + +bool vis_window_new(Vis *vis, const char *filename); +void vis_window_split(Vis *ed, const char *filename); +void vis_window_vsplit(Vis *ed, const char *filename); +void vis_window_next(Vis *ed); +void vis_window_prev(Vis *ed); + +void vis_statusbar_set(Vis*, vis_statusbar_t); + +/* library initialization code, should be run at startup */ +void vis_init(void); +short vis_color_reserve(short fg, short bg); +short vis_color_get(short fg, short bg); + +#endif @@ -19,7 +19,8 @@ #include <wchar.h> #include <ctype.h> #include <errno.h> -#include "editor.h" +#include "vis.h" +#include "window.h" #include "text.h" #include "text-motions.h" #include "text-objects.h" @@ -56,11 +57,9 @@ typedef struct { /* cursor position */ 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 */ + WINDOW *win; /* curses window for the text area */ + int width, height; /* window text area size */ 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 */ @@ -68,52 +67,25 @@ struct Win { /* window showing part of a file */ 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 */ + void (*cursor_moved)(Win*, void *); + void *cursor_moved_data; 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 bool window_scroll_lines_down(Win *win, int n); +static bool window_scroll_lines_up(Win *win, int n); -static void window_selection_clear(Win *win) { +void window_selection_clear(Win *win) { win->sel.start = win->sel.end = (size_t)-1; window_draw(win); window_cursor_update(win); @@ -139,7 +111,7 @@ static void window_clear(Win *win) { win->col = 0; } -static Filerange window_selection_get(Win *win) { +Filerange window_selection_get(Win *win) { Filerange sel = win->sel; if (sel.start == (size_t)-1) { sel.end = (size_t)-1; @@ -153,6 +125,9 @@ static Filerange window_selection_get(Win *win) { return sel; } +Filerange window_viewport_get(Win *win) { + return (Filerange){ .start = win->start, .end = win->end }; +} /* 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) @@ -260,18 +235,16 @@ static bool window_addch(Win *win, Char *c) { } } -static void window_statusbar_draw(Win *win) { - if (!win->editor->statusbar) - return; +void window_cursor_getxy(Win *win, size_t *lineno, size_t *col) { Cursor *cursor = &win->cursor; Line *line = cursor->line; - size_t lineno = line->lineno; - int col = cursor->col; - while (line->prev && line->prev->lineno == lineno) { + *lineno = line->lineno; + *col = cursor->col; + while (line->prev && line->prev->lineno == *lineno) { line = line->prev; - col += line->width; + *col += line->width; } - win->editor->statusbar(win->statuswin, win->editor->win == win, text_filename(win->text), cursor->line->lineno, col+1); + *col += 1; } /* place the cursor according to the screen coordinates in win->{row,col} and @@ -284,13 +257,14 @@ static size_t window_cursor_update(Win *win) { window_draw(win); } wmove(win->win, cursor->row, cursor->col); - window_statusbar_draw(win); + if (win->cursor_moved) + win->cursor_moved(win, win->cursor_moved_data); 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) { +void window_cursor_to(Win *win, size_t pos) { Line *line = win->topline; int row = 0; int col = 0; @@ -299,10 +273,21 @@ static size_t cursor_move_to(Win *win, size_t pos) { if (pos > max) pos = max > 0 ? max - 1 : 0; - if (pos < win->start || pos > win->end) { - win->start = pos; + for (int i = 0; i < 2 && (pos < win->start || pos > win->end); i++) { + win->start = i == 0 ? text_line_start(win->text, pos) : pos; window_draw(win); - } else { + } + +#if 0 + Win *win = vis->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); +#endif + size_t cur = win->start; while (line && line != win->lastline && cur < pos) { if (cur + line->len > pos) @@ -324,27 +309,17 @@ static size_t cursor_move_to(Win *win, size_t pos) { 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); + window_cursor_update(win); } /* 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) { +void window_draw(Win *win) { window_clear(win); wmove(win->win, 0, 0); /* current absolute file position */ @@ -462,12 +437,9 @@ static void window_draw(Win *win) { 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) +bool window_resize(Win *win, int width, int height) { + if (wresize(win->win, height, width) == ERR) return false; - // TODO: only grow memory area win->height = height; win->width = width; @@ -475,83 +447,46 @@ static bool window_resize(Win *win, int width, int height) { 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) { +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) { +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 *window_new(Text *text) { + if (!text) + return NULL; Win *win = calloc(1, sizeof(Win)); - if (!win) + if (!win || !(win->win = newwin(0, 0, 0, 0))) { + window_free(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)) { + win->text = text; + win->tabwidth = 8; // TODO make configurable + + int width, height; + getmaxyx(win->win, height, width); + if (!window_resize(win, width, height)) { window_free(win); return NULL; } window_selection_clear(win); - cursor_move_to(win, pos); + window_cursor_to(win, 0); 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) @@ -559,8 +494,7 @@ static size_t pos_by_line(Win *win, Line *line) { return pos; } -size_t editor_char_prev(Editor *ed) { - Win *win = ed->win; +size_t window_char_prev(Win *win) { Cursor *cursor = &win->cursor; Line *line = cursor->line; @@ -580,8 +514,7 @@ size_t editor_char_prev(Editor *ed) { return window_cursor_update(win); } -size_t editor_char_next(Editor *ed) { - Win *win = ed->win; +size_t window_char_next(Win *win) { Cursor *cursor = &win->cursor; Line *line = cursor->line; @@ -621,7 +554,7 @@ static size_t cursor_offset(Cursor *cursor) { return off; } -static bool scroll_line_down(Win *win, int n) { +static bool window_scroll_lines_down(Win *win, int n) { Line *line; if (win->end == text_size(win->text)) return false; @@ -635,7 +568,7 @@ static bool scroll_line_down(Win *win, int n) { return true; } -static bool scroll_line_up(Win *win, int n) { +static bool window_scroll_lines_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 @@ -668,46 +601,31 @@ static bool scroll_line_up(Win *win, int n) { 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); +size_t window_page_up(Win *win) { + if (!window_scroll_lines_up(win, win->height)) + window_cursor_to(win, win->start); return win->cursor.pos; } -size_t editor_page_down(Editor *ed) { - Win *win = ed->win; +size_t window_page_down(Win *win) { Cursor *cursor = &win->cursor; - if (win->end == text_size(win->text)) - return cursor_move_to(win, win->end); + if (win->end == text_size(win->text)) { + window_cursor_to(win, win->end); + return win->end; + } win->start = win->end; window_draw(win); int col = cursor->col; - cursor_move_to(win, win->start); + window_cursor_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; +size_t window_line_up(Win *win) { Cursor *cursor = &win->cursor; if (!cursor->line->prev) { - scroll_line_up(win, 1); + window_scroll_lines_up(win, 1); return cursor->pos; } cursor->row--; @@ -716,12 +634,11 @@ size_t editor_line_up(Editor *ed) { return window_cursor_update(win); } -size_t editor_line_down(Editor *ed) { - Win *win = ed->win; +size_t window_line_down(Win *win) { Cursor *cursor = &win->cursor; if (!cursor->line->next) { if (cursor->line == win->bottomline) - scroll_line_down(ed->win, 1); + window_scroll_lines_down(win, 1); return cursor->pos; } cursor->row++; @@ -730,196 +647,21 @@ size_t editor_line_down(Editor *ed) { 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); +void window_update(Win *win) { + wnoutrefresh(win->win); } -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; +size_t window_delete_key(Win *win) { + Cursor *cursor = &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); + text_delete(win->text, cursor->pos, len); + window_draw(win); + window_cursor_to(win, cursor->pos); + return cursor->pos; } -size_t editor_backspace(Editor *ed) { - Win *win = ed->win; +size_t window_backspace_key(Win *win) { Cursor *cursor = &win->cursor; if (win->start == cursor->pos) { if (win->start == 0) @@ -928,30 +670,31 @@ size_t editor_backspace(Editor *ed) { * 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); + window_scroll_lines_up(win, 1); + window_cursor_to(win, pos); } - editor_char_prev(ed); + window_char_prev(win); size_t pos = cursor->pos; size_t len = cursor->line->cells[cursor->col].len; - editor_windows_delete(ed, pos, len); + text_delete(win->text, pos, len); window_draw(win); - return cursor_move_to(ed->win, pos); + window_cursor_to(win, pos); + return pos; } -size_t editor_insert(Editor *ed, const char *c, size_t len) { - Win *win = ed->win; +size_t window_insert_key(Win *win, const char *c, size_t len) { size_t pos = win->cursor.pos; - editor_windows_insert(ed, pos, c, len); + text_insert_raw(win->text, pos, c, len); if (win->cursor.line == win->bottomline && memchr(c, '\n', len)) - scroll_line_down(win, 1); + window_scroll_lines_down(win, 1); else window_draw(win); - return cursor_move_to(win, pos + len); + pos += len; + window_cursor_to(win, pos); + return pos; } -size_t editor_replace(Editor *ed, const char *c, size_t len) { - Win *win = ed->win; +size_t window_replace_key(Win *win, const char *c, size_t len) { Cursor *cursor = &win->cursor; Line *line = cursor->line; size_t pos = cursor->pos; @@ -960,210 +703,43 @@ size_t editor_replace(Editor *ed, const char *c, size_t len) { size_t oldlen = line->cells[cursor->col].len; text_delete(win->text, pos, oldlen); } - editor_windows_insert(ed, pos, c, len); + text_insert_raw(win->text, pos, c, len); if (cursor->line == win->bottomline && memchr(c, '\n', len)) - scroll_line_down(win, 1); + window_scroll_lines_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 - } + pos += len; + window_cursor_to(win, pos); + return 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); +size_t window_cursor_get(Win *win) { + return win->cursor.pos; } -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 window_scroll_to(Win *win, size_t pos) { + while (pos < win->start && window_scroll_lines_up(win, 1)); + while (pos > win->end && window_scroll_lines_down(win, 1)); + window_cursor_to(win, pos); } -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 window_selection_start(Win *win) { + win->sel.start = window_cursor_get(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 window_selection_end(Win *win) { + win->sel.end = window_cursor_get(win); } -void editor_mark_set(Editor *ed, Mark mark, size_t pos) { - text_mark_set(ed->win->text, mark, pos); +void window_syntax_set(Win *win, Syntax *syntax) { + win->syntax = syntax; } -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); +Syntax *window_syntax_get(Win *win) { + return win->syntax; } -void editor_mark_clear(Editor *ed, Mark mark) { - text_mark_clear(ed->win->text, mark); +void window_cursor_watch(Win *win, void (*cursor_moved)(Win*, void *), void *data) { + win->cursor_moved = cursor_moved; + win->cursor_moved_data = data; } diff --git a/window.h b/window.h new file mode 100644 index 0000000..0b0f933 --- /dev/null +++ b/window.h @@ -0,0 +1,47 @@ +#ifndef EDITOR_H +#define EDITOR_H + +#include <curses.h> +#include "text.h" + +typedef struct Syntax Syntax; +typedef struct Win Win; +typedef size_t Filepos; + +Win *window_new(Text*); +void window_free(Win*); + +/* keyboard input at cursor position */ +size_t window_insert_key(Win*, const char *c, size_t len); +size_t window_replace_key(Win*, const char *c, size_t len); +size_t window_backspace_key(Win*); +size_t window_delete_key(Win*); + +bool window_resize(Win*, int width, int height); +void window_move(Win*, int x, int y); +void window_draw(Win*); +/* flush all changes made to the ncurses windows to the screen */ +void window_update(Win*); + +/* cursor movements, also updates selection if one is active, returns new cursor postion */ +size_t window_page_down(Win*); +size_t window_page_up(Win*); +size_t window_char_next(Win*); +size_t window_char_prev(Win*); +size_t window_line_down(Win*); +size_t window_line_up(Win*); + +size_t window_cursor_get(Win*); +void window_cursor_getxy(Win*, size_t *lineno, size_t *col); +void window_scroll_to(Win*, size_t pos); +void window_cursor_to(Win*, size_t pos); +void window_selection_start(Win*); +void window_selection_end(Win*); +Filerange window_selection_get(Win*); +void window_selection_clear(Win*); +Filerange window_viewport_get(Win*); +void window_syntax_set(Win*, Syntax*); +Syntax *window_syntax_get(Win*); +void window_cursor_watch(Win *win, void (*cursor_moved)(Win*, void*), void *data); + +#endif |
