st-autocomplete-20240703-6508693.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
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