st-0.8.5-autocomplete-20220313-024831-testrelease.diff (20634B)
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-13 02:45:53.503785745 +0400 80 @@ -0,0 +1,302 @@ 81 +#!/usr/bin/perl 82 +######################################################################### 83 +# Copyright (C) 2012-2021 Wojciech Siewierski, Gaspar Vardanyan # 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 +# A reference to a function that transforms the completed word 102 +# into a regex matching the completions. Usually generated by 103 +# generate_matcher(). 104 +# 105 +# For example 106 +# $fun = generate_matcher(".*"); 107 +# $fun->("foo"); 108 +# would return "f.*o.*o" 109 +# 110 +# In other words, indirectly decides which characters can 111 +# appear in the completion. 112 +my $matcher; 113 + 114 +# A regular expression matching a character before each match. 115 +# For example, it you want to match the text after a 116 +# whitespace, set it to "\s". 117 +my $char_class_before; 118 + 119 +# A regular expression matching every character in the entered 120 +# text that will be used to find matching completions. Usually 121 +# "\w" or similar. 122 +my $char_class_to_complete; 123 + 124 +# A regular expression matching every allowed last character 125 +# of the completion (uses greedy matching). 126 +my $char_class_at_end; 127 + 128 +if ($cmd eq 'word-complete') { 129 + # Basic word completion. Completes the current word 130 + # without any special matching. 131 + $char_class_before = '[^-\w]'; 132 + $matcher = sub { quotemeta shift }; # identity 133 + $char_class_at_end = '[-\w]'; 134 + $char_class_to_complete = '[-\w]'; 135 +} elsif ($cmd eq 'WORD-complete') { 136 + # The same as above but in the Vim meaning of a "WORD" -- 137 + # whitespace delimited. 138 + $char_class_before = '\s'; 139 + $matcher = sub { quotemeta shift }; 140 + $char_class_at_end = '\S'; 141 + $char_class_to_complete = '\S'; 142 +} elsif ($cmd eq 'fuzzy-word-complete' || 143 + $cmd eq 'skeleton-word-complete') { 144 + # Fuzzy completion of the current word. 145 + $char_class_before = '[^-\w]'; 146 + $matcher = generate_matcher('[-\w]*'); 147 + $char_class_at_end = '[-\w]'; 148 + $char_class_to_complete = '[-\w]'; 149 +} elsif ($cmd eq 'fuzzy-WORD-complete') { 150 + # Fuzzy completion of the current WORD. 151 + $char_class_before = '\s'; 152 + $matcher = generate_matcher('\S*'); 153 + $char_class_at_end = '\S'; 154 + $char_class_to_complete = '\S'; 155 +} elsif ($cmd eq 'fuzzy-complete' || 156 + $cmd eq 'skeleton-complete') { 157 + # Fuzzy completion of an arbitrary text. 158 + $char_class_before = '\W'; 159 + $matcher = generate_matcher('.*?'); 160 + $char_class_at_end = '\w'; 161 + $char_class_to_complete = '\S'; 162 +} elsif ($cmd eq 'suffix-complete') { 163 + # Fuzzy completion of an completing suffixes, like 164 + # completing test=hello from /blah/hello. 165 + $char_class_before = '\S'; 166 + $matcher = generate_matcher('\S*'); 167 + $char_class_at_end = '\S'; 168 + $char_class_to_complete = '\S'; 169 +} elsif ($cmd eq 'surround-complete') { 170 + # Completing contents of quotes and braces. 171 + 172 + # Here we are using three named groups: s, b, p for quotes, braces 173 + # and parenthesis. 174 + $char_class_before = '((?<q>["\'`])|(?<b>\[)|(?<p>\())'; 175 + 176 + $matcher = generate_matcher('.*?'); 177 + 178 + # Here we match text till enclosing pair, using perl conditionals in 179 + # regexps (?(condition)yes-expression|no-expression). 180 + # \0 is used to hack concatenation with '*' later in the code. 181 + $char_class_at_end = '.*?(.(?=(?(<b>)\]|((?(<p>)\)|\g{q})))))\0'; 182 + $char_class_to_complete = '\S'; 183 +} 184 + 185 +my $lines = []; 186 + 187 +my $last_line = -1; 188 +my $lines_after_cursor = 0; 189 + 190 +while (<STDIN>) 191 +{ 192 + $last_line++; 193 + 194 + if ($last_line <= $cursor_row) 195 + { 196 + push @{$lines}, $_; 197 + } 198 + else 199 + { 200 + unshift @{$lines}, $_; 201 + $lines_after_cursor++; 202 + } 203 +} 204 + 205 +$cursor_row = $last_line; 206 + 207 +# read the word behind the cursor 208 +$_ = substr(@{$lines} [$cursor_row], 0, $cursor_column); # get the current line up to the cursor... 209 +s/.*?($char_class_to_complete*)$/$1/; # ...and read the last word from it 210 +my $word_to_complete = $_; 211 + 212 +# ignore the completed word itself 213 +$self->{already_completed}{$word_to_complete} = 1; 214 + 215 +print stdout "$word_to_complete\n"; 216 + 217 +# search for matches 218 +if ($word_to_complete) 219 +{ 220 + while (my $completion = find_match($self, 221 + $word_to_complete, 222 + $self->{next_row} // $cursor_row, 223 + $matcher->($word_to_complete), 224 + $char_class_before, 225 + $char_class_at_end) 226 + ) { 227 + calc_match_coords($self, 228 + $self->{next_row}+1, 229 + $completion); 230 + print stdout "$completion @{$self->{highlight}}\n"; 231 + } 232 + 233 + leave($self); 234 +} 235 + 236 + 237 + 238 +###################################################################### 239 + 240 +# Finds the next matching completion in the row current row or above 241 +# while skipping duplicates using skip_duplicates(). 242 +sub find_match { 243 + my ($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_; 244 + $self->{matches_in_row} //= []; 245 + 246 + # cycle through all the matches in the current row if not starting a new search 247 + if (@{$self->{matches_in_row}}) { 248 + return skip_duplicates($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end); 249 + } 250 + 251 + 252 + my $i; 253 + # search through all the rows starting with current one or one above the last checked 254 + for ($i = $current_row; $i >= 0; --$i) { 255 + my $line = @{$lines} [$i]; # get the line of text from the row 256 + 257 + if ($i == $cursor_row) { 258 + $line = substr $line, 0, $cursor_column; 259 + } 260 + 261 + $_ = $line; 262 + 263 + # find all the matches in the current line 264 + my $match; 265 + push @{$self->{matches_in_row}}, $+{match} while ($_, $match) = / 266 + (.*${char_class_before}) 267 + (?<match> 268 + ${regexp} 269 + ${char_class_at_end}* 270 + ) 271 + /ix; 272 + # corner case: match at the very beginning of line 273 + push @{$self->{matches_in_row}}, $+{match} if $line =~ /^(${char_class_before}){0}(?<match>$regexp$char_class_at_end*)/i; 274 + 275 + if (@{$self->{matches_in_row}}) { 276 + # remember which row should be searched next 277 + $self->{next_row} = --$i; 278 + 279 + # arguments needed for find_match() mutual recursion 280 + return skip_duplicates($self, $word_to_match, $i, $regexp, $char_class_before, $char_class_at_end); 281 + } 282 + } 283 + 284 + # no more possible completions, revert to the original word 285 + $self->{next_row} = -1 if $i < 0; 286 + 287 + return undef; 288 +} 289 + 290 +###################################################################### 291 + 292 +# Checks whether the completion found by find_match() was already 293 +# found and if it was, calls find_match() again to find the next 294 +# completion. 295 +# 296 +# Takes all the arguments that find_match() would take, to make a 297 +# mutually recursive call. 298 +sub skip_duplicates { 299 + my $self = $_[0]; 300 + my $current_row = $_[2]; 301 + my $completion; 302 + if ($current_row >= $lines_after_cursor) 303 + { 304 + $completion = shift @{$self->{matches_in_row}}; # get the rightmost one 305 + } 306 + else 307 + { 308 + $completion = pop @{$self->{matches_in_row}}; # get the rightmost one 309 + } 310 + 311 + # check for duplicates 312 + if (exists $self->{already_completed}{$completion}) { 313 + # skip this completion 314 + return find_match(@_); 315 + } else { 316 + $self->{already_completed}{$completion} = 1; 317 + return $completion; 318 + } 319 +} 320 + 321 +###################################################################### 322 + 323 +# Returns a function that takes a string and returns that string with 324 +# this function's argument inserted between its every two characters. 325 +# The resulting string is used as a regular expression matching the 326 +# completion candidates. 327 +sub generate_matcher { 328 + my $regex_between = shift; 329 + 330 + sub { 331 + $_ = shift; 332 + 333 + # sorry for this lispy code, I couldn't resist ;) 334 + (join "$regex_between", 335 + (map quotemeta, 336 + (split //))) 337 + } 338 +} 339 + 340 +###################################################################### 341 + 342 +sub calc_match_coords { 343 + my ($self, $linenum, $completion) = @_; 344 + 345 + my $line = @{$lines} [$linenum]; 346 + my $re = quotemeta $completion; 347 + 348 + $line =~ /$re/; 349 + 350 + #my ($beg_row, $beg_col) = $line->coord_of($-[0]); 351 + #my ($end_row, $end_col) = $line->coord_of($+[0]); 352 + my $beg = $-[0]; 353 + my $end = $+[0]; 354 + 355 + if (exists $self->{highlight}) { 356 + delete $self->{highlight}; 357 + } 358 + # () # TODO: what does () do in perl ???? 359 + 360 + if ($linenum >= $lines_after_cursor) 361 + { 362 + $linenum -= $lines_after_cursor; 363 + } 364 + else 365 + { 366 + $linenum = $last_line - $linenum; 367 + } 368 + 369 + # $self->{highlight} = [$beg_row, $beg_col, $end_row, $end_col]; 370 + $self->{highlight} = [$linenum, $beg, $end]; 371 +} 372 + 373 +###################################################################### 374 + 375 +sub leave { 376 + my ($self) = @_; 377 + 378 + delete $self->{next_row}; 379 + delete $self->{matches_in_row}; 380 + delete $self->{already_completed}; 381 + delete $self->{highlight}; 382 +} 383 diff -uraN st-0.8.5/st.c st-autocomplete/st.c 384 --- st-0.8.5/st.c 2022-03-13 02:45:34.586842452 +0400 385 +++ st-autocomplete/st.c 2022-03-13 02:45:34.586842452 +0400 386 @@ -17,6 +17,7 @@ 387 #include <unistd.h> 388 #include <wchar.h> 389 390 +#include "autocomplete.h" 391 #include "st.h" 392 #include "win.h" 393 394 @@ -2569,6 +2570,8 @@ 395 return; 396 } 397 398 + autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); 399 + 400 /* 401 * slide screen to keep cursor where we expect it - 402 * tscrollup would work here, but we can optimize to 403 @@ -2688,3 +2691,241 @@ 404 tfulldirt(); 405 draw(); 406 } 407 + 408 +void autocomplete (const Arg * arg) 409 +{ 410 + static _Bool active = 0; 411 + 412 + int acmpl_cmdindex = arg -> i; 413 + 414 + static int acmpl_cmdindex_prev; 415 + 416 + if (active == 0) 417 + acmpl_cmdindex_prev = acmpl_cmdindex; 418 + 419 + static const char * const (acmpl_cmd []) = { 420 + [ACMPL_DEACTIVATE] = "__DEACTIVATE__", 421 + [ACMPL_WORD] = "word-complete", 422 + [ACMPL_WWORD] = "WORD-complete", 423 + [ACMPL_FUZZY_WORD] = "fuzzy-word-complete", 424 + [ACMPL_FUZZY_WWORD] = "fuzzy-WORD-complete", 425 + [ACMPL_FUZZY] = "fuzzy-complete", 426 + [ACMPL_SUFFIX] = "suffix-complete", 427 + [ACMPL_SURROUND] = "surround-complete", 428 + [ACMPL_UNDO] = "__UNDO__", 429 + }; 430 + 431 + static char acmpl [1000]; // ACMPL_ISSUE: why 1000? 432 + 433 + static FILE * acmpl_exec = NULL; 434 + static int acmpl_status; 435 + 436 + static const char * stbuffile; 437 + static char target [1000]; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col 438 + static size_t targetlen; 439 + 440 + static char completion [1000] = {0}; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col 441 + static size_t complen_prev = 0; // NOTE: always clear this variable after clearing completion 442 + 443 + static int cx, cy; 444 + 445 + // ACMPL_ISSUE: crashes when term.row is too small 446 + 447 +// Check for deactivation 448 + 449 + if (acmpl_cmdindex == ACMPL_DEACTIVATE) 450 + { 451 + 452 +// Deactivate autocomplete mode keeping current completion 453 + 454 + if (active) 455 + { 456 + active = 0; 457 + pclose (acmpl_exec); 458 + remove (stbuffile); 459 + 460 + if (complen_prev) 461 + { 462 + selclear (); 463 + complen_prev = 0; 464 + } 465 + } 466 + 467 + return; 468 + } 469 + 470 +// Check for undo 471 + 472 + if (acmpl_cmdindex == ACMPL_UNDO) 473 + { 474 + 475 +// Deactivate autocomplete mode recovering target 476 + 477 + if (active) 478 + { 479 + active = 0; 480 + pclose (acmpl_exec); 481 + remove (stbuffile); 482 + 483 + if (complen_prev) 484 + { 485 + selclear (); 486 + for (size_t i = 0; i < complen_prev; i++) 487 + ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is the right way 488 + complen_prev = 0; 489 + ttywrite (target, targetlen, 0); // ACMPL_ISSUE: I'm not sure that this is a right solution 490 + } 491 + } 492 + 493 + return; 494 + } 495 + 496 +// Check for command change 497 + 498 + if (acmpl_cmdindex != acmpl_cmdindex_prev) 499 + { 500 + 501 +// If command is changed, goto acmpl_begin avoiding rewriting st buffer 502 + 503 + if (active) 504 + { 505 + acmpl_cmdindex_prev = acmpl_cmdindex; 506 + 507 + goto acmpl_begin; 508 + } 509 + } 510 + 511 +// If not active 512 + 513 + if (active == 0) 514 + { 515 + acmpl_cmdindex_prev = acmpl_cmdindex; 516 + cx = term.c.x; 517 + cy = term.c.y; 518 + 519 +// Write st buffer to a temp file 520 + 521 + stbuffile = tmpnam (NULL); // ACMPL_ISSUE: check for return value ... 522 + // ACMPL_ISSUE: use coprocesses instead of temp files 523 + 524 + FILE * stbuf = fopen (stbuffile, "w"); // ACMPL_ISSUE: check for opening error ... 525 + char * stbufline = malloc (term.col + 2); // ACMPL_ISSUE: check for allocating error ... 526 + 527 + for (size_t y = 0; y < term.row; y++) 528 + { 529 + size_t x = 0; 530 + for (; x < term.col; x++) 531 + utf8encode (term.line [y] [x].u, stbufline + x); 532 + if (term.line [y] [x - 1].mode & ATTR_WRAP) 533 + { 534 + x--; 535 + if (y <= cy) cy--; 536 + } 537 + else 538 + { 539 + stbufline [x] = '\n'; 540 + } 541 + stbufline [x + 1] = 0; 542 + fputs (stbufline, stbuf); 543 + } 544 + 545 + free (stbufline); 546 + fclose (stbuf); 547 + 548 +acmpl_begin: 549 + 550 +// Run st-autocomplete 551 + 552 + sprintf ( 553 + acmpl, 554 + "cat %100s | st-autocomplete %500s %d %d", // ACMPL_ISSUE: why 100 and 500? 555 + stbuffile, 556 + acmpl_cmd [acmpl_cmdindex], 557 + cy, 558 + cx 559 + ); 560 + 561 + acmpl_exec = popen (acmpl, "r"); // ACMPL_ISSUE: popen isn't defined by The Standard. Does it work in BSDs for example? 562 + // ACMPL_ISSUE: check for popen error ... 563 + 564 +// Read the target, targetlen 565 + 566 + fscanf (acmpl_exec, "%500s\n", target); // ACMPL_ISSUE: check for scanning error ... 567 + targetlen = strlen (target); 568 + } 569 + 570 +// Read a completion if exists (acmpl_status) 571 + 572 + unsigned line, beg, end; 573 + 574 + acmpl_status = fscanf (acmpl_exec, "%500s %u %u %u\n", completion, & line, & beg, & end); 575 + // ACMPL_ISSUE: why 500? use term.col instead 576 + 577 +// Exit if no completions found 578 + 579 + if (active == 0 && acmpl_status == EOF) 580 + { 581 + 582 +// Close st-autocomplete and exit without activating the autocomplete mode 583 + 584 + pclose (acmpl_exec); 585 + remove (stbuffile); 586 + return; 587 + } 588 + 589 +// If completions found, enable autocomplete mode and autocomplete the target 590 + 591 + active = 1; 592 + 593 +// Clear target before first completion 594 + 595 + if (complen_prev == 0) 596 + { 597 + for (size_t i = 0; i < targetlen; i++) 598 + ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution 599 + } 600 + 601 +// Clear previuos completion if this is not the first 602 + 603 + else 604 + { 605 + selclear (); 606 + for (size_t i = 0; i < complen_prev; i++) 607 + ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution 608 + complen_prev = 0; 609 + } 610 + 611 +// If no more completions found, reset and restart 612 + 613 + if (acmpl_status == EOF) 614 + { 615 + active = 0; 616 + pclose (acmpl_exec); 617 + ttywrite (target, targetlen, 0); 618 + goto acmpl_begin; 619 + } 620 + 621 +// Count wrapped lines before the current line 622 + 623 + int wl = 0; 624 + 625 + int tl = line; 626 + 627 + for (int l = 0; l < tl; l++) 628 + if (term.line [l] [term.col - 1].mode & ATTR_WRAP) 629 + { 630 + wl++; 631 + tl++; 632 + } 633 + 634 +// Read the new completion and autcomplete 635 + 636 + end--; 637 + 638 + selstart (beg, line + wl, 0); 639 + selextend (end % term.col, line + wl + end / term.col, 1, 0); 640 + xsetsel (getsel ()); 641 + 642 + complen_prev = strlen (completion); 643 + ttywrite (completion, complen_prev, 0); 644 +} 645 diff -uraN st-0.8.5/st.h st-autocomplete/st.h 646 --- st-0.8.5/st.h 2022-03-13 02:45:34.586842452 +0400 647 +++ st-autocomplete/st.h 2022-03-13 02:45:34.586842452 +0400 648 @@ -77,6 +77,8 @@ 649 const char *s; 650 } Arg; 651 652 +void autocomplete (const Arg *); 653 + 654 void die(const char *, ...); 655 void redraw(void); 656 void draw(void); 657 diff -uraN st-0.8.5/x.c st-autocomplete/x.c 658 --- st-0.8.5/x.c 2022-03-13 02:45:34.586842452 +0400 659 +++ st-autocomplete/x.c 2022-03-13 02:45:34.590175835 +0400 660 @@ -1834,11 +1834,20 @@ 661 /* 1. shortcuts */ 662 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { 663 if (ksym == bp->keysym && match(bp->mod, e->state)) { 664 + if (bp -> func != autocomplete) 665 + autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); 666 bp->func(&(bp->arg)); 667 return; 668 } 669 } 670 671 + if (!( 672 + len == 0 && 673 + e -> state & ~ignoremod // ACMPL_ISSUE: I'm not sure that this is the right way 674 + | ACMPL_MOD == ACMPL_MOD 675 + )) 676 + autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); 677 + 678 /* 2. custom keys from config.h */ 679 if ((customkey = kmap(ksym, e->state))) { 680 ttywrite(customkey, strlen(customkey), 1);