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

Mikael Magnusson <[email protected]>
Newsgroups gmane.comp.shells.zsh.devel
Message-ID <CAHYJk3RgjUvdanrXqU9EUtL41ZkNsFudhUp0qjQFD=uPzzfypg@mail.gmail.com>
[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 <[email protected]> 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
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.