From 6dbde56f9604766d53331a08e4c8716ae82ed346 Mon Sep 17 00:00:00 2001 From: elpekenin Date: Fri, 26 May 2023 20:28:26 +0200 Subject: [PATCH 1/3] Initial code --- docs/feature_autocorrect.md | 11 ++-- quantum/process_keycode/process_autocorrect.c | 55 ++++++++++++++++++- quantum/process_keycode/process_autocorrect.h | 3 +- users/drashna/drashna.c | 2 +- 4 files changed, 62 insertions(+), 9 deletions(-) diff --git a/docs/feature_autocorrect.md b/docs/feature_autocorrect.md index 9f80c93f8274..7272e170ec70 100644 --- a/docs/feature_autocorrect.md +++ b/docs/feature_autocorrect.md @@ -198,7 +198,7 @@ bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *t ### Apply Autocorrect -Additionally, `apply_autocorrect(uint8_t backspaces, const char *str)` allows for users to add additional handling to the autocorrection, or replace the functionality entirely. This passes on the number of backspaces needed to replace the words, as well as the replacement string (partial word, not the full word). +Additionally, `apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct)` allows for users to add additional handling to the autocorrection, or replace the functionality entirely. This passes on the number of backspaces needed to replace the words, as well as the replacement string (partial word, not the full word), and the typo and corrected strings (complete words). #### Apply Autocorrect Example @@ -209,7 +209,7 @@ This following example will play a sound when a typo is autocorrected and execut float autocorrect_song[][2] = SONG(TERMINAL_SOUND); #endif -bool apply_autocorrect(uint8_t backspaces, const char *str) { +bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) { #ifdef AUDIO_ENABLE PLAY_SONG(autocorrect_song); #endif @@ -223,14 +223,17 @@ bool apply_autocorrect(uint8_t backspaces, const char *str) { ?> In this callback function, `return false` will stop the normal processing of autocorrect, which requires manually handling of removing the "bad" characters and typing the new characters. -!> ***IMPORTANT***: `str` is a pointer to `PROGMEM` data for the autocorrection. If you return false, and want to send the string, this needs to use `send_string_P` and not `send_string` or `SEND_STRING`. +!> ***IMPORTANT***: `str` is a pointer to `PROGMEM` data for the autocorrection. If you return false, and want to send the string, this needs to use `send_string_P` and not `send_string` nor `SEND_STRING`. You can also use `apply_autocorrect` to detect and display the event but allow internal code to execute the autocorrection with `return true`: ```c -bool apply_autocorrect(uint8_t backspaces, const char *str) { +bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) { #ifdef OLED_ENABLE oled_write_P(PSTR("Auto-corrected"), false); +#endif +#ifdef CONSOLE_ENABLE + printf("'%s' was corrected to '%s'\n", typo, correct); #endif return true; } diff --git a/quantum/process_keycode/process_autocorrect.c b/quantum/process_keycode/process_autocorrect.c index 137678826646..821873ff1d07 100644 --- a/quantum/process_keycode/process_autocorrect.c +++ b/quantum/process_keycode/process_autocorrect.c @@ -1,5 +1,6 @@ // Copyright 2021 Google LLC // Copyright 2021 @filterpaper +// Copyright 2023 Pablo Martinez (@elpekenin) // SPDX-License-Identifier: Apache-2.0 // Original source: https://getreuer.info/posts/keyboards/autocorrection @@ -157,10 +158,12 @@ __attribute__((weak)) bool process_autocorrect_user(uint16_t *keycode, keyrecord * * @param backspaces number of characters to remove * @param str pointer to PROGMEM string to replace mistyped seletion with + * @param typo the wrong string that triggered a correction + * @param correct what it would become after the changes * @return true apply correction * @return false user handled replacement */ -__attribute__((weak)) bool apply_autocorrect(uint8_t backspaces, const char *str) { +__attribute__((weak)) bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct) { return true; } @@ -284,11 +287,57 @@ bool process_autocorrect(uint16_t keycode, keyrecord_t *record) { if (code & 128) { // A typo was found! Apply autocorrect. const uint8_t backspaces = (code & 63) + !record->event.pressed; - if (apply_autocorrect(backspaces, (char const *)(autocorrect_data + state + 1))) { + const char * changes = (const char *)(autocorrect_data + state + 1); + + /* Gather info about the typo'd word + * + * Since buffer may contain several words, delimited by spaces, we + * iterate from the end to find the start and length of the typo + */ + char typo[AUTOCORRECT_MAX_LENGTH + 1] = {0}; // extra char for null terminator + + uint8_t typo_len = 0; + uint8_t typo_start = 0; + bool space_last = typo_buffer[typo_buffer_size - 1] == KC_SPC; + for (uint8_t i = typo_buffer_size; i > 0; --i) { + // stop counting after finding space (unless it is the last thing) + if (typo_buffer[i - 1] == KC_SPC && i != typo_buffer_size) { + typo_start = i; + break; + } + + ++typo_len; + } + + // when detecting 'typo:', reduce the length of the string by one + if (space_last) { + --typo_len; + } + + // convert buffer of keycodes into a string + for (uint8_t i = 0; i < typo_len; ++i) { + typo[i] = typo_buffer[typo_start + i] - KC_A + 'a'; + } + + /* Gather the corrected word + * + * A) Correction of 'typo:' -- Code takes into account + * an extra backspace to delete the space (which we dont copy) + * for this reason the offset is correct to "skip" the null terminator + * + * B) When correctign 'typo' -- Need extra offset for terminator + */ + char correct[AUTOCORRECT_MAX_LENGTH + 10] = {0}; // let's hope this is big enough + + uint8_t offset = space_last ? backspaces : backspaces + 1; + strcpy(correct, typo); + strcpy_P(correct + typo_len - offset, changes); + + if (apply_autocorrect(backspaces, changes, typo, correct)) { for (uint8_t i = 0; i < backspaces; ++i) { tap_code(KC_BSPC); } - send_string_P((char const *)(autocorrect_data + state + 1)); + send_string_P(changes); } if (keycode == KC_SPC) { diff --git a/quantum/process_keycode/process_autocorrect.h b/quantum/process_keycode/process_autocorrect.h index c7596107e505..9739534f9ea6 100644 --- a/quantum/process_keycode/process_autocorrect.h +++ b/quantum/process_keycode/process_autocorrect.h @@ -1,5 +1,6 @@ // Copyright 2021 Google LLC // Copyright 2021 @filterpaper +// Copyright 2023 Pablo Martinez (@elpekenin) // SPDX-License-Identifier: Apache-2.0 // Original source: https://getreuer.info/posts/keyboards/autocorrection @@ -9,7 +10,7 @@ bool process_autocorrect(uint16_t keycode, keyrecord_t *record); bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods); -bool apply_autocorrect(uint8_t backspaces, const char *str); +bool apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct); bool autocorrect_is_enabled(void); void autocorrect_enable(void); diff --git a/users/drashna/drashna.c b/users/drashna/drashna.c index 259810c70f14..01a4fc7bfbb9 100644 --- a/users/drashna/drashna.c +++ b/users/drashna/drashna.c @@ -139,7 +139,7 @@ float autocorrect_song[][2] = SONG(PLOVER_GOODBYE_SOUND); # endif # endif -bool apply_autocorrect(uint8_t backspaces, const char* str) { +bool apply_autocorrect(uint8_t backspaces, const char* str, char *typo, char *correct) { if (layer_state_is(_GAMEPAD)) { return false; } From 7d49c19112e046152bcaa57d648751c1b988f630 Mon Sep 17 00:00:00 2001 From: elpekenin Date: Fri, 26 May 2023 20:33:33 +0200 Subject: [PATCH 2/3] Fix typo --- quantum/process_keycode/process_autocorrect.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quantum/process_keycode/process_autocorrect.c b/quantum/process_keycode/process_autocorrect.c index 821873ff1d07..dae4b4e00561 100644 --- a/quantum/process_keycode/process_autocorrect.c +++ b/quantum/process_keycode/process_autocorrect.c @@ -325,7 +325,7 @@ bool process_autocorrect(uint16_t keycode, keyrecord_t *record) { * an extra backspace to delete the space (which we dont copy) * for this reason the offset is correct to "skip" the null terminator * - * B) When correctign 'typo' -- Need extra offset for terminator + * B) When correcting 'typo' -- Need extra offset for terminator */ char correct[AUTOCORRECT_MAX_LENGTH + 10] = {0}; // let's hope this is big enough From 6f6e756ca4469b6b880d64e243615980b167ca8e Mon Sep 17 00:00:00 2001 From: elpekenin Date: Tue, 30 May 2023 15:23:12 +0200 Subject: [PATCH 3/3] Add clarification --- docs/feature_autocorrect.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/feature_autocorrect.md b/docs/feature_autocorrect.md index 7272e170ec70..3a0a49095c6b 100644 --- a/docs/feature_autocorrect.md +++ b/docs/feature_autocorrect.md @@ -200,6 +200,8 @@ bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *t Additionally, `apply_autocorrect(uint8_t backspaces, const char *str, char *typo, char *correct)` allows for users to add additional handling to the autocorrection, or replace the functionality entirely. This passes on the number of backspaces needed to replace the words, as well as the replacement string (partial word, not the full word), and the typo and corrected strings (complete words). +?> Due to the way code works (no notion of words, just a stream of letters), the `typo` and `correct` strings are a best bet and could be "wrong". For example you may get `wordtpyo` & `wordtypo` instead of the expected `tpyo` & `typo`. + #### Apply Autocorrect Example This following example will play a sound when a typo is autocorrected and execute the autocorrection itself: