summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md1
-rw-r--r--config.c112
-rw-r--r--ds.h8
-rw-r--r--main.c12
-rw-r--r--ted.c25
-rw-r--r--ted.cfg12
-rw-r--r--ted.h15
-rw-r--r--util.c4
-rw-r--r--util.h2
9 files changed, 102 insertions, 89 deletions
diff --git a/README.md b/README.md
index 4cbddae..83c037d 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,7 @@ explaining what they do.
Keyboard shortcuts are of the form `key combo = action`, where `action` is an argument (number or string),
followed by a command. The commands match the things in the command palette (Ctrl+Shift+p), but `:` is added to the beginning to make
it clear it's a command.
+A list of key names can be found [here](https://wiki.libsdl.org/SDL2/SDL_Keycode).
Colors are formatted like `#rgb`, `#rgba`, `#rrggbb` or `#rrggbbaa`, where r, g, b, and a are red, green,
blue, and alpha (transparency/opacity). You can use a [color picker](https://www.google.com/search?q=color+picker) to help you out.
diff --git a/config.c b/config.c
index 25915cc..45bc093 100644
--- a/config.c
+++ b/config.c
@@ -162,6 +162,7 @@ static void config_err(ConfigReader *cfg, const char *fmt, ...) {
va_start(args, fmt);
vsnprintf(error + strlen(error), sizeof error - strlen(error) - 1, fmt, args);
va_end(args);
+ ted_seterr(cfg->ted, "%s", error);
}
static void context_copy(SettingsContext *dest, const SettingsContext *src) {
@@ -221,6 +222,7 @@ static void settings_copy(Settings *dest, const Settings *src) {
if (src->language_extensions[i])
dest->language_extensions[i] = str_dup(src->language_extensions[i]);
}
+ dest->key_actions = arr_copy(src->key_actions);
}
static void context_free(SettingsContext *ctx) {
@@ -265,83 +267,35 @@ static u32 config_parse_key_combo(ConfigReader *cfg, const char *str) {
}
// read key
- SDL_Scancode scancode = SDL_GetScancodeFromName(str);
- if (scancode == SDL_SCANCODE_UNKNOWN) {
+ SDL_Keycode keycode = SDL_GetKeyFromName(str);
+ if (keycode == SDLK_UNKNOWN) {
typedef struct {
const char *keyname1;
const char *keyname2; // alternate key name
- SDL_Scancode scancode;
- bool shift;
+ SDL_Keycode keycode;
} KeyName;
static KeyName const key_names[] = {
- {"Apostrophe", "Single Quote", SDL_SCANCODE_APOSTROPHE, 0},
- {"Backslash", 0, SDL_SCANCODE_BACKSLASH, 0},
- {"Comma", 0, SDL_SCANCODE_COMMA, 0},
- {"Equals", 0, SDL_SCANCODE_EQUALS, 0},
- {"Grave", "Backtick", SDL_SCANCODE_GRAVE, 0},
- {"Keypad Plus", 0, SDL_SCANCODE_KP_PLUS, 0},
- {"Keypad Minus", 0, SDL_SCANCODE_KP_MINUS, 0},
- {"Keypad Divide", 0, SDL_SCANCODE_KP_DIVIDE, 0},
- {"Keypad Multiply", 0, SDL_SCANCODE_KP_MULTIPLY, 0},
- {"Left Bracket", 0, SDL_SCANCODE_LEFTBRACKET, 0}, // [
- {"Right Bracket", 0, SDL_SCANCODE_RIGHTBRACKET, 0}, // ]
- {"Dash", 0, SDL_SCANCODE_MINUS, 0},
- {"Minus", 0, SDL_SCANCODE_MINUS, 0},
- {"Period", 0, SDL_SCANCODE_PERIOD, 0},
- {"Semicolon", 0, SDL_SCANCODE_SEMICOLON, 0},
- {"Slash", 0, SDL_SCANCODE_SLASH, 0},
- {"Enter", 0, SDL_SCANCODE_RETURN, 0},
- {"Keypad Return", 0, SDL_SCANCODE_KP_ENTER, 0},
- {"Exclaim", "Exclamation Mark", SDL_SCANCODE_1, 1},
- {"!", 0, SDL_SCANCODE_1, 1},
- {"At", "@", SDL_SCANCODE_2, 1},
- {"Hash", "#", SDL_SCANCODE_3, 1},
- {"Dollar", "$", SDL_SCANCODE_4, 1},
- {"Percent", "%", SDL_SCANCODE_5, 1},
- {"Caret", "^", SDL_SCANCODE_6, 1},
- {"Ampersand", "&", SDL_SCANCODE_7, 1},
- {"Asterisk", "*", SDL_SCANCODE_8, 1},
- {"Left Paren", "(", SDL_SCANCODE_9, 1},
- {"Right Paren", ")", SDL_SCANCODE_0, 1},
- {"Underscore", "_", SDL_SCANCODE_MINUS, 1},
- {"Plus", "+", SDL_SCANCODE_EQUALS, 1},
- {"Left Brace", "{", SDL_SCANCODE_LEFTBRACKET, 1},
- {"Right Brace", "}", SDL_SCANCODE_RIGHTBRACKET, 1},
- {"Pipe", "|", SDL_SCANCODE_BACKSLASH, 1},
- {"Colon", ":", SDL_SCANCODE_SEMICOLON, 1},
- {"Double Quote", "\"", SDL_SCANCODE_APOSTROPHE, 1},
- {"Less Than", "<", SDL_SCANCODE_COMMA, 1},
- {"Greater Than", ">", SDL_SCANCODE_PERIOD, 1},
- {"Question Mark", "?", SDL_SCANCODE_SLASH, 1},
- {"Question", 0, SDL_SCANCODE_SLASH, 1},
- {"Tilde", "~", SDL_SCANCODE_GRAVE, 1},
- {"X1", "x1", SCANCODE_MOUSE_X1, 0},
- {"X2", "x2", SCANCODE_MOUSE_X2, 0}
+ {"X1", NULL, KEYCODE_X1},
+ {"X2", NULL, KEYCODE_X2},
+ {"Enter", NULL, SDLK_RETURN},
+ {"Equals", "Equal", SDLK_EQUALS},
};
-
// @TODO(optimize): sort key_names (and split keyname1/2); do a binary search
for (size_t i = 0; i < arr_count(key_names); ++i) {
KeyName const *k = &key_names[i];
- if (streq(str, k->keyname1) || (k->keyname2 && streq(str, k->keyname2))) {
- scancode = k->scancode;
- if (k->shift) {
- if (modifier & KEY_MODIFIER_SHIFT) {
- config_err(cfg, "Shift+%s is redundant.", str);
- return 0;
- }
- modifier |= KEY_MODIFIER_SHIFT;
- }
+ if (streq_case_insensitive(str, k->keyname1) || (k->keyname2 && streq_case_insensitive(str, k->keyname2))) {
+ keycode = k->keycode;
break;
}
}
- if (scancode == SDL_SCANCODE_UNKNOWN) {
- if (isdigit(str[0])) { // direct scancode numbers, e.g. Ctrl+24 or Ctrl+08
+ if (keycode == SDLK_UNKNOWN) {
+ if (isdigit(str[0])) { // direct keycode numbers, e.g. Ctrl+24 or Ctrl+08
char *endp;
long n = strtol(str, &endp, 10);
- if (*endp == '\0' && n > 0 && n < SCANCODE_COUNT) {
- scancode = (SDL_Scancode)n;
+ if (*endp == '\0' && n > 0) {
+ keycode = (SDL_Keycode)n;
} else {
- config_err(cfg, "Invalid scancode number: %s", str);
+ config_err(cfg, "Invalid keycode number: %s", str);
return 0;
}
} else {
@@ -350,7 +304,7 @@ static u32 config_parse_key_combo(ConfigReader *cfg, const char *str) {
}
}
}
- return (u32)scancode << 3 | modifier;
+ return KEY_COMBO(modifier, keycode);
}
@@ -767,7 +721,9 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi
config_err(cfg, "'%s' is not a valid color. Colors should look like #rgb, #rgba, #rrggbb, or #rrggbbaa.", value);
}
} else {
- #if DEBUG
+ // don't actually produce this error.
+ // we have removed colors in the past and might again in the future.
+ #if 0
config_err(cfg, "No such color setting: %s", key);
#endif
}
@@ -775,7 +731,18 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi
case SECTION_KEYBOARD: {
// lines like Ctrl+Down = 10 :down
u32 key_combo = config_parse_key_combo(cfg, key);
- KeyAction *action = &settings->key_actions[key_combo];
+ KeyAction *action = NULL;
+ // check if we already have an action for this key combo
+ arr_foreach_ptr(settings->key_actions, KeyAction, act) {
+ if (act->key_combo == key_combo) {
+ action = act;
+ break;
+ }
+ }
+ // if this is a new key combo, add an element to the key_actions array
+ if (!action)
+ action = arr_addp(settings->key_actions);
+ action->key_combo = key_combo;
llong argument = 1; // default argument = 1
if (isdigit(*value)) {
// read the argument
@@ -948,6 +915,15 @@ static void config_parse_line(ConfigReader *cfg, Settings *settings, const Confi
}
}
+static int key_action_qsort_cmp_combo(const void *av, const void *bv) {
+ const KeyAction *a = av, *b = bv;
+ if (a->key_combo < b->key_combo)
+ return -1;
+ if (a->key_combo > b->key_combo)
+ return 1;
+ return 0;
+}
+
void config_parse(Ted *ted, ConfigPart **pparts) {
config_init_settings();
@@ -1020,6 +996,11 @@ void config_parse(Ted *ted, ConfigPart **pparts) {
}
arr_clear(*pparts);
+
+ arr_foreach_ptr(ted->all_settings, Settings, s) {
+ // sort key_actions by key_combo.
+ arr_qsort(s->key_actions, key_action_qsort_cmp_combo);
+ }
}
static int gluint_cmp(const void *av, const void *bv) {
@@ -1056,6 +1037,7 @@ void config_free(Ted *ted) {
free(settings->language_extensions[i]);
gl_rc_sab_decref(&settings->bg_shader);
gl_rc_texture_decref(&settings->bg_texture);
+ arr_free(settings->key_actions);
}
diff --git a/ds.h b/ds.h
index 397dcfa..a588be7 100644
--- a/ds.h
+++ b/ds.h
@@ -192,6 +192,13 @@ static void *arr_remove_(void *arr, size_t member_size, size_t index) {
}
}
+static void *arr_copy_(const void *arr, size_t member_size) {
+ void *new_arr = NULL;
+ arr_set_len_(&new_arr, member_size, arr_len(arr));
+ memcpy(new_arr, arr, member_size * arr_len(arr));
+ return new_arr;
+}
+
#ifdef __cplusplus
#define arr_cast_typeof(a) (decltype(a))
#elif defined __GNUC__
@@ -224,6 +231,7 @@ static void *arr_remove_(void *arr, size_t member_size, size_t index) {
#define arr_pop_last(a) ((a)[--arr_hdr_(a)->len])
#define arr_size_in_bytes(a) (arr_len(a) * sizeof *(a))
#define arr_lastp(a) ((a) ? &(a)[arr_len(a)-1] : NULL)
+#define arr_copy(a) arr_cast_typeof(a) arr_copy_((a), sizeof *(a))
#define arr_foreach_ptr_end(a, type, var, end) type *end = (a) + arr_len(a); \
for (type *var = (a); var != end; ++var)
// Iterate through each element of the array, setting var to a pointer to the element.
diff --git a/main.c b/main.c
index 2254d51..d324299 100644
--- a/main.c
+++ b/main.c
@@ -1,7 +1,6 @@
/*
@TODO:
- rename ted_seterr to ted_error
-- use keycodes instead of scancodes (maybe that will make numlock modifier no longer necessary)
- go to declaration with LSP
- ted.h documentation
- handle multiple symbols with same name in go-to-definition menu
@@ -30,7 +29,8 @@
- bad json can give "Unexpected error: client exited without proper shutdown sequence"
FUTURE FEATURES:
- add numlock as a key modifier? (but make sure "Ctrl+S" handles both "No NumLock+Ctrl+S" and "NumLock+Ctrl+S")
-- comment-start + comment-end settings
+- font setting & support for multiple fonts to cover more characters
+- comment-start & comment-end settings
- robust find (results shouldn't move around when you type things)
- multiple files with command line arguments
- :set-build-command
@@ -633,9 +633,9 @@ int main(int argc, char **argv) {
float x = (float)event.button.x, y = (float)event.button.y;
if (button == SDL_BUTTON_X1) {
- ted_press_key(ted, SCANCODE_MOUSE_X1, key_modifier);
+ ted_press_key(ted, KEYCODE_X1, key_modifier);
} else if (button == SDL_BUTTON_X2) {
- ted_press_key(ted, SCANCODE_MOUSE_X2, key_modifier);
+ ted_press_key(ted, KEYCODE_X2, key_modifier);
}
if (button < arr_count(ted->nmouse_clicks)
@@ -707,9 +707,9 @@ int main(int argc, char **argv) {
ted->hover.time = 0.0;
} break;
case SDL_KEYDOWN: {
- SDL_Scancode scancode = event.key.keysym.scancode;
+ SDL_Keycode keycode = event.key.keysym.sym;
SDL_Keymod modifier = event.key.keysym.mod;
- ted_press_key(ted, scancode, modifier);
+ ted_press_key(ted, keycode, modifier);
} break;
case SDL_TEXTINPUT: {
char *text = event.text.text;
diff --git a/ted.c b/ted.c
index 5033467..389bd53 100644
--- a/ted.c
+++ b/ted.c
@@ -549,17 +549,28 @@ void ted_load_configs(Ted *ted, bool reloading) {
}
}
-void ted_press_key(Ted *ted, SDL_Scancode scancode, SDL_Keymod modifier) {
- u32 key_combo = (u32)scancode << 3 |
+void ted_press_key(Ted *ted, SDL_Keycode keycode, SDL_Keymod modifier) {
+ u32 key_combo = KEY_COMBO(
(u32)((modifier & (KMOD_LCTRL|KMOD_RCTRL)) != 0) << KEY_MODIFIER_CTRL_BIT |
(u32)((modifier & (KMOD_LSHIFT|KMOD_RSHIFT)) != 0) << KEY_MODIFIER_SHIFT_BIT |
- (u32)((modifier & (KMOD_LALT|KMOD_RALT)) != 0) << KEY_MODIFIER_ALT_BIT;
- if (key_combo < KEY_COMBO_COUNT) {
- KeyAction *action = &ted_active_settings(ted)->key_actions[key_combo];
- if (action->command) {
- command_execute(ted, action->command, action->argument);
+ (u32)((modifier & (KMOD_LALT|KMOD_RALT)) != 0) << KEY_MODIFIER_ALT_BIT,
+ keycode);
+
+ const KeyAction *const key_actions = ted_active_settings(ted)->key_actions;
+ u32 lo = 0;
+ u32 hi = arr_len(key_actions);
+ while (lo < hi) {
+ u32 mid = (lo + hi) / 2;
+ if (key_actions[mid].key_combo < key_combo) {
+ lo = mid + 1;
+ } else if (key_actions[mid].key_combo > key_combo) {
+ hi = mid;
+ } else {
+ command_execute(ted, key_actions[mid].command, key_actions[mid].argument);
+ return;
}
}
+ // nothing bound to this key
}
bool ted_get_mouse_buffer_pos(Ted *ted, TextBuffer **pbuffer, BufferPos *ppos) {
diff --git a/ted.cfg b/ted.cfg
index fa36df3..7e94d19 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -140,10 +140,10 @@ Up = :up
Shift+Up = :select-up
Down = :down
Shift+Down = :select-down
-Ctrl+Up = 1 :up-blank-line
-Ctrl+Shift+Up = 1 :select-up-blank-line
-Ctrl+Down = 1 :down-blank-line
-Ctrl+Shift+Down = 1 :select-down-blank-line
+Ctrl+Up = :up-blank-line
+Ctrl+Shift+Up = :select-up-blank-line
+Ctrl+Down = :down-blank-line
+Ctrl+Shift+Down = :select-down-blank-line
Home = :start-of-line
Shift+Home = :select-start-of-line
Ctrl+Home = :start-of-file
@@ -248,7 +248,7 @@ Alt+0 = 9 :tab-switch
X1 = :tab-prev
X2 = :tab-next
-Ctrl++ = 3 :increase-text-size
+Ctrl+Equals = 3 :increase-text-size
Ctrl+- = 3 :decrease-text-size
Ctrl+Alt+Shift+v = :view-only
@@ -256,6 +256,8 @@ Ctrl+Alt+Shift+v = :view-only
F4 = :build
Ctrl+[ = :build-prev-error
Ctrl+] = :build-next-error
+# Ctrl+Shift+1 applies to QWERTY keyboards (and others), Ctrl+! for AZERTY (and others)
+Ctrl+Shift+1 = :shell
Ctrl+! = :shell
Ctrl+t = :generate-tags
diff --git a/ted.h b/ted.h
index 7f759b5..fad04ac 100644
--- a/ted.h
+++ b/ted.h
@@ -95,9 +95,10 @@ ENUM_U8 {
#define SYNTAX_CODE SYNTAX_PREPROCESSOR // for markdown
#define SYNTAX_LINK SYNTAX_CONSTANT // for markdown
-#define SCANCODE_MOUSE_X1 (SDL_NUM_SCANCODES)
-#define SCANCODE_MOUSE_X2 (SDL_NUM_SCANCODES+1)
-#define SCANCODE_COUNT (SDL_NUM_SCANCODES+2)
+enum {
+ KEYCODE_X1 = 1<<20,
+ KEYCODE_X2
+};
// a "key combo" is some subset of {control, shift, alt} + some key.
#define KEY_COMBO_COUNT (SCANCODE_COUNT << 3)
#define KEY_MODIFIER_CTRL_BIT 0
@@ -106,9 +107,11 @@ ENUM_U8 {
#define KEY_MODIFIER_CTRL ((u32)1<<KEY_MODIFIER_CTRL_BIT)
#define KEY_MODIFIER_SHIFT ((u32)1<<KEY_MODIFIER_SHIFT_BIT)
#define KEY_MODIFIER_ALT ((u32)1<<KEY_MODIFIER_ALT_BIT)
-// ctrl+alt+c is encoded as SDL_SCANCODE_C << 3 | KEY_MODIFIER_CTRL | KEY_MODIFIER_ALT
+// annoyingly SDL sets bit 30 for some keycodes
+#define KEY_COMBO(modifier, key) ((u32)(modifier) | ((u32)(key) & ~(1u<<30)) << 3 | ((u32)(key & (1<<30)) >> 10))
typedef struct KeyAction {
+ u32 key_combo;
u32 line_number; // config line number where this was set
Command command; // this will be 0 (COMMAND_UNKNOWN) if there's no action for the key
i64 argument;
@@ -192,7 +195,7 @@ typedef struct {
char build_default_command[256];
// [i] = comma-separated string of file extensions for language i, or NULL for none
char *language_extensions[LANG_COUNT];
- KeyAction key_actions[KEY_COMBO_COUNT];
+ KeyAction *key_actions; // dynamic array, sorted by KEY_COMBO(modifier, key)
} Settings;
// a position in the buffer
@@ -1098,7 +1101,7 @@ void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer);
// switch to this node
void ted_node_switch(Ted *ted, Node *node);
void ted_load_configs(Ted *ted, bool reloading);
-void ted_press_key(Ted *ted, SDL_Scancode scancode, SDL_Keymod modifier);
+void ted_press_key(Ted *ted, SDL_Keycode keycode, SDL_Keymod modifier);
bool ted_get_mouse_buffer_pos(Ted *ted, TextBuffer **pbuffer, BufferPos *ppos);
void ted_flash_error_cursor(Ted *ted);
void ted_go_to_position(Ted *ted, const char *path, u32 line, u32 index, bool is_lsp);
diff --git a/util.c b/util.c
index f09da3c..d20077b 100644
--- a/util.c
+++ b/util.c
@@ -298,6 +298,10 @@ int strcmp_case_insensitive(const char *a, const char *b) {
#endif
}
+bool streq_case_insensitive(const char *a, const char *b) {
+ return strcmp_case_insensitive(a, b) == 0;
+}
+
int str_qsort_case_insensitive_cmp(const void *av, const void *bv) {
const char *const *a = av, *const *b = bv;
return strcmp_case_insensitive(*a, *b);
diff --git a/util.h b/util.h
index adf71e0..c6bfab7 100644
--- a/util.h
+++ b/util.h
@@ -100,6 +100,8 @@ void print_bytes(const u8 *bytes, size_t n);
char *strstr_case_insensitive(const char *haystack, const char *needle);
// like strcmp, but case-insensitive
int strcmp_case_insensitive(const char *a, const char *b);
+// like streq, but case-insensitive
+bool streq_case_insensitive(const char *a, const char *b);
// function to be passed into qsort for case insensitive sorting
int str_qsort_case_insensitive_cmp(const void *av, const void *bv);
// the actual file name part of the path; get rid of the containing directory.