From f988bf37bc0f1d8c9b93fc70643f6c88bc216c4c Mon Sep 17 00:00:00 2001 From: q3aql Date: Sun, 10 Jul 2022 14:17:48 +0200 Subject: [PATCH] Patches applied --- patch.sh | 6 + ...st-0.8.5-autocomplete-20220327-230120.diff | 703 ++++++++++++++++++ .../st-blinking_cursor-20211116-2f6e597.diff | 153 ++++ .../st-defaultfontsize-20210225-4ef0cbd.diff | 79 ++ patches/st-scrollback-0.8.5.diff | 350 +++++++++ 5 files changed, 1291 insertions(+) create mode 100644 patch.sh create mode 100644 patches/st-0.8.5-autocomplete-20220327-230120.diff create mode 100644 patches/st-blinking_cursor-20211116-2f6e597.diff create mode 100644 patches/st-defaultfontsize-20210225-4ef0cbd.diff create mode 100644 patches/st-scrollback-0.8.5.diff diff --git a/patch.sh b/patch.sh new file mode 100644 index 0000000..fc590d9 --- /dev/null +++ b/patch.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +patch < patches/st-blinking_cursor-20211116-2f6e597.diff +patch < patches/st-scrollback-0.8.5.diff +patch < patches/st-0.8.5-autocomplete-20220327-230120.diff +patch < patches/st-defaultfontsize-20210225-4ef0cbd.diff diff --git a/patches/st-0.8.5-autocomplete-20220327-230120.diff b/patches/st-0.8.5-autocomplete-20220327-230120.diff new file mode 100644 index 0000000..74aa2b7 --- /dev/null +++ b/patches/st-0.8.5-autocomplete-20220327-230120.diff @@ -0,0 +1,703 @@ +diff -uraN st-0.8.5/autocomplete.h st-autocomplete/autocomplete.h +--- st-0.8.5/autocomplete.h 1970-01-01 04:00:00.000000000 +0400 ++++ st-autocomplete/autocomplete.h 2022-03-13 02:45:34.586842452 +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.5/config.def.h st-autocomplete/config.def.h +--- st-0.8.5/config.def.h 2022-03-13 02:45:34.586842452 +0400 ++++ st-autocomplete/config.def.h 2022-03-13 02:45:34.586842452 +0400 +@@ -170,6 +170,8 @@ + */ + static uint forcemousemod = ShiftMask; + ++#include "autocomplete.h" ++ + /* + * Internal mouse shortcuts. + * Beware that overloading Button1 will disable the selection. +@@ -187,6 +189,8 @@ + #define MODKEY Mod1Mask + #define TERMMOD (ControlMask|ShiftMask) + ++#define ACMPL_MOD ControlMask|Mod1Mask ++ + static Shortcut shortcuts[] = { + /* mask keysym function argument */ + { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} }, +@@ -201,6 +205,14 @@ + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, ++ { ACMPL_MOD, XK_slash, autocomplete, { .i = ACMPL_WORD } }, ++ { ACMPL_MOD, XK_period, autocomplete, { .i = ACMPL_FUZZY_WORD } }, ++ { ACMPL_MOD, XK_comma, autocomplete, { .i = ACMPL_FUZZY } }, ++ { ACMPL_MOD, XK_apostrophe, autocomplete, { .i = ACMPL_SUFFIX } }, ++ { ACMPL_MOD, XK_semicolon, autocomplete, { .i = ACMPL_SURROUND } }, ++ { ACMPL_MOD, XK_bracketright,autocomplete, { .i = ACMPL_WWORD } }, ++ { ACMPL_MOD, XK_bracketleft, autocomplete, { .i = ACMPL_FUZZY_WWORD } }, ++ { ACMPL_MOD, XK_equal, autocomplete, { .i = ACMPL_UNDO } }, + }; + + /* +diff -uraN st-0.8.5/Makefile st-autocomplete/Makefile +--- st-0.8.5/Makefile 2022-03-13 02:45:34.586842452 +0400 ++++ st-autocomplete/Makefile 2022-03-13 02:45:34.586842452 +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.5/st-autocomplete st-autocomplete/st-autocomplete +--- st-0.8.5/st-autocomplete 1970-01-01 04:00:00.000000000 +0400 ++++ st-autocomplete/st-autocomplete 2022-03-27 22:57:29.018288223 +0400 +@@ -0,0 +1,310 @@ ++#!/usr/bin/perl ++######################################################################### ++# Copyright (C) 2012-2017 Wojciech Siewierski # ++# # ++# 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 . # ++######################################################################### ++ ++my ($cmd, $cursor_row, $cursor_column) = @ARGV; ++ ++my $lines = []; ++my $lines1 = []; ++ ++my $last_line = -1; ++my $lines_before_cursor = 0; ++ ++while () ++{ ++ $last_line++; ++ ++ s/[^[:print:]]/?/g; ++ ++ if ($last_line < $cursor_row) ++ { ++ unshift @{$lines1}, $_; ++ $lines_before_cursor++; ++ } ++ else ++ { ++ unshift @{$lines}, $_; ++ } ++} ++ ++foreach (@{$lines1}) ++{ ++ unshift @{$lines}, $_; ++} ++ ++my $cursor_row_in = $cursor_row; ++ ++$cursor_row = $last_line; ++ ++ ++$self = {}; ++ ++# 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 = '((?["\'`])|(?\[)|(?

\())'; ++ ++ $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 = '.*?(.(?=(?()\]|((?(

)\)|\g{q})))))\0'; ++ $char_class_to_complete = '\S'; ++} ++ ++ ++# use the last used word or read the word behind the cursor ++my $word_to_complete = read_word_at_coord($self, $cursor_row, $cursor_column, ++ $char_class_to_complete); ++ ++print stdout "$word_to_complete\n"; ++ ++if ($word_to_complete) { ++ while (1) { ++ # ignore the completed word itself ++ $self->{already_completed}{$word_to_complete} = 1; ++ ++ # continue the last search or start from the current row ++ my $completion = find_match($self, ++ $word_to_complete, ++ $self->{next_row} // $cursor_row, ++ $matcher->($word_to_complete), ++ $char_class_before, ++ $char_class_at_end); ++ if ($completion) { ++ print stdout $completion."\n".join ("\n", @{$self->{highlight}})."\n"; ++ } ++ else { ++ last; ++ } ++ } ++} ++ ++###################################################################### ++ ++sub highlight_match { ++ my ($self, $linenum, $completion) = @_; ++ ++ # clear_highlight($self); ++ ++ my $line = @{$lines}[$linenum]; ++ my $re = quotemeta $completion; ++ ++ $line =~ /$re/; ++ ++ my $beg = $-[0]; ++ my $end = $+[0]; ++ ++ if ($linenum >= $lines_before_cursor) ++ { ++ $lline = $last_line - $lines_before_cursor; ++ $linenum -= $lines_before_cursor; ++ $linenum = $lline - $linenum; ++ $linenum += $lines_before_cursor; ++ } ++ ++ ++ $self->{highlight} = [$linenum, $beg, $end]; ++} ++ ++###################################################################### ++ ++sub read_word_at_coord { ++ my ($self, $row, $col, $char_class) = @_; ++ ++ $_ = substr(@{$lines} [$row], 0, $col); # get the current line up to the cursor... ++ s/.*?($char_class*)$/$1/; # ...and read the last word from it ++ return $_; ++} ++ ++###################################################################### ++ ++# 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 //))) ++ } ++} ++ ++###################################################################### ++ ++# 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, $word_to_match, $current_row, $regexp, $char_class_before, $char_class_at_end) = @_; ++ my $completion; ++ ++ if ($current_row <= $lines_before_cursor) ++ { ++ $completion = shift @{$self->{matches_in_row}}; # get the leftmost one ++ } ++ else ++ { ++ $completion = pop @{$self->{matches_in_row}}; # get the leftmost one ++ } ++ ++ # check for duplicates ++ if (exists $self->{already_completed}{$completion}) { ++ # skip this completion ++ return find_match(@_); ++ } else { ++ $self->{already_completed}{$completion} = 1; ++ ++ highlight_match($self, ++ $self->{next_row}+1, ++ $completion); ++ ++ return $completion; ++ } ++} ++ ++###################################################################### ++ ++# 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}) ++ (? ++ ${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}(?$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 ++ # undo_completion($self) if $i < 0; ++ ++ return undef; ++} +diff -uraN st-0.8.5/st.c st-autocomplete/st.c +--- st-0.8.5/st.c 2022-03-13 02:45:34.586842452 +0400 ++++ st-autocomplete/st.c 2022-03-27 22:28:39.041693478 +0400 +@@ -17,6 +17,7 @@ + #include + #include + ++#include "autocomplete.h" + #include "st.h" + #include "win.h" + +@@ -2569,6 +2570,8 @@ + return; + } + ++ autocomplete ((const Arg []) { ACMPL_DEACTIVATE }); ++ + /* + * slide screen to keep cursor where we expect it - + * tscrollup would work here, but we can optimize to +@@ -2688,3 +2691,256 @@ + 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 ++ ++ static int cx, cy; ++ ++ // ACMPL_ISSUE: crashes when term.row is too small ++ ++// 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; ++ cx = term.c.x; ++ cy = term.c.y; ++ ++// Write st buffer to a temp file ++ ++ stbuffile = tmpnam (NULL); // ACMPL_ISSUE: check for return value ... ++ // ACMPL_ISSUE: use coprocesses instead of temp files ++ ++ FILE * stbuf = fopen (stbuffile, "w"); // ACMPL_ISSUE: check for opening error ... ++ char * stbufline = malloc (term.col + 2); // ACMPL_ISSUE: check for allocating error ... ++ ++ int cxp = 0; ++ ++ for (size_t y = 0; y < term.row; y++) ++ { ++ if (y == term.c.y) cx += cxp * term.col; ++ ++ size_t x = 0; ++ for (; x < term.col; x++) ++ utf8encode (term.line [y] [x].u, stbufline + x); ++ if (term.line [y] [x - 1].mode & ATTR_WRAP) ++ { ++ x--; ++ if (y <= term.c.y) cy--; ++ cxp++; ++ } ++ else ++ { ++ stbufline [x] = '\n'; ++ cxp = 0; ++ } ++ stbufline [x + 1] = 0; ++ fputs (stbufline, stbuf); ++ } ++ ++ free (stbufline); ++ fclose (stbuf); ++ ++acmpl_begin: ++ ++// Run st-autocomplete ++ ++ sprintf ( ++ acmpl, ++ "cat %100s | st-autocomplete %500s %d %d", // ACMPL_ISSUE: why 100 and 500? ++ stbuffile, ++ acmpl_cmd [acmpl_cmdindex], ++ cy, ++ cx ++ ); ++ ++ acmpl_exec = popen (acmpl, "r"); // ACMPL_ISSUE: popen isn't defined by The Standard. Does it work in BSDs for example? ++ // ACMPL_ISSUE: check for popen error ... ++ ++// Read the target, targetlen ++ ++ fscanf (acmpl_exec, "%500s\n", target); // ACMPL_ISSUE: check for scanning error ... ++ targetlen = strlen (target); ++ } ++ ++// Read a completion if exists (acmpl_status) ++ ++ unsigned line, beg, end; ++ ++ acmpl_status = fscanf (acmpl_exec, "%500[^\n]\n%u\n%u\n%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; ++ } ++ ++// Count wrapped lines before the current line ++ ++ int wl = 0; ++ ++ int tl = line; ++ ++ for (int l = 0; l < tl; l++) ++ if (term.line [l] [term.col - 1].mode & ATTR_WRAP) ++ { ++ wl++; ++ tl++; ++ } ++ ++// Autcomplete ++ ++ complen_prev = strlen (completion); ++ ttywrite (completion, complen_prev, 0); ++ ++ if (line == cy && beg > cx) ++ { ++ beg += complen_prev - targetlen; ++ end += complen_prev - targetlen; ++ ++ // ACMPL_ISSUE: highlignthing doesn't work when "line == cy && beg > cx", ++ // but coordinates are correct... ++ } ++ ++ end--; ++ ++ selstart (beg % term.col, line + wl + beg / term.col, 0); ++ selextend (end % term.col, line + wl + end / term.col, 1, 0); ++ xsetsel (getsel ()); ++} +diff -uraN st-0.8.5/st.h st-autocomplete/st.h +--- st-0.8.5/st.h 2022-03-13 02:45:34.586842452 +0400 ++++ st-autocomplete/st.h 2022-03-13 02:45:34.586842452 +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.5/x.c st-autocomplete/x.c +--- st-0.8.5/x.c 2022-03-13 02:45:34.586842452 +0400 ++++ st-autocomplete/x.c 2022-03-13 02:45:34.590175835 +0400 +@@ -1834,11 +1834,20 @@ + /* 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; + } + } + ++ if (!( ++ len == 0 && ++ e -> state & ~ignoremod // ACMPL_ISSUE: I'm not sure that this is the right way ++ | ACMPL_MOD == ACMPL_MOD ++ )) ++ 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/patches/st-blinking_cursor-20211116-2f6e597.diff b/patches/st-blinking_cursor-20211116-2f6e597.diff new file mode 100644 index 0000000..91c871a --- /dev/null +++ b/patches/st-blinking_cursor-20211116-2f6e597.diff @@ -0,0 +1,153 @@ +From a3cdd0753bf578cd4e6db7c6507481f3b5c38aea Mon Sep 17 00:00:00 2001 +From: Steve Ward +Date: Tue, 16 Nov 2021 14:15:06 -0500 +Subject: [PATCH] Allow blinking cursor + +--- + config.def.h | 19 +++++++++++++------ + x.c | 47 +++++++++++++++++++++++++++++++++++------------ + 2 files changed, 48 insertions(+), 18 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 6f05dce..1a5fed0 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -133,13 +133,20 @@ static unsigned int defaultcs = 256; + static unsigned int defaultrcs = 257; + + /* +- * Default shape of cursor +- * 2: Block ("█") +- * 4: Underline ("_") +- * 6: Bar ("|") +- * 7: Snowman ("☃") ++ * https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h4-Functions-using-CSI-_-ordered-by-the-final-character-lparen-s-rparen:CSI-Ps-SP-q.1D81 ++ * Default style of cursor ++ * 0: blinking block ++ * 1: blinking block (default) ++ * 2: steady block ("█") ++ * 3: blinking underline ++ * 4: steady underline ("_") ++ * 5: blinking bar ++ * 6: steady bar ("|") ++ * 7: blinking st cursor ++ * 8: steady st cursor + */ +-static unsigned int cursorshape = 2; ++static unsigned int cursorstyle = 1; ++static Rune stcursor = 0x2603; /* snowman ("☃") */ + + /* + * Default columns and rows numbers +diff --git a/x.c b/x.c +index 89786b8..7d2447d 100644 +--- a/x.c ++++ b/x.c +@@ -253,6 +253,7 @@ static char *opt_name = NULL; + static char *opt_title = NULL; + + static int oldbutton = 3; /* button event on startup: 3 = release */ ++static int cursorblinks = 0; + + void + clipcopy(const Arg *dummy) +@@ -1529,29 +1530,44 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og) + /* draw the new one */ + if (IS_SET(MODE_FOCUSED)) { + switch (win.cursor) { +- case 7: /* st extension */ +- g.u = 0x2603; /* snowman (U+2603) */ ++ default: ++ case 0: /* blinking block */ ++ case 1: /* blinking block (default) */ ++ if (IS_SET(MODE_BLINK)) ++ break; + /* FALLTHROUGH */ +- case 0: /* Blinking Block */ +- case 1: /* Blinking Block (Default) */ +- case 2: /* Steady Block */ ++ case 2: /* steady block */ + xdrawglyph(g, cx, cy); + break; +- case 3: /* Blinking Underline */ +- case 4: /* Steady Underline */ ++ case 3: /* blinking underline */ ++ if (IS_SET(MODE_BLINK)) ++ break; ++ /* FALLTHROUGH */ ++ case 4: /* steady underline */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + (cy + 1) * win.ch - \ + cursorthickness, + win.cw, cursorthickness); + break; +- case 5: /* Blinking bar */ +- case 6: /* Steady bar */ ++ case 5: /* blinking bar */ ++ if (IS_SET(MODE_BLINK)) ++ break; ++ /* FALLTHROUGH */ ++ case 6: /* steady bar */ + XftDrawRect(xw.draw, &drawcol, + borderpx + cx * win.cw, + borderpx + cy * win.ch, + cursorthickness, win.ch); + break; ++ case 7: /* blinking st cursor */ ++ if (IS_SET(MODE_BLINK)) ++ break; ++ /* FALLTHROUGH */ ++ case 8: /* steady st cursor */ ++ g.u = stcursor; ++ xdrawglyph(g, cx, cy); ++ break; + } + } else { + XftDrawRect(xw.draw, &drawcol, +@@ -1708,9 +1724,12 @@ xsetmode(int set, unsigned int flags) + int + xsetcursor(int cursor) + { +- if (!BETWEEN(cursor, 0, 7)) /* 7: st extension */ ++ if (!BETWEEN(cursor, 0, 8)) /* 7-8: st extensions */ + return 1; + win.cursor = cursor; ++ cursorblinks = win.cursor == 0 || win.cursor == 1 || ++ win.cursor == 3 || win.cursor == 5 || ++ win.cursor == 7; + return 0; + } + +@@ -1954,6 +1973,10 @@ run(void) + if (FD_ISSET(ttyfd, &rfd) || xev) { + if (!drawing) { + trigger = now; ++ if (IS_SET(MODE_BLINK)) { ++ win.mode ^= MODE_BLINK; ++ } ++ lastblink = now; + drawing = 1; + } + timeout = (maxlatency - TIMEDIFF(now, trigger)) \ +@@ -1964,7 +1987,7 @@ run(void) + + /* idle detected or maxlatency exhausted -> draw */ + timeout = -1; +- if (blinktimeout && tattrset(ATTR_BLINK)) { ++ if (blinktimeout && (cursorblinks || tattrset(ATTR_BLINK))) { + timeout = blinktimeout - TIMEDIFF(now, lastblink); + if (timeout <= 0) { + if (-timeout > blinktimeout) /* start visible */ +@@ -2000,7 +2023,7 @@ main(int argc, char *argv[]) + { + xw.l = xw.t = 0; + xw.isfixed = False; +- xsetcursor(cursorshape); ++ xsetcursor(cursorstyle); + + ARGBEGIN { + case 'a': +-- +2.34.0 + diff --git a/patches/st-defaultfontsize-20210225-4ef0cbd.diff b/patches/st-defaultfontsize-20210225-4ef0cbd.diff new file mode 100644 index 0000000..ee6f46d --- /dev/null +++ b/patches/st-defaultfontsize-20210225-4ef0cbd.diff @@ -0,0 +1,79 @@ +From e3c97b85f0e94011e77af9259d379c956f9d6d64 Mon Sep 17 00:00:00 2001 +From: Randy Palamar +Date: Thu, 25 Feb 2021 23:53:47 -0700 +Subject: [PATCH] support setting the default font size on invocation + +--- + st.1 | 8 ++++++++ + x.c | 8 +++++++- + 2 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/st.1 b/st.1 +index 39120b4..57ddfb8 100644 +--- a/st.1 ++++ b/st.1 +@@ -22,6 +22,8 @@ st \- simple terminal + .IR line ] + .RB [ \-w + .IR windowid ] ++.RB [ \-z ++.IR fontsize ] + .RB [[ \-e ] + .IR command + .RI [ arguments ...]] +@@ -44,6 +46,8 @@ st \- simple terminal + .IR title ] + .RB [ \-w + .IR windowid ] ++.RB [ \-z ++.IR fontsize ] + .RB \-l + .IR line + .RI [ stty_args ...] +@@ -91,6 +95,10 @@ defines the window title (default 'st'). + embeds st within the window identified by + .I windowid + .TP ++.BI \-z " fontsize" ++sets the default fontsize to ++.I fontsize ++.TP + .BI \-l " line" + use a tty + .I line +diff --git a/x.c b/x.c +index 120e495..224f26e 100644 +--- a/x.c ++++ b/x.c +@@ -4,6 +4,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1114,7 +1115,7 @@ xinit(int cols, int rows) + die("could not init fontconfig.\n"); + + usedfont = (opt_font == NULL)? font : opt_font; +- xloadfonts(usedfont, 0); ++ xloadfonts(usedfont, defaultfontsize); + + /* colors */ + xw.cmap = XDefaultColormap(xw.dpy, xw.scr); +@@ -2038,6 +2039,11 @@ main(int argc, char *argv[]) + case 'v': + die("%s " VERSION "\n", argv0); + break; ++ case 'z': ++ defaultfontsize = strtod(EARGF(usage()), NULL); ++ if (!(defaultfontsize > 0)) ++ usage(); ++ break; + default: + usage(); + } ARGEND; +-- +2.26.2 + diff --git a/patches/st-scrollback-0.8.5.diff b/patches/st-scrollback-0.8.5.diff new file mode 100644 index 0000000..750111d --- /dev/null +++ b/patches/st-scrollback-0.8.5.diff @@ -0,0 +1,350 @@ +diff --git a/config.def.h b/config.def.h +index 91ab8ca..e3b469b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -201,6 +201,8 @@ static Shortcut shortcuts[] = { + { TERMMOD, XK_Y, selpaste, {.i = 0} }, + { ShiftMask, XK_Insert, selpaste, {.i = 0} }, + { TERMMOD, XK_Num_Lock, numlock, {.i = 0} }, ++ { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} }, ++ { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} }, + }; + + /* +diff --git a/st.c b/st.c +index 51049ba..cd750f2 100644 +--- a/st.c ++++ b/st.c +@@ -35,6 +35,7 @@ + #define ESC_ARG_SIZ 16 + #define STR_BUF_SIZ ESC_BUF_SIZ + #define STR_ARG_SIZ ESC_ARG_SIZ ++#define HISTSIZE 2000 + + /* macros */ + #define IS_SET(flag) ((term.mode & (flag)) != 0) +@@ -42,6 +43,9 @@ + #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) + #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) + #define ISDELIM(u) (u && wcschr(worddelimiters, u)) ++#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ ++ term.scr + HISTSIZE + 1) % HISTSIZE] : \ ++ term.line[(y) - term.scr]) + + enum term_mode { + MODE_WRAP = 1 << 0, +@@ -115,6 +119,9 @@ typedef struct { + int col; /* nb col */ + Line *line; /* screen */ + Line *alt; /* alternate screen */ ++ Line hist[HISTSIZE]; /* history buffer */ ++ int histi; /* history index */ ++ int scr; /* scroll back */ + int *dirty; /* dirtyness of lines */ + TCursor c; /* cursor */ + int ocx; /* old cursor col */ +@@ -184,8 +191,8 @@ static void tnewline(int); + static void tputtab(int); + static void tputc(Rune); + static void treset(void); +-static void tscrollup(int, int); +-static void tscrolldown(int, int); ++static void tscrollup(int, int, int); ++static void tscrolldown(int, int, int); + static void tsetattr(const int *, int); + static void tsetchar(Rune, const Glyph *, int, int); + static void tsetdirt(int, int); +@@ -416,10 +423,10 @@ tlinelen(int y) + { + int i = term.col; + +- if (term.line[y][i - 1].mode & ATTR_WRAP) ++ if (TLINE(y)[i - 1].mode & ATTR_WRAP) + return i; + +- while (i > 0 && term.line[y][i - 1].u == ' ') ++ while (i > 0 && TLINE(y)[i - 1].u == ' ') + --i; + + return i; +@@ -528,7 +535,7 @@ selsnap(int *x, int *y, int direction) + * Snap around if the word wraps around at the end or + * beginning of a line. + */ +- prevgp = &term.line[*y][*x]; ++ prevgp = &TLINE(*y)[*x]; + prevdelim = ISDELIM(prevgp->u); + for (;;) { + newx = *x + direction; +@@ -543,14 +550,14 @@ selsnap(int *x, int *y, int direction) + yt = *y, xt = *x; + else + yt = newy, xt = newx; +- if (!(term.line[yt][xt].mode & ATTR_WRAP)) ++ if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) + break; + } + + if (newx >= tlinelen(newy)) + break; + +- gp = &term.line[newy][newx]; ++ gp = &TLINE(newy)[newx]; + delim = ISDELIM(gp->u); + if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim + || (delim && gp->u != prevgp->u))) +@@ -571,14 +578,14 @@ selsnap(int *x, int *y, int direction) + *x = (direction < 0) ? 0 : term.col - 1; + if (direction < 0) { + for (; *y > 0; *y += direction) { +- if (!(term.line[*y-1][term.col-1].mode ++ if (!(TLINE(*y-1)[term.col-1].mode + & ATTR_WRAP)) { + break; + } + } + } else if (direction > 0) { + for (; *y < term.row-1; *y += direction) { +- if (!(term.line[*y][term.col-1].mode ++ if (!(TLINE(*y)[term.col-1].mode + & ATTR_WRAP)) { + break; + } +@@ -609,13 +616,13 @@ getsel(void) + } + + if (sel.type == SEL_RECTANGULAR) { +- gp = &term.line[y][sel.nb.x]; ++ gp = &TLINE(y)[sel.nb.x]; + lastx = sel.ne.x; + } else { +- gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0]; ++ gp = &TLINE(y)[sel.nb.y == y ? sel.nb.x : 0]; + lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; + } +- last = &term.line[y][MIN(lastx, linelen-1)]; ++ last = &TLINE(y)[MIN(lastx, linelen-1)]; + while (last >= gp && last->u == ' ') + --last; + +@@ -851,6 +858,9 @@ void + ttywrite(const char *s, size_t n, int may_echo) + { + const char *next; ++ Arg arg = (Arg) { .i = term.scr }; ++ ++ kscrolldown(&arg); + + if (may_echo && IS_SET(MODE_ECHO)) + twrite(s, n, 1); +@@ -1062,12 +1072,52 @@ tswapscreen(void) + } + + void +-tscrolldown(int orig, int n) ++kscrolldown(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (n > term.scr) ++ n = term.scr; ++ ++ if (term.scr > 0) { ++ term.scr -= n; ++ selscroll(0, -n); ++ tfulldirt(); ++ } ++} ++ ++void ++kscrollup(const Arg* a) ++{ ++ int n = a->i; ++ ++ if (n < 0) ++ n = term.row + n; ++ ++ if (term.scr <= HISTSIZE-n) { ++ term.scr += n; ++ selscroll(0, n); ++ tfulldirt(); ++ } ++} ++ ++void ++tscrolldown(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); ++ if (copyhist) { ++ term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[term.bot]; ++ term.line[term.bot] = temp; ++ } ++ + + tsetdirt(orig, term.bot-n); + tclearregion(0, term.bot-n+1, term.col-1, term.bot); +@@ -1078,17 +1128,28 @@ tscrolldown(int orig, int n) + term.line[i-n] = temp; + } + +- selscroll(orig, n); ++ if (term.scr == 0) ++ selscroll(orig, n); + } + + void +-tscrollup(int orig, int n) ++tscrollup(int orig, int n, int copyhist) + { + int i; + Line temp; + + LIMIT(n, 0, term.bot-orig+1); + ++ if (copyhist) { ++ term.histi = (term.histi + 1) % HISTSIZE; ++ temp = term.hist[term.histi]; ++ term.hist[term.histi] = term.line[orig]; ++ term.line[orig] = temp; ++ } ++ ++ if (term.scr > 0 && term.scr < HISTSIZE) ++ term.scr = MIN(term.scr + n, HISTSIZE-1); ++ + tclearregion(0, orig, term.col-1, orig+n-1); + tsetdirt(orig+n, term.bot); + +@@ -1098,7 +1159,8 @@ tscrollup(int orig, int n) + term.line[i+n] = temp; + } + +- selscroll(orig, -n); ++ if (term.scr == 0) ++ selscroll(orig, -n); + } + + void +@@ -1127,7 +1189,7 @@ tnewline(int first_col) + int y = term.c.y; + + if (y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + y++; + } +@@ -1292,14 +1354,14 @@ void + tinsertblankline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrolldown(term.c.y, n); ++ tscrolldown(term.c.y, n, 0); + } + + void + tdeleteline(int n) + { + if (BETWEEN(term.c.y, term.top, term.bot)) +- tscrollup(term.c.y, n); ++ tscrollup(term.c.y, n, 0); + } + + int32_t +@@ -1736,11 +1798,11 @@ csihandle(void) + break; + case 'S': /* SU -- Scroll line up */ + DEFAULT(csiescseq.arg[0], 1); +- tscrollup(term.top, csiescseq.arg[0]); ++ tscrollup(term.top, csiescseq.arg[0], 0); + break; + case 'T': /* SD -- Scroll line down */ + DEFAULT(csiescseq.arg[0], 1); +- tscrolldown(term.top, csiescseq.arg[0]); ++ tscrolldown(term.top, csiescseq.arg[0], 0); + break; + case 'L': /* IL -- Insert blank lines */ + DEFAULT(csiescseq.arg[0], 1); +@@ -2330,7 +2392,7 @@ eschandle(uchar ascii) + return 0; + case 'D': /* IND -- Linefeed */ + if (term.c.y == term.bot) { +- tscrollup(term.top, 1); ++ tscrollup(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y+1); + } +@@ -2343,7 +2405,7 @@ eschandle(uchar ascii) + break; + case 'M': /* RI -- Reverse index */ + if (term.c.y == term.top) { +- tscrolldown(term.top, 1); ++ tscrolldown(term.top, 1, 1); + } else { + tmoveto(term.c.x, term.c.y-1); + } +@@ -2557,7 +2619,7 @@ twrite(const char *buf, int buflen, int show_ctrl) + void + tresize(int col, int row) + { +- int i; ++ int i, j; + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + int *bp; +@@ -2594,6 +2656,14 @@ tresize(int col, int row) + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + ++ for (i = 0; i < HISTSIZE; i++) { ++ term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); ++ for (j = mincol; j < col; j++) { ++ term.hist[i][j] = term.c.attr; ++ term.hist[i][j].u = ' '; ++ } ++ } ++ + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); +@@ -2652,7 +2722,7 @@ drawregion(int x1, int y1, int x2, int y2) + continue; + + term.dirty[y] = 0; +- xdrawline(term.line[y], x1, y, x2); ++ xdrawline(TLINE(y), x1, y, x2); + } + } + +@@ -2673,8 +2743,9 @@ draw(void) + cx--; + + drawregion(0, 0, term.col, term.row); +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]); ++ if (term.scr == 0) ++ xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], ++ term.ocx, term.ocy, term.line[term.ocy][term.ocx]); + term.ocx = cx; + term.ocy = term.c.y; + xfinishdraw(); +diff --git a/st.h b/st.h +index 519b9bd..da36b34 100644 +--- a/st.h ++++ b/st.h +@@ -81,6 +81,8 @@ void die(const char *, ...); + void redraw(void); + void draw(void); + ++void kscrolldown(const Arg *); ++void kscrollup(const Arg *); + void printscreen(const Arg *); + void printsel(const Arg *); + void sendbreak(const Arg *);