sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

commit 95044286716bc4a3c598c2eb53e21cd0a51adfe1
parent 7dfaca59a499cd9f8c5f2ababe0418c7c090a20b
Author: GasparVardanyan <gaspar.vardanyan.mailbox@gmail.com>
Date:   Wed, 15 Dec 2021 19:15:10 +0400

[st][patch] autocomplete

Diffstat:
Ast.suckless.org/patches/autocomplete/index.md | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ast.suckless.org/patches/autocomplete/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff | 601+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ast.suckless.org/patches/autocomplete/usage.gif | 0
3 files changed, 688 insertions(+), 0 deletions(-)

diff --git a/st.suckless.org/patches/autocomplete/index.md b/st.suckless.org/patches/autocomplete/index.md @@ -0,0 +1,87 @@ +autocomplete +============ +urxvt's autocomplete-ALL-the-things extension's port for st +The original extension: https://github.com/Vifon/autocomplete-ALL-the-things + +Description +----------- + +Completes the currently typed word using the other words visible in the st window. + +For example the terminal looks like this: + +![Preview](usage.gif) + +It was strongly inspired by `dabbrev` feature in GNU Emacs and later by +[skeleton-complete](https://github.com/baohaojun/skeleton-complete) by Bao +Haojun. + +Basic usage +----------- + +Just press the key bound (in the example above: Alt-Ctrl-slash) to +complete the word at point. Subsequent uses cycle through other +possible completions. + +Advanced usage +-------------- + +For more advanced completions use `aAtt:fuzzy-word-complete` +(Alt-Shift-slash) and `aAtt:skeleton-complete`. It's behavior is inspired by +[skeleton-complete](https://github.com/baohaojun/skeleton-complete), please see +[its readme](http://baohaojun.github.io/skeleton-complete.html) for a more +detailed explanation. + +Available commands +------------------- + +* `ACMPL_WORD` -- classical prefix-based completion for words +* `ACMPL_WWORD` -- completion for WORDS (in the Vim meaning) +* `ACMPL_FUZZY_WORD` -- fuzzy completion for words +* `ACMPL_FUZZY_WWORD` -- fuzzy completion for WORDS +* `ACMPL_FUZZY` -- fuzzy completion for arbitrary strings +* `ACMPL_SUFFIX` -- fuzzy completion for suffixes, disrespecting + prefixes of both completion and given strings +* `ACMPL_SURROUND` -- fuzzy completion for surrounded by quotes or + braces strings, excluding surrounding +* `ACMPL_UNDO` -- undo the completion (must be used right after a completion) +* `ACMPL_DEACTIVATE` -- exit the autocompletion mode + +Acknowledgments +--------------- + +Thanks to Stanislav Seletskiy for various contributions. + +Thanks to Bao Haojun for the great idea of [skeleton-complete](https://github.com/baohaojun/skeleton-complete). + +Copyright +--------- + +Copyright (C) 2012-2021 Wojciech Siewierski, Gaspar Vardanyan + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +Download +-------- +This patch is under testing phase: +* [st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff](st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff) + +Contribution +------------ +You can create issues and do pull requests on [this gitlab repo](https://gitlab.com/GasparVardanyan/st-autocomplete). + +Authors +------- +* [Wojciech Siewierski](https://github.com/vifon) +* [Gaspar Vardanyan](https://gitlab.com/GasparVardanyan) diff --git a/st.suckless.org/patches/autocomplete/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff b/st.suckless.org/patches/autocomplete/st-autocomplete-20211215-181216-st-0.8.4-testrelease.diff @@ -0,0 +1,601 @@ +diff -uraN st-0.8.4/autocomplete.h st-autocomplete/autocomplete.h +--- st-0.8.4/autocomplete.h 1970-01-01 04:00:00.000000000 +0400 ++++ st-autocomplete/autocomplete.h 2021-12-14 20:20:03.322050025 +0400 +@@ -0,0 +1,16 @@ ++# ifndef __ST_AUTOCOMPLETE_H ++# define __ST_AUTOCOMPLETE_H ++ ++enum { ++ ACMPL_DEACTIVATE, ++ ACMPL_WORD, ++ ACMPL_WWORD, ++ ACMPL_FUZZY_WORD, ++ ACMPL_FUZZY_WWORD, ++ ACMPL_FUZZY, ++ ACMPL_SUFFIX, ++ ACMPL_SURROUND, ++ ACMPL_UNDO, ++}; ++ ++# endif // __ST_AUTOCOMPLETE_H +diff -uraN st-0.8.4/config.def.h st-autocomplete/config.def.h +--- st-0.8.4/config.def.h 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/config.def.h 2021-12-14 20:22:30.088821478 +0400 +@@ -168,6 +168,8 @@ + */ + static uint forcemousemod = ShiftMask; + ++# include "autocomplete.h" ++ + /* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. +@@ -199,6 +201,14 @@ + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, ++ { ControlMask|Mod1Mask, XK_slash, autocomplete, { .i = ACMPL_WORD } }, ++ { ControlMask|Mod1Mask, XK_period, autocomplete, { .i = ACMPL_FUZZY_WORD } }, ++ { ControlMask|Mod1Mask, XK_comma, autocomplete, { .i = ACMPL_FUZZY } }, ++ { ControlMask|Mod1Mask, XK_apostrophe, autocomplete, { .i = ACMPL_SUFFIX } }, ++ { ControlMask|Mod1Mask, XK_semicolon, autocomplete, { .i = ACMPL_SURROUND } }, ++ { ControlMask|Mod1Mask, XK_bracketright,autocomplete, { .i = ACMPL_WWORD } }, ++ { ControlMask|Mod1Mask, XK_bracketleft, autocomplete, { .i = ACMPL_FUZZY_WWORD } }, ++ { ControlMask|Mod1Mask, XK_equal, autocomplete, { .i = ACMPL_UNDO } }, + }; + + /* +diff -uraN st-0.8.4/Makefile st-autocomplete/Makefile +--- st-0.8.4/Makefile 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/Makefile 2021-12-15 07:12:40.291573671 +0400 +@@ -44,6 +44,8 @@ + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f st $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/st ++ cp -f st-autocomplete $(DESTDIR)$(PREFIX)/bin ++ chmod 755 $(DESTDIR)$(PREFIX)/bin/st-autocomplete + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < st.1 > $(DESTDIR)$(MANPREFIX)/man1/st.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/st.1 +@@ -52,6 +54,7 @@ + + uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/st ++ rm -f $(DESTDIR)$(PREFIX)/bin/st-autocomplete + rm -f $(DESTDIR)$(MANPREFIX)/man1/st.1 + + .PHONY: all options clean dist install uninstall +diff -uraN st-0.8.4/st-autocomplete st-autocomplete/st-autocomplete +--- st-0.8.4/st-autocomplete 1970-01-01 04:00:00.000000000 +0400 ++++ st-autocomplete/st-autocomplete 2021-12-14 20:18:13.171971360 +0400 +@@ -0,0 +1,266 @@ ++#!/usr/bin/perl ++######################################################################### ++# Copyright (C) 2012-2021 Wojciech Siewierski, Gaspar Vardanyan # ++# # ++# This program is free software: you can redistribute it and/or modify # ++# it under the terms of the GNU General Public License as published by # ++# the Free Software Foundation, either version 3 of the License, or # ++# (at your option) any later version. # ++# # ++# This program is distributed in the hope that it will be useful, # ++# but WITHOUT ANY WARRANTY; without even the implied warranty of # ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # ++# GNU General Public License for more details. # ++# # ++# You should have received a copy of the GNU General Public License # ++# along with this program. If not, see <http://www.gnu.org/licenses/>. # ++######################################################################### ++ ++my ($cmd, $cursor_row, $cursor_column) = @ARGV; ++ ++# A reference to a function that transforms the completed word ++# into a regex matching the completions. Usually generated by ++# generate_matcher(). ++# ++# For example ++# $fun = generate_matcher(".*"); ++# $fun->("foo"); ++# would return "f.*o.*o" ++# ++# In other words, indirectly decides which characters can ++# appear in the completion. ++my $matcher; ++ ++# A regular expression matching a character before each match. ++# For example, it you want to match the text after a ++# whitespace, set it to "\s". ++my $char_class_before; ++ ++# A regular expression matching every character in the entered ++# text that will be used to find matching completions. Usually ++# "\w" or similar. ++my $char_class_to_complete; ++ ++# A regular expression matching every allowed last character ++# of the completion (uses greedy matching). ++my $char_class_at_end; ++ ++if ($cmd eq 'word-complete') { ++ # Basic word completion. Completes the current word ++ # without any special matching. ++ $char_class_before = '[^-\w]'; ++ $matcher = sub { quotemeta shift }; # identity ++ $char_class_at_end = '[-\w]'; ++ $char_class_to_complete = '[-\w]'; ++} elsif ($cmd eq 'WORD-complete') { ++ # The same as above but in the Vim meaning of a "WORD" -- ++ # whitespace delimited. ++ $char_class_before = '\s'; ++ $matcher = sub { quotemeta shift }; ++ $char_class_at_end = '\S'; ++ $char_class_to_complete = '\S'; ++} elsif ($cmd eq 'fuzzy-word-complete' || ++ $cmd eq 'skeleton-word-complete') { ++ # Fuzzy completion of the current word. ++ $char_class_before = '[^-\w]'; ++ $matcher = generate_matcher('[-\w]*'); ++ $char_class_at_end = '[-\w]'; ++ $char_class_to_complete = '[-\w]'; ++} elsif ($cmd eq 'fuzzy-WORD-complete') { ++ # Fuzzy completion of the current WORD. ++ $char_class_before = '\s'; ++ $matcher = generate_matcher('\S*'); ++ $char_class_at_end = '\S'; ++ $char_class_to_complete = '\S'; ++} elsif ($cmd eq 'fuzzy-complete' || ++ $cmd eq 'skeleton-complete') { ++ # Fuzzy completion of an arbitrary text. ++ $char_class_before = '\W'; ++ $matcher = generate_matcher('.*?'); ++ $char_class_at_end = '\w'; ++ $char_class_to_complete = '\S'; ++} elsif ($cmd eq 'suffix-complete') { ++ # Fuzzy completion of an completing suffixes, like ++ # completing test=hello from /blah/hello. ++ $char_class_before = '\S'; ++ $matcher = generate_matcher('\S*'); ++ $char_class_at_end = '\S'; ++ $char_class_to_complete = '\S'; ++} elsif ($cmd eq 'surround-complete') { ++ # Completing contents of quotes and braces. ++ ++ # Here we are using three named groups: s, b, p for quotes, braces ++ # and parenthesis. ++ $char_class_before = '((?<q>["\'`])|(?<b>\[)|(?<p>\())'; ++ ++ $matcher = generate_matcher('.*?'); ++ ++ # Here we match text till enclosing pair, using perl conditionals in ++ # regexps (?(condition)yes-expression|no-expression). ++ # \0 is used to hack concatenation with '*' later in the code. ++ $char_class_at_end = '.*?(.(?=(?(<b>)\]|((?(<p>)\)|\g{q})))))\0'; ++ $char_class_to_complete = '\S'; ++} ++ ++my $lines = []; ++ ++while (<STDIN>) ++{ ++ push @{$lines}, $_; ++} ++ ++# read the word behind the cursor ++$_ = substr(@{$lines} [$cursor_row], 0, $cursor_column); # get the current line up to the cursor... ++s/.*?($char_class_to_complete*)$/$1/; # ...and read the last word from it ++my $word_to_complete = $_; ++ ++# ignore the completed word itself ++$self->{already_completed}{$word_to_complete} = 1; ++ ++print stdout "$word_to_complete\n"; ++ ++# search for matches ++while (my $completion = find_match($self, ++ $word_to_complete, ++ $self->{next_row} // $cursor_row, ++ $matcher->($word_to_complete), ++ $char_class_before, ++ $char_class_at_end) ++) { ++ calc_match_coords($self, ++ $self->{next_row}+1, ++ $completion); ++ print stdout "$completion @{$self->{highlight}}\n"; ++} ++ ++leave($self); ++ ++ ++ ++###################################################################### ++ ++# Finds the next matching completion in the row current row or above ++# while skipping duplicates using skip_duplicates(). ++sub find_match { ++ my ($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_; ++ $self->{matches_in_row} //= []; ++ ++ # cycle through all the matches in the current row if not starting a new search ++ if (@{$self->{matches_in_row}}) { ++ return skip_duplicates($self, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end); ++ } ++ ++ ++ my $i; ++ # search through all the rows starting with current one or one above the last checked ++ for ($i = $current_row; $i >= 0; --$i) { ++ my $line = @{$lines} [$i]; # get the line of text from the row ++ ++ if ($i == $cursor_row) { ++ $line = substr $line, 0, $cursor_column; ++ } ++ ++ $_ = $line; ++ ++ # find all the matches in the current line ++ my $match; ++ push @{$self->{matches_in_row}}, $+{match} while ($_, $match) = / ++ (.*${char_class_before}) ++ (?<match> ++ ${regexp} ++ ${char_class_at_end}* ++ ) ++ /ix; ++ # corner case: match at the very beginning of line ++ push @{$self->{matches_in_row}}, $+{match} if $line =~ /^(${char_class_before}){0}(?<match>$regexp$char_class_at_end*)/i; ++ ++ if (@{$self->{matches_in_row}}) { ++ # remember which row should be searched next ++ $self->{next_row} = --$i; ++ ++ # arguments needed for find_match() mutual recursion ++ return skip_duplicates($self, $word_to_match, $i, $regexp, $char_class_before, $char_class_at_end); ++ } ++ } ++ ++ # no more possible completions, revert to the original word ++ $self->{next_row} = -1 if $i < 0; ++ ++ return undef; ++} ++ ++###################################################################### ++ ++# Checks whether the completion found by find_match() was already ++# found and if it was, calls find_match() again to find the next ++# completion. ++# ++# Takes all the arguments that find_match() would take, to make a ++# mutually recursive call. ++sub skip_duplicates { ++ my $self = $_[0]; ++ my $completion = shift @{$self->{matches_in_row}}; # get the rightmost one ++ ++ # check for duplicates ++ if (exists $self->{already_completed}{$completion}) { ++ # skip this completion ++ return find_match(@_); ++ } else { ++ $self->{already_completed}{$completion} = 1; ++ return $completion; ++ } ++} ++ ++###################################################################### ++ ++# Returns a function that takes a string and returns that string with ++# this function's argument inserted between its every two characters. ++# The resulting string is used as a regular expression matching the ++# completion candidates. ++sub generate_matcher { ++ my $regex_between = shift; ++ ++ sub { ++ $_ = shift; ++ ++ # sorry for this lispy code, I couldn't resist ;) ++ (join "$regex_between", ++ (map quotemeta, ++ (split //))) ++ } ++} ++ ++###################################################################### ++ ++sub calc_match_coords { ++ my ($self, $linenum, $completion) = @_; ++ ++ my $line = @{$lines} [$linenum]; ++ my $re = quotemeta $completion; ++ ++ $line =~ /$re/; ++ ++ #my ($beg_row, $beg_col) = $line->coord_of($-[0]); ++ #my ($end_row, $end_col) = $line->coord_of($+[0]); ++ my $beg = $-[0]; ++ my $end = $+[0]; ++ ++ if (exists $self->{highlight}) { ++ delete $self->{highlight}; ++ } ++ # () # TODO: what does () do in perl ???? ++ ++ # $self->{highlight} = [$beg_row, $beg_col, $end_row, $end_col]; ++ $self->{highlight} = [$linenum, $beg, $end]; ++} ++ ++###################################################################### ++ ++sub leave { ++ my ($self) = @_; ++ ++ delete $self->{next_row}; ++ delete $self->{matches_in_row}; ++ delete $self->{already_completed}; ++ delete $self->{highlight}; ++} +diff -uraN st-0.8.4/st.c st-autocomplete/st.c +--- st-0.8.4/st.c 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/st.c 2021-12-15 07:44:05.609586643 +0400 +@@ -17,6 +17,7 @@ + #include <unistd.h> + #include <wchar.h> + ++#include "autocomplete.h" + #include "st.h" + #include "win.h" + +@@ -2476,6 +2477,9 @@ + return; + } + ++ if ( row < term.row || col < term.col ) ++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); ++ + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to +@@ -2595,3 +2599,211 @@ + tfulldirt(); + draw(); + } ++ ++void autocomplete (const Arg * arg) ++{ ++ static _Bool active = 0; ++ ++ int acmpl_cmdindex = arg -> i; ++ ++ static int acmpl_cmdindex_prev; ++ ++ if (active == 0) ++ acmpl_cmdindex_prev = acmpl_cmdindex; ++ ++ static const char * const (acmpl_cmd []) = { ++ [ACMPL_DEACTIVATE] = "__DEACTIVATE__", ++ [ACMPL_WORD] = "word-complete", ++ [ACMPL_WWORD] = "WORD-complete", ++ [ACMPL_FUZZY_WORD] = "fuzzy-word-complete", ++ [ACMPL_FUZZY_WWORD] = "fuzzy-WORD-complete", ++ [ACMPL_FUZZY] = "fuzzy-complete", ++ [ACMPL_SUFFIX] = "suffix-complete", ++ [ACMPL_SURROUND] = "surround-complete", ++ [ACMPL_UNDO] = "__UNDO__", ++ }; ++ ++ static char acmpl [1000]; // ACMPL_ISSUE: why 1000? ++ ++ static FILE * acmpl_exec = NULL; ++ static int acmpl_status; ++ ++ static const char * stbuffile; ++ static char target [1000]; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col ++ static size_t targetlen; ++ ++ static char completion [1000] = {0}; // ACMPL_ISSUE: why 1000? dynamically allocate char array of size term.col ++ static size_t complen_prev = 0; // NOTE: always clear this variable after clearing completion ++ ++// Check for deactivation ++ ++ if (acmpl_cmdindex == ACMPL_DEACTIVATE) ++ { ++ ++// Deactivate autocomplete mode keeping current completion ++ ++ if (active) ++ { ++ active = 0; ++ pclose (acmpl_exec); ++ remove (stbuffile); ++ ++ if (complen_prev) ++ { ++ selclear (); ++ complen_prev = 0; ++ } ++ } ++ ++ return; ++ } ++ ++// Check for undo ++ ++ if (acmpl_cmdindex == ACMPL_UNDO) ++ { ++ ++// Deactivate autocomplete mode recovering target ++ ++ if (active) ++ { ++ active = 0; ++ pclose (acmpl_exec); ++ remove (stbuffile); ++ ++ if (complen_prev) ++ { ++ selclear (); ++ for (size_t i = 0; i < complen_prev; i++) ++ ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is the right way ++ complen_prev = 0; ++ ttywrite (target, targetlen, 0); // ACMPL_ISSUE: I'm not sure that this is a right solution ++ } ++ } ++ ++ return; ++ } ++ ++// Check for command change ++ ++ if (acmpl_cmdindex != acmpl_cmdindex_prev) ++ { ++ ++// If command is changed, goto acmpl_begin avoiding rewriting st buffer ++ ++ if (active) ++ { ++ acmpl_cmdindex_prev = acmpl_cmdindex; ++ ++ goto acmpl_begin; ++ } ++ } ++ ++// If not active ++ ++ if (active == 0) ++ { ++ acmpl_cmdindex_prev = acmpl_cmdindex; ++ ++// Write st buffer to a temp file ++ ++ stbuffile = tmpnam (NULL); // check for return value ... ++ // ACMPL_ISSUE: use coprocesses instead of temp files ++ sprintf ( ++ acmpl, ++ "cat %100s | st-autocomplete %500s %d %d", // ACMPL_ISSUE: why 100 and 500? ++ stbuffile, ++ acmpl_cmd [acmpl_cmdindex], ++ term.c.y, ++ term.c.x ++ ); ++ ++ FILE * stbuf = fopen (stbuffile, "w"); // check for opening error ... ++ char * stbufline = malloc (term.col + 2); // check for allocating error ... ++ ++ for (size_t y = 0; y < term.row; y++) ++ { ++ size_t x = 0; ++ for (; x < term.col; x++) ++ utf8encode (term.line [y] [x].u, stbufline + x); ++ stbufline [x] = '\n'; ++ stbufline [x + 1] = 0; ++ fputs (stbufline, stbuf); ++ } ++ ++ free (stbufline); ++ fclose (stbuf); ++ ++acmpl_begin: ++ ++// Run st-autocomplete ++ ++ acmpl_exec = popen (acmpl, "r"); // ACMPL_ISSUE: popen isn't defined by The Standard. Does it work in BSDs for example? ++ // check for popen error ... ++ ++// Read the target, targetlen ++ ++ fscanf (acmpl_exec, "%500s\n", target); // check for scanning error ... ++ targetlen = strlen (target); ++ } ++ ++// Read a completion if exists (acmpl_status) ++ ++ unsigned line, beg, end; ++ ++ acmpl_status = fscanf (acmpl_exec, "%500s %u %u %u\n", completion, & line, & beg, & end); ++ // ACMPL_ISSUE: why 500? use term.col instead ++ ++// Exit if no completions found ++ ++ if (active == 0 && acmpl_status == EOF) ++ { ++ ++// Close st-autocomplete and exit without activating the autocomplete mode ++ ++ pclose (acmpl_exec); ++ remove (stbuffile); ++ return; ++ } ++ ++// If completions found, enable autocomplete mode and autocomplete the target ++ ++ active = 1; ++ ++// Clear target before first completion ++ ++ if (complen_prev == 0) ++ { ++ for (size_t i = 0; i < targetlen; i++) ++ ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution ++ } ++ ++// Clear previuos completion if this is not the first ++ ++ else ++ { ++ selclear (); ++ for (size_t i = 0; i < complen_prev; i++) ++ ttywrite ((char []) { '\b' }, 1, 1); // ACMPL_ISSUE: I'm not sure that this is a right solution ++ complen_prev = 0; ++ } ++ ++// If no more completions found, reset and restart ++ ++ if (acmpl_status == EOF) ++ { ++ active = 0; ++ pclose (acmpl_exec); ++ ttywrite (target, targetlen, 0); ++ goto acmpl_begin; ++ } ++ ++// Read the new completion and autcomplete ++ ++ selstart (beg, line, 0); ++ selextend (end - 1, line, 1, 0); ++ xsetsel (getsel ()); ++ ++ complen_prev = strlen (completion); ++ ttywrite (completion, complen_prev, 0); ++} +diff -uraN st-0.8.4/st.h st-autocomplete/st.h +--- st-0.8.4/st.h 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/st.h 2021-12-14 20:28:59.272432749 +0400 +@@ -77,6 +77,8 @@ + const char *s; + } Arg; + ++void autocomplete (const Arg *); ++ + void die(const char *, ...); + void redraw(void); + void draw(void); +diff -uraN st-0.8.4/x.c st-autocomplete/x.c +--- st-0.8.4/x.c 2021-12-14 20:03:03.981322164 +0400 ++++ st-autocomplete/x.c 2021-12-14 20:30:30.045830893 +0400 +@@ -1803,11 +1803,15 @@ + /* 1. shortcuts */ + for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { + if (ksym == bp->keysym && match(bp->mod, e->state)) { ++ if (bp -> func != autocomplete) ++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); + bp->func(&(bp->arg)); + return; + } + } + ++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); ++ + /* 2. custom keys from config.h */ + if ((customkey = kmap(ksym, e->state))) { + ttywrite(customkey, strlen(customkey), 1); diff --git a/st.suckless.org/patches/autocomplete/usage.gif b/st.suckless.org/patches/autocomplete/usage.gif Binary files differ.