Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add s/S/x/X actions, various synonyms, mode hooks, userspace docs; optionally differentiate w & e motions; fix process hooks & redo on Mac; automate updating firmware size table in readme #1

Merged
merged 19 commits into from
Jun 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 81 additions & 43 deletions README.org

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions src/actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,23 @@
// This should be used whenever using one of these shortcuts
#ifdef VIM_FOR_MAC
#define VCMD LCMD
#define VIM_REDO VCMD(LSFT(KC_Z))
#else
#define VCMD LCTL
#define VIM_REDO VCMD(KC_Y)
#endif

// These are the main keys for each vim core vim action
// These + VIM_REDO (defined above) are the main keys for each vim core vim action
#define VIM_CHANGE KC_DEL
#define VIM_DELETE VCMD(KC_X) // note that you may prefer a simple delete here since we only are using one clipboard
#define VIM_YANK VCMD(KC_C)
// Other commands
#define VIM_PASTE VCMD(KC_V)
#define VIM_UNDO VCMD(KC_Z)
#define VIM_REDO VCMD(KC_Y)
#define VIM_FIND VCMD(KC_F)
#define VIM_SAVE VCMD(KC_S)
#define VIM_X KC_DEL
#define VIM_SHIFT_X KC_BSPC

// Process function to handle text objects ie in or around word
bool process_text_objects(uint16_t keycode, const keyrecord_t *record);
Expand Down
58 changes: 57 additions & 1 deletion src/modes.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ bool process_normal_mode_user(uint16_t keycode, const keyrecord_t *record) {

// The function that handles normal mode keycode inputs
bool process_normal_mode(uint16_t keycode, const keyrecord_t *record) {
if (!process_normal_mode_user(keycode, record)) {
return false;
}
#ifdef VIM_DOT_REPEAT
bool should_record_action = true;
#define NO_RECORD_ACTION() should_record_action = false;
Expand Down Expand Up @@ -120,6 +123,15 @@ bool process_normal_mode(uint16_t keycode, const keyrecord_t *record) {
case KC_D:
start_delete_action();
break;
case LSFT(KC_S):
VIM_HOME();
VIM_SHIFT_END();
change_action();
break;
case KC_S:
tap_code16(LSFT(KC_RIGHT));
change_action();
break;
case LSFT(KC_Y):
VIM_SHIFT_END();
yank_action();
Expand Down Expand Up @@ -169,6 +181,9 @@ bool process_normal_mode(uint16_t keycode, const keyrecord_t *record) {
case KC_X:
tap_code16(VIM_X);
break;
case LSFT(KC_X):
tap_code16(VIM_SHIFT_X);
break;
#ifdef VIM_COLON_CMDS
case KC_COLON:
process_func = process_colon_cmd;
Expand Down Expand Up @@ -211,7 +226,7 @@ bool process_normal_mode(uint16_t keycode, const keyrecord_t *record) {
return false;
}

// Allow the user to add their own bindings to both visual modes
// Allow the user to add their own bindings to visual mode
// Note, this should be optimized away unless there is a user definition
__attribute__ ((weak))
bool process_visual_mode_user(uint16_t keycode, const keyrecord_t *record) {
Expand All @@ -220,6 +235,9 @@ bool process_visual_mode_user(uint16_t keycode, const keyrecord_t *record) {

// The function that handles visual mode keycode inputs
bool process_visual_mode(uint16_t keycode, const keyrecord_t *record) {
if (!process_visual_mode_user(keycode, record)) {
return false;
}
// handle motions on their own so they can be pressed and held
if (!process_motions(keycode, record, QK_LSFT)) {
return false;
Expand All @@ -232,9 +250,11 @@ bool process_visual_mode(uint16_t keycode, const keyrecord_t *record) {
if (record->event.pressed) {
switch (keycode) {
case KC_C:
case KC_S:
change_action();
return false;
case KC_D:
case KC_X:
delete_action();
return false;
case KC_Y:
Expand Down Expand Up @@ -265,8 +285,18 @@ bool process_visual_mode(uint16_t keycode, const keyrecord_t *record) {
return false;
}

// Allow the user to add their own bindings to visual line mode
// Note, this should be optimized away unless there is a user definition
__attribute__ ((weak))
bool process_visual_line_mode_user(uint16_t keycode, const keyrecord_t *record) {
return true;
}

// The function that handles visual line mode keycode inputs
bool process_visual_line_mode(uint16_t keycode, const keyrecord_t *record) {
if (!process_visual_line_mode_user(keycode, record)) {
return false;
}
// handle motions on their own so they can be pressed and held
switch (keycode) {
case KC_J:
Expand All @@ -289,10 +319,12 @@ bool process_visual_line_mode(uint16_t keycode, const keyrecord_t *record) {
if (record->event.pressed) {
switch (keycode) {
case KC_C:
case KC_S:
tap_code16(LSFT(KC_LEFT));
change_action();
return false;
case KC_D:
case KC_X:
delete_line_action();
return false;
case KC_Y:
Expand Down Expand Up @@ -350,22 +382,40 @@ bool process_insert_mode(uint16_t keycode, const keyrecord_t *record) {
}


// Allow the user to set custom state when normal mode is entered
__attribute__ ((weak))
void normal_mode_user(void) {
}

// Function to enter into normal mode
void normal_mode(void) {
normal_mode_user();
vim_current_mode = NORMAL_MODE;
process_func = process_normal_mode;
}

// Allow the user to set custom state when insert mode is entered
__attribute__ ((weak))
void insert_mode_user(void) {
}

// Function to enter into insert mode
void insert_mode(void) {
insert_mode_user();
vim_current_mode = INSERT_MODE;
// need to clear motion keys if they are currently pressed
clear_keyboard();
process_func = process_insert_mode;
}

// Allow the user to set custom state when visual mode is entered
__attribute__ ((weak))
void visual_mode_user(void) {
}

// Function to enter into visual mode
void visual_mode(void) {
visual_mode_user();
vim_current_mode = VISUAL_MODE;
#ifndef NO_VISUAL_MODE
#ifdef BETTER_VISUAL_MODE
Expand All @@ -378,8 +428,14 @@ void visual_mode(void) {
}


// Allow the user to set custom state when visual line mode is entered
__attribute__ ((weak))
void visual_line_mode_user(void) {
}

// Function to enter into visual line mode
void visual_line_mode(void) {
visual_line_mode_user();
vim_current_mode = VISUAL_LINE_MODE;
#ifndef NO_VISUAL_LINE_MODE
#ifdef BETTER_VISUAL_MODE
Expand Down
21 changes: 18 additions & 3 deletions src/motions.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,36 @@ bool process_motions(uint16_t keycode, const keyrecord_t *record, uint16_t qk_mo
return false;
case KC_B:
case VIM_B:
case LSFT(KC_B):
set_visual_direction(V_BACKWARD);
register_motion(qk_mods | VIM_B, record);
return false;
case KC_E: // currently this doesn't do much
case KC_W:
case VIM_W:
case LSFT(KC_W):
#ifdef VIM_W_BEGINNING_OF_WORD
set_visual_direction(V_FORWARD);
register_motion(qk_mods | VIM_W, record);
if (record->event.pressed) {
register_code16(qk_mods | VIM_W);
} else {
Copy link
Owner

@andrewjrae andrewjrae Jun 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is generally a good solution to the holding problem, but it makes viw act like vaw. This is because the object action finishes on the press, returning to visual mode, then w is released and the selection moves to the start of the next word.

The other issue I noticed is that cw, dw etc. act like ce, since it also finishes it's action on press.

There's two simple solutions to these issues, you could make actions finish on release, which should be okay, the delay is probably not terribly noticeable. However, it allows for a strange ability to hold any of the motion keys during an action, ie dj down multiple lines and delete on release. But maybe that's a feature not a bug hehe, I don't think anyone would accidentally hold down long enough to delete more than they intended.

The other solution is to just make all the motions aside from hjkl not hold-able. I personally never hold w, b, or e, but perhaps that's a more important feature for other people. Frankly, I could go either way, but I think I'm leaning towards making them just be tapping keys, as I think it's generally easier to develop with and should break less stuff (you'd probably have to patch dot repeat if you made actions go on release).

After thinking some more, I think you should probably go for the no hold approach, unless you really think we should support holding w. If you do think it's greatly important it can be held, just make sure all the other features are happy, you'll need to make dot repeat act on release at the very least.

EDIT:
I've given this some more thought, and decided that being able to hold w is somewhat important since we can't do f or t motions. I think for now, simply adding the better w motion as a macro option is the way to go (with a warning that some things break). And down the line we can take a look at making things work on release (or if you have the time you can do it now, but my gut feeling is that I'd prefer it in a different PR since this one would get pretty big.

unregister_code16(qk_mods | VIM_W);
tap_code16(qk_mods | VIM_W);
tap_code16(qk_mods | VIM_B);
}
return false;
#endif
case KC_E:
case LSFT(KC_E):
set_visual_direction(V_FORWARD);
register_motion(qk_mods | VIM_E, record);
return false;
case KC_0:
case VIM_0:
case KC_CIRC: // ^
set_visual_direction(V_BACKWARD);
register_motion(qk_mods | VIM_0, record);
return false;
case KC_DLR:
case KC_DLR: // $
case VIM_DLR:
set_visual_direction(V_FORWARD);
register_motion(qk_mods | VIM_DLR, record);
Expand Down
10 changes: 10 additions & 0 deletions update-readme/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM qmkfm/qmk_firmware:latest

ENV MY_KEYMAP_PATH=/qmk_firmware/keyboards/uno/keymaps/qmk-vim-update-readme

WORKDIR /qmk_firmware

COPY keymap $MY_KEYMAP_PATH
COPY run.py /qmk_firmware/qmk_vim_update_readme.py

CMD python3 qmk_vim_update_readme.py
Empty file added update-readme/keymap/config.h
Empty file.
21 changes: 21 additions & 0 deletions update-readme/keymap/keymap.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include QMK_KEYBOARD_H
#include "qmk-vim/src/vim.h"

enum keycodes {
QMK_VIM = SAFE_RANGE
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT(QMK_VIM)
};

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_vim_mode(keycode, record)) {
return false;
}
if (keycode == QMK_VIM && record->event.pressed) {
toggle_vim_mode();
return false;
}
return true;
}
1 change: 1 addition & 0 deletions update-readme/keymap/rules.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include keyboards/uno/keymaps/qmk-vim-update-readme/qmk-vim/rules.mk
52 changes: 52 additions & 0 deletions update-readme/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import os
import re
import subprocess

# Path to QMK keymap used for firmware size calculations
MY_KEYMAP_PATH = os.getenv('MY_KEYMAP_PATH')
README_PATH = os.path.join(MY_KEYMAP_PATH, 'qmk-vim/README.org')

# Split readme into sections: before feature macros table, table itself, and after table
BEFORE, MACROS, AFTER = 0, 1, 2
lines = [[], [], []]
position = BEFORE
with open(README_PATH) as f:
for line in f:
if ((position == BEFORE and line.startswith('| ='))
or (position == MACROS and not line.startswith('|'))):
position += 1
lines[position].append(line)

# Compile and parse output for firmware size
def get_firmware_size():
process = subprocess.run(
['qmk', 'compile', '-kb', 'uno', '-km', 'qmk-vim-update-readme'], capture_output=True)
matches = re.search(r'The firmware size is fine - (\d+)', process.stdout.decode('utf-8'))
return int(matches[1])

# Determine firmware size without any macros defined
baseline = get_firmware_size()

# Iterate over rows in table
for i in range(len(lines[MACROS])):
# Parse macro name
line = lines[MACROS][i]
cells = line.split('|')
macro = cells[1].strip('= ')
# Add macro to config
with open(os.path.join(MY_KEYMAP_PATH, 'config.h'), 'w') as f:
f.write(f'#define {macro}')
# Determine firmware size difference
size = get_firmware_size()
size_diff = baseline - size
if size_diff >= 0:
new_val_str = f' +{size_diff} B'
else:
new_val_str = f' {size_diff} B'
# Update table
cells[3] = new_val_str.ljust(len(cells[3]), ' ')
lines[MACROS][i] = '|'.join(cells)

# Overwrite readme with new values
with open(README_PATH, 'w') as f:
f.write(''.join(line for section in lines for line in section))