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