]> git.mdlowis.com Git - proto/aos.git/commitdiff
optimize regex searching to search lin bye line gtk-bins
authorMike Lowis <mike.lowis@gentex.com>
Fri, 3 Oct 2025 20:13:07 +0000 (16:13 -0400)
committerMike Lowis <mike.lowis@gentex.com>
Fri, 3 Oct 2025 20:13:07 +0000 (16:13 -0400)
bin/editor.c

index 7fd649b6a629ce47e7f07098e537814fe6af9097..ed1ec067bdc5f9c458709113b469e4fc9f926e5d 100644 (file)
@@ -30,6 +30,8 @@ typedef struct
     EditItem *redo;
     bool edit_in_progress;
     GtkTextTag *highlight_tag;
+    char *line_buffer;      // Reusable buffer for line text
+    size_t buffer_capacity; // Current capacity of line_buffer
 } WindowContext;
 
 #ifdef __APPLE__
@@ -46,6 +48,66 @@ static void CreateWindow(GtkApplication *app, char *path);
 // Edit Helper Routines
 //---------------------------------------------------------
 
+static void ensure_line_buffer_capacity(WindowContext *ctx, size_t needed_size)
+{
+    if (ctx->buffer_capacity < needed_size)
+    {
+        // Grow buffer to at least the needed size, with some extra space
+        ctx->buffer_capacity = needed_size + 1024; // Add 1KB extra
+        ctx->line_buffer = g_realloc(ctx->line_buffer, ctx->buffer_capacity);
+    }
+}
+
+static void free_line_buffer(WindowContext *ctx)
+{
+    if (ctx->line_buffer)
+    {
+        g_free(ctx->line_buffer);
+        ctx->line_buffer = NULL;
+        ctx->buffer_capacity = 0;
+    }
+}
+
+static size_t copy_text_iter_range_to_buffer(WindowContext *ctx,
+                                             const GtkTextIter *start,
+                                             const GtkTextIter *end)
+{
+    // Calculate the length needed (in bytes)
+    gint start_offset = gtk_text_iter_get_offset(start);
+    gint end_offset = gtk_text_iter_get_offset(end);
+    size_t char_count = end_offset - start_offset;
+
+    if (char_count == 0)
+    {
+        if (ctx->line_buffer && ctx->buffer_capacity > 0)
+        {
+            ctx->line_buffer[0] = '\0';
+        }
+        return 0;
+    }
+
+    // Estimate byte count (UTF-8 can be 1-4 bytes per character)
+    size_t estimated_bytes = char_count * 4 + 1; // +1 for null terminator
+    ensure_line_buffer_capacity(ctx, estimated_bytes);
+
+    // Copy text character by character to avoid allocation
+    GtkTextIter iter = *start;
+    size_t pos = 0;
+
+    while (!gtk_text_iter_equal(&iter, end) && pos < ctx->buffer_capacity - 1)
+    {
+        gunichar ch = gtk_text_iter_get_char(&iter);
+        gint bytes = g_unichar_to_utf8(ch, &ctx->line_buffer[pos]);
+        pos += bytes;
+
+        if (!gtk_text_iter_forward_char(&iter))
+            break;
+    }
+
+    ctx->line_buffer[pos] = '\0';
+    return pos;
+}
+
 static void UpdateUndoRedoIcons(WindowContext *ctx)
 {
     gtk_widget_set_sensitive(GTK_WIDGET(ctx->save_icon), TRUE);
@@ -232,13 +294,6 @@ static void OnCmdChanged(GtkApplication *app, gpointer user_data)
         return;
     }
 
-    // Get the entire text buffer content
-    char *buffer_text = gtk_text_buffer_get_text(ctx->buffer, &start, &end, FALSE);
-    if (!buffer_text)
-    {
-        return;
-    }
-
     // Compile the regex pattern
     GError *error = NULL;
     GRegex *regex = g_regex_new(pattern, 0, 0, &error);
@@ -266,54 +321,78 @@ static void OnCmdChanged(GtkApplication *app, gpointer user_data)
             search_start = match_end;
         }
 
-        g_free(buffer_text);
         printf("cmd (literal): '%s'\n", pattern);
         return;
     }
 
-    // Find all regex matches
-    GMatchInfo *match_info = NULL;
+    // Process buffer line by line using reusable buffer
+    gint line_count = gtk_text_buffer_get_line_count(ctx->buffer);
     gboolean first_match = TRUE;
+    gint current_char_offset = 0; // Track absolute character position for proper highlighting
 
-    if (g_regex_match(regex, buffer_text, 0, &match_info))
+    for (gint line = 0; line < line_count; line++)
     {
-        do
-        {
-            gint start_pos, end_pos;
-            if (g_match_info_fetch_pos(match_info, 0, &start_pos, &end_pos))
-            {
-                // Convert byte positions to text iterators
-                GtkTextIter match_start, match_end;
-                gtk_text_buffer_get_iter_at_offset(ctx->buffer, &match_start, start_pos);
-                gtk_text_buffer_get_iter_at_offset(ctx->buffer, &match_end, end_pos);
+        GtkTextIter line_start, line_end;
+        gtk_text_buffer_get_iter_at_line(ctx->buffer, &line_start, line);
+        line_end = line_start;
+        gtk_text_iter_forward_to_line_end(&line_end);
 
-                // Apply highlight tag to this match
-                gtk_text_buffer_apply_tag(ctx->buffer, ctx->highlight_tag, &match_start, &match_end);
+        // Copy line text to reusable buffer without allocation
+        size_t line_length = copy_text_iter_range_to_buffer(ctx, &line_start, &line_end);
 
-                // If this is the first match, scroll to center it in the view
-                if (first_match)
+        if (line_length == 0)
+        {
+            current_char_offset += gtk_text_iter_get_chars_in_line(&line_start);
+            continue;
+        }
+
+        // Find matches in this line
+        GMatchInfo *match_info = NULL;
+        if (g_regex_match(regex, ctx->line_buffer, 0, &match_info))
+        {
+            do
+            {
+                gint start_pos, end_pos;
+                if (g_match_info_fetch_pos(match_info, 0, &start_pos, &end_pos))
                 {
-                    gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ctx->text_view), &match_start, 0.0, TRUE, 0.5, 0.5);
-                    first_match = FALSE;
+                    // Convert line-relative positions to buffer positions
+                    GtkTextIter match_start, match_end;
+                    match_start = line_start;
+                    match_end = line_start;
+
+                    // Move to the match positions (handling UTF-8)
+                    gtk_text_iter_forward_chars(&match_start, g_utf8_pointer_to_offset(ctx->line_buffer, ctx->line_buffer + start_pos));
+                    gtk_text_iter_forward_chars(&match_end, g_utf8_pointer_to_offset(ctx->line_buffer, ctx->line_buffer + end_pos));
+
+                    // Apply highlight tag
+                    gtk_text_buffer_apply_tag(ctx->buffer, ctx->highlight_tag, &match_start, &match_end);
+
+                    // Scroll to first match
+                    if (first_match)
+                    {
+                        gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ctx->text_view), &match_start, 0.0, TRUE, 0.5, 0.5);
+                        first_match = FALSE;
+                    }
                 }
-            }
-        } while (g_match_info_next(match_info, NULL));
-    }
+            } while (g_match_info_next(match_info, NULL));
+        }
 
-    // Clean up
-    if (match_info)
-    {
-        g_match_info_free(match_info);
+        if (match_info)
+        {
+            g_match_info_free(match_info);
+        }
     }
+
+    // Clean up regex
     g_regex_unref(regex);
-    g_free(buffer_text);
 
-    printf("cmd (regex): '%s'\n", pattern);
+    printf("cmd (regex optimized): '%s'\n", pattern);
 }
 
 static void OnQuit(GtkApplication *app, gpointer user_data)
 {
     WindowContext *ctx = (WindowContext *)user_data;
+    free_line_buffer(ctx); // Clean up the reusable line buffer
     gtk_window_close(GTK_WINDOW(ctx->window));
     g_free(ctx);
 }