| st-vimBrowse-20200212-26cdfeb.diff - sites - public wiki contents of suckless.o… | |
| git clone git://git.suckless.org/sites | |
| Log | |
| Files | |
| Refs | |
| --- | |
| st-vimBrowse-20200212-26cdfeb.diff (85174B) | |
| --- | |
| 1 From 22471790fcaf1505890bcb53752a6df87cfd3592 Mon Sep 17 00:00:00 2001 | |
| 2 From: Julius Huelsmann <[email protected]> | |
| 3 Date: Wed, 12 Feb 2020 19:50:06 +0100 | |
| 4 Subject: [PATCH] port: vim patch 2020-02 to current HEAD of st | |
| 5 | |
| 6 --- | |
| 7 Makefile | 9 +- | |
| 8 config.def.h | 54 ++++ | |
| 9 config.h | 513 +++++++++++++++++++++++++++++++++ | |
| 10 dynamicArray.h | 175 ++++++++++++ | |
| 11 error.h | 47 ++++ | |
| 12 glyph.h | 30 ++ | |
| 13 normalMode.c | 750 +++++++++++++++++++++++++++++++++++++++++++++++++ | |
| 14 normalMode.h | 36 +++ | |
| 15 st.c | 249 +++++++++------- | |
| 16 st.h | 36 ++- | |
| 17 term.h | 73 +++++ | |
| 18 win.h | 2 + | |
| 19 x.c | 48 +++- | |
| 20 13 files changed, 1894 insertions(+), 128 deletions(-) | |
| 21 create mode 100644 config.h | |
| 22 create mode 100644 dynamicArray.h | |
| 23 create mode 100644 error.h | |
| 24 create mode 100644 glyph.h | |
| 25 create mode 100644 normalMode.c | |
| 26 create mode 100644 normalMode.h | |
| 27 create mode 100644 term.h | |
| 28 | |
| 29 diff --git a/Makefile b/Makefile | |
| 30 index 470ac86..af8f0be 100644 | |
| 31 --- a/Makefile | |
| 32 +++ b/Makefile | |
| 33 @@ -4,7 +4,7 @@ | |
| 34 | |
| 35 include config.mk | |
| 36 | |
| 37 -SRC = st.c x.c | |
| 38 +SRC = st.c x.c normalMode.c | |
| 39 OBJ = $(SRC:.c=.o) | |
| 40 | |
| 41 all: options st | |
| 42 @@ -21,8 +21,8 @@ config.h: | |
| 43 .c.o: | |
| 44 $(CC) $(STCFLAGS) -c $< | |
| 45 | |
| 46 -st.o: config.h st.h win.h | |
| 47 -x.o: arg.h config.h st.h win.h | |
| 48 +st.o: config.h st.h win.h dynamicArray.h normalMode.h term.h glyph.h er… | |
| 49 +x.o: arg.h config.h st.h win.h dynamicArray.h normalMode.h term.h glyph… | |
| 50 | |
| 51 $(OBJ): config.h config.mk | |
| 52 | |
| 53 @@ -35,7 +35,8 @@ clean: | |
| 54 dist: clean | |
| 55 mkdir -p st-$(VERSION) | |
| 56 cp -R FAQ LEGACY TODO LICENSE Makefile README config.mk\ | |
| 57 - config.def.h st.info st.1 arg.h st.h win.h $(SRC)\ | |
| 58 + config.def.h st.info st.1 arg.h st.h win.h dynamicArray… | |
| 59 + normalMode.h term.h error.h $(SRC)\ | |
| 60 st-$(VERSION) | |
| 61 tar -cf - st-$(VERSION) | gzip > st-$(VERSION).tar.gz | |
| 62 rm -rf st-$(VERSION) | |
| 63 diff --git a/config.def.h b/config.def.h | |
| 64 index 546edda..92b541d 100644 | |
| 65 --- a/config.def.h | |
| 66 +++ b/config.def.h | |
| 67 @@ -149,6 +149,14 @@ static unsigned int mousebg = 0; | |
| 68 * doesn't match the ones requested. | |
| 69 */ | |
| 70 static unsigned int defaultattr = 11; | |
| 71 +/// Colors for the entities that are 'highlighted' in normal mode (sear… | |
| 72 +/// results currently on screen) [Vim Browse]. | |
| 73 +static unsigned int highlightBg = 160; | |
| 74 +static unsigned int highlightFg = 15; | |
| 75 +/// Colors for highlighting the current cursor position (row + col) in … | |
| 76 +/// mode [Vim Browse]. | |
| 77 +static unsigned int currentBg = 8; | |
| 78 +static unsigned int currentFg = 15; | |
| 79 | |
| 80 /* | |
| 81 * Force mouse select/shortcuts while mask is active (when MODE_MOUSE i… | |
| 82 @@ -170,10 +178,12 @@ static MouseShortcut mshortcuts[] = { | |
| 83 | |
| 84 /* Internal keyboard shortcuts. */ | |
| 85 #define MODKEY Mod1Mask | |
| 86 +#define AltMask Mod1Mask | |
| 87 #define TERMMOD (ControlMask|ShiftMask) | |
| 88 | |
| 89 static Shortcut shortcuts[] = { | |
| 90 /* mask keysym function argumen… | |
| 91 + { AltMask, XK_c, normalMode, {.i = … | |
| 92 { XK_ANY_MOD, XK_Break, sendbreak, {.i = … | |
| 93 { ControlMask, XK_Print, toggleprinter, {.i = … | |
| 94 { ShiftMask, XK_Print, printscreen, {.i = … | |
| 95 @@ -186,6 +196,8 @@ static Shortcut shortcuts[] = { | |
| 96 { TERMMOD, XK_Y, selpaste, {.i = … | |
| 97 { ShiftMask, XK_Insert, selpaste, {.i = … | |
| 98 { TERMMOD, XK_Num_Lock, numlock, {.i = … | |
| 99 + { ShiftMask, XK_Page_Up, kscrollup, {.i = -… | |
| 100 + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -… | |
| 101 }; | |
| 102 | |
| 103 /* | |
| 104 @@ -457,3 +469,45 @@ static char ascii_printable[] = | |
| 105 " !\"#$%&'()*+,-./0123456789:;<=>?" | |
| 106 "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" | |
| 107 "`abcdefghijklmnopqrstuvwxyz{|}~"; | |
| 108 + | |
| 109 + | |
| 110 +/// word sepearors normal mode | |
| 111 +/// [Vim Browse]. | |
| 112 +char wordDelimSmall[] = " \t!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; | |
| 113 +char wordDelimLarge[] = " \t"; /// <Word sepearors normal mode (capital… | |
| 114 + | |
| 115 +/// Shortcusts executed in normal mode (which should not already be in … | |
| 116 +/// [Vim Browse]. | |
| 117 +struct NormalModeShortcuts normalModeShortcuts [] = { | |
| 118 + { 'R', "?Building\n" }, | |
| 119 + { 'r', "/Building\n" }, | |
| 120 + { 'F', "?: error:\n" }, | |
| 121 + { 'f', "/: error:\n" }, | |
| 122 + { 'Q', "?[Leaving vim, starting execution]\n" }, | |
| 123 + { 'S', "Qf" }, | |
| 124 + { 'X', "?juli@machine\n" }, | |
| 125 + { 'x', "/juli@machine\n" }, | |
| 126 +}; | |
| 127 + | |
| 128 +size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / … | |
| 129 + | |
| 130 +/// Style of the command string visualized in normal mode in the right … | |
| 131 +/// [Vim Browse]. | |
| 132 +Glyph const styleCommand = {' ', ATTR_ITALIC | ATTR_FAINT, 7, 16}; | |
| 133 +/// Style of the search string visualized in normal mode in the right c… | |
| 134 +/// [Vim Browse]. | |
| 135 +Glyph const styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16}; | |
| 136 + | |
| 137 +/// Colors used in normal mode in order to highlight different operatio… | |
| 138 +/// empathise the current position on screen in the status area [Vim … | |
| 139 +unsigned int bgCommandYank = 11; | |
| 140 +unsigned int bgCommandVisual = 4; | |
| 141 +unsigned int bgCommandVisualLine = 12; | |
| 142 + | |
| 143 +unsigned int fgCommandYank = 232; | |
| 144 +unsigned int fgCommandVisual = 232; | |
| 145 +unsigned int fgCommandVisualLine = 232; | |
| 146 + | |
| 147 +unsigned int bgPos = 15; | |
| 148 +unsigned int fgPos = 16; | |
| 149 + | |
| 150 diff --git a/config.h b/config.h | |
| 151 new file mode 100644 | |
| 152 index 0000000..92b541d | |
| 153 --- /dev/null | |
| 154 +++ b/config.h | |
| 155 @@ -0,0 +1,513 @@ | |
| 156 +/* See LICENSE file for copyright and license details. */ | |
| 157 + | |
| 158 +/* | |
| 159 + * appearance | |
| 160 + * | |
| 161 + * font: see http://freedesktop.org/software/fontconfig/fontconfig-user… | |
| 162 + */ | |
| 163 +static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohi… | |
| 164 +static int borderpx = 2; | |
| 165 + | |
| 166 +/* | |
| 167 + * What program is execed by st depends of these precedence rules: | |
| 168 + * 1: program passed with -e | |
| 169 + * 2: utmp option | |
| 170 + * 3: SHELL environment variable | |
| 171 + * 4: value of shell in /etc/passwd | |
| 172 + * 5: value of shell in config.h | |
| 173 + */ | |
| 174 +static char *shell = "/bin/sh"; | |
| 175 +char *utmp = NULL; | |
| 176 +char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; | |
| 177 + | |
| 178 +/* identification sequence returned in DA and DECID */ | |
| 179 +char *vtiden = "\033[?6c"; | |
| 180 + | |
| 181 +/* Kerning / character bounding-box multipliers */ | |
| 182 +static float cwscale = 1.0; | |
| 183 +static float chscale = 1.0; | |
| 184 + | |
| 185 +/* | |
| 186 + * word delimiter string | |
| 187 + * | |
| 188 + * More advanced example: L" `'\"()[]{}" | |
| 189 + */ | |
| 190 +wchar_t *worddelimiters = L" "; | |
| 191 + | |
| 192 +/* selection timeouts (in milliseconds) */ | |
| 193 +static unsigned int doubleclicktimeout = 300; | |
| 194 +static unsigned int tripleclicktimeout = 600; | |
| 195 + | |
| 196 +/* alt screens */ | |
| 197 +int allowaltscreen = 1; | |
| 198 + | |
| 199 +/* frames per second st should at maximum draw to the screen */ | |
| 200 +static unsigned int xfps = 120; | |
| 201 +static unsigned int actionfps = 30; | |
| 202 + | |
| 203 +/* | |
| 204 + * blinking timeout (set to 0 to disable blinking) for the terminal bli… | |
| 205 + * attribute. | |
| 206 + */ | |
| 207 +static unsigned int blinktimeout = 800; | |
| 208 + | |
| 209 +/* | |
| 210 + * thickness of underline and bar cursors | |
| 211 + */ | |
| 212 +static unsigned int cursorthickness = 2; | |
| 213 + | |
| 214 +/* | |
| 215 + * bell volume. It must be a value between -100 and 100. Use 0 for disa… | |
| 216 + * it | |
| 217 + */ | |
| 218 +static int bellvolume = 0; | |
| 219 + | |
| 220 +/* default TERM value */ | |
| 221 +char *termname = "st-256color"; | |
| 222 + | |
| 223 +/* | |
| 224 + * spaces per tab | |
| 225 + * | |
| 226 + * When you are changing this value, don't forget to adapt the »it« v… | |
| 227 + * the st.info and appropriately install the st.info in the environment… | |
| 228 + * you use this st version. | |
| 229 + * | |
| 230 + * it#$tabspaces, | |
| 231 + * | |
| 232 + * Secondly make sure your kernel is not expanding tabs. When running `… | |
| 233 + * -a` »tab0« should appear. You can tell the terminal to not expand … | |
| 234 + * running following command: | |
| 235 + * | |
| 236 + * stty tabs | |
| 237 + */ | |
| 238 +unsigned int tabspaces = 8; | |
| 239 + | |
| 240 +/* Terminal colors (16 first used in escape sequence) */ | |
| 241 +static const char *colorname[] = { | |
| 242 + /* 8 normal colors */ | |
| 243 + "black", | |
| 244 + "red3", | |
| 245 + "green3", | |
| 246 + "yellow3", | |
| 247 + "blue2", | |
| 248 + "magenta3", | |
| 249 + "cyan3", | |
| 250 + "gray90", | |
| 251 + | |
| 252 + /* 8 bright colors */ | |
| 253 + "gray50", | |
| 254 + "red", | |
| 255 + "green", | |
| 256 + "yellow", | |
| 257 + "#5c5cff", | |
| 258 + "magenta", | |
| 259 + "cyan", | |
| 260 + "white", | |
| 261 + | |
| 262 + [255] = 0, | |
| 263 + | |
| 264 + /* more colors can be added after 255 to use with DefaultXX */ | |
| 265 + "#cccccc", | |
| 266 + "#555555", | |
| 267 +}; | |
| 268 + | |
| 269 + | |
| 270 +/* | |
| 271 + * Default colors (colorname index) | |
| 272 + * foreground, background, cursor, reverse cursor | |
| 273 + */ | |
| 274 +unsigned int defaultfg = 7; | |
| 275 +unsigned int defaultbg = 0; | |
| 276 +static unsigned int defaultcs = 256; | |
| 277 +static unsigned int defaultrcs = 257; | |
| 278 + | |
| 279 +/* | |
| 280 + * Default shape of cursor | |
| 281 + * 2: Block ("█") | |
| 282 + * 4: Underline ("_") | |
| 283 + * 6: Bar ("|") | |
| 284 + * 7: Snowman ("☃") | |
| 285 + */ | |
| 286 +static unsigned int cursorshape = 2; | |
| 287 + | |
| 288 +/* | |
| 289 + * Default columns and rows numbers | |
| 290 + */ | |
| 291 + | |
| 292 +static unsigned int cols = 80; | |
| 293 +static unsigned int rows = 24; | |
| 294 + | |
| 295 +/* | |
| 296 + * Default colour and shape of the mouse cursor | |
| 297 + */ | |
| 298 +static unsigned int mouseshape = XC_xterm; | |
| 299 +static unsigned int mousefg = 7; | |
| 300 +static unsigned int mousebg = 0; | |
| 301 + | |
| 302 +/* | |
| 303 + * Color used to display font attributes when fontconfig selected a fon… | |
| 304 + * doesn't match the ones requested. | |
| 305 + */ | |
| 306 +static unsigned int defaultattr = 11; | |
| 307 +/// Colors for the entities that are 'highlighted' in normal mode (sear… | |
| 308 +/// results currently on screen) [Vim Browse]. | |
| 309 +static unsigned int highlightBg = 160; | |
| 310 +static unsigned int highlightFg = 15; | |
| 311 +/// Colors for highlighting the current cursor position (row + col) in … | |
| 312 +/// mode [Vim Browse]. | |
| 313 +static unsigned int currentBg = 8; | |
| 314 +static unsigned int currentFg = 15; | |
| 315 + | |
| 316 +/* | |
| 317 + * Force mouse select/shortcuts while mask is active (when MODE_MOUSE i… | |
| 318 + * Note that if you want to use ShiftMask with selmasks, set this to an… | |
| 319 + * modifier, set to 0 to not use it. | |
| 320 + */ | |
| 321 +static uint forcemousemod = ShiftMask; | |
| 322 + | |
| 323 +/* | |
| 324 + * Internal mouse shortcuts. | |
| 325 + * Beware that overloading Button1 will disable the selection. | |
| 326 + */ | |
| 327 +static MouseShortcut mshortcuts[] = { | |
| 328 + /* mask button function argument … | |
| 329 + { XK_ANY_MOD, Button2, selpaste, {.i = 0}, … | |
| 330 + { XK_ANY_MOD, Button4, ttysend, {.s = "\031"} … | |
| 331 + { XK_ANY_MOD, Button5, ttysend, {.s = "\005"} … | |
| 332 +}; | |
| 333 + | |
| 334 +/* Internal keyboard shortcuts. */ | |
| 335 +#define MODKEY Mod1Mask | |
| 336 +#define AltMask Mod1Mask | |
| 337 +#define TERMMOD (ControlMask|ShiftMask) | |
| 338 + | |
| 339 +static Shortcut shortcuts[] = { | |
| 340 + /* mask keysym function argumen… | |
| 341 + { AltMask, XK_c, normalMode, {.i = … | |
| 342 + { XK_ANY_MOD, XK_Break, sendbreak, {.i = … | |
| 343 + { ControlMask, XK_Print, toggleprinter, {.i = … | |
| 344 + { ShiftMask, XK_Print, printscreen, {.i = … | |
| 345 + { XK_ANY_MOD, XK_Print, printsel, {.i = … | |
| 346 + { TERMMOD, XK_Prior, zoom, {.f = +… | |
| 347 + { TERMMOD, XK_Next, zoom, {.f = -… | |
| 348 + { TERMMOD, XK_Home, zoomreset, {.f = … | |
| 349 + { TERMMOD, XK_C, clipcopy, {.i = … | |
| 350 + { TERMMOD, XK_V, clippaste, {.i = … | |
| 351 + { TERMMOD, XK_Y, selpaste, {.i = … | |
| 352 + { ShiftMask, XK_Insert, selpaste, {.i = … | |
| 353 + { TERMMOD, XK_Num_Lock, numlock, {.i = … | |
| 354 + { ShiftMask, XK_Page_Up, kscrollup, {.i = -… | |
| 355 + { ShiftMask, XK_Page_Down, kscrolldown, {.i = -… | |
| 356 +}; | |
| 357 + | |
| 358 +/* | |
| 359 + * Special keys (change & recompile st.info accordingly) | |
| 360 + * | |
| 361 + * Mask value: | |
| 362 + * * Use XK_ANY_MOD to match the key no matter modifiers state | |
| 363 + * * Use XK_NO_MOD to match the key alone (no modifiers) | |
| 364 + * appkey value: | |
| 365 + * * 0: no value | |
| 366 + * * > 0: keypad application mode enabled | |
| 367 + * * = 2: term.numlock = 1 | |
| 368 + * * < 0: keypad application mode disabled | |
| 369 + * appcursor value: | |
| 370 + * * 0: no value | |
| 371 + * * > 0: cursor application mode enabled | |
| 372 + * * < 0: cursor application mode disabled | |
| 373 + * | |
| 374 + * Be careful with the order of the definitions because st searches in | |
| 375 + * this table sequentially, so any XK_ANY_MOD must be in the last | |
| 376 + * position for a key. | |
| 377 + */ | |
| 378 + | |
| 379 +/* | |
| 380 + * If you want keys other than the X11 function keys (0xFD00 - 0xFFFF) | |
| 381 + * to be mapped below, add them to this array. | |
| 382 + */ | |
| 383 +static KeySym mappedkeys[] = { -1 }; | |
| 384 + | |
| 385 +/* | |
| 386 + * State bits to ignore when matching key or button events. By default, | |
| 387 + * numlock (Mod2Mask) and keyboard layout (XK_SWITCH_MOD) are ignored. | |
| 388 + */ | |
| 389 +static uint ignoremod = Mod2Mask|XK_SWITCH_MOD; | |
| 390 + | |
| 391 +/* | |
| 392 + * This is the huge key array which defines all compatibility to the Li… | |
| 393 + * world. Please decide about changes wisely. | |
| 394 + */ | |
| 395 +static Key key[] = { | |
| 396 + /* keysym mask string appkey appcurso… | |
| 397 + { XK_KP_Home, ShiftMask, "\033[2J", 0, -1}, | |
| 398 + { XK_KP_Home, ShiftMask, "\033[1;2H", 0, +1}, | |
| 399 + { XK_KP_Home, XK_ANY_MOD, "\033[H", 0, -1}, | |
| 400 + { XK_KP_Home, XK_ANY_MOD, "\033[1~", 0, +1}, | |
| 401 + { XK_KP_Up, XK_ANY_MOD, "\033Ox", +1, 0}, | |
| 402 + { XK_KP_Up, XK_ANY_MOD, "\033[A", 0, -1}, | |
| 403 + { XK_KP_Up, XK_ANY_MOD, "\033OA", 0, +1}, | |
| 404 + { XK_KP_Down, XK_ANY_MOD, "\033Or", +1, 0}, | |
| 405 + { XK_KP_Down, XK_ANY_MOD, "\033[B", 0, -1}, | |
| 406 + { XK_KP_Down, XK_ANY_MOD, "\033OB", 0, +1}, | |
| 407 + { XK_KP_Left, XK_ANY_MOD, "\033Ot", +1, 0}, | |
| 408 + { XK_KP_Left, XK_ANY_MOD, "\033[D", 0, -1}, | |
| 409 + { XK_KP_Left, XK_ANY_MOD, "\033OD", 0, +1}, | |
| 410 + { XK_KP_Right, XK_ANY_MOD, "\033Ov", +1, 0}, | |
| 411 + { XK_KP_Right, XK_ANY_MOD, "\033[C", 0, -1}, | |
| 412 + { XK_KP_Right, XK_ANY_MOD, "\033OC", 0, +1}, | |
| 413 + { XK_KP_Prior, ShiftMask, "\033[5;2~", 0, 0}, | |
| 414 + { XK_KP_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, | |
| 415 + { XK_KP_Begin, XK_ANY_MOD, "\033[E", 0, 0}, | |
| 416 + { XK_KP_End, ControlMask, "\033[J", -1, 0}, | |
| 417 + { XK_KP_End, ControlMask, "\033[1;5F", +1, 0}, | |
| 418 + { XK_KP_End, ShiftMask, "\033[K", -1, 0}, | |
| 419 + { XK_KP_End, ShiftMask, "\033[1;2F", +1, 0}, | |
| 420 + { XK_KP_End, XK_ANY_MOD, "\033[4~", 0, 0}, | |
| 421 + { XK_KP_Next, ShiftMask, "\033[6;2~", 0, 0}, | |
| 422 + { XK_KP_Next, XK_ANY_MOD, "\033[6~", 0, 0}, | |
| 423 + { XK_KP_Insert, ShiftMask, "\033[2;2~", +1, 0}, | |
| 424 + { XK_KP_Insert, ShiftMask, "\033[4l", -1, 0}, | |
| 425 + { XK_KP_Insert, ControlMask, "\033[L", -1, 0}, | |
| 426 + { XK_KP_Insert, ControlMask, "\033[2;5~", +1, 0}, | |
| 427 + { XK_KP_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, | |
| 428 + { XK_KP_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, | |
| 429 + { XK_KP_Delete, ControlMask, "\033[M", -1, 0}, | |
| 430 + { XK_KP_Delete, ControlMask, "\033[3;5~", +1, 0}, | |
| 431 + { XK_KP_Delete, ShiftMask, "\033[2K", -1, 0}, | |
| 432 + { XK_KP_Delete, ShiftMask, "\033[3;2~", +1, 0}, | |
| 433 + { XK_KP_Delete, XK_ANY_MOD, "\033[P", -1, 0}, | |
| 434 + { XK_KP_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, | |
| 435 + { XK_KP_Multiply, XK_ANY_MOD, "\033Oj", +2, 0}, | |
| 436 + { XK_KP_Add, XK_ANY_MOD, "\033Ok", +2, 0}, | |
| 437 + { XK_KP_Enter, XK_ANY_MOD, "\033OM", +2, 0}, | |
| 438 + { XK_KP_Enter, XK_ANY_MOD, "\r", -1, 0}, | |
| 439 + { XK_KP_Subtract, XK_ANY_MOD, "\033Om", +2, 0}, | |
| 440 + { XK_KP_Decimal, XK_ANY_MOD, "\033On", +2, 0}, | |
| 441 + { XK_KP_Divide, XK_ANY_MOD, "\033Oo", +2, 0}, | |
| 442 + { XK_KP_0, XK_ANY_MOD, "\033Op", +2, 0}, | |
| 443 + { XK_KP_1, XK_ANY_MOD, "\033Oq", +2, 0}, | |
| 444 + { XK_KP_2, XK_ANY_MOD, "\033Or", +2, 0}, | |
| 445 + { XK_KP_3, XK_ANY_MOD, "\033Os", +2, 0}, | |
| 446 + { XK_KP_4, XK_ANY_MOD, "\033Ot", +2, 0}, | |
| 447 + { XK_KP_5, XK_ANY_MOD, "\033Ou", +2, 0}, | |
| 448 + { XK_KP_6, XK_ANY_MOD, "\033Ov", +2, 0}, | |
| 449 + { XK_KP_7, XK_ANY_MOD, "\033Ow", +2, 0}, | |
| 450 + { XK_KP_8, XK_ANY_MOD, "\033Ox", +2, 0}, | |
| 451 + { XK_KP_9, XK_ANY_MOD, "\033Oy", +2, 0}, | |
| 452 + { XK_Up, ShiftMask, "\033[1;2A", 0, 0}, | |
| 453 + { XK_Up, Mod1Mask, "\033[1;3A", 0, 0}, | |
| 454 + { XK_Up, ShiftMask|Mod1Mask,"\033[1;4A", 0, 0}, | |
| 455 + { XK_Up, ControlMask, "\033[1;5A", 0, 0}, | |
| 456 + { XK_Up, ShiftMask|ControlMask,"\033[1;6A", 0, 0}, | |
| 457 + { XK_Up, ControlMask|Mod1Mask,"\033[1;7A", 0, 0}, | |
| 458 + { XK_Up,ShiftMask|ControlMask|Mod1Mask,"\033[1;8A", 0, 0}, | |
| 459 + { XK_Up, XK_ANY_MOD, "\033[A", 0, -1}, | |
| 460 + { XK_Up, XK_ANY_MOD, "\033OA", 0, +1}, | |
| 461 + { XK_Down, ShiftMask, "\033[1;2B", 0, 0}, | |
| 462 + { XK_Down, Mod1Mask, "\033[1;3B", 0, 0}, | |
| 463 + { XK_Down, ShiftMask|Mod1Mask,"\033[1;4B", 0, 0}, | |
| 464 + { XK_Down, ControlMask, "\033[1;5B", 0, 0}, | |
| 465 + { XK_Down, ShiftMask|ControlMask,"\033[1;6B", 0, 0}, | |
| 466 + { XK_Down, ControlMask|Mod1Mask,"\033[1;7B", 0, 0}, | |
| 467 + { XK_Down,ShiftMask|ControlMask|Mod1Mask,"\033[1;8B",0, 0}, | |
| 468 + { XK_Down, XK_ANY_MOD, "\033[B", 0, -1}, | |
| 469 + { XK_Down, XK_ANY_MOD, "\033OB", 0, +1}, | |
| 470 + { XK_Left, ShiftMask, "\033[1;2D", 0, 0}, | |
| 471 + { XK_Left, Mod1Mask, "\033[1;3D", 0, 0}, | |
| 472 + { XK_Left, ShiftMask|Mod1Mask,"\033[1;4D", 0, 0}, | |
| 473 + { XK_Left, ControlMask, "\033[1;5D", 0, 0}, | |
| 474 + { XK_Left, ShiftMask|ControlMask,"\033[1;6D", 0, 0}, | |
| 475 + { XK_Left, ControlMask|Mod1Mask,"\033[1;7D", 0, 0}, | |
| 476 + { XK_Left,ShiftMask|ControlMask|Mod1Mask,"\033[1;8D",0, 0}, | |
| 477 + { XK_Left, XK_ANY_MOD, "\033[D", 0, -1}, | |
| 478 + { XK_Left, XK_ANY_MOD, "\033OD", 0, +1}, | |
| 479 + { XK_Right, ShiftMask, "\033[1;2C", 0, 0}, | |
| 480 + { XK_Right, Mod1Mask, "\033[1;3C", 0, 0}, | |
| 481 + { XK_Right, ShiftMask|Mod1Mask,"\033[1;4C", 0, 0}, | |
| 482 + { XK_Right, ControlMask, "\033[1;5C", 0, 0}, | |
| 483 + { XK_Right, ShiftMask|ControlMask,"\033[1;6C", 0, 0}, | |
| 484 + { XK_Right, ControlMask|Mod1Mask,"\033[1;7C", 0, 0}, | |
| 485 + { XK_Right,ShiftMask|ControlMask|Mod1Mask,"\033[1;8C",0, 0}, | |
| 486 + { XK_Right, XK_ANY_MOD, "\033[C", 0, -1}, | |
| 487 + { XK_Right, XK_ANY_MOD, "\033OC", 0, +1}, | |
| 488 + { XK_ISO_Left_Tab, ShiftMask, "\033[Z", 0, 0}, | |
| 489 + { XK_Return, Mod1Mask, "\033\r", 0, 0}, | |
| 490 + { XK_Return, XK_ANY_MOD, "\r", 0, 0}, | |
| 491 + { XK_Insert, ShiftMask, "\033[4l", -1, 0}, | |
| 492 + { XK_Insert, ShiftMask, "\033[2;2~", +1, 0}, | |
| 493 + { XK_Insert, ControlMask, "\033[L", -1, 0}, | |
| 494 + { XK_Insert, ControlMask, "\033[2;5~", +1, 0}, | |
| 495 + { XK_Insert, XK_ANY_MOD, "\033[4h", -1, 0}, | |
| 496 + { XK_Insert, XK_ANY_MOD, "\033[2~", +1, 0}, | |
| 497 + { XK_Delete, ControlMask, "\033[M", -1, 0}, | |
| 498 + { XK_Delete, ControlMask, "\033[3;5~", +1, 0}, | |
| 499 + { XK_Delete, ShiftMask, "\033[2K", -1, 0}, | |
| 500 + { XK_Delete, ShiftMask, "\033[3;2~", +1, 0}, | |
| 501 + { XK_Delete, XK_ANY_MOD, "\033[P", -1, 0}, | |
| 502 + { XK_Delete, XK_ANY_MOD, "\033[3~", +1, 0}, | |
| 503 + { XK_BackSpace, XK_NO_MOD, "\177", 0, 0}, | |
| 504 + { XK_BackSpace, Mod1Mask, "\033\177", 0, 0}, | |
| 505 + { XK_Home, ShiftMask, "\033[2J", 0, -1}, | |
| 506 + { XK_Home, ShiftMask, "\033[1;2H", 0, +1}, | |
| 507 + { XK_Home, XK_ANY_MOD, "\033[H", 0, -1}, | |
| 508 + { XK_Home, XK_ANY_MOD, "\033[1~", 0, +1}, | |
| 509 + { XK_End, ControlMask, "\033[J", -1, 0}, | |
| 510 + { XK_End, ControlMask, "\033[1;5F", +1, 0}, | |
| 511 + { XK_End, ShiftMask, "\033[K", -1, 0}, | |
| 512 + { XK_End, ShiftMask, "\033[1;2F", +1, 0}, | |
| 513 + { XK_End, XK_ANY_MOD, "\033[4~", 0, 0}, | |
| 514 + { XK_Prior, ControlMask, "\033[5;5~", 0, 0}, | |
| 515 + { XK_Prior, ShiftMask, "\033[5;2~", 0, 0}, | |
| 516 + { XK_Prior, XK_ANY_MOD, "\033[5~", 0, 0}, | |
| 517 + { XK_Next, ControlMask, "\033[6;5~", 0, 0}, | |
| 518 + { XK_Next, ShiftMask, "\033[6;2~", 0, 0}, | |
| 519 + { XK_Next, XK_ANY_MOD, "\033[6~", 0, 0}, | |
| 520 + { XK_F1, XK_NO_MOD, "\033OP" , 0, 0}, | |
| 521 + { XK_F1, /* F13 */ ShiftMask, "\033[1;2P", 0, 0}, | |
| 522 + { XK_F1, /* F25 */ ControlMask, "\033[1;5P", 0, 0}, | |
| 523 + { XK_F1, /* F37 */ Mod4Mask, "\033[1;6P", 0, 0}, | |
| 524 + { XK_F1, /* F49 */ Mod1Mask, "\033[1;3P", 0, 0}, | |
| 525 + { XK_F1, /* F61 */ Mod3Mask, "\033[1;4P", 0, 0}, | |
| 526 + { XK_F2, XK_NO_MOD, "\033OQ" , 0, 0}, | |
| 527 + { XK_F2, /* F14 */ ShiftMask, "\033[1;2Q", 0, 0}, | |
| 528 + { XK_F2, /* F26 */ ControlMask, "\033[1;5Q", 0, 0}, | |
| 529 + { XK_F2, /* F38 */ Mod4Mask, "\033[1;6Q", 0, 0}, | |
| 530 + { XK_F2, /* F50 */ Mod1Mask, "\033[1;3Q", 0, 0}, | |
| 531 + { XK_F2, /* F62 */ Mod3Mask, "\033[1;4Q", 0, 0}, | |
| 532 + { XK_F3, XK_NO_MOD, "\033OR" , 0, 0}, | |
| 533 + { XK_F3, /* F15 */ ShiftMask, "\033[1;2R", 0, 0}, | |
| 534 + { XK_F3, /* F27 */ ControlMask, "\033[1;5R", 0, 0}, | |
| 535 + { XK_F3, /* F39 */ Mod4Mask, "\033[1;6R", 0, 0}, | |
| 536 + { XK_F3, /* F51 */ Mod1Mask, "\033[1;3R", 0, 0}, | |
| 537 + { XK_F3, /* F63 */ Mod3Mask, "\033[1;4R", 0, 0}, | |
| 538 + { XK_F4, XK_NO_MOD, "\033OS" , 0, 0}, | |
| 539 + { XK_F4, /* F16 */ ShiftMask, "\033[1;2S", 0, 0}, | |
| 540 + { XK_F4, /* F28 */ ControlMask, "\033[1;5S", 0, 0}, | |
| 541 + { XK_F4, /* F40 */ Mod4Mask, "\033[1;6S", 0, 0}, | |
| 542 + { XK_F4, /* F52 */ Mod1Mask, "\033[1;3S", 0, 0}, | |
| 543 + { XK_F5, XK_NO_MOD, "\033[15~", 0, 0}, | |
| 544 + { XK_F5, /* F17 */ ShiftMask, "\033[15;2~", 0, 0}, | |
| 545 + { XK_F5, /* F29 */ ControlMask, "\033[15;5~", 0, 0}, | |
| 546 + { XK_F5, /* F41 */ Mod4Mask, "\033[15;6~", 0, 0}, | |
| 547 + { XK_F5, /* F53 */ Mod1Mask, "\033[15;3~", 0, 0}, | |
| 548 + { XK_F6, XK_NO_MOD, "\033[17~", 0, 0}, | |
| 549 + { XK_F6, /* F18 */ ShiftMask, "\033[17;2~", 0, 0}, | |
| 550 + { XK_F6, /* F30 */ ControlMask, "\033[17;5~", 0, 0}, | |
| 551 + { XK_F6, /* F42 */ Mod4Mask, "\033[17;6~", 0, 0}, | |
| 552 + { XK_F6, /* F54 */ Mod1Mask, "\033[17;3~", 0, 0}, | |
| 553 + { XK_F7, XK_NO_MOD, "\033[18~", 0, 0}, | |
| 554 + { XK_F7, /* F19 */ ShiftMask, "\033[18;2~", 0, 0}, | |
| 555 + { XK_F7, /* F31 */ ControlMask, "\033[18;5~", 0, 0}, | |
| 556 + { XK_F7, /* F43 */ Mod4Mask, "\033[18;6~", 0, 0}, | |
| 557 + { XK_F7, /* F55 */ Mod1Mask, "\033[18;3~", 0, 0}, | |
| 558 + { XK_F8, XK_NO_MOD, "\033[19~", 0, 0}, | |
| 559 + { XK_F8, /* F20 */ ShiftMask, "\033[19;2~", 0, 0}, | |
| 560 + { XK_F8, /* F32 */ ControlMask, "\033[19;5~", 0, 0}, | |
| 561 + { XK_F8, /* F44 */ Mod4Mask, "\033[19;6~", 0, 0}, | |
| 562 + { XK_F8, /* F56 */ Mod1Mask, "\033[19;3~", 0, 0}, | |
| 563 + { XK_F9, XK_NO_MOD, "\033[20~", 0, 0}, | |
| 564 + { XK_F9, /* F21 */ ShiftMask, "\033[20;2~", 0, 0}, | |
| 565 + { XK_F9, /* F33 */ ControlMask, "\033[20;5~", 0, 0}, | |
| 566 + { XK_F9, /* F45 */ Mod4Mask, "\033[20;6~", 0, 0}, | |
| 567 + { XK_F9, /* F57 */ Mod1Mask, "\033[20;3~", 0, 0}, | |
| 568 + { XK_F10, XK_NO_MOD, "\033[21~", 0, 0}, | |
| 569 + { XK_F10, /* F22 */ ShiftMask, "\033[21;2~", 0, 0}, | |
| 570 + { XK_F10, /* F34 */ ControlMask, "\033[21;5~", 0, 0}, | |
| 571 + { XK_F10, /* F46 */ Mod4Mask, "\033[21;6~", 0, 0}, | |
| 572 + { XK_F10, /* F58 */ Mod1Mask, "\033[21;3~", 0, 0}, | |
| 573 + { XK_F11, XK_NO_MOD, "\033[23~", 0, 0}, | |
| 574 + { XK_F11, /* F23 */ ShiftMask, "\033[23;2~", 0, 0}, | |
| 575 + { XK_F11, /* F35 */ ControlMask, "\033[23;5~", 0, 0}, | |
| 576 + { XK_F11, /* F47 */ Mod4Mask, "\033[23;6~", 0, 0}, | |
| 577 + { XK_F11, /* F59 */ Mod1Mask, "\033[23;3~", 0, 0}, | |
| 578 + { XK_F12, XK_NO_MOD, "\033[24~", 0, 0}, | |
| 579 + { XK_F12, /* F24 */ ShiftMask, "\033[24;2~", 0, 0}, | |
| 580 + { XK_F12, /* F36 */ ControlMask, "\033[24;5~", 0, 0}, | |
| 581 + { XK_F12, /* F48 */ Mod4Mask, "\033[24;6~", 0, 0}, | |
| 582 + { XK_F12, /* F60 */ Mod1Mask, "\033[24;3~", 0, 0}, | |
| 583 + { XK_F13, XK_NO_MOD, "\033[1;2P", 0, 0}, | |
| 584 + { XK_F14, XK_NO_MOD, "\033[1;2Q", 0, 0}, | |
| 585 + { XK_F15, XK_NO_MOD, "\033[1;2R", 0, 0}, | |
| 586 + { XK_F16, XK_NO_MOD, "\033[1;2S", 0, 0}, | |
| 587 + { XK_F17, XK_NO_MOD, "\033[15;2~", 0, 0}, | |
| 588 + { XK_F18, XK_NO_MOD, "\033[17;2~", 0, 0}, | |
| 589 + { XK_F19, XK_NO_MOD, "\033[18;2~", 0, 0}, | |
| 590 + { XK_F20, XK_NO_MOD, "\033[19;2~", 0, 0}, | |
| 591 + { XK_F21, XK_NO_MOD, "\033[20;2~", 0, 0}, | |
| 592 + { XK_F22, XK_NO_MOD, "\033[21;2~", 0, 0}, | |
| 593 + { XK_F23, XK_NO_MOD, "\033[23;2~", 0, 0}, | |
| 594 + { XK_F24, XK_NO_MOD, "\033[24;2~", 0, 0}, | |
| 595 + { XK_F25, XK_NO_MOD, "\033[1;5P", 0, 0}, | |
| 596 + { XK_F26, XK_NO_MOD, "\033[1;5Q", 0, 0}, | |
| 597 + { XK_F27, XK_NO_MOD, "\033[1;5R", 0, 0}, | |
| 598 + { XK_F28, XK_NO_MOD, "\033[1;5S", 0, 0}, | |
| 599 + { XK_F29, XK_NO_MOD, "\033[15;5~", 0, 0}, | |
| 600 + { XK_F30, XK_NO_MOD, "\033[17;5~", 0, 0}, | |
| 601 + { XK_F31, XK_NO_MOD, "\033[18;5~", 0, 0}, | |
| 602 + { XK_F32, XK_NO_MOD, "\033[19;5~", 0, 0}, | |
| 603 + { XK_F33, XK_NO_MOD, "\033[20;5~", 0, 0}, | |
| 604 + { XK_F34, XK_NO_MOD, "\033[21;5~", 0, 0}, | |
| 605 + { XK_F35, XK_NO_MOD, "\033[23;5~", 0, 0}, | |
| 606 +}; | |
| 607 + | |
| 608 +/* | |
| 609 + * Selection types' masks. | |
| 610 + * Use the same masks as usual. | |
| 611 + * Button1Mask is always unset, to make masks match between ButtonPress. | |
| 612 + * ButtonRelease and MotionNotify. | |
| 613 + * If no match is found, regular selection is used. | |
| 614 + */ | |
| 615 +static uint selmasks[] = { | |
| 616 + [SEL_RECTANGULAR] = Mod1Mask, | |
| 617 +}; | |
| 618 + | |
| 619 +/* | |
| 620 + * Printable characters in ASCII, used to estimate the advance width | |
| 621 + * of single wide characters. | |
| 622 + */ | |
| 623 +static char ascii_printable[] = | |
| 624 + " !\"#$%&'()*+,-./0123456789:;<=>?" | |
| 625 + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" | |
| 626 + "`abcdefghijklmnopqrstuvwxyz{|}~"; | |
| 627 + | |
| 628 + | |
| 629 +/// word sepearors normal mode | |
| 630 +/// [Vim Browse]. | |
| 631 +char wordDelimSmall[] = " \t!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; | |
| 632 +char wordDelimLarge[] = " \t"; /// <Word sepearors normal mode (capital… | |
| 633 + | |
| 634 +/// Shortcusts executed in normal mode (which should not already be in … | |
| 635 +/// [Vim Browse]. | |
| 636 +struct NormalModeShortcuts normalModeShortcuts [] = { | |
| 637 + { 'R', "?Building\n" }, | |
| 638 + { 'r', "/Building\n" }, | |
| 639 + { 'F', "?: error:\n" }, | |
| 640 + { 'f', "/: error:\n" }, | |
| 641 + { 'Q', "?[Leaving vim, starting execution]\n" }, | |
| 642 + { 'S', "Qf" }, | |
| 643 + { 'X', "?juli@machine\n" }, | |
| 644 + { 'x', "/juli@machine\n" }, | |
| 645 +}; | |
| 646 + | |
| 647 +size_t const amountNormalModeShortcuts = sizeof(normalModeShortcuts) / … | |
| 648 + | |
| 649 +/// Style of the command string visualized in normal mode in the right … | |
| 650 +/// [Vim Browse]. | |
| 651 +Glyph const styleCommand = {' ', ATTR_ITALIC | ATTR_FAINT, 7, 16}; | |
| 652 +/// Style of the search string visualized in normal mode in the right c… | |
| 653 +/// [Vim Browse]. | |
| 654 +Glyph const styleSearch = {' ', ATTR_ITALIC | ATTR_BOLD_FAINT, 7, 16}; | |
| 655 + | |
| 656 +/// Colors used in normal mode in order to highlight different operatio… | |
| 657 +/// empathise the current position on screen in the status area [Vim … | |
| 658 +unsigned int bgCommandYank = 11; | |
| 659 +unsigned int bgCommandVisual = 4; | |
| 660 +unsigned int bgCommandVisualLine = 12; | |
| 661 + | |
| 662 +unsigned int fgCommandYank = 232; | |
| 663 +unsigned int fgCommandVisual = 232; | |
| 664 +unsigned int fgCommandVisualLine = 232; | |
| 665 + | |
| 666 +unsigned int bgPos = 15; | |
| 667 +unsigned int fgPos = 16; | |
| 668 + | |
| 669 diff --git a/dynamicArray.h b/dynamicArray.h | |
| 670 new file mode 100644 | |
| 671 index 0000000..8af5d9c | |
| 672 --- /dev/null | |
| 673 +++ b/dynamicArray.h | |
| 674 @@ -0,0 +1,175 @@ | |
| 675 +#ifndef DYNAMIC_ARRAY_H | |
| 676 +#define DYNAMIC_ARRAY_H | |
| 677 + | |
| 678 +#include "error.h" | |
| 679 + | |
| 680 +#include <stdint.h> | |
| 681 +#include <stdlib.h> | |
| 682 +#include <string.h> | |
| 683 +#include <stdbool.h> | |
| 684 + | |
| 685 +/// Struct for which this file offers functionality in order to expand … | |
| 686 +/// and set / get its content. | |
| 687 +typedef struct DynamicArray { | |
| 688 + /// Size of the datatype contained in the array. | |
| 689 + uint8_t itemSize; | |
| 690 + /// Amount of bytes currently initialized | |
| 691 + uint32_t index; | |
| 692 + /// Amount of bytes currently reserved (not necessarily initial… | |
| 693 + uint32_t allocated; | |
| 694 + /// Actual content. | |
| 695 + char* content; | |
| 696 +} DynamicArray; | |
| 697 + | |
| 698 +#define EXPAND_STEP 15 | |
| 699 + | |
| 700 +/// Default initializers for the dynamic array. | |
| 701 +#define CHAR_ARRAY {1, 0, 0, NULL} | |
| 702 +#define WORD_ARRAY {2, 0, 0, NULL} | |
| 703 +#define DWORD_ARRAY {4, 0, 0, NULL} | |
| 704 +#define QWORD_ARRAY {8, 0, 0, NULL} | |
| 705 +/// (Wasteful) utf-8 array, that always used 4 bytes in order to displa… | |
| 706 +/// character, even if the space is not required. | |
| 707 +#define UTF8_ARRAY DWORD_ARRAY | |
| 708 + | |
| 709 +/// Check that at least \p bytes are allocated, if true implying that | |
| 710 +/// \p s->content[\bytes - 1] is allocated. | |
| 711 +static inline bool | |
| 712 +isAllocated(DynamicArray const *s, uint32_t bytes) { | |
| 713 + return s != NULL && s->allocated >= bytes; | |
| 714 +} | |
| 715 + | |
| 716 +/// @see #isAllocated | |
| 717 +static inline bool | |
| 718 +isInitialized(DynamicArray const *s, uint32_t bytes) { | |
| 719 + return s != NULL && s->index >= bytes; | |
| 720 +} | |
| 721 + | |
| 722 +/// Return the next element in \p s and increment index without checkin… | |
| 723 +static inline char* | |
| 724 +gnext(DynamicArray *s) { | |
| 725 + ENSURE(s!=NULL, return NULL); | |
| 726 + ENSURE(s->index % s->itemSize == 0 && "(index not aligned)", | |
| 727 + s->index += s->itemSize - (s->index % s->itemSi… | |
| 728 + ENSURE(isAllocated(s, s->index + 2 * s->itemSize), return NULL); | |
| 729 + return s->content + (s->index += s->itemSize); | |
| 730 +} | |
| 731 + | |
| 732 +/// View element \p i in \p s. | |
| 733 +static inline char* | |
| 734 +view(DynamicArray const * s, uint32_t i) { | |
| 735 + ENSURE((s != NULL) && isAllocated(s, (i+1) * s->itemSize), retu… | |
| 736 + return s->content + i*s->itemSize; | |
| 737 +} | |
| 738 + | |
| 739 +/// Inspect element content[size() - 1 - i]. | |
| 740 +static inline char * | |
| 741 +viewEnd(DynamicArray const *s, uint32_t i) { | |
| 742 + ENSURE((s != NULL) && isInitialized(s, i * s->itemSize), return… | |
| 743 + ENSURE(s->index%s->itemSize == 0 && "(index not aligned)", retu… | |
| 744 + return s->content + s->index - (i + 1) * s->itemSize; | |
| 745 +} | |
| 746 + | |
| 747 +/// Set conent without applying | |
| 748 +static inline bool | |
| 749 +setValues(DynamicArray* s, char const *vals, uint32_t amount) { | |
| 750 + ENSURE(vals != NULL, return false); | |
| 751 + ENSURE((s != NULL) && isAllocated(s, s->index + amount), return… | |
| 752 + memcpy(s->content + s->index, vals, amount); | |
| 753 + return true; | |
| 754 +} | |
| 755 + | |
| 756 +static inline bool | |
| 757 +snext(DynamicArray* s, char const *vals, uint32_t amount) { | |
| 758 + bool const success = setValues(s, vals, amount); | |
| 759 + ENSURE(success, return false); | |
| 760 + uint8_t const rest = amount % s->itemSize; | |
| 761 + uint32_t const newSize = s->index + amount + (rest ? s->itemSiz… | |
| 762 + ENSURE(isAllocated(s, newSize), return false); | |
| 763 + s->index = newSize; | |
| 764 + return true; | |
| 765 +} | |
| 766 + | |
| 767 +/// Empty \p s. | |
| 768 +static inline void | |
| 769 +empty(DynamicArray* s) { | |
| 770 + ENSURE((s != NULL), return); | |
| 771 + s->index = 0; | |
| 772 +} | |
| 773 + | |
| 774 +/// Check if \p s has initialized content (which can be the case even i… | |
| 775 +/// is allocated). | |
| 776 +static inline bool | |
| 777 +isEmpty(DynamicArray const * s) { | |
| 778 + ENSURE((s != NULL), return true); | |
| 779 + return s->index == 0; | |
| 780 +} | |
| 781 + | |
| 782 +static inline int | |
| 783 +size(DynamicArray const * s) { | |
| 784 + ENSURE(s != NULL, return 0); | |
| 785 + ENSURE(s->itemSize != 0, return 0); | |
| 786 + return s->index / s->itemSize; | |
| 787 +} | |
| 788 + | |
| 789 +static inline void | |
| 790 +pop(DynamicArray* s) { | |
| 791 + ENSURE((s != NULL), return); | |
| 792 + ENSURE(s->index % s->itemSize == 0 && "(index not aligned)", | |
| 793 + s->index += s->itemSize - (s->index % s->itemSi… | |
| 794 + ENSURE(isInitialized(s, s->itemSize), return); | |
| 795 + s->index -= s->itemSize; | |
| 796 +} | |
| 797 + | |
| 798 +static inline bool | |
| 799 +checkSetNext(DynamicArray *s, char const *c, uint32_t amount) { | |
| 800 + ENSURE(s != NULL && c != NULL, return false); | |
| 801 + if (s->allocated < s->index + s->itemSize * amount) { | |
| 802 + uint32_t const diff = s->index+s->itemSize*amount-s->al… | |
| 803 + uint32_t const newAlloSize = s->allocated + (diff > EXP… | |
| 804 + ? diff : EXPAND_STEP) * s->itemSize; | |
| 805 + char* tmp = realloc(s->content, newAlloSize); | |
| 806 + if (tmp == NULL) { return false; } | |
| 807 + s->allocated = newAlloSize; | |
| 808 + s->content = tmp; | |
| 809 + assert(s->allocated >= s->index + s->itemSize * amount); | |
| 810 + } | |
| 811 + if (amount) { snext(s, c, amount); } | |
| 812 + return true; | |
| 813 +} | |
| 814 + | |
| 815 +static inline bool | |
| 816 +checkSetNextV(DynamicArray *s, char const c) { | |
| 817 + return checkSetNext(s, &c, 1); | |
| 818 +} | |
| 819 + | |
| 820 +static inline bool | |
| 821 +checkSetNextP(DynamicArray *s, char const *c) { | |
| 822 + ENSURE(c != NULL, return false); | |
| 823 + return checkSetNext(s, c, strlen(c)); | |
| 824 +} | |
| 825 + | |
| 826 +/// Expand the currently initialized content in \p s and the allocated … | |
| 827 +/// memory if required. | |
| 828 +static char * | |
| 829 +expand(DynamicArray *s) { | |
| 830 + ENSURE(s != NULL, return NULL); | |
| 831 + if (s->allocated < s->index + s->itemSize) { | |
| 832 + uint32_t const diff = s->index + s->itemSize - s->alloc… | |
| 833 + uint32_t const newAlloSize = s->allocated + (diff > EXP… | |
| 834 + ? diff : EXPAND_STEP) * s->itemSize; | |
| 835 + char* tmp = realloc(s->content, newAlloSize); | |
| 836 + if (tmp == NULL) { return NULL; } | |
| 837 + s->allocated = newAlloSize; | |
| 838 + s->content = tmp; | |
| 839 + assert(s->allocated >= s->index + s->itemSize); | |
| 840 + } | |
| 841 + s->index+=s->itemSize; | |
| 842 + return viewEnd(s, 0); | |
| 843 +} | |
| 844 + | |
| 845 +#define append(s, c) checkSetNext((s), (char const *) (c), (s)->itemSiz… | |
| 846 +#define appendPartial(s, c, i) checkSetNext((s), (char const *) (c), (i… | |
| 847 + | |
| 848 + | |
| 849 +#endif // DYNAMIC_ARRAY_H | |
| 850 diff --git a/error.h b/error.h | |
| 851 new file mode 100644 | |
| 852 index 0000000..91c621f | |
| 853 --- /dev/null | |
| 854 +++ b/error.h | |
| 855 @@ -0,0 +1,47 @@ | |
| 856 +#ifndef ERROR_H | |
| 857 +#define ERROR_H | |
| 858 + | |
| 859 +#include <assert.h> | |
| 860 + | |
| 861 +// Flag which determines whether to fail if a required condition is not… | |
| 862 +// to adapt the condition in order to work properly. | |
| 863 +// Attention: Be sure to perform a clean build after you alter preproce… | |
| 864 +// directives / definitions. | |
| 865 +//#define FAIL_ON_ERROR | |
| 866 + | |
| 867 +#include <stdio.h> | |
| 868 + | |
| 869 +/// | |
| 870 +/// Function used in case the fail-on-error mode is disabled (via defin… | |
| 871 +/// to report errors. In debug production mode, alias st to st 2> error… | |
| 872 +static void reportError(char const * cond, char const * stt, char const… | |
| 873 + unsigned int line ) { | |
| 874 + unsigned int const maxErrorCount = 100; | |
| 875 + static unsigned int errorCount = 0; | |
| 876 + if (++errorCount == 1) { | |
| 877 + printf("Report the following bug to " | |
| 878 + "https://github.com/juliusHuelsmann/st.… | |
| 879 + } | |
| 880 + if (errorCount < maxErrorCount) { | |
| 881 + printf("Bug:\tCondition '%s' evaluates to false.\n\tPer… | |
| 882 + " '%s' to counteract.\n\tFile:%s:%u\n", | |
| 883 + cond, stt, file, line); | |
| 884 + } else if (errorCount == maxErrorCount) { | |
| 885 + printf("Max amount of reported errors %u is reached. Fr… | |
| 886 + "on, no additional errors will be repor… | |
| 887 + maxErrorCount); | |
| 888 + } | |
| 889 +} | |
| 890 + | |
| 891 +/// Note that everyting condition checked / endforced with #ENSURE is | |
| 892 +/// considered an error, and behaves like an error depending on the fla… | |
| 893 +#ifdef FAIL_ON_ERROR | |
| 894 +#define ENSURE(cond, stt) assert(cond); | |
| 895 +#else // FAIL_ON_ERROR | |
| 896 +#define ENSURE(cond, stt) if (!(cond)) { … | |
| 897 + reportError(#cond, #stt, __FILE__, __… | |
| 898 + stt; … | |
| 899 + } | |
| 900 +#endif // FAIL_ON_ERROR | |
| 901 + | |
| 902 +#endif // ERROR_H | |
| 903 diff --git a/glyph.h b/glyph.h | |
| 904 new file mode 100644 | |
| 905 index 0000000..84aa252 | |
| 906 --- /dev/null | |
| 907 +++ b/glyph.h | |
| 908 @@ -0,0 +1,30 @@ | |
| 909 +#ifndef LINE_H | |
| 910 +#define LINE_H | |
| 911 + | |
| 912 +// | |
| 913 +// Contains the representation of the entities in the buffer (Line, Gyl… | |
| 914 +// is used by every part of the software implmeneting terminal logic. | |
| 915 +// | |
| 916 + | |
| 917 +#include <stdint.h> | |
| 918 + | |
| 919 +enum selection_type { | |
| 920 + SEL_REGULAR = 1, | |
| 921 + SEL_RECTANGULAR = 2 | |
| 922 +}; | |
| 923 + | |
| 924 +typedef uint_least32_t Rune; | |
| 925 + | |
| 926 +#define Glyph Glyph_ | |
| 927 + | |
| 928 +typedef struct { | |
| 929 + Rune u; /* character code */ | |
| 930 + unsigned short mode; /* attribute flags */ | |
| 931 + uint32_t fg; /* foreground */ | |
| 932 + uint32_t bg; /* background */ | |
| 933 +} Glyph; | |
| 934 + | |
| 935 + | |
| 936 +typedef Glyph *Line; | |
| 937 + | |
| 938 +#endif // LINE_H | |
| 939 diff --git a/normalMode.c b/normalMode.c | |
| 940 new file mode 100644 | |
| 941 index 0000000..59a5a89 | |
| 942 --- /dev/null | |
| 943 +++ b/normalMode.c | |
| 944 @@ -0,0 +1,750 @@ | |
| 945 +/* See LICENSE for license details. */ | |
| 946 +#include "normalMode.h" | |
| 947 +#include "dynamicArray.h" | |
| 948 +#include "term.h" | |
| 949 +#include "win.h" | |
| 950 +#include "error.h" | |
| 951 + | |
| 952 +#include <X11/keysym.h> | |
| 953 +#include <X11/XKBlib.h> | |
| 954 + | |
| 955 +#include <ctype.h> | |
| 956 +#include <stdio.h> | |
| 957 +#include <limits.h> | |
| 958 +#include <math.h> | |
| 959 + | |
| 960 +#define LEN(a) (sizeof(a) / sizeof(a)[0]) | |
| 961 +#define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | |
| 962 +#define FALLTHROUGH __attribute__((fallthrough)); | |
| 963 +#define SEC(var,ini,h,r) var = ini; if (!var) { h; return r; } | |
| 964 +#define EXPAND(v1,v2,r) char *SEC(v1, expand(v2), empty(v2), tru… | |
| 965 +#define currentCommand (toggle ? &commandHist0 : &commandHist1) | |
| 966 +#define lastCommand (toggle ? &commandHist1 : &commandHist0) | |
| 967 + | |
| 968 +// | |
| 969 +// Interface to the terminal | |
| 970 +extern Glyph const styleCommand, styleSearch; | |
| 971 +extern NormalModeShortcuts normalModeShortcuts[]; | |
| 972 +extern size_t const amountNormalModeShortcuts; | |
| 973 +extern char wordDelimSmall[]; | |
| 974 +extern char wordDelimLarge[]; | |
| 975 +extern unsigned int fgCommandYank, fgCommandVisual, fgCommandVisualLine, | |
| 976 + bgCommandYank, bgCommandVisual, bgCommandVisualLine, bgPos, fgPo… | |
| 977 + | |
| 978 +extern void selclear(void); | |
| 979 +extern void tsetdirt(int, int); | |
| 980 +extern size_t utf8encode(Rune, char *); | |
| 981 +extern size_t utf8decode(const char *, Rune *, size_t); | |
| 982 +extern size_t utf8decodebyte(char c, size_t *i); | |
| 983 + | |
| 984 +extern void selextend(int, int, int, int, int); | |
| 985 +extern void selstart(int, int, int, int); | |
| 986 +extern char *getsel(void); | |
| 987 +extern void tfulldirt(void); | |
| 988 + | |
| 989 +// | |
| 990 +// `Private` structs | |
| 991 +typedef struct { uint32_t x; uint32_t y; uint32_t yScr; } Position; | |
| 992 + | |
| 993 +/// Entire normal mode state, consisting of an operation and a motion. | |
| 994 +typedef struct { | |
| 995 + Position initialPosition; | |
| 996 + struct OperationState { | |
| 997 + enum Operation { | |
| 998 + noop = ' ', visual='v', visualLine='V', yank = … | |
| 999 + Position startPosition; | |
| 1000 + enum Infix { infix_none = 0, infix_i = 1, infix_a = 2, … | |
| 1001 + } command; | |
| 1002 + struct MotionState { | |
| 1003 + uint32_t amount; | |
| 1004 + enum Search {none, forward, backward} search; | |
| 1005 + Position searchPosition; | |
| 1006 + bool finished; | |
| 1007 + } motion; | |
| 1008 +} NormalModeState; | |
| 1009 + | |
| 1010 +/// Default state if no operation is performed. | |
| 1011 +NormalModeState defaultNormalMode = { | |
| 1012 + {0,0,0}, {noop, {0, 0, 0}, false}, {0, none, {0, 0, 0}, tr… | |
| 1013 +}; | |
| 1014 +NormalModeState stateVB = { | |
| 1015 + {0,0,0}, {noop, {0, 0, 0}, false}, {0, none, {0, 0, 0}, tr… | |
| 1016 +}; | |
| 1017 + | |
| 1018 +DynamicArray searchString = UTF8_ARRAY; | |
| 1019 +DynamicArray commandHist0 = UTF8_ARRAY; | |
| 1020 +DynamicArray commandHist1 = UTF8_ARRAY; | |
| 1021 +DynamicArray highlights = DWORD_ARRAY; | |
| 1022 + | |
| 1023 +/// History command toggle | |
| 1024 +static bool toggle = false; | |
| 1025 + | |
| 1026 +// | |
| 1027 +// Utility functions | |
| 1028 +static inline int intervalDiff(int v, int a, int b) { | |
| 1029 + return (v < a) ? (v - a) : ((v > b) ? (v - b) : 0); | |
| 1030 +} | |
| 1031 +static inline void swap(DynamicArray *const a, DynamicArray *const b) { | |
| 1032 + DynamicArray tmp = *a; *a = *b; *b = tmp; | |
| 1033 +} | |
| 1034 +static inline int max(int a, int b) { return a > b ? a : b; } | |
| 1035 +static inline int min(int a, int b) { return a < b ? a : b; } | |
| 1036 +static inline int mod(int a, int b) { for (; a < 0; a += b); return a %… | |
| 1037 +static inline bool contains (char c, char const * values, uint32_t memS… | |
| 1038 + ENSURE(values != NULL, return false); | |
| 1039 + for (uint32_t i = 0; i < memSize; ++i) if (c == values[i]) retu… | |
| 1040 + return false; | |
| 1041 +} | |
| 1042 +static inline void applyPosition(Position const *pos) { | |
| 1043 + ENSURE(pos != NULL, return); | |
| 1044 + term.c.x = pos->x; | |
| 1045 + term.c.y = pos->y; | |
| 1046 + term.scr = pos->yScr; | |
| 1047 +} | |
| 1048 +static inline int getSearchDirection(void) { | |
| 1049 + return stateVB.motion.search == forward ? 1 : -1; | |
| 1050 +} | |
| 1051 + | |
| 1052 +// Utilities for working with the current version of the scrollback pa… | |
| 1053 +static bool moveLine(int32_t const amount) { | |
| 1054 + int32_t const reqShift = intervalDiff(term.c.y+=amount, 0, term… | |
| 1055 + term.c.y -= reqShift; | |
| 1056 + int32_t const sDiff = intervalDiff(term.scr-=reqShift, 0, HISTS… | |
| 1057 + term.scr -= sDiff; | |
| 1058 + return sDiff == 0; | |
| 1059 +} | |
| 1060 + | |
| 1061 +static void moveLetter(int32_t const amount) { | |
| 1062 + int32_t value = (term.c.x += amount) / term.col; | |
| 1063 + if (value -= (term.c.x < 0)) { | |
| 1064 + term.c.x = moveLine(value) ? mod(term.c.x, term.col) | |
| 1065 + : max(min(term.c.x,term.col - 1), 0); | |
| 1066 + } | |
| 1067 + assert(BETWEEN(term.c.x,0,term.col-1)&&BETWEEN(term.c.y,0,term.… | |
| 1068 +} | |
| 1069 + | |
| 1070 +// | |
| 1071 +// `Private` functions: | |
| 1072 + | |
| 1073 +// Functions: Temporarily display string on screen. | |
| 1074 + | |
| 1075 +/// Display string at end of a specified line without writing it into t… | |
| 1076 +/// @param str string that is to be displayed | |
| 1077 +/// @param g glyph | |
| 1078 +/// @param yPos | |
| 1079 +static void | |
| 1080 +displayString(DynamicArray const *str, Glyph const *g, int yPos, bool p… | |
| 1081 + ENSURE((str != NULL) && (g != NULL) && (term.row > 0), return); | |
| 1082 + ENSURE(yPos >= 0, yPos = 0); | |
| 1083 + ENSURE(yPos < term.row, yPos = term.row - 1); | |
| 1084 + // Arbritary limit to avoid withhelding too much info from user. | |
| 1085 + int const maxFractionOverridden = 3; | |
| 1086 + // Threshold: if there is no space to print, do not print, but … | |
| 1087 + // repsonsibility for printing back to [st]. | |
| 1088 + if (term.col < maxFractionOverridden) { … | |
| 1089 + term.dirty[yPos] = 1; | |
| 1090 + return; | |
| 1091 + } | |
| 1092 + int32_t const botSz = prePos * 6; //< sz for position indication | |
| 1093 + // Determine the dimensions of used chunk of screen. | |
| 1094 + int32_t const overrideSize = min(size(str) + botSz, | |
| 1095 + term.col / maxFractionOverridden); … | |
| 1096 + int32_t const overrideEnd = term.col - 2; | |
| 1097 + // Has to follow trivially hence th assert: | |
| 1098 + // overrideSize <(1)= term.col/3 <(0)= term.col = overrideEnd … | |
| 1099 + assert(overrideSize <= overrideEnd + 1); | |
| 1100 + int32_t const overrideStart = 1 + overrideEnd - overrideSize; | |
| 1101 + // display history[history.size() - (overrideSize - botSz)::-1] | |
| 1102 + Glyph *SEC(line, malloc(sizeof(Glyph) * (overrideSize)),,) | |
| 1103 + int32_t offset = (size(str) - overrideSize - 1 + botSz) * str->… | |
| 1104 + for (uint32_t chr = 0; chr < overrideSize - botSz; ++chr) { | |
| 1105 + line[chr] = *g; | |
| 1106 + line[chr].u = *((Rune*) (str->content+(offset+=str->ite… | |
| 1107 + } | |
| 1108 + if (prePos) { | |
| 1109 + ENSURE(term.scr < HISTSIZE, term.scr = HISTSIZE - 1); | |
| 1110 + int32_t const p=round((HISTSIZE-1-term.scr)*100./(HISTS… | |
| 1111 + char prc [8]; | |
| 1112 + switch (term.scr) { | |
| 1113 + case HISTSIZE - 1: strcpy(prc, " [TOP]"); break; | |
| 1114 + case 0: strcpy(prc, " [BOT]"); break; | |
| 1115 + default: sprintf(prc, " % 3d%c ", p,… | |
| 1116 + } | |
| 1117 + for (uint32_t chr = 0; chr < botSz; ++chr) { | |
| 1118 + line[chr + overrideSize - botSz] =*g; | |
| 1119 + line[chr + overrideSize - botSz].fg = fgPos; | |
| 1120 + line[chr + overrideSize - botSz].bg = bgPos; | |
| 1121 + utf8decode(&prc[chr],&line[chr+overrideSize-bot… | |
| 1122 + } | |
| 1123 + line[overrideSize - botSz] =*g; | |
| 1124 + } | |
| 1125 + xdrawline(TLINE(yPos), 0, yPos, overrideStart); | |
| 1126 + term.c.y -= term.row; term.c.x -= term.col; // not highlight ha… | |
| 1127 + xdrawline(line-overrideStart, overrideStart, yPos, overrideEnd … | |
| 1128 + term.c.y += term.row; term.c.x += term.col; | |
| 1129 + free(line); | |
| 1130 +} | |
| 1131 + | |
| 1132 +static inline void printCommandString(void) { | |
| 1133 + Glyph g = styleCommand; | |
| 1134 + switch(stateVB.command.op) { | |
| 1135 + case yank: g.fg = fgCommandYank; g.bg = bgCommandYank; … | |
| 1136 + case visual: g.fg=fgCommandVisual; g.bg=bgCommandVisual… | |
| 1137 + case visualLine: g.fg=fgCommandVisualLine; | |
| 1138 + g.bg=bgCommandVisualLine; | |
| 1139 + } | |
| 1140 + displayString(isEmpty(currentCommand) ? lastCommand : currentCo… | |
| 1141 + &g, term.row - 1, true); | |
| 1142 +} | |
| 1143 + | |
| 1144 +static inline void printSearchString(void) { | |
| 1145 + displayString(&searchString, &styleSearch, term.row - 2, false); | |
| 1146 +} | |
| 1147 + | |
| 1148 +// NormalMode Operation / Motion utilies. | |
| 1149 + | |
| 1150 +static inline bool isMotionFinished(void) { return stateVB.motion.finis… | |
| 1151 + | |
| 1152 +static inline void finishMotion(void) { stateVB.motion.finished = true;… | |
| 1153 + | |
| 1154 +static inline bool isOperationFinished(void) { | |
| 1155 + return stateVB.command.op==noop && stateVB.command.infix==infix… | |
| 1156 +} | |
| 1157 + | |
| 1158 +/// Register that the current comamnd is finished and a new command is … | |
| 1159 +static inline void startNewCommand(bool abort) { | |
| 1160 + if (!abort) { toggle = !toggle; } | |
| 1161 + empty(currentCommand); | |
| 1162 +} | |
| 1163 + | |
| 1164 +static inline void finishOperation(void) { | |
| 1165 + stateVB.command = defaultNormalMode.command; | |
| 1166 + assert(isOperationFinished()); | |
| 1167 + // After an operation is finished, the selection has to be rele… | |
| 1168 + // no highlights are to be released. | |
| 1169 + selclear(); | |
| 1170 + empty(&highlights); | |
| 1171 + // THe command string is reset for a new command. | |
| 1172 + startNewCommand(true); | |
| 1173 +} | |
| 1174 + | |
| 1175 +static inline void enableOperation(enum Operation o) { | |
| 1176 + finishOperation(); | |
| 1177 + stateVB.command.op = o; | |
| 1178 + stateVB.command.infix = infix_none; | |
| 1179 + stateVB.command.startPosition.x = term.c.x; | |
| 1180 + stateVB.command.startPosition.y = term.c.y; | |
| 1181 + stateVB.command.startPosition.yScr = term.scr; | |
| 1182 +} | |
| 1183 + | |
| 1184 +/// @param abort: If enabled, the command exits without registering | |
| 1185 +/// @return Whether the the application is ready to yield control… | |
| 1186 +//the normal command flow | |
| 1187 +static bool terminateCommand(bool abort) { | |
| 1188 + bool const exitOperation = isMotionFinished(); | |
| 1189 + bool exitNormalMode = false; | |
| 1190 + finishMotion(); | |
| 1191 + | |
| 1192 + if (exitOperation) { | |
| 1193 + exitNormalMode = isOperationFinished(); | |
| 1194 + finishOperation(); | |
| 1195 + } | |
| 1196 + printCommandString(); | |
| 1197 + printSearchString(); | |
| 1198 + return exitNormalMode; | |
| 1199 +} | |
| 1200 + | |
| 1201 +static inline void exitCommand(void) { terminateCommand(false); } | |
| 1202 + | |
| 1203 +static inline void abortCommand(void) { terminateCommand(true); } | |
| 1204 + | |
| 1205 +/// Go to next occurrence of string relative to the current location | |
| 1206 +/// conduct search, starting at start pos | |
| 1207 +static bool gotoString(int8_t sign) { | |
| 1208 + moveLetter(sign); | |
| 1209 + uint32_t const searchStrSize = size(&searchString); | |
| 1210 + uint32_t const maxIter = (HISTSIZE+term.row) * term.col + searc… | |
| 1211 + uint32_t findIdx = 0; | |
| 1212 + for (uint32_t cIteration = 0; findIdx < searchStrSize | |
| 1213 + && ++cIteration <= maxIter; moveLetter(sign)) { | |
| 1214 + char const * const SEC(next, sign==1 | |
| 1215 + ? view(&searchString, findIdx) | |
| 1216 + : viewEnd(&searchString, findIdx), , fa… | |
| 1217 + uint32_t const searchChar = *((uint32_t*) next); | |
| 1218 + | |
| 1219 + if (TLINE(term.c.y)[term.c.x].u == searchChar) { ++find… | |
| 1220 + else { findIdx = 0; } | |
| 1221 + } | |
| 1222 + bool const found = findIdx == searchStrSize; | |
| 1223 + for (uint32_t i = 0; found && i < searchStrSize; ++i) moveLette… | |
| 1224 + return found; | |
| 1225 +} | |
| 1226 + | |
| 1227 +/// Highlight all found strings on the current screen. | |
| 1228 +static void highlightStringOnScreen(void) { | |
| 1229 + if (isEmpty(&searchString)) { return; } | |
| 1230 + empty(&highlights); | |
| 1231 + uint32_t const searchStringSize = size(&searchString); | |
| 1232 + uint32_t findIdx = 0; | |
| 1233 + uint32_t xStart, yStart; | |
| 1234 + bool success = true; | |
| 1235 + for (int y = 0; y < term.row && success; y++) { | |
| 1236 + for (int x = 0; x < term.col && success; x++) { | |
| 1237 + char const* const SEC(next, | |
| 1238 + view(&searchString,findIdx),,) | |
| 1239 + if (TLINE(y)[x].u == (Rune) *((uint32_t*)(next)… | |
| 1240 + if (++findIdx == 1) { | |
| 1241 + xStart = x; | |
| 1242 + yStart = y; | |
| 1243 + } | |
| 1244 + if (findIdx == searchStringSize) { | |
| 1245 + success = success | |
| 1246 + && append(&highlights, … | |
| 1247 + && append(&highlights, … | |
| 1248 + findIdx = 0; //term.dirty[yStar… | |
| 1249 + } | |
| 1250 + } else { findIdx = 0; } | |
| 1251 + } | |
| 1252 + } | |
| 1253 + if (!success) { empty(&highlights); } | |
| 1254 +} | |
| 1255 + | |
| 1256 +static bool gotoStringAndHighlight(int8_t sign) { | |
| 1257 + // Find hte next occurrence of the #searchString in direc… | |
| 1258 + bool const found = gotoString(sign); | |
| 1259 + if (!found) { applyPosition(&stateVB.motion.searchPosition); } | |
| 1260 + highlightStringOnScreen(); | |
| 1261 + //tsetdirt(0, term.row-3); //< everything except for the 'statu… | |
| 1262 + return found; | |
| 1263 +} | |
| 1264 + | |
| 1265 +static bool pressKeys(char const* nullTerminatedString, size_t end) { | |
| 1266 + bool sc = true; | |
| 1267 + for (size_t i = 0; i < end && sc; ++i) { | |
| 1268 + sc = kpressNormalMode(&nullTerminatedString[i], 1, fals… | |
| 1269 + } | |
| 1270 + return sc; | |
| 1271 +} | |
| 1272 + | |
| 1273 +static bool executeCommand(DynamicArray const *command) { | |
| 1274 + size_t end=size(command); | |
| 1275 + char decoded [32]; | |
| 1276 + bool succ = true; | |
| 1277 + size_t len; | |
| 1278 + for (size_t i = 0; i < end && succ; ++i) { | |
| 1279 + char const *const SEC(nextRune, view(command, i),,false) | |
| 1280 + len = utf8encode(*((Rune *) nextRune), decoded); | |
| 1281 + succ = kpressNormalMode(decoded, len, false, NULL); | |
| 1282 + } | |
| 1283 + return succ; | |
| 1284 +} | |
| 1285 + | |
| 1286 +struct { char const first; char const second; } const Brackets [] = | |
| 1287 +{ {'(', ')'}, {'<', '>'}, {'{', '}'}, {'[', ']'}, }; | |
| 1288 + | |
| 1289 + | |
| 1290 +/// Emits Command prefix and suffix when i motion is performed (e.g. yi… | |
| 1291 +/// | |
| 1292 +/// @param c: motion character | |
| 1293 +/// @param expandMode: 1 for 'i', 2 for 'a' | |
| 1294 +/// @param first, second: Dynamic arrays in which the prefix and postfix | |
| 1295 +/// commands will be returned | |
| 1296 +/// @return whether the command could be extracted succes… | |
| 1297 +static bool expandExpression(char const c, enum Infix expandMode, | |
| 1298 + char operation, DynamicArray *cmd) { | |
| 1299 + empty(cmd); | |
| 1300 + bool s = true; //< used in order to detect memory allocation er… | |
| 1301 + char const lower = tolower(c); | |
| 1302 + // Motions | |
| 1303 + if (lower == 'w') { | |
| 1304 + // translated into wb[command]e resp. WB[command]E, whi… | |
| 1305 + // file even when at the fist letter. Does not work for… | |
| 1306 + // letter words though. | |
| 1307 + int const diff = c - lower; | |
| 1308 + s = s && checkSetNextV(cmd, c); | |
| 1309 + s = s && checkSetNextV(cmd, (signed char)(((int)'b') + … | |
| 1310 + s = s && checkSetNextV(cmd, operation); | |
| 1311 + s = s && checkSetNextV(cmd, (signed char)(((int)'e')+ d… | |
| 1312 + return s; | |
| 1313 + } | |
| 1314 + // Symmetrical brackets (quotation marks) | |
| 1315 + if (c == '\'' || c == '"') { | |
| 1316 + // Local ambiguity -> do nothing. It cannot be determin… | |
| 1317 + // the current char is the 1st or last char of the sele… | |
| 1318 + // <---- search here? -- ['] -- or search here? ---> | |
| 1319 + if (TLINE(term.c.y)[term.c.x].u == c) { | |
| 1320 + return false; | |
| 1321 + } | |
| 1322 + // Prefix | |
| 1323 + char res [] = {'?', c, '\n'}; | |
| 1324 + s = s && checkSetNextP(cmd, res); | |
| 1325 + // infix | |
| 1326 + bool const iffy = expandMode == infix_i; | |
| 1327 + if (iffy) { s = s && checkSetNextV(cmd, 'l'); } | |
| 1328 + s = s && checkSetNextV(cmd, operation); | |
| 1329 + if (!iffy) { s = s && checkSetNextV(cmd, 'l'); } | |
| 1330 + // suffix | |
| 1331 + res[0] = '/'; | |
| 1332 + s = s && checkSetNextP(cmd, res); | |
| 1333 + if (iffy) { s = s && checkSetNextV(cmd, 'h'); } | |
| 1334 + return s; | |
| 1335 + } | |
| 1336 + // Brackets: Does not if in range / if the brackets belong toge… | |
| 1337 + for (size_t pid = 0; pid < sizeof(Brackets); ++pid) { | |
| 1338 + if(Brackets[pid].first == c || Brackets[pid].second == … | |
| 1339 + if (TLINE(term.c.y)[term.c.x].u!=Brackets[pid].… | |
| 1340 + s = s && checkSetNextV(cmd, '?'); | |
| 1341 + s = s && checkSetNextV(cmd, Brackets[pi… | |
| 1342 + s = s && checkSetNextV(cmd, '\n'); | |
| 1343 + } | |
| 1344 + bool const iffy = expandMode == infix_i; | |
| 1345 + if (iffy) { s = s && checkSetNextV(cmd, 'l'); } | |
| 1346 + s = s && checkSetNextV(cmd, operation); | |
| 1347 + if (!iffy) { s = s && checkSetNextV(cmd, 'l'); } | |
| 1348 + s = s && checkSetNextV(cmd, '/'); | |
| 1349 + s = s && checkSetNextV(cmd, Brackets[pid].secon… | |
| 1350 + s = s && checkSetNextV(cmd, '\n'); | |
| 1351 + if (iffy) { s = s && checkSetNextV(cmd, 'h'); } | |
| 1352 + return s; | |
| 1353 + } | |
| 1354 + } | |
| 1355 + /**/ | |
| 1356 + // search string | |
| 1357 + // complicated search operation: <tag> | |
| 1358 + if (c == 't') { | |
| 1359 + // XXX: (Bug in vim: @vit ) | |
| 1360 + // <tag_name attr="hier" a2="\<sch\>"> [current pos] </… | |
| 1361 + | |
| 1362 + // 1. Copy history ( tag := hist[?<\n:/ \n] ) | |
| 1363 + // 2. Copy history ( first_find := hist[?<\n: next plac… | |
| 1364 + // history where count '>' > count '<' | |
| 1365 + // (can be behind current pos) ) | |
| 1366 + // 3. first := [?first_find][#first_ind]l | |
| 1367 + // second:= [/tag">"]h | |
| 1368 + //return true; // XXX: not implmented yet. | |
| 1369 + } | |
| 1370 + return false; | |
| 1371 +} | |
| 1372 + | |
| 1373 +// | |
| 1374 +// Public API | |
| 1375 +// | |
| 1376 + | |
| 1377 +void onMove(void) { | |
| 1378 + stateVB.initialPosition.x = term.c.x; | |
| 1379 + stateVB.initialPosition.y = term.c.y; | |
| 1380 + stateVB.initialPosition.yScr = term.scr; | |
| 1381 +} | |
| 1382 + | |
| 1383 +int highlighted(int x, int y) { | |
| 1384 + // Compute the legal bounds for a hit: | |
| 1385 + int32_t const stringSize = size(&searchString); | |
| 1386 + int32_t xMin = x - stringSize; | |
| 1387 + int32_t yMin = y; | |
| 1388 + while (xMin < 0 && yMin > 0) { | |
| 1389 + xMin += term.col; | |
| 1390 + --yMin; | |
| 1391 + } | |
| 1392 + if (xMin < 0) { xMin = 0; } | |
| 1393 + | |
| 1394 + uint32_t highSize = size(&highlights); | |
| 1395 + ENSURE(highSize % 2 == 0, empty(&highlights); return false;); | |
| 1396 + highSize /= 2; | |
| 1397 + uint32_t *ptr = (uint32_t*) highlights.content; | |
| 1398 + for (uint32_t i = 0; i < highSize; ++i) { | |
| 1399 + int32_t const sx = (int32_t) *(ptr++); | |
| 1400 + int32_t const sy = (int32_t) *(ptr++); | |
| 1401 + if (BETWEEN(sy, yMin, y) && (sy != yMin || sx > xMin) | |
| 1402 + && (sy != y || sx <= x)) { | |
| 1403 + return true; | |
| 1404 + } | |
| 1405 + } | |
| 1406 + return false; | |
| 1407 +} | |
| 1408 + | |
| 1409 +ExitState kpressNormalMode(char const * cs, int len, bool ctrl, void co… | |
| 1410 + KeySym const * const ksym = (KeySym*) v; | |
| 1411 + bool const esc = ksym && *ksym == XK_Escape; | |
| 1412 + bool const enter = (ksym && *ksym==XK_Return) || (len==1 &&cs[0… | |
| 1413 + bool const quantifier = len == 1 && (BETWEEN(cs[0], 49, 57) | |
| 1414 + || (cs[0] == 48 && stateVB.motion.amount)); | |
| 1415 + int const previousScroll = term.scr; | |
| 1416 + // [ESC] or [ENTER] abort resp. finish the current level of ope… | |
| 1417 + // Typing 'i' if no operation is currently performed behaves li… | |
| 1418 + if (esc || enter || (len == 1 && cs[0] == 'i' && isMotionFinish… | |
| 1419 + && isOperationFinished())) { | |
| 1420 + if (terminateCommand(!enter)) { | |
| 1421 + applyPosition(&stateVB.initialPosition); | |
| 1422 + Position const pc = stateVB.initialPosition; | |
| 1423 + stateVB = defaultNormalMode; | |
| 1424 + stateVB.initialPosition = pc; | |
| 1425 + tfulldirt(); | |
| 1426 + return finished; | |
| 1427 + } | |
| 1428 + len = 0; | |
| 1429 + goto motionFinish; | |
| 1430 + } | |
| 1431 + // Backspace | |
| 1432 + if (ksym && *ksym == XK_BackSpace) { | |
| 1433 + bool s = stateVB.motion.search!=none&&!stateVB.motion.f… | |
| 1434 + bool q = stateVB.motion.amount != 0; | |
| 1435 + if (!(s || q)) { return failed; } | |
| 1436 + len = 0; | |
| 1437 + | |
| 1438 + if (!isEmpty(currentCommand)) { pop(currentCommand); } | |
| 1439 + if (s) { | |
| 1440 + if (!isEmpty(&searchString)) { pop(&searchStrin… | |
| 1441 + else if (isEmpty(&searchString)) { | |
| 1442 + exitCommand(); | |
| 1443 + return success; | |
| 1444 + } | |
| 1445 + } else if (q) { | |
| 1446 + stateVB.motion.amount /= 10; | |
| 1447 + goto finishNoAppend; | |
| 1448 + } | |
| 1449 + } | |
| 1450 + | |
| 1451 + // Search: append to search string, then search & highlight | |
| 1452 + if (stateVB.motion.search != none && !stateVB.motion.finished) { | |
| 1453 + if (len >= 1) { | |
| 1454 + EXPAND(kSearch, &searchString, true) | |
| 1455 + utf8decode(cs, (Rune*)(kSearch), len); | |
| 1456 + } | |
| 1457 + applyPosition(&stateVB.motion.searchPosition); | |
| 1458 + gotoStringAndHighlight(getSearchDirection()); | |
| 1459 + goto finish; | |
| 1460 + } | |
| 1461 + if (len == 0) { return failed; } | |
| 1462 + // Quantifiers | |
| 1463 + if (quantifier) { | |
| 1464 + stateVB.motion.amount = min(SHRT_MAX, | |
| 1465 + stateVB.motion.amount * 10 + cs[0] - 48… | |
| 1466 + goto finish; | |
| 1467 + } | |
| 1468 + // 'i' mode enabled, hence the expression is to be expanded: | |
| 1469 + // [start_expression(cs[0])] [operation] [stop_expression(cs[0]… | |
| 1470 + if (stateVB.command.infix != infix_none && stateVB.command.op !… | |
| 1471 + DynamicArray cmd = CHAR_ARRAY; | |
| 1472 + char const operation = stateVB.command.op; | |
| 1473 + bool succ = expandExpression(cs[0], | |
| 1474 + stateVB.command.infix, visual, &cmd); | |
| 1475 + if (operation == yank) { | |
| 1476 + succ = succ && checkSetNextV(&cmd, operation); | |
| 1477 + } | |
| 1478 + NormalModeState const st = stateVB; | |
| 1479 + TCursor const tc = term.c; | |
| 1480 + stateVB.command.infix = infix_none; | |
| 1481 + if (succ) { | |
| 1482 + stateVB.command.op = noop; | |
| 1483 + for (int i = 0; i < size(&cmd) && succ; ++i) { | |
| 1484 + succ = pressKeys(&cmd.content[i], 1); | |
| 1485 + } | |
| 1486 + if (!succ) { // go back to the old position, ap… | |
| 1487 + stateVB = st; | |
| 1488 + term.c = tc; | |
| 1489 + } | |
| 1490 + empty(currentCommand); | |
| 1491 + for (uint32_t i = 0; i < size(&cmd); ++i) { | |
| 1492 + EXPAND(kCommand, currentCommand, true) | |
| 1493 + utf8decode(cmd.content+i, (Rune*)(kComm… | |
| 1494 + } | |
| 1495 + } | |
| 1496 + free(cmd.content); | |
| 1497 + goto finishNoAppend; | |
| 1498 + } | |
| 1499 + // Commands (V / v or y) | |
| 1500 + switch(cs[0]) { | |
| 1501 + case '.': | |
| 1502 + { | |
| 1503 + if (isEmpty(currentCommand)) { toggle = !toggle… | |
| 1504 + DynamicArray cmd = UTF8_ARRAY; | |
| 1505 + swap(&cmd, currentCommand); | |
| 1506 + executeCommand(&cmd) ? success : failed; | |
| 1507 + swap(&cmd, currentCommand); | |
| 1508 + free(cmd.content); | |
| 1509 + goto finishNoAppend; | |
| 1510 + } | |
| 1511 + case 'i': stateVB.command.infix = infix_i; goto finish; | |
| 1512 + case 'a': stateVB.command.infix = infix_a; goto finish; | |
| 1513 + case 'y': | |
| 1514 + switch(stateVB.command.op) { | |
| 1515 + case noop: //< Start yank mode & set #op | |
| 1516 + enableOperation(yank); | |
| 1517 + selstart(term.c.x, term.c.y,ter… | |
| 1518 + goto finish; | |
| 1519 + case yank: //< Complete yank [y#amount … | |
| 1520 + selstart(0, term.c.y, term.scr,… | |
| 1521 + int const origY = term.c.y; | |
| 1522 + moveLine(max(stateVB.motion.amo… | |
| 1523 + selextend(term.col-1,term.c.y,t… | |
| 1524 + SEL_RECTANGULAR… | |
| 1525 + term.c.y = origY; | |
| 1526 + FALLTHROUGH | |
| 1527 + case visualLine: // Yank visual selecti… | |
| 1528 + case visual: | |
| 1529 + xsetsel(getsel()); | |
| 1530 + xclipcopy(); | |
| 1531 + exitCommand(); | |
| 1532 + goto finish; | |
| 1533 + default: | |
| 1534 + return failed; | |
| 1535 + } | |
| 1536 + case visual: | |
| 1537 + case visualLine: | |
| 1538 + if (stateVB.command.op == cs[0]) { | |
| 1539 + finishOperation(); | |
| 1540 + return true; | |
| 1541 + } else { | |
| 1542 + enableOperation(cs[0]); | |
| 1543 + selstart(cs[0] == visualLine ? 0 : term… | |
| 1544 + term.c.y, term.scr, 0); | |
| 1545 + goto finish; | |
| 1546 + } | |
| 1547 + } | |
| 1548 + // CTRL Motions | |
| 1549 + int32_t sign = -1; //< if command goes 'forward'(1) or 'back… | |
| 1550 + if (ctrl) { | |
| 1551 + if (ksym == NULL) { return false; } | |
| 1552 + switch(*ksym) { | |
| 1553 + case XK_f: | |
| 1554 + term.scr = max(term.scr - max(term.row-… | |
| 1555 + term.c.y = 0; | |
| 1556 + goto finish; | |
| 1557 + case XK_b: | |
| 1558 + term.scr = min(term.scr + max(term.row … | |
| 1559 + HISTSIZE - 1); | |
| 1560 + term.c.y = term.bot; | |
| 1561 + goto finish; | |
| 1562 + case XK_u: | |
| 1563 + term.scr = min(term.scr+term.row/2, HIS… | |
| 1564 + goto finish; | |
| 1565 + case XK_d: | |
| 1566 + term.scr = max(term.scr - term.row / 2,… | |
| 1567 + goto finish; | |
| 1568 + default: return false; | |
| 1569 + } | |
| 1570 + } | |
| 1571 + // Motions | |
| 1572 + switch(cs[0]) { | |
| 1573 + case 'c': empty(&commandHist0); empty(&commandHist1); | |
| 1574 + goto finishNoAppend; | |
| 1575 + case 'j': sign = 1; FALLTHROUGH | |
| 1576 + case 'k': moveLine(max(stateVB.motion.amount,1) * sign); | |
| 1577 + goto motionFinish; | |
| 1578 + case 'H': term.c.y = 0; | |
| 1579 + goto motionFinish; | |
| 1580 + case 'M': term.c.y = term.bot / 2; | |
| 1581 + goto motionFinish; | |
| 1582 + case 'L': term.c.y = term.bot; | |
| 1583 + goto motionFinish; | |
| 1584 + case 'G': applyPosition(&stateVB.initialPosition); | |
| 1585 + goto motionFinish; | |
| 1586 + case 'l': sign = 1; FALLTHROUGH | |
| 1587 + case 'h': moveLetter(sign * max(stateVB.motion.amount,1… | |
| 1588 + goto motionFinish; | |
| 1589 + case '0': term.c.x = 0; | |
| 1590 + goto motionFinish; | |
| 1591 + case '$': term.c.x = term.col-1; | |
| 1592 + goto motionFinish; | |
| 1593 + case 'w': FALLTHROUGH | |
| 1594 + case 'W': FALLTHROUGH | |
| 1595 + case 'e': FALLTHROUGH | |
| 1596 + case 'E': sign = 1; FALLTHROUGH | |
| 1597 + case 'B': FALLTHROUGH | |
| 1598 + case 'b': { | |
| 1599 + char const * const wDelim = | |
| 1600 + cs[0] <= 90 ? wordDelimLarge : wordDeli… | |
| 1601 + uint32_t const wDelimLen = strlen(wDelim); | |
| 1602 + | |
| 1603 + bool const startSpaceIsSeparator = | |
| 1604 + !(cs[0] == 'w' || cs[0] == 'W'); | |
| 1605 + // Whether to start & end with offset: | |
| 1606 + bool const performOffset = startSpaceIsSeparato… | |
| 1607 + // Max iteration := One complete hist traversal. | |
| 1608 + uint32_t const maxIter = (HISTSIZE+term.row) * … | |
| 1609 + // Doesn't work exactly as in vim: Linebreak is | |
| 1610 + // counted as 'normal' separator, hence a jump … | |
| 1611 + // span multiple lines here. | |
| 1612 + stateVB.motion.amount = max(stateVB.motion.amou… | |
| 1613 + for (;stateVB.motion.amount>0;--stateVB.motion.… | |
| 1614 + uint8_t state = 0; | |
| 1615 + if (performOffset) { moveLetter(sign); } | |
| 1616 + for (uint32_t cIt = 0; cIt ++ < maxIter… | |
| 1617 + if (startSpaceIsSeparator == co… | |
| 1618 + if (state == 1) { | |
| 1619 + if (performOffs… | |
| 1620 + moveLet… | |
| 1621 + } | |
| 1622 + break; | |
| 1623 + } | |
| 1624 + } else if (state == 0) { state … | |
| 1625 + } | |
| 1626 + } | |
| 1627 + goto motionFinish; | |
| 1628 + } | |
| 1629 + case '/': sign = 1; FALLTHROUGH | |
| 1630 + case '?': | |
| 1631 + empty(&searchString); | |
| 1632 + stateVB.motion.search = sign == 1 ? forward :… | |
| 1633 + stateVB.motion.searchPosition.x = term.c.x; | |
| 1634 + stateVB.motion.searchPosition.y = term.c.y; | |
| 1635 + stateVB.motion.searchPosition.yScr = term.scr; | |
| 1636 + stateVB.motion.finished = false; | |
| 1637 + goto finish; | |
| 1638 + case 'n': sign = 1; FALLTHROUGH | |
| 1639 + case 'N': { | |
| 1640 + if (stateVB.motion.search == none) return faile… | |
| 1641 + if (stateVB.motion.search == backward) { sign *… | |
| 1642 + bool b = true; int ox = term.c.x; | |
| 1643 + int oy = term.c.y ; int scr = term.scr; | |
| 1644 + int32_t i = max(stateVB.motion.amount, 1); | |
| 1645 + for (;i>0 && (b=gotoString(sign)); --i) { | |
| 1646 + oy = term.c.y; scr = term.scr; | |
| 1647 + } | |
| 1648 + if (!b) { term.c.x = ox; term.c.y = oy; term.sc… | |
| 1649 + goto motionFinish; | |
| 1650 + } | |
| 1651 + case 't': // Toggle selection mode and set dirt. | |
| 1652 + sel.type = sel.type == SEL_REGULAR | |
| 1653 + ? SEL_RECTANGULAR : SEL_REGULAR; | |
| 1654 + //tsetdirt(sel.nb.y, sel.ne.y); | |
| 1655 + goto motionFinish; | |
| 1656 + } | |
| 1657 + // Custom commands | |
| 1658 + for (size_t i = 0; i < amountNormalModeShortcuts; ++i) { | |
| 1659 + if (cs[0] == normalModeShortcuts[i].key) { | |
| 1660 + return pressKeys(normalModeShortcuts[i].value, | |
| 1661 + strlen(normalModeShortcuts[i].v… | |
| 1662 + ? success : failed; | |
| 1663 + } | |
| 1664 + } | |
| 1665 + return failed; | |
| 1666 +motionFinish: | |
| 1667 + stateVB.motion.amount = 0; | |
| 1668 + //if (isMotionFinished() && stateVB.command.op == yank) { | |
| 1669 + if (stateVB.command.op == yank) { | |
| 1670 + selextend(term.c.x, term.c.y, term.scr, sel.type, 0); | |
| 1671 + xsetsel(getsel()); | |
| 1672 + xclipcopy(); | |
| 1673 + exitCommand(); | |
| 1674 + } | |
| 1675 +finish: | |
| 1676 + if (len == 1 && !ctrl) { // XXX: for now. | |
| 1677 + EXPAND(kCommand, currentCommand, true) | |
| 1678 + utf8decode(cs, (Rune*)(kCommand), len); | |
| 1679 + } | |
| 1680 +finishNoAppend: | |
| 1681 + if (stateVB.command.op == visual) { | |
| 1682 + selextend(term.c.x, term.c.y, term.scr, sel.type, 0); | |
| 1683 + } else if (stateVB.command.op == visualLine) { | |
| 1684 + selextend(term.col-1, term.c.y, term.scr, sel.type, 0); | |
| 1685 + } | |
| 1686 + | |
| 1687 + if (previousScroll != term.scr && !isEmpty(&searchString)) { | |
| 1688 + highlightStringOnScreen(); | |
| 1689 + } | |
| 1690 + tsetdirt(0, term.row-3); //< Required because of the cursor cro… | |
| 1691 + printCommandString(); | |
| 1692 + printSearchString(); | |
| 1693 + return success; | |
| 1694 +} | |
| 1695 diff --git a/normalMode.h b/normalMode.h | |
| 1696 new file mode 100644 | |
| 1697 index 0000000..7d88259 | |
| 1698 --- /dev/null | |
| 1699 +++ b/normalMode.h | |
| 1700 @@ -0,0 +1,36 @@ | |
| 1701 +/* See LICENSE for license details. */ | |
| 1702 +#ifndef NORMAL_MODE_H | |
| 1703 +#define NORMAL_MODE_H | |
| 1704 + | |
| 1705 +#include <stdbool.h> | |
| 1706 +#include <stddef.h> | |
| 1707 +#include <stdint.h> | |
| 1708 + | |
| 1709 +/// Used in the configuration file to define custom shortcuts. | |
| 1710 +typedef struct NormalModeShortcuts { | |
| 1711 + char key; | |
| 1712 + char *value; | |
| 1713 +} NormalModeShortcuts; | |
| 1714 + | |
| 1715 +/// Holds the exit status of the #kpressNormalMode function, which info… | |
| 1716 +/// caller when to exit normal mode. | |
| 1717 +typedef enum ExitState { | |
| 1718 + failed = 0, | |
| 1719 + success = 1, | |
| 1720 + finished = 2, | |
| 1721 +} ExitState; | |
| 1722 + | |
| 1723 +/// Called when curr position is altered. | |
| 1724 +void onMove(void); | |
| 1725 + | |
| 1726 +/// Function which returns whether the value at position provided as ar… | |
| 1727 +/// is to be highlighted. | |
| 1728 +int highlighted(int, int); | |
| 1729 + | |
| 1730 +/// Handles keys in normal mode. | |
| 1731 +ExitState kpressNormalMode(char const * decoded, int len, bool ctrlPres… | |
| 1732 + void const * ksym); | |
| 1733 + //bool esc, bool enter, bool backspace, void* keysym); | |
| 1734 + | |
| 1735 + | |
| 1736 +#endif // NORMAL_MODE_H | |
| 1737 diff --git a/st.c b/st.c | |
| 1738 index 3e48410..d8bf7ab 100644 | |
| 1739 --- a/st.c | |
| 1740 +++ b/st.c | |
| 1741 @@ -1,8 +1,10 @@ | |
| 1742 /* See LICENSE for license details. */ | |
| 1743 +#include <assert.h> | |
| 1744 #include <ctype.h> | |
| 1745 #include <errno.h> | |
| 1746 #include <fcntl.h> | |
| 1747 #include <limits.h> | |
| 1748 +#include <math.h> | |
| 1749 #include <pwd.h> | |
| 1750 #include <stdarg.h> | |
| 1751 #include <stdio.h> | |
| 1752 @@ -17,6 +19,8 @@ | |
| 1753 #include <unistd.h> | |
| 1754 #include <wchar.h> | |
| 1755 | |
| 1756 + | |
| 1757 +#include "term.h" | |
| 1758 #include "st.h" | |
| 1759 #include "win.h" | |
| 1760 | |
| 1761 @@ -42,6 +46,7 @@ | |
| 1762 #define ISCONTROLC1(c) (BETWEEN(c, 0x80, 0x9f)) | |
| 1763 #define ISCONTROL(c) (ISCONTROLC0(c) || ISCONTROLC1(c)) | |
| 1764 #define ISDELIM(u) (u && wcschr(worddelimiters, u)) | |
| 1765 +#define INTERVAL(x, a, b) (x) < (a) ? (a) : (x) > (b) ? (b) : (x) | |
| 1766 | |
| 1767 enum term_mode { | |
| 1768 MODE_WRAP = 1 << 0, | |
| 1769 @@ -86,51 +91,6 @@ enum escape_state { | |
| 1770 ESC_DCS =128, | |
| 1771 }; | |
| 1772 | |
| 1773 -typedef struct { | |
| 1774 - Glyph attr; /* current char attributes */ | |
| 1775 - int x; | |
| 1776 - int y; | |
| 1777 - char state; | |
| 1778 -} TCursor; | |
| 1779 - | |
| 1780 -typedef struct { | |
| 1781 - int mode; | |
| 1782 - int type; | |
| 1783 - int snap; | |
| 1784 - /* | |
| 1785 - * Selection variables: | |
| 1786 - * nb – normalized coordinates of the beginning of the select… | |
| 1787 - * ne – normalized coordinates of the end of the selection | |
| 1788 - * ob – original coordinates of the beginning of the selection | |
| 1789 - * oe – original coordinates of the end of the selection | |
| 1790 - */ | |
| 1791 - struct { | |
| 1792 - int x, y; | |
| 1793 - } nb, ne, ob, oe; | |
| 1794 - | |
| 1795 - int alt; | |
| 1796 -} Selection; | |
| 1797 - | |
| 1798 -/* Internal representation of the screen */ | |
| 1799 -typedef struct { | |
| 1800 - int row; /* nb row */ | |
| 1801 - int col; /* nb col */ | |
| 1802 - Line *line; /* screen */ | |
| 1803 - Line *alt; /* alternate screen */ | |
| 1804 - int *dirty; /* dirtyness of lines */ | |
| 1805 - TCursor c; /* cursor */ | |
| 1806 - int ocx; /* old cursor col */ | |
| 1807 - int ocy; /* old cursor row */ | |
| 1808 - int top; /* top scroll limit */ | |
| 1809 - int bot; /* bottom scroll limit */ | |
| 1810 - int mode; /* terminal mode flags */ | |
| 1811 - int esc; /* escape state flags */ | |
| 1812 - char trantbl[4]; /* charset table translation */ | |
| 1813 - int charset; /* current charset */ | |
| 1814 - int icharset; /* selected charset for sequence */ | |
| 1815 - int *tabs; | |
| 1816 -} Term; | |
| 1817 - | |
| 1818 /* CSI Escape sequence structs */ | |
| 1819 /* ESC '[' [[ [<priv>] <arg> [;]] <mode> [<mode>]] */ | |
| 1820 typedef struct { | |
| 1821 @@ -153,6 +113,8 @@ typedef struct { | |
| 1822 int narg; /* nb of args */ | |
| 1823 } STREscape; | |
| 1824 | |
| 1825 +void tfulldirt(void); | |
| 1826 + | |
| 1827 static void execsh(char *, char **); | |
| 1828 static void stty(char **); | |
| 1829 static void sigchld(int); | |
| 1830 @@ -185,16 +147,14 @@ static void tnewline(int); | |
| 1831 static void tputtab(int); | |
| 1832 static void tputc(Rune); | |
| 1833 static void treset(void); | |
| 1834 -static void tscrollup(int, int); | |
| 1835 -static void tscrolldown(int, int); | |
| 1836 +static void tscrollup(int, int, int); | |
| 1837 +static void tscrolldown(int, int, int); | |
| 1838 static void tsetattr(int *, int); | |
| 1839 static void tsetchar(Rune, Glyph *, int, int); | |
| 1840 -static void tsetdirt(int, int); | |
| 1841 static void tsetscroll(int, int); | |
| 1842 static void tswapscreen(void); | |
| 1843 static void tsetmode(int, int, int *, int); | |
| 1844 static int twrite(const char *, int, int); | |
| 1845 -static void tfulldirt(void); | |
| 1846 static void tcontrolcode(uchar ); | |
| 1847 static void tdectest(char ); | |
| 1848 static void tdefutf8(char); | |
| 1849 @@ -208,8 +168,6 @@ static void selnormalize(void); | |
| 1850 static void selscroll(int, int); | |
| 1851 static void selsnap(int *, int *, int); | |
| 1852 | |
| 1853 -static size_t utf8decode(const char *, Rune *, size_t); | |
| 1854 -static Rune utf8decodebyte(char, size_t *); | |
| 1855 static char utf8encodebyte(Rune, size_t); | |
| 1856 static size_t utf8validate(Rune *, size_t); | |
| 1857 | |
| 1858 @@ -219,8 +177,8 @@ static char base64dec_getc(const char **); | |
| 1859 static ssize_t xwrite(int, const char *, size_t); | |
| 1860 | |
| 1861 /* Globals */ | |
| 1862 -static Term term; | |
| 1863 -static Selection sel; | |
| 1864 +Term term; | |
| 1865 +Selection sel; | |
| 1866 static CSIEscape csiescseq; | |
| 1867 static STREscape strescseq; | |
| 1868 static int iofd = 1; | |
| 1869 @@ -414,17 +372,22 @@ tlinelen(int y) | |
| 1870 { | |
| 1871 int i = term.col; | |
| 1872 | |
| 1873 - if (term.line[y][i - 1].mode & ATTR_WRAP) | |
| 1874 + if (TLINE(y)[i - 1].mode & ATTR_WRAP) | |
| 1875 return i; | |
| 1876 | |
| 1877 - while (i > 0 && term.line[y][i - 1].u == ' ') | |
| 1878 + while (i > 0 && TLINE(y)[i - 1].u == ' ') | |
| 1879 --i; | |
| 1880 | |
| 1881 return i; | |
| 1882 } | |
| 1883 | |
| 1884 void | |
| 1885 -selstart(int col, int row, int snap) | |
| 1886 +xselstart(int col, int row, int snap) { | |
| 1887 + selstart(col, row, term.scr, snap); | |
| 1888 +} | |
| 1889 + | |
| 1890 +void | |
| 1891 +selstart(int col, int row, int scroll, int snap) | |
| 1892 { | |
| 1893 selclear(); | |
| 1894 sel.mode = SEL_EMPTY; | |
| 1895 @@ -433,6 +396,7 @@ selstart(int col, int row, int snap) | |
| 1896 sel.snap = snap; | |
| 1897 sel.oe.x = sel.ob.x = col; | |
| 1898 sel.oe.y = sel.ob.y = row; | |
| 1899 + sel.oe.scroll = sel.ob.scroll = scroll; | |
| 1900 selnormalize(); | |
| 1901 | |
| 1902 if (sel.snap != 0) | |
| 1903 @@ -441,10 +405,13 @@ selstart(int col, int row, int snap) | |
| 1904 } | |
| 1905 | |
| 1906 void | |
| 1907 -selextend(int col, int row, int type, int done) | |
| 1908 -{ | |
| 1909 - int oldey, oldex, oldsby, oldsey, oldtype; | |
| 1910 +xselextend(int col, int row, int type, int done) { | |
| 1911 + selextend(col, row, term.scr, type, done); | |
| 1912 +} | |
| 1913 | |
| 1914 +void | |
| 1915 +selextend(int col, int row, int scroll, int type, int done) | |
| 1916 +{ | |
| 1917 if (sel.mode == SEL_IDLE) | |
| 1918 return; | |
| 1919 if (done && sel.mode == SEL_EMPTY) { | |
| 1920 @@ -452,18 +419,22 @@ selextend(int col, int row, int type, int done) | |
| 1921 return; | |
| 1922 } | |
| 1923 | |
| 1924 - oldey = sel.oe.y; | |
| 1925 - oldex = sel.oe.x; | |
| 1926 - oldsby = sel.nb.y; | |
| 1927 - oldsey = sel.ne.y; | |
| 1928 - oldtype = sel.type; | |
| 1929 + int const oldey = sel.oe.y; | |
| 1930 + int const oldex = sel.oe.x; | |
| 1931 + int const oldscroll = sel.oe.scroll; | |
| 1932 + int const oldsby = sel.nb.y; | |
| 1933 + int const oldsey = sel.ne.y; | |
| 1934 + int const oldtype = sel.type; | |
| 1935 | |
| 1936 sel.oe.x = col; | |
| 1937 sel.oe.y = row; | |
| 1938 + sel.oe.scroll = scroll; | |
| 1939 + | |
| 1940 selnormalize(); | |
| 1941 sel.type = type; | |
| 1942 | |
| 1943 - if (oldey != sel.oe.y || oldex != sel.oe.x || oldtype != sel.ty… | |
| 1944 + if (oldey != sel.oe.y || oldex != sel.oe.x || oldscroll != sel.… | |
| 1945 + || oldtype != sel.type || sel.mode == SEL_EMPTY) | |
| 1946 tsetdirt(MIN(sel.nb.y, oldsby), MAX(sel.ne.y, oldsey)); | |
| 1947 | |
| 1948 sel.mode = done ? SEL_IDLE : SEL_READY; | |
| 1949 @@ -472,17 +443,21 @@ selextend(int col, int row, int type, int done) | |
| 1950 void | |
| 1951 selnormalize(void) | |
| 1952 { | |
| 1953 - int i; | |
| 1954 - | |
| 1955 - if (sel.type == SEL_REGULAR && sel.ob.y != sel.oe.y) { | |
| 1956 - sel.nb.x = sel.ob.y < sel.oe.y ? sel.ob.x : sel.oe.x; | |
| 1957 - sel.ne.x = sel.ob.y < sel.oe.y ? sel.oe.x : sel.ob.x; | |
| 1958 + sel.nb.y = INTERVAL(sel.ob.y + term.scr - sel.ob.scroll, 0, ter… | |
| 1959 + sel.ne.y = INTERVAL(sel.oe.y + term.scr - sel.oe.scroll, 0, ter… | |
| 1960 + if (sel.type == SEL_REGULAR && sel.nb.y != sel.ne.y) { | |
| 1961 + sel.nb.x = sel.nb.y < sel.ne.y ? sel.ob.x : sel.oe.x; | |
| 1962 + sel.ne.x = sel.nb.y < sel.ne.y ? sel.oe.x : sel.ob.x; | |
| 1963 } else { | |
| 1964 sel.nb.x = MIN(sel.ob.x, sel.oe.x); | |
| 1965 sel.ne.x = MAX(sel.ob.x, sel.oe.x); | |
| 1966 } | |
| 1967 - sel.nb.y = MIN(sel.ob.y, sel.oe.y); | |
| 1968 - sel.ne.y = MAX(sel.ob.y, sel.oe.y); | |
| 1969 + | |
| 1970 + if (sel.nb.y > sel.ne.y) { | |
| 1971 + int32_t const tmp = sel.nb.y; | |
| 1972 + sel.nb.y = sel.ne.y; | |
| 1973 + sel.ne.y = tmp; | |
| 1974 + } | |
| 1975 | |
| 1976 selsnap(&sel.nb.x, &sel.nb.y, -1); | |
| 1977 selsnap(&sel.ne.x, &sel.ne.y, +1); | |
| 1978 @@ -490,7 +465,7 @@ selnormalize(void) | |
| 1979 /* expand selection over line breaks */ | |
| 1980 if (sel.type == SEL_RECTANGULAR) | |
| 1981 return; | |
| 1982 - i = tlinelen(sel.nb.y); | |
| 1983 + int i = tlinelen(sel.nb.y); | |
| 1984 if (i < sel.nb.x) | |
| 1985 sel.nb.x = i; | |
| 1986 if (tlinelen(sel.ne.y) <= sel.ne.x) | |
| 1987 @@ -526,7 +501,7 @@ selsnap(int *x, int *y, int direction) | |
| 1988 * Snap around if the word wraps around at the end or | |
| 1989 * beginning of a line. | |
| 1990 */ | |
| 1991 - prevgp = &term.line[*y][*x]; | |
| 1992 + prevgp = &TLINE(*y)[*x]; | |
| 1993 prevdelim = ISDELIM(prevgp->u); | |
| 1994 for (;;) { | |
| 1995 newx = *x + direction; | |
| 1996 @@ -541,14 +516,14 @@ selsnap(int *x, int *y, int direction) | |
| 1997 yt = *y, xt = *x; | |
| 1998 else | |
| 1999 yt = newy, xt = newx; | |
| 2000 - if (!(term.line[yt][xt].mode & ATTR_WRA… | |
| 2001 + if (!(TLINE(yt)[xt].mode & ATTR_WRAP)) | |
| 2002 break; | |
| 2003 } | |
| 2004 | |
| 2005 if (newx >= tlinelen(newy)) | |
| 2006 break; | |
| 2007 | |
| 2008 - gp = &term.line[newy][newx]; | |
| 2009 + gp = &TLINE(newy)[newx]; | |
| 2010 delim = ISDELIM(gp->u); | |
| 2011 if (!(gp->mode & ATTR_WDUMMY) && (delim != prev… | |
| 2012 || (delim && gp->u != prevgp->u… | |
| 2013 @@ -569,14 +544,14 @@ selsnap(int *x, int *y, int direction) | |
| 2014 *x = (direction < 0) ? 0 : term.col - 1; | |
| 2015 if (direction < 0) { | |
| 2016 for (; *y > 0; *y += direction) { | |
| 2017 - if (!(term.line[*y-1][term.col-1].mode | |
| 2018 + if (!(TLINE(*y-1)[term.col-1].mode | |
| 2019 & ATTR_WRAP)) { | |
| 2020 break; | |
| 2021 } | |
| 2022 } | |
| 2023 } else if (direction > 0) { | |
| 2024 for (; *y < term.row-1; *y += direction) { | |
| 2025 - if (!(term.line[*y][term.col-1].mode | |
| 2026 + if (!(TLINE(*y)[term.col-1].mode | |
| 2027 & ATTR_WRAP)) { | |
| 2028 break; | |
| 2029 } | |
| 2030 @@ -596,24 +571,32 @@ getsel(void) | |
| 2031 if (sel.ob.x == -1) | |
| 2032 return NULL; | |
| 2033 | |
| 2034 - bufsize = (term.col+1) * (sel.ne.y-sel.nb.y+1) * UTF_SIZ; | |
| 2035 + int32_t syb = sel.ob.y - sel.ob.scroll + term.scr; | |
| 2036 + int32_t sye = sel.oe.y - sel.oe.scroll + term.scr; | |
| 2037 + if (syb > sye) { | |
| 2038 + int32_t tmp = sye; | |
| 2039 + sye = syb; | |
| 2040 + syb = tmp; | |
| 2041 + } | |
| 2042 + | |
| 2043 + bufsize = (term.col+1) * (sye - syb + 1) * UTF_SIZ; | |
| 2044 ptr = str = xmalloc(bufsize); | |
| 2045 | |
| 2046 /* append every set & selected glyph to the selection */ | |
| 2047 - for (y = sel.nb.y; y <= sel.ne.y; y++) { | |
| 2048 + for (y = syb; y <= sye; y++) { | |
| 2049 if ((linelen = tlinelen(y)) == 0) { | |
| 2050 *ptr++ = '\n'; | |
| 2051 continue; | |
| 2052 } | |
| 2053 | |
| 2054 if (sel.type == SEL_RECTANGULAR) { | |
| 2055 - gp = &term.line[y][sel.nb.x]; | |
| 2056 + gp = &TLINE(y)[sel.nb.x]; | |
| 2057 lastx = sel.ne.x; | |
| 2058 } else { | |
| 2059 - gp = &term.line[y][sel.nb.y == y ? sel.nb.x : 0… | |
| 2060 - lastx = (sel.ne.y == y) ? sel.ne.x : term.col-1; | |
| 2061 + gp = &TLINE(y)[syb == y ? sel.nb.x : 0]; | |
| 2062 + lastx = (sye == y) ? sel.ne.x : term.col-1; | |
| 2063 } | |
| 2064 - last = &term.line[y][MIN(lastx, linelen-1)]; | |
| 2065 + last = &TLINE(y)[MIN(lastx, linelen-1)]; | |
| 2066 while (last >= gp && last->u == ' ') | |
| 2067 --last; | |
| 2068 | |
| 2069 @@ -836,6 +819,9 @@ void | |
| 2070 ttywrite(const char *s, size_t n, int may_echo) | |
| 2071 { | |
| 2072 const char *next; | |
| 2073 + Arg arg = (Arg) { .i = term.scr }; | |
| 2074 + | |
| 2075 + kscrolldown(&arg); | |
| 2076 | |
| 2077 if (may_echo && IS_SET(MODE_ECHO)) | |
| 2078 twrite(s, n, 1); | |
| 2079 @@ -1047,13 +1033,53 @@ tswapscreen(void) | |
| 2080 } | |
| 2081 | |
| 2082 void | |
| 2083 -tscrolldown(int orig, int n) | |
| 2084 +kscrolldown(const Arg* a) | |
| 2085 +{ | |
| 2086 + int n = a->i; | |
| 2087 + | |
| 2088 + if (n < 0) | |
| 2089 + n = term.row + n; | |
| 2090 + | |
| 2091 + if (n > term.scr) | |
| 2092 + n = term.scr; | |
| 2093 + | |
| 2094 + if (term.scr > 0) { | |
| 2095 + term.scr -= n; | |
| 2096 + selscroll(0, -n); | |
| 2097 + tfulldirt(); | |
| 2098 + } | |
| 2099 +} | |
| 2100 + | |
| 2101 +void | |
| 2102 +kscrollup(const Arg* a) | |
| 2103 +{ | |
| 2104 + int n = a->i; | |
| 2105 + | |
| 2106 + if (n < 0) | |
| 2107 + n = term.row + n; | |
| 2108 + | |
| 2109 + if (term.scr <= HISTSIZE-n) { | |
| 2110 + term.scr += n; | |
| 2111 + selscroll(0, n); | |
| 2112 + tfulldirt(); | |
| 2113 + } | |
| 2114 +} | |
| 2115 + | |
| 2116 +void | |
| 2117 +tscrolldown(int orig, int n, int copyhist) | |
| 2118 { | |
| 2119 int i; | |
| 2120 Line temp; | |
| 2121 | |
| 2122 LIMIT(n, 0, term.bot-orig+1); | |
| 2123 | |
| 2124 + if (copyhist) { | |
| 2125 + term.histi = (term.histi - 1 + HISTSIZE) % HISTSIZE; | |
| 2126 + temp = term.hist[term.histi]; | |
| 2127 + term.hist[term.histi] = term.line[term.bot]; | |
| 2128 + term.line[term.bot] = temp; | |
| 2129 + } | |
| 2130 + | |
| 2131 tsetdirt(orig, term.bot-n); | |
| 2132 tclearregion(0, term.bot-n+1, term.col-1, term.bot); | |
| 2133 | |
| 2134 @@ -1067,13 +1093,23 @@ tscrolldown(int orig, int n) | |
| 2135 } | |
| 2136 | |
| 2137 void | |
| 2138 -tscrollup(int orig, int n) | |
| 2139 +tscrollup(int orig, int n, int copyhist) | |
| 2140 { | |
| 2141 int i; | |
| 2142 Line temp; | |
| 2143 | |
| 2144 LIMIT(n, 0, term.bot-orig+1); | |
| 2145 | |
| 2146 + if (copyhist) { | |
| 2147 + term.histi = (term.histi + 1) % HISTSIZE; | |
| 2148 + temp = term.hist[term.histi]; | |
| 2149 + term.hist[term.histi] = term.line[orig]; | |
| 2150 + term.line[orig] = temp; | |
| 2151 + } | |
| 2152 + | |
| 2153 + if (term.scr > 0 && term.scr < HISTSIZE) | |
| 2154 + term.scr = MIN(term.scr + n, HISTSIZE-1); | |
| 2155 + | |
| 2156 tclearregion(0, orig, term.col-1, orig+n-1); | |
| 2157 tsetdirt(orig+n, term.bot); | |
| 2158 | |
| 2159 @@ -1093,6 +1129,7 @@ selscroll(int orig, int n) | |
| 2160 return; | |
| 2161 | |
| 2162 if (BETWEEN(sel.ob.y, orig, term.bot) || BETWEEN(sel.oe.y, orig… | |
| 2163 + sel.oe.scroll = sel.ob.scroll = term.scr; | |
| 2164 if ((sel.ob.y += n) > term.bot || (sel.oe.y += n) < ter… | |
| 2165 selclear(); | |
| 2166 return; | |
| 2167 @@ -1122,13 +1159,19 @@ tnewline(int first_col) | |
| 2168 int y = term.c.y; | |
| 2169 | |
| 2170 if (y == term.bot) { | |
| 2171 - tscrollup(term.top, 1); | |
| 2172 + tscrollup(term.top, 1, 1); | |
| 2173 } else { | |
| 2174 y++; | |
| 2175 } | |
| 2176 tmoveto(first_col ? 0 : term.c.x, y); | |
| 2177 } | |
| 2178 | |
| 2179 +int | |
| 2180 +currentLine(int x, int y) | |
| 2181 +{ | |
| 2182 + return (x == term.c.x || y == term.c.y); | |
| 2183 +} | |
| 2184 + | |
| 2185 void | |
| 2186 csiparse(void) | |
| 2187 { | |
| 2188 @@ -1181,6 +1224,8 @@ tmoveto(int x, int y) | |
| 2189 term.c.state &= ~CURSOR_WRAPNEXT; | |
| 2190 term.c.x = LIMIT(x, 0, term.col-1); | |
| 2191 term.c.y = LIMIT(y, miny, maxy); | |
| 2192 + // Set the last position in order to restore after normal mode … | |
| 2193 + onMove(); | |
| 2194 } | |
| 2195 | |
| 2196 void | |
| 2197 @@ -1287,14 +1332,14 @@ void | |
| 2198 tinsertblankline(int n) | |
| 2199 { | |
| 2200 if (BETWEEN(term.c.y, term.top, term.bot)) | |
| 2201 - tscrolldown(term.c.y, n); | |
| 2202 + tscrolldown(term.c.y, n, 0); | |
| 2203 } | |
| 2204 | |
| 2205 void | |
| 2206 tdeleteline(int n) | |
| 2207 { | |
| 2208 if (BETWEEN(term.c.y, term.top, term.bot)) | |
| 2209 - tscrollup(term.c.y, n); | |
| 2210 + tscrollup(term.c.y, n, 0); | |
| 2211 } | |
| 2212 | |
| 2213 int32_t | |
| 2214 @@ -1725,11 +1770,11 @@ csihandle(void) | |
| 2215 break; | |
| 2216 case 'S': /* SU -- Scroll <n> line up */ | |
| 2217 DEFAULT(csiescseq.arg[0], 1); | |
| 2218 - tscrollup(term.top, csiescseq.arg[0]); | |
| 2219 + tscrollup(term.top, csiescseq.arg[0], 0); | |
| 2220 break; | |
| 2221 case 'T': /* SD -- Scroll <n> line down */ | |
| 2222 DEFAULT(csiescseq.arg[0], 1); | |
| 2223 - tscrolldown(term.top, csiescseq.arg[0]); | |
| 2224 + tscrolldown(term.top, csiescseq.arg[0], 0); | |
| 2225 break; | |
| 2226 case 'L': /* IL -- Insert <n> blank lines */ | |
| 2227 DEFAULT(csiescseq.arg[0], 1); | |
| 2228 @@ -2235,7 +2280,7 @@ eschandle(uchar ascii) | |
| 2229 return 0; | |
| 2230 case 'D': /* IND -- Linefeed */ | |
| 2231 if (term.c.y == term.bot) { | |
| 2232 - tscrollup(term.top, 1); | |
| 2233 + tscrollup(term.top, 1, 1); | |
| 2234 } else { | |
| 2235 tmoveto(term.c.x, term.c.y+1); | |
| 2236 } | |
| 2237 @@ -2248,7 +2293,7 @@ eschandle(uchar ascii) | |
| 2238 break; | |
| 2239 case 'M': /* RI -- Reverse index */ | |
| 2240 if (term.c.y == term.top) { | |
| 2241 - tscrolldown(term.top, 1); | |
| 2242 + tscrolldown(term.top, 1, 1); | |
| 2243 } else { | |
| 2244 tmoveto(term.c.x, term.c.y-1); | |
| 2245 } | |
| 2246 @@ -2290,7 +2335,7 @@ tputc(Rune u) | |
| 2247 { | |
| 2248 char c[UTF_SIZ]; | |
| 2249 int control; | |
| 2250 - int width, len; | |
| 2251 + int width = 0, len; | |
| 2252 Glyph *gp; | |
| 2253 | |
| 2254 control = ISCONTROL(u); | |
| 2255 @@ -2469,7 +2514,7 @@ twrite(const char *buf, int buflen, int show_ctrl) | |
| 2256 void | |
| 2257 tresize(int col, int row) | |
| 2258 { | |
| 2259 - int i; | |
| 2260 + int i, j; | |
| 2261 int minrow = MIN(row, term.row); | |
| 2262 int mincol = MIN(col, term.col); | |
| 2263 int *bp; | |
| 2264 @@ -2506,6 +2551,14 @@ tresize(int col, int row) | |
| 2265 term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); | |
| 2266 term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); | |
| 2267 | |
| 2268 + for (i = 0; i < HISTSIZE; i++) { | |
| 2269 + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyp… | |
| 2270 + for (j = mincol; j < col; j++) { | |
| 2271 + term.hist[i][j] = term.c.attr; | |
| 2272 + term.hist[i][j].u = ' '; | |
| 2273 + } | |
| 2274 + } | |
| 2275 + | |
| 2276 /* resize each row to new width, zero-pad if needed */ | |
| 2277 for (i = 0; i < minrow; i++) { | |
| 2278 term.line[i] = xrealloc(term.line[i], col * sizeof(Glyp… | |
| 2279 @@ -2563,7 +2616,7 @@ drawregion(int x1, int y1, int x2, int y2) | |
| 2280 continue; | |
| 2281 | |
| 2282 term.dirty[y] = 0; | |
| 2283 - xdrawline(term.line[y], x1, y, x2); | |
| 2284 + xdrawline(TLINE(y), x1, y, x2); | |
| 2285 } | |
| 2286 } | |
| 2287 | |
| 2288 @@ -2584,8 +2637,8 @@ draw(void) | |
| 2289 cx--; | |
| 2290 | |
| 2291 drawregion(0, 0, term.col, term.row); | |
| 2292 - xdrawcursor(cx, term.c.y, term.line[term.c.y][cx], | |
| 2293 - term.ocx, term.ocy, term.line[term.ocy][term.oc… | |
| 2294 + xdrawcursor(cx, term.c.y, TLINE(term.c.y)[cx], | |
| 2295 + term.ocx, term.ocy, TLINE(term.ocy)[term.ocx]); | |
| 2296 term.ocx = cx, term.ocy = term.c.y; | |
| 2297 xfinishdraw(); | |
| 2298 xximspot(term.ocx, term.ocy); | |
| 2299 diff --git a/st.h b/st.h | |
| 2300 index a1928ca..3aad9f3 100644 | |
| 2301 --- a/st.h | |
| 2302 +++ b/st.h | |
| 2303 @@ -1,5 +1,8 @@ | |
| 2304 /* See LICENSE for license details. */ | |
| 2305 | |
| 2306 +#include "glyph.h" | |
| 2307 +#include "normalMode.h" | |
| 2308 + | |
| 2309 #include <stdint.h> | |
| 2310 #include <sys/types.h> | |
| 2311 | |
| 2312 @@ -33,6 +36,8 @@ enum glyph_attribute { | |
| 2313 ATTR_WRAP = 1 << 8, | |
| 2314 ATTR_WIDE = 1 << 9, | |
| 2315 ATTR_WDUMMY = 1 << 10, | |
| 2316 + ATTR_HIGHLIGHT = 1 << 12, | |
| 2317 + ATTR_CURRENT = 1 << 13, | |
| 2318 ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT, | |
| 2319 }; | |
| 2320 | |
| 2321 @@ -42,11 +47,6 @@ enum selection_mode { | |
| 2322 SEL_READY = 2 | |
| 2323 }; | |
| 2324 | |
| 2325 -enum selection_type { | |
| 2326 - SEL_REGULAR = 1, | |
| 2327 - SEL_RECTANGULAR = 2 | |
| 2328 -}; | |
| 2329 - | |
| 2330 enum selection_snap { | |
| 2331 SNAP_WORD = 1, | |
| 2332 SNAP_LINE = 2 | |
| 2333 @@ -57,18 +57,6 @@ typedef unsigned int uint; | |
| 2334 typedef unsigned long ulong; | |
| 2335 typedef unsigned short ushort; | |
| 2336 | |
| 2337 -typedef uint_least32_t Rune; | |
| 2338 - | |
| 2339 -#define Glyph Glyph_ | |
| 2340 -typedef struct { | |
| 2341 - Rune u; /* character code */ | |
| 2342 - ushort mode; /* attribute flags */ | |
| 2343 - uint32_t fg; /* foreground */ | |
| 2344 - uint32_t bg; /* background */ | |
| 2345 -} Glyph; | |
| 2346 - | |
| 2347 -typedef Glyph *Line; | |
| 2348 - | |
| 2349 typedef union { | |
| 2350 int i; | |
| 2351 uint ui; | |
| 2352 @@ -81,6 +69,11 @@ void die(const char *, ...); | |
| 2353 void redraw(void); | |
| 2354 void draw(void); | |
| 2355 | |
| 2356 +int currentLine(int, int); | |
| 2357 +void kscrolldown(const Arg *); | |
| 2358 +void kscrollup(const Arg *); | |
| 2359 +void normalMode(Arg const *); | |
| 2360 + | |
| 2361 void printscreen(const Arg *); | |
| 2362 void printsel(const Arg *); | |
| 2363 void sendbreak(const Arg *); | |
| 2364 @@ -90,6 +83,9 @@ int tattrset(int); | |
| 2365 void tnew(int, int); | |
| 2366 void tresize(int, int); | |
| 2367 void tsetdirtattr(int); | |
| 2368 +size_t utf8decode(const char *, Rune *, size_t); | |
| 2369 +Rune utf8decodebyte(char, size_t *); | |
| 2370 +void tsetdirt(int, int); | |
| 2371 void ttyhangup(void); | |
| 2372 int ttynew(char *, char *, char *, char **); | |
| 2373 size_t ttyread(void); | |
| 2374 @@ -100,8 +96,10 @@ void resettitle(void); | |
| 2375 | |
| 2376 void selclear(void); | |
| 2377 void selinit(void); | |
| 2378 -void selstart(int, int, int); | |
| 2379 -void selextend(int, int, int, int); | |
| 2380 +void selstart(int, int, int, int); | |
| 2381 +void xselstart(int, int, int); | |
| 2382 +void selextend(int, int, int, int, int); | |
| 2383 +void xselextend(int, int, int, int); | |
| 2384 int selected(int, int); | |
| 2385 char *getsel(void); | |
| 2386 | |
| 2387 diff --git a/term.h b/term.h | |
| 2388 new file mode 100644 | |
| 2389 index 0000000..23adf0e | |
| 2390 --- /dev/null | |
| 2391 +++ b/term.h | |
| 2392 @@ -0,0 +1,73 @@ | |
| 2393 +#ifndef TERM_H | |
| 2394 +#define TERM_H | |
| 2395 + | |
| 2396 +// | |
| 2397 +// Internal terminal structs. | |
| 2398 +// | |
| 2399 + | |
| 2400 +#include "glyph.h" | |
| 2401 + | |
| 2402 +#include <stdint.h> | |
| 2403 + | |
| 2404 +#define HISTSIZE 2500 | |
| 2405 + | |
| 2406 +typedef struct { | |
| 2407 + Glyph attr; /* current char attributes */ | |
| 2408 + int x; | |
| 2409 + int y; | |
| 2410 + char state; | |
| 2411 +} TCursor; | |
| 2412 + | |
| 2413 +typedef struct { | |
| 2414 + int mode; | |
| 2415 + int type; | |
| 2416 + int snap; | |
| 2417 + /// Selection variables: | |
| 2418 + /// ob – original coordinates of the beginning of the selecti… | |
| 2419 + /// oe – original coordinates of the end of the selection | |
| 2420 + struct { | |
| 2421 + int x, y, scroll; | |
| 2422 + } ob, oe; | |
| 2423 + /// Selection variables; currently displayed chunk. | |
| 2424 + /// nb – normalized coordinates of the beginning of the selec… | |
| 2425 + /// ne – normalized coordinates of the end of the selection | |
| 2426 + struct { | |
| 2427 + int x, y; | |
| 2428 + } nb, ne; | |
| 2429 + | |
| 2430 + int alt; | |
| 2431 +} Selection; | |
| 2432 + | |
| 2433 +/* Internal representation of the screen */ | |
| 2434 +typedef struct { | |
| 2435 + int row; /* nb row */ | |
| 2436 + int col; /* nb col */ | |
| 2437 + Line *line; /* screen */ | |
| 2438 + Line *alt; /* alternate screen */ | |
| 2439 + Line hist[HISTSIZE]; /* history buffer */ | |
| 2440 + int histi; /* history index */ | |
| 2441 + int scr; /* scroll back */ | |
| 2442 + int *dirty; /* dirtyness of lines */ | |
| 2443 + TCursor c; /* cursor */ | |
| 2444 + int ocx; /* old cursor col */ | |
| 2445 + int ocy; /* old cursor row */ | |
| 2446 + int top; /* top scroll limit */ | |
| 2447 + int bot; /* bottom scroll limit */ | |
| 2448 + int mode; /* terminal mode flags */ | |
| 2449 + int esc; /* escape state flags */ | |
| 2450 + char trantbl[4]; /* charset table translation */ | |
| 2451 + int charset; /* current charset */ | |
| 2452 + int icharset; /* selected charset for sequence */ | |
| 2453 + int *tabs; | |
| 2454 +} Term; | |
| 2455 + | |
| 2456 +extern Term term; | |
| 2457 + | |
| 2458 +#define TLINE(y) ((y) < term.scr ? term.hist[((y) + term.histi - \ | |
| 2459 + term.scr + HISTSIZE + 1) % HISTSIZE] : \ | |
| 2460 + term.line[(y) - term.scr]) | |
| 2461 + | |
| 2462 +extern Selection sel; | |
| 2463 + | |
| 2464 + | |
| 2465 +#endif // TERM_H | |
| 2466 diff --git a/win.h b/win.h | |
| 2467 index a6ef1b9..1a6fefe 100644 | |
| 2468 --- a/win.h | |
| 2469 +++ b/win.h | |
| 2470 @@ -19,6 +19,7 @@ enum win_mode { | |
| 2471 MODE_MOUSEMANY = 1 << 15, | |
| 2472 MODE_BRCKTPASTE = 1 << 16, | |
| 2473 MODE_NUMLOCK = 1 << 17, | |
| 2474 + MODE_NORMAL = 1 << 18, | |
| 2475 MODE_MOUSE = MODE_MOUSEBTN|MODE_MOUSEMOTION|MODE_MOUSEX10\ | |
| 2476 |MODE_MOUSEMANY, | |
| 2477 }; | |
| 2478 @@ -27,6 +28,7 @@ void xbell(void); | |
| 2479 void xclipcopy(void); | |
| 2480 void xdrawcursor(int, int, Glyph, int, int, Glyph); | |
| 2481 void xdrawline(Line, int, int, int); | |
| 2482 +void xdrawglyph(Glyph, int, int); | |
| 2483 void xfinishdraw(void); | |
| 2484 void xloadcols(void); | |
| 2485 int xsetcolorname(int, const char *); | |
| 2486 diff --git a/x.c b/x.c | |
| 2487 index 1f62129..e297946 100644 | |
| 2488 --- a/x.c | |
| 2489 +++ b/x.c | |
| 2490 @@ -143,7 +143,6 @@ typedef struct { | |
| 2491 static inline ushort sixd_to_16bit(int); | |
| 2492 static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, … | |
| 2493 static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, i… | |
| 2494 -static void xdrawglyph(Glyph, int, int); | |
| 2495 static void xclear(int, int, int, int); | |
| 2496 static int xgeommasktogravity(int); | |
| 2497 static int ximopen(Display *); | |
| 2498 @@ -355,7 +354,7 @@ mousesel(XEvent *e, int done) | |
| 2499 break; | |
| 2500 } | |
| 2501 } | |
| 2502 - selextend(evcol(e), evrow(e), seltype, done); | |
| 2503 + xselextend(evcol(e), evrow(e), seltype, done); | |
| 2504 if (done) | |
| 2505 setsel(getsel(), e->xbutton.time); | |
| 2506 } | |
| 2507 @@ -471,7 +470,7 @@ bpress(XEvent *e) | |
| 2508 xsel.tclick2 = xsel.tclick1; | |
| 2509 xsel.tclick1 = now; | |
| 2510 | |
| 2511 - selstart(evcol(e), evrow(e), snap); | |
| 2512 + xselstart(evcol(e), evrow(e), snap); | |
| 2513 } | |
| 2514 } | |
| 2515 | |
| 2516 @@ -757,6 +756,13 @@ xloadcolor(int i, const char *name, Color *ncolor) | |
| 2517 return XftColorAllocName(xw.dpy, xw.vis, xw.cmap, name, ncolor); | |
| 2518 } | |
| 2519 | |
| 2520 +void | |
| 2521 +normalMode(Arg const *_) { | |
| 2522 + (void) _; | |
| 2523 + win.mode ^= MODE_NORMAL; | |
| 2524 +} | |
| 2525 + | |
| 2526 + | |
| 2527 void | |
| 2528 xloadcols(void) | |
| 2529 { | |
| 2530 @@ -1344,6 +1350,14 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs… | |
| 2531 base.fg = defaultattr; | |
| 2532 } | |
| 2533 | |
| 2534 + if (base.mode & ATTR_HIGHLIGHT) { | |
| 2535 + base.bg = highlightBg; | |
| 2536 + base.fg = highlightFg; | |
| 2537 + } else if ((base.mode & ATTR_CURRENT) && (win.mode & MODE_NORMA… | |
| 2538 + base.bg = currentBg; | |
| 2539 + base.fg = currentFg; | |
| 2540 + } | |
| 2541 + | |
| 2542 if (IS_TRUECOL(base.fg)) { | |
| 2543 colfg.alpha = 0xffff; | |
| 2544 colfg.red = TRUERED(base.fg); | |
| 2545 @@ -1433,7 +1447,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs,… | |
| 2546 xclear(winx, winy + win.ch, winx + width, win.h); | |
| 2547 | |
| 2548 /* Clean up the region we want to draw to. */ | |
| 2549 - XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); | |
| 2550 + XftDrawRect(xw.draw, bg, winx, winy, width, win.ch); | |
| 2551 | |
| 2552 /* Set the clip region because Xft is sometimes dirty. */ | |
| 2553 r.x = 0; | |
| 2554 @@ -1476,8 +1490,9 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int o… | |
| 2555 Color drawcol; | |
| 2556 | |
| 2557 /* remove the old cursor */ | |
| 2558 - if (selected(ox, oy)) | |
| 2559 - og.mode ^= ATTR_REVERSE; | |
| 2560 + if (selected(ox, oy)) og.mode ^= ATTR_REVERSE; | |
| 2561 + if (highlighted(ox, oy)) { og.mode ^= ATTR_HIGHLIGHT; } | |
| 2562 + if (currentLine(ox, oy)) { og.mode ^= ATTR_CURRENT; } | |
| 2563 xdrawglyph(og, ox, oy); | |
| 2564 | |
| 2565 if (IS_SET(MODE_HIDE)) | |
| 2566 @@ -1509,6 +1524,11 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int … | |
| 2567 drawcol = dc.col[g.bg]; | |
| 2568 } | |
| 2569 | |
| 2570 + if ((g.mode & ATTR_CURRENT) && (win.mode & MODE_NORMAL)) { | |
| 2571 + g.bg = currentBg; | |
| 2572 + g.fg = currentFg; | |
| 2573 + } | |
| 2574 + | |
| 2575 /* draw the new one */ | |
| 2576 if (IS_SET(MODE_FOCUSED)) { | |
| 2577 switch (win.cursor) { | |
| 2578 @@ -1592,12 +1612,18 @@ xdrawline(Line line, int x1, int y1, int x2) | |
| 2579 | |
| 2580 numspecs = xmakeglyphfontspecs(specs, &line[x1], x2 - x1, x1, y… | |
| 2581 i = ox = 0; | |
| 2582 - for (x = x1; x < x2 && i < numspecs; x++) { | |
| 2583 + for (x = x1; x < x2 && i < numspecs; ++x) { | |
| 2584 new = line[x]; | |
| 2585 if (new.mode == ATTR_WDUMMY) | |
| 2586 continue; | |
| 2587 if (selected(x, y1)) | |
| 2588 new.mode ^= ATTR_REVERSE; | |
| 2589 + if (highlighted(x, y1)) { | |
| 2590 + new.mode ^= ATTR_HIGHLIGHT; | |
| 2591 + } | |
| 2592 + if (currentLine(x, y1)) { | |
| 2593 + new.mode ^= ATTR_CURRENT; | |
| 2594 + } | |
| 2595 if (i > 0 && ATTRCMP(base, new)) { | |
| 2596 xdrawglyphfontspecs(specs, base, i, ox, y1); | |
| 2597 specs += i; | |
| 2598 @@ -1786,6 +1812,14 @@ kpress(XEvent *ev) | |
| 2599 len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &… | |
| 2600 else | |
| 2601 len = XLookupString(e, buf, sizeof buf, &ksym, NULL); | |
| 2602 + | |
| 2603 + if (IS_SET(MODE_NORMAL)) { | |
| 2604 + ExitState const es = kpressNormalMode(buf, len, // strl… | |
| 2605 + match(ControlMask, e->state), | |
| 2606 + &ksym); | |
| 2607 + if (es == finished) { normalMode(NULL); } | |
| 2608 + return; | |
| 2609 + } | |
| 2610 /* 1. shortcuts */ | |
| 2611 for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) { | |
| 2612 if (ksym == bp->keysym && match(bp->mod, e->state)) { | |
| 2613 -- | |
| 2614 2.25.0 | |
| 2615 |