181 lines
7.5 KiB
C
181 lines
7.5 KiB
C
|
|
// retoor <retoor@molodetz.nl>
|
||
|
|
#include "markdown.h"
|
||
|
|
#include <ctype.h>
|
||
|
|
#include <stdbool.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
// --- ANSI Escape Codes ---
|
||
|
|
#define RESET "\033[0m"
|
||
|
|
#define BOLD "\033[1m"
|
||
|
|
#define ITALIC "\033[3m"
|
||
|
|
#define STRIKETHROUGH "\033[9m"
|
||
|
|
#define FG_YELLOW "\033[33m"
|
||
|
|
#define FG_BLUE "\033[34m"
|
||
|
|
#define FG_CYAN "\033[36m"
|
||
|
|
#define FG_MAGENTA "\033[35m"
|
||
|
|
#define BG_YELLOW_FG_BLACK "\033[43;30m"
|
||
|
|
/**
|
||
|
|
* @brief Checks if a given word is a programming language keyword.
|
||
|
|
*/
|
||
|
|
static int is_keyword(const char *word) {
|
||
|
|
const char *keywords[] = {
|
||
|
|
"int", "float", "double", "char", "void", "if", "else", "while", "for",
|
||
|
|
"return", "struct", "printf", "let", "fn", "impl", "match", "enum", "trait", "use", "mod", "pub",
|
||
|
|
"const", "static", "def", "class", "import", "from", "as", "with", "try", "except",
|
||
|
|
"finally", "lambda", "async", "await", "public", "private", "protected", "interface", "extends",
|
||
|
|
"implements", "new", "synchronized", "var", "switch", "case", "break", "continue",
|
||
|
|
"namespace", "template", "typename", "virtual", "override", "friend", "package", "func", "type", "go", "defer", "select",
|
||
|
|
"then", "elif", "fi", "esac", "do", "done", "using"};
|
||
|
|
for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
|
||
|
|
if (strcmp(word, keywords[i]) == 0) return 1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
void highlight_code(const char *code) {
|
||
|
|
const char *ptr = code;
|
||
|
|
char buffer[4096];
|
||
|
|
size_t index = 0;
|
||
|
|
while (*ptr) {
|
||
|
|
if (isalpha((unsigned char)*ptr) || *ptr == '_') {
|
||
|
|
while (isalnum((unsigned char)*ptr) || *ptr == '_') {
|
||
|
|
if (index < sizeof(buffer) - 1) buffer[index++] = *ptr++;
|
||
|
|
else ptr++;
|
||
|
|
}
|
||
|
|
buffer[index] = '\0';
|
||
|
|
if (is_keyword(buffer)) printf(FG_BLUE "%s" RESET FG_YELLOW, buffer);
|
||
|
|
else printf("%s", buffer);
|
||
|
|
index = 0;
|
||
|
|
} else if (isdigit((unsigned char)*ptr)) {
|
||
|
|
while (isdigit((unsigned char)*ptr)) {
|
||
|
|
if (index < sizeof(buffer) - 1) buffer[index++] = *ptr++;
|
||
|
|
else ptr++;
|
||
|
|
}
|
||
|
|
buffer[index] = '\0';
|
||
|
|
printf(FG_CYAN "%s" RESET FG_YELLOW, buffer);
|
||
|
|
index = 0;
|
||
|
|
} else {
|
||
|
|
putchar(*ptr);
|
||
|
|
ptr++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
void parse_markdown_to_ansi(const char *markdown) {
|
||
|
|
const char *ptr = markdown;
|
||
|
|
bool is_start_of_line = true;
|
||
|
|
while (*ptr) {
|
||
|
|
if (is_start_of_line && strncmp(ptr, "```", 3) == 0) {
|
||
|
|
ptr += 3;
|
||
|
|
while (*ptr && *ptr != '\n') ptr++;
|
||
|
|
if (*ptr) ptr++;
|
||
|
|
const char *code_start = ptr;
|
||
|
|
const char *code_end = strstr(code_start, "```");
|
||
|
|
if (code_end) {
|
||
|
|
char *block_buffer = strndup(code_start, (size_t)(code_end - code_start));
|
||
|
|
printf(FG_YELLOW);
|
||
|
|
highlight_code(block_buffer);
|
||
|
|
printf(RESET);
|
||
|
|
free(block_buffer);
|
||
|
|
ptr = code_end + 3;
|
||
|
|
if (*ptr == '\n') ptr++;
|
||
|
|
is_start_of_line = true;
|
||
|
|
continue;
|
||
|
|
} else {
|
||
|
|
printf(FG_YELLOW);
|
||
|
|
highlight_code(code_start);
|
||
|
|
printf(RESET);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (is_start_of_line) {
|
||
|
|
const char *line_start_ptr = ptr;
|
||
|
|
int indent_level = 0;
|
||
|
|
while (*ptr == ' ') { indent_level++; ptr++; }
|
||
|
|
bool block_processed = true;
|
||
|
|
if (strncmp(ptr, "###### ", 7) == 0) { printf(BOLD FG_YELLOW); ptr += 7; }
|
||
|
|
else if (strncmp(ptr, "##### ", 6) == 0) { printf(BOLD FG_YELLOW); ptr += 6; }
|
||
|
|
else if (strncmp(ptr, "#### ", 5) == 0) { printf(BOLD FG_YELLOW); ptr += 5; }
|
||
|
|
else if (strncmp(ptr, "### ", 4) == 0) { printf(BOLD FG_YELLOW); ptr += 4; }
|
||
|
|
else if (strncmp(ptr, "## ", 3) == 0) { printf(BOLD FG_YELLOW); ptr += 3; }
|
||
|
|
else if (strncmp(ptr, "# ", 2) == 0) { printf(BOLD FG_YELLOW); ptr += 2; }
|
||
|
|
else if ((strncmp(ptr, "---", 3) == 0 || strncmp(ptr, "***", 3) == 0) && (*(ptr + 3) == '\n' || *(ptr + 3) == '\0')) {
|
||
|
|
printf(FG_CYAN "─────────────────────────────────────────────────────────────────────────" RESET "\n");
|
||
|
|
ptr += 3; if (*ptr == '\n') ptr++; is_start_of_line = true; continue;
|
||
|
|
} else if (strncmp(ptr, "> ", 2) == 0) {
|
||
|
|
for (int i = 0; i < indent_level; i++) putchar(' ');
|
||
|
|
printf(ITALIC FG_CYAN "â–Ž " RESET); ptr += 2; is_start_of_line = false; continue;
|
||
|
|
} else if ((*ptr == '*' || *ptr == '-' || *ptr == '+') && *(ptr + 1) == ' ') {
|
||
|
|
for (int i = 0; i < indent_level; i++) putchar(' ');
|
||
|
|
printf(FG_MAGENTA "• " RESET); ptr += 2; is_start_of_line = false; continue;
|
||
|
|
} else {
|
||
|
|
const char *temp_ptr = ptr;
|
||
|
|
while (isdigit((unsigned char)*temp_ptr)) temp_ptr++;
|
||
|
|
if (temp_ptr > ptr && *temp_ptr == '.' && *(temp_ptr + 1) == ' ') {
|
||
|
|
for (int i = 0; i < indent_level; i++) putchar(' ');
|
||
|
|
printf(FG_MAGENTA); fwrite(ptr, 1, (size_t)(temp_ptr - ptr) + 1, stdout); printf(" " RESET);
|
||
|
|
ptr = temp_ptr + 2; is_start_of_line = false; continue;
|
||
|
|
} else { block_processed = false; ptr = line_start_ptr; }
|
||
|
|
}
|
||
|
|
if (block_processed) {
|
||
|
|
while (*ptr && *ptr != '\n') putchar(*ptr++);
|
||
|
|
printf(RESET "\n"); if (*ptr == '\n') ptr++;
|
||
|
|
is_start_of_line = true; continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (strncmp(ptr, "***", 3) == 0 || strncmp(ptr, "___", 3) == 0) {
|
||
|
|
const char *marker = strncmp(ptr, "***", 3) == 0 ? "***" : "___";
|
||
|
|
printf(BOLD ITALIC); ptr += 3;
|
||
|
|
const char *end = strstr(ptr, marker);
|
||
|
|
if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 3; }
|
||
|
|
else { fputs(ptr, stdout); ptr += strlen(ptr); }
|
||
|
|
printf(RESET); continue;
|
||
|
|
}
|
||
|
|
if (strncmp(ptr, "**", 2) == 0 || strncmp(ptr, "__", 2) == 0) {
|
||
|
|
const char *marker = strncmp(ptr, "**", 2) == 0 ? "**" : "__";
|
||
|
|
printf(BOLD); ptr += 2;
|
||
|
|
const char *end = strstr(ptr, marker);
|
||
|
|
if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; }
|
||
|
|
else { fputs(ptr, stdout); ptr += strlen(ptr); }
|
||
|
|
printf(RESET); continue;
|
||
|
|
}
|
||
|
|
if (strncmp(ptr, "~~", 2) == 0) {
|
||
|
|
printf(STRIKETHROUGH); ptr += 2;
|
||
|
|
const char *end = strstr(ptr, "~~");
|
||
|
|
if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; }
|
||
|
|
else { fputs(ptr, stdout); ptr += strlen(ptr); }
|
||
|
|
printf(RESET); continue;
|
||
|
|
}
|
||
|
|
if (strncmp(ptr, "==", 2) == 0) {
|
||
|
|
printf(BG_YELLOW_FG_BLACK); ptr += 2;
|
||
|
|
const char *end = strstr(ptr, "==");
|
||
|
|
if (end) { fwrite(ptr, 1, (size_t)(end - ptr), stdout); ptr = end + 2; }
|
||
|
|
else { fputs(ptr, stdout); ptr += strlen(ptr); }
|
||
|
|
printf(RESET); continue;
|
||
|
|
}
|
||
|
|
if (*ptr == '`' && *(ptr + 1) != '`') {
|
||
|
|
printf(FG_YELLOW); ptr++; const char *start = ptr;
|
||
|
|
while (*ptr && *ptr != '`') ptr++;
|
||
|
|
fwrite(start, 1, (size_t)(ptr - start), stdout); if (*ptr == '`') ptr++;
|
||
|
|
printf(RESET); continue;
|
||
|
|
}
|
||
|
|
if (*ptr == '[') {
|
||
|
|
const char *text_start = ptr + 1;
|
||
|
|
const char *text_end = strchr(text_start, ']');
|
||
|
|
if (text_end && *(text_end + 1) == '(') {
|
||
|
|
const char *url_start = text_end + 2;
|
||
|
|
const char *url_end = strchr(url_start, ')');
|
||
|
|
if (url_end) {
|
||
|
|
printf(FG_BLUE); fwrite(text_start, 1, (size_t)(text_end - text_start), stdout);
|
||
|
|
printf(RESET " ("); printf(ITALIC FG_CYAN);
|
||
|
|
fwrite(url_start, 1, (size_t)(url_end - url_start), stdout);
|
||
|
|
printf(RESET ")"); ptr = url_end + 1; continue;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (*ptr == '\n') is_start_of_line = true;
|
||
|
|
else if (!isspace((unsigned char)*ptr)) is_start_of_line = false;
|
||
|
|
putchar(*ptr);
|
||
|
|
ptr++;
|
||
|
|
}
|
||
|
|
}
|