diff options
Diffstat (limited to 'users/xtonhasvim')
-rw-r--r-- | users/xtonhasvim/readme.md | 10 | ||||
-rw-r--r-- | users/xtonhasvim/rules.mk | 1 | ||||
-rw-r--r-- | users/xtonhasvim/xtonhasvim.c | 599 | ||||
-rw-r--r-- | users/xtonhasvim/xtonhasvim.h | 66 |
4 files changed, 676 insertions, 0 deletions
diff --git a/users/xtonhasvim/readme.md b/users/xtonhasvim/readme.md new file mode 100644 index 0000000000..e08e972f4d --- /dev/null +++ b/users/xtonhasvim/readme.md @@ -0,0 +1,10 @@ + +# Xton has Vim! + +Contains common code for Xton's vim emulation (vimulation?) layer. + +Inspired/stolen from the `ergodox_ez/vim` keymap. Rewritten to be a more straightforward state machine and support more macros. Vim layers `_CMD` and `_EDIT` are designed to lay on top of an otherwise fully-functional layout. `_CMD` runs the entire vim state machine while `_EDIT` should lay across your base layer and mask off just the escape key. + +Works via OSX text editing shortcuts, mainly MOD+arrow combinations. This has some limitations and only works on OSX. + +The `_CMD` layer will temporarily disable itself while *CMD* or *ALT* are held down so that typical OSX shortcuts can be used without switching out of vim mode. diff --git a/users/xtonhasvim/rules.mk b/users/xtonhasvim/rules.mk new file mode 100644 index 0000000000..3777917f8f --- /dev/null +++ b/users/xtonhasvim/rules.mk @@ -0,0 +1 @@ +SRC += xtonhasvim.c diff --git a/users/xtonhasvim/xtonhasvim.c b/users/xtonhasvim/xtonhasvim.c new file mode 100644 index 0000000000..85048401d8 --- /dev/null +++ b/users/xtonhasvim/xtonhasvim.c @@ -0,0 +1,599 @@ + /* Copyright 2015-2017 Christon DeWan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "xtonhasvim.h" + +/************************************ + * helper foo + ************************************/ + +#define PRESS(kc) register_code(kc) +#define RELEASE(kc) unregister_code(kc) + +static void TAP(uint16_t keycode) { + PRESS(keycode); + RELEASE(keycode); +} + +static void CMD(uint16_t keycode) { + PRESS(KC_LGUI); + TAP(keycode); + RELEASE(KC_LGUI); +} + +static void CTRL(uint16_t keycode) { + PRESS(KC_LCTRL); + TAP(keycode); + RELEASE(KC_LCTRL); +} + +static void SHIFT(uint16_t keycode) { + PRESS(KC_LSHIFT); + TAP(keycode); + RELEASE(KC_LSHIFT); +} + +static void ALT(uint16_t keycode) { + PRESS(KC_LALT); + TAP(keycode); + RELEASE(KC_LALT); +} + + +uint16_t vstate = VIM_START; +bool yank_was_lines = false; +bool SHIFTED = false; +uint32_t mod_override_layer_state = 0; +uint16_t mod_override_triggering_key = 0; +bool do_check_kb_clear = false; + +void vim_reset(void) { + vstate = VIM_START; + SHIFTED = false; + yank_was_lines = false; +} + +void edit(void) { vstate = VIM_START; layer_on(_EDIT); layer_off(_CMD); } +#define EDIT edit() + + +void simple_movement(uint16_t keycode) { + switch(keycode) { + case VIM_B: + PRESS(KC_LALT); + SHIFT(KC_LEFT); // select to start of this word + RELEASE(KC_LALT); + break; + case VIM_E: + PRESS(KC_LALT); + SHIFT(KC_RIGHT); // select to end of this word + RELEASE(KC_LALT); + break; + case VIM_H: + SHIFT(KC_LEFT); + break; + case VIM_J: + CMD(KC_LEFT); + SHIFT(KC_DOWN); + SHIFT(KC_DOWN); + break; + case VIM_K: + CMD(KC_LEFT); + TAP(KC_DOWN); + SHIFT(KC_UP); + SHIFT(KC_UP); + break; + case VIM_L: + SHIFT(KC_RIGHT); + break; + case VIM_W: + PRESS(KC_LALT); + SHIFT(KC_RIGHT); // select to end of this word + SHIFT(KC_RIGHT); // select to end of next word + SHIFT(KC_LEFT); // select to start of next word + RELEASE(KC_LALT); + break; + } +} + +bool process_record_xtonhasvim(uint16_t keycode, keyrecord_t *record) { + if(record->event.pressed && layer_state_is(_CMD) && IS_MOD(keycode)) { + mod_override_layer_state = layer_state; + mod_override_triggering_key = keycode; + layer_clear(); + return true; // let the event fall through... + } + if(mod_override_layer_state && !record->event.pressed && keycode == mod_override_triggering_key) { + layer_state_set(mod_override_layer_state); + mod_override_layer_state = 0; + mod_override_triggering_key = 0; + return true; + } + + if (VIM_START <= keycode && keycode <= VIM_ESC) { + if(keycode == VIM_SHIFT) { + SHIFTED = record->event.pressed; + return false; + } + + if (record->event.pressed) { + if(keycode == VIM_START) { + // entry from anywhere + layer_on(_CMD); + vstate = VIM_START; + return false; + } + switch(vstate) { + case VIM_START: + switch(keycode){ + /***************************** + * ground state + *****************************/ + case VIM_A: + if(SHIFTED) { + // CMD(KC_RIGHT); + CTRL(KC_E); + } else { + TAP(KC_RIGHT); + } + EDIT; + break; + case VIM_B: + PRESS(KC_LALT); + PRESS(KC_LEFT); + break; + case VIM_C: + if(SHIFTED) { + PRESS(KC_LSHIFT); + CMD(KC_RIGHT); + RELEASE(KC_LSHIFT); + CMD(KC_X); + yank_was_lines = false; + EDIT; + } else { + vstate = VIM_C; + } + break; + case VIM_D: + if(SHIFTED) { + TAP(KC_K); + } else { + vstate = VIM_D; + } + break; + case VIM_E: + PRESS(KC_LALT); + PRESS(KC_RIGHT); + break; + case VIM_G: + if(SHIFTED) { + TAP(KC_END); + } else { + vstate = VIM_G; + } + break; + case VIM_H: + PRESS(KC_LEFT); + break; + case VIM_I: + if(SHIFTED){ + CTRL(KC_A); + } + EDIT; + break; + case VIM_J: + if(SHIFTED) { + CMD(KC_RIGHT); + TAP(KC_DEL); + } else { + PRESS(KC_DOWN); + } + break; + case VIM_K: + PRESS(KC_UP); + break; + case VIM_L: + PRESS(KC_RIGHT); + break; + case VIM_O: + if(SHIFTED) { + CMD(KC_LEFT); + TAP(KC_ENTER); + TAP(KC_UP); + EDIT; + } else { + CMD(KC_RIGHT); + TAP(KC_ENTER); + EDIT; + } + break; + case VIM_P: + if(SHIFTED) { + CMD(KC_LEFT); + CMD(KC_V); + } else { + if(yank_was_lines) { + CMD(KC_RIGHT); + TAP(KC_RIGHT); + CMD(KC_V); + } else { + CMD(KC_V); + } + } + break; + case VIM_S: + // s for substitute? + if(SHIFTED) { + CMD(KC_LEFT); + PRESS(KC_LSHIFT); + CMD(KC_RIGHT); + RELEASE(KC_LSHIFT); + CMD(KC_X); + yank_was_lines = false; + EDIT; + } else { + SHIFT(KC_RIGHT); + CMD(KC_X); + yank_was_lines = false; + EDIT; + } + break; + case VIM_U: + if(SHIFTED) { + PRESS(KC_LSFT); + CMD(KC_Z); + RELEASE(KC_LSHIFT); + } else { + CMD(KC_Z); + } + break; + case VIM_V: + if(SHIFTED) { + CMD(KC_LEFT); + SHIFT(KC_DOWN); + vstate = VIM_VS; + } else { + vstate = VIM_V; + } + break; + case VIM_W: + PRESS(KC_LALT); + TAP(KC_RIGHT); + TAP(KC_RIGHT); + TAP(KC_LEFT); + RELEASE(KC_LALT); + break; + case VIM_X: + // SHIFT(KC_RIGHT); + // CMD(KC_X); + PRESS(KC_DEL); + break; + case VIM_Y: + if(SHIFTED) { + CMD(KC_LEFT); + SHIFT(KC_DOWN); + CMD(KC_C); + TAP(KC_RIGHT); + yank_was_lines = true; + } else { + vstate = VIM_Y; + } + break; + case VIM_COMMA: + if(SHIFTED) { + // indent + CMD(KC_LBRACKET); + } else { + // toggle comment + CMD(KC_SLASH); + } + break; + case VIM_PERIOD: + if(SHIFTED) { + // outdent + CMD(KC_RBRACKET); + } + break; + } + break; + case VIM_C: + /***************************** + * c- ...for change. I never use this... + *****************************/ + switch(keycode) { + case VIM_B: + case VIM_E: + case VIM_H: + case VIM_J: + case VIM_K: + case VIM_L: + case VIM_W: + simple_movement(keycode); + CMD(KC_X); + yank_was_lines = false; + EDIT; + break; + + case VIM_C: + CMD(KC_LEFT); + PRESS(KC_LSHIFT); + CMD(KC_RIGHT); + RELEASE(KC_LSHIFT); + CMD(KC_X); + yank_was_lines = false; + EDIT; + break; + case VIM_I: + vstate = VIM_CI; + break; + default: + vstate = VIM_START; + break; + } + break; + case VIM_CI: + /***************************** + * ci- ...change inner word + *****************************/ + switch(keycode) { + case VIM_W: + ALT(KC_LEFT); + PRESS(KC_LSHIFT); + ALT(KC_RIGHT); + RELEASE(KC_LSHIFT); + CMD(KC_X); + yank_was_lines = false; + EDIT; + default: + vstate = VIM_START; + break; + } + break; + case VIM_D: + /***************************** + * d- ...delete stuff + *****************************/ + switch(keycode) { + case VIM_B: + case VIM_E: + case VIM_H: + case VIM_J: + case VIM_K: + case VIM_L: + case VIM_W: + simple_movement(keycode); + CMD(KC_X); + yank_was_lines = false; + break; + case VIM_D: + CMD(KC_LEFT); + SHIFT(KC_DOWN); + CMD(KC_X); + yank_was_lines = true; + vstate = VIM_START; + break; + case VIM_I: + vstate = VIM_DI; + break; + default: + vstate = VIM_START; + break; + } + break; + case VIM_DI: + /***************************** + * ci- ...delete a word... FROM THE INSIDE! + *****************************/ + switch(keycode) { + case VIM_W: + ALT(KC_LEFT); + PRESS(KC_LSHIFT); + ALT(KC_RIGHT); + RELEASE(KC_LSHIFT); + CMD(KC_X); + yank_was_lines = false; + vstate = VIM_START; + default: + vstate = VIM_START; + break; + } + break; + case VIM_V: + /***************************** + * visual! + *****************************/ + switch(keycode) { + case VIM_D: + case VIM_X: + CMD(KC_X); + yank_was_lines = false; + vstate = VIM_START; + break; + case VIM_B: + PRESS(KC_LALT); + PRESS(KC_LSHIFT); + PRESS(KC_LEFT); + // leave open for key repeat + break; + case VIM_E: + PRESS(KC_LALT); + PRESS(KC_LSHIFT); + PRESS(KC_RIGHT); + // leave open for key repeat + break; + case VIM_H: + PRESS(KC_LSHIFT); + PRESS(KC_LEFT); + break; + case VIM_I: + vstate = VIM_VI; + break; + case VIM_J: + PRESS(KC_LSHIFT); + PRESS(KC_DOWN); + break; + case VIM_K: + PRESS(KC_LSHIFT); + PRESS(KC_UP); + break; + case VIM_L: + PRESS(KC_LSHIFT); + PRESS(KC_RIGHT); + break; + case VIM_W: + PRESS(KC_LALT); + SHIFT(KC_RIGHT); // select to end of this word + SHIFT(KC_RIGHT); // select to end of next word + SHIFT(KC_LEFT); // select to start of next word + RELEASE(KC_LALT); + break; + case VIM_Y: + CMD(KC_C); + TAP(KC_RIGHT); + yank_was_lines = false; + vstate = VIM_START; + break; + case VIM_V: + case VIM_ESC: + TAP(KC_RIGHT); + vstate = VIM_START; + break; + default: + // do nothing + break; + } + break; + case VIM_VI: + /***************************** + * vi- ...select a word... FROM THE INSIDE! + *****************************/ + switch(keycode) { + case VIM_W: + ALT(KC_LEFT); + PRESS(KC_LSHIFT); + ALT(KC_RIGHT); + RELEASE(KC_LSHIFT); + vstate = VIM_V; + default: + // ignore + vstate = VIM_V; + break; + } + break; + case VIM_VS: + /***************************** + * visual line + *****************************/ + switch(keycode) { + case VIM_D: + case VIM_X: + CMD(KC_X); + yank_was_lines = true; + vstate = VIM_START; + break; + case VIM_J: + PRESS(KC_LSHIFT); + PRESS(KC_DOWN); + break; + case VIM_K: + PRESS(KC_LSHIFT); + PRESS(KC_UP); + break; + case VIM_Y: + CMD(KC_C); + yank_was_lines = true; + TAP(KC_RIGHT); + vstate = VIM_START; + break; + case VIM_V: + case VIM_ESC: + TAP(KC_RIGHT); + vstate = VIM_START; + break; + default: + // do nothing + break; + } + break; + case VIM_G: + /***************************** + * gg, and a grab-bag of other macros i find useful + *****************************/ + switch(keycode) { + case VIM_G: + TAP(KC_HOME); + break; + // codes b + case VIM_H: + CTRL(KC_A); + break; + case VIM_J: + PRESS(KC_PGDN); + break; + case VIM_K: + PRESS(KC_PGUP); + break; + case VIM_L: + CTRL(KC_E); + break; + default: + // do nothing + break; + } + vstate = VIM_START; + break; + case VIM_Y: + /***************************** + * yoink! + *****************************/ + switch(keycode) { + case VIM_B: + case VIM_E: + case VIM_H: + case VIM_J: + case VIM_K: + case VIM_L: + case VIM_W: + simple_movement(keycode); + CMD(KC_C); + TAP(KC_RIGHT); + yank_was_lines = false; + break; + case VIM_Y: + CMD(KC_LEFT); + SHIFT(KC_DOWN); + CMD(KC_C); + TAP(KC_RIGHT); + yank_was_lines = true; + break; + default: + // NOTHING + break; + } + vstate = VIM_START; + break; + } + } else { + /************************ + * key release events + ************************/ + clear_keyboard(); + } + return false; + } else { + return true; + } +} diff --git a/users/xtonhasvim/xtonhasvim.h b/users/xtonhasvim/xtonhasvim.h new file mode 100644 index 0000000000..21b794c033 --- /dev/null +++ b/users/xtonhasvim/xtonhasvim.h @@ -0,0 +1,66 @@ + /* Copyright 2015-2017 Christon DeWan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef USERSPACE +#define USERSPACE + +#include QMK_KEYBOARD_H +#include "action_layer.h" + +#define X_____X KC_NO + +bool process_record_xtonhasvim(uint16_t keycode, keyrecord_t *record); + +enum xtonhasvim_keycodes { + DUMMY = SAFE_RANGE, + VIM_START, // bookend for vim states + VIM_A, + VIM_B, + VIM_C, + VIM_CI, + VIM_D, + VIM_DI, + VIM_E, + VIM_H, + VIM_G, + VIM_I, + VIM_J, + VIM_K, + VIM_L, + VIM_O, + VIM_P, + VIM_S, + VIM_U, + VIM_V, + VIM_VS, // visual-line + VIM_VI, + VIM_W, + VIM_X, + VIM_Y, + VIM_PERIOD, // to support indent/outdent + VIM_COMMA, // and toggle comments + VIM_SHIFT, // avoid side-effect of supporting real shift. + VIM_ESC, // bookend + VIM_SAFE_RANGE // start other keycodes here. +}; + +enum xtonhasvim_layers { + _EDIT = 12, + _CMD +}; + + +#endif |