aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--colors.c12
-rw-r--r--config.def.h157
-rw-r--r--editor.h109
-rw-r--r--main.c234
-rw-r--r--vis.c516
-rw-r--r--vis.h170
-rw-r--r--window.c (renamed from editor.c)654
-rw-r--r--window.h47
9 files changed, 996 insertions, 910 deletions
diff --git a/Makefile b/Makefile
index b39cd7e..3884944 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/colors.c b/colors.c
index d1a0357..4a447f6 100644
--- a/colors.c
+++ b/colors.c
@@ -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
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..7d18285
--- /dev/null
+++ b/main.c
@@ -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;
+}
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);
}
diff --git a/vis.h b/vis.h
new file mode 100644
index 0000000..2acee21
--- /dev/null
+++ b/vis.h
@@ -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
diff --git a/editor.c b/window.c
index 85b366f..835236d 100644
--- a/editor.c
+++ b/window.c
@@ -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