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