aboutsummaryrefslogtreecommitdiff
path: root/editor.c
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2014-09-09 21:21:30 +0200
committerMarc André Tanner <mat@brain-dump.org>2014-09-09 21:21:30 +0200
commit78e0601e4ee68177ef597e75d08da786b0cabd3d (patch)
tree51243a7e083e7d17d825ca50c448327d9cbf547b /editor.c
parent98646851ade5386e3f72bfda4d94db20b65fde78 (diff)
downloadvis-78e0601e4ee68177ef597e75d08da786b0cabd3d.tar.gz
vis-78e0601e4ee68177ef597e75d08da786b0cabd3d.tar.xz
Rename vis.[ch] to editor.[ch] and main.c to vis.c
Diffstat (limited to 'editor.c')
-rw-r--r--editor.c483
1 files changed, 483 insertions, 0 deletions
diff --git a/editor.c b/editor.c
new file mode 100644
index 0000000..9af00d4
--- /dev/null
+++ b/editor.c
@@ -0,0 +1,483 @@
+#define _BSD_SOURCE
+#include <stdlib.h>
+#include <string.h>
+#include "editor.h"
+#include "util.h"
+
+static EditorWin *editor_window_new_text(Editor *ed, Text *text);
+static void editor_window_free(Editor *ed, EditorWin *win);
+static void editor_window_split_internal(Editor *ed, const char *filename);
+static void editor_windows_invalidate(Editor *ed, size_t start, size_t end);
+static void editor_window_draw(EditorWin *win);
+static void editor_windows_arrange_horizontal(Editor *ed);
+static void editor_windows_arrange_vertical(Editor *ed);
+
+static Prompt *editor_prompt_new();
+static void editor_prompt_free(Prompt *prompt);
+static void editor_prompt_clear(Prompt *prompt);
+static void editor_prompt_resize(Prompt *prompt, int width, int height);
+static void editor_prompt_move(Prompt *prompt, int x, int y);
+static void editor_prompt_draw(Prompt *prompt);
+static void editor_prompt_update(Prompt *prompt);
+
+static void editor_window_resize(EditorWin *win, int width, int height) {
+ window_resize(win->win, width, win->statuswin ? height - 1 : height);
+ if (win->statuswin)
+ wresize(win->statuswin, 1, width);
+ win->width = width;
+ win->height = height;
+}
+
+static void editor_window_move(EditorWin *win, int x, int y) {
+ window_move(win->win, x, y);
+ if (win->statuswin)
+ mvwin(win->statuswin, y + win->height - 1, x);
+}
+
+static void editor_window_statusbar_draw(EditorWin *win) {
+ size_t line, col;
+ if (win->statuswin && win->editor->statusbar) {
+ window_cursor_getxy(win->win, &line, &col);
+ win->editor->statusbar(win->statuswin, win->editor->win == win,
+ text_filename(win->text), line, col);
+ }
+}
+
+static void editor_window_cursor_moved(Win *win, void *data) {
+ editor_window_statusbar_draw(data);
+}
+
+void editor_statusbar_set(Editor *ed, editor_statusbar_t statusbar) {
+ ed->statusbar = statusbar;
+}
+
+static void editor_windows_arrange_horizontal(Editor *ed) {
+ int n = 0, x = 0, y = 0;
+ for (EditorWin *win = ed->windows; win; win = win->next)
+ n++;
+ int height = ed->height / n;
+ for (EditorWin *win = ed->windows; win; win = win->next) {
+ editor_window_resize(win, ed->width, win->next ? height : ed->height - y);
+ editor_window_move(win, x, y);
+ y += height;
+ }
+}
+
+static void editor_windows_arrange_vertical(Editor *ed) {
+ int n = 0, x = 0, y = 0;
+ for (EditorWin *win = ed->windows; win; win = win->next)
+ n++;
+ int width = ed->width / n - 1;
+ for (EditorWin *win = ed->windows; win; win = win->next) {
+ editor_window_resize(win, win->next ? width : ed->width - x, ed->height);
+ editor_window_move(win, x, y);
+ x += width;
+ if (win->next)
+ mvvline(0, x++, ACS_VLINE, ed->height);
+ }
+}
+
+static void editor_window_split_internal(Editor *ed, const char *filename) {
+ EditorWin *sel = ed->win;
+ if (filename) {
+ // TODO? move this to editor_window_new
+ sel = NULL;
+ for (EditorWin *w = ed->windows; w; w = w->next) {
+ const char *f = text_filename(w->text);
+ if (f && strcmp(f, filename) == 0) {
+ sel = w;
+ break;
+ }
+ }
+ }
+ if (sel) {
+ EditorWin *win = editor_window_new_text(ed, 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));
+ } else {
+ editor_window_new(ed, filename);
+ }
+}
+
+void editor_window_split(Editor *ed, const char *filename) {
+ editor_window_split_internal(ed, filename);
+ ed->windows_arrange = editor_windows_arrange_horizontal;
+ editor_draw(ed);
+}
+
+void editor_window_vsplit(Editor *ed, const char *filename) {
+ editor_window_split_internal(ed, filename);
+ ed->windows_arrange = editor_windows_arrange_vertical;
+ editor_draw(ed);
+}
+
+void editor_resize(Editor *ed, int width, int height) {
+ ed->width = width;
+ ed->height = height;
+ if (ed->prompt->active) {
+ ed->height--;
+ editor_prompt_resize(ed->prompt, ed->width, 1);
+ editor_prompt_move(ed->prompt, 0, ed->height);
+ editor_prompt_draw(ed->prompt);
+ }
+ editor_draw(ed);
+}
+
+void editor_window_next(Editor *ed) {
+ EditorWin *sel = ed->win;
+ if (!sel)
+ return;
+ ed->win = ed->win->next;
+ if (!ed->win)
+ ed->win = ed->windows;
+ editor_window_statusbar_draw(sel);
+ editor_window_statusbar_draw(ed->win);
+}
+
+void editor_window_prev(Editor *ed) {
+ EditorWin *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);
+ editor_window_statusbar_draw(sel);
+ editor_window_statusbar_draw(ed->win);
+}
+
+static void editor_windows_invalidate(Editor *ed, size_t start, size_t end) {
+ for (EditorWin *win = ed->windows; win; win = win->next) {
+ if (ed->win != win && ed->win->text == win->text) {
+ Filerange view = window_viewport_get(win->win);
+ if ((view.start <= start && start <= view.end) ||
+ (view.start <= end && end <= view.end))
+ editor_window_draw(win);
+ }
+ }
+ editor_window_draw(ed->win);
+}
+
+
+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_window_draw(EditorWin *win) {
+ // TODO window_draw draw should restore cursor position
+ window_draw(win->win);
+ window_cursor_to(win->win, window_cursor_get(win->win));
+}
+
+void editor_draw(Editor *ed) {
+ erase();
+ ed->windows_arrange(ed);
+ for (EditorWin *win = ed->windows; win; win = win->next) {
+ if (ed->win != win)
+ editor_window_draw(win);
+ }
+ editor_window_draw(ed->win);
+ wnoutrefresh(stdscr);
+}
+
+void editor_update(Editor *ed) {
+ for (EditorWin *win = ed->windows; win; win = win->next) {
+ if (ed->win != win) {
+ if (win->statuswin)
+ wnoutrefresh(win->statuswin);
+ window_update(win->win);
+ }
+ }
+
+ if (ed->win->statuswin)
+ wnoutrefresh(ed->win->statuswin);
+ if (ed->prompt && ed->prompt->active)
+ editor_prompt_update(ed->prompt);
+ window_update(ed->win->win);
+}
+
+static void editor_window_free(Editor *ed, EditorWin *win) {
+ if (!win)
+ return;
+ window_free(win->win);
+ if (win->statuswin)
+ delwin(win->statuswin);
+ bool needed = false;
+ for (EditorWin *w = ed ? ed->windows : NULL; w; w = w->next) {
+ if (w->text == win->text) {
+ needed = true;
+ break;
+ }
+ }
+ if (!needed)
+ text_free(win->text);
+ free(win);
+}
+
+static EditorWin *editor_window_new_text(Editor *ed, Text *text) {
+ EditorWin *win = calloc(1, sizeof(EditorWin));
+ if (!win)
+ return NULL;
+ win->editor = ed;
+ win->text = text;
+ win->win = window_new(win->text);
+ win->statuswin = newwin(1, ed->width, 0, 0);
+ if (!win->win || !win->statuswin) {
+ editor_window_free(ed, win);
+ return NULL;
+ }
+ window_cursor_watch(win->win, editor_window_cursor_moved, win);
+ if (ed->windows)
+ ed->windows->prev = win;
+ win->next = ed->windows;
+ win->prev = NULL;
+ ed->windows = win;
+ ed->win = win;
+ return win;
+}
+
+bool editor_window_new(Editor *ed, const char *filename) {
+ Text *text = text_load(filename);
+ if (!text)
+ return false;
+
+ EditorWin *win = editor_window_new_text(ed, text);
+ if (!win) {
+ text_free(text);
+ return false;
+ }
+
+ if (filename) {
+ for (Syntax *syn = ed->syntaxes; syn && syn->name; syn++) {
+ if (!regexec(&syn->file_regex, filename, 0, NULL, 0)) {
+ window_syntax_set(win->win, syn);
+ break;
+ }
+ }
+ }
+
+ editor_draw(ed);
+
+ return true;
+}
+
+static void editor_window_detach(Editor *ed, EditorWin *win) {
+ if (win->prev)
+ win->prev->next = win->next;
+ if (win->next)
+ win->next->prev = win->prev;
+ if (ed->windows == win)
+ ed->windows = win->next;
+ win->next = win->prev = NULL;
+}
+
+void editor_window_close(Editor *ed) {
+ EditorWin *win = ed->win;
+ ed->win = win->next ? win->next : win->prev;
+ editor_window_detach(ed, win);
+ editor_window_free(ed, win);
+}
+
+Editor *editor_new(int width, int height) {
+ Editor *ed = calloc(1, sizeof(Editor));
+ if (!ed)
+ return NULL;
+ if (!(ed->prompt = editor_prompt_new()))
+ goto err;
+ if (!(ed->search_pattern = text_regex_new()))
+ goto err;
+ ed->width = width;
+ ed->height = height;
+ ed->windows_arrange = editor_windows_arrange_horizontal;
+ ed->running = true;
+ return ed;
+err:
+ editor_free(ed);
+ return NULL;
+}
+
+void editor_free(Editor *ed) {
+ while (ed->windows)
+ editor_window_close(ed);
+ editor_prompt_free(ed->prompt);
+ text_regex_free(ed->search_pattern);
+ for (int i = 0; i < REG_LAST; i++)
+ register_free(&ed->registers[i]);
+ editor_syntax_unload(ed);
+ free(ed);
+}
+
+void editor_insert_key(Editor *ed, const char *c, size_t len) {
+ Win *win = ed->win->win;
+ size_t start = window_cursor_get(win);
+ window_insert_key(win, c, len);
+ editor_windows_invalidate(ed, start, start + len);
+}
+
+void editor_replace_key(Editor *ed, const char *c, size_t len) {
+ Win *win = ed->win->win;
+ size_t start = window_cursor_get(win);
+ window_replace_key(win, c, len);
+ editor_windows_invalidate(ed, start, start + 6);
+}
+
+void editor_backspace_key(Editor *ed) {
+ Win *win = ed->win->win;
+ size_t end = window_cursor_get(win);
+ size_t start = window_backspace_key(win);
+ editor_windows_invalidate(ed, start, end);
+}
+
+void editor_delete_key(Editor *ed) {
+ size_t start = window_delete_key(ed->win->win);
+ editor_windows_invalidate(ed, start, start + 6);
+}
+
+void editor_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, pos + len);
+}
+
+void editor_delete(Editor *ed, size_t pos, size_t len) {
+ text_delete(ed->win->text, pos, len);
+ editor_windows_invalidate(ed, pos, pos + len);
+}
+
+
+static void editor_prompt_free(Prompt *prompt) {
+ if (!prompt)
+ return;
+ editor_window_free(NULL, prompt->win);
+ if (prompt->titlewin)
+ delwin(prompt->titlewin);
+ free(prompt->title);
+ free(prompt);
+}
+
+static Prompt *editor_prompt_new() {
+ Text *text = text_load(NULL);
+ if (!text)
+ return NULL;
+ Prompt *prompt = calloc(1, sizeof(Prompt));
+ if (!prompt)
+ goto err;
+
+ if (!(prompt->win = calloc(1, sizeof(EditorWin))))
+ goto err;
+
+ if (!(prompt->win->win = window_new(text)))
+ goto err;
+
+ prompt->win->text = text;
+
+ if (!(prompt->titlewin = newwin(0, 0, 0, 0)))
+ goto err;
+
+ return prompt;
+err:
+ if (!prompt || !prompt->win)
+ text_free(text);
+ editor_prompt_free(prompt);
+ return NULL;
+}
+
+static void editor_prompt_resize(Prompt *prompt, int width, int height) {
+ size_t title_width = strlen(prompt->title);
+ wresize(prompt->titlewin, height, title_width);
+ editor_window_resize(prompt->win, width - title_width, height);
+}
+
+static void editor_prompt_move(Prompt *prompt, int x, int y) {
+ size_t title_width = strlen(prompt->title);
+ mvwin(prompt->titlewin, y, x);
+ editor_window_move(prompt->win, x + title_width, y);
+}
+
+void editor_prompt_show(Editor *ed, const char *title) {
+ Prompt *prompt = ed->prompt;
+ if (prompt->active)
+ return;
+ prompt->active = true;
+ prompt->editor = ed->win;
+ ed->win = prompt->win;
+ free(prompt->title);
+ prompt->title = strdup(title);
+ editor_resize(ed, ed->width, ed->height);
+}
+
+static void editor_prompt_draw(Prompt *prompt) {
+ mvwaddstr(prompt->titlewin, 0, 0, prompt->title);
+}
+
+static void editor_prompt_update(Prompt *prompt) {
+ wnoutrefresh(prompt->titlewin);
+}
+
+static void editor_prompt_clear(Prompt *prompt) {
+ Text *text = prompt->win->text;
+ while (text_undo(text));
+}
+
+void editor_prompt_hide(Editor *ed) {
+ Prompt *prompt = ed->prompt;
+ if (!prompt->active)
+ return;
+ prompt->active = false;
+ ed->win = prompt->editor;
+ prompt->editor = NULL;
+ ed->height++;
+ editor_prompt_clear(prompt);
+ editor_draw(ed);
+}
+
+void editor_prompt_set(Editor *ed, const char *line) {
+ Text *text = ed->prompt->win->text;
+ editor_prompt_clear(ed->prompt);
+ text_insert_raw(text, 0, line, strlen(line));
+ editor_window_draw(ed->prompt->win);
+}
+
+char *editor_prompt_get(Editor *ed) {
+ Text *text = ed->prompt->win->text;
+ char *buf = malloc(text_size(text) + 1);
+ if (!buf)
+ return NULL;
+ size_t len = text_bytes_get(text, 0, text_size(text), buf);
+ buf[len] = '\0';
+ return buf;
+}