diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-03-22 14:58:12 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-03-22 15:00:51 +0300 |
commit | fe69fdb29f994db3bbcaffcb202856618b03b9b0 (patch) | |
tree | 3108dbb02c2df0fced836b8fef6216a04bbab770 /src/core-commands.c | |
parent | 867d22e419afe769f05ad26b61c6ea5ea1432c3c (diff) | |
download | sciteco-fe69fdb29f994db3bbcaffcb202856618b03b9b0.tar.gz |
factored out all cursor movement and deletion commands into the new compilation unit move-commands.c
* This made sense to include both `W` and `V`, so we also included `D` and `K`.
`^Q` is included since it converts between lines and glyphs.
* These are all single-letter commands, so they aren't complete parser states
but callbacks to be referenced in teco_machine_main_transition_t.
Diffstat (limited to 'src/core-commands.c')
-rw-r--r-- | src/core-commands.c | 600 |
1 files changed, 1 insertions, 599 deletions
diff --git a/src/core-commands.c b/src/core-commands.c index 8cbb4be..f39f6f9 100644 --- a/src/core-commands.c +++ b/src/core-commands.c @@ -44,6 +44,7 @@ #include "qreg.h" #include "qreg-commands.h" #include "goto-commands.h" +#include "move-commands.h" #include "core-commands.h" static teco_state_t *teco_state_control_input(teco_machine_main_t *ctx, gunichar chr, GError **error); @@ -516,422 +517,6 @@ teco_state_start_cmdline_pop(teco_machine_main_t *ctx, GError **error) g_set_error_literal(error, TECO_ERROR, TECO_ERROR_CMDLINE, ""); } -/*$ J jump - * [position]J -- Go to position in buffer - * [position]:J -> Success|Failure - * - * Sets dot to <position>. - * If <position> is omitted, 0 is implied and \(lqJ\(rq will - * go to the beginning of the buffer. - * - * If <position> is outside the range of the buffer, the - * command yields an error. - * If colon-modified, the command will instead return a - * condition boolean signalling whether the position could - * be changed or not. - */ -static void -teco_state_start_jump(teco_machine_main_t *ctx, GError **error) -{ - teco_int_t v; - - if (!teco_expressions_pop_num_calc(&v, 0, error)) - return; - - gssize pos = teco_interface_glyphs2bytes(v); - if (pos >= 0) { - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_GOTOPOS, - teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), 0); - teco_interface_ssm(SCI_GOTOPOS, pos, 0); - - if (teco_machine_main_eval_colon(ctx) > 0) - teco_expressions_push(TECO_SUCCESS); - } else if (teco_machine_main_eval_colon(ctx) > 0) { - teco_expressions_push(TECO_FAILURE); - } else { - teco_error_move_set(error, "J"); - return; - } -} - -static teco_bool_t -teco_move_chars(teco_int_t n) -{ - sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); - gssize next_pos = teco_interface_glyphs2bytes_relative(pos, n); - if (next_pos < 0) - return TECO_FAILURE; - - teco_interface_ssm(SCI_GOTOPOS, next_pos, 0); - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0); - - return TECO_SUCCESS; -} - -/*$ C move - * [n]C -- Move dot <n> characters - * -C - * [n]:C -> Success|Failure - * - * Adds <n> to dot. 1 or -1 is implied if <n> is omitted. - * Fails if <n> would move dot off-page. - * The colon modifier results in a success-boolean being - * returned instead. - */ -static void -teco_state_start_move(teco_machine_main_t *ctx, GError **error) -{ - teco_int_t v; - - if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error)) - return; - - teco_bool_t rc = teco_move_chars(v); - if (teco_machine_main_eval_colon(ctx) > 0) { - teco_expressions_push(rc); - } else if (teco_is_failure(rc)) { - teco_error_move_set(error, "C"); - return; - } -} - -/*$ R reverse - * [n]R -- Move dot <n> characters backwards - * -R - * [n]:R -> Success|Failure - * - * Subtracts <n> from dot. - * It is equivalent to \(lq-nC\(rq. - */ -static void -teco_state_start_reverse(teco_machine_main_t *ctx, GError **error) -{ - teco_int_t v; - - if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error)) - return; - - teco_bool_t rc = teco_move_chars(-v); - if (teco_machine_main_eval_colon(ctx) > 0) { - teco_expressions_push(rc); - } else if (teco_is_failure(rc)) { - teco_error_move_set(error, "R"); - return; - } -} - -static teco_bool_t -teco_move_lines(teco_int_t n) -{ - sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); - sptr_t line = teco_interface_ssm(SCI_LINEFROMPOSITION, pos, 0) + n; - - if (!teco_validate_line(line)) - return TECO_FAILURE; - - teco_interface_ssm(SCI_GOTOLINE, line, 0); - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0); - - return TECO_SUCCESS; -} - -/*$ L line - * [n]L -- Move dot <n> lines forwards - * -L - * [n]:L -> Success|Failure - * - * Move dot to the beginning of the line specified - * relatively to the current line. - * Therefore a value of 0 for <n> goes to the - * beginning of the current line, 1 will go to the - * next line, -1 to the previous line etc. - * If <n> is omitted, 1 or -1 is implied depending on - * the sign prefix. - * - * If <n> would move dot off-page, the command yields - * an error. - * The colon-modifer results in a condition boolean - * being returned instead. - */ -static void -teco_state_start_line(teco_machine_main_t *ctx, GError **error) -{ - teco_int_t v; - - if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error)) - return; - - teco_bool_t rc = teco_move_lines(v); - if (teco_machine_main_eval_colon(ctx) > 0) { - teco_expressions_push(rc); - } else if (teco_is_failure(rc)) { - teco_error_move_set(error, "L"); - return; - } -} - -/*$ B backwards - * [n]B -- Move dot <n> lines backwards - * -B - * [n]:B -> Success|Failure - * - * Move dot to the beginning of the line <n> - * lines before the current one. - * It is equivalent to \(lq-nL\(rq. - */ -static void -teco_state_start_back(teco_machine_main_t *ctx, GError **error) -{ - teco_int_t v; - - if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error)) - return; - - teco_bool_t rc = teco_move_lines(-v); - if (teco_machine_main_eval_colon(ctx) > 0) { - teco_expressions_push(rc); - } else if (teco_is_failure(rc)) { - teco_error_move_set(error, "B"); - return; - } -} - -/* - * NOTE: This implementation has a constant/maximum number of Scintilla - * messages, compared to using SCI_WORDENDPOSITION. - * This pays out only beginning at n > 3, though. - * But most importantly SCI_WORDENDPOSITION(p, FALSE) does not actually skip - * over all non-word characters. - */ -static gboolean -teco_find_words(gsize *pos, teco_int_t n) -{ - if (!n) - return TRUE; - - g_auto(teco_string_t) wchars; - wchars.len = teco_interface_ssm(SCI_GETWORDCHARS, 0, 0); - wchars.data = g_malloc(wchars.len + 1); - teco_interface_ssm(SCI_GETWORDCHARS, 0, (sptr_t)wchars.data); - wchars.data[wchars.len] = '\0'; - - sptr_t gap = teco_interface_ssm(SCI_GETGAPPOSITION, 0, 0); - - if (n > 0) { - /* scan forward */ - gsize len = teco_interface_ssm(SCI_GETLENGTH, 0, 0); - gsize range_len = gap > *pos ? gap - *pos : len - *pos; - if (!range_len) - return FALSE; - const gchar *buffer, *p; - p = buffer = (const gchar *)teco_interface_ssm(SCI_GETRANGEPOINTER, *pos, range_len); - - while (n--) { - gboolean skip_word = TRUE; - - for (;;) { - if (*pos == len) - /* end of document */ - return n == 0; - if (p-buffer >= range_len) { - g_assert(*pos == gap); - range_len = len - gap; - p = buffer = (const gchar *)teco_interface_ssm(SCI_GETRANGEPOINTER, gap, range_len); - } - /* - * FIXME: Is this safe or do we have to look up Unicode code points? - */ - if ((!teco_string_contains(&wchars, *p)) == skip_word) { - if (!skip_word) - break; - skip_word = !skip_word; - continue; - } - (*pos)++; - p++; - } - } - - return TRUE; - } - - /* scan backwards */ - gsize range_len = gap < *pos ? *pos - gap : *pos; - if (!range_len) - return FALSE; - const gchar *buffer, *p; - buffer = (const gchar *)teco_interface_ssm(SCI_GETRANGEPOINTER, *pos - range_len, range_len); - p = buffer+range_len; - - while (n++) { - gboolean skip_word = FALSE; - - for (;;) { - if (*pos == 0) - /* beginning of document */ - return n == 0; - if (p == buffer) { - g_assert(*pos == gap); - range_len = *pos; - buffer = (const gchar *)teco_interface_ssm(SCI_GETRANGEPOINTER, 0, range_len); - p = buffer+range_len; - } - /* - * FIXME: Is this safe or do we have to look up Unicode code points? - */ - if ((!teco_string_contains(&wchars, p[-1])) == skip_word) { - if (skip_word) - break; - skip_word = !skip_word; - continue; - } - (*pos)--; - p--; - } - } - - return TRUE; -} - -/*$ W word - * [n]W -- Move dot by words - * -W - * [n]:W -> Success|Failure - * - * Move dot <n> words forward. - * - If <n> is positive, dot is positioned at the beginning - * of the word <n> words after the current one. - * - If <n> is negative, dot is positioned at the beginning - * of the word, <-n> words before the current one. - * - If <n> is zero, dot is not moved. - * - * \(lqW\(rq uses Scintilla's definition of a word as - * configurable using the - * .B SCI_SETWORDCHARS - * message. - * - * If the requested word would lie beyond the range of the - * buffer, the command yields an error. - * If colon-modified it instead returns a condition code. - */ -static void -teco_state_start_word(teco_machine_main_t *ctx, GError **error) -{ - teco_int_t v; - if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error)) - return; - sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); - - gsize word_pos = pos; - if (!teco_find_words(&word_pos, v)) { - if (!teco_machine_main_eval_colon(ctx)) - teco_error_move_set(error, "W"); - else - teco_expressions_push(TECO_FAILURE); - return; - } - - if (teco_current_doc_must_undo()) - undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0); - teco_interface_ssm(SCI_GOTOPOS, word_pos, 0); - if (teco_machine_main_eval_colon(ctx) > 0) - teco_expressions_push(TECO_SUCCESS); -} - -static gboolean -teco_delete_words(teco_int_t n) -{ - if (!n) - return TRUE; - - sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); - - gsize start_pos = pos, end_pos = pos; - if (!teco_find_words(n > 0 ? &end_pos : &start_pos, n)) - return FALSE; - g_assert(start_pos <= end_pos); - - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); - teco_interface_ssm(SCI_DELETERANGE, start_pos, end_pos-start_pos); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); - - if (teco_current_doc_must_undo()) { - undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0); - undo__teco_interface_ssm(SCI_UNDO, 0, 0); - } - teco_ring_dirtify(); - - return TRUE; -} - -/*$ V - * [n]V -- Delete words forward - * -V - * [n]:V -> Success|Failure - * - * Deletes the next <n> words until the beginning of the - * n'th word after the current one. - * If <n> is negative, deletes up to the beginning of the - * word, <-n> words before the current one. - * \(lq-V\(rq in the middle of a word deletes until the beginning - * of the word. - * If <n> is omitted, 1 or -1 is implied depending on the - * sign prefix. - * - * It uses Scintilla's definition of a word as configurable - * using the - * .B SCI_SETWORDCHARS - * message. - * - * If the words to delete extend beyond the range of the - * buffer, the command yields an error. - * If colon-modified it instead returns a condition code. - */ -static void -teco_state_start_delete_words(teco_machine_main_t *ctx, GError **error) -{ - teco_int_t v; - - if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error)) - return; - - gboolean rc = teco_delete_words(v); - if (teco_machine_main_eval_colon(ctx) > 0) { - teco_expressions_push(teco_bool(rc)); - } else if (!rc) { - teco_error_words_set(error, "V"); - return; - } -} - -/*$ Y - * [n]Y -- Delete word backwards - * -Y - * [n]:Y -> Success|Failure - * - * Delete <n> words backward. - * <n>Y is equivalent to \(lq-nV\(rq. - */ -static void -teco_state_start_delete_words_back(teco_machine_main_t *ctx, GError **error) -{ - teco_int_t v; - - if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error)) - return; - - gboolean rc = teco_delete_words(-v); - if (teco_machine_main_eval_colon(ctx) > 0) { - teco_expressions_push(teco_bool(rc)); - } else if (!rc) { - teco_error_words_set(error, "Y"); - return; - } -} - /*$ "=" print * <n>= -- Show value as message * @@ -960,128 +545,6 @@ teco_state_start_print(teco_machine_main_t *ctx, GError **error) teco_interface_msg(TECO_MSG_USER, "%" TECO_INT_FORMAT, v); } -static gboolean -teco_state_start_kill(teco_machine_main_t *ctx, const gchar *cmd, gboolean by_lines, GError **error) -{ - teco_bool_t rc; - gssize from, len; /* in bytes */ - - if (!teco_expressions_eval(FALSE, error)) - return FALSE; - - if (teco_expressions_args() <= 1) { - from = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); - if (by_lines) { - teco_int_t line; - if (!teco_expressions_pop_num_calc(&line, teco_num_sign, error)) - return FALSE; - line += teco_interface_ssm(SCI_LINEFROMPOSITION, from, 0); - len = teco_interface_ssm(SCI_POSITIONFROMLINE, line, 0) - from; - rc = teco_bool(teco_validate_line(line)); - } else { - teco_int_t len_glyphs; - if (!teco_expressions_pop_num_calc(&len_glyphs, teco_num_sign, error)) - return FALSE; - gssize to = teco_interface_glyphs2bytes_relative(from, len_glyphs); - rc = teco_bool(to >= 0); - len = to-from; - } - if (len < 0) { - len *= -1; - from -= len; - } - } else { - teco_int_t to_glyphs = teco_expressions_pop_num(0); - gssize to = teco_interface_glyphs2bytes(to_glyphs); - teco_int_t from_glyphs = teco_expressions_pop_num(0); - from = teco_interface_glyphs2bytes(from_glyphs); - len = to - from; - rc = teco_bool(len >= 0 && from >= 0 && to >= 0); - } - - if (teco_machine_main_eval_colon(ctx) > 0) { - teco_expressions_push(rc); - } else if (teco_is_failure(rc)) { - teco_error_range_set(error, cmd); - return FALSE; - } - - if (len == 0 || teco_is_failure(rc)) - return TRUE; - - if (teco_current_doc_must_undo()) { - sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); - undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0); - undo__teco_interface_ssm(SCI_UNDO, 0, 0); - } - - /* - * Should always generate an undo action. - */ - teco_interface_ssm(SCI_BEGINUNDOACTION, 0, 0); - teco_interface_ssm(SCI_DELETERANGE, from, len); - teco_interface_ssm(SCI_ENDUNDOACTION, 0, 0); - teco_ring_dirtify(); - - return TRUE; -} - -/*$ K kill - * [n]K -- Kill lines - * -K - * from,to K - * [n]:K -> Success|Failure - * from,to:K -> Success|Failure - * - * Deletes characters up to the beginning of the - * line <n> lines after or before the current one. - * If <n> is 0, \(lqK\(rq will delete up to the beginning - * of the current line. - * If <n> is omitted, the sign prefix will be implied. - * So to delete the entire line regardless of the position - * in it, one can use \(lq0KK\(rq. - * - * If the deletion is beyond the buffer's range, the command - * will yield an error unless it has been colon-modified - * so it returns a condition code. - * - * If two arguments <from> and <to> are available, the - * command is synonymous to <from>,<to>D. - */ -static void -teco_state_start_kill_lines(teco_machine_main_t *ctx, GError **error) -{ - teco_state_start_kill(ctx, "K", TRUE, error); -} - -/*$ D delete - * [n]D -- Delete characters - * -D - * from,to D - * [n]:D -> Success|Failure - * from,to:D -> Success|Failure - * - * If <n> is positive, the next <n> characters (up to and - * character .+<n>) are deleted. - * If <n> is negative, the previous <n> characters are - * deleted. - * If <n> is omitted, the sign prefix will be implied. - * - * If two arguments can be popped from the stack, the - * command will delete the characters with absolute - * position <from> up to <to> from the current buffer. - * - * If the character range to delete is beyond the buffer's - * range, the command will yield an error unless it has - * been colon-modified so it returns a condition code - * instead. - */ -static void -teco_state_start_delete_chars(teco_machine_main_t *ctx, GError **error) -{ - teco_state_start_kill(ctx, "D", FALSE, error); -} - /*$ A * [n]A -> code -- Get character code from buffer * -A -> code @@ -1854,67 +1317,6 @@ teco_state_control_radix(teco_machine_main_t *ctx, GError **error) } } -/*$ ^Q lines2glyphs glyphs2lines - * [n]^Q -> glyphs -- Convert between lines and glyph lengths or positions - * [position]:^Q -> line - * - * Converts between line and glyph arguments. - * It returns the number of glyphs between dot and the <n>-th next - * line (or previous line if <n> is negative). - * Consequently \(lq^QC\(rq is equivalent to \(lqL\(rq, but less efficient. - * - * If colon-modified, an absolute buffer position is converted to the line that - * contains this position, beginning with 1. - * Without arguments, \(lq:^Q\(rq returns the current line. - */ -/* - * FIXME: Perhaps there should be a way to convert an absolute line to an - * absolute position. - */ -static void -teco_state_control_lines2glyphs(teco_machine_main_t *ctx, GError **error) -{ - if (!teco_expressions_eval(FALSE, error)) - return; - - if (teco_machine_main_eval_colon(ctx)) { - gssize pos; - - if (!teco_expressions_args()) { - pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); - } else { - teco_int_t v; - - if (!teco_expressions_pop_num_calc(&v, 0, error)) - return; - - pos = teco_interface_glyphs2bytes(v); - if (pos < 0) { - teco_error_range_set(error, "^Q"); - return; - } - } - - teco_expressions_push(teco_interface_ssm(SCI_LINEFROMPOSITION, pos, 0)+1); - } else { - teco_int_t v; - - if (!teco_expressions_pop_num_calc(&v, teco_num_sign, error)) - return; - - sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0); - sptr_t line = teco_interface_ssm(SCI_LINEFROMPOSITION, pos, 0) + v; - - if (!teco_validate_line(line)) { - teco_error_range_set(error, "^Q"); - return; - } - - sptr_t line_pos = teco_interface_ssm(SCI_POSITIONFROMLINE, line, 0); - teco_expressions_push(teco_interface_bytes2glyphs(line_pos) - teco_interface_bytes2glyphs(pos)); - } -} - /*$ ^E glyphs2bytes bytes2glyphs * glyphs^E -> bytes -- Translate between glyph and byte indexes * bytes:^E -> glyphs |