Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: PATCH: Preliminary-ish hacky implementation of custom correction keymaps



[maybe i should have explicitly noted that this is in response to
zw/51544 https://www.zsh.org/mla/workers/2023/msg00273.html since the
mail i replied to is from a while ago, the archive doesn't seem to
have picked up on the in-reply-to header]

On Mon, May 4, 2026 at 7:04 AM Mikael Magnusson <mikachu@xxxxxxxxx> wrote:
>
> The name of the parameter is totally up for bikeshedding if anyone feels
> like we've decided on an appropriate namespace for new parameters of
> this kind. Note that this isn't actually in the zle module, though.
>
> As you can probably guess, I got a bit tired of fiddling around with
> pointers so for now the final newline has to be included. I can fix that
> to be a bit more ergonomic if everyone agrees this is a good idea
> overall.
>
> This doesn't expose the weird layout of the internal keymap variables,
> so you can just set 8 lines of up to 12 characters each, and all the
> tabs and newline characters are filled in as appropriate (I hope). I
> changed a couple of the newlines in the hardcoded strings to tabs, so
> that echoing the parameter actually shows you 8 lines. If this affects
> the algorithm noticably, I guess that's too bad, I didn't even try to
> understand how it works :).
>
> I guess this line implies they should be fairly interchangeable?
>     if (!(z = strchr(keymap, p[0])) || *z == '\n' || *z == '\t')
>
> Anyway, if you set the parameter to an empty value, it restores the
> default according to the option, and you can also do things like
> CORRECT_KEYMAP[4,6]=xyz if you want. If the range includes a newline
> and you don't, that's still an error though.
>
> ---
>  Src/params.c |   4 ++
>  Src/utils.c  | 104 ++++++++++++++++++++++++++++++++++++++++-----------
>  2 files changed, 87 insertions(+), 21 deletions(-)
>
> diff --git a/Src/params.c b/Src/params.c
> index aabfc31206..e4d596a74c 100644
> --- a/Src/params.c
> +++ b/Src/params.c
> @@ -248,6 +248,9 @@ static const struct gsu_scalar underscore_gsu =
>  { underscoregetfn, nullstrsetfn, stdunsetfn };
>  static const struct gsu_scalar keyboard_hack_gsu =
>  { keyboardhackgetfn, keyboardhacksetfn, stdunsetfn };
> +static const struct gsu_scalar correct_gsu =
> +{ get_correct_keymap, set_correct_keymap, stdunsetfn };
> +
>  #ifdef USE_LOCALE
>  static const struct gsu_scalar lc_blah_gsu =
>  { strgetfn, lcsetfn, stdunsetfn };
> @@ -322,6 +325,7 @@ IPDEF2("WORDCHARS", wordchars_gsu, 0),
>  IPDEF2("IFS", ifs_gsu, PM_DONTIMPORT),
>  IPDEF2("_", underscore_gsu, PM_DONTIMPORT),
>  IPDEF2("KEYBOARD_HACK", keyboard_hack_gsu, PM_DONTIMPORT),
> +IPDEF2("CORRECT_KEYMAP", correct_gsu, PM_DONTIMPORT),
>  IPDEF2("0", argzero_gsu, 0),
>
>  #ifdef USE_LOCALE
> diff --git a/Src/utils.c b/Src/utils.c
> index 13752e7569..1dec76a3d1 100644
> --- a/Src/utils.c
> +++ b/Src/utils.c
> @@ -4694,44 +4694,106 @@ mindist(char *dir, char *mindistguess, char *mindistbest, int wantdir)
>      return mindistd;
>  }
>
> -/**/
> -static int
> -spdist(char *s, char *t, int thresh)
> -{
> -    /* TODO: Correction for non-ASCII and multibyte-input keyboards. */
> -    char *p, *q;
> -    const char qwertykeymap[] =
> -    "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
> +char qwertykeymap[] =
> +"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
>  \t1234567890-=\t\
>  \tqwertyuiop[]\t\
> -\tasdfghjkl;'\n\t\
> +\tasdfghjkl;'\t\t\
>  \tzxcvbnm,./\t\t\t\
>  \n\n\n\n\n\n\n\n\n\n\n\n\n\n\
>  \t!@#$%^&*()_+\t\
>  \tQWERTYUIOP{}\t\
> -\tASDFGHJKL:\"\n\t\
> -\tZXCVBNM<>?\n\n\t\
> +\tASDFGHJKL:\"\t\t\
> +\tZXCVBNM<>?\t\t\t\
>  \n\n\n\n\n\n\n\n\n\n\n\n\n\n";
> -    const char dvorakkeymap[] =
> -    "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
> +char dvorakkeymap[] =
> +"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
>  \t1234567890[]\t\
>  \t',.pyfgcrl/=\t\
> -\taoeuidhtns-\n\t\
> +\taoeuidhtns-\t\t\
>  \t;qjkxbmwvz\t\t\t\
>  \n\n\n\n\n\n\n\n\n\n\n\n\n\n\
>  \t!@#$%^&*(){}\t\
>  \t\"<>PYFGCRL?+\t\
> -\tAOEUIDHTNS_\n\t\
> -\t:QJKXBMWVZ\n\n\t\
> +\tAOEUIDHTNS_\t\t\
> +\t:QJKXBMWVZ\t\t\t\
>  \n\n\n\n\n\n\n\n\n\n\n\n\n\n";
> -    const char *keymap;
> -    if ( isset( DVORAK ) )
> -      keymap = dvorakkeymap;
> -    else
> -      keymap = qwertykeymap;
> +char *keymap = NULL;
> +
> +/**/
> +char *
> +get_correct_keymap(UNUSED(Param pm))
> +{
> +    if (!keymap || keymap == dvorakkeymap || keymap == qwertykeymap) {
> +       keymap = isset(DVORAK) ? dvorakkeymap : qwertykeymap;
> +    }
> +    char pretty_keymap[13*8 + 2];
> +    pretty_keymap[13*8 + 1] = '\0';
> +
> +    const size_t pos[] = { 14 + 1, 14*2 + 1, 14*3 + 1, 14*4 + 1, 14*6 + 1, 14*7 + 1, 14*8 + 1, 14*9 + 1 };
> +    int line;
> +    for (line = 0; line < 8; line++) {
> +       memcpy(&pretty_keymap[13*line], &keymap[pos[line]], 12);
> +       pretty_keymap[13*line + 12] = '\n';
> +    }
> +    return dupstring(pretty_keymap);
> +}
> +
> +/**/
> +void
> +set_correct_keymap(UNUSED(Param pm), char *x)
> +{
> +    char *oldkeymap = keymap;
> +    if (x && *x) {
> +       char newkeymap[155];
> +       memset(newkeymap, '\t', 154);
> +       memset(&newkeymap[0], '\n', 14);
> +       memset(&newkeymap[5*14], '\n', 14);
> +       memset(&newkeymap[10*14], '\n', 14);
> +       newkeymap[154] = '\0';
> +       int line;
> +       char *p = x;
> +       const size_t pos[] = { 14 + 1, 14*2 + 1, 14*3 + 1, 14*4 + 1, 14*6 + 1, 14*7 + 1, 14*8 + 1, 14*9 + 1 };
> +       for (line = 0; line < 8; line++) {
> +           char *end = strchr(p, '\n');
> +           if (!end) {
> +               zwarn("CORRECT_KEYMAP needs to be set to 8 lines (even the last line needs a trailing newline in this rfc patch, because I'm lazy)");
> +               free(x);
> +               return;
> +           }
> +           if (end - p > 12) {
> +               zwarn("Lines in CORRECT_KEYMAP can be at most 12 characters, line %d was longer", line + 1);
> +               free(x);
> +               return;
> +           }
> +           memcpy(&newkeymap[pos[line]], p, end-p);
> +           p = end+1;
> +       }
> +       keymap = ztrdup(newkeymap);
> +       free(x);
> +    } else {
> +       keymap = NULL;
> +    }
> +    if (oldkeymap && oldkeymap != qwertykeymap && oldkeymap != dvorakkeymap)
> +       free(oldkeymap);
> +}
>
> +/**/
> +static int
> +spdist(char *s, char *t, int thresh)
> +{
> +    /* TODO: Correction for non-ASCII and multibyte-input keyboards. */
> +    char *p, *q;
>      if (!strcmp(s, t))
>         return 0;
> +
> +    if (!keymap || keymap == dvorakkeymap || keymap == qwertykeymap) {
> +       if ( isset( DVORAK ) )
> +           keymap = dvorakkeymap;
> +       else
> +           keymap = qwertykeymap;
> +    }
> +
>      /* any number of upper/lower mistakes allowed (dist = 1) */
>      for (p = s, q = t; *p && tulower(*p) == tulower(*q); p++, q++);
>      if (!*p && !*q)
> --
> 2.38.1
>




Messages sorted by: Reverse Date, Date, Thread, Author