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__
// 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);
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);
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);
}