keys_basic.c (104268B)
1 /* FIXME: all movement commands that modify the selection should first check if the selection is valid! */ 2 /* FIXME: should motion callbacks be ignored in visual mode as they currently are? */ 3 /* FIXME: check allowed modes at beginning of functions */ 4 /* FIXME: the stacks here are shared for all views which can cause weird 5 behavior, but I'm not sure what would be more logical */ 6 /* FIXME: cursor isn't shown properly on spaces at end of softlines */ 7 /* FIXME: selection is sometimes not reset when it is "clicked away" */ 8 /* FIXME: use weak cursor */ 9 /* FIXME: spaces at end of soft line are weird in bidi text 10 -> space is hidden when e.g. ltr text left and rtl text on right is wrapped */ 11 /* FIXME: some weird things still happen with selections staying as "ghosts" 12 and being deleted at some later time even though they're not shown anymore */ 13 /* FIXME: delete everything concerned with selections in insert mode since 14 they are now not allowed at all */ 15 /* FIXME: a lot of error checking in the individual functions may be redundant 16 now that more checking is done beforehand for the allowed keys */ 17 /* FIXME: sort functions a bit better, maybe split file */ 18 /* FIXME: documentation */ 19 #include <stdio.h> 20 #include <ctype.h> 21 #include <stdlib.h> 22 23 #include <X11/Xlib.h> 24 #include <X11/Xutil.h> 25 #include <pango/pangoxft.h> 26 #include <X11/extensions/Xdbe.h> 27 #include <X11/keysym.h> 28 #include <X11/XF86keysym.h> 29 #include <X11/cursorfont.h> 30 31 #if ENABLE_UTF8PROC 32 #include "utf8proc.h" 33 #endif 34 35 #include "util.h" 36 #include "assert.h" 37 #include "memory.h" 38 #include "common.h" 39 #include "txtbuf.h" 40 #include "undo.h" 41 #include "cache.h" 42 #include "window.h" 43 #include "buffer.h" 44 #include "view.h" 45 #include "search.h" 46 47 #include "keys.h" 48 #include "keys_basic.h" 49 #include "keys_command.h" 50 #include "configparser.h" 51 52 /************************************************************************* 53 * Declarations for all functions that can be used in the configuration. * 54 *************************************************************************/ 55 56 static struct action cursor_left(ledit_view *view, char *text, size_t len); 57 static struct action cursor_right(ledit_view *view, char *text, size_t len); 58 static struct action cursor_up(ledit_view *view, char *text, size_t len); 59 static struct action cursor_down(ledit_view *view, char *text, size_t len); 60 static struct action break_line(ledit_view *view, char *text, size_t len); 61 static struct action return_to_normal(ledit_view *view, char *text, size_t len); 62 static struct action enter_insert(ledit_view *view, char *text, size_t len); 63 static struct action cursor_to_beginning(ledit_view *view, char *text, size_t len); 64 static struct action key_0(ledit_view *view, char *text, size_t len); 65 static struct action push_0(ledit_view *view, char *text, size_t len); 66 static struct action push_1(ledit_view *view, char *text, size_t len); 67 static struct action push_2(ledit_view *view, char *text, size_t len); 68 static struct action push_3(ledit_view *view, char *text, size_t len); 69 static struct action push_4(ledit_view *view, char *text, size_t len); 70 static struct action push_5(ledit_view *view, char *text, size_t len); 71 static struct action push_6(ledit_view *view, char *text, size_t len); 72 static struct action push_7(ledit_view *view, char *text, size_t len); 73 static struct action push_8(ledit_view *view, char *text, size_t len); 74 static struct action push_9(ledit_view *view, char *text, size_t len); 75 static struct action delete(ledit_view *view, char *text, size_t len); 76 static struct action enter_visual(ledit_view *view, char *text, size_t len); 77 static struct action switch_selection_end(ledit_view *view, char *text, size_t len); 78 static struct action clipcopy(ledit_view *view, char *text, size_t len); 79 static struct action clippaste(ledit_view *view, char *text, size_t len); 80 static struct action show_line(ledit_view *view, char *text, size_t len); 81 static struct action enter_commandedit(ledit_view *view, char *text, size_t len); 82 static struct action enter_searchedit_backward(ledit_view *view, char *text, size_t len); 83 static struct action enter_searchedit_forward(ledit_view *view, char *text, size_t len); 84 static struct action key_search_next(ledit_view *view, char *text, size_t len); 85 static struct action key_search_prev(ledit_view *view, char *text, size_t len); 86 static struct action undo(ledit_view *view, char *text, size_t len); 87 static struct action redo(ledit_view *view, char *text, size_t len); 88 static struct action insert_mode_insert_text(ledit_view *view, char *text, size_t len); 89 static struct action repeat_command(ledit_view *view, char *text, size_t len); 90 static struct action screen_up(ledit_view *view, char *text, size_t len); 91 static struct action screen_down(ledit_view *view, char *text, size_t len); 92 static struct action scroll_with_cursor_up(ledit_view *view, char *text, size_t len); 93 static struct action scroll_with_cursor_down(ledit_view *view, char *text, size_t len); 94 static struct action scroll_lines_up(ledit_view *view, char *text, size_t len); 95 static struct action scroll_lines_down(ledit_view *view, char *text, size_t len); 96 static struct action move_to_line(ledit_view *view, char *text, size_t len); 97 static struct action paste_normal(ledit_view *view, char *text, size_t len); 98 static struct action paste_normal_backwards(ledit_view *view, char *text, size_t len); 99 static struct action change(ledit_view *view, char *text, size_t len); 100 static struct action move_to_eol(ledit_view *view, char *text, size_t len); 101 static struct action insert_mark(ledit_view *view, char *text, size_t len); 102 static struct action jump_to_mark(ledit_view *view, char *text, size_t len); 103 static struct action next_word(ledit_view *view, char *text, size_t len); 104 static struct action next_word_end(ledit_view *view, char *text, size_t len); 105 static struct action next_bigword(ledit_view *view, char *text, size_t len); 106 static struct action next_bigword_end(ledit_view *view, char *text, size_t len); 107 static struct action prev_word(ledit_view *view, char *text, size_t len); 108 static struct action prev_bigword(ledit_view *view, char *text, size_t len); 109 static struct action append_after_eol(ledit_view *view, char *text, size_t len); 110 static struct action append_after_cursor(ledit_view *view, char *text, size_t len); 111 static struct action append_line_above(ledit_view *view, char *text, size_t len); 112 static struct action append_line_below(ledit_view *view, char *text, size_t len); 113 static struct action find_next_char_forwards(ledit_view *view, char *text, size_t len); 114 static struct action find_next_char_backwards(ledit_view *view, char *text, size_t len); 115 static struct action find_char_forwards(ledit_view *view, char *text, size_t len); 116 static struct action find_char_backwards(ledit_view *view, char *text, size_t len); 117 static struct action change_to_eol(ledit_view *view, char *text, size_t len); 118 static struct action delete_to_eol(ledit_view *view, char *text, size_t len); 119 static struct action delete_chars_forwards(ledit_view *view, char *text, size_t len); 120 static struct action delete_chars_backwards(ledit_view *view, char *text, size_t len); 121 static struct action delete_chars_forwards_multiline(ledit_view *view, char *text, size_t len); 122 static struct action delete_chars_backwards_multiline(ledit_view *view, char *text, size_t len); 123 static struct action delete_graphemes_forwards(ledit_view *view, char *text, size_t len); 124 static struct action delete_graphemes_backwards(ledit_view *view, char *text, size_t len); 125 static struct action delete_graphemes_forwards_multiline(ledit_view *view, char *text, size_t len); 126 static struct action delete_graphemes_backwards_multiline(ledit_view *view, char *text, size_t len); 127 static struct action yank(ledit_view *view, char *text, size_t len); 128 static struct action yank_lines(ledit_view *view, char *text, size_t len); 129 static struct action uppercase(ledit_view *view, char *text, size_t len); 130 static struct action lowercase(ledit_view *view, char *text, size_t len); 131 static struct action replace(ledit_view *view, char *text, size_t len); 132 static struct action cursor_to_first_non_ws(ledit_view *view, char *text, size_t len); 133 static struct action join_lines(ledit_view *view, char *text, size_t len); 134 static struct action insert_at_beginning(ledit_view *view, char *text, size_t len); 135 static struct action toggle_hard_line_based(ledit_view *view, char *text, size_t len); 136 137 /*********************************************** 138 * String-function mapping for config parsing. * 139 ***********************************************/ 140 141 /* FIXME: delete-backwards, delete-forwards should be renamed; 142 *key functions should be renamed (they're very vague) */ 143 144 typedef enum { 145 KEY_FLAG_NONE = 0, 146 KEY_FLAG_JUMP_TO_CURSOR = 1, 147 KEY_FLAG_LOCK_ALLOWED = 2 148 } basic_key_cb_flags; 149 150 typedef struct action (*basic_key_cb_func)(ledit_view *, char *, size_t); 151 152 struct basic_key_cb { 153 char *text; 154 basic_key_cb_func func; 155 basic_key_cb_flags flags; 156 ledit_mode allowed_modes; 157 }; 158 159 int 160 basic_key_cb_modemask_is_valid(basic_key_cb *cb, ledit_mode modes) { 161 return (~cb->allowed_modes & modes) == 0; 162 } 163 164 /* FIXME: make functions work in more modes (e.g. cursor-to-first-non-whitespace) */ 165 static struct basic_key_cb basic_key_cb_map[] = { 166 {"append-after-cursor", &append_after_cursor, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, 167 {"append-after-eol", &append_after_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, 168 {"append-line-above", &append_line_above, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, 169 {"append-line-below", &append_line_below, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, 170 {"break-line", &break_line, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 171 {"change", &change, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, 172 {"change-to-eol", &change_to_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, 173 {"clipboard-copy", &clipcopy, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 174 {"clipboard-paste", &clippaste, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, 175 {"cursor-down", &cursor_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, VISUAL|INSERT|NORMAL}, 176 {"cursor-left", &cursor_left, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, VISUAL|INSERT|NORMAL}, 177 {"cursor-right", &cursor_right, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, VISUAL|INSERT|NORMAL}, 178 {"cursor-to-beginning", &cursor_to_beginning, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 179 {"cursor-to-first-non-whitespace", &cursor_to_first_non_ws, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 180 {"cursor-up", &cursor_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, VISUAL|INSERT|NORMAL}, 181 {"delete", &delete, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL|INSERT}, 182 {"delete-chars-backwards", &delete_chars_backwards, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 183 {"delete-chars-backwards-multiline", &delete_chars_backwards_multiline, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 184 {"delete-chars-forwards", &delete_chars_forwards, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 185 {"delete-chars-forwards-multiline", &delete_chars_forwards_multiline, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 186 {"delete-graphemes-backwards", &delete_graphemes_backwards, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 187 {"delete-graphemes-backwards-multiline", &delete_graphemes_backwards_multiline, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 188 {"delete-graphemes-forwards", &delete_graphemes_forwards, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 189 {"delete-graphemes-forwards-multiline", &delete_graphemes_forwards_multiline, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 190 {"delete-to-eol", &delete_to_eol, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 191 {"enter-commandedit", &enter_commandedit, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 192 {"enter-insert", &enter_insert, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|VISUAL}, 193 {"enter-searchedit-backwards", &enter_searchedit_backward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL}, 194 {"enter-searchedit-forwards", &enter_searchedit_forward, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL}, 195 {"enter-visual", &enter_visual, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 196 {"find-char-backwards", &find_char_backwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 197 {"find-char-forwards", &find_char_forwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 198 {"find-next-char-backwards", &find_next_char_backwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 199 {"find-next-char-forwards", &find_next_char_forwards, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 200 {"insert-at-beginning", &insert_at_beginning, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, 201 {"insert-mark", &insert_mark, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 202 {"insert-text", &insert_mode_insert_text, KEY_FLAG_JUMP_TO_CURSOR, INSERT}, 203 {"join-lines", &join_lines, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 204 {"jump-to-mark", &jump_to_mark, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 205 {"key-0", &key_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 206 {"lowercase", &lowercase, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 207 {"move-to-eol", &move_to_eol, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 208 {"move-to-line", &move_to_line, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 209 {"next-bigword", &next_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 210 {"next-bigword-end", &next_bigword_end, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 211 {"next-word", &next_word, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 212 {"next-word-end", &next_word_end, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 213 {"paste-buffer", &paste_normal, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 214 {"paste-buffer-backwards", &paste_normal_backwards, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 215 {"previous-bigword", &prev_bigword, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 216 {"previous-word", &prev_word, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 217 {"push-0", &push_0, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 218 {"push-1", &push_1, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 219 {"push-2", &push_2, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 220 {"push-3", &push_3, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 221 {"push-4", &push_4, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 222 {"push-5", &push_5, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 223 {"push-6", &push_6, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 224 {"push-7", &push_7, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 225 {"push-8", &push_8, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 226 {"push-9", &push_9, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 227 {"redo", &redo, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 228 {"repeat-command", &repeat_command, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, 229 {"replace", &replace, KEY_FLAG_JUMP_TO_CURSOR, NORMAL}, 230 {"return-to-normal", &return_to_normal, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 231 {"screen-down", &screen_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, 232 {"screen-up", &screen_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, 233 {"scroll-lines-down", &scroll_lines_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, 234 {"scroll-lines-up", &scroll_lines_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, 235 {"scroll-with-cursor-down", &scroll_with_cursor_down, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, 236 {"scroll-with-cursor-up", &scroll_with_cursor_up, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, 237 {"search-next", &key_search_next, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL}, 238 {"search-previous", &key_search_prev, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT|VISUAL}, 239 {"show-line", &show_line, KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 240 {"switch-selection-end", &switch_selection_end, KEY_FLAG_JUMP_TO_CURSOR, VISUAL}, 241 {"toggle-hard-line-based", &toggle_hard_line_based, KEY_FLAG_NONE|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 242 {"undo", &undo, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 243 {"uppercase", &uppercase, KEY_FLAG_JUMP_TO_CURSOR, NORMAL|INSERT}, 244 {"yank", &yank, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|VISUAL|INSERT}, 245 {"yank-lines", &yank_lines, KEY_FLAG_JUMP_TO_CURSOR|KEY_FLAG_LOCK_ALLOWED, NORMAL|INSERT}, 246 }; 247 248 GEN_CB_MAP_HELPERS(basic_key_cb_map, basic_key_cb, text) 249 250 /*************************************************** 251 * General global variables and utility functions. * 252 ***************************************************/ 253 254 enum key_type { 255 KEY_INVALID = 0, 256 KEY_MOTION_CHAR = 4, 257 KEY_MOTION_LINE = 8, 258 KEY_MOTION = 4|8, 259 KEY_MOTIONALLOWED = 16, 260 KEY_NUMBER = 32, 261 KEY_NUMBERALLOWED = 64, 262 KEY_ANY = 0xFF 263 }; 264 265 /* note: this is supposed to be global for all views/buffers */ 266 static int paste_buffer_line_based = 0; 267 static txtbuf *paste_buffer = NULL; 268 static int last_lines_scrolled = -1; 269 270 struct repetition_stack_elem { 271 char *key_text; 272 size_t len; 273 KeySym sym; 274 unsigned int key_state; 275 int lang_index; 276 }; 277 278 static struct { 279 int replaying; 280 size_t len, alloc, cur_pos; 281 struct repetition_stack_elem *stack; 282 size_t tmp_len, tmp_alloc; 283 struct repetition_stack_elem *tmp_stack; 284 } repetition_stack = {0, 0, 0, 0, NULL, 0, 0, NULL}; 285 286 typedef void (*motion_callback)(ledit_view *view, size_t line, size_t char_pos, enum key_type type); 287 288 struct key_stack_elem { 289 enum key_type key; 290 enum key_type followup; /* allowed keys to complete the keybinding */ 291 /* callback function that motion commands call to complete a command - 292 * line and char_pos already include the repetition stored in this stack 293 * element; type is the type of motion command (used to determine if 294 * the command should operate on lines or chars) */ 295 motion_callback motion_cb; 296 int count; /* number of repetitions */ 297 }; 298 299 static struct { 300 size_t len, alloc; 301 struct key_stack_elem *stack; 302 } key_stack = {0, 0, NULL}; 303 304 static struct action (*grab_char_cb)(ledit_view *view, char *text, size_t len) = NULL; 305 306 void 307 basic_key_cleanup(void) { 308 /* this should be safe since push_repetition_stack sets all new 309 elements to NULL when resizing the stack */ 310 for (size_t i = 0; i < repetition_stack.alloc; i++) { 311 free(repetition_stack.stack[i].key_text); 312 } 313 for (size_t i = 0; i < repetition_stack.tmp_alloc; i++) { 314 free(repetition_stack.tmp_stack[i].key_text); 315 } 316 free(repetition_stack.stack); 317 free(repetition_stack.tmp_stack); 318 free(key_stack.stack); 319 } 320 321 /* No, this isn't actually a stack. So what? */ 322 static struct repetition_stack_elem *push_repetition_stack(void); 323 static void finalize_repetition_stack(void); 324 static struct repetition_stack_elem *get_cur_repetition_stack_elem(void); 325 static void unwind_repetition_stack(void); 326 static void advance_repetition_stack(void); 327 static void discard_repetition_stack(void); 328 329 static int key_stack_empty(void); 330 static struct key_stack_elem *push_key_stack(void); 331 static struct key_stack_elem *peek_key_stack(void); 332 static struct key_stack_elem *pop_key_stack(void); 333 void clear_key_stack(void); 334 335 static void move_cursor_left_right(ledit_view *view, int dir, int allow_illegal_index); 336 static void move_cursor_up_down(ledit_view *view, int dir); 337 static void push_num(ledit_view *view, int num); 338 static void delete_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type); 339 static void yank_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type); 340 static void get_new_line_softline( 341 ledit_view *view, size_t cur_line, size_t cur_index, 342 int movement, size_t *new_line_ret, int *new_softline_ret 343 ); 344 static void move_cursor_logically(ledit_view *view, int movement_dir, int allow_illegal_index); 345 static void change_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type); 346 static void push_undo_empty_insert(ledit_view *view, size_t line, size_t index, int start_group); 347 static void move_half_screen(ledit_view *view, int movement); 348 349 static struct action 350 view_locked_error(ledit_view *view) { 351 window_show_message(view->window, view->lock_text, -1); 352 return (struct action){ACTION_NONE, NULL}; 353 } 354 355 #define CHECK_VIEW_LOCKED(view) if (view->lock_text) return view_locked_error(view) 356 #define CHECK_VIEW_LOCKED_NORETURN(view) if (view->lock_text) (void)view_locked_error(view) 357 358 static int 359 key_stack_empty(void) { 360 return key_stack.len == 0; 361 } 362 363 static struct key_stack_elem * 364 push_key_stack(void) { 365 struct key_stack_elem *e; 366 if (key_stack.len >= key_stack.alloc) { 367 size_t new_alloc = ideal_array_size(key_stack.alloc, add_sz(key_stack.len, 1)); 368 key_stack.stack = ledit_reallocarray( 369 key_stack.stack, new_alloc, sizeof(struct key_stack_elem) 370 ); 371 key_stack.alloc = new_alloc; 372 } 373 e = &key_stack.stack[key_stack.len]; 374 e->key = KEY_INVALID; 375 e->followup = KEY_INVALID; 376 e->motion_cb = NULL; 377 e->count = 0; 378 key_stack.len++; 379 return e; 380 } 381 382 /* Note: for peek and pop, the returned element is only valid 383 * until the next element is pushed */ 384 /* Note on the note: that's not entirely true for peek */ 385 static struct key_stack_elem * 386 peek_key_stack(void) { 387 if (key_stack.len > 0) 388 return &key_stack.stack[key_stack.len - 1]; 389 return NULL; 390 } 391 392 static struct key_stack_elem * 393 pop_key_stack(void) { 394 if (key_stack.len > 0) { 395 key_stack.len--; 396 return &key_stack.stack[key_stack.len]; 397 } 398 return NULL; 399 } 400 401 void 402 clear_key_stack(void) { 403 key_stack.len = 0; 404 } 405 406 static struct action 407 err_invalid_key(ledit_view *view) { 408 window_show_message(view->window, "Invalid key", -1); 409 clear_key_stack(); 410 discard_repetition_stack(); 411 return (struct action){ACTION_NONE, NULL}; 412 } 413 414 /* 415 * Get the number of times a command should be repeated and the motion 416 * callback, if there was one on the stack. 417 * Note that *cb_ret is set to NULL if a non-null address is given and 418 * there is no motion callback on the stack. 419 * An empty stack or a stack where the top element has a count of 0 420 * leads to 0 being returned. 421 * In case of error, -1 is returned: 422 * - When the top or second element is not a number key but also does 423 * not comtain a motion callback. 424 * - When the stack is not empty after removing the top element and 425 * possibly a second one if the top one was a number key. 426 */ 427 static int 428 get_key_repeat_and_motion_cb(ledit_view *view, motion_callback *cb_ret) { 429 int num = 1; 430 struct key_stack_elem *e = pop_key_stack(); 431 if (e != NULL) { 432 if (e->key & KEY_NUMBER) { 433 num = e->count; 434 e = pop_key_stack(); 435 } else if (e->count == 0) { 436 num = 0; 437 } 438 if (e != NULL) { 439 int new_count = e->count > 0 ? e->count : 1; 440 if (INT_MAX / new_count < num) { 441 window_show_message( 442 view->window, 443 "Integer overflow in key repetition", -1 444 ); 445 num = INT_MAX; 446 } 447 num *= new_count; 448 } 449 } else { 450 num = 0; 451 } 452 if (e != NULL && !(e->key & KEY_NUMBER) && e->motion_cb == NULL) 453 num = -1; 454 else if (!key_stack_empty()) 455 num = -1; 456 if (cb_ret != NULL && e != NULL && e->motion_cb != NULL) 457 *cb_ret = e->motion_cb; 458 else if (cb_ret != NULL) 459 *cb_ret = NULL; 460 return num; 461 } 462 463 /* 464 * Get the number of times a command should be repeated, or -1 if anything 465 * invalid was on the stack - this is for commands that just take a repeat 466 * count and nothing else (cursor movement keys are different because they 467 * can use other elements on the key stack too, for instance call a callback 468 * as is done for deletion. 469 * Note that an empty stack leads to 0 being returned even though most commands 470 * use 1 as repeat then so the caller can distinguish between empty stack and 471 * a repetition count of 1. 472 */ 473 static int 474 get_key_repeat(void) { 475 int num = 1; 476 struct key_stack_elem *e = pop_key_stack(); 477 if (e != NULL) { 478 if (e->key & KEY_NUMBER) { 479 num = e->count > 0 ? e->count : 1; 480 e = pop_key_stack(); 481 } 482 if (e != NULL) { 483 /* the key was not a number, or there was another 484 element under it on the stack -> error */ 485 num = -1; 486 } 487 } else { 488 num = 0; 489 } 490 clear_key_stack(); 491 return num; 492 } 493 494 static struct repetition_stack_elem * 495 push_repetition_stack(void) { 496 struct repetition_stack_elem *e; 497 if (repetition_stack.tmp_len >= repetition_stack.tmp_alloc) { 498 size_t new_alloc = ideal_array_size(repetition_stack.tmp_alloc, add_sz(repetition_stack.tmp_len, 1)); 499 repetition_stack.tmp_stack = ledit_reallocarray( 500 repetition_stack.tmp_stack, 501 new_alloc, sizeof(struct repetition_stack_elem) 502 ); 503 for (size_t i = repetition_stack.tmp_alloc; i < new_alloc; i++) { 504 repetition_stack.tmp_stack[i].key_text = NULL; 505 } 506 repetition_stack.tmp_alloc = new_alloc; 507 } 508 e = &repetition_stack.tmp_stack[repetition_stack.tmp_len]; 509 e->key_text = NULL; 510 e->len = 0; 511 e->sym = 0; 512 e->key_state = 0; 513 e->lang_index = 0; 514 repetition_stack.tmp_len++; 515 return e; 516 } 517 518 static struct repetition_stack_elem * 519 get_cur_repetition_stack_elem(void) { 520 if (repetition_stack.cur_pos >= repetition_stack.len) 521 return NULL; 522 return &repetition_stack.stack[repetition_stack.cur_pos]; 523 } 524 525 static void 526 unwind_repetition_stack(void) { 527 repetition_stack.cur_pos = 0; 528 } 529 530 static void 531 discard_repetition_stack(void) { 532 if (repetition_stack.replaying) 533 return; 534 for (size_t i = 0; i < repetition_stack.tmp_len; i++) { 535 free(repetition_stack.tmp_stack[i].key_text); 536 repetition_stack.tmp_stack[i].key_text = NULL; 537 } 538 repetition_stack.tmp_len = 0; 539 } 540 541 static void 542 advance_repetition_stack(void) { 543 repetition_stack.cur_pos++; 544 } 545 546 static void 547 finalize_repetition_stack(void) { 548 if (repetition_stack.replaying) 549 return; 550 size_t tmp; 551 for (size_t i = 0; i < repetition_stack.len; i++) { 552 free(repetition_stack.stack[i].key_text); 553 repetition_stack.stack[i].key_text = NULL; 554 } 555 struct repetition_stack_elem *tmpstack; 556 repetition_stack.len = repetition_stack.tmp_len; 557 repetition_stack.tmp_len = 0; 558 tmp = repetition_stack.alloc; 559 repetition_stack.alloc = repetition_stack.tmp_alloc; 560 repetition_stack.tmp_alloc = tmp; 561 tmpstack = repetition_stack.stack; 562 repetition_stack.stack = repetition_stack.tmp_stack; 563 repetition_stack.tmp_stack = tmpstack; 564 } 565 566 /* get the new line and softline when moving 'movement' softlines 567 (or hardlines if hard_line_based is set) up or 568 down (negative means up, positive means down) */ 569 static void 570 get_new_line_softline( 571 ledit_view *view, size_t cur_line, size_t cur_index, int movement, 572 size_t *new_line_ret, int *new_softline_ret) { 573 if (view->buffer->hard_line_based) { 574 if (movement < 0 && (size_t)-movement > cur_line) 575 *new_line_ret = 0; 576 else 577 *new_line_ret = cur_line + movement; 578 if (*new_line_ret >= view->lines_num) 579 *new_line_ret = view->lines_num - 1; 580 *new_softline_ret = 0; 581 } else { 582 int softline = view_pos_to_softline(view, cur_line, cur_index); 583 if (movement > 0) { 584 int softlines = view_get_softline_count(view, cur_line); 585 if (softlines - softline > movement) { 586 *new_line_ret = cur_line; 587 *new_softline_ret = softline + movement; 588 } else { 589 movement -= (softlines - softline - 1); 590 size_t endline = cur_line + 1; 591 while (movement > 0 && endline < view->lines_num) { 592 softlines = view_get_softline_count(view, endline); 593 movement -= softlines; 594 endline++; 595 } 596 endline--; 597 if (movement <= 0) { 598 *new_softline_ret = movement + softlines - 1; 599 } else { 600 *new_softline_ret = softlines - 1; 601 } 602 *new_line_ret = endline; 603 } 604 } else if (movement < 0) { 605 int softlines = 0; 606 if (softline + movement >= 0) { 607 *new_line_ret = cur_line; 608 *new_softline_ret = softline + movement; 609 } else { 610 movement += softline; 611 size_t endline = cur_line; 612 while (movement < 0 && endline > 0) { 613 softlines = view_get_softline_count(view, endline-1); 614 movement += softlines; 615 endline--; 616 } 617 if (movement >= 0) { 618 *new_softline_ret = movement; 619 } else { 620 *new_softline_ret = 0; 621 } 622 *new_line_ret = endline; 623 } 624 } else { 625 *new_line_ret = cur_line; 626 *new_softline_ret = softline; 627 } 628 } 629 } 630 631 /* FIXME: don't overwrite view->cur_line, etc. here? */ 632 static void 633 delete_range( 634 ledit_view *view, 635 int line_based, int selected, 636 size_t line_index1, size_t byte_index1, 637 size_t line_index2, size_t byte_index2, 638 int copy_to_buffer) { 639 (void)selected; /* FIXME */ 640 if (copy_to_buffer && !paste_buffer) 641 paste_buffer = txtbuf_new(); 642 txtbuf *buf = copy_to_buffer ? paste_buffer : NULL; 643 enum delete_mode delmode = DELETE_CHAR; 644 if (line_based) { 645 if (view->buffer->hard_line_based) 646 delmode = DELETE_HARDLINE; 647 else 648 delmode = DELETE_SOFTLINE; 649 } 650 view_delete_range( 651 view, delmode, 1, 652 line_index1, byte_index1, 653 line_index2, byte_index2, 654 &view->cur_line, &view->cur_index, 655 buf 656 ); 657 } 658 659 /* FIXME: better interface for this; documentation */ 660 static void 661 insert_text( 662 ledit_view *view, 663 size_t line, size_t index, 664 char *text, size_t len, 665 size_t cur_line1, size_t cur_index1, 666 size_t cur_line2, size_t cur_index2, 667 int set_range_start, int set_range_end, int start_group) { 668 ledit_range cur_range; 669 if (set_range_start) { 670 cur_range.line1 = cur_line1; 671 cur_range.byte1 = cur_index1; 672 } else { 673 cur_range.line1 = view->cur_line; 674 cur_range.byte1 = view->cur_index; 675 } 676 /* this is mainly for pasting, where the new line and index 677 should not be at the end of the pasted text */ 678 if (set_range_end) { 679 cur_range.line2 = cur_line2; 680 cur_range.byte2 = cur_index2; 681 } else { 682 /* to make static analysis happy */ 683 cur_range.line2 = cur_range.byte2 = 0; 684 } 685 /* FIXME: why did I ever decide to make set_range_end 686 mean exactly the opposite for the two functions? */ 687 buffer_insert_with_undo( 688 view->buffer, cur_range, !set_range_end, 689 start_group, view->mode, 690 line, index, text, len, 691 &view->cur_line, &view->cur_index 692 ); 693 if (set_range_end) { 694 view->cur_line = cur_line2; 695 view->cur_index = cur_index2; 696 } 697 } 698 699 static int 700 delete_selection(ledit_view *view) { 701 if (view->sel_valid && (view->sel.line1 != view->sel.line2 || view->sel.byte1 != view->sel.byte2)) { 702 delete_range( 703 view, 0, 0, 704 view->sel.line1, view->sel.byte1, 705 view->sel.line2, view->sel.byte2, 1 706 ); 707 paste_buffer_line_based = 0; 708 view->sel_valid = 0; 709 view->sel.line1 = view->sel.line2 = view->cur_line; 710 view->sel.byte1 = view->sel.byte2 = view->cur_index; 711 view_wipe_line_cursor_attrs(view, view->cur_line); 712 return 1; 713 } 714 return 0; 715 } 716 717 /******************************************** 718 * Functions that were declared at the top. * 719 ********************************************/ 720 721 /* used to set cursor - I guess this is sort of a hack */ 722 static void 723 push_undo_empty_insert(ledit_view *view, size_t line, size_t index, int start_group) { 724 /* WARNING: Don't abuse txtbuf like this unless you're stupid like me. */ 725 txtbuf ins_buf = {.text = "", .len = 0, .cap = 0}; 726 ledit_range range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index}; 727 undo_push_insert( 728 view->buffer->undo, &ins_buf, range, range, start_group, view->mode 729 ); 730 } 731 732 static struct action 733 append_line_above(ledit_view *view, char *text, size_t len) { 734 size_t start, end; 735 /* do this here already so the mode group is the same for the newline insertion */ 736 enter_insert(view, text, len); 737 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end); 738 if (view->buffer->hard_line_based || start == 0) { 739 insert_text(view, view->cur_line, 0, "\n", 1, 0, 0, view->cur_line, 0, 0, 1, 1); 740 } else { 741 /* FIXME: this interface really is horrible */ 742 insert_text(view, view->cur_line, start, "\n\n", 2, 0, 0, view->cur_line + 1, 0, 0, 1, 1); 743 } 744 return (struct action){ACTION_NONE, NULL}; 745 } 746 747 static struct action 748 append_line_below(ledit_view *view, char *text, size_t len) { 749 size_t start, end; 750 enter_insert(view, text, len); 751 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end); 752 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); 753 if (view->buffer->hard_line_based || end == ll->len) { 754 insert_text(view, view->cur_line, ll->len, "\n", 1, 0, 0, view->cur_line + 1, 0, 0, 1, 1); 755 } else { 756 insert_text(view, view->cur_line, end, "\n\n", 2, 0, 0, view->cur_line + 1, 0, 0, 1, 1); 757 } 758 return (struct action){ACTION_NONE, NULL}; 759 } 760 761 static struct action 762 append_after_cursor(ledit_view *view, char *text, size_t len) { 763 enter_insert(view, text, len); 764 /* make cursor jump back to original position on undo */ 765 push_undo_empty_insert(view, view->cur_line, view->cur_index, 1); 766 view_next_cursor_pos( 767 view, view->cur_line, view->cur_index, 1, 0, NULL, &view->cur_index 768 ); 769 return (struct action){ACTION_NONE, NULL}; 770 } 771 772 static struct action 773 append_after_eol(ledit_view *view, char *text, size_t len) { 774 size_t start, end; 775 enter_insert(view, text, len); 776 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end); 777 /* make cursor jump back to original position on undo */ 778 push_undo_empty_insert(view, view->cur_line, view->cur_index, 1); 779 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); 780 if (view->buffer->hard_line_based) 781 view->cur_index = ll->len; 782 else 783 view->cur_index = end; 784 return (struct action){ACTION_NONE, NULL}; 785 } 786 787 static struct action 788 move_to_line(ledit_view *view, char *text, size_t len) { 789 (void)text; 790 (void)len; 791 motion_callback cb = NULL; 792 int repeat = get_key_repeat_and_motion_cb(view, &cb); 793 size_t line; 794 if (repeat > 0) 795 line = (size_t)repeat > view->lines_num ? view->lines_num : (size_t)repeat; 796 else if (repeat == 0) 797 line = view->lines_num; 798 else 799 return err_invalid_key(view); 800 if (cb != NULL) { 801 /* this is a bit of a hack - because move_to_line always works 802 with hard lines, it sets the index to ll->len so e.g. the delete 803 callback deletes until the end of the line even in soft line mode */ 804 ledit_line *ll = buffer_get_line(view->buffer, line - 1); 805 cb(view, line - 1, ll->len, KEY_MOTION_LINE); 806 } else { 807 if (view->mode == NORMAL) 808 view_wipe_line_cursor_attrs(view, view->cur_line); 809 else if (view->mode == VISUAL) 810 view_set_selection(view, view->sel.line1, view->sel.byte1, line - 1, 0); 811 view->cur_line = line - 1; 812 view->cur_index = 0; 813 int text_w, text_h; 814 window_get_textview_size(view->window, &text_w, &text_h); 815 ledit_view_line *vl = view_get_line(view, view->cur_line); 816 int x, y, h; 817 view_get_cursor_pixel_pos(view, view->cur_line, 0, &x, &y, &h); 818 /* if cursor is not on screen anymore, move to middle of screen */ 819 if (vl->y_offset < view->display_offset || 820 vl->y_offset + h > view->display_offset + text_h) { 821 view_scroll(view, vl->y_offset - text_h / 2); 822 } 823 if (view->mode == NORMAL) 824 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 825 discard_repetition_stack(); 826 } 827 return (struct action){ACTION_NONE, NULL}; 828 } 829 830 /* FIXME: should these scrolling functions change behavior when hard_line_based == 1? */ 831 static void 832 scroll_lines(ledit_view *view, int lines, int dir) { 833 if (last_lines_scrolled <= 0 && lines <= 0) { 834 /* no scroll command yet - scroll half of screen */ 835 move_half_screen(view, dir); 836 } else { 837 int x, y, h, sli; 838 int final_lines, text_w, text_h; 839 ledit_view_line *vl = view_get_line(view, view->cur_line); 840 view_get_cursor_pixel_pos(view, view->cur_line, view->cur_index, &x, &y, &h); 841 /* get the middle position of char */ 842 view_pos_to_x_softline(view, view->cur_line, view->cur_index, &x, &sli); 843 long abs_pos = vl->y_offset + y; 844 window_get_textview_size(view->window, &text_w, &text_h); 845 if (lines > 0) 846 final_lines = last_lines_scrolled = lines; 847 else 848 final_lines = last_lines_scrolled; 849 view_wipe_line_cursor_attrs(view, view->cur_line); 850 get_new_line_softline( 851 view, view->cur_line, view->cur_index, 852 dir < 0 ? -final_lines : final_lines, 853 &view->cur_line, &sli 854 ); 855 size_t start, end; 856 view_get_softline_bounds(view, view->cur_line, sli, &start, &end); 857 vl = view_get_line(view, view->cur_line); 858 view->cur_index = view_x_softline_to_pos(view, view->cur_line, x, sli); 859 view_get_cursor_pixel_pos(view, view->cur_line, view->cur_index, &x, &y, &h); 860 long new_abs_pos = vl->y_offset + y; 861 view_scroll(view, view->display_offset + (new_abs_pos - abs_pos)); 862 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 863 } 864 } 865 866 static struct action 867 scroll_lines_up(ledit_view *view, char *text, size_t len) { 868 (void)text; 869 (void)len; 870 int repeat = get_key_repeat(); 871 if (repeat >= 0) 872 scroll_lines(view, repeat, -1); 873 else 874 window_show_message(view->window, "Invalid key", -1); 875 discard_repetition_stack(); 876 return (struct action){ACTION_NONE, NULL}; 877 } 878 879 static struct action 880 scroll_lines_down(ledit_view *view, char *text, size_t len) { 881 (void)text; 882 (void)len; 883 int repeat = get_key_repeat(); 884 if (repeat >= 0) 885 scroll_lines(view, repeat, 1); 886 else 887 window_show_message(view->window, "Invalid key", -1); 888 discard_repetition_stack(); 889 return (struct action){ACTION_NONE, NULL}; 890 } 891 892 static void 893 scroll_with_cursor(ledit_view *view, int movement) { 894 int x, y, h; 895 view_get_cursor_pixel_pos(view, view->cur_line, view->cur_index, &x, &y, &h); 896 int pix_movement = movement * h; /* FIXME: overflow */ 897 view_scroll(view, view->display_offset + pix_movement); 898 size_t old_line = view->cur_line; 899 size_t old_index = view->cur_index; 900 view_get_nearest_legal_pos( 901 view, old_line, old_index, 902 &view->cur_line, &view->cur_index 903 ); 904 if (old_line != view->cur_line || old_index != view->cur_index) { 905 view_wipe_line_cursor_attrs(view, old_line); 906 /* if cursor is at top or bottom of screen, snap it to the 907 very edge to avoid it looking weird */ 908 if (movement > 0) { 909 view_scroll_to_pos_top( 910 view, view->cur_line, view->cur_index 911 ); 912 } else { 913 view_scroll_to_pos_bottom( 914 view, view->cur_line, view->cur_index 915 ); 916 } 917 } 918 if (view->mode == NORMAL) { 919 view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index); 920 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 921 } 922 } 923 924 static struct action 925 scroll_with_cursor_up(ledit_view *view, char *text, size_t len) { 926 (void)text; 927 (void)len; 928 int repeat = get_key_repeat(); 929 if (repeat >= 0) 930 scroll_with_cursor(view, -(repeat == 0 ? 1 : repeat)); 931 else 932 window_show_message(view->window, "Invalid key", -1); 933 discard_repetition_stack(); 934 return (struct action){ACTION_NONE, NULL}; 935 } 936 937 static struct action 938 scroll_with_cursor_down(ledit_view *view, char *text, size_t len) { 939 (void)text; 940 (void)len; 941 int repeat = get_key_repeat(); 942 if (repeat >= 0) 943 scroll_with_cursor(view, repeat == 0 ? 1 : repeat); 944 else 945 window_show_message(view->window, "Invalid key", -1); 946 discard_repetition_stack(); 947 return (struct action){ACTION_NONE, NULL}; 948 } 949 950 /* FIXME: Make all these scrolling functions work in visual mode */ 951 /* movement is multiplied with half the window height and the result is added to the display offset 952 the cursor is moved to the bottom if movement is upwards, to the top otherwise 953 FIXME: this is slightly different now 954 (unless the screen is already at the very top or bottom - then it is the other way around) */ 955 /* FIXME: this is a bit weird at the moment */ 956 static void 957 move_half_screen(ledit_view *view, int movement) { 958 int w, h; 959 window_get_textview_size(view->window, &w, &h); 960 /* FIXME: overflow */ 961 int total = movement * h/2; 962 ledit_view_line *vl = view_get_line(view, view->cur_line); 963 int cur_x, cur_y, cur_h; 964 view_get_cursor_pixel_pos( 965 view, view->cur_line, view->cur_index, &cur_x, &cur_y, &cur_h 966 ); 967 long real_cur_y = vl->y_offset - view->display_offset + cur_y; 968 /* new pixel position of cursor */ 969 /* Note: this usually causes at least part of a line of overlap 970 because ensure_cursor_shown scrolls back a bit if the line 971 isn't completely shown (this behavior could be changed using 972 view_get_nearest_legal_pos) */ 973 int y = movement > 0 ? 0 : h; 974 int half_screen = abs(movement) % 2 == 1; 975 if (half_screen) { 976 /* if only half screens are moved and we are at the beginning or 977 end, just move the cursor the movement amount instead of 978 moving it to the very top or bottom */ 979 if (view->display_offset + total <= 0 || 980 view->display_offset + total + h >= view->total_height) { 981 y = real_cur_y + total; 982 } 983 } else { 984 if (view->display_offset + total <= 0) 985 y = 0; 986 else if (view->display_offset + total + h > view->total_height) 987 y = h; 988 } 989 if (y < 0) 990 y = 0; 991 if (y > h) 992 y = h; 993 view_scroll(view, view->display_offset + total); 994 view_wipe_line_cursor_attrs(view, view->cur_line); 995 /* try to keep current x position of cursor */ 996 int x, softline; 997 /* FIXME: properly document what uses PANGO_SCALE and what not */ 998 view_pos_to_x_softline(view, view->cur_line, view->cur_index, &x, &softline); 999 view_xy_to_line_byte( 1000 view, x / PANGO_SCALE, y, 0, 1001 &view->cur_line, &view->cur_index 1002 ); 1003 if (view->mode == NORMAL) { 1004 view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index); 1005 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1006 } 1007 } 1008 1009 static struct action 1010 screen_up(ledit_view *view, char *text, size_t len) { 1011 (void)text; 1012 (void)len; 1013 int repeat = get_key_repeat(); 1014 /* FIXME: overflow */ 1015 if (repeat >= 0) 1016 move_half_screen(view, -(repeat == 0 ? 2 : repeat*2)); 1017 else 1018 window_show_message(view->window, "Invalid key", -1); 1019 discard_repetition_stack(); 1020 return (struct action){ACTION_NONE, NULL}; 1021 } 1022 1023 static struct action 1024 screen_down(ledit_view *view, char *text, size_t len) { 1025 (void)text; 1026 (void)len; 1027 int repeat = get_key_repeat(); 1028 if (repeat >= 0) 1029 move_half_screen(view, repeat == 0 ? 2 : repeat*2); 1030 else 1031 window_show_message(view->window, "Invalid key", -1); 1032 discard_repetition_stack(); 1033 return (struct action){ACTION_NONE, NULL}; 1034 } 1035 1036 static struct action 1037 delete_to_eol(ledit_view *view, char *text, size_t len) { 1038 (void)text; 1039 (void)len; 1040 if (!key_stack_empty()) 1041 return err_invalid_key(view); 1042 size_t start, end; 1043 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); 1044 if (view->buffer->hard_line_based) { 1045 end = ll->len; 1046 } else { 1047 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end); 1048 } 1049 delete_range( 1050 view, 0, 0, 1051 view->cur_line, view->cur_index, 1052 view->cur_line, end, view->mode != INSERT 1053 ); 1054 if (view->mode != INSERT) 1055 paste_buffer_line_based = 0; 1056 if (view->mode == NORMAL) { 1057 view->cur_index = view_get_legal_normal_pos( 1058 view, view->cur_line, view->cur_index 1059 ); 1060 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1061 } 1062 return (struct action){ACTION_NONE, NULL}; 1063 } 1064 1065 static struct action 1066 change_to_eol(ledit_view *view, char *text, size_t len) { 1067 (void)text; 1068 (void)len; 1069 if (!key_stack_empty()) 1070 return err_invalid_key(view); 1071 view_set_mode(view, INSERT); 1072 size_t start, end; 1073 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); 1074 if (view->buffer->hard_line_based) { 1075 end = ll->len; 1076 } else { 1077 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end); 1078 } 1079 delete_range( 1080 view, 0, 0, 1081 view->cur_line, view->cur_index, 1082 view->cur_line, end, 1 1083 ); 1084 paste_buffer_line_based = 0; 1085 view_wipe_line_cursor_attrs(view, view->cur_line); 1086 return (struct action){ACTION_NONE, NULL}; 1087 } 1088 1089 /* FIXME: clear selection on most commands */ 1090 /* FIXME: don't include escape when repeating change with '.'? */ 1091 static struct action 1092 change(ledit_view *view, char *text, size_t len) { 1093 (void)text; 1094 (void)len; 1095 motion_callback cb = NULL; 1096 int num = get_key_repeat_and_motion_cb(view, &cb); 1097 if (num == -1) 1098 return err_invalid_key(view); 1099 if (view->mode == VISUAL) { 1100 view_set_mode(view, INSERT); 1101 delete_selection(view); 1102 view_wipe_line_cursor_attrs(view, view->cur_line); 1103 clear_key_stack(); 1104 } else { 1105 if (cb == &change_cb) { 1106 int lines = num > 0 ? num : 1; 1107 size_t new_line; 1108 int new_softline; 1109 get_new_line_softline( 1110 view, view->cur_line, view->cur_index, 1111 lines - 1, &new_line, &new_softline 1112 ); 1113 size_t start, end; 1114 view_get_softline_bounds(view, new_line, new_softline, &start, &end); 1115 cb(view, new_line, start, KEY_MOTION_LINE); 1116 clear_key_stack(); 1117 } else if (cb != NULL) { 1118 return err_invalid_key(view); 1119 } else { 1120 struct key_stack_elem *e = push_key_stack(); 1121 e->key = KEY_MOTIONALLOWED|KEY_NUMBERALLOWED; 1122 e->count = num; 1123 e->motion_cb = &change_cb; 1124 } 1125 } 1126 return (struct action){ACTION_NONE, NULL}; 1127 } 1128 1129 static void 1130 change_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type) { 1131 CHECK_VIEW_LOCKED_NORETURN(view); 1132 /* set mode first so the deletion is included in the undo group */ 1133 view_set_mode(view, INSERT); 1134 int line_based = type == KEY_MOTION_LINE ? 1 : 0; 1135 /* this hackery is needed to avoid deleting the entire last line and 1136 instead leave an empty line - this should be made nicer (FIXME) */ 1137 size_t pos1 = view->cur_index, pos2 = char_pos; 1138 if (line_based && !view->buffer->hard_line_based) { 1139 size_t pos1, pos2, tmp; 1140 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &pos1, &tmp); 1141 view_get_pos_softline_bounds(view, line, char_pos, &tmp, &pos2); 1142 } else if (line_based && view->buffer->hard_line_based) { 1143 pos1 = 0; 1144 ledit_line *ll = buffer_get_line(view->buffer, line); 1145 pos2 = ll->len; 1146 } 1147 /* force line_based to 0 (see comment about hackery above) */ 1148 delete_range( 1149 view, 0, 0, 1150 view->cur_line, pos1, 1151 line, pos2, view->mode != INSERT 1152 ); 1153 if (view->mode != INSERT) 1154 paste_buffer_line_based = line_based; 1155 view_wipe_line_cursor_attrs(view, view->cur_line); 1156 } 1157 1158 static struct action 1159 yank(ledit_view *view, char *text, size_t len) { 1160 (void)text; 1161 (void)len; 1162 if (!paste_buffer) 1163 paste_buffer = txtbuf_new(); 1164 if (view->mode == VISUAL) { 1165 sort_range( 1166 &view->sel.line1, &view->sel.byte1, &view->sel.line2, &view->sel.byte2 1167 ); 1168 buffer_copy_text_to_txtbuf( 1169 view->buffer, paste_buffer, 1170 view->sel.line1, view->sel.byte1, view->sel.line2, view->sel.byte2 1171 ); 1172 paste_buffer_line_based = 0; 1173 view->cur_line = view->sel.line1; 1174 view->cur_index = view->sel.byte1; 1175 view_wipe_selection(view); 1176 view_set_mode(view, NORMAL); 1177 view->cur_index = view_get_legal_normal_pos( 1178 view, view->cur_line, view->cur_index 1179 ); 1180 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1181 clear_key_stack(); 1182 } else { 1183 motion_callback cb = NULL; 1184 int num = get_key_repeat_and_motion_cb(view, &cb); 1185 if (num == -1) 1186 return err_invalid_key(view); 1187 if (cb == &yank_cb) { 1188 if (num == 0) 1189 num = 1; 1190 size_t new_line; 1191 int new_softline; 1192 get_new_line_softline( 1193 view, view->cur_line, view->cur_index, 1194 num - 1, &new_line, &new_softline 1195 ); 1196 size_t start, end; 1197 view_get_softline_bounds(view, new_line, new_softline, &start, &end); 1198 cb(view, new_line, start, KEY_MOTION_LINE); 1199 clear_key_stack(); 1200 } else if (cb == NULL) { 1201 struct key_stack_elem *e = push_key_stack(); 1202 e->key = KEY_MOTIONALLOWED|KEY_NUMBERALLOWED; 1203 e->count = num; 1204 e->motion_cb = &yank_cb; 1205 } else { 1206 /* FIXME: proper error */ 1207 clear_key_stack(); 1208 } 1209 } 1210 discard_repetition_stack(); 1211 return (struct action){ACTION_NONE, NULL}; 1212 } 1213 1214 static struct action 1215 yank_lines(ledit_view *view, char *text, size_t len) { 1216 (void)text; 1217 (void)len; 1218 int num = get_key_repeat(); 1219 if (num == -1) 1220 return err_invalid_key(view); 1221 else if (num == 0) 1222 num = 1; 1223 size_t new_line; 1224 int new_softline; 1225 get_new_line_softline( 1226 view, view->cur_line, view->cur_index, 1227 num - 1, &new_line, &new_softline 1228 ); 1229 size_t start, end; 1230 view_get_softline_bounds(view, new_line, new_softline, &start, &end); 1231 yank_cb(view, new_line, start, KEY_MOTION_LINE); 1232 clear_key_stack(); 1233 return (struct action){ACTION_NONE, NULL}; 1234 } 1235 1236 static void 1237 yank_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type) { 1238 int line_based = type == KEY_MOTION_LINE ? 1 : 0; 1239 size_t l1 = view->cur_line, l2 = line, b1 = view->cur_index, b2 = char_pos; 1240 if (!paste_buffer) 1241 paste_buffer = txtbuf_new(); 1242 if (l2 < l1 || (l1 == l2 && b2 < b1)) { 1243 swap_sz(&l1, &l2); 1244 swap_sz(&b1, &b2); 1245 } 1246 if (line_based && !view->buffer->hard_line_based) { 1247 size_t start1, end2, tmp; 1248 view_get_pos_softline_bounds(view, l1, b1, &start1, &tmp); 1249 view_get_pos_softline_bounds(view, l2, b2, &tmp, &end2); 1250 ledit_line *ll = buffer_get_line(view->buffer, l2); 1251 if (end2 == ll->len && l2 < view->lines_num - 1) { 1252 l2++; 1253 end2 = 0; 1254 } 1255 buffer_copy_text_to_txtbuf( 1256 view->buffer, paste_buffer, l1, start1, l2, end2 1257 ); 1258 } else if (line_based && view->buffer->hard_line_based) { 1259 ledit_line *ll = buffer_get_line(view->buffer, l2); 1260 size_t end = ll->len; 1261 if (l2 < view->lines_num - 1) { 1262 l2++; 1263 end = 0; 1264 } 1265 buffer_copy_text_to_txtbuf( 1266 view->buffer, paste_buffer, l1, 0, l2, end 1267 ); 1268 } else { 1269 buffer_copy_text_to_txtbuf( 1270 view->buffer, paste_buffer, l1, b1, l2, b2 1271 ); 1272 } 1273 paste_buffer_line_based = line_based; 1274 discard_repetition_stack(); 1275 } 1276 1277 static struct action 1278 delete(ledit_view *view, char *text, size_t len) { 1279 (void)text; 1280 (void)len; 1281 motion_callback cb = NULL; 1282 int num = get_key_repeat_and_motion_cb(view, &cb); 1283 if (num == -1) 1284 return err_invalid_key(view); 1285 if (delete_selection(view)) { 1286 clear_key_stack(); 1287 } else { 1288 /* FIXME: checking equality of the function pointer may be a bit risky */ 1289 /* -> actually, it shouldn't be a problem */ 1290 if (cb == &delete_cb) { 1291 int lines = num > 0 ? num : 1; 1292 size_t new_line; 1293 int new_softline; 1294 get_new_line_softline( 1295 view, view->cur_line, view->cur_index, 1296 lines - 1, &new_line, &new_softline 1297 ); 1298 size_t start, end; 1299 view_get_softline_bounds(view, new_line, new_softline, &start, &end); 1300 cb(view, new_line, start, KEY_MOTION_LINE); 1301 clear_key_stack(); 1302 } else if (cb != NULL) { 1303 return err_invalid_key(view); 1304 } else { 1305 struct key_stack_elem *e = push_key_stack(); 1306 e->key = KEY_MOTIONALLOWED|KEY_NUMBERALLOWED; 1307 e->count = num; 1308 e->motion_cb = &delete_cb; 1309 } 1310 } 1311 return (struct action){ACTION_NONE, NULL}; 1312 } 1313 1314 /* FIXME: should this get number of lines to remove or actual end line? */ 1315 static void 1316 delete_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type) { 1317 CHECK_VIEW_LOCKED_NORETURN(view); 1318 view_wipe_line_cursor_attrs(view, view->cur_line); 1319 int line_based = type == KEY_MOTION_LINE ? 1 : 0; 1320 delete_range( 1321 view, line_based, 0, 1322 view->cur_line, view->cur_index, 1323 line, char_pos, view->mode != INSERT 1324 ); 1325 if (view->mode != INSERT) { 1326 paste_buffer_line_based = line_based; 1327 finalize_repetition_stack(); 1328 } 1329 if (view->mode == NORMAL) 1330 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1331 } 1332 1333 /* Note that these paste functions are a bit weird when working with softlines - 1334 they always make sure the pasted text is separated from the surrounding text by 1335 hard lines, which may be unexpected, but the alternatives I could think of are 1336 even weirder */ 1337 static struct action 1338 paste_normal(ledit_view *view, char *text, size_t len) { 1339 (void)text; 1340 (void)len; 1341 if (!paste_buffer) { 1342 window_show_message(view->window, "Nothing to paste", -1); 1343 discard_repetition_stack(); 1344 return (struct action){ACTION_NONE, NULL}; 1345 } 1346 if (paste_buffer_line_based) { 1347 view_wipe_line_cursor_attrs(view, view->cur_line); 1348 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); 1349 size_t brk = 0; 1350 if (view->buffer->hard_line_based) { 1351 brk = ll->len; 1352 } else { 1353 size_t tmp; 1354 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &tmp, &brk); 1355 } 1356 /* FIXME: this is a bit inefficient because insert_text does not 1357 use the *_base functions, but maybe this way is a bit safer */ 1358 insert_text( 1359 view, view->cur_line, brk, 1360 "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1, 1 1361 ); 1362 size_t text_len = paste_buffer->len; 1363 ll = buffer_get_line(view->buffer, view->cur_line + 1); 1364 if (ll->len == 0 && paste_buffer->text[text_len-1] == '\n') { 1365 /* remove trailing newline if it exists and text is already on own line */ 1366 text_len--; 1367 paste_buffer->text[text_len] = '\0'; 1368 } else if (ll->len > 0 && paste_buffer->text[text_len-1] != '\n') { 1369 /* ensure pasted text is on its own hard line */ 1370 insert_text( 1371 view, view->cur_line + 1, 0, 1372 "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1, 0 1373 ); 1374 } 1375 insert_text( 1376 view, view->cur_line + 1, 0, 1377 paste_buffer->text, text_len, 0, 0, view->cur_line + 1, 0, 0, 1, 0 1378 ); 1379 } else { 1380 size_t old_line = view->cur_line; 1381 size_t old_index = view->cur_index; 1382 /* must allow illegal index so text can be pasted at end of line */ 1383 move_cursor_logically(view, 1, 1); 1384 insert_text( 1385 view, view->cur_line, view->cur_index, 1386 paste_buffer->text, paste_buffer->len, 1387 old_line, old_index, view->cur_line, view->cur_index, 1, 1, 1 1388 ); 1389 } 1390 if (view->mode == NORMAL) 1391 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1392 if (view->mode != INSERT) 1393 finalize_repetition_stack(); 1394 return (struct action){ACTION_NONE, NULL}; 1395 } 1396 1397 static struct action 1398 paste_normal_backwards(ledit_view *view, char *text, size_t len) { 1399 (void)text; 1400 (void)len; 1401 if (!paste_buffer) { 1402 window_show_message(view->window, "Nothing to paste", -1); 1403 discard_repetition_stack(); 1404 return (struct action){ACTION_NONE, NULL}; 1405 } 1406 if (paste_buffer_line_based) { 1407 view_wipe_line_cursor_attrs(view, view->cur_line); 1408 size_t brk = 0; 1409 if (!view->buffer->hard_line_based) { 1410 size_t tmp; 1411 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &brk, &tmp); 1412 } 1413 /* FIXME: better interface without these weird int args */ 1414 insert_text( 1415 view, view->cur_line, brk, 1416 "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1, 1 1417 ); 1418 size_t text_len = paste_buffer->len; 1419 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); 1420 if (paste_buffer->text[text_len-1] == '\n') { 1421 /* remove trailing newline if it exists */ 1422 text_len--; 1423 paste_buffer->text[text_len] = '\0'; 1424 } 1425 size_t new_line = view->cur_line; 1426 if (ll->len > 0) { 1427 /* ensure pasted text is on its own hard line */ 1428 insert_text( 1429 view, view->cur_line, ll->len, 1430 "\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1, 0 1431 ); 1432 new_line = view->cur_line + 1; 1433 } 1434 insert_text( 1435 view, new_line, 0, 1436 paste_buffer->text, text_len, 0, 0, new_line, 0, 0, 1, 0 1437 ); 1438 } else { 1439 insert_text( 1440 view, view->cur_line, view->cur_index, 1441 paste_buffer->text, paste_buffer->len, 1442 0, 0, view->cur_line, view->cur_index, 0, 1, 1 1443 ); 1444 } 1445 if (view->mode == NORMAL) 1446 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1447 if (view->mode != INSERT) 1448 finalize_repetition_stack(); 1449 return (struct action){ACTION_NONE, NULL}; 1450 } 1451 1452 static struct action 1453 key_0(ledit_view *view, char *text, size_t len) { 1454 struct key_stack_elem *e = peek_key_stack(); 1455 if (!e || (e->key & KEY_MOTIONALLOWED)) { 1456 return cursor_to_beginning(view, text, len); 1457 } else if (e->key & KEY_NUMBER) { 1458 return push_0(view, text, len); 1459 } else { 1460 return err_invalid_key(view); 1461 } 1462 } 1463 1464 static void 1465 push_num(ledit_view *view, int num) { 1466 struct key_stack_elem *e = peek_key_stack(); 1467 if (!e || !(e->key & KEY_NUMBER)) { 1468 e = push_key_stack(); 1469 e->key = KEY_NUMBER; 1470 e->followup = KEY_NUMBER|KEY_NUMBERALLOWED; 1471 } 1472 if (INT_MAX / 10 < e->count) { 1473 window_show_message( 1474 view->window, 1475 "Integer overflow in key repetition", -1 1476 ); 1477 clear_key_stack(); 1478 return; 1479 } 1480 e->count *= 10; 1481 if (INT_MAX - num < e->count) { 1482 window_show_message( 1483 view->window, 1484 "Integer overflow in key repetition", -1 1485 ); 1486 clear_key_stack(); 1487 return; 1488 } 1489 e->count += num; 1490 } 1491 1492 static struct action 1493 push_0(ledit_view *view, char *text, size_t len) { 1494 (void)view; 1495 (void)text; 1496 (void)len; 1497 push_num(view, 0); 1498 return (struct action){ACTION_NONE, NULL}; 1499 } 1500 1501 static struct action 1502 push_1(ledit_view *view, char *text, size_t len) { 1503 (void)view; 1504 (void)text; 1505 (void)len; 1506 push_num(view, 1); 1507 return (struct action){ACTION_NONE, NULL}; 1508 } 1509 1510 static struct action 1511 push_2(ledit_view *view, char *text, size_t len) { 1512 (void)view; 1513 (void)text; 1514 (void)len; 1515 push_num(view, 2); 1516 return (struct action){ACTION_NONE, NULL}; 1517 } 1518 1519 static struct action 1520 push_3(ledit_view *view, char *text, size_t len) { 1521 (void)view; 1522 (void)text; 1523 (void)len; 1524 push_num(view, 3); 1525 return (struct action){ACTION_NONE, NULL}; 1526 } 1527 1528 static struct action 1529 push_4(ledit_view *view, char *text, size_t len) { 1530 (void)view; 1531 (void)text; 1532 (void)len; 1533 push_num(view, 4); 1534 return (struct action){ACTION_NONE, NULL}; 1535 } 1536 1537 static struct action 1538 push_5(ledit_view *view, char *text, size_t len) { 1539 (void)view; 1540 (void)text; 1541 (void)len; 1542 push_num(view, 5); 1543 return (struct action){ACTION_NONE, NULL}; 1544 } 1545 1546 static struct action 1547 push_6(ledit_view *view, char *text, size_t len) { 1548 (void)view; 1549 (void)text; 1550 (void)len; 1551 push_num(view, 6); 1552 return (struct action){ACTION_NONE, NULL}; 1553 } 1554 1555 static struct action 1556 push_7(ledit_view *view, char *text, size_t len) { 1557 (void)view; 1558 (void)text; 1559 (void)len; 1560 push_num(view, 7); 1561 return (struct action){ACTION_NONE, NULL}; 1562 } 1563 1564 static struct action 1565 push_8(ledit_view *view, char *text, size_t len) { 1566 (void)view; 1567 (void)text; 1568 (void)len; 1569 push_num(view, 8); 1570 return (struct action){ACTION_NONE, NULL}; 1571 } 1572 1573 static struct action 1574 push_9(ledit_view *view, char *text, size_t len) { 1575 (void)view; 1576 (void)text; 1577 (void)len; 1578 push_num(view, 9); 1579 return (struct action){ACTION_NONE, NULL}; 1580 } 1581 1582 /* FIXME: function to look at pango property to decide when to delete entire grapheme */ 1583 /* FIXME: The cursor may be in an illegal position after one of the delete_chars* 1584 functions, but calling get_legal_normal_pos also would be weird because it 1585 wouldn't necessarily be at the deletion index anymore */ 1586 #define GEN_DELETE_FUNCS(type, next_func, prev_func) \ 1587 static struct action \ 1588 delete_##type##_backwards_base(ledit_view *view, char *text, size_t len, int multiline) { \ 1589 (void)text; \ 1590 (void)len; \ 1591 int num = get_key_repeat(); \ 1592 if (num == -1) { \ 1593 window_show_message(view->window, "Invalid key", -1); \ 1594 return (struct action){ACTION_NONE, NULL}; \ 1595 } else if (num == 0) { \ 1596 num = 1; \ 1597 } \ 1598 size_t start_line, start_index; \ 1599 prev_func( \ 1600 view, view->cur_line, view->cur_index, \ 1601 num, multiline, &start_line, &start_index \ 1602 ); \ 1603 delete_range( \ 1604 view, 0, 0, \ 1605 start_line, start_index, \ 1606 view->cur_line, view->cur_index, view->mode != INSERT \ 1607 ); \ 1608 view->cur_line = start_line; \ 1609 view->cur_index = start_index; \ 1610 if (view->mode == NORMAL) \ 1611 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); \ 1612 if (view->mode != INSERT) { \ 1613 paste_buffer_line_based = 0; \ 1614 finalize_repetition_stack(); \ 1615 } \ 1616 return (struct action){ACTION_NONE, NULL}; \ 1617 } \ 1618 \ 1619 static struct action \ 1620 delete_##type##_backwards(ledit_view *view, char *text, size_t len) { \ 1621 return delete_##type##_backwards_base(view, text, len, 0); \ 1622 } \ 1623 \ 1624 static struct action \ 1625 delete_##type##_backwards_multiline(ledit_view *view, char *text, size_t len) { \ 1626 return delete_##type##_backwards_base(view, text, len, 1); \ 1627 } \ 1628 \ 1629 static struct action \ 1630 delete_##type##_forwards_base(ledit_view *view, char *text, size_t len, int multiline) { \ 1631 (void)text; \ 1632 (void)len; \ 1633 int num = get_key_repeat(); \ 1634 if (num == -1) { \ 1635 window_show_message(view->window, "Invalid key", -1); \ 1636 return (struct action){ACTION_NONE, NULL}; \ 1637 } else if (num == 0) { \ 1638 num = 1; \ 1639 } \ 1640 size_t end_line, end_index; \ 1641 next_func( \ 1642 view, view->cur_line, view->cur_index, \ 1643 num, multiline, &end_line, &end_index \ 1644 ); \ 1645 delete_range( \ 1646 view, 0, 0, \ 1647 view->cur_line, view->cur_index, \ 1648 end_line, end_index, view->mode != INSERT \ 1649 ); \ 1650 if (view->mode == NORMAL) \ 1651 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); \ 1652 if (view->mode != INSERT) { \ 1653 paste_buffer_line_based = 0; \ 1654 finalize_repetition_stack(); \ 1655 } \ 1656 return (struct action){ACTION_NONE, NULL}; \ 1657 } \ 1658 \ 1659 static struct action \ 1660 delete_##type##_forwards(ledit_view *view, char *text, size_t len) { \ 1661 return delete_##type##_forwards_base(view, text, len, 0); \ 1662 } \ 1663 \ 1664 static struct action \ 1665 delete_##type##_forwards_multiline(ledit_view *view, char *text, size_t len) { \ 1666 return delete_##type##_forwards_base(view, text, len, 1); \ 1667 } 1668 1669 /* Yes, I know, all these helpers are ugly... */ 1670 #define buffer_next_char_pos_helper(view, line, byte, num, multiline, line_ret, byte_ret) \ 1671 buffer_next_char_pos((view)->buffer, line, byte, num, multiline, line_ret, byte_ret) 1672 #define buffer_prev_char_pos_helper(view, line, byte, num, multiline, line_ret, byte_ret) \ 1673 buffer_prev_char_pos((view)->buffer, line, byte, num, multiline, line_ret, byte_ret) 1674 1675 GEN_DELETE_FUNCS(graphemes, view_next_cursor_pos, view_prev_cursor_pos) 1676 GEN_DELETE_FUNCS(chars, buffer_next_char_pos_helper, buffer_prev_char_pos_helper) 1677 1678 static struct action 1679 move_to_eol(ledit_view *view, char *text, size_t len) { 1680 (void)text; 1681 (void)len; 1682 motion_callback cb; 1683 int num = get_key_repeat_and_motion_cb(view, &cb); 1684 if (num == -1) 1685 return err_invalid_key(view); 1686 if (num == 0) 1687 num = 1; 1688 view_wipe_line_cursor_attrs(view, view->cur_line); 1689 size_t new_line; 1690 int new_softline; 1691 get_new_line_softline( 1692 view, view->cur_line, view->cur_index, num - 1, 1693 &new_line, &new_softline 1694 ); 1695 ledit_line *ll = buffer_get_line(view->buffer, new_line); 1696 size_t end_index = ll->len; 1697 if (!view->buffer->hard_line_based) { 1698 size_t tmp; 1699 view_get_softline_bounds(view, new_line, new_softline, &tmp, &end_index); 1700 } 1701 if (cb != NULL) { 1702 cb(view, new_line, end_index, KEY_MOTION_CHAR); 1703 } else { 1704 view->cur_line = new_line; 1705 view->cur_index = end_index; 1706 if (view->mode == VISUAL) { 1707 view_set_selection( 1708 view, 1709 view->sel.line1, view->sel.byte1, 1710 new_line, end_index 1711 ); 1712 } else if (view->mode == NORMAL) { 1713 /* FIXME: this is weird because the cursor is actually on the 1714 next soft line, but the alternative has too many weird effects 1715 with bidi text */ 1716 view->cur_index = view_get_legal_normal_pos( 1717 view, view->cur_line, view->cur_index 1718 ); 1719 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1720 } 1721 } 1722 return (struct action){ACTION_NONE, NULL}; 1723 } 1724 1725 #define GEN_WORD_MOVEMENT(name, func) \ 1726 static struct action \ 1727 name(ledit_view *view, char *text, size_t len) { \ 1728 (void)text; \ 1729 (void)len; \ 1730 motion_callback cb; \ 1731 int num = get_key_repeat_and_motion_cb(view, &cb); \ 1732 if (num == -1) \ 1733 return err_invalid_key(view); \ 1734 if (num == 0) \ 1735 num = 1; \ 1736 size_t new_line, new_index, new_real_index; \ 1737 func( \ 1738 view, \ 1739 view->cur_line, view->cur_index, num, \ 1740 &new_line, &new_index, &new_real_index \ 1741 ); \ 1742 if (cb != NULL) { \ 1743 cb(view, new_line, new_real_index, KEY_MOTION_CHAR); \ 1744 } else { \ 1745 if (view->mode == VISUAL) { \ 1746 view_set_selection( \ 1747 view, \ 1748 view->sel.line1, view->sel.byte1, \ 1749 new_line, new_real_index \ 1750 ); \ 1751 view->cur_line = new_line; \ 1752 view->cur_index = new_real_index; \ 1753 } else { \ 1754 if (new_line != view->cur_line) \ 1755 view_wipe_line_cursor_attrs( \ 1756 view, view->cur_line \ 1757 ); \ 1758 view->cur_line = new_line; \ 1759 view->cur_index = new_index; \ 1760 if (view->mode == NORMAL) { \ 1761 view_set_line_cursor_attrs( \ 1762 view, view->cur_line, view->cur_index \ 1763 ); \ 1764 } \ 1765 } \ 1766 discard_repetition_stack(); \ 1767 } \ 1768 clear_key_stack(); \ 1769 return (struct action){ACTION_NONE, NULL}; \ 1770 } 1771 1772 GEN_WORD_MOVEMENT(next_word, view_next_word) 1773 GEN_WORD_MOVEMENT(next_word_end, view_next_word_end) 1774 GEN_WORD_MOVEMENT(next_bigword, view_next_bigword) 1775 GEN_WORD_MOVEMENT(next_bigword_end, view_next_bigword_end) 1776 GEN_WORD_MOVEMENT(prev_word, view_prev_word) 1777 GEN_WORD_MOVEMENT(prev_bigword, view_prev_bigword) 1778 1779 static void 1780 move_cursor_left_right(ledit_view *view, int dir, int allow_illegal_index) { 1781 motion_callback cb; 1782 int num = get_key_repeat_and_motion_cb(view, &cb); 1783 if (num == -1) 1784 (void)err_invalid_key(view); /* FIXME: why do I not return here? */ 1785 if (num == 0) 1786 num = 1; 1787 1788 ledit_line *cur_line = buffer_get_line(view->buffer, view->cur_line); 1789 /* FIXME: standardize interface - num * dir or separately? */ 1790 size_t last_index; 1791 size_t new_index = view_move_cursor_visually( 1792 view, view->cur_line, view->cur_index, num * dir, &last_index 1793 ); 1794 /* when in normal mode, the cursor cannot be at the very end 1795 of the line because it's always covering a character */ 1796 if (new_index >= cur_line->len) { 1797 if (!allow_illegal_index && 1798 view->mode == NORMAL && cb == NULL) { 1799 new_index = last_index; 1800 } else { 1801 /* FIXME: I guess this is unnecessary */ 1802 new_index = cur_line->len; 1803 } 1804 } 1805 if (cb != NULL) { 1806 cb(view, view->cur_line, new_index, KEY_MOTION_CHAR); 1807 } else { 1808 view->cur_index = new_index; 1809 if (view->mode == VISUAL) { 1810 /* FIXME: check if view->sel_valid and only use it then (also change in other places) */ 1811 view_set_selection(view, view->sel.line1, view->sel.byte1, view->cur_line, new_index); 1812 } else if (view->mode == INSERT && view->sel_valid) { 1813 /* FIXME: I guess this is unnecessary now that no 1814 selection is allowed in insert mode */ 1815 view_wipe_selection(view); 1816 } else if (view->mode == NORMAL) { 1817 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1818 } 1819 view->redraw = 1; 1820 discard_repetition_stack(); 1821 } 1822 clear_key_stack(); 1823 } 1824 1825 static struct action 1826 cursor_left(ledit_view *view, char *text, size_t len) { 1827 (void)text; 1828 (void)len; 1829 move_cursor_left_right(view, -1, 0); 1830 return (struct action){ACTION_NONE, NULL}; 1831 } 1832 1833 static struct action 1834 cursor_right(ledit_view *view, char *text, size_t len) { 1835 (void)text; 1836 (void)len; 1837 move_cursor_left_right(view, 1, 0); 1838 return (struct action){ACTION_NONE, NULL}; 1839 } 1840 1841 static struct action 1842 break_line(ledit_view *view, char *text, size_t len) { 1843 (void)text; 1844 (void)len; 1845 int start_group = 1; 1846 /* FIXME: this is unnecessary now because no selection is supported in insert mode */ 1847 if (delete_selection(view)) 1848 start_group = 0; 1849 if (view->mode == NORMAL) 1850 view_wipe_line_cursor_attrs(view, view->cur_line); 1851 insert_text(view, view->cur_line, view->cur_index, "\n", 1, 0, 0, 0, 0, 0, 0, start_group); 1852 if (view->mode == NORMAL) { 1853 view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index); 1854 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1855 } 1856 return (struct action){ACTION_NONE, NULL}; 1857 } 1858 1859 static void 1860 move_cursor_logically(ledit_view *view, int movement_dir, int allow_illegal_index) { 1861 if (movement_dir < 0) { 1862 view_prev_cursor_pos( 1863 view, view->cur_line, view->cur_index, 1, 0, NULL, &view->cur_index 1864 ); 1865 } else { 1866 view_next_cursor_pos( 1867 view, view->cur_line, view->cur_index, 1, 0, NULL, &view->cur_index 1868 ); 1869 } 1870 if (!allow_illegal_index) { 1871 view->cur_index = view_get_legal_normal_pos( 1872 view, view->cur_line, view->cur_index 1873 ); 1874 } 1875 } 1876 1877 static struct action 1878 return_to_normal(ledit_view *view, char *text, size_t len) { 1879 (void)text; 1880 (void)len; 1881 clear_key_stack(); 1882 if (view->mode == INSERT) 1883 finalize_repetition_stack(); 1884 /* FIXME: I guess this is unnecessary now that insert mode does not support selection */ 1885 if (view->mode == INSERT && view->sel_valid) { 1886 view_set_mode(view, VISUAL); 1887 } else if (view->mode != NORMAL) { 1888 view_set_mode(view, NORMAL); 1889 move_cursor_logically(view, -1, 0); 1890 view_wipe_selection(view); 1891 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1892 } 1893 return (struct action){ACTION_NONE, NULL}; 1894 } 1895 1896 static struct action 1897 enter_insert(ledit_view *view, char *text, size_t len) { 1898 (void)text; 1899 (void)len; 1900 if (view->mode == VISUAL) { 1901 view_wipe_selection(view); 1902 } 1903 view_wipe_line_cursor_attrs(view, view->cur_line); 1904 view_set_mode(view, INSERT); 1905 clear_key_stack(); 1906 return (struct action){ACTION_NONE, NULL}; 1907 } 1908 1909 /* FIXME: Check if previous key allows motion command - or should this be checked automatically before? */ 1910 static void 1911 move_cursor_up_down(ledit_view *view, int dir) { 1912 size_t new_line; 1913 int new_softline; 1914 1915 motion_callback cb; 1916 int num = get_key_repeat_and_motion_cb(view, &cb); 1917 if (num == -1) 1918 (void)err_invalid_key(view); 1919 if (num == 0) 1920 num = 1; 1921 num *= dir; 1922 1923 get_new_line_softline( 1924 view, view->cur_line, view->cur_index, 1925 num, &new_line, &new_softline 1926 ); 1927 1928 if (cb != NULL) { 1929 size_t start, end; 1930 view_get_softline_bounds(view, new_line, new_softline, &start, &end); 1931 cb(view, new_line, start, KEY_MOTION_LINE); 1932 } else { 1933 int lineno, x, diff = 0, old_line = view->cur_line; 1934 view_pos_to_x_softline(view, view->cur_line, view->cur_index, &x, &lineno); 1935 view->cur_index = view_x_softline_to_pos(view, new_line, x, new_softline); 1936 if (view->cur_line != new_line) 1937 diff = 1; 1938 view->cur_line = new_line; 1939 1940 if (view->mode == VISUAL) { 1941 view_set_selection(view, view->sel.line1, view->sel.byte1, view->cur_line, view->cur_index); 1942 } else if (view->mode == NORMAL) { 1943 if (diff) 1944 view_wipe_line_cursor_attrs(view, old_line); 1945 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 1946 } 1947 view->redraw = 1; 1948 discard_repetition_stack(); 1949 } 1950 clear_key_stack(); 1951 } 1952 1953 static struct action 1954 cursor_down(ledit_view *view, char *text, size_t len) { 1955 (void)text; 1956 (void)len; 1957 move_cursor_up_down(view, 1); 1958 return (struct action){ACTION_NONE, NULL}; 1959 } 1960 1961 static struct action 1962 cursor_up(ledit_view *view, char *text, size_t len) { 1963 (void)text; 1964 (void)len; 1965 move_cursor_up_down(view, -1); 1966 return (struct action){ACTION_NONE, NULL}; 1967 } 1968 1969 static struct action 1970 join_lines(ledit_view *view, char *text, size_t len) { 1971 (void)text; 1972 (void)len; 1973 int num = get_key_repeat(); 1974 if (num == -1) 1975 return err_invalid_key(view); 1976 if (num == 0) 1977 num = 1; 1978 int start_group = 1; 1979 ledit_line *ll1; 1980 ledit_line *ll2; 1981 size_t cur_line = view->cur_line; 1982 /* don't return yet so the stuff at the bottom gets called, 1983 in particular finalize_repetition_stack */ 1984 if (cur_line == view->lines_num - 1) 1985 window_show_message(view->window, "No following lines to join", -1); 1986 for (int i = 0; i < num; i++) { 1987 if (cur_line == view->lines_num - 1) 1988 break; 1989 ll1 = buffer_get_line(view->buffer, cur_line); 1990 ll2 = buffer_get_line(view->buffer, cur_line + 1); 1991 /* figure out if the current line ends in whitespace - 1992 this could probably be improved */ 1993 size_t last_char_byte = line_prev_utf8(ll1, ll1->len); 1994 size_t last_ws = view_line_next_non_whitespace(view, cur_line, last_char_byte); 1995 int end_in_ws = (last_ws == ll1->len); /* also works if ll1->len == 0 */ 1996 size_t start_idx = view_line_next_non_whitespace(view, cur_line + 1, 0); 1997 /* save len here because view_delete_range_base calls view_get_legal_normal_pos, 1998 so the returned index may not be right for the following space insertion */ 1999 /* although, on second thought, that only happens when the next line is empty, 2000 which is a special case that is ignored below... */ 2001 size_t len = ll1->len; 2002 size_t len2 = ll2->len; 2003 view_delete_range_base( 2004 view, DELETE_CHAR, start_group, 2005 cur_line, ll1->len, cur_line + 1, start_idx, 2006 &view->cur_line, &view->cur_index, NULL 2007 ); 2008 /* insert space if there is no other whitespace */ 2009 if (!end_in_ws && len2 > 0) { 2010 ledit_range cur_range = {.line1 = view->cur_line, .byte1 = view->cur_index, .line2 = 0, .byte2 = 0}; 2011 buffer_insert_with_undo_base( 2012 view->buffer, cur_range, 1, 0, 2013 view->mode, cur_line, len, 2014 " ", 1, &view->cur_line, &view->cur_index 2015 ); 2016 } 2017 start_group = 0; 2018 } 2019 buffer_recalc_all_views_from_line(view->buffer, cur_line); 2020 /* FIXME: should view_set_line_cursor_attrs just have this check included? */ 2021 if (view->mode == NORMAL) { 2022 view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index); 2023 view_set_line_cursor_attrs( 2024 view, view->cur_line, view->cur_index 2025 ); 2026 } 2027 if (view->mode != INSERT) 2028 finalize_repetition_stack(); 2029 return (struct action){ACTION_NONE, NULL}; 2030 } 2031 2032 static struct action 2033 insert_at_beginning(ledit_view *view, char *text, size_t len) { 2034 if (!key_stack_empty()) 2035 return err_invalid_key(view); 2036 enter_insert(view, text, len); 2037 size_t new_index = 0; 2038 if (!view->buffer->hard_line_based) { 2039 size_t tmp; 2040 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &new_index, &tmp); 2041 } 2042 push_undo_empty_insert(view, view->cur_line, view->cur_index, 1); 2043 view->cur_index = new_index; 2044 view_wipe_line_cursor_attrs(view, view->cur_line); 2045 return (struct action){ACTION_NONE, NULL}; 2046 } 2047 2048 static struct action 2049 cursor_to_first_non_ws(ledit_view *view, char *text, size_t len) { 2050 (void)text; 2051 (void)len; 2052 motion_callback cb; 2053 int num = get_key_repeat_and_motion_cb(view, &cb); 2054 if (num != 0) 2055 return err_invalid_key(view); 2056 size_t new_index = 0; 2057 if (view->buffer->hard_line_based) { 2058 new_index = view_line_next_non_whitespace(view, view->cur_line, 0); 2059 } else { 2060 size_t start, end; 2061 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end); 2062 new_index = view_line_next_non_whitespace(view, view->cur_line, start); 2063 /* next non-whitespace might be on next softline */ 2064 if (new_index >= end) { 2065 view_prev_cursor_pos( 2066 view, view->cur_line, end, 1, 0, NULL, &new_index 2067 ); 2068 } 2069 } 2070 if (cb != NULL) { 2071 cb(view, view->cur_line, new_index, KEY_MOTION_CHAR); 2072 } else { 2073 view->cur_index = new_index; 2074 if (view->mode == VISUAL) { 2075 view_set_selection( 2076 view, 2077 view->sel.line1, view->sel.byte1, 2078 view->cur_line, view->cur_index 2079 ); 2080 } else if (view->mode == NORMAL) { 2081 view_set_line_cursor_attrs( 2082 view, view->cur_line, view->cur_index 2083 ); 2084 } 2085 discard_repetition_stack(); 2086 } 2087 return (struct action){ACTION_NONE, NULL}; 2088 } 2089 2090 static struct action 2091 cursor_to_beginning(ledit_view *view, char *text, size_t len) { 2092 (void)text; 2093 (void)len; 2094 motion_callback cb; 2095 int num = get_key_repeat_and_motion_cb(view, &cb); 2096 if (num != 0) 2097 return err_invalid_key(view); 2098 /* FIXME: should anything be done with num? */ 2099 size_t start_index = 0; 2100 if (!view->buffer->hard_line_based) { 2101 size_t tmp; 2102 view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start_index, &tmp); 2103 } 2104 if (cb != NULL) { 2105 cb(view, view->cur_line, start_index, KEY_MOTION_CHAR); 2106 } else { 2107 view->cur_index = start_index; 2108 if (view->mode == VISUAL) { 2109 view_set_selection( 2110 view, 2111 view->sel.line1, view->sel.byte1, 2112 view->cur_line, view->cur_index 2113 ); 2114 } else if (view->mode == NORMAL) { 2115 view_set_line_cursor_attrs( 2116 view, view->cur_line, view->cur_index 2117 ); 2118 } 2119 discard_repetition_stack(); 2120 } 2121 clear_key_stack(); 2122 return (struct action){ACTION_NONE, NULL}; 2123 } 2124 2125 static struct action 2126 enter_visual(ledit_view *view, char *text, size_t len) { 2127 (void)text; 2128 (void)len; 2129 view_set_mode(view, VISUAL); 2130 /* FIXME: set view->sel_valid? */ 2131 view->sel.line1 = view->sel.line2 = view->cur_line; 2132 view->sel.byte1 = view->sel.byte2 = view->cur_index; 2133 view_wipe_line_cursor_attrs(view, view->cur_line); 2134 clear_key_stack(); /* FIXME: error if not empty? */ 2135 return (struct action){ACTION_NONE, NULL}; 2136 } 2137 2138 static struct action 2139 switch_selection_end(ledit_view *view, char *text, size_t len) { 2140 (void)text; 2141 (void)len; 2142 swap_sz(&view->sel.line1, &view->sel.line2); 2143 swap_sz(&view->sel.byte1, &view->sel.byte2); 2144 view->cur_line = view->sel.line2; 2145 view->cur_index = view->sel.byte2; 2146 return (struct action){ACTION_NONE, NULL}; 2147 } 2148 2149 static struct action 2150 enter_commandedit(ledit_view *view, char *text, size_t len) { 2151 (void)text; 2152 (void)len; 2153 /* FIXME: wipe selection? */ 2154 char *str = view->sel_valid ? ":'<,'>" : ":"; 2155 window_set_bottom_bar_text(view->window, str, -1); 2156 window_set_bottom_bar_cursor(view->window, strlen(str)); 2157 window_set_bottom_bar_min_pos(view->window, 1); 2158 view->cur_command_type = CMD_EDIT; 2159 window_set_bottom_bar_text_shown(view->window, 1); 2160 discard_repetition_stack(); 2161 return (struct action){ACTION_GRABKEY, &command_key_handler}; 2162 } 2163 2164 /* FIXME: support visual mode - maybe change selection to new position 2165 or at least support only searching within the range given by the 2166 selection */ 2167 static struct action 2168 enter_searchedit_forward(ledit_view *view, char *text, size_t len) { 2169 (void)text; 2170 (void)len; 2171 window_set_bottom_bar_text(view->window, "/", -1); 2172 window_set_bottom_bar_min_pos(view->window, 1); 2173 window_set_bottom_bar_cursor(view->window, 1); 2174 view->cur_command_type = CMD_EDITSEARCH; 2175 window_set_bottom_bar_text_shown(view->window, 1); 2176 discard_repetition_stack(); 2177 return (struct action){ACTION_GRABKEY, &command_key_handler}; 2178 } 2179 2180 static struct action 2181 enter_searchedit_backward(ledit_view *view, char *text, size_t len) { 2182 (void)text; 2183 (void)len; 2184 window_set_bottom_bar_text(view->window, "?", -1); 2185 window_set_bottom_bar_min_pos(view->window, 1); 2186 window_set_bottom_bar_cursor(view->window, 1); 2187 view->cur_command_type = CMD_EDITSEARCHB; 2188 window_set_bottom_bar_text_shown(view->window, 1); 2189 discard_repetition_stack(); 2190 return (struct action){ACTION_GRABKEY, &command_key_handler}; 2191 } 2192 2193 /* FIXME: differentiate between jumping to line and index like nvi */ 2194 static struct action 2195 insert_mark_cb(ledit_view *view, char *text, size_t len) { 2196 grab_char_cb = NULL; 2197 buffer_insert_mark( 2198 view->buffer, text, len, view->cur_line, view->cur_index 2199 ); 2200 return (struct action){ACTION_NONE, NULL}; 2201 } 2202 2203 static struct action 2204 jump_to_mark_cb(ledit_view *view, char *text, size_t len) { 2205 grab_char_cb = NULL; 2206 motion_callback cb; 2207 int num = get_key_repeat_and_motion_cb(view, &cb); 2208 if (num > 0) 2209 return err_invalid_key(view); 2210 size_t line = 0, index = 0; 2211 /* FIXME: better error */ 2212 if (buffer_get_mark(view->buffer, text, len, &line, &index)) 2213 return err_invalid_key(view); 2214 if (view->mode == VISUAL) { 2215 view_set_selection( 2216 view, view->sel.line1, view->sel.byte1, line, index 2217 ); 2218 view->cur_line = line; 2219 view->cur_index = index; 2220 } else { 2221 if (cb) { 2222 cb(view, line, index, KEY_MOTION_LINE); 2223 } else { 2224 view_wipe_line_cursor_attrs(view, view->cur_line); 2225 view->cur_line = line; 2226 view->cur_index = index; 2227 if (view->mode == NORMAL) { 2228 view->cur_index = view_get_legal_normal_pos( 2229 view, view->cur_line, view->cur_index 2230 ); 2231 view_set_line_cursor_attrs( 2232 view, view->cur_line, view->cur_index 2233 ); 2234 } 2235 discard_repetition_stack(); 2236 } 2237 } 2238 return (struct action){ACTION_NONE, NULL}; 2239 } 2240 2241 static struct action 2242 insert_mark(ledit_view *view, char *text, size_t len) { 2243 (void)view; 2244 (void)text; 2245 (void)len; 2246 grab_char_cb = &insert_mark_cb; 2247 discard_repetition_stack(); 2248 return (struct action){ACTION_NONE, NULL}; 2249 } 2250 2251 static struct action 2252 jump_to_mark(ledit_view *view, char *text, size_t len) { 2253 (void)view; 2254 (void)text; 2255 (void)len; 2256 grab_char_cb = &jump_to_mark_cb; 2257 /* FIXME: should it be discarded here? */ 2258 discard_repetition_stack(); 2259 return (struct action){ACTION_NONE, NULL}; 2260 } 2261 2262 /* FIXME: support visual mode, i.e. change selection to new place? */ 2263 static struct action 2264 key_search_next(ledit_view *view, char *text, size_t len) { 2265 (void)text; 2266 (void)len; 2267 search_next(view); 2268 discard_repetition_stack(); 2269 return (struct action){ACTION_NONE, NULL}; 2270 } 2271 2272 static struct action 2273 key_search_prev(ledit_view *view, char *text, size_t len) { 2274 (void)text; 2275 (void)len; 2276 search_prev(view); 2277 discard_repetition_stack(); 2278 return (struct action){ACTION_NONE, NULL}; 2279 } 2280 2281 static struct action 2282 show_line(ledit_view *view, char *text, size_t len) { 2283 (void)text; 2284 (void)len; 2285 window_show_message_fmt( 2286 view->window, 2287 "%s: %s: line %zu of %zu", 2288 view->buffer->filename ? view->buffer->filename : "(no filename)", 2289 view->buffer->modified ? "modified" : "unmodified", 2290 add_sz(view->cur_line, 1), view->lines_num 2291 ); 2292 discard_repetition_stack(); 2293 return (struct action){ACTION_NONE, NULL}; 2294 } 2295 2296 static struct action 2297 undo(ledit_view *view, char *text, size_t len) { 2298 (void)text; 2299 (void)len; 2300 int num = get_key_repeat(); 2301 if (num == -1) 2302 return err_invalid_key(view); 2303 if (num == 0) 2304 num = 1; 2305 view_wipe_selection(view); 2306 view_undo(view, num); 2307 if (view->mode != INSERT) 2308 finalize_repetition_stack(); 2309 return (struct action){ACTION_NONE, NULL}; 2310 } 2311 2312 static struct action 2313 redo(ledit_view *view, char *text, size_t len) { 2314 (void)text; 2315 (void)len; 2316 int num = get_key_repeat(); 2317 if (num == -1) 2318 return err_invalid_key(view); 2319 if (num == 0) 2320 num = 1; 2321 view_wipe_selection(view); 2322 view_redo(view, num); 2323 if (view->mode != INSERT) 2324 finalize_repetition_stack(); 2325 return (struct action){ACTION_NONE, NULL}; 2326 } 2327 2328 static struct action 2329 insert_mode_insert_text(ledit_view *view, char *text, size_t len) { 2330 if (!key_stack_empty()) 2331 return err_invalid_key(view); 2332 /* this shouldn't be necessary */ 2333 delete_selection(view); 2334 insert_text(view, view->cur_line, view->cur_index, text, len, 0, 0, 0, 0, 0, 0, 1); 2335 return (struct action){ACTION_NONE, NULL}; 2336 } 2337 2338 static struct action 2339 clipcopy(ledit_view *view, char *text, size_t len) { 2340 (void)text; 2341 (void)len; 2342 if (!key_stack_empty()) 2343 return err_invalid_key(view); 2344 /* FIXME: abstract this through view */ 2345 clipboard_primary_to_clipboard(view->buffer->clipboard); 2346 discard_repetition_stack(); 2347 return (struct action){ACTION_NONE, NULL}; 2348 } 2349 2350 static struct action 2351 clippaste(ledit_view *view, char *text, size_t len) { 2352 (void)text; 2353 (void)len; 2354 if (!key_stack_empty()) 2355 return err_invalid_key(view); 2356 /* FIXME: the selection deletion and pasting should be in the same undo group */ 2357 if (view->mode == VISUAL) { 2358 /* Note; this sets the current position */ 2359 delete_selection(view); 2360 } 2361 view_paste_clipboard(view); 2362 if (view->mode != INSERT) 2363 finalize_repetition_stack(); 2364 return (struct action){ACTION_NONE, NULL}; 2365 } 2366 2367 /* FIXME: make sure the found position is valid cursor position? */ 2368 static int 2369 search_str_backwards(char *haystack, size_t hlen, char *needle, size_t nlen, size_t start_index, size_t *ret) { 2370 if (start_index > hlen) 2371 return -1; 2372 size_t new_index = start_index; 2373 for (; new_index > 0; new_index--) { 2374 if (!strncmp(haystack + new_index - 1, needle, nlen)) { 2375 *ret = new_index - 1; 2376 return 0; 2377 } 2378 } 2379 return -1; 2380 } 2381 2382 static int 2383 search_str_forwards(char *haystack, size_t hlen, char *needle, size_t nlen, size_t start_index, size_t *ret) { 2384 if (start_index >= hlen) 2385 return -1; 2386 /* duplicate so it is nul-terminated */ 2387 char *search_str = ledit_strndup(needle, nlen); 2388 char *res = strstr(haystack + start_index + 1, search_str); 2389 free(search_str); 2390 /* FIXME: is this legal? */ 2391 if (res) { 2392 *ret = (size_t)(res - haystack); 2393 return 0; 2394 } else { 2395 return -1; 2396 } 2397 } 2398 2399 /* just to make the macro below work for all cases */ 2400 /* FIXME: is there a more elegant way to do this? */ 2401 static void 2402 dummy_cursor_helper( 2403 ledit_view *view, size_t line, size_t byte, 2404 int num, int multiline, size_t *line_ret, size_t *byte_ret) { 2405 (void)view; (void)num; (void)multiline; 2406 if (line_ret) 2407 *line_ret = line; 2408 if (byte_ret) 2409 *byte_ret = byte; 2410 } 2411 2412 /* FIXME: add checks to functions that current mode is supported */ 2413 2414 /* name is the name of the generated pair of functions 2415 search_func is used to get the next index (possibly called 2416 repeatedly if there is a repeat number on the key stack) 2417 funcm = func motion, funcn = func normal, funcv = func visual 2418 -> these are called to modify the index returned by search_func 2419 cur_funcm is called to get the index for a motion callback 2420 cur_funcn is called to position the cursor in normal and insert mode 2421 cur_funcv is called to position the cursor in visual mode */ 2422 #define GEN_MOVE_TO_CHAR(name, search_func, cur_funcm, cur_funcn, cur_funcv) \ 2423 static struct action \ 2424 name##_cb(ledit_view *view, char *text, size_t len) { \ 2425 motion_callback cb = NULL; \ 2426 int num = get_key_repeat_and_motion_cb(view, &cb); \ 2427 if (num == -1) \ 2428 return err_invalid_key(view); \ 2429 if (num == 0) \ 2430 num = 1; \ 2431 ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); \ 2432 size_t new_index = view->cur_index; \ 2433 int ret = -1; \ 2434 for (int i = 0; i < num; i++) { \ 2435 if ((ret = search_func( \ 2436 ll->text, ll->len, text, len, \ 2437 new_index, &new_index)) == -1) { \ 2438 break; \ 2439 } \ 2440 } \ 2441 if (!ret) { \ 2442 if (cb != NULL) { \ 2443 cur_funcm( \ 2444 view, view->cur_line, new_index, \ 2445 1, 0, NULL, &new_index \ 2446 ); \ 2447 cb(view, view->cur_line, new_index, KEY_MOTION_CHAR); \ 2448 } else { \ 2449 if (view->mode == VISUAL) { \ 2450 cur_funcv( \ 2451 view, view->cur_line, new_index, \ 2452 1, 0, NULL, &view->cur_index \ 2453 ); \ 2454 view_set_selection( \ 2455 view, \ 2456 view->sel.line1, view->sel.byte1, \ 2457 view->cur_line, view->cur_index \ 2458 ); \ 2459 } else { \ 2460 cur_funcn( \ 2461 view, view->cur_line, new_index, \ 2462 1, 0, NULL, &view->cur_index \ 2463 ); \ 2464 if (view->mode == NORMAL) { \ 2465 view_set_line_cursor_attrs( \ 2466 view, view->cur_line, view->cur_index \ 2467 ); \ 2468 } \ 2469 } \ 2470 discard_repetition_stack(); \ 2471 } \ 2472 } \ 2473 clear_key_stack(); \ 2474 grab_char_cb = NULL; \ 2475 return (struct action){ACTION_NONE, NULL}; \ 2476 } \ 2477 \ 2478 static struct action \ 2479 name(ledit_view *view, char *text, size_t len) { \ 2480 (void)view; \ 2481 (void)text; \ 2482 (void)len; \ 2483 grab_char_cb = &name##_cb; \ 2484 return (struct action){ACTION_NONE, NULL}; \ 2485 } 2486 2487 /* FIXME: more sensible names */ 2488 /* FIXME: dummy_cursor_helper is kind of ugly */ 2489 GEN_MOVE_TO_CHAR( 2490 find_next_char_forwards, search_str_forwards, 2491 dummy_cursor_helper, view_prev_cursor_pos, dummy_cursor_helper 2492 ) 2493 GEN_MOVE_TO_CHAR( 2494 find_next_char_backwards, search_str_backwards, 2495 view_next_cursor_pos, view_next_cursor_pos, view_next_cursor_pos 2496 ) 2497 GEN_MOVE_TO_CHAR( 2498 find_char_forwards, search_str_forwards, 2499 view_next_cursor_pos, dummy_cursor_helper, dummy_cursor_helper 2500 ) 2501 GEN_MOVE_TO_CHAR( 2502 find_char_backwards, search_str_backwards, 2503 dummy_cursor_helper, dummy_cursor_helper, dummy_cursor_helper 2504 ) 2505 2506 static struct action 2507 loweruppercase(ledit_view *view, int upper) { 2508 /* FIXME: shouldn't CHECK_VIEW_LOCKED be in a lot more places? */ 2509 CHECK_VIEW_LOCKED(view); 2510 /* FIXME: move most of this to convenience functions in buffer.c */ 2511 ledit_line *line = buffer_get_line(view->buffer, view->cur_line); 2512 if (view->cur_index >= line->len) 2513 return (struct action){ACTION_NONE, NULL}; 2514 buffer_normalize_line(line); 2515 size_t start_index = view->cur_index; 2516 #if ENABLE_UTF8PROC 2517 utf8proc_int32_t c; 2518 /* FIXME: this cast to utf8proc_uint8_t probably doesn't break anything, but could it? */ 2519 utf8proc_ssize_t origlen = utf8proc_iterate((utf8proc_uint8_t *)line->text + start_index, line->len - start_index, &c); 2520 /* FIXME: show error message? */ 2521 if (c < 0 || origlen < 1) 2522 return (struct action){ACTION_NONE, NULL}; 2523 utf8proc_int32_t u; 2524 if (upper) 2525 u = utf8proc_toupper(c); 2526 else 2527 u = utf8proc_tolower(c); 2528 utf8proc_uint8_t u8[4]; 2529 utf8proc_ssize_t newlen = utf8proc_encode_char(u, u8); 2530 if (newlen < 1) 2531 return (struct action){ACTION_NONE, NULL}; 2532 delete_range( 2533 view, 0, 0, 2534 view->cur_line, start_index, view->cur_line, start_index + origlen, 0 2535 ); 2536 insert_text( 2537 view, view->cur_line, start_index, (char *)u8, newlen, 2538 view->cur_line, start_index, view->cur_line, start_index, 1, 1, 0 2539 ); 2540 #else 2541 char c; 2542 if (upper) 2543 c = toupper((unsigned char)line->text[view->cur_index]); 2544 else 2545 c = tolower((unsigned char)line->text[view->cur_index]); 2546 delete_range( 2547 view, 0, 0, 2548 view->cur_line, start_index, view->cur_line, start_index + 1, 0 2549 ); 2550 insert_text( 2551 view, view->cur_line, start_index, &c, 1, 2552 view->cur_line, start_index, view->cur_line, start_index, 1, 1, 0 2553 ); 2554 #endif 2555 /* If the last character on a line is replaced, the cursor would jump 2556 backwards due to the deletion, so it has to be set to the original 2557 position again */ 2558 view->cur_index = start_index; 2559 push_undo_empty_insert(view, view->cur_line, view->cur_index, 0); 2560 if (view->mode == NORMAL) 2561 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 2562 finalize_repetition_stack(); 2563 return (struct action){ACTION_NONE, NULL}; 2564 } 2565 2566 static struct action 2567 uppercase(ledit_view *view, char *text, size_t len) { 2568 (void)text; 2569 (void)len; 2570 return loweruppercase(view, 1); 2571 } 2572 2573 static struct action 2574 lowercase(ledit_view *view, char *text, size_t len) { 2575 (void)text; 2576 (void)len; 2577 return loweruppercase(view, 0); 2578 } 2579 2580 static struct action 2581 replace_cb(ledit_view *view, char *text, size_t len) { 2582 CHECK_VIEW_LOCKED(view); 2583 size_t start_index = view->cur_index; 2584 /* FIXME: replace with (key repeat) * text instead of just text */ 2585 /* FIXME: cursor pos or char? */ 2586 size_t end_index; 2587 view_next_cursor_pos( 2588 view, view->cur_line, view->cur_index, 1, 0, NULL, &end_index 2589 ); 2590 delete_range( 2591 view, 0, 0, 2592 view->cur_line, start_index, view->cur_line, end_index, 0 2593 ); 2594 insert_text( 2595 view, view->cur_line, start_index, text, len, 2596 view->cur_line, start_index, view->cur_line, start_index, 1, 1, 0 2597 ); 2598 /* If the last character on a line is replaced, the cursor would jump 2599 backwards due to the deletion, so it has to be set to the original 2600 position again */ 2601 view->cur_index = start_index; 2602 push_undo_empty_insert(view, view->cur_line, view->cur_index, 0); 2603 if (view->mode == NORMAL) 2604 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); 2605 grab_char_cb = NULL; 2606 finalize_repetition_stack(); 2607 return (struct action){ACTION_NONE, NULL}; 2608 } 2609 2610 static struct action 2611 replace(ledit_view *view, char *text, size_t len) { 2612 (void)view; 2613 (void)text; 2614 (void)len; 2615 int num = get_key_repeat(); 2616 if (num != 0) 2617 return err_invalid_key(view); 2618 grab_char_cb = &replace_cb; 2619 return (struct action){ACTION_NONE, NULL}; 2620 } 2621 2622 static struct action 2623 toggle_hard_line_based(ledit_view *view, char *text, size_t len) { 2624 (void)view; 2625 (void)text; 2626 (void)len; 2627 int num = get_key_repeat(); 2628 if (num != 0) 2629 return err_invalid_key(view); 2630 buffer_set_hard_line_based(view->buffer, !view->buffer->hard_line_based); 2631 discard_repetition_stack(); 2632 return (struct action){ACTION_NONE, NULL}; 2633 } 2634 2635 static struct action 2636 handle_key(ledit_view *view, char *key_text, size_t len, KeySym sym, unsigned int key_state, size_t lang_index, int *found, basic_key_cb_flags *flags) { 2637 basic_key_array *cur_keys = config_get_basic_keys(lang_index); 2638 size_t num_keys = cur_keys->num_keys; 2639 /* FIXME: check if control chars in text */ 2640 /* FIXME: this is a bit of a hack because it's hardcoded */ 2641 if (grab_char_cb && sym == XK_Escape) { 2642 grab_char_cb = NULL; 2643 return (struct action){ACTION_NONE, NULL}; 2644 } else if (len > 0 && grab_char_cb) { 2645 *found = 1; 2646 *flags = 0; 2647 return grab_char_cb(view, key_text, len); 2648 } 2649 *found = 0; 2650 for (size_t i = 0; i < num_keys; i++) { 2651 if (cur_keys->keys[i].text) { 2652 if (len > 0 && 2653 (cur_keys->keys[i].modes & view->mode) && 2654 ((!strncmp(cur_keys->keys[i].text, key_text, len) && 2655 match_key(cur_keys->keys[i].mods, key_state & ~ShiftMask)) || 2656 cur_keys->keys[i].text[0] == '\0')) { 2657 /* FIXME: seems a bit hacky to remove shift, but it 2658 is needed to make keys that use shift match */ 2659 *flags = cur_keys->keys[i].cb->flags; 2660 *found = 1; 2661 if (!(*flags & KEY_FLAG_LOCK_ALLOWED) && view->lock_text) 2662 return view_locked_error(view); 2663 return cur_keys->keys[i].cb->func(view, key_text, len); 2664 } 2665 } else if ((cur_keys->keys[i].modes & view->mode) && 2666 cur_keys->keys[i].keysym == sym && 2667 match_key(cur_keys->keys[i].mods, key_state)) { 2668 *flags = cur_keys->keys[i].cb->flags; 2669 *found = 1; 2670 if (!(*flags & KEY_FLAG_LOCK_ALLOWED) && view->lock_text) 2671 return view_locked_error(view); 2672 return cur_keys->keys[i].cb->func(view, key_text, len); 2673 } 2674 } 2675 return (struct action){ACTION_NONE, NULL}; 2676 } 2677 2678 static struct action 2679 repeat_command(ledit_view *view, char *text, size_t len) { 2680 (void)view; 2681 (void)text; 2682 (void)len; 2683 int num = get_key_repeat(); 2684 if (num == -1) 2685 return err_invalid_key(view); 2686 if (num == 0) 2687 num = 1; 2688 if (repetition_stack.len == 0) { 2689 window_show_message(view->window, "No previous command", -1); 2690 discard_repetition_stack(); 2691 return (struct action){ACTION_NONE, NULL}; 2692 } 2693 int found; 2694 basic_key_cb_flags flags; 2695 repetition_stack.replaying = 1; 2696 for (int i = 0; i < num; i++) { 2697 unwind_repetition_stack(); 2698 struct repetition_stack_elem *e = get_cur_repetition_stack_elem(); 2699 while (e) { 2700 (void)handle_key(view, e->key_text, e->len, e->sym, e->key_state, e->lang_index, &found, &flags); 2701 advance_repetition_stack(); 2702 e = get_cur_repetition_stack_elem(); 2703 } 2704 } 2705 repetition_stack.replaying = 0; 2706 discard_repetition_stack(); 2707 clear_key_stack(); 2708 return (struct action){ACTION_NONE, NULL}; 2709 } 2710 2711 struct action 2712 basic_key_handler(ledit_view *view, unsigned int key_state, KeySym sym, char *buf, int n, int lang_index) { 2713 struct repetition_stack_elem *re = push_repetition_stack(); 2714 re->key_text = ledit_strndup(buf, (size_t)n); 2715 re->len = (size_t)n; 2716 re->sym = sym; 2717 re->key_state = key_state; 2718 re->lang_index = lang_index; 2719 2720 /* FIXME: figure out when to actually hide message and 2721 ensure cursor shown */ 2722 /* FIXME: clean up interface (what has a setter method and what not) */ 2723 int found = 0; 2724 int msg_shown = view->window->message_shown; 2725 view->window->message_shown = 0; /* FIXME: this is hacky */ 2726 basic_key_cb_flags flags; 2727 struct action act = handle_key(view, buf, (size_t)n, sym, key_state, lang_index, &found, &flags); 2728 /* FIXME: make message hiding a property of each command so e.g. cursor keys also hide it */ 2729 if (found && n > 0 && !view->window->message_shown) 2730 window_hide_message(view->window); 2731 else if (msg_shown) 2732 view->window->message_shown = msg_shown; 2733 2734 if (found && (flags & KEY_FLAG_JUMP_TO_CURSOR)) 2735 view_ensure_cursor_shown(view); 2736 /* FIXME: this also doesn't show real invalid keys in insert mode 2737 -> it needs to be this way to avoid showing anything on modifier 2738 keys, but maybe it should just check explicitly for modifier keys? */ 2739 if (!found && n > 0) { 2740 window_show_message(view->window, "Invalid key", -1); 2741 discard_repetition_stack(); 2742 clear_key_stack(); 2743 } 2744 return act; 2745 }