config.c (55261B)
1 #include "config.h" 2 #include "extension.h" 3 #include <ctype.h> 4 5 #define MODKEY Mod1Mask 6 7 // TODO: search hilight no longer works because syntax is ran after drawing search highlight 8 9 //////////////////////////////////////// 10 // apperance 11 // 12 13 // font: see http://freedesktop.org/software/fontconfig/fontconfig-user.html 14 char *fontconfig = "Liberation Mono:pixelsize=21:antialias=true:autohint=true"; 15 //char *fontconfig = "Iosevka:pixelsize=20:antialias=true:autohint=true"; 16 17 // pixels of border around the window 18 int border_px = 2; 19 20 // default size of the editor 21 unsigned int default_cols = 80; 22 unsigned int default_rows = 24; 23 24 // Kerning / character bounding-box multipliers 25 float cw_scale = 1.0; 26 float ch_scale = 1.0; 27 28 int wrap_buffer = 0; 29 30 // spaces per tab (tabs will self align) 31 unsigned int tabspaces = 8; 32 unsigned int default_indent_len = 0; // 0 means tab 33 34 // Default shape of cursor 35 // 2: block ("█") 36 // 4: underline ("_") 37 // 6: bar ("|") 38 // TODO: half_block 39 // TODO: enums 40 unsigned int cursor_shape = 2; 41 42 // thickness of underline and bar cursors 43 unsigned int cursor_thickness = 2; 44 45 /////////////////////////////////////////// 46 // custom window modes 47 // 48 49 enum window_modes { 50 WB_MODES_START = WB_MODES_DEFAULT_END, 51 #include "extensions/window_modes/choose_one_of_selection.enums" 52 WB_MODES_END 53 }; 54 55 /////////////////////////////////////////// 56 // color scheme 57 // the syntax highlighting is applied in one pass, 58 // so you can't have nested syntax highlighting 59 // 60 61 #include "extensions/syntax/syntax.h" 62 63 #include "extensions/syntax/schemes/gruvbox.h" 64 65 // disable coloring functions for the syntax schemes below 66 #undef function_color 67 //#define function_color normal_color 68 69 #include "extensions/syntax/gd.h" 70 #include "extensions/syntax/c.h" 71 72 const struct syntax_scheme syntax[] = { 73 {".c", c_word_seperators, c_syntax, LEN(c_syntax), c_indent, LEN(c_indent)}, 74 {".h", c_word_seperators, c_syntax, LEN(c_syntax), c_indent, LEN(c_indent)}, 75 {".gd", gd_word_seperators, gd_syntax, LEN(gd_syntax), gd_indent, LEN(gd_indent)}, 76 77 {0}, 78 }; 79 80 const struct syntax_scheme* syntax_schemes = syntax; 81 82 ///////////////////////////////////////// 83 // Shortcuts 84 // 85 86 enum vim_modes { 87 VIM_NORMAL, 88 VIM_INSERT, 89 VIM_REPLACE, 90 VIM_SINGULAR_REPLACE, 91 VIM_VISUAL, 92 VIM_VISUAL_LINE, 93 VIM_VISUAL_BLOCK, 94 }; 95 96 static int vim_mode = VIM_NORMAL; 97 98 enum inside_around_delimiters { 99 VIM_INSIDE_ROUND_BRACKETS, 100 VIM_INSIDE_SQUARE_BRACKETS, 101 VIM_INSIDE_CURLY_BRACKETS, 102 VIM_INSIDE_ANGLE_BRACKET, 103 VIM_INSIDE_DOUBLE_QUOTES, 104 VIM_INSIDE_SINGLE_QUOTES, 105 VIM_INSIDE_BACK_QUOTES, 106 VIM_INSIDE_PARAGRAPH, 107 108 VIM_INSIDE_COUNT, 109 VIM_AROUND_START = VIM_INSIDE_COUNT, 110 111 VIM_AROUND_ROUND_BRACKETS = VIM_AROUND_START, 112 VIM_AROUND_SQUARE_BRACKETS, 113 VIM_AROUND_CURLY_BRACKETS, 114 VIM_AROUND_ANGLE_BRACKET, 115 VIM_AROUND_DOUBLE_QUOTES, 116 VIM_AROUND_SINGLE_QUOTES, 117 VIM_AROUND_BACK_QUOTES, 118 VIM_AROUND_PARAGRAPH, 119 120 VIM_AROUND_END = VIM_AROUND_PARAGRAPH, 121 }; 122 123 _Static_assert(VIM_AROUND_END - VIM_AROUND_START + 1 == VIM_INSIDE_COUNT, "Bracket count does not match"); 124 125 126 const struct delimiter vim_delimiters[VIM_INSIDE_COUNT] = { 127 {"(", ")"}, 128 {"[", "]"}, 129 {"{", "}"}, 130 {"<", ">"}, 131 {"\"", "\""}, 132 {"'", "'"}, 133 {"`", "`"}, 134 {"\n\n", "\n\n"}, // TODO: this works poorly 135 }; 136 137 struct delimiter ignore_delimiters[] = { 138 139 // C comments 140 {"//", "\n"}, 141 {"/*", "*/"}, 142 143 // Python multiline strings 144 {"\"\"\"", "\"\"\""}, 145 146 // Strings 147 {"\"", "\""}, 148 {"'", "''"}, 149 {"'", "''"}, 150 {"`", "`"}, 151 152 {0}, 153 }; 154 155 // TODO: create a "delete in comments" function that 156 // selects around the current ignore delimiter 157 158 enum misc_delimiters { 159 VIM_MISC_START = VIM_AROUND_END + 1, 160 161 VIM_CURRENT_WORD = VIM_MISC_START, 162 VIM_CURRENT_LINE, 163 //VIM_CURRENT_LINE_INSIDE, 164 VIM_CURRENT_WORD_AND_SURROUNDING_WHITESPACE, 165 VIM_NEXT_QUOUTE, 166 167 VIM_PREV_WORD_START, 168 VIM_TO_START_OF_WORD, 169 VIM_TO_END_OF_WORD, 170 171 VIM_PREV_STRING_START, 172 VIM_TO_START_OF_STRING, 173 VIM_TO_END_OF_STRING, 174 175 VIM_TO_START_OF_LINE, 176 VIM_TO_END_OF_INDENT, 177 VIM_TO_END_OF_LINE, 178 VIM_TO_START_OF_FILE, 179 VIM_TO_END_OF_FILE, 180 181 VIM_SAME_INDENT_PLUS, 182 183 VIM_CURRENT_SELECTION, 184 185 VIM_MISC_COUNT, 186 }; 187 188 189 const char* vim_default_word_seperators = "., '\n\t*+-/%!~<>=(){}[]\"^&|\\`´?:;"; 190 const char* vim_override_word_seperators = "., '\n\t*+-/%!~<>=(){}[]\"^&|\\`´?:;_"; 191 192 struct chained_keybind { 193 unsigned int mod; 194 KeySym keysym; 195 // 0 return value means it will keep this command level 196 // +1 return value means it will move into this commands next_keybinds 197 // +2 return value means it will continue looking another command 198 // -1 return value means it will reset the chained keybind 199 // -2 return value means it will reset the chained keybind (special case, e.g not reset state) 200 int(*func)(int custom_mode); 201 int custom_mode; 202 const char* description; 203 struct chained_keybind* next_keybinds; 204 int keybind_count; 205 }; 206 207 #define CHAINED_KEYBIND_LEN 1024 208 struct keypress_logg { 209 KeySym ksym; 210 unsigned int modkey; 211 int len; 212 char* buf; 213 }; 214 struct keypress_logg* previous_chain; 215 struct keypress_logg current_chain[CHAINED_KEYBIND_LEN] = {0}; 216 int previous_chain_len; 217 int chain_len; 218 219 int last_used_command_index; 220 221 static void 222 vim_copy_log(struct keypress_logg** dest, int dest_len, const struct keypress_logg* src, int src_len) 223 { 224 *dest = xrealloc(*dest, src_len * sizeof(struct keypress_logg)); 225 if (dest_len == 0) 226 (*dest)->buf = NULL; 227 228 int total_buf_len = 0; 229 for (int i = 0; i < src_len; i++) 230 total_buf_len += src[i].len; 231 char* chars = xrealloc((*dest)->buf, total_buf_len); 232 233 for (int i = 0; i < src_len; i++) { 234 struct keypress_logg* t = (*dest) + i; 235 struct keypress_logg s = src[i]; 236 t->buf = chars; 237 memcpy(t->buf, s.buf, s.len); 238 t->ksym = s.ksym; 239 t->modkey = s.modkey; 240 t->len = s.len; 241 chars += s.len; 242 } 243 } 244 245 static int 246 vim_chain_parse_count_raw() 247 { 248 int last_ksym_was_num = 0; 249 int sum = 0; 250 int current_num = 0; 251 for (int i = last_used_command_index; i < chain_len; i++) { 252 int current_val; 253 switch(current_chain[i].ksym) { 254 case XK_0: current_val = 0; break; 255 case XK_1: current_val = 1; break; 256 case XK_2: current_val = 2; break; 257 case XK_3: current_val = 3; break; 258 case XK_4: current_val = 4; break; 259 case XK_5: current_val = 5; break; 260 case XK_6: current_val = 6; break; 261 case XK_7: current_val = 7; break; 262 case XK_8: current_val = 8; break; 263 case XK_9: current_val = 9; break; 264 default: current_val =-1; break; 265 } 266 if (current_val < 0) { 267 if (last_ksym_was_num) { 268 sum += current_num; 269 current_num = 0; 270 last_ksym_was_num = 0; 271 } 272 continue; 273 } 274 if (last_ksym_was_num) 275 current_num *= 10; 276 else 277 last_ksym_was_num = 1; 278 current_num += current_val; 279 } 280 if (last_ksym_was_num) 281 sum += current_num; 282 283 return sum; 284 } 285 286 static int 287 vim_chain_parse_count(int custom_mode) 288 { 289 if (custom_mode == VIM_TO_END_OF_FILE || custom_mode == VIM_TO_START_OF_FILE) 290 return 1; 291 int sum = vim_chain_parse_count_raw(); 292 if (sum == 0) 293 sum = 1; 294 return sum; 295 } 296 297 static int 298 vim_get_delimiter(int delimiter_type, int offset, int* start, int* end) 299 { 300 if (delimiter_type < 0) 301 return 0; 302 soft_assert(start, static int tmp; start = &tmp;); 303 soft_assert(end, static int tmp; end = &tmp;); 304 305 struct file_buffer* fb = get_fb(focused_window); 306 LIMIT(offset, 0, fb->len-1); 307 308 int count; 309 310 if (delimiter_type < VIM_MISC_START) { 311 int around = delimiter_type >= VIM_AROUND_START ? 1 : 0; 312 if (around) 313 delimiter_type -= VIM_AROUND_START; 314 315 soft_assert(delimiter_type < VIM_INSIDE_COUNT, return 0;); 316 if (!fb_get_delimiter(fb, offset, vim_delimiters[delimiter_type], ignore_delimiters, start, end)) { 317 int next_offset = fb_seek_string(fb, offset, vim_delimiters[delimiter_type].start); 318 if (next_offset < 0 || next_offset - offset >= 115) 319 return 0; 320 if (!fb_get_delimiter(fb, next_offset, vim_delimiters[delimiter_type], ignore_delimiters, start, end)) 321 return 0; 322 } 323 324 if (!around) 325 *start += strlen(vim_delimiters[delimiter_type].start); 326 else 327 *end += strlen(vim_delimiters[delimiter_type].end); 328 329 return 1; 330 } 331 332 333 #ifdef SYNTAX_H_ 334 const struct syntax_scheme* cs = fb_get_syntax_scheme(fb); 335 const char* word_seperators = cs ? cs->word_seperators : vim_default_word_seperators; 336 #else 337 const char* word_seperators = vim_default_word_seperators; 338 #endif 339 if (vim_override_word_seperators) 340 word_seperators = vim_override_word_seperators; 341 342 switch(delimiter_type) { 343 case VIM_CURRENT_WORD: 344 if (isspace(fb->contents[offset])) { 345 int not_whitespace = fb_seek_not_whitespace_backwards(fb, offset); 346 if (not_whitespace >= 0) 347 not_whitespace += 1; 348 int line_start = fb_seek_char_backwards(fb, offset, '\n'); 349 *start = MAX(not_whitespace, line_start); 350 not_whitespace = fb_seek_not_whitespace(fb, offset); 351 line_start = fb_seek_char(fb, offset, '\n'); 352 *end = MIN(not_whitespace, line_start); 353 if (*end < 0) 354 *end = MAX(not_whitespace, line_start); 355 } else if (fb_is_on_a_word(fb, offset, word_seperators)) { 356 *start = fb_seek_start_of_word_backwards(fb, offset, word_seperators); 357 *end = fb_seek_word_end(fb, offset, word_seperators); 358 } else { 359 const char* word_seperators_no_newline = "., '\t*+-/%!~<>=(){}[]\"^&|\\`´?:;"; 360 *start = fb_seek_word_backwards(fb, offset, word_seperators_no_newline); 361 if (*start >= 0) 362 *start += 1; 363 *end = fb_seek_word(fb, offset, word_seperators_no_newline); 364 } 365 if (*start < 0 || *end < 0) 366 return 0; 367 return 1; 368 case VIM_CURRENT_LINE: 369 *start = fb_seek_char_backwards(fb, offset, '\n'); 370 *end = fb_seek_char(fb, offset, '\n'); 371 if (*start < 0) 372 *start = 0; 373 if (*end < 0) 374 *end = fb->len-1; 375 if (*end > 0) 376 *end += 1; 377 return 1; 378 case VIM_TO_END_OF_LINE: 379 *end = fb_seek_char(fb, offset, '\n'); 380 if (*end < 0) 381 return 0; 382 *start = offset; 383 return 1; 384 case VIM_TO_START_OF_LINE: 385 *start = fb_seek_char_backwards(fb, offset-1, '\n'); 386 if (*start < 0) 387 *start = 0; 388 *end = offset; 389 return 1; 390 case VIM_TO_END_OF_INDENT: 391 *start = fb_seek_char_backwards(fb, offset, '\n'); 392 if (*start < 0) 393 *start = 0; 394 if (*start + 1 < fb->len-1 && isspace(fb->contents[*start + 1])) { 395 int not_whitespace = fb_seek_not_whitespace(fb, *start + 1); 396 int line_end = fb_seek_char(fb, *start + 1, '\n'); 397 if (line_end < not_whitespace) 398 return 0; 399 if (not_whitespace >= 0) 400 *start = not_whitespace; 401 } 402 *end = offset; 403 return 1; 404 case VIM_TO_START_OF_FILE: 405 count = vim_chain_parse_count_raw(); 406 if (count) { 407 count--; 408 int old_offset = focused_window->cursor_offset; 409 410 focused_window->cursor_offset = 0; 411 wb_move_lines(focused_window, count, 0); 412 int new_offset = fb_seek_char_backwards(fb, focused_window->cursor_offset, '\n'); 413 if (new_offset < 0) 414 new_offset = focused_window->cursor_offset; 415 416 focused_window->cursor_offset = old_offset; 417 418 if (new_offset < old_offset) { 419 *start = new_offset; 420 *end = old_offset; 421 } else { 422 *end = new_offset; 423 *start = old_offset; 424 } 425 printf("%d %d\n", *start, *end); 426 } else { 427 *start = 0; 428 *end = offset; 429 } 430 return 1; 431 case VIM_TO_END_OF_FILE: 432 count = vim_chain_parse_count_raw(); 433 if (count) { 434 count--; 435 int old_offset = focused_window->cursor_offset; 436 focused_window->cursor_offset = 0; 437 wb_move_lines(focused_window, count, 0); 438 focused_window->cursor_offset = old_offset; 439 int new_offset = fb_seek_char_backwards(fb, focused_window->cursor_offset, '\n'); 440 if (new_offset < old_offset) { 441 *start = new_offset; 442 *end = old_offset; 443 } else { 444 *end = new_offset; 445 *start = old_offset; 446 } 447 } else { 448 *start = offset; 449 *end = fb->len-1; 450 } 451 return 1; 452 case VIM_TO_START_OF_WORD: 453 *end = fb_seek_word(fb, offset, word_seperators); 454 *start = offset; 455 if (*end < 0) 456 return 0; 457 return 1; 458 case VIM_PREV_WORD_START: 459 *start = fb_seek_start_of_word_backwards(fb, offset-1, word_seperators); 460 *end = offset; 461 if (*start < 0) 462 return 0; 463 return 1; 464 case VIM_PREV_STRING_START: 465 *end = offset; 466 offset--; 467 if (isspace(fb->contents[offset])) 468 offset = fb_seek_not_whitespace_backwards(fb, offset); 469 *start = fb_seek_whitespace_backwards(fb, offset-1); 470 if (*start < 0) 471 return 0; 472 *start += 1; 473 return 1; 474 case VIM_TO_END_OF_WORD: 475 *end = fb_seek_word_end(fb, offset, word_seperators); 476 *start = offset; 477 if (*end < 0) 478 return 0; 479 return 1; 480 case VIM_TO_START_OF_STRING: 481 *start = offset; 482 if (!isspace(fb->contents[offset])) 483 offset = fb_seek_whitespace(fb, offset); 484 *end = fb_seek_not_whitespace(fb, offset); 485 if (*end < 0) 486 return 0; 487 return 1; 488 case VIM_TO_END_OF_STRING: 489 *start = offset; 490 if (isspace(fb->contents[offset])) 491 offset = fb_seek_not_whitespace(fb, offset); 492 *end = fb_seek_whitespace(fb, offset); 493 if (*end < 0) 494 return 0; 495 return 1; 496 case VIM_CURRENT_SELECTION: 497 if (fb_is_selection_start_top_left(fb)) { 498 *start = fb->s1o; 499 *end = fb->s2o + 1; 500 } else { 501 *start = fb->s2o; 502 *end = fb->s1o + 1; 503 } 504 return 1; 505 default: 506 return 0; 507 } 508 } 509 510 static int 511 vim_change_mode(int custom_mode) 512 { 513 int previous_mode = vim_mode; 514 515 if (custom_mode == VIM_NORMAL) { 516 if (vim_mode == VIM_INSERT) 517 wb_move_on_line(focused_window, -1, CURSOR_COMMAND_MOVEMENT); 518 struct file_buffer* fb = get_fb(focused_window); 519 cursor_shape = 2; 520 fb->mode &= ~FB_SELECT_MASK; 521 fb->mode &= ~FB_SEARCH_BLOCKING_MASK; 522 fb->mode &= ~FB_SEARCH_NON_BLOCKING; 523 fb->mode &= ~FB_SEARCH_NON_BLOCKING_BACKWARDS; 524 fb->mode &= ~FB_SEARCH_BLOCKING_BACKWARDS; 525 writef_to_status_bar("Escape"); 526 } else if (custom_mode == VIM_INSERT) { 527 cursor_shape = 6; 528 writef_to_status_bar("-- INSERT --"); 529 } else if (custom_mode == VIM_REPLACE) { 530 cursor_shape = 4; 531 } else if (custom_mode == VIM_VISUAL || VIM_VISUAL_LINE) { 532 struct file_buffer* fb = get_fb(focused_window); 533 fb->mode |= FB_SELECTION_ON; 534 if (custom_mode == VIM_VISUAL) { 535 fb->s1o = fb->s2o = focused_window->cursor_offset; 536 writef_to_status_bar("-- VISUAL --"); 537 } else if (custom_mode == VIM_VISUAL_LINE) { 538 fb->s1o = fb_seek_char(fb, focused_window->cursor_offset, '\n'); 539 fb->s2o = fb_seek_char_backwards(fb, focused_window->cursor_offset, '\n'); 540 fb->mode |= FB_LINE_SELECT; 541 writef_to_status_bar("-- VISUAL LINE --"); 542 543 custom_mode = VIM_VISUAL; 544 } 545 } 546 vim_mode = custom_mode; 547 if (custom_mode == VIM_VISUAL || (vim_mode == previous_mode)) 548 return -2; 549 return -1; 550 } 551 552 static int 553 vim_exit(int custom_mode) { 554 exit(0); 555 } 556 557 static int 558 vim_move_on_line(int custom_mode) 559 { 560 if (focused_window->mode != WB_FILE_BROWSER) 561 wb_move_on_line(focused_window, custom_mode * vim_chain_parse_count(0), CURSOR_RIGHT_LEFT_MOVEMENT); 562 return -2; 563 } 564 565 static int 566 vim_move_lines(int custom_mode) 567 { 568 wb_move_lines(focused_window, custom_mode * vim_chain_parse_count(0), 0); 569 wb_move_to_x(focused_window, focused_window->cursor_col, CURSOR_UP_DOWN_MOVEMENT); 570 return -2; 571 } 572 573 static int 574 vim_change_window(int custom_mode) 575 { 576 int count = vim_chain_parse_count(0); 577 while(count--) { 578 focused_node = window_switch_to_window(focused_node, custom_mode); 579 focused_window = &focused_node->wb; 580 } 581 return -2; 582 } 583 584 static int 585 vim_resize_window(int custom_mode) 586 { 587 float amount = (custom_mode == MOVE_RIGHT || custom_mode == MOVE_LEFT) ? 0.1f : 0.05f; 588 window_node_resize(focused_node, custom_mode, amount * vim_chain_parse_count(0)); 589 return -2; 590 } 591 592 static int 593 vim_swap_to_next_file_buffer(int custom_mode) 594 { 595 focused_window->fb_index += vim_chain_parse_count(0); 596 return -2; 597 } 598 599 static int 600 vim_split_window(int custom_mode) 601 { 602 int count = vim_chain_parse_count(0); 603 while(count--) { 604 window_node_split(focused_node, 0.5, custom_mode); 605 #if 1 606 if (focused_node->node2) { 607 focused_node = focused_node->node2; 608 focused_window = &focused_node->wb; 609 } 610 #else 611 if (focused_node->node1) { 612 focused_node = focused_node->node1; 613 focused_window = &focused_node->wb; 614 } 615 #endif 616 } 617 return -2; 618 } 619 620 static int 621 vim_delete_window(int custom_mode) 622 { 623 int count = vim_chain_parse_count(0); 624 while(count--) { 625 struct window_split_node* new_node = window_node_delete(focused_node); 626 while (new_node->mode != WINDOW_SINGULAR) 627 new_node = new_node->node1; 628 focused_node = new_node; 629 focused_window = &focused_node->wb; 630 } 631 return -2; 632 } 633 634 static int 635 vim_kill_buffer(int custom_mode) 636 { 637 int count = vim_chain_parse_count(0); 638 while(count--) 639 destroy_fb_entry(focused_node, &root_node); 640 return -2; 641 } 642 643 static int 644 vim_save_buffer(int custom_mode) 645 { 646 fb_write_to_filepath(get_fb(focused_window)); 647 return -2; 648 } 649 650 static int 651 vim_zoomabs(int custom_mode) 652 { 653 xunloadfonts(); 654 xloadfonts(fontconfig, custom_mode); 655 cresize(0, 0); 656 xhints(); 657 return -2; 658 } 659 660 static int 661 vim_zoom(int custom_mode) 662 { 663 vim_zoomabs(usedfontsize + custom_mode); 664 return -2; 665 } 666 667 static int 668 vim_zoomreset(int custom_mode) 669 { 670 if (defaultfontsize > 0) 671 vim_zoomabs(defaultfontsize); 672 return -2; 673 } 674 675 static int 676 vim_zero(int custom_mode) 677 { 678 if (vim_chain_parse_count_raw()) 679 return 0; 680 return 2; 681 } 682 683 static int 684 vim_yank(int custom_mode) 685 { 686 struct file_buffer* fb = get_fb(focused_window); 687 char* buf; 688 int len; 689 if (custom_mode == VIM_CURRENT_SELECTION || fb->mode & FB_SELECTION_ON) { 690 buf = fb_get_selection(fb, &len); 691 wb_move_cursor_to_selection_start(focused_window); 692 fb->mode &= ~FB_SELECT_MASK; 693 vim_change_mode(VIM_NORMAL); 694 } else { 695 int start, end; 696 if (!vim_get_delimiter(custom_mode, focused_window->cursor_offset, &start, &end)) { 697 writef_to_status_bar("unable to yank area"); 698 return -1; 699 } 700 buf = fb_get_string_between_offsets(fb, start, end); 701 len = end - start; 702 } 703 set_clipboard_copy(buf, len); 704 writef_to_status_bar("area yanked"); 705 return -2; 706 } 707 708 static int paste_behind = 0; 709 710 static int 711 vim_paste(int custom_mode) 712 { 713 custom_mode = paste_behind; 714 execute_clipbaord_event(); 715 return -1; 716 } 717 718 static int 719 vim_auto_indent_current_line(int custom_mode) 720 { 721 struct file_buffer* fb = get_fb(focused_window); 722 focused_window->cursor_offset = fb_seek_char_backwards(fb, focused_window->cursor_offset, '\n'); 723 int indent_size = fb_auto_indent(fb, focused_window->cursor_offset); 724 725 focused_window->cursor_offset = MIN(fb_seek_not_whitespace(fb, focused_window->cursor_offset), 726 fb_seek_char(fb, focused_window->cursor_offset, '\n')); 727 728 window_node_move_all_cursors_on_same_fb(&root_node, focused_node, focused_window->fb_index, focused_window->cursor_offset, 729 wb_move_offset_relative, indent_size, CURSOR_COMMAND_MOVEMENT); 730 return -2; 731 } 732 733 static int 734 vim_open_file_browser(int custom_mode) 735 { 736 int last_fb = focused_window->fb_index; 737 struct file_buffer* fb = get_fb(focused_window); 738 739 char* path = file_path_get_path(fb->file_path); 740 *focused_window = wb_new(fb_new_entry(path)); 741 focused_window->cursor_col = last_fb; 742 free(path); 743 return -1; 744 } 745 746 int 747 vim_search_for_buffer(int custom_mode) 748 { 749 if (focused_window->mode != WB_NORMAL) 750 return -2; 751 *focused_node->search = 0; 752 focused_node->selected = 0; 753 focused_window->mode = WB_SEARCH_FOR_BUFFERS; 754 return -2; 755 } 756 757 int 758 vim_search_keyword_in_buffers(int custom_mode) 759 { 760 if (focused_window->mode != WB_NORMAL) 761 return -2; 762 *focused_node->search = 0; 763 focused_node->selected = 0; 764 focused_window->mode = WB_SEARCH_KEYWORD_ALL_BUFFERS; 765 return -2; 766 } 767 768 static int 769 vim_delete(int custom_mode) 770 { 771 int count = vim_chain_parse_count(custom_mode); 772 while (count--) { 773 int start, end; 774 if (!vim_get_delimiter(custom_mode, focused_window->cursor_offset, &start, &end)) { 775 writef_to_status_bar("unable to find section to delete"); 776 return -1; 777 } 778 if (start - end == 0) 779 return -2; 780 struct file_buffer* fb = get_fb(focused_window); 781 fb_remove(fb, start, end - start, 1, 1); 782 call_extension(fb_contents_updated, fb, focused_window->cursor_offset, FB_CONTENT_BIG_CHANGE); 783 wb_move_to_offset(focused_window, start, CURSOR_COMMAND_MOVEMENT); 784 window_node_move_all_cursors_on_same_fb(&root_node, focused_node, focused_window->fb_index, end, 785 wb_move_offset_relative, end - start, CURSOR_COMMAND_MOVEMENT); 786 if (custom_mode == VIM_CURRENT_SELECTION) { 787 vim_change_mode(VIM_NORMAL); 788 break; 789 } 790 } 791 return -1; 792 } 793 794 static int 795 vim_change(int custom_mode) 796 { 797 int count = vim_chain_parse_count(custom_mode); 798 while (count--) { 799 int start, end; 800 if (!vim_get_delimiter(custom_mode, focused_window->cursor_offset, &start, &end)) { 801 writef_to_status_bar("unable to find section to change"); 802 return -1; 803 } 804 if (start - end == 0) 805 return -2; 806 struct file_buffer* fb = get_fb(focused_window); 807 fb_remove(fb, start, end - start, 1, 1); 808 call_extension(fb_contents_updated, fb, focused_window->cursor_offset, FB_CONTENT_BIG_CHANGE); 809 wb_move_to_offset(focused_window, start, CURSOR_COMMAND_MOVEMENT); 810 window_node_move_all_cursors_on_same_fb(&root_node, focused_node, focused_window->fb_index, end, 811 wb_move_offset_relative, end - start, CURSOR_COMMAND_MOVEMENT); 812 if (custom_mode == VIM_CURRENT_SELECTION) 813 break; 814 815 } 816 vim_change_mode(VIM_INSERT); 817 return -1; 818 } 819 820 static int 821 vim_visual_delimit(int custom_mode) 822 { 823 int start, end; 824 if (!vim_get_delimiter(custom_mode, focused_window->cursor_offset, &start, &end)) { 825 writef_to_status_bar("unable to find section to highlight"); 826 return -1; 827 } 828 829 end--; 830 struct file_buffer* fb = get_fb(focused_window); 831 fb->mode |= FB_SELECTION_ON; 832 fb->mode &= ~FB_SELECT_MODE_MASK; 833 writef_to_status_bar("-- VISUAL --"); 834 835 if (end == focused_window->cursor_offset) { 836 fb->s1o = end; 837 fb->s2o = start; 838 839 focused_window->cursor_offset = start; 840 return -1; 841 } 842 843 fb->s1o = start; 844 fb->s2o = end; 845 846 focused_window->cursor_offset = end; 847 return -2; 848 } 849 850 static int vim_enter(int custom_mode) {return 1;} 851 852 static int 853 vim_append(int custom_mode) 854 { 855 vim_change_mode(VIM_INSERT); 856 wb_move_on_line(focused_window, 1, CURSOR_COMMAND_MOVEMENT); 857 return -1; 858 } 859 860 static int 861 vim_insert_start(int custom_mode) 862 { 863 vim_change_mode(VIM_INSERT); 864 int start, tmp; 865 if (vim_get_delimiter(VIM_TO_END_OF_INDENT, focused_window->cursor_offset, &start, &tmp)) 866 wb_move_to_offset(focused_window, start, CURSOR_COMMAND_MOVEMENT); 867 return -1; 868 } 869 870 static int 871 vim_insert_end(int custom_mode) 872 { 873 vim_change_mode(VIM_INSERT); 874 int end, tmp; 875 if (vim_get_delimiter(VIM_TO_END_OF_LINE, focused_window->cursor_offset, &tmp, &end)) 876 wb_move_to_offset(focused_window, end, CURSOR_COMMAND_MOVEMENT); 877 return -1; 878 } 879 880 static int 881 vim_insert_new_line(int custom_mode) 882 { 883 vim_change_mode(VIM_INSERT); 884 struct file_buffer* fb = get_fb(focused_window); 885 886 int offset = focused_window->cursor_offset; 887 offset = fb_seek_char(fb, offset, '\n'); 888 if (offset < 0) 889 offset = fb->len-1; 890 focused_window->cursor_offset = offset; 891 wb_move_offset_relative(focused_window, 1, CURSOR_COMMAND_MOVEMENT); 892 893 fb_insert(fb, "\n", 1, offset, 0); 894 window_node_move_all_cursors_on_same_fb(&root_node, focused_node, focused_window->fb_index, focused_window->cursor_offset, 895 wb_move_offset_relative, 1, CURSOR_COMMAND_MOVEMENT); 896 vim_auto_indent_current_line(0); 897 898 return -1; 899 } 900 901 static int 902 vim_remove_newline_at_end(int custom_mode) 903 { 904 int count = vim_chain_parse_count(0); 905 struct file_buffer* fb = get_fb(focused_window); 906 while (count--) { 907 int offset = fb_seek_char(fb, focused_window->cursor_offset, '\n'); 908 fb_remove(fb, offset, 1, 1, 0); 909 wb_move_to_offset(focused_window, offset, CURSOR_COMMAND_MOVEMENT); 910 window_node_move_all_cursors_on_same_fb(&root_node, focused_node, focused_window->fb_index, offset, 911 wb_move_offset_relative, -1, CURSOR_COMMAND_MOVEMENT); 912 913 LIMIT(offset, 0, fb->len); 914 if (isspace(fb->contents[offset])) { 915 int start, end; 916 int not_whitespace = fb_seek_not_whitespace_backwards(fb, offset); 917 if (not_whitespace >= 0) 918 not_whitespace += 1; 919 int line_start = fb_seek_char_backwards(fb, offset, '\n'); 920 start = MAX(not_whitespace, line_start); 921 922 not_whitespace = fb_seek_not_whitespace(fb, offset); 923 line_start = fb_seek_char(fb, offset, '\n'); 924 end = MIN(not_whitespace, line_start); 925 if (end < 0) 926 end = MAX(not_whitespace, line_start); 927 928 if (end < 0 || start < 0) 929 return -1; 930 931 if (fb->contents[offset] != '\n') { 932 fb_remove(fb, start, end - start, 1, 0); 933 fb_insert(fb, " ", 1, start, 1); 934 start++; 935 wb_move_to_offset(focused_window, start, CURSOR_COMMAND_MOVEMENT); 936 window_node_move_all_cursors_on_same_fb(&root_node, focused_node, focused_window->fb_index, end, 937 wb_move_offset_relative, end - start, CURSOR_COMMAND_MOVEMENT); 938 } 939 } else { 940 fb_insert(fb, " ", 1, offset, 1); 941 } 942 } 943 return -1; 944 } 945 946 static int 947 vim_undo(int custom_mode) 948 { 949 int count = vim_chain_parse_count(0); 950 while (count--) 951 fb_undo(get_fb(focused_window)); 952 return -2; 953 } 954 955 static int 956 vim_redo(int custom_mode) 957 { 958 int count = vim_chain_parse_count(0); 959 while (count--) 960 fb_redo(get_fb(focused_window)); 961 return -2; 962 } 963 964 static int search_reversed = 0; 965 static int vim_next(int custom_mode); 966 static int vim_prev(int custom_mode); 967 968 int 969 vim_next(int custom_mode) 970 { 971 if (!custom_mode && search_reversed) 972 return vim_prev(1); 973 974 get_fb(focused_window)->mode |= FB_SEARCH_BLOCKING_IDLE; 975 int new_offset = wb_seek_string_wrap(focused_window, focused_window->cursor_offset+1, 976 get_fb(focused_window)->search_term); 977 if (new_offset < 0) { 978 writef_to_status_bar("no results for \"%s\"", get_fb(focused_window)->search_term); 979 return -1; 980 } else if (focused_window->cursor_offset > new_offset) { 981 writef_to_status_bar("search wrapped"); 982 } 983 focused_window->cursor_offset = new_offset; 984 return -2; 985 } 986 987 int 988 vim_prev(int custom_mode) 989 { 990 if (!custom_mode && search_reversed) 991 return vim_next(1); 992 993 get_fb(focused_window)->mode |= FB_SEARCH_BLOCKING_IDLE; 994 int new_offset = wb_seek_string_wrap_backwards(focused_window, focused_window->cursor_offset-1, 995 get_fb(focused_window)->search_term); 996 if (new_offset < 0) { 997 writef_to_status_bar("no results for \"%s\"", get_fb(focused_window)->search_term); 998 return -1; 999 } else if (focused_window->cursor_offset < new_offset) { 1000 writef_to_status_bar("search wrapped"); 1001 } 1002 focused_window->cursor_offset = new_offset; 1003 return -2; 1004 } 1005 1006 static int 1007 vim_search(int custom_mode) 1008 { 1009 search_reversed = custom_mode; 1010 get_fb(focused_window)->mode &= ~FB_SEARCH_BLOCKING_IDLE; 1011 get_fb(focused_window)->mode |= FB_SEARCH_BLOCKING; 1012 writef_to_status_bar("search: %s", get_fb(focused_window)->search_term); 1013 return -2; 1014 } 1015 1016 static int 1017 vim_home(int custom_mode) 1018 { 1019 struct file_buffer* fb = get_fb(focused_window); 1020 int new_offset = fb_seek_char_backwards(fb, focused_window->cursor_offset, '\n'); 1021 if (new_offset < 0) 1022 new_offset = 0; 1023 wb_move_to_offset(focused_window, new_offset, CURSOR_COMMAND_MOVEMENT); 1024 return -2; 1025 } 1026 1027 static int 1028 vim_end(int custom_mode) 1029 { 1030 struct file_buffer* fb = get_fb(focused_window); 1031 int new_offset = fb_seek_char(fb, focused_window->cursor_offset, '\n'); 1032 if (new_offset < 0) 1033 new_offset = fb->len-1; 1034 wb_move_to_offset(focused_window, new_offset, CURSOR_COMMAND_MOVEMENT); 1035 return -2; 1036 } 1037 1038 static int 1039 vim_move_down_one_screen_devided_by(int custom_mode) 1040 { 1041 soft_assert(custom_mode, return -2;); 1042 int count = vim_chain_parse_count(0); 1043 1044 wb_move_lines(focused_window, ((focused_node->maxy - focused_node->miny) / custom_mode) * count, 0); 1045 wb_move_to_x(focused_window, focused_window->cursor_col, CURSOR_UP_DOWN_MOVEMENT); 1046 focused_window->y_scroll += ((focused_node->maxy - focused_node->miny) / custom_mode) * count; 1047 return -2; 1048 } 1049 1050 static int 1051 vim_move_scroll(int custom_mode) 1052 { 1053 int count = vim_chain_parse_count(0); 1054 focused_window->y_scroll += custom_mode * count; 1055 return -2; 1056 } 1057 1058 static int 1059 vim_center_scroll(int custom_mode) 1060 { 1061 struct file_buffer* fb = get_fb(focused_window); 1062 int tmp, y; 1063 fb_offset_to_xy(fb, focused_window->cursor_offset, focused_node->maxx, focused_window->y_scroll, &tmp, &y, &tmp); 1064 focused_window->y_scroll += y - ((focused_node->maxy - focused_node->miny) / 2); 1065 return -2; 1066 } 1067 1068 // TODO: make (x/delete) remove something to the on it when it's next to new line 1069 // instead of doing nothing 1070 static int 1071 vim_remove_one_char(int custom_mode) 1072 { 1073 struct window_split_node* excluded = (custom_mode == 0) ? focused_node : NULL; 1074 1075 int old_offset = focused_window->cursor_offset; 1076 if (custom_mode) 1077 wb_move_on_line(focused_window, custom_mode, CURSOR_DO_NOT_CALLBACK); 1078 int offset = focused_window->cursor_offset; 1079 focused_window->cursor_offset = old_offset; 1080 1081 int times = vim_chain_parse_count(0); 1082 struct file_buffer* fb = get_fb(focused_window); 1083 while (times-- && fb->contents[offset] != '\n') { 1084 int len = fb_remove(fb, offset, 1, 0, 0); 1085 window_node_move_all_cursors_on_same_fb(&root_node, excluded, focused_window->fb_index, offset, 1086 wb_move_offset_relative, -len, CURSOR_COMMAND_MOVEMENT); 1087 } 1088 return -1; 1089 } 1090 1091 static int 1092 vim_backspace(int custom_mode) 1093 { 1094 struct file_buffer* fb = get_fb(focused_window); 1095 int offset = focused_window->cursor_offset-1; 1096 if (offset <= 0 || offset >= fb->len) 1097 return -1; 1098 if (fb->contents[offset] == '\n') { 1099 fb_remove(fb, offset, 1, 1, 0); 1100 window_node_move_all_cursors_on_same_fb(&root_node, NULL, focused_window->fb_index, offset, 1101 wb_move_offset_relative, -1, CURSOR_COMMAND_MOVEMENT); 1102 } else { 1103 vim_remove_one_char(-1); 1104 } 1105 return -1; 1106 } 1107 1108 static int 1109 vim_insert_return(int custom_mode) 1110 { 1111 struct file_buffer* fb = get_fb(focused_window); 1112 int offset = focused_window->cursor_offset; 1113 1114 fb_insert(fb, "\n", 1, offset, 0); 1115 int indent_size = fb_auto_indent(fb, offset); 1116 window_node_move_all_cursors_on_same_fb(&root_node, NULL, focused_window->fb_index, offset, 1117 wb_move_offset_relative, indent_size + 1, 0); 1118 indent_size = fb_auto_indent(fb, offset + 1 + indent_size); 1119 window_node_move_all_cursors_on_same_fb(&root_node, NULL, focused_window->fb_index, offset, 1120 wb_move_offset_relative, indent_size, CURSOR_COMMAND_MOVEMENT); 1121 window_node_move_all_yscrolls(&root_node, focused_node, focused_window->fb_index, focused_window->cursor_offset, 1); 1122 return -1; 1123 } 1124 1125 static int 1126 vim_insert_tab(int custom_mode) 1127 { 1128 int offset = focused_window->cursor_offset; 1129 struct file_buffer* fb = get_fb(focused_window); 1130 1131 fb_insert(fb, "\t", 1, offset, 0); 1132 window_node_move_all_cursors_on_same_fb(&root_node, NULL, focused_window->fb_index, offset, 1133 wb_move_on_line, 1, CURSOR_COMMAND_MOVEMENT); 1134 return -1; 1135 } 1136 1137 static int 1138 vim_move(int custom_mode) 1139 { 1140 int count = vim_chain_parse_count(custom_mode); 1141 struct file_buffer* fb = get_fb(focused_window); 1142 while(count--) { 1143 int offset = focused_window->cursor_offset; 1144 if (custom_mode != VIM_PREV_STRING_START && custom_mode != VIM_PREV_WORD_START && offset + 1 < fb->len) 1145 offset++; 1146 int start, end; 1147 if(!vim_get_delimiter(custom_mode, offset, &start, &end)) 1148 return -1; 1149 int other; 1150 if (abs(offset - start) > abs(offset - end)) 1151 other = start; 1152 else 1153 other = end; 1154 wb_move_to_offset(focused_window, other, CURSOR_COMMAND_MOVEMENT); 1155 } 1156 return -2; 1157 } 1158 1159 static int vim_repeat_last_command(int custom_mode); 1160 1161 #define numbers() \ 1162 {0, XK_0, vim_zero}, \ 1163 {0, XK_1}, \ 1164 {0, XK_2}, \ 1165 {0, XK_3}, \ 1166 {0, XK_4}, \ 1167 {0, XK_5}, \ 1168 {0, XK_6}, \ 1169 {0, XK_7}, \ 1170 {0, XK_8}, \ 1171 {0, XK_9} 1172 // count = 10 1173 1174 #define glue(_start, _end) _start##_end 1175 1176 #define VIM_DELIMITER_REGIONS(_func, _inside_around) \ 1177 {XK_ANY_MOD, XK_w, _func, VIM_CURRENT_WORD}, \ 1178 {XK_ANY_MOD, XK_parenleft, _func, glue(VIM_, _inside_around##_ROUND_BRACKETS)}, \ 1179 {XK_ANY_MOD, XK_parenright, _func, glue(VIM_, _inside_around##_ROUND_BRACKETS)}, \ 1180 {XK_ANY_MOD, XK_b, _func, glue(VIM_, _inside_around##_ROUND_BRACKETS)}, \ 1181 {XK_ANY_MOD, XK_bracketleft, _func, glue(VIM_, _inside_around##_SQUARE_BRACKETS)}, \ 1182 {XK_ANY_MOD, XK_bracketright, _func, glue(VIM_, _inside_around##_SQUARE_BRACKETS)}, \ 1183 {XK_ANY_MOD, XK_braceleft, _func, glue(VIM_, _inside_around##_CURLY_BRACKETS)}, \ 1184 {XK_ANY_MOD, XK_braceright, _func, glue(VIM_, _inside_around##_CURLY_BRACKETS)}, \ 1185 {XK_ANY_MOD, XK_B, _func, glue(VIM_, _inside_around##_CURLY_BRACKETS)}, \ 1186 {XK_ANY_MOD, XK_less, _func, glue(VIM_, _inside_around##_ANGLE_BRACKET)}, \ 1187 {XK_ANY_MOD, XK_greater, _func, glue(VIM_, _inside_around##_ANGLE_BRACKET)}, \ 1188 {XK_ANY_MOD, XK_t, _func, glue(VIM_, _inside_around##_ANGLE_BRACKET)}, \ 1189 {XK_ANY_MOD, XK_quotedbl, _func, glue(VIM_, _inside_around##_DOUBLE_QUOTES)}, \ 1190 {XK_ANY_MOD, XK_apostrophe, _func, glue(VIM_, _inside_around##_SINGLE_QUOTES)}, \ 1191 {XK_ANY_MOD, XK_p, _func, glue(VIM_, _inside_around##_PARAGRAPH)}, \ 1192 {XK_ANY_MOD, XK_grave, _func, glue(VIM_, _inside_around##_BACK_QUOTES)} 1193 // count = 16 1194 1195 #define CHAIN_COUNT(_x) (_x) 1196 1197 struct chained_keybind normal_mode_keybinds[] = { 1198 numbers(), 1199 // se specific keybinds, all followed by SPC, inspired by Emacs evil-mode 1200 {XK_ANY_MOD, XK_space, vim_enter, 0, "se commands [...]", (struct chained_keybind[]) { 1201 {0, XK_h, vim_change_window, MOVE_LEFT}, 1202 {0, XK_j, vim_change_window, MOVE_DOWN}, 1203 {0, XK_k, vim_change_window, MOVE_UP}, 1204 {0, XK_l, vim_change_window, MOVE_RIGHT}, 1205 {XK_ANY_MOD, XK_h, vim_resize_window, MOVE_LEFT}, 1206 {XK_ANY_MOD, XK_j, vim_resize_window, MOVE_DOWN}, 1207 {XK_ANY_MOD, XK_k, vim_resize_window, MOVE_UP}, 1208 {XK_ANY_MOD, XK_l, vim_resize_window, MOVE_RIGHT}, 1209 {XK_ANY_MOD, XK_H, vim_resize_window, MOVE_LEFT}, 1210 {XK_ANY_MOD, XK_J, vim_resize_window, MOVE_DOWN}, 1211 {XK_ANY_MOD, XK_K, vim_resize_window, MOVE_UP}, 1212 {XK_ANY_MOD, XK_L, vim_resize_window, MOVE_RIGHT}, 1213 {ShiftMask, XK_W, vim_save_buffer}, 1214 {ControlMask, XK_s, vim_save_buffer}, 1215 {0, XK_s, vim_split_window, WINDOW_VERTICAL}, 1216 {0, XK_v, vim_split_window, WINDOW_HORISONTAL}, 1217 {0, XK_d, vim_delete_window}, 1218 {0, XK_Tab, vim_swap_to_next_file_buffer}, 1219 {XK_ANY_MOD, XK_D, vim_kill_buffer}, 1220 {0, XK_b, vim_enter, 0, "buffer [...]", (struct chained_keybind[]) { 1221 {0, XK_k, vim_kill_buffer}, 1222 {0, XK_s, vim_search_for_buffer}, 1223 {0, XK_w, vim_save_buffer}, 1224 {0, XK_space, vim_search_for_buffer}, 1225 {XK_ANY_MOD, XK_slash, vim_search_keyword_in_buffers}, 1226 }, CHAIN_COUNT(5), 1227 }, 1228 {0, XK_space, vim_open_file_browser}, 1229 {ControlMask, XK_space, vim_search_for_buffer}, 1230 {0, XK_p, vim_search_for_buffer}, 1231 {XK_ANY_MOD, XK_slash, vim_search_keyword_in_buffers}, 1232 {XK_ANY_MOD, XK_plus, vim_zoom, +1}, 1233 {XK_ANY_MOD, XK_minus, vim_zoom, -1}, 1234 {XK_ANY_MOD, XK_Home, vim_zoomreset}, 1235 numbers(), 1236 }, CHAIN_COUNT(36), 1237 }, 1238 1239 // movement 1240 {0, XK_h, vim_move_on_line, -1}, 1241 {0, XK_j, vim_move_lines, +1}, 1242 {0, XK_k, vim_move_lines, -1}, 1243 {0, XK_l, vim_move_on_line, +1}, 1244 {0, XK_Left, vim_move_on_line, -1}, 1245 {0, XK_Down, vim_move_lines, +1}, 1246 {0, XK_Up, vim_move_lines, -1}, 1247 {0, XK_Right, vim_move_on_line, +1}, 1248 {0, XK_BackSpace, vim_move_on_line, -1}, 1249 {0, XK_Home, vim_home}, 1250 {0, XK_End, vim_end}, 1251 {0, XK_0, vim_move, VIM_TO_START_OF_LINE}, 1252 {ControlMask, XK_Right, vim_move, VIM_TO_END_OF_STRING}, 1253 {ControlMask, XK_Left, vim_move, VIM_PREV_STRING_START}, 1254 {0, XK_w, vim_move, VIM_TO_START_OF_WORD}, 1255 {ShiftMask, XK_W, vim_move, VIM_TO_START_OF_STRING}, 1256 {0, XK_e, vim_move, VIM_TO_END_OF_WORD}, 1257 {ShiftMask, XK_E, vim_move, VIM_TO_END_OF_STRING}, 1258 {0, XK_b, vim_move, VIM_PREV_WORD_START}, 1259 {ShiftMask, XK_B, vim_move, VIM_PREV_STRING_START}, 1260 {XK_ANY_MOD, XK_dollar, vim_move, VIM_TO_END_OF_LINE}, 1261 {XK_ANY_MOD, XK_G, vim_move, VIM_TO_END_OF_FILE}, 1262 {0, XK_g, vim_enter, 0, "goto", (struct chained_keybind[]) { 1263 {0, XK_g, vim_move, VIM_TO_START_OF_FILE}, 1264 }, CHAIN_COUNT(1), 1265 }, 1266 1267 // scroll 1268 {0, XK_Page_Down, vim_move_down_one_screen_devided_by, 1}, 1269 {0, XK_Page_Up, vim_move_down_one_screen_devided_by, -1}, 1270 {ControlMask, XK_d, vim_move_down_one_screen_devided_by, 2}, 1271 {ControlMask, XK_u, vim_move_down_one_screen_devided_by, -2}, 1272 {ControlMask, XK_f, vim_move_down_one_screen_devided_by, 1}, 1273 {ControlMask, XK_b, vim_move_down_one_screen_devided_by, -1}, 1274 {ControlMask, XK_e, vim_move_scroll, 1}, 1275 {ControlMask, XK_y, vim_move_scroll, -1}, 1276 {0, XK_z, vim_enter, 0, NULL, (struct chained_keybind[]) { 1277 {0, XK_z, vim_center_scroll}, 1278 }, CHAIN_COUNT(1), 1279 }, 1280 1281 // misc 1282 {XK_ANY_MOD, XK_Escape, vim_change_mode, VIM_NORMAL}, 1283 {0, XK_Tab, vim_auto_indent_current_line}, 1284 {0, XK_q, vim_exit}, 1285 {0, XK_u, vim_undo}, 1286 {0, XK_period, vim_repeat_last_command}, 1287 {ControlMask, XK_r, vim_redo}, 1288 {XK_ANY_MOD, XK_slash, vim_search}, 1289 {XK_ANY_MOD, XK_question, vim_search, 1}, 1290 {0, XK_n, vim_next}, 1291 {ShiftMask, XK_N, vim_prev}, 1292 // copy / yank 1293 {0, XK_p, vim_paste}, 1294 {0, XK_y, vim_enter, 0, "yank", (struct chained_keybind[]) { 1295 {0, XK_y, vim_yank, VIM_CURRENT_LINE}, 1296 {XK_ANY_MOD, XK_I, vim_yank, VIM_TO_END_OF_INDENT}, 1297 {XK_ANY_MOD, XK_0, vim_yank, VIM_TO_START_OF_LINE}, 1298 {XK_ANY_MOD, XK_A, vim_yank, VIM_TO_END_OF_LINE}, 1299 {XK_ANY_MOD, XK_G, vim_yank, VIM_TO_END_OF_FILE}, 1300 {XK_ANY_MOD, XK_w, vim_yank, VIM_TO_START_OF_WORD}, 1301 {XK_ANY_MOD, XK_W, vim_yank, VIM_TO_START_OF_STRING}, 1302 {XK_ANY_MOD, XK_e, vim_yank, VIM_TO_END_OF_WORD}, 1303 {XK_ANY_MOD, XK_E, vim_yank, VIM_TO_END_OF_STRING}, 1304 {XK_ANY_MOD, XK_b, vim_yank, VIM_PREV_WORD_START}, 1305 {XK_ANY_MOD, XK_B, vim_yank, VIM_PREV_STRING_START}, 1306 {XK_ANY_MOD, XK_dollar, vim_yank, VIM_TO_END_OF_LINE}, 1307 {0, XK_g, vim_enter, 0, NULL, (struct chained_keybind[]) { 1308 {0, XK_g, vim_yank, VIM_TO_START_OF_FILE}, 1309 }, CHAIN_COUNT(1), 1310 }, 1311 {0, XK_i, vim_enter, 0, "inside", (struct chained_keybind[]) { 1312 VIM_DELIMITER_REGIONS(vim_yank, INSIDE) 1313 }, CHAIN_COUNT(16), 1314 }, 1315 {0, XK_a, vim_enter, 0, "around", (struct chained_keybind[]) { 1316 VIM_DELIMITER_REGIONS(vim_yank, AROUND) 1317 }, CHAIN_COUNT(16), 1318 }, 1319 numbers(), 1320 }, CHAIN_COUNT(25) 1321 }, 1322 1323 // insert 1324 {0, XK_i, vim_change_mode, VIM_INSERT}, 1325 {XK_ANY_MOD, XK_I, vim_insert_start}, 1326 {0, XK_a, vim_append}, 1327 {XK_ANY_MOD, XK_A, vim_insert_end}, 1328 {0, XK_o, vim_insert_new_line}, 1329 1330 // visual 1331 {0, XK_v, vim_change_mode, VIM_VISUAL}, 1332 {ShiftMask, XK_V, vim_change_mode, VIM_VISUAL_LINE}, 1333 //{ControlMask, XK_v, vim_change_mode, VIM_VISUAL}, 1334 1335 // deleting 1336 {XK_ANY_MOD, XK_J, vim_remove_newline_at_end}, 1337 {0, XK_x, vim_remove_one_char}, 1338 {XK_ANY_MOD, XK_X, vim_remove_one_char, -1}, 1339 {0, XK_Delete, vim_remove_one_char, +1}, 1340 {ControlMask, XK_BackSpace, vim_delete, VIM_PREV_STRING_START}, 1341 {ControlMask, XK_Delete, vim_delete, VIM_TO_END_OF_STRING}, 1342 {XK_ANY_MOD, XK_D, vim_delete, VIM_TO_END_OF_LINE}, 1343 {0, XK_d, vim_enter, 0, "delete", (struct chained_keybind[]) { 1344 {0, XK_d, vim_delete, VIM_CURRENT_LINE}, 1345 {0, XK_l, vim_remove_one_char}, 1346 {0, XK_h, vim_remove_one_char, -1}, 1347 {XK_ANY_MOD, XK_I, vim_delete, VIM_TO_END_OF_INDENT}, 1348 {XK_ANY_MOD, XK_A, vim_delete, VIM_TO_END_OF_LINE}, 1349 {XK_ANY_MOD, XK_G, vim_delete, VIM_TO_END_OF_FILE}, 1350 {XK_ANY_MOD, XK_w, vim_delete, VIM_TO_START_OF_WORD}, 1351 {XK_ANY_MOD, XK_W, vim_delete, VIM_TO_START_OF_STRING}, 1352 {XK_ANY_MOD, XK_e, vim_delete, VIM_TO_END_OF_WORD}, 1353 {XK_ANY_MOD, XK_E, vim_delete, VIM_TO_END_OF_STRING}, 1354 {XK_ANY_MOD, XK_b, vim_delete, VIM_PREV_WORD_START}, 1355 {XK_ANY_MOD, XK_B, vim_delete, VIM_PREV_STRING_START}, 1356 {XK_ANY_MOD, XK_dollar, vim_delete, VIM_TO_END_OF_LINE}, 1357 {0, XK_g, vim_enter, 0, NULL, (struct chained_keybind[]) { 1358 {0, XK_g, vim_delete, VIM_TO_START_OF_FILE}, 1359 }, CHAIN_COUNT(1), 1360 }, 1361 {0, XK_i, vim_enter, 0, "inside", (struct chained_keybind[]) { 1362 VIM_DELIMITER_REGIONS(vim_delete, INSIDE) 1363 }, CHAIN_COUNT(16), 1364 }, 1365 {0, XK_a, vim_enter, 0, "around", (struct chained_keybind[]) { 1366 VIM_DELIMITER_REGIONS(vim_delete, AROUND) 1367 }, CHAIN_COUNT(16), 1368 }, 1369 numbers(), 1370 {XK_ANY_MOD, XK_0, vim_delete, VIM_TO_START_OF_LINE}, 1371 }, CHAIN_COUNT(27), 1372 }, 1373 // change 1374 {XK_ANY_MOD, XK_C, vim_change, VIM_TO_END_OF_LINE}, 1375 {0, XK_c, vim_enter, 0, "change", (struct chained_keybind[]) { 1376 {0, XK_c, vim_change, VIM_CURRENT_LINE}, 1377 //{0, XK_l, vim_remove_one_char}, 1378 //{0, XK_h, vim_remove_one_char, -1}, 1379 {XK_ANY_MOD, XK_I, vim_change, VIM_TO_END_OF_INDENT}, 1380 {XK_ANY_MOD, XK_A, vim_change, VIM_TO_END_OF_LINE}, 1381 {XK_ANY_MOD, XK_G, vim_change, VIM_TO_END_OF_FILE}, 1382 {XK_ANY_MOD, XK_w, vim_change, VIM_TO_START_OF_WORD}, 1383 {XK_ANY_MOD, XK_W, vim_change, VIM_TO_START_OF_STRING}, 1384 {XK_ANY_MOD, XK_e, vim_change, VIM_TO_END_OF_WORD}, 1385 {XK_ANY_MOD, XK_E, vim_change, VIM_TO_END_OF_STRING}, 1386 {XK_ANY_MOD, XK_b, vim_change, VIM_PREV_WORD_START}, 1387 {XK_ANY_MOD, XK_B, vim_change, VIM_PREV_STRING_START}, 1388 {XK_ANY_MOD, XK_dollar, vim_change, VIM_TO_END_OF_LINE}, 1389 {0, XK_g, vim_enter, 0, NULL, (struct chained_keybind[]) { 1390 {0, XK_g, vim_change, VIM_TO_START_OF_FILE}, 1391 }, CHAIN_COUNT(1), 1392 }, 1393 {0, XK_i, vim_enter, 0, "inside", (struct chained_keybind[]) { 1394 VIM_DELIMITER_REGIONS(vim_change, INSIDE) 1395 }, CHAIN_COUNT(16), 1396 }, 1397 {0, XK_a, vim_enter, 0, "around", (struct chained_keybind[]) { 1398 VIM_DELIMITER_REGIONS(vim_change, AROUND) 1399 }, CHAIN_COUNT(16), 1400 }, 1401 numbers(), 1402 {XK_ANY_MOD, XK_0, vim_change, VIM_TO_START_OF_LINE}, 1403 }, CHAIN_COUNT(27-2), 1404 }, 1405 }; 1406 1407 struct chained_keybind visual_mode_keybinds[] = { 1408 numbers(), 1409 {XK_ANY_MOD, XK_Escape, vim_change_mode, VIM_NORMAL}, 1410 {0, XK_v, vim_change_mode, VIM_NORMAL}, 1411 1412 {0, XK_h, vim_move_on_line, -1}, 1413 {0, XK_j, vim_move_lines, +1}, 1414 {0, XK_k, vim_move_lines, -1}, 1415 {0, XK_l, vim_move_on_line, +1}, 1416 {0, XK_Left, vim_move_on_line, -1}, 1417 {0, XK_Down, vim_move_lines, +1}, 1418 {0, XK_Up, vim_move_lines, -1}, 1419 {0, XK_Right, vim_move_on_line, +1}, 1420 {0, XK_BackSpace, vim_move_on_line, -1}, 1421 {0, XK_Home, vim_home}, 1422 {0, XK_End, vim_end}, 1423 {ControlMask, XK_Right, vim_move, VIM_TO_END_OF_STRING}, 1424 {ControlMask, XK_Left, vim_move, VIM_PREV_STRING_START}, 1425 {0, XK_w, vim_move, VIM_TO_START_OF_WORD}, 1426 {ShiftMask, XK_W, vim_move, VIM_TO_START_OF_STRING}, 1427 {0, XK_e, vim_move, VIM_TO_END_OF_WORD}, 1428 {ShiftMask, XK_E, vim_move, VIM_TO_END_OF_STRING}, 1429 {0, XK_b, vim_move, VIM_PREV_WORD_START}, 1430 {ShiftMask, XK_B, vim_move, VIM_PREV_STRING_START}, 1431 1432 {XK_ANY_MOD, XK_0, vim_move, VIM_TO_START_OF_LINE}, 1433 {XK_ANY_MOD, XK_A, vim_move, VIM_TO_END_OF_LINE}, 1434 {XK_ANY_MOD, XK_G, vim_move, VIM_TO_END_OF_FILE}, 1435 {XK_ANY_MOD, XK_w, vim_move, VIM_TO_START_OF_WORD}, 1436 {XK_ANY_MOD, XK_W, vim_move, VIM_TO_START_OF_STRING}, 1437 {XK_ANY_MOD, XK_e, vim_move, VIM_TO_END_OF_WORD}, 1438 {XK_ANY_MOD, XK_E, vim_move, VIM_TO_END_OF_STRING}, 1439 {XK_ANY_MOD, XK_b, vim_move, VIM_PREV_WORD_START}, 1440 {XK_ANY_MOD, XK_B, vim_move, VIM_PREV_STRING_START}, 1441 {XK_ANY_MOD, XK_dollar, vim_move, VIM_TO_END_OF_LINE}, 1442 {0, XK_i, vim_enter, 0, "inside", (struct chained_keybind[]) { 1443 VIM_DELIMITER_REGIONS(vim_visual_delimit, INSIDE) 1444 }, CHAIN_COUNT(16), 1445 }, 1446 {0, XK_a, vim_enter, 0, "around", (struct chained_keybind[]) { 1447 VIM_DELIMITER_REGIONS(vim_visual_delimit, AROUND) 1448 }, CHAIN_COUNT(16), 1449 }, 1450 1451 {0, XK_d, vim_delete, VIM_CURRENT_SELECTION}, 1452 {0, XK_x, vim_delete, VIM_CURRENT_SELECTION}, 1453 {XK_ANY_MOD, XK_X, vim_delete, VIM_CURRENT_SELECTION}, 1454 {0, XK_y, vim_yank, VIM_CURRENT_SELECTION}, 1455 }; 1456 1457 struct chained_keybind insert_mode_keybinds[] = { 1458 {XK_ANY_MOD, XK_Escape, vim_change_mode, VIM_NORMAL}, 1459 {0, XK_Page_Down, vim_move_down_one_screen_devided_by, 1}, 1460 {0, XK_Page_Up, vim_move_down_one_screen_devided_by, -1}, 1461 {0, XK_Home, vim_home}, 1462 {0, XK_End, vim_end}, 1463 {0, XK_Left, vim_move_on_line, -1}, 1464 {0, XK_Down, vim_move_lines, +1}, 1465 {0, XK_Up, vim_move_lines, -1}, 1466 {0, XK_Right, vim_move_on_line, +1}, 1467 {ControlMask, XK_Right, vim_move, VIM_TO_END_OF_STRING}, 1468 {ControlMask, XK_Left, vim_move, VIM_PREV_STRING_START}, 1469 1470 {0, XK_BackSpace, vim_backspace, -1}, 1471 {0, XK_Delete, vim_remove_one_char}, 1472 {ControlMask, XK_BackSpace, vim_delete, VIM_PREV_STRING_START}, 1473 {ControlMask, XK_Delete, vim_delete, VIM_TO_END_OF_STRING}, 1474 {0, XK_Return, vim_insert_return}, 1475 {0, XK_Tab, vim_insert_tab}, 1476 }; 1477 1478 static void 1479 push_keypress_to_log(KeySym ksym, unsigned int modkey, const char* buf, int len) 1480 { 1481 if (chain_len+1 <= CHAINED_KEYBIND_LEN) { 1482 current_chain[chain_len].ksym = ksym; 1483 current_chain[chain_len].modkey = modkey; 1484 if (len != 0) 1485 current_chain[chain_len].buf = xrealloc(current_chain[chain_len].buf, len); 1486 memcpy(current_chain[chain_len].buf, buf, len); 1487 current_chain[chain_len].len = len; 1488 chain_len++; 1489 } 1490 } 1491 1492 static int 1493 do_chained_keybinds(struct chained_keybind** keybinds, int* keybind_count, KeySym ksym, unsigned int modkey, const char* buf, int len) 1494 { 1495 // remove modifiers 1496 switch(ksym) { 1497 case XK_Shift_L: case XK_Shift_R: 1498 case XK_Alt_L: case XK_Alt_R: 1499 case XK_Control_L: case XK_Control_R: 1500 case XK_Super_L: case XK_Super_R: 1501 case XK_Meta_L: case XK_Meta_R: 1502 case XK_ISO_Group_Latch: case XK_ISO_Group_Shift: case XK_ISO_Group_Lock: 1503 case XK_ISO_Level3_Latch: case XK_ISO_Level3_Lock: case XK_ISO_Level3_Shift: 1504 case XK_ISO_Level5_Latch: case XK_ISO_Level5_Lock: case XK_ISO_Level5_Shift: 1505 case XK_ISO_Level2_Latch: 1506 return 3; 1507 } 1508 push_keypress_to_log(ksym, modkey, buf, len); 1509 for (int i = 0; i < *keybind_count; i++) { 1510 struct chained_keybind keybind = (*keybinds)[i]; 1511 if (ksym == keybind.keysym && match(keybind.mod, modkey)) { 1512 if (!keybind.func) 1513 return 1; 1514 1515 int res = keybind.func(keybind.custom_mode); 1516 1517 // TODO: add enums for random values 1518 if (!res) 1519 return 1; 1520 if (res == 2) 1521 continue; 1522 1523 if (res < 0) { 1524 *keybinds = NULL; 1525 *keybind_count = 0; 1526 if (res == -2) 1527 return 4; 1528 return 2; 1529 } 1530 if (res == 1) { 1531 soft_assert(keybind.next_keybinds && keybind.keybind_count, 1532 *keybinds = NULL; 1533 return 0; 1534 ); 1535 *keybinds = keybind.next_keybinds; 1536 *keybind_count = keybind.keybind_count; 1537 return 1; 1538 } 1539 } 1540 } 1541 *keybinds = NULL; 1542 return 0; 1543 } 1544 1545 static const char* 1546 get_ksym_string_no_xk(KeySym ksym, unsigned int modkey) 1547 { 1548 static char str[1024]; 1549 *str = 0; 1550 char* ksname; 1551 if (!(ksname = XKeysymToString(ksym))) 1552 ksname = "(no name)"; 1553 else if (strcmp(ksname, "XK_") == 0) 1554 ksname += 3; 1555 1556 if (modkey != XK_ANY_MOD) { 1557 if (modkey & ControlMask) 1558 strcat(str, "C-"); 1559 if (modkey & MODKEY) 1560 strcat(str, "M-"); 1561 } 1562 strncat(str, ksname, sizeof(str) - strlen(str) - 1); 1563 1564 return str; 1565 } 1566 1567 static const char* 1568 current_key_chain_string(int dont_add_minus_end) 1569 { 1570 if (!chain_len) 1571 return NULL; 1572 1573 static char buf[STATUS_BAR_MAX_LEN]; 1574 *buf = 0; 1575 int len = 0; 1576 1577 for(int i = 0; i < chain_len; i++) { 1578 const char* str = get_ksym_string_no_xk(current_chain[i].ksym, current_chain[i].modkey); 1579 int new_len = strlen(str); 1580 if (new_len + len < STATUS_BAR_MAX_LEN) { 1581 strcat(buf, str); 1582 len += new_len; 1583 } 1584 1585 if (len + 1 < STATUS_BAR_MAX_LEN) { 1586 if (i + 1 >= chain_len) { 1587 if (!dont_add_minus_end) 1588 strcat(buf, "-"); 1589 } else { 1590 strcat(buf, " "); 1591 } 1592 len++; 1593 } 1594 } 1595 1596 return buf; 1597 } 1598 1599 static const char* 1600 current_keybind_options(struct chained_keybind* keybinds, int keybind_len) 1601 { 1602 if (!keybind_len || !keybinds) 1603 return "[no options]"; 1604 static char buf[STATUS_BAR_MAX_LEN]; 1605 strcpy(buf, "["); 1606 int len = 0; 1607 1608 for(int i = 0; i < keybind_len; i++) { 1609 switch(keybinds[i].keysym) { 1610 case XK_0: case XK_1: case XK_2: 1611 case XK_3: case XK_4: case XK_5: 1612 case XK_6: case XK_7: case XK_8: 1613 case XK_9: 1614 if (!keybinds[i].description) 1615 continue; 1616 } 1617 1618 const char* str = get_ksym_string_no_xk(keybinds[i].keysym, keybinds[i].mod); 1619 int new_len = strlen(str); 1620 if (new_len + len < STATUS_BAR_MAX_LEN) { 1621 strcat(buf, str); 1622 len += new_len; 1623 } 1624 if (keybinds[i].description) { 1625 new_len = strlen(keybinds[i].description) + sizeof(" → ") - 1; 1626 if (new_len + len < STATUS_BAR_MAX_LEN) { 1627 strcat(buf, " → "); 1628 strcat(buf, keybinds[i].description); 1629 len += new_len; 1630 } 1631 } 1632 1633 if (i + 1 < keybind_len && len + 2 < STATUS_BAR_MAX_LEN) { 1634 strcat(buf, ", "); 1635 len += 2; 1636 } 1637 } 1638 if (len + 1 < STATUS_BAR_MAX_LEN) 1639 strcat(buf, "]"); 1640 return buf; 1641 } 1642 1643 ///////////////////////////////////////////////// 1644 // callbacks 1645 // 1646 1647 #include "extensions/default_status_bar.h" 1648 #include "extensions/window_modes/choose_one_of_selection.h" 1649 #include "extensions/undo.h" 1650 #include "extensions/keep_cursor_col.h" 1651 #include "extensions/move_selection_with_cursor.h" 1652 #include "extensions/startup_message.h" 1653 1654 static int keypress_actions(KeySym keycode, int modkey, const char* buf, int len); 1655 static int vim_paste_callback(struct file_buffer* fb, char* data, int len); 1656 static const struct extension vim = { 1657 .keypress = keypress_actions, 1658 .fb_paste = vim_paste_callback, 1659 }; 1660 1661 struct extension_meta config_extensions[] = { 1662 {.e = startup_message, .enabled = 1}, 1663 {.e = file_browser, .enabled = 1}, 1664 {.e = search_open_fb, .enabled = 1}, 1665 {.e = search_keywords_open_fb, .enabled = 1}, 1666 1667 {.e = syntax_e, .enabled = 1}, 1668 1669 {.e = undo, .enabled = 1}, 1670 {.e = keep_cursor_col, .enabled = 1}, 1671 {.e = move_selection_with_cursor, .enabled = 1}, 1672 {.e = vim, .enabled = 1}, 1673 1674 {.e = default_status_bar, .enabled = 1}, 1675 1676 {.end = 1} 1677 }; 1678 1679 struct extension_meta* extensions = config_extensions; 1680 1681 static int 1682 vim_paste_callback(struct file_buffer* fb, char* data, int len) 1683 { 1684 int offset = focused_window->fb_index; 1685 if (data[len-1] == '\n') { 1686 if (paste_behind) { 1687 offset = fb_seek_char_backwards(fb, offset, '\n'); 1688 offset = MAX(offset, 0); 1689 } else { 1690 offset = fb_seek_char(fb, offset, '\n'); 1691 if (offset < 0) 1692 offset = fb->len-1; 1693 else 1694 offset++; 1695 } 1696 } else if (paste_behind) { 1697 offset = MAX(offset - 1, 0); 1698 } 1699 fb_insert(fb, data, len, offset, 1); 1700 window_node_move_all_cursors_on_same_fb(&root_node, (paste_behind) ? NULL : focused_node, focused_window->fb_index, offset, 1701 wb_move_offset_relative, len, CURSOR_COMMAND_MOVEMENT); 1702 paste_behind = 0; 1703 return 0; 1704 } 1705 1706 static int 1707 vim_repeat_last_command(int custom_mode) 1708 { 1709 static int recursioin_prevention = 0; 1710 if (recursioin_prevention || !previous_chain_len) 1711 return -2; 1712 recursioin_prevention = 1; 1713 vim_change_mode(VIM_NORMAL); 1714 1715 int count = vim_chain_parse_count(0); 1716 while(count--) { 1717 chain_len = 0; 1718 last_used_command_index = 0; 1719 for(int i = 0; i < previous_chain_len; i++) { 1720 struct keypress_logg pc = previous_chain[i]; 1721 keypress_actions(pc.ksym, pc.modkey, pc.buf, pc.len); 1722 } 1723 } 1724 1725 vim_change_mode(VIM_NORMAL); 1726 recursioin_prevention = 0; 1727 writef_to_status_bar("repeated %d inputs", previous_chain_len); 1728 return -2; 1729 } 1730 1731 void insert_string(const char* buf, int len) 1732 { 1733 struct file_buffer* fb = get_fb(focused_window); 1734 1735 if (buf[0] >= 32 || len > 1) { 1736 fb_delete_selection(fb); 1737 fb_insert(fb, buf, len, focused_window->cursor_offset, 0); 1738 window_node_move_all_cursors_on_same_fb(&root_node, NULL, focused_window->fb_index, focused_window->cursor_offset, 1739 wb_move_offset_relative, len, CURSOR_COMMAND_MOVEMENT); 1740 } else { 1741 writef_to_status_bar("unhandled control character 0x%x\n", buf[0]); 1742 } 1743 } 1744 1745 int 1746 keypress_actions(KeySym keycode, int modkey, const char* buf, int len) 1747 { 1748 // current keybind that is set 1749 static struct chained_keybind* keybinds; 1750 static int keybind_len; 1751 if (vim_mode == VIM_NORMAL) { 1752 if (!keybinds) { 1753 keybinds = normal_mode_keybinds; 1754 keybind_len = LEN(normal_mode_keybinds); 1755 } 1756 } else if (vim_mode == VIM_INSERT) { 1757 if (!keybinds) { 1758 keybinds = insert_mode_keybinds; 1759 keybind_len = LEN(insert_mode_keybinds); 1760 } 1761 } else if (vim_mode == VIM_VISUAL) { 1762 if (!keybinds) { 1763 keybinds = visual_mode_keybinds; 1764 keybind_len = LEN(visual_mode_keybinds); 1765 } 1766 } 1767 1768 char tmp_status_bar[STATUS_BAR_MAX_LEN]; 1769 memcpy(tmp_status_bar, status_bar_contents, STATUS_BAR_MAX_LEN); 1770 1771 int last_mode = vim_mode; 1772 int res = do_chained_keybinds(&keybinds, &keybind_len, keycode, modkey, buf, len); 1773 1774 int status_bar_changed = strcmp(tmp_status_bar, status_bar_contents) != 0; 1775 1776 if (res == 3) 1777 return 0; 1778 if (res == 2) 1779 last_used_command_index = MAX(0, chain_len - 1); 1780 if (!res) { 1781 if (vim_mode == VIM_INSERT) { 1782 insert_string(buf, len); 1783 } else { 1784 if (vim_mode == VIM_NORMAL) { 1785 const char* chain_str = current_key_chain_string(1); 1786 writef_to_status_bar("keybind \"%s\" does not exist", chain_str); 1787 1788 // reset chain 1789 chain_len = 0; 1790 last_used_command_index = 0; 1791 } else { 1792 writef_to_status_bar("keybind does not exist"); 1793 } 1794 } 1795 } else if ((res == 2 || res == 4) && vim_mode == VIM_NORMAL) { 1796 // reset chain 1797 if (chain_len > 1 && !status_bar_changed && last_mode == vim_mode) { 1798 const char* chain_str = current_key_chain_string(1); 1799 if (chain_str) 1800 writef_to_status_bar("%s", chain_str); 1801 } 1802 if (res != 4) { 1803 vim_copy_log(&previous_chain, previous_chain_len, current_chain, chain_len); 1804 previous_chain_len = chain_len; 1805 } 1806 chain_len = 0; 1807 last_used_command_index = 0; 1808 } else if (vim_mode == VIM_NORMAL) { 1809 const char* chain_str = current_key_chain_string(0); 1810 const char* options = current_keybind_options(keybinds, keybind_len); 1811 if (chain_str && !status_bar_changed) 1812 writef_to_status_bar("%s %s", chain_str, options); 1813 } else { 1814 const char* options = current_keybind_options(keybinds, keybind_len); 1815 1816 if (chain_len > 0 && strcmp(options, "[no options]") != 0) { 1817 const char* latest_keypress = get_ksym_string_no_xk(current_chain[chain_len - 1].ksym, current_chain[chain_len - 1].modkey); 1818 writef_to_status_bar("%s- %s", latest_keypress, options); 1819 } 1820 } 1821 return 0; 1822 } 1823 1824 // TODO: gg goes to line but not G