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);