diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2023-02-08 23:44:46 +0100 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2023-02-08 23:44:46 +0100 |
| commit | cc3d9d8d97f40fe81978306cbc4510152fca8ae7 (patch) | |
| tree | 18f565ab6a097087e17bfac20b3827145ff2dcbe /core/mdv.c | |
| parent | 0fe4cdd277dab82b4f0b09ebebace2ce0009bddd (diff) | |
add markdown viewer (mdv)
Diffstat (limited to 'core/mdv.c')
| -rw-r--r-- | core/mdv.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/core/mdv.c b/core/mdv.c new file mode 100644 index 0000000..25b7b28 --- /dev/null +++ b/core/mdv.c @@ -0,0 +1,238 @@ +#include "../lib/io/io.h" +#include "../lib/sys/sizes.h" +#include "../lib/tctl/tctl.h" +#include "../lib/aec/aec.h" + +#define MAX_WIDTH 80 + +#define TOGGLE(mask, type, sgron, sgroff) \ + if (mask & type) { \ + mask ^= type; \ + wstdf("%S", sgroff); \ + } else { \ + mask |= type; \ + wstdf("%S", sgron); \ + } + +u16 heading_numbers[6] = { 0 }; +window_size_t ws; + +enum Style { + ITALIC = 1, + BOLD = 2, + STRIKETROUGH = 4, + MARKED = 8, +}; + + +void next_heading(int type) +{ + heading_numbers[type]++; + + for (int i = type + 1; i < 5; ++i) + heading_numbers[i] = 0; +} + + +void show_box() +{ + char c; + u8 width = 0; + read(STDIN_FD, &c, 1); + read(STDIN_FD, &c, 1); + read(STDIN_FD, &c, 1); + u8 use_backup = 0; + char backup[3]; + + wstd("┌"); + for (int i = 0; i < MAX_WIDTH - 3; ++i) wstd("─"); + wstd("┐\n│ "); + + while (use_backup || read(STDIN_FD, &c, 1)) { + if (use_backup) { + c = backup[3 - use_backup]; + use_backup--; + } + + if (c == '\n') { + for (int i = width + 1; i < MAX_WIDTH - 4; ++i) wstd(" "); + + width = 0; + read(STDIN_FD, backup, 3); + + if (backup[0] == '`' && backup[1] == '`' && backup[2] == '`') + break; + + use_backup = 3; + + wstd(" │\n│ "); + } else { + ++width; + + if (width == MAX_WIDTH - 4) { + wstd(" │\n│ "); + width = 1; + } + + putchar(c); + } + } + + wstd(" │\n└"); + for (int i = 0; i < MAX_WIDTH - 3; ++i) wstd("─"); + wstd("┘\n\n"); +} + + +void show_and_replace_leading_char(char leading, char *replace) +{ + char c; + u8 last_was_newline = 0; + wstdf(" %s", replace); + + while (read(STDIN_FD, &c, 1)) { + if (c == '\n') { + if (last_was_newline) + break; + + last_was_newline = 1; + wstd("\n "); + } else { + last_was_newline = 0; + + if (c == leading) + wstd(replace); + else + putchar(c); + } + } + + wstd("\n"); +} + + +void show_numbered_list() +{ + char c; + u8 last_was_newline = 0; + read(STDIN_FD, &c, 1); + + wstd(" 1."); + + while (read(STDIN_FD, &c, 1)) { + if (c == '\n') { + if (last_was_newline) + break; + + last_was_newline = 1; + wstd("\n "); + } else { + last_was_newline = 0; + putchar(c); + } + } + + wstd("\n"); +} + + +void show_paragraph() +{ + u8 last_was_newline = 0; + char c; + u8 width = 1; + u8 markup_mask = 0; + + while (read(STDIN_FD, &c, 1)) { + if (c == '\n') { + if (last_was_newline) + break; + + last_was_newline = 1; + } else { + last_was_newline = 0; + if (width == MAX_WIDTH) { + wstd("\n"); + width = 0; + } + + switch (c) { + case '*': + read(STDIN_FD, &c, 1); + if (c == '*') { + TOGGLE(markup_mask, BOLD, SGR_BOLD, SGR_INTENSITY_OFF); + } else { + TOGGLE(markup_mask, ITALIC, SGR_ITALIC, SGR_ITALIC_OFF); + + if (c == '\n') { + last_was_newline = 1; + } else { + putchar(c); + } + } + break; + case '~': TOGGLE(markup_mask, STRIKETROUGH, SGR_CROSSED_OUT, SGR_CROSSED_OUT_OFF); break; + case '`': TOGGLE(markup_mask, MARKED, SGR_REVERSE, SGR_REVERSE_OFF); break; + default: putchar(c); break; + } + + ++width; + } + } + + wstdf("\n\n%S", SGR_RESET); +} + + +void show_heading() +{ + u8 heading_type = 1; + char c; + + while (read(STDIN_FD, &c, 1) && c == '#') heading_type++; + + if (heading_type > 5) + heading_type = 5; + + next_heading(heading_type - 1); + + wstdf("%S%i", SGR_FAINT, heading_numbers[0]); + + for (int i = 1; heading_numbers[i]; ++i) + wstdf(".%i", heading_numbers[i]); + + wstdf("%S ", SGR_RESET); + + wstdf("%S", SGR_BOLD); + + if (c != ' ') putchar(c); + + while (read(STDIN_FD, &c, 1) && c != '\n') putchar(c); + wstdf("\n\n%S", SGR_RESET); +} + +int main(int argc, char **argv) +{ + if (argc != 1) { + wf(STDERR_FD, "mdv\n"); + return -1; + } + + ws = tctl_get_window_size(); + + char c; + + while (read(STDIN_FD, &c, 1)) { + switch(c) { + case '\n': break; + case '#': show_heading(); break; + case '1': show_numbered_list(); break; + case '`': show_box(); break; + case '-': show_and_replace_leading_char('-', "•"); break; + case '>': show_and_replace_leading_char('>', "│"); break; + default: putchar(c); show_paragraph(); + } + } + + return 0; +} |