]> git.mdlowis.com Git - proto/aos.git/commitdiff
added regex searching
authorMike Lowis <mike.lowis@gentex.com>
Fri, 3 Oct 2025 19:15:00 +0000 (15:15 -0400)
committerMike Lowis <mike.lowis@gentex.com>
Fri, 3 Oct 2025 19:15:00 +0000 (15:15 -0400)
bin/editor.c

index 2097a21c447a4285aa21d0c54663d5b5ca48e3e1..7fd649b6a629ce47e7f07098e537814fe6af9097 100644 (file)
@@ -5,96 +5,98 @@
 //   * implement command language
 //   * implement editing shortcuts
 
-typedef struct EditItem {
-    struct EditItem* next; /* pointer to next operation in the stack */
+typedef struct EditItem
+{
+    struct EditItem *next; /* pointer to next operation in the stack */
     gint beg;              /* beginning of affected region */
     gint end;              /* end of affected region*/
-    gchar* data;           /* pointer to deleted character data */
+    gchar *data;           /* pointer to deleted character data */
     gint transid;          /* id of transaction this item is a part of */
 } EditItem;
 
-typedef struct {
-    char* path;
-    GtkApplication* app;
-    GtkWidget* window;
-    GtkWidget* text_view;
-    GtkTextBuffer* buffer;
-    GtkEntry* cmd_entry;
-    GtkToolItem* save_icon;
-    GtkToolItem* save_as_icon;
-    GtkToolItem* undo_icon;
-    GtkToolItem* redo_icon;
-    EditItem* undo;
-    EditItem* redo;
+typedef struct
+{
+    char *path;
+    GtkApplication *app;
+    GtkWidget *window;
+    GtkWidget *text_view;
+    GtkTextBuffer *buffer;
+    GtkEntry *cmd_entry;
+    GtkToolItem *save_icon;
+    GtkToolItem *save_as_icon;
+    GtkToolItem *undo_icon;
+    GtkToolItem *redo_icon;
+    EditItem *undo;
+    EditItem *redo;
     bool edit_in_progress;
+    GtkTextTag *highlight_tag;
 } WindowContext;
 
 #ifdef __APPLE__
-    #define MOD_MASK     GDK_META_MASK
-    #define DEFAULT_FONT "18pt Monaco"
+#define MOD_MASK GDK_META_MASK
+#define DEFAULT_FONT "18pt Monaco"
 #else
-    #define MOD_MASK      GDK_CONTROL_MASK
-    #define DEFAULT_FONT "14pt Liberation Mono"
+#define MOD_MASK GDK_CONTROL_MASK
+#define DEFAULT_FONT "14pt Liberation Mono"
 #endif
 
-static void CreateWindow(GtkApplication *app, char* path);
+static void CreateWindow(GtkApplication *app, char *path);
 
 //---------------------------------------------------------
 // Edit Helper Routines
 //---------------------------------------------------------
 
-static void UpdateUndoRedoIcons(WindowContext* ctx)
+static void UpdateUndoRedoIcons(WindowContext *ctx)
 {
     gtk_widget_set_sensitive(GTK_WIDGET(ctx->save_icon), TRUE);
     gtk_widget_set_sensitive(GTK_WIDGET(ctx->undo_icon), (ctx->undo != NULL));
     gtk_widget_set_sensitive(GTK_WIDGET(ctx->redo_icon), (ctx->redo != NULL));
 }
 
-static void ReverseEdit(WindowContext* ctx, EditItem** from, EditItem** to)
+static void ReverseEdit(WindowContext *ctx, EditItem **from, EditItem **to)
 {
-    EditItem* item = *from;
+    EditItem *item = *from;
     if (item)
     {
         *from = item->next;
-       GtkTextIter start, stop;
-       gtk_text_buffer_get_start_iter(ctx->buffer, &start);
-       gtk_text_buffer_get_start_iter(ctx->buffer, &stop);
-       gtk_text_iter_set_offset(&start, item->beg);
-       gtk_text_iter_set_offset(&stop,  item->end);
-       ctx->edit_in_progress = true;
-       if (item->data) // If action was a delete
-       {
-           gtk_text_buffer_insert(ctx->buffer, &start, item->data, item->end - item->beg);
-           g_free(item->data);
-           item->data = NULL;
-       }
-       else // it was an insert
-       {
-           item->data = (char*)gtk_text_buffer_get_text(ctx->buffer, &start, &stop, FALSE);
-           gtk_text_buffer_delete(ctx->buffer, &start, &stop);
-       }
-       item->next = *to;
-       *to = item;
-       ctx->edit_in_progress = false;
+        GtkTextIter start, stop;
+        gtk_text_buffer_get_start_iter(ctx->buffer, &start);
+        gtk_text_buffer_get_start_iter(ctx->buffer, &stop);
+        gtk_text_iter_set_offset(&start, item->beg);
+        gtk_text_iter_set_offset(&stop, item->end);
+        ctx->edit_in_progress = true;
+        if (item->data) // If action was a delete
+        {
+            gtk_text_buffer_insert(ctx->buffer, &start, item->data, item->end - item->beg);
+            g_free(item->data);
+            item->data = NULL;
+        }
+        else // it was an insert
+        {
+            item->data = (char *)gtk_text_buffer_get_text(ctx->buffer, &start, &stop, FALSE);
+            gtk_text_buffer_delete(ctx->buffer, &start, &stop);
+        }
+        item->next = *to;
+        *to = item;
+        ctx->edit_in_progress = false;
     }
     UpdateUndoRedoIcons(ctx);
 }
 
-static void ClearEdits(EditItem** edits)
+static void ClearEdits(EditItem **edits)
 {
-    EditItem* curr = *edits;
+    EditItem *curr = *edits;
     *edits = NULL;
     while (curr)
     {
         if (curr->data)
-       {
-           g_free(curr->data);
-       }
-       EditItem* dead = curr;
-       curr = dead->next;
-       g_free(dead);
+        {
+            g_free(curr->data);
+        }
+        EditItem *dead = curr;
+        curr = dead->next;
+        g_free(dead);
     }
-
 }
 
 //---------------------------------------------------------
@@ -105,7 +107,7 @@ static void OnOpenApp(GtkApplication *app, GFile **files, gint n_files, gpointer
 {
     for (int i = 0; i < n_files; i++)
     {
-       char* path = g_file_get_path(files[i]);
+        char *path = g_file_get_path(files[i]);
         CreateWindow(app, path);
     }
 }
@@ -117,33 +119,33 @@ static void OnActivateApp(GtkApplication *app, gpointer user_data)
 
 static void OnNew(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     CreateWindow(ctx->app, NULL);
 }
 
 static void OnOpen(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
-    GtkWidget* dialog = gtk_file_chooser_dialog_new (
-      "Open File", GTK_WINDOW(ctx->window), GTK_FILE_CHOOSER_ACTION_OPEN,
-      "_Cancel", GTK_RESPONSE_CANCEL,
-      "_Open", GTK_RESPONSE_ACCEPT,
-      NULL);
-    gint res = gtk_dialog_run (GTK_DIALOG (dialog));
+    WindowContext *ctx = (WindowContext *)user_data;
+    GtkWidget *dialog = gtk_file_chooser_dialog_new(
+        "Open File", GTK_WINDOW(ctx->window), GTK_FILE_CHOOSER_ACTION_OPEN,
+        "_Cancel", GTK_RESPONSE_CANCEL,
+        "_Open", GTK_RESPONSE_ACCEPT,
+        NULL);
+    gint res = gtk_dialog_run(GTK_DIALOG(dialog));
     if (res == GTK_RESPONSE_ACCEPT)
     {
-        GtkFileChooser* chooser = GTK_FILE_CHOOSER (dialog);
-        char* path = gtk_file_chooser_get_filename (chooser);
+        GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
+        char *path = gtk_file_chooser_get_filename(chooser);
         CreateWindow(ctx->app, path);
     }
-    gtk_widget_destroy (dialog);
+    gtk_widget_destroy(dialog);
 }
 
 static void OnSaveAs(GtkApplication *app, gpointer user_data);
 
 static void OnSave(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
 
     if (ctx->path == NULL)
     {
@@ -158,7 +160,7 @@ static void OnSave(GtkApplication *app, gpointer user_data)
         char *text = gtk_text_buffer_get_text(ctx->buffer, &start, &end, FALSE);
 
         // Save the text to a file
-        FILE* file = fopen(ctx->path, "w");
+        FILE *file = fopen(ctx->path, "w");
         if (file)
         {
             fputs(text, file);
@@ -173,32 +175,32 @@ static void OnSave(GtkApplication *app, gpointer user_data)
 
 static void OnSaveAs(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
-    GtkWidget* dialog = gtk_file_chooser_dialog_new (
-      "Save File As", GTK_WINDOW(ctx->window), GTK_FILE_CHOOSER_ACTION_SAVE,
-      "_Cancel", GTK_RESPONSE_CANCEL,
-      "_Save As", GTK_RESPONSE_ACCEPT,
-      NULL);
-    gint res = gtk_dialog_run (GTK_DIALOG (dialog));
+    WindowContext *ctx = (WindowContext *)user_data;
+    GtkWidget *dialog = gtk_file_chooser_dialog_new(
+        "Save File As", GTK_WINDOW(ctx->window), GTK_FILE_CHOOSER_ACTION_SAVE,
+        "_Cancel", GTK_RESPONSE_CANCEL,
+        "_Save As", GTK_RESPONSE_ACCEPT,
+        NULL);
+    gint res = gtk_dialog_run(GTK_DIALOG(dialog));
     if (res == GTK_RESPONSE_ACCEPT)
     {
-        GtkFileChooser* chooser = GTK_FILE_CHOOSER (dialog);
-        ctx->path = gtk_file_chooser_get_filename (chooser);
-       gtk_window_set_title(GTK_WINDOW(ctx->window), ctx->path);
-       OnSave(app, user_data);
+        GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
+        ctx->path = gtk_file_chooser_get_filename(chooser);
+        gtk_window_set_title(GTK_WINDOW(ctx->window), ctx->path);
+        OnSave(app, user_data);
     }
-    gtk_widget_destroy (dialog);
+    gtk_widget_destroy(dialog);
 }
 
 static void OnUndo(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     ReverseEdit(ctx, &ctx->undo, &ctx->redo);
 }
 
 static void OnRedo(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     ReverseEdit(ctx, &ctx->redo, &ctx->undo);
 }
 
@@ -214,15 +216,104 @@ static void OnHelp(GtkApplication *app, gpointer user_data)
 
 static void OnCmdChanged(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
-    GtkEntryBuffer* buffer = gtk_entry_get_buffer(ctx->cmd_entry);
-    char *text = (char*)gtk_entry_buffer_get_text(buffer);
-    printf("cmd: '%s'\n", text);
+    WindowContext *ctx = (WindowContext *)user_data;
+    GtkEntryBuffer *buffer = gtk_entry_get_buffer(ctx->cmd_entry);
+    char *pattern = (char *)gtk_entry_buffer_get_text(buffer);
+
+    // Remove all previous highlights
+    GtkTextIter start, end;
+    gtk_text_buffer_get_start_iter(ctx->buffer, &start);
+    gtk_text_buffer_get_end_iter(ctx->buffer, &end);
+    gtk_text_buffer_remove_tag(ctx->buffer, ctx->highlight_tag, &start, &end);
+
+    // If search pattern is empty, nothing more to do
+    if (!pattern || strlen(pattern) == 0)
+    {
+        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);
+
+    if (!regex)
+    {
+        // If regex compilation fails, fall back to literal text search
+        g_clear_error(&error);
+        GtkTextIter search_start, match_start, match_end;
+        gboolean first_match = TRUE;
+        gtk_text_buffer_get_start_iter(ctx->buffer, &search_start);
+
+        while (gtk_text_iter_forward_search(&search_start, pattern,
+                                            GTK_TEXT_SEARCH_TEXT_ONLY,
+                                            &match_start, &match_end, NULL))
+        {
+            gtk_text_buffer_apply_tag(ctx->buffer, ctx->highlight_tag, &match_start, &match_end);
+
+            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;
+            }
+
+            search_start = match_end;
+        }
+
+        g_free(buffer_text);
+        printf("cmd (literal): '%s'\n", pattern);
+        return;
+    }
+
+    // Find all regex matches
+    GMatchInfo *match_info = NULL;
+    gboolean first_match = TRUE;
+
+    if (g_regex_match(regex, buffer_text, 0, &match_info))
+    {
+        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);
+
+                // Apply highlight tag to this match
+                gtk_text_buffer_apply_tag(ctx->buffer, ctx->highlight_tag, &match_start, &match_end);
+
+                // If this is the first match, scroll to center it in the view
+                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));
+    }
+
+    // Clean up
+    if (match_info)
+    {
+        g_match_info_free(match_info);
+    }
+    g_regex_unref(regex);
+    g_free(buffer_text);
+
+    printf("cmd (regex): '%s'\n", pattern);
 }
 
 static void OnQuit(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     gtk_window_close(GTK_WINDOW(ctx->window));
     g_free(ctx);
 }
@@ -234,9 +325,9 @@ static void OnJoin(GtkApplication *app, gpointer user_data)
 
 static void OnSelectLine(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     GtkTextIter start, end;
-    gtk_text_buffer_get_selection_bounds (ctx->buffer, &start, &end);
+    gtk_text_buffer_get_selection_bounds(ctx->buffer, &start, &end);
     gtk_text_iter_set_line_offset(&start, 0);
     gtk_text_iter_forward_to_line_end(&end);
     gtk_text_buffer_move_mark_by_name(ctx->buffer, "insert", &start);
@@ -265,7 +356,7 @@ static void OnHighlightPrev(GtkApplication *app, gpointer user_data)
 
 static void OnNewline(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     if (gtk_widget_has_focus(GTK_WIDGET(ctx->cmd_entry)))
     {
         OnExec(app, user_data);
@@ -274,12 +365,13 @@ static void OnNewline(GtkApplication *app, gpointer user_data)
     {
         GtkTextIter start, end;
         gtk_text_buffer_get_selection_bounds(ctx->buffer, &start, NULL);
-       end = start;
+        end = start;
         gtk_text_iter_set_line_offset(&start, 0);
         gtk_text_iter_forward_to_line_end(&end);
-        char* text = gtk_text_buffer_get_text(ctx->buffer, &start, &end, FALSE);
-        char* indent_end = text;
-        for (; *indent_end == ' ' || *indent_end == '\t'; indent_end++);
+        char *text = gtk_text_buffer_get_text(ctx->buffer, &start, &end, FALSE);
+        char *indent_end = text;
+        for (; *indent_end == ' ' || *indent_end == '\t'; indent_end++)
+            ;
         *indent_end = '\0';
         gtk_text_buffer_delete_selection(ctx->buffer, FALSE, FALSE);
         gtk_text_buffer_insert_at_cursor(ctx->buffer, "\n", 1);
@@ -290,16 +382,16 @@ static void OnNewline(GtkApplication *app, gpointer user_data)
 
 static void OnTab(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     if (!gtk_widget_has_focus(GTK_WIDGET(ctx->cmd_entry)))
     {
-       gtk_text_buffer_insert_at_cursor(ctx->buffer, "    ", 4);
+        gtk_text_buffer_insert_at_cursor(ctx->buffer, "    ", 4);
     }
 }
 
 static void OnToggleCommand(GtkApplication *app, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     if (gtk_widget_has_focus(GTK_WIDGET(ctx->cmd_entry)))
     {
         gtk_widget_grab_focus(GTK_WIDGET(ctx->text_view));
@@ -310,32 +402,32 @@ static void OnToggleCommand(GtkApplication *app, gpointer user_data)
     }
 }
 
-struct {
+struct
+{
     int mods;
     int key;
-    void (*action)(GtkApplication*, gpointer);
+    void (*action)(GtkApplication *, gpointer);
 } Keyboard_Shortcuts[] = {
-    { MOD_MASK, GDK_KEY_o,            OnOpen          },
-    { MOD_MASK, GDK_KEY_s,            OnSave          },
-    { MOD_MASK, GDK_KEY_q,            OnQuit          },
-    { MOD_MASK, GDK_KEY_z,            OnUndo          },
-    { MOD_MASK, GDK_KEY_y,            OnRedo          },
-    { MOD_MASK, GDK_KEY_j,            OnJoin          },
-    { MOD_MASK, GDK_KEY_l,            OnSelectLine    },
-    { MOD_MASK, GDK_KEY_bracketleft,  OnUnindent      },
-    { MOD_MASK, GDK_KEY_bracketright, OnIndent        },
-    { MOD_MASK, GDK_KEY_slash,        OnToggleComment },
-    { MOD_MASK, GDK_KEY_t,            OnToggleCommand },
-    { 0,        GDK_KEY_Escape,       OnHighlightPrev },
-    { 0,        GDK_KEY_Return,       OnNewline       },
-    { 0,        GDK_KEY_Tab,          OnTab           },
-    { 0, 0, NULL }
-};
+    {MOD_MASK, GDK_KEY_o, OnOpen},
+    {MOD_MASK, GDK_KEY_s, OnSave},
+    {MOD_MASK, GDK_KEY_q, OnQuit},
+    {MOD_MASK, GDK_KEY_z, OnUndo},
+    {MOD_MASK, GDK_KEY_y, OnRedo},
+    {MOD_MASK, GDK_KEY_j, OnJoin},
+    {MOD_MASK, GDK_KEY_l, OnSelectLine},
+    {MOD_MASK, GDK_KEY_bracketleft, OnUnindent},
+    {MOD_MASK, GDK_KEY_bracketright, OnIndent},
+    {MOD_MASK, GDK_KEY_slash, OnToggleComment},
+    {MOD_MASK, GDK_KEY_t, OnToggleCommand},
+    {0, GDK_KEY_Escape, OnHighlightPrev},
+    {0, GDK_KEY_Return, OnNewline},
+    {0, GDK_KEY_Tab, OnTab},
+    {0, 0, NULL}};
 
 static gboolean OnKeyPress(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
-    for(int i = 0; Keyboard_Shortcuts[i].action; i++)
+    WindowContext *ctx = (WindowContext *)user_data;
+    for (int i = 0; Keyboard_Shortcuts[i].action; i++)
     {
         if ((event->keyval == Keyboard_Shortcuts[i].key) &&
             ((event->state & Keyboard_Shortcuts[i].mods) == Keyboard_Shortcuts[i].mods))
@@ -347,65 +439,65 @@ static gboolean OnKeyPress(GtkWidget *widget, GdkEventKey *event, gpointer user_
     return FALSE;
 }
 
-void OnInsert(GtkTextBuffer* self, const GtkTextIter* location, gchar* text, gint len, gpointer user_data)
+void OnInsert(GtkTextBuffer *self, const GtkTextIter *location, gchar *text, gint len, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     if (!ctx->edit_in_progress)
     {
         gtk_widget_set_sensitive(GTK_WIDGET(ctx->save_icon), TRUE);
-       gint offset = gtk_text_iter_get_offset(location);
-       if (ctx->undo && !ctx->undo->data && ctx->undo->end == offset)
-       {
+        gint offset = gtk_text_iter_get_offset(location);
+        if (ctx->undo && !ctx->undo->data && ctx->undo->end == offset)
+        {
             ctx->undo->end = offset + len;
-       }
-       else
-       {
-            EditItem* item = g_malloc0(sizeof(EditItem));
-            item->beg  = gtk_text_iter_get_offset(location);
-            item->end  = item->beg + strlen((char*)text);
+        }
+        else
+        {
+            EditItem *item = g_malloc0(sizeof(EditItem));
+            item->beg = gtk_text_iter_get_offset(location);
+            item->end = item->beg + strlen((char *)text);
             item->next = ctx->undo;
             ctx->undo = item;
-       }
-       ClearEdits(&ctx->redo);
+        }
+        ClearEdits(&ctx->redo);
     }
 }
 
-void OnDelete(GtkTextBuffer* self, const GtkTextIter* start, const GtkTextIter* end, gpointer user_data)
+void OnDelete(GtkTextBuffer *self, const GtkTextIter *start, const GtkTextIter *end, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     if (!ctx->edit_in_progress)
     {
         gtk_widget_set_sensitive(GTK_WIDGET(ctx->save_icon), TRUE);
         gint first = gtk_text_iter_get_offset(start);
-       gint last = gtk_text_iter_get_offset(end);
-
-       if (ctx->undo && ctx->undo->data && ctx->undo->beg == last)
-       {
-           gint size = (last - first) + (ctx->undo->end - ctx->undo->beg);
-           char* new_data = g_malloc(size + 1);
-           char* del_data = gtk_text_buffer_get_text(ctx->buffer, start, end, FALSE);
-           strncpy(new_data, del_data, (last-first));
-           strncpy(&new_data[last-first], ctx->undo->data, (ctx->undo->end - ctx->undo->beg));
-           new_data[size] = '\0';
-           ctx->undo->data = new_data;
-           ctx->undo->beg = first;
-       }
-       else
-       {
-            EditItem* item = g_malloc0(sizeof(EditItem));
+        gint last = gtk_text_iter_get_offset(end);
+
+        if (ctx->undo && ctx->undo->data && ctx->undo->beg == last)
+        {
+            gint size = (last - first) + (ctx->undo->end - ctx->undo->beg);
+            char *new_data = g_malloc(size + 1);
+            char *del_data = gtk_text_buffer_get_text(ctx->buffer, start, end, FALSE);
+            strncpy(new_data, del_data, (last - first));
+            strncpy(&new_data[last - first], ctx->undo->data, (ctx->undo->end - ctx->undo->beg));
+            new_data[size] = '\0';
+            ctx->undo->data = new_data;
+            ctx->undo->beg = first;
+        }
+        else
+        {
+            EditItem *item = g_malloc0(sizeof(EditItem));
             item->beg = gtk_text_iter_get_offset(start);
             item->end = gtk_text_iter_get_offset(end);
             item->data = gtk_text_buffer_get_text(ctx->buffer, start, end, FALSE);
             item->next = ctx->undo;
             ctx->undo = item;
-       }
-       ClearEdits(&ctx->redo);
+        }
+        ClearEdits(&ctx->redo);
     }
 }
 
-void OnStopEdit(GtkTextBuffer* self, gpointer user_data)
+void OnStopEdit(GtkTextBuffer *self, gpointer user_data)
 {
-    WindowContext* ctx = (WindowContext*)user_data;
+    WindowContext *ctx = (WindowContext *)user_data;
     UpdateUndoRedoIcons(ctx);
 }
 
@@ -413,39 +505,39 @@ void OnStopEdit(GtkTextBuffer* self, gpointer user_data)
 // UI Creation Routines
 //---------------------------------------------------------
 
-GtkToolItem* toolbar_add_icon(GtkWidget* toolbar, char* icon_name, char* label)
+GtkToolItem *toolbar_add_icon(GtkWidget *toolbar, char *icon_name, char *label)
 {
-    GtkWidget* icon = gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR);
-    GtkToolItem* button = gtk_tool_button_new (icon, label);
+    GtkWidget *icon = gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR);
+    GtkToolItem *button = gtk_tool_button_new(icon, label);
     gtk_tool_item_set_tooltip_text(button, label);
     gtk_toolbar_insert(GTK_TOOLBAR(toolbar), button, -1);
     return button;
 }
 
-void toolbar_add_separator(GtkWidget* toolbar)
+void toolbar_add_separator(GtkWidget *toolbar)
 {
-    GtkToolItem* sep = gtk_separator_tool_item_new();
+    GtkToolItem *sep = gtk_separator_tool_item_new();
     gtk_toolbar_insert(GTK_TOOLBAR(toolbar), sep, -1);
 }
 
-GtkEntry* toolbar_add_text_entry(GtkWidget* toolbar)
+GtkEntry *toolbar_add_text_entry(GtkWidget *toolbar)
 {
-    GtkToolItem* item = gtk_tool_item_new();
-    GtkWidget* entry = gtk_entry_new();
+    GtkToolItem *item = gtk_tool_item_new();
+    GtkWidget *entry = gtk_entry_new();
     gtk_tool_item_set_expand(item, TRUE);
     gtk_container_add(GTK_CONTAINER(item), GTK_WIDGET(entry));
     gtk_toolbar_insert(GTK_TOOLBAR(toolbar), GTK_TOOL_ITEM(item), -1);
     return GTK_ENTRY(entry);
 }
 
-static void CreateWindow(GtkApplication *app, char* path)
+static void CreateWindow(GtkApplication *app, char *path)
 {
     GtkWidget *vbox;
     GtkWidget *scrolled_window;
     GtkWidget *toolbar;
 
     // Create the main window
-    WindowContext* ctx = g_malloc0(sizeof(WindowContext));
+    WindowContext *ctx = g_malloc0(sizeof(WindowContext));
     ctx->app = app;
     ctx->path = path;
     ctx->window = gtk_application_window_new(app);
@@ -461,8 +553,8 @@ static void CreateWindow(GtkApplication *app, char* path)
     toolbar = gtk_toolbar_new();
     gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), FALSE);
     gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS);
-    GtkToolItem* new_icon  = toolbar_add_icon(toolbar, "document-new", "New File");
-    GtkToolItem* open_icon = toolbar_add_icon(toolbar, "document-open", "Open File");
+    GtkToolItem *new_icon = toolbar_add_icon(toolbar, "document-new", "New File");
+    GtkToolItem *open_icon = toolbar_add_icon(toolbar, "document-open", "Open File");
     ctx->save_icon = toolbar_add_icon(toolbar, "document-save", "Save File");
     ctx->save_as_icon = toolbar_add_icon(toolbar, "document-save-as", "Save File As...");
     gtk_widget_set_sensitive(GTK_WIDGET(ctx->save_icon), (path == NULL ? TRUE : FALSE));
@@ -471,8 +563,8 @@ static void CreateWindow(GtkApplication *app, char* path)
     ctx->redo_icon = toolbar_add_icon(toolbar, "edit-redo", "Redo");
     toolbar_add_separator(toolbar);
     ctx->cmd_entry = toolbar_add_text_entry(toolbar);
-    GtkToolItem* exec_icon = toolbar_add_icon(toolbar, "media-playback-start", "Execute");
-    GtkToolItem* help_icon = toolbar_add_icon(toolbar, "dialog-question", "Help");
+    GtkToolItem *exec_icon = toolbar_add_icon(toolbar, "media-playback-start", "Execute");
+    GtkToolItem *help_icon = toolbar_add_icon(toolbar, "dialog-question", "Help");
 
     // Set default state of the icons
     gtk_widget_set_sensitive(GTK_WIDGET(ctx->save_icon), FALSE);
@@ -482,16 +574,23 @@ static void CreateWindow(GtkApplication *app, char* path)
     // Create the text view and scrolled window
     ctx->text_view = gtk_text_view_new();
     ctx->buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ctx->text_view));
+
+    // Create highlight tag for search results
+    ctx->highlight_tag = gtk_text_buffer_create_tag(ctx->buffer, "highlight",
+                                                    "background", "yellow",
+                                                    "foreground", "black",
+                                                    NULL);
+
     if (path)
     {
-        FILE* file = fopen(path, "r");
+        FILE *file = fopen(path, "r");
         if (file)
-       {
+        {
             // Allocate memory for the file
             fseek(file, 0, SEEK_END);
             size_t file_size = ftell(file);
             fseek(file, 0, SEEK_SET);
-            char* file_contents = (char *)g_malloc(file_size + 1);
+            char *file_contents = (char *)g_malloc(file_size + 1);
 
             // Read file contents
             fread(file_contents, 1, file_size, file);
@@ -501,21 +600,21 @@ static void CreateWindow(GtkApplication *app, char* path)
             // Set the text buffer content
             gtk_text_buffer_set_text(ctx->buffer, file_contents, -1);
             g_free(file_contents);
-       }
+        }
     }
 
-    GtkCssProvider *provider = gtk_css_provider_new ();
-    gtk_css_provider_load_from_data (
+    GtkCssProvider *provider = gtk_css_provider_new();
+    gtk_css_provider_load_from_data(
         provider, "textview { font: " DEFAULT_FONT "; }", -1, NULL);
-    GtkStyleContext *context = gtk_widget_get_style_context (ctx->text_view);
-    gtk_style_context_add_provider (
-        context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
-    gtk_text_view_set_top_margin(GTK_TEXT_VIEW (ctx->text_view), 10);
-    gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW (ctx->text_view), 10);
-    gtk_text_view_set_left_margin(GTK_TEXT_VIEW (ctx->text_view), 10);
-    gtk_text_view_set_right_margin(GTK_TEXT_VIEW (ctx->text_view), 10);
-    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW (ctx->text_view), GTK_WRAP_NONE);
-    gtk_text_view_set_monospace(GTK_TEXT_VIEW (ctx->text_view), TRUE);
+    GtkStyleContext *context = gtk_widget_get_style_context(ctx->text_view);
+    gtk_style_context_add_provider(
+        context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+    gtk_text_view_set_top_margin(GTK_TEXT_VIEW(ctx->text_view), 10);
+    gtk_text_view_set_bottom_margin(GTK_TEXT_VIEW(ctx->text_view), 10);
+    gtk_text_view_set_left_margin(GTK_TEXT_VIEW(ctx->text_view), 10);
+    gtk_text_view_set_right_margin(GTK_TEXT_VIEW(ctx->text_view), 10);
+    gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(ctx->text_view), GTK_WRAP_NONE);
+    gtk_text_view_set_monospace(GTK_TEXT_VIEW(ctx->text_view), TRUE);
     scrolled_window = gtk_scrolled_window_new(NULL, NULL);
     gtk_container_add(GTK_CONTAINER(scrolled_window), ctx->text_view);
 
@@ -524,19 +623,19 @@ static void CreateWindow(GtkApplication *app, char* path)
     gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
 
     // Add event handlers
-    g_signal_connect(new_icon,          "clicked",           G_CALLBACK(OnNew),        ctx);
-    g_signal_connect(open_icon,         "clicked",           G_CALLBACK(OnOpen),       ctx);
-    g_signal_connect(ctx->save_icon,    "clicked",           G_CALLBACK(OnSave),       ctx);
-    g_signal_connect(ctx->save_as_icon, "clicked",           G_CALLBACK(OnSaveAs),     ctx);
-    g_signal_connect(ctx->undo_icon,    "clicked",           G_CALLBACK(OnUndo),       ctx);
-    g_signal_connect(ctx->redo_icon,    "clicked",           G_CALLBACK(OnRedo),       ctx);
-    g_signal_connect(exec_icon,         "clicked",           G_CALLBACK(OnExec),       ctx);
-    g_signal_connect(help_icon,         "clicked",           G_CALLBACK(OnHelp),       ctx);
-    g_signal_connect(ctx->buffer,       "insert-text",       G_CALLBACK(OnInsert),     ctx);
-    g_signal_connect(ctx->buffer,       "delete-range",      G_CALLBACK(OnDelete),     ctx);
-    g_signal_connect(ctx->buffer,       "end-user-action",   G_CALLBACK(OnStopEdit),   ctx);
-    g_signal_connect(ctx->cmd_entry,    "changed",           G_CALLBACK(OnCmdChanged), ctx);
-    g_signal_connect(ctx->window,       "key-press-event",   G_CALLBACK(OnKeyPress),   ctx);
+    g_signal_connect(new_icon, "clicked", G_CALLBACK(OnNew), ctx);
+    g_signal_connect(open_icon, "clicked", G_CALLBACK(OnOpen), ctx);
+    g_signal_connect(ctx->save_icon, "clicked", G_CALLBACK(OnSave), ctx);
+    g_signal_connect(ctx->save_as_icon, "clicked", G_CALLBACK(OnSaveAs), ctx);
+    g_signal_connect(ctx->undo_icon, "clicked", G_CALLBACK(OnUndo), ctx);
+    g_signal_connect(ctx->redo_icon, "clicked", G_CALLBACK(OnRedo), ctx);
+    g_signal_connect(exec_icon, "clicked", G_CALLBACK(OnExec), ctx);
+    g_signal_connect(help_icon, "clicked", G_CALLBACK(OnHelp), ctx);
+    g_signal_connect(ctx->buffer, "insert-text", G_CALLBACK(OnInsert), ctx);
+    g_signal_connect(ctx->buffer, "delete-range", G_CALLBACK(OnDelete), ctx);
+    g_signal_connect(ctx->buffer, "end-user-action", G_CALLBACK(OnStopEdit), ctx);
+    g_signal_connect(ctx->cmd_entry, "changed", G_CALLBACK(OnCmdChanged), ctx);
+    g_signal_connect(ctx->window, "key-press-event", G_CALLBACK(OnKeyPress), ctx);
 
     // Show all the widgets
     gtk_widget_show_all(ctx->window);
@@ -548,11 +647,10 @@ static void CreateWindow(GtkApplication *app, char* path)
 //---------------------------------------------------------
 int main(int argc, char **argv)
 {
-    GtkApplication* app = gtk_application_new(NULL, G_APPLICATION_HANDLES_OPEN);
+    GtkApplication *app = gtk_application_new(NULL, G_APPLICATION_HANDLES_OPEN);
     g_signal_connect(app, "activate", G_CALLBACK(OnActivateApp), NULL);
     g_signal_connect(app, "open", G_CALLBACK(OnOpenApp), NULL);
     int status = g_application_run(G_APPLICATION(app), argc, argv);
     g_object_unref(app);
     return status;
 }
-