diff --git a/apps/bim.c b/apps/bim.c index 893ac61a..d8e87d65 100644 --- a/apps/bim.c +++ b/apps/bim.c @@ -372,6 +372,7 @@ buffer_t * env; #define MODE_NORMAL 0 #define MODE_INSERT 1 #define MODE_LINE_SELECTION 2 +#define MODE_REPLACE 3 /** * Available buffers @@ -2015,6 +2016,11 @@ void redraw_commandline(void) { printf("-- LINE SELECTION --"); clear_to_end(); reset(); + } else if (env->mode == MODE_REPLACE) { + set_bold(); + printf("-- REPLACE --"); + clear_to_end(); + reset(); } else { clear_to_end(); } @@ -3658,6 +3664,25 @@ void insert_char(unsigned int c) { set_modified(); } +/** + * Replace a single character at the current cursor point + */ +void replace_char(unsigned int c) { + if (env->col_no < 1 || env->col_no > env->lines[env->line_no-1]->actual) return; + + char_t _c; + _c.codepoint = c; + _c.flags = 0; + _c.display_width = codepoint_width(c); + + env->lines[env->line_no-1]->text[env->col_no-1] = _c; + recalculate_tabs(env->lines[env->line_no-1]); + recalculate_syntax(env->lines[env->line_no-1], env->line_no); + + redraw_line(env->line_no - env->offset - 1, env->line_no-1); + set_modified(); +} + /** * Move the cursor the start of the previous word. */ @@ -3817,7 +3842,7 @@ int handle_escape(int * this_buf, int * timeout, int c) { goto_line(env->line_no - (global_config.term_height - 6)); break; case '3': - if (env->mode == MODE_INSERT) { + if (env->mode == MODE_INSERT || env->mode == MODE_REPLACE) { if (env->col_no < env->lines[env->line_no - 1]->actual + 1) { line_delete(env->lines[env->line_no - 1], env->col_no, env->line_no - 1); redraw_line(env->line_no - env->offset - 1, env->line_no-1); @@ -4103,6 +4128,49 @@ _leave_select_line: redraw_all(); } +/** + * Backspace from the current cursor position. + */ +void delete_at_cursor(void) { + if (env->col_no > 1) { + line_delete(env->lines[env->line_no - 1], env->col_no - 1, env->line_no - 1); + env->col_no -= 1; + redraw_line(env->line_no - env->offset - 1, env->line_no-1); + set_modified(); + redraw_statusbar(); + place_cursor_actual(); + } else if (env->line_no > 1) { + int tmp = env->lines[env->line_no - 2]->actual; + merge_lines(env->lines, env->line_no - 1); + env->line_no -= 1; + env->col_no = tmp+1; + redraw_text(); + set_modified(); + redraw_statusbar(); + place_cursor_actual(); + } +} + +/** + * Break the current line in two at the current cursor position. + */ +void insert_line_feed(void) { + if (env->col_no == env->lines[env->line_no - 1]->actual + 1) { + env->lines = add_line(env->lines, env->line_no); + } else { + env->lines = split_line(env->lines, env->line_no, env->col_no - 1); + } + env->col_no = 1; + env->line_no += 1; + add_indent(env->line_no-1,env->line_no-2); + if (env->line_no > env->offset + global_config.term_height - global_config.bottom_size - 1) { + env->offset += 1; + } + redraw_text(); + set_modified(); + redraw_statusbar(); + place_cursor_actual(); +} /** * INSERT mode @@ -4143,41 +4211,10 @@ void insert_mode(void) { break; case DELETE_KEY: case BACKSPACE_KEY: - if (env->col_no > 1) { - line_delete(env->lines[env->line_no - 1], env->col_no - 1, env->line_no - 1); - env->col_no -= 1; - redraw_line(env->line_no - env->offset - 1, env->line_no-1); - set_modified(); - redraw_statusbar(); - place_cursor_actual(); - } else if (env->line_no > 1) { - int tmp = env->lines[env->line_no - 2]->actual; - merge_lines(env->lines, env->line_no - 1); - env->line_no -= 1; - env->col_no = tmp+1; - redraw_text(); - set_modified(); - redraw_statusbar(); - place_cursor_actual(); - } + delete_at_cursor(); break; case ENTER_KEY: - if (env->col_no == env->lines[env->line_no - 1]->actual + 1) { - env->lines = add_line(env->lines, env->line_no); - } else { - /* oh oh god we're all gonna die */ - env->lines = split_line(env->lines, env->line_no, env->col_no - 1); - } - env->col_no = 1; - env->line_no += 1; - add_indent(env->line_no-1,env->line_no-2); - if (env->line_no > env->offset + global_config.term_height - global_config.bottom_size - 1) { - env->offset += 1; - } - redraw_text(); - set_modified(); - redraw_statusbar(); - place_cursor_actual(); + insert_line_feed(); break; case '\t': if (env->tabs) { @@ -4209,6 +4246,80 @@ void insert_mode(void) { } } +/* + * REPLACE mode + * + * Like insert, but replaces characters. + */ +void replace_mode(void) { + int cin; + uint32_t c; + + /* Set mode line */ + env->mode = MODE_REPLACE; + redraw_commandline(); + + /* Place the cursor in the text area */ + place_cursor_actual(); + + int timeout = 0; + int this_buf[20]; + uint32_t istate = 0; + while ((cin = bim_getch())) { + if (cin == -1) { + if (timeout && this_buf[timeout-1] == '\033') { + leave_insert(); + return; + } + timeout = 0; + continue; + } + if (!decode(&istate, &c, cin)) { + if (timeout == 0) { + switch (c) { + case '\033': + if (timeout == 0) { + this_buf[timeout] = c; + timeout++; + } + break; + case DELETE_KEY: + case BACKSPACE_KEY: + if (env->line_no > 1 && env->col_no == 1) { + env->line_no--; + env->col_no = env->lines[env->line_no-1]->actual; + place_cursor_actual(); + } else { + cursor_left(); + } + break; + case ENTER_KEY: + insert_line_feed(); + break; + default: + if (env->col_no <= env->lines[env->line_no - 1]->actual) { + replace_char(c); + env->col_no += 1; + } else { + insert_char(c); + } + redraw_statusbar(); + place_cursor_actual(); + break; + } + } else { + if (handle_escape(this_buf,&timeout,c)) { + bim_unget(c); + leave_insert(); + return; + } + } + } else if (istate == UTF8_REJECT) { + istate = 0; + } + } +} + static void show_usage(char * argv[]) { printf( "bim - Text editor\n" @@ -4358,7 +4469,13 @@ int main(int argc, char * argv[]) { break; case DELETE_KEY: case BACKSPACE_KEY: - cursor_left(); + if (env->line_no > 1 && env->col_no == 1) { + env->line_no--; + env->col_no = env->lines[env->line_no-1]->actual; + place_cursor_actual(); + } else { + cursor_left(); + } break; case ':': /* Switch to command mode */ @@ -4465,6 +4582,13 @@ _insert: redraw_commandline(); timeout = 0; break; + case 'R': + if (env->readonly) goto _readonly; + replace_mode(); + redraw_statusbar(); + redraw_commandline(); + timeout = 0; + break; _readonly: render_error("Buffer is read-only"); break;