st-0.8.5-autocomplete-20220327-230120.diff - sites - public wiki contents of suckless.org
 (HTM) git clone git://git.suckless.org/sites
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       st-0.8.5-autocomplete-20220327-230120.diff (20937B)
       ---
            1 diff -uraN st-0.8.5/autocomplete.h st-autocomplete/autocomplete.h
            2 --- st-0.8.5/autocomplete.h        1970-01-01 04:00:00.000000000 +0400
            3 +++ st-autocomplete/autocomplete.h        2022-03-13 02:45:34.586842452 +0400
            4 @@ -0,0 +1,16 @@
            5 +# ifndef __ST_AUTOCOMPLETE_H
            6 +# define __ST_AUTOCOMPLETE_H
            7 +
            8 +enum {
            9 +        ACMPL_DEACTIVATE,
           10 +        ACMPL_WORD,
           11 +        ACMPL_WWORD,
           12 +        ACMPL_FUZZY_WORD,
           13 +        ACMPL_FUZZY_WWORD,
           14 +        ACMPL_FUZZY,
           15 +        ACMPL_SUFFIX,
           16 +        ACMPL_SURROUND,
           17 +        ACMPL_UNDO,
           18 +};
           19 +
           20 +# endif // __ST_AUTOCOMPLETE_H
           21 diff -uraN st-0.8.5/config.def.h st-autocomplete/config.def.h
           22 --- st-0.8.5/config.def.h        2022-03-13 02:45:34.586842452 +0400
           23 +++ st-autocomplete/config.def.h        2022-03-13 02:45:34.586842452 +0400
           24 @@ -170,6 +170,8 @@
           25   */
           26  static uint forcemousemod = ShiftMask;
           27  
           28 +#include "autocomplete.h"
           29 +
           30  /*
           31   * Internal mouse shortcuts.
           32   * Beware that overloading Button1 will disable the selection.
           33 @@ -187,6 +189,8 @@
           34  #define MODKEY Mod1Mask
           35  #define TERMMOD (ControlMask|ShiftMask)
           36  
           37 +#define ACMPL_MOD ControlMask|Mod1Mask
           38 +
           39  static Shortcut shortcuts[] = {
           40          /* mask                 keysym          function        argument */
           41          { XK_ANY_MOD,           XK_Break,       sendbreak,      {.i =  0} },
           42 @@ -201,6 +205,14 @@
           43          { TERMMOD,              XK_Y,           selpaste,       {.i =  0} },
           44          { ShiftMask,            XK_Insert,      selpaste,       {.i =  0} },
           45          { TERMMOD,              XK_Num_Lock,    numlock,        {.i =  0} },
           46 +        { ACMPL_MOD,            XK_slash,       autocomplete,   { .i = ACMPL_WORD        } },
           47 +        { ACMPL_MOD,            XK_period,      autocomplete,   { .i = ACMPL_FUZZY_WORD  } },
           48 +        { ACMPL_MOD,            XK_comma,       autocomplete,   { .i = ACMPL_FUZZY       } },
           49 +        { ACMPL_MOD,            XK_apostrophe,  autocomplete,   { .i = ACMPL_SUFFIX      } },
           50 +        { ACMPL_MOD,            XK_semicolon,   autocomplete,   { .i = ACMPL_SURROUND    } },
           51 +        { ACMPL_MOD,            XK_bracketright,autocomplete,   { .i = ACMPL_WWORD       } },
           52 +        { ACMPL_MOD,            XK_bracketleft, autocomplete,   { .i = ACMPL_FUZZY_WWORD } },
           53 +        { ACMPL_MOD,            XK_equal,       autocomplete,   { .i = ACMPL_UNDO        } },
           54  };
           55  
           56  /*
           57 diff -uraN st-0.8.5/Makefile st-autocomplete/Makefile
           58 --- st-0.8.5/Makefile        2022-03-13 02:45:34.586842452 +0400
           59 +++ st-autocomplete/Makefile        2022-03-13 02:45:34.586842452 +0400
           60 @@ -44,6 +44,8 @@
           61          mkdir -p $(DESTDIR)$(PREFIX)/bin
           62          cp -f st $(DESTDIR)$(PREFIX)/bin
           63          chmod 755 $(DESTDIR)$(PREFIX)/bin/st
           64 +        cp -f st-autocomplete $(DESTDIR)$(PREFIX)/bin
           65 +        chmod 755 $(DESTDIR)$(PREFIX)/bin/st-autocomplete
           66          mkdir -p $(DESTDIR)$(MANPREFIX)/man1
           67          sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1
           68          chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1
           69 @@ -52,6 +54,7 @@
           70  
           71  uninstall:
           72          rm -f $(DESTDIR)$(PREFIX)/bin/st
           73 +        rm -f $(DESTDIR)$(PREFIX)/bin/st-autocomplete
           74          rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1
           75  
           76  .PHONY: all options clean dist install uninstall
           77 diff -uraN st-0.8.5/st-autocomplete st-autocomplete/st-autocomplete
           78 --- st-0.8.5/st-autocomplete        1970-01-01 04:00:00.000000000 +0400
           79 +++ st-autocomplete/st-autocomplete        2022-03-27 22:57:29.018288223 +0400
           80 @@ -0,0 +1,310 @@
           81 +#!/usr/bin/perl
           82 +#########################################################################
           83 +# Copyright (C) 2012-2017  Wojciech Siewierski                          #
           84 +#                                                                       #
           85 +# This program is free software: you can redistribute it and/or modify  #
           86 +# it under the terms of the GNU General Public License as published by  #
           87 +# the Free Software Foundation, either version 3 of the License, or     #
           88 +# (at your option) any later version.                                   #
           89 +#                                                                       #
           90 +# This program is distributed in the hope that it will be useful,       #
           91 +# but WITHOUT ANY WARRANTY; without even the implied warranty of        #
           92 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
           93 +# GNU General Public License for more details.                          #
           94 +#                                                                       #
           95 +# You should have received a copy of the GNU General Public License     #
           96 +# along with this program.  If not, see <http://www.gnu.org/licenses/>. #
           97 +#########################################################################
           98 +
           99 +my ($cmd, $cursor_row, $cursor_column) = @ARGV;
          100 +
          101 +my $lines = [];
          102 +my $lines1 = [];
          103 +
          104 +my $last_line = -1;
          105 +my $lines_before_cursor = 0;
          106 +
          107 +while (<stdin>)
          108 +{
          109 +        $last_line++;
          110 +
          111 +        s/[^[:print:]]/?/g;
          112 +
          113 +        if ($last_line < $cursor_row)
          114 +        {
          115 +                unshift @{$lines1}, $_;
          116 +                $lines_before_cursor++;
          117 +        }
          118 +        else
          119 +        {
          120 +                unshift @{$lines}, $_;
          121 +        }
          122 +}
          123 +
          124 +foreach (@{$lines1})
          125 +{
          126 +        unshift @{$lines}, $_;
          127 +}
          128 +
          129 +my $cursor_row_in = $cursor_row;
          130 +
          131 +$cursor_row = $last_line;
          132 +
          133 +
          134 +$self = {};
          135 +
          136 +# A reference to a function that transforms the completed word
          137 +# into a regex matching the completions. Usually generated by
          138 +# generate_matcher().
          139 +#
          140 +# For example
          141 +#   $fun = generate_matcher(".*");
          142 +#   $fun->("foo");
          143 +# would return "f.*o.*o"
          144 +#
          145 +# In other words, indirectly decides which characters can
          146 +# appear in the completion.
          147 +my $matcher;
          148 +
          149 +# A regular expression matching a character before each match.
          150 +# For example, it you want to match the text after a
          151 +# whitespace, set it to "\s".
          152 +my $char_class_before;
          153 +
          154 +# A regular expression matching every character in the entered
          155 +# text that will be used to find matching completions. Usually
          156 +# "\w" or similar.
          157 +my $char_class_to_complete;
          158 +
          159 +# A regular expression matching every allowed last character
          160 +# of the completion (uses greedy matching).
          161 +my $char_class_at_end;
          162 +
          163 +if ($cmd eq 'word-complete') {
          164 +        # Basic word completion. Completes the current word
          165 +        # without any special matching.
          166 +        $char_class_before      = '[^-\w]';
          167 +        $matcher                = sub { quotemeta shift }; # identity
          168 +        $char_class_at_end      = '[-\w]';
          169 +        $char_class_to_complete = '[-\w]';
          170 +} elsif ($cmd eq 'WORD-complete') {
          171 +        # The same as above but in the Vim meaning of a "WORD" --
          172 +        # whitespace delimited.
          173 +        $char_class_before      = '\s';
          174 +        $matcher                = sub { quotemeta shift };
          175 +        $char_class_at_end      = '\S';
          176 +        $char_class_to_complete = '\S';
          177 +} elsif ($cmd eq 'fuzzy-word-complete' ||
          178 +                 $cmd eq 'skeleton-word-complete') {
          179 +        # Fuzzy completion of the current word.
          180 +        $char_class_before      = '[^-\w]';
          181 +        $matcher                = generate_matcher('[-\w]*');
          182 +        $char_class_at_end      = '[-\w]';
          183 +        $char_class_to_complete = '[-\w]';
          184 +} elsif ($cmd eq 'fuzzy-WORD-complete') {
          185 +        # Fuzzy completion of the current WORD.
          186 +        $char_class_before      = '\s';
          187 +        $matcher                = generate_matcher('\S*');
          188 +        $char_class_at_end      = '\S';
          189 +        $char_class_to_complete = '\S';
          190 +} elsif ($cmd eq 'fuzzy-complete' ||
          191 +                 $cmd eq 'skeleton-complete') {
          192 +        # Fuzzy completion of an arbitrary text.
          193 +        $char_class_before      = '\W';
          194 +        $matcher                = generate_matcher('.*?');
          195 +        $char_class_at_end      = '\w';
          196 +        $char_class_to_complete = '\S';
          197 +} elsif ($cmd eq 'suffix-complete') {
          198 +        # Fuzzy completion of an completing suffixes, like
          199 +        # completing test=hello from /blah/hello.
          200 +        $char_class_before      = '\S';
          201 +        $matcher                = generate_matcher('\S*');
          202 +        $char_class_at_end      = '\S';
          203 +        $char_class_to_complete = '\S';
          204 +} elsif ($cmd eq 'surround-complete') {
          205 +        # Completing contents of quotes and braces.
          206 +
          207 +        # Here we are using three named groups: s, b, p for quotes, braces
          208 +        # and parenthesis.
          209 +        $char_class_before      = '((?<q>["\'`])|(?<b>\[)|(?<p>\())';
          210 +
          211 +        $matcher                = generate_matcher('.*?');
          212 +
          213 +        # Here we match text till enclosing pair, using perl conditionals in
          214 +        # regexps (?(condition)yes-expression|no-expression).
          215 +        # \0 is used to hack concatenation with '*' later in the code.
          216 +        $char_class_at_end      = '.*?(.(?=(?(<b>)\]|((?(<p>)\)|\g{q})))))\0';
          217 +        $char_class_to_complete = '\S';
          218 +}
          219 +
          220 +
          221 +# use the last used word or read the word behind the cursor
          222 +my $word_to_complete = read_word_at_coord($self, $cursor_row, $cursor_column,
          223 +                                                                                  $char_class_to_complete);
          224 +
          225 +print stdout "$word_to_complete\n";
          226 +
          227 +if ($word_to_complete) {
          228 +        while (1) {
          229 +                # ignore the completed word itself
          230 +                $self->{already_completed}{$word_to_complete} = 1;
          231 +
          232 +                # continue the last search or start from the current row
          233 +                my $completion = find_match($self,
          234 +                                                                        $word_to_complete,
          235 +                                                                        $self->{next_row} // $cursor_row,
          236 +                                                                        $matcher->($word_to_complete),
          237 +                                                                        $char_class_before,
          238 +                                                                        $char_class_at_end);
          239 +                if ($completion) {
          240 +                        print stdout $completion."\n".join ("\n", @{$self->{highlight}})."\n";
          241 +                }
          242 +                else {
          243 +                        last;
          244 +                }
          245 +        }
          246 +}
          247 +
          248 +######################################################################
          249 +
          250 +sub highlight_match {
          251 +    my ($self, $linenum, $completion) = @_;
          252 +
          253 +    # clear_highlight($self);
          254 +
          255 +    my $line = @{$lines}[$linenum];
          256 +    my $re = quotemeta $completion;
          257 +
          258 +    $line =~ /$re/;
          259 +
          260 +    my $beg = $-[0];
          261 +    my $end = $+[0];
          262 +
          263 +        if ($linenum >= $lines_before_cursor)
          264 +        {
          265 +                $lline = $last_line - $lines_before_cursor;
          266 +                $linenum -= $lines_before_cursor;
          267 +                $linenum = $lline - $linenum;
          268 +                $linenum += $lines_before_cursor;
          269 +        }
          270 +
          271 +
          272 +    $self->{highlight} = [$linenum, $beg, $end];
          273 +}
          274 +
          275 +######################################################################
          276 +
          277 +sub read_word_at_coord {
          278 +    my ($self, $row, $col, $char_class) = @_;
          279 +
          280 +    $_ = substr(@{$lines} [$row], 0, $col); # get the current line up to the cursor...
          281 +    s/.*?($char_class*)$/$1/;               # ...and read the last word from it
          282 +    return $_;
          283 +}
          284 +
          285 +######################################################################
          286 +
          287 +# Returns a function that takes a string and returns that string with
          288 +# this function's argument inserted between its every two characters.
          289 +# The resulting string is used as a regular expression matching the
          290 +# completion candidates.
          291 +sub generate_matcher {
          292 +    my $regex_between = shift;
          293 +
          294 +    sub {
          295 +        $_ = shift;
          296 +
          297 +        # sorry for this lispy code, I couldn't resist ;)
          298 +        (join "$regex_between",
          299 +         (map quotemeta,
          300 +          (split //)))
          301 +    }
          302 +}
          303 +
          304 +######################################################################
          305 +
          306 +# Checks whether the completion found by find_match() was already
          307 +# found and if it was, calls find_match() again to find the next
          308 +# completion.
          309 +#
          310 +# Takes all the arguments that find_match() would take, to make a
          311 +# mutually recursive call.
          312 +sub skip_duplicates {
          313 +    my ($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_;
          314 +    my $completion;
          315 +
          316 +        if ($current_row <= $lines_before_cursor)
          317 +        {
          318 +                $completion = shift @{$self->{matches_in_row}}; # get the leftmost one
          319 +        }
          320 +        else
          321 +        {
          322 +                $completion = pop @{$self->{matches_in_row}}; # get the leftmost one
          323 +        }
          324 +
          325 +    # check for duplicates
          326 +    if (exists $self->{already_completed}{$completion}) {
          327 +        # skip this completion
          328 +        return find_match(@_);
          329 +    } else {
          330 +        $self->{already_completed}{$completion} = 1;
          331 +
          332 +                highlight_match($self,
          333 +                                                $self->{next_row}+1,
          334 +                                                $completion);
          335 +
          336 +        return $completion;
          337 +    }
          338 +}
          339 +
          340 +######################################################################
          341 +
          342 +# Finds the next matching completion in the row current row or above
          343 +# while skipping duplicates using skip_duplicates().
          344 +sub find_match {
          345 +    my ($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_;
          346 +    $self->{matches_in_row} //= [];
          347 +
          348 +    # cycle through all the matches in the current row if not starting a new search
          349 +    if (@{$self->{matches_in_row}}) {
          350 +        return skip_duplicates($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end);
          351 +    }
          352 +
          353 +
          354 +    my $i;
          355 +    # search through all the rows starting with current one or one above the last checked
          356 +    for ($i = $current_row; $i >= 0; --$i) {
          357 +        my $line = @{$lines}[$i];   # get the line of text from the row
          358 +
          359 +        # if ($i == $cursor_row) {
          360 +        #     $line = substr $line, 0, $cursor_column;
          361 +        # }
          362 +
          363 +        $_ = $line;
          364 +
          365 +        # find all the matches in the current line
          366 +        my $match;
          367 +        push @{$self->{matches_in_row}}, $+{match} while ($_, $match) = /
          368 +                                                                         (.*${char_class_before})
          369 +                                                                         (?<match>
          370 +                                                                             ${regexp}
          371 +                                                                             ${char_class_at_end}*
          372 +                                                                         )
          373 +                                                                     /ix;
          374 +        # corner case: match at the very beginning of line
          375 +        push @{$self->{matches_in_row}}, $+{match} if $line =~ /^(${char_class_before}){0}(?<match>$regexp$char_class_at_end*)/i;
          376 +
          377 +        if (@{$self->{matches_in_row}}) {
          378 +            # remember which row should be searched next
          379 +            $self->{next_row} = --$i;
          380 +
          381 +            # arguments needed for find_match() mutual recursion
          382 +            return skip_duplicates($self, $word_to_match, $i, $regexp, $char_class_before, $char_class_at_end);
          383 +        }
          384 +    }
          385 +
          386 +    # # no more possible completions, revert to the original word
          387 +    # undo_completion($self) if $i < 0;
          388 +
          389 +    return undef;
          390 +}
          391 diff -uraN st-0.8.5/st.c st-autocomplete/st.c
          392 --- st-0.8.5/st.c        2022-03-13 02:45:34.586842452 +0400
          393 +++ st-autocomplete/st.c        2022-03-27 22:28:39.041693478 +0400
          394 @@ -17,6 +17,7 @@
          395  #include <unistd.h>
          396  #include <wchar.h>
          397  
          398 +#include "autocomplete.h"
          399  #include "st.h"
          400  #include "win.h"
          401  
          402 @@ -2569,6 +2570,8 @@
          403                  return;
          404          }
          405  
          406 +        autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
          407 +
          408          /*
          409           * slide screen to keep cursor where we expect it -
          410           * tscrollup would work here, but we can optimize to
          411 @@ -2688,3 +2691,256 @@
          412          tfulldirt();
          413          draw();
          414  }
          415 +
          416 +void autocomplete (const Arg * arg)
          417 +{
          418 +        static _Bool active = 0;
          419 +
          420 +        int acmpl_cmdindex = arg -> i;
          421 +
          422 +        static int acmpl_cmdindex_prev;
          423 +
          424 +        if (active == 0)
          425 +                acmpl_cmdindex_prev = acmpl_cmdindex;
          426 +
          427 +        static const char * const (acmpl_cmd []) = {
          428 +                [ACMPL_DEACTIVATE]        = "__DEACTIVATE__",
          429 +                [ACMPL_WORD]                = "word-complete",
          430 +                [ACMPL_WWORD]                = "WORD-complete",
          431 +                [ACMPL_FUZZY_WORD]        = "fuzzy-word-complete",
          432 +                [ACMPL_FUZZY_WWORD]        = "fuzzy-WORD-complete",
          433 +                [ACMPL_FUZZY]                = "fuzzy-complete",
          434 +                [ACMPL_SUFFIX]                = "suffix-complete",
          435 +                [ACMPL_SURROUND]        = "surround-complete",
          436 +                [ACMPL_UNDO]                = "__UNDO__",
          437 +        };
          438 +
          439 +        static char acmpl [1000];                // ACMPL_ISSUE: why 1000?
          440 +
          441 +        static FILE * acmpl_exec = NULL;
          442 +        static int acmpl_status;
          443 +
          444 +        static const char * stbuffile;
          445 +        static char target [1000];                // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col
          446 +        static size_t targetlen;
          447 +
          448 +        static char completion [1000] = {0};                // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col
          449 +        static size_t complen_prev = 0;                // NOTE: always clear this variable after clearing completion
          450 +
          451 +        static int cx, cy;
          452 +
          453 +        // ACMPL_ISSUE: crashes when term.row is too small
          454 +
          455 +// Check for deactivation
          456 +
          457 +        if (acmpl_cmdindex == ACMPL_DEACTIVATE)
          458 +        {
          459 +
          460 +//     Deactivate autocomplete mode keeping current completion
          461 +
          462 +                if (active)
          463 +                {
          464 +                        active = 0;
          465 +                        pclose (acmpl_exec);
          466 +                        remove (stbuffile);
          467 +
          468 +                        if (complen_prev)
          469 +                        {
          470 +                                selclear ();
          471 +                                complen_prev = 0;
          472 +                        }
          473 +                }
          474 +
          475 +                return;
          476 +        }
          477 +
          478 +// Check for undo
          479 +
          480 +        if (acmpl_cmdindex == ACMPL_UNDO)
          481 +        {
          482 +
          483 +//     Deactivate autocomplete mode recovering target
          484 +
          485 +                if (active)
          486 +                {
          487 +                        active = 0;
          488 +                        pclose (acmpl_exec);
          489 +                        remove (stbuffile);
          490 +
          491 +                        if (complen_prev)
          492 +                        {
          493 +                                selclear ();
          494 +                                for (size_t i = 0; i < complen_prev; i++)
          495 +                                        ttywrite ((char []) { '\b' }, 1, 1);        // ACMPL_ISSUE: I'm not sure that this is the right way
          496 +                                complen_prev = 0;
          497 +                                ttywrite (target, targetlen, 0);                // ACMPL_ISSUE: I'm not sure that this is a right solution
          498 +                        }
          499 +                }
          500 +
          501 +                return;
          502 +        }
          503 +
          504 +// Check for command change
          505 +
          506 +        if (acmpl_cmdindex != acmpl_cmdindex_prev)
          507 +        {
          508 +
          509 +//     If command is changed, goto acmpl_begin avoiding rewriting st buffer
          510 +
          511 +                if (active)
          512 +                {
          513 +                        acmpl_cmdindex_prev = acmpl_cmdindex;
          514 +
          515 +                        goto acmpl_begin;
          516 +                }
          517 +        }
          518 +
          519 +// If not active
          520 +
          521 +        if (active == 0)
          522 +        {
          523 +                acmpl_cmdindex_prev = acmpl_cmdindex;
          524 +                cx = term.c.x;
          525 +                cy = term.c.y;
          526 +
          527 +//     Write st buffer to a temp file
          528 +
          529 +                stbuffile = tmpnam (NULL);                // ACMPL_ISSUE: check for return value ...
          530 +                                                                                // ACMPL_ISSUE: use coprocesses instead of temp files
          531 +
          532 +                FILE * stbuf = fopen (stbuffile, "w"); // ACMPL_ISSUE: check for opening error ...
          533 +                char * stbufline = malloc (term.col + 2); // ACMPL_ISSUE: check for allocating error ...
          534 +
          535 +                int cxp = 0;
          536 +
          537 +                for (size_t y = 0; y < term.row; y++)
          538 +                {
          539 +                        if (y == term.c.y) cx += cxp * term.col;
          540 +
          541 +                        size_t x = 0;
          542 +                        for (; x < term.col; x++)
          543 +                                utf8encode (term.line [y] [x].u, stbufline + x);
          544 +                        if (term.line [y] [x - 1].mode & ATTR_WRAP)
          545 +                        {
          546 +                                x--;
          547 +                                if (y <= term.c.y) cy--;
          548 +                                cxp++;
          549 +                        }
          550 +                        else
          551 +                        {
          552 +                                stbufline [x] = '\n';
          553 +                                cxp = 0;
          554 +                        }
          555 +                        stbufline [x + 1] = 0;
          556 +                        fputs (stbufline, stbuf);
          557 +                }
          558 +
          559 +                free (stbufline);
          560 +                fclose (stbuf);
          561 +
          562 +acmpl_begin:
          563 +
          564 +//     Run st-autocomplete
          565 +
          566 +                sprintf (
          567 +                        acmpl,
          568 +                        "cat %100s | st-autocomplete %500s %d %d",        // ACMPL_ISSUE: why 100 and 500?
          569 +                        stbuffile,
          570 +                        acmpl_cmd [acmpl_cmdindex],
          571 +                        cy,
          572 +                        cx
          573 +                );
          574 +
          575 +                acmpl_exec = popen (acmpl, "r");                // ACMPL_ISSUE: popen isn't defined by The Standard. Does it work in BSDs for example?
          576 +                                                                                                // ACMPL_ISSUE: check for popen error ...
          577 +
          578 +//     Read the target, targetlen
          579 +
          580 +                fscanf (acmpl_exec, "%500s\n", target); // ACMPL_ISSUE: check for scanning error ...
          581 +                targetlen = strlen (target);
          582 +        }
          583 +
          584 +// Read a completion if exists (acmpl_status)
          585 +
          586 +        unsigned line, beg, end;
          587 +
          588 +        acmpl_status = fscanf (acmpl_exec, "%500[^\n]\n%u\n%u\n%u\n", completion, & line, & beg, & end);
          589 +                                                                                                // ACMPL_ISSUE: why 500? use term.col instead
          590 +
          591 +// Exit if no completions found
          592 +
          593 +        if (active == 0 && acmpl_status == EOF)
          594 +        {
          595 +
          596 +//    Close st-autocomplete and exit without activating the autocomplete mode
          597 +
          598 +                pclose (acmpl_exec);
          599 +                remove (stbuffile);
          600 +                return;
          601 +        }
          602 +
          603 +// If completions found, enable autocomplete mode and autocomplete the target
          604 +
          605 +        active = 1;
          606 +
          607 +// Clear target before first completion
          608 +
          609 +        if (complen_prev == 0)
          610 +        {
          611 +                for (size_t i = 0; i < targetlen; i++)
          612 +                        ttywrite ((char []) { '\b' }, 1, 1);        // ACMPL_ISSUE: I'm not sure that this is a right solution
          613 +        }
          614 +
          615 +// Clear previuos completion if this is not the first
          616 +
          617 +        else
          618 +        {
          619 +                selclear ();
          620 +                for (size_t i = 0; i < complen_prev; i++)
          621 +                        ttywrite ((char []) { '\b' }, 1, 1);        // ACMPL_ISSUE: I'm not sure that this is a right solution
          622 +                complen_prev = 0;
          623 +        }
          624 +
          625 +// If no more completions found, reset and restart
          626 +
          627 +        if (acmpl_status == EOF)
          628 +        {
          629 +                active = 0;
          630 +                pclose (acmpl_exec);
          631 +                ttywrite (target, targetlen, 0);
          632 +                goto acmpl_begin;
          633 +        }
          634 +
          635 +// Count wrapped lines before the current line
          636 +
          637 +        int wl = 0;
          638 +
          639 +        int tl = line;
          640 +
          641 +        for (int l = 0; l < tl; l++)
          642 +                if (term.line [l] [term.col - 1].mode & ATTR_WRAP)
          643 +                {
          644 +                        wl++;
          645 +                        tl++;
          646 +                }
          647 +
          648 +// Autcomplete
          649 +
          650 +        complen_prev = strlen (completion);
          651 +        ttywrite (completion, complen_prev, 0);
          652 +
          653 +        if (line == cy && beg > cx)
          654 +        {
          655 +                beg += complen_prev - targetlen;
          656 +                end += complen_prev - targetlen;
          657 +
          658 +                // ACMPL_ISSUE: highlignthing doesn't work when "line == cy && beg > cx",
          659 +                //                                but coordinates are correct...
          660 +        }
          661 +
          662 +        end--;
          663 +
          664 +        selstart (beg % term.col, line + wl + beg / term.col, 0);
          665 +        selextend (end % term.col, line + wl + end / term.col, 1, 0);
          666 +        xsetsel (getsel ());
          667 +}
          668 diff -uraN st-0.8.5/st.h st-autocomplete/st.h
          669 --- st-0.8.5/st.h        2022-03-13 02:45:34.586842452 +0400
          670 +++ st-autocomplete/st.h        2022-03-13 02:45:34.586842452 +0400
          671 @@ -77,6 +77,8 @@
          672          const char *s;
          673  } Arg;
          674  
          675 +void autocomplete (const Arg *);
          676 +
          677  void die(const char *, ...);
          678  void redraw(void);
          679  void draw(void);
          680 diff -uraN st-0.8.5/x.c st-autocomplete/x.c
          681 --- st-0.8.5/x.c        2022-03-13 02:45:34.586842452 +0400
          682 +++ st-autocomplete/x.c        2022-03-13 02:45:34.590175835 +0400
          683 @@ -1834,11 +1834,20 @@
          684          /* 1. shortcuts */
          685          for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
          686                  if (ksym == bp->keysym && match(bp->mod, e->state)) {
          687 +                        if (bp -> func != autocomplete)
          688 +                                autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
          689                          bp->func(&(bp->arg));
          690                          return;
          691                  }
          692          }
          693  
          694 +        if (!(
          695 +                len == 0 &&
          696 +                e -> state & ~ignoremod                // ACMPL_ISSUE: I'm not sure that this is the right way
          697 +                                | ACMPL_MOD == ACMPL_MOD
          698 +        ))
          699 +                autocomplete ((const Arg []) { ACMPL_DEACTIVATE });
          700 +
          701          /* 2. custom keys from config.h */
          702          if ((customkey = kmap(ksym, e->state))) {
          703                  ttywrite(customkey, strlen(customkey), 1);