tAdd support for config file - ledit - Text editor (WIP) | |
git clone git://lumidify.org/ledit.git (fast, but not encrypted) | |
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow) | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 1b405e16faddabd36b26297c27c746aa7788a2d4 | |
parent aadae71b088e1d224e1f6615524d0cc0d51eae7a | |
Author: lumidify <[email protected]> | |
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 | |
t@@ -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 | |
t@@ -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 <[email protected]> | |
+Copyright (c) 2021, 2022 lumidify <[email protected]> | |
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 | |
t@@ -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 \ | |
t@@ -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 \ | |
t@@ -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 … | |
-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}… | |
+LDFLAGS_LEDIT = ${EXTRA_FLAGS_SANITIZE${SANITIZE}} ${EXTRA_LDFLAGS_DEBUG${DEBU… | |
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} | |
t@@ -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} | |
t@@ -97,7 +106,7 @@ clean: | |
dist: | |
rm -rf "${NAME}-${VERSION}" | |
mkdir -p "${NAME}-${VERSION}" | |
- cp -f ${MAN1} ${SRC} ${HDR} ${CONFIGHDR} ${MISCFILES} "${NAME}-${VERSI… | |
+ cp -f ${MAN1} ${MAN5} ${SRC} ${HDR} ${CONFIGHDR} ${MISCFILES} "${NAME}… | |
tar cf - "${NAME}-${VERSION}" | gzip -c > "${NAME}-${VERSION}.tar.gz" | |
rm -rf "${NAME}-${VERSION}" | |
diff --git a/README b/README | |
t@@ -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. | |
t@@ -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 l… | |
gopher://lumidify.org/0/doc/ledit/ledit-current.txt | |
gopher://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/0/doc/… | |
http://lumidify.org/doc/ledit/ledit-current.html | |
http://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/doc/ledi… | |
https://lumidify.org/doc/ledit/ledit-current.html | |
+ | |
+gopher://lumidify.org/0/doc/ledit/leditrc-current.txt | |
+gopher://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/0/doc/… | |
+http://lumidify.org/doc/ledit/leditrc-current.html | |
+http://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/doc/ledi… | |
+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 | |
t@@ -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" | |
t@@ -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, siz… | |
+buffer_add_view(ledit_buffer *buffer, ledit_mode mode, size_t line, size_t pos… | |
size_t new_num = add_sz(buffer->views_num, 1); | |
buffer->views = ledit_reallocarray(buffer->views, new_num, sizeof(ledi… | |
- buffer->views[buffer->views_num] = view_create(buffer, theme, mode, li… | |
+ 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; | |
t@@ -374,11 +373,11 @@ buffer_load_file(ledit_buffer *buffer, char *filename, s… | |
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; | |
t@@ -397,11 +396,11 @@ buffer_write_to_file(ledit_buffer *buffer, FILE *file, c… | |
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; | |
t@@ -413,7 +412,7 @@ buffer_write_to_fd(ledit_buffer *buffer, int fd, char **er… | |
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 */ | |
t@@ -428,7 +427,7 @@ buffer_write_to_filename(ledit_buffer *buffer, char *filen… | |
if (!file) goto error; | |
return buffer_write_to_file(buffer, file, errstr); | |
error: | |
- if (*errstr) | |
+ if (errstr) | |
*errstr = strerror(errno); | |
return 1; | |
} | |
t@@ -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); | |
} | |
t@@ -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 star… | |
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) { | |
t@@ -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 | |
t@@ -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 scrol… | |
); | |
/* | |
diff --git a/config.h b/config.h | |
t@@ -1,3 +1,10 @@ | |
+#ifndef _CONFIG_H_ | |
+#define _CONFIG_H_ | |
+ | |
+/************************* | |
+ * General configuration * | |
+ *************************/ | |
+ | |
/* Note: These have to be less than one second */ | |
/* | |
t@@ -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 | |
t@@ -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->cu… | |
+ while (isspace(c = s->text[s->cur])) { | |
+ s->cur++; | |
+ if (c == '\n') { | |
+ struct token tok = (struct token){s->text + s-… | |
+ s->cur_line++; | |
+ s->line_start = s->cur; | |
+ return tok; | |
+ } | |
+ if (s->cur >= s->len) | |
+ return (struct token){NULL, 0, END, s->cur_lin… | |
+ } | |
+ | |
+ 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->c… | |
+ s->cur++; | |
+ break; | |
+ case '}': | |
+ tok = (struct token){s->text + s->cur, 1, RBRACE, s->c… | |
+ s->cur++; | |
+ break; | |
+ case '=': | |
+ tok = (struct token){s->text + s->cur, 1, EQUALS, s->c… | |
+ 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->c… | |
+ 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->c… | |
+ 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 … | |
+ tok.type = ERROR; | |
+ tok.line_offset = s->cur - s->line_sta… | |
+ } | |
+ 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->le… | |
+ statement->args = ledit_reallocarray(statement->args, statement->cap, … | |
+ 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… | |
+ *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 = … | |
+ 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_strin… | |
+ assignment.value->type = OBJ_STRING; | |
+ tok = next_token(s); | |
+ if (tok.type == STRING) { | |
+ *errstr = print_fmt( | |
+ "%s: Invalid assignment at… | |
+ filename, tok.line, tok.li… | |
+ ); | |
+ free(assignment.value); | |
+ goto error; | |
+ } | |
+ } else if (tok2.type == LBRACE) { | |
+ assignment.value->type = OBJ_LIST; | |
+ /* just in case */ | |
+ assignment.value->obj.list = (ast_list… | |
+ if (parse_list(s, &assignment.value->o… | |
+ free(assignment.value); | |
+ goto error; | |
+ } | |
+ tok = next_token(s); | |
+ if (tok.type == STRING) { | |
+ *errstr = print_fmt( | |
+ "%s: Invalid assignment at… | |
+ filename, tok.line, tok.li… | |
+ ); | |
+ destroy_list(&assignment.value… | |
+ free(assignment.value); | |
+ goto error; | |
+ } | |
+ } else { | |
+ *errstr = print_fmt( | |
+ "%s: Invalid assignment at line %z… | |
+ filename, tok2.line, tok2.line_off… | |
+ ); | |
+ free(assignment.value); | |
+ goto error; | |
+ } | |
+ list_append(ret, (ast_obj){.tok = orig_tok, .o… | |
+ } else { | |
+ *errstr = print_fmt( | |
+ "%s: Invalid token '%s' at line %zu, offse… | |
+ filename, toktype_str(tok2.type), tok2.lin… | |
+ ); | |
+ 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, o… | |
+ 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, o… | |
+ 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.lin… | |
+ ); | |
+ 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+no… | |
+ 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 reus… | |
+ 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 *th… | |
+ *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 **er… | |
+ ); | |
+ void (*destroy_func)(ledit_common *common, void *obj); | |
+ const char *default_value; | |
+ int initialized; | |
+ } settings[] = { | |
+ {"text-font", &theme->text_font, &parse_theme_string, &destroy… | |
+ {"text-size", &theme->text_size, &parse_theme_number, &destroy… | |
+ {"scrollbar-width", &theme->scrollbar_width, &parse_theme_numb… | |
+ {"scrollbar-step", &theme->scrollbar_step, &parse_theme_number… | |
+ {"text-fg", &theme->text_fg, &parse_theme_color, &destroy_them… | |
+ {"text-bg", &theme->text_bg, &parse_theme_color, &destroy_them… | |
+ {"cursor-fg", &theme->cursor_fg, &parse_theme_color, &destroy_… | |
+ {"cursor-bg", &theme->cursor_bg, &parse_theme_color, &destroy_… | |
+ {"selection-fg", &theme->selection_fg, &parse_theme_color, &de… | |
+ {"selection-bg", &theme->selection_bg, &parse_theme_color, &de… | |
+ {"bar-fg", &theme->bar_fg, &parse_theme_color, &destroy_theme_… | |
+ {"bar-bg", &theme->bar_bg, &parse_theme_color, &destroy_theme_… | |
+ {"bar-cursor", &theme->bar_cursor, &parse_theme_color, &destro… | |
+ {"scrollbar-fg", &theme->scrollbar_fg, &parse_theme_color, &de… | |
+ {"scrollbar-bg", &theme->scrollbar_bg, &parse_theme_color, &de… | |
+ }; | |
+ | |
+ 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_offs… | |
+ if (theme_list->objs[i].type != OBJ_ASSIGNMENT) { | |
+ *errstr = print_fmt( | |
+ "%s: Invalid statement in theme configurat… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto cleanup; | |
+ } else if (theme_list->objs[i].obj.assignment.value->t… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid assignment in theme configura… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto cleanup; | |
+ } | |
+ | |
+ char *key = theme_list->objs[i].obj.assignment.tok.tex… | |
+ size_t key_len = theme_list->objs[i].obj.assignment.to… | |
+ char *val = theme_list->objs[i].obj.assignment.value->… | |
+ size_t val_len = theme_list->objs[i].obj.assignment.va… | |
+ | |
+ 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_… | |
+ /* FIXME: maybe just make this a warni… | |
+ if (settings[j].initialized) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate definition … | |
+ "'%.*s' at line %zu, posit… | |
+ filename, key_len, key, li… | |
+ ); | |
+ goto cleanup; | |
+ } | |
+ if (settings[j].parse_func( | |
+ common, settings[j].obj, val, val_… | |
+ settings[j].key, filename, line, l… | |
+ 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 r… | |
+ /* 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 **… | |
+ 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,… | |
+ cur += 6; | |
+ *mode_ret |= NORMAL; | |
+ } else if (str_array_equal("visual", modemask_str + cur, LEDIT… | |
+ cur += 6; | |
+ *mode_ret |= VISUAL; | |
+ } else if (str_array_equal("insert", modemask_str + cur, LEDIT… | |
+ 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, l… | |
+ cur += 5; | |
+ *mask_ret |= ShiftMask; | |
+ } else if (str_array_equal("lock", modmask_str + cur, LEDIT_MI… | |
+ cur += 4; | |
+ *mask_ret |= LockMask; | |
+ } else if (str_array_equal("control", modmask_str + cur, LEDIT… | |
+ cur += 7; | |
+ *mask_ret |= ControlMask; | |
+ } else if (str_array_equal("mod1", modmask_str + cur, LEDIT_MI… | |
+ cur += 4; | |
+ *mask_ret |= Mod1Mask; | |
+ } else if (str_array_equal("mod2", modmask_str + cur, LEDIT_MI… | |
+ cur += 4; | |
+ *mask_ret |= Mod2Mask; | |
+ } else if (str_array_equal("mod3", modmask_str + cur, LEDIT_MI… | |
+ cur += 4; | |
+ *mask_ret |= Mod3Mask; | |
+ } else if (str_array_equal("mod4", modmask_str + cur, LEDIT_MI… | |
+ cur += 4; | |
+ *mask_ret |= Mod4Mask; | |
+ } else if (str_array_equal("any", modmask_str + cur, LEDIT_MIN… | |
+ 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… | |
+ while (cur < len) { | |
+ if (str_array_equal("substitute", mode_str + cur, LEDIT_MIN(10… | |
+ cur += 10; | |
+ *mode_ret |= CMD_SUBSTITUTE; | |
+ } else if (str_array_equal("edit-search-backwards", mode_str +… | |
+ cur += 21; | |
+ *mode_ret |= CMD_EDITSEARCHB; | |
+ } else if (str_array_equal("edit-search", mode_str + cur, LEDI… | |
+ cur += 11; | |
+ *mode_ret |= CMD_EDITSEARCH; | |
+ } else if (str_array_equal("edit", mode_str + cur, LEDIT_MIN(4… | |
+ 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(c… | |
+ cfg->basic_keys = ledit_reallocarray(cfg->basic_keys, cfg->all… | |
+ cfg->command_keys = ledit_reallocarray(cfg->command_keys, cfg-… | |
+ cfg->cmds = ledit_reallocarray(cfg->cmds, cfg->alloc_langs, si… | |
+ cfg->langs = ledit_reallocarray(cfg->langs, cfg->alloc_langs, … | |
+ } | |
+ 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) || s… | |
+ 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, &… | |
+ msg = "Invalid mods specification"; … | |
+ goto error; … | |
+ } … | |
+ mods_init = 1; … | |
+ } else if (str_array_equal("modes", st->args[i].text, st->args… | |
+ 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,… | |
+ msg = "Invalid modes specification"; … | |
+ goto error; … | |
+ } else if (!cb_type##_modemask_is_valid(m->cb, m->mode… | |
+ msg = "Function not defined for all given mode… | |
+ goto error; … | |
+ } … | |
+ modes_init = 1; … | |
+ } else if (str_array_equal("keysym", st->args[i].text, st->arg… | |
+ 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… | |
+ msg = "Invalid keysym specification"; … | |
+ goto error; … | |
+ } … | |
+ keysym_init = 1; … | |
+ } else if (str_array_equal("text", st->args[i].text, st->args[… | |
+ 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].… | |
+ text_init = 1; … | |
+ } else if (str_array_equal("catchall", st->args[i].text, st->a… | |
+ 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_offse… | |
+ ); … | |
+ } … | |
+ if (m->text) … | |
+ free(m->text); … | |
+ return 1; … | |
+} | |
+ | |
+GEN_PARSE_STATEMENT(parse_basic_key_statement, basic_key_cb, basic_key_mapping… | |
+GEN_PARSE_STATEMENT(parse_command_key_statement, command_key_cb, command_key_m… | |
+ | |
+static int | |
+parse_command_statement(ast_statement *st, command_mapping *m, char *filename,… | |
+ 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) || s… | |
+ 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_offse… | |
+ ); | |
+ } | |
+ /* 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… | |
+ arr->keys = ledit_reallocarray(arr->keys, arr->alloc_keys, siz… | |
+ } | |
+ 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… | |
+ arr->keys = ledit_reallocarray(arr->keys, arr->alloc_keys, siz… | |
+ } | |
+ 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… | |
+ arr->cmds = ledit_reallocarray(arr->cmds, arr->alloc_cmds, siz… | |
+ } | |
+ 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 **errst… | |
+ 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_o… | |
+ ); | |
+ 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_ST… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid language setting in bindings … | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } else if (cfg->langs[cur_lang]) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate language setting in binding… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } | |
+ char *val = list->objs[i].obj.assignment.value->obj.st… | |
+ size_t val_len = list->objs[i].obj.assignment.value->o… | |
+ 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_LI… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid basic-keys setting in binding… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } else if (basic_keys_init) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate basic-keys setting in bindi… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } | |
+ ast_list *slist = &list->objs[i].obj.assignment.value-… | |
+ 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… | |
+ "at line %zu, offset %zu", filenam… | |
+ ); | |
+ goto error; | |
+ } | |
+ basic_key_mapping m; | |
+ if (parse_basic_key_statement(&slist->objs[j].… | |
+ 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_LI… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid command-keys setting in bindi… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } else if (command_keys_init) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate command-keys setting in bin… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } | |
+ ast_list *slist = &list->objs[i].obj.assignment.value-… | |
+ 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 … | |
+ "at line %zu, offset %zu", filenam… | |
+ ); | |
+ goto error; | |
+ } | |
+ command_key_mapping m; | |
+ if (parse_command_key_statement(&slist->objs[j… | |
+ goto error; | |
+ push_command_key_mapping(&cfg->command_keys[0]… | |
+ } | |
+ command_keys_init = 1; | |
+ } else if (str_array_equal("commands", key, key_len)) { | |
+ if (list->objs[i].obj.assignment.value->type != OBJ_LI… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid commands setting in bindings … | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } else if (commands_init) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate commands setting in binding… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } | |
+ ast_list *slist = &list->objs[i].obj.assignment.value-… | |
+ 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 b… | |
+ "at line %zu, offset %zu", filenam… | |
+ ); | |
+ goto error; | |
+ } | |
+ command_mapping m; | |
+ if (parse_command_statement(&slist->objs[j].ob… | |
+ 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… | |
+ setting in the bindings configuration, all actual bindings are defa… | |
+ 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 def… | |
+ 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… | |
+ if (!m.cb) { | |
+ *errstr = print_fmt("default config: Invalid b… | |
+ goto error; | |
+ } else if (!basic_key_cb_modemask_is_valid(m.cb, basic… | |
+ *errstr = print_fmt("default config: Function … | |
+ goto error; | |
+ } | |
+ m.text = basic_keys_default[i].text ? ledit_strdup(bas… | |
+ 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 d… | |
+ 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_defau… | |
+ if (!m.cb) { | |
+ *errstr = print_fmt("default config: Invalid c… | |
+ goto error; | |
+ } else if (!command_key_cb_modemask_is_valid(m.cb, com… | |
+ *errstr = print_fmt("default config: Function … | |
+ goto error; | |
+ } | |
+ m.text = command_keys_default[i].text ? ledit_strdup(c… | |
+ 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 defau… | |
+ command_mapping m; | |
+ for (size_t i = 0; i < LENGTH(commands_default); i++) { | |
+ m.cb = command_cb_map_get_entry(commands_default[i].fu… | |
+ if (!m.cb) { | |
+ *errstr = print_fmt("default config: Invalid c… | |
+ 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 config… | |
+ "at list %zu, offset %zu", filename, line, line_o… | |
+ ); | |
+ 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_ST… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid language setting in language … | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } else if (cfg->langs[cur_lang]) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate language setting in languag… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } | |
+ char *val = list->objs[i].obj.assignment.value->obj.st… | |
+ size_t val_len = list->objs[i].obj.assignment.value->o… | |
+ 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_LI… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid key-mapping setting in langua… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } else if (key_mapping_init) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate key-mapping setting in lang… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } | |
+ ast_list *slist = &list->objs[i].obj.assignment.value-… | |
+ 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 i… | |
+ "at line %zu, offset %zu", filenam… | |
+ ); | |
+ goto error; | |
+ } | |
+ ast_statement *st = &slist->objs[j].obj.statem… | |
+ if (!str_array_equal("map", st->func_tok.text,… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid key-mapping statement… | |
+ "at line %zu, offset %zu", filenam… | |
+ ); | |
+ goto error; | |
+ } | |
+ /* FIXME: any way to speed this up? I guess on… | |
+ for (size_t k = 0; k < bkmap->num_keys; k++) { | |
+ if (bkmap->keys[k].text && str_array_e… | |
+ free(bkmap->keys[k].text); | |
+ bkmap->keys[k].text = ledit_st… | |
+ } | |
+ } | |
+ for (size_t k = 0; k < ckmap->num_keys; k++) { | |
+ if (ckmap->keys[k].text && str_array_e… | |
+ free(ckmap->keys[k].text); | |
+ ckmap->keys[k].text = ledit_st… | |
+ } | |
+ } | |
+ } | |
+ key_mapping_init = 1; | |
+ } else if (str_array_equal("command-mapping", key, key_len)) { | |
+ if (list->objs[i].obj.assignment.value->type != OBJ_LI… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid command-mapping setting in la… | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } else if (command_mapping_init) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate command-mapping setting in … | |
+ "at line %zu, offset %zu", filename, line,… | |
+ ); | |
+ goto error; | |
+ } | |
+ ast_list *slist = &list->objs[i].obj.assignment.value-… | |
+ 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 setti… | |
+ "at line %zu, offset %zu", filenam… | |
+ ); | |
+ goto error; | |
+ } | |
+ ast_statement *st = &slist->objs[j].obj.statem… | |
+ if (!str_array_equal("map", st->func_tok.text,… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid command-mapping state… | |
+ "at line %zu, offset %zu", filenam… | |
+ ); | |
+ goto error; | |
+ } | |
+ for (size_t k = 0; k < cmap->num_cmds; k++) { | |
+ if (str_array_equal(cmap->cmds[k].text… | |
+ free(cmap->cmds[k].text); | |
+ cmap->cmds[k].text = ledit_str… | |
+ } | |
+ } | |
+ } | |
+ command_mapping_init = 1; | |
+ } | |
+ } | |
+ if (!cfg->langs[cur_lang]) { | |
+ /* FIXME: pass actual beginning line and offset so this doesn'… | |
+ use the line and offset of the first list element */ | |
+ if (list->len > 0) { | |
+ *errstr = print_fmt( | |
+ "%s: Missing language setting in language mapping … | |
+ "at line %zu, offset %zu", filename, list->objs[0]… | |
+ ); | |
+ } else { | |
+ *errstr = print_fmt("%s: Missing language setting in l… | |
+ } | |
+ 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… | |
+ memmove(arr1->keys, cfg->basic_keys[0].keys, arr1->num_keys * sizeof(b… | |
+ 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_k… | |
+ memmove(arr2->keys, cfg->command_keys[0].keys, arr2->num_keys * sizeof… | |
+ 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_m… | |
+ memmove(arr3->cmds, cfg->cmds[0].cmds, arr3->num_cmds * sizeof(command… | |
+ 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, o… | |
+ break; | |
+ case OBJ_STATEMENT: | |
+ fprintf(stderr, "STATEMENT: %.*s ", (int)obj->obj.statement.fu… | |
+ for (size_t i = 0; i < obj->obj.statement.len; i++) { | |
+ fprintf(stderr, "%.*s ", (int)obj->obj.statement.args[… | |
+ } | |
+ fprintf(stderr, "\n"); | |
+ break; | |
+ case OBJ_ASSIGNMENT: | |
+ fprintf(stderr, "ASSIGNMENT: %.*s =\n", (int)obj->obj.assignme… | |
+ 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\… | |
+ (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\… | |
+ (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, ass… | |
+ if (theme_init) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate theme definition at… | |
+ filename, assignment->tok.line, as… | |
+ ); | |
+ goto error; | |
+ } else if (assignment->value->type != OBJ_LIST… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid theme definition at l… | |
+ filename, assignment->tok.line, as… | |
+ ); | |
+ goto error; | |
+ } | |
+ cfg.theme = load_theme(common, &assignment->va… | |
+ if (!cfg.theme) | |
+ goto error; | |
+ theme_init = 1; | |
+ } else if (str_array_equal("bindings", assignment->tok… | |
+ if (bindings_init) { | |
+ *errstr = print_fmt( | |
+ "%s: Duplicate definition of bindi… | |
+ filename, assignment->tok.line, as… | |
+ ); | |
+ goto error; | |
+ } | |
+ push_lang(&cfg); | |
+ if (assignment->value->type != OBJ_LIST) { | |
+ *errstr = print_fmt( | |
+ "%s: Invalid definition of binding… | |
+ filename, assignment->tok.line, as… | |
+ ); | |
+ goto error; | |
+ } | |
+ if (load_bindings(&cfg, &assignment->value->ob… | |
+ goto error; | |
+ bindings_init = 1; | |
+ } else if (str_array_equal("language-mapping", assignm… | |
+ if (cfg.num_langs == 0) { | |
+ ledit_debug("No key/command bindings c… | |
+ push_lang(&cfg); | |
+ /* load default config */ | |
+ ast_list empty_list = {.objs = NULL, .… | |
+ /* shouldn't usually happen */ | |
+ if (load_bindings(&cfg, &empty_list, f… | |
+ goto error; | |
+ bindings_init = 1; | |
+ } else if (assignment->value->type != OBJ_LIST… | |
+ *errstr = print_fmt( | |
+ "%s: Invalid definition of languag… | |
+ filename, assignment->tok.line, as… | |
+ ); | |
+ goto error; | |
+ } | |
+ | |
+ append_mapping(&cfg); | |
+ | |
+ if (load_mapping(&cfg, &assignment->value->obj… | |
+ goto error; | |
+ mappings_init = 1; | |
+ } else { | |
+ *errstr = print_fmt( | |
+ "%s: Invalid assignment at line %zu, offse… | |
+ filename, assignment->tok.line, assignment… | |
+ ); | |
+ 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.… | |
+ ); | |
+ 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 defau… | |
+ 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 defau… | |
+ 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]… | |
+ 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 k… | |
+ /* 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(bkm… | |
+ free(bkmap->keys[k].text); | |
+ bkmap->keys[k].text = ledit_st… | |
+ } | |
+ } | |
+ for (size_t k = 0; k < ckmap->num_keys; k++) { | |
+ if (ckmap->keys[k].text && !strcmp(ckm… | |
+ free(ckmap->keys[k].text); | |
+ ckmap->keys[k].text = ledit_st… | |
+ } | |
+ } | |
+ } | |
+ 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, mappin… | |
+ free(cmap->cmds[k].text); | |
+ cmap->cmds[k].text = ledit_str… | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ 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 | |
t@@ -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 | |
t@@ -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 | Mod2… | |
diff --git a/keys.h b/keys.h | |
t@@ -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 | |
t@@ -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> | |
t@@ -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 … | |
+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… | |
+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 le… | |
+static struct action enter_searchedit_backward(ledit_view *view, char *text, s… | |
+static struct action enter_searchedit_forward(ledit_view *view, char *text, si… | |
+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, siz… | |
+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_… | |
+static struct action scroll_with_cursor_down(ledit_view *view, char *text, siz… | |
+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 le… | |
+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… | |
+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 … | |
+static struct action append_line_above(ledit_view *view, char *text, size_t le… | |
+static struct action append_line_below(ledit_view *view, char *text, size_t le… | |
+static struct action find_next_char_forwards(ledit_view *view, char *text, siz… | |
+static struct action find_next_char_backwards(ledit_view *view, char *text, si… | |
+static struct action find_char_forwards(ledit_view *view, char *text, size_t l… | |
+static struct action find_char_backwards(ledit_view *view, char *text, size_t … | |
+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_… | |
+static struct action delete_chars_backwards(ledit_view *view, char *text, size… | |
+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… | |
+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 … | |
+static struct action toggle_hard_line_based(ledit_view *view, char *text, size… | |
+ | |
+/*********************************************** | |
+ * 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-whitespa… | |
+static struct basic_key_cb basic_key_cb_map[] = { | |
+ {"append-after-cursor", &append_after_cursor, KEY_FLAG_JUMP_TO_CURSOR,… | |
+ {"append-after-eol", &append_after_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMA… | |
+ {"append-line-above", &append_line_above, KEY_FLAG_JUMP_TO_CURSOR, NOR… | |
+ {"append-line-below", &append_line_below, KEY_FLAG_JUMP_TO_CURSOR, NOR… | |
+ {"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_AL… | |
+ {"clipboard-paste", &clippaste, KEY_FLAG_JUMP_TO_CURSOR, INSERT}, | |
+ {"cursor-down", &cursor_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_AL… | |
+ {"cursor-left", &cursor_left, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_AL… | |
+ {"cursor-right", &cursor_right, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_… | |
+ {"cursor-to-beginning", &cursor_to_beginning, KEY_FLAG_JUMP_TO_CURSOR|… | |
+ {"cursor-to-first-non-whitespace", &cursor_to_first_non_ws, KEY_FLAG_J… | |
+ {"cursor-up", &cursor_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
+ {"delete", &delete, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL}, | |
+ {"delete-backwards", &delete_chars_backwards, KEY_FLAG_JUMP_TO_CURSOR,… | |
+ {"delete-forwards", &delete_chars_forwards, KEY_FLAG_JUMP_TO_CURSOR, N… | |
+ {"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_… | |
+ {"enter-insert", &enter_insert, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL… | |
+ {"enter-searchedit-backwards", &enter_searchedit_backward, KEY_FLAG_NO… | |
+ {"enter-searchedit-forwards", &enter_searchedit_forward, KEY_FLAG_NONE… | |
+ {"enter-visual", &enter_visual, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, | |
+ {"escape-key", &escape_key, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLO… | |
+ {"find-char-backwards", &find_char_backwards, KEY_FLAG_JUMP_TO_CURSOR|… | |
+ {"find-char-forwards", &find_char_forwards, KEY_FLAG_JUMP_TO_CURSOR|KE… | |
+ {"find-next-char-backwards", &find_next_char_backwards, KEY_FLAG_JUMP_… | |
+ {"find-next-char-forwards", &find_next_char_forwards, KEY_FLAG_JUMP_TO… | |
+ {"insert-at-beginning", &insert_at_beginning, KEY_FLAG_JUMP_TO_CURSOR,… | |
+ {"insert-text", &insert_mode_insert_text, KEY_FLAG_JUMP_TO_CURSOR, INS… | |
+ {"join-lines", &join_lines, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, | |
+ {"jump-to-mark", &jump_to_mark, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_… | |
+ {"key-0", &key_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMA… | |
+ {"mark-line", &mark_line, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|… | |
+ {"move-to-eol", &move_to_eol, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_AL… | |
+ {"move-to-line", &move_to_line, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_… | |
+ {"next-bigword", &next_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_… | |
+ {"next-bigword-end", &next_bigword_end, KEY_FLAG_JUMP_TO_CURSOR|KEY_FL… | |
+ {"next-word", &next_word, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
+ {"next-word-end", &next_word_end, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOC… | |
+ {"paste-normal", &paste_normal, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, | |
+ {"paste-normal-backwards", &paste_normal_backwards, KEY_FLAG_JUMP_TO_C… | |
+ {"previous-bigword", &prev_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_L… | |
+ {"previous-word", &prev_word, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_AL… | |
+ {"push-0", &push_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"push-1", &push_1, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"push-2", &push_2, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"push-3", &push_3, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"push-4", &push_4, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"push-5", &push_5, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"push-6", &push_6, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"push-7", &push_7, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"push-8", &push_8, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"push-9", &push_9, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NOR… | |
+ {"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_AL… | |
+ {"screen-up", &screen_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWE… | |
+ {"scroll-lines-down", &scroll_lines_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_… | |
+ {"scroll-lines-up", &scroll_lines_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG… | |
+ {"scroll-with-cursor-down", &scroll_with_cursor_down, KEY_FLAG_JUMP_TO… | |
+ {"scroll-with-cursor-up", &scroll_with_cursor_up, KEY_FLAG_JUMP_TO_CUR… | |
+ {"search-next", &key_search_next, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOC… | |
+ {"search-previous", &key_search_prev, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG… | |
+ {"show-line", &show_line, KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL}, | |
+ {"switch-selection-end", &switch_selection_end, KEY_FLAG_JUMP_TO_CURSO… | |
+ {"toggle-hard-line-based", &toggle_hard_line_based, KEY_FLAG_NONE|KEY_… | |
+ {"undo", &undo, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, | |
+ {"yank", &yank, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|… | |
+ {"yank-lines", &yank_lines, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLO… | |
+}; | |
+ | |
+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; | |
t@@ -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; | |
} | |
t@@ -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; | |
t@@ -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) { | |
t@@ -549,6 +762,7 @@ delete_chars_backwards(ledit_view *view, char *text, size_… | |
/* 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_… | |
+ /* 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,… | |
ledit_range cur_range = {.line1 = line, .byte1 = index, .line2 = line,… | |
t@@ -1271,6 +1485,18 @@ paste_normal_backwards(ledit_view *view, char *text, si… | |
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(); | |
t@@ -2242,10 +2468,9 @@ toggle_hard_line_based(ledit_view *view, char *text, si… | |
} | |
static struct action | |
-handle_key(ledit_view *view, char *key_text, size_t len, KeySym sym, unsigned … | |
- 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 … | |
+ 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) { | |
t@@ -2253,31 +2478,32 @@ handle_key(ledit_view *view, char *key_text, size_t le… | |
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_k… | |
- ((!strncmp(cur_keys[i].text, key_text, len) && | |
- match_key(cur_keys[i].mods, key_state & ~ShiftM… | |
- 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 & ~… | |
+ cur_keys->keys[i].text[0] == '\0')) { | |
/* FIXME: seems a bit hacky to remove shift, b… | |
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-… | |
+ return view_locked_error(view); | |
+ return cur_keys->keys[i].cb->func(view, key_te… | |
} | |
- } else if ((cur_keys[i].modes & view->mode) && | |
- cur_keys[i].keysym == sym && | |
- (cur_keys[i].prev_keys == KEY_ANY || (!e && (cur_ke… | |
- 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_te… | |
+ return view_locked_error(view); | |
+ return cur_keys->keys[i].cb->func(view, key_text, len); | |
} | |
} | |
return (struct action){ACTION_NONE, NULL}; | |
t@@ -2299,13 +2525,13 @@ repeat_command(ledit_view *view, char *text, size_t le… | |
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_ele… | |
while (e) { | |
- (void)handle_key(view, e->key_text, e->len, e->sym, e-… | |
+ (void)handle_key(view, e->key_text, e->len, e->sym, e-… | |
advance_repetition_stack(); | |
e = get_cur_repetition_stack_elem(); | |
} | |
t@@ -2338,14 +2564,14 @@ basic_key_handler(ledit_view *view, XEvent *event, int… | |
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, l… | |
+ basic_key_cb_flags flags; | |
+ struct action act = handle_key(view, buf, (size_t)n, sym, key_state, l… | |
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 | |
t@@ -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_inde… | |
diff --git a/keys_basic_config.h b/keys_basic_config.h | |
t@@ -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 *… | |
- KEY_ANY = 0xFF | |
-}; | |
- | |
-struct key { | |
- char *text; /* for keys that … | |
- unsigned int mods; /* modifier mask … | |
- KeySym keysym; /* for other keys… | |
- ledit_mode modes; /* modes in which… | |
- enum key_type prev_keys; /* allowed previo… | |
- enum key_type type; /* type of key - … | |
- struct action (*func)(ledit_view *, char *, size_t); /* callback funct… | |
-}; | |
- | |
-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 … | |
-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… | |
-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 le… | |
-static struct action enter_searchedit_backward(ledit_view *view, char *text, s… | |
-static struct action enter_searchedit_forward(ledit_view *view, char *text, si… | |
-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, siz… | |
-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_… | |
-static struct action scroll_with_cursor_down(ledit_view *view, char *text, siz… | |
-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 le… | |
-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… | |
-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 … | |
-static struct action append_line_above(ledit_view *view, char *text, size_t le… | |
-static struct action append_line_below(ledit_view *view, char *text, size_t le… | |
-static struct action find_next_char_forwards(ledit_view *view, char *text, siz… | |
-static struct action find_next_char_backwards(ledit_view *view, char *text, si… | |
-static struct action find_char_forwards(ledit_view *view, char *text, size_t l… | |
-static struct action find_char_backwards(ledit_view *view, char *text, size_t … | |
-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_… | |
-static struct action delete_chars_backwards(ledit_view *view, char *text, size… | |
-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… | |
-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 … | |
-static struct action toggle_hard_line_based(ledit_view *view, char *text, size… | |
- | |
-/* 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, &bac… | |
- {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KE… | |
- {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|K… | |
- {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_… | |
- {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KE… | |
- {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN… | |
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &delete… | |
- {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ENSURE_CURSOR_… | |
- {"i", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_… | |
- {"h", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY… | |
- {"l", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY… | |
- {"j", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY… | |
- {"k", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY… | |
- {"h", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_N… | |
- {"t", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &toggle_hard_… | |
- {NULL, 0, XK_space, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUM… | |
- {"j", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_N… | |
- {"n", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_N… | |
- {"p", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_N… | |
- {"0", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &push… | |
- {"1", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"2", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"3", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"4", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"5", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"6", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"7", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"8", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"9", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"x", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &de… | |
- {"X", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &de… | |
- {"d", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"y", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"Y", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &ya… | |
- {"c", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"v", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_visual}, | |
- {"o", 0, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &switch_select… | |
- {"c", ControlMask, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &cli… | |
- {"v", ControlMask, 0, INSERT, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &cli… | |
- {"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_ne… | |
- {"N", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_pr… | |
- {"u", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &un… | |
- {"U", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &re… | |
- {".", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &re… | |
- {"z", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &undo… | |
- {"y", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &redo… | |
- {"b", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"f", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"e", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"y", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"d", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"u", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"$", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"w", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"e", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"W", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"E", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"b", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"B", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"G", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"J", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &jo… | |
- {"I", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &insert_at_beg… | |
- {"p", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal}, | |
- {"P", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal_… | |
- {"A", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_… | |
- {"a", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_… | |
- {"O", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_a… | |
- {"o", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_b… | |
- {"m", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &mark_line}, | |
- {"'", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"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_SHO… | |
- {"t", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"T", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"f", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"F", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_inse… | |
-}; | |
- | |
-static struct key keys_de[] = { | |
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &bac… | |
- {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KE… | |
- {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|K… | |
- {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_… | |
- {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KE… | |
- {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN… | |
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &delete… | |
- {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ENSURE_CURSOR_… | |
- {"i", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_… | |
- {"h", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY… | |
- {"l", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY… | |
- {"j", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY… | |
- {"k", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KEY… | |
- {"h", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_N… | |
- {"t", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &toggle_hard_… | |
- {NULL, 0, XK_space, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUM… | |
- {"j", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_N… | |
- {"n", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_N… | |
- {"p", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_N… | |
- {"0", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &push… | |
- {"1", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"2", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"3", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"4", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"5", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"6", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"7", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"8", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"9", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"x", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &de… | |
- {"X", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &de… | |
- {"d", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"z", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"Z", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &ya… | |
- {"c", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"v", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_visual}, | |
- {"o", 0, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &switch_select… | |
- {"c", ControlMask, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &cli… | |
- {"v", ControlMask, 0, INSERT, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &cli… | |
- {"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_ne… | |
- {"N", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_pr… | |
- {"u", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &un… | |
- {"U", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &re… | |
- {".", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &re… | |
- {"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_… | |
- {"f", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"e", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"z", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"d", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"u", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_… | |
- {"$", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"w", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"e", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"W", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"E", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"b", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"B", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"G", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"J", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &jo… | |
- {"I", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &insert_at_beg… | |
- {"p", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal}, | |
- {"P", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal_… | |
- {"A", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_… | |
- {"a", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after_… | |
- {"O", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_a… | |
- {"o", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_b… | |
- {"m", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &mark_line}, | |
- {"ä", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CU… | |
- {"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_SHO… | |
- {"t", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"T", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"f", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"F", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_inse… | |
-}; | |
- | |
-static struct key keys_ur[] = { | |
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &bac… | |
- {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KE… | |
- {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|K… | |
- {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_… | |
- {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KE… | |
- {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN… | |
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &delete… | |
- {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ENSURE_CURSOR_… | |
- {"ی", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter… | |
- {"ح", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KE… | |
- {"ل", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KE… | |
- {"ج", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KE… | |
- {"ک", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, KE… | |
- {"ح", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_… | |
- {"ت", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &toggle_hard… | |
- {NULL, 0, XK_space, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUM… | |
- {"ج", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_… | |
- {"ن", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_… | |
- {"پ", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_… | |
- {"0", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &push… | |
- {"1", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"2", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"3", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"4", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"5", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"6", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"7", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"8", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"9", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"ش", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &d… | |
- {"ژ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &d… | |
- {"د", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ے", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"َ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &y… | |
- {"چ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ط", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_visual… | |
- {"ہ", 0, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &switch_selec… | |
- {"چ", ControlMask, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &cl… | |
- {"ط", ControlMask, 0, INSERT, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &cl… | |
- {"گ", 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_n… | |
- {"ں", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_p… | |
- {"ء", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &u… | |
- {"ئ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &r… | |
- {"۔", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &r… | |
- {"ز", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &und… | |
- {"َ", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &red… | |
- {"ب", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR… | |
- {"ف", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR… | |
- {"ع", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR… | |
- {"ے", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR… | |
- {"د", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR… | |
- {"ء", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR… | |
- {"$", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"و", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ع", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ؤ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ٰ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ب", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {".", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KEY… | |
- {"غ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ض", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &j… | |
- {"ِ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &insert_at_be… | |
- {"پ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal… | |
- {"ُ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_normal… | |
- {"آ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after… | |
- {"ا", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_after… | |
- {"ۃ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_… | |
- {"ہ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line_… | |
- {"م", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &mark_line}, | |
- {"'", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"ث", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &change_to_eo… | |
- {"ڈ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &delete_to_eo… | |
- {"ر", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &replace}, | |
- {"^", 0, 0, NORMAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHO… | |
- {"ت", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ٹ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ف", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"ّ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, KE… | |
- {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_inse… | |
-}; | |
- | |
-static struct key keys_hi[] = { | |
- {NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &bac… | |
- {NULL, 0, XK_Left, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KE… | |
- {NULL, 0, XK_Right, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|K… | |
- {NULL, 0, XK_Up, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_… | |
- {NULL, 0, XK_Down, VISUAL|INSERT|NORMAL, KEY_NONE|KEY_MOTIONALLOWED|KE… | |
- {NULL, XK_ANY_MOD, XK_Return, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN… | |
- {NULL, 0, XK_Delete, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &delete… | |
- {NULL, 0, XK_Escape, NORMAL|VISUAL|INSERT, KEY_ANY, KEY_ENSURE_CURSOR_… | |
- {"ि", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &ente… | |
- {"ह", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, K… | |
- {"ल", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, K… | |
- {"ज", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, K… | |
- {"क", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUMBER, K… | |
- {"ह", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY… | |
- {"त", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_NONE, &toggle_har… | |
- {NULL, 0, XK_space, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY_NUM… | |
- {"ज", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY… | |
- {"न", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY… | |
- {"प", ControlMask, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED|KEY… | |
- {"0", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"0", 0, 0, NORMAL|VISUAL, KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &push… | |
- {"1", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"2", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"3", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"4", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"5", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"6", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"7", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"8", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"9", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_NUMBERALLOWED, KEY… | |
- {"्", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &… | |
- {"ॉ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &… | |
- {"द", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"य", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"ञ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &… | |
- {"च", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"ड", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &enter_visua… | |
- {"ो", 0, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &switch_sele… | |
- {"च", ControlMask, 0, VISUAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &c… | |
- {"ड", ControlMask, 0, INSERT, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &c… | |
- {"ग", 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_… | |
- {"ण", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &key_search_… | |
- {"ु", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &… | |
- {"ू", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &… | |
- {".", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &re… | |
- {"श", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &un… | |
- {"य", ControlMask, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &re… | |
- {"ब", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSO… | |
- {"ट", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSO… | |
- {"े", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSO… | |
- {"य", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSO… | |
- {"द", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSO… | |
- {"ु", ControlMask, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSO… | |
- {"$", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"व", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"े", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"ॐ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"ै", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"ब", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"भ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"घ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"झ", 0, 0, NORMAL, KEY_NONE|KEY_NUMBER, KEY_ENSURE_CURSOR_SHOWN, &… | |
- {"ी", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &insert_at_b… | |
- {"प", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_norma… | |
- {"फ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &paste_norma… | |
- {"आ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_afte… | |
- {"ा", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_afte… | |
- {"ौ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line… | |
- {"ो", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &append_line… | |
- {"म", 0, 0, NORMAL|VISUAL, KEY_NONE, KEY_NONE, &mark_line}, | |
- {"'", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CUR… | |
- {"छ", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &change_to_e… | |
- {"ध", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &delete_to_e… | |
- {"र", 0, 0, NORMAL, KEY_NONE, KEY_ENSURE_CURSOR_SHOWN, &replace}, | |
- {"^", 0, 0, NORMAL, KEY_NONE|KEY_MOTIONALLOWED, KEY_ENSURE_CURSOR_SHO… | |
- {"त", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"थ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"ट", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"ठ", 0, 0, NORMAL|VISUAL, KEY_NONE|KEY_NUMBER|KEY_MOTIONALLOWED, K… | |
- {"", 0, 0, INSERT, KEY_ANY, KEY_ENSURE_CURSOR_SHOWN, &insert_mode_inse… | |
-}; | |
- | |
-GEN_KEY_ARRAY(struct key, keys_en, keys_de, keys_ur, keys_hi); | |
diff --git a/keys_command.c b/keys_command.c | |
t@@ -1,3 +1,4 @@ | |
+/* FIXME: remove CHECK_VIEW_LOCKED when it is confirmed that the new system wo… | |
/* FIXME: Parse commands properly and allow combinations of commands */ | |
#include <stdio.h> | |
#include <ctype.h> | |
t@@ -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" | |
t@@ -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… | |
+static int substitute_yes_all(ledit_view *view, char *key_text, size_t len, si… | |
+static int substitute_no(ledit_view *view, char *key_text, size_t len, size_t … | |
+static int substitute_no_all(ledit_view *view, char *key_text, size_t len, siz… | |
+static int edit_insert_text(ledit_view *view, char *key_text, size_t len, size… | |
+static int edit_cursor_left(ledit_view *view, char *key_text, size_t len, size… | |
+static int edit_cursor_right(ledit_view *view, char *key_text, size_t len, siz… | |
+static int edit_cursor_to_end(ledit_view *view, char *key_text, size_t len, si… | |
+static int edit_cursor_to_beginning(ledit_view *view, char *key_text, size_t l… | |
+static int edit_backspace(ledit_view *view, char *key_text, size_t len, size_t… | |
+static int edit_delete(ledit_view *view, char *key_text, size_t len, size_t la… | |
+static int edit_submit(ledit_view *view, char *key_text, size_t len, size_t la… | |
+static int edit_prevcommand(ledit_view *view, char *key_text, size_t len, size… | |
+static int edit_nextcommand(ledit_view *view, char *key_text, size_t len, size… | |
+static int edit_prevsearch(ledit_view *view, char *key_text, size_t len, size_… | |
+static int edit_nextsearch(ledit_view *view, char *key_text, size_t len, size_… | |
+static int editsearch_submit(ledit_view *view, char *key_text, size_t len, siz… | |
+static int editsearchb_submit(ledit_view *view, char *key_text, size_t len, si… | |
+static int edit_discard(ledit_view *view, char *key_text, size_t len, size_t l… | |
+ | |
+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 … | |
+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|CM… | |
+ {"edit-cursor-left", &edit_cursor_left, KEY_FLAG_LOCK_ALLOWED, CMD_EDI… | |
+ {"edit-cursor-right", &edit_cursor_right, KEY_FLAG_LOCK_ALLOWED, CMD_E… | |
+ {"edit-cursor-to-beginning", &edit_cursor_to_beginning, KEY_FLAG_LOCK_… | |
+ {"edit-cursor-to-end", &edit_cursor_to_end, KEY_FLAG_LOCK_ALLOWED, CMD… | |
+ {"edit-delete", &edit_delete, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_EDIT… | |
+ {"edit-discard", &edit_discard, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT|CMD_ED… | |
+ {"edit-insert-text", &edit_insert_text, KEY_FLAG_LOCK_ALLOWED, CMD_EDI… | |
+ {"edit-next-command", &edit_nextcommand, KEY_FLAG_LOCK_ALLOWED, CMD_ED… | |
+ {"edit-next-search", &edit_nextsearch, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT… | |
+ {"edit-previous-command", &edit_prevcommand, KEY_FLAG_LOCK_ALLOWED, CM… | |
+ {"edit-previous-search", &edit_prevsearch, KEY_FLAG_LOCK_ALLOWED, CMD_… | |
+ {"edit-submit", &edit_submit, KEY_FLAG_LOCK_ALLOWED, CMD_EDIT}, | |
+ {"edit-submit-backwards-search", &editsearchb_submit, KEY_FLAG_LOCK_AL… | |
+ {"edit-submit-search", &editsearch_submit, KEY_FLAG_LOCK_ALLOWED, CMD_… | |
+ {"substitute-no", &substitute_no, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOC… | |
+ {"substitute-no-all", &substitute_no_all, KEY_FLAG_JUMP_TO_CURSOR|KEY_… | |
+ {"substitute-yes", &substitute_yes, KEY_FLAG_JUMP_TO_CURSOR, CMD_SUBST… | |
+ {"substitute-yes-all", &substitute_yes_all, KEY_FLAG_JUMP_TO_CURSOR, C… | |
+}; | |
+ | |
+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_LO… | |
+}; | |
+ | |
+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; | |
t@@ -99,20 +203,17 @@ view_locked_error(ledit_view *view) { | |
#define CHECK_VIEW_LOCKED(view) if (view->lock_text) return view_locked_error(… | |
-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_ind… | |
-/* 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; | |
t@@ -120,7 +221,6 @@ handle_write(ledit_view *view, char *cmd, size_t l1, size_… | |
/* 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; | |
t@@ -132,7 +232,7 @@ handle_write(ledit_view *view, char *cmd, size_t l1, size_… | |
stored = 0; | |
} | |
/* FIXME: file locks */ | |
- char *errstr; | |
+ char *errstr = NULL; | |
if (filename) { | |
struct stat sb; | |
/* There technically is a race between checking stat and actua… | |
t@@ -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; | |
t@@ -194,7 +293,7 @@ create_view(ledit_view *view, char *cmd, size_t l1, size_t… | |
(void)cmd; | |
(void)l1; | |
(void)l2; | |
- buffer_add_view(view->buffer, view->theme, view->mode, view->cur_line,… | |
+ buffer_add_view(view->buffer, view->mode, view->cur_line, view->cur_in… | |
return 0; | |
} | |
t@@ -205,7 +304,6 @@ close_view(ledit_view *view, char *cmd, size_t l1, size_t … | |
(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; | |
t@@ -220,7 +318,7 @@ close_view(ledit_view *view, char *cmd, size_t l1, size_t … | |
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; | |
t@@ -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; | |
t@@ -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, | |
t@@ -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; | |
t@@ -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 … | |
+ size_t marklen = aftermark_idx - (cur + 1); | |
size_t l, b; | |
- if (!strncmp(c + 1, "<", strlen("<")) && view->sel_val… | |
+ if (*(c + 1) == '<' && view->sel_valid) { | |
l = view->sel.line1 < view->sel.line2 ? view->… | |
- } else if (!strncmp(c + 1, ">", strlen(">")) && view->… | |
+ } else if (*(c + 1) == '>' && view->sel_valid) { | |
l = view->sel.line1 > view->sel.line2 ? view->… | |
- } else { | |
- if (buffer_get_mark(view->buffer, c + 1, markl… | |
- *errstr_ret = "Invalid mark"; | |
- return 1; | |
- } | |
+ } else if (buffer_get_mark(view->buffer, c + 1, markle… | |
+ *errstr_ret = "Invalid mark"; | |
+ return 1; | |
} | |
if (!*l1_valid) { | |
l1 = l + 1; | |
t@@ -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)) { | |
t@@ -505,7 +587,7 @@ parse_range( | |
l1 = 1; | |
l2 = view->lines_num; | |
*l1_valid = *l2_valid = 1; | |
- c++; | |
+ cur++; | |
s = 0; | |
break; | |
} else { | |
t@@ -543,7 +625,7 @@ parse_range( | |
} else { | |
break; | |
} | |
- c++; | |
+ cur++; | |
} | |
if ((!*l1_valid || !*l2_valid) && !(s & START_RANGE)) { | |
*errstr_ret = "Invalid range"; | |
t@@ -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; | |
t@@ -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, &e… | |
+ size_t start_idx; | |
+ if (parse_range(view, cmd, len, &start_idx, &l1, &l2, &l1_valid, &l2_v… | |
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… | |
+ window_show_message(view->window, "Command doe… | |
+ return 0; | |
+ } else if (view->lock_text && !(cur_cmds->cmds[i].cb->… | |
+ window_show_message(view->window, view->lock_t… | |
+ return 0; | |
+ } | |
+ return cur_cmds->cmds[i].cb->func(view, cur_str + text… | |
} | |
} | |
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) { | |
t@@ -606,18 +710,16 @@ substitute_yes(ledit_view *view, char *key_text, size_t … | |
} | |
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_i… | |
+ (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; | |
t@@ -631,16 +733,16 @@ substitute_no(ledit_view *view, char *key_text, size_t l… | |
} | |
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_in… | |
+ (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_ind… | |
+ (void)lang_index; | |
/* FIXME: overflow */ | |
window_insert_bottom_bar_text(view->window, key_text, len); | |
window_set_bottom_bar_cursor( | |
t@@ -650,57 +752,50 @@ edit_insert_text(ledit_view *view, char *key_text, size_… | |
} | |
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_i… | |
+ (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 … | |
+ (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_ind… | |
+ (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_in… | |
+ (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); | |
t@@ -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_ind… | |
+ (void)key_text; (void)len; (void)lang_index; | |
if (cmdhistory.cur > 0) { | |
cmdhistory.cur--; | |
window_set_bottom_bar_realtext(view->window, cmdhistory.cmds[c… | |
t@@ -732,9 +826,8 @@ edit_prevcommand(ledit_view *view, char *key_text, size_t … | |
} | |
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_ind… | |
+ (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[c… | |
t@@ -747,9 +840,8 @@ edit_nextcommand(ledit_view *view, char *key_text, size_t … | |
} | |
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_inde… | |
+ (void)key_text; (void)len; (void)lang_index; | |
if (searchhistory.cur > 0) { | |
searchhistory.cur--; | |
window_set_bottom_bar_realtext(view->window, searchhistory.cmd… | |
t@@ -759,9 +851,8 @@ edit_prevsearch(ledit_view *view, char *key_text, size_t l… | |
} | |
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_inde… | |
+ (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.cmd… | |
t@@ -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_in… | |
+ (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); | |
t@@ -818,9 +908,8 @@ editsearch_submit(ledit_view *view, char *key_text, size_t… | |
} | |
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_i… | |
+ (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); | |
t@@ -841,9 +930,8 @@ editsearchb_submit(ledit_view *view, char *key_text, size_… | |
} | |
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; | |
t@@ -854,28 +942,46 @@ command_key_handler(ledit_view *view, XEvent *event, int… | |
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 & ~ShiftMa… | |
- cur_keys[i].text[0] == '\0')) { | |
- grabkey = cur_keys[i].func(view, buf, (size_t)… | |
+ (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 & ~S… | |
+ cur_keys->keys[i].text[0] == '\0')) { | |
+ flags = cur_keys->keys[i].cb->flags; | |
+ if (!(flags & KEY_FLAG_LOCK_ALLOWED) && view->… | |
+ (void)view_locked_error(view); | |
+ grabkey = 0; | |
+ break; | |
+ } | |
+ grabkey = cur_keys->keys[i].cb->func(view, buf… | |
+ 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_tex… | |
+ (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_… | |
+ 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 | |
t@@ -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 | |
t@@ -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 l… | |
-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… | |
- unsigned int mods; /* modifier mask */ | |
- KeySym keysym; /* for other keys, e.g. arr… | |
- 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 | |
t@@ -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}, /* FIX… | |
+ {"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 fini… | |
+ {"undo", "z", ControlMask, 0, INSERT}, | |
+ {"redo", "y", ControlMask, 0, INSERT}, /* FIXME: this is confusing wit… | |
+ {"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_EDIT… | |
+ {"edit-cursor-left", NULL, 0, XK_Left, CMD_EDIT|CMD_EDITSEARCH|CMD_EDI… | |
+ {"edit-cursor-right", NULL, 0, XK_Right, CMD_EDIT|CMD_EDITSEARCH|CMD_E… | |
+ {"edit-cursor-right", NULL, 0, XK_Right, CMD_EDIT|CMD_EDITSEARCH|CMD_E… | |
+ {"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_EDITSEARCH… | |
+ {"edit-next-search", NULL, 0, XK_Down, CMD_EDITSEARCH|CMD_EDITSEARCHB}, | |
+ {"edit-backspace", NULL, 0, XK_BackSpace, CMD_EDIT|CMD_EDITSEARCH|CMD_… | |
+ {"edit-delete", NULL, 0, XK_Delete, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITSE… | |
+ {"edit-cursor-to-end", NULL, 0, XK_End, CMD_EDIT|CMD_EDITSEARCH|CMD_ED… | |
+ {"edit-cursor-to-beginning", NULL, 0, XK_Home, CMD_EDIT|CMD_EDITSEARCH… | |
+ {"edit-discard", NULL, 0, XK_Escape, CMD_EDIT|CMD_EDITSEARCH|CMD_EDITS… | |
+ {"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 | |
t@@ -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 | |
t@@ -8,6 +8,7 @@ | |
.Nd weird text editor | |
.Sh SYNOPSIS | |
.Nm | |
+.Op Fl c Ar config | |
.Op Ar file | |
.Sh DESCRIPTION | |
.Nm | |
t@@ -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. | |
t@@ -693,12 +705,6 @@ Also note that the commands which take filenames currentl… | |
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 | |
t@@ -819,71 +825,8 @@ Note that the text is pasted at the current cursor positi… | |
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 | |
t@@ -930,7 +873,8 @@ Pango developers decided to hide spaces at the end of a li… | |
.Sh SEE ALSO | |
.Xr ed 1 , | |
.Xr vi 1 , | |
-.Xr vim 1 | |
+.Xr vim 1 , | |
+.Xr leditrc 5 | |
.Sh AUTHORS | |
.An lumidify Aq Mt [email protected] | |
.Sh BUGS | |
t@@ -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 | |
t@@ -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> | |
t@@ -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" | |
t@@ -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[]); | |
t@@ -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) { | |
t@@ -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 */ | |
t@@ -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)) { | |
t@@ -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(); | |
t@@ -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 = … | |
+ 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 … | |
+ 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… | |
+ } | |
+ if (failure) { | |
+ fprintf(stderr, "Unable to load configuration: %s\n", … | |
+ if (load_default_errstr) | |
+ fprintf(stderr, "Also unable to load default c… | |
+ 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 nanose… | |
+ #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 sc… | |
/* 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; | |
t@@ -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 | |
t@@ -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… | |
- } else if (readonly) { | |
- window_show_message_fmt(view->window, "%s: rea… | |
- } 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, … | |
+ } else if (readonly) { | |
+ window_show_message_fmt(view->window, … | |
+ } else { | |
+ window_show_message(view->window, argv… | |
+ } | |
} | |
} | |
} | |
t@@ -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? */ | |
t@@ -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); | |
} | |
t@@ -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 | |
t@@ -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 [email protected] | |
diff --git a/leditrc.example b/leditrc.example | |
t@@ -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 … | |
+ 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 … | |
+ 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-sear… | |
+ bind edit-submit-backwards-search keysym return mods any modes… | |
+ bind edit-cursor-left keysym left modes edit|edit-search|edit-… | |
+ bind edit-cursor-right keysym right modes edit|edit-search|edi… | |
+ 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-sea… | |
+ bind edit-next-search keysym down modes edit-search|edit-searc… | |
+ bind edit-backspace keysym backspace modes edit|edit-search|ed… | |
+ bind edit-delete keysym delete modes edit|edit-search|edit-sea… | |
+ bind edit-cursor-to-end keysym end modes edit|edit-search|edit… | |
+ bind edit-cursor-to-beginning keysym home modes edit|edit-sear… | |
+ bind edit-discard keysym escape modes edit|edit-search|edit-se… | |
+ bind edit-insert-text catchall modes edit|edit-search|edit-sea… | |
+ } | |
+ 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 | |
t@@ -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); | |
} | |
t@@ -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 <[email protected]> | |
t@@ -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 | |
t@@ -3,6 +3,7 @@ | |
#include <stddef.h> | |
#include <stdint.h> | |
+#include <stdarg.h> | |
/* | |
* These functions all wrap the regular functions but exit on error. | |
t@@ -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 | |
t@@ -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, &them… | |
- XftColorAllocName(common->dpy, common->vis, common->cm, TEXT_BG, &them… | |
- XftColorAllocName(common->dpy, common->vis, common->cm, CURSOR_FG, &th… | |
- XftColorAllocName(common->dpy, common->vis, common->cm, CURSOR_BG, &th… | |
- XftColorAllocName(common->dpy, common->vis, common->cm, SELECTION_FG, … | |
- XftColorAllocName(common->dpy, common->vis, common->cm, SELECTION_BG, … | |
- XftColorAllocName(common->dpy, common->vis, common->cm, BAR_FG, &theme… | |
- XftColorAllocName(common->dpy, common->vis, common->cm, BAR_BG, &theme… | |
- XftColorAllocName(common->dpy, common->vis, common->cm, BAR_CURSOR, &t… | |
- XftColorAllocName(common->dpy, common->vis, common->cm, SCROLLBAR_FG, … | |
- XftColorAllocName(common->dpy, common->vis, common->cm, 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_f… | |
- XftColorFree(common->dpy, common->vis, common->cm, &theme->selection_b… | |
- 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_f… | |
- XftColorFree(common->dpy, common->vis, common->cm, &theme->scrollbar_b… | |
- free(theme); | |
-} | |
diff --git a/theme.h b/theme.h | |
t@@ -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 | |
t@@ -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 */ | |
t@@ -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 | |
t@@ -1,5 +1,7 @@ | |
+#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
+#include <stdarg.h> | |
#include "util.h" | |
#include "memory.h" | |
t@@ -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 | |
t@@ -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; | |
} | |
t@@ -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-term… | |
+ /* FIXME: Test this because I was tired while writing it */ | |
+ int cmp = strncmp(buf1->text, buf2->text, LEDIT_MIN(buf1->len, buf2->l… | |
+ 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 | |
t@@ -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 { | |
t@@ -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 | |
t@@ -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 | |
t@@ -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, | |
t@@ -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 | |
t@@ -1,3 +1,4 @@ | |
+#include <string.h> | |
#include <stddef.h> | |
#include "memory.h" | |
t@@ -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(); | |
t@@ -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 | |
t@@ -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. | |
t@@ -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__, … | |
+ #define ledit_debug_fmt(fmt, ...) do {fprintf(stderr, "%s:%d: " fmt, _… | |
+#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 | |
t@@ -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; | |
t@@ -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 … | |
+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) | |
t@@ -108,8 +108,7 @@ view_create(ledit_buffer *buffer, ledit_theme *theme, ledi… | |
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}; | |
t@@ -328,12 +327,13 @@ get_pango_attributes(size_t start_byte, size_t end_byte,… | |
/* 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) { | |
t@@ -349,8 +349,8 @@ set_line_layout_attrs(ledit_view *view, size_t line, Pango… | |
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… | |
} | |
t@@ -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 */ | |
t@@ -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; | |
} | |
t@@ -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; | |
t@@ -1884,7 +1886,7 @@ view_redraw_text(ledit_view *view) { | |
h += vline->h; | |
} | |
- XSetForeground(view->buffer->common->dpy, view->window->gc, view->them… | |
+ XSetForeground(view->buffer->common->dpy, view->window->gc, theme->cur… | |
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 | |
t@@ -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; | |
t@@ -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 e… | |
/* 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 */ | |
t@@ -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, si… | |
/* | |
* Lock a view. | |
diff --git a/window.c b/window.c | |
t@@ -14,7 +14,6 @@ | |
#include <X11/extensions/Xdbe.h> | |
#include "util.h" | |
-#include "theme.h" | |
#include "memory.h" | |
#include "common.h" | |
#include "txtbuf.h" | |
t@@ -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 { | |
t@@ -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; | |
t@@ -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-… | |
pango_layout_get_pixel_size(window->bb->line, &window->bb->line_w, &wi… | |
draw_grow(window, window->bb->line_draw, window->bb->line_w, window->b… | |
- XftDrawRect(window->bb->line_draw->xftdraw, &window->theme->bar_bg, 0,… | |
- pango_xft_render_layout(window->bb->line_draw->xftdraw, &window->theme… | |
+ XftDrawRect(window->bb->line_draw->xftdraw, &theme->bar_bg, 0, 0, wind… | |
+ pango_xft_render_layout(window->bb->line_draw->xftdraw, &theme->bar_fg… | |
recalc_text_size(window); | |
window->redraw = 1; | |
} | |
t@@ -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) { | |
t@@ -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, &wi… | |
draw_grow(window, window->bb->mode_draw, window->bb->mode_w, window->b… | |
- XftDrawRect(window->bb->mode_draw->xftdraw, &window->theme->bar_bg, 0,… | |
- pango_xft_render_layout(window->bb->mode_draw->xftdraw, &window->theme… | |
+ XftDrawRect(window->bb->mode_draw->xftdraw, &theme->bar_bg, 0, 0, wind… | |
+ pango_xft_render_layout(window->bb->mode_draw->xftdraw, &theme->bar_fg… | |
recalc_text_size(window); | |
window->redraw = 1; | |
} | |
t@@ -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; | |
t@@ -569,7 +575,6 @@ window_create(ledit_common *common, ledit_theme *theme, le… | |
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); | |
t@@ -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); | |
t@@ -671,7 +677,8 @@ window_cleanup(void) { | |
void | |
window_clear(ledit_window *window) { | |
- XSetForeground(window->common->dpy, window->gc, window->theme->text_bg… | |
+ 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… | |
); | |
t@@ -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_b… | |
XFillRectangle( | |
t@@ -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_n… | |
window_resize( | |
window, | |
window->last_resize_event.xconfigure.width, | |
t@@ -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; | |
} | |
} | |
} | |
t@@ -1036,6 +1044,7 @@ window_register_motion(ledit_window *window, XEvent *eve… | |
/* 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; | |
t@@ -1062,7 +1071,7 @@ window_button_press(ledit_window *window, XEvent *event,… | |
break; | |
case Button4: | |
case Button5: | |
- window->scroll_offset += scroll_num * window->theme->s… | |
+ window->scroll_offset += scroll_num * theme->scrollbar… | |
if (window->scroll_offset < 0) | |
window->scroll_offset = 0; | |
if (window->scroll_offset + window->text_h > window->s… | |
diff --git a/window.h b/window.h | |
t@@ -16,7 +16,6 @@ | |
#include <X11/extensions/Xdbe.h> | |
#include <pango/pangoxft.h> | |
-#include "theme.h" | |
#include "common.h" | |
#include "txtbuf.h" | |
t@@ -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 … | |
+ of being delayed due to the event filtering - this is noticeable in… | |
+ window managers that resize the window immediately after it is crea… | |
+ The whole event filtering system needs to be rethought anyways, but… | |
+ at least sort of works for the time being. (FIXME) */ | |
+ int first_resize; | |
int scroll_num; | |
int scroll_delta; | |
t@@ -75,7 +80,6 @@ typedef struct { | |
XVaNestedList spotlist; | |
ledit_common *common; | |
- ledit_theme *theme; | |
/* various callbacks */ | |
void (*paste_callback)(void *, char *, size_t); | |
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_mo… | |
+ledit_window *window_create(ledit_common *common, ledit_mode mode); | |
/* | |
* Destroy a window. |