diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | README | 3 | ||||
| -rw-r--r-- | config.def.h | 2 | ||||
| -rw-r--r-- | editor.c | 40 | ||||
| -rw-r--r-- | editor.h | 7 | ||||
| -rw-r--r-- | ring-buffer.c | 85 | ||||
| -rw-r--r-- | ring-buffer.h | 21 | ||||
| -rw-r--r-- | vis.c | 48 |
8 files changed, 189 insertions, 19 deletions
@@ -1,6 +1,6 @@ include config.mk -SRC = editor.c window.c text.c text-motions.c text-objects.c register.c buffer.c +SRC = editor.c window.c text.c text-motions.c text-objects.c register.c buffer.c ring-buffer.c HDR := ${SRC:.c=.h} macro.h syntax.h util.h config.def.h SRC += vis.c OBJ = ${SRC:.c=.o} @@ -475,7 +475,8 @@ and their current support in vis. Jump list and change list ------------------------- - Neither the jump list nor the change lists are currently supported. + A per window, fixed size, file local jump list is implemented. + The change list is currently not supported. Mouse support ------------- diff --git a/config.def.h b/config.def.h index 2954ea9..3d5500f 100644 --- a/config.def.h +++ b/config.def.h @@ -382,6 +382,8 @@ static KeyBinding vis_mode_normal[] = { { { CONTROL('D') }, wscroll, { .i = +PAGE_HALF } }, { { CONTROL('E') }, wslide, { .i = -1 } }, { { CONTROL('Y') }, wslide, { .i = +1 } }, + { { CONTROL('O') }, jumplist, { .i = -1 } }, + { { CONTROL('I') }, jumplist, { .i = +1 } }, { { NONE('a') }, insertmode, { .i = MOVE_CHAR_NEXT } }, { { NONE('A') }, insertmode, { .i = MOVE_LINE_END } }, { { NONE('C') }, change, { .i = MOVE_LINE_END } }, @@ -136,6 +136,42 @@ bool editor_window_split(EditorWin *original) { return true; } +void editor_window_jumplist_add(EditorWin *win, size_t pos) { + Mark mark = text_mark_set(win->text, pos); + if (mark) + ringbuf_add(win->jumplist, mark); +} + +size_t editor_window_jumplist_prev(EditorWin *win) { + size_t cur = window_cursor_get(win->win); + for (;;) { + Mark mark = ringbuf_prev(win->jumplist); + if (!mark) + return cur; + size_t pos = text_mark_get(win->text, mark); + if (pos != EPOS && pos != cur) + return pos; + } + return cur; +} + +size_t editor_window_jumplist_next(EditorWin *win) { + size_t cur = window_cursor_get(win->win); + for (;;) { + Mark mark = ringbuf_next(win->jumplist); + if (!mark) + return cur; + size_t pos = text_mark_get(win->text, mark); + if (pos != EPOS && pos != cur) + return pos; + } + return cur; +} + +void editor_window_jumplist_invalidate(EditorWin *win) { + ringbuf_invalidate(win->jumplist); +} + void editor_resize(Editor *ed, int width, int height) { ed->width = width; ed->height = height; @@ -281,6 +317,7 @@ static void editor_window_free(Editor *ed, EditorWin *win) { window_free(win->win); if (win->statuswin) delwin(win->statuswin); + ringbuf_free(win->jumplist); bool needed = false; for (EditorWin *w = ed ? ed->windows : NULL; w; w = w->next) { if (w->text == win->text) { @@ -301,7 +338,8 @@ static EditorWin *editor_window_new_text(Editor *ed, Text *text) { win->text = text; win->win = window_new(win->text); win->statuswin = newwin(1, ed->width, 0, 0); - if (!win->win || !win->statuswin) { + win->jumplist = ringbuf_alloc(31); + if (!win->win || !win->statuswin || !win->jumplist) { editor_window_free(ed, win); return NULL; } @@ -8,6 +8,7 @@ #include "register.h" #include "macro.h" #include "syntax.h" +#include "ring-buffer.h" typedef struct Editor Editor; typedef struct EditorWin EditorWin; @@ -16,6 +17,7 @@ struct EditorWin { Editor *editor; /* editor instance to which this window belongs */ Text *text; /* underlying text management */ Win *win; /* window for the text area */ + RingBuffer *jumplist; /* LRU jump management */ WINDOW *statuswin; /* curses window for the statusbar */ int width, height; /* window size including the statusbar */ EditorWin *prev, *next; /* neighbouring windows */ @@ -150,6 +152,11 @@ bool editor_window_split(EditorWin*); /* focus the next / previous window */ void editor_window_next(Editor*); void editor_window_prev(Editor*); + +void editor_window_jumplist_add(EditorWin*, size_t pos); +size_t editor_window_jumplist_prev(EditorWin*); +size_t editor_window_jumplist_next(EditorWin*); +void editor_window_jumplist_invalidate(EditorWin*); /* rearrange all windows either vertically or horizontally */ void editor_windows_arrange_vertical(Editor*); void editor_windows_arrange_horizontal(Editor*); diff --git a/ring-buffer.c b/ring-buffer.c new file mode 100644 index 0000000..537fb90 --- /dev/null +++ b/ring-buffer.c @@ -0,0 +1,85 @@ +#include "ring-buffer.h" +#include <stdlib.h> + +struct RingBuffer { + int cur; /* index of current element, last added etc. */ + int start; /* index of first/oldest element */ + int end; /* index of reserved/empty slot */ + size_t size; /* buffer capacity / number of slots */ + bool iterating; /* whether we are in a sequence of prev/next calls */ + const void *data[]; /* user supplied buffer content */ +}; + +static int ringbuf_index_prev(RingBuffer *buf, int i) { + return (i-1+buf->size) % buf->size; +} + +static int ringbuf_index_next(RingBuffer *buf, int i) { + return (i+1) % buf->size; +} + +static bool ringbuf_isfull(RingBuffer *buf) { + return ringbuf_index_next(buf, buf->end) == buf->start; +} + +static bool ringbuf_isempty(RingBuffer *buf) { + return buf->start == buf->end; +} + +static bool ringbuf_isfirst(RingBuffer *buf) { + return buf->cur == buf->start; +} + +static bool ringbuf_islast(RingBuffer *buf) { + return ringbuf_index_next(buf, buf->cur) == buf->end; +} + +const void *ringbuf_prev(RingBuffer *buf) { + if (ringbuf_isempty(buf) || (ringbuf_isfirst(buf) && buf->iterating)) + return NULL; + if (buf->iterating) + buf->cur = ringbuf_index_prev(buf, buf->cur); + buf->iterating = true; + return buf->data[buf->cur]; +} + +const void *ringbuf_next(RingBuffer *buf) { + if (ringbuf_isempty(buf) || ringbuf_islast(buf)) + return NULL; + buf->cur = ringbuf_index_next(buf, buf->cur); + buf->iterating = true; + return buf->data[buf->cur]; +} + +void ringbuf_add(RingBuffer *buf, const void *value) { + if (ringbuf_isempty(buf)) { + buf->end = ringbuf_index_next(buf, buf->end); + } else if (!ringbuf_islast(buf)) { + buf->cur = ringbuf_index_next(buf, buf->cur); + buf->end = ringbuf_index_next(buf, buf->cur); + } else if (ringbuf_isfull(buf)) { + buf->start = ringbuf_index_next(buf, buf->start); + buf->cur = ringbuf_index_next(buf, buf->cur); + buf->end = ringbuf_index_next(buf, buf->end); + } else { + buf->cur = ringbuf_index_next(buf, buf->cur); + buf->end = ringbuf_index_next(buf, buf->end); + } + buf->data[buf->cur] = value; + buf->iterating = false; +} + +void ringbuf_invalidate(RingBuffer *buf) { + buf->iterating = false; +} + +RingBuffer *ringbuf_alloc(size_t size) { + RingBuffer *buf; + if ((buf = calloc(1, sizeof(*buf) + (++size)*sizeof(buf->data[0])))) + buf->size = size; + return buf; +} + +void ringbuf_free(RingBuffer *buf) { + free(buf); +} diff --git a/ring-buffer.h b/ring-buffer.h new file mode 100644 index 0000000..cfd70e0 --- /dev/null +++ b/ring-buffer.h @@ -0,0 +1,21 @@ +#ifndef RING_BUFFER_H +#define RING_BUFFER_H + +#include <stdbool.h> +#include <stddef.h> + +/* + * Circular buffer with functions for accessing elements in order. + * One slot always remains unused to distinguish between the empty/full case. + */ + +typedef struct RingBuffer RingBuffer; + +RingBuffer *ringbuf_alloc(size_t size); +void ringbuf_free(RingBuffer*); +void ringbuf_add(RingBuffer*, const void *value); +const void *ringbuf_prev(RingBuffer*); +const void *ringbuf_next(RingBuffer*); +void ringbuf_invalidate(RingBuffer*); + +#endif @@ -110,6 +110,7 @@ typedef struct { INCLUSIVE = 1 << 2, EXCLUSIVE = 1 << 3, IDEMPOTENT = 1 << 4, + JUMP = 1 << 5, } type; int count; } Movement; @@ -287,7 +288,7 @@ static Movement moves[] = { [MOVE_LINE_LASTCHAR] = { .txt = text_line_lastchar, .type = LINEWISE|INCLUSIVE }, [MOVE_LINE_END] = { .txt = text_line_end, .type = LINEWISE }, [MOVE_LINE_NEXT] = { .txt = text_line_next, .type = LINEWISE }, - [MOVE_LINE] = { .cmd = line, .type = LINEWISE|IDEMPOTENT}, + [MOVE_LINE] = { .cmd = line, .type = LINEWISE|IDEMPOTENT|JUMP}, [MOVE_COLUMN] = { .cmd = column, .type = CHARWISE|IDEMPOTENT}, [MOVE_CHAR_PREV] = { .win = window_char_prev }, [MOVE_CHAR_NEXT] = { .win = window_char_next }, @@ -301,24 +302,24 @@ static Movement moves[] = { [MOVE_LONGWORD_END_NEXT] = { .txt = text_longword_end_next, .type = CHARWISE|INCLUSIVE }, [MOVE_SENTENCE_PREV] = { .txt = text_sentence_prev, .type = LINEWISE }, [MOVE_SENTENCE_NEXT] = { .txt = text_sentence_next, .type = LINEWISE }, - [MOVE_PARAGRAPH_PREV] = { .txt = text_paragraph_prev, .type = LINEWISE }, - [MOVE_PARAGRAPH_NEXT] = { .txt = text_paragraph_next, .type = LINEWISE }, - [MOVE_BRACKET_MATCH] = { .txt = text_bracket_match, .type = LINEWISE|INCLUSIVE }, - [MOVE_FILE_BEGIN] = { .txt = text_begin, .type = LINEWISE }, - [MOVE_FILE_END] = { .txt = text_end, .type = LINEWISE }, + [MOVE_PARAGRAPH_PREV] = { .txt = text_paragraph_prev, .type = LINEWISE|JUMP }, + [MOVE_PARAGRAPH_NEXT] = { .txt = text_paragraph_next, .type = LINEWISE|JUMP }, + [MOVE_BRACKET_MATCH] = { .txt = text_bracket_match, .type = LINEWISE|INCLUSIVE|JUMP }, + [MOVE_FILE_BEGIN] = { .txt = text_begin, .type = LINEWISE|JUMP }, + [MOVE_FILE_END] = { .txt = text_end, .type = LINEWISE|JUMP }, [MOVE_LEFT_TO] = { .cmd = to_left, .type = LINEWISE }, [MOVE_RIGHT_TO] = { .cmd = to, .type = LINEWISE|INCLUSIVE }, [MOVE_LEFT_TILL] = { .cmd = till_left, .type = LINEWISE }, [MOVE_RIGHT_TILL] = { .cmd = till, .type = LINEWISE|INCLUSIVE }, - [MOVE_MARK] = { .cmd = mark_goto, .type = LINEWISE }, - [MOVE_MARK_LINE] = { .cmd = mark_line_goto, .type = LINEWISE }, - [MOVE_SEARCH_WORD_FORWARD] = { .cmd = search_word_forward, .type = LINEWISE }, - [MOVE_SEARCH_WORD_BACKWARD]= { .cmd = search_word_backward, .type = LINEWISE }, - [MOVE_SEARCH_FORWARD] = { .cmd = search_forward, .type = LINEWISE }, - [MOVE_SEARCH_BACKWARD] = { .cmd = search_backward, .type = LINEWISE }, - [MOVE_WINDOW_LINE_TOP] = { .cmd = window_lines_top, .type = LINEWISE }, - [MOVE_WINDOW_LINE_MIDDLE] = { .cmd = window_lines_middle, .type = LINEWISE }, - [MOVE_WINDOW_LINE_BOTTOM] = { .cmd = window_lines_bottom, .type = LINEWISE }, + [MOVE_MARK] = { .cmd = mark_goto, .type = LINEWISE|JUMP }, + [MOVE_MARK_LINE] = { .cmd = mark_line_goto, .type = LINEWISE|JUMP }, + [MOVE_SEARCH_WORD_FORWARD] = { .cmd = search_word_forward, .type = LINEWISE|JUMP }, + [MOVE_SEARCH_WORD_BACKWARD]= { .cmd = search_word_backward, .type = LINEWISE|JUMP }, + [MOVE_SEARCH_FORWARD] = { .cmd = search_forward, .type = LINEWISE|JUMP }, + [MOVE_SEARCH_BACKWARD] = { .cmd = search_backward, .type = LINEWISE|JUMP }, + [MOVE_WINDOW_LINE_TOP] = { .cmd = window_lines_top, .type = LINEWISE|JUMP }, + [MOVE_WINDOW_LINE_MIDDLE] = { .cmd = window_lines_middle, .type = LINEWISE|JUMP }, + [MOVE_WINDOW_LINE_BOTTOM] = { .cmd = window_lines_bottom, .type = LINEWISE|JUMP }, }; /* these can be passed as int argument to textobj(&(const Arg){ .i = TEXT_OBJ_* }) */ @@ -379,6 +380,8 @@ static TextObject *moves_linewise[] = { }; /** functions to be called from keybindings */ +/* navigate jumplist either in forward (arg->i>0) or backward (arg->i<0) direction */ +static void jumplist(const Arg *arg); static void macro_record(const Arg *arg); static void macro_replay(const Arg *arg); /* temporarily suspend the editor and return to the shell, type 'fg' to get back */ @@ -775,6 +778,16 @@ static size_t window_lines_bottom(const Arg *arg) { /** key bindings functions of type: void (*func)(const Arg*) */ +static void jumplist(const Arg *arg) { + size_t pos; + if (arg->i > 0) + pos = editor_window_jumplist_next(vis->win); + else + pos = editor_window_jumplist_prev(vis->win); + if (pos != EPOS) + window_cursor_to(vis->win->win, pos); +} + static Macro *key2macro(const Arg *arg) { if (arg->i) return &vis->macros[arg->i]; @@ -1143,7 +1156,10 @@ static void action_do(Action *a) { window_scroll_to(win, pos); else window_cursor_to(win, pos); - + if (a->movement->type & JUMP) + editor_window_jumplist_add(vis->win, pos); + else + editor_window_jumplist_invalidate(vis->win); } else if (a->movement->type & INCLUSIVE) { Iterator it = text_iterator_get(txt, c.range.end); text_iterator_char_next(&it, NULL); |
