diff options
-rw-r--r-- | README.md | 44 | ||||
-rw-r--r-- | examples/skhdrc | 41 | ||||
-rw-r--r-- | src/hotkey.c | 78 | ||||
-rw-r--r-- | src/hotkey.h | 42 | ||||
-rw-r--r-- | src/parse.c | 178 | ||||
-rw-r--r-- | src/parse.h | 15 | ||||
-rw-r--r-- | src/skhd.c | 19 | ||||
-rw-r--r-- | src/tokenize.c | 27 | ||||
-rw-r--r-- | src/tokenize.h | 6 |
9 files changed, 382 insertions, 68 deletions
@@ -2,10 +2,6 @@ **skhd** is a simple hotkey daemon for macOS. It is a stripped version of [**khd**](https://github.com/koekeishiya/khd) (although rewritten from scratch), that sacrifices the more advanced features in favour of increased responsiveness and performance. - -**skhd** uses a subset of the grammar that **khd** uses. In other words, **khd** is able to load and run **skhd** config files without having -to make any changes to the file. This makes it very simple to migrate between **skhd** and **khd** on demand. - **skhd** is able to hotload its config file, meaning that hotkeys can be edited and updated live while **skhd** is running. **skhd** has an improved parser that is able to accurately identify both the line and character position of any symbol that does not @@ -17,11 +13,10 @@ feature comparison between **skhd** and **khd** | feature | skhd | khd | |:--------------------------:|:----:|:---:| | hotload config file | [x] | [ ] | -| store hotkey in hashmap | [x] | [ ] | -| store hotkey in linked-list| [ ] | [x] | +| store hotkeys in hashmap | [x] | [ ] | | require unix domain socket | [ ] | [x] | | hotkey passthrough | [x] | [x] | -| modal hotkey-system | [ ] | [x] | +| modal hotkey-system | [x] | [x] | | application specific hotkey| [ ] | [x] | | modifier only hotkey | [ ] | [x] | | caps-lock as hotkey | [ ] | [x] | @@ -76,12 +71,16 @@ A list of all built-in modifier and literal keywords can be found [here](https:/ A hotkey is written according to the following rules: ``` -hotkey = <keysym> ':' <command> | - <keysym> '->' ':' <command> +hotkey = <mode> '<' <action> | <action> + +mode = 'name of mode' | <mode> ',' <mode> + +action = <keysym> ':' <command> | <keysym> '->' ':' <command> + <keysym> ';' <mode> | <keysym> '->' ';' <mode> keysym = <mod> '-' <key> | <key> -mod = 'built-in mod keyword' | <mod> '+' <mod> +mod = 'modifier keyword' | <mod> '+' <mod> key = <literal> | <keycode> @@ -102,3 +101,28 @@ command = command is executed through '$SHELL -c' and an EOL character signifies the end of the bind. ``` + +A mode can be defined in two different ways: +``` +# define mode 'switcher' with an on_enter command +:: switcher : chunkc border::color 0xff24ccaa + +# define modes without an on_enter command +:: swap +:: focus +``` + +Modal sample: +``` +# defines a new mode named 'test' +:: test + +# from 'default' mode, activate mode 'test' +cmd - x ; test + +# from 'test' mode, activate mode 'default' +test < cmd - x ; default + +# launch a new terminal instance when in either 'default' or 'test' mode +default, test < cmd - return : open -na /Applications/Terminal.app +``` diff --git a/examples/skhdrc b/examples/skhdrc index 59f3c2f..f472e3e 100644 --- a/examples/skhdrc +++ b/examples/skhdrc @@ -3,8 +3,12 @@ # # A hotkey is written according to the following rules: # -# hotkey = <keysym> ':' <command> | -# <keysym> '->' ':' <command> +# hotkey = <mode> '<' <action> | <action> +# +# mode = 'name of mode' | <mode> ',' <mode> +# +# action = <keysym> ':' <command> | <keysym> '->' ':' <command> +# <keysym> ';' <mode> | <keysym> '->' ';' <mode> # # keysym = <mod> '-' <key> | <key> # @@ -28,6 +32,24 @@ # prepend '\' at the end of the previous line. # # an EOL character signifies the end of the bind. +# +# +# NOTE(koekeishiya): Modal operations +# +# defines a new mode 'switcher' with an on_enter command +# :: switcher : chunkc border::color 0xff24ccaa +# +# defines a new mode named 'test' +# :: test +# +# from 'default' mode, activate mode 'test' +# cmd - x ; test +# +# from 'test' mode, activate mode 'default' +# test < cmd - x ; default +# +# launch a new terminal instance when in either 'default' or 'test' mode +# default, test < cmd - return : open -na /Applications/Terminal.app # open terminal, blazingly fast compared to iTerm/Hyper @@ -80,6 +102,17 @@ shift + alt - 4 : chunkc tiling::window --send-to-desktop 4 shift + alt - 5 : chunkc tiling::window --send-to-desktop 5 shift + alt - 6 : chunkc tiling::window --send-to-desktop 6 +# send window to desktop and follow focus +shift + cmd - x : chunkc tiling::window --send-to-desktop $(chunkc get _last_active_desktop) +shift + cmd - z : chunkc tiling::window --send-to-desktop prev; qes -k "cmd + alt - z" +shift + cmd - c : chunkc tiling::window --send-to-desktop next; qes -k "cmd + alt - c" +shift + cmd - 1 : chunkc tiling::window --send-to-desktop 1; qes -k "cmd + alt - 1" +shift + cmd - 2 : chunkc tiling::window --send-to-desktop 2; qes -k "cmd + alt - 2" +shift + cmd - 3 : chunkc tiling::window --send-to-desktop 3; qes -k "cmd + alt - 3" +shift + cmd - 4 : chunkc tiling::window --send-to-desktop 4; qes -k "cmd + alt - 4" +shift + cmd - 5 : chunkc tiling::window --send-to-desktop 5; qes -k "cmd + alt - 5" +shift + cmd - 6 : chunkc tiling::window --send-to-desktop 6; qes -k "cmd + alt - 6" + # focus monitor ctrl + alt - z : chunkc tiling::monitor -f prev ctrl + alt - c : chunkc tiling::monitor -f next @@ -157,3 +190,7 @@ ctrl + alt - s : chunkc tiling::desktop --layout monocle ctrl + alt - d : chunkc tiling::desktop --layout float ctrl + alt - w : chunkc tiling::desktop --deserialize ~/.chunkwm_layouts/dev_1 + +cmd - 7 : qes -k "shift + alt - 7" +cmd - 8 : qes -k "shift + alt - 8" +cmd - 9 : qes -k "shift + alt - 9" diff --git a/src/hotkey.c b/src/hotkey.c index d9ebde6..af1e410 100644 --- a/src/hotkey.c +++ b/src/hotkey.c @@ -61,6 +61,29 @@ unsigned long hash_hotkey(struct hotkey *a) return a->key; } +bool same_mode(char *a, char *b) +{ + while (*a && *b && *a == *b) { + ++a; + ++b; + } + return *a == '\0' && *b == '\0'; +} + +unsigned long hash_mode(char *key) +{ + unsigned long hash = 0, high; + while(*key) { + hash = (hash << 4) + *key++; + high = hash & 0xF0000000; + if(high) { + hash ^= (high >> 24); + } + hash &= ~high; + } + return hash; +} + internal bool fork_and_exec(char *command) { @@ -81,26 +104,40 @@ fork_and_exec(char *command) return true; } -bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *hotkey_map) +bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *mode_map, struct mode **current_mode) { bool result = false; struct hotkey *hotkey; - if ((hotkey = table_find(hotkey_map, eventkey))) { - if (fork_and_exec(hotkey->command)) { + if ((hotkey = table_find(&(*current_mode)->hotkey_map, eventkey))) { + if (has_flags(hotkey, Hotkey_Flag_Activate)) { + *current_mode = table_find(mode_map, hotkey->command); + if ((*current_mode)->command) { + fork_and_exec((*current_mode)->command); + } + result = has_flags(hotkey, Hotkey_Flag_Passthrough) ? false : true; + } else if (fork_and_exec(hotkey->command)) { result = has_flags(hotkey, Hotkey_Flag_Passthrough) ? false : true; } } return result; } -void free_hotkeys(struct table *hotkey_map) +internal void +free_hotkeys(struct table *hotkey_map, struct table *freed_pointers) { int count; void **hotkeys = table_reset(hotkey_map, &count); + for (int index = 0; index < count; ++index) { struct hotkey *hotkey = (struct hotkey *) hotkeys[index]; - free(hotkey->command); - free(hotkey); + if (table_find(freed_pointers, hotkey)) { + // we have already freed this pointer, do nothing! + // printf("info: %p has already been freed!\n", hotkey); + } else { + table_add(freed_pointers, hotkey, hotkey); + free(hotkey->command); + free(hotkey); + } } if (count) { @@ -108,6 +145,35 @@ void free_hotkeys(struct table *hotkey_map) } } +unsigned long hash_pointer(struct hotkey *a) { return (unsigned long)a; } +bool same_pointer(struct hotkey *a, struct hotkey *b) { return a == b; } + +void free_mode_map(struct table *mode_map) +{ + int count; + struct table freed_pointers; + void **modes = table_reset(mode_map, &count); + + if (count) { + table_init(&freed_pointers, 13, + (table_hash_func) hash_pointer, + (table_compare_func) same_pointer); + } + + for (int index = 0; index < count; ++index) { + struct mode *mode = (struct mode *) modes[index]; + if (mode->command) free(mode->command); + if (mode->name) free(mode->name); + free_hotkeys(&mode->hotkey_map, &freed_pointers); + free(mode); + } + + if (count) { + free(modes); + table_free(&freed_pointers); + } +} + internal void cgevent_lrmod_flag_to_hotkey_lrmod_flag(CGEventFlags flags, struct hotkey *eventkey, int mod) { diff --git a/src/hotkey.h b/src/hotkey.h index 40ea630..530520b 100644 --- a/src/hotkey.h +++ b/src/hotkey.h @@ -24,31 +24,45 @@ enum osx_event_mask enum hotkey_flag { - Hotkey_Flag_Alt = (1 << 0), - Hotkey_Flag_LAlt = (1 << 1), - Hotkey_Flag_RAlt = (1 << 2), - Hotkey_Flag_Shift = (1 << 3), - Hotkey_Flag_LShift = (1 << 4), - Hotkey_Flag_RShift = (1 << 5), - Hotkey_Flag_Cmd = (1 << 6), - Hotkey_Flag_LCmd = (1 << 7), - Hotkey_Flag_RCmd = (1 << 8), - Hotkey_Flag_Control = (1 << 9), + Hotkey_Flag_Alt = (1 << 0), + Hotkey_Flag_LAlt = (1 << 1), + Hotkey_Flag_RAlt = (1 << 2), + Hotkey_Flag_Shift = (1 << 3), + Hotkey_Flag_LShift = (1 << 4), + Hotkey_Flag_RShift = (1 << 5), + Hotkey_Flag_Cmd = (1 << 6), + Hotkey_Flag_LCmd = (1 << 7), + Hotkey_Flag_RCmd = (1 << 8), + Hotkey_Flag_Control = (1 << 9), Hotkey_Flag_LControl = (1 << 10), Hotkey_Flag_RControl = (1 << 11), Hotkey_Flag_Fn = (1 << 12), Hotkey_Flag_Passthrough = (1 << 13), + Hotkey_Flag_Activate = (1 << 14), Hotkey_Flag_Hyper = (Hotkey_Flag_Cmd | Hotkey_Flag_Alt | Hotkey_Flag_Shift | Hotkey_Flag_Control) }; +#include "hashtable.h" + +struct mode +{ + int line; + int cursor; + char *name; + char *command; + struct table hotkey_map; +}; + struct hotkey { uint32_t flags; uint32_t key; char *command; + int mode_count; + struct mode *mode_list[16]; }; static inline void @@ -70,13 +84,15 @@ clear_flags(struct hotkey *hotkey, uint32_t flag) hotkey->flags &= ~flag; } +bool same_mode(char *a, char *b); +unsigned long hash_mode(char *key); + bool same_hotkey(struct hotkey *a, struct hotkey *b); unsigned long hash_hotkey(struct hotkey *a); -bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *hotkey_map); +bool find_and_exec_hotkey(struct hotkey *eventkey, struct table *mode_map, struct mode **current_mode); void cgeventflags_to_hotkeyflags(CGEventFlags flags, struct hotkey *eventkey); -struct table; -void free_hotkeys(struct table *hotkey_map); +void free_mode_map(struct table *mode_map); #endif diff --git a/src/parse.c b/src/parse.c index 14b1d14..3e60dd1 100644 --- a/src/parse.c +++ b/src/parse.c @@ -12,6 +12,30 @@ #define internal static +internal struct mode * +find_or_init_default_mode(struct parser *parser) +{ + struct mode *default_mode; + + if ((default_mode = table_find(parser->mode_map, "default"))) { + return default_mode; + } + + default_mode = malloc(sizeof(struct mode)); + default_mode->line = -1; + default_mode->cursor = -1; + default_mode->name = copy_string("default"); + + table_init(&default_mode->hotkey_map, 131, + (table_hash_func) hash_hotkey, + (table_compare_func) same_hotkey); + + default_mode->command = NULL; + table_add(parser->mode_map, default_mode->name, default_mode); + + return default_mode; +} + internal char * read_file(const char *file) { @@ -122,8 +146,8 @@ internal enum hotkey_flag modifier_flags_value[] = internal uint32_t parse_modifier(struct parser *parser) { - uint32_t flags = 0; struct token modifier = parser_previous(parser); + uint32_t flags = 0; for (int i = 0; i < array_count(modifier_flags_str); ++i) { if (token_equals(modifier, modifier_flags_str[i])) { @@ -137,34 +161,76 @@ parse_modifier(struct parser *parser) if (parser_match(parser, Token_Modifier)) { flags |= parse_modifier(parser); } else { - parser_report_error(parser, "expected modifier"); + parser_report_error(parser, Error_Unexpected_Token, "expected modifier"); } } return flags; } +internal void +parse_mode(struct parser *parser, struct hotkey *hotkey) +{ + struct token identifier = parser_previous(parser); + + char *name = copy_string_count(identifier.text, identifier.length); + struct mode *mode = table_find(parser->mode_map, name); + free(name); + + if (!mode && token_equals(identifier, "default")) { + mode = find_or_init_default_mode(parser); + } else if (!mode) { + parser_report_error(parser, Error_Undeclared_Ident, "undeclared identifier"); + return; + } + + hotkey->mode_list[hotkey->mode_count++] = mode; + printf("\tmode: '%s'\n", mode->name); + + if (parser_match(parser, Token_Comma)) { + if (parser_match(parser, Token_Identifier)) { + parse_mode(parser, hotkey); + } else { + parser_report_error(parser, Error_Unexpected_Token, "expected identifier"); + } + } +} + internal struct hotkey * parse_hotkey(struct parser *parser) { struct hotkey *hotkey = malloc(sizeof(struct hotkey)); - int found_modifier; + memset(hotkey, 0, sizeof(struct hotkey)); + bool found_modifier; printf("hotkey :: #%d {\n", parser->current_token.line); - if (parser_match(parser, Token_Modifier)) { - hotkey->flags = parse_modifier(parser); + if (parser_match(parser, Token_Identifier)) { + parse_mode(parser, hotkey); if (parser->error) { goto err; } - found_modifier = 1; + } + + if (hotkey->mode_count > 0) { + if (!parser_match(parser, Token_Insert)) { + parser_report_error(parser, Error_Unexpected_Token, "expected '<'"); + goto err; + } } else { - hotkey->flags = found_modifier = 0; + hotkey->mode_list[hotkey->mode_count++] = find_or_init_default_mode(parser); + } + + if ((found_modifier = parser_match(parser, Token_Modifier))) { + hotkey->flags = parse_modifier(parser); + if (parser->error) { + goto err; + } } if (found_modifier) { if (!parser_match(parser, Token_Dash)) { - parser_report_error(parser, "expected '-'"); + parser_report_error(parser, Error_Unexpected_Token, "expected '-'"); goto err; } } @@ -176,7 +242,7 @@ parse_hotkey(struct parser *parser) } else if (parser_match(parser, Token_Literal)) { parse_key_literal(parser, hotkey); } else { - parser_report_error(parser, "expected key-literal"); + parser_report_error(parser, Error_Unexpected_Token, "expected key-literal"); goto err; } @@ -186,13 +252,19 @@ parse_hotkey(struct parser *parser) if (parser_match(parser, Token_Command)) { hotkey->command = parse_command(parser); + } else if (parser_match(parser, Token_Activate)) { + hotkey->flags |= Hotkey_Flag_Activate; + hotkey->command = parse_command(parser); + if (!table_find(parser->mode_map, hotkey->command)) { + parser_report_error(parser, Error_Undeclared_Ident, "undeclared identifier"); + goto err; + } } else { - parser_report_error(parser, "expected ':' followed by command"); + parser_report_error(parser, Error_Unexpected_Token, "expected ':' followed by command or ';' followed by mode"); goto err; } printf("}\n"); - return hotkey; err: @@ -200,23 +272,73 @@ err: return NULL; } -void parse_config(struct parser *parser, struct table *hotkey_map) +internal struct mode * +parse_mode_decl(struct parser *parser) +{ + struct mode *mode = malloc(sizeof(struct mode)); + struct token identifier = parser_previous(parser); + + mode->line = identifier.line; + mode->cursor = identifier.cursor; + mode->name = copy_string_count(identifier.text, identifier.length); + + table_init(&mode->hotkey_map, 131, + (table_hash_func) hash_hotkey, + (table_compare_func) same_hotkey); + + if (parser_match(parser, Token_Command)) { + mode->command = copy_string_count(parser->previous_token.text, parser->previous_token.length); + } else { + mode->command = NULL; + } + + return mode; +} + +void parse_declaration(struct parser *parser) { + struct mode *mode; + parser_match(parser, Token_Decl); + if (parser_match(parser, Token_Identifier)) { + mode = parse_mode_decl(parser); + if (table_find(parser->mode_map, mode->name)) { + parser_report_error(parser, Error_Duplicate_Ident, + "#%d:%d duplicate declaration '%s'\n", + mode->line, mode->cursor, mode->name); + } else { + table_add(parser->mode_map, mode->name, mode); + } + } else { + parser_report_error(parser, Error_Unexpected_Token, "expected identifier"); + } +} + +void parse_config(struct parser *parser) +{ + struct mode *mode; struct hotkey *hotkey; + while (!parser_eof(parser)) { - if ((parser_check(parser, Token_Modifier)) || + if (parser->error) { + free_mode_map(parser->mode_map); + return; + } + + if ((parser_check(parser, Token_Identifier)) || + (parser_check(parser, Token_Modifier)) || (parser_check(parser, Token_Literal)) || (parser_check(parser, Token_Key_Hex)) || (parser_check(parser, Token_Key))) { if ((hotkey = parse_hotkey(parser))) { - table_add(hotkey_map, hotkey, hotkey); - } else if (parser->error) { - free_hotkeys(hotkey_map); - return; + for (int i = 0; i < hotkey->mode_count; ++i) { + mode = hotkey->mode_list[i]; + table_add(&mode->hotkey_map, hotkey, hotkey); + } } + } else if (parser_check(parser, Token_Decl)) { + parse_declaration(parser); } else { - parser_report_error(parser, "expected modifier or key-literal"); - return; + parser_report_error(parser, Error_Unexpected_Token, "expected decl, modifier or key-literal"); } } } @@ -265,13 +387,23 @@ bool parser_match(struct parser *parser, enum token_type type) return false; } -void parser_report_error(struct parser *parser, const char *format, ...) +void parser_report_error(struct parser *parser, enum parse_error_type error_type, const char *format, ...) { va_list args; va_start(args, format); - fprintf(stderr, "#%d:%d error: ", parser->current_token.line, parser->current_token.cursor); - vfprintf(stderr, format, args); - fprintf(stderr, ", but got '%.*s'\n", parser->current_token.length, parser->current_token.text); + + if (error_type == Error_Unexpected_Token) { + fprintf(stderr, "#%d:%d ", parser->current_token.line, parser->current_token.cursor); + vfprintf(stderr, format, args); + fprintf(stderr, ", but got '%.*s'\n", parser->current_token.length, parser->current_token.text); + } else if (error_type == Error_Undeclared_Ident) { + fprintf(stderr, "#%d:%d ", parser->previous_token.line, parser->previous_token.cursor); + vfprintf(stderr, format, args); + fprintf(stderr, " '%.*s'\n", parser->previous_token.length, parser->previous_token.text); + } else if (error_type == Error_Duplicate_Ident) { + vfprintf(stderr, format, args); + } + va_end(args); parser->error = true; } diff --git a/src/parse.h b/src/parse.h index 241e7ff..1c7719d 100644 --- a/src/parse.h +++ b/src/parse.h @@ -4,16 +4,25 @@ #include "tokenize.h" #include <stdbool.h> +struct table; struct parser { struct token previous_token; struct token current_token; struct tokenizer tokenizer; + struct table *mode_map; bool error; }; -struct table; -void parse_config(struct parser *parser, struct table *hotkey_map); +enum parse_error_type +{ + Error_Unexpected_Token, + Error_Undeclared_Ident, + Error_Duplicate_Ident, +}; + + +void parse_config(struct parser *parser); struct token parser_peek(struct parser *parser); struct token parser_previous(struct parser *parser); @@ -23,6 +32,6 @@ bool parser_check(struct parser *parser, enum token_type type); bool parser_match(struct parser *parser, enum token_type type); bool parser_init(struct parser *parser, char *file); void parser_destroy(struct parser *parser); -void parser_report_error(struct parser *parser, const char *format, ...); +void parser_report_error(struct parser *parser, enum parse_error_type error_type, const char *format, ...); #endif @@ -43,6 +43,8 @@ internal unsigned major_version = 0; internal unsigned minor_version = 0; internal unsigned patch_version = 14; internal struct table hotkey_map; +internal struct table mode_map; +internal struct mode *current_mode; internal char *config_file; internal void @@ -69,16 +71,19 @@ parse_config_helper(char *absolutepath) { struct parser parser; if (parser_init(&parser, absolutepath)) { - parse_config(&parser, &hotkey_map); + parser.mode_map = &mode_map; + parse_config(&parser); parser_destroy(&parser); + current_mode = table_find(&mode_map, "default"); } else { + current_mode = NULL; warn("skhd: could not open file '%s'\n", absolutepath); } } internal HOTLOADER_CALLBACK(config_handler) { - free_hotkeys(&hotkey_map); + free_mode_map(&mode_map); parse_config_helper(absolutepath); } @@ -92,11 +97,12 @@ internal EVENT_TAP_CALLBACK(key_handler) CGEventTapEnable(event_tap->handle, 1); } break; case kCGEventKeyDown: { + if (!current_mode) return event; uint32_t flags = CGEventGetFlags(event); uint32_t key = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode); struct hotkey eventkey = { .flags = 0, .key = key }; cgeventflags_to_hotkeyflags(flags, &eventkey); - bool result = find_and_exec_hotkey(&eventkey, &hotkey_map); + bool result = find_and_exec_hotkey(&eventkey, &mode_map, ¤t_mode); if (result) { return NULL; } @@ -190,10 +196,9 @@ int main(int argc, char **argv) set_config_path(); } - table_init(&hotkey_map, - 131, - (table_hash_func) hash_hotkey, - (table_compare_func) same_hotkey); + table_init(&mode_map, 13, + (table_hash_func) hash_mode, + (table_compare_func) same_mode); printf("skhd: using config '%s'\n", config_file); parse_config_helper(config_file); diff --git a/src/tokenize.c b/src/tokenize.c index 691b9c8..82cd466 100644 --- a/src/tokenize.c +++ b/src/tokenize.c @@ -94,7 +94,7 @@ resolve_identifier_type(struct token token) } } - return Token_Unknown; + return Token_Identifier; } struct token @@ -121,6 +121,8 @@ get_token(struct tokenizer *tokenizer) switch (c) { case '\0':{ token.type = Token_EndOfStream; } break; case '+': { token.type = Token_Plus; } break; + case ',': { token.type = Token_Comma; } break; + case '<': { token.type = Token_Insert; } break; case '#': { eat_comment(tokenizer); token = get_token(tokenizer); @@ -134,16 +136,33 @@ get_token(struct tokenizer *tokenizer) token.type = Token_Dash; } } break; - case ':': { + case ';': { eat_whitespace(tokenizer); token.text = tokenizer->at; token.line = tokenizer->line; token.cursor = tokenizer->cursor; - eat_command(tokenizer); + eat_identifier(tokenizer); token.length = tokenizer->at - token.text; - token.type = Token_Command; + token.type = Token_Activate; + } break; + case ':': { + if (*tokenizer->at && *tokenizer->at == ':') { + advance(tokenizer); + token.length = tokenizer->at - token.text; + token.type = Token_Decl; + } else { + eat_whitespace(tokenizer); + + token.text = tokenizer->at; + token.line = tokenizer->line; + token.cursor = tokenizer->cursor; + + eat_command(tokenizer); + token.length = tokenizer->at - token.text; + token.type = Token_Command; + } } break; default: { if (c == '0' && *tokenizer->at == 'x') { diff --git a/src/tokenize.h b/src/tokenize.h index 36b4350..114ed0d 100644 --- a/src/tokenize.h +++ b/src/tokenize.h @@ -28,12 +28,18 @@ static const char *literal_keycode_str[] = enum token_type { + Token_Identifier, + Token_Activate, + Token_Command, Token_Modifier, Token_Literal, Token_Key_Hex, Token_Key, + Token_Decl, + Token_Comma, + Token_Insert, Token_Plus, Token_Dash, Token_Arrow, |