Assembled from klammertext-dev f344a37d772c. The commands, the library,
the Standard Klammer Set, the reflection klammerset and the Python
binding build and run natively on Windows (MSYS2 UCRT64 gcc, python.org
Python), and both test tiers pass there. doc/install/windows_source_install.md
is the fifth install guide: a source install parallel to the Linux and
macOS ones, verified by following it on a pristine Windows 11.
Behaviour on every system: paths Klammertext prints use forward slashes;
output is LF; the command line is UTF-8 on Windows; @eval :shell is a
POSIX shell command everywhere -- on Windows it runs the sh Klammertext
ships, BusyBox for Windows (win/busybox/, GPLv2, with its licence and the
source pointer). KLAMMERTEXT_FONTS and KLAMMERTEXT_KLAMMERSETS are lists in
the system's PATH convention (';' on Windows). On Linux and macOS nothing
observable changes.
223 lines
7.4 KiB
C++
223 lines
7.4 KiB
C++
#include <regex>
|
|
|
|
#include "util.h"
|
|
#include "file.h"
|
|
#include "html_util.h"
|
|
#include "heading.h"
|
|
#include "log.h"
|
|
#include "show.h"
|
|
|
|
std::ostream& operator<<(std::ostream& os, const Heading h)
|
|
{
|
|
os << h.m_level << " "
|
|
<< h.m_filename << " "
|
|
<< h.m_section << " "
|
|
<< h.m_id << " "
|
|
<< h.m_number << " "
|
|
<< h.m_title;
|
|
return os;
|
|
}
|
|
|
|
std::string levels_to_section(std::vector<unsigned int> levels)
|
|
{
|
|
(void)K::log(3);
|
|
std::string result {};
|
|
auto count = levels.size();
|
|
for (unsigned int i = 0; i < count; i++) {
|
|
if (levels[i] == 0)
|
|
break;
|
|
result += std::to_string(levels[i]);
|
|
if (i < count - 1)
|
|
result += ".";
|
|
}
|
|
if (count > 1)
|
|
result = result.substr(0, result.size()-1);
|
|
return result;
|
|
}
|
|
|
|
int part_number = 1;
|
|
int unnumbered_id = 0;
|
|
|
|
std::string make_html_table_of_contents(std::vector<Heading> headings)
|
|
{
|
|
elements_t toc { html::elt("h1", "Contents") };
|
|
for (auto [level, filename, section, id, number, title] : headings) {
|
|
toc.push_back(
|
|
html::elt("div",
|
|
html::elt("a", {
|
|
html::elt("span", section).attr("class", "level_number"),
|
|
title })
|
|
.attr("href", "^#" + id)
|
|
.attr("class", "level"))
|
|
.attr("class", "level" + std::to_string(level)));
|
|
}
|
|
//auto toc_elt = html::elt("div", toc).attr("id", "toc");
|
|
std::string result { "<div id=\"pagetoc\">\n" + to_string(toc) + "\n</div>\n" };
|
|
return result;
|
|
}
|
|
|
|
std::string toc_link_attrs(
|
|
const std::string& section, const std::string& title, const std::string& filename, const std::string& target, int level)
|
|
{
|
|
std::string basename = file_basename(filename);
|
|
std::stringstream ss {};
|
|
ss << "class=\"navlink\" data-page=\"" << basename
|
|
<< "\" data-target=\"" << target << "\">\n<span class=\"section-title\" level=\"" << level << "\">\n";
|
|
if (section.size() > 0) {
|
|
ss << "<span class=\"section-number\">" << section << "</span>\n";
|
|
}
|
|
ss << "<span class=\"section-title-text\">" << title << "</span>\n"
|
|
<< "</span>\n";
|
|
return ss.str();
|
|
}
|
|
|
|
|
|
std::pair<int, std::string>
|
|
make_navigation_table_of_contents(std::vector<Heading> headings)
|
|
{
|
|
std::stringstream toc {};
|
|
long unsigned int i = 1;
|
|
//int max_level = 0;
|
|
//int last_level = 0;
|
|
int max_level = 1;
|
|
int last_level = 1;
|
|
|
|
toc << "<ul id=\"toc_items\">\n";
|
|
int min_level = 7;
|
|
for (auto h : headings) {
|
|
min_level = std::min(h.m_level, min_level);
|
|
}
|
|
|
|
bool unnumbered_start = headings.size() > 0 ? headings[0].m_section.empty() : false;
|
|
if (unnumbered_start) { // Unnumbered first section (preface, for example)
|
|
toc << "<ul>\n";
|
|
}
|
|
for (auto [item_level, filename, section, id, number, title] : headings) {
|
|
// msg() << " level: " << item_level << "; filename: " << filename << "; section: " << section
|
|
// << "; id: " << id << "; number: " << number << "; title: " << title << "\n";
|
|
|
|
bool promote_unnumbered = unnumbered_start && min_level == 0 and section.empty();
|
|
if (promote_unnumbered) {
|
|
item_level--;
|
|
}
|
|
std::string basename = file_basename(filename);
|
|
max_level = std::max(max_level, item_level);
|
|
//int next_level = i < headings.size() ? headings[i].m_level : 0;
|
|
|
|
int next_level = i < headings.size() ? headings[i].m_level : 1;
|
|
|
|
if (promote_unnumbered) {
|
|
next_level = std::max(0, next_level - 1);
|
|
}
|
|
|
|
// std::cout << "|" << item_level << "|" << section << "|" << id << "|"
|
|
// << title << "| " << "next: " << next_level << "\n";
|
|
|
|
std::string link = toc_link_attrs(section, title, basename, id, item_level);
|
|
|
|
if (next_level == item_level) {
|
|
toc << "<li " << link << "</li>\n";
|
|
/*
|
|
} else if (next_level > item_level) {
|
|
toc << "<li><span class=\"caret\" data-level=\"" << item_level << "\">"
|
|
<< "<span " << link<< "</span></span>\n"
|
|
<< "<ul class=\"nested\">\n";
|
|
*/
|
|
} else if (next_level > item_level) {
|
|
toc << "<li><span class=\"caret\" data-level=\"" << item_level << "\"></span>\n"
|
|
<< "<span " << link<< "</span>\n"
|
|
<< "<ul class=\"nested";
|
|
/*
|
|
if (section.size() == 0) {
|
|
toc << " unnumbered";
|
|
}
|
|
*/
|
|
toc << "\">\n";
|
|
} else {
|
|
//toc << "<li " << link << "</li>\n</ul>\n</li>\n";
|
|
toc << "<li " << link << "</li>\n";
|
|
/*
|
|
if (unnumbered_start) {
|
|
next_level += 1;
|
|
}
|
|
*/
|
|
int offset = unnumbered_start ? 1 : 0;
|
|
//int offset = unnumbered_start ? 3 : 2;
|
|
offset = 0;
|
|
|
|
for (int lev = item_level; lev > next_level + offset; lev--) {
|
|
toc << "</ul>\n</li>\n";
|
|
}
|
|
unnumbered_start = false;
|
|
}
|
|
i++;
|
|
last_level = item_level;
|
|
}
|
|
toc << "</ul>\n";
|
|
for (int j = 0; j < last_level - 2; j++) {
|
|
toc << "</li>\n</ul>\n";
|
|
}
|
|
return {max_level, toc.str()};
|
|
}
|
|
|
|
void show_table_of_contents(
|
|
const std::string& kt_root_filename, const std::string& title, std::vector<Heading> headings)
|
|
{
|
|
std::size_t width = kt_root_filename.size();
|
|
for (Heading h : headings) {
|
|
width = std::max(width, h.m_filename.size());
|
|
}
|
|
width += 2;
|
|
int tab = 2;
|
|
std::string current_filename = "";
|
|
std::string bar = " | ";
|
|
std::cout << "\n" << std::string(width - kt_root_filename.size(), ' ')
|
|
<< kt_root_filename << bar << title << "\n";
|
|
for (auto [level, filename, section, id, number, htitle] : headings) {
|
|
std::string f = "";
|
|
if (filename != current_filename) {
|
|
f = std::string(width - filename.size(), ' ') + filename + bar;
|
|
current_filename = filename;
|
|
} else {
|
|
f = std::string(width, ' ') + bar;
|
|
}
|
|
std::cout << f << std::string((level-1) * tab, ' ') << section << " " << htitle << "\n";
|
|
}
|
|
std::cout << "\n";
|
|
}
|
|
|
|
std::string cache_table_of_contents(
|
|
const std::string& kt_root_filename, const std::string& title, std::vector<Heading> headings)
|
|
{
|
|
std::size_t width = kt_root_filename.size();
|
|
for (Heading h : headings) {
|
|
width = std::max(width, h.m_filename.size());
|
|
}
|
|
width += 2;
|
|
int tab = 2;
|
|
std::string current_filename = "";
|
|
std::string bar = " | ";
|
|
std::stringstream ss {};
|
|
ss << "\n" << std::string(width - kt_root_filename.size(), ' ')
|
|
<< kt_root_filename << bar << title << "\n";
|
|
for (auto [level, filename, section, id, number, htitle] : headings) {
|
|
std::string f = "";
|
|
if (filename != current_filename) {
|
|
f = std::string(width - filename.size(), ' ') + filename + bar;
|
|
current_filename = filename;
|
|
} else {
|
|
f = std::string(width, ' ') + bar;
|
|
}
|
|
ss << f << std::string((level) * tab, ' ');
|
|
if (section.size() > 0) {
|
|
ss << section << " ";
|
|
}
|
|
ss << htitle << "\n";
|
|
}
|
|
ss << "\n";
|
|
std::string cache_filename =
|
|
cache_directory(kt_root_filename, "_html_pages") + "/toc.txt";
|
|
string_to_file(cache_filename, ss.str());
|
|
return cache_filename;
|
|
}
|