From 512f79e1cbb9afa48661290982ad18eb9ed86b7b Mon Sep 17 00:00:00 2001 From: "A. Madani" Date: Tue, 14 Jul 2026 19:24:24 +0300 Subject: [PATCH] Doxygen comments, app, editor, git, index, project, ui, tests https://github.com/Logic-gate/cleaf/issues/2 --- .gitignore | 3 + src/app/app_about_view_actions.inc.c | 37 +++- src/app/app_file_search_actions.inc.c | 56 +++++ src/app/app_preference_actions.inc.c | 53 +++++ src/app/app_window_events.inc.c | 23 ++ src/app/app_window_lifecycle.inc.c | 14 ++ src/app/app_window_state.inc.c | 20 ++ src/app/app_window_tabs.inc.c | 23 ++ src/editor/editor_tab_context_menu.inc.c | 207 +++++++++++++----- .../editor_tab_highlight_scheduling.inc.c | 18 ++ src/editor/editor_tab_hover_events.inc.c | 26 +++ src/editor/editor_tab_hover_refs.inc.c | 50 +++++ src/editor/editor_tab_lifecycle.inc.c | 62 ++++++ .../editor_tab_signals_preferences.inc.c | 26 +++ src/git/git_actions.inc.c | 47 ++++ src/git/git_core.inc.c | 62 ++++++ src/git/git_credentials.inc.c | 14 ++ src/index/index_candidates.inc.c | 17 ++ src/index/index_collect.inc.c | 35 +++ src/index/index_references.inc.c | 32 +++ src/project/project_context.inc.c | 41 ++++ src/project/project_rows.inc.c | 31 ++- src/project/project_tree.inc.c | 42 +++- src/project/project_util.inc.c | 32 +++ src/ui/ui_base_css.inc.c | 5 + src/ui/ui_editor_css.inc.c | 8 + tests/unit_tests.c | 42 ++++ 27 files changed, 966 insertions(+), 60 deletions(-) diff --git a/.gitignore b/.gitignore index 016b821..cffea4c 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,6 @@ dkms.conf .cleaf-backups/ .cleaf-save-*.tmp .cleaf-latex-build/ + +# Github actions +.github/ diff --git a/src/app/app_about_view_actions.inc.c b/src/app/app_about_view_actions.inc.c index 00801d5..6729512 100644 --- a/src/app/app_about_view_actions.inc.c +++ b/src/app/app_about_view_actions.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/app/app_about_view_actions.inc.c + * @brief Cleaf app about view actions module. + */ + GtkWidget *pref_section(const char *text) { GtkWidget *label = gtk_label_new(text ? text : ""); gtk_label_set_xalign(GTK_LABEL(label), 0.0f); @@ -5,6 +10,9 @@ GtkWidget *pref_section(const char *text) { return label; } +/** + * @brief Pref tab label. + */ GtkWidget *pref_tab_label(const char *text) { GtkWidget *label = gtk_label_new(text ? text : ""); // Perhaps this should be in config or part of the themeing engine. @@ -17,6 +25,9 @@ GtkWidget *pref_tab_label(const char *text) { } +/** + * @brief Cleaf prefers dark theme. + */ static gboolean cleaf_prefers_dark_theme(void) { GtkSettings *settings = gtk_settings_get_default(); gboolean dark = FALSE; @@ -28,6 +39,9 @@ static gboolean cleaf_prefers_dark_theme(void) { return dark; } +/** + * @brief Cleaf logo path for theme. + */ static char *cleaf_logo_path_for_theme(gboolean dark) { const char *filename = dark ? "cleaf-logo-dark.png" : "cleaf-logo-light.png"; @@ -42,6 +56,9 @@ static char *cleaf_logo_path_for_theme(gboolean dark) { return NULL; } +/** + * @brief Cleaf about logo new. + */ static GtkWidget *cleaf_about_logo_new(void) { char *path = cleaf_logo_path_for_theme(cleaf_prefers_dark_theme()); if (!path) return NULL; @@ -49,13 +66,16 @@ static GtkWidget *cleaf_about_logo_new(void) { GtkWidget *picture = gtk_picture_new_for_filename(path); gtk_widget_set_size_request(picture, 170, 170); gtk_picture_set_can_shrink(GTK_PICTURE(picture), TRUE); - gtk_picture_set_keep_aspect_ratio(GTK_PICTURE(picture), TRUE); + gtk_picture_set_content_fit(GTK_PICTURE(picture), GTK_CONTENT_FIT_CONTAIN); gtk_widget_set_halign(picture, GTK_ALIGN_CENTER); gtk_widget_set_valign(picture, GTK_ALIGN_CENTER); g_free(path); return picture; } +/** + * @brief Cleaf about label new. + */ static GtkWidget *cleaf_about_label_new(const char *text, gboolean title) { GtkWidget *label = gtk_label_new(text ? text : ""); gtk_label_set_xalign(GTK_LABEL(label), 0.5f); @@ -65,6 +85,9 @@ static GtkWidget *cleaf_about_label_new(const char *text, gboolean title) { return label; } +/** + * @brief Action about. + */ void action_about(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -97,6 +120,9 @@ void action_about(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action show preferences. + */ void action_show_preferences(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -107,6 +133,9 @@ void action_show_preferences(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action toggle minimap. + */ void action_toggle_minimap(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -118,6 +147,9 @@ void action_toggle_minimap(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action toggle preview. + */ void action_toggle_preview(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -128,6 +160,9 @@ void action_toggle_preview(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action render latex. + */ void action_render_latex(GtkWidget *widget, gpointer user_data) { (void)widget; EditorTab *tab = app_window_current_tab(user_data); diff --git a/src/app/app_file_search_actions.inc.c b/src/app/app_file_search_actions.inc.c index f533272..af51ec2 100644 --- a/src/app/app_file_search_actions.inc.c +++ b/src/app/app_file_search_actions.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/app/app_file_search_actions.inc.c + * @brief Cleaf app file search actions module. + */ + void set_search_panel(EditorWindow *win, gboolean visible, gboolean replace_mode) { if (!win || !win->search_revealer) return; /* Find and Replace as one panel. @@ -16,6 +21,9 @@ void set_search_panel(EditorWindow *win, gboolean visible, gboolean replace_mode } +/** + * @brief Action new. + */ void action_new(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -24,6 +32,9 @@ void action_new(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action open. + */ void action_open(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -37,6 +48,9 @@ void action_open(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action open folder. + */ void action_open_folder(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -61,6 +75,9 @@ void action_open_folder(GtkWidget *widget, gpointer user_data) { g_free(folder); } +/** + * @brief Cleaf executable path. + */ static char *cleaf_executable_path(void) { GError *error = NULL; char *path = g_file_read_link("/proc/self/exe", &error); @@ -72,6 +89,9 @@ static char *cleaf_executable_path(void) { return g_strdup("cleaf"); } +/** + * @brief Action open folder new instance. + */ void action_open_folder_new_instance(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -111,6 +131,9 @@ void action_open_folder_new_instance(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action save. + */ void action_save(GtkWidget *widget, gpointer user_data) { (void)widget; EditorTab *tab = app_window_current_tab(user_data); @@ -118,6 +141,9 @@ void action_save(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action save as. + */ void action_save_as(GtkWidget *widget, gpointer user_data) { (void)widget; EditorTab *tab = app_window_current_tab(user_data); @@ -125,24 +151,36 @@ void action_save_as(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action show find. + */ void action_show_find(GtkWidget *widget, gpointer user_data) { (void)widget; set_search_panel(user_data, TRUE, FALSE); } +/** + * @brief Action show replace. + */ void action_show_replace(GtkWidget *widget, gpointer user_data) { (void)widget; set_search_panel(user_data, TRUE, TRUE); } +/** + * @brief Action hide search. + */ void action_hide_search(GtkWidget *widget, gpointer user_data) { (void)widget; set_search_panel(user_data, FALSE, FALSE); } +/** + * @brief Action find next. + */ void action_find_next(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -152,6 +190,9 @@ void action_find_next(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action find prev. + */ void action_find_prev(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -161,6 +202,9 @@ void action_find_prev(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action replace. + */ void action_replace(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -171,6 +215,9 @@ void action_replace(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action replace all. + */ void action_replace_all(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -181,6 +228,9 @@ void action_replace_all(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action comment. + */ void action_comment(GtkWidget *widget, gpointer user_data) { (void)widget; EditorTab *tab = app_window_current_tab(user_data); @@ -188,6 +238,9 @@ void action_comment(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action reload syntax. + */ void action_reload_syntax(GtkWidget *widget, gpointer user_data) { // Syntax definitions are runtime files, so reload them without restarting the editor (void)widget; @@ -195,6 +248,9 @@ void action_reload_syntax(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action syntax diagnostics. + */ void action_syntax_diagnostics(GtkWidget *widget, gpointer user_data) { //Should add functional diagnostics. This is just for show. (void)widget; diff --git a/src/app/app_preference_actions.inc.c b/src/app/app_preference_actions.inc.c index 6f6973a..a6fd1e3 100644 --- a/src/app/app_preference_actions.inc.c +++ b/src/app/app_preference_actions.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/app/app_preference_actions.inc.c + * @brief Cleaf app preference actions module. + */ + void set_tab_policy(EditorWindow *win, guint width, gboolean insert_spaces) { if (!win) return; if (width == 0u) width = 4u; @@ -9,18 +14,27 @@ void set_tab_policy(EditorWindow *win, guint width, gboolean insert_spaces) { } +/** + * @brief Action tab spaces 2. + */ void action_tab_spaces_2(GtkWidget *widget, gpointer user_data) { (void)widget; set_tab_policy(user_data, 2u, TRUE); } +/** + * @brief Action tab spaces 4. + */ void action_tab_spaces_4(GtkWidget *widget, gpointer user_data) { (void)widget; set_tab_policy(user_data, 4u, TRUE); } +/** + * @brief Action tab spaces 8. + */ void action_tab_spaces_8(GtkWidget *widget, gpointer user_data) { //I found this in Sublime, I have no idea who uses 8...propably legacy use. (void)widget; @@ -28,12 +42,18 @@ void action_tab_spaces_8(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action tab hard tabs. + */ void action_tab_hard_tabs(GtkWidget *widget, gpointer user_data) { (void)widget; set_tab_policy(user_data, 4u, FALSE); } +/** + * @brief Action toggle autocomplete. + */ void action_toggle_autocomplete(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -45,6 +65,9 @@ void action_toggle_autocomplete(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action toggle autosave. + */ void action_toggle_autosave(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -58,6 +81,9 @@ void action_toggle_autosave(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action toggle backup. + */ void action_toggle_backup(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -68,6 +94,9 @@ void action_toggle_backup(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Rgba to hex. + */ char *rgba_to_hex(const GdkRGBA *rgba) { if (!rgba) return g_strdup("#000000"); /* @@ -87,6 +116,9 @@ char *rgba_to_hex(const GdkRGBA *rgba) { } +/** + * @brief Choose color for slot. + */ void choose_color_for_slot(EditorWindow *win, GtkWidget *parent_widget, const char *title, @@ -116,36 +148,54 @@ void choose_color_for_slot(EditorWindow *win, } +/** + * @brief Action choose background. + */ void action_choose_background(GtkWidget *widget, gpointer user_data) { EditorWindow *win = user_data; choose_color_for_slot(win, widget, "Editor Background", win ? &win->editor_bg_color : NULL); } +/** + * @brief Action choose sidebar background. + */ void action_choose_sidebar_background(GtkWidget *widget, gpointer user_data) { EditorWindow *win = user_data; choose_color_for_slot(win, widget, "Sidebar Background", win ? &win->sidebar_bg_color : NULL); } +/** + * @brief Action choose tabbar background. + */ void action_choose_tabbar_background(GtkWidget *widget, gpointer user_data) { EditorWindow *win = user_data; choose_color_for_slot(win, widget, "Tab Bar Background", win ? &win->tabbar_bg_color : NULL); } +/** + * @brief Action choose scroll preview background. + */ void action_choose_scroll_preview_background(GtkWidget *widget, gpointer user_data) { EditorWindow *win = user_data; choose_color_for_slot(win, widget, "Scroll Preview Background", win ? &win->scroll_preview_bg_color : NULL); } +/** + * @brief Action choose popover background. + */ void action_choose_popover_background(GtkWidget *widget, gpointer user_data) { EditorWindow *win = user_data; choose_color_for_slot(win, widget, "Popover Background", win ? &win->popover_bg_color : NULL); } +/** + * @brief Action reset background. + */ void action_reset_background(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -156,6 +206,9 @@ void action_reset_background(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Action reset all backgrounds. + */ void action_reset_all_backgrounds(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; diff --git a/src/app/app_window_events.inc.c b/src/app/app_window_events.inc.c index 64dd977..8545b01 100644 --- a/src/app/app_window_events.inc.c +++ b/src/app/app_window_events.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/app/app_window_events.inc.c + * @brief Cleaf app window events module. + */ + static EditorTab *find_tab_for_path(EditorWindow *win, const char *canonical_path) { if (!win || !win->notebook || !canonical_path) return NULL; @@ -27,6 +32,9 @@ static EditorTab *find_tab_for_path(EditorWindow *win, const char *canonical_pat } +/** + * @brief App window open file. + */ gboolean app_window_open_file(EditorWindow *win, const char *path) { if (!win || !path || path[0] == '\0') return FALSE; @@ -68,6 +76,9 @@ gboolean app_window_open_file(EditorWindow *win, const char *path) { } +/** + * @brief On switch page. + */ void on_switch_page(GtkNotebook *notebook, GtkWidget *page, guint page_num, @@ -81,6 +92,9 @@ void on_switch_page(GtkNotebook *notebook, } +/** + * @brief On syntax changed. + */ void on_syntax_changed(GtkDropDown *drop_down, GParamSpec *pspec, gpointer user_data) { @@ -111,6 +125,9 @@ void on_syntax_changed(GtkDropDown *drop_down, } +/** + * @brief On window close request. + */ gboolean on_window_close_request(GtkWindow *window, gpointer user_data) { (void)window; @@ -128,6 +145,9 @@ gboolean on_window_close_request(GtkWindow *window, gpointer user_data) { } +/** + * @brief Switch page delta. + */ void switch_page_delta(EditorWindow *win, int delta) { if (!win || !win->notebook) return; @@ -145,6 +165,9 @@ void switch_page_delta(EditorWindow *win, int delta) { } +/** + * @brief On window key pressed. + */ gboolean on_window_key_pressed(GtkEventControllerKey *controller, guint keyval, guint keycode, diff --git a/src/app/app_window_lifecycle.inc.c b/src/app/app_window_lifecycle.inc.c index 34b13ce..c8cedae 100644 --- a/src/app/app_window_lifecycle.inc.c +++ b/src/app/app_window_lifecycle.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/app/app_window_lifecycle.inc.c + * @brief Cleaf app window lifecycle module. + */ + EditorTab *app_window_current_tab(EditorWindow *win) { if (!win || !win->notebook) return NULL; gint page = gtk_notebook_get_current_page(GTK_NOTEBOOK(win->notebook)); @@ -7,6 +12,9 @@ EditorTab *app_window_current_tab(EditorWindow *win) { return g_object_get_data(G_OBJECT(child), "cleaf-tab"); } +/** + * @brief Codex status changed. + */ static void codex_status_changed(CodexClient *client, CodexClientState state, const char *detail, @@ -27,6 +35,9 @@ static void codex_status_changed(CodexClient *client, } +/** + * @brief App window new. + */ EditorWindow *app_window_new(GtkApplication *application) { EditorWindow *win = g_new0(EditorWindow, 1); if (!win) return NULL; @@ -203,6 +214,9 @@ EditorWindow *app_window_new(GtkApplication *application) { } +/** + * @brief App window free. + */ void app_window_free(EditorWindow *win) { if (!win) return; project_tree_close_context(win); diff --git a/src/app/app_window_state.inc.c b/src/app/app_window_state.inc.c index 74d075b..2e6ad69 100644 --- a/src/app/app_window_state.inc.c +++ b/src/app/app_window_state.inc.c @@ -1,9 +1,17 @@ +/** + * @file src/app/app_window_state.inc.c + * @brief Cleaf app window state module. + */ + GtkWindow *app_window_gtk(EditorWindow *win) { // Some helpers accept NULL parents, so keep this wrapper safe. return win && win->window ? GTK_WINDOW(win->window) : NULL; } +/** + * @brief App window set status. + */ void app_window_set_status(EditorWindow *win, const char *text) { if (!win || !win->status_label) return; @@ -12,6 +20,9 @@ void app_window_set_status(EditorWindow *win, const char *text) { } +/** + * @brief Canonical or dup. + */ static char *canonical_or_dup(const char *path) { if (!path || path[0] == '\0') return NULL; @@ -24,6 +35,9 @@ static char *canonical_or_dup(const char *path) { } +/** + * @brief App window is file locked. + */ gboolean app_window_is_file_locked(EditorWindow *win, const char *path) { if (!win || !win->locked_paths || !path) return FALSE; @@ -37,6 +51,9 @@ gboolean app_window_is_file_locked(EditorWindow *win, const char *path) { } +/** + * @brief App window set file locked. + */ void app_window_set_file_locked(EditorWindow *win, const char *path, gboolean locked) { @@ -87,6 +104,9 @@ void app_window_set_file_locked(EditorWindow *win, } +/** + * @brief App window note path renamed. + */ void app_window_note_path_renamed(EditorWindow *win, const char *old_path, const char *new_path) { diff --git a/src/app/app_window_tabs.inc.c b/src/app/app_window_tabs.inc.c index 99bd1e5..cb9b2c6 100644 --- a/src/app/app_window_tabs.inc.c +++ b/src/app/app_window_tabs.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/app/app_window_tabs.inc.c + * @brief Cleaf app window tabs module. + */ + void app_window_add_tab(EditorWindow *win, EditorTab *tab, gboolean switch_to_tab) { @@ -37,6 +42,9 @@ void app_window_add_tab(EditorWindow *win, } +/** + * @brief App window close tab. + */ void app_window_close_tab(EditorWindow *win, EditorTab *tab) { if (!win || !tab) return; @@ -74,6 +82,9 @@ void app_window_close_tab(EditorWindow *win, EditorTab *tab) { } +/** + * @brief App window close all tabs. + */ gboolean app_window_close_all_tabs(EditorWindow *win) { if (!win || !win->notebook) return TRUE; @@ -113,6 +124,9 @@ gboolean app_window_close_all_tabs(EditorWindow *win) { } +/** + * @brief Combo index for syntax. + */ int combo_index_for_syntax(EditorWindow *win, SyntaxDef *syntax) { if (!win || !syntax) return 0; @@ -130,6 +144,9 @@ int combo_index_for_syntax(EditorWindow *win, SyntaxDef *syntax) { } +/** + * @brief Populate syntax combo. + */ void populate_syntax_combo(EditorWindow *win, EditorTab *tab) { if (!win || !win->syntax_combo) return; @@ -171,6 +188,9 @@ void populate_syntax_combo(EditorWindow *win, EditorTab *tab) { } +/** + * @brief App window update ui. + */ void app_window_update_ui(EditorWindow *win) { if (!win) return; @@ -225,6 +245,9 @@ void app_window_update_ui(EditorWindow *win) { } +/** + * @brief App window reload syntaxes. + */ void app_window_reload_syntaxes(EditorWindow *win) { if (!win) return; diff --git a/src/editor/editor_tab_context_menu.inc.c b/src/editor/editor_tab_context_menu.inc.c index a9e44df..0b5d3be 100644 --- a/src/editor/editor_tab_context_menu.inc.c +++ b/src/editor/editor_tab_context_menu.inc.c @@ -1,4 +1,18 @@ -static void context_popover_closed(GtkPopover *popover, gpointer user_data) { +/** + * context_popover_closed: + * @popover: the context-menu popover that was closed + * @user_data: the text view that owns the active context popover + * + * Clears the text view's stored popover pointer when it still refers to + * @popover, then destroys the one-shot popover. + * + * The identity check avoids clearing a newer popover that may have replaced + * the closed one before this callback runs. + */ +static void +context_popover_closed(GtkPopover *popover, + gpointer user_data) +{ GtkWidget *parent = user_data; /* @@ -6,38 +20,84 @@ static void context_popover_closed(GtkPopover *popover, gpointer user_data) { * still the same popover, because a new one may have been opened already. */ if (parent) { - GtkWidget *stored = g_object_get_data(G_OBJECT(parent), - "cleaf-context-popover"); + GtkWidget *stored; + + stored = g_object_get_data(G_OBJECT(parent), + "cleaf-context-popover"); + if (stored == GTK_WIDGET(popover)) { - g_object_set_data(G_OBJECT(parent), "cleaf-context-popover", NULL); + g_object_set_data(G_OBJECT(parent), + "cleaf-context-popover", + NULL); } } - // Context popovers are one-shot widgets and should be destroyed after closing. + /* + * Context popovers are one-shot widgets and should not remain allocated + * after GTK has closed them. + */ cleaf_widget_destroy(GTK_WIDGET(popover)); } -static GtkWidget *context_button(EditorTab *tab, - const char *label, - GCallback callback) { - // Keep context-menu buttons visually consistent with the rest of Cleaf. +/** + * context_button: + * @tab: the editor tab passed to the button callback + * @label: the text displayed by the button + * @callback: the callback invoked when the button is activated + * + * Creates a context-menu button using Cleaf's shared flat-button styling. + * + * Returns: (transfer full): a newly created context-menu button + */ +static GtkWidget * +context_button(EditorTab *tab, + const char *label, + GCallback callback) +{ + /* + * Use the shared constructor so context-menu buttons remain visually and + * behaviorally consistent with the rest of Cleaf. + */ return cleaf_flat_button_new(label, NULL, callback, tab); } -void on_text_view_right_click(GtkGestureClick *gesture, - int n_press, - double x, - double y, - gpointer user_data) { +/** + * on_text_view_right_click: + * @gesture: the click gesture that received the right-button event + * @n_press: the number of presses in the current click sequence + * @x: the horizontal click position relative to the text view + * @y: the vertical click position relative to the text view + * @user_data: the #EditorTab associated with the text view + * + * Opens the editor context menu at the pointer position. + * + * Any previously stored context popover is destroyed before the new one is + * attached. This keeps one active context menu per text view and prevents + * stale popovers from retaining references to editor state. + */ +void +on_text_view_right_click(GtkGestureClick *gesture, + int n_press, + double x, + double y, + gpointer user_data) +{ + EditorTab *tab = user_data; + GtkWidget *widget; + GtkWidget *old_popover; + GtkWidget *popover; + GtkWidget *box; + GdkRectangle rect; + (void)n_press; - EditorTab *tab = user_data; - GtkWidget *widget = gtk_event_controller_get_widget( + widget = gtk_event_controller_get_widget( GTK_EVENT_CONTROLLER(gesture)); - if (!tab || !widget) return; + if (!tab || !widget) + return; /* * Claim the sequence so the right-click is handled by Cleaf's context menu @@ -50,68 +110,103 @@ void on_text_view_right_click(GtkGestureClick *gesture, * Only one context popover should exist per text view. Destroy the previous * one before opening a new menu at the current pointer position. */ - GtkWidget *old_popover = g_object_get_data(G_OBJECT(widget), - "cleaf-context-popover"); + old_popover = g_object_get_data(G_OBJECT(widget), + "cleaf-context-popover"); + if (old_popover && GTK_IS_POPOVER(old_popover)) { cleaf_popover_hide(old_popover); cleaf_widget_destroy(old_popover); } - GtkWidget *popover = gtk_popover_new(); + popover = gtk_popover_new(); /* - * Attach the popover to the text view so GTK can position it relative to the - * editor instead of treating it as a detached window. + * Attach the popover to the text view so GTK can position it relative to + * the editor instead of treating it as a detached window. */ cleaf_popover_attach(popover, widget); gtk_popover_set_has_arrow(GTK_POPOVER(popover), FALSE); gtk_widget_add_css_class(popover, "cleaf-context-popover"); - // Store it on the widget so the next right-click can replace it cleanly. - g_object_set_data(G_OBJECT(widget), "cleaf-context-popover", popover); + /* + * Store the current popover on the text view so a later right-click can + * replace it without leaving multiple menus attached. + */ + g_object_set_data(G_OBJECT(widget), + "cleaf-context-popover", + popover); - GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); cleaf_set_all_margins(box, 6); - gtk_box_append(GTK_BOX(box), context_button(tab, "Undo", - G_CALLBACK(menu_undo))); - gtk_box_append(GTK_BOX(box), context_button(tab, "Redo", - G_CALLBACK(menu_redo))); - - gtk_box_append(GTK_BOX(box), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)); - - gtk_box_append(GTK_BOX(box), context_button(tab, "Cut", - G_CALLBACK(menu_cut))); - gtk_box_append(GTK_BOX(box), context_button(tab, "Copy", - G_CALLBACK(menu_copy))); - gtk_box_append(GTK_BOX(box), context_button(tab, "Paste", - G_CALLBACK(menu_paste))); - gtk_box_append(GTK_BOX(box), context_button(tab, "Select All", - G_CALLBACK(menu_select_all))); - - gtk_box_append(GTK_BOX(box), gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)); - - gtk_box_append(GTK_BOX(box), context_button(tab, "Cut Line", - G_CALLBACK(menu_cut_line))); - gtk_box_append(GTK_BOX(box), context_button(tab, "Paste Cut Line", - G_CALLBACK(menu_paste_line))); - gtk_box_append(GTK_BOX(box), context_button(tab, "Toggle Comment", - G_CALLBACK(menu_comment))); - gtk_box_append(GTK_BOX(box), context_button(tab, "Auto Complete", - G_CALLBACK(menu_complete))); + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Undo", + G_CALLBACK(menu_undo))); + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Redo", + G_CALLBACK(menu_redo))); + + gtk_box_append( + GTK_BOX(box), + gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)); + + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Cut", + G_CALLBACK(menu_cut))); + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Copy", + G_CALLBACK(menu_copy))); + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Paste", + G_CALLBACK(menu_paste))); + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Select All", + G_CALLBACK(menu_select_all))); + + gtk_box_append( + GTK_BOX(box), + gtk_separator_new(GTK_ORIENTATION_HORIZONTAL)); + + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Cut Line", + G_CALLBACK(menu_cut_line))); + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Paste Cut Line", + G_CALLBACK(menu_paste_line))); + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Toggle Comment", + G_CALLBACK(menu_comment))); + gtk_box_append(GTK_BOX(box), + context_button(tab, + "Auto Complete", + G_CALLBACK(menu_complete))); /* - * Use the click position as a tiny target rectangle. This makes the popover - * appear where the user opened the menu. + * Use the click position as a tiny target rectangle so the popover opens + * where the user requested the context menu. */ - GdkRectangle rect = { (int)x, (int)y, 1, 1 }; + rect = (GdkRectangle) { + .x = (int)x, + .y = (int)y, + .width = 1, + .height = 1 + }; gtk_popover_set_child(GTK_POPOVER(popover), box); gtk_popover_set_pointing_to(GTK_POPOVER(popover), &rect); /* - * When GTK closes the popover, clear the stored pointer and destroy the - * one-shot menu widget. + * The closed callback clears the stored pointer before destroying the + * one-shot menu. */ g_signal_connect(popover, "closed", diff --git a/src/editor/editor_tab_highlight_scheduling.inc.c b/src/editor/editor_tab_highlight_scheduling.inc.c index 18a3466..ac99f5d 100644 --- a/src/editor/editor_tab_highlight_scheduling.inc.c +++ b/src/editor/editor_tab_highlight_scheduling.inc.c @@ -1,6 +1,12 @@ +/** + * @file src/editor/editor_tab_highlight_scheduling.inc.c + * @brief Cleaf editor tab highlight scheduling module. + */ + void editor_tab_apply_highlight(EditorTab *tab) { if (!tab || !tab->buffer) return; /* Cleaf no longer applies regex YAML highlighting to the editor buffer. + * This change came when moving to GtkSourceView. * Highlighting is owned by GtkSourceView; YAML rules only generate optional * GtkSourceView style-scheme overrides when the user enables them. */ @@ -8,6 +14,9 @@ void editor_tab_apply_highlight(EditorTab *tab) { } +/** + * @brief Minimap timeout cb. + */ gboolean minimap_timeout_cb(gpointer user_data) { EditorTab *tab = user_data; if (!tab) return G_SOURCE_REMOVE; @@ -33,6 +42,9 @@ void editor_tab_schedule_minimap_update(EditorTab *tab) { } +/** + * @brief Preview timeout cb. + */ gboolean preview_timeout_cb(gpointer user_data) { EditorTab *tab = user_data; if (!tab) return G_SOURCE_REMOVE; @@ -62,6 +74,9 @@ void editor_tab_schedule_preview_update(EditorTab *tab) { } +/** + * @brief Highlight timeout cb. + */ gboolean highlight_timeout_cb(gpointer user_data) { EditorTab *tab = user_data; if (!tab) return G_SOURCE_REMOVE; @@ -71,6 +86,9 @@ gboolean highlight_timeout_cb(gpointer user_data) { } +/** + * @brief Editor tab schedule highlight. + */ void editor_tab_schedule_highlight(EditorTab *tab) { if (!tab) return; /* GtkSourceView performs syntax highlighting internally. There is no diff --git a/src/editor/editor_tab_hover_events.inc.c b/src/editor/editor_tab_hover_events.inc.c index b246875..308ac17 100644 --- a/src/editor/editor_tab_hover_events.inc.c +++ b/src/editor/editor_tab_hover_events.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/editor/editor_tab_hover_events.inc.c + * @brief Cleaf editor tab hover events module. + */ + gboolean hover_timeout_cb(gpointer user_data) { EditorTab *tab = user_data; if (!tab) return G_SOURCE_REMOVE; @@ -93,6 +98,9 @@ gboolean hover_timeout_cb(gpointer user_data) { } +/** + * @brief On text view motion. + */ void on_text_view_motion(GtkEventControllerMotion *controller, double x, double y, @@ -133,6 +141,9 @@ void on_text_view_motion(GtkEventControllerMotion *controller, } +/** + * @brief On text view leave. + */ void on_text_view_leave(GtkEventControllerMotion *controller, gpointer user_data) { (void)controller; @@ -153,6 +164,9 @@ void on_text_view_leave(GtkEventControllerMotion *controller, } +/** + * @brief On hover popover enter. + */ void on_hover_popover_enter(GtkEventControllerMotion *controller, double x, double y, @@ -170,6 +184,9 @@ void on_hover_popover_enter(GtkEventControllerMotion *controller, } +/** + * @brief On hover popover leave. + */ void on_hover_popover_leave(GtkEventControllerMotion *controller, gpointer user_data) { (void)controller; @@ -183,6 +200,9 @@ void on_hover_popover_leave(GtkEventControllerMotion *controller, } +/** + * @brief Color preview row new. + */ GtkWidget *color_preview_row_new(EditorTab *tab, const char *hex) { GtkWidget *row = gtk_list_box_row_new(); @@ -217,6 +237,9 @@ GtkWidget *color_preview_row_new(EditorTab *tab, const char *hex) { } +/** + * @brief Show color preview in hover. + */ void show_color_preview_in_hover(EditorTab *tab, const char *hex, GtkTextIter *where) { @@ -268,6 +291,9 @@ void show_color_preview_in_hover(EditorTab *tab, } +/** + * @brief Maybe show color preview. + */ void maybe_show_color_preview(EditorTab *tab) { if (!tab || !tab->buffer || !tab->hover_popover || !tab->hover_list) return; if (!tab->inspect_reference_active) return; diff --git a/src/editor/editor_tab_hover_refs.inc.c b/src/editor/editor_tab_hover_refs.inc.c index 0a092f6..5f37060 100644 --- a/src/editor/editor_tab_hover_refs.inc.c +++ b/src/editor/editor_tab_hover_refs.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/editor/editor_tab_hover_refs.inc.c + * @brief Cleaf editor tab hover refs module. + */ + gboolean hex_to_rgba(const char *text, GdkRGBA *rgba) { if (!text || !rgba) return FALSE; @@ -26,6 +31,9 @@ gboolean hex_to_rgba(const char *text, GdkRGBA *rgba) { } +/** + * @brief On color swatch draw. + */ void on_color_swatch_draw(GtkDrawingArea *area, cairo_t *cr, int width, @@ -50,6 +58,9 @@ void on_color_swatch_draw(GtkDrawingArea *area, } +/** + * @brief Hide color preview. + */ void hide_color_preview(EditorTab *tab) { if (!tab || !tab->color_preview_valid) return; @@ -61,6 +72,9 @@ void hide_color_preview(EditorTab *tab) { } +/** + * @brief Cancel hover hide. + */ void cancel_hover_hide(EditorTab *tab) { if (!tab) return; if (tab->hover_hide_timeout != 0u) { @@ -69,6 +83,9 @@ void cancel_hover_hide(EditorTab *tab) { } +/** + * @brief Hide hover preview. + */ void hide_hover_preview(EditorTab *tab) { if (!tab) return; @@ -89,6 +106,9 @@ void hide_hover_preview(EditorTab *tab) { } +/** + * @brief Hover transition timeout cb. + */ gboolean hover_transition_timeout_cb(gpointer user_data) { EditorTab *tab = user_data; if (!tab) return G_SOURCE_REMOVE; @@ -106,6 +126,9 @@ gboolean hover_transition_timeout_cb(gpointer user_data) { } +/** + * @brief Schedule hover transition hide. + */ void schedule_hover_transition_hide(EditorTab *tab) { if (!tab || tab->hover_pointer_inside) return; if (tab->hover_hide_timeout != 0u) return; @@ -119,6 +142,9 @@ void schedule_hover_transition_hide(EditorTab *tab) { } +/** + * @brief Word at iter. + */ char *word_at_iter(GtkTextBuffer *buffer, GtkTextIter *iter) { if (!buffer || !iter) return NULL; @@ -161,6 +187,9 @@ char *word_at_iter(GtkTextBuffer *buffer, GtkTextIter *iter) { } +/** + * @brief Set hover anchor from iter. + */ static void set_hover_anchor_from_iter(EditorTab *tab, GtkTextIter *iter) { if (!tab || !iter || !tab->text_view) return; @@ -177,6 +206,9 @@ static void set_hover_anchor_from_iter(EditorTab *tab, GtkTextIter *iter) { } +/** + * @brief Schedule reference lookup at iter. + */ static void schedule_reference_lookup_at_iter(EditorTab *tab, GtkTextIter *iter) { if (!tab || !iter || !tab->buffer) return; @@ -217,6 +249,9 @@ static void schedule_reference_lookup_at_iter(EditorTab *tab, } +/** + * @brief Editor tab show reference at pointer or cursor. + */ void editor_tab_show_reference_at_pointer_or_cursor(EditorTab *tab) { if (!tab || !tab->text_view || !tab->buffer) return; @@ -240,6 +275,9 @@ void editor_tab_show_reference_at_pointer_or_cursor(EditorTab *tab) { } +/** + * @brief Hover clear rows. + */ void hover_clear_rows(EditorTab *tab) { if (!tab || !tab->hover_list) return; @@ -248,6 +286,9 @@ void hover_clear_rows(EditorTab *tab) { } +/** + * @brief Editor tab jump to line internal. + */ void editor_tab_jump_to_line_internal(EditorTab *tab, guint line) { if (!tab || !tab->buffer || line == 0u) return; @@ -277,12 +318,18 @@ void editor_tab_jump_to_line_internal(EditorTab *tab, guint line) { } +/** + * @brief Editor tab jump to line. + */ void editor_tab_jump_to_line(EditorTab *tab, guint line) { // Public wrapper keeps callers away from the internal implementation name. editor_tab_jump_to_line_internal(tab, line); } +/** + * @brief Hover row activated. + */ void hover_row_activated(GtkListBox *box, GtkListBoxRow *row, gpointer user_data) { @@ -317,6 +364,9 @@ void hover_row_activated(GtkListBox *box, } +/** + * @brief Reference row new. + */ GtkWidget *reference_row_new(IndexReference *ref) { GtkWidget *row = gtk_list_box_row_new(); diff --git a/src/editor/editor_tab_lifecycle.inc.c b/src/editor/editor_tab_lifecycle.inc.c index 70200f2..d0693b4 100644 --- a/src/editor/editor_tab_lifecycle.inc.c +++ b/src/editor/editor_tab_lifecycle.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/editor/editor_tab_lifecycle.inc.c + * @brief Cleaf editor tab lifecycle module. + */ + char *editor_tab_basename(EditorTab *tab) { if (!tab || !tab->file_path) return g_strdup("Untitled"); @@ -17,6 +22,9 @@ char *editor_tab_basename(EditorTab *tab) { } +/** + * @brief Buffer text. + */ char *buffer_text(EditorTab *tab) { GtkTextIter start; GtkTextIter end; @@ -27,6 +35,9 @@ char *buffer_text(EditorTab *tab) { } +/** + * @brief Insert text tagless. + */ void insert_text_tagless(GtkTextBuffer *buffer, GtkTextIter *where, const char *text) { @@ -37,12 +48,18 @@ void insert_text_tagless(GtkTextBuffer *buffer, } +/** + * @brief Cancel timeout id. + */ static void cancel_timeout_id(guint *id) { // Keep timeout cancellation behind one helper in case source handling changes. cleaf_source_cancel(id); } +/** + * @brief Editor tab large file mode. + */ gboolean editor_tab_large_file_mode(EditorTab *tab) { if (!tab || !tab->buffer) return FALSE; @@ -55,24 +72,36 @@ gboolean editor_tab_large_file_mode(EditorTab *tab) { } +/** + * @brief Editor tab live features allowed. + */ gboolean editor_tab_live_features_allowed(EditorTab *tab) { // Live features are disabled for large files to avoid editor lag. return tab && tab->buffer && !editor_tab_large_file_mode(tab); } +/** + * @brief Editor tab highlighting allowed. + */ gboolean editor_tab_highlighting_allowed(EditorTab *tab) { // Highlighting needs an active syntax and at least one loaded rule. return tab && tab->buffer && tab->active_syntax && tab->active_syntax->rules; } +/** + * @brief Editor tab reference features allowed. + */ gboolean editor_tab_reference_features_allowed(EditorTab *tab) { // Reference lookup only needs a valid text buffer for now. return tab && tab->buffer; } +/** + * @brief Editor tab cancel live work. + */ void editor_tab_cancel_live_work(EditorTab *tab) { if (!tab) return; @@ -95,6 +124,9 @@ void editor_tab_cancel_live_work(EditorTab *tab) { } +/** + * @brief Editor tab lightweight ui timeout cb. + */ gboolean editor_tab_lightweight_ui_timeout_cb(gpointer user_data) { EditorTab *tab = user_data; if (!tab) return G_SOURCE_REMOVE; @@ -114,6 +146,9 @@ gboolean editor_tab_lightweight_ui_timeout_cb(gpointer user_data) { } +/** + * @brief Editor tab schedule lightweight ui refresh. + */ void editor_tab_schedule_lightweight_ui_refresh(EditorTab *tab) { if (!tab) return; @@ -126,6 +161,9 @@ void editor_tab_schedule_lightweight_ui_refresh(EditorTab *tab) { } +/** + * @brief Popup append item. + */ void popup_append_item(GtkWidget *menu, const char *label, GCallback callback, @@ -138,6 +176,9 @@ void popup_append_item(GtkWidget *menu, } +/** + * @brief On close button clicked. + */ void on_close_button_clicked(GtkWidget *widget, gpointer user_data) { (void)widget; @@ -148,6 +189,9 @@ void on_close_button_clicked(GtkWidget *widget, gpointer user_data) { } +/** + * @brief Editor tab text rect to popover parent. + */ gboolean editor_tab_text_rect_to_popover_parent(EditorTab *tab, GdkRectangle *rect) { if (!tab || !rect || !tab->text_view || !tab->popover_parent) { @@ -176,6 +220,9 @@ gboolean editor_tab_text_rect_to_popover_parent(EditorTab *tab, } +/** + * @brief Editor tab destroy popovers. + */ void editor_tab_destroy_popovers(EditorTab *tab) { if (!tab) return; @@ -195,6 +242,9 @@ void editor_tab_destroy_popovers(EditorTab *tab) { } +/** + * @brief Editor tab free. + */ void editor_tab_free(EditorTab *tab) { if (!tab) return; @@ -228,11 +278,17 @@ void editor_tab_free(EditorTab *tab) { } +/** + * @brief Editor tab is locked. + */ gboolean editor_tab_is_locked(EditorTab *tab) { return tab ? tab->locked : FALSE; } +/** + * @brief Editor tab set locked. + */ void editor_tab_set_locked(EditorTab *tab, gboolean locked) { if (!tab) return; @@ -248,6 +304,9 @@ void editor_tab_set_locked(EditorTab *tab, gboolean locked) { } +/** + * @brief Editor tab update title. + */ void editor_tab_update_title(EditorTab *tab) { if (!tab) return; @@ -269,6 +328,9 @@ void editor_tab_update_title(EditorTab *tab) { } +/** + * @brief Editor tab update status. + */ void editor_tab_update_status(EditorTab *tab) { if (!tab || !tab->win) return; diff --git a/src/editor/editor_tab_signals_preferences.inc.c b/src/editor/editor_tab_signals_preferences.inc.c index 193a569..fc70c29 100644 --- a/src/editor/editor_tab_signals_preferences.inc.c +++ b/src/editor/editor_tab_signals_preferences.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/editor/editor_tab_signals_preferences.inc.c + * @brief Cleaf editor tab signals preferences module. + */ + void editor_tab_set_syntax(EditorTab *tab, SyntaxDef *syntax, gboolean manual) { if (!tab) return; tab->active_syntax = syntax; @@ -19,6 +24,9 @@ void editor_tab_set_syntax(EditorTab *tab, SyntaxDef *syntax, gboolean manual) { } +/** + * @brief Editor tab auto select syntax. + */ void editor_tab_auto_select_syntax(EditorTab *tab) { if (!tab || !tab->win || tab->manual_syntax_override) return; SyntaxDef *syntax = syntax_for_path(tab->win->syntaxes, tab->file_path); @@ -28,6 +36,9 @@ void editor_tab_auto_select_syntax(EditorTab *tab) { +/** + * @brief On mark set. + */ void on_mark_set(GtkTextBuffer *buffer, GtkTextIter *location, GtkTextMark *mark, gpointer user_data) { (void)buffer; (void)location; @@ -120,6 +131,9 @@ void on_buffer_changed(GtkTextBuffer *buffer, gpointer user_data) { } +/** + * @brief Editor tab set tab policy. + */ void editor_tab_set_tab_policy(EditorTab *tab, guint width, gboolean insert_spaces) { if (!tab) return; if (width == 0u) width = 4u; @@ -141,6 +155,9 @@ void editor_tab_set_tab_policy(EditorTab *tab, guint width, gboolean insert_spac } +/** + * @brief Editor tab apply preferences. + */ void editor_tab_apply_preferences(EditorTab *tab) { if (!tab || !tab->win || !tab->text_view) return; gtk_text_view_set_monospace(GTK_TEXT_VIEW(tab->text_view), !tab->win->use_system_interface_font); @@ -150,6 +167,9 @@ void editor_tab_apply_preferences(EditorTab *tab) { } +/** + * @brief Editor tab set minimap visible. + */ void editor_tab_set_minimap_visible(EditorTab *tab, gboolean visible) { if (!tab || !tab->minimap_scrolled) return; if (visible) { @@ -163,6 +183,9 @@ void editor_tab_set_minimap_visible(EditorTab *tab, gboolean visible) { +/** + * @brief Editor tab set preview visible. + */ void editor_tab_set_preview_visible(EditorTab *tab, gboolean visible) { if (!tab || !tab->preview_scrolled) return; if (visible && preview_is_supported(tab)) { @@ -173,6 +196,9 @@ void editor_tab_set_preview_visible(EditorTab *tab, gboolean visible) { } } +/** + * @brief Editor tab set backup enabled. + */ void editor_tab_set_backup_enabled(EditorTab *tab, gboolean enabled) { if (!tab) return; tab->backup_enabled = enabled; diff --git a/src/git/git_actions.inc.c b/src/git/git_actions.inc.c index 6ee10dc..e09239a 100644 --- a/src/git/git_actions.inc.c +++ b/src/git/git_actions.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/git/git_actions.inc.c + * @brief Cleaf git actions module. + */ + static void show_git_output(EditorWindow *win, const char *title, const char *body) { @@ -45,6 +50,9 @@ static void show_git_output(EditorWindow *win, cleaf_widget_destroy(window); } +/** + * @brief Show git error. + */ static void show_git_error(EditorWindow *win, const char *operation, const CleafGitResult *result) { @@ -66,6 +74,9 @@ static void show_git_error(EditorWindow *win, g_string_free(text, TRUE); } +/** + * @brief Show result or error. + */ static void show_result_or_error(EditorWindow *win, const char *title, const CleafGitResult *result, @@ -86,6 +97,9 @@ static void show_result_or_error(EditorWindow *win, g_string_free(text, TRUE); } +/** + * @brief Repo summary. + */ static char *repo_summary(const char *repo) { GString *summary = g_string_new(NULL); g_string_append_printf(summary, "Repository: %s\n", repo ? repo : "none"); @@ -109,6 +123,9 @@ static char *repo_summary(const char *repo) { return g_string_free(summary, FALSE); } +/** + * @brief Action git status. + */ void action_git_status(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -138,6 +155,9 @@ void action_git_status(GtkWidget *widget, gpointer user_data) { g_free(repo); } +/** + * @brief Diff syntax. + */ static SyntaxDef *diff_syntax(EditorWindow *win) { if (!win || !win->syntaxes) return NULL; for (guint i = 0u; i < win->syntaxes->len; i++) { @@ -147,6 +167,9 @@ static SyntaxDef *diff_syntax(EditorWindow *win) { return NULL; } +/** + * @brief Open text tab. + */ static void open_text_tab(EditorWindow *win, const char *title, const char *text, @@ -165,6 +188,9 @@ static void open_text_tab(EditorWindow *win, editor_tab_update_status(tab); } +/** + * @brief Action git diff. + */ void action_git_diff(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -191,11 +217,17 @@ void action_git_diff(GtkWidget *widget, gpointer user_data) { g_free(repo); } +/** + * @brief Refresh after command. + */ static void refresh_after_command(EditorWindow *win, const char *status) { if (status) app_window_set_status(win, status); cleaf_git_refresh_and_rebuild(win); } +/** + * @brief Action git stage. + */ void action_git_stage(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -217,6 +249,9 @@ void action_git_stage(GtkWidget *widget, gpointer user_data) { g_free(repo); } +/** + * @brief Action git unstage. + */ void action_git_unstage(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -242,6 +277,9 @@ void action_git_unstage(GtkWidget *widget, gpointer user_data) { g_free(repo); } +/** + * @brief Action git stage all. + */ void action_git_stage_all(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -259,6 +297,9 @@ void action_git_stage_all(GtkWidget *widget, gpointer user_data) { g_free(repo); } +/** + * @brief Action git commit. + */ void action_git_commit(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -304,6 +345,9 @@ void action_git_commit(GtkWidget *widget, gpointer user_data) { g_free(repo); } +/** + * @brief Action git pull. + */ void action_git_pull(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -320,6 +364,9 @@ void action_git_pull(GtkWidget *widget, gpointer user_data) { g_free(repo); } +/** + * @brief Action git push. + */ void action_git_push(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; diff --git a/src/git/git_core.inc.c b/src/git/git_core.inc.c index 4ed8dc8..7965043 100644 --- a/src/git/git_core.inc.c +++ b/src/git/git_core.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/git/git_core.inc.c + * @brief Cleaf git core module. + */ + static void git_result_clear(CleafGitResult *result) { if (!result) return; g_clear_pointer(&result->out, g_free); @@ -7,6 +12,9 @@ static void git_result_clear(CleafGitResult *result) { result->kind = CLEAF_GIT_ERROR_NONE; } +/** + * @brief Contains ascii. + */ static gboolean contains_ascii(const char *haystack, const char *needle) { if (!haystack || !needle) return FALSE; char *h = g_ascii_strdown(haystack, -1); @@ -17,6 +25,9 @@ static gboolean contains_ascii(const char *haystack, const char *needle) { return found; } +/** + * @brief Classify git error. + */ static CleafGitErrorKind classify_git_error(const char *out, const char *err) { const char *text = err && err[0] ? err : out; if (!text || text[0] == '\0') return CLEAF_GIT_ERROR_OTHER; @@ -60,6 +71,9 @@ static CleafGitErrorKind classify_git_error(const char *out, const char *err) { return CLEAF_GIT_ERROR_OTHER; } +/** + * @brief Git error title. + */ static const char *git_error_title(CleafGitErrorKind kind) { switch (kind) { case CLEAF_GIT_ERROR_NOT_REPO: return "Not a Git repository"; @@ -78,6 +92,9 @@ static const char *git_error_title(CleafGitErrorKind kind) { } } +/** + * @brief Git error hint. + */ static const char *git_error_hint(CleafGitErrorKind kind) { switch (kind) { case CLEAF_GIT_ERROR_AUTH: @@ -103,6 +120,9 @@ static const char *git_error_hint(CleafGitErrorKind kind) { } } +/** + * @brief Limit output. + */ static void limit_output(char **text) { if (!text || !*text) return; if (strlen(*text) <= CLEAF_GIT_MAX_OUTPUT) return; @@ -176,6 +196,9 @@ static gboolean run_argv(const char * const *argv, return FALSE; } +/** + * @brief Git argv new. + */ static GPtrArray *git_argv_new(const char *repo) { GPtrArray *argv = g_ptr_array_new_with_free_func(g_free); g_ptr_array_add(argv, g_strdup("git")); @@ -186,10 +209,16 @@ static GPtrArray *git_argv_new(const char *repo) { return argv; } +/** + * @brief Argv add. + */ static void argv_add(GPtrArray *argv, const char *arg) { if (argv && arg) g_ptr_array_add(argv, g_strdup(arg)); } +/** + * @brief Run git args. + */ static gboolean run_git_args(const char *repo, const char *stdin_text, CleafGitResult *result, @@ -206,12 +235,18 @@ static gboolean run_git_args(const char *repo, return ok; } +/** + * @brief Chomp dup. + */ static char *chomp_dup(const char *text) { char *copy = g_strdup(text ? text : ""); g_strchomp(copy); return copy; } +/** + * @brief Dir for path. + */ static char *dir_for_path(const char *path) { if (!path || path[0] == '\0') return NULL; if (g_file_test(path, G_FILE_TEST_IS_DIR)) return g_canonicalize_filename(path, NULL); @@ -221,6 +256,9 @@ static char *dir_for_path(const char *path) { return canon; } +/** + * @brief Cleaf git repo for path. + */ char *cleaf_git_repo_for_path(const char *path) { char *dir = dir_for_path(path); if (!dir) return NULL; @@ -238,6 +276,9 @@ char *cleaf_git_repo_for_path(const char *path) { return repo; } +/** + * @brief Relpath for repo. + */ static char *relpath_for_repo(const char *repo, const char *path) { if (!repo || !path) return NULL; char *canon = g_canonicalize_filename(path, NULL); @@ -253,6 +294,9 @@ static char *relpath_for_repo(const char *repo, const char *path) { return rel; } +/** + * @brief Current repo. + */ static char *current_repo(EditorWindow *win, char **rel_file_out) { if (rel_file_out) *rel_file_out = NULL; EditorTab *tab = app_window_current_tab(win); @@ -273,6 +317,9 @@ static char *current_repo(EditorWindow *win, char **rel_file_out) { return NULL; } +/** + * @brief Marker for status. + */ static const char *marker_for_status(const char *record) { if (!record || strlen(record) < 3u) return ""; char x = record[0]; @@ -288,6 +335,9 @@ static const char *marker_for_status(const char *record) { return ""; } +/** + * @brief Status map add. + */ static void status_map_add(EditorWindow *win, const char *repo, const char *record) { @@ -309,6 +359,9 @@ static void status_map_add(EditorWindow *win, g_free(path); } +/** + * @brief Refresh repo status. + */ static void refresh_repo_status(EditorWindow *win, const char *repo) { if (!win || !repo) return; CleafGitResult result; @@ -328,6 +381,9 @@ static void refresh_repo_status(EditorWindow *win, const char *repo) { git_result_clear(&result); } +/** + * @brief Cleaf git status for file. + */ const char *cleaf_git_status_for_file(EditorWindow *win, const char *path) { if (!win || !win->git_file_status || !path) return NULL; char *canon = g_canonicalize_filename(path, NULL); @@ -336,6 +392,9 @@ const char *cleaf_git_status_for_file(EditorWindow *win, const char *path) { return status; } +/** + * @brief Cleaf git refresh all. + */ void cleaf_git_refresh_all(EditorWindow *win) { if (!win) return; if (!win->git_file_status) { @@ -357,6 +416,9 @@ void cleaf_git_refresh_all(EditorWindow *win) { g_hash_table_destroy(seen); } +/** + * @brief Cleaf git refresh and rebuild. + */ void cleaf_git_refresh_and_rebuild(EditorWindow *win) { cleaf_git_refresh_all(win); project_tree_refresh(win); diff --git a/src/git/git_credentials.inc.c b/src/git/git_credentials.inc.c index 89230bc..3199559 100644 --- a/src/git/git_credentials.inc.c +++ b/src/git/git_credentials.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/git/git_credentials.inc.c + * @brief Cleaf git credentials module. + */ + static char *remote_host_for_repo(const char *repo) { CleafGitResult result; if (!run_git_args(repo, NULL, &result, "remote", "get-url", "origin", NULL)) { @@ -24,6 +29,9 @@ static char *remote_host_for_repo(const char *repo) { return host; } +/** + * @brief Credential dialog. + */ static char *credential_dialog(EditorWindow *win, char **protocol_out, char **host_out, @@ -160,6 +168,9 @@ void action_git_credentials(GtkWidget *widget, gpointer user_data) { g_free(protocol); g_free(host); g_free(username); g_free(secret); g_free(helper); } +/** + * @brief Action git refresh. + */ void action_git_refresh(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; @@ -167,6 +178,9 @@ void action_git_refresh(GtkWidget *widget, gpointer user_data) { app_window_set_status(win, "Git status refreshed."); } +/** + * @brief Action git run. + */ void action_git_run(GtkWidget *widget, gpointer user_data) { (void)widget; EditorWindow *win = user_data; diff --git a/src/index/index_candidates.inc.c b/src/index/index_candidates.inc.c index 7b7eb04..0150658 100644 --- a/src/index/index_candidates.inc.c +++ b/src/index/index_candidates.inc.c @@ -1,5 +1,13 @@ +/** + * @file src/index/index_candidates.inc.c + * @brief Cleaf index candidates module. + */ + static void collect_include_paths_from_text(GPtrArray *paths, GHashTable *seen, EditorTab *tab, const char *text, guint depth); +/** + * @brief Add include candidate. + */ static void add_include_candidate(GPtrArray *paths, GHashTable *seen, EditorTab *tab, const char *name, gboolean system_include) { if (!paths || !seen || !tab || !name || name[0] == '\0') return; if (!system_include && tab->file_path) { @@ -28,6 +36,9 @@ static void add_include_candidate(GPtrArray *paths, GHashTable *seen, EditorTab } } +/** + * @brief Collect include paths from text. + */ static void collect_include_paths_from_text(GPtrArray *paths, GHashTable *seen, EditorTab *tab, const char *text, guint depth) { if (!paths || !seen || !tab || !text || depth > CLEAF_INDEX_MAX_INCLUDE_DEPTH) return; const char *p = text; @@ -70,12 +81,18 @@ static void collect_include_paths_from_text(GPtrArray *paths, GHashTable *seen, } } +/** + * @brief Sort strings. + */ static gint sort_strings(gconstpointer a, gconstpointer b) { const char *sa = *(char * const *)a; const char *sb = *(char * const *)b; return g_ascii_strcasecmp(sa ? sa : "", sb ? sb : ""); } +/** + * @brief Index candidates for tab. + */ GPtrArray *index_candidates_for_tab(EditorTab *tab, const char *prefix, guint max_results) { if (max_results == 0u) max_results = 64u; GPtrArray *out = g_ptr_array_new_with_free_func(g_free); diff --git a/src/index/index_collect.inc.c b/src/index/index_collect.inc.c index b8b8549..e3e4505 100644 --- a/src/index/index_collect.inc.c +++ b/src/index/index_collect.inc.c @@ -1,12 +1,23 @@ +/** + * @file src/index/index_collect.inc.c + * @brief Cleaf index collect module. + */ + static gboolean ascii_word_start(char ch) { return g_ascii_isalpha(ch) || ch == '_'; } +/** + * @brief Ascii word char. + */ static gboolean ascii_word_char(char ch) { return g_ascii_isalnum(ch) || ch == '_'; } +/** + * @brief Tab text. + */ static char *tab_text(EditorTab *tab) { if (!tab || !tab->buffer) return NULL; GtkTextIter start; @@ -15,6 +26,9 @@ static char *tab_text(EditorTab *tab) { return gtk_text_buffer_get_text(tab->buffer, &start, &end, FALSE); } +/** + * @brief Has word boundary. + */ static gboolean has_word_boundary(const char *line, const char *word) { if (!line || !word || word[0] == '\0') return FALSE; const char *p = line; @@ -55,6 +69,9 @@ static gboolean read_small_file(const char *path, char **out_text) { return TRUE; } +/** + * @brief Add unique. + */ static void add_unique(GPtrArray *out, GHashTable *seen, const char *word, const char *prefix, guint max_results) { if (!out || !seen || !word || !prefix) return; if (out->len >= max_results) return; @@ -67,6 +84,9 @@ static void add_unique(GPtrArray *out, GHashTable *seen, const char *word, const g_ptr_array_add(out, g_strdup(word)); } +/** + * @brief Collect identifiers. + */ static void collect_identifiers(GPtrArray *out, GHashTable *seen, const char *text, const char *prefix, guint max_results) { if (!out || !seen || !text || !prefix) return; const char *p = text; @@ -83,6 +103,9 @@ static void collect_identifiers(GPtrArray *out, GHashTable *seen, const char *te } } +/** + * @brief Collect c declarations. + */ static void collect_c_declarations(GPtrArray *out, GHashTable *seen, const char *text, const char *prefix, guint max_results) { if (!text) return; const char *p = text; @@ -143,6 +166,9 @@ static void collect_c_declarations(GPtrArray *out, GHashTable *seen, const char } } +/** + * @brief Add file path. + */ static gboolean add_file_path(GPtrArray *paths, GHashTable *seen, const char *path) { if (!paths || !seen || !path) return FALSE; char *canonical = g_canonicalize_filename(path, NULL); @@ -156,6 +182,9 @@ static gboolean add_file_path(GPtrArray *paths, GHashTable *seen, const char *pa return TRUE; } +/** + * @brief Collect project files rec. + */ static void collect_project_files_rec(GPtrArray *paths, GHashTable *seen, const char *dir, guint depth, GPtrArray *syntaxes) { if (!paths || !seen || !dir || depth > 8u || paths->len >= CLEAF_INDEX_MAX_PROJECT_FILES) return; GDir *gdir = g_dir_open(dir, 0, NULL); @@ -176,6 +205,9 @@ static void collect_project_files_rec(GPtrArray *paths, GHashTable *seen, const g_dir_close(gdir); } +/** + * @brief Collect project files for window. + */ static void collect_project_files_for_window(GPtrArray *paths, GHashTable *seen, EditorWindow *win) { @@ -187,6 +219,9 @@ static void collect_project_files_for_window(GPtrArray *paths, } } +/** + * @brief Find in projects. + */ static char *find_in_projects(EditorWindow *win, const char *basename) { if (!win || !basename) return NULL; GPtrArray *paths = g_ptr_array_new_with_free_func(g_free); diff --git a/src/index/index_references.inc.c b/src/index/index_references.inc.c index 2d8bff0..a3a4bd0 100644 --- a/src/index/index_references.inc.c +++ b/src/index/index_references.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/index/index_references.inc.c + * @brief Cleaf index references module. + */ + static char *definition_in_text(const char *path_label, const char *text, const char *word) { if (!text || !word || word[0] == '\0') return NULL; const char *p = text; @@ -30,6 +35,9 @@ static char *definition_in_text(const char *path_label, const char *text, const return NULL; } +/** + * @brief Index definition for word. + */ char *index_definition_for_word(EditorTab *tab, const char *word) { if (!tab || !word || strlen(word) < 2u) return NULL; char *text = tab_text(tab); @@ -57,6 +65,9 @@ char *index_definition_for_word(EditorTab *tab, const char *word) { return def; } +/** + * @brief Index reference free. + */ void index_reference_free(gpointer data) { IndexReference *ref = data; if (!ref) return; @@ -67,6 +78,9 @@ void index_reference_free(gpointer data) { g_free(ref); } +/** + * @brief Relative display path. + */ static char *relative_display_path(EditorTab *tab, const char *path) { if (!path) return g_strdup("current buffer"); const char *root = tab && tab->win ? project_root_for_path(tab->win, path) : NULL; @@ -78,11 +92,17 @@ static char *relative_display_path(EditorTab *tab, const char *path) { return g_filename_display_name(path); } +/** + * @brief Language for path. + */ static const char *language_for_path(EditorTab *tab, const char *path) { if (!path) return "Buffer"; return syntax_language_for_path(tab && tab->win ? tab->win->syntaxes : NULL, path); } +/** + * @brief Line looks definition. + */ static gboolean line_looks_definition(const char *trim, const char *word) { if (!trim || !word || word[0] == '\0') return FALSE; gsize wlen = strlen(word); @@ -101,6 +121,9 @@ static gboolean line_looks_definition(const char *trim, const char *word) { return FALSE; } +/** + * @brief Seen reference. + */ static gboolean seen_reference(GHashTable *seen, const char *path, guint line) { char *key = g_strdup_printf("%s:%u", path ? path : "", line); gboolean found = g_hash_table_contains(seen, key); @@ -109,6 +132,9 @@ static gboolean seen_reference(GHashTable *seen, const char *path, guint line) { return found; } +/** + * @brief Add reference. + */ static void add_reference(GPtrArray *out, GHashTable *seen, EditorTab *tab, const char *path, guint line, const char *snippet, const char *kind, guint max_results) { if (!out || !seen || !snippet || out->len >= max_results) return; if (seen_reference(seen, path, line)) return; @@ -122,6 +148,9 @@ static void add_reference(GPtrArray *out, GHashTable *seen, EditorTab *tab, cons g_ptr_array_add(out, ref); } +/** + * @brief Collect references from text. + */ static void collect_references_from_text(GPtrArray *out, GHashTable *seen, EditorTab *tab, const char *path, const char *text, const char *word, guint max_results, gboolean definitions_first) { if (!out || !text || !word) return; const char *p = text; @@ -144,6 +173,9 @@ static void collect_references_from_text(GPtrArray *out, GHashTable *seen, Edito } } +/** + * @brief Index references for word. + */ GPtrArray *index_references_for_word(EditorTab *tab, const char *word, guint max_results) { if (max_results == 0u) max_results = 20u; GPtrArray *out = g_ptr_array_new_with_free_func(index_reference_free); diff --git a/src/project/project_context.inc.c b/src/project/project_context.inc.c index fd9f497..986bd7f 100644 --- a/src/project/project_context.inc.c +++ b/src/project/project_context.inc.c @@ -1,8 +1,16 @@ +/** + * @file src/project/project_context.inc.c + * @brief Cleaf project context module. + */ + static gboolean project_path_is_expanded(EditorWindow *win, const char *path) { return win && win->project_expanded && path && g_hash_table_contains(win->project_expanded, path); } +/** + * @brief Project set expanded. + */ static void project_set_expanded(EditorWindow *win, const char *path, gboolean expanded) { @@ -22,6 +30,9 @@ static void project_set_expanded(EditorWindow *win, } } +/** + * @brief Row label. + */ static GtkWidget *row_label(const char *text, const char *css_class) { GtkWidget *label = gtk_label_new(text ? text : ""); gtk_label_set_xalign(GTK_LABEL(label), 0.0f); @@ -31,6 +42,9 @@ static GtkWidget *row_label(const char *text, const char *css_class) { return label; } +/** + * @brief Close project folder for path. + */ static void close_project_folder_for_path(EditorWindow *win, const char *path) { if (!win || !win->project_roots || !path) return; @@ -69,6 +83,9 @@ static void close_project_folder_for_path(EditorWindow *win, const char *path) { g_free(root_copy); } +/** + * @brief Project context open. + */ static void project_context_open(GtkWidget *widget, gpointer user_data) { (void)widget; ProjectAction *action = user_data; @@ -84,6 +101,9 @@ static void project_context_open(GtkWidget *widget, gpointer user_data) { (void)app_window_open_file(action->win, action->path); } +/** + * @brief Project context close folder. + */ static void project_context_close_folder(GtkWidget *widget, gpointer user_data) { (void)widget; ProjectAction *action = user_data; @@ -91,6 +111,9 @@ static void project_context_close_folder(GtkWidget *widget, gpointer user_data) close_project_folder_for_path(action->win, action->path); } +/** + * @brief Project context toggle lock. + */ static void project_context_toggle_lock(GtkWidget *widget, gpointer user_data) { (void)widget; ProjectAction *action = user_data; @@ -109,12 +132,18 @@ static void project_context_toggle_lock(GtkWidget *widget, gpointer user_data) { g_free(base); } +/** + * @brief Invalid new name. + */ static gboolean invalid_new_name(const char *name) { if (!name || name[0] == '\0') return TRUE; if (g_strcmp0(name, ".") == 0 || g_strcmp0(name, "..") == 0) return TRUE; return strchr(name, G_DIR_SEPARATOR) != NULL; } +/** + * @brief Project context rename. + */ static void project_context_rename(GtkWidget *widget, gpointer user_data) { (void)widget; ProjectAction *action = user_data; @@ -187,6 +216,9 @@ static void project_context_rename(GtkWidget *widget, gpointer user_data) { g_free(old_base); } +/** + * @brief Project context button. + */ static GtkWidget *project_context_button(const char *label, GCallback callback, EditorWindow *win, @@ -200,6 +232,9 @@ static GtkWidget *project_context_button(const char *label, return button; } +/** + * @brief Project context popover closed. + */ static void project_context_popover_closed(GtkPopover *popover, gpointer user_data) { GtkWidget *owner = user_data; @@ -214,6 +249,9 @@ static void project_context_popover_closed(GtkPopover *popover, cleaf_widget_destroy(GTK_WIDGET(popover)); } +/** + * @brief Project context popover close for list. + */ static void project_context_popover_close_for_list(EditorWindow *win) { if (!win || !win->project_list) return; GtkWidget *old_popover = g_object_get_data(G_OBJECT(win->project_list), @@ -226,6 +264,9 @@ static void project_context_popover_close_for_list(EditorWindow *win) { } } +/** + * @brief On project row right click. + */ static void on_project_row_right_click(GtkGestureClick *gesture, int n_press, double x, diff --git a/src/project/project_rows.inc.c b/src/project/project_rows.inc.c index 3580ee2..415cdd2 100644 --- a/src/project/project_rows.inc.c +++ b/src/project/project_rows.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/project/project_rows.inc.c + * @brief Cleaf project rows module. + */ + static GtkWidget *project_icon_widget_for_path(const char *path, gboolean is_dir) { GPtrArray *candidates = project_icon_candidates_for_path(path, is_dir); @@ -12,7 +17,7 @@ static GtkWidget *project_icon_widget_for_path(const char *path, gtk_widget_set_halign(icon, GTK_ALIGN_CENTER); gtk_widget_set_valign(icon, GTK_ALIGN_CENTER); gtk_picture_set_can_shrink(GTK_PICTURE(icon), TRUE); - gtk_picture_set_keep_aspect_ratio(GTK_PICTURE(icon), TRUE); + gtk_picture_set_content_fit(GTK_PICTURE(icon), GTK_CONTENT_FIT_CONTAIN); GdkDisplay *display = gdk_display_get_default(); GtkIconTheme *theme = display ? gtk_icon_theme_get_for_display(display) : NULL; @@ -49,6 +54,9 @@ static GtkWidget *project_icon_widget_for_path(const char *path, return icon; } +/** + * @brief Git status css class. + */ static const char *git_status_css_class(const char *status) { if (!status || status[0] == '\0') return NULL; switch (status[0]) { @@ -64,6 +72,9 @@ static const char *git_status_css_class(const char *status) { } +/** + * @brief Append project row. + */ static void append_project_row(ProjectBuild *build, const char *path, guint depth) { @@ -140,6 +151,9 @@ static void append_project_row(ProjectBuild *build, g_free(base); } +/** + * @brief Add visible path. + */ static void add_visible_path(ProjectBuild *build, const char *path, guint depth) { @@ -166,20 +180,32 @@ static void add_visible_path(ProjectBuild *build, g_ptr_array_free(names, TRUE); } +/** + * @brief Project root count. + */ guint project_root_count(EditorWindow *win) { if (!win || !win->project_roots) return 0u; return win->project_roots->len; } +/** + * @brief Project root at. + */ const char *project_root_at(EditorWindow *win, guint index) { if (!win || !win->project_roots || index >= win->project_roots->len) return NULL; return g_ptr_array_index(win->project_roots, index); } +/** + * @brief Project has roots. + */ gboolean project_has_roots(EditorWindow *win) { return project_root_count(win) > 0u; } +/** + * @brief Project root for path. + */ const char *project_root_for_path(EditorWindow *win, const char *path) { if (!win || !path) return NULL; const char *best = NULL; @@ -203,6 +229,9 @@ const char *project_root_for_path(EditorWindow *win, const char *path) { return best; } +/** + * @brief Project root exists. + */ static gboolean project_root_exists(EditorWindow *win, const char *canonical) { if (!canonical) return FALSE; guint count = project_root_count(win); diff --git a/src/project/project_tree.inc.c b/src/project/project_tree.inc.c index 4130f78..6830104 100644 --- a/src/project/project_tree.inc.c +++ b/src/project/project_tree.inc.c @@ -1,8 +1,16 @@ +/** + * @file src/project/project_tree.inc.c + * @brief Cleaf project tree module. + */ + typedef struct { - GtkWidget *scrolled; - gdouble value; + GtkWidget *scrolled; /**< Scrolled. */ + gdouble value; /**< Value. */ } ProjectScrollRestore; +/** + * @brief Project tree scrolled window. + */ static GtkWidget *project_tree_scrolled_window(EditorWindow *win) { GtkWidget *widget = win ? win->project_list : NULL; while (widget) { @@ -12,6 +20,9 @@ static GtkWidget *project_tree_scrolled_window(EditorWindow *win) { return NULL; } +/** + * @brief Project restore scroll cb. + */ static gboolean project_restore_scroll_cb(gpointer user_data) { ProjectScrollRestore *restore = user_data; if (!restore || !restore->scrolled) { @@ -30,6 +41,9 @@ static gboolean project_restore_scroll_cb(gpointer user_data) { return G_SOURCE_REMOVE; } +/** + * @brief Project restore scroll later. + */ static void project_restore_scroll_later(GtkWidget *scrolled, gdouble value) { if (!scrolled) return; ProjectScrollRestore *restore = g_new0(ProjectScrollRestore, 1); @@ -38,6 +52,9 @@ static void project_restore_scroll_later(GtkWidget *scrolled, gdouble value) { g_idle_add_full(G_PRIORITY_LOW, project_restore_scroll_cb, restore, NULL); } +/** + * @brief Project tree rebuild. + */ static void project_tree_rebuild(EditorWindow *win) { if (!win || !win->project_list) return; @@ -69,6 +86,9 @@ static void project_tree_rebuild(EditorWindow *win) { project_restore_scroll_later(scrolled, scroll_value); } +/** + * @brief On project row activated. + */ static void on_project_row_activated(GtkListBox *list_box, GtkListBoxRow *row, gpointer user_data) { @@ -89,6 +109,9 @@ static void on_project_row_activated(GtkListBox *list_box, (void)app_window_open_file(win, data->path); } +/** + * @brief Section label. + */ static GtkWidget *section_label(const char *text) { GtkWidget *label = gtk_label_new(text ? text : ""); gtk_label_set_xalign(GTK_LABEL(label), 0.0f); @@ -99,6 +122,9 @@ static GtkWidget *section_label(const char *text) { return label; } +/** + * @brief Project tree create. + */ GtkWidget *project_tree_create(EditorWindow *win) { if (!win) return NULL; @@ -132,14 +158,23 @@ GtkWidget *project_tree_create(EditorWindow *win) { return box; } +/** + * @brief Project tree close context. + */ void project_tree_close_context(EditorWindow *win) { project_context_popover_close_for_list(win); } +/** + * @brief Project tree refresh. + */ void project_tree_refresh(EditorWindow *win) { project_tree_rebuild(win); } +/** + * @brief Project tree clear. + */ void project_tree_clear(EditorWindow *win) { if (!win) return; project_context_popover_close_for_list(win); @@ -149,6 +184,9 @@ void project_tree_clear(EditorWindow *win) { project_tree_rebuild(win); } +/** + * @brief Project tree load folder. + */ void project_tree_load_folder(EditorWindow *win, const char *folder_path) { if (!win || !folder_path || !g_file_test(folder_path, G_FILE_TEST_IS_DIR)) { diff --git a/src/project/project_util.inc.c b/src/project/project_util.inc.c index 6148ea3..716e6a5 100644 --- a/src/project/project_util.inc.c +++ b/src/project/project_util.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/project/project_util.inc.c + * @brief Cleaf project util module. + */ + static void project_row_free(gpointer data) { ProjectRow *row = data; if (!row) return; @@ -5,6 +10,9 @@ static void project_row_free(gpointer data) { g_free(row); } +/** + * @brief Project action free. + */ static void project_action_free(gpointer data) { ProjectAction *action = data; if (!action) return; @@ -12,6 +20,9 @@ static void project_action_free(gpointer data) { g_free(action); } +/** + * @brief Project action new. + */ static ProjectAction *project_action_new(EditorWindow *win, const char *path, gboolean is_dir) { @@ -22,6 +33,9 @@ static ProjectAction *project_action_new(EditorWindow *win, return action; } +/** + * @brief Compare names. + */ static gint compare_names(gconstpointer a, gconstpointer b) { const char *sa = *(char * const *)a; const char *sb = *(char * const *)b; @@ -34,6 +48,9 @@ static gint compare_names(gconstpointer a, gconstpointer b) { return g_ascii_strcasecmp(sa ? sa : "", sb ? sb : ""); } +/** + * @brief Should skip name. + */ static gboolean should_skip_name(const char *name) { static const char *skip[] = { ".git", ".cache", ".venv", ".cleaf-backups", ".cleaf-autosave", ".cleaf-latex-build", @@ -47,6 +64,9 @@ static gboolean should_skip_name(const char *name) { return FALSE; } +/** + * @brief Sorted dir names. + */ static GPtrArray *sorted_dir_names(const char *path) { GDir *dir = g_dir_open(path, 0, NULL); if (!dir) return NULL; @@ -74,6 +94,9 @@ static GPtrArray *sorted_dir_names(const char *path) { return names; } +/** + * @brief Add icon candidate. + */ static void add_icon_candidate(GPtrArray *candidates, const char *name) { if (!candidates || !name || name[0] == '\0') return; @@ -84,6 +107,9 @@ static void add_icon_candidate(GPtrArray *candidates, const char *name) { g_ptr_array_add(candidates, g_strdup(name)); } +/** + * @brief Add icon candidates from gicon. + */ static void add_icon_candidates_from_gicon(GPtrArray *candidates, GIcon *icon) { if (!candidates || !icon) return; @@ -95,6 +121,9 @@ static void add_icon_candidates_from_gicon(GPtrArray *candidates, GIcon *icon) { } } +/** + * @brief Add extension icon candidates. + */ static void add_extension_icon_candidates(GPtrArray *candidates, const char *path) { if (!candidates || !path) return; @@ -163,6 +192,9 @@ static void add_extension_icon_candidates(GPtrArray *candidates, const char *pat } } +/** + * @brief Project icon candidates for path. + */ static GPtrArray *project_icon_candidates_for_path(const char *path, gboolean is_dir) { GPtrArray *candidates = g_ptr_array_new_with_free_func(g_free); diff --git a/src/ui/ui_base_css.inc.c b/src/ui/ui_base_css.inc.c index 4f955ef..f53253f 100644 --- a/src/ui/ui_base_css.inc.c +++ b/src/ui/ui_base_css.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/ui/ui_base_css.inc.c + * @brief Cleaf ui base css module. + */ + void cleaf_apply_css(void) { static const char *css_chunks[] = { "window.cleaf-window," diff --git a/src/ui/ui_editor_css.inc.c b/src/ui/ui_editor_css.inc.c index 08042eb..8e44bcb 100644 --- a/src/ui/ui_editor_css.inc.c +++ b/src/ui/ui_editor_css.inc.c @@ -1,3 +1,8 @@ +/** + * @file src/ui/ui_editor_css.inc.c + * @brief Cleaf ui editor css module. + */ + static void append_bg_rule(GString *css, const char *selector, const char *color) { if (!css || !selector || !color || color[0] != '#') return; @@ -6,6 +11,9 @@ static void append_bg_rule(GString *css, const char *selector, selector, color, color); } +/** + * @brief Append fg rule. + */ static void append_fg_rule(GString *css, const char *selector, const char *color) { if (!css || !selector || !color || color[0] != '#') return; diff --git a/tests/unit_tests.c b/tests/unit_tests.c index b075548..92128c8 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1,9 +1,24 @@ +/** + * @file unit_tests.c + * @brief Unit tests for protocol serialization and syntax diagnostics helpers. + * + * These tests intentionally avoid constructing GTK windows. They exercise + * deterministic utility code that can run in a normal headless process. + */ + #include #include #include "../src/codex_protocol.h" #include "../src/syntax.h" +/** + * @brief Verifies that a Codex protocol request survives serialize/parse. + * + * The test builds a small JSON-RPC style request, serializes it to the + * line-oriented protocol format, parses it back, and checks the id, method, + * and params payload. + */ static void test_codex_protocol_request_round_trip(void) { JsonNode *params = codex_protocol_object_params(); JsonObject *params_object = json_node_get_object(params); @@ -32,6 +47,12 @@ static void test_codex_protocol_request_round_trip(void) { json_node_free(params); } +/** + * @brief Verifies malformed JSON is rejected with a JSON parser error. + * + * This covers the negative path for the protocol parser so callers can rely on + * a false return value and a populated GError when the input line is invalid. + */ static void test_codex_protocol_rejects_invalid_json(void) { JsonNode *root = NULL; GError *error = NULL; @@ -43,6 +64,13 @@ static void test_codex_protocol_rejects_invalid_json(void) { g_clear_error(&error); } +/** + * @brief Verifies syntax diagnostics describe an empty syntax registry. + * + * The diagnostics text should still be useful when no syntax definitions are + * loaded, so the test checks that the output contains the standard heading and + * the explicit "none" marker. + */ static void test_syntax_diagnostics_empty(void) { GPtrArray *syntaxes = g_ptr_array_new(); char *text = syntax_diagnostics(syntaxes); @@ -55,6 +83,13 @@ static void test_syntax_diagnostics_empty(void) { g_ptr_array_unref(syntaxes); } +/** + * @brief Verifies syntax diagnostics summarize a populated syntax definition. + * + * A synthetic SyntaxDef is assembled in memory and passed to the diagnostics + * formatter. The test checks visible summary fields such as name, icon, + * completion count, extensions, and index status. + */ static void test_syntax_diagnostics_summary(void) { GPtrArray *syntaxes = g_ptr_array_new(); GPtrArray *extensions = g_ptr_array_new(); @@ -96,6 +131,13 @@ static void test_syntax_diagnostics_summary(void) { g_ptr_array_unref(line_close_pairs); } +/** + * @brief Registers and runs the unit test suite. + * + * @param argc Command-line argument count supplied by the test runner. + * @param argv Command-line argument vector supplied by the test runner. + * @return The GLib test runner exit status. + */ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); g_test_add_func("/codex/protocol/request_round_trip", test_codex_protocol_request_round_trip);