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