aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README3
-rw-r--r--config.def.h2
-rw-r--r--editor.c40
-rw-r--r--editor.h7
-rw-r--r--ring-buffer.c85
-rw-r--r--ring-buffer.h21
-rw-r--r--vis.c48
8 files changed, 189 insertions, 19 deletions
diff --git a/Makefile b/Makefile
index 3599eb2..7d65a57 100644
--- a/Makefile
+++ b/Makefile
@@ -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}
diff --git a/README b/README
index 108ff4d..2dc4718 100644
--- a/README
+++ b/README
@@ -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 } },
diff --git a/editor.c b/editor.c
index 1a2a494..a8c7c18 100644
--- a/editor.c
+++ b/editor.c
@@ -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;
}
diff --git a/editor.h b/editor.h
index 206d3ea..5dcb122 100644
--- a/editor.h
+++ b/editor.h
@@ -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
diff --git a/vis.c b/vis.c
index 6d267ed..a334792 100644
--- a/vis.c
+++ b/vis.c
@@ -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);