QMK/keyboards/betalupi_ergodox/features/hid_spellcheck.c

185 lines
4.7 KiB
C

#ifdef ENABLE_HID_SPELLCHECK
#include "keymap_russian.h"
#include "keymap_us_international.h"
#include "features/hid_spellcheck.h"
#include "features/beta_rawhid.h"
#include "extra_mappings.h"
uint8_t spellcheck_buffer[SPELLCHECK_BUFFER_MAX] = {0};
uint8_t spellcheck_buffer_size = 0;
// Return one of the following.
#define SPELLCHECK_WORD 2
#define SPELLCHECK_SPACE 1
#define SPELLCHECK_NEITHER 0
uint8_t keycode_type_en(uint8_t mods, uint16_t keycode) {
if (
KC_A <= keycode && keycode <= KC_Z // basic letters
) {
return SPELLCHECK_WORD;
} else if (
( // Include these
(KC_1 <= keycode && keycode <= KC_SLSH) || // Symbols
// Treat " (shifted ') as a word boundary.
((keycode == KC_QUOT) && ((mods & MOD_MASK_SHIFT) != 0))
) && !( // Don't include these
(keycode == KC_ESC) ||
(keycode == KC_ENTER)
)
) {
return SPELLCHECK_SPACE;
} else {
return SPELLCHECK_NEITHER;
}
}
uint8_t keycode_type_ru(uint8_t mods, uint16_t keycode) {
if (
KC_A <= keycode && keycode <= KC_Z // basic letters
) {
return SPELLCHECK_WORD;
} else if (
KC_1 <= keycode && keycode <= KC_0
) {
return SPELLCHECK_SPACE;
}
switch (keycode) {
case RU_YO:
case RU_HA:
case RU_HARD:
case RU_E:
case RU_BE:
case RU_YU:
return SPELLCHECK_WORD;
case RU_MINS:
case RU_EQL:
case RU_DOT:
case RU_BSLS:
return SPELLCHECK_SPACE;
}
return SPELLCHECK_NEITHER;
}
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_BSPC) {
// Remove last character from buffer
if (spellcheck_buffer_size > 0) { --spellcheck_buffer_size; }
return true;
}
uint8_t keycode_type;
switch (layer_layouts[biton32(layer_state)]) {
case LAYOUT_EN:
keycode_type = keycode_type_en(mods, keycode);
break;
case LAYOUT_RU:
keycode_type = keycode_type_ru(mods, keycode);
break;
default:
keycode_type = SPELLCHECK_NEITHER;
break;
}
switch(keycode_type) {
// No modifications are needed if this is a regular word character
case SPELLCHECK_WORD:
// 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;
}
spellcheck_buffer[spellcheck_buffer_size++] = (uint8_t) keycode;
return true;
case SPELLCHECK_SPACE:
// If this is a word break, send word and reset buffer
if (spellcheck_buffer_size > 0) {
hid_send_word();
spellcheck_buffer_size = 0;
}
return true;
case SPELLCHECK_NEITHER:
spellcheck_buffer_size = 0;
return true;
}
return true;
}
#endif