aboutsummaryrefslogtreecommitdiff
path: root/config/markdown_plugin.c
blob: 1026d31a418410393cee77c163b968b2a77777a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#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