/* 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" #include "fancylighting.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; static bool yank_was_lines = false; static bool SHIFTED = false; static uint32_t mod_override_layer_state = 0; static uint16_t mod_override_triggering_key = 0; static void edit(void) { vstate = VIM_START; layer_clear(); } #define EDIT edit() static 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; } } static void comma_period(uint16_t keycode) { switch (keycode) { 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; } } __attribute__ ((weak)) bool process_record_keymap(uint16_t keycode, keyrecord_t *record) { return true; } bool process_record_user(uint16_t keycode, keyrecord_t *record) { /* keymap gets first whack */ if(!process_record_keymap(keycode, record)) return false; /****** FIREY_RETURN *****/ if(record->event.pressed && keycode == FIREY_RETURN) { start_firey_return(); TAP(KC_ENT); } /****** mod passthru *****/ if(record->event.pressed && layer_state_is(vim_cmd_layer()) && (IS_MOD(keycode) || keycode == LSFT(KC_LALT))) { mod_override_layer_state = layer_state; mod_override_triggering_key = keycode; // TODO: change this to track key location instead 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(vim_cmd_layer()); vstate = VIM_START; // reset state yank_was_lines = false; SHIFTED = false; mod_override_layer_state = 0; mod_override_triggering_key = 0; 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) { CTRL(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: case VIM_PERIOD: comma_period(keycode); 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; vstate = VIM_START; 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_P: CMD(KC_V); vstate = VIM_START; 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; case VIM_COMMA: case VIM_PERIOD: comma_period(keycode); 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_P: CMD(KC_V); vstate = VIM_START; break; case VIM_V: case VIM_ESC: TAP(KC_RIGHT); vstate = VIM_START; break; case VIM_COMMA: case VIM_PERIOD: comma_period(keycode); 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; } }