#include "ecex.h" #define MD_TEXT_TITLE 1 typedef struct md_state { ecex_plugin_t *plugin; } md_state_t; static int md_line_is_fence(const char *line) { const char *p = line; if (!p) return 0; while (*p == ' ' || *p == '\t') ++p; return p[0] == '`' && p[1] == '`' && p[2] == '`'; } static int md_in_code_at_line(buffer_t *buffer, int line) { char scratch[256]; int in_code = 0; int i; if (!buffer || line <= 0) return 0; for (i = 0; i < line; ++i) { if (ecex_buffer_line_copy_text(buffer, i, scratch, (int)sizeof(scratch)) < 0) continue; if (md_line_is_fence(scratch)) in_code = !in_code; } return in_code; } static int md_render(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, void *userdata) { md_state_t *state = (md_state_t *)userdata; int draw_count; int line_count; int scroll; int y; int h; int line_h; int in_code = 0; int i; if (!ed || !buffer || !ctx || !state) return 0; draw_count = ecex_plugin_slot_i32_get_scalar(state->plugin, "draw_count", 0) + 1; ecex_plugin_slot_i32_set_scalar(state->plugin, "draw_count", draw_count); if (draw_count <= 2 || (draw_count % 120) == 0) { ecex_log_int("markdown_plugin_draw: count=", draw_count); } line_count = ecex_buffer_line_count_int(buffer); ecex_log_int("markdown_plugin_draw: line_count=", line_count); if (line_count < 0) line_count = 0; ecex_plugin_text_set_from_buffer_title(state->plugin, MD_TEXT_TITLE, buffer); ecex_draw_markdown_canvas_auto_i(ctx, state->plugin, MD_TEXT_TITLE); y = ecex_markdown_body_y_px(ctx); h = ecex_draw_context_height_px(ctx); line_h = ecex_draw_context_line_height_px(ctx); scroll = ecex_buffer_scroll_line_index(buffer); if (scroll < 0) scroll = 0; if (scroll > line_count) scroll = line_count; in_code = md_in_code_at_line(buffer, scroll); for (i = scroll; i < line_count && y < h - line_h; ++i) { int packed; int advance; if (i < scroll + 4) ecex_log_int("markdown_plugin_draw: host line=", i); packed = ecex_markdown_draw_buffer_line_i(ctx, state->plugin, buffer, i, y, in_code); in_code = (packed & 0x10000) ? 1 : 0; advance = packed & 0xffff; if (advance <= 0) advance = line_h; y += advance; } return 0; } static void md_free_state(void *userdata) { md_state_t *state = (md_state_t *)userdata; if (!state) return; ecex_plugin_text_free_all(state->plugin); ecex_plugin_object_free(state->plugin, state); } static int md_view_buffer(ecex_t *ed, buffer_t *buffer) { md_state_t *state; if (!ed || !buffer) return -1; if (ecex_buffer_has_renderer(buffer)) ecex_buffer_clear_renderer(buffer); ecex_plugin_t *plugin = ecex_plugin_find(ed, "markdown"); state = (md_state_t *)ecex_plugin_object_calloc(plugin, "state", 1, sizeof(*state)); if (!state) return -1; state->plugin = plugin; if (ecex_buffer_set_renderer(buffer, md_render, state, md_free_state, ECEX_RENDER_REPLACE_CONTENT) != 0) { ecex_plugin_object_free(plugin, state); return -1; } ecex_buffer_set_major_mode_by_name(ed, buffer, "markdown-mode"); return 0; } static int md_file_handler(ecex_t *ed, buffer_t *buffer) { if (!buffer) return -1; if (ecex_buffer_has_renderer(buffer)) return 0; return md_view_buffer(ed, buffer); } static int cmd_markdown_view(ecex_t *ed) { return md_view_buffer(ed, ecex_current_buffer(ed)); } static int cmd_markdown_source(ecex_t *ed) { buffer_t *buffer = ecex_current_buffer(ed); if (!buffer) return -1; if (ecex_buffer_has_renderer(buffer)) ecex_buffer_clear_renderer(buffer); ecex_buffer_set_major_mode_by_name(ed, buffer, "markdown-mode"); return 0; } static int cmd_markdown_toggle(ecex_t *ed) { buffer_t *buffer = ecex_current_buffer(ed); if (!buffer) return -1; if (ecex_buffer_has_renderer(buffer)) return cmd_markdown_source(ed); return md_view_buffer(ed, buffer); } ECEX_PLUGIN_BEGIN(ecex_markdown_plugin, "markdown") ECEX_CONFIG_MODE("markdown-mode"); ECEX_CONFIG_COMMAND("markdown-view", cmd_markdown_view); ECEX_CONFIG_COMMAND("markdown-source", cmd_markdown_source); ECEX_CONFIG_COMMAND("markdown-toggle", cmd_markdown_toggle); ECEX_CONFIG_MODE_BIND("markdown-mode", "C-c C-c", "markdown-toggle"); ECEX_CONFIG_MODE_BIND("markdown-mode", "C-c C-s", "markdown-source"); ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".md", md_file_handler)); ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".markdown", md_file_handler)); ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".mdown", md_file_handler)); ECEX_CONFIG_TRY(ecex_plugin_file_handler_register(plugin, ".mkd", md_file_handler)); return 0; ECEX_PLUGIN_END #ifndef ECEX_NO_STANDALONE_CONFIG ECEX_CONFIG_BEGIN ECEX_CONFIG_INCLUDE(ecex_markdown_plugin); ECEX_CONFIG_END #endif