111 lines
3.6 KiB
C
111 lines
3.6 KiB
C
#include "spellcheck.h"
|
|
#include "rawhid.h"
|
|
|
|
uint8_t spellcheck_buffer[SPELLCHECK_BUFFER_MAX] = {0};
|
|
uint8_t spellcheck_buffer_size = 0;
|
|
|
|
bool process_spellcheck(uint16_t keycode, keyrecord_t* record) {
|
|
|
|
|
|
// Ignore key release; we only process key presses.
|
|
if (!record->event.pressed) { return true; }
|
|
|
|
#ifndef NO_ACTION_ONESHOT
|
|
const uint8_t mods = get_mods() | get_oneshot_mods();
|
|
#else
|
|
const uint8_t mods = get_mods();
|
|
#endif
|
|
|
|
|
|
// Disable autocorrection while a mod other than shift is active.
|
|
if ((mods & ~MOD_MASK_SHIFT) != 0) {
|
|
spellcheck_buffer_size = 0;
|
|
return true;
|
|
}
|
|
|
|
// The following switch cases address various kinds of keycodes. This logic is
|
|
// split over two switches rather than merged into one. The first switch may
|
|
// extract a basic keycode which is then further handled by the second switch,
|
|
// e.g. a layer-tap key with Caps Lock `LT(layer, KC_CAPS)`.
|
|
switch (keycode) {
|
|
#ifndef NO_ACTION_TAPPING
|
|
case QK_MOD_TAP ... QK_MOD_TAP_MAX: // Tap-hold keys.
|
|
#ifndef NO_ACTION_LAYER
|
|
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
|
|
#endif
|
|
// Ignore when tap-hold keys are held.
|
|
if (record->tap.count == 0) { return true; }
|
|
// Otherwise when tapped, get the basic keycode.
|
|
// Fallthrough intended.
|
|
#endif
|
|
|
|
// Handle shifted keys, e.g. symbols like KC_EXLM = S(KC_1).
|
|
case QK_LSFT ... QK_LSFT + 255:
|
|
case QK_RSFT ... QK_RSFT + 255:
|
|
keycode &= 0xff; // Get the basic keycode.
|
|
break;
|
|
|
|
// NOTE: Space Cadet keys expose no info to check whether they are being
|
|
// tapped vs. held. This makes autocorrection ambiguous, e.g. KC_LCPO might
|
|
// be '(', which we would treat as a word break, or it might be shift, which
|
|
// we would treat as having no effect. To behave cautiously, we allow Space
|
|
// Cadet keycodes to fall to the logic below and clear autocorrection state.
|
|
}
|
|
|
|
switch (keycode) {
|
|
// Ignore shifts, Caps Lock, one-shot mods, and layer switch keys.
|
|
case KC_NO:
|
|
case KC_LSFT:
|
|
case KC_RSFT:
|
|
case KC_CAPS:
|
|
case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:
|
|
case QK_TO ... QK_TO_MAX:
|
|
case QK_MOMENTARY ... QK_MOMENTARY_MAX:
|
|
case QK_DEF_LAYER ... QK_DEF_LAYER_MAX:
|
|
case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
|
|
case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
|
|
case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
|
|
case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:
|
|
return true; // Ignore these keys.
|
|
}
|
|
|
|
if (keycode == KC_QUOT) {
|
|
// Treat " (shifted ') as a word boundary.
|
|
if ((mods & MOD_MASK_SHIFT) != 0) { keycode = KC_SPC; }
|
|
} else if (!(KC_A <= keycode && keycode <= KC_Z)) {
|
|
if (keycode == KC_BSPC) {
|
|
// Remove last character from the buffer.
|
|
if (spellcheck_buffer_size > 0) { --spellcheck_buffer_size; }
|
|
return true;
|
|
} else if (KC_1 <= keycode && keycode <= KC_SLSH && keycode != KC_ESC) {
|
|
// Set a word boundary if space, period, digit, etc. is pressed.
|
|
// Behave more conservatively for the enter key. Reset, so that enter
|
|
// can't be used on a word ending.
|
|
if (keycode == KC_ENT) { spellcheck_buffer_size = 0; }
|
|
keycode = KC_SPC;
|
|
} else {
|
|
// Clear state if some other non-alpha key is pressed.
|
|
spellcheck_buffer_size = 0;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// If the buffer is full, rotate it to discard the oldest character.
|
|
if (spellcheck_buffer_size >= SPELLCHECK_BUFFER_MAX) {
|
|
memmove(spellcheck_buffer, spellcheck_buffer + 1, SPELLCHECK_BUFFER_MAX - 1);
|
|
spellcheck_buffer_size = SPELLCHECK_BUFFER_MAX - 1;
|
|
}
|
|
|
|
// NOTE: `keycode` must be a basic keycode (0-255) by this point.
|
|
|
|
// A to Z keycodes
|
|
if (keycode >= 0x04 && keycode <= 0x1D) {
|
|
spellcheck_buffer[spellcheck_buffer_size++] = (uint8_t) keycode;
|
|
} else if (spellcheck_buffer_size > 0) {
|
|
hid_send_word();
|
|
spellcheck_buffer_size = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|