Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ build/
.zig-cache/
git_check.sh
.graptos/
.graptos-task.ini

# Github actions
.github/
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
APP_NAME := graptos
APP_ID := io.github.graptos.Editor
VERSION := 0.23.74
VERSION := 0.23.82
PREFIX ?= /usr/local
CC ?= cc
BUILD_DIR := build
Expand Down Expand Up @@ -130,7 +130,7 @@ check: $(VERSION_HEADER)
test: $(BUILD_DIR)/unit_tests
$<

$(BUILD_DIR)/unit_tests: $(TEST_DIR)/unit_tests.c $(SRC_DIR)/codex_protocol.c $(SRC_DIR)/syntax_diagnostics.c $(SRC_DIR)/project_init.c $(SRC_DIR)/formatter.c $(SRC_DIR)/formatter_lexer.c $(SRC_DIR)/formatter_layout.c $(SRC_DIR)/formatter_spacing.c $(SRC_DIR)/formatter_scope.c $(SRC_DIR)/syntax.c $(SRC_DIR)/editor_notes.c $(SRC_DIR)/plugin.c | $(BUILD_DIR)
$(BUILD_DIR)/unit_tests: $(TEST_DIR)/unit_tests.c $(SRC_DIR)/codex_protocol.c $(SRC_DIR)/syntax_diagnostics.c $(SRC_DIR)/project_init.c $(SRC_DIR)/formatter.c $(SRC_DIR)/formatter_lexer.c $(SRC_DIR)/formatter_layout.c $(SRC_DIR)/formatter_spacing.c $(SRC_DIR)/formatter_scope.c $(SRC_DIR)/syntax.c $(SRC_DIR)/syntax_loader.c $(SRC_DIR)/editor_notes.c $(SRC_DIR)/plugin.c | $(BUILD_DIR)
$(CC) $(CPPFLAGS) -DGRAPTOS_PLUGIN_NO_UI $(CFLAGS) $(WARNINGS) $(GTK_SYSTEM_CFLAGS) -std=c11 $^ $(GTK_LIBS) -o $@

smoke-test: $(BUILD_DIR)/smoke_window
Expand Down
72 changes: 72 additions & 0 deletions data/plugins/beancount-tools/beancount_tools.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* @file beancount_tools.c
* @brief Native plugin entry point for Beancount ledger editing.
* @details Feature modules are included into one translation unit so plugin
* builds can select modules with compile-time flags while still
* producing one shared object.
*/

#include "beancount_tools_config.h"

#include "beancount_tools_common.c"

#if BEANCOUNT_TOOLS_ENABLE_COMPLETION
#include "beancount_tools_completion.c"
#endif

#if BEANCOUNT_TOOLS_ENABLE_HOVER
#include "beancount_tools_hover.c"
#endif

#if BEANCOUNT_TOOLS_ENABLE_COMMANDS
#include "beancount_tools_commands.c"
#endif

#if BEANCOUNT_TOOLS_ENABLE_REPORT
#include "beancount_tools_report.c"
#endif

/**
* @brief Register this plugin with Graptoς.
* @param host Host capability object supplied by Graptoς.
* @return TRUE when registration succeeds.
*/
gboolean graptos_plugin_register(GraptosPluginHost *host) {
if (graptos_plugin_host_api_version(host) != GRAPTOS_PLUGIN_API_VERSION) return FALSE;
gboolean t_done = TRUE;
#if BEANCOUNT_TOOLS_ENABLE_COMPLETION
t_done = graptos_plugin_host_register_completion_provider(host,
"beancount-completion",
"Beancount",
beancount_completion_candidates,
NULL,
NULL) && t_done;
t_done = graptos_plugin_host_register_editor_line_command_with_shortcut(host,
"beancount-complete",
"Beancount Complete",
"Ctrl+Alt+B",
show_beancount_completion,
NULL,
NULL) && t_done;
#endif
#if BEANCOUNT_TOOLS_ENABLE_HOVER
t_done = graptos_plugin_host_register_hover_provider(host,
"beancount-account-balance",
"Beancount balance",
account_balance_hover,
NULL,
NULL) && t_done;
#endif
#if BEANCOUNT_TOOLS_ENABLE_COMMANDS
t_done = graptos_plugin_host_register_editor_line_command(host, "beancount-validate", "Validate Ledger", validate_ledger, NULL, NULL) && t_done;
t_done = graptos_plugin_host_register_editor_line_command(host, "beancount-query", "Query Selection", query_selection, NULL, NULL) && t_done;
t_done = graptos_plugin_host_register_editor_line_command(host, "beancount-summary", "Ledger Summary", ledger_summary, NULL, NULL) && t_done;
t_done = graptos_plugin_host_register_editor_line_command(host, "beancount-format", "Format Ledger", format_ledger, NULL, NULL) && t_done;
t_done = graptos_plugin_host_register_editor_line_command(host, "beancount-context", "Account Context", account_context, NULL, NULL) && t_done;
#endif
#if BEANCOUNT_TOOLS_ENABLE_REPORT
t_done = graptos_plugin_host_register_editor_line_command(host, "beancount-balance-preview", "Balance Sheet Preview", balance_sheet_preview, NULL, NULL) && t_done;
t_done = graptos_plugin_host_register_editor_line_command(host, "beancount-balance-export", "Export Balance Sheet PDF", export_balance_sheet_pdf, NULL, NULL) && t_done;
#endif
return t_done;
}
181 changes: 181 additions & 0 deletions data/plugins/beancount-tools/beancount_tools_commands.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
/**
* @brief Publish bean-check diagnostics from tool output.
* @param context Command context supplied by Graptoς.
* @param output Combined bean-check output.
*/
static void publish_check_diagnostics(GraptosPluginCommandContext *context,
const char *output) {
graptos_plugin_context_clear_diagnostics(context);
char **lines = g_strsplit(output ? output : "", "\n", -1);
for (guint i = 0u; lines && lines[i]; i++) {
const char *p = lines[i];
while ((p = strchr(p, ':')) != NULL) {
p++;
if (!g_ascii_isdigit(*p)) continue;
guint line = 0u;
while (g_ascii_isdigit(*p)) {
line = line * 10u + (guint)(*p - '0');
p++;
}
if (*p == ':' && line > 0u) {
graptos_plugin_context_add_line_diagnostic(context, line, lines[i]);
break;
}
}
}
g_strfreev(lines);
}

/**
* @brief Run bean-check and publish line diagnostics.
* @param context Command context supplied by Graptoς.
* @param user_data Plugin data supplied during registration.
*/
static void validate_ledger(GraptosPluginCommandContext *context, gpointer user_data) {
(void)user_data;
char *path = saved_beancount_path(context, "Validate Ledger");
if (!path) return;
if (!g_find_program_in_path("bean-check")) {
graptos_plugin_context_show_output(context, "Validate Ledger", "bean-check Missing", "Install Beancount to use bean-check.");
g_free(path);
return;
}
char *cwd = g_path_get_dirname(path);
char *argv[] = { "bean-check", path, NULL };
char *stdout_text = NULL;
char *stderr_text = NULL;
gint status = 0;
(void)run_tool(cwd, argv, &stdout_text, &stderr_text, &status);
char *combined = g_strconcat(stdout_text ? stdout_text : "", stderr_text ? stderr_text : "", NULL);
publish_check_diagnostics(context, combined);
char *body = status == 0 && (!combined || !combined[0])
? g_strdup("bean-check passed.")
: format_tool_output("bean-check", status, stdout_text, stderr_text);
graptos_plugin_context_show_output(context, "Validate Ledger", "Bean Check", body);
g_free(body);
g_free(combined);
g_free(stdout_text);
g_free(stderr_text);
g_free(cwd);
g_free(path);
}

/**
* @brief Run a bean-query command.
* @param context Command context supplied by Graptoς.
* @param query Beancount query text.
* @param title Dialog title.
*/
static void run_query(GraptosPluginCommandContext *context,
const char *query,
const char *title) {
char *path = saved_beancount_path(context, title);
if (!path) return;
if (!g_find_program_in_path("bean-query")) {
graptos_plugin_context_show_output(context, title, "bean-query Missing", "Install Beancount to use bean-query.");
g_free(path);
return;
}
char *cwd = g_path_get_dirname(path);
char *argv[] = { "bean-query", path, (char *)query, NULL };
char *stdout_text = NULL;
char *stderr_text = NULL;
gint status = 0;
(void)run_tool(cwd, argv, &stdout_text, &stderr_text, &status);
char *body = format_tool_output(query, status, stdout_text, stderr_text);
graptos_plugin_context_show_output(context, title, "Bean Query", body);
g_free(body);
g_free(stdout_text);
g_free(stderr_text);
g_free(cwd);
g_free(path);
}

/**
* @brief Query the selection or fall back to balances.
* @param context Command context supplied by Graptoς.
* @param user_data Plugin data supplied during registration.
*/
static void query_selection(GraptosPluginCommandContext *context, gpointer user_data) {
(void)user_data;
char *selection = graptos_plugin_context_selection(context);
g_strstrip(selection);
run_query(context, selection && selection[0] ? selection : "BALANCES", "Query Selection");
g_free(selection);
}

/**
* @brief Show a compact ledger balance summary.
* @param context Command context supplied by Graptoς.
* @param user_data Plugin data supplied during registration.
*/
static void ledger_summary(GraptosPluginCommandContext *context, gpointer user_data) {
(void)user_data;
run_query(context, "BALANCES", "Ledger Summary");
}

/**
* @brief Replace the editor text with bean-format output.
* @param context Command context supplied by Graptoς.
* @param user_data Plugin data supplied during registration.
*/
static void format_ledger(GraptosPluginCommandContext *context, gpointer user_data) {
(void)user_data;
char *path = saved_beancount_path(context, "Format Ledger");
if (!path) return;
if (!g_find_program_in_path("bean-format")) {
graptos_plugin_context_show_output(context, "Format Ledger", "bean-format Missing", "Install Beancount to use bean-format.");
g_free(path);
return;
}
char *cwd = g_path_get_dirname(path);
char *argv[] = { "bean-format", path, NULL };
char *stdout_text = NULL;
char *stderr_text = NULL;
gint status = 0;
(void)run_tool(cwd, argv, &stdout_text, &stderr_text, &status);
if (status == 0 && stdout_text && stdout_text[0]) {
if (graptos_plugin_context_replace_text(context, stdout_text)) {
graptos_plugin_context_set_status(context, "Beancount formatted");
}
} else {
char *body = format_tool_output("bean-format", status, stdout_text, stderr_text);
graptos_plugin_context_show_output(context, "Format Ledger", "Bean Format", body);
g_free(body);
}
g_free(stdout_text);
g_free(stderr_text);
g_free(cwd);
g_free(path);
}

/**
* @brief Show bean-doctor context for the active line.
* @param context Command context supplied by Graptoς.
* @param user_data Plugin data supplied during registration.
*/
static void account_context(GraptosPluginCommandContext *context, gpointer user_data) {
(void)user_data;
char *path = saved_beancount_path(context, "Account Context");
if (!path) return;
if (!g_find_program_in_path("bean-doctor")) {
graptos_plugin_context_show_output(context, "Account Context", "bean-doctor Missing", "Install Beancount to use bean-doctor.");
g_free(path);
return;
}
char *line = g_strdup_printf("%u", graptos_plugin_context_line(context));
char *cwd = g_path_get_dirname(path);
char *argv[] = { "bean-doctor", "context", path, line, NULL };
char *stdout_text = NULL;
char *stderr_text = NULL;
gint status = 0;
(void)run_tool(cwd, argv, &stdout_text, &stderr_text, &status);
char *body = format_tool_output("bean-doctor context", status, stdout_text, stderr_text);
graptos_plugin_context_show_output(context, "Account Context", "Bean Doctor", body);
g_free(body);
g_free(stdout_text);
g_free(stderr_text);
g_free(cwd);
g_free(line);
g_free(path);
}
Loading