st-vimBrowse-20200604-295a43f.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
st-vimBrowse-20200604-295a43f.diff (64935B)
---
1 diff -ruN st-default/config.def.h st1/config.def.h
2 --- st-default/config.def.h 2020-06-04 11:15:55.164135902 +0200
3 +++ st1/config.def.h 2020-06-04 11:15:28.476134951 +0200
4 @@ -56,6 +56,10 @@
5 static double minlatency = 8;
6 static double maxlatency = 33;
7
8 +/* frames per second st should at maximum draw to the screen */
9 +static unsigned int xfps = 120;
10 +static unsigned int actionfps = 30;
11 +
12 /*
13 * blinking timeout (set to 0 to disable blinking) for the terminal blinking
14 * attribute.
15 @@ -160,6 +164,14 @@
16 * doesn't match the ones requested.
17 */
18 static unsigned int defaultattr = 11;
19 +/// Colors for the entities that are 'highlighted' in normal mode (search
20 +/// results currently on screen) [Vim Browse].
21 +static unsigned int highlightBg = 160;
22 +static unsigned int highlightFg = 15;
23 +/// Colors for highlighting the current cursor position (row + col) in normal
24 +/// mode [Vim Browse].
25 +static unsigned int currentBg = 8;
26 +static unsigned int currentFg = 15;
27
28 /*
29 * Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
30 @@ -175,18 +187,18 @@
31 static MouseShortcut mshortcuts[] = {
32 /* mask button function argument release */
33 { XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
34 - { ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
35 { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
36 - { ShiftMask, Button5, ttysend, {.s = "\033[6;2~"} },
37 { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} },
38 };
39
40 /* Internal keyboard shortcuts. */
41 #define MODKEY Mod1Mask
42 +#define AltMask Mod1Mask
43 #define TERMMOD (ControlMask|ShiftMask)
44
45 static Shortcut shortcuts[] = {
46 /* mask keysym function argument */
47 + { AltMask, XK_c, normalMode, {.i = 0} },
48 { XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
49 { ControlMask, XK_Print, toggleprinter, {.i = 0} },
50 { ShiftMask, XK_Print, printscreen, {.i = 0} },
51 @@ -199,6 +211,8 @@
52 { TERMMOD, XK_Y, selpaste, {.i = 0} },
53 { ShiftMask, XK_Insert, selpaste, {.i = 0} },
54 { TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
55 + { ShiftMask, XK_Page_Up, kscrollup, {.i = -1} },
56 + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -1} },
57 };
58
59 /*
60 @@ -470,3 +484,45 @@
61 " !\"#$%&'()*+,-./0123456789:;<=>?"
62 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
63 "`abcdefghijklmnopqrstuvwxyz{|}~";
64 +
65 +
66 +/// word sepearors normal mode
67 +/// [Vim Browse].
68 +char wordDelimSmall[] = " \t!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
69 +char wordDelimLarge[] = " \t"; /// <Word sepearors normal mode (capital W)
70 +
71 +/// Shortcusts executed in normal mode (which should not already be in use)
72 +/// [Vim Browse].
73 +struct NormalModeShortcuts normalModeShortcuts [] = {
74 + { 'R', "?Building\n" },
75 + { 'r', "/Building\n" },
76 + { 'F', "?: error:\n" },
77 + { 'f', "/: error:\n" },
78 + { 'Q', "?[Leaving vim, starting execution]\n" },
79 + { 'S', "Qf" },
80 + { 'X', "?juli@machine\n" },
81 + { 'x', "/juli@machine\n" },
82 +};
83 +
84 +size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / sizeof(*normalModeShortcuts);
85 +
86 +/// Style of the command string visualized in normal mode in the right corner
87 +/// [Vim Browse].
88 +Glyph const styleCommand = {' ', ATTR_ITALIC | ATTR_FAINT, 7, 16};
89 +/// Style of the search string visualized in normal mode in the right corner.
90 +/// [Vim Browse].
91 +Glyph const styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16};
92 +
93 +/// Colors used in normal mode in order to highlight different operations and
94 +/// empathise the current position on screen in the status area [Vim Browse].
95 +unsigned int bgCommandYank = 11;
96 +unsigned int bgCommandVisual = 4;
97 +unsigned int bgCommandVisualLine = 12;
98 +
99 +unsigned int fgCommandYank = 232;
100 +unsigned int fgCommandVisual = 232;
101 +unsigned int fgCommandVisualLine = 232;
102 +
103 +unsigned int bgPos = 15;
104 +unsigned int fgPos = 16;
105 +
106 diff -ruN st-default/dynamicArray.h st1/dynamicArray.h
107 --- st-default/dynamicArray.h 1970-01-01 01:00:00.000000000 +0100
108 +++ st1/dynamicArray.h 2020-06-04 11:04:30.227111509 +0200
109 @@ -0,0 +1,175 @@
110 +#ifndef DYNAMIC_ARRAY_H
111 +#define DYNAMIC_ARRAY_H
112 +
113 +#include "error.h"
114 +
115 +#include <stdint.h>
116 +#include <stdlib.h>
117 +#include <string.h>
118 +#include <stdbool.h>
119 +
120 +/// Struct for which this file offers functionality in order to expand the array
121 +/// and set / get its content.
122 +typedef struct DynamicArray {
123 + /// Size of the datatype contained in the array.
124 + uint8_t itemSize;
125 + /// Amount of bytes currently initialized
126 + uint32_t index;
127 + /// Amount of bytes currently reserved (not necessarily initialized)
128 + uint32_t allocated;
129 + /// Actual content.
130 + char* content;
131 +} DynamicArray;
132 +
133 +#define EXPAND_STEP 15
134 +
135 +/// Default initializers for the dynamic array.
136 +#define CHAR_ARRAY {1, 0, 0, NULL}
137 +#define WORD_ARRAY {2, 0, 0, NULL}
138 +#define DWORD_ARRAY {4, 0, 0, NULL}
139 +#define QWORD_ARRAY {8, 0, 0, NULL}
140 +/// (Wasteful) utf-8 array, that always used 4 bytes in order to display a
141 +/// character, even if the space is not required.
142 +#define UTF8_ARRAY DWORD_ARRAY
143 +
144 +/// Check that at least \p bytes are allocated, if true implying that
145 +/// \p s->content[\bytes - 1] is allocated.
146 +static inline bool
147 +isAllocated(DynamicArray const *s, uint32_t bytes) {
148 + return s != NULL && s->allocated >= bytes;
149 +}
150 +
151 +/// @see #isAllocated
152 +static inline bool
153 +isInitialized(DynamicArray const *s, uint32_t bytes) {
154 + return s != NULL && s->index >= bytes;
155 +}
156 +
157 +/// Return the next element in \p s and increment index without checking bounds.
158 +static inline char*
159 +gnext(DynamicArray *s) {
160 + ENSURE(s!=NULL, return NULL);
161 + ENSURE(s->index % s->itemSize == 0 && "(index not aligned)",
162 + s->index += s->itemSize - (s->index % s->itemSize));
163 + ENSURE(isAllocated(s, s->index + 2 * s->itemSize), return NULL);
164 + return s->content + (s->index += s->itemSize);
165 +}
166 +
167 +/// View element \p i in \p s.
168 +static inline char*
169 +view(DynamicArray const * s, uint32_t i) {
170 + ENSURE((s != NULL) && isAllocated(s, (i+1) * s->itemSize), return NULL);
171 + return s->content + i*s->itemSize;
172 +}
173 +
174 +/// Inspect element content[size() - 1 - i].
175 +static inline char *
176 +viewEnd(DynamicArray const *s, uint32_t i) {
177 + ENSURE((s != NULL) && isInitialized(s, i * s->itemSize), return NULL);
178 + ENSURE(s->index%s->itemSize == 0 && "(index not aligned)", return NULL);
179 + return s->content + s->index - (i + 1) * s->itemSize;
180 +}
181 +
182 +/// Set conent without applying
183 +static inline bool
184 +setValues(DynamicArray* s, char const *vals, uint32_t amount) {
185 + ENSURE(vals != NULL, return false);
186 + ENSURE((s != NULL) && isAllocated(s, s->index + amount), return false);
187 + memcpy(s->content + s->index, vals, amount);
188 + return true;
189 +}
190 +
191 +static inline bool
192 +snext(DynamicArray* s, char const *vals, uint32_t amount) {
193 + bool const success = setValues(s, vals, amount);
194 + ENSURE(success, return false);
195 + uint8_t const rest = amount % s->itemSize;
196 + uint32_t const newSize = s->index + amount + (rest ? s->itemSize : 0);
197 + ENSURE(isAllocated(s, newSize), return false);
198 + s->index = newSize;
199 + return true;
200 +}
201 +
202 +/// Empty \p s.
203 +static inline void
204 +empty(DynamicArray* s) {
205 + ENSURE((s != NULL), return);
206 + s->index = 0;
207 +}
208 +
209 +/// Check if \p s has initialized content (which can be the case even if memory
210 +/// is allocated).
211 +static inline bool
212 +isEmpty(DynamicArray const * s) {
213 + ENSURE((s != NULL), return true);
214 + return s->index == 0;
215 +}
216 +
217 +static inline int
218 +size(DynamicArray const * s) {
219 + ENSURE(s != NULL, return 0);
220 + ENSURE(s->itemSize != 0, return 0);
221 + return s->index / s->itemSize;
222 +}
223 +
224 +static inline void
225 +pop(DynamicArray* s) {
226 + ENSURE((s != NULL), return);
227 + ENSURE(s->index % s->itemSize == 0 && "(index not aligned)",
228 + s->index += s->itemSize - (s->index % s->itemSize));
229 + ENSURE(isInitialized(s, s->itemSize), return);
230 + s->index -= s->itemSize;
231 +}
232 +
233 +static inline bool
234 +checkSetNext(DynamicArray *s, char const *c, uint32_t amount) {
235 + ENSURE(s != NULL && c != NULL, return false);
236 + if (s->allocated < s->index + s->itemSize * amount) {
237 + uint32_t const diff = s->index+s->itemSize*amount-s->allocated;
238 + uint32_t const newAlloSize = s->allocated + (diff > EXPAND_STEP
239 + ? diff : EXPAND_STEP) * s->itemSize;
240 + char* tmp = realloc(s->content, newAlloSize);
241 + if (tmp == NULL) { return false; }
242 + s->allocated = newAlloSize;
243 + s->content = tmp;
244 + assert(s->allocated >= s->index + s->itemSize * amount);
245 + }
246 + if (amount) { snext(s, c, amount); }
247 + return true;
248 +}
249 +
250 +static inline bool
251 +checkSetNextV(DynamicArray *s, char const c) {
252 + return checkSetNext(s, &c, 1);
253 +}
254 +
255 +static inline bool
256 +checkSetNextP(DynamicArray *s, char const *c) {
257 + ENSURE(c != NULL, return false);
258 + return checkSetNext(s, c, strlen(c));
259 +}
260 +
261 +/// Expand the currently initialized content in \p s and the allocated chunk of
262 +/// memory if required.
263 +static char *
264 +expand(DynamicArray *s) {
265 + ENSURE(s != NULL, return NULL);
266 + if (s->allocated < s->index + s->itemSize) {
267 + uint32_t const diff = s->index + s->itemSize - s->allocated;
268 + uint32_t const newAlloSize = s->allocated + (diff > EXPAND_STEP
269 + ? diff : EXPAND_STEP) * s->itemSize;
270 + char* tmp = realloc(s->content, newAlloSize);
271 + if (tmp == NULL) { return NULL; }
272 + s->allocated = newAlloSize;
273 + s->content = tmp;
274 + assert(s->allocated >= s->index + s->itemSize);
275 + }
276 + s->index+=s->itemSize;
277 + return viewEnd(s, 0);
278 +}
279 +
280 +#define append(s, c) checkSetNext((s), (char const *) (c), (s)->itemSize)
281 +#define appendPartial(s, c, i) checkSetNext((s), (char const *) (c), (i))
282 +
283 +
284 +#endif // DYNAMIC_ARRAY_H
285 diff -ruN st-default/error.h st1/error.h
286 --- st-default/error.h 1970-01-01 01:00:00.000000000 +0100
287 +++ st1/error.h 2020-06-04 11:04:30.227111509 +0200
288 @@ -0,0 +1,47 @@
289 +#ifndef ERROR_H
290 +#define ERROR_H
291 +
292 +#include <assert.h>
293 +
294 +// Flag which determines whether to fail if a required condition is not met, or
295 +// to adapt the condition in order to work properly.
296 +// Attention: Be sure to perform a clean build after you alter preprocessor
297 +// directives / definitions.
298 +//#define FAIL_ON_ERROR
299 +
300 +#include <stdio.h>
301 +
302 +///
303 +/// Function used in case the fail-on-error mode is disabled (via definition)
304 +/// to report errors. In debug production mode, alias st to st 2> error.log.
305 +static void reportError(char const * cond, char const * stt, char const * file,
306 + unsigned int line ) {
307 + unsigned int const maxErrorCount = 100;
308 + static unsigned int errorCount = 0;
309 + if (++errorCount == 1) {
310 + printf("Report the following bug to "
311 + "https://github.com/juliusHuelsmann/st.\n");
312 + }
313 + if (errorCount < maxErrorCount) {
314 + printf("Bug:\tCondition '%s' evaluates to false.\n\tPerforming"
315 + " '%s' to counteract.\n\tFile:%s:%u\n",
316 + cond, stt, file, line);
317 + } else if (errorCount == maxErrorCount) {
318 + printf("Max amount of reported errors %u is reached. From here"
319 + "on, no additional errors will be reported.\n",
320 + maxErrorCount);
321 + }
322 +}
323 +
324 +/// Note that everyting condition checked / endforced with #ENSURE is
325 +/// considered an error, and behaves like an error depending on the flag.
326 +#ifdef FAIL_ON_ERROR
327 +#define ENSURE(cond, stt) assert(cond);
328 +#else // FAIL_ON_ERROR
329 +#define ENSURE(cond, stt) if (!(cond)) { \
330 + reportError(#cond, #stt, __FILE__, __LINE__); \
331 + stt; \
332 + }
333 +#endif // FAIL_ON_ERROR
334 +
335 +#endif // ERROR_H
336 diff -ruN st-default/glyph.h st1/glyph.h
337 --- st-default/glyph.h 1970-01-01 01:00:00.000000000 +0100
338 +++ st1/glyph.h 2020-06-04 11:04:30.228111510 +0200
339 @@ -0,0 +1,30 @@
340 +#ifndef LINE_H
341 +#define LINE_H
342 +
343 +//
344 +// Contains the representation of the entities in the buffer (Line, Gylph), that
345 +// is used by every part of the software implmeneting terminal logic.
346 +//
347 +
348 +#include <stdint.h>
349 +
350 +enum selection_type {
351 + SEL_REGULAR = 1,
352 + SEL_RECTANGULAR = 2
353 +};
354 +
355 +typedef uint_least32_t Rune;
356 +
357 +#define Glyph Glyph_
358 +
359 +typedef struct {
360 + Rune u; /* character code */
361 + unsigned short mode; /* attribute flags */
362 + uint32_t fg; /* foreground */
363 + uint32_t bg; /* background */
364 +} Glyph;
365 +
366 +
367 +typedef Glyph *Line;
368 +
369 +#endif // LINE_H
370 diff -ruN st-default/Makefile st1/Makefile
371 --- st-default/Makefile 2020-06-04 11:15:55.164135902 +0200
372 +++ st1/Makefile 2020-06-04 11:04:30.228111510 +0200
373 @@ -4,7 +4,7 @@
374
375 include config.mk
376
377 -SRC = st.c x.c
378 +SRC = st.c x.c normalMode.c
379 OBJ = $(SRC:.c=.o)
380
381 all: options st
382 @@ -21,8 +21,8 @@
383 .c.o:
384 $(CC) $(STCFLAGS) -c $<
385
386 -st.o: config.h st.h win.h
387 -x.o: arg.h config.h st.h win.h
388 +st.o: config.h st.h win.h dynamicArray.h normalMode.h term.h glyph.h error.h
389 +x.o: arg.h config.h st.h win.h dynamicArray.h normalMode.h term.h glyph.h error.h
390
391 $(OBJ): config.h config.mk
392
393 @@ -35,7 +35,8 @@
394 dist: clean
395 mkdir -p st-$(VERSION)
396 cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\
397 - config.def.h st.info st.1 arg.h st.h win.h $(SRC)\
398 + config.def.h st.info st.1 arg.h st.h win.h dynamicArray.h\
399 + normalMode.h term.h error.h $(SRC)\
400 st-$(VERSION)
401 tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz
402 rm -rf st-$(VERSION)
403 diff -ruN st-default/normalMode.c st1/normalMode.c
404 --- st-default/normalMode.c 1970-01-01 01:00:00.000000000 +0100
405 +++ st1/normalMode.c 2020-06-04 11:04:30.229111510 +0200
406 @@ -0,0 +1,752 @@
407 +/* See LICENSE for license details. */
408 +#include "normalMode.h"
409 +#include "dynamicArray.h"
410 +#include "term.h"
411 +#include "win.h"
412 +#include "error.h"
413 +
414 +#include <X11/keysym.h>
415 +#include <X11/XKBlib.h>
416 +
417 +#include <ctype.h>
418 +#include <stdio.h>
419 +#include <limits.h>
420 +#include <math.h>
421 +
422 +#define LEN(a) (sizeof(a) / sizeof(a)[0])
423 +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b))
424 +//#define FALLTHROUGH __attribute__((fallthrough));
425 +#define FALLTHROUGH
426 +#define SEC(var,ini,h,r) var = ini; if (!var) { h; return r; }
427 +#define EXPAND(v1,v2,r) char *SEC(v1, expand(v2), empty(v2), true)
428 +#define currentCommand (toggle ? &commandHist0 : &commandHist1)
429 +#define lastCommand (toggle ? &commandHist1 : &commandHist0)
430 +
431 +//
432 +// Interface to the terminal
433 +extern Glyph const styleCommand, styleSearch;
434 +extern NormalModeShortcuts normalModeShortcuts[];
435 +extern size_t const amountNormalModeShortcuts;
436 +extern char wordDelimSmall[];
437 +extern char wordDelimLarge[];
438 +extern unsigned int fgCommandYank, fgCommandVisual, fgCommandVisualLine,
439 + bgCommandYank, bgCommandVisual, bgCommandVisualLine, bgPos, fgPos;
440 +
441 +extern void selclear(void);
442 +extern void tsetdirt(int, int);
443 +extern size_t utf8encode(Rune, char *);
444 +extern size_t utf8decode(const char *, Rune *, size_t);
445 +extern size_t utf8decodebyte(char c, size_t *i);
446 +
447 +extern void selextend(int, int, int, int, int);
448 +extern void selstart(int, int, int, int);
449 +extern char *getsel(void);
450 +extern void tfulldirt(void);
451 +
452 +//
453 +// `Private` structs
454 +typedef struct { uint32_t x; uint32_t y; uint32_t yScr; } Position;
455 +
456 +/// Entire normal mode state, consisting of an operation and a motion.
457 +typedef struct {
458 + Position initialPosition;
459 + struct OperationState {
460 + enum Operation {
461 + noop = ' ', visual='v', visualLine='V', yank = 'y' } op;
462 + Position startPosition;
463 + enum Infix { infix_none = 0, infix_i = 1, infix_a = 2, } infix;
464 + } command;
465 + struct MotionState {
466 + uint32_t amount;
467 + enum Search {none, forward, backward} search;
468 + Position searchPosition;
469 + bool finished;
470 + } motion;
471 +} NormalModeState;
472 +
473 +/// Default state if no operation is performed.
474 +NormalModeState defaultNormalMode = {
475 + {0,0,0}, {noop, {0, 0, 0}, false}, {0, none, {0, 0, 0}, true}
476 +};
477 +NormalModeState stateVB = {
478 + {0,0,0}, {noop, {0, 0, 0}, false}, {0, none, {0, 0, 0}, true}
479 +};
480 +
481 +DynamicArray searchString = UTF8_ARRAY;
482 +DynamicArray commandHist0 = UTF8_ARRAY;
483 +DynamicArray commandHist1 = UTF8_ARRAY;
484 +DynamicArray highlights = DWORD_ARRAY;
485 +
486 +/// History command toggle
487 +static bool toggle = false;
488 +
489 +//
490 +// Utility functions
491 +static inline int intervalDiff(int v, int a, int b) {
492 + return (v < a) ? (v - a) : ((v > b) ? (v - b) : 0);
493 +}
494 +static inline void swap(DynamicArray *const a, DynamicArray *const b) {
495 + DynamicArray tmp = *a; *a = *b; *b = tmp;
496 +}
497 +static inline int max(int a, int b) { return a > b ? a : b; }
498 +static inline int min(int a, int b) { return a < b ? a : b; }
499 +static inline int mod(int a, int b) { for (; a < 0; a += b); return a % b; }
500 +static inline bool contains (char c, char const * values, uint32_t memSize) {
501 + ENSURE(values != NULL, return false);
502 + for (uint32_t i = 0; i < memSize; ++i) if (c == values[i]) return true;
503 + return false;
504 +}
505 +static inline void applyPosition(Position const *pos) {
506 + ENSURE(pos != NULL, return);
507 + term.c.x = pos->x;
508 + term.c.y = pos->y;
509 + term.scr = pos->yScr;
510 +}
511 +static inline int getSearchDirection(void) {
512 + return stateVB.motion.search == forward ? 1 : -1;
513 +}
514 +
515 +// Utilities for working with the current version of the scrollback patch.
516 +static bool moveLine(int32_t const amount) {
517 + int32_t const reqShift = intervalDiff(term.c.y+=amount, 0, term.row-1);
518 + term.c.y -= reqShift;
519 + int32_t const sDiff = intervalDiff(term.scr-=reqShift, 0, HISTSIZE-1);
520 + term.scr -= sDiff;
521 + return sDiff == 0;
522 +}
523 +
524 +static void moveLetter(int32_t const amount) {
525 + int32_t value = (term.c.x += amount) / term.col;
526 + if (value -= (term.c.x < 0)) {
527 + term.c.x = moveLine(value) ? mod(term.c.x, term.col)
528 + : max(min(term.c.x,term.col - 1), 0);
529 + }
530 + assert(BETWEEN(term.c.x,0,term.col-1)&&BETWEEN(term.c.y,0,term.row-1));
531 +}
532 +
533 +//
534 +// `Private` functions:
535 +
536 +// Functions: Temporarily display string on screen.
537 +
538 +/// Display string at end of a specified line without writing it into the buffer
539 +/// @param str string that is to be displayed
540 +/// @param g glyph
541 +/// @param yPos
542 +static void
543 +displayString(DynamicArray const *str, Glyph const *g, int yPos, bool prePos) {
544 + ENSURE((str != NULL) && (g != NULL) && (term.row > 0), return);
545 + ENSURE(yPos >= 0, yPos = 0);
546 + ENSURE(yPos < term.row, yPos = term.row - 1);
547 + // Arbritary limit to avoid withhelding too much info from user.
548 + int const maxFractionOverridden = 3;
549 + // Threshold: if there is no space to print, do not print, but transfer
550 + // repsonsibility for printing back to [st].
551 + if (term.col < maxFractionOverridden) { // (0)
552 + term.dirty[yPos] = 1;
553 + return;
554 + }
555 + int32_t const botSz = prePos * 6; //< sz for position indication
556 + // Determine the dimensions of used chunk of screen.
557 + int32_t const overrideSize = min(size(str) + botSz,
558 + term.col / maxFractionOverridden); // (1)
559 + int32_t const overrideEnd = term.col - 2;
560 + // Has to follow trivially hence th assert:
561 + // overrideSize <(1)= term.col/3 <(0)= term.col = overrideEnd + 1.
562 + assert(overrideSize <= overrideEnd + 1);
563 + int32_t const overrideStart = 1 + overrideEnd - overrideSize;
564 + // display history[history.size() - (overrideSize - botSz)::-1]
565 + Glyph *SEC(line, malloc(sizeof(Glyph) * (overrideSize)),,)
566 + int32_t offset = (size(str) - overrideSize - 1 + botSz) * str->itemSize;
567 + for (uint32_t chr = 0; chr < overrideSize - botSz; ++chr) {
568 + line[chr] = *g;
569 + line[chr].u = *((Rune*) (str->content+(offset+=str->itemSize)));
570 + }
571 + if (prePos) {
572 + ENSURE(term.scr < HISTSIZE, term.scr = HISTSIZE - 1);
573 + int const p=(int)(0.5+(HISTSIZE-1-term.scr)*100./(HISTSIZE-1));
574 + int const v = min(max(p, 0), 100);
575 + char prc [10];
576 + switch (term.scr) {
577 + case HISTSIZE - 1: strcpy(prc, " [TOP]"); break;
578 + case 0: strcpy(prc, " [BOT]"); break;
579 + default: sprintf(prc, " % 3d%c ", v, '%');
580 + }
581 + for (uint32_t chr = 0; chr < botSz; ++chr) {
582 + line[chr + overrideSize - botSz] =*g;
583 + line[chr + overrideSize - botSz].fg = fgPos;
584 + line[chr + overrideSize - botSz].bg = bgPos;
585 + utf8decode(&prc[chr],&line[chr+overrideSize-botSz].u,1);
586 + }
587 + line[overrideSize - botSz] =*g;
588 + }
589 + xdrawline(TLINE(yPos), 0, yPos, overrideStart);
590 + term.c.y -= term.row; term.c.x -= term.col; // not highlight hack
591 + xdrawline(line-overrideStart, overrideStart, yPos, overrideEnd + 1);
592 + term.c.y += term.row; term.c.x += term.col;
593 + free(line);
594 +}
595 +
596 +static inline void printCommandString(void) {
597 + Glyph g = styleCommand;
598 + switch(stateVB.command.op) {
599 + case yank: g.fg = fgCommandYank; g.bg = bgCommandYank; break;
600 + case visual: g.fg=fgCommandVisual; g.bg=bgCommandVisual; break;
601 + case visualLine: g.fg=fgCommandVisualLine;
602 + g.bg=bgCommandVisualLine;
603 + }
604 + displayString(isEmpty(currentCommand) ? lastCommand : currentCommand,
605 + &g, term.row - 1, true);
606 +}
607 +
608 +static inline void printSearchString(void) {
609 + displayString(&searchString, &styleSearch, term.row - 2, false);
610 +}
611 +
612 +// NormalMode Operation / Motion utilies.
613 +
614 +static inline bool isMotionFinished(void) { return stateVB.motion.finished; }
615 +
616 +static inline void finishMotion(void) { stateVB.motion.finished = true; }
617 +
618 +static inline bool isOperationFinished(void) {
619 + return stateVB.command.op==noop && stateVB.command.infix==infix_none;
620 +}
621 +
622 +/// Register that the current comamnd is finished and a new command is lgoged
623 +static inline void startNewCommand(bool abort) {
624 + if (!abort) { toggle = !toggle; }
625 + empty(currentCommand);
626 +}
627 +
628 +static inline void finishOperation(void) {
629 + stateVB.command = defaultNormalMode.command;
630 + assert(isOperationFinished());
631 + // After an operation is finished, the selection has to be released and
632 + // no highlights are to be released.
633 + selclear();
634 + empty(&highlights);
635 + // THe command string is reset for a new command.
636 + startNewCommand(true);
637 +}
638 +
639 +static inline void enableOperation(enum Operation o) {
640 + finishOperation();
641 + stateVB.command.op = o;
642 + stateVB.command.infix = infix_none;
643 + stateVB.command.startPosition.x = term.c.x;
644 + stateVB.command.startPosition.y = term.c.y;
645 + stateVB.command.startPosition.yScr = term.scr;
646 +}
647 +
648 +/// @param abort: If enabled, the command exits without registering
649 +/// @return Whether the the application is ready to yield control back to
650 +//the normal command flow
651 +static bool terminateCommand(bool abort) {
652 + bool const exitOperation = isMotionFinished();
653 + bool exitNormalMode = false;
654 + finishMotion();
655 +
656 + if (exitOperation) {
657 + exitNormalMode = isOperationFinished();
658 + finishOperation();
659 + }
660 + printCommandString();
661 + printSearchString();
662 + return exitNormalMode;
663 +}
664 +
665 +static inline void exitCommand(void) { terminateCommand(false); }
666 +
667 +static inline void abortCommand(void) { terminateCommand(true); }
668 +
669 +/// Go to next occurrence of string relative to the current location
670 +/// conduct search, starting at start pos
671 +static bool gotoString(int8_t sign) {
672 + moveLetter(sign);
673 + uint32_t const searchStrSize = size(&searchString);
674 + uint32_t const maxIter = (HISTSIZE+term.row) * term.col + searchStrSize;
675 + uint32_t findIdx = 0;
676 + for (uint32_t cIteration = 0; findIdx < searchStrSize
677 + && ++cIteration <= maxIter; moveLetter(sign)) {
678 + char const * const SEC(next, sign==1
679 + ? view(&searchString, findIdx)
680 + : viewEnd(&searchString, findIdx), , false)
681 + uint32_t const searchChar = *((uint32_t*) next);
682 +
683 + if (TLINE(term.c.y)[term.c.x].u == searchChar) { ++findIdx; }
684 + else { findIdx = 0; }
685 + }
686 + bool const found = findIdx == searchStrSize;
687 + for (uint32_t i = 0; found && i < searchStrSize; ++i) moveLetter(-sign);
688 + return found;
689 +}
690 +
691 +/// Highlight all found strings on the current screen.
692 +static void highlightStringOnScreen(void) {
693 + if (isEmpty(&searchString)) { return; }
694 + empty(&highlights);
695 + uint32_t const searchStringSize = size(&searchString);
696 + uint32_t findIdx = 0;
697 + uint32_t xStart, yStart;
698 + bool success = true;
699 + for (int y = 0; y < term.row && success; y++) {
700 + for (int x = 0; x < term.col && success; x++) {
701 + char const* const SEC(next,
702 + view(&searchString,findIdx),,)
703 + if (TLINE(y)[x].u == (Rune) *((uint32_t*)(next))) {
704 + if (++findIdx == 1) {
705 + xStart = x;
706 + yStart = y;
707 + }
708 + if (findIdx == searchStringSize) {
709 + success = success
710 + && append(&highlights, &xStart)
711 + && append(&highlights, &yStart);
712 + findIdx = 0; //term.dirty[yStart] = 1;
713 + }
714 + } else { findIdx = 0; }
715 + }
716 + }
717 + if (!success) { empty(&highlights); }
718 +}
719 +
720 +static bool gotoStringAndHighlight(int8_t sign) {
721 + // Find hte next occurrence of the #searchString in direction #sign
722 + bool const found = gotoString(sign);
723 + if (!found) { applyPosition(&stateVB.motion.searchPosition); }
724 + highlightStringOnScreen();
725 + //tsetdirt(0, term.row-3); //< everything except for the 'status bar'
726 + return found;
727 +}
728 +
729 +static bool pressKeys(char const* nullTerminatedString, size_t end) {
730 + bool sc = true;
731 + for (size_t i = 0; i < end && sc; ++i) {
732 + sc = kpressNormalMode(&nullTerminatedString[i], 1, false, NULL);
733 + }
734 + return sc;
735 +}
736 +
737 +static bool executeCommand(DynamicArray const *command) {
738 + size_t end=size(command);
739 + char decoded [32];
740 + bool succ = true;
741 + size_t len;
742 + for (size_t i = 0; i < end && succ; ++i) {
743 + char const *const SEC(nextRune, view(command, i),,false)
744 + len = utf8encode(*((Rune *) nextRune), decoded);
745 + succ = kpressNormalMode(decoded, len, false, NULL);
746 + }
747 + return succ;
748 +}
749 +
750 +struct { char const first; char const second; } const Brackets [] =
751 +{ {'(', ')'}, {'<', '>'}, {'{', '}'}, {'[', ']'}, };
752 +
753 +
754 +/// Emits Command prefix and suffix when i motion is performed (e.g. yiw).
755 +///
756 +/// @param c: motion character
757 +/// @param expandMode: 1 for 'i', 2 for 'a'
758 +/// @param first, second: Dynamic arrays in which the prefix and postfix
759 +/// commands will be returned
760 +/// @return whether the command could be extracted successfully.
761 +static bool expandExpression(char const c, enum Infix expandMode,
762 + char operation, DynamicArray *cmd) {
763 + empty(cmd);
764 + bool s = true; //< used in order to detect memory allocation errors.
765 + char const lower = tolower(c);
766 + // Motions
767 + if (lower == 'w') {
768 + // translated into wb[command]e resp. WB[command]E, which works
769 + // file even when at the fist letter. Does not work for single
770 + // letter words though.
771 + int const diff = c - lower;
772 + s = s && checkSetNextV(cmd, c);
773 + s = s && checkSetNextV(cmd, (signed char)(((int)'b') + diff));
774 + s = s && checkSetNextV(cmd, operation);
775 + s = s && checkSetNextV(cmd, (signed char)(((int)'e')+ diff));
776 + return s;
777 + }
778 + // Symmetrical brackets (quotation marks)
779 + if (c == '\'' || c == '"') {
780 + // Local ambiguity -> do nothing. It cannot be determined if
781 + // the current char is the 1st or last char of the selection.
782 + // <---- search here? -- ['] -- or search here? --->
783 + if (TLINE(term.c.y)[term.c.x].u == c) {
784 + return false;
785 + }
786 + // Prefix
787 + char res [] = {'?', c, '\n'};
788 + s = s && checkSetNextP(cmd, res);
789 + // infix
790 + bool const iffy = expandMode == infix_i;
791 + if (iffy) { s = s && checkSetNextV(cmd, 'l'); }
792 + s = s && checkSetNextV(cmd, operation);
793 + if (!iffy) { s = s && checkSetNextV(cmd, 'l'); }
794 + // suffix
795 + res[0] = '/';
796 + s = s && checkSetNextP(cmd, res);
797 + if (iffy) { s = s && checkSetNextV(cmd, 'h'); }
798 + return s;
799 + }
800 + // Brackets: Does not if in range / if the brackets belong togehter.
801 + for (size_t pid = 0; pid < sizeof(Brackets); ++pid) {
802 + if(Brackets[pid].first == c || Brackets[pid].second == c) {
803 + if (TLINE(term.c.y)[term.c.x].u!=Brackets[pid].first) {
804 + s = s && checkSetNextV(cmd, '?');
805 + s = s && checkSetNextV(cmd, Brackets[pid].first);
806 + s = s && checkSetNextV(cmd, '\n');
807 + }
808 + bool const iffy = expandMode == infix_i;
809 + if (iffy) { s = s && checkSetNextV(cmd, 'l'); }
810 + s = s && checkSetNextV(cmd, operation);
811 + if (!iffy) { s = s && checkSetNextV(cmd, 'l'); }
812 + s = s && checkSetNextV(cmd, '/');
813 + s = s && checkSetNextV(cmd, Brackets[pid].second);
814 + s = s && checkSetNextV(cmd, '\n');
815 + if (iffy) { s = s && checkSetNextV(cmd, 'h'); }
816 + return s;
817 + }
818 + }
819 + /**/
820 + // search string
821 + // complicated search operation: <tag>
822 + if (c == 't') {
823 + // XXX: (Bug in vim: @vit )
824 + // <tag_name attr="hier" a2="\<sch\>"> [current pos] </tag_name>
825 +
826 + // 1. Copy history ( tag := hist[?<\n:/ \n] )
827 + // 2. Copy history ( first_find := hist[?<\n: next place in
828 + // history where count '>' > count '<'
829 + // (can be behind current pos) )
830 + // 3. first := [?first_find][#first_ind]l
831 + // second:= [/tag">"]h
832 + //return true; // XXX: not implmented yet.
833 + }
834 + return false;
835 +}
836 +
837 +//
838 +// Public API
839 +//
840 +
841 +void onMove(void) {
842 + stateVB.initialPosition.x = term.c.x;
843 + stateVB.initialPosition.y = term.c.y;
844 + stateVB.initialPosition.yScr = term.scr;
845 +}
846 +
847 +int highlighted(int x, int y) {
848 + // Compute the legal bounds for a hit:
849 + int32_t const stringSize = size(&searchString);
850 + int32_t xMin = x - stringSize;
851 + int32_t yMin = y;
852 + while (xMin < 0 && yMin > 0) {
853 + xMin += term.col;
854 + --yMin;
855 + }
856 + if (xMin < 0) { xMin = 0; }
857 +
858 + uint32_t highSize = size(&highlights);
859 + ENSURE(highSize % 2 == 0, empty(&highlights); return false;);
860 + highSize /= 2;
861 + uint32_t *ptr = (uint32_t*) highlights.content;
862 + for (uint32_t i = 0; i < highSize; ++i) {
863 + int32_t const sx = (int32_t) *(ptr++);
864 + int32_t const sy = (int32_t) *(ptr++);
865 + if (BETWEEN(sy, yMin, y) && (sy != yMin || sx > xMin)
866 + && (sy != y || sx <= x)) {
867 + return true;
868 + }
869 + }
870 + return false;
871 +}
872 +
873 +ExitState kpressNormalMode(char const * cs, int len, bool ctrl, void const *v) {
874 + KeySym const * const ksym = (KeySym*) v;
875 + bool const esc = ksym && *ksym == XK_Escape;
876 + bool const enter = (ksym && *ksym==XK_Return) || (len==1 &&cs[0]=='\n');
877 + bool const quantifier = len == 1 && (BETWEEN(cs[0], 49, 57)
878 + || (cs[0] == 48 && stateVB.motion.amount));
879 + int const previousScroll = term.scr;
880 + // [ESC] or [ENTER] abort resp. finish the current level of operation.
881 + // Typing 'i' if no operation is currently performed behaves like ESC.
882 + if (esc || enter || (len == 1 && cs[0] == 'i' && isMotionFinished()
883 + && isOperationFinished())) {
884 + if (terminateCommand(!enter)) {
885 + applyPosition(&stateVB.initialPosition);
886 + Position const pc = stateVB.initialPosition;
887 + stateVB = defaultNormalMode;
888 + stateVB.initialPosition = pc;
889 + tfulldirt();
890 + return finished;
891 + }
892 + len = 0;
893 + goto motionFinish;
894 + }
895 + // Backspace
896 + if (ksym && *ksym == XK_BackSpace) {
897 + bool s = stateVB.motion.search!=none&&!stateVB.motion.finished;
898 + bool q = stateVB.motion.amount != 0;
899 + if (!(s || q)) { return failed; }
900 + len = 0;
901 +
902 + if (!isEmpty(currentCommand)) { pop(currentCommand); }
903 + if (s) {
904 + if (!isEmpty(&searchString)) { pop(&searchString); }
905 + else if (isEmpty(&searchString)) {
906 + exitCommand();
907 + return success;
908 + }
909 + } else if (q) {
910 + stateVB.motion.amount /= 10;
911 + goto finishNoAppend;
912 + }
913 + }
914 +
915 + // Search: append to search string, then search & highlight
916 + if (stateVB.motion.search != none && !stateVB.motion.finished) {
917 + if (len >= 1) {
918 + EXPAND(kSearch, &searchString, true)
919 + utf8decode(cs, (Rune*)(kSearch), len);
920 + }
921 + applyPosition(&stateVB.motion.searchPosition);
922 + gotoStringAndHighlight(getSearchDirection());
923 + goto finish;
924 + }
925 + if (len == 0) { return failed; }
926 + // Quantifiers
927 + if (quantifier) {
928 + stateVB.motion.amount = min(SHRT_MAX,
929 + stateVB.motion.amount * 10 + cs[0] - 48);
930 + goto finish;
931 + }
932 + // 'i' mode enabled, hence the expression is to be expanded:
933 + // [start_expression(cs[0])] [operation] [stop_expression(cs[0])]
934 + if (stateVB.command.infix != infix_none && stateVB.command.op != noop) {
935 + DynamicArray cmd = CHAR_ARRAY;
936 + char const operation = stateVB.command.op;
937 + bool succ = expandExpression(cs[0],
938 + stateVB.command.infix, visual, &cmd);
939 + if (operation == yank) {
940 + succ = succ && checkSetNextV(&cmd, operation);
941 + }
942 + NormalModeState const st = stateVB;
943 + TCursor const tc = term.c;
944 + stateVB.command.infix = infix_none;
945 + if (succ) {
946 + stateVB.command.op = noop;
947 + for (int i = 0; i < size(&cmd) && succ; ++i) {
948 + succ = pressKeys(&cmd.content[i], 1);
949 + }
950 + if (!succ) { // go back to the old position, apply op
951 + stateVB = st;
952 + term.c = tc;
953 + }
954 + empty(currentCommand);
955 + for (uint32_t i = 0; i < size(&cmd); ++i) {
956 + EXPAND(kCommand, currentCommand, true)
957 + utf8decode(cmd.content+i, (Rune*)(kCommand),1);
958 + }
959 + }
960 + free(cmd.content);
961 + goto finishNoAppend;
962 + }
963 + // Commands (V / v or y)
964 + switch(cs[0]) {
965 + case '.':
966 + {
967 + if (isEmpty(currentCommand)) { toggle = !toggle; }
968 + DynamicArray cmd = UTF8_ARRAY;
969 + swap(&cmd, currentCommand);
970 + executeCommand(&cmd) ? success : failed;
971 + swap(&cmd, currentCommand);
972 + free(cmd.content);
973 + goto finishNoAppend;
974 + }
975 + case 'i': stateVB.command.infix = infix_i; goto finish;
976 + case 'a': stateVB.command.infix = infix_a; goto finish;
977 + case 'y':
978 + switch(stateVB.command.op) {
979 + case noop: //< Start yank mode & set #op
980 + enableOperation(yank);
981 + selstart(term.c.x, term.c.y,term.scr,0);
982 + goto finish;
983 + case yank: //< Complete yank [y#amount j]
984 + selstart(0, term.c.y, term.scr, 0);
985 + int const origY = term.c.y;
986 + moveLine(max(stateVB.motion.amount, 1));
987 + selextend(term.col-1,term.c.y,term.scr,
988 + SEL_RECTANGULAR, 0);
989 + term.c.y = origY;
990 + FALLTHROUGH
991 + case visualLine: // Yank visual selection
992 + case visual:
993 + xsetsel(getsel());
994 + xclipcopy();
995 + exitCommand();
996 + goto finish;
997 + default:
998 + return failed;
999 + }
1000 + case visual:
1001 + case visualLine:
1002 + if (stateVB.command.op == cs[0]) {
1003 + finishOperation();
1004 + return true;
1005 + } else {
1006 + enableOperation(cs[0]);
1007 + selstart(cs[0] == visualLine ? 0 : term.c.x,
1008 + term.c.y, term.scr, 0);
1009 + goto finish;
1010 + }
1011 + }
1012 + // CTRL Motions
1013 + int32_t sign = -1; //< if command goes 'forward'(1) or 'backward'(-1)
1014 + if (ctrl) {
1015 + if (ksym == NULL) { return false; }
1016 + switch(*ksym) {
1017 + case XK_f:
1018 + term.scr = max(term.scr - max(term.row-2,1), 0);
1019 + term.c.y = 0;
1020 + goto finish;
1021 + case XK_b:
1022 + term.scr = min(term.scr + max(term.row - 2, 1),
1023 + HISTSIZE - 1);
1024 + term.c.y = term.bot;
1025 + goto finish;
1026 + case XK_u:
1027 + term.scr = min(term.scr+term.row/2, HISTSIZE-1);
1028 + goto finish;
1029 + case XK_d:
1030 + term.scr = max(term.scr - term.row / 2, 0);
1031 + goto finish;
1032 + default: return false;
1033 + }
1034 + }
1035 + // Motions
1036 + switch(cs[0]) {
1037 + case 'c': empty(&commandHist0); empty(&commandHist1);
1038 + goto finishNoAppend;
1039 + case 'j': sign = 1; FALLTHROUGH
1040 + case 'k': moveLine(max(stateVB.motion.amount,1) * sign);
1041 + goto motionFinish;
1042 + case 'H': term.c.y = 0;
1043 + goto motionFinish;
1044 + case 'M': term.c.y = term.bot / 2;
1045 + goto motionFinish;
1046 + case 'L': term.c.y = term.bot;
1047 + goto motionFinish;
1048 + case 'G': applyPosition(&stateVB.initialPosition);
1049 + goto motionFinish;
1050 + case 'l': sign = 1; FALLTHROUGH
1051 + case 'h': moveLetter(sign * max(stateVB.motion.amount,1));
1052 + goto motionFinish;
1053 + case '0': term.c.x = 0;
1054 + goto motionFinish;
1055 + case '$': term.c.x = term.col-1;
1056 + goto motionFinish;
1057 + case 'w': FALLTHROUGH
1058 + case 'W': FALLTHROUGH
1059 + case 'e': FALLTHROUGH
1060 + case 'E': sign = 1; FALLTHROUGH
1061 + case 'B': FALLTHROUGH
1062 + case 'b': {
1063 + char const * const wDelim =
1064 + cs[0] <= 90 ? wordDelimLarge : wordDelimSmall;
1065 + uint32_t const wDelimLen = strlen(wDelim);
1066 +
1067 + bool const startSpaceIsSeparator =
1068 + !(cs[0] == 'w' || cs[0] == 'W');
1069 + // Whether to start & end with offset:
1070 + bool const performOffset = startSpaceIsSeparator;
1071 + // Max iteration := One complete hist traversal.
1072 + uint32_t const maxIter = (HISTSIZE+term.row) * term.col;
1073 + // Doesn't work exactly as in vim: Linebreak is
1074 + // counted as 'normal' separator, hence a jump can
1075 + // span multiple lines here.
1076 + stateVB.motion.amount = max(stateVB.motion.amount, 1);
1077 + for (;stateVB.motion.amount>0;--stateVB.motion.amount) {
1078 + uint8_t state = 0;
1079 + if (performOffset) { moveLetter(sign); }
1080 + for (uint32_t cIt = 0; cIt ++ < maxIter; moveLetter(sign)) {
1081 + if (startSpaceIsSeparator == contains(TLINE(term.c.y)[term.c.x].u, wDelim, wDelimLen)) {
1082 + if (state == 1) {
1083 + if (performOffset) {
1084 + moveLetter(-sign);
1085 + }
1086 + break;
1087 + }
1088 + } else if (state == 0) { state = 1; }
1089 + }
1090 + }
1091 + goto motionFinish;
1092 + }
1093 + case '/': sign = 1; FALLTHROUGH
1094 + case '?':
1095 + empty(&searchString);
1096 + stateVB.motion.search = sign == 1 ? forward : backward;
1097 + stateVB.motion.searchPosition.x = term.c.x;
1098 + stateVB.motion.searchPosition.y = term.c.y;
1099 + stateVB.motion.searchPosition.yScr = term.scr;
1100 + stateVB.motion.finished = false;
1101 + goto finish;
1102 + case 'n': sign = 1; FALLTHROUGH
1103 + case 'N': {
1104 + if (stateVB.motion.search == none) return failed;
1105 + if (stateVB.motion.search == backward) { sign *= -1; }
1106 + bool b = true; int ox = term.c.x;
1107 + int oy = term.c.y ; int scr = term.scr;
1108 + int32_t i = max(stateVB.motion.amount, 1);
1109 + for (;i>0 && (b=gotoString(sign)); --i) {
1110 + oy = term.c.y; scr = term.scr;
1111 + }
1112 + if (!b) { term.c.x = ox; term.c.y = oy; term.scr = scr;}
1113 + goto motionFinish;
1114 + }
1115 + case 't': // Toggle selection mode and set dirt.
1116 + sel.type = sel.type == SEL_REGULAR
1117 + ? SEL_RECTANGULAR : SEL_REGULAR;
1118 + //tsetdirt(sel.nb.y, sel.ne.y);
1119 + goto motionFinish;
1120 + }
1121 + // Custom commands
1122 + for (size_t i = 0; i < amountNormalModeShortcuts; ++i) {
1123 + if (cs[0] == normalModeShortcuts[i].key) {
1124 + return pressKeys(normalModeShortcuts[i].value,
1125 + strlen(normalModeShortcuts[i].value))
1126 + ? success : failed;
1127 + }
1128 + }
1129 + return failed;
1130 +motionFinish:
1131 + stateVB.motion.amount = 0;
1132 + //if (isMotionFinished() && stateVB.command.op == yank) {
1133 + if (stateVB.command.op == yank) {
1134 + selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
1135 + xsetsel(getsel());
1136 + xclipcopy();
1137 + exitCommand();
1138 + }
1139 +finish:
1140 + if (len == 1 && !ctrl) { // XXX: for now.
1141 + EXPAND(kCommand, currentCommand, true)
1142 + utf8decode(cs, (Rune*)(kCommand), len);
1143 + }
1144 +finishNoAppend:
1145 + if (stateVB.command.op == visual) {
1146 + selextend(term.c.x, term.c.y, term.scr, sel.type, 0);
1147 + } else if (stateVB.command.op == visualLine) {
1148 + selextend(term.col-1, term.c.y, term.scr, sel.type, 0);
1149 + }
1150 +
1151 + if (previousScroll != term.scr && !isEmpty(&searchString)) {
1152 + highlightStringOnScreen();
1153 + }
1154 + tsetdirt(0, term.row-3); //< Required because of the cursor cross.
1155 + printCommandString();
1156 + printSearchString();
1157 + return success;
1158 +}
1159 diff -ruN st-default/normalMode.h st1/normalMode.h
1160 --- st-default/normalMode.h 1970-01-01 01:00:00.000000000 +0100
1161 +++ st1/normalMode.h 2020-06-04 11:04:30.229111510 +0200
1162 @@ -0,0 +1,36 @@
1163 +/* See LICENSE for license details. */
1164 +#ifndef NORMAL_MODE_H
1165 +#define NORMAL_MODE_H
1166 +
1167 +#include <stdbool.h>
1168 +#include <stddef.h>
1169 +#include <stdint.h>
1170 +
1171 +/// Used in the configuration file to define custom shortcuts.
1172 +typedef struct NormalModeShortcuts {
1173 + char key;
1174 + char *value;
1175 +} NormalModeShortcuts;
1176 +
1177 +/// Holds the exit status of the #kpressNormalMode function, which informs the
1178 +/// caller when to exit normal mode.
1179 +typedef enum ExitState {
1180 + failed = 0,
1181 + success = 1,
1182 + finished = 2,
1183 +} ExitState;
1184 +
1185 +/// Called when curr position is altered.
1186 +void onMove(void);
1187 +
1188 +/// Function which returns whether the value at position provided as arguments
1189 +/// is to be highlighted.
1190 +int highlighted(int, int);
1191 +
1192 +/// Handles keys in normal mode.
1193 +ExitState kpressNormalMode(char const * decoded, int len, bool ctrlPressed,
1194 + void const * ksym);
1195 + //bool esc, bool enter, bool backspace, void* keysym);
1196 +
1197 +
1198 +#endif // NORMAL_MODE_H
1199 Binary files st-default/normalMode.o and st1/normalMode.o differ
1200 Binary files st-default/st and st1/st differ
1201 diff -ruN st-default/st.c st1/st.c
1202 --- st-default/st.c 2020-06-04 11:15:55.165135902 +0200
1203 +++ st1/st.c 2020-06-04 11:04:30.231111510 +0200
1204 @@ -1,8 +1,10 @@
1205 /* See LICENSE for license details. */
1206 +#include <assert.h>
1207 #include <ctype.h>
1208 #include <errno.h>
1209 #include <fcntl.h>
1210 #include <limits.h>
1211 +#include <math.h>
1212 #include <pwd.h>
1213 #include <stdarg.h>
1214 #include <stdio.h>
1215 @@ -17,6 +19,8 @@
1216 #include <unistd.h>
1217 #include <wchar.h>
1218
1219 +
1220 +#include "term.h"
1221 #include "st.h"
1222 #include "win.h"
1223
1224 @@ -42,6 +46,7 @@
1225 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f))
1226 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c))
1227 #define ISDELIM(u) (u && wcschr(worddelimiters, u))
1228 +#define INTERVAL(x, a, b) (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
1229
1230 enum term_mode {
1231 MODE_WRAP = 1 << 0,
1232 @@ -86,17 +91,17 @@
1233 ESC_DCS =128,
1234 };
1235
1236 -typedef struct {
1237 - Glyph attr; /* current char attributes */
1238 - int x;
1239 - int y;
1240 - char state;
1241 -} TCursor;
1242 -
1243 -typedef struct {
1244 - int mode;
1245 - int type;
1246 - int snap;
1247 +/*typedef struct {*/
1248 + /*Glyph attr; [> current char attributes <]*/
1249 + /*int x;*/
1250 + /*int y;*/
1251 + /*char state;*/
1252 +/*} TCursor;*/
1253 +
1254 +/*typedef struct {*/
1255 + /*int mode;*/
1256 + /*int type;*/
1257 + /*int snap;*/
1258 /*
1259 * Selection variables:
1260 * nb – normalized coordinates of the beginning of the selection
1261 @@ -104,33 +109,33 @@
1262 * ob – original coordinates of the beginning of the selection
1263 * oe – original coordinates of the end of the selection
1264 */
1265 - struct {
1266 - int x, y;
1267 - } nb, ne, ob, oe;
1268 -
1269 - int alt;
1270 -} Selection;
1271 -
1272 -/* Internal representation of the screen */
1273 -typedef struct {
1274 - int row; /* nb row */
1275 - int col; /* nb col */
1276 - Line *line; /* screen */
1277 - Line *alt; /* alternate screen */
1278 - int *dirty; /* dirtyness of lines */
1279 - TCursor c; /* cursor */
1280 - int ocx; /* old cursor col */
1281 - int ocy; /* old cursor row */
1282 - int top; /* top scroll limit */
1283 - int bot; /* bottom scroll limit */
1284 - int mode; /* terminal mode flags */
1285 - int esc; /* escape state flags */
1286 - char trantbl[4]; /* charset table translation */
1287 - int charset; /* current charset */
1288 - int icharset; /* selected charset for sequence */
1289 - int *tabs;
1290 - Rune lastc; /* last printed char outside of sequence, 0 if control */
1291 -} Term;
1292 + /*struct {*/
1293 + /*int x, y;*/
1294 + /*} nb, ne, ob, oe;*/
1295 +
1296 + /*int alt;*/
1297 +/*} Selection;*/
1298 +
1299 +/*[> Internal representation of the screen <]*/
1300 +/*typedef struct {*/
1301 + /*int row; [> nb row <]*/
1302 + /*int col; [> nb col <]*/
1303 + /*Line *line; [> screen <]*/
1304 + /*Line *alt; [> alternate screen <]*/
1305 + /*int *dirty; [> dirtyness of lines <]*/
1306 + /*TCursor c; [> cursor <]*/
1307 + /*int ocx; [> old cursor col <]*/
1308 + /*int ocy; [> old cursor row <]*/
1309 + /*int top; [> top scroll limit <]*/
1310 + /*int bot; [> bottom scroll limit <]*/
1311 + /*int mode; [> terminal mode flags <]*/
1312 + /*int esc; [> escape state flags <]*/
1313 + /*char trantbl[4]; [> charset table translation <]*/
1314 + /*int charset; [> current charset <]*/
1315 + /*int icharset; [> selected charset for sequence <]*/
1316 + /*int *tabs;*/
1317 + /*Rune lastc; [> last printed char outside of sequence, 0 if control <]*/
1318 +/*} Term;*/
1319
1320 /* CSI Escape sequence structs */
1321 /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
1322 @@ -154,6 +159,8 @@
1323 int narg; /* nb of args */
1324 } STREscape;
1325
1326 +void tfulldirt(void);
1327 +
1328 static void execsh(char *, char **);
1329 static void stty(char **);
1330 static void sigchld(int);
1331 @@ -186,16 +193,14 @@
1332 static void tputtab(int);
1333 static void tputc(Rune);
1334 static void treset(void);
1335 -static void tscrollup(int, int);
1336 -static void tscrolldown(int, int);
1337 +static void tscrollup(int, int, int);
1338 +static void tscrolldown(int, int, int);
1339 static void tsetattr(int *, int);
1340 static void tsetchar(Rune, Glyph *, int, int);
1341 -static void tsetdirt(int, int);
1342 static void tsetscroll(int, int);
1343 static void tswapscreen(void);
1344 static void tsetmode(int, int, int *, int);
1345 static int twrite(const char *, int, int);
1346 -static void tfulldirt(void);
1347 static void tcontrolcode(uchar );
1348 static void tdectest(char );
1349 static void tdefutf8(char);
1350 @@ -209,8 +214,6 @@
1351 static void selscroll(int, int);
1352 static void selsnap(int *, int *, int);
1353
1354 -static size_t utf8decode(const char *, Rune *, size_t);
1355 -static Rune utf8decodebyte(char, size_t *);
1356 static char utf8encodebyte(Rune, size_t);
1357 static size_t utf8validate(Rune *, size_t);
1358
1359 @@ -220,8 +223,8 @@
1360 static ssize_t xwrite(int, const char *, size_t);
1361
1362 /* Globals */
1363 -static Term term;
1364 -static Selection sel;
1365 +Term term;
1366 +Selection sel;
1367 static CSIEscape csiescseq;
1368 static STREscape strescseq;
1369 static int iofd = 1;
1370 @@ -416,17 +419,22 @@
1371 {
1372 int i = term.col;
1373
1374 - if (term.line[y][i - 1].mode & ATTR_WRAP)
1375 + if (TLINE(y)[i - 1].mode & ATTR_WRAP)
1376 return i;
1377
1378 - while (i > 0 && term.line[y][i - 1].u == ' ')
1379 + while (i > 0 && TLINE(y)[i - 1].u == ' ')
1380 --i;
1381
1382 return i;
1383 }
1384
1385 void
1386 -selstart(int col, int row, int snap)
1387 +xselstart(int col, int row, int snap) {
1388 + selstart(col, row, term.scr, snap);
1389 +}
1390 +
1391 +void
1392 +selstart(int col, int row, int scroll, int snap)
1393 {
1394 selclear();
1395 sel.mode = SEL_EMPTY;
1396 @@ -435,6 +443,7 @@
1397 sel.snap = snap;
1398 sel.oe.x = sel.ob.x = col;
1399 sel.oe.y = sel.ob.y = row;
1400 + sel.oe.scroll = sel.ob.scroll = scroll;
1401 selnormalize();
1402
1403 if (sel.snap != 0)
1404 @@ -443,10 +452,13 @@
1405 }
1406
1407 void
1408 -selextend(int col, int row, int type, int done)
1409 -{
1410 - int oldey, oldex, oldsby, oldsey, oldtype;
1411 +xselextend(int col, int row, int type, int done) {
1412 + selextend(col, row, term.scr, type, done);
1413 +}
1414
1415 +void
1416 +selextend(int col, int row, int scroll, int type, int done)
1417 +{
1418 if (sel.mode == SEL_IDLE)
1419 return;
1420 if (done && sel.mode == SEL_EMPTY) {
1421 @@ -454,18 +466,22 @@
1422 return;
1423 }
1424
1425 - oldey = sel.oe.y;
1426 - oldex = sel.oe.x;
1427 - oldsby = sel.nb.y;
1428 - oldsey = sel.ne.y;
1429 - oldtype = sel.type;
1430 + int const oldey = sel.oe.y;
1431 + int const oldex = sel.oe.x;
1432 + int const oldscroll = sel.oe.scroll;
1433 + int const oldsby = sel.nb.y;
1434 + int const oldsey = sel.ne.y;
1435 + int const oldtype = sel.type;
1436
1437 sel.oe.x = col;
1438 sel.oe.y = row;
1439 + sel.oe.scroll = scroll;
1440 +
1441 selnormalize();
1442 sel.type = type;
1443
1444 - if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.type || sel.mode == SEL_EMPTY)
1445 + if (oldey != sel.oe.y || oldex != sel.oe.x || oldscroll != sel.oe.scroll
1446 + || oldtype != sel.type || sel.mode == SEL_EMPTY)
1447 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey));
1448
1449 sel.mode = done ? SEL_IDLE : SEL_READY;
1450 @@ -474,17 +490,21 @@
1451 void
1452 selnormalize(void)
1453 {
1454 - int i;
1455 -
1456 - if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) {
1457 - sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x;
1458 - sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x;
1459 + sel.nb.y = INTERVAL(sel.ob.y + term.scr - sel.ob.scroll, 0, term.bot);
1460 + sel.ne.y = INTERVAL(sel.oe.y + term.scr - sel.oe.scroll, 0, term.bot);
1461 + if (sel.type == SEL_REGULAR && sel.nb.y != sel.ne.y) {
1462 + sel.nb.x = sel.nb.y < sel.ne.y ? sel.ob.x : sel.oe.x;
1463 + sel.ne.x = sel.nb.y < sel.ne.y ? sel.oe.x : sel.ob.x;
1464 } else {
1465 sel.nb.x = MIN(sel.ob.x, sel.oe.x);
1466 sel.ne.x = MAX(sel.ob.x, sel.oe.x);
1467 }
1468 - sel.nb.y = MIN(sel.ob.y, sel.oe.y);
1469 - sel.ne.y = MAX(sel.ob.y, sel.oe.y);
1470 +
1471 + if (sel.nb.y > sel.ne.y) {
1472 + int32_t const tmp = sel.nb.y;
1473 + sel.nb.y = sel.ne.y;
1474 + sel.ne.y = tmp;
1475 + }
1476
1477 selsnap(&sel.nb.x, &sel.nb.y, -1);
1478 selsnap(&sel.ne.x, &sel.ne.y, +1);
1479 @@ -492,7 +512,7 @@
1480 /* expand selection over line breaks */
1481 if (sel.type == SEL_RECTANGULAR)
1482 return;
1483 - i = tlinelen(sel.nb.y);
1484 + int i = tlinelen(sel.nb.y);
1485 if (i < sel.nb.x)
1486 sel.nb.x = i;
1487 if (tlinelen(sel.ne.y) <= sel.ne.x)
1488 @@ -528,7 +548,7 @@
1489 * Snap around if the word wraps around at the end or
1490 * beginning of a line.
1491 */
1492 - prevgp = &term.line[*y][*x];
1493 + prevgp = &TLINE(*y)[*x];
1494 prevdelim = ISDELIM(prevgp->u);
1495 for (;;) {
1496 newx = *x + direction;
1497 @@ -543,14 +563,14 @@
1498 yt = *y, xt = *x;
1499 else
1500 yt = newy, xt = newx;
1501 - if (!(term.line[yt][xt].mode & ATTR_WRAP))
1502 + if (!(TLINE(yt)[xt].mode & ATTR_WRAP))
1503 break;
1504 }
1505
1506 if (newx >= tlinelen(newy))
1507 break;
1508
1509 - gp = &term.line[newy][newx];
1510 + gp = &TLINE(newy)[newx];
1511 delim = ISDELIM(gp->u);
1512 if (!(gp->mode & ATTR_WDUMMY) && (delim != prevdelim
1513 || (delim && gp->u != prevgp->u)))
1514 @@ -571,14 +591,14 @@
1515 *x = (direction < 0) ? 0 : term.col - 1;
1516 if (direction < 0) {
1517 for (; *y > 0; *y += direction) {
1518 - if (!(term.line[*y-1][term.col-1].mode
1519 + if (!(TLINE(*y-1)[term.col-1].mode
1520 & ATTR_WRAP)) {
1521 break;
1522 }
1523 }
1524 } else if (direction > 0) {
1525 for (; *y < term.row-1; *y += direction) {
1526 - if (!(term.line[*y][term.col-1].mode
1527 + if (!(TLINE(*y)[term.col-1].mode
1528 & ATTR_WRAP)) {
1529 break;
1530 }
1531 @@ -598,24 +618,32 @@
1532 if (sel.ob.x == -1)
1533 return NULL;
1534
1535 - bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ;
1536 + int32_t syb = sel.ob.y - sel.ob.scroll + term.scr;
1537 + int32_t sye = sel.oe.y - sel.oe.scroll + term.scr;
1538 + if (syb > sye) {
1539 + int32_t tmp = sye;
1540 + sye = syb;
1541 + syb = tmp;
1542 + }
1543 +
1544 + bufsize = (term.col+1) * (sye - syb + 1) * UTF_SIZ;
1545 ptr = str = xmalloc(bufsize);
1546
1547 /* append every set & selected glyph to the selection */
1548 - for (y = sel.nb.y; y <= sel.ne.y; y++) {
1549 + for (y = syb; y <= sye; y++) {
1550 if ((linelen = tlinelen(y)) == 0) {
1551 *ptr++ = '\n';
1552 continue;
1553 }
1554
1555 if (sel.type == SEL_RECTANGULAR) {
1556 - gp = &term.line[y][sel.nb.x];
1557 + gp = &TLINE(y)[sel.nb.x];
1558 lastx = sel.ne.x;
1559 } else {
1560 - gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0];
1561 - lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1;
1562 + gp = &TLINE(y)[syb == y ? sel.nb.x : 0];
1563 + lastx = (sye == y) ? sel.ne.x : term.col-1;
1564 }
1565 - last = &term.line[y][MIN(lastx, linelen-1)];
1566 + last = &TLINE(y)[MIN(lastx, linelen-1)];
1567 while (last >= gp && last->u == ' ')
1568 --last;
1569
1570 @@ -850,6 +878,9 @@
1571 ttywrite(const char *s, size_t n, int may_echo)
1572 {
1573 const char *next;
1574 + Arg arg = (Arg) { .i = term.scr };
1575 +
1576 + kscrolldown(&arg);
1577
1578 if (may_echo && IS_SET(MODE_ECHO))
1579 twrite(s, n, 1);
1580 @@ -1061,13 +1092,53 @@
1581 }
1582
1583 void
1584 -tscrolldown(int orig, int n)
1585 +kscrolldown(const Arg* a)
1586 +{
1587 + int n = a->i;
1588 +
1589 + if (n < 0)
1590 + n = term.row + n;
1591 +
1592 + if (n > term.scr)
1593 + n = term.scr;
1594 +
1595 + if (term.scr > 0) {
1596 + term.scr -= n;
1597 + selscroll(0, -n);
1598 + tfulldirt();
1599 + }
1600 +}
1601 +
1602 +void
1603 +kscrollup(const Arg* a)
1604 +{
1605 + int n = a->i;
1606 +
1607 + if (n < 0)
1608 + n = term.row + n;
1609 +
1610 + if (term.scr <= HISTSIZE-n) {
1611 + term.scr += n;
1612 + selscroll(0, n);
1613 + tfulldirt();
1614 + }
1615 +}
1616 +
1617 +void
1618 +tscrolldown(int orig, int n, int copyhist)
1619 {
1620 int i;
1621 Line temp;
1622
1623 LIMIT(n, 0, term.bot-orig+1);
1624
1625 + if (copyhist) {
1626 + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE;
1627 + temp = term.hist[term.histi];
1628 + term.hist[term.histi] = term.line[term.bot];
1629 + term.line[term.bot] = temp;
1630 + }
1631 +
1632 tsetdirt(orig, term.bot-n);
1633 tclearregion(0, term.bot-n+1, term.col-1, term.bot);
1634
1635 @@ -1081,13 +1152,23 @@
1636 }
1637
1638 void
1639 -tscrollup(int orig, int n)
1640 +tscrollup(int orig, int n, int copyhist)
1641 {
1642 int i;
1643 Line temp;
1644
1645 LIMIT(n, 0, term.bot-orig+1);
1646
1647 + if (copyhist) {
1648 + term.histi = (term.histi + 1) % HISTSIZE;
1649 + temp = term.hist[term.histi];
1650 + term.hist[term.histi] = term.line[orig];
1651 + term.line[orig] = temp;
1652 + }
1653 +
1654 + if (term.scr > 0 && term.scr < HISTSIZE)
1655 + term.scr = MIN(term.scr + n, HISTSIZE-1);
1656 +
1657 tclearregion(0, orig, term.col-1, orig+n-1);
1658 tsetdirt(orig+n, term.bot);
1659
1660 @@ -1109,6 +1190,7 @@
1661 if (BETWEEN(sel.nb.y, orig, term.bot) != BETWEEN(sel.ne.y, orig, term.bot)) {
1662 selclear();
1663 } else if (BETWEEN(sel.nb.y, orig, term.bot)) {
1664 + sel.oe.scroll = sel.ob.scroll = term.scr;
1665 sel.ob.y += n;
1666 sel.oe.y += n;
1667 if (sel.ob.y < term.top || sel.ob.y > term.bot ||
1668 @@ -1126,13 +1208,19 @@
1669 int y = term.c.y;
1670
1671 if (y == term.bot) {
1672 - tscrollup(term.top, 1);
1673 + tscrollup(term.top, 1, 1);
1674 } else {
1675 y++;
1676 }
1677 tmoveto(first_col ? 0 : term.c.x, y);
1678 }
1679
1680 +int
1681 +currentLine(int x, int y)
1682 +{
1683 + return (x == term.c.x || y == term.c.y);
1684 +}
1685 +
1686 void
1687 csiparse(void)
1688 {
1689 @@ -1185,6 +1273,8 @@
1690 term.c.state &= ~CURSOR_WRAPNEXT;
1691 term.c.x = LIMIT(x, 0, term.col-1);
1692 term.c.y = LIMIT(y, miny, maxy);
1693 + // Set the last position in order to restore after normal mode exits.
1694 + onMove();
1695 }
1696
1697 void
1698 @@ -1291,14 +1381,14 @@
1699 tinsertblankline(int n)
1700 {
1701 if (BETWEEN(term.c.y, term.top, term.bot))
1702 - tscrolldown(term.c.y, n);
1703 + tscrolldown(term.c.y, n, 0);
1704 }
1705
1706 void
1707 tdeleteline(int n)
1708 {
1709 if (BETWEEN(term.c.y, term.top, term.bot))
1710 - tscrollup(term.c.y, n);
1711 + tscrollup(term.c.y, n, 0);
1712 }
1713
1714 int32_t
1715 @@ -1735,11 +1825,11 @@
1716 break;
1717 case 'S': /* SU -- Scroll <n> line up */
1718 DEFAULT(csiescseq.arg[0], 1);
1719 - tscrollup(term.top, csiescseq.arg[0]);
1720 + tscrollup(term.top, csiescseq.arg[0], 0);
1721 break;
1722 case 'T': /* SD -- Scroll <n> line down */
1723 DEFAULT(csiescseq.arg[0], 1);
1724 - tscrolldown(term.top, csiescseq.arg[0]);
1725 + tscrolldown(term.top, csiescseq.arg[0], 0);
1726 break;
1727 case 'L': /* IL -- Insert <n> blank lines */
1728 DEFAULT(csiescseq.arg[0], 1);
1729 @@ -2246,7 +2336,7 @@
1730 return 0;
1731 case 'D': /* IND -- Linefeed */
1732 if (term.c.y == term.bot) {
1733 - tscrollup(term.top, 1);
1734 + tscrollup(term.top, 1, 1);
1735 } else {
1736 tmoveto(term.c.x, term.c.y+1);
1737 }
1738 @@ -2259,7 +2349,7 @@
1739 break;
1740 case 'M': /* RI -- Reverse index */
1741 if (term.c.y == term.top) {
1742 - tscrolldown(term.top, 1);
1743 + tscrolldown(term.top, 1, 1);
1744 } else {
1745 tmoveto(term.c.x, term.c.y-1);
1746 }
1747 @@ -2301,7 +2391,7 @@
1748 {
1749 char c[UTF_SIZ];
1750 int control;
1751 - int width, len;
1752 + int width = 0, len;
1753 Glyph *gp;
1754
1755 control = ISCONTROL(u);
1756 @@ -2481,7 +2571,7 @@
1757 void
1758 tresize(int col, int row)
1759 {
1760 - int i;
1761 + int i, j;
1762 int minrow = MIN(row, term.row);
1763 int mincol = MIN(col, term.col);
1764 int *bp;
1765 @@ -2518,6 +2608,14 @@
1766 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty));
1767 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs));
1768
1769 + for (i = 0; i < HISTSIZE; i++) {
1770 + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph));
1771 + for (j = mincol; j < col; j++) {
1772 + term.hist[i][j] = term.c.attr;
1773 + term.hist[i][j].u = ' ';
1774 + }
1775 + }
1776 +
1777 /* resize each row to new width, zero-pad if needed */
1778 for (i = 0; i < minrow; i++) {
1779 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph));
1780 @@ -2576,7 +2674,7 @@
1781 continue;
1782
1783 term.dirty[y] = 0;
1784 - xdrawline(term.line[y], x1, y, x2);
1785 + xdrawline(TLINE(y), x1, y, x2);
1786 }
1787 }
1788
1789 @@ -2597,8 +2695,8 @@
1790 cx--;
1791
1792 drawregion(0, 0, term.col, term.row);
1793 - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
1794 - term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
1795 + xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
1796 + term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
1797 term.ocx = cx;
1798 term.ocy = term.c.y;
1799 xfinishdraw();
1800 diff -ruN st-default/st.c.rej st1/st.c.rej
1801 --- st-default/st.c.rej 1970-01-01 01:00:00.000000000 +0100
1802 +++ st1/st.c.rej 2020-06-04 11:04:30.231111510 +0200
1803 @@ -0,0 +1,73 @@
1804 +--- st.c
1805 ++++ st.c
1806 +@@ -91,51 +96,6 @@ enum escape_state {
1807 + ESC_DCS =128,
1808 + };
1809 +
1810 +-typedef struct {
1811 +- Glyph attr; /* current char attributes */
1812 +- int x;
1813 +- int y;
1814 +- char state;
1815 +-} TCursor;
1816 +-
1817 +-typedef struct {
1818 +- int mode;
1819 +- int type;
1820 +- int snap;
1821 +- /*
1822 +- * Selection variables:
1823 +- * nb – normalized coordinates of the beginning of the selection
1824 +- * ne – normalized coordinates of the end of the selection
1825 +- * ob – original coordinates of the beginning of the selection
1826 +- * oe – original coordinates of the end of the selection
1827 +- */
1828 +- struct {
1829 +- int x, y;
1830 +- } nb, ne, ob, oe;
1831 +-
1832 +- int alt;
1833 +-} Selection;
1834 +-
1835 +-/* Internal representation of the screen */
1836 +-typedef struct {
1837 +- int row; /* nb row */
1838 +- int col; /* nb col */
1839 +- Line *line; /* screen */
1840 +- Line *alt; /* alternate screen */
1841 +- int *dirty; /* dirtyness of lines */
1842 +- TCursor c; /* cursor */
1843 +- int ocx; /* old cursor col */
1844 +- int ocy; /* old cursor row */
1845 +- int top; /* top scroll limit */
1846 +- int bot; /* bottom scroll limit */
1847 +- int mode; /* terminal mode flags */
1848 +- int esc; /* escape state flags */
1849 +- char trantbl[4]; /* charset table translation */
1850 +- int charset; /* current charset */
1851 +- int icharset; /* selected charset for sequence */
1852 +- int *tabs;
1853 +-} Term;
1854 +-
1855 + /* CSI Escape sequence structs */
1856 + /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */
1857 + typedef struct {
1858 +@@ -1174,6 +1210,7 @@ selscroll(int orig, int n)
1859 + return;
1860 +
1861 + if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig, term.bot)) {
1862 ++ sel.oe.scroll = sel.ob.scroll = term.scr;
1863 + if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < term.top) {
1864 + selclear();
1865 + return;
1866 +@@ -2681,8 +2734,8 @@ draw(void)
1867 + cx--;
1868 +
1869 + drawregion(0, 0, term.col, term.row);
1870 +- xdrawcursor(cx, term.c.y, term.line[term.c.y][cx],
1871 +- term.ocx, term.ocy, term.line[term.ocy][term.ocx]);
1872 ++ xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx],
1873 ++ term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]);
1874 + term.ocx = cx, term.ocy = term.c.y;
1875 + xfinishdraw();
1876 + xximspot(term.ocx, term.ocy);
1877 diff -ruN st-default/st.h st1/st.h
1878 --- st-default/st.h 2020-06-04 11:15:55.165135902 +0200
1879 +++ st1/st.h 2020-06-04 11:04:30.232111510 +0200
1880 @@ -1,5 +1,8 @@
1881 /* See LICENSE for license details. */
1882
1883 +#include "glyph.h"
1884 +#include "normalMode.h"
1885 +
1886 #include <stdint.h>
1887 #include <sys/types.h>
1888
1889 @@ -33,6 +36,8 @@
1890 ATTR_WRAP = 1 << 8,
1891 ATTR_WIDE = 1 << 9,
1892 ATTR_WDUMMY = 1 << 10,
1893 + ATTR_HIGHLIGHT = 1 << 12,
1894 + ATTR_CURRENT = 1 << 13,
1895 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
1896 };
1897
1898 @@ -42,11 +47,6 @@
1899 SEL_READY = 2
1900 };
1901
1902 -enum selection_type {
1903 - SEL_REGULAR = 1,
1904 - SEL_RECTANGULAR = 2
1905 -};
1906 -
1907 enum selection_snap {
1908 SNAP_WORD = 1,
1909 SNAP_LINE = 2
1910 @@ -57,18 +57,6 @@
1911 typedef unsigned long ulong;
1912 typedef unsigned short ushort;
1913
1914 -typedef uint_least32_t Rune;
1915 -
1916 -#define Glyph Glyph_
1917 -typedef struct {
1918 - Rune u; /* character code */
1919 - ushort mode; /* attribute flags */
1920 - uint32_t fg; /* foreground */
1921 - uint32_t bg; /* background */
1922 -} Glyph;
1923 -
1924 -typedef Glyph *Line;
1925 -
1926 typedef union {
1927 int i;
1928 uint ui;
1929 @@ -81,6 +69,11 @@
1930 void redraw(void);
1931 void draw(void);
1932
1933 +int currentLine(int, int);
1934 +void kscrolldown(const Arg *);
1935 +void kscrollup(const Arg *);
1936 +void normalMode(Arg const *);
1937 +
1938 void printscreen(const Arg *);
1939 void printsel(const Arg *);
1940 void sendbreak(const Arg *);
1941 @@ -90,6 +83,9 @@
1942 void tnew(int, int);
1943 void tresize(int, int);
1944 void tsetdirtattr(int);
1945 +size_t utf8decode(const char *, Rune *, size_t);
1946 +Rune utf8decodebyte(char, size_t *);
1947 +void tsetdirt(int, int);
1948 void ttyhangup(void);
1949 int ttynew(char *, char *, char *, char **);
1950 size_t ttyread(void);
1951 @@ -100,8 +96,10 @@
1952
1953 void selclear(void);
1954 void selinit(void);
1955 -void selstart(int, int, int);
1956 -void selextend(int, int, int, int);
1957 +void selstart(int, int, int, int);
1958 +void xselstart(int, int, int);
1959 +void selextend(int, int, int, int, int);
1960 +void xselextend(int, int, int, int);
1961 int selected(int, int);
1962 char *getsel(void);
1963
1964 Binary files st-default/st.o and st1/st.o differ
1965 diff -ruN st-default/term.h st1/term.h
1966 --- st-default/term.h 1970-01-01 01:00:00.000000000 +0100
1967 +++ st1/term.h 2020-06-04 11:04:30.232111510 +0200
1968 @@ -0,0 +1,74 @@
1969 +#ifndef TERM_H
1970 +#define TERM_H
1971 +
1972 +//
1973 +// Internal terminal structs.
1974 +//
1975 +
1976 +#include "glyph.h"
1977 +
1978 +#include <stdint.h>
1979 +
1980 +#define HISTSIZE 2500
1981 +
1982 +typedef struct {
1983 + Glyph attr; /* current char attributes */
1984 + int x;
1985 + int y;
1986 + char state;
1987 +} TCursor;
1988 +
1989 +typedef struct {
1990 + int mode;
1991 + int type;
1992 + int snap;
1993 + /// Selection variables:
1994 + /// ob – original coordinates of the beginning of the selection
1995 + /// oe – original coordinates of the end of the selection
1996 + struct {
1997 + int x, y, scroll;
1998 + } ob, oe;
1999 + /// Selection variables; currently displayed chunk.
2000 + /// nb – normalized coordinates of the beginning of the selection
2001 + /// ne – normalized coordinates of the end of the selection
2002 + struct {
2003 + int x, y;
2004 + } nb, ne;
2005 +
2006 + int alt;
2007 +} Selection;
2008 +
2009 +/* Internal representation of the screen */
2010 +typedef struct {
2011 + int row; /* nb row */
2012 + int col; /* nb col */
2013 + Line *line; /* screen */
2014 + Line *alt; /* alternate screen */
2015 + Line hist[HISTSIZE]; /* history buffer */
2016 + int histi; /* history index */
2017 + int scr; /* scroll back */
2018 + int *dirty; /* dirtyness of lines */
2019 + TCursor c; /* cursor */
2020 + int ocx; /* old cursor col */
2021 + int ocy; /* old cursor row */
2022 + int top; /* top scroll limit */
2023 + int bot; /* bottom scroll limit */
2024 + int mode; /* terminal mode flags */
2025 + int esc; /* escape state flags */
2026 + char trantbl[4]; /* charset table translation */
2027 + int charset; /* current charset */
2028 + int icharset; /* selected charset for sequence */
2029 + int *tabs;
2030 + Rune lastc;
2031 +} Term;
2032 +
2033 +extern Term term;
2034 +
2035 +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \
2036 + term.scr + HISTSIZE + 1) % HISTSIZE] : \
2037 + term.line[(y) - term.scr])
2038 +
2039 +extern Selection sel;
2040 +
2041 +
2042 +#endif // TERM_H
2043 diff -ruN st-default/win.h st1/win.h
2044 --- st-default/win.h 2020-06-04 11:15:55.166135902 +0200
2045 +++ st1/win.h 2020-06-04 11:04:30.232111510 +0200
2046 @@ -19,6 +19,7 @@
2047 MODE_MOUSEMANY = 1 << 15,
2048 MODE_BRCKTPASTE = 1 << 16,
2049 MODE_NUMLOCK = 1 << 17,
2050 + MODE_NORMAL = 1 << 18,
2051 MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\
2052 |MODE_MOUSEMANY,
2053 };
2054 @@ -27,6 +28,7 @@
2055 void xclipcopy(void);
2056 void xdrawcursor(int, int, Glyph, int, int, Glyph);
2057 void xdrawline(Line, int, int, int);
2058 +void xdrawglyph(Glyph, int, int);
2059 void xfinishdraw(void);
2060 void xloadcols(void);
2061 int xsetcolorname(int, const char *);
2062 diff -ruN st-default/x.c st1/x.c
2063 --- st-default/x.c 2020-06-04 11:15:55.166135902 +0200
2064 +++ st1/x.c 2020-06-04 11:04:30.232111510 +0200
2065 @@ -143,7 +143,6 @@
2066 static inline ushort sixd_to_16bit(int);
2067 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
2068 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
2069 -static void xdrawglyph(Glyph, int, int);
2070 static void xclear(int, int, int, int);
2071 static int xgeommasktogravity(int);
2072 static int ximopen(Display *);
2073 @@ -356,7 +355,7 @@
2074 break;
2075 }
2076 }
2077 - selextend(evcol(e), evrow(e), seltype, done);
2078 + xselextend(evcol(e), evrow(e), seltype, done);
2079 if (done)
2080 setsel(getsel(), e->xbutton.time);
2081 }
2082 @@ -486,7 +485,7 @@
2083 xsel.tclick2 = xsel.tclick1;
2084 xsel.tclick1 = now;
2085
2086 - selstart(evcol(e), evrow(e), snap);
2087 + xselstart(evcol(e), evrow(e), snap);
2088 }
2089 }
2090
2091 @@ -773,6 +772,13 @@
2092 }
2093
2094 void
2095 +normalMode(Arg const *_) {
2096 + (void) _;
2097 + win.mode ^= MODE_NORMAL;
2098 +}
2099 +
2100 +
2101 +void
2102 xloadcols(void)
2103 {
2104 int i;
2105 @@ -1358,6 +1364,14 @@
2106 base.fg = defaultattr;
2107 }
2108
2109 + if (base.mode & ATTR_HIGHLIGHT) {
2110 + base.bg = highlightBg;
2111 + base.fg = highlightFg;
2112 + } else if ((base.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) {
2113 + base.bg = currentBg;
2114 + base.fg = currentFg;
2115 + }
2116 +
2117 if (IS_TRUECOL(base.fg)) {
2118 colfg.alpha = 0xffff;
2119 colfg.red = TRUERED(base.fg);
2120 @@ -1447,7 +1461,7 @@
2121 xclear(winx, winy + win.ch, winx + width, win.h);
2122
2123 /* Clean up the region we want to draw to. */
2124 - XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
2125 + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch);
2126
2127 /* Set the clip region because Xft is sometimes dirty. */
2128 r.x = 0;
2129 @@ -1490,8 +1504,9 @@
2130 Color drawcol;
2131
2132 /* remove the old cursor */
2133 - if (selected(ox, oy))
2134 - og.mode ^= ATTR_REVERSE;
2135 + if (selected(ox, oy)) og.mode ^= ATTR_REVERSE;
2136 + if (highlighted(ox, oy)) { og.mode ^= ATTR_HIGHLIGHT; }
2137 + if (currentLine(ox, oy)) { og.mode ^= ATTR_CURRENT; }
2138 xdrawglyph(og, ox, oy);
2139
2140 if (IS_SET(MODE_HIDE))
2141 @@ -1523,6 +1538,11 @@
2142 drawcol = dc.col[g.bg];
2143 }
2144
2145 + if ((g.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) {
2146 + g.bg = currentBg;
2147 + g.fg = currentFg;
2148 + }
2149 +
2150 /* draw the new one */
2151 if (IS_SET(MODE_FOCUSED)) {
2152 switch (win.cursor) {
2153 @@ -1607,12 +1627,18 @@
2154
2155 numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y1);
2156 i = ox = 0;
2157 - for (x = x1; x < x2 && i < numspecs; x++) {
2158 + for (x = x1; x < x2 && i < numspecs; ++x) {
2159 new = line[x];
2160 if (new.mode == ATTR_WDUMMY)
2161 continue;
2162 if (selected(x, y1))
2163 new.mode ^= ATTR_REVERSE;
2164 + if (highlighted(x, y1)) {
2165 + new.mode ^= ATTR_HIGHLIGHT;
2166 + }
2167 + if (currentLine(x, y1)) {
2168 + new.mode ^= ATTR_CURRENT;
2169 + }
2170 if (i > 0 && ATTRCMP(base, new)) {
2171 xdrawglyphfontspecs(specs, base, i, ox, y1);
2172 specs += i;
2173 @@ -1800,6 +1826,14 @@
2174 len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
2175 else
2176 len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
2177 +
2178 + if (IS_SET(MODE_NORMAL)) {
2179 + ExitState const es = kpressNormalMode(buf, len, // strlen(buf),
2180 + match(ControlMask, e->state),
2181 + &ksym);
2182 + if (es == finished) { normalMode(NULL); }
2183 + return;
2184 + }
2185 /* 1. shortcuts */
2186 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
2187 if (ksym == bp->keysym && match(bp->mod, e->state)) {
2188 Binary files st-default/x.o and st1/x.o differ