commit 1b405e16faddabd36b26297c27c746aa7788a2d4
parent aadae71b088e1d224e1f6615524d0cc0d51eae7a
Author: lumidify <nobody@lumidify.org>
Date: Thu, 26 May 2022 21:53:19 +0200
Add support for config file
Yay, it's another monster commit!
Diffstat:
M | IDEAS | | | 5 | +++++ |
M | LICENSE | | | 4 | +++- |
M | Makefile | | | 33 | +++++++++++++++++++++------------ |
M | README | | | 26 | ++++++++++++++++++++++---- |
M | buffer.c | | | 23 | ++++++++++++++--------- |
M | buffer.h | | | 3 | +-- |
M | config.h | | | 9 | +++++++++ |
A | configparser.c | | | 1810 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | configparser.h | | | 80 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | keys.c | | | 14 | -------------- |
M | keys.h | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
M | keys_basic.c | | | 282 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
M | keys_basic.h | | | 5 | +++++ |
D | keys_basic_config.h | | | 457 | ------------------------------------------------------------------------------- |
M | keys_command.c | | | 362 | +++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------- |
M | keys_command.h | | | 8 | ++++++++ |
D | keys_command_config.h | | | 196 | ------------------------------------------------------------------------------- |
M | keys_config.h | | | 203 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
M | ledit.1 | | | 96 | +++++++++++++++---------------------------------------------------------------- |
M | ledit.c | | | 141 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------- |
A | leditrc.5 | | | 347 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | leditrc.example | | | 300 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | memory.c | | | 26 | ++++++++++++++++++++++---- |
M | memory.h | | | 6 | ++++++ |
D | theme.c | | | 57 | --------------------------------------------------------- |
D | theme.h | | | 39 | --------------------------------------- |
M | theme_config.h | | | 20 | +++++++++++++++++--- |
M | txtbuf.c | | | 59 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- |
M | txtbuf.h | | | 30 | ++++++++++++++++++++++++++++++ |
A | uglycrap.h | | | 15 | +++++++++++++++ |
M | undo.h | | | 2 | ++ |
M | util.c | | | 20 | ++++++++++++++++++++ |
M | util.h | | | 28 | ++++++++++++++++++++++++++++ |
M | view.c | | | 24 | +++++++++++++----------- |
M | view.h | | | 21 | ++++----------------- |
M | window.c | | | 33 | +++++++++++++++++++++------------ |
M | window.h | | | 10 | +++++++--- |
37 files changed, 3715 insertions(+), 1126 deletions(-)
diff --git a/IDEAS b/IDEAS
@@ -10,3 +10,8 @@
-> I'm not sure it that's even possible in a portable way, though,
since the keyboard layouts can be set in many different ways, so
the entire state would somehow have to be saved to restore it again.
+ -> Wouldn't it also make more sense to avoid the whole keyboard
+ configuration and instead just temporarily switch to the default
+ layout in order to map the keycodes to text? I'm not sure how to do
+ that, though. It might not be possible at all since text can also
+ be inserted through input methods.
diff --git a/LICENSE b/LICENSE
@@ -1,9 +1,11 @@
Note 1: Some stuff is stolen from st (https://st.suckless.org)
Note 2: Some stuff is stolen from OpenBSD (https://openbsd.org)
+Note 3: pango-compat.{c,h} contains a bit of code copied from
+ Pango in order to be compatible with older versions.
ISC License
-Copyright (c) 2022 lumidify <nobody@lumidify.org>
+Copyright (c) 2021, 2022 lumidify <nobody@lumidify.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
diff --git a/Makefile b/Makefile
@@ -8,20 +8,24 @@ MANPREFIX = ${PREFIX}/man
BIN = ${NAME}
MAN1 = ${BIN:=.1}
+MAN5 = leditrc.5
MISCFILES = Makefile README LICENSE IDEAS NOTES TODO
+DEBUG=0
+SANITIZE=0
+
OBJ = \
assert.o \
buffer.o \
view.o \
cache.o \
keys.o \
+ configparser.o \
keys_basic.o \
keys_command.o \
ledit.o \
memory.o \
search.o \
- theme.o \
txtbuf.o \
undo.o \
util.o \
@@ -38,11 +42,11 @@ HDR = \
cache.h \
common.h \
keys.h \
+ configparser.h \
keys_basic.h \
keys_command.h \
memory.h \
search.h \
- theme.h \
txtbuf.h \
undo.h \
util.h \
@@ -50,26 +54,27 @@ HDR = \
window.h \
cleanup.h \
macros.h \
- pango-compat.h
+ pango-compat.h \
+ uglycrap.h
CONFIGHDR = \
config.h \
theme_config.h \
- keys_basic_config.h \
- keys_command_config.h \
keys_config.h
-CFLAGS_LEDIT = ${CFLAGS} -g -Wall -Wextra -pedantic -D_POSIX_C_SOURCE=200809L `pkg-config --cflags x11 xkbfile pangoxft xext`
-LDFLAGS_LEDIT = ${LDFLAGS} `pkg-config --libs x11 xkbfile pangoxft xext` -lm
+EXTRA_CFLAGS_DEBUG0 = ${CFLAGS}
+EXTRA_LDFLAGS_DEBUG0 = ${LDFLAGS}
+EXTRA_CFLAGS_DEBUG1 = -DLEDIT_DEBUG -g
+EXTRA_FLAGS_SANITIZE1 = -fsanitize=address
+
+CFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_CFLAGS_DEBUG${DEBUG}} -Wall -Wextra -pedantic -D_POSIX_C_SOURCE=200809L -std=c99 `pkg-config --cflags x11 xkbfile pangoxft xext`
+LDFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_LDFLAGS_DEBUG${DEBUG}} `pkg-config --libs x11 xkbfile pangoxft xext` -lm
all: ${BIN}
# FIXME: make this nicer
ledit.o window.o : config.h
-theme.o : theme_config.h
-keys_basic.o : keys_basic_config.h keys_config.h
-keys_command.o : keys_command_config.h keys_config.h
-keys.o : keys_config.h
+configparser.o : keys_config.h theme_config.h
${OBJ} : ${HDR}
@@ -84,12 +89,16 @@ install: all
cp -f ${BIN} "${DESTDIR}${PREFIX}/bin"
for f in ${BIN}; do chmod 755 "${DESTDIR}${PREFIX}/bin/$$f"; done
mkdir -p "${DESTDIR}${MANPREFIX}/man1"
+ mkdir -p "${DESTDIR}${MANPREFIX}/man5"
cp -f ${MAN1} "${DESTDIR}${MANPREFIX}/man1"
+ cp -f ${MAN5} "${DESTDIR}${MANPREFIX}/man5"
for m in ${MAN1}; do chmod 644 "${DESTDIR}${MANPREFIX}/man1/$$m"; done
+ for m in ${MAN5}; do chmod 644 "${DESTDIR}${MANPREFIX}/man5/$$m"; done
uninstall:
for f in ${BIN}; do rm -f "${DESTDIR}${PREFIX}/bin/$$f"; done
for m in ${MAN1}; do rm -f "${DESTDIR}${MANPREFIX}/man1/$$m"; done
+ for m in ${MAN5}; do rm -f "${DESTDIR}${MANPREFIX}/man5/$$m"; done
clean:
rm -f ${BIN} ${OBJ}
@@ -97,7 +106,7 @@ clean:
dist:
rm -rf "${NAME}-${VERSION}"
mkdir -p "${NAME}-${VERSION}"
- cp -f ${MAN1} ${SRC} ${HDR} ${CONFIGHDR} ${MISCFILES} "${NAME}-${VERSION}"
+ cp -f ${MAN1} ${MAN5} ${SRC} ${HDR} ${CONFIGHDR} ${MISCFILES} "${NAME}-${VERSION}"
tar cf - "${NAME}-${VERSION}" | gzip -c > "${NAME}-${VERSION}.tar.gz"
rm -rf "${NAME}-${VERSION}"
diff --git a/README b/README
@@ -1,9 +1,6 @@
WARNING: This is work in progress! A lot of bugs still need to be fixed
before this can be used as a real text editor.
-Note: The compiler flags currently still include -g to add debug symbols.
-This will be removed when ledit is officially released (if I remember...).
-
ledit is a vi-like text editor for people who switch between keyboard
layouts frequently and/or work with languages that require complex text
layout.
@@ -18,10 +15,31 @@ On OpenBSD: pango
On MX Linux: libpango1.0-dev, libx11-dev, libxkbfile-dev
(this is just from memory, I need to test it with a fresh system sometime)
-The documentation can be viewed in ledit.1 or at the following locations:
+Installation:
+Simply run 'make' and 'make install'
+To compile with debugging symbols and some debug output, run 'make DEBUG=1'
+To compile with fsanitize=address, run 'make SANITIZE=1'
+
+A sample configuration file can be found in leditrc.example.
+Copy this to ~/.leditrc in order to use it.
+
+The documentation can be viewed in ledit.1 and leditrc.5 or at the following locations:
gopher://lumidify.org/0/doc/ledit/ledit-current.txt
gopher://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/0/doc/ledit/ledit-current.txt
http://lumidify.org/doc/ledit/ledit-current.html
http://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/doc/ledit/ledit-current.html
https://lumidify.org/doc/ledit/ledit-current.html
+
+gopher://lumidify.org/0/doc/ledit/leditrc-current.txt
+gopher://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/0/doc/ledit/leditrc-current.txt
+http://lumidify.org/doc/ledit/leditrc-current.html
+http://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/doc/ledit/leditrc-current.html
+https://lumidify.org/doc/ledit/leditrc-current.html
+
+Note that the documentation is far from finished! None of the functions are
+documented yet in leditrc.5 and ledit.1 is somewhat outdated. This will
+be fixed sometime...
+
+Also note that nothing is stable at the moment. In particular, some of the
+function names mentioned in leditrc.5 will probably be changed still.
diff --git a/buffer.c b/buffer.c
@@ -17,7 +17,6 @@
#include "util.h"
#include "undo.h"
#include "cache.h"
-#include "theme.h"
#include "memory.h"
#include "common.h"
#include "txtbuf.h"
@@ -284,10 +283,10 @@ buffer_set_hard_line_based(ledit_buffer *buffer, int hl) {
}
void
-buffer_add_view(ledit_buffer *buffer, ledit_theme *theme, ledit_mode mode, size_t line, size_t pos, long scroll_offset) {
+buffer_add_view(ledit_buffer *buffer, ledit_mode mode, size_t line, size_t pos, long scroll_offset) {
size_t new_num = add_sz(buffer->views_num, 1);
buffer->views = ledit_reallocarray(buffer->views, new_num, sizeof(ledit_view *));
- buffer->views[buffer->views_num] = view_create(buffer, theme, mode, line, pos);
+ buffer->views[buffer->views_num] = view_create(buffer, mode, line, pos);
set_view_hard_line_text(buffer, buffer->views[buffer->views_num]);
view_scroll(buffer->views[buffer->views_num], scroll_offset);
buffer->views_num = new_num;
@@ -374,11 +373,11 @@ buffer_load_file(ledit_buffer *buffer, char *filename, size_t line, char **errst
buffer->modified = 0;
return 0;
error:
- if (*errstr)
+ if (errstr)
*errstr = strerror(errno);
return 1;
errorclose:
- if (*errstr)
+ if (errstr)
*errstr = strerror(errno);
fclose(file);
return 1;
@@ -397,11 +396,11 @@ buffer_write_to_file(ledit_buffer *buffer, FILE *file, char **errstr) {
buffer->modified = 0;
return 0;
error:
- if (*errstr)
+ if (errstr)
*errstr = strerror(errno);
return 1;
errorclose:
- if (*errstr)
+ if (errstr)
*errstr = strerror(errno);
fclose(file);
return 1;
@@ -413,7 +412,7 @@ buffer_write_to_fd(ledit_buffer *buffer, int fd, char **errstr) {
if (!file) goto error;
return buffer_write_to_file(buffer, file, errstr);
error:
- if (*errstr)
+ if (errstr)
*errstr = strerror(errno);
/* catching errors on the close wouldn't
really make much sense anymore */
@@ -428,7 +427,7 @@ buffer_write_to_filename(ledit_buffer *buffer, char *filename, char **errstr) {
if (!file) goto error;
return buffer_write_to_file(buffer, file, errstr);
error:
- if (*errstr)
+ if (errstr)
*errstr = strerror(errno);
return 1;
}
@@ -445,6 +444,10 @@ buffer_destroy(ledit_buffer *buffer) {
if (buffer->filename)
free(buffer->filename);
marklist_destroy(buffer->marklist);
+ for (size_t i = 0; i < buffer->views_num; i++) {
+ view_destroy(buffer->views[i]);
+ }
+ free(buffer->views);
free(buffer);
}
@@ -854,6 +857,7 @@ line_byte_to_char(ledit_line *line, size_t byte) {
static void
buffer_delete_line_section_base(ledit_buffer *buffer, size_t line, size_t start, size_t length) {
ledit_line *l = buffer_get_line(buffer, line);
+ /* FIXME: somehow make sure this doesn't get optimized out? */
(void)add_sz(start, length); /* just check that no overflow */
ledit_assert(start + length <= l->len);
if (start <= l->gap && start + length >= l->gap) {
@@ -979,6 +983,7 @@ buffer_delete_with_undo_base(
size_t line_index2, size_t byte_index2,
txtbuf *text_ret) {
/* FIXME: global txtbuf to avoid allocating each time */
+ /* actually, could just use stack variable here */
txtbuf *buf = text_ret != NULL ? text_ret : txtbuf_new();
sort_range(&line_index1, &byte_index1, &line_index2, &byte_index2);
delete_range_base(
diff --git a/buffer.h b/buffer.h
@@ -79,8 +79,7 @@ void buffer_set_hard_line_based(ledit_buffer *buffer, int hl);
* 'scroll_offset' is the initial pixel scroll offset.
*/
void buffer_add_view(
- ledit_buffer *buffer, ledit_theme *theme,
- ledit_mode mode, size_t line, size_t pos, long scroll_offset
+ ledit_buffer *buffer, ledit_mode mode, size_t line, size_t pos, long scroll_offset
);
/*
diff --git a/config.h b/config.h
@@ -1,3 +1,10 @@
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+/*************************
+ * General configuration *
+ *************************/
+
/* Note: These have to be less than one second */
/*
@@ -17,3 +24,5 @@
* events - events inbetween are discarded (nanoseconds)
*/
#define RESIZE_TICK (long long)200000000
+
+#endif /* _CONFIG_H_ */
diff --git a/configparser.c b/configparser.c
@@ -0,0 +1,1810 @@
+#ifdef LEDIT_DEBUG
+#include <time.h>
+#include "macros.h"
+#endif
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "util.h"
+#include "memory.h"
+#include "assert.h"
+#include "configparser.h"
+#include "theme_config.h"
+#include "keys_config.h"
+
+/* FIXME: Replace this entire parser with something sensible.
+ The current handwritten parser is mainly for the lulz. */
+
+/* FIXME: standardize error messages */
+/* FIXME: it isn't entirely correct to give size_t as length for
+ string in print_fmt (supposed to be int) */
+
+struct config {
+ ledit_theme *theme;
+ basic_key_array *basic_keys;
+ command_key_array *command_keys;
+ command_array *cmds;
+ char **langs;
+ size_t num_langs;
+ size_t alloc_langs;
+} config = {NULL};
+
+enum toktype {
+ STRING,
+ LBRACE,
+ RBRACE,
+ EQUALS,
+ NEWLINE,
+ ERROR,
+ END
+};
+
+static const char *
+toktype_str(enum toktype type) {
+ switch (type) {
+ case STRING:
+ return "string";
+ break;
+ case LBRACE:
+ return "left brace";
+ break;
+ case RBRACE:
+ return "right brace";
+ break;
+ case EQUALS:
+ return "equals";
+ break;
+ case NEWLINE:
+ return "newline";
+ break;
+ case ERROR:
+ return "error";
+ break;
+ case END:
+ return "end of file";
+ break;
+ default:
+ return "unknown";
+ }
+}
+
+struct token {
+ char *text;
+ size_t len;
+ enum toktype type;
+ size_t line; /* line in original input */
+ size_t line_offset; /* offset from start of line */
+};
+
+struct lexstate {
+ char *text;
+ size_t len; /* length of text */
+ size_t cur; /* current byte position */
+ size_t cur_line; /* current line */
+ size_t line_start; /* byte offset of start of current line */
+};
+
+static struct token
+next_token(struct lexstate *s) {
+ char c;
+ struct token tok;
+ while (1) {
+ if (s->cur >= s->len)
+ return (struct token){NULL, 0, END, s->cur_line, s->cur - s->line_start + 1};
+ while (isspace(c = s->text[s->cur])) {
+ s->cur++;
+ if (c == '\n') {
+ struct token tok = (struct token){s->text + s->cur - 1, 1, NEWLINE, s->cur_line, s->cur - s->line_start};
+ s->cur_line++;
+ s->line_start = s->cur;
+ return tok;
+ }
+ if (s->cur >= s->len)
+ return (struct token){NULL, 0, END, s->cur_line, s->cur - s->line_start + 1};
+ }
+
+ switch (s->text[s->cur]) {
+ case '#':
+ s->cur++;
+ while (s->cur < s->len && s->text[s->cur] != '\n')
+ s->cur++;
+ continue;
+ case '{':
+ tok = (struct token){s->text + s->cur, 1, LBRACE, s->cur_line, s->cur - s->line_start + 1};
+ s->cur++;
+ break;
+ case '}':
+ tok = (struct token){s->text + s->cur, 1, RBRACE, s->cur_line, s->cur - s->line_start + 1};
+ s->cur++;
+ break;
+ case '=':
+ tok = (struct token){s->text + s->cur, 1, EQUALS, s->cur_line, s->cur - s->line_start + 1};
+ s->cur++;
+ break;
+ case '"':
+ /* FIXME: error if next char is not whitespace or end */
+ s->cur++;
+ tok = (struct token){s->text + s->cur, 0, STRING, s->cur_line, s->cur - s->line_start + 1};
+ size_t shift = 0, bs = 0;
+ int finished = 0;
+ while (s->cur < s->len) {
+ char c = s->text[s->cur];
+ if (c == '\n') {
+ break;
+ } else if (c == '\\') {
+ shift += bs;
+ tok.len += bs;
+ bs = (bs + 1) % 2;
+ } else if (c == '"') {
+ if (bs) {
+ shift++;
+ tok.len++;
+ bs = 0;
+ } else {
+ s->cur++;
+ finished = 1;
+ break;
+ }
+ } else {
+ tok.len++;
+ }
+ s->text[s->cur - shift] = s->text[s->cur];
+ s->cur++;
+ }
+ if (!finished) {
+ tok.text = "Unfinished string";
+ tok.len = strlen("Unfinished string");
+ tok.type = ERROR;
+ }
+ break;
+ default:
+ tok = (struct token){s->text + s->cur, 1, STRING, s->cur_line, s->cur - s->line_start + 1};
+ s->cur++;
+ while (s->cur < s->len) {
+ char c = s->text[s->cur];
+ if (isspace(c) || c == '{' || c == '}' || c == '=') {
+ break;
+ } else if (c == '"') {
+ tok.text = "Unexpected start of string";
+ tok.len = strlen("Unexpected start of string");
+ tok.type = ERROR;
+ tok.line_offset = s->cur - s->line_start + 1;
+ }
+ tok.len++;
+ s->cur++;
+ }
+ }
+ return tok;
+ }
+}
+
+typedef struct ast_obj ast_obj;
+
+typedef struct {
+ ast_obj *objs;
+ size_t len, cap;
+} ast_list;
+
+typedef struct {
+ struct token tok;
+} ast_string;
+
+typedef struct {
+ struct token tok;
+ ast_obj *value;
+} ast_assignment;
+
+typedef struct {
+ struct token func_tok;
+ struct token *args;
+ size_t len, cap;
+} ast_statement;
+
+enum objtype {
+ OBJ_LIST,
+ OBJ_STRING,
+ OBJ_ASSIGNMENT,
+ OBJ_STATEMENT
+};
+
+struct ast_obj {
+ struct token tok;
+ union {
+ ast_list list;
+ ast_string str;
+ ast_assignment assignment;
+ ast_statement statement;
+ } obj;
+ enum objtype type;
+};
+
+/* Note: These functions only free everything inside the object
+ so they can be used with stack variables (or array elements)! */
+
+static void destroy_obj(ast_obj *obj);
+
+static void
+destroy_list(ast_list *list) {
+ if (!list)
+ return;
+ for (size_t i = 0; i < list->len; i++) {
+ destroy_obj(&list->objs[i]);
+ }
+ free(list->objs);
+ list->objs = NULL;
+ list->len = list->cap = 0;
+}
+
+static void
+destroy_obj(ast_obj *obj) {
+ if (!obj)
+ return;
+ switch (obj->type) {
+ case OBJ_LIST:
+ destroy_list(&obj->obj.list);
+ break;
+ case OBJ_ASSIGNMENT:
+ destroy_obj(obj->obj.assignment.value);
+ free(obj->obj.assignment.value);
+ obj->obj.assignment.value = NULL;
+ break;
+ case OBJ_STATEMENT:
+ free(obj->obj.statement.args);
+ obj->obj.statement.args = NULL;
+ obj->obj.statement.len = obj->obj.statement.cap = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+/* FIXME: overflow */
+static void
+list_append(ast_list *list, ast_obj o) {
+ list->cap = ideal_array_size(list->cap, add_sz(list->len, 1));
+ list->objs = ledit_reallocarray(list->objs, list->cap, sizeof(ast_obj));
+ list->objs[list->len++] = o;
+}
+
+static void
+statement_append(ast_statement *statement, struct token tok) {
+ statement->cap = ideal_array_size(statement->cap, add_sz(statement->len, 1));
+ statement->args = ledit_reallocarray(statement->args, statement->cap, sizeof(struct token));
+ statement->args[statement->len++] = tok;
+}
+
+/* FIXME: make this a bit nicer */
+/* Note: A lot of the ugliness is because of the
+ (failed) attempt to somewhat optimize everything */
+
+static int
+parse_list(struct lexstate *s, ast_list *ret, int implicit_end, char *filename, char **errstr) {
+ *ret = (ast_list){NULL, 0, 0};
+ struct token tok = next_token(s);
+ struct token tok2;
+ while (1) {
+ switch (tok.type) {
+ case STRING:
+ tok2 = next_token(s);
+ if (tok2.type == STRING) {
+ ast_statement statement = {tok, NULL, 0, 0};
+ /* FIXME: maybe allow lists in statements? */
+ while (tok2.type == STRING) {
+ statement_append(&statement, tok2);
+ tok2 = next_token(s);
+ }
+ list_append(ret, (ast_obj){.tok = tok, .obj = {.statement = statement}, .type = OBJ_STATEMENT});
+ tok = tok2;
+ } else if (tok2.type == EQUALS) {
+ ast_assignment assignment = {tok, NULL};
+ assignment.value = ledit_malloc(sizeof(ast_obj));
+ tok2 = next_token(s);
+ assignment.value->tok = tok2;
+ struct token orig_tok = tok;
+ if (tok2.type == STRING) {
+ assignment.value->obj.str = (ast_string){tok2};
+ assignment.value->type = OBJ_STRING;
+ tok = next_token(s);
+ if (tok.type == STRING) {
+ *errstr = print_fmt(
+ "%s: Invalid assignment at line %zu, offset %zu",
+ filename, tok.line, tok.line_offset
+ );
+ free(assignment.value);
+ goto error;
+ }
+ } else if (tok2.type == LBRACE) {
+ assignment.value->type = OBJ_LIST;
+ /* just in case */
+ assignment.value->obj.list = (ast_list){NULL, 0, 0};
+ if (parse_list(s, &assignment.value->obj.list, 0, filename, errstr)) {
+ free(assignment.value);
+ goto error;
+ }
+ tok = next_token(s);
+ if (tok.type == STRING) {
+ *errstr = print_fmt(
+ "%s: Invalid assignment at line %zu, offset %zu",
+ filename, tok.line, tok.line_offset
+ );
+ destroy_list(&assignment.value->obj.list);
+ free(assignment.value);
+ goto error;
+ }
+ } else {
+ *errstr = print_fmt(
+ "%s: Invalid assignment at line %zu, offset %zu",
+ filename, tok2.line, tok2.line_offset
+ );
+ free(assignment.value);
+ goto error;
+ }
+ list_append(ret, (ast_obj){.tok = orig_tok, .obj = {.assignment = assignment}, .type = OBJ_ASSIGNMENT});
+ } else {
+ *errstr = print_fmt(
+ "%s: Invalid token '%s' at line %zu, offset %zu",
+ filename, toktype_str(tok2.type), tok2.line, tok2.line_offset
+ );
+ goto error;
+ }
+ break;
+ case NEWLINE:
+ tok = next_token(s);
+ break;
+ case RBRACE:
+ if (implicit_end) {
+ *errstr = print_fmt(
+ "%s: Unexpected right brace at line %zu, offset %zu",
+ filename, tok.line, tok.line_offset
+ );
+ goto error;
+ } else {
+ return 0;
+ }
+ case END:
+ if (!implicit_end) {
+ *errstr = print_fmt(
+ "%s: Unexpected end of file at line %zu, offset %zu",
+ filename, tok.line, tok.line_offset
+ );
+ goto error;
+ } else {
+ return 0;
+ }
+ case LBRACE:
+ case EQUALS:
+ case ERROR:
+ default:
+ *errstr = print_fmt(
+ "%s: Unexpected token '%s' at line %zu, offset %zu",
+ filename, toktype_str(tok.type), tok.line, tok.line_offset
+ );
+ goto error;
+ }
+ }
+ return 0;
+error:
+ destroy_list(ret);
+ return 1;
+}
+
+static char *
+load_file(char *filename, size_t *len_ret, char **errstr) {
+ long len;
+ char *file_contents;
+ FILE *file;
+
+ /* FIXME: https://wiki.sei.cmu.edu/confluence/display/c/FIO19-C.+Do+not+use+fseek()+and+ftell()+to+compute+the+size+of+a+regular+file */
+ file = fopen(filename, "r");
+ if (!file) goto error;
+ if (fseek(file, 0, SEEK_END)) goto errorclose;
+ len = ftell(file);
+ if (len < 0) goto errorclose;
+ if (fseek(file, 0, SEEK_SET)) goto errorclose;
+ file_contents = ledit_malloc(add_sz((size_t)len, 1));
+ clearerr(file);
+ fread(file_contents, 1, (size_t)len, file);
+ if (ferror(file)) goto errorclose;
+ file_contents[len] = '\0';
+ if (fclose(file)) goto error;
+ *len_ret = (size_t)len;
+ return file_contents;
+error:
+ if (errstr)
+ *errstr = strerror(errno);
+ return NULL;
+errorclose:
+ if (errstr)
+ *errstr = strerror(errno);
+ fclose(file);
+ return NULL;
+}
+
+/* FIXME: max recursion depth in parser */
+
+static int
+parse_theme_color(
+ ledit_common *common,
+ void *obj, const char *val, size_t val_len, char *key,
+ char *filename, size_t line, size_t line_offset, char **errstr) {
+ XftColor *dst = (XftColor *)obj;
+ char col[8]; /* 7 for '#' and 6 hex values + 1 for '\0' */
+ if (val_len == 7 && val[0] == '#') {
+ strncpy(col, val, val_len);
+ col[val_len] = '\0';
+ } else if (val_len == 6) {
+ col[0] = '#';
+ strncpy(col + 1, val, val_len);
+ col[val_len + 1] = '\0';
+ } else {
+ goto error;
+ }
+ /* FIXME: XftColorAllocValue */
+ if (!XftColorAllocName(common->dpy, common->vis, common->cm, col, dst))
+ goto error;
+ return 0;
+error:
+ *errstr = print_fmt(
+ "%s: Unable to parse color specification "
+ "'%.*s' for '%s' at line %zu, position %zu",
+ filename, val_len, val, key, line, line_offset
+ );
+ return 1;
+}
+
+static void
+destroy_theme_color(ledit_common *common, void *obj) {
+ XftColor *color = (XftColor *)obj;
+ XftColorFree(common->dpy, common->vis, common->cm, color);
+}
+
+/* based partially on OpenBSD's strtonum */
+static int
+parse_theme_number(
+ ledit_common *common,
+ void *obj, const char *val, size_t val_len, char *key,
+ char *filename, size_t line, size_t line_offset, char **errstr) {
+ (void)common;
+ int *num = (int *)obj;
+ /* the string needs to be nul-terminated
+ if it contains more than 9 digits, it's illegal anyways */
+ if (val_len > 9)
+ goto error;
+ char str[10];
+ strncpy(str, val, val_len);
+ str[val_len] = '\0';
+ char *end;
+ long l = strtol(str, &end, 10);
+ if (str == end || *end != '\0' ||
+ l < 0 || l > INT_MAX || ((l == LONG_MIN ||
+ l == LONG_MAX) && errno == ERANGE)) {
+ goto error;
+ }
+ *num = (int)l;
+ return 0;
+error:
+ *errstr = print_fmt(
+ "%s: Invalid number '%.*s' "
+ "for '%s' at line %zu, position %zu",
+ filename, val_len, val, key, line, line_offset
+ );
+ return 1;
+}
+
+static void
+destroy_theme_number(ledit_common *common, void *obj) {
+ (void)common;
+ (void)obj;
+}
+
+static int
+parse_theme_string(
+ ledit_common *common,
+ void *obj, const char *val, size_t val_len, char *key,
+ char *filename, size_t line, size_t line_offset, char **errstr) {
+ (void)common; (void)key;
+ (void)filename; (void)line; (void)line_offset; (void)errstr;
+
+ char **obj_str = (char **)obj;
+ *obj_str = ledit_strndup(val, val_len);
+ return 0;
+}
+
+static void
+destroy_theme_string(ledit_common *common, void *obj) {
+ (void)common;
+ char **obj_str = (char **)obj;
+ free(*obj_str);
+}
+
+/* FIXME: This interface is absolutely horrible - it's mainly this way to reuse the
+ theme array for the destroy function */
+/* If theme is NULL, a new theme is loaded, else it is destroyed */
+static ledit_theme *
+load_destroy_theme(ledit_common *common, ast_list *theme_list, ledit_theme *theme, char *filename, char **errstr) {
+ *errstr = NULL;
+ int default_init = theme ? 1 : 0;
+ if (!theme)
+ theme = ledit_malloc(sizeof(ledit_theme));
+
+ struct {
+ char *key;
+ void *obj;
+ int (*parse_func)(
+ ledit_common *common,
+ void *obj, const char *val, size_t val_len, char *key,
+ char *filename, size_t line, size_t line_offset, char **errstr
+ );
+ void (*destroy_func)(ledit_common *common, void *obj);
+ const char *default_value;
+ int initialized;
+ } settings[] = {
+ {"text-font", &theme->text_font, &parse_theme_string, &destroy_theme_string, TEXT_FONT, default_init},
+ {"text-size", &theme->text_size, &parse_theme_number, &destroy_theme_number, TEXT_SIZE, default_init},
+ {"scrollbar-width", &theme->scrollbar_width, &parse_theme_number, &destroy_theme_number, SCROLLBAR_WIDTH, default_init},
+ {"scrollbar-step", &theme->scrollbar_step, &parse_theme_number, &destroy_theme_number, SCROLLBAR_STEP, default_init},
+ {"text-fg", &theme->text_fg, &parse_theme_color, &destroy_theme_color, TEXT_FG, default_init},
+ {"text-bg", &theme->text_bg, &parse_theme_color, &destroy_theme_color, TEXT_BG, default_init},
+ {"cursor-fg", &theme->cursor_fg, &parse_theme_color, &destroy_theme_color, CURSOR_FG, default_init},
+ {"cursor-bg", &theme->cursor_bg, &parse_theme_color, &destroy_theme_color, CURSOR_BG, default_init},
+ {"selection-fg", &theme->selection_fg, &parse_theme_color, &destroy_theme_color, SELECTION_FG, default_init},
+ {"selection-bg", &theme->selection_bg, &parse_theme_color, &destroy_theme_color, SELECTION_BG, default_init},
+ {"bar-fg", &theme->bar_fg, &parse_theme_color, &destroy_theme_color, BAR_FG, default_init},
+ {"bar-bg", &theme->bar_bg, &parse_theme_color, &destroy_theme_color, BAR_BG, default_init},
+ {"bar-cursor", &theme->bar_cursor, &parse_theme_color, &destroy_theme_color, BAR_CURSOR, default_init},
+ {"scrollbar-fg", &theme->scrollbar_fg, &parse_theme_color, &destroy_theme_color, SCROLLBAR_FG, default_init},
+ {"scrollbar-bg", &theme->scrollbar_bg, &parse_theme_color, &destroy_theme_color, SCROLLBAR_BG, default_init},
+ };
+
+ if (default_init)
+ goto cleanup;
+
+ if (theme_list) {
+ for (size_t i = 0; i < theme_list->len; i++) {
+ size_t line = theme_list->objs[i].tok.line;
+ size_t line_offset = theme_list->objs[i].tok.line_offset;
+ if (theme_list->objs[i].type != OBJ_ASSIGNMENT) {
+ *errstr = print_fmt(
+ "%s: Invalid statement in theme configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto cleanup;
+ } else if (theme_list->objs[i].obj.assignment.value->type != OBJ_STRING) {
+ *errstr = print_fmt(
+ "%s: Invalid assignment in theme configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto cleanup;
+ }
+
+ char *key = theme_list->objs[i].obj.assignment.tok.text;
+ size_t key_len = theme_list->objs[i].obj.assignment.tok.len;
+ char *val = theme_list->objs[i].obj.assignment.value->obj.str.tok.text;
+ size_t val_len = theme_list->objs[i].obj.assignment.value->obj.str.tok.len;
+
+ int found = 0;
+ /* FIXME: use binary search maybe */
+ for (size_t j = 0; j < LENGTH(settings); j++) {
+ if (str_array_equal(settings[j].key, key, key_len)) {
+ /* FIXME: maybe just make this a warning? */
+ if (settings[j].initialized) {
+ *errstr = print_fmt(
+ "%s: Duplicate definition of "
+ "'%.*s' at line %zu, position %zu",
+ filename, key_len, key, line, line_offset
+ );
+ goto cleanup;
+ }
+ if (settings[j].parse_func(
+ common, settings[j].obj, val, val_len,
+ settings[j].key, filename, line, line_offset, errstr)) {
+ goto cleanup;
+ }
+ settings[j].initialized = 1;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ *errstr = print_fmt(
+ "%s: Invalid theme setting "
+ "'%.*s' at line %zu, position %zu",
+ filename, key_len, key, line, line_offset
+ );
+ goto cleanup;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < LENGTH(settings); i++) {
+ if (!settings[i].initialized) {
+ /* FIXME: kind of inefficient to calculate strlen at runtime */
+ /* FIXME: line number doesn't make sense */
+ if (settings[i].parse_func(
+ common, settings[i].obj, settings[i].default_value,
+ strlen(settings[i].default_value), settings[i].key,
+ "default config", 0, 0, errstr)) {
+ goto cleanup;
+ }
+ }
+ }
+
+ return theme;
+cleanup:
+ for (size_t i = 0; i < LENGTH(settings); i++) {
+ if (settings[i].initialized) {
+ settings[i].destroy_func(common, settings[i].obj);
+ }
+ }
+ free(theme);
+ return NULL;
+}
+
+static ledit_theme *
+load_theme(ledit_common *common, ast_list *theme_list, char *filename, char **errstr) {
+ return load_destroy_theme(common, theme_list, NULL, filename, errstr);
+}
+
+static void
+destroy_theme(ledit_common *common, ledit_theme *theme) {
+ char *errstr = NULL;
+ if (!theme)
+ return;
+ (void)load_destroy_theme(common, NULL, theme, NULL, &errstr);
+ /* shouldn't happen... */
+ if (errstr)
+ free(errstr);
+}
+
+/* This only destroys the members inside 'cfg' since the config
+ * struct itself is usually not on the heap. */
+static void
+config_destroy(ledit_common *common, struct config *cfg) {
+ if (cfg->theme)
+ destroy_theme(common, cfg->theme);
+ cfg->theme = NULL;
+ for (size_t i = 0; i < cfg->num_langs; i++) {
+ for (size_t j = 0; j < cfg->basic_keys[i].num_keys; j++) {
+ free(cfg->basic_keys[i].keys[j].text);
+ }
+ free(cfg->basic_keys[i].keys);
+ for (size_t j = 0; j < cfg->command_keys[i].num_keys; j++) {
+ free(cfg->command_keys[i].keys[j].text);
+ }
+ free(cfg->command_keys[i].keys);
+ for (size_t j = 0; j < cfg->cmds[i].num_cmds; j++) {
+ free(cfg->cmds[i].cmds[j].text);
+ }
+ free(cfg->cmds[i].cmds);
+ free(cfg->langs[i]);
+ }
+ free(cfg->basic_keys);
+ free(cfg->command_keys);
+ free(cfg->cmds);
+ free(cfg->langs);
+ cfg->basic_keys = NULL;
+ cfg->command_keys = NULL;
+ cfg->cmds = NULL;
+ cfg->langs = NULL;
+ cfg->num_langs = cfg->alloc_langs = 0;
+}
+
+void
+config_cleanup(ledit_common *common) {
+ config_destroy(common, &config);
+}
+
+/* FIXME: which additional ones are needed here? */
+static struct keysym_mapping {
+ char *name;
+ KeySym keysym;
+} keysym_map[] = {
+ {"backspace", XK_BackSpace},
+ {"begin", XK_Begin},
+ {"break", XK_Break},
+ {"cancel", XK_Cancel},
+ {"clear", XK_Clear},
+ {"delete", XK_Delete},
+ {"down", XK_Down},
+ {"end", XK_End},
+ {"escape", XK_Escape},
+ {"execute", XK_Execute},
+
+ {"f1", XK_F1},
+ {"f10", XK_F10},
+ {"f11", XK_F11},
+ {"f12", XK_F12},
+ {"f13", XK_F13},
+ {"f14", XK_F14},
+ {"f15", XK_F15},
+ {"f16", XK_F16},
+ {"f17", XK_F17},
+ {"f18", XK_F18},
+ {"f19", XK_F19},
+ {"f2", XK_F2},
+ {"f20", XK_F20},
+ {"f21", XK_F21},
+ {"f22", XK_F22},
+ {"f23", XK_F23},
+ {"f24", XK_F24},
+ {"f25", XK_F25},
+ {"f26", XK_F26},
+ {"f27", XK_F27},
+ {"f28", XK_F28},
+ {"f29", XK_F29},
+ {"f3", XK_F3},
+ {"f30", XK_F30},
+ {"f31", XK_F31},
+ {"f32", XK_F32},
+ {"f33", XK_F33},
+ {"f34", XK_F34},
+ {"f35", XK_F35},
+ {"f4", XK_F4},
+ {"f5", XK_F5},
+ {"f6", XK_F6},
+ {"f7", XK_F7},
+ {"f8", XK_F8},
+ {"f9", XK_F9},
+
+ {"find", XK_Find},
+ {"help", XK_Help},
+ {"home", XK_Home},
+ {"insert", XK_Insert},
+
+ {"kp-0", XK_KP_0},
+ {"kp-1", XK_KP_1},
+ {"kp-2", XK_KP_2},
+ {"kp-3", XK_KP_3},
+ {"kp-4", XK_KP_4},
+ {"kp-5", XK_KP_5},
+ {"kp-6", XK_KP_6},
+ {"kp-7", XK_KP_7},
+ {"kp-8", XK_KP_8},
+ {"kp-9", XK_KP_9},
+ {"kp-add", XK_KP_Add},
+ {"kp-begin", XK_KP_Begin},
+ {"kp-decimal", XK_KP_Decimal},
+ {"kp-delete", XK_KP_Delete},
+ {"kp-divide", XK_KP_Divide},
+ {"kp-down", XK_KP_Down},
+ {"kp-end", XK_KP_End},
+ {"kp-enter", XK_KP_Enter},
+ {"kp-equal", XK_KP_Equal},
+ {"kp-f1", XK_KP_F1},
+ {"kp-f2", XK_KP_F2},
+ {"kp-f3", XK_KP_F3},
+ {"kp-f4", XK_KP_F4},
+ {"kp-home", XK_KP_Home},
+ {"kp-insert", XK_KP_Insert},
+ {"kp-left", XK_KP_Left},
+ {"kp-multiply", XK_KP_Multiply},
+ {"kp-next", XK_KP_Next},
+ {"kp-page-down", XK_KP_Page_Down},
+ {"kp-page-up", XK_KP_Page_Up},
+ {"kp-prior", XK_KP_Prior},
+ {"kp-right", XK_KP_Right},
+ {"kp-separator", XK_KP_Separator},
+ {"kp-space", XK_KP_Space},
+ {"kp-subtract", XK_KP_Subtract},
+ {"kp-tab", XK_KP_Tab},
+ {"kp-up", XK_KP_Up},
+
+ {"l1", XK_L1},
+ {"l10", XK_L10},
+ {"l2", XK_L2},
+ {"l3", XK_L3},
+ {"l4", XK_L4},
+ {"l5", XK_L5},
+ {"l6", XK_L6},
+ {"l7", XK_L7},
+ {"l8", XK_L8},
+ {"l9", XK_L9},
+
+ {"left", XK_Left},
+ {"linefeed", XK_Linefeed},
+ {"menu", XK_Menu},
+ {"mode-switch", XK_Mode_switch},
+ {"next", XK_Next},
+ {"num-lock", XK_Num_Lock},
+ {"page-down", XK_Page_Down},
+ {"page-up", XK_Page_Up},
+ {"pause", XK_Pause},
+ {"print", XK_Print},
+ {"prior", XK_Prior},
+
+ {"r1", XK_R1},
+ {"r10", XK_R10},
+ {"r11", XK_R11},
+ {"r12", XK_R12},
+ {"r13", XK_R13},
+ {"r14", XK_R14},
+ {"r15", XK_R15},
+ {"r2", XK_R2},
+ {"r3", XK_R3},
+ {"r4", XK_R4},
+ {"r5", XK_R5},
+ {"r6", XK_R6},
+ {"r7", XK_R7},
+ {"r8", XK_R8},
+ {"r9", XK_R9},
+
+ {"redo", XK_Redo},
+ {"return", XK_Return},
+ {"right", XK_Right},
+ {"script-switch", XK_script_switch},
+ {"scroll-lock", XK_Scroll_Lock},
+ {"select", XK_Select},
+ {"space", XK_space},
+ {"sysreq", XK_Sys_Req},
+ {"tab", XK_Tab},
+ {"up", XK_Up},
+ {"undo", XK_Undo},
+};
+
+GEN_CB_MAP_HELPERS(keysym_map, struct keysym_mapping, name)
+
+static int
+parse_keysym(char *keysym_str, size_t len, KeySym *sym) {
+ struct keysym_mapping *km = keysym_map_get_entry(keysym_str, len);
+ if (!km)
+ return 1;
+ *sym = km->keysym;
+ return 0;
+}
+
+static int
+parse_modemask(char *modemask_str, size_t len, ledit_mode *mode_ret) {
+ size_t cur = 0;
+ *mode_ret = 0;
+ while (cur < len) {
+ if (str_array_equal("normal", modemask_str + cur, LEDIT_MIN(6, len - cur))) {
+ cur += 6;
+ *mode_ret |= NORMAL;
+ } else if (str_array_equal("visual", modemask_str + cur, LEDIT_MIN(6, len - cur))) {
+ cur += 6;
+ *mode_ret |= VISUAL;
+ } else if (str_array_equal("insert", modemask_str + cur, LEDIT_MIN(6, len - cur))) {
+ cur += 6;
+ *mode_ret |= INSERT;
+ } else {
+ return 1;
+ }
+ if (cur < len && modemask_str[cur] != '|')
+ return 1;
+ else
+ cur++;
+ }
+ return 0;
+}
+
+static int
+parse_modmask(char *modmask_str, size_t len, unsigned int *mask_ret) {
+ size_t cur = 0;
+ *mask_ret = 0;
+ while (cur < len) {
+ if (str_array_equal("shift", modmask_str + cur, LEDIT_MIN(5, len - cur))) {
+ cur += 5;
+ *mask_ret |= ShiftMask;
+ } else if (str_array_equal("lock", modmask_str + cur, LEDIT_MIN(4, len - cur))) {
+ cur += 4;
+ *mask_ret |= LockMask;
+ } else if (str_array_equal("control", modmask_str + cur, LEDIT_MIN(7, len - cur))) {
+ cur += 7;
+ *mask_ret |= ControlMask;
+ } else if (str_array_equal("mod1", modmask_str + cur, LEDIT_MIN(4, len - cur))) {
+ cur += 4;
+ *mask_ret |= Mod1Mask;
+ } else if (str_array_equal("mod2", modmask_str + cur, LEDIT_MIN(4, len - cur))) {
+ cur += 4;
+ *mask_ret |= Mod2Mask;
+ } else if (str_array_equal("mod3", modmask_str + cur, LEDIT_MIN(4, len - cur))) {
+ cur += 4;
+ *mask_ret |= Mod3Mask;
+ } else if (str_array_equal("mod4", modmask_str + cur, LEDIT_MIN(4, len - cur))) {
+ cur += 4;
+ *mask_ret |= Mod4Mask;
+ } else if (str_array_equal("any", modmask_str + cur, LEDIT_MIN(3, len - cur))) {
+ cur += 3;
+ *mask_ret = UINT_MAX;
+ } else {
+ return 1;
+ }
+ if (cur < len && modmask_str[cur] != '|')
+ return 1;
+ else
+ cur++;
+ }
+ return 0;
+}
+
+/* FIXME: it would probably be safer to not write the string lengths by hand... */
+static int
+parse_command_modemask(char *mode_str, size_t len, command_mode *mode_ret) {
+ size_t cur = 0;
+ *mode_ret = 0;
+ /* IMPORTANT: these need to be sorted appropriately so e.g. edit doesn't mess with edit-search */
+ while (cur < len) {
+ if (str_array_equal("substitute", mode_str + cur, LEDIT_MIN(10, len - cur))) {
+ cur += 10;
+ *mode_ret |= CMD_SUBSTITUTE;
+ } else if (str_array_equal("edit-search-backwards", mode_str + cur, LEDIT_MIN(21, len - cur))) {
+ cur += 21;
+ *mode_ret |= CMD_EDITSEARCHB;
+ } else if (str_array_equal("edit-search", mode_str + cur, LEDIT_MIN(11, len - cur))) {
+ cur += 11;
+ *mode_ret |= CMD_EDITSEARCH;
+ } else if (str_array_equal("edit", mode_str + cur, LEDIT_MIN(4, len - cur))) {
+ cur += 4;
+ *mode_ret |= CMD_EDIT;
+ } else {
+ return 1;
+ }
+ if (cur < len && mode_str[cur] != '|') {
+ return 1;
+ } else {
+ cur++;
+ }
+ }
+ return 0;
+}
+
+/* FIXME: generic dynamic array */
+
+static void
+push_lang(struct config *cfg) {
+ if (cfg->num_langs == cfg->alloc_langs) {
+ cfg->alloc_langs = ideal_array_size(cfg->alloc_langs, add_sz(cfg->num_langs, 1));
+ cfg->basic_keys = ledit_reallocarray(cfg->basic_keys, cfg->alloc_langs, sizeof(basic_key_array));
+ cfg->command_keys = ledit_reallocarray(cfg->command_keys, cfg->alloc_langs, sizeof(command_key_array));
+ cfg->cmds = ledit_reallocarray(cfg->cmds, cfg->alloc_langs, sizeof(command_array));
+ cfg->langs = ledit_reallocarray(cfg->langs, cfg->alloc_langs, sizeof(char *));
+ }
+ basic_key_array *arr1 = &cfg->basic_keys[cfg->num_langs];
+ arr1->keys = NULL;
+ arr1->num_keys = arr1->alloc_keys = 0;
+ command_key_array *arr2 = &cfg->command_keys[cfg->num_langs];
+ arr2->keys = NULL;
+ arr2->num_keys = arr2->alloc_keys = 0;
+ command_array *arr3 = &cfg->cmds[cfg->num_langs];
+ arr3->cmds = NULL;
+ arr3->num_cmds = arr3->alloc_cmds = 0;
+ cfg->langs[cfg->num_langs] = NULL;
+ cfg->num_langs++;
+}
+
+#define GEN_PARSE_STATEMENT(name, cb_type, mapping_type, mode_parse_func) \
+static int \
+name(ast_statement *st, mapping_type *m, char *filename, char **errstr) { \
+ size_t line = st->func_tok.line; \
+ size_t line_offset = st->func_tok.line_offset; \
+ m->cb = NULL; \
+ m->text = NULL; \
+ m->mods = 0; \
+ m->modes = 0; \
+ m->keysym = 0; \
+ char *msg = NULL; \
+ if (!str_array_equal("bind", st->func_tok.text, st->func_tok.len) || st->len < 1) { \
+ msg = "Invalid statement"; \
+ goto error; \
+ } \
+ m->cb = cb_type##_map_get_entry(st->args[0].text, st->args[0].len); \
+ if (!m->cb) { \
+ msg = "Invalid function specification"; \
+ goto error; \
+ } \
+ int text_init = 0, keysym_init = 0, modes_init = 0, mods_init = 0; \
+ for (size_t i = 1; i < st->len; i++) { \
+ line = st->args[i].line; \
+ line_offset = st->args[i].line_offset; \
+ if (str_array_equal("mods", st->args[i].text, st->args[i].len)) { \
+ if (mods_init) { \
+ msg = "Duplicate mods specification"; \
+ goto error; \
+ } else if (i == st->len - 1) { \
+ msg = "Unfinished statement"; \
+ goto error; \
+ } \
+ i++; \
+ if (parse_modmask(st->args[i].text, st->args[i].len, &m->mods)) { \
+ msg = "Invalid mods specification"; \
+ goto error; \
+ } \
+ mods_init = 1; \
+ } else if (str_array_equal("modes", st->args[i].text, st->args[i].len)) { \
+ if (modes_init) { \
+ msg = "Duplicate modes specification"; \
+ goto error; \
+ } else if (i == st->len - 1) { \
+ msg = "Unfinished statement"; \
+ goto error; \
+ } \
+ i++; \
+ if (mode_parse_func(st->args[i].text, st->args[i].len, &m->modes)) { \
+ msg = "Invalid modes specification"; \
+ goto error; \
+ } else if (!cb_type##_modemask_is_valid(m->cb, m->modes)) { \
+ msg = "Function not defined for all given modes"; \
+ goto error; \
+ } \
+ modes_init = 1; \
+ } else if (str_array_equal("keysym", st->args[i].text, st->args[i].len)) { \
+ if (text_init) { \
+ msg = "Text already specified"; \
+ goto error; \
+ } else if (keysym_init) { \
+ msg = "Duplicate keysym specification"; \
+ goto error; \
+ } else if (i == st->len - 1) { \
+ msg = "Unfinished statement"; \
+ goto error; \
+ } \
+ i++; \
+ if (parse_keysym(st->args[i].text, st->args[i].len, &m->keysym)) { \
+ msg = "Invalid keysym specification"; \
+ goto error; \
+ } \
+ keysym_init = 1; \
+ } else if (str_array_equal("text", st->args[i].text, st->args[i].len)) { \
+ if (keysym_init) { \
+ msg = "Keysym already specified"; \
+ goto error; \
+ } else if (text_init) { \
+ msg = "Duplicate text specification"; \
+ goto error; \
+ } else if (i == st->len - 1) { \
+ msg = "Unfinished statement"; \
+ goto error; \
+ } \
+ i++; \
+ m->text = ledit_strndup(st->args[i].text, st->args[i].len); \
+ text_init = 1; \
+ } else if (str_array_equal("catchall", st->args[i].text, st->args[i].len)) { \
+ if (keysym_init) { \
+ msg = "Keysym already specified"; \
+ goto error; \
+ } else if (text_init) { \
+ msg = "Duplicate text specification"; \
+ goto error; \
+ } \
+ m->text = ledit_strdup(""); \
+ text_init = 1; \
+ } else { \
+ msg = "Invalid statement"; \
+ goto error; \
+ } \
+ } \
+ if (!text_init && !keysym_init) { \
+ msg = "No text or keysym specified"; \
+ goto error; \
+ } \
+ if (!modes_init) { \
+ msg = "No modes specified"; \
+ goto error; \
+ } \
+ return 0; \
+error: \
+ if (msg) { \
+ *errstr = print_fmt( \
+ "%s, line %zu, offset %zu: %s", filename, line, line_offset, msg \
+ ); \
+ } \
+ if (m->text) \
+ free(m->text); \
+ return 1; \
+}
+
+GEN_PARSE_STATEMENT(parse_basic_key_statement, basic_key_cb, basic_key_mapping, parse_modemask)
+GEN_PARSE_STATEMENT(parse_command_key_statement, command_key_cb, command_key_mapping, parse_command_modemask)
+
+static int
+parse_command_statement(ast_statement *st, command_mapping *m, char *filename, char **errstr) {
+ size_t line = st->func_tok.line;
+ size_t line_offset = st->func_tok.line_offset;
+ m->cb = NULL;
+ m->text = NULL;
+ char *msg = NULL;
+ if (!str_array_equal("bind", st->func_tok.text, st->func_tok.len) || st->len != 2) {
+ msg = "Invalid statement";
+ goto error;
+ }
+ m->cb = command_cb_map_get_entry(st->args[0].text, st->args[0].len);
+ if (!m->cb) {
+ msg = "Invalid function specification";
+ goto error;
+ }
+ m->text = ledit_strndup(st->args[1].text, st->args[1].len);
+ return 0;
+error:
+ if (msg) {
+ *errstr = print_fmt(
+ "%s, line %zu, offset %zu: %s", filename, line, line_offset, msg
+ );
+ }
+ /* I guess this is unnecessary */
+ if (m->text)
+ free(m->text);
+ return 1;
+}
+
+static void
+push_basic_key_mapping(basic_key_array *arr, basic_key_mapping m) {
+ if (arr->num_keys == arr->alloc_keys) {
+ arr->alloc_keys = ideal_array_size(arr->alloc_keys, add_sz(arr->num_keys, 1));
+ arr->keys = ledit_reallocarray(arr->keys, arr->alloc_keys, sizeof(basic_key_mapping));
+ }
+ arr->keys[arr->num_keys] = m;
+ arr->num_keys++;
+}
+
+static void
+push_command_key_mapping(command_key_array *arr, command_key_mapping m) {
+ if (arr->num_keys == arr->alloc_keys) {
+ arr->alloc_keys = ideal_array_size(arr->alloc_keys, add_sz(arr->num_keys, 1));
+ arr->keys = ledit_reallocarray(arr->keys, arr->alloc_keys, sizeof(command_key_mapping));
+ }
+ arr->keys[arr->num_keys] = m;
+ arr->num_keys++;
+}
+
+static void
+push_command_mapping(command_array *arr, command_mapping m) {
+ if (arr->num_cmds == arr->alloc_cmds) {
+ arr->alloc_cmds = ideal_array_size(arr->alloc_cmds, add_sz(arr->num_cmds, 1));
+ arr->cmds = ledit_reallocarray(arr->cmds, arr->alloc_cmds, sizeof(command_mapping));
+ }
+ arr->cmds[arr->num_cmds] = m;
+ arr->num_cmds++;
+}
+
+/* FIXME: This could be made a lot nicer and less repetitive */
+static int
+load_bindings(struct config *cfg, ast_list *list, char *filename, char **errstr) {
+ int basic_keys_init = 0, command_keys_init = 0, commands_init = 0;
+ size_t cur_lang = cfg->num_langs - 1; /* FIXME: ensure no underflow */
+ for (size_t i = 0; i < list->len; i++) {
+ size_t line = list->objs[i].tok.line;
+ size_t line_offset = list->objs[i].tok.line_offset;
+ if (list->objs[i].type != OBJ_ASSIGNMENT) {
+ *errstr = print_fmt(
+ "%s: Invalid statement in bindings configuration "
+ "at list %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ char *key = list->objs[i].obj.assignment.tok.text;
+ size_t key_len = list->objs[i].obj.assignment.tok.len;
+ if (str_array_equal("language", key, key_len)) {
+ if (list->objs[i].obj.assignment.value->type != OBJ_STRING) {
+ *errstr = print_fmt(
+ "%s: Invalid language setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ } else if (cfg->langs[cur_lang]) {
+ *errstr = print_fmt(
+ "%s: Duplicate language setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ char *val = list->objs[i].obj.assignment.value->obj.str.tok.text;
+ size_t val_len = list->objs[i].obj.assignment.value->obj.str.tok.len;
+ cfg->langs[cur_lang] = ledit_strndup(val, val_len);
+ } else if (str_array_equal("basic-keys", key, key_len)) {
+ if (list->objs[i].obj.assignment.value->type != OBJ_LIST) {
+ *errstr = print_fmt(
+ "%s: Invalid basic-keys setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ } else if (basic_keys_init) {
+ *errstr = print_fmt(
+ "%s: Duplicate basic-keys setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ ast_list *slist = &list->objs[i].obj.assignment.value->obj.list;
+ for (size_t j = 0; j < slist->len; j++) {
+ line = slist->objs[j].tok.line;
+ line_offset = slist->objs[j].tok.line_offset;
+ if (slist->objs[j].type != OBJ_STATEMENT) {
+ *errstr = print_fmt(
+ "%s: Invalid basic-keys setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ basic_key_mapping m;
+ if (parse_basic_key_statement(&slist->objs[j].obj.statement, &m, filename, errstr))
+ goto error;
+ push_basic_key_mapping(&cfg->basic_keys[0], m);
+ }
+ basic_keys_init = 1;
+ } else if (str_array_equal("command-keys", key, key_len)) {
+ if (list->objs[i].obj.assignment.value->type != OBJ_LIST) {
+ *errstr = print_fmt(
+ "%s: Invalid command-keys setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ } else if (command_keys_init) {
+ *errstr = print_fmt(
+ "%s: Duplicate command-keys setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ ast_list *slist = &list->objs[i].obj.assignment.value->obj.list;
+ for (size_t j = 0; j < slist->len; j++) {
+ line = slist->objs[j].tok.line;
+ line_offset = slist->objs[j].tok.line_offset;
+ if (slist->objs[j].type != OBJ_STATEMENT) {
+ *errstr = print_fmt(
+ "%s: Invalid command-keys setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ command_key_mapping m;
+ if (parse_command_key_statement(&slist->objs[j].obj.statement, &m, filename, errstr))
+ goto error;
+ push_command_key_mapping(&cfg->command_keys[0], m);
+ }
+ command_keys_init = 1;
+ } else if (str_array_equal("commands", key, key_len)) {
+ if (list->objs[i].obj.assignment.value->type != OBJ_LIST) {
+ *errstr = print_fmt(
+ "%s: Invalid commands setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ } else if (commands_init) {
+ *errstr = print_fmt(
+ "%s: Duplicate commands setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ ast_list *slist = &list->objs[i].obj.assignment.value->obj.list;
+ for (size_t j = 0; j < slist->len; j++) {
+ line = slist->objs[j].tok.line;
+ line_offset = slist->objs[j].tok.line_offset;
+ if (slist->objs[j].type != OBJ_STATEMENT) {
+ *errstr = print_fmt(
+ "%s: Invalid commands setting in bindings configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ command_mapping m;
+ if (parse_command_statement(&slist->objs[j].obj.statement, &m, filename, errstr))
+ goto error;
+ push_command_mapping(&cfg->cmds[0], m);
+ }
+ commands_init = 1;
+ }
+ }
+
+ /* FIXME: the behavior here is a bit weird - if there is nothing other than a language
+ setting in the bindings configuration, all actual bindings are default, but the
+ associated language is different */
+ if (!cfg->langs[cur_lang]) {
+ cfg->langs[cur_lang] = ledit_strdup(language_default);
+ }
+ /* FIXME: avoid calling strlen */
+ if (!basic_keys_init) {
+ ledit_debug("No basic keys configured in bindings; loading defaults\n");
+ basic_key_mapping m;
+ for (size_t i = 0; i < LENGTH(basic_keys_default); i++) {
+ m.cb = basic_key_cb_map_get_entry(basic_keys_default[i].func_name, strlen(basic_keys_default[i].func_name));
+ if (!m.cb) {
+ *errstr = print_fmt("default config: Invalid basic key function name '%s'", basic_keys_default[i].func_name);
+ goto error;
+ } else if (!basic_key_cb_modemask_is_valid(m.cb, basic_keys_default[i].modes)) {
+ *errstr = print_fmt("default config: Function '%s' not defined for all given modes", basic_keys_default[i].func_name);
+ goto error;
+ }
+ m.text = basic_keys_default[i].text ? ledit_strdup(basic_keys_default[i].text) : NULL;
+ m.mods = basic_keys_default[i].mods;
+ m.modes = basic_keys_default[i].modes;
+ m.keysym = basic_keys_default[i].keysym;
+ push_basic_key_mapping(&cfg->basic_keys[0], m);
+ }
+ }
+ if (!command_keys_init) {
+ ledit_debug("No command keys configured in bindings; loading defaults\n");
+ command_key_mapping m;
+ for (size_t i = 0; i < LENGTH(command_keys_default); i++) {
+ m.cb = command_key_cb_map_get_entry(command_keys_default[i].func_name, strlen(command_keys_default[i].func_name));
+ if (!m.cb) {
+ *errstr = print_fmt("default config: Invalid command key function name '%s'", command_keys_default[i].func_name);
+ goto error;
+ } else if (!command_key_cb_modemask_is_valid(m.cb, command_keys_default[i].modes)) {
+ *errstr = print_fmt("default config: Function '%s' not defined for all given modes", command_keys_default[i].func_name);
+ goto error;
+ }
+ m.text = command_keys_default[i].text ? ledit_strdup(command_keys_default[i].text) : NULL;
+ m.mods = command_keys_default[i].mods;
+ m.modes = command_keys_default[i].modes;
+ m.keysym = command_keys_default[i].keysym;
+ push_command_key_mapping(&cfg->command_keys[0], m);
+ }
+ }
+ /* FIXME: guard against NULL text in default config! */
+ if (!commands_init) {
+ ledit_debug("No commands configured in bindings; loading defaults\n");
+ command_mapping m;
+ for (size_t i = 0; i < LENGTH(commands_default); i++) {
+ m.cb = command_cb_map_get_entry(commands_default[i].func_name, strlen(commands_default[i].func_name));
+ if (!m.cb) {
+ *errstr = print_fmt("default config: Invalid command function name '%s'", commands_default[i].func_name);
+ goto error;
+ }
+ m.text = ledit_strdup(commands_default[i].text);
+ push_command_mapping(&cfg->cmds[0], m);
+ }
+ }
+ return 0;
+/* FIXME: simplify error handling by doing more here */
+error:
+ return 1;
+}
+
+static int
+load_mapping(struct config *cfg, ast_list *list, char *filename, char **errstr) {
+ int key_mapping_init = 0, command_mapping_init = 0;
+ size_t cur_lang = cfg->num_langs - 1; /* FIXME: ensure no underflow */
+ for (size_t i = 0; i < list->len; i++) {
+ size_t line = list->objs[i].tok.line;
+ size_t line_offset = list->objs[i].tok.line_offset;
+ if (list->objs[i].type != OBJ_ASSIGNMENT) {
+ *errstr = print_fmt(
+ "%s: Invalid statement in language mapping configuration "
+ "at list %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ char *key = list->objs[i].obj.assignment.tok.text;
+ size_t key_len = list->objs[i].obj.assignment.tok.len;
+ basic_key_array *bkmap = &cfg->basic_keys[cur_lang];
+ command_key_array *ckmap = &cfg->command_keys[cur_lang];
+ command_array *cmap = &cfg->cmds[cur_lang];
+ if (str_array_equal("language", key, key_len)) {
+ if (list->objs[i].obj.assignment.value->type != OBJ_STRING) {
+ *errstr = print_fmt(
+ "%s: Invalid language setting in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ } else if (cfg->langs[cur_lang]) {
+ *errstr = print_fmt(
+ "%s: Duplicate language setting in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ char *val = list->objs[i].obj.assignment.value->obj.str.tok.text;
+ size_t val_len = list->objs[i].obj.assignment.value->obj.str.tok.len;
+ cfg->langs[cur_lang] = ledit_strndup(val, val_len);
+ } else if (str_array_equal("key-mapping", key, key_len)) {
+ if (list->objs[i].obj.assignment.value->type != OBJ_LIST) {
+ *errstr = print_fmt(
+ "%s: Invalid key-mapping setting in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ } else if (key_mapping_init) {
+ *errstr = print_fmt(
+ "%s: Duplicate key-mapping setting in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ ast_list *slist = &list->objs[i].obj.assignment.value->obj.list;
+ for (size_t j = 0; j < slist->len; j++) {
+ line = slist->objs[j].tok.line;
+ line_offset = slist->objs[j].tok.line_offset;
+ if (slist->objs[j].type != OBJ_STATEMENT) {
+ *errstr = print_fmt(
+ "%s: Invalid key-mapping setting in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ ast_statement *st = &slist->objs[j].obj.statement;
+ if (!str_array_equal("map", st->func_tok.text, st->func_tok.len) || st->len != 2) {
+ *errstr = print_fmt(
+ "%s: Invalid key-mapping statement in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ /* FIXME: any way to speed this up? I guess once the keys can be binary-searched... */
+ for (size_t k = 0; k < bkmap->num_keys; k++) {
+ if (bkmap->keys[k].text && str_array_equal(bkmap->keys[k].text, st->args[1].text, st->args[1].len)) {
+ free(bkmap->keys[k].text);
+ bkmap->keys[k].text = ledit_strndup(st->args[0].text, st->args[0].len);
+ }
+ }
+ for (size_t k = 0; k < ckmap->num_keys; k++) {
+ if (ckmap->keys[k].text && str_array_equal(ckmap->keys[k].text, st->args[1].text, st->args[1].len)) {
+ free(ckmap->keys[k].text);
+ ckmap->keys[k].text = ledit_strndup(st->args[0].text, st->args[0].len);
+ }
+ }
+ }
+ key_mapping_init = 1;
+ } else if (str_array_equal("command-mapping", key, key_len)) {
+ if (list->objs[i].obj.assignment.value->type != OBJ_LIST) {
+ *errstr = print_fmt(
+ "%s: Invalid command-mapping setting in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ } else if (command_mapping_init) {
+ *errstr = print_fmt(
+ "%s: Duplicate command-mapping setting in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ ast_list *slist = &list->objs[i].obj.assignment.value->obj.list;
+ for (size_t j = 0; j < slist->len; j++) {
+ line = slist->objs[j].tok.line;
+ line_offset = slist->objs[j].tok.line_offset;
+ if (slist->objs[j].type != OBJ_STATEMENT) {
+ *errstr = print_fmt(
+ "%s: Invalid command-mapping setting in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ ast_statement *st = &slist->objs[j].obj.statement;
+ if (!str_array_equal("map", st->func_tok.text, st->func_tok.len) || st->len != 2) {
+ *errstr = print_fmt(
+ "%s: Invalid command-mapping statement in language mapping configuration "
+ "at line %zu, offset %zu", filename, line, line_offset
+ );
+ goto error;
+ }
+ for (size_t k = 0; k < cmap->num_cmds; k++) {
+ if (str_array_equal(cmap->cmds[k].text, st->args[1].text, st->args[1].len)) {
+ free(cmap->cmds[k].text);
+ cmap->cmds[k].text = ledit_strndup(st->args[0].text, st->args[0].len);
+ }
+ }
+ }
+ command_mapping_init = 1;
+ }
+ }
+ if (!cfg->langs[cur_lang]) {
+ /* FIXME: pass actual beginning line and offset so this doesn't have to
+ use the line and offset of the first list element */
+ if (list->len > 0) {
+ *errstr = print_fmt(
+ "%s: Missing language setting in language mapping configuration "
+ "at line %zu, offset %zu", filename, list->objs[0].tok.line, list->objs[0].tok.line_offset
+ );
+ } else {
+ *errstr = print_fmt("%s: Missing language setting in language mapping configuration", filename);
+ }
+ goto error;
+ }
+ return 0;
+error:
+ return 1;
+}
+
+static void
+append_mapping(struct config *cfg) {
+ push_lang(cfg);
+ ledit_assert(cfg->num_langs > 1);
+
+ /* first duplicate original mappings before replacing the text */
+ /* FIXME: optimize this to avoid useless reallocations */
+ size_t cur_lang = cfg->num_langs - 1;
+ basic_key_array *arr1 = &cfg->basic_keys[cur_lang];
+ arr1->num_keys = arr1->alloc_keys = cfg->basic_keys[0].num_keys;
+ arr1->keys = ledit_reallocarray(NULL, arr1->num_keys, sizeof(basic_key_mapping));
+ memmove(arr1->keys, cfg->basic_keys[0].keys, arr1->num_keys * sizeof(basic_key_mapping));
+ for (size_t i = 0; i < arr1->num_keys; i++) {
+ if (arr1->keys[i].text)
+ arr1->keys[i].text = ledit_strdup(arr1->keys[i].text);
+ }
+
+
+ command_key_array *arr2 = &cfg->command_keys[cur_lang];
+ arr2->num_keys = arr2->alloc_keys = cfg->command_keys[0].num_keys;
+ arr2->keys = ledit_reallocarray(NULL, arr2->num_keys, sizeof(command_key_mapping));
+ memmove(arr2->keys, cfg->command_keys[0].keys, arr2->num_keys * sizeof(command_key_mapping));
+ for (size_t i = 0; i < arr2->num_keys; i++) {
+ if (arr2->keys[i].text)
+ arr2->keys[i].text = ledit_strdup(arr2->keys[i].text);
+ }
+
+ command_array *arr3 = &cfg->cmds[cur_lang];
+ arr3->num_cmds = arr3->alloc_cmds = cfg->cmds[0].num_cmds;
+ arr3->cmds = ledit_reallocarray(NULL, arr3->num_cmds, sizeof(command_mapping));
+ memmove(arr3->cmds, cfg->cmds[0].cmds, arr3->num_cmds * sizeof(command_mapping));
+ for (size_t i = 0; i < arr3->num_cmds; i++) {
+ arr3->cmds[i].text = ledit_strdup(arr3->cmds[i].text);
+ }
+}
+
+#ifdef LEDIT_DEBUG
+static void
+debug_print_obj(ast_obj *obj, int shiftwidth) {
+ for (int i = 0; i < shiftwidth; i++) {
+ fprintf(stderr, " ");
+ }
+ switch (obj->type) {
+ case OBJ_STRING:
+ fprintf(stderr, "STRING: %.*s\n", (int)obj->obj.str.tok.len, obj->obj.str.tok.text);
+ break;
+ case OBJ_STATEMENT:
+ fprintf(stderr, "STATEMENT: %.*s ", (int)obj->obj.statement.func_tok.len, obj->obj.statement.func_tok.text);
+ for (size_t i = 0; i < obj->obj.statement.len; i++) {
+ fprintf(stderr, "%.*s ", (int)obj->obj.statement.args[i].len, obj->obj.statement.args[i].text);
+ }
+ fprintf(stderr, "\n");
+ break;
+ case OBJ_ASSIGNMENT:
+ fprintf(stderr, "ASSIGNMENT: %.*s =\n", (int)obj->obj.assignment.tok.len, obj->obj.assignment.tok.text);
+ debug_print_obj(obj->obj.assignment.value, shiftwidth + 4);
+ break;
+ case OBJ_LIST:
+ fprintf(stderr, "LIST:\n");
+ for (size_t i = 0; i < obj->obj.list.len; i++) {
+ debug_print_obj(&obj->obj.list.objs[i], shiftwidth + 4);
+ }
+ break;
+ }
+}
+#endif
+
+/* WARNING: *errstr must be freed! */
+int
+config_loadfile(ledit_common *common, char *filename, char **errstr) {
+ #ifdef LEDIT_DEBUG
+ struct timespec now, elapsed, last;
+ clock_gettime(CLOCK_MONOTONIC, &last);
+ #endif
+ size_t len;
+ *errstr = NULL;
+ ast_list list = {.objs = NULL, .len = 0, .cap = 0};
+ char *file_contents = NULL;
+ if (filename) {
+ file_contents = load_file(filename, &len, errstr);
+ if (!file_contents) return 1;
+ #ifdef LEDIT_DEBUG
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ledit_timespecsub(&now, &last, &elapsed);
+ ledit_debug_fmt(
+ "Time to load config file: %lld seconds, %ld nanoseconds\n",
+ (long long)elapsed.tv_sec, elapsed.tv_nsec
+ );
+ last = now;
+ #endif
+ /* start at line 1 to make error messages more useful */
+ struct lexstate s = {file_contents, len, 0, 1, 0};
+ if (parse_list(&s, &list, 1, filename, errstr)) {
+ free(file_contents);
+ return 1;
+ }
+ #ifdef LEDIT_DEBUG
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ledit_timespecsub(&now, &last, &elapsed);
+ ledit_debug_fmt(
+ "Time to parse config file: %lld seconds, %ld nanoseconds\n",
+ (long long)elapsed.tv_sec, elapsed.tv_nsec
+ );
+ #endif
+ }
+
+ #ifdef LEDIT_DEBUG
+ clock_gettime(CLOCK_MONOTONIC, &last);
+ for (size_t i = 0; i < list.len; i++) {
+ debug_print_obj(&list.objs[i], 0);
+ }
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ledit_timespecsub(&now, &last, &elapsed);
+ ledit_debug_fmt(
+ "Time to print useless information: %lld seconds, %ld nanoseconds\n",
+ (long long)elapsed.tv_sec, elapsed.tv_nsec
+ );
+ clock_gettime(CLOCK_MONOTONIC, &last);
+ #endif
+
+ struct config cfg = {NULL};
+ int theme_init = 0, bindings_init = 0, mappings_init = 0;
+ ast_assignment *assignment;
+ for (size_t i = 0; i < list.len; i++) {
+ switch (list.objs[i].type) {
+ case OBJ_ASSIGNMENT:
+ assignment = &list.objs[i].obj.assignment;
+ if (str_array_equal("theme", assignment->tok.text, assignment->tok.len)) {
+ if (theme_init) {
+ *errstr = print_fmt(
+ "%s: Duplicate theme definition at line %zu, offset %zu",
+ filename, assignment->tok.line, assignment->tok.line_offset
+ );
+ goto error;
+ } else if (assignment->value->type != OBJ_LIST) {
+ *errstr = print_fmt(
+ "%s: Invalid theme definition at line %zu, offset %zu",
+ filename, assignment->tok.line, assignment->tok.line_offset
+ );
+ goto error;
+ }
+ cfg.theme = load_theme(common, &assignment->value->obj.list, filename, errstr);
+ if (!cfg.theme)
+ goto error;
+ theme_init = 1;
+ } else if (str_array_equal("bindings", assignment->tok.text, assignment->tok.len)) {
+ if (bindings_init) {
+ *errstr = print_fmt(
+ "%s: Duplicate definition of bindings at line %zu, offset %zu",
+ filename, assignment->tok.line, assignment->tok.line_offset
+ );
+ goto error;
+ }
+ push_lang(&cfg);
+ if (assignment->value->type != OBJ_LIST) {
+ *errstr = print_fmt(
+ "%s: Invalid definition of bindings at line %zu, offset %zu",
+ filename, assignment->tok.line, assignment->tok.line_offset
+ );
+ goto error;
+ }
+ if (load_bindings(&cfg, &assignment->value->obj.list, filename, errstr))
+ goto error;
+ bindings_init = 1;
+ } else if (str_array_equal("language-mapping", assignment->tok.text, assignment->tok.len)) {
+ if (cfg.num_langs == 0) {
+ ledit_debug("No key/command bindings configured; loading defaults\n");
+ push_lang(&cfg);
+ /* load default config */
+ ast_list empty_list = {.objs = NULL, .len = 0, .cap = 0};
+ /* shouldn't usually happen */
+ if (load_bindings(&cfg, &empty_list, filename, errstr))
+ goto error;
+ bindings_init = 1;
+ } else if (assignment->value->type != OBJ_LIST) {
+ *errstr = print_fmt(
+ "%s: Invalid definition of language mapping at line %zu, offset %zu",
+ filename, assignment->tok.line, assignment->tok.line_offset
+ );
+ goto error;
+ }
+
+ append_mapping(&cfg);
+
+ if (load_mapping(&cfg, &assignment->value->obj.list, filename, errstr))
+ goto error;
+ mappings_init = 1;
+ } else {
+ *errstr = print_fmt(
+ "%s: Invalid assignment at line %zu, offset %zu",
+ filename, assignment->tok.line, assignment->tok.line_offset
+ );
+ goto error;
+ }
+ break;
+ default:
+ *errstr = print_fmt(
+ "%s: Invalid statement at line %zu, offset %zu",
+ filename, list.objs[i].tok.line, list.objs[i].tok.line_offset
+ );
+ goto error;
+ }
+ }
+ if (!theme_init) {
+ ledit_debug("No theme configured; loading defaults\n");
+ cfg.theme = load_theme(common, NULL, NULL, errstr);
+ if (!cfg.theme)
+ goto error;
+ }
+ if (!bindings_init) {
+ ledit_debug("No key/command bindings configured; loading defaults\n");
+ push_lang(&cfg);
+ /* load default config */
+ ast_list empty_list = {.objs = NULL, .len = 0, .cap = 0};
+ /* shouldn't usually happen */
+ if (load_bindings(&cfg, &empty_list, NULL, errstr))
+ goto error;
+ }
+ if (!mappings_init) {
+ ledit_debug("No key/command mappings configured; loading defaults\n");
+ for (size_t i = 0; i < LENGTH(mappings_default); i++) {
+ append_mapping(&cfg);
+ size_t cur_lang = cfg.num_langs - 1;
+ cfg.langs[cur_lang] = ledit_strdup(mappings_default[i].lang);
+ basic_key_array *bkmap = &cfg.basic_keys[cur_lang];
+ command_key_array *ckmap = &cfg.command_keys[cur_lang];
+ command_array *cmap = &cfg.cmds[cur_lang];
+ /* FIXME: any way to speed this up? I guess once the keys can be binary-searched... */
+ /* FIXME: duplicated code from above */
+ for (size_t j = 0; j < mappings_default[i].keys_len; j++) {
+ for (size_t k = 0; k < bkmap->num_keys; k++) {
+ if (bkmap->keys[k].text && !strcmp(bkmap->keys[k].text, mappings_default[i].keys[j].to)) {
+ free(bkmap->keys[k].text);
+ bkmap->keys[k].text = ledit_strdup(mappings_default[i].keys[j].from);
+ }
+ }
+ for (size_t k = 0; k < ckmap->num_keys; k++) {
+ if (ckmap->keys[k].text && !strcmp(ckmap->keys[k].text, mappings_default[i].keys[j].to)) {
+ free(ckmap->keys[k].text);
+ ckmap->keys[k].text = ledit_strdup(mappings_default[i].keys[j].from);
+ }
+ }
+ }
+ for (size_t j = 0; j < mappings_default[i].cmds_len; j++) {
+ for (size_t k = 0; k < cmap->num_cmds; k++) {
+ if (!strcmp(cmap->cmds[k].text, mappings_default[i].keys[j].to)) {
+ free(cmap->cmds[k].text);
+ cmap->cmds[k].text = ledit_strdup(mappings_default[i].keys[j].from);
+ }
+ }
+ }
+ }
+ }
+ destroy_list(&list);
+ free(file_contents);
+ config_destroy(common, &config);
+ config = cfg;
+ #ifdef LEDIT_DEBUG
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ledit_timespecsub(&now, &last, &elapsed);
+ ledit_debug_fmt(
+ "Time to interpret config file: %lld seconds, %ld nanoseconds\n",
+ (long long)elapsed.tv_sec, elapsed.tv_nsec
+ );
+ #endif
+ return 0;
+error:
+ destroy_list(&list);
+ free(file_contents);
+ config_destroy(common, &cfg);
+ return 1;
+}
+
+ledit_theme *
+config_get_theme(void) {
+ ledit_assert(config.theme != NULL);
+ return config.theme;
+}
+
+basic_key_array *
+config_get_basic_keys(size_t lang_index) {
+ ledit_assert(lang_index < config.num_langs);
+ return &config.basic_keys[lang_index];
+}
+
+command_key_array *
+config_get_command_keys(size_t lang_index) {
+ ledit_assert(lang_index < config.num_langs);
+ return &config.command_keys[lang_index];
+}
+
+command_array *
+config_get_commands(size_t lang_index) {
+ ledit_assert(lang_index < config.num_langs);
+ return &config.cmds[lang_index];
+}
+
+int
+config_get_language_index(char *lang, size_t *idx_ret) {
+ for (size_t i = 0; i < config.num_langs; i++) {
+ if (!strcmp(lang, config.langs[i])) {
+ *idx_ret = i;
+ return 0;
+ }
+ }
+ return 1;
+}
diff --git a/configparser.h b/configparser.h
@@ -0,0 +1,80 @@
+#ifndef _CONFIGPARSER_H_
+#define _CONFIGPARSER_H_
+
+#include "common.h"
+#include "uglycrap.h"
+#include "keys_command.h"
+#include "keys_basic.h"
+
+typedef struct {
+ int scrollbar_width;
+ int scrollbar_step;
+ int text_size;
+ XftColor text_fg;
+ XftColor text_bg;
+ XftColor cursor_fg;
+ XftColor cursor_bg;
+ XftColor selection_fg;
+ XftColor selection_bg;
+ XftColor bar_fg;
+ XftColor bar_bg;
+ XftColor bar_cursor;
+ XftColor scrollbar_fg;
+ XftColor scrollbar_bg;
+ const char *text_font;
+} ledit_theme;
+
+typedef struct {
+ char *text; /* for keys that correspond with text */
+ unsigned int mods; /* modifier mask */
+ KeySym keysym; /* for other keys, e.g. arrow keys */
+ ledit_mode modes; /* modes in which this keybinding is functional */
+ basic_key_cb *cb; /* callback */
+} basic_key_mapping;
+
+typedef struct {
+ char *text; /* for keys that correspond with text */
+ unsigned int mods; /* modifier mask */
+ KeySym keysym; /* for other keys, e.g. arrow keys */
+ command_mode modes; /* substitute, etc. */
+ command_key_cb *cb; /* callback */
+} command_key_mapping;
+
+typedef struct {
+ char *text; /* text typed to call command */
+ command_cb *cb; /* callback */
+} command_mapping;
+
+typedef struct {
+ basic_key_mapping *keys;
+ size_t num_keys;
+ size_t alloc_keys;
+} basic_key_array;
+
+typedef struct {
+ command_key_mapping *keys;
+ size_t num_keys;
+ size_t alloc_keys;
+} command_key_array;
+
+typedef struct {
+ command_mapping *cmds;
+ size_t num_cmds;
+ size_t alloc_cmds;
+} command_array;
+
+/* Note: The config is initialized immediately when ledit starts, so these
+ * should not return NULL (unless an invalid language index is given), but
+ * it's still better to check just in case. */
+
+/* Note: The returned pointers are invalidated if the config is reloaded. */
+
+ledit_theme *config_get_theme(void);
+basic_key_array *config_get_basic_keys(size_t lang_index);
+command_key_array *config_get_command_keys(size_t lang_index);
+command_array *config_get_commands(size_t lang_index);
+int config_get_language_index(char *lang, size_t *idx_ret);
+int config_loadfile(ledit_common *common, char *filename, char **errstr);
+void config_cleanup(ledit_common *common);
+
+#endif
diff --git a/keys.c b/keys.c
@@ -10,22 +10,8 @@
#include "memory.h"
#include "common.h"
#include "txtbuf.h"
-#include "theme.h"
#include "window.h"
#include "keys.h"
-#include "keys_config.h"
-
-KEY_LANGS;
-
-int
-get_language_index(char *lang) {
- for (size_t i = 0; i < LENGTH(key_langs); i++) {
- if (!strcmp(key_langs[i], lang)) {
- return i;
- }
- }
- return -1;
-}
/* FIXME: Does this break anything? */
/*static unsigned int importantmod = ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask;*/
diff --git a/keys.h b/keys.h
@@ -19,4 +19,51 @@ void preprocess_key(
char *buf_ret, int buf_size, int *buf_len_ret
);
+/* FIXME: documentation */
+#define GEN_CB_MAP_HELPERS(name, typename, cmp_entry) \
+ \
+static int name##_sorted = 0; \
+ \
+/* \
+ * IMPORTANT: The text passed to *_get_entry may not be nul-terminated, \
+ * so a txtbuf has to be used for the bsearch comparison helper. \
+ */ \
+ \
+static int \
+name##_search_helper(const void *keyv, const void *entryv) { \
+ txtbuf *key = (txtbuf *)keyv; \
+ typename *entry = (typename *)entryv; \
+ int ret = strncmp(key->text, entry->cmp_entry, key->len); \
+ if (ret == 0) { \
+ if (entry->cmp_entry[key->len] == '\0') \
+ return 0; \
+ else \
+ return -1; \
+ } \
+ return ret; \
+} \
+ \
+static int \
+name##_sort_helper(const void *entry1v, const void *entry2v) { \
+ typename *entry1 = (typename *)entry1v; \
+ typename *entry2 = (typename *)entry2v; \
+ return strcmp(entry1->cmp_entry, entry2->cmp_entry); \
+} \
+ \
+typename * \
+name##_get_entry(char *text, size_t len) { \
+ /* just in case */ \
+ if (!name##_sorted) { \
+ qsort( \
+ name, LENGTH(name), \
+ sizeof(name[0]), &name##_sort_helper); \
+ name##_sorted = 1; \
+ } \
+ txtbuf tmp = {.len = len, .cap = len, .text = text}; \
+ return bsearch( \
+ &tmp, name, LENGTH(name), \
+ sizeof(name[0]), &name##_search_helper \
+ ); \
+}
+
#endif
diff --git a/keys_basic.c b/keys_basic.c
@@ -12,6 +12,8 @@
they are now not allowed at all */
/* FIXME: a lot of error checking in the individual functions may be redundant
now that more checking is done beforehand for the allowed keys */
+/* FIXME: sort functions a bit better, maybe split file */
+/* FIXME: documentation */
#include <stdio.h>
#include <stdlib.h>
@@ -29,17 +31,216 @@
#include "txtbuf.h"
#include "undo.h"
#include "cache.h"
-#include "theme.h"
#include "window.h"
#include "buffer.h"
#include "view.h"
#include "search.h"
#include "keys.h"
-#include "keys_config.h"
#include "keys_basic.h"
#include "keys_command.h"
-#include "keys_basic_config.h"
+#include "configparser.h"
+
+/*************************************************************************
+ * Declarations for all functions that can be used in the configuration. *
+ *************************************************************************/
+
+static struct action backspace(ledit_view *view, char *text, size_t len);
+static struct action cursor_left(ledit_view *view, char *text, size_t len);
+static struct action cursor_right(ledit_view *view, char *text, size_t len);
+static struct action cursor_up(ledit_view *view, char *text, size_t len);
+static struct action cursor_down(ledit_view *view, char *text, size_t len);
+static struct action return_key(ledit_view *view, char *text, size_t len);
+static struct action delete_key(ledit_view *view, char *text, size_t len);
+static struct action escape_key(ledit_view *view, char *text, size_t len);
+static struct action enter_insert(ledit_view *view, char *text, size_t len);
+static struct action cursor_to_beginning(ledit_view *view, char *text, size_t len);
+static struct action key_0(ledit_view *view, char *text, size_t len);
+static struct action push_0(ledit_view *view, char *text, size_t len);
+static struct action push_1(ledit_view *view, char *text, size_t len);
+static struct action push_2(ledit_view *view, char *text, size_t len);
+static struct action push_3(ledit_view *view, char *text, size_t len);
+static struct action push_4(ledit_view *view, char *text, size_t len);
+static struct action push_5(ledit_view *view, char *text, size_t len);
+static struct action push_6(ledit_view *view, char *text, size_t len);
+static struct action push_7(ledit_view *view, char *text, size_t len);
+static struct action push_8(ledit_view *view, char *text, size_t len);
+static struct action push_9(ledit_view *view, char *text, size_t len);
+static struct action delete(ledit_view *view, char *text, size_t len);
+static struct action enter_visual(ledit_view *view, char *text, size_t len);
+static struct action switch_selection_end(ledit_view *view, char *text, size_t len);
+static struct action clipcopy(ledit_view *view, char *text, size_t len);
+static struct action clippaste(ledit_view *view, char *text, size_t len);
+static struct action show_line(ledit_view *view, char *text, size_t len);
+static struct action enter_commandedit(ledit_view *view, char *text, size_t len);
+static struct action enter_searchedit_backward(ledit_view *view, char *text, size_t len);
+static struct action enter_searchedit_forward(ledit_view *view, char *text, size_t len);
+static struct action key_search_next(ledit_view *view, char *text, size_t len);
+static struct action key_search_prev(ledit_view *view, char *text, size_t len);
+static struct action undo(ledit_view *view, char *text, size_t len);
+static struct action redo(ledit_view *view, char *text, size_t len);
+static struct action insert_mode_insert_text(ledit_view *view, char *text, size_t len);
+static struct action repeat_command(ledit_view *view, char *text, size_t len);
+static struct action screen_up(ledit_view *view, char *text, size_t len);
+static struct action screen_down(ledit_view *view, char *text, size_t len);
+static struct action scroll_with_cursor_up(ledit_view *view, char *text, size_t len);
+static struct action scroll_with_cursor_down(ledit_view *view, char *text, size_t len);
+static struct action scroll_lines_up(ledit_view *view, char *text, size_t len);
+static struct action scroll_lines_down(ledit_view *view, char *text, size_t len);
+static struct action move_to_line(ledit_view *view, char *text, size_t len);
+static struct action paste_normal(ledit_view *view, char *text, size_t len);
+static struct action paste_normal_backwards(ledit_view *view, char *text, size_t len);
+static struct action change(ledit_view *view, char *text, size_t len);
+static struct action move_to_eol(ledit_view *view, char *text, size_t len);
+static struct action mark_line(ledit_view *view, char *text, size_t len);
+static struct action jump_to_mark(ledit_view *view, char *text, size_t len);
+static struct action next_word(ledit_view *view, char *text, size_t len);
+static struct action next_word_end(ledit_view *view, char *text, size_t len);
+static struct action next_bigword(ledit_view *view, char *text, size_t len);
+static struct action next_bigword_end(ledit_view *view, char *text, size_t len);
+static struct action prev_word(ledit_view *view, char *text, size_t len);
+static struct action prev_bigword(ledit_view *view, char *text, size_t len);
+static struct action append_after_eol(ledit_view *view, char *text, size_t len);
+static struct action append_after_cursor(ledit_view *view, char *text, size_t len);
+static struct action append_line_above(ledit_view *view, char *text, size_t len);
+static struct action append_line_below(ledit_view *view, char *text, size_t len);
+static struct action find_next_char_forwards(ledit_view *view, char *text, size_t len);
+static struct action find_next_char_backwards(ledit_view *view, char *text, size_t len);
+static struct action find_char_forwards(ledit_view *view, char *text, size_t len);
+static struct action find_char_backwards(ledit_view *view, char *text, size_t len);
+static struct action change_to_eol(ledit_view *view, char *text, size_t len);
+static struct action delete_to_eol(ledit_view *view, char *text, size_t len);
+static struct action delete_chars_forwards(ledit_view *view, char *text, size_t len);
+static struct action delete_chars_backwards(ledit_view *view, char *text, size_t len);
+static struct action yank(ledit_view *view, char *text, size_t len);
+static struct action yank_lines(ledit_view *view, char *text, size_t len);
+static struct action replace(ledit_view *view, char *text, size_t len);
+static struct action cursor_to_first_non_ws(ledit_view *view, char *text, size_t len);
+static struct action join_lines(ledit_view *view, char *text, size_t len);
+static struct action insert_at_beginning(ledit_view *view, char *text, size_t len);
+static struct action toggle_hard_line_based(ledit_view *view, char *text, size_t len);
+
+/***********************************************
+ * String-function mapping for config parsing. *
+ ***********************************************/
+
+/* FIXME: delete-backwards, delete-forwards should be renamed;
+ *key functions should be renamed (they're very vague) */
+
+typedef enum {
+ KEY_FLAG_NONE = 0,
+ KEY_FLAG_JUMP_TO_CURSOR = 1,
+ KEY_FLAG_LOCK_ALLOWED = 2
+} basic_key_cb_flags;
+
+typedef struct action (*basic_key_cb_func)(ledit_view *, char *, size_t);
+
+struct basic_key_cb {
+ char *text;
+ basic_key_cb_func func;
+ basic_key_cb_flags flags;
+ ledit_mode allowed_modes;
+};
+
+int
+basic_key_cb_modemask_is_valid(basic_key_cb *cb, ledit_mode modes) {
+ return (~cb->allowed_modes & modes) == 0;
+}
+
+/* FIXME: make functions work in more modes (e.g. cursor-to-first-non-whitespace) */
+static struct basic_key_cb basic_key_cb_map[] = {
+ {"append-after-cursor", &append_after_cursor, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"append-after-eol", &append_after_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"append-line-above", &append_line_above, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"append-line-below", &append_line_below, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"backspace", &backspace, KEY_FLAG_JUMP_TO_CURSOR, INSERT},
+ {"change", &change, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL},
+ {"change-to-eol", &change_to_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"clipboard-copy", &clipcopy, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, VISUAL},
+ {"clipboard-paste", &clippaste, KEY_FLAG_JUMP_TO_CURSOR, INSERT},
+ {"cursor-down", &cursor_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, VISUAL|INSERT|NORMAL},
+ {"cursor-left", &cursor_left, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, VISUAL|INSERT|NORMAL},
+ {"cursor-right", &cursor_right, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, VISUAL|INSERT|NORMAL},
+ {"cursor-to-beginning", &cursor_to_beginning, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"cursor-to-first-non-whitespace", &cursor_to_first_non_ws, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"cursor-up", &cursor_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, VISUAL|INSERT|NORMAL},
+ {"delete", &delete, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL},
+ {"delete-backwards", &delete_chars_backwards, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"delete-forwards", &delete_chars_forwards, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"delete-key", &delete_key, KEY_FLAG_JUMP_TO_CURSOR, INSERT},
+ {"delete-to-eol", &delete_to_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"enter-commandedit", &enter_commandedit, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"enter-insert", &enter_insert, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL},
+ {"enter-searchedit-backwards", &enter_searchedit_backward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"enter-searchedit-forwards", &enter_searchedit_forward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"enter-visual", &enter_visual, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"escape-key", &escape_key, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT},
+ {"find-char-backwards", &find_char_backwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"find-char-forwards", &find_char_forwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"find-next-char-backwards", &find_next_char_backwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"find-next-char-forwards", &find_next_char_forwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"insert-at-beginning", &insert_at_beginning, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"insert-text", &insert_mode_insert_text, KEY_FLAG_JUMP_TO_CURSOR, INSERT},
+ {"join-lines", &join_lines, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"jump-to-mark", &jump_to_mark, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"key-0", &key_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"mark-line", &mark_line, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"move-to-eol", &move_to_eol, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"move-to-line", &move_to_line, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"next-bigword", &next_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"next-bigword-end", &next_bigword_end, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"next-word", &next_word, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"next-word-end", &next_word_end, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"paste-normal", &paste_normal, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"paste-normal-backwards", &paste_normal_backwards, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"previous-bigword", &prev_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"previous-word", &prev_word, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-0", &push_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-1", &push_1, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-2", &push_2, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-3", &push_3, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-4", &push_4, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-5", &push_5, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-6", &push_6, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-7", &push_7, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-8", &push_8, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"push-9", &push_9, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"redo", &redo, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT},
+ {"repeat-command", &repeat_command, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"replace", &replace, KEY_FLAG_JUMP_TO_CURSOR, NORMAL},
+ {"return-key", &return_key, KEY_FLAG_JUMP_TO_CURSOR, INSERT},
+ {"screen-down", &screen_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"screen-up", &screen_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"scroll-lines-down", &scroll_lines_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"scroll-lines-up", &scroll_lines_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"scroll-with-cursor-down", &scroll_with_cursor_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"scroll-with-cursor-up", &scroll_with_cursor_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"search-next", &key_search_next, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"search-previous", &key_search_prev, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+ {"show-line", &show_line, KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"switch-selection-end", &switch_selection_end, KEY_FLAG_JUMP_TO_CURSOR, VISUAL},
+ {"toggle-hard-line-based", &toggle_hard_line_based, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL}, /* FIXME: also in INSERT */
+ {"undo", &undo, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT},
+ {"yank", &yank, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL},
+ {"yank-lines", &yank_lines, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL},
+};
+
+GEN_CB_MAP_HELPERS(basic_key_cb_map, basic_key_cb, text)
+
+/***************************************************
+ * General global variables and utility functions. *
+ ***************************************************/
+
+enum key_type {
+ KEY_INVALID = 0,
+ KEY_MOTION_CHAR = 4,
+ KEY_MOTION_LINE = 8,
+ KEY_MOTION = 4|8,
+ KEY_MOTIONALLOWED = 16,
+ KEY_NUMBER = 32,
+ KEY_NUMBERALLOWED = 64,
+ KEY_ANY = 0xFF
+};
/* note: this is supposed to be global for all views/buffers */
int paste_buffer_line_based = 0;
@@ -310,6 +511,10 @@ static void
discard_repetition_stack(void) {
if (repetition_stack.replaying)
return;
+ for (size_t i = 0; i < repetition_stack.tmp_len; i++) {
+ free(repetition_stack.tmp_stack[i].key_text);
+ repetition_stack.tmp_stack[i].key_text = NULL;
+ }
repetition_stack.tmp_len = 0;
}
@@ -323,6 +528,10 @@ finalize_repetition_stack(void) {
if (repetition_stack.replaying)
return;
size_t tmp;
+ for (size_t i = 0; i < repetition_stack.len; i++) {
+ free(repetition_stack.stack[i].key_text);
+ repetition_stack.stack[i].key_text = NULL;
+ }
struct repetition_stack_elem *tmpstack;
repetition_stack.len = repetition_stack.tmp_len;
repetition_stack.tmp_len = 0;
@@ -485,6 +694,10 @@ delete_selection(ledit_view *view) {
return 0;
}
+/********************************************
+ * Functions that were declared at the top. *
+ ********************************************/
+
/* FIXME: should these delete characters or graphemes? */
static struct action
delete_chars_forwards(ledit_view *view, char *text, size_t len) {
@@ -549,6 +762,7 @@ delete_chars_backwards(ledit_view *view, char *text, size_t len) {
/* used to set cursor - I guess this is sort of a hack */
static void
push_undo_empty_insert(ledit_view *view, size_t line, size_t index, int start_group) {
+ /* WARNING: Don't abuse txtbuf like this unless you're stupid like me. */
txtbuf ins_buf = {.text = "", .len = 0, .cap = 0};
ledit_range ins_range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index};
ledit_range cur_range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index};
@@ -1271,6 +1485,18 @@ paste_normal_backwards(ledit_view *view, char *text, size_t len) {
return (struct action){ACTION_NONE, NULL};
}
+static struct action
+key_0(ledit_view *view, char *text, size_t len) {
+ struct key_stack_elem *e = peek_key_stack();
+ if (!e || (e->key & KEY_MOTIONALLOWED)) {
+ return cursor_to_beginning(view, text, len);
+ } else if (e->key & KEY_NUMBER) {
+ return push_0(view, text, len);
+ } else {
+ return err_invalid_key(view);
+ }
+}
+
static void
push_num(ledit_view *view, int num) {
struct key_stack_elem *e = peek_key_stack();
@@ -2242,10 +2468,9 @@ toggle_hard_line_based(ledit_view *view, char *text, size_t len) {
}
static struct action
-handle_key(ledit_view *view, char *key_text, size_t len, KeySym sym, unsigned int key_state, int lang_index, int *found, enum key_type *type) {
- struct key *cur_keys = keys[lang_index].keys;
- int num_keys = keys[lang_index].num_keys;
- struct key_stack_elem *e = peek_key_stack();
+handle_key(ledit_view *view, char *key_text, size_t len, KeySym sym, unsigned int key_state, size_t lang_index, int *found, basic_key_cb_flags *flags) {
+ basic_key_array *cur_keys = config_get_basic_keys(lang_index);
+ size_t num_keys = cur_keys->num_keys;
/* FIXME: check if control chars in text */
/* FIXME: this is a bit of a hack because it's hardcoded */
if (grab_char_cb && sym == XK_Escape) {
@@ -2253,31 +2478,32 @@ handle_key(ledit_view *view, char *key_text, size_t len, KeySym sym, unsigned in
return (struct action){ACTION_NONE, NULL};
} else if (len > 0 && grab_char_cb) {
*found = 1;
- *type = 0;
return grab_char_cb(view, key_text, len);
}
*found = 0;
- for (int i = 0; i < num_keys; i++) {
- if (cur_keys[i].text) {
+ for (size_t i = 0; i < num_keys; i++) {
+ if (cur_keys->keys[i].text) {
if (len > 0 &&
- (cur_keys[i].modes & view->mode) &&
- (cur_keys[i].prev_keys == KEY_ANY || (!e && (cur_keys[i].prev_keys & KEY_NONE)) || (e && (e->key & cur_keys[i].prev_keys))) &&
- ((!strncmp(cur_keys[i].text, key_text, len) &&
- match_key(cur_keys[i].mods, key_state & ~ShiftMask)) ||
- cur_keys[i].text[0] == '\0')) {
+ (cur_keys->keys[i].modes & view->mode) &&
+ ((!strncmp(cur_keys->keys[i].text, key_text, len) &&
+ match_key(cur_keys->keys[i].mods, key_state & ~ShiftMask)) ||
+ cur_keys->keys[i].text[0] == '\0')) {
/* FIXME: seems a bit hacky to remove shift, but it
is needed to make keys that use shift match */
- *type = cur_keys[i].type;
+ *flags = cur_keys->keys[i].cb->flags;
*found = 1;
- return cur_keys[i].func(view, key_text, len);
+ if (!(*flags & KEY_FLAG_LOCK_ALLOWED) && view->lock_text)
+ return view_locked_error(view);
+ return cur_keys->keys[i].cb->func(view, key_text, len);
}
- } else if ((cur_keys[i].modes & view->mode) &&
- cur_keys[i].keysym == sym &&
- (cur_keys[i].prev_keys == KEY_ANY || (!e && (cur_keys[i].prev_keys & KEY_NONE)) || (e && (e->key & cur_keys[i].prev_keys))) &&
- match_key(cur_keys[i].mods, key_state)) {
- *type = cur_keys[i].type;
+ } else if ((cur_keys->keys[i].modes & view->mode) &&
+ cur_keys->keys[i].keysym == sym &&
+ match_key(cur_keys->keys[i].mods, key_state)) {
+ *flags = cur_keys->keys[i].cb->flags;
*found = 1;
- return cur_keys[i].func(view, key_text, len);
+ if (!(*flags & KEY_FLAG_LOCK_ALLOWED) && view->lock_text)
+ return view_locked_error(view);
+ return cur_keys->keys[i].cb->func(view, key_text, len);
}
}
return (struct action){ACTION_NONE, NULL};
@@ -2299,13 +2525,13 @@ repeat_command(ledit_view *view, char *text, size_t len) {
return (struct action){ACTION_NONE, NULL};
}
int found;
- enum key_type type;
+ basic_key_cb_flags flags;
repetition_stack.replaying = 1;
for (int i = 0; i < num; i++) {
unwind_repetition_stack();
struct repetition_stack_elem *e = get_cur_repetition_stack_elem();
while (e) {
- (void)handle_key(view, e->key_text, e->len, e->sym, e->key_state, e->lang_index, &found, &type);
+ (void)handle_key(view, e->key_text, e->len, e->sym, e->key_state, e->lang_index, &found, &flags);
advance_repetition_stack();
e = get_cur_repetition_stack_elem();
}
@@ -2338,14 +2564,14 @@ basic_key_handler(ledit_view *view, XEvent *event, int lang_index) {
int found = 0;
int msg_shown = view->window->message_shown;
view->window->message_shown = 0; /* FIXME: this is hacky */
- enum key_type type;
- struct action act = handle_key(view, buf, (size_t)n, sym, key_state, lang_index, &found, &type);
+ basic_key_cb_flags flags;
+ struct action act = handle_key(view, buf, (size_t)n, sym, key_state, lang_index, &found, &flags);
if (found && n > 0 && !view->window->message_shown)
window_hide_message(view->window);
else if (msg_shown)
view->window->message_shown = msg_shown;
- if (found && (type & KEY_ENSURE_CURSOR_SHOWN))
+ if (found && (flags & KEY_FLAG_JUMP_TO_CURSOR))
view_ensure_cursor_shown(view);
if (!found && n > 0) {
window_show_message(view->window, "Invalid key", -1);
diff --git a/keys_basic.h b/keys_basic.h
@@ -4,6 +4,11 @@
#include <X11/Xlib.h>
#include "view.h"
+typedef struct basic_key_cb basic_key_cb;
+
+basic_key_cb *basic_key_cb_map_get_entry(char *text, size_t len);
+int basic_key_cb_modemask_is_valid(basic_key_cb *cb, ledit_mode modes);
+
/* perform cleanup of global data */
void basic_key_cleanup(void);
struct action basic_key_handler(ledit_view *view, XEvent *event, int lang_index);
diff --git a/keys_basic_config.h b/keys_basic_config.h
@@ -1,457 +0,0 @@
-/*
- * These are all the regular keys used in normal, visual, and insert mode.
- */
-
-/*
- * Note: The key types are currently very inconsistent and don't always make
- * sense. This will hopefully be fixed sometime. (FIXME)
- */
-
-enum key_type {
- KEY_INVALID = 0,
- KEY_NONE = 2, /* FIXME: perhaps rather KEY_EMPTY? */
- KEY_MOTION_CHAR = 4,
- KEY_MOTION_LINE = 8,
- KEY_MOTION = 4|8,
- KEY_MOTIONALLOWED = 16,
- KEY_NUMBER = 32,
- KEY_NUMBERALLOWED = 64,
- KEY_ENSURE_CURSOR_SHOWN = 128, /* jump to cursor if it is off screen */ /* FIXME: maybe rather KEY_JUMP_TO_CURSOR? */
- KEY_ANY = 0xFF
-};
-
-struct key {
- char *text; /* for keys that correspond with text */
- unsigned int mods; /* modifier mask */
- KeySym keysym; /* for other keys, e.g. arrow keys */
- ledit_mode modes; /* modes in which this keybinding is functional */
- enum key_type prev_keys; /* allowed previous keys */
- enum key_type type; /* type of key - mainly used for ensure_cursor_shown */
- struct action (*func)(ledit_view *, char *, size_t); /* callback function */
-};
-
-static struct action backspace(ledit_view *view, char *text, size_t len);
-static struct action cursor_left(ledit_view *view, char *text, size_t len);
-static struct action cursor_right(ledit_view *view, char *text, size_t len);
-static struct action cursor_up(ledit_view *view, char *text, size_t len);
-static struct action cursor_down(ledit_view *view, char *text, size_t len);
-static struct action return_key(ledit_view *view, char *text, size_t len);
-static struct action delete_key(ledit_view *view, char *text, size_t len);
-static struct action escape_key(ledit_view *view, char *text, size_t len);
-static struct action enter_insert(ledit_view *view, char *text, size_t len);
-static struct action cursor_to_beginning(ledit_view *view, char *text, size_t len);
-static struct action push_0(ledit_view *view, char *text, size_t len);
-static struct action push_1(ledit_view *view, char *text, size_t len);
-static struct action push_2(ledit_view *view, char *text, size_t len);
-static struct action push_3(ledit_view *view, char *text, size_t len);
-static struct action push_4(ledit_view *view, char *text, size_t len);
-static struct action push_5(ledit_view *view, char *text, size_t len);
-static struct action push_6(ledit_view *view, char *text, size_t len);
-static struct action push_7(ledit_view *view, char *text, size_t len);
-static struct action push_8(ledit_view *view, char *text, size_t len);
-static struct action push_9(ledit_view *view, char *text, size_t len);
-static struct action delete(ledit_view *view, char *text, size_t len);
-static struct action enter_visual(ledit_view *view, char *text, size_t len);
-static struct action switch_selection_end(ledit_view *view, char *text, size_t len);
-static struct action clipcopy(ledit_view *view, char *text, size_t len);
-static struct action clippaste(ledit_view *view, char *text, size_t len);
-static struct action show_line(ledit_view *view, char *text, size_t len);
-static struct action enter_commandedit(ledit_view *view, char *text, size_t len);
-static struct action enter_searchedit_backward(ledit_view *view, char *text, size_t len);
-static struct action enter_searchedit_forward(ledit_view *view, char *text, size_t len);
-static struct action key_search_next(ledit_view *view, char *text, size_t len);
-static struct action key_search_prev(ledit_view *view, char *text, size_t len);
-static struct action undo(ledit_view *view, char *text, size_t len);
-static struct action redo(ledit_view *view, char *text, size_t len);
-static struct action insert_mode_insert_text(ledit_view *view, char *text, size_t len);
-static struct action repeat_command(ledit_view *view, char *text, size_t len);
-static struct action screen_up(ledit_view *view, char *text, size_t len);
-static struct action screen_down(ledit_view *view, char *text, size_t len);
-static struct action scroll_with_cursor_up(ledit_view *view, char *text, size_t len);
-static struct action scroll_with_cursor_down(ledit_view *view, char *text, size_t len);
-static struct action scroll_lines_up(ledit_view *view, char *text, size_t len);
-static struct action scroll_lines_down(ledit_view *view, char *text, size_t len);
-static struct action move_to_line(ledit_view *view, char *text, size_t len);
-static struct action paste_normal(ledit_view *view, char *text, size_t len);
-static struct action paste_normal_backwards(ledit_view *view, char *text, size_t len);
-static struct action change(ledit_view *view, char *text, size_t len);
-static struct action move_to_eol(ledit_view *view, char *text, size_t len);
-static struct action mark_line(ledit_view *view, char *text, size_t len);
-static struct action jump_to_mark(ledit_view *view, char *text, size_t len);
-static struct action next_word(ledit_view *view, char *text, size_t len);
-static struct action next_word_end(ledit_view *view, char *text, size_t len);
-static struct action next_bigword(ledit_view *view, char *text, size_t len);
-static struct action next_bigword_end(ledit_view *view, char *text, size_t len);
-static struct action prev_word(ledit_view *view, char *text, size_t len);
-static struct action prev_bigword(ledit_view *view, char *text, size_t len);
-static struct action append_after_eol(ledit_view *view, char *text, size_t len);
-static struct action append_after_cursor(ledit_view *view, char *text, size_t len);
-static struct action append_line_above(ledit_view *view, char *text, size_t len);
-static struct action append_line_below(ledit_view *view, char *text, size_t len);
-static struct action find_next_char_forwards(ledit_view *view, char *text, size_t len);
-static struct action find_next_char_backwards(ledit_view *view, char *text, size_t len);
-static struct action find_char_forwards(ledit_view *view, char *text, size_t len);
-static struct action find_char_backwards(ledit_view *view, char *text, size_t len);
-static struct action change_to_eol(ledit_view *view, char *text, size_t len);
-static struct action delete_to_eol(ledit_view *view, char *text, size_t len);
-static struct action delete_chars_forwards(ledit_view *view, char *text, size_t len);
-static struct action delete_chars_backwards(ledit_view *view, char *text, size_t len);
-static struct action yank(ledit_view *view, char *text, size_t len);
-static struct action yank_lines(ledit_view *view, char *text, size_t len);
-static struct action replace(ledit_view *view, char *text, size_t len);
-static struct action cursor_to_first_non_ws(ledit_view *view, char *text, size_t len);
-static struct action join_lines(ledit_view *view, char *text, size_t len);
-static struct action insert_at_beginning(ledit_view *view, char *text, size_t len);
-static struct action toggle_hard_line_based(ledit_view *view, char *text, size_t len);
-
-/* FIXME: maybe sort these and use binary search
- -> but that would mess with the catch-all keys */
-static struct key keys_en[] = {
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &backspace},
- {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &return_key},
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &delete_key},
- {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &escape_key},
- {"i", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_insert},
- {"h", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {"l", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {"j", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"k", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {"h", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {"t", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &toggle_hard_line_based},
- {NULL, 0, XK_space, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {"j", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"n", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"p", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {"0", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_beginning},
- {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &push_0},
- {"1", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_1},
- {"2", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_2},
- {"3", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_3},
- {"4", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_4},
- {"5", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_5},
- {"6", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_6},
- {"7", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_7},
- {"8", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_8},
- {"9", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_9},
- {"x", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_forwards},
- {"X", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_backwards},
- {"d", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &delete},
- {"y", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &yank},
- {"Y", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &yank_lines},
- {"c", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &change},
- {"v", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_visual},
- {"o", 0, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &switch_selection_end},
- {"c", ControlMask, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clipcopy},
- {"v", ControlMask, 0, INSERT, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clippaste},
- {"g", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &show_line},
- {":", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &enter_commandedit},
- {"?", 0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_backward},
- {"/", 0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_forward},
- {"n", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_next},
- {"N", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_prev},
- {"u", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &undo},
- {"U", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &redo},
- {".", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &repeat_command}, /* FIXME: only allow after finished key sequence */
- {"z", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &undo},
- {"y", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &redo}, /* FIXME: this is confusing with ctrl+y in normal mode */
- {"b", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_up},
- {"f", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_down},
- {"e", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_down},
- {"y", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_up},
- {"d", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_down},
- {"u", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_up},
- {"$", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_eol},
- {"w", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word},
- {"e", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word_end},
- {"W", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword},
- {"E", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword_end},
- {"b", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_word},
- {"B", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_bigword},
- {"G", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_line},
- {"J", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &join_lines},
- {"I", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &insert_at_beginning},
- {"p", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal},
- {"P", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal_backwards},
- {"A", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_eol},
- {"a", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_cursor},
- {"O", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_above},
- {"o", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_below},
- {"m", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &mark_line},
- {"'", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &jump_to_mark},
- {"C", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &change_to_eol},
- {"D", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &delete_to_eol},
- {"r", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &replace},
- {"^", 0, 0, NORMAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_first_non_ws},
- {"t", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_forwards},
- {"T", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_backwards},
- {"f", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_forwards},
- {"F", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_backwards},
- {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_insert_text}
-};
-
-static struct key keys_de[] = {
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &backspace},
- {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &return_key},
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &delete_key},
- {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &escape_key},
- {"i", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_insert},
- {"h", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {"l", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {"j", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"k", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {"h", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {"t", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &toggle_hard_line_based},
- {NULL, 0, XK_space, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {"j", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"n", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"p", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {"0", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_beginning},
- {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &push_0},
- {"1", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_1},
- {"2", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_2},
- {"3", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_3},
- {"4", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_4},
- {"5", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_5},
- {"6", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_6},
- {"7", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_7},
- {"8", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_8},
- {"9", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_9},
- {"x", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_forwards},
- {"X", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_backwards},
- {"d", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &delete},
- {"z", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &yank},
- {"Z", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &yank_lines},
- {"c", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &change},
- {"v", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_visual},
- {"o", 0, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &switch_selection_end},
- {"c", ControlMask, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clipcopy},
- {"v", ControlMask, 0, INSERT, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clippaste},
- {"g", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &show_line},
- {"Ö", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &enter_commandedit},
- {"_", 0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_backward},
- {"-", 0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_forward},
- {"n", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_next},
- {"N", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_prev},
- {"u", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &undo},
- {"U", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &redo},
- {".", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &repeat_command},
- {"y", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &undo},
- {"z", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &redo},
- {"b", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_up},
- {"f", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_down},
- {"e", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_down},
- {"z", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_up},
- {"d", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_down},
- {"u", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_up},
- {"$", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_eol},
- {"w", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word},
- {"e", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word_end},
- {"W", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword},
- {"E", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword_end},
- {"b", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_word},
- {"B", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_bigword},
- {"G", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_line},
- {"J", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &join_lines},
- {"I", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &insert_at_beginning},
- {"p", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal},
- {"P", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal_backwards},
- {"A", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_eol},
- {"a", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_cursor},
- {"O", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_above},
- {"o", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_below},
- {"m", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &mark_line},
- {"ä", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &jump_to_mark},
- {"C", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &change_to_eol},
- {"D", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &delete_to_eol},
- {"r", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &replace},
- {"&", 0, 0, NORMAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_first_non_ws},
- {"t", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_forwards},
- {"T", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_backwards},
- {"f", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_forwards},
- {"F", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_backwards},
- {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_insert_text}
-};
-
-static struct key keys_ur[] = {
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &backspace},
- {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &return_key},
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &delete_key},
- {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &escape_key},
- {"ی", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_insert},
- {"ح", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {"ل", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {"ج", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"ک", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {"ح", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {"ت", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &toggle_hard_line_based},
- {NULL, 0, XK_space, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {"ج", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"ن", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"پ", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {"0", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_beginning},
- {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &push_0},
- {"1", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_1},
- {"2", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_2},
- {"3", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_3},
- {"4", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_4},
- {"5", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_5},
- {"6", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_6},
- {"7", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_7},
- {"8", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_8},
- {"9", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_9},
- {"ش", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_forwards},
- {"ژ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_backwards},
- {"د", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &delete},
- {"ے", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &yank},
- {"َ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &yank_lines},
- {"چ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &change},
- {"ط", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_visual},
- {"ہ", 0, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &switch_selection_end},
- {"چ", ControlMask, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clipcopy},
- {"ط", ControlMask, 0, INSERT, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clippaste},
- {"گ", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &show_line},
- {":", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &enter_commandedit},
- {"؟", 0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_backward},
- {"/", 0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_forward},
- {"ن", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_next},
- {"ں", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_prev},
- {"ء", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &undo},
- {"ئ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &redo},
- {"۔", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &repeat_command},
- {"ز", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &undo},
- {"َ", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &redo},
- {"ب", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_up},
- {"ف", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_down},
- {"ع", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_down},
- {"ے", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_up},
- {"د", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_down},
- {"ء", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_up},
- {"$", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_eol},
- {"و", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word},
- {"ع", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word_end},
- {"ؤ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword},
- {"ٰ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword_end},
- {"ب", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_word},
- {".", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_bigword},
- {"غ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_line},
- {"ض", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &join_lines},
- {"ِ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &insert_at_beginning},
- {"پ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal},
- {"ُ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal_backwards},
- {"آ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_eol},
- {"ا", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_cursor},
- {"ۃ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_above},
- {"ہ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_below},
- {"م", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &mark_line},
- {"'", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &jump_to_mark},
- {"ث", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &change_to_eol},
- {"ڈ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &delete_to_eol},
- {"ر", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &replace},
- {"^", 0, 0, NORMAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_first_non_ws},
- {"ت", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_forwards},
- {"ٹ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_backwards},
- {"ف", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_forwards},
- {"ّ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_backwards},
- {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_insert_text}
-};
-
-static struct key keys_hi[] = {
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &backspace},
- {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &return_key},
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &delete_key},
- {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &escape_key},
- {"ि", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_insert},
- {"ह", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {"ल", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {"ज", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"क", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {"ह", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_left},
- {"त", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &toggle_hard_line_based},
- {NULL, 0, XK_space, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_right},
- {"ज", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"न", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_down},
- {"प", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &cursor_up},
- {"0", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_beginning},
- {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &push_0},
- {"1", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_1},
- {"2", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_2},
- {"3", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_3},
- {"4", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_4},
- {"5", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_5},
- {"6", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_6},
- {"7", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_7},
- {"8", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_8},
- {"9", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY_ENSURE_CURSOR_SHOWN, &push_9},
- {"्", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_forwards},
- {"ॉ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &delete_chars_backwards},
- {"द", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &delete},
- {"य", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &yank},
- {"ञ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &yank_lines},
- {"च", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &change},
- {"ड", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_visual},
- {"ो", 0, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &switch_selection_end},
- {"च", ControlMask, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clipcopy},
- {"ड", ControlMask, 0, INSERT, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &clippaste},
- {"ग", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &show_line},
- {":", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &enter_commandedit},
- {"?", 0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_backward},
- {"/", 0, 0, NORMAL, KEY_NONE, KEY_NONE, &enter_searchedit_forward},
- {"न", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_next},
- {"ण", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_prev},
- {"ु", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &undo},
- {"ू", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &redo},
- {".", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &repeat_command},
- {"श", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &undo},
- {"य", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &redo},
- {"ब", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_up},
- {"ट", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &screen_down},
- {"े", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_down},
- {"य", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_with_cursor_up},
- {"द", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_down},
- {"ु", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &scroll_lines_up},
- {"$", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_eol},
- {"व", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word},
- {"े", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_word_end},
- {"ॐ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword},
- {"ै", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &next_bigword_end},
- {"ब", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_word},
- {"भ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &prev_bigword},
- {"घ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &move_to_line},
- {"झ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &join_lines},
- {"ी", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &insert_at_beginning},
- {"प", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal},
- {"फ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal_backwards},
- {"आ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_eol},
- {"ा", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_cursor},
- {"ौ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_above},
- {"ो", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_below},
- {"म", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &mark_line},
- {"'", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &jump_to_mark},
- {"छ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &change_to_eol},
- {"ध", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &delete_to_eol},
- {"र", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &replace},
- {"^", 0, 0, NORMAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &cursor_to_first_non_ws},
- {"त", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_forwards},
- {"थ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_next_char_backwards},
- {"ट", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_forwards},
- {"ठ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHOWN, &find_char_backwards},
- {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_insert_text}
-};
-
-GEN_KEY_ARRAY(struct key, keys_en, keys_de, keys_ur, keys_hi);
diff --git a/keys_command.c b/keys_command.c
@@ -1,3 +1,4 @@
+/* FIXME: remove CHECK_VIEW_LOCKED when it is confirmed that the new system works properly */
/* FIXME: Parse commands properly and allow combinations of commands */
#include <stdio.h>
#include <ctype.h>
@@ -18,7 +19,6 @@
#include "txtbuf.h"
#include "undo.h"
#include "cache.h"
-#include "theme.h"
#include "window.h"
#include "buffer.h"
#include "view.h"
@@ -27,9 +27,113 @@
#include "util.h"
#include "keys.h"
-#include "keys_config.h"
#include "keys_command.h"
-#include "keys_command_config.h"
+#include "configparser.h"
+
+/*************************************************************************
+ * Declarations for all functions that can be used in the configuration. *
+ *************************************************************************/
+
+static int substitute_yes(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int substitute_yes_all(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int substitute_no(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int substitute_no_all(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_insert_text(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_cursor_left(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_cursor_right(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_cursor_to_end(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_cursor_to_beginning(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_backspace(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_delete(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_submit(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_prevcommand(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_nextcommand(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_prevsearch(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_nextsearch(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int editsearch_submit(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int editsearchb_submit(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+static int edit_discard(ledit_view *view, char *key_text, size_t len, size_t lang_index);
+
+static int create_view(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int close_view(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int handle_quit(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int handle_write_quit(ledit_view *view, char *cmd, size_t l1, size_t l2);
+static int handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2);
+
+/***********************************************
+ * String-function mapping for config parsing. *
+ ***********************************************/
+
+typedef enum {
+ KEY_FLAG_NONE = 0,
+ KEY_FLAG_JUMP_TO_CURSOR = 1,
+ KEY_FLAG_LOCK_ALLOWED = 2
+} command_key_cb_flags;
+
+typedef enum {
+ CMD_FLAG_NONE = 0,
+ CMD_FLAG_OPTIONAL_RANGE = 1,
+ CMD_FLAG_LOCK_ALLOWED = 2
+} command_cb_flags;
+
+typedef int (*command_key_cb_func)(ledit_view *, char *, size_t, size_t);
+struct command_key_cb {
+ char *text;
+ command_key_cb_func func;
+ command_key_cb_flags flags;
+ command_mode allowed_modes;
+};
+
+typedef int (*command_cb_func)(ledit_view *view, char *cmd, size_t l1, size_t l2);
+struct command_cb {
+ char *text;
+ command_cb_func func;
+ command_cb_flags flags;
+};
+
+int
+command_key_cb_modemask_is_valid(command_key_cb *cb, command_mode modes) {
+ return (~cb->allowed_modes & modes) == 0;
+}
+
+static command_key_cb command_key_cb_map[] = {
+ {"edit-backspace", &edit_backspace, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-cursor-left", &edit_cursor_left, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-cursor-right", &edit_cursor_right, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-cursor-to-beginning", &edit_cursor_to_beginning, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-cursor-to-end", &edit_cursor_to_end, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-delete", &edit_delete, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-discard", &edit_discard, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-insert-text", &edit_insert_text, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-next-command", &edit_nextcommand, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT},
+ {"edit-next-search", &edit_nextsearch, KEY_FLAG_LOCK_ALLOWED, CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-previous-command", &edit_prevcommand, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT},
+ {"edit-previous-search", &edit_prevsearch, KEY_FLAG_LOCK_ALLOWED, CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-submit", &edit_submit, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT},
+ {"edit-submit-backwards-search", &editsearchb_submit, KEY_FLAG_LOCK_ALLOWED, CMD_EDITSEARCHB},
+ {"edit-submit-search", &editsearch_submit, KEY_FLAG_LOCK_ALLOWED, CMD_EDITSEARCH},
+ {"substitute-no", &substitute_no, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, CMD_SUBSTITUTE},
+ {"substitute-no-all", &substitute_no_all, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, CMD_SUBSTITUTE},
+ {"substitute-yes", &substitute_yes, KEY_FLAG_JUMP_TO_CURSOR, CMD_SUBSTITUTE},
+ {"substitute-yes-all", &substitute_yes_all, KEY_FLAG_JUMP_TO_CURSOR, CMD_SUBSTITUTE},
+};
+
+static command_cb command_cb_map[] = {
+ {"close-view", &close_view, CMD_FLAG_LOCK_ALLOWED},
+ {"create-view", &create_view, CMD_FLAG_LOCK_ALLOWED},
+ {"quit", &handle_quit, CMD_FLAG_LOCK_ALLOWED},
+ {"substitute", &handle_substitute, CMD_FLAG_OPTIONAL_RANGE},
+ {"write", &handle_write, CMD_FLAG_OPTIONAL_RANGE|CMD_FLAG_LOCK_ALLOWED},
+ {"write-quit", &handle_write_quit, CMD_FLAG_OPTIONAL_RANGE|CMD_FLAG_LOCK_ALLOWED},
+};
+
+GEN_CB_MAP_HELPERS(command_key_cb_map, command_key_cb, text)
+GEN_CB_MAP_HELPERS(command_cb_map, command_cb, text)
+
+/***************************************************
+ * General global variables and utility functions. *
+ ***************************************************/
static struct {
char *search;
@@ -99,20 +203,17 @@ view_locked_error(ledit_view *view) {
#define CHECK_VIEW_LOCKED(view) if (view->lock_text) return view_locked_error(view)
-static int create_view(ledit_view *view, char *cmd, size_t l1, size_t l2);
-static int close_view(ledit_view *view, char *cmd, size_t l1, size_t l2);
-static int handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2);
-static int handle_quit(ledit_view *view, char *cmd, size_t l1, size_t l2);
-static int handle_write_quit(ledit_view *view, char *cmd, size_t l1, size_t l2);
-static int handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2);
+/********************************************************************
+ * Functions for handling commands typed in line editor (:w, etc.). *
+ ********************************************************************/
+
static int parse_range(
- ledit_view *view, char *cmd, size_t len, char **cmd_ret,
+ ledit_view *view, char *cmd, size_t len, size_t *idx_ret,
size_t *line1_ret, size_t *line2_ret, int *l1_valid, int *l2_valid,
char **errstr_ret
);
-static int handle_cmd(ledit_view *view, char *cmd, size_t len);
+static int handle_cmd(ledit_view *view, char *cmd, size_t len, size_t lang_index);
-/* FIXME: remove command name before passing to handlers */
static int
handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2) {
(void)l1;
@@ -120,7 +221,6 @@ handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2) {
/* FIXME: allow writing only part of file */
char *filename = view->buffer->filename;
int stored = 1;
- cmd++; /* remove 'w' */
int force = 0;
if (*cmd == '!') {
force = 1;
@@ -132,7 +232,7 @@ handle_write(ledit_view *view, char *cmd, size_t l1, size_t l2) {
stored = 0;
}
/* FIXME: file locks */
- char *errstr;
+ char *errstr = NULL;
if (filename) {
struct stat sb;
/* There technically is a race between checking stat and actually
@@ -176,7 +276,6 @@ static int
handle_quit(ledit_view *view, char *cmd, size_t l1, size_t l2) {
(void)l1;
(void)l2;
- cmd++;
int force = 0;
if (*cmd == '!')
force = 1;
@@ -194,7 +293,7 @@ create_view(ledit_view *view, char *cmd, size_t l1, size_t l2) {
(void)cmd;
(void)l1;
(void)l2;
- buffer_add_view(view->buffer, view->theme, view->mode, view->cur_line, view->cur_index, view->display_offset);
+ buffer_add_view(view->buffer, view->mode, view->cur_line, view->cur_index, view->display_offset);
return 0;
}
@@ -205,7 +304,6 @@ close_view(ledit_view *view, char *cmd, size_t l1, size_t l2) {
(void)l2;
/* FIXME: This will lead to problems if I add something that
requires access to the view after the command is handled. */
- cmd++;
int force = 0;
if (*cmd == '!')
force = 1;
@@ -220,7 +318,7 @@ close_view(ledit_view *view, char *cmd, size_t l1, size_t l2) {
static int
handle_write_quit(ledit_view *view, char *cmd, size_t l1, size_t l2) {
- handle_write(view, cmd + 1, l1, l2);
+ handle_write(view, cmd, l1, l2);
ledit_cleanup();
exit(0);
return 0;
@@ -328,7 +426,6 @@ substitute_all_remaining(ledit_view *view) {
static int
handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2) {
CHECK_VIEW_LOCKED(view);
- cmd++; /* remove 's' at beginning */
size_t len = strlen(cmd);
char *sep = NULL;
if (len == 0) goto error;
@@ -399,34 +496,19 @@ enum cmd_type {
CMD_OPTIONAL_RANGE
};
-static const struct {
- char *cmd;
- enum cmd_type type;
- int (*handler)(ledit_view *view, char *cmd, size_t l1, size_t l2);
-} cmds[] = {
- {"wq", CMD_OPTIONAL_RANGE, &handle_write_quit},
- {"w", CMD_OPTIONAL_RANGE, &handle_write},
- {"q", CMD_NORMAL, &handle_quit},
- {"v", CMD_NORMAL, &create_view},
- {"c", CMD_NORMAL, &close_view},
- {"s", CMD_OPTIONAL_RANGE, &handle_substitute}
-};
-
/*
. current line
$ last line
% all lines
*/
-/* FIXME: ACTUALLY USE LEN!!! */
/* NOTE: Marks are only recognized here if they are one unicode character! */
/* NOTE: Only the line range of the selection is used at the moment. */
static int
parse_range(
- ledit_view *view, char *cmd, size_t len, char **cmd_ret,
+ ledit_view *view, char *cmd, size_t len, size_t *idx_ret,
size_t *line1_ret, size_t *line2_ret, int *l1_valid, int *l2_valid,
char **errstr_ret) {
- (void)len;
*errstr_ret = "";
enum {
START_LINENO = 1,
@@ -437,8 +519,10 @@ parse_range(
size_t l1 = 0, l2 = 0;
*l1_valid = 0;
*l2_valid = 0;
- char *c = cmd;
- while (*c != '\0') {
+ size_t cur = 0;
+ char *c;
+ while (cur < len) {
+ c = &cmd[cur];
if (isdigit(*c)) {
if (s & IN_LINENO) {
size_t *final = &l2;
@@ -466,22 +550,20 @@ parse_range(
s = IN_LINENO;
}
} else if (*c == '\'' && (s & START_LINENO)) {
- if (c[1] == '\0' || c[2] == '\0') {
+ if (len - cur <= 2) {
*errstr_ret = "Invalid range";
return 1;
}
- char *aftermark = next_utf8(c + 2);
- size_t marklen = aftermark - (c + 1);
+ size_t aftermark_idx = next_utf8_len(c + 2, len - cur - 2);
+ size_t marklen = aftermark_idx - (cur + 1);
size_t l, b;
- if (!strncmp(c + 1, "<", strlen("<")) && view->sel_valid) {
+ if (*(c + 1) == '<' && view->sel_valid) {
l = view->sel.line1 < view->sel.line2 ? view->sel.line1 : view->sel.line2;
- } else if (!strncmp(c + 1, ">", strlen(">")) && view->sel_valid) {
+ } else if (*(c + 1) == '>' && view->sel_valid) {
l = view->sel.line1 > view->sel.line2 ? view->sel.line1 : view->sel.line2;
- } else {
- if (buffer_get_mark(view->buffer, c + 1, marklen, &l, &b)) {
- *errstr_ret = "Invalid mark";
- return 1;
- }
+ } else if (buffer_get_mark(view->buffer, c + 1, marklen, &l, &b)) {
+ *errstr_ret = "Invalid mark";
+ return 1;
}
if (!*l1_valid) {
l1 = l + 1;
@@ -490,7 +572,7 @@ parse_range(
l2 = l + 1;
*l2_valid = 1;
}
- c = aftermark;
+ cur = aftermark_idx;
s = 0;
continue;
} else if (*c == ',' && !(s & START_RANGE)) {
@@ -505,7 +587,7 @@ parse_range(
l1 = 1;
l2 = view->lines_num;
*l1_valid = *l2_valid = 1;
- c++;
+ cur++;
s = 0;
break;
} else {
@@ -543,7 +625,7 @@ parse_range(
} else {
break;
}
- c++;
+ cur++;
}
if ((!*l1_valid || !*l2_valid) && !(s & START_RANGE)) {
*errstr_ret = "Invalid range";
@@ -553,7 +635,7 @@ parse_range(
*errstr_ret = "Invalid line number in range";
return 1;
}
- *cmd_ret = c;
+ *idx_ret = cur;
/* ranges are given 1-indexed by user */
*line1_ret = l1 - 1;
*line2_ret = l2 - 1;
@@ -561,36 +643,58 @@ parse_range(
}
static int
-handle_cmd(ledit_view *view, char *cmd, size_t len) {
+handle_cmd(ledit_view *view, char *cmd, size_t len, size_t lang_index) {
if (len < 1)
return 0;
push_cmdhistory(cmd, len);
- char *c;
size_t l1, l2;
int l1_valid, l2_valid;
char *errstr;
- if (parse_range(view, cmd, len, &c, &l1, &l2, &l1_valid, &l2_valid, &errstr)) {
+ size_t start_idx;
+ if (parse_range(view, cmd, len, &start_idx, &l1, &l2, &l1_valid, &l2_valid, &errstr)) {
window_show_message(view->window, errstr, -1);
return 0;
}
+ if (start_idx >= len) {
+ window_show_message(view->window, "Invalid command", -1);
+ return 0;
+ }
+ size_t rem_len = len - start_idx;
+ char *cur_str = cmd + start_idx;
int range_given = l1_valid && l2_valid;
if (!range_given) {
l1 = l2 = view->cur_line;
}
- for (size_t i = 0; i < LENGTH(cmds); i++) {
- if (!strncmp(cmds[i].cmd, c, strlen(cmds[i].cmd)) &&
- (!range_given || cmds[i].type == CMD_OPTIONAL_RANGE)) {
- return cmds[i].handler(view, c, l1, l2);
+ command_array *cur_cmds = config_get_commands(lang_index);
+ char *cmd_text;
+ size_t text_len;
+ for (size_t i = 0; i < cur_cmds->num_cmds; i++) {
+ cmd_text = cur_cmds->cmds[i].text;
+ text_len = strlen(cmd_text);
+ if (rem_len < text_len)
+ continue;
+ if (!strncmp(cmd_text, cur_str, text_len)) {
+ if (range_given && !(cur_cmds->cmds[i].cb->flags & CMD_OPTIONAL_RANGE)) {
+ window_show_message(view->window, "Command does not take range", -1);
+ return 0;
+ } else if (view->lock_text && !(cur_cmds->cmds[i].cb->flags & CMD_FLAG_LOCK_ALLOWED)) {
+ window_show_message(view->window, view->lock_text, -1);
+ return 0;
+ }
+ return cur_cmds->cmds[i].cb->func(view, cur_str + text_len, l1, l2);
}
}
window_show_message(view->window, "Invalid command", -1);
return 0;
}
+/***********************************
+ * Functions called on keypresses. *
+ ***********************************/
+
static int
-substitute_yes(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+substitute_yes(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
substitute_single(view);
buffer_recalc_line(view->buffer, sub_state.line);
if (!sub_state.global) {
@@ -606,18 +710,16 @@ substitute_yes(ledit_view *view, char *key_text, size_t len) {
}
static int
-substitute_yes_all(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+substitute_yes_all(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
substitute_all_remaining(view);
show_num_substituted(view);
return 0;
}
static int
-substitute_no(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+substitute_no(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
if (!sub_state.global) {
sub_state.line++;
sub_state.byte = 0;
@@ -631,16 +733,16 @@ substitute_no(ledit_view *view, char *key_text, size_t len) {
}
static int
-substitute_no_all(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+substitute_no_all(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
buffer_unlock_all_views(view->buffer);
show_num_substituted(view);
return 0;
}
static int
-edit_insert_text(ledit_view *view, char *key_text, size_t len) {
+edit_insert_text(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)lang_index;
/* FIXME: overflow */
window_insert_bottom_bar_text(view->window, key_text, len);
window_set_bottom_bar_cursor(
@@ -650,57 +752,50 @@ edit_insert_text(ledit_view *view, char *key_text, size_t len) {
}
static int
-edit_cursor_to_end(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_cursor_to_end(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
window_bottom_bar_cursor_to_end(view->window);
return 1;
}
static int
-edit_cursor_to_beginning(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_cursor_to_beginning(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
window_bottom_bar_cursor_to_beginning(view->window);
return 1;
}
static int
-edit_cursor_left(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_cursor_left(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
window_move_bottom_bar_cursor(view->window, -1);
return 1;
}
static int
-edit_cursor_right(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_cursor_right(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
window_move_bottom_bar_cursor(view->window, 1);
return 1;
}
static int
-edit_backspace(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_backspace(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
window_delete_bottom_bar_char(view->window, -1);
return 1;
}
static int
-edit_delete(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_delete(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
window_delete_bottom_bar_char(view->window, 1);
return 1;
}
static int
-edit_submit(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_submit(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len;
window_set_bottom_bar_text_shown(view->window, 0);
char *text = window_get_bottom_bar_text(view->window);
int min_pos = window_get_bottom_bar_min_pos(view->window);
@@ -714,15 +809,14 @@ edit_submit(ledit_view *view, char *key_text, size_t len) {
}
/* FIXME: this is hacky */
char *cmd = ledit_strndup(text, textlen);
- int ret = handle_cmd(view, cmd, (size_t)textlen);
+ int ret = handle_cmd(view, cmd, (size_t)textlen, lang_index);
free(cmd);
return ret;
}
static int
-edit_prevcommand(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_prevcommand(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
if (cmdhistory.cur > 0) {
cmdhistory.cur--;
window_set_bottom_bar_realtext(view->window, cmdhistory.cmds[cmdhistory.cur], -1);
@@ -732,9 +826,8 @@ edit_prevcommand(ledit_view *view, char *key_text, size_t len) {
}
static int
-edit_nextcommand(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_nextcommand(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
if (cmdhistory.len > 0 && cmdhistory.cur < cmdhistory.len - 1) {
cmdhistory.cur++;
window_set_bottom_bar_realtext(view->window, cmdhistory.cmds[cmdhistory.cur], -1);
@@ -747,9 +840,8 @@ edit_nextcommand(ledit_view *view, char *key_text, size_t len) {
}
static int
-edit_prevsearch(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_prevsearch(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
if (searchhistory.cur > 0) {
searchhistory.cur--;
window_set_bottom_bar_realtext(view->window, searchhistory.cmds[searchhistory.cur], -1);
@@ -759,9 +851,8 @@ edit_prevsearch(ledit_view *view, char *key_text, size_t len) {
}
static int
-edit_nextsearch(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+edit_nextsearch(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
if (searchhistory.len > 0 && searchhistory.cur < searchhistory.len - 1) {
searchhistory.cur++;
window_set_bottom_bar_realtext(view->window, searchhistory.cmds[searchhistory.cur], -1);
@@ -795,9 +886,8 @@ search_prev(ledit_view *view) {
}
static int
-editsearch_submit(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+editsearch_submit(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
window_set_bottom_bar_text_shown(view->window, 0);
char *text = window_get_bottom_bar_text(view->window);
int min_pos = window_get_bottom_bar_min_pos(view->window);
@@ -818,9 +908,8 @@ editsearch_submit(ledit_view *view, char *key_text, size_t len) {
}
static int
-editsearchb_submit(ledit_view *view, char *key_text, size_t len) {
- (void)key_text;
- (void)len;
+editsearchb_submit(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)key_text; (void)len; (void)lang_index;
window_set_bottom_bar_text_shown(view->window, 0);
char *text = window_get_bottom_bar_text(view->window);
int min_pos = window_get_bottom_bar_min_pos(view->window);
@@ -841,9 +930,8 @@ editsearchb_submit(ledit_view *view, char *key_text, size_t len) {
}
static int
-edit_discard(ledit_view *view, char *key_text, size_t len) {
- (void)view;
- (void)key_text;
+edit_discard(ledit_view *view, char *key_text, size_t len, size_t lang_index) {
+ (void)view; (void)key_text; (void)lang_index;
(void)len;
window_set_bottom_bar_text_shown(view->window, 0);
return 0;
@@ -854,28 +942,46 @@ command_key_handler(ledit_view *view, XEvent *event, int lang_index) {
char buf[64];
KeySym sym;
int n;
- struct key *cur_keys = keys[lang_index].keys;
- int num_keys = keys[lang_index].num_keys;
+ command_key_array *cur_keys = config_get_command_keys(lang_index);
+ size_t num_keys = cur_keys->num_keys;
unsigned int key_state = event->xkey.state;
preprocess_key(view->window, event, &sym, buf, sizeof(buf), &n);
- int grabkey = 1;
- for (int i = 0; i < num_keys; i++) {
- if (cur_keys[i].text) {
+ int grabkey = 1, found = 0;
+ command_key_cb_flags flags = KEY_FLAG_NONE;
+ for (size_t i = 0; i < num_keys; i++) {
+ if (cur_keys->keys[i].text) {
if (n > 0 &&
- (cur_keys[i].type == view->cur_command_type) &&
- ((!strncmp(cur_keys[i].text, buf, n) &&
- match_key(cur_keys[i].mods, key_state & ~ShiftMask)) ||
- cur_keys[i].text[0] == '\0')) {
- grabkey = cur_keys[i].func(view, buf, (size_t)n);
+ (cur_keys->keys[i].modes & view->cur_command_type) &&
+ ((!strncmp(cur_keys->keys[i].text, buf, n) &&
+ match_key(cur_keys->keys[i].mods, key_state & ~ShiftMask)) ||
+ cur_keys->keys[i].text[0] == '\0')) {
+ flags = cur_keys->keys[i].cb->flags;
+ if (!(flags & KEY_FLAG_LOCK_ALLOWED) && view->lock_text) {
+ (void)view_locked_error(view);
+ grabkey = 0;
+ break;
+ }
+ grabkey = cur_keys->keys[i].cb->func(view, buf, (size_t)n, lang_index);
+ found = 1;
+ break;
+ }
+ } else if ((cur_keys->keys[i].modes & view->cur_command_type) &&
+ (cur_keys->keys[i].keysym == sym) &&
+ (match_key(cur_keys->keys[i].mods, key_state))) {
+ flags = cur_keys->keys[i].cb->flags;
+ if (!(flags & KEY_FLAG_LOCK_ALLOWED) && view->lock_text) {
+ (void)view_locked_error(view);
+ grabkey = 0;
break;
}
- } else if ((cur_keys[i].type == view->cur_command_type) &&
- (cur_keys[i].keysym == sym) &&
- (match_key(cur_keys[i].mods, key_state))) {
- grabkey = cur_keys[i].func(view, buf, (size_t)n);
+ grabkey = cur_keys->keys[i].cb->func(view, buf, (size_t)n, lang_index);
+ found = 1;
break;
}
}
+ if (found && (flags & KEY_FLAG_JUMP_TO_CURSOR))
+ view_ensure_cursor_shown(view);
+ /* FIXME: proper error on invalid key */
if (grabkey)
return (struct action){ACTION_GRABKEY, &command_key_handler};
else
diff --git a/keys_command.h b/keys_command.h
@@ -3,6 +3,14 @@
#include <X11/Xlib.h>
#include "view.h"
+#include "uglycrap.h"
+
+typedef struct command_key_cb command_key_cb;
+typedef struct command_cb command_cb;
+
+int command_key_cb_modemask_is_valid(command_key_cb *cb, command_mode modes);
+command_key_cb *command_key_cb_map_get_entry(char *text, size_t len);
+command_cb *command_cb_map_get_entry(char *text, size_t len);
/* these are only here so they can also be used by keys_basic */
void search_next(ledit_view *view);
diff --git a/keys_command_config.h b/keys_command_config.h
@@ -1,196 +0,0 @@
-/*
- * These are the keys used by special commands that require a special key
- * handler. This includes keys used to edit the line entry at the bottom
- * and keys used for confirmation (e.g. when substituting).
- */
-
-static int substitute_yes(ledit_view *view, char *key_text, size_t len);
-static int substitute_yes_all(ledit_view *view, char *key_text, size_t len);
-static int substitute_no(ledit_view *view, char *key_text, size_t len);
-static int substitute_no_all(ledit_view *view, char *key_text, size_t len);
-static int edit_insert_text(ledit_view *view, char *key_text, size_t len);
-static int edit_cursor_left(ledit_view *view, char *key_text, size_t len);
-static int edit_cursor_right(ledit_view *view, char *key_text, size_t len);
-static int edit_cursor_to_end(ledit_view *view, char *key_text, size_t len);
-static int edit_cursor_to_beginning(ledit_view *view, char *key_text, size_t len);
-static int edit_backspace(ledit_view *view, char *key_text, size_t len);
-static int edit_delete(ledit_view *view, char *key_text, size_t len);
-static int edit_submit(ledit_view *view, char *key_text, size_t len);
-static int edit_prevcommand(ledit_view *view, char *key_text, size_t len);
-static int edit_nextcommand(ledit_view *view, char *key_text, size_t len);
-static int edit_prevsearch(ledit_view *view, char *key_text, size_t len);
-static int edit_nextsearch(ledit_view *view, char *key_text, size_t len);
-static int editsearch_submit(ledit_view *view, char *key_text, size_t len);
-static int editsearchb_submit(ledit_view *view, char *key_text, size_t len);
-static int edit_discard(ledit_view *view, char *key_text, size_t len);
-
-struct key {
- char *text; /* for keys that correspond with text */
- unsigned int mods; /* modifier mask */
- KeySym keysym; /* for other keys, e.g. arrow keys */
- enum ledit_command_type type; /* substitute, etc. */
- int (*func)(ledit_view *, char *, size_t); /* callback function */
-};
-
-/* "" means catch-all, i.e. all keys with text are given to that callback */
-static struct key keys_en[] = {
- {"y", 0, 0, CMD_SUBSTITUTE, &substitute_yes},
- {"Y", 0, 0, CMD_SUBSTITUTE, &substitute_yes_all},
- {"n", 0, 0, CMD_SUBSTITUTE, &substitute_no},
- {"N", 0, 0, CMD_SUBSTITUTE, &substitute_no_all},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDIT, &edit_submit},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCH, &editsearch_submit},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCHB, &editsearchb_submit},
- {NULL, 0, XK_Left, CMD_EDIT, &edit_cursor_left},
- {NULL, 0, XK_Left, CMD_EDITSEARCH, &edit_cursor_left},
- {NULL, 0, XK_Left, CMD_EDITSEARCHB, &edit_cursor_left},
- {NULL, 0, XK_Right, CMD_EDIT, &edit_cursor_right},
- {NULL, 0, XK_Right, CMD_EDITSEARCH, &edit_cursor_right},
- {NULL, 0, XK_Right, CMD_EDITSEARCHB, &edit_cursor_right},
- {NULL, 0, XK_Up, CMD_EDIT, &edit_prevcommand},
- {NULL, 0, XK_Up, CMD_EDITSEARCH, &edit_prevsearch},
- {NULL, 0, XK_Up, CMD_EDITSEARCHB, &edit_prevsearch},
- {NULL, 0, XK_Down, CMD_EDIT, &edit_nextcommand},
- {NULL, 0, XK_Down, CMD_EDITSEARCH, &edit_nextsearch},
- {NULL, 0, XK_Down, CMD_EDITSEARCHB, &edit_nextsearch},
- {NULL, 0, XK_BackSpace, CMD_EDIT, &edit_backspace},
- {NULL, 0, XK_BackSpace, CMD_EDITSEARCH, &edit_backspace},
- {NULL, 0, XK_BackSpace, CMD_EDITSEARCHB, &edit_backspace},
- {NULL, 0, XK_Delete, CMD_EDIT, &edit_delete},
- {NULL, 0, XK_Delete, CMD_EDITSEARCH, &edit_delete},
- {NULL, 0, XK_Delete, CMD_EDITSEARCHB, &edit_delete},
- {NULL, 0, XK_End, CMD_EDIT, &edit_cursor_to_end},
- {NULL, 0, XK_End, CMD_EDITSEARCH, &edit_cursor_to_end},
- {NULL, 0, XK_End, CMD_EDITSEARCHB, &edit_cursor_to_end},
- {NULL, 0, XK_Home, CMD_EDIT, &edit_cursor_to_beginning},
- {NULL, 0, XK_Home, CMD_EDITSEARCH, &edit_cursor_to_beginning},
- {NULL, 0, XK_Home, CMD_EDITSEARCHB, &edit_cursor_to_beginning},
- {NULL, 0, XK_Escape, CMD_EDIT, &edit_discard},
- {NULL, 0, XK_Escape, CMD_EDITSEARCH, &edit_discard},
- {NULL, 0, XK_Escape, CMD_EDITSEARCHB, &edit_discard},
- {"", 0, 0, CMD_EDIT, &edit_insert_text},
- {"", 0, 0, CMD_EDITSEARCH, &edit_insert_text},
- {"", 0, 0, CMD_EDITSEARCHB, &edit_insert_text}
-};
-
-static struct key keys_de[] = {
- {"z", 0, 0, CMD_SUBSTITUTE, &substitute_yes},
- {"Z", 0, 0, CMD_SUBSTITUTE, &substitute_yes_all},
- {"n", 0, 0, CMD_SUBSTITUTE, &substitute_no},
- {"N", 0, 0, CMD_SUBSTITUTE, &substitute_no_all},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDIT, &edit_submit},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCH, &editsearch_submit},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCHB, &editsearchb_submit},
- {NULL, 0, XK_Left, CMD_EDIT, &edit_cursor_left},
- {NULL, 0, XK_Left, CMD_EDITSEARCH, &edit_cursor_left},
- {NULL, 0, XK_Left, CMD_EDITSEARCHB, &edit_cursor_left},
- {NULL, 0, XK_Right, CMD_EDIT, &edit_cursor_right},
- {NULL, 0, XK_Right, CMD_EDITSEARCH, &edit_cursor_right},
- {NULL, 0, XK_Right, CMD_EDITSEARCHB, &edit_cursor_right},
- {NULL, 0, XK_Up, CMD_EDIT, &edit_prevcommand},
- {NULL, 0, XK_Up, CMD_EDITSEARCH, &edit_prevsearch},
- {NULL, 0, XK_Up, CMD_EDITSEARCHB, &edit_prevsearch},
- {NULL, 0, XK_Down, CMD_EDIT, &edit_nextcommand},
- {NULL, 0, XK_Down, CMD_EDITSEARCH, &edit_nextsearch},
- {NULL, 0, XK_Down, CMD_EDITSEARCHB, &edit_nextsearch},
- {NULL, 0, XK_BackSpace, CMD_EDIT, &edit_backspace},
- {NULL, 0, XK_BackSpace, CMD_EDITSEARCH, &edit_backspace},
- {NULL, 0, XK_BackSpace, CMD_EDITSEARCHB, &edit_backspace},
- {NULL, 0, XK_Delete, CMD_EDIT, &edit_delete},
- {NULL, 0, XK_Delete, CMD_EDITSEARCH, &edit_delete},
- {NULL, 0, XK_Delete, CMD_EDITSEARCHB, &edit_delete},
- {NULL, 0, XK_End, CMD_EDIT, &edit_cursor_to_end},
- {NULL, 0, XK_End, CMD_EDITSEARCH, &edit_cursor_to_end},
- {NULL, 0, XK_End, CMD_EDITSEARCHB, &edit_cursor_to_end},
- {NULL, 0, XK_Home, CMD_EDIT, &edit_cursor_to_beginning},
- {NULL, 0, XK_Home, CMD_EDITSEARCH, &edit_cursor_to_beginning},
- {NULL, 0, XK_Home, CMD_EDITSEARCHB, &edit_cursor_to_beginning},
- {NULL, 0, XK_Escape, CMD_EDIT, &edit_discard},
- {NULL, 0, XK_Escape, CMD_EDITSEARCH, &edit_discard},
- {NULL, 0, XK_Escape, CMD_EDITSEARCHB, &edit_discard},
- {"", 0, 0, CMD_EDIT, &edit_insert_text},
- {"", 0, 0, CMD_EDITSEARCH, &edit_insert_text},
- {"", 0, 0, CMD_EDITSEARCHB, &edit_insert_text}
-};
-
-static struct key keys_ur[] = {
- {"ے", 0, 0, CMD_SUBSTITUTE, &substitute_yes},
- {"َ", 0, 0, CMD_SUBSTITUTE, &substitute_yes_all},
- {"ن", 0, 0, CMD_SUBSTITUTE, &substitute_no},
- {"ں", 0, 0, CMD_SUBSTITUTE, &substitute_no_all},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDIT, &edit_submit},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCH, &editsearch_submit},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCHB, &editsearchb_submit},
- {NULL, 0, XK_Left, CMD_EDIT, &edit_cursor_left},
- {NULL, 0, XK_Left, CMD_EDITSEARCH, &edit_cursor_left},
- {NULL, 0, XK_Left, CMD_EDITSEARCHB, &edit_cursor_left},
- {NULL, 0, XK_Right, CMD_EDIT, &edit_cursor_right},
- {NULL, 0, XK_Right, CMD_EDITSEARCH, &edit_cursor_right},
- {NULL, 0, XK_Right, CMD_EDITSEARCHB, &edit_cursor_right},
- {NULL, 0, XK_Up, CMD_EDIT, &edit_prevcommand},
- {NULL, 0, XK_Up, CMD_EDITSEARCH, &edit_prevsearch},
- {NULL, 0, XK_Up, CMD_EDITSEARCHB, &edit_prevsearch},
- {NULL, 0, XK_Down, CMD_EDIT, &edit_nextcommand},
- {NULL, 0, XK_Down, CMD_EDITSEARCH, &edit_nextsearch},
- {NULL, 0, XK_Down, CMD_EDITSEARCHB, &edit_nextsearch},
- {NULL, 0, XK_BackSpace, CMD_EDIT, &edit_backspace},
- {NULL, 0, XK_BackSpace, CMD_EDITSEARCH, &edit_backspace},
- {NULL, 0, XK_BackSpace, CMD_EDITSEARCHB, &edit_backspace},
- {NULL, 0, XK_Delete, CMD_EDIT, &edit_delete},
- {NULL, 0, XK_Delete, CMD_EDITSEARCH, &edit_delete},
- {NULL, 0, XK_Delete, CMD_EDITSEARCHB, &edit_delete},
- {NULL, 0, XK_End, CMD_EDIT, &edit_cursor_to_end},
- {NULL, 0, XK_End, CMD_EDITSEARCH, &edit_cursor_to_end},
- {NULL, 0, XK_End, CMD_EDITSEARCHB, &edit_cursor_to_end},
- {NULL, 0, XK_Home, CMD_EDIT, &edit_cursor_to_beginning},
- {NULL, 0, XK_Home, CMD_EDITSEARCH, &edit_cursor_to_beginning},
- {NULL, 0, XK_Home, CMD_EDITSEARCHB, &edit_cursor_to_beginning},
- {NULL, 0, XK_Escape, CMD_EDIT, &edit_discard},
- {NULL, 0, XK_Escape, CMD_EDITSEARCH, &edit_discard},
- {NULL, 0, XK_Escape, CMD_EDITSEARCHB, &edit_discard},
- {"", 0, 0, CMD_EDIT, &edit_insert_text},
- {"", 0, 0, CMD_EDITSEARCH, &edit_insert_text},
- {"", 0, 0, CMD_EDITSEARCHB, &edit_insert_text}
-};
-
-static struct key keys_hi[] = {
- {"य", 0, 0, CMD_SUBSTITUTE, &substitute_yes},
- {"ञ", 0, 0, CMD_SUBSTITUTE, &substitute_yes_all},
- {"न", 0, 0, CMD_SUBSTITUTE, &substitute_no},
- {"ण", 0, 0, CMD_SUBSTITUTE, &substitute_no_all},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDIT, &edit_submit},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCH, &editsearch_submit},
- {NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCHB, &editsearchb_submit},
- {NULL, 0, XK_Left, CMD_EDIT, &edit_cursor_left},
- {NULL, 0, XK_Left, CMD_EDITSEARCH, &edit_cursor_left},
- {NULL, 0, XK_Left, CMD_EDITSEARCHB, &edit_cursor_left},
- {NULL, 0, XK_Right, CMD_EDIT, &edit_cursor_right},
- {NULL, 0, XK_Right, CMD_EDITSEARCH, &edit_cursor_right},
- {NULL, 0, XK_Right, CMD_EDITSEARCHB, &edit_cursor_right},
- {NULL, 0, XK_Up, CMD_EDIT, &edit_prevcommand},
- {NULL, 0, XK_Up, CMD_EDITSEARCH, &edit_prevsearch},
- {NULL, 0, XK_Up, CMD_EDITSEARCHB, &edit_prevsearch},
- {NULL, 0, XK_Down, CMD_EDIT, &edit_nextcommand},
- {NULL, 0, XK_Down, CMD_EDITSEARCH, &edit_nextsearch},
- {NULL, 0, XK_Down, CMD_EDITSEARCHB, &edit_nextsearch},
- {NULL, 0, XK_BackSpace, CMD_EDIT, &edit_backspace},
- {NULL, 0, XK_BackSpace, CMD_EDITSEARCH, &edit_backspace},
- {NULL, 0, XK_BackSpace, CMD_EDITSEARCHB, &edit_backspace},
- {NULL, 0, XK_Delete, CMD_EDIT, &edit_delete},
- {NULL, 0, XK_Delete, CMD_EDITSEARCH, &edit_delete},
- {NULL, 0, XK_Delete, CMD_EDITSEARCHB, &edit_delete},
- {NULL, 0, XK_End, CMD_EDIT, &edit_cursor_to_end},
- {NULL, 0, XK_End, CMD_EDITSEARCH, &edit_cursor_to_end},
- {NULL, 0, XK_End, CMD_EDITSEARCHB, &edit_cursor_to_end},
- {NULL, 0, XK_Home, CMD_EDIT, &edit_cursor_to_beginning},
- {NULL, 0, XK_Home, CMD_EDITSEARCH, &edit_cursor_to_beginning},
- {NULL, 0, XK_Home, CMD_EDITSEARCHB, &edit_cursor_to_beginning},
- {NULL, 0, XK_Escape, CMD_EDIT, &edit_discard},
- {NULL, 0, XK_Escape, CMD_EDITSEARCH, &edit_discard},
- {NULL, 0, XK_Escape, CMD_EDITSEARCHB, &edit_discard},
- {"", 0, 0, CMD_EDIT, &edit_insert_text},
- {"", 0, 0, CMD_EDITSEARCH, &edit_insert_text},
- {"", 0, 0, CMD_EDITSEARCHB, &edit_insert_text}
-};
-
-GEN_KEY_ARRAY(struct key, keys_en, keys_de, keys_hi, keys_ur);
diff --git a/keys_config.h b/keys_config.h
@@ -1,31 +1,182 @@
#ifndef _KEYS_CONFIG_H_
#define _KEYS_CONFIG_H_
+#include "X11/Xlib.h"
+#include "X11/keysym.h"
+
+#include "common.h"
#include "keys.h"
-/*
- * These are the language strings compared with the language strings that
- * xkb gives in order to change the key mapping on layout change events.
- */
-#define KEY_LANGS \
-static char *key_langs[] = { \
- "English (US)", \
- "German", \
- "Urdu (Pakistan)", \
- "Hindi (Bolnagri)" \
-}
-
-#define GEN_KEY_ARRAY(key_struct, en, de, ur, hi) \
-static struct { \
- key_struct *keys; \
- int num_keys; \
-} keys[] = { \
- {en, LENGTH(en)}, \
- {de, LENGTH(de)}, \
- {ur, LENGTH(ur)}, \
- {hi, LENGTH(hi)} \
-}
-
-#define LANG_KEYS(index) &keys[index]
-
-#endif
+/*********************************
+ * Key and command configuration *
+ *********************************/
+
+char *language_default = "English (US)";
+
+/* FIXME: binary search keys */
+struct {
+ char *func_name;
+ char *text;
+ unsigned int mods;
+ KeySym keysym;
+ ledit_mode modes;
+} basic_keys_default[] = {
+ {"backspace", NULL, 0, XK_BackSpace, INSERT},
+ {"cursor-left", NULL, 0, XK_Left, VISUAL|INSERT|NORMAL},
+ {"cursor-right", NULL, 0, XK_Right, VISUAL|INSERT|NORMAL},
+ {"cursor-up", NULL, 0, XK_Up, VISUAL|INSERT|NORMAL},
+ {"cursor-down", NULL, 0, XK_Down, VISUAL|INSERT|NORMAL},
+ {"return-key", NULL, XK_ANY_MOD, XK_Return, INSERT},
+ {"delete-key", NULL, 0, XK_Delete, INSERT},
+ {"escape-key", NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT},
+ {"enter-insert", "i", 0, 0, NORMAL|VISUAL},
+ {"cursor-left", "h", 0, 0, NORMAL|VISUAL},
+ {"cursor-left", "h", ControlMask, 0, NORMAL|VISUAL},
+ {"cursor-right", "l", 0, 0, NORMAL|VISUAL},
+ {"cursor-up", "k", 0, 0, NORMAL|VISUAL},
+ {"cursor-down", "j", 0, 0, NORMAL|VISUAL},
+ {"toggle-hard-line-based", "t", ControlMask, 0, NORMAL|VISUAL}, /* FIXME: also in insert */
+ {"cursor-right", NULL, 0, XK_space, NORMAL|VISUAL},
+ {"cursor-down", "j", ControlMask, 0, NORMAL|VISUAL},
+ {"cursor-down", "n", ControlMask, 0, NORMAL|VISUAL},
+ {"cursor-up", "p", ControlMask, 0, NORMAL|VISUAL},
+ {"key-0", "0", 0, 0, NORMAL|VISUAL},
+ {"push-1", "1", 0, 0, NORMAL|VISUAL},
+ {"push-2", "2", 0, 0, NORMAL|VISUAL},
+ {"push-3", "3", 0, 0, NORMAL|VISUAL},
+ {"push-4", "4", 0, 0, NORMAL|VISUAL},
+ {"push-5", "5", 0, 0, NORMAL|VISUAL},
+ {"push-6", "6", 0, 0, NORMAL|VISUAL},
+ {"push-7", "7", 0, 0, NORMAL|VISUAL},
+ {"push-8", "8", 0, 0, NORMAL|VISUAL},
+ {"push-9", "9", 0, 0, NORMAL|VISUAL},
+ {"delete-forwards", "x", 0, 0, NORMAL},
+ {"delete-backwards", "X", 0, 0, NORMAL},
+ {"delete", "d", 0, 0, NORMAL|VISUAL},
+ {"yank", "y", 0, 0, NORMAL|VISUAL},
+ {"yank-lines", "Y", 0, 0, NORMAL},
+ {"change", "c", 0, 0, NORMAL|VISUAL},
+ {"enter-visual", "v", 0, 0, NORMAL},
+ {"switch-selection-end", "o", 0, 0, VISUAL},
+ {"clipboard-copy", "c", ControlMask, 0, VISUAL},
+ {"clipboard-paste", "v", ControlMask, 0, INSERT},
+ {"show-line", "g", ControlMask, 0, NORMAL|VISUAL},
+ {"enter-commandedit", ":", 0, 0, NORMAL|VISUAL},
+ {"enter-searchedit-backwards", "?", 0, 0, NORMAL},
+ {"enter-searchedit-forwards", "/", 0, 0, NORMAL},
+ {"search-next", "n", 0, 0, NORMAL},
+ {"search-previous", "N", 0, 0, NORMAL},
+ {"undo", "u", 0, 0, NORMAL},
+ {"redo", "U", 0, 0, NORMAL},
+ {"repeat-command", ".", 0, 0, NORMAL}, /* FIXME: only allow after finished key sequence */
+ {"undo", "z", ControlMask, 0, INSERT},
+ {"redo", "y", ControlMask, 0, INSERT}, /* FIXME: this is confusing with ctrl-y in normal mode */
+ {"screen-up", "b", ControlMask, 0, NORMAL},
+ {"screen-down", "f", ControlMask, 0, NORMAL},
+ {"scroll-with-cursor-down", "e", ControlMask, 0, NORMAL},
+ {"scroll-with-cursor-up", "y", ControlMask, 0, NORMAL},
+ {"scroll-lines-down", "d", ControlMask, 0, NORMAL},
+ {"scroll-lines-up", "u", ControlMask, 0, NORMAL},
+ {"move-to-eol", "$", 0, 0, NORMAL|VISUAL},
+ {"next-word", "w", 0, 0, NORMAL|VISUAL},
+ {"next-word-end", "e", 0, 0, NORMAL|VISUAL},
+ {"next-bigword", "W", 0, 0, NORMAL|VISUAL},
+ {"next-bigword-end", "E", 0, 0, NORMAL|VISUAL},
+ {"previous-word", "b", 0, 0, NORMAL|VISUAL},
+ {"previous-bigword", "B", 0, 0, NORMAL|VISUAL},
+ {"move-to-line", "G", 0, 0, NORMAL|VISUAL},
+ {"join-lines", "J", 0, 0, NORMAL},
+ {"insert-at-beginning", "I", 0, 0, NORMAL},
+ {"paste-normal", "p", 0, 0, NORMAL},
+ {"paste-normal-backwards", "P", 0, 0, NORMAL},
+ {"append-after-eol", "A", 0, 0, NORMAL},
+ {"append-after-cursor", "a", 0, 0, NORMAL},
+ {"append-line-above", "O", 0, 0, NORMAL},
+ {"append-line-below", "o", 0, 0, NORMAL},
+ {"mark-line", "m", 0, 0, NORMAL|VISUAL},
+ {"jump-to-mark", "'", 0, 0, NORMAL|VISUAL},
+ {"change-to-eol", "C", 0, 0, NORMAL},
+ {"delete-to-eol", "D", 0, 0, NORMAL},
+ {"replace", "r", 0, 0, NORMAL},
+ {"cursor-to-first-non-whitespace", "^", 0, 0, NORMAL},
+ {"find-next-char-forwards", "t", 0, 0, NORMAL|VISUAL},
+ {"find-next-char-backwards", "T", 0, 0, NORMAL|VISUAL},
+ {"find-char-forwards", "f", 0, 0, NORMAL|VISUAL},
+ {"find-char-backwards", "F", 0, 0, NORMAL|VISUAL},
+ {"insert-text", "", 0, 0, INSERT}
+};
+
+struct {
+ char *func_name;
+ char *text;
+ unsigned int mods;
+ KeySym keysym;
+ command_mode modes;
+} command_keys_default[] = {
+ {"substitute-yes", "y", 0, 0, CMD_SUBSTITUTE},
+ {"substitute-yes-all", "Y", 0, 0, CMD_SUBSTITUTE},
+ {"substitute-no", "n", 0, 0, CMD_SUBSTITUTE},
+ {"substitute-no-all", "N", 0, 0, CMD_SUBSTITUTE},
+ {"edit-submit", NULL, XK_ANY_MOD, XK_Return, CMD_EDIT},
+ {"edit-submit-search", NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCH},
+ {"edit-submit-backwards-search", NULL, XK_ANY_MOD, XK_Return, CMD_EDITSEARCHB},
+ {"edit-cursor-left", NULL, 0, XK_Left, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-cursor-right", NULL, 0, XK_Right, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-cursor-right", NULL, 0, XK_Right, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-previous-command", NULL, 0, XK_Up, CMD_EDIT},
+ {"edit-next-command", NULL, 0, XK_Down, CMD_EDIT},
+ {"edit-previous-search", NULL, 0, XK_Up, CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-next-search", NULL, 0, XK_Down, CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-backspace", NULL, 0, XK_BackSpace, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-delete", NULL, 0, XK_Delete, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-cursor-to-end", NULL, 0, XK_End, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-cursor-to-beginning", NULL, 0, XK_Home, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-discard", NULL, 0, XK_Escape, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB},
+ {"edit-insert-text", "", 0, 0, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSEARCHB}
+};
+
+struct {
+ char *func_name;
+ char *text;
+} commands_default[] = {
+ {"write-quit", "wg"},
+ {"write", "w"},
+ {"quit", "q"},
+ {"create-view", "v"},
+ {"close-view", "c"},
+ {"substitute", "s"}
+};
+
+/*****************************************
+ * Key and command mapping configuration *
+ *****************************************/
+
+struct mapping {
+ char *from;
+ char *to;
+};
+
+struct language_mapping {
+ char *lang;
+ struct mapping *keys;
+ struct mapping *cmds;
+ size_t keys_len;
+ size_t cmds_len;
+};
+
+struct mapping key_mapping_de[] = {
+ {"z", "y"},
+ {"y", "z"},
+ {"Z", "Y"},
+ {"Y", "Z"},
+ {"Ö", ":"},
+ {"_", "?"},
+ {"-", "/"},
+ {"ä", "'"}
+};
+
+struct language_mapping mappings_default[] = {
+ {"German", key_mapping_de, NULL, LENGTH(key_mapping_de), 0}
+};
+
+#endif /* _KEYS_CONFIG_H_ */
diff --git a/ledit.1 b/ledit.1
@@ -1,6 +1,6 @@
.\" WARNING: Some parts of this are stolen shamelessly from OpenBSD's
.\" vi(1) manpage!
-.Dd December 26, 2021
+.Dd May 26, 2022
.Dt LEDIT 1
.Os
.Sh NAME
@@ -8,6 +8,7 @@
.Nd weird text editor
.Sh SYNOPSIS
.Nm
+.Op Fl c Ar config
.Op Ar file
.Sh DESCRIPTION
.Nm
@@ -45,6 +46,17 @@ that is not the main goal.
.Pp
.Nm
is not a good text editor.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl c Ar config
+Load the configuration file given by
+.Ar config .
+If this option is not specified,
+.Nm
+will attempt to read the configuration file
+.Pa .leditrc
+in the user's home directory before using the defaults.
+.El
.Sh BASIC CONCEPTS
Some terminology should probably be explained in order to understand the
rest of this manual.
@@ -693,12 +705,6 @@ Also note that the commands which take filenames currently use the entire rest o
the line as the filename instead of doing any string parsing.
This may be changed in the future.
.Pp
-The commands do not fit in very well with the rest of the program since they
-are hard-coded in English and cannot be remapped in other languages.
-This is because it isn't entirely clear how a remapping would even work
-because the commands are shown on screen, which might look weird, especially
-if they are remapped to non-printable characters.
-.Pp
.Bl -tag -width Ds -compact
.It Xo
.Cm :w
@@ -819,71 +825,8 @@ Note that the text is pasted at the current cursor position, not the
position of the mouse cursor.
The author prefers this way.
.Sh CONFIGURATION
-.Nm
-currently has to be configured entirely by changing header files and
-recompiling.
-This will probably be changed in the future, but there hasn't been
-time for it yet.
-.Pp
-There are five configuration headers:
-.Bl -tag -width Ds
-.It Pa config.h
-This contains several timing parameters that most users will probably
-not need to change.
-.It Pa keys_config.h
-This contains the list of languages for which keys are available.
-The language strings in
-.Va key_langs
-are matched exactly with the strings returned by XKB, which seem to
-sometimes be different on different systems, so they will probably
-need to be configured properly. Also note that there are many
-variants of some keyboard layouts, all with small differences, so
-the mappings will often have to be adjusted slightly.
-.It Pa keys_basic_config.h
-This contains the keys used during regular text editing.
-The first entry in each key is the text that is associated with the text.
-If this is
-.Dv NULL ,
-the third entry, which may contain a symbolic key name, is used for the
-matching instead.
-If the key text is an empty string, it is a catch-all, and the actual
-text is given to the handling function.
-.Pp
-Note that the list of keys is currently traversed from top to bottom,
-so catch-all keys should all be in the end in order to not inferfere
-with other mappings.
-.Pp
-The second entry is a mask of modifier keys that need to be
-pressed during the key press.
-If this is set to
-.Dv XK_ANY_MOD ,
-the modifier keys are ignored.
-.Pp
-The other entries should not be touched unless the actual handling
-function implemented in
-.Pa keys_basic.c
-has been changed.
-.Pp
-Note that the key handling is currently a bit weird, so there might
-be unexpected results sometimes.
-Please report these.
-.It Pa keys_command_config.h
-This is similar to
-.Pa keys_basic_config.h ,
-but contains the keys used during line editing mode or while performing
-commands such as substitution with confirmation.
-.It Pa theme_config.h
-This contains the configuration of the theme.
-The configuration options are described in the file itself.
-.El
-.Pp
-This short explanation of the configuration is not very good currently.
-That will hopefully be changed in the future.
-.Pp
-Note that there are a few actions that are currently hard-coded and cannot
-be configured.
-This includes all mouse actions and using escape to cancel a multi-key
-command.
+See
+.Xr leditrc 5 .
.Sh MISCELLANEOUS
.Nm
includes a fair number of sanity checks (asserts) to make sure some illegal
@@ -930,7 +873,8 @@ Pango developers decided to hide spaces at the end of a line.
.Sh SEE ALSO
.Xr ed 1 ,
.Xr vi 1 ,
-.Xr vim 1
+.Xr vim 1 ,
+.Xr leditrc 5
.Sh AUTHORS
.An lumidify Aq Mt nobody@lumidify.org
.Sh BUGS
@@ -938,8 +882,4 @@ Too many to count.
See
.Sx TINY SUBSET OF BUGS .
.Sh TINY SUBSET OF BUGS
-The keyboard mapping is currently only changed when a keyboard change event
-occurs.
-This means that it always is the default mapping when
-.Nm
-starts, regardless of the current keyboard layout.
+Well, I guess I'm too lazy to collect bugs right now.
diff --git a/ledit.c b/ledit.c
@@ -10,6 +10,7 @@
/* FIXME: horizontal scrolling (also need cache to avoid too large pixmaps) */
/* TODO: allow extending selection with shift+mouse like in e.g. gtk */
+#include <pwd.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
@@ -24,8 +25,8 @@
#include <X11/extensions/Xdbe.h>
#include <X11/extensions/XKBrules.h>
+#include "util.h"
#include "view.h"
-#include "theme.h"
#include "buffer.h"
#include "common.h"
#include "window.h"
@@ -37,6 +38,7 @@
#include "keys.h"
#include "keys_basic.h"
#include "keys_command.h"
+#include "configparser.h"
static void mainloop(void);
static void setup(int argc, char *argv[]);
@@ -45,10 +47,9 @@ static void redraw(void);
static void change_keyboard(char *lang);
static void key_press(ledit_view *view, XEvent *event);
-ledit_theme *theme = NULL;
ledit_buffer *buffer = NULL;
ledit_common common;
-int cur_lang = 0;
+size_t cur_lang = 0;
static void
mainloop(void) {
@@ -76,7 +77,7 @@ mainloop(void) {
);
XSync(common.dpy, False);
int running = 1;
- int change_kbd = 0;
+ int change_kbd = 1;
redraw();
/* store last draw time so framerate can be limited */
@@ -193,13 +194,33 @@ mainloop(void) {
}
}
+extern char *optarg;
+extern int optind;
+
static void
setup(int argc, char *argv[]) {
setlocale(LC_CTYPE, "");
XSetLocaleModifiers("");
+ char c;
+ char *opt_filename = NULL;
+ while ((c = getopt(argc, argv, "c:")) != -1) {
+ switch (c) {
+ case 'c':
+ opt_filename = optarg;
+ break;
+ default:
+ fprintf(stderr, "USAGE: ledit [-c config] [file]\n");
+ exit(1);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
common.dpy = XOpenDisplay(NULL);
common.screen = DefaultScreen(common.dpy);
+ /* FIXME: fallback when no db support */
/* based on http://wili.cc/blog/xdbe.html */
int major, minor;
if (XdbeQueryExtension(common.dpy, &major, &minor)) {
@@ -233,6 +254,8 @@ setup(int argc, char *argv[]) {
exit(1);
}
common.vis = xvisinfo_match->visual;
+ XFree(xvisinfo_match);
+ XdbeFreeVisualInfo(info);
} else {
fprintf(stderr, "No Xdbe support.\n");
ledit_cleanup();
@@ -242,15 +265,83 @@ setup(int argc, char *argv[]) {
common.depth = DefaultDepth(common.dpy, common.screen);
common.cm = DefaultColormap(common.dpy, common.screen);
- theme = theme_create(&common);
+ #ifdef LEDIT_DEBUG
+ struct timespec now, elapsed, last;
+ clock_gettime(CLOCK_MONOTONIC, &last);
+ #endif
+
+ char *stat_errstr = NULL, *load_errstr = NULL, *load_default_errstr = NULL;
+ char *cfgfile = NULL;
+ if (!opt_filename) {
+ uid_t uid = getuid();
+ struct passwd *pw = getpwuid(uid);
+ if (!pw)
+ fprintf(stderr, "Unable to determine home directory\n");
+ else
+ cfgfile = ledit_strcat(pw->pw_dir, "/.leditrc");
+ struct stat cfgst;
+ if (stat(cfgfile, &cfgst)) {
+ free(cfgfile);
+ cfgfile = NULL;
+ }
+ } else {
+ struct stat cfgst;
+ if (stat(opt_filename, &cfgst)) {
+ stat_errstr = print_fmt("Unable to load configuration file '%s'", opt_filename);
+ fprintf(stderr, "%s\n", stat_errstr);
+ } else {
+ cfgfile = ledit_strdup(opt_filename);
+ }
+ }
+ if (config_loadfile(&common, cfgfile, &load_errstr)) {
+ fprintf(stderr, "%s\n", load_errstr);
+ int failure = 1;
+ if (cfgfile) {
+ /* retry with default config */
+ failure = config_loadfile(&common, NULL, &load_default_errstr);
+ }
+ if (failure) {
+ fprintf(stderr, "Unable to load configuration: %s\n", load_errstr);
+ if (load_default_errstr)
+ fprintf(stderr, "Also unable to load default configuration: %s\n", load_default_errstr);
+ free(stat_errstr);
+ free(load_errstr);
+ free(load_default_errstr);
+ ledit_cleanup();
+ exit(1);
+ }
+ }
+ free(load_default_errstr);
+ free(cfgfile);
+
+ #ifdef LEDIT_DEBUG
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ ledit_timespecsub(&now, &last, &elapsed);
+ ledit_debug_fmt("Time to load config (total): %lld seconds, %ld nanoseconds\n", (long long)elapsed.tv_sec, elapsed.tv_nsec);
+ #endif
+
buffer = buffer_create(&common);
- buffer_add_view(buffer, theme, NORMAL, 0, 0, 0);
+ buffer_add_view(buffer, NORMAL, 0, 0, 0);
/* FIXME: don't access view directly here */
ledit_view *view = buffer->views[0];
+ /* FIXME: this message may be wiped immediately */
+ /* -> maybe allow showing multiple messages? */
+ /* currently, the more important message is just prioritized */
+ int show_error = 0;
+ if (stat_errstr || load_errstr) {
+ show_error = 1;
+ if (stat_errstr)
+ window_show_message(view->window, stat_errstr, -1);
+ else if (load_errstr)
+ window_show_message(view->window, load_errstr, -1);
+ free(stat_errstr);
+ free(load_errstr);
+ }
view_set_line_cursor_attrs(view, view->cur_line, view->cur_index);
+ /* FIXME: maybe also log all errors instead of just showing them on screen? */
/* FIXME: Support multiple buffers/files */
/* FIXME: check if file may be binary */
- if (argc > 1) {
+ if (argc >= 1) {
/* FIXME: move this to different file */
char *load_err;
struct stat sb;
@@ -258,7 +349,7 @@ setup(int argc, char *argv[]) {
int readonly = 0;
int error = 0;
/* FIXME: maybe copy vi and open file in /tmp by default? */
- if (stat(argv[1], &sb)) {
+ if (stat(argv[0], &sb)) {
if (errno == ENOENT) {
/* note that there may still be a failure
when trying to write if a directory in
@@ -267,32 +358,35 @@ setup(int argc, char *argv[]) {
} else {
window_show_message_fmt(
view->window, "Error opening file '%s': %s",
- argv[1], strerror(errno)
+ argv[0], strerror(errno)
);
error = 1;
}
}
- if (access(argv[1], W_OK)) {
+ if (access(argv[0], W_OK)) {
readonly = 1;
}
if (!newfile) {
- if (buffer_load_file(buffer, argv[1], 0, &load_err)) {
+ if (buffer_load_file(buffer, argv[0], 0, &load_err)) {
window_show_message_fmt(
view->window, "Error opening file '%s': %s",
- argv[1], load_err
+ argv[0], load_err
);
error = 1;
}
buffer->file_mtime = sb.st_mtim;
}
if (!error) {
- buffer->filename = ledit_strdup(argv[1]);
- if (newfile) {
- window_show_message_fmt(view->window, "%s: new file", argv[1]);
- } else if (readonly) {
- window_show_message_fmt(view->window, "%s: readonly", argv[1]);
- } else {
- window_show_message(view->window, argv[1], -1);
+ buffer->filename = ledit_strdup(argv[0]);
+ /* FIXME: show this *in addition* to error */
+ if (!show_error) {
+ if (newfile) {
+ window_show_message_fmt(view->window, "%s: new file", argv[0]);
+ } else if (readonly) {
+ window_show_message_fmt(view->window, "%s: readonly", argv[0]);
+ } else {
+ window_show_message(view->window, argv[0], -1);
+ }
}
}
}
@@ -302,6 +396,8 @@ setup(int argc, char *argv[]) {
void
ledit_emergencydump(void) {
+ /* FIXME: pre-allocate memory for template to avoid memory errors?
+ -> probably overkill since something else will fail anyways */
if (!buffer)
return;
/* FIXME: maybe write assertion message to file? */
@@ -355,8 +451,7 @@ ledit_cleanup(void) {
command_key_cleanup();
if (buffer)
buffer_destroy(buffer);
- if (theme)
- theme_destroy(&common, theme);
+ config_cleanup(&common);
XCloseDisplay(common.dpy);
}
@@ -369,8 +464,8 @@ redraw(void) {
static void
change_keyboard(char *lang) {
- cur_lang = get_language_index(lang);
- if (cur_lang < 0) {
+ ledit_debug_fmt("New keyboard layout: %s\n", lang);
+ if (config_get_language_index(lang, &cur_lang)) {
for (size_t i = 0; i < buffer->views_num; i++) {
window_show_message_fmt(
buffer->views[i]->window,
diff --git a/leditrc.5 b/leditrc.5
@@ -0,0 +1,347 @@
+.Dd May 26, 2022
+.Dt LEDITRC 5
+.Os
+.Sh NAME
+.Nm leditrc
+.Nd configuration file for
+.Xr ledit 1
+.Sh DESCRIPTION
+.Nm
+is the configuration file for the text editor
+.Xr ledit 1 ,
+which can be used to configure the theme and key bindings used.
+.Pp
+The parser recognizes four different types of structures:
+strings, lists, statements, and assignments.
+.Pp
+A string is simply any sequence of characters surrounded by double quotes.
+Double quotes must be backslash-escaped.
+If a string does not contain any whitespace or the special
+characters
+.Sq \&" ,
+.Sq { ,
+.Sq } ,
+or
+.Sq = ,
+the double quotes are not required.
+.Pp
+A statement is a sequence of strings, separated by whitespace and
+all on the same line.
+.Pp
+An assignment is of the form
+.Aq identifier
+=
+.Aq structure ,
+where
+.Aq identifier
+is a string and
+.Aq structure
+is a string or a list.
+.Pp
+A list is a sequence of assignments and/or statements that is
+enclosed by curly braces.
+The assignments/statements must be separated by newlines.
+.Pp
+The configuration file consists of several top-level assignments
+which are described in the following sections.
+.Sh THEME
+The theme may be configured by assigning
+.Ar theme
+to a list of assignments, each of which sets one of the following
+possible properties.
+Colors are given in the form #RRGGBB, where the
+.Sq #
+is optional (mainly because
+.Sq #
+also starts comments in the configuration file format).
+.Bl -tag -width Ds
+.It Ar text-font
+Font used for all text.
+Default: Monospace
+.It Ar text-size
+Text size (in points or whatever pango uses).
+Default: 12
+.It Ar text-fg
+Text color in main editing area.
+Default: #000000
+.It Ar text-bg
+Background color in main editing area.
+Default: #FFFFFF
+.It Ar cursor-fg
+Color of text under cursor.
+Default: #FFFFFF
+.It Ar cursor-bg
+Color of text cursor.
+Default: #000000
+.It Ar selection-fg
+Color of selected text.
+Default: #FFFFFF
+.It Ar selection-bg
+Color of selection.
+Default: #000000
+.It Ar bar-fg
+Color of text in status bar/line editor.
+Default: #000000
+.It Ar bar-bg
+Background color of status bar/line editor.
+Default: #CCCCCC
+.It Ar bar-cursor
+Color of text cursor in line editor.
+Default: #000000
+.It Ar scrollbar-width
+Width of scrollbar in pixels.
+Default: 10
+.It Ar scrollbar-step
+Number of pixels scrolled with each scroll event.
+Default: 20
+.It Ar scrollbar-bg
+Background color of scrollbar.
+Default: #CCCCCC
+.It Ar scrollbar-fg
+Color of scrollbar handle.
+Default: #000000
+.El
+.Sh BINDINGS
+The key bindings may be configured by assigning
+.Ar bindings
+to a list of the following assignments.
+.Bl -tag -width Ds
+.It Ar language
+.Pp
+This is the language string for the key layout, as given by XKB.
+.It Ar basic-keys
+.Pp
+This is a list of statements of the form
+.Pp
+.Sy bind
+.Aq func_name
+.Op Sy keysym Aq keysym
+.Op Sy text Aq text
+.Op Sy catchall
+.Op Sy modes Aq modes
+.Op Sy mods Aq mods
+.Pp
+.Sy keysym
+is the symbolic description for a key, such as
+.Ar space .
+The full list is not documented yet (FIXME!).
+.Sy text
+is the text corresponding to a key.
+.Sy catchall
+is a catchall for any key which can for instance be used to insert text.
+Note that a key binding containing
+.Sy catchall
+should always be at the end of the list so it does not prevent
+any other key bindings from being used.
+.Pp
+Exactly one of
+.Sy text ,
+.Sy keysym ,
+and
+.Sy catchall
+must be specified.
+.Pp
+.Sy mods
+specifies modifier keys.
+The current options are
+.Ar shift ,
+.Ar lock ,
+.Ar control ,
+.Ar mod1 ,
+.Ar mod2 ,
+.Ar mod3 ,
+.Ar mod4 ,
+.Ar mod5 ,
+and
+.Ar any .
+.Pp
+.Sy modes
+specifies the allowed modes and can be a combination of
+.Ar normal ,
+.Ar visual ,
+and
+.Ar insert .
+.Pp
+Multiple mods or modes can be given by joining them with
+.Sq | .
+.Pp
+.Aq func_name
+may be one of the following functions.
+The possible modes are listed beside the function names.
+.Bl -tag -width Ds
+.It Ar append-after-cursor Op normal
+.It Ar append-after-eol Op normal
+.It Ar append-line-above Op normal
+.It Ar append-line-below Op normal
+.It Ar backspace Op insert
+.It Ar change Op normal, visual
+.It Ar change-to-eol Op normal
+.It Ar clipboard-copy Op visual
+.It Ar clipboard-paste Op insert
+.It Ar cursor-down Op normal, visual, insert
+.It Ar cursor-left Op normal, visual, insert
+.It Ar cursor-right Op normal, visual, insert
+.It Ar cursor-to-beginning Op normal, visual
+.It Ar cursor-to-first-non-whitespace Op normal
+.It Ar cursor-up Op normal, visual, insert
+.It Ar delete Op normal, visual
+.It Ar delete-backwards Op normal
+.It Ar delete-forwards Op normal
+.It Ar delete-key Op insert
+.It Ar delete-to-eol Op normal
+.It Ar enter-commandedit Op normal, visual
+.It Ar enter-insert Op normal, visual
+.It Ar enter-searchedit-backwards Op normal
+.It Ar enter-searchedit-forwards Op normal
+.It Ar enter-visual Op normal
+.It Ar escape-key Op normal, visual, insert
+.It Ar find-char-backwards Op normal, visual
+.It Ar find-char-forwards Op normal, visual
+.It Ar find-next-char-backwards Op normal, visual
+.It Ar find-next-char-forwards Op normal, visual
+.It Ar insert-at-beginning Op normal
+.It Ar insert-text Op insert
+.It Ar join-lines Op normal
+.It Ar jump-to-mark Op normal, visual
+.It Ar key-0 Op normal, visual
+.It Ar mark-line Op normal, visual
+.It Ar move-to-eol Op normal, visual
+.It Ar move-to-line Op normal, visual
+.It Ar next-bigword Op normal, visual
+.It Ar next-bigword-end Op normal, visual
+.It Ar next-word Op normal, visual
+.It Ar next-word-end Op normal, visual
+.It Ar paste-normal Op normal
+.It Ar paste-normal-backwards normal
+.It Ar previous-bigword Op normal, visual
+.It Ar previous-word Op normal, visual
+.It Ar push-0 Op normal, visual
+.It Ar push-1 Op normal, visual
+.It Ar push-2 Op normal, visual
+.It Ar push-3 Op normal, visual
+.It Ar push-4 Op normal, visual
+.It Ar push-5 Op normal, visual
+.It Ar push-6 Op normal, visual
+.It Ar push-7 Op normal, visual
+.It Ar push-8 Op normal, visual
+.It Ar push-9 Op normal, visual
+.It Ar redo Op normal, insert
+.It Ar repeat-command Op normal
+.It Ar replace Op normal
+.It Ar return-key Op insert
+.It Ar screen-down Op normal
+.It Ar screen-up Op normal
+.It Ar scroll-lines-down Op normal
+.It Ar scroll-lines-up Op normal
+.It Ar scroll-with-cursor-down Op normal
+.It Ar scroll-with-cursor-up Op normal
+.It Ar search-next Op normal
+.It Ar search-previous Op normal
+.It Ar show-line Op normal, visual
+.It Ar switch-selection-end Op visual
+.It Ar toggle-hard-line-based Op normal, visual
+.It Ar undo Op normal, insert
+.It Ar yank Op normal, visual
+.It Ar yank-lines Op normal
+.El
+.Pp
+Note that some of these functions should work in other modes
+as well, but I still need to fix that (FIXME!).
+.It Ar command-keys
+.Pp
+This is the same as
+.Ar basic-keys ,
+except that
+.Sy modes
+must be a combination of
+.Ar substitute ,
+.Ar edit ,
+.Ar edit-search ,
+and
+.Ar edit-search-backwards .
+.Pp
+The possible functions are given in the following list, with
+the possible modes listed beside each function.
+.Bl -tag -width Ds
+.It Ar edit-backspace Op edit, edit-search, edit-search-backwards
+.It Ar edit-cursor-left Op edit, edit-search, edit-search-backwards
+.It Ar edit-cursor-right Op edit, edit-search, edit-search-backwards
+.It Ar edit-cursor-to-beginning Op edit, edit-search, edit-search-backwards
+.It Ar edit-cursor-to-end Op edit, edit-search, edit-search-backwards
+.It Ar edit-delete Op edit, edit-search, edit-search-backwards
+.It Ar edit-discard Op edit, edit-search, edit-search-backwards
+.It Ar edit-insert-text Op edit, edit-search, edit-search-backwards
+.It Ar edit-next-command Op edit
+.It Ar edit-next-search Op edit-search, edit-search-backwards
+.It Ar edit-previous-command Op edit
+.It Ar edit-previous-search Op edit-search, edit-search-backwards
+.It Ar edit-submit Op edit
+.It Ar edit-submit-backwards-search Op edit-search-backwards
+.It Ar edit-submit-search Op edit-search
+.It Ar substitute-no Op substitute
+.It Ar substitute-no-all Op substitute
+.It Ar substitute-yes Op substitute
+.It Ar substitute-yes-all Op substitute
+.El
+.It Ar commands
+.Pp
+This is a list of statements of the form
+.Pp
+.Sy bind
+.Aq func_name
+.Aq text
+.Pp
+The possible functions are given in the following list.
+.Bl -tag -width Ds
+.It Ar close-view
+.It Ar create-view
+.It Ar quit
+.It Ar substitute
+.It Ar write
+.It Ar write-quit
+.El
+.El
+.Pp
+If the
+.Ar bindings
+configuration or any part of it is left out, the
+default is used.
+.Sh LANGUAGE MAPPINGS
+A language mapping defines a mapping for the text associated with each
+key or command so the bindings still work with other keyboard layouts.
+Language mappings may be defined by assigning
+.Ar language-mapping
+to a list of the following assignments, once for each language.
+Note that any definition of
+.Ar language-mapping
+must come after
+.Ar bindings .
+.Bl -tag -width Ds
+.It Ar language
+.Pp
+This is the language string for the key layout, as in
+.Ar bindings .
+.It Ar key-mapping
+.Pp
+This is a list of statements of the form
+.Pp
+.Sy map
+.Aq foreign
+.Aq native
+.Pp
+where
+.Aq foreign
+is the key text in the new language mapping and
+.Aq native
+is the key text given in
+.Ar bindings .
+.It Ar command-mapping
+.Pp
+This is the same as
+.Ar key-mapping ,
+but for the commands.
+.El
+.Sh SEE ALSO
+.Xr ledit 1
+.Sh AUTHORS
+.An lumidify Aq Mt nobody@lumidify.org
diff --git a/leditrc.example b/leditrc.example
@@ -0,0 +1,300 @@
+theme = {
+ text-font = Monospace
+ text-size = 12
+ text-fg = 000000
+ text-bg = FFFFFF
+ cursor-fg = FFFFFF
+ cursor-bg = 000000
+ selection-fg = ffffff
+ selection-bg = 000000
+ bar-fg = 000000
+ bar-bg = CCCCCC
+ bar-cursor = 000000
+ scrollbar-width = 10
+ scrollbar-step = 20
+ scrollbar-bg = CCCCCC
+ scrollbar-fg = 000000
+}
+
+bindings = {
+ language = "English (US)"
+ basic-keys = {
+ bind backspace keysym backspace modes insert
+ bind cursor-left keysym left modes visual|insert|normal
+ bind cursor-right keysym right modes visual|insert|normal
+ bind cursor-up keysym up modes visual|insert|normal
+ bind cursor-down keysym down modes visual|insert|normal
+ bind return-key keysym return modes insert mods any
+ bind delete-key keysym delete modes insert mods any
+ bind escape-key keysym escape modes normal|visual|insert mods any
+ bind enter-insert text "i" modes normal|visual
+ bind cursor-left text "h" modes normal|visual
+ bind cursor-right text "l" modes normal|visual
+ bind cursor-down text "j" modes normal|visual
+ bind cursor-up text "k" modes normal|visual
+ bind cursor-left text "h" modes normal|visual mods control
+ bind toggle-hard-line-based text "t" modes normal|visual mods control
+ bind cursor-right keysym space modes normal|visual
+ bind cursor-down text "j" modes normal|visual mods control
+ bind cursor-down text "n" modes normal|visual mods control
+ bind cursor-up text "p" modes normal|visual mods control
+ bind key-0 text "0" modes normal|visual
+ bind push-1 text "1" modes normal|visual
+ bind push-2 text "2" modes normal|visual
+ bind push-3 text "3" modes normal|visual
+ bind push-4 text "4" modes normal|visual
+ bind push-5 text "5" modes normal|visual
+ bind push-6 text "6" modes normal|visual
+ bind push-7 text "7" modes normal|visual
+ bind push-8 text "8" modes normal|visual
+ bind push-9 text "9" modes normal|visual
+ bind delete-forwards text "x" modes normal
+ bind delete-backwards text "X" modes normal
+ bind delete text "d" modes normal|visual
+ bind yank text "y" modes normal|visual
+ bind yank-lines text "Y" modes normal
+ bind change text "c" modes normal|visual
+ bind enter-visual text "v" modes normal
+ bind switch-selection-end text "o" modes visual
+ bind clipboard-copy text "c" modes visual mods control
+ bind clipboard-paste text "v" modes insert mods control
+ bind show-line text "g" modes normal|visual mods control
+ bind enter-commandedit text ":" modes normal|visual
+ bind enter-searchedit-backwards text "?" modes normal
+ bind enter-searchedit-forwards text "/" modes normal
+ bind search-next text "n" modes normal
+ bind search-previous text "N" modes normal
+ bind undo text "u" modes normal
+ bind redo text "U" modes normal
+ bind repeat-command text "." modes normal
+ bind undo text "z" modes insert mods control
+ bind redo text "y" modes insert mods control
+ bind screen-up text "b" modes normal mods control
+ bind screen-down text "f" modes normal mods control
+ bind scroll-with-cursor-down text "e" modes normal mods control
+ bind scroll-with-cursor-up text "y" modes normal mods control
+ bind scroll-lines-down text "d" modes normal mods control
+ bind scroll-lines-up text "u" modes normal mods control
+ bind move-to-eol text "$" modes normal|visual
+ bind next-word text "w" modes normal|visual
+ bind next-word-end text "e" modes normal|visual
+ bind next-bigword text "W" modes normal|visual
+ bind next-bigword-end text "E" modes normal|visual
+ bind previous-word text "b" modes normal|visual
+ bind previous-bigword text "B" modes normal|visual
+ bind move-to-line text "G" modes normal|visual
+ bind join-lines text "J" modes normal
+ bind insert-at-beginning text "I" modes normal
+ bind paste-normal text "p" modes normal
+ bind paste-normal-backwards text "P" modes normal
+ bind append-after-eol text "A" modes normal
+ bind append-after-cursor text "a" modes normal
+ bind append-line-above text "O" modes normal
+ bind append-line-below text "o" modes normal
+ bind mark-line text "m" modes normal|visual
+ bind jump-to-mark text "'" modes normal|visual
+ bind change-to-eol text "C" modes normal
+ bind delete-to-eol text "D" modes normal
+ bind replace text "r" modes normal
+ bind cursor-to-first-non-whitespace text "^" modes normal
+ bind find-next-char-forwards text "t" modes normal|visual
+ bind find-next-char-backwards text "T" modes normal|visual
+ bind find-char-forwards text "f" modes normal|visual
+ bind find-char-backwards text "F" modes normal|visual
+ bind insert-text catchall modes insert
+ }
+ command-keys = {
+ bind substitute-yes text "y" modes substitute
+ bind substitute-yes-all text "Y" modes substitute
+ bind substitute-no text "n" modes substitute
+ bind substitute-no-all text "N" modes substitute
+ bind edit-submit keysym return mods any modes edit
+ bind edit-submit-search keysym return mods any modes edit-search
+ bind edit-submit-backwards-search keysym return mods any modes edit-search-backwards
+ bind edit-cursor-left keysym left modes edit|edit-search|edit-search-backwards
+ bind edit-cursor-right keysym right modes edit|edit-search|edit-search-backwards
+ bind edit-previous-command keysym up modes edit
+ bind edit-next-command keysym down modes edit
+ bind edit-previous-search keysym up modes edit-search|edit-search-backwards
+ bind edit-next-search keysym down modes edit-search|edit-search-backwards
+ bind edit-backspace keysym backspace modes edit|edit-search|edit-search-backwards
+ bind edit-delete keysym delete modes edit|edit-search|edit-search-backwards
+ bind edit-cursor-to-end keysym end modes edit|edit-search|edit-search-backwards
+ bind edit-cursor-to-beginning keysym home modes edit|edit-search|edit-search-backwards
+ bind edit-discard keysym escape modes edit|edit-search|edit-search-backwards
+ bind edit-insert-text catchall modes edit|edit-search|edit-search-backwards
+ }
+ commands = {
+ bind write-quit "wq"
+ bind write "w"
+ bind quit "q"
+ bind create-view "v"
+ bind close-view "c"
+ bind substitute "s"
+ }
+}
+
+language-mapping = {
+ language = "German"
+ key-mapping = {
+ map "z" "y"
+ map "y" "z"
+ map "Z" "Y"
+ map "Ö" ":"
+ map "_" "?"
+ map "-" "/"
+ map "ä" "'"
+ }
+ command-mapping = {
+ map "wq" "wq"
+ map "w" "w"
+ map "q" "q"
+ map "v" "v"
+ map "c" "c"
+ map "s" "s"
+ }
+}
+
+language-mapping = {
+ language = "Hindi (Bolnagri)"
+ key-mapping = {
+ map "0" "0"
+ map "1" "1"
+ map "2" "2"
+ map "3" "3"
+ map "4" "4"
+ map "5" "5"
+ map "6" "6"
+ map "7" "7"
+ map "8" "8"
+ map "9" "9"
+ map "ा" "a"
+ map "आ" "A"
+ map "ब" "b"
+ map "भ" "B"
+ map "च" "c"
+ map "छ" "C"
+ map "द" "d"
+ map "ध" "D"
+ map "े" "e"
+ map "ै" "E"
+ map "ट" "f"
+ map "ठ" "F"
+ map "ग" "g"
+ map "घ" "G"
+ map "ह" "h"
+ map "ि" "i"
+ map "ी" "I"
+ map "ज" "j"
+ map "झ" "J"
+ map "क" "k"
+ map "ल" "l"
+ map "म" "m"
+ map "न" "n"
+ map "ण" "N"
+ map "ो" "o"
+ map "ौ" "O"
+ map "प" "p"
+ map "फ" "P"
+ map "र" "r"
+ map "त" "t"
+ map "थ" "T"
+ map "ु" "u"
+ map "ू" "U"
+ map "ड" "v"
+ map "व" "w"
+ map "ॐ" "W"
+ map "्" "x"
+ map "ॉ" "X"
+ map "य" "y"
+ map "ञ" "Y"
+ map "श" "z"
+ map ":" ":"
+ map "?" "?"
+ map "/" "/"
+ map "." "."
+ map "$" "$"
+ map "'" "'"
+ map "^" "^"
+ }
+ command-mapping = {
+ map "वग" "wq"
+ map "व" "w"
+ map "" "q"
+ map "ड" "v"
+ map "च" "c"
+ map "स" "s"
+ }
+}
+
+language-mapping = {
+ language = "Urdu (Pakistan)"
+ key-mapping = {
+ map "0" "0"
+ map "1" "1"
+ map "2" "2"
+ map "3" "3"
+ map "4" "4"
+ map "5" "5"
+ map "6" "6"
+ map "7" "7"
+ map "8" "8"
+ map "9" "9"
+ map "ا" "a"
+ map "آ" "A"
+ map "ب" "b"
+ map "." "B"
+ map "چ" "c"
+ map "ث" "C"
+ map "د" "d"
+ map "ڈ" "D"
+ map "ع" "e"
+ map "ٰ" "E"
+ map "ف" "f"
+ map "ّ" "F"
+ map "گ" "g"
+ map "غ" "G"
+ map "ح" "h"
+ map "ی" "i"
+ map "ِ" "I"
+ map "ج" "j"
+ map "ض" "J"
+ map "ک" "k"
+ map "ل" "l"
+ map "م" "m"
+ map "ن" "n"
+ map "ں" "N"
+ map "ہ" "o"
+ map "ۃ" "O"
+ map "پ" "p"
+ map "ُ" "P"
+ map "ر" "r"
+ map "ت" "t"
+ map "ٹ" "T"
+ map "ء" "u"
+ map "ئ" "U"
+ map "ط" "v"
+ map "و" "w"
+ map "ؤ" "W"
+ map "ش" "x"
+ map "ژ" "X"
+ map "ے" "y"
+ map "َ" "Y"
+ map "ز" "z"
+ map ":" ":"
+ map "؟" "?"
+ map "/" "/"
+ map "۔" "."
+ map "$" "$"
+ map "'" "'"
+ map "^" "^"
+ }
+ command-mapping = {
+ map "وگ" "wq"
+ map "و" "w"
+ map "ق" "q"
+ map "ط" "v"
+ map "چ" "c"
+ map "س" "s"
+ }
+}
diff --git a/memory.c b/memory.c
@@ -10,6 +10,7 @@
static void
fatal_err(const char *msg) {
fprintf(stderr, "%s", msg);
+ /* FIXME: maybe don't cleanup here - it will probably fail anyways */
ledit_cleanup();
exit(1);
}
@@ -82,6 +83,23 @@ ledit_strcat(const char *str1, const char *str2) {
return ret;
}
+char *
+print_fmt(char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int len = vsnprintf(NULL, 0, fmt, args);
+ /* FIXME: what should be done on error? */
+ if (len < 0)
+ fatal_err("Error in vsnprintf called from print_fmt");
+ /* FIXME: overflow */
+ char *str = ledit_malloc(len + 1);
+ va_end(args);
+ va_start(args, fmt);
+ vsnprintf(str, len + 1, fmt, args);
+ va_end(args);
+ return str;
+}
+
/*
* This (reallocarray) is from OpenBSD (adapted to exit on error):
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
@@ -96,11 +114,11 @@ ledit_strcat(const char *str1, const char *str2) {
void *
ledit_reallocarray(void *optr, size_t nmemb, size_t size)
{
- if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
- nmemb > 0 && SIZE_MAX / nmemb < size) {
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size) {
err_overflow();
- }
- return ledit_realloc(optr, size * nmemb);
+ }
+ return ledit_realloc(optr, size * nmemb);
}
void
diff --git a/memory.h b/memory.h
@@ -3,6 +3,7 @@
#include <stddef.h>
#include <stdint.h>
+#include <stdarg.h>
/*
* These functions all wrap the regular functions but exit on error.
@@ -19,6 +20,11 @@ void *ledit_realloc(void *ptr, size_t size);
*/
char *ledit_strcat(const char *str1, const char *str2);
+/* This acts like snprintf but automatically allocates
+ a string of the appropriate size.
+ Like the other functions here, it exits on error. */
+char *print_fmt(char *fmt, ...);
+
/*
* This is like OpenBSD's reallocarray but exits on error.
*/
diff --git a/theme.c b/theme.c
@@ -1,57 +0,0 @@
-#include <stddef.h>
-
-#include <X11/Xlib.h>
-#include <X11/Xft/Xft.h>
-
-#include "memory.h"
-#include "common.h"
-#include "theme.h"
-#include "theme_config.h"
-
-ledit_theme *
-theme_create(ledit_common *common) {
- ledit_theme *theme = ledit_malloc(sizeof(ledit_theme));
- theme->scrollbar_width = SCROLLBAR_WIDTH;
- theme->scrollbar_step = SCROLLBAR_STEP;
- theme->text_font = TEXT_FONT;
- theme->text_size = TEXT_SIZE;
- theme->text_fg_hex = TEXT_FG;
- theme->text_bg_hex = TEXT_BG;
- theme->cursor_fg_hex = CURSOR_FG;
- theme->cursor_bg_hex = CURSOR_BG;
- theme->selection_fg_hex = SELECTION_FG;
- theme->selection_bg_hex = SELECTION_BG;
- theme->bar_fg_hex = BAR_FG;
- theme->bar_bg_hex = BAR_BG;
- theme->bar_cursor_hex = BAR_CURSOR;
- theme->scrollbar_fg_hex = SCROLLBAR_FG;
- theme->scrollbar_bg_hex = SCROLLBAR_BG;
- XftColorAllocName(common->dpy, common->vis, common->cm, TEXT_FG, &theme->text_fg);
- XftColorAllocName(common->dpy, common->vis, common->cm, TEXT_BG, &theme->text_bg);
- XftColorAllocName(common->dpy, common->vis, common->cm, CURSOR_FG, &theme->cursor_fg);
- XftColorAllocName(common->dpy, common->vis, common->cm, CURSOR_BG, &theme->cursor_bg);
- XftColorAllocName(common->dpy, common->vis, common->cm, SELECTION_FG, &theme->selection_fg);
- XftColorAllocName(common->dpy, common->vis, common->cm, SELECTION_BG, &theme->selection_bg);
- XftColorAllocName(common->dpy, common->vis, common->cm, BAR_FG, &theme->bar_fg);
- XftColorAllocName(common->dpy, common->vis, common->cm, BAR_BG, &theme->bar_bg);
- XftColorAllocName(common->dpy, common->vis, common->cm, BAR_CURSOR, &theme->bar_cursor);
- XftColorAllocName(common->dpy, common->vis, common->cm, SCROLLBAR_FG, &theme->scrollbar_fg);
- XftColorAllocName(common->dpy, common->vis, common->cm, SCROLLBAR_BG, &theme->scrollbar_bg);
- return theme;
-}
-
-void
-theme_destroy(ledit_common *common, ledit_theme *theme) {
- XftColorFree(common->dpy, common->vis, common->cm, &theme->text_fg);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->text_bg);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->cursor_fg);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->cursor_bg);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->selection_fg);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->selection_bg);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->bar_fg);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->bar_bg);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->bar_cursor);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->scrollbar_fg);
- XftColorFree(common->dpy, common->vis, common->cm, &theme->scrollbar_bg);
- free(theme);
-}
diff --git a/theme.h b/theme.h
@@ -1,39 +0,0 @@
-#ifndef _THEME_H_
-#define _THEME_H_
-
-#include <X11/Xft/Xft.h>
-#include "common.h"
-
-typedef struct {
- int scrollbar_width;
- int scrollbar_step;
- int text_size;
- XftColor text_fg;
- XftColor text_bg;
- XftColor cursor_fg;
- XftColor cursor_bg;
- XftColor selection_fg;
- XftColor selection_bg;
- XftColor bar_fg;
- XftColor bar_bg;
- XftColor bar_cursor;
- XftColor scrollbar_fg;
- XftColor scrollbar_bg;
- const char *text_font;
- const char *text_fg_hex;
- const char *text_bg_hex;
- const char *cursor_fg_hex;
- const char *cursor_bg_hex;
- const char *selection_fg_hex;
- const char *selection_bg_hex;
- const char *bar_fg_hex;
- const char *bar_bg_hex;
- const char *bar_cursor_hex;
- const char *scrollbar_fg_hex;
- const char *scrollbar_bg_hex;
-} ledit_theme;
-
-ledit_theme *theme_create(ledit_common *common);
-void theme_destroy(ledit_common *common, ledit_theme *theme);
-
-#endif
diff --git a/theme_config.h b/theme_config.h
@@ -1,7 +1,19 @@
+#ifndef _THEME_CONFIG_H_
+#define _THEME_CONFIG_H_
+
+/***********************
+ * Theme configuration *
+ ***********************/
+
+/* Note: Integer values have to be given as strings because
+ that simplifies some things in the config parser. */
+
+/* FIXME: Check what happens when 0 is given for integer values */
+
/* font used for all text */
static const char *TEXT_FONT = "Monospace";
/* size used for text in points or whatever pango uses */
-static const int TEXT_SIZE = 12;
+static const char *TEXT_SIZE = "12";
/* text color of main text area */
static const char *TEXT_FG = "#000000";
/* background color of main text area */
@@ -24,10 +36,12 @@ static const char *BAR_CURSOR = "#000000";
/* FIXME: give in units other than pixels */
/* scrollbar width in pixels */
-static const int SCROLLBAR_WIDTH = 10;
+static const char *SCROLLBAR_WIDTH = "10";
/* number of pixels scrolled with every scroll event */
-static const int SCROLLBAR_STEP = 20;
+static const char *SCROLLBAR_STEP = "20";
/* background color of scrollbar */
static const char *SCROLLBAR_BG = "#CCCCCC";
/* color of scrollbar handle */
static const char *SCROLLBAR_FG = "#000000";
+
+#endif /* _THEME_CONFIG_H_ */
diff --git a/txtbuf.c b/txtbuf.c
@@ -1,5 +1,7 @@
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
#include "util.h"
#include "memory.h"
@@ -14,16 +16,49 @@ txtbuf_new(void) {
return buf;
}
+txtbuf *
+txtbuf_new_from_char(char *str) {
+ txtbuf *buf = ledit_malloc(sizeof(txtbuf));
+ buf->text = ledit_strdup(str);
+ buf->len = strlen(str);
+ buf->cap = buf->len + 1;
+ return buf;
+}
+
+txtbuf *
+txtbuf_new_from_char_len(char *str, size_t len) {
+ txtbuf *buf = ledit_malloc(sizeof(txtbuf));
+ buf->text = ledit_strndup(str, len);
+ buf->len = len;
+ buf->cap = len + 1;
+ return buf;
+}
+
+void
+txtbuf_fmt(txtbuf *buf, char *fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
+ int len = vsnprintf(buf->text, buf->cap, fmt, args);
+ /* FIXME: len can never be negative, right? */
+ /* FIXME: maybe also shrink here */
+ if ((size_t)len >= buf->cap) {
+ va_end(args);
+ va_start(args, fmt);
+ txtbuf_resize(buf, len);
+ vsnprintf(buf->text, buf->cap, fmt, args);
+ }
+ buf->len = len;
+ va_end(args);
+}
+
void
txtbuf_resize(txtbuf *buf, size_t sz) {
/* always leave room for extra \0 */
- /* FIXME: '\0' isn't actually used anywhere */
size_t cap = ideal_array_size(buf->cap, add_sz(sz, 1));
if (cap != buf->cap) {
buf->text = ledit_realloc(buf->text, cap);
buf->cap = cap;
}
- ledit_assert(buf->cap >= add_sz(sz, 1));
}
void
@@ -38,6 +73,7 @@ void
txtbuf_copy(txtbuf *dst, txtbuf *src) {
txtbuf_resize(dst, src->len);
memcpy(dst->text, src->text, src->len);
+ dst->text[src->len] = '\0';
dst->len = src->len;
}
@@ -47,3 +83,22 @@ txtbuf_dup(txtbuf *src) {
txtbuf_copy(dst, src);
return dst;
}
+
+int
+txtbuf_cmp(txtbuf *buf1, txtbuf *buf2) {
+ /* FIXME: I guess strcmp would be possible as well since it's nul-terminated now */
+ /* FIXME: Test this because I was tired while writing it */
+ int cmp = strncmp(buf1->text, buf2->text, LEDIT_MIN(buf1->len, buf2->len));
+ if (cmp == 0) {
+ if (buf1->len < buf2->len)
+ return -1;
+ else if (buf1->len > buf2->len)
+ return 1;
+ }
+ return cmp;
+}
+
+int
+txtbuf_eql(txtbuf *buf1, txtbuf *buf2) {
+ return txtbuf_cmp(buf1, buf2) == 0;
+}
diff --git a/txtbuf.h b/txtbuf.h
@@ -5,6 +5,7 @@
/*
* txtbuf is really just a string data type that is badly named.
+ * The stored text is always nul-terminated.
*/
typedef struct {
@@ -18,6 +19,35 @@ typedef struct {
txtbuf *txtbuf_new(void);
/*
+ * Create a new txtbuf, initializing it with the nul-terminated
+ * string 'str'. The input string is copied.
+ */
+txtbuf *txtbuf_new_from_char(char *str);
+
+/*
+ * Create a new txtbuf, initializing it with the string 'str'
+ * of length 'len'. The input string is copied.
+ */
+txtbuf *txtbuf_new_from_char_len(char *str, size_t len);
+
+/*
+ * Replace the stored text in 'buf' with the text generated by
+ * 'snprintf' when called with the given format string and args.
+ */
+void txtbuf_fmt(txtbuf *buf, char *fmt, ...);
+
+/*
+ * Compare the text of two txtbuf's like 'strcmp'.
+ */
+int txtbuf_cmp(txtbuf *buf1, txtbuf *buf2);
+
+/*
+ * Convenience function for calling 'txtbuf_cmp' and checking if the
+ * return value is 0, i.e. the strings are equal.
+ */
+int txtbuf_eql(txtbuf *buf1, txtbuf *buf2);
+
+/*
* Make sure the txtbuf has space for at least the given size,
* plus '\0' at the end.
*/
diff --git a/uglycrap.h b/uglycrap.h
@@ -0,0 +1,15 @@
+#ifndef _UGLYCRAP_H_
+#define _UGLYCRAP_H_
+
+/* FIXME: Figure out where to put it - it would make sens to put it in
+ keys_command.h, but it is needed by view.h to make the command mode
+ per-view, but I don't want view.* to depend on keys_command.h */
+
+typedef enum command_mode {
+ CMD_EDIT = 1, /* edit command */
+ CMD_EDITSEARCH = 2, /* edit search term */
+ CMD_EDITSEARCHB = 4, /* edit search term for backwards search */
+ CMD_SUBSTITUTE = 8 /* confirm substitution */
+} command_mode;
+
+#endif
diff --git a/undo.h b/undo.h
@@ -73,6 +73,7 @@ void undo_change_mode_group(undo_stack *undo);
/*
* Push an insert action onto the undo stack.
* See documentation at top for details on the other arguments.
+ * 'text' is copied, so the original should be freed.
*/
void undo_push_insert(
undo_stack *undo, txtbuf *text,
@@ -83,6 +84,7 @@ void undo_push_insert(
/*
* Push an delete action onto the undo stack.
* See documentation at top for details on the other arguments.
+ * 'text' is copied, so the original should be freed.
*/
void undo_push_delete(
undo_stack *undo, txtbuf *text,
diff --git a/util.c b/util.c
@@ -1,3 +1,4 @@
+#include <string.h>
#include <stddef.h>
#include "memory.h"
@@ -9,6 +10,15 @@ next_utf8(char *str) {
}
size_t
+next_utf8_len(char *str, size_t len) {
+ size_t cur = 0;
+ while (cur < len && (str[cur] & 0xC0) == 0x80)
+ cur++;
+ return cur;
+}
+
+/* FIXME: change these to macros somehow */
+size_t
add_sz(size_t a, size_t b) {
if (a > SIZE_MAX - b)
err_overflow();
@@ -38,3 +48,13 @@ sort_range(size_t *l1, size_t *b1, size_t *l2, size_t *b2) {
swap_sz(b1, b2);
}
}
+
+int
+str_array_equal(char *terminated, char *array, size_t len) {
+ if (!strncmp(terminated, array, len)) {
+ /* 'terminated' and 'array' are equal for the first 'len'
+ characters, so this index in 'terminated' must exist */
+ return terminated[len] == '\0';
+ }
+ return 0;
+}
diff --git a/util.h b/util.h
@@ -9,6 +9,13 @@
char *next_utf8(char *str);
/*
+ * Same as above, but also works with non-nul-terminated strings.
+ * Instead of a pointer, the index of the next utf8 char is
+ * returned.
+ */
+size_t next_utf8_len(char *str, size_t len);
+
+/*
* Add size_t values and abort if overflow would occur.
* FIXME: Maybe someone with actual experience could tell me
* if this overflow checking actually works.
@@ -19,4 +26,25 @@ size_t add_sz3(size_t a, size_t b, size_t c);
void swap_sz(size_t *a, size_t *b);
void sort_range(size_t *l1, size_t *b1, size_t *l2, size_t *b2);
+/*
+ * Compare the nul-terminated string 'terminated' with the char
+ * array 'array' with length 'len'.
+ * Returns non-zero if they are equal, 0 otherwise.
+ */
+/* Note: this doesn't work if array contains '\0'. */
+int str_array_equal(char *terminated, char *array, size_t len);
+
+#define LEDIT_MIN(x, y) ((x) < (y) ? (x) : (y))
+#define LEDIT_MAX(x, y) ((x) > (y) ? (x) : (y))
+
+/* Apparently, ISO C99 requires at least one argument for
+ variadic macros, so there are two versions of the macro here. */
+#ifdef LEDIT_DEBUG
+ #define ledit_debug(fmt) do {fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__);} while (0)
+ #define ledit_debug_fmt(fmt, ...) do {fprintf(stderr, "%s:%d: " fmt, __FILE__, __LINE__, __VA_ARGS__);} while (0)
+#else
+ #define ledit_debug(fmt) do {} while (0)
+ #define ledit_debug_fmt(fmt, ...) do {} while (0)
+#endif
+
#endif
diff --git a/view.c b/view.c
@@ -18,10 +18,10 @@
#include "txtbuf.h"
#include "undo.h"
#include "cache.h"
-#include "theme.h"
#include "window.h"
#include "buffer.h"
#include "assert.h"
+#include "configparser.h"
/* Basic attributes set for all text. */
static PangoAttrList *basic_attrs = NULL;
@@ -96,7 +96,7 @@ view_set_mode(ledit_view *view, ledit_mode mode) {
}
ledit_view *
-view_create(ledit_buffer *buffer, ledit_theme *theme, ledit_mode mode, size_t line, size_t pos) {
+view_create(ledit_buffer *buffer, ledit_mode mode, size_t line, size_t pos) {
if (basic_attrs == NULL) {
basic_attrs = pango_attr_list_new();
#if PANGO_VERSION_CHECK(1, 44, 0)
@@ -108,8 +108,7 @@ view_create(ledit_buffer *buffer, ledit_theme *theme, ledit_mode mode, size_t li
ledit_view *view = ledit_malloc(sizeof(ledit_view));
view->mode = mode;
view->buffer = buffer;
- view->window = window_create(buffer->common, theme, mode);
- view->theme = theme;
+ view->window = window_create(buffer->common, mode);
view->cache = cache_create(buffer->common->dpy);
view->lock_text = NULL;
view->cur_action = (struct action){ACTION_NONE, NULL};
@@ -328,12 +327,13 @@ get_pango_attributes(size_t start_byte, size_t end_byte, XRenderColor fg, XRende
/* this takes layout directly to possibly avoid infinite recursion */
static void
set_line_layout_attrs(ledit_view *view, size_t line, PangoLayout *layout) {
+ ledit_theme *theme = config_get_theme();
ledit_line *ll = buffer_get_line(view->buffer, line);
ledit_view_line *vl = view_get_line(view, line);
PangoAttrList *list = NULL;
if (view->sel_valid) {
- XRenderColor fg = view->theme->selection_fg.color;
- XRenderColor bg = view->theme->selection_bg.color;
+ XRenderColor fg = theme->selection_fg.color;
+ XRenderColor bg = theme->selection_bg.color;
ledit_range sel = view->sel;
sort_range(&sel.line1, &sel.byte1, &sel.line2, &sel.byte2);
if (sel.line1 < line && sel.line2 > line) {
@@ -349,8 +349,8 @@ set_line_layout_attrs(ledit_view *view, size_t line, PangoLayout *layout) {
list = get_pango_attributes(0, sel.byte2, fg, bg);
}
} else if (vl->cursor_index_valid) {
- XRenderColor fg = view->theme->cursor_fg.color;
- XRenderColor bg = view->theme->cursor_bg.color;
+ XRenderColor fg = theme->cursor_fg.color;
+ XRenderColor bg = theme->cursor_bg.color;
/* FIXME: does just adding one really do the right thing? */
list = get_pango_attributes(vl->cursor_index, vl->cursor_index + 1, fg, bg);
}
@@ -392,6 +392,7 @@ line_visible_callback(void *data, size_t line) {
/* FIXME: standardize variable names (line/line_index, etc.) */
void
render_line(ledit_view *view, size_t line_index) {
+ ledit_theme *theme = config_get_theme();
/* FIXME: check for <= 0 on size */
ledit_view_line *ll = view_get_line(view, line_index);
ledit_assert(!ll->h_dirty); /* FIXME */
@@ -430,8 +431,8 @@ render_line(ledit_view *view, size_t line_index) {
pix->h = new_h;
XftDrawChange(pix->draw, pix->pixmap);
}
- XftDrawRect(pix->draw, &view->theme->text_bg, 0, 0, ll->w, ll->h);
- pango_xft_render_layout(pix->draw, &view->theme->text_fg, layout, 0, 0);
+ XftDrawRect(pix->draw, &theme->text_bg, 0, 0, ll->w, ll->h);
+ pango_xft_render_layout(pix->draw, &theme->text_fg, layout, 0, 0);
ll->dirty = 0;
}
@@ -1839,6 +1840,7 @@ view_button_handler(void *data, XEvent *event) {
static void
view_redraw_text(ledit_view *view) {
+ ledit_theme *theme = config_get_theme();
int h = 0;
int cur_line_y = 0;
int cursor_displayed = 0;
@@ -1884,7 +1886,7 @@ view_redraw_text(ledit_view *view) {
h += vline->h;
}
- XSetForeground(view->buffer->common->dpy, view->window->gc, view->theme->cursor_bg.pixel);
+ XSetForeground(view->buffer->common->dpy, view->window->gc, theme->cursor_bg.pixel);
PangoRectangle strong, weak;
ledit_line *cur_line = buffer_get_line(view->buffer, view->cur_line);
PangoLayout *layout = get_pango_layout(view, view->cur_line);
diff --git a/view.h b/view.h
@@ -10,8 +10,8 @@
#include "common.h"
#include "txtbuf.h"
#include "window.h"
-#include "theme.h"
#include "cache.h"
+#include "uglycrap.h"
typedef struct ledit_view ledit_view;
@@ -49,24 +49,14 @@ typedef struct {
char h_dirty; /* whether height needs to be recalculated */
} ledit_view_line;
-/* FIXME: It's kind of ugly to put this here instead of keys_command.h,
- but it has to be per-view, so I don't know any other option. */
-enum ledit_command_type {
- CMD_EDIT, /* edit command */
- CMD_EDITSEARCH, /* edit search term */
- CMD_EDITSEARCHB, /* edit search term for backwards search */
- CMD_SUBSTITUTE /* confirm substitution */
-};
-
struct ledit_view {
ledit_buffer *buffer; /* parent buffer */
ledit_window *window; /* window showing this view */
- ledit_theme *theme; /* current theme in use */
ledit_cache *cache; /* cache for pixmaps and pango layouts */
ledit_view_line *lines; /* array of lines, stored as gap buffer */
char *lock_text; /* text to show if view is locked, i.e. no edits allowed */
/* current command type - used by key handler in keys_command.c */
- enum ledit_command_type cur_command_type;
+ command_mode cur_command_type;
struct action cur_action; /* current action to execute on key press */
size_t lines_cap; /* size of lines array */
size_t lines_gap; /* position of gap for line gap buffer */
@@ -98,14 +88,11 @@ enum delete_mode {
void view_set_mode(ledit_view *view, ledit_mode mode);
/*
- * Create a view with associated buffer 'buffer' and theme 'theme'.
+ * Create a view with associated buffer 'buffer'
* The initial mode, line, and byte position are given, respectively,
* by 'mode', 'line', and 'pos'.
*/
-ledit_view *view_create(
- ledit_buffer *buffer, ledit_theme *theme,
- ledit_mode mode, size_t line, size_t pos
-);
+ledit_view *view_create(ledit_buffer *buffer, ledit_mode mode, size_t line, size_t pos);
/*
* Lock a view.
diff --git a/window.c b/window.c
@@ -14,7 +14,6 @@
#include <X11/extensions/Xdbe.h>
#include "util.h"
-#include "theme.h"
#include "memory.h"
#include "common.h"
#include "txtbuf.h"
@@ -23,6 +22,7 @@
#include "config.h"
#include "assert.h"
#include "draw_util.h"
+#include "configparser.h"
/* FIXME: Everything to do with the bottom bar is extremely hacky */
struct bottom_bar {
@@ -94,10 +94,11 @@ window_get_primary_clipboard_buffer(void) {
/* FIXME: guard against negative width/height */
static void
recalc_text_size(ledit_window *window) {
+ ledit_theme *theme = config_get_theme();
int bar_h = window->bb->mode_h;
if (window->bottom_text_shown || window->message_shown)
bar_h = window->bb->line_h;
- window->text_w = window->w - window->theme->scrollbar_width;
+ window->text_w = window->w - theme->scrollbar_width;
window->text_h = window->h - bar_h;
if (window->text_w < 0)
window->text_w = 0;
@@ -120,12 +121,13 @@ resize_line_text(ledit_window *window, int min_size) {
static void
redraw_line_text(ledit_window *window) {
+ ledit_theme *theme = config_get_theme();
/* FIXME: set_text doesn't really belong here */
pango_layout_set_text(window->bb->line, window->bb->line_text, window->bb->line_len);
pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &window->bb->line_h);
draw_grow(window, window->bb->line_draw, window->bb->line_w, window->bb->line_h);
- XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->bar_bg, 0, 0, window->bb->line_w, window->bb->line_h);
- pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme->bar_fg, window->bb->line, 0, 0);
+ XftDrawRect(window->bb->line_draw->xftdraw, &theme->bar_bg, 0, 0, window->bb->line_w, window->bb->line_h);
+ pango_xft_render_layout(window->bb->line_draw->xftdraw, &theme->bar_fg, window->bb->line, 0, 0);
recalc_text_size(window);
window->redraw = 1;
}
@@ -332,6 +334,7 @@ window_hide_message(ledit_window *window) {
void
window_set_mode(ledit_window *window, ledit_mode mode) {
+ ledit_theme *theme = config_get_theme();
window->mode = mode;
char *text;
switch (mode) {
@@ -353,8 +356,8 @@ window_set_mode(ledit_window *window, ledit_mode mode) {
free(final_text);
pango_layout_get_pixel_size(window->bb->mode, &window->bb->mode_w, &window->bb->mode_h);
draw_grow(window, window->bb->mode_draw, window->bb->mode_w, window->bb->mode_h);
- XftDrawRect(window->bb->mode_draw->xftdraw, &window->theme->bar_bg, 0, 0, window->bb->mode_w, window->bb->mode_h);
- pango_xft_render_layout(window->bb->mode_draw->xftdraw, &window->theme->bar_fg, window->bb->mode, 0, 0);
+ XftDrawRect(window->bb->mode_draw->xftdraw, &theme->bar_bg, 0, 0, window->bb->mode_w, window->bb->mode_h);
+ pango_xft_render_layout(window->bb->mode_draw->xftdraw, &theme->bar_fg, window->bb->mode, 0, 0);
recalc_text_size(window);
window->redraw = 1;
}
@@ -510,10 +513,13 @@ xximspot(ledit_window *window, int x, int y) {
}
ledit_window *
-window_create(ledit_common *common, ledit_theme *theme, ledit_mode mode) {
+window_create(ledit_common *common, ledit_mode mode) {
XGCValues gcv;
+ ledit_theme *theme = config_get_theme();
+
ledit_window *window = ledit_malloc(sizeof(ledit_window));
+ window->first_resize = 1;
window->mode = mode;
window->scroll_dragging = 0;
@@ -569,7 +575,6 @@ window_create(ledit_common *common, ledit_theme *theme, ledit_mode mode) {
XSetWMProtocols(common->dpy, window->xwin, &window->wm_delete_msg, 1);
window->common = common;
- window->theme = theme;
window->bb = ledit_malloc(sizeof(bottom_bar));
window->bb->mode = pango_layout_new(window->context);
@@ -652,6 +657,7 @@ window_destroy(ledit_window *window) {
/*g_object_unref(window->context);*/
g_object_unref(window->fontmap);
+ XFreeGC(window->common->dpy, window->gc);
if (window->spotlist)
XFree(window->spotlist);
XDestroyWindow(window->common->dpy, window->xwin);
@@ -671,7 +677,8 @@ window_cleanup(void) {
void
window_clear(ledit_window *window) {
- XSetForeground(window->common->dpy, window->gc, window->theme->text_bg.pixel);
+ ledit_theme *theme = config_get_theme();
+ XSetForeground(window->common->dpy, window->gc, theme->text_bg.pixel);
XFillRectangle(
window->common->dpy, window->drawable, window->gc, 0, 0, window->w, window->h
);
@@ -679,7 +686,7 @@ window_clear(ledit_window *window) {
void
window_redraw(ledit_window *window) {
- ledit_theme *t = window->theme;
+ ledit_theme *t = config_get_theme();
if (window->scroll_max > window->text_h) {
XSetForeground(window->common->dpy, window->gc, t->scrollbar_bg.pixel);
XFillRectangle(
@@ -791,7 +798,7 @@ window_handle_filtered_events(ledit_window *window) {
if (window->last_resize_valid) {
clock_gettime(CLOCK_MONOTONIC, &now);
ledit_timespecsub(&now, &window->last_resize, &elapsed);
- if (elapsed.tv_sec > 0 || elapsed.tv_nsec >= RESIZE_TICK) {
+ if (window->first_resize || elapsed.tv_sec > 0 || elapsed.tv_nsec >= RESIZE_TICK) {
window_resize(
window,
window->last_resize_event.xconfigure.width,
@@ -800,6 +807,7 @@ window_handle_filtered_events(ledit_window *window) {
window->last_resize = now;
window->last_resize_valid = 0;
window->redraw = 1;
+ window->first_resize = 0;
}
}
}
@@ -1036,6 +1044,7 @@ window_register_motion(ledit_window *window, XEvent *event) {
/* FIXME: improve set_scroll_pos; make it a bit clearer */
void
window_button_press(ledit_window *window, XEvent *event, int scroll_num) {
+ ledit_theme *theme = config_get_theme();
int x = event->xbutton.x;
int y = event->xbutton.y;
double scroll_h, scroll_y;
@@ -1062,7 +1071,7 @@ window_button_press(ledit_window *window, XEvent *event, int scroll_num) {
break;
case Button4:
case Button5:
- window->scroll_offset += scroll_num * window->theme->scrollbar_step;
+ window->scroll_offset += scroll_num * theme->scrollbar_step;
if (window->scroll_offset < 0)
window->scroll_offset = 0;
if (window->scroll_offset + window->text_h > window->scroll_max) {
diff --git a/window.h b/window.h
@@ -16,7 +16,6 @@
#include <X11/extensions/Xdbe.h>
#include <pango/pangoxft.h>
-#include "theme.h"
#include "common.h"
#include "txtbuf.h"
@@ -65,6 +64,12 @@ typedef struct {
int last_scroll_valid;
int last_motion_valid;
int last_resize_valid;
+ /* This is a hack to make the first resizing of the window go quickly instead
+ of being delayed due to the event filtering - this is noticeable in tiling
+ window managers that resize the window immediately after it is created.
+ The whole event filtering system needs to be rethought anyways, but this
+ at least sort of works for the time being. (FIXME) */
+ int first_resize;
int scroll_num;
int scroll_delta;
@@ -75,7 +80,6 @@ typedef struct {
XVaNestedList spotlist;
ledit_common *common;
- ledit_theme *theme;
/* various callbacks */
void (*paste_callback)(void *, char *, size_t);
@@ -94,7 +98,7 @@ typedef struct {
/*
* Create a window with initial mode 'mode'.
*/
-ledit_window *window_create(ledit_common *common, ledit_theme *theme, ledit_mode mode);
+ledit_window *window_create(ledit_common *common, ledit_mode mode);
/*
* Destroy a window.