jcs's openbsd hax
openbsd

Completely rewrite rewindow(), the function that chooses a new starting byte for the editing window when the cursor leaves the editing window to the right or to the left side.

The main reason for the rewrite is that UTF-8 handling was broken and would
often start the editing window on a UTF-8 continuation byte, sending UTF-8
syntax errors to the terminal. Besides, the function was unnecessarily
complicated (iterating twice) and inefficient (iterating the whole buffer
from the beginning even when the cursor was far out to the right) without
even being exact (picking a bit position that brings the cursor near the
middle of the window, but nor always as close to the middle as possible).
The old algorithm was rougly O(L + 2*W), where W is half of the window
width, and L is the amount of text left of the window.

Instead, pick an algorithm that is simpler, more robust, and faster
than before, but avoid complication by still being content with an
approximate solution that can be implemented with a simple O(W)
backwards linear search. Do not go overboard with an O(W log W)
binary search or an O(W^2) linear search, either of which would
be required to find the optimal position.

OK millert@

schwarze 3a33cb1c 2834b6af

+53 -17
+53 -17
bin/ksh/vi.c
··· 1 - /* $OpenBSD: vi.c,v 1.66 2025/05/19 14:27:38 schwarze Exp $ */ 1 + /* $OpenBSD: vi.c,v 1.67 2025/07/20 21:24:07 schwarze Exp $ */ 2 2 3 3 /* 4 4 * vi command editing ··· 1789 1789 static void 1790 1790 rewindow(void) 1791 1791 { 1792 - int tcur, tcol; 1793 - int holdcur1, holdcol1; 1794 - int holdcur2, holdcol2; 1792 + int cur; /* byte# in the main command line buffer */ 1793 + int col; /* corresponding display column */ 1794 + int tabc; /* columns a tab character can take up */ 1795 + int thisc; /* columns the current character requires */ 1796 + unsigned char uc; /* the current byte */ 1795 1797 1796 - holdcur1 = holdcur2 = tcur = 0; 1797 - holdcol1 = holdcol2 = tcol = 0; 1798 - while (tcur < es->cursor) { 1799 - if (tcol - holdcol2 > winwidth / 2) { 1800 - holdcur1 = holdcur2; 1801 - holdcol1 = holdcol2; 1802 - holdcur2 = tcur; 1803 - holdcol2 = tcol; 1798 + /* The desired cursor position is near the middle of the window. */ 1799 + cur = es->cursor; 1800 + col = winwidth / 2; 1801 + tabc = 0; 1802 + 1803 + /* Step left to find the desired left margin. */ 1804 + while (cur > 0 && col > 0) { 1805 + uc = es->cbuf[--cur]; 1806 + 1807 + /* Never start the window on a continuation byte. */ 1808 + if (isu8cont(uc)) 1809 + continue; 1810 + 1811 + if (uc == '\t') { 1812 + /* 1813 + * If two tabs occur close together, 1814 + * count the right one, including optional 1815 + * characters between both, as 8 columns. 1816 + */ 1817 + if (tabc > 0) { 1818 + col -= 8; 1819 + /* Prefer starting after a tab. */ 1820 + if (col <= 0) 1821 + cur++; 1822 + } 1823 + 1824 + /* 1825 + * A tab can be preceded by up to 7 characters 1826 + * without taking up additional space. 1827 + */ 1828 + tabc = 7; 1829 + continue; 1804 1830 } 1805 - tcol = newcol((unsigned char) es->cbuf[tcur++], tcol); 1831 + thisc = char_len(uc); 1832 + if (tabc > 0) { 1833 + if (tabc > thisc) { 1834 + /* The character still fits in the tab. */ 1835 + tabc -= thisc; 1836 + continue; 1837 + } 1838 + col -= 8; /* The tab is now full. */ 1839 + thisc -= tabc; /* This may produce overflow. */ 1840 + tabc = 0; 1841 + } 1842 + 1843 + /* Handle a normal character or the overflow. */ 1844 + col -= thisc; 1806 1845 } 1807 - while (tcol - holdcol1 > winwidth / 2) 1808 - holdcol1 = newcol((unsigned char) es->cbuf[holdcur1++], 1809 - holdcol1); 1810 - es->winleft = holdcur1; 1846 + es->winleft = cur; 1811 1847 } 1812 1848 1813 1849 /* Printing the byte ch at display column col moves to which column? */