#include "gemtext.h" #include namespace roro::gemini { namespace { std::string trim(const std::string& s) { size_t a = s.find_first_not_of(" \t"), b = s.find_last_not_of(" \t"); return a == std::string::npos ? "" : s.substr(a, b - a + 1); } } // namespace GemLine parseGemLine(const std::string& line, bool& pre) { GemLine g; if (line.compare(0, 3, "```") == 0) { g.type = LineType::PreToggle; g.text = trim(line.substr(3)); pre = !pre; } else if (pre) { g.type = LineType::Preformatted; g.text = line; } else if (line.compare(0, 2, "=>") == 0) { g.type = LineType::Link; std::string rest = trim(line.substr(2)); size_t gap = rest.find_first_of(" \t"); g.url = rest.substr(0, gap); g.text = gap == std::string::npos ? "" : trim(rest.substr(gap)); if (g.text.empty()) g.text = g.url; } else if (line.compare(0, 3, "###") == 0) { g.type = LineType::Heading3; g.text = trim(line.substr(3)); } else if (line.compare(0, 2, "##") == 0) { g.type = LineType::Heading2; g.text = trim(line.substr(2)); } else if (line.compare(0, 1, "#") == 0) { g.type = LineType::Heading1; g.text = trim(line.substr(1)); } else if (line.compare(0, 2, "* ") == 0) { g.type = LineType::ListItem; g.text = trim(line.substr(2)); } else if (line.compare(0, 1, ">") == 0) { g.type = LineType::Quote; g.text = trim(line.substr(1)); } else { g.text = line; } return g; } std::vector parseGemtext(const std::string& document) { std::vector lines; bool pre = false; size_t start = 0; while (start < document.size()) { size_t end = document.find('\n', start); if (end == std::string::npos) end = document.size(); std::string line = document.substr(start, end - start); if (!line.empty() && line.back() == '\r') line.pop_back(); start = end + 1; lines.push_back(parseGemLine(line, pre)); } return lines; } std::string displayText(const std::string& s) { std::string out; size_t column = 0; for (size_t i = 0; i < s.size();) { unsigned char c = static_cast(s[i]); if (c == '\t') { do out += ' '; while (++column % 4); i++; continue; } if (c < 0x80) { out += static_cast(c); i++; } else { int len = (c & 0xE0) == 0xC0 ? 2 : (c & 0xF0) == 0xE0 ? 3 : (c & 0xF8) == 0xF0 ? 4 : 0; bool valid = len > 0 && i + len <= s.size(); for (int k = 1; valid && k < len; k++) valid = (static_cast(s[i + k]) & 0xC0) == 0x80; if (!valid) { out += '?'; i++; } else { uint32_t cp = len == 2 ? (c & 0x1F) : len == 3 ? (c & 0x0F) : (c & 0x07); for (int k = 1; k < len; k++) cp = (cp << 6) | (static_cast(s[i + k]) & 0x3F); if (cp <= 0xFF) out.append(s, i, len); // Latin-1: the fonts have it else out += '?'; i += len; } } column++; } return out; } } // namespace roro::gemini