aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/move-commands.c
diff options
context:
space:
mode:
authorRobin Haberkorn <rhaberkorn@fmsbw.de>2026-07-25 01:55:28 +0200
committerRobin Haberkorn <rhaberkorn@fmsbw.de>2026-07-25 01:55:28 +0200
commit685507922b0b75da5935076395a5b1ec1ef58356 (patch)
treebebc7c421856a6da363d2f9f7010b1ed211fee6c /src/move-commands.c
parent378b9da1476cdcac14332b89f6286a450c4a7757 (diff)
revised and improved the Unicode glyph-to-byte conversion heuristics
Previously almost all glyph-to-byte offset conversions consulted Scintilla's line index and counted characters on the resulting line. For instance a simple expression like `.+1J` would scan the same line twice completely, which would be very slow on pathologically long lines. Even insertions did that due to having to update the ^Y ranges. If you repeat such an operation over all characters as in `<.+1:J;>` you would have complexity O(n^2) for n = line length. Only commands with an explicit relative nature like `C` and `A` would use teco_view_glyph2bytes_relative() which scans beginning at dot as long as the relative movement is less than 1024 glyphs. Wit the new heuristics almost all glyph-to-byte and byte-to-glyph conversions can make use of that optimization. This requires that dot must at all times be known in glyphs as well - the byte position is managed by Scintilla (SCI_GETCURRENTPOS). We therefore introduced teco_current_doc_set_dot() and teco_current_doc_get_dot() to update dot in the current buffer or Q-Register -- it cannot be stored along with the view since Q-Registers share a single view. A number of auxiliary functions have been introduced for converting relative to a known (glyphs,bytes) offset pair and for converting absolute and relative positions with regard to the current doc and SCI_GETCURRENTPOS position. Of course this is error-prone since the glyph and dot positions are interdependant - they must always be kept in sync. With these new optimizations even pathologically long lines can (usually) be managed even in UTF-8 documents. It does not address slow-downs in Scintilla's line layout, yet. grosciteco.tes for instance runs twice as fast now.
Diffstat (limited to 'src/move-commands.c')
-rw-r--r--src/move-commands.c72
1 files changed, 57 insertions, 15 deletions
diff --git a/src/move-commands.c b/src/move-commands.c
index 45afc4e..9ef893e 100644
--- a/src/move-commands.c
+++ b/src/move-commands.c
@@ -58,12 +58,13 @@ teco_state_start_jump(teco_machine_main_t *ctx, GError **error)
if (!teco_expressions_pop_num_calc(&v, 0, error))
return;
- gssize pos = teco_interface_glyphs2bytes(v);
- if (pos >= 0) {
+ gssize next_pos = teco_interface_glyphs2bytes_absdot(v);
+ if (next_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);
+ teco_interface_ssm(SCI_GOTOPOS, next_pos, 0);
+ teco_current_doc_set_dot(v);
if (teco_machine_main_eval_colon(ctx) > 0)
teco_expressions_push(TECO_SUCCESS);
@@ -78,14 +79,15 @@ teco_state_start_jump(teco_machine_main_t *ctx, GError **error)
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);
+ gssize next_pos = teco_interface_glyphs2bytes_reldot(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);
+ undo__teco_interface_ssm(SCI_GOTOPOS, teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0), 0);
+
+ teco_interface_ssm(SCI_GOTOPOS, next_pos, 0);
+ teco_current_doc_set_dot(teco_current_doc_get_dot() + n);
return TECO_SUCCESS;
}
@@ -151,7 +153,15 @@ teco_move_lines(teco_int_t n)
if (!teco_validate_line(line))
return TECO_FAILURE;
- teco_interface_ssm(SCI_GOTOLINE, line, 0);
+ sptr_t next_pos = teco_interface_ssm(SCI_POSITIONFROMLINE, line, 0);
+ /*
+ * FIXME: Perhaps it would be more efficient to fall back to the
+ * line index cache via teco_view_bytes2glyphs_unicode() since next_pos
+ * is guaranteed to fall on line starts.
+ */
+ teco_current_doc_set_dot(teco_interface_bytes2glyphs_absdot(next_pos));
+ teco_interface_ssm(SCI_GOTOPOS, next_pos, 0);
+
if (teco_current_doc_must_undo())
undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0);
@@ -388,6 +398,8 @@ teco_state_start_words(teco_machine_main_t *ctx, const gchar *cmd, gint factor,
if (rc) {
if (teco_current_doc_must_undo())
undo__teco_interface_ssm(SCI_GOTOPOS, pos, 0);
+
+ teco_current_doc_set_dot(teco_interface_bytes2glyphs_absdot(word_pos));
teco_interface_ssm(SCI_GOTOPOS, word_pos, 0);
}
@@ -457,6 +469,9 @@ teco_state_start_delete_words(teco_machine_main_t *ctx, const gchar *cmd, gint f
if (rc && start_pos != end_pos) {
g_assert(start_pos < end_pos);
+ if (start_pos < pos)
+ teco_current_doc_set_dot(teco_interface_bytes2glyphs_absdot(start_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);
@@ -487,8 +502,10 @@ teco_state_start_kill(teco_machine_main_t *ctx, const gchar *cmd, gboolean by_li
if (!teco_expressions_eval(FALSE, error))
return FALSE;
+ sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
+
if (teco_expressions_args() <= 1) {
- from = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
+ from = pos;
if (by_lines) {
teco_int_t line;
if (!teco_expressions_pop_num_calc(&line, teco_num_sign, error))
@@ -496,13 +513,24 @@ teco_state_start_kill(teco_machine_main_t *ctx, const gchar *cmd, gboolean by_li
line += teco_interface_ssm(SCI_LINEFROMPOSITION, from, 0);
len = teco_interface_ssm(SCI_POSITIONFROMLINE, line, 0) - from;
rc = teco_bool(teco_validate_line(line));
+
+ /*
+ * FIXME: Perhaps it would be more efficient to fall back to the
+ * line index cache via teco_view_bytes2glyphs_unicode() since from+len
+ * is guaranteed to fall on line starts.
+ */
+ if (teco_is_success(rc) && len < 0)
+ teco_current_doc_set_dot(teco_interface_bytes2glyphs_reldot(len));
} 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);
+ gssize to = teco_interface_glyphs2bytes_reldot(len_glyphs);
rc = teco_bool(to >= 0);
len = to-from;
+
+ if (teco_is_success(rc) && len_glyphs < 0)
+ teco_current_doc_set_dot(teco_current_doc_get_dot() + len_glyphs);
}
if (len < 0) {
len *= -1;
@@ -510,11 +538,18 @@ teco_state_start_kill(teco_machine_main_t *ctx, const gchar *cmd, gboolean by_li
}
} else {
teco_int_t to_glyphs = teco_expressions_pop_num(0);
- gssize to = teco_interface_glyphs2bytes(to_glyphs);
+ gssize to = teco_interface_glyphs2bytes_absdot(to_glyphs);
teco_int_t from_glyphs = teco_expressions_pop_num(0);
- from = teco_interface_glyphs2bytes(from_glyphs);
+ from = teco_interface_glyphs2bytes_absdot(from_glyphs);
len = to - from;
rc = teco_bool(len >= 0 && from >= 0 && to >= 0);
+
+ if (teco_is_success(rc)) {
+ if (to < pos)
+ teco_current_doc_set_dot(teco_current_doc_get_dot() - (to_glyphs-from_glyphs));
+ else if (from < pos)
+ teco_current_doc_set_dot(from_glyphs);
+ }
}
if (teco_machine_main_eval_colon(ctx) > 0) {
@@ -528,7 +563,6 @@ teco_state_start_kill(teco_machine_main_t *ctx, const gchar *cmd, gboolean by_li
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);
}
@@ -629,7 +663,11 @@ teco_state_control_lines2glyphs(teco_machine_main_t *ctx, GError **error)
if (!teco_expressions_args()) {
pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
} else {
- pos = teco_interface_glyphs2bytes(teco_expressions_pop_num(0));
+ /*
+ * FIXME: Could be optimized by directly calling
+ * SCI_LINEFROMINDEXPOSITION.
+ */
+ pos = teco_interface_glyphs2bytes_absdot(teco_expressions_pop_num(0));
if (pos < 0) {
teco_error_range_set(error, "^Q");
return;
@@ -652,6 +690,10 @@ teco_state_control_lines2glyphs(teco_machine_main_t *ctx, GError **error)
}
sptr_t line_pos = teco_interface_ssm(SCI_POSITIONFROMLINE, line, 0);
- teco_expressions_push(teco_interface_bytes2glyphs(line_pos) - teco_interface_bytes2glyphs(pos));
+ /*
+ * FIXME: Could be optimized with SCI_INDEXPOSITIONFROMLINE.
+ */
+ teco_expressions_push(teco_interface_bytes2glyphs_absdot(line_pos) -
+ teco_current_doc_get_dot());
}
}