aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--README7
-rw-r--r--buffer.c54
-rw-r--r--buffer.h20
-rw-r--r--config.def.h11
-rw-r--r--editor.h3
-rw-r--r--macro.h11
-rw-r--r--register.c30
-rw-r--r--register.h3
-rw-r--r--vis.c98
10 files changed, 179 insertions, 62 deletions
diff --git a/Makefile b/Makefile
index 92c7a81..3599eb2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
include config.mk
-SRC = editor.c window.c text.c text-motions.c text-objects.c register.c
-HDR := ${SRC:.c=.h} syntax.h util.h config.def.h
+SRC = editor.c window.c text.c text-motions.c text-objects.c register.c buffer.c
+HDR := ${SRC:.c=.h} macro.h syntax.h util.h config.def.h
SRC += vis.c
OBJ = ${SRC:.c=.o}
ALL = ${SRC} ${HDR} config.mk Makefile LICENSE README vis.1
diff --git a/README b/README
index a447662..75808bf 100644
--- a/README
+++ b/README
@@ -414,6 +414,12 @@ and their current support in vis.
again). The same restriction also applies to commands which are not
implemented in terms of operators, such as 'o', 'O', 'J' etc.
+ Macros
+ ------
+
+ [a-z] are recoginized macro names, q starts a recording, @ plays it back.
+ @@ refers to the least recently recorded macro.
+
Command line prompt
-------------------
@@ -496,7 +502,6 @@ and their current support in vis.
- right-to-left text
- tabs (as in multiple workspaces)
- ex mode
- - macro recording
How to help?
------------
diff --git a/buffer.c b/buffer.c
new file mode 100644
index 0000000..9722101
--- /dev/null
+++ b/buffer.c
@@ -0,0 +1,54 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "buffer.h"
+#include "util.h"
+
+#define BUF_SIZE 1024
+
+bool buffer_alloc(Buffer *buf, size_t size) {
+ if (size < BUF_SIZE)
+ size = BUF_SIZE;
+ if (buf->size < size) {
+ if (buf->size > 0)
+ size *= 2;
+ buf->data = realloc(buf->data, size);
+ if (!buf->data) {
+ buf->size = 0;
+ buf->len = 0;
+ return false;
+ }
+ buf->size = size;
+ }
+ return true;
+}
+
+void buffer_truncate(Buffer *buf) {
+ buf->len = 0;
+}
+
+void buffer_free(Buffer *buf) {
+ if (!buf)
+ return;
+ free(buf->data);
+ buf->data = NULL;
+ buf->len = 0;
+ buf->size = 0;
+}
+
+bool buffer_put(Buffer *buf, void *data, size_t len) {
+ if (!buffer_alloc(buf, len))
+ return false;
+ memcpy(buf->data, data, len);
+ buf->len = len;
+ return true;
+}
+
+bool buffer_append(Buffer *buf, void *data, size_t len) {
+ size_t rem = buf->size - buf->len;
+ if (len > rem && !buffer_alloc(buf, buf->size + len - rem))
+ return false;
+ memcpy(buf->data + buf->len, data, len);
+ buf->len += len;
+ return true;
+}
diff --git a/buffer.h b/buffer.h
new file mode 100644
index 0000000..190f579
--- /dev/null
+++ b/buffer.h
@@ -0,0 +1,20 @@
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include <stddef.h>
+#include <stdbool.h>
+#include "text.h"
+
+typedef struct {
+ char *data; /* NULL if empty */
+ size_t len; /* current length of data */
+ size_t size; /* maximal capacity of the buffer */
+} Buffer;
+
+void buffer_free(Buffer *buf);
+bool buffer_alloc(Buffer *buf, size_t size);
+void buffer_truncate(Buffer *buf);
+bool buffer_put(Buffer *buf, void *data, size_t len);
+bool buffer_append(Buffer *buf, void *data, size_t len);
+
+#endif
diff --git a/config.def.h b/config.def.h
index f41dd81..16fd674 100644
--- a/config.def.h
+++ b/config.def.h
@@ -73,10 +73,11 @@ static void statusbar(EditorWin *win) {
window_cursor_getxy(win->win, &line, &col);
wattrset(win->statuswin, focused ? A_REVERSE|A_BOLD : A_REVERSE);
mvwhline(win->statuswin, 0, 0, ' ', win->width);
- mvwprintw(win->statuswin, 0, 0, "%s %s %s",
+ mvwprintw(win->statuswin, 0, 0, "%s %s %s %s",
mode->name && mode->name[0] == '-' ? mode->name : "",
text_filename_get(win->text),
- text_modified(win->text) ? "[+]" : "");
+ text_modified(win->text) ? "[+]" : "",
+ vis->recording ? "recording": "");
char buf[win->width + 1];
int len = snprintf(buf, win->width, "%d, %d", line, col);
if (len > 0) {
@@ -86,9 +87,11 @@ static void statusbar(EditorWin *win) {
}
/* called before any other keybindings are checked, if the function returns false
- * the key is completely ignored. used to clear a user visible message. */
+ * the key is completely ignored. */
static bool vis_keypress(Key *key) {
editor_info_hide(vis);
+ if (vis->recording)
+ macro_append(vis->recording, key, sizeof(*key));
return true;
}
@@ -407,6 +410,8 @@ static KeyBinding vis_mode_normal[] = {
{ { NONE('z'), NONE('t') }, window, { .w = window_redraw_top } },
{ { NONE('z'), NONE('z') }, window, { .w = window_redraw_center } },
{ { NONE('z'), NONE('b') }, window, { .w = window_redraw_bottom } },
+ { { NONE('q') }, macro_record, { NULL } },
+ { { NONE('@') }, macro_replay, { NULL } },
{ /* empty last element, array terminator */ },
};
diff --git a/editor.h b/editor.h
index 718ca96..206d3ea 100644
--- a/editor.h
+++ b/editor.h
@@ -6,6 +6,7 @@
#include <stdbool.h>
#include "window.h"
#include "register.h"
+#include "macro.h"
#include "syntax.h"
typedef struct Editor Editor;
@@ -96,6 +97,8 @@ struct Editor {
EditorWin *win; /* currently active window */
Syntax *syntaxes; /* NULL terminated array of syntax definitions */
Register registers[REG_LAST]; /* register used for copy and paste */
+ Macro macros[26]; /* recorded macros */
+ Macro *recording, *last_recording;/* currently and least recently recorded macro */
Prompt *prompt; /* used to get user input */
char info[255]; /* a user message currently being displayed */
Regex *search_pattern; /* last used search pattern */
diff --git a/macro.h b/macro.h
new file mode 100644
index 0000000..c605cdf
--- /dev/null
+++ b/macro.h
@@ -0,0 +1,11 @@
+#ifndef MACRO_H
+#define MACRO_H
+
+#include "buffer.h"
+
+typedef Buffer Macro;
+#define macro_free buffer_free
+#define macro_reset buffer_truncate
+#define macro_append buffer_append
+
+#endif
diff --git a/register.c b/register.c
index db43d71..cc25414 100644
--- a/register.c
+++ b/register.c
@@ -2,37 +2,17 @@
#include <string.h>
#include "register.h"
+#include "buffer.h"
+#include "text.h"
#include "util.h"
-#define REG_SIZE 1024
-
-static bool register_alloc(Register *reg, size_t size) {
- if (size < REG_SIZE)
- size = REG_SIZE;
- if (reg->size < size) {
- reg->data = realloc(reg->data, size);
- if (!reg->data) {
- reg->size = 0;
- reg->len = 0;
- return false;
- }
- reg->size = size;
- }
- return true;
-}
-
void register_free(Register *reg) {
- if (!reg)
- return;
- free(reg->data);
- reg->data = NULL;
- reg->len = 0;
- reg->size = 0;
+ buffer_free((Buffer*)reg);
}
bool register_put(Register *reg, Text *txt, Filerange *range) {
size_t len = range->end - range->start;
- if (!register_alloc(reg, len))
+ if (!buffer_alloc((Buffer*)reg, len))
return false;
reg->len = text_bytes_get(txt, range->start, len, reg->data);
return true;
@@ -41,7 +21,7 @@ bool register_put(Register *reg, Text *txt, Filerange *range) {
bool register_append(Register *reg, Text *txt, Filerange *range) {
size_t rem = reg->size - reg->len;
size_t len = range->end - range->start;
- if (len > rem && !register_alloc(reg, reg->size + len - rem))
+ if (len > rem && !buffer_alloc((Buffer*)reg, reg->size + len - rem))
return false;
reg->len += text_bytes_get(txt, range->start, len, reg->data + reg->len);
return true;
diff --git a/register.h b/register.h
index 506b86a..02890e8 100644
--- a/register.h
+++ b/register.h
@@ -3,8 +3,9 @@
#include <stddef.h>
#include <stdbool.h>
-#include "text.h"
+#include "buffer.h"
+/* definition has to match Buffer */
typedef struct {
char *data; /* NULL if empty */
size_t len; /* current length of data */
diff --git a/vis.c b/vis.c
index 1d9f9e5..6015fcb 100644
--- a/vis.c
+++ b/vis.c
@@ -375,6 +375,8 @@ static TextObject *moves_linewise[] = {
};
/** functions to be called from keybindings */
+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 */
static void suspend(const Arg *arg);
/* switch to mode indicated by arg->i */
@@ -511,6 +513,7 @@ static void switchmode_to(Mode *new_mode);
#include "config.h"
static Key getkey(void);
+static void keypress(Key *key);
static void action_do(Action *a);
static bool exec_command(char type, char *cmdline);
@@ -778,6 +781,40 @@ static size_t window_lines_bottom(const Arg *arg) {
/** key bindings functions of type: void (*func)(const Arg*) */
+static Macro *key2macro(const Arg *arg) {
+ if (arg->i)
+ return &vis->macros[arg->i];
+ Key key = getkey();
+ if (key.str[0] >= 'a' && key.str[0] <= 'z')
+ return &vis->macros[key.str[0] - 'a'];
+ if (key.str[0] == '@')
+ return vis->last_recording;
+ return NULL;
+}
+
+static void macro_record(const Arg *arg) {
+ if (vis->recording) {
+ /* hack to remove last recorded key, otherwise upon replay
+ * we would start another recording */
+ vis->recording->len -= sizeof(Key);
+ vis->last_recording = vis->recording;
+ vis->recording = NULL;
+ } else {
+ vis->recording = key2macro(arg);
+ if (vis->recording)
+ macro_reset(vis->recording);
+ }
+ editor_draw(vis);
+}
+
+static void macro_replay(const Arg *arg) {
+ Macro *macro = key2macro(arg);
+ if (!macro || macro == vis->recording)
+ return;
+ for (size_t i = 0; i < macro->len; i += sizeof(Key))
+ keypress((Key*)(macro->data + i));
+}
+
static void suspend(const Arg *arg) {
endwin();
raise(SIGSTOP);
@@ -1691,6 +1728,36 @@ static KeyBinding *keybinding(Mode *mode, KeyCombo keys) {
return NULL;
}
+static void keypress(Key *key) {
+ static KeyCombo keys;
+ static int keylen;
+
+ if (config->keypress && !config->keypress(key))
+ return;
+
+ keys[keylen++] = *key;
+ KeyBinding *action = keybinding(mode, keys);
+
+ if (action) {
+ int combolen = 0;
+ while (combolen < MAX_KEYS && keyvalid(&action->key[combolen]))
+ combolen++;
+ if (keylen < combolen)
+ return; /* combo not yet complete */
+ /* need to reset state before calling action->func in case
+ * it will call us (=keypress) again as e.g. macro_replay */
+ keylen = 0;
+ memset(keys, 0, sizeof(keys));
+ if (action->func)
+ action->func(&action->arg);
+ } else if (keylen == 1 && key->code == 0 && mode->input) {
+ mode->input(key->str, strlen(key->str));
+ }
+
+ keylen = 0;
+ memset(keys, 0, sizeof(keys));
+}
+
static Key getkey(void) {
Key key = { .str = "\0\0\0\0\0\0", .code = 0 };
int keycode = getch(), len = 0;
@@ -1716,8 +1783,6 @@ static Key getkey(void) {
static void mainloop() {
struct timespec idle = { .tv_nsec = 0 }, *timeout = NULL;
- KeyCombo keys;
- int keylen = 0;
sigset_t emptyset, blockset;
sigemptyset(&emptyset);
sigemptyset(&blockset);
@@ -1754,35 +1819,8 @@ static void mainloop() {
}
Key key = getkey();
- if (config->keypress && !config->keypress(&key))
- continue;
+ keypress(&key);
- keys[keylen++] = key;
- KeyBinding *action = keybinding(mode, keys);
-
- if (action) {
- int combolen = 0;
- while (combolen < MAX_KEYS && keyvalid(&action->key[combolen]))
- combolen++;
- if (combolen == keylen) {
- if (action->func)
- action->func(&action->arg);
- keylen = 0;
- memset(keys, 0, sizeof(keys));
- }
- continue;
- } else {
- int oldkeylen = keylen;
- keylen = 0;
- memset(keys, 0, sizeof(keys));
- if (oldkeylen > 1)
- continue; /* cancel partial action */
- }
-
- if (key.code) /* ignore curses KEY_* */
- continue;
- if (mode->input)
- mode->input(key.str, strlen(key.str));
if (mode->idle)
timeout = &idle;
}