aboutsummaryrefslogtreecommitdiff
path: root/vis.c
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2014-09-01 22:01:35 +0200
committerMarc André Tanner <mat@brain-dump.org>2014-09-01 22:01:35 +0200
commit28db11a2c10894d0154786646234b060bc9a6202 (patch)
treeb7c48ca473a287cb9e426ce337dd9c4e9aed1156 /vis.c
parentc72fe77e5d3cf4ecb3b691a5fa582d0e96b5ec1e (diff)
downloadvis-28db11a2c10894d0154786646234b060bc9a6202.tar.gz
vis-28db11a2c10894d0154786646234b060bc9a6202.tar.xz
Refactor frontend code
window.[ch] now contains a somewhat generic editor window which is then enhanced in vis.[ch] with a statusbar.
Diffstat (limited to 'vis.c')
-rw-r--r--vis.c516
1 files changed, 341 insertions, 175 deletions
diff --git a/vis.c b/vis.c
index 00b6582..e83908a 100644
--- a/vis.c
+++ b/vis.c
@@ -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);
}