process_autocorrect.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. // Copyright 2021 Google LLC
  2. // Copyright 2021 @filterpaper
  3. // SPDX-License-Identifier: Apache-2.0
  4. // Original source: https://getreuer.info/posts/keyboards/autocorrection
  5. #include "process_autocorrect.h"
  6. #include <string.h>
  7. #include "keycode_config.h"
  8. #if __has_include("autocorrect_data.h")
  9. # include "autocorrect_data.h"
  10. #else
  11. # pragma message "Autocorrect is using the default library."
  12. # include "autocorrect_data_default.h"
  13. #endif
  14. static uint8_t typo_buffer[AUTOCORRECT_MAX_LENGTH] = {KC_SPC};
  15. static uint8_t typo_buffer_size = 1;
  16. /**
  17. * @brief function for querying the enabled state of autocorrect
  18. *
  19. * @return true if enabled
  20. * @return false if disabled
  21. */
  22. bool autocorrect_is_enabled(void) {
  23. return keymap_config.autocorrect_enable;
  24. }
  25. /**
  26. * @brief Enables autocorrect and saves state to eeprom
  27. *
  28. */
  29. void autocorrect_enable(void) {
  30. keymap_config.autocorrect_enable = true;
  31. eeconfig_update_keymap(keymap_config.raw);
  32. }
  33. /**
  34. * @brief Disables autocorrect and saves state to eeprom
  35. *
  36. */
  37. void autocorrect_disable(void) {
  38. keymap_config.autocorrect_enable = false;
  39. typo_buffer_size = 0;
  40. eeconfig_update_keymap(keymap_config.raw);
  41. }
  42. /**
  43. * @brief Toggles autocorrect's status and save state to eeprom
  44. *
  45. */
  46. void autocorrect_toggle(void) {
  47. keymap_config.autocorrect_enable = !keymap_config.autocorrect_enable;
  48. typo_buffer_size = 0;
  49. eeconfig_update_keymap(keymap_config.raw);
  50. }
  51. /**
  52. * @brief handler for user to override whether autocorrect should process this keypress
  53. *
  54. * @param keycode Keycode registered by matrix press, per keymap
  55. * @param record keyrecord_t structure
  56. * @param typo_buffer_size passed along to allow resetting of autocorrect buffer
  57. * @param mods allow processing of mod status
  58. * @return true Allow autocorection
  59. * @return false Stop processing and escape from autocorrect.
  60. */
  61. __attribute__((weak)) bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) {
  62. return process_autocorrect_default_handler(keycode, record, typo_buffer_size, mods);
  63. }
  64. /**
  65. * @brief fallback handler for determining if autocorrect should process this keypress
  66. * can be used by user callback to get the basic keycode being "wrapped"
  67. *
  68. * NOTE: These values may have been edited by user callback before getting here
  69. *
  70. * @param keycode Keycode registered by matrix press, per keymap
  71. * @param record keyrecord_t structure
  72. * @param typo_buffer_size passed along to allow resetting of autocorrect buffer
  73. * @param mods allow processing of mod status
  74. * @return true Allow autocorection
  75. * @return false Stop processing and escape from autocorrect.
  76. */
  77. bool process_autocorrect_default_handler(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) {
  78. // See quantum_keycodes.h for reference on these matched ranges.
  79. switch (*keycode) {
  80. // Exclude these keycodes from processing.
  81. case KC_LSFT:
  82. case KC_RSFT:
  83. case KC_CAPS:
  84. case QK_TO ... QK_TO_MAX:
  85. case QK_MOMENTARY ... QK_MOMENTARY_MAX:
  86. case QK_DEF_LAYER ... QK_DEF_LAYER_MAX:
  87. case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
  88. case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
  89. case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
  90. case QK_LAYER_MOD ... QK_LAYER_MOD_MAX:
  91. case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:
  92. return false;
  93. // Mask for base keycode from shifted keys.
  94. case QK_LSFT ... QK_LSFT + 255:
  95. case QK_RSFT ... QK_RSFT + 255:
  96. if (*keycode >= QK_LSFT && *keycode <= (QK_LSFT + 255)) {
  97. *mods |= MOD_LSFT;
  98. } else {
  99. *mods |= MOD_RSFT;
  100. }
  101. *keycode = QK_MODS_GET_BASIC_KEYCODE(*keycode); // Get the basic keycode.
  102. return true;
  103. #ifndef NO_ACTION_TAPPING
  104. // Exclude tap-hold keys when they are held down
  105. // and mask for base keycode when they are tapped.
  106. case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
  107. # ifdef NO_ACTION_LAYER
  108. // Exclude Layer Tap, if layers are disabled
  109. // but action tapping is still enabled.
  110. return false;
  111. # else
  112. // Exclude hold keycode
  113. if (!record->tap.count) {
  114. return false;
  115. }
  116. *keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(*keycode);
  117. break;
  118. # endif
  119. case QK_MOD_TAP ... QK_MOD_TAP_MAX:
  120. // Exclude hold keycode
  121. if (!record->tap.count) {
  122. return false;
  123. }
  124. *keycode = QK_MOD_TAP_GET_TAP_KEYCODE(*keycode);
  125. break;
  126. #else
  127. case QK_MOD_TAP ... QK_MOD_TAP_MAX:
  128. case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
  129. // Exclude if disabled
  130. return false;
  131. #endif
  132. // Exclude swap hands keys when they are held down
  133. // and mask for base keycode when they are tapped.
  134. case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
  135. #ifdef SWAP_HANDS_ENABLE
  136. // Note: IS_SWAP_HANDS_KEYCODE() actually tests for the special action keycodes like SH_TOGG, SH_TT, ...,
  137. // which currently overlap the SH_T(kc) range.
  138. if (IS_SWAP_HANDS_KEYCODE(*keycode)
  139. # ifndef NO_ACTION_TAPPING
  140. || !record->tap.count
  141. # endif // NO_ACTION_TAPPING
  142. ) {
  143. return false;
  144. }
  145. *keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(*keycode);
  146. break;
  147. #else
  148. // Exclude if disabled
  149. return false;
  150. #endif
  151. }
  152. // Disable autocorrect while a mod other than shift is active.
  153. if ((*mods & ~MOD_MASK_SHIFT) != 0) {
  154. *typo_buffer_size = 0;
  155. return false;
  156. }
  157. return true;
  158. }
  159. /**
  160. * @brief handling for when autocorrection has been triggered
  161. *
  162. * @param backspaces number of characters to remove
  163. * @param str pointer to PROGMEM string to replace mistyped seletion with
  164. * @return true apply correction
  165. * @return false user handled replacement
  166. */
  167. __attribute__((weak)) bool apply_autocorrect(uint8_t backspaces, const char *str) {
  168. return true;
  169. }
  170. /**
  171. * @brief Process handler for autocorrect feature
  172. *
  173. * @param keycode Keycode registered by matrix press, per keymap
  174. * @param record keyrecord_t structure
  175. * @return true Continue processing keycodes, and send to host
  176. * @return false Stop processing keycodes, and don't send to host
  177. */
  178. bool process_autocorrect(uint16_t keycode, keyrecord_t *record) {
  179. uint8_t mods = get_mods();
  180. #ifndef NO_ACTION_ONESHOT
  181. mods |= get_oneshot_mods();
  182. #endif
  183. if ((keycode >= QK_AUTOCORRECT_ON && keycode <= QK_AUTOCORRECT_TOGGLE) && record->event.pressed) {
  184. if (keycode == QK_AUTOCORRECT_ON) {
  185. autocorrect_enable();
  186. } else if (keycode == QK_AUTOCORRECT_OFF) {
  187. autocorrect_disable();
  188. } else if (keycode == QK_AUTOCORRECT_TOGGLE) {
  189. autocorrect_toggle();
  190. } else {
  191. return true;
  192. }
  193. return false;
  194. }
  195. if (!keymap_config.autocorrect_enable) {
  196. typo_buffer_size = 0;
  197. return true;
  198. }
  199. if (!record->event.pressed) {
  200. return true;
  201. }
  202. // autocorrect keycode verification and extraction
  203. if (!process_autocorrect_user(&keycode, record, &typo_buffer_size, &mods)) {
  204. return true;
  205. }
  206. // keycode buffer check
  207. switch (keycode) {
  208. case KC_A ... KC_Z:
  209. // process normally
  210. break;
  211. case KC_1 ... KC_0:
  212. case KC_TAB ... KC_SEMICOLON:
  213. case KC_GRAVE ... KC_SLASH:
  214. // Set a word boundary if space, period, digit, etc. is pressed.
  215. keycode = KC_SPC;
  216. break;
  217. case KC_ENTER:
  218. // Behave more conservatively for the enter key. Reset, so that enter
  219. // can't be used on a word ending.
  220. typo_buffer_size = 0;
  221. keycode = KC_SPC;
  222. break;
  223. case KC_BSPC:
  224. // Remove last character from the buffer.
  225. if (typo_buffer_size > 0) {
  226. --typo_buffer_size;
  227. }
  228. return true;
  229. case KC_QUOTE:
  230. // Treat " (shifted ') as a word boundary.
  231. if ((mods & MOD_MASK_SHIFT) != 0) {
  232. keycode = KC_SPC;
  233. }
  234. break;
  235. default:
  236. // Clear state if some other non-alpha key is pressed.
  237. typo_buffer_size = 0;
  238. return true;
  239. }
  240. // Rotate oldest character if buffer is full.
  241. if (typo_buffer_size >= AUTOCORRECT_MAX_LENGTH) {
  242. memmove(typo_buffer, typo_buffer + 1, AUTOCORRECT_MAX_LENGTH - 1);
  243. typo_buffer_size = AUTOCORRECT_MAX_LENGTH - 1;
  244. }
  245. // Append `keycode` to buffer.
  246. typo_buffer[typo_buffer_size++] = keycode;
  247. // Return if buffer is smaller than the shortest word.
  248. if (typo_buffer_size < AUTOCORRECT_MIN_LENGTH) {
  249. return true;
  250. }
  251. // Check for typo in buffer using a trie stored in `autocorrect_data`.
  252. uint16_t state = 0;
  253. uint8_t code = pgm_read_byte(autocorrect_data + state);
  254. for (int8_t i = typo_buffer_size - 1; i >= 0; --i) {
  255. uint8_t const key_i = typo_buffer[i];
  256. if (code & 64) { // Check for match in node with multiple children.
  257. code &= 63;
  258. for (; code != key_i; code = pgm_read_byte(autocorrect_data + (state += 3))) {
  259. if (!code) return true;
  260. }
  261. // Follow link to child node.
  262. state = (pgm_read_byte(autocorrect_data + state + 1) | pgm_read_byte(autocorrect_data + state + 2) << 8);
  263. // Check for match in node with single child.
  264. } else if (code != key_i) {
  265. return true;
  266. } else if (!(code = pgm_read_byte(autocorrect_data + (++state)))) {
  267. ++state;
  268. }
  269. // Stop if `state` becomes an invalid index. This should not normally
  270. // happen, it is a safeguard in case of a bug, data corruption, etc.
  271. if (state >= DICTIONARY_SIZE) {
  272. return true;
  273. }
  274. code = pgm_read_byte(autocorrect_data + state);
  275. if (code & 128) { // A typo was found! Apply autocorrect.
  276. const uint8_t backspaces = (code & 63) + !record->event.pressed;
  277. if (apply_autocorrect(backspaces, (char const *)(autocorrect_data + state + 1))) {
  278. for (uint8_t i = 0; i < backspaces; ++i) {
  279. tap_code(KC_BSPC);
  280. }
  281. send_string_P((char const *)(autocorrect_data + state + 1));
  282. }
  283. if (keycode == KC_SPC) {
  284. typo_buffer[0] = KC_SPC;
  285. typo_buffer_size = 1;
  286. return true;
  287. } else {
  288. typo_buffer_size = 0;
  289. return false;
  290. }
  291. }
  292. }
  293. return true;
  294. }