st-vimBrowse-20191203-2b8333f.diff - sites - public wiki contents of suckless.o… | |
git clone git://git.suckless.org/sites | |
Log | |
Files | |
Refs | |
--- | |
st-vimBrowse-20191203-2b8333f.diff (45812B) | |
--- | |
1 From de020f0c06440fd19a36e1b001ef9e0058f73369 Mon Sep 17 00:00:00 2001 | |
2 From: Julius Huelsmann <[email protected]> | |
3 Date: Thu, 7 Nov 2019 09:08:49 +0100 | |
4 Subject: [PATCH 1/2] [PATCH:VIM]: first version | |
5 | |
6 --- | |
7 Makefile | 6 +- | |
8 config.def.h | 27 ++ | |
9 dynamicArray.h | 90 ++++++ | |
10 st.c | 794 +++++++++++++++++++++++++++++++++++++++++++++---- | |
11 st.h | 31 +- | |
12 win.h | 2 + | |
13 x.c | 51 +++- | |
14 7 files changed, 936 insertions(+), 65 deletions(-) | |
15 create mode 100644 dynamicArray.h | |
16 | |
17 diff --git a/Makefile b/Makefile | |
18 index 470ac86..7d93347 100644 | |
19 --- a/Makefile | |
20 +++ b/Makefile | |
21 @@ -21,8 +21,8 @@ config.h: | |
22 .c.o: | |
23 $(CC) $(STCFLAGS) -c $< | |
24 | |
25 -st.o: config.h st.h win.h | |
26 -x.o: arg.h config.h st.h win.h | |
27 +st.o: config.h st.h win.h dynamicArray.h | |
28 +x.o: arg.h config.h st.h win.h dynamicArray.h | |
29 | |
30 $(OBJ): config.h config.mk | |
31 | |
32 @@ -35,7 +35,7 @@ clean: | |
33 dist: clean | |
34 mkdir -p st-$(VERSION) | |
35 cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ | |
36 - config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ | |
37 + config.def.h st.info st.1 arg.h st.h win.h dynamicArray… | |
38 st-$(VERSION) | |
39 tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz | |
40 rm -rf st-$(VERSION) | |
41 diff --git a/config.def.h b/config.def.h | |
42 index 6ebea98..1b0e501 100644 | |
43 --- a/config.def.h | |
44 +++ b/config.def.h | |
45 @@ -149,6 +149,12 @@ static unsigned int mousebg = 0; | |
46 * doesn't match the ones requested. | |
47 */ | |
48 static unsigned int defaultattr = 11; | |
49 +/// Colors for the entities that are highlighted in normal mode. | |
50 +static unsigned int highlightBg = 160; | |
51 +static unsigned int highlightFg = 15; | |
52 +/// Colors for the line and column that is marked 'current' in normal m… | |
53 +static unsigned int currentBg = 0; | |
54 +static unsigned int currentFg = 15; | |
55 | |
56 /* | |
57 * Internal mouse shortcuts. | |
58 @@ -162,10 +168,12 @@ static MouseShortcut mshortcuts[] = { | |
59 | |
60 /* Internal keyboard shortcuts. */ | |
61 #define MODKEY Mod1Mask | |
62 +#define AltMask Mod1Mask | |
63 #define TERMMOD (ControlMask|ShiftMask) | |
64 | |
65 static Shortcut shortcuts[] = { | |
66 /* mask keysym function argumen… | |
67 + { AltMask, XK_c, normalMode, {.i = … | |
68 { XK_ANY_MOD, XK_Break, sendbreak, {.i = … | |
69 { ControlMask, XK_Print, toggleprinter, {.i = … | |
70 { ShiftMask, XK_Print, printscreen, {.i = … | |
71 @@ -178,6 +186,8 @@ static Shortcut shortcuts[] = { | |
72 { TERMMOD, XK_Y, selpaste, {.i = … | |
73 { ShiftMask, XK_Insert, selpaste, {.i = … | |
74 { TERMMOD, XK_Num_Lock, numlock, {.i = … | |
75 + { ShiftMask, XK_Page_Up, kscrollup, {.i = -… | |
76 + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -… | |
77 }; | |
78 | |
79 /* | |
80 @@ -456,3 +466,20 @@ static char ascii_printable[] = | |
81 " !\"#$%&'()*+,-./0123456789:;<=>?" | |
82 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" | |
83 "`abcdefghijklmnopqrstuvwxyz{|}~"; | |
84 + | |
85 + | |
86 +/// word sepearors normal mode | |
87 +char wordDelimSmall[] = " \t!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; | |
88 +char wordDelimLarge[] = " \t"; /// <Word sepearors normal mode (capital… | |
89 + | |
90 +/// Shortcusts executed in normal mode (which should not already be in … | |
91 +struct NormalModeShortcuts normalModeShortcuts [] = { | |
92 + { 'C', "?Building\n" }, | |
93 + { 'c', "/Building\n" }, | |
94 + { 'F', "?: error:\n" }, | |
95 + { 'f', "/: error:\n" }, | |
96 + { 'X', "?juli@machine\n" }, | |
97 + { 'x', "/juli@machine\n" }, | |
98 +}; | |
99 + | |
100 +size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / … | |
101 diff --git a/dynamicArray.h b/dynamicArray.h | |
102 new file mode 100644 | |
103 index 0000000..c65fbef | |
104 --- /dev/null | |
105 +++ b/dynamicArray.h | |
106 @@ -0,0 +1,90 @@ | |
107 +#include <stdint.h> | |
108 +#include <assert.h> | |
109 +#include <stdlib.h> | |
110 +#include <string.h> | |
111 +#include <stdbool.h> | |
112 + | |
113 +/// Struct for which this file offers functionality in order to expand … | |
114 +/// and set / get its content. | |
115 +typedef struct DynamicArray { | |
116 + uint8_t itemSize; | |
117 + uint32_t index; | |
118 + uint32_t allocated; | |
119 + char* content; | |
120 +} DynamicArray; | |
121 + | |
122 +#define EXPAND_STEP 15 | |
123 + | |
124 +/// Default initializers for the dynamic array. | |
125 +#define CHAR_ARRAY {1, 0, 0, NULL} | |
126 +#define WORD_ARRAY {2, 0, 0, NULL} | |
127 +#define DWORD_ARRAY {4, 0, 0, NULL} | |
128 +#define QWORD_ARRAY {8, 0, 0, NULL} | |
129 +/// (Wasteful) utf-8 array, that always used 4 bytes in order to displa… | |
130 +/// even if the space is not required. | |
131 +#define UTF8_ARRAY DWORD_ARRAY | |
132 + | |
133 + | |
134 +inline char* | |
135 +gnext(DynamicArray *s) { return &s->content[s->index+=s->itemSize]; } | |
136 + | |
137 +inline char* | |
138 +get(DynamicArray const * s) { return &s->content[s->index]; } | |
139 + | |
140 +inline char* | |
141 +view(DynamicArray const * s, uint32_t i) { | |
142 + return s->content + i*s->itemSize; | |
143 +} | |
144 + | |
145 +inline char * | |
146 +viewEnd(DynamicArray const *s, uint32_t i) { | |
147 + return s->content + s->index - (i + 1) * s->itemSize; | |
148 +} | |
149 + | |
150 +inline void | |
151 +set(DynamicArray* s, char const *vals, uint8_t amount) { | |
152 + assert(amount <= s->itemSize); | |
153 + memcpy(s->content + s->index, vals, amount); | |
154 +} | |
155 + | |
156 +inline void | |
157 +snext(DynamicArray* s, char const *vals, uint8_t amount) { | |
158 + set(s, vals, amount); | |
159 + s->index+=s->itemSize; | |
160 +} | |
161 + | |
162 +inline void | |
163 +empty(DynamicArray* s) { s->index = 0; } | |
164 + | |
165 +inline bool | |
166 +isEmpty(DynamicArray* s) { return s->index == 0; } | |
167 + | |
168 +inline uint32_t | |
169 +size(DynamicArray const * s) { return s->index / s->itemSize; } | |
170 + | |
171 +inline void | |
172 +pop(DynamicArray* s) { s->index -= s->itemSize; } | |
173 + | |
174 +inline void checkSetNext(DynamicArray *s, char const *c, uint8_t amount… | |
175 + if (s->index + s->itemSize >= s->allocated) { | |
176 + if ((s->content = (char *)realloc( | |
177 + s->content, s->allocate… | |
178 + exit(1); | |
179 + }; | |
180 + } | |
181 + if (amount) { snext(s, c, amount); } | |
182 +} | |
183 + | |
184 +char *checkGetNext(DynamicArray *s) { | |
185 + if (s->index + s->itemSize >= s->allocated) { | |
186 + if ((s->content = (char *)realloc( | |
187 + s->content, s->allocate… | |
188 + exit(1); | |
189 + }; | |
190 + } | |
191 + s->index+=s->itemSize; | |
192 + return viewEnd(s, 0); | |
193 +} | |
194 + | |
195 +#define append(s, c) checkSetNext((s), (char const *) (c), (s)->itemSiz… | |
196 +#define appendPartial(s, c, i) checkSetNext((s), (char const *) (c), (i… | |
197 diff --git a/st.c b/st.c | |
198 index ede7ae6..27bfca8 100644 | |
199 --- a/st.c | |
200 +++ b/st.c | |
201 @@ -1,8 +1,10 @@ | |
202 /* See LICENSE for license details. */ | |
203 +#include <assert.h> | |
204 #include <ctype.h> | |
205 #include <errno.h> | |
206 #include <fcntl.h> | |
207 #include <limits.h> | |
208 +#include <math.h> | |
209 #include <pwd.h> | |
210 #include <stdarg.h> | |
211 #include <stdio.h> | |
212 @@ -17,8 +19,10 @@ | |
213 #include <unistd.h> | |
214 #include <wchar.h> | |
215 | |
216 + | |
217 #include "st.h" | |
218 #include "win.h" | |
219 +#include "dynamicArray.h" | |
220 | |
221 #if defined(__linux) | |
222 #include <pty.h> | |
223 @@ -35,6 +39,8 @@ | |
224 #define ESC_ARG_SIZ 16 | |
225 #define STR_BUF_SIZ ESC_BUF_SIZ | |
226 #define STR_ARG_SIZ ESC_ARG_SIZ | |
227 +//#define HISTSIZE 100 | |
228 +#define HISTSIZE 2000 | |
229 | |
230 /* macros */ | |
231 #define IS_SET(flag) ((term.mode & (flag)) != 0) | |
232 @@ -42,6 +48,9 @@ | |
233 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) | |
234 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) | |
235 #define ISDELIM(u) (u && wcschr(worddelimiters, u)) | |
236 +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term… | |
237 + term.scr + HISTSIZE + 1) % HISTSIZE] : \ | |
238 + term.line[(y) - term.scr]) | |
239 | |
240 enum term_mode { | |
241 MODE_WRAP = 1 << 0, | |
242 @@ -97,16 +106,18 @@ typedef struct { | |
243 int mode; | |
244 int type; | |
245 int snap; | |
246 - /* | |
247 - * Selection variables: | |
248 - * nb – normalized coordinates of the beginning of the select… | |
249 - * ne – normalized coordinates of the end of the selection | |
250 - * ob – original coordinates of the beginning of the selection | |
251 - * oe – original coordinates of the end of the selection | |
252 - */ | |
253 + /// Selection variables: | |
254 + /// ob – original coordinates of the beginning of the selecti… | |
255 + /// oe – original coordinates of the end of the selection | |
256 + struct { | |
257 + int x, y, scroll; | |
258 + } ob, oe; | |
259 + /// Selection variables; currently displayed chunk. | |
260 + /// nb – normalized coordinates of the beginning of the selec… | |
261 + /// ne – normalized coordinates of the end of the selection | |
262 struct { | |
263 int x, y; | |
264 - } nb, ne, ob, oe; | |
265 + } nb, ne; | |
266 | |
267 int alt; | |
268 } Selection; | |
269 @@ -117,6 +128,9 @@ typedef struct { | |
270 int col; /* nb col */ | |
271 Line *line; /* screen */ | |
272 Line *alt; /* alternate screen */ | |
273 + Line hist[HISTSIZE]; /* history buffer */ | |
274 + int histi; /* history index */ | |
275 + int scr; /* scroll back */ | |
276 int *dirty; /* dirtyness of lines */ | |
277 TCursor c; /* cursor */ | |
278 int ocx; /* old cursor col */ | |
279 @@ -152,6 +166,50 @@ typedef struct { | |
280 int narg; /* nb of args */ | |
281 } STREscape; | |
282 | |
283 +/// Position (x, y , and current scroll in the y dimension). | |
284 +typedef struct Position { | |
285 + uint32_t x; | |
286 + uint32_t y; | |
287 + uint32_t yScr; | |
288 +} Position; | |
289 + | |
290 +/// The entire normal mode state, consisting of an operation | |
291 +/// and a motion. | |
292 +struct NormalModeState { | |
293 + Position initialPosition; | |
294 + // Operation: | |
295 + struct OperationState { | |
296 + enum Operation { | |
297 + noop, | |
298 + visual, | |
299 + visualLine, | |
300 + yank | |
301 + } op; | |
302 + Position startPosition; | |
303 + } command; | |
304 + // Motions: | |
305 + struct MotionState { | |
306 + uint32_t amount; | |
307 + enum Search { | |
308 + none, | |
309 + forward, | |
310 + backward, | |
311 + } search; | |
312 + Position searchPosition; | |
313 + bool finished; | |
314 + } motion; | |
315 +} stateNormalMode; | |
316 + | |
317 + | |
318 +DynamicArray searchString = UTF8_ARRAY; | |
319 +DynamicArray commandHist0 = UTF8_ARRAY; | |
320 +DynamicArray commandHist1 = UTF8_ARRAY; | |
321 +DynamicArray highlights = QWORD_ARRAY; | |
322 +/// History command toggle | |
323 +bool toggle = false; | |
324 +#define currentCommand toggle ? &commandHist0 : &commandHist1 | |
325 +#define lastCommand toggle ? &commandHist1 : &commandHist0 | |
326 + | |
327 static void execsh(char *, char **); | |
328 static void stty(char **); | |
329 static void sigchld(int); | |
330 @@ -184,8 +242,8 @@ static void tnewline(int); | |
331 static void tputtab(int); | |
332 static void tputc(Rune); | |
333 static void treset(void); | |
334 -static void tscrollup(int, int); | |
335 -static void tscrolldown(int, int); | |
336 +static void tscrollup(int, int, int); | |
337 +static void tscrolldown(int, int, int); | |
338 static void tsetattr(int *, int); | |
339 static void tsetchar(Rune, Glyph *, int, int); | |
340 static void tsetdirt(int, int); | |
341 @@ -231,6 +289,12 @@ static uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0x… | |
342 static Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10… | |
343 static Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10F… | |
344 | |
345 +void applyPosition(Position const *pos) { | |
346 + term.c.x = pos->x; | |
347 + term.c.y = pos->y; | |
348 + term.scr = pos->yScr; | |
349 +} | |
350 + | |
351 ssize_t | |
352 xwrite(int fd, const char *s, size_t len) | |
353 { | |
354 @@ -409,17 +473,22 @@ tlinelen(int y) | |
355 { | |
356 int i = term.col; | |
357 | |
358 - if (term.line[y][i - 1].mode & ATTR_WRAP) | |
359 + if (TLINE(y)[i - 1].mode & ATTR_WRAP) | |
360 return i; | |
361 | |
362 - while (i > 0 && term.line[y][i - 1].u == ' ') | |
363 + while (i > 0 && TLINE(y)[i - 1].u == ' ') | |
364 --i; | |
365 | |
366 return i; | |
367 } | |
368 | |
369 void | |
370 -selstart(int col, int row, int snap) | |
371 +xselstart(int col, int row, int snap) { | |
372 + selstart(col, row, term.scr, snap); | |
373 +} | |
374 + | |
375 +void | |
376 +selstart(int col, int row, int scroll, int snap) | |
377 { | |
378 selclear(); | |
379 sel.mode = SEL_EMPTY; | |
380 @@ -428,6 +497,7 @@ selstart(int col, int row, int snap) | |
381 sel.snap = snap; | |
382 sel.oe.x = sel.ob.x = col; | |
383 sel.oe.y = sel.ob.y = row; | |
384 + sel.oe.scroll = sel.ob.scroll = scroll; | |
385 selnormalize(); | |
386 | |
387 if (sel.snap != 0) | |
388 @@ -436,10 +506,13 @@ selstart(int col, int row, int snap) | |
389 } | |
390 | |
391 void | |
392 -selextend(int col, int row, int type, int done) | |
393 -{ | |
394 - int oldey, oldex, oldsby, oldsey, oldtype; | |
395 +xselextend(int col, int row, int type, int done) { | |
396 + selextend(col, row, term.scr, type, done); | |
397 +} | |
398 | |
399 +void | |
400 +selextend(int col, int row, int scroll, int type, int done) | |
401 +{ | |
402 if (sel.mode == SEL_IDLE) | |
403 return; | |
404 if (done && sel.mode == SEL_EMPTY) { | |
405 @@ -447,18 +520,22 @@ selextend(int col, int row, int type, int done) | |
406 return; | |
407 } | |
408 | |
409 - oldey = sel.oe.y; | |
410 - oldex = sel.oe.x; | |
411 - oldsby = sel.nb.y; | |
412 - oldsey = sel.ne.y; | |
413 - oldtype = sel.type; | |
414 + int const oldey = sel.oe.y; | |
415 + int const oldex = sel.oe.x; | |
416 + int const oldscroll = sel.oe.scroll; | |
417 + int const oldsby = sel.nb.y; | |
418 + int const oldsey = sel.ne.y; | |
419 + int const oldtype = sel.type; | |
420 | |
421 sel.oe.x = col; | |
422 sel.oe.y = row; | |
423 + sel.oe.scroll = scroll; | |
424 + | |
425 selnormalize(); | |
426 sel.type = type; | |
427 | |
428 - if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.ty… | |
429 + if (oldey != sel.oe.y || oldex != sel.oe.x || oldscroll != sel.… | |
430 + || oldtype != sel.type || sel.mode == SEL_EMPTY) | |
431 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); | |
432 | |
433 sel.mode = done ? SEL_IDLE : SEL_READY; | |
434 @@ -467,17 +544,21 @@ selextend(int col, int row, int type, int done) | |
435 void | |
436 selnormalize(void) | |
437 { | |
438 - int i; | |
439 - | |
440 - if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { | |
441 - sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; | |
442 - sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; | |
443 + sel.nb.y = INTERVAL(sel.ob.y + term.scr - sel.ob.scroll, 0, ter… | |
444 + sel.ne.y = INTERVAL(sel.oe.y + term.scr - sel.oe.scroll, 0, ter… | |
445 + if (sel.type == SEL_REGULAR && sel.nb.y != sel.ne.y) { | |
446 + sel.nb.x = sel.nb.y < sel.ne.y ? sel.ob.x : sel.oe.x; | |
447 + sel.ne.x = sel.nb.y < sel.ne.y ? sel.oe.x : sel.ob.x; | |
448 } else { | |
449 sel.nb.x = MIN(sel.ob.x, sel.oe.x); | |
450 sel.ne.x = MAX(sel.ob.x, sel.oe.x); | |
451 } | |
452 - sel.nb.y = MIN(sel.ob.y, sel.oe.y); | |
453 - sel.ne.y = MAX(sel.ob.y, sel.oe.y); | |
454 + | |
455 + if (sel.nb.y > sel.ne.y) { | |
456 + int32_t const tmp = sel.nb.y; | |
457 + sel.nb.y = sel.ne.y; | |
458 + sel.ne.y = tmp; | |
459 + } | |
460 | |
461 selsnap(&sel.nb.x, &sel.nb.y, -1); | |
462 selsnap(&sel.ne.x, &sel.ne.y, +1); | |
463 @@ -485,7 +566,7 @@ selnormalize(void) | |
464 /* expand selection over line breaks */ | |
465 if (sel.type == SEL_RECTANGULAR) | |
466 return; | |
467 - i = tlinelen(sel.nb.y); | |
468 + int i = tlinelen(sel.nb.y); | |
469 if (i < sel.nb.x) | |
470 sel.nb.x = i; | |
471 if (tlinelen(sel.ne.y) <= sel.ne.x) | |
472 @@ -521,7 +602,7 @@ selsnap(int *x, int *y, int direction) | |
473 * Snap around if the word wraps around at the end or | |
474 * beginning of a line. | |
475 */ | |
476 - prevgp = &term.line[*y][*x]; | |
477 + prevgp = &TLINE(*y)[*x]; | |
478 prevdelim = ISDELIM(prevgp->u); | |
479 for (;;) { | |
480 newx = *x + direction; | |
481 @@ -536,14 +617,14 @@ selsnap(int *x, int *y, int direction) | |
482 yt = *y, xt = *x; | |
483 else | |
484 yt = newy, xt = newx; | |
485 - if (!(term.line[yt][xt].mode & ATTR_WRA… | |
486 + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) | |
487 break; | |
488 } | |
489 | |
490 if (newx >= tlinelen(newy)) | |
491 break; | |
492 | |
493 - gp = &term.line[newy][newx]; | |
494 + gp = &TLINE(newy)[newx]; | |
495 delim = ISDELIM(gp->u); | |
496 if (!(gp->mode & ATTR_WDUMMY) && (delim != prev… | |
497 || (delim && gp->u != prevgp->u… | |
498 @@ -564,14 +645,14 @@ selsnap(int *x, int *y, int direction) | |
499 *x = (direction < 0) ? 0 : term.col - 1; | |
500 if (direction < 0) { | |
501 for (; *y > 0; *y += direction) { | |
502 - if (!(term.line[*y-1][term.col-1].mode | |
503 + if (!(TLINE(*y-1)[term.col-1].mode | |
504 & ATTR_WRAP)) { | |
505 break; | |
506 } | |
507 } | |
508 } else if (direction > 0) { | |
509 for (; *y < term.row-1; *y += direction) { | |
510 - if (!(term.line[*y][term.col-1].mode | |
511 + if (!(TLINE(*y)[term.col-1].mode | |
512 & ATTR_WRAP)) { | |
513 break; | |
514 } | |
515 @@ -591,24 +672,32 @@ getsel(void) | |
516 if (sel.ob.x == -1) | |
517 return NULL; | |
518 | |
519 - bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; | |
520 + int32_t syb = sel.ob.y - sel.ob.scroll + term.scr; | |
521 + int32_t sye = sel.oe.y - sel.oe.scroll + term.scr; | |
522 + if (syb > sye) { | |
523 + int32_t tmp = sye; | |
524 + sye = syb; | |
525 + syb = tmp; | |
526 + } | |
527 + | |
528 + bufsize = (term.col+1) * (sye - syb + 1) * UTF_SIZ; | |
529 ptr = str = xmalloc(bufsize); | |
530 | |
531 /* append every set & selected glyph to the selection */ | |
532 - for (y = sel.nb.y; y <= sel.ne.y; y++) { | |
533 + for (y = syb; y <= sye; y++) { | |
534 if ((linelen = tlinelen(y)) == 0) { | |
535 *ptr++ = '\n'; | |
536 continue; | |
537 } | |
538 | |
539 if (sel.type == SEL_RECTANGULAR) { | |
540 - gp = &term.line[y][sel.nb.x]; | |
541 + gp = &TLINE(y)[sel.nb.x]; | |
542 lastx = sel.ne.x; | |
543 } else { | |
544 - gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0… | |
545 - lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; | |
546 + gp = &TLINE(y)[syb == y ? sel.nb.x : 0]; | |
547 + lastx = (sye == y) ? sel.ne.x : term.col-1; | |
548 } | |
549 - last = &term.line[y][MIN(lastx, linelen-1)]; | |
550 + last = &TLINE(y)[MIN(lastx, linelen-1)]; | |
551 while (last >= gp && last->u == ' ') | |
552 --last; | |
553 | |
554 @@ -831,6 +920,9 @@ void | |
555 ttywrite(const char *s, size_t n, int may_echo) | |
556 { | |
557 const char *next; | |
558 + Arg arg = (Arg) { .i = term.scr }; | |
559 + | |
560 + kscrolldown(&arg); | |
561 | |
562 if (may_echo && IS_SET(MODE_ECHO)) | |
563 twrite(s, n, 1); | |
564 @@ -1042,13 +1134,53 @@ tswapscreen(void) | |
565 } | |
566 | |
567 void | |
568 -tscrolldown(int orig, int n) | |
569 +kscrolldown(const Arg* a) | |
570 +{ | |
571 + int n = a->i; | |
572 + | |
573 + if (n < 0) | |
574 + n = term.row + n; | |
575 + | |
576 + if (n > term.scr) | |
577 + n = term.scr; | |
578 + | |
579 + if (term.scr > 0) { | |
580 + term.scr -= n; | |
581 + selscroll(0, -n); | |
582 + tfulldirt(); | |
583 + } | |
584 +} | |
585 + | |
586 +void | |
587 +kscrollup(const Arg* a) | |
588 +{ | |
589 + int n = a->i; | |
590 + | |
591 + if (n < 0) | |
592 + n = term.row + n; | |
593 + | |
594 + if (term.scr <= HISTSIZE-n) { | |
595 + term.scr += n; | |
596 + selscroll(0, n); | |
597 + tfulldirt(); | |
598 + } | |
599 +} | |
600 + | |
601 +void | |
602 +tscrolldown(int orig, int n, int copyhist) | |
603 { | |
604 int i; | |
605 Line temp; | |
606 | |
607 LIMIT(n, 0, term.bot-orig+1); | |
608 | |
609 + if (copyhist) { | |
610 + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; | |
611 + temp = term.hist[term.histi]; | |
612 + term.hist[term.histi] = term.line[term.bot]; | |
613 + term.line[term.bot] = temp; | |
614 + } | |
615 + | |
616 tsetdirt(orig, term.bot-n); | |
617 tclearregion(0, term.bot-n+1, term.col-1, term.bot); | |
618 | |
619 @@ -1062,13 +1194,23 @@ tscrolldown(int orig, int n) | |
620 } | |
621 | |
622 void | |
623 -tscrollup(int orig, int n) | |
624 +tscrollup(int orig, int n, int copyhist) | |
625 { | |
626 int i; | |
627 Line temp; | |
628 | |
629 LIMIT(n, 0, term.bot-orig+1); | |
630 | |
631 + if (copyhist) { | |
632 + term.histi = (term.histi + 1) % HISTSIZE; | |
633 + temp = term.hist[term.histi]; | |
634 + term.hist[term.histi] = term.line[orig]; | |
635 + term.line[orig] = temp; | |
636 + } | |
637 + | |
638 + if (term.scr > 0 && term.scr < HISTSIZE) | |
639 + term.scr = MIN(term.scr + n, HISTSIZE-1); | |
640 + | |
641 tclearregion(0, orig, term.col-1, orig+n-1); | |
642 tsetdirt(orig+n, term.bot); | |
643 | |
644 @@ -1088,6 +1230,7 @@ selscroll(int orig, int n) | |
645 return; | |
646 | |
647 if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig… | |
648 + sel.oe.scroll = sel.ob.scroll = term.scr; | |
649 if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < ter… | |
650 selclear(); | |
651 return; | |
652 @@ -1117,13 +1260,544 @@ tnewline(int first_col) | |
653 int y = term.c.y; | |
654 | |
655 if (y == term.bot) { | |
656 - tscrollup(term.top, 1); | |
657 + tscrollup(term.top, 1, 1); | |
658 } else { | |
659 y++; | |
660 } | |
661 tmoveto(first_col ? 0 : term.c.x, y); | |
662 } | |
663 | |
664 +int | |
665 +currentLine(int x, int y) | |
666 +{ | |
667 + return (x == term.c.x || y == term.c.y); | |
668 +} | |
669 + | |
670 +int | |
671 +highlighted(int x, int y) | |
672 +{ | |
673 + // Compute the legal bounds for a hit: | |
674 + int32_t const stringSize = size(&searchString); | |
675 + int32_t xMin = x - stringSize; | |
676 + int32_t yMin = y; | |
677 + while (xMin < 0 && yMin > 0) { //< I think this temds to be mor… | |
678 + xMin += term.col; // division + modulo. | |
679 + --yMin; | |
680 + } | |
681 + if (xMin < 0) { xMin = 0; } | |
682 + | |
683 + uint32_t highSize = size(&highlights); | |
684 + uint32_t *ptr = (uint32_t*) highlights.content; | |
685 + for (uint32_t i = 0; i < highSize; ++i) { | |
686 + int32_t const sx = *(ptr++); | |
687 + int32_t const sy = *(ptr++); | |
688 + if (BETWEEN(sy, yMin, y) && (sy != yMin || sx > xMin) &… | |
689 + return true; | |
690 + } | |
691 + } | |
692 + return false; | |
693 +} | |
694 + | |
695 +int mod(int a, int b) { | |
696 + while (a < 0) { | |
697 + a+= b; | |
698 + } | |
699 + return a % b; | |
700 +} | |
701 + | |
702 +void displayString(DynamicArray const *str, Glyph *g, int yPos) { | |
703 + // Threshold: if there is nothing or no space to print, do not … | |
704 + if (term.col == 0 || str->index == 0) { | |
705 + term.dirty[yPos] = 1; //< mark this line as 'dirty', be… | |
706 + // marked dirty when scrolling due to string display. | |
707 + return; | |
708 + } | |
709 + | |
710 + uint32_t lineSize = MIN(size(str), term.col / 3); | |
711 + uint32_t xEnd = term.col - 1; | |
712 + assert(lineSize <= 1 + xEnd); //< as lineSize <= term.col/3 <= … | |
713 + uint32_t xStart = 1 + xEnd - lineSize; | |
714 + | |
715 + Line line = malloc(sizeof(Glyph) * lineSize); | |
716 + assert(str->index - 1 >= lineSize - 1); //< lineSize <= str->i… | |
717 + | |
718 + for (uint32_t lineIdx = 0; lineIdx < lineSize; lineIdx++) { | |
719 + line[lineIdx] = *g; | |
720 + char* end = viewEnd(str, lineSize - lineIdx - 1); | |
721 + memcpy(&line[lineIdx].u, end, str->itemSize); | |
722 + } | |
723 + xdrawline(TLINE(yPos), 0, yPos, xStart); | |
724 + xdrawline(line -xStart, xStart, yPos, xEnd+1); | |
725 + free(line); // that sucks. | |
726 +} | |
727 + | |
728 +/// Print either the current command or the last comman din case the cu… | |
729 +void printCommandString() { | |
730 + Glyph g = {'c', ATTR_ITALIC | ATTR_FAINT , defaultfg, defaultbg… | |
731 + if (term.c.y == term.row-1) { g.mode ^= ATTR_CURRENT; } //< don… | |
732 + DynamicArray * cc = currentCommand; | |
733 + displayString(isEmpty(cc) ? lastCommand : cc, &g, term.row - 1); | |
734 + //displayString(lastCommand, &g, term.row - 2); | |
735 +} | |
736 + | |
737 +void printSearchString() { | |
738 + Glyph g = {'c', ATTR_ITALIC | ATTR_BOLD_FAINT, defaultfg, defau… | |
739 + if (term.c.y == term.row-2) { g.mode ^= ATTR_CURRENT; } //< don… | |
740 + displayString(&searchString, &g, term.row - 2); | |
741 +} | |
742 + | |
743 +/// Default state if no operation is performed. | |
744 +struct NormalModeState defaultNormalMode = {{0,0,0}, {noop, {0, 0, 0}},… | |
745 + | |
746 +void enableMode(enum Operation o) { | |
747 + stateNormalMode.command.op = o; | |
748 + stateNormalMode.command.startPosition.x = term.c.x; | |
749 + stateNormalMode.command.startPosition.y = term.c.y; | |
750 + stateNormalMode.command.startPosition.yScr = term.scr; | |
751 +} | |
752 + | |
753 +bool normalModeEnabled = false; | |
754 + | |
755 +void onNormalModeStart() { | |
756 + normalModeEnabled = true; | |
757 +} | |
758 + | |
759 +void onNormalModeStop() { //XXX breaks if resized | |
760 + normalModeEnabled = false; | |
761 + applyPosition(&stateNormalMode.initialPosition); | |
762 +} | |
763 + | |
764 +void moveLine(int8_t sign) { | |
765 + if (sign == -1) { | |
766 + if (term.c.y-- == 0) { | |
767 + if (++term.scr == HISTSIZE) { | |
768 + term.c.y = term.row - 1; | |
769 + term.scr = 0; | |
770 + } else { | |
771 + term.c.y = 0; | |
772 + } | |
773 + } | |
774 + } else { | |
775 + term.c.x = 0; | |
776 + if (++term.c.y == term.row) { | |
777 + if (term.scr-- == 0) { | |
778 + term.c.y = 0; | |
779 + term.scr = HISTSIZE - 1; | |
780 + } else { | |
781 + term.c.y = term.row - 1; | |
782 + } | |
783 + } | |
784 + } | |
785 +} | |
786 + | |
787 +void moveLetter(int8_t sign) { | |
788 + term.c.x += sign; | |
789 + if (!BETWEEN(term.c.x, 0, term.col-1)) { | |
790 + if (term.c.x < 0) { | |
791 + term.c.x = term.col - 1; | |
792 + moveLine(sign); | |
793 + } else { | |
794 + term.c.x = 0; | |
795 + moveLine(sign); | |
796 + } | |
797 + } | |
798 +} | |
799 + | |
800 +bool contains (char ksym, char const * values, uint32_t amount) { | |
801 + for (uint32_t i = 0; i < amount; i++) { if (ksym == values[i]) … | |
802 + return false; | |
803 +} | |
804 + | |
805 + | |
806 +void terminateCommand(bool abort) { | |
807 + stateNormalMode.command = defaultNormalMode.command; //< clear … | |
808 + stateNormalMode.motion = defaultNormalMode.motion; | |
809 + selclear(); //< clear … | |
810 + | |
811 + if (!abort) { toggle = !toggle; } | |
812 + empty(currentCommand); | |
813 + | |
814 + printCommandString(); | |
815 + printSearchString(); | |
816 + //tsetdirt(0, term.row-3); | |
817 +} | |
818 +inline void exitCommand() { terminateCommand(false); } | |
819 +inline void abortCommand() { terminateCommand(true); } | |
820 + | |
821 +/// Go to next occurrence of string relative to the current location | |
822 +/// conduct search, starting at start pos | |
823 +bool | |
824 +gotoString(int8_t sign) { | |
825 + uint32_t findIndex = 0; | |
826 + uint32_t searchStringSize = size(&searchString); | |
827 + uint32_t const maxIteration = (HISTSIZE + term.row) * term.col … | |
828 + for (uint32_t cIteration = 0; findIndex < searchStringSize | |
829 + && cIteration ++ < maxIteration; moveLetter(sig… | |
830 + uint32_t const searchChar = *((uint32_t*)(sign == 1 ? v… | |
831 + : viewEnd(&searchString, findIn… | |
832 + | |
833 + uint32_t const fu = TLINE(term.c.y)[term.c.x].u; | |
834 + | |
835 + if (fu == searchChar) findIndex++; | |
836 + else findIndex = 0; | |
837 + } | |
838 + bool const found = findIndex == searchStringSize; | |
839 + if (found) { for (uint32_t i = 0; i < searchStringSize; i++) { … | |
840 + return found; | |
841 +} | |
842 + | |
843 +/// Find the next occurrence of a word | |
844 +bool | |
845 +gotoNextString(int8_t sign) { | |
846 + moveLetter(sign); | |
847 + return gotoString(sign); | |
848 +} | |
849 + | |
850 +/// Highlight all found strings on the current screen. | |
851 +void | |
852 +highlightStringOnScreen() { | |
853 + if (isEmpty(&searchString)) { return; } | |
854 + uint32_t const searchStringSize = size(&searchString); | |
855 + uint32_t findIndex = 0; | |
856 + uint32_t xStart, yStart; | |
857 + for (uint32_t y = 0; y < term.row; y++) { | |
858 + for (uint32_t x = 0; x < term.col; x++) { | |
859 + if (TLINE(y)[x].u == *((uint32_t*)(view(&search… | |
860 + if (findIndex++ == 0) { | |
861 + xStart = x; | |
862 + yStart = y; | |
863 + } | |
864 + if (findIndex == searchStringSize) { | |
865 + // mark selected | |
866 + append(&highlights, &xStart); | |
867 + append(&highlights, &yStart); | |
868 + | |
869 + findIndex = 0; | |
870 + term.dirty[yStart] = 1; | |
871 + } | |
872 + } else { | |
873 + findIndex = 0; | |
874 + } | |
875 + } | |
876 + } | |
877 +} | |
878 + | |
879 +void gotoStringAndHighlight(int8_t sign) { | |
880 + bool const found = gotoString(sign); //< find the next string … | |
881 + empty(&highlights); //< remove previous highlights | |
882 + if (found) { //< apply new highlights … | |
883 + //if (sign == -1) { moveLetter(-1); } | |
884 + highlightStringOnScreen(sign); | |
885 + } else { //< go to the position wh… | |
886 + applyPosition(&stateNormalMode.motion.searchPosition); | |
887 + } | |
888 + tsetdirt(0, term.row-3); //< repaint everything ex… | |
889 + // is painted separately. | |
890 +} | |
891 + | |
892 +void pressKeys(char const* nullTerminatedString) { | |
893 + size_t end; | |
894 + for (size_t i = 0, end=strlen(nullTerminatedString); i < end; +… | |
895 + if (nullTerminatedString[i] == '\n') { | |
896 + kpressNormalMode(&nullTerminatedString[i], 0, f… | |
897 + } else { | |
898 + kpressNormalMode(&nullTerminatedString[i], 1, f… | |
899 + } | |
900 + } | |
901 +} | |
902 + | |
903 +void executeCommand(DynamicArray const *command) { | |
904 + size_t end; | |
905 + char decoded [32]; | |
906 + for (size_t i = 0, end=size(command); i < end; ++i) { | |
907 + size_t len = utf8encode(*((Rune*)view(command, i)) , de… | |
908 + kpressNormalMode(decoded, len, false, false, false); | |
909 + } | |
910 + //kpressNormalMode(NULL, 0, false, true, false); | |
911 +} | |
912 + | |
913 +void kpressNormalMode(char const * ksym, uint32_t len, bool esc, bool e… | |
914 + // [ESC] or [ENTER] abort resp. finish the current operation or | |
915 + // the Normal Mode if no operation is currently executed. | |
916 + if (esc || enter) { | |
917 + if (stateNormalMode.command.op == noop | |
918 + && stateNormalMode.motion.search == none | |
919 + && stateNormalMode.motion.amount == 0) { | |
920 + terminateCommand(!enter); | |
921 + empty(&highlights); | |
922 + tfulldirt(); // < this also removes the search … | |
923 + normalMode(NULL); | |
924 + } else { | |
925 + if (enter && stateNormalMode.motion.search != n… | |
926 + exitCommand(); //stateNormalMode.motion… | |
927 + return; | |
928 + } else { | |
929 + abortCommand(); | |
930 + } | |
931 + } | |
932 + return; | |
933 + } //< ! (esc || enter) | |
934 + // Search: append to search string & conduct search for best hi… | |
935 + // highlighting all other occurrences on the current pa… | |
936 + if (stateNormalMode.motion.search != none && !stateNormalMode.m… | |
937 + int8_t const sign = stateNormalMode.motion.search == fo… | |
938 + // Apply start position. | |
939 + if (backspace) { // XXX: if a quantifier is subject to … | |
940 + // from the command string. | |
941 + if (!isEmpty(currentCommand) && !isEmpty(&searc… | |
942 + pop(currentCommand); | |
943 + pop(&searchString); | |
944 + } else if (isEmpty(currentCommand) || isEmpty(&… | |
945 + empty(&highlights); | |
946 + stateNormalMode.motion = defaultNormalM… | |
947 + selclear(); … | |
948 + return; … | |
949 + } | |
950 + applyPosition(&stateNormalMode.motion.searchPos… | |
951 + } else { | |
952 + if (len > 0) { | |
953 + char* kSearch = checkGetNext(&searchStr… | |
954 + utf8decode(ksym, (Rune*)(kSearch), len); | |
955 + | |
956 + char* kCommand = checkGetNext(currentCo… | |
957 + utf8decode(ksym, (Rune*)(kCommand), len… | |
958 + } | |
959 + } | |
960 + if (sign == -1) { moveLetter(1); } | |
961 + gotoStringAndHighlight(sign); //< go to the next occurr… | |
962 + // all occurrences curre… | |
963 + | |
964 + if (stateNormalMode.command.op == visual) { | |
965 + selextend(term.c.x, term.c.y, term.scr, sel.typ… | |
966 + } else if (stateNormalMode.command.op == visualLine) { | |
967 + selextend(term.col-1, term.c.y, term.scr, sel.t… | |
968 + } | |
969 + printCommandString(); | |
970 + printSearchString(); | |
971 + return; | |
972 + } | |
973 + | |
974 + if (len == 0) { return; } | |
975 + // V / v or y take precedence over movement commands. | |
976 + switch(ksym[0]) { | |
977 + case '.': | |
978 + { | |
979 + | |
980 + if (!isEmpty(currentCommand)) { toggle … | |
981 + executeCommand(lastCommand); | |
982 + } | |
983 + return; | |
984 + case 'y': //< Yank mode | |
985 + { | |
986 + char* kCommand = checkGetNext(currentCo… | |
987 + utf8decode(ksym, (Rune*)(kCommand), len… | |
988 + switch(stateNormalMode.command.op) { | |
989 + case noop: //< Start … | |
990 + enableMode(yank); | |
991 + selstart(term.c.x, term… | |
992 + empty(currentCommand); | |
993 + break; | |
994 + case visualLine: //< Comple… | |
995 + case visual: | |
996 + xsetsel(getsel()); … | |
997 + xclipcopy(); | |
998 + exitCommand(); … | |
999 + break; | |
1000 + case yank: //< Comple… | |
1001 + selstart(0, term.c.y, t… | |
1002 + uint32_t const origY = … | |
1003 + for (int32_t i = 0; i <… | |
1004 + selextend(term.col-1, t… | |
1005 + xsetsel(getsel()); | |
1006 + xclipcopy(); | |
1007 + term.c.y = origY; | |
1008 + exitCommand(); | |
1009 + } | |
1010 + } | |
1011 + printCommandString(); | |
1012 + printSearchString(); | |
1013 + return; | |
1014 + case 'v': //< Visual Mode: Toggle mode. | |
1015 + case 'V': | |
1016 + { | |
1017 + enum Operation mode = ksym[0] == 'v' ? … | |
1018 + bool assign = stateNormalMode.command.o… | |
1019 + abortCommand(); | |
1020 + if (assign) { | |
1021 + enableMode(mode); | |
1022 + char* kCommand = checkGetNext(c… | |
1023 + utf8decode(ksym, (Rune*)(kComma… | |
1024 + if (mode == visualLine) { | |
1025 + selstart(0, term.c.y, t… | |
1026 + selextend(term.col-1, t… | |
1027 + } else { | |
1028 + selstart(term.c.x, term… | |
1029 + } | |
1030 + } | |
1031 + } | |
1032 + return; | |
1033 + } | |
1034 + // Perform the movement. | |
1035 + int32_t sign = -1; //< whehter a command goes 'forward' (1) … | |
1036 + bool discard = false; //< discard input, as it does not have a … | |
1037 + switch(ksym[0]) { | |
1038 + case 'j': sign = 1; | |
1039 + case 'k': | |
1040 + term.c.y += sig… | |
1041 + break; | |
1042 + case 'H': term.c.y = 0; break; //< [numer]H … | |
1043 + case 'M': term.c.y = term.bot / 2; break; | |
1044 + case 'L': term.c.y = term.bot; break; //< [numer]L … | |
1045 + case 'G': //< a little different from vim, but in this… | |
1046 + applyPosition(&… | |
1047 + case 'l': sign = 1; | |
1048 + case 'h': | |
1049 + { | |
1050 + int32_t… | |
1051 + term.c.… | |
1052 + while (… | |
1053 + term.c.… | |
1054 + break; | |
1055 + } | |
1056 + case '0': | |
1057 + if (stateNormal… | |
1058 + else { discard … | |
1059 + break; | |
1060 + case '$': term.c.x = term.col-1; break; | |
1061 + case 'w': | |
1062 + case 'W': | |
1063 + case 'e': | |
1064 + case 'E': sign = 1; | |
1065 + case 'B': | |
1066 + case 'b': | |
1067 + { | |
1068 + bool co… | |
1069 + bool co… | |
1070 + char co… | |
1071 + uint32_… | |
1072 + bool co… | |
1073 + uint32_… | |
1074 + | |
1075 + // does… | |
1076 + // Line… | |
1077 + stateNo… | |
1078 + for (; … | |
1079 + … | |
1080 + … | |
1081 + … | |
1082 + … | |
1083 + … | |
1084 + … | |
1085 + … | |
1086 + … | |
1087 + … | |
1088 + … | |
1089 + } | |
1090 + break; | |
1091 + } | |
1092 + case '/': sign = 1; | |
1093 + case '?': | |
1094 + empty(&searchSt… | |
1095 + stateNormalMode… | |
1096 + stateNormalMode… | |
1097 + stateNormalMode… | |
1098 + stateNormalMode… | |
1099 + stateNormalMode… | |
1100 + break; | |
1101 + case 'n': sign = 1; | |
1102 + case 'N': | |
1103 + toggle = !toggl… | |
1104 + empty(currentCo… | |
1105 + if (stateNormal… | |
1106 + stateNo… | |
1107 + stateNo… | |
1108 + } | |
1109 + for (int32_t am… | |
1110 + if (sta… | |
1111 + moveLet… | |
1112 + gotoStr… | |
1113 + } | |
1114 + break; | |
1115 + case 't': | |
1116 + if (sel.type ==… | |
1117 + sel.typ… | |
1118 + } else { | |
1119 + sel.typ… | |
1120 + } | |
1121 + tsetdirt(sel.nb… | |
1122 + discard = true; | |
1123 + default: | |
1124 + discard = true; | |
1125 + } | |
1126 + bool const isNumber = len == 1 && BETWEEN(ksym[0], 48, 57); | |
1127 + if (isNumber) { //< record numbers | |
1128 + discard = false; | |
1129 + stateNormalMode.motion.amount = | |
1130 + MIN(SHRT_MAX, stateNormalMode.motion.amount * 1… | |
1131 + } else if (!discard) { | |
1132 + stateNormalMode.motion.amount = 0; | |
1133 + } | |
1134 + | |
1135 + if (discard) { | |
1136 + for (size_t i = 0; i < amountNormalModeShortcuts; ++i) { | |
1137 + if (ksym[0] == normalModeShortcuts[i].key) { | |
1138 + pressKeys(normalModeShortcuts[i].value); | |
1139 + } | |
1140 + } | |
1141 + } else { | |
1142 + char* kCommand = checkGetNext(currentCommand); | |
1143 + utf8decode(ksym, (Rune*)(kCommand), len); | |
1144 + | |
1145 + int diff = 0; | |
1146 + if (term.c.y > 0) { | |
1147 + if (term.c.y > term.bot) { | |
1148 + diff = term.bot - term.c.y; | |
1149 + term.c.y = term.bot; | |
1150 + } | |
1151 + } else { | |
1152 + if (term.c.y < 0) { | |
1153 + diff = -term.c.y; | |
1154 + term.c.y = 0; | |
1155 + } | |
1156 + } | |
1157 + | |
1158 + int const _newScr = term.scr + diff; | |
1159 + term.c.y = _newScr < 0 ? 0 : (_newScr >= HISTSIZE ? ter… | |
1160 + term.scr = mod(_newScr, HISTSIZE); | |
1161 + | |
1162 + if (!isEmpty(&highlights)) { | |
1163 + empty(&highlights); | |
1164 + highlightStringOnScreen(); | |
1165 + } | |
1166 + | |
1167 + tsetdirt(0, term.row-3); | |
1168 + printCommandString(); | |
1169 + printSearchString(); | |
1170 + | |
1171 + if (stateNormalMode.command.op == visual) { | |
1172 + selextend(term.c.x, term.c.y, term.scr, sel.typ… | |
1173 + } else if (stateNormalMode.command.op == visualLine) { | |
1174 + selextend(term.col-1, term.c.y, term.scr, sel.t… | |
1175 + } else { | |
1176 + if (!isNumber && (stateNormalMode.motion.search… | |
1177 + || stateNormalMode.motion.finis… | |
1178 + toggle = !toggle; | |
1179 + empty(currentCommand); | |
1180 + } | |
1181 + if (stateNormalMode.command.op == yank) { | |
1182 + if (!isNumber && !discard) { | |
1183 + // copy | |
1184 + selextend(term.c.x, term.c.y, t… | |
1185 + xsetsel(getsel()); | |
1186 + xclipcopy(); | |
1187 + applyPosition(&stateNormalMode.… | |
1188 + exitCommand(); | |
1189 + } | |
1190 + } | |
1191 + } | |
1192 + } | |
1193 +} | |
1194 + | |
1195 void | |
1196 csiparse(void) | |
1197 { | |
1198 @@ -1176,6 +1850,10 @@ tmoveto(int x, int y) | |
1199 term.c.state &= ~CURSOR_WRAPNEXT; | |
1200 term.c.x = LIMIT(x, 0, term.col-1); | |
1201 term.c.y = LIMIT(y, miny, maxy); | |
1202 + // Set the last position in order to restore after normal mode … | |
1203 + stateNormalMode.initialPosition.x = term.c.x; | |
1204 + stateNormalMode.initialPosition.y = term.c.y; | |
1205 + stateNormalMode.initialPosition.yScr = term.scr; | |
1206 } | |
1207 | |
1208 void | |
1209 @@ -1282,14 +1960,14 @@ void | |
1210 tinsertblankline(int n) | |
1211 { | |
1212 if (BETWEEN(term.c.y, term.top, term.bot)) | |
1213 - tscrolldown(term.c.y, n); | |
1214 + tscrolldown(term.c.y, n, 0); | |
1215 } | |
1216 | |
1217 void | |
1218 tdeleteline(int n) | |
1219 { | |
1220 if (BETWEEN(term.c.y, term.top, term.bot)) | |
1221 - tscrollup(term.c.y, n); | |
1222 + tscrollup(term.c.y, n, 0); | |
1223 } | |
1224 | |
1225 int32_t | |
1226 @@ -1720,11 +2398,11 @@ csihandle(void) | |
1227 break; | |
1228 case 'S': /* SU -- Scroll <n> line up */ | |
1229 DEFAULT(csiescseq.arg[0], 1); | |
1230 - tscrollup(term.top, csiescseq.arg[0]); | |
1231 + tscrollup(term.top, csiescseq.arg[0], 0); | |
1232 break; | |
1233 case 'T': /* SD -- Scroll <n> line down */ | |
1234 DEFAULT(csiescseq.arg[0], 1); | |
1235 - tscrolldown(term.top, csiescseq.arg[0]); | |
1236 + tscrolldown(term.top, csiescseq.arg[0], 0); | |
1237 break; | |
1238 case 'L': /* IL -- Insert <n> blank lines */ | |
1239 DEFAULT(csiescseq.arg[0], 1); | |
1240 @@ -2227,7 +2905,7 @@ eschandle(uchar ascii) | |
1241 return 0; | |
1242 case 'D': /* IND -- Linefeed */ | |
1243 if (term.c.y == term.bot) { | |
1244 - tscrollup(term.top, 1); | |
1245 + tscrollup(term.top, 1, 1); | |
1246 } else { | |
1247 tmoveto(term.c.x, term.c.y+1); | |
1248 } | |
1249 @@ -2240,7 +2918,7 @@ eschandle(uchar ascii) | |
1250 break; | |
1251 case 'M': /* RI -- Reverse index */ | |
1252 if (term.c.y == term.top) { | |
1253 - tscrolldown(term.top, 1); | |
1254 + tscrolldown(term.top, 1, 1); | |
1255 } else { | |
1256 tmoveto(term.c.x, term.c.y-1); | |
1257 } | |
1258 @@ -2458,7 +3136,7 @@ twrite(const char *buf, int buflen, int show_ctrl) | |
1259 void | |
1260 tresize(int col, int row) | |
1261 { | |
1262 - int i; | |
1263 + int i, j; | |
1264 int minrow = MIN(row, term.row); | |
1265 int mincol = MIN(col, term.col); | |
1266 int *bp; | |
1267 @@ -2495,6 +3173,14 @@ tresize(int col, int row) | |
1268 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); | |
1269 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); | |
1270 | |
1271 + for (i = 0; i < HISTSIZE; i++) { | |
1272 + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyp… | |
1273 + for (j = mincol; j < col; j++) { | |
1274 + term.hist[i][j] = term.c.attr; | |
1275 + term.hist[i][j].u = ' '; | |
1276 + } | |
1277 + } | |
1278 + | |
1279 /* resize each row to new width, zero-pad if needed */ | |
1280 for (i = 0; i < minrow; i++) { | |
1281 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyp… | |
1282 @@ -2552,7 +3238,7 @@ drawregion(int x1, int y1, int x2, int y2) | |
1283 continue; | |
1284 | |
1285 term.dirty[y] = 0; | |
1286 - xdrawline(term.line[y], x1, y, x2); | |
1287 + xdrawline(TLINE(y), x1, y, x2); | |
1288 } | |
1289 } | |
1290 | |
1291 @@ -2573,8 +3259,8 @@ draw(void) | |
1292 cx--; | |
1293 | |
1294 drawregion(0, 0, term.col, term.row); | |
1295 - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
1296 - term.ocx, term.ocy, term.line[term.ocy][term.oc… | |
1297 + xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], | |
1298 + term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]); | |
1299 term.ocx = cx, term.ocy = term.c.y; | |
1300 xfinishdraw(); | |
1301 xximspot(term.ocx, term.ocy); | |
1302 diff --git a/st.h b/st.h | |
1303 index 4da3051..7bd8bba 100644 | |
1304 --- a/st.h | |
1305 +++ b/st.h | |
1306 @@ -1,5 +1,6 @@ | |
1307 /* See LICENSE for license details. */ | |
1308 | |
1309 +#include <stdbool.h> | |
1310 #include <stdint.h> | |
1311 #include <sys/types.h> | |
1312 | |
1313 @@ -10,6 +11,8 @@ | |
1314 #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | |
1315 #define DIVCEIL(n, d) (((n) + ((d) - 1)) / (d)) | |
1316 #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | |
1317 +#define INTERVAL(x, a, b) (x) < (a) ? (a) : (x) > (b) ? … | |
1318 +#define INTERVAL_DIFF(x, a, b) (x) < (a) ? (x) - (a) : (… | |
1319 #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b)… | |
1320 #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg !=… | |
1321 (a).bg != (b).bg) | |
1322 @@ -33,6 +36,8 @@ enum glyph_attribute { | |
1323 ATTR_WRAP = 1 << 8, | |
1324 ATTR_WIDE = 1 << 9, | |
1325 ATTR_WDUMMY = 1 << 10, | |
1326 + ATTR_HIGHLIGHT = 1 << 11 | ATTR_UNDERLINE, | |
1327 + ATTR_CURRENT = 1 << 12, | |
1328 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | |
1329 }; | |
1330 | |
1331 @@ -80,6 +85,14 @@ void die(const char *, ...); | |
1332 void redraw(void); | |
1333 void draw(void); | |
1334 | |
1335 +int highlighted(int, int); | |
1336 +int currentLine(int, int); | |
1337 +void kscrolldown(const Arg *); | |
1338 +void kscrollup(const Arg *); | |
1339 +void kpressNormalMode(char const * ksym, uint32_t len, bool esc, bool e… | |
1340 +void normalMode(Arg const *); | |
1341 +void onNormalModeStart(); | |
1342 +void onNormalModeStop(); | |
1343 void printscreen(const Arg *); | |
1344 void printsel(const Arg *); | |
1345 void sendbreak(const Arg *); | |
1346 @@ -99,8 +112,10 @@ void resettitle(void); | |
1347 | |
1348 void selclear(void); | |
1349 void selinit(void); | |
1350 -void selstart(int, int, int); | |
1351 -void selextend(int, int, int, int); | |
1352 +void selstart(int, int, int, int); | |
1353 +void xselstart(int, int, int); | |
1354 +void selextend(int, int, int, int, int); | |
1355 +void xselextend(int, int, int, int); | |
1356 int selected(int, int); | |
1357 char *getsel(void); | |
1358 | |
1359 @@ -110,6 +125,8 @@ void *xmalloc(size_t); | |
1360 void *xrealloc(void *, size_t); | |
1361 char *xstrdup(char *); | |
1362 | |
1363 + | |
1364 + | |
1365 /* config.h globals */ | |
1366 extern char *utmp; | |
1367 extern char *stty_args; | |
1368 @@ -120,3 +137,13 @@ extern char *termname; | |
1369 extern unsigned int tabspaces; | |
1370 extern unsigned int defaultfg; | |
1371 extern unsigned int defaultbg; | |
1372 +extern char wordDelimSmall[]; | |
1373 +extern char wordDelimLarge[]; | |
1374 + | |
1375 +typedef struct NormalModeShortcuts { | |
1376 + char key; | |
1377 + char *value; | |
1378 +} NormalModeShortcuts; | |
1379 + | |
1380 +extern NormalModeShortcuts normalModeShortcuts[]; | |
1381 +extern size_t const amountNormalModeShortcuts; | |
1382 diff --git a/win.h b/win.h | |
1383 index a6ef1b9..1a6fefe 100644 | |
1384 --- a/win.h | |
1385 +++ b/win.h | |
1386 @@ -19,6 +19,7 @@ enum win_mode { | |
1387 MODE_MOUSEMANY = 1 << 15, | |
1388 MODE_BRCKTPASTE = 1 << 16, | |
1389 MODE_NUMLOCK = 1 << 17, | |
1390 + MODE_NORMAL = 1 << 18, | |
1391 MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ | |
1392 |MODE_MOUSEMANY, | |
1393 }; | |
1394 @@ -27,6 +28,7 @@ void xbell(void); | |
1395 void xclipcopy(void); | |
1396 void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
1397 void xdrawline(Line, int, int, int); | |
1398 +void xdrawglyph(Glyph, int, int); | |
1399 void xfinishdraw(void); | |
1400 void xloadcols(void); | |
1401 int xsetcolorname(int, const char *); | |
1402 diff --git a/x.c b/x.c | |
1403 index 5828a3b..ccf1751 100644 | |
1404 --- a/x.c | |
1405 +++ b/x.c | |
1406 @@ -136,7 +136,6 @@ typedef struct { | |
1407 static inline ushort sixd_to_16bit(int); | |
1408 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, … | |
1409 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, i… | |
1410 -static void xdrawglyph(Glyph, int, int); | |
1411 static void xclear(int, int, int, int); | |
1412 static int xgeommasktogravity(int); | |
1413 static void ximopen(Display *); | |
1414 @@ -340,7 +339,7 @@ mousesel(XEvent *e, int done) | |
1415 break; | |
1416 } | |
1417 } | |
1418 - selextend(evcol(e), evrow(e), seltype, done); | |
1419 + xselextend(evcol(e), evrow(e), seltype, done); | |
1420 if (done) | |
1421 setsel(getsel(), e->xbutton.time); | |
1422 } | |
1423 @@ -444,7 +443,7 @@ bpress(XEvent *e) | |
1424 xsel.tclick2 = xsel.tclick1; | |
1425 xsel.tclick1 = now; | |
1426 | |
1427 - selstart(evcol(e), evrow(e), snap); | |
1428 + xselstart(evcol(e), evrow(e), snap); | |
1429 } | |
1430 } | |
1431 | |
1432 @@ -730,6 +729,19 @@ xloadcolor(int i, const char *name, Color *ncolor) | |
1433 return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); | |
1434 } | |
1435 | |
1436 +void | |
1437 +normalMode(Arg const *_) //< the argument is just for the sake of | |
1438 + // adhering to the function format. | |
1439 +{ | |
1440 + win.mode ^= MODE_NORMAL; //< toggle normal mode via exclusive o… | |
1441 + if (win.mode & MODE_NORMAL) { | |
1442 + onNormalModeStart(); | |
1443 + } else { | |
1444 + onNormalModeStop(); | |
1445 + } | |
1446 +} | |
1447 + | |
1448 + | |
1449 void | |
1450 xloadcols(void) | |
1451 { | |
1452 @@ -1296,6 +1308,14 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs… | |
1453 base.fg = defaultattr; | |
1454 } | |
1455 | |
1456 + if (base.mode & ATTR_HIGHLIGHT) { | |
1457 + base.bg = highlightBg; | |
1458 + base.fg = highlightFg; | |
1459 + } else if ((base.mode & ATTR_CURRENT) && (win.mode & MODE_NORMA… | |
1460 + base.bg = currentBg; | |
1461 + base.fg = currentFg; | |
1462 + } | |
1463 + | |
1464 if (IS_TRUECOL(base.fg)) { | |
1465 colfg.alpha = 0xffff; | |
1466 colfg.red = TRUERED(base.fg); | |
1467 @@ -1428,8 +1448,9 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int o… | |
1468 Color drawcol; | |
1469 | |
1470 /* remove the old cursor */ | |
1471 - if (selected(ox, oy)) | |
1472 - og.mode ^= ATTR_REVERSE; | |
1473 + if (selected(ox, oy)) og.mode ^= ATTR_REVERSE; | |
1474 + if (highlighted(ox, oy)) { og.mode ^= ATTR_HIGHLIGHT; } | |
1475 + if (currentLine(ox, oy)) { og.mode ^= ATTR_CURRENT; } | |
1476 xdrawglyph(og, ox, oy); | |
1477 | |
1478 if (IS_SET(MODE_HIDE)) | |
1479 @@ -1461,6 +1482,11 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int … | |
1480 drawcol = dc.col[g.bg]; | |
1481 } | |
1482 | |
1483 + if ((g.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) { | |
1484 + g.bg = currentBg; | |
1485 + g.fg = currentFg; | |
1486 + } | |
1487 + | |
1488 /* draw the new one */ | |
1489 if (IS_SET(MODE_FOCUSED)) { | |
1490 switch (win.cursor) { | |
1491 @@ -1550,6 +1576,12 @@ xdrawline(Line line, int x1, int y1, int x2) | |
1492 continue; | |
1493 if (selected(x, y1)) | |
1494 new.mode ^= ATTR_REVERSE; | |
1495 + if (highlighted(x, y1)) { | |
1496 + new.mode ^= ATTR_HIGHLIGHT; | |
1497 + } | |
1498 + if (currentLine(x, y1)) { | |
1499 + new.mode ^= ATTR_CURRENT; | |
1500 + } | |
1501 if (i > 0 && ATTRCMP(base, new)) { | |
1502 xdrawglyphfontspecs(specs, base, i, ox, y1); | |
1503 specs += i; | |
1504 @@ -1731,6 +1763,12 @@ kpress(XEvent *ev) | |
1505 return; | |
1506 | |
1507 len = XmbLookupString(xw.xic, e, buf, sizeof buf, &ksym, &statu… | |
1508 + if (IS_SET(MODE_NORMAL)) { | |
1509 + kpressNormalMode(buf, strlen(buf), | |
1510 + ksym == XK_Escape, ksym == XK_Return, k… | |
1511 + return; | |
1512 + } | |
1513 + | |
1514 /* 1. shortcuts */ | |
1515 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { | |
1516 if (ksym == bp->keysym && match(bp->mod, e->state)) { | |
1517 @@ -1870,8 +1908,9 @@ run(void) | |
1518 XNextEvent(xw.dpy, &ev); | |
1519 if (XFilterEvent(&ev, None)) | |
1520 continue; | |
1521 - if (handler[ev.type]) | |
1522 + if (handler[ev.type]) { | |
1523 (handler[ev.type])(&ev); | |
1524 + } | |
1525 } | |
1526 | |
1527 draw(); | |
1528 -- | |
1529 2.24.0 | |
1530 | |
1531 | |
1532 From aae3d1c2e5437a3bd2c79ae0cb2ad6380b10adce Mon Sep 17 00:00:00 2001 | |
1533 From: Kevin Velghe <[email protected]> | |
1534 Date: Mon, 2 Dec 2019 23:25:52 +0100 | |
1535 Subject: [PATCH 2/2] Merge contribution of paretje: fix underlined text | |
1536 | |
1537 https://github.com/juliusHuelsmann/st/issues/13 | |
1538 --- | |
1539 st.h | 4 ++-- | |
1540 1 file changed, 2 insertions(+), 2 deletions(-) | |
1541 | |
1542 diff --git a/st.h b/st.h | |
1543 index 7bd8bba..4a78440 100644 | |
1544 --- a/st.h | |
1545 +++ b/st.h | |
1546 @@ -36,8 +36,8 @@ enum glyph_attribute { | |
1547 ATTR_WRAP = 1 << 8, | |
1548 ATTR_WIDE = 1 << 9, | |
1549 ATTR_WDUMMY = 1 << 10, | |
1550 - ATTR_HIGHLIGHT = 1 << 11 | ATTR_UNDERLINE, | |
1551 - ATTR_CURRENT = 1 << 12, | |
1552 + ATTR_HIGHLIGHT = 1 << 12, | |
1553 + ATTR_CURRENT = 1 << 13, | |
1554 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | |
1555 }; | |
1556 | |
1557 -- | |
1558 2.24.0 | |
1559 |