Refactored AsciiDoc export more, escape in titles.

Fixes #1.
This commit is contained in:
tastytea 2019-06-06 16:51:13 +02:00
parent 98bb87ed93
commit 17b33eacbf
Signed by: tastytea
GPG Key ID: CFC39497F1B26E07
2 changed files with 179 additions and 151 deletions

View File

@ -20,7 +20,6 @@
#include <locale>
#include <curlpp/cURLpp.hpp>
#include "version.hpp"
#include "time.hpp"
#include "export.hpp"
using std::cerr;
@ -29,186 +28,206 @@ using std::regex;
using std::regex_replace;
using tagpair = std::pair<string,vector<Database::entry>>;
namespace Export
void Export::AsciiDoc::print() const
{
void AsciiDoc::print() const
try
{
try
{
_out << "= Visited things\n"
<< ":Author: remwharead " << global::version << endl
<< ":Date: "
<< timepoint_to_string(system_clock::now()) << endl
<< ":TOC: right" << endl
<< ":TOCLevels: 2" << endl
<< endl;
_out << "= Visited things\n"
<< ":Author: remwharead " << global::version << endl
<< ":Date: "
<< timepoint_to_string(system_clock::now()) << endl
<< ":TOC: right" << endl
<< ":TOCLevels: 2" << endl
<< endl;
std::map<string,vector<Database::entry>> alltags;
string day;
for (const Database::entry &entry : _entries)
tagmap alltags;
string day;
for (const Database::entry &entry : _entries)
{
const string newday = get_day(entry);
if (newday != day)
{
const string datetime = timepoint_to_string(entry.datetime);
const string newday = datetime.substr(0, datetime.find('T'));
const string time = datetime.substr(datetime.find('T') + 1);
if (newday != day)
day = newday;
_out << "== " << day << endl << endl;
}
_out << "[[dt_" << timepoint_to_string(entry.datetime) << "]]\n";
_out << "* link:" << entry.uri;
if (!entry.title.empty())
{
_out << '[' << replace_in_title(entry.title) << ']';
}
else
{
_out << "[]";
}
_out << " +" << endl;
_out << '_' << get_time(entry).substr(0, 5) << '_';
if (!entry.archive_uri.empty())
{
_out << " (link:" << entry.archive_uri << "[archived version])";
}
bool separator = false;
for (const string &tag : entry.tags)
{
if (tag.empty())
{
day = newday;
_out << "== " << day << endl << endl;
continue;
}
if (!separator)
{
_out << "\n| ";
separator = true;
}
_out << "[[dt_" << datetime << "]]\n";
_out << "* link:" << entry.uri;
if (!entry.title.empty())
auto globaltag = alltags.find(tag);
if (globaltag != alltags.end())
{
_out << '[' << entry.title << ']';
globaltag->second.push_back(entry);
}
else
{
_out << "[]";
alltags.insert({ tag, { entry } });
}
_out << " +" << endl;
_out << '_' << time.substr(0, 5) << '_';
if (!entry.archive_uri.empty())
_out << "xref:t_" << replace_in_tag(tag) << "[" << tag << ']';
if (tag != *(entry.tags.rbegin()))
{
_out << " (link:" << entry.archive_uri << "[archived version])";
_out << ", ";
}
bool separator = false;
for (const string &tag : entry.tags)
{
if (tag.empty())
{
continue;
}
if (!separator)
{
_out << "\n| ";
separator = true;
}
auto globaltag = alltags.find(tag);
if (globaltag != alltags.end())
{
globaltag->second.push_back(entry);
}
else
{
alltags.insert({ tag, { entry } });
}
_out << "xref:t_" << replace_in_tags(tag) << "[" << tag << ']';
if (tag != *(entry.tags.rbegin()))
{
_out << ", ";
}
}
if (!entry.description.empty())
{
_out << " +" << endl << entry.description;
}
_out << endl << endl;
}
if (!alltags.empty())
if (!entry.description.empty())
{
print_tags(alltags);
_out << " +" << endl << entry.description;
}
_out << endl << endl;
}
catch (std::exception &e)
if (!alltags.empty())
{
cerr << "Error in " << __func__ << ": " << e.what() << endl;
print_tags(alltags);
}
}
const string AsciiDoc::replace_in_tags(string text) const
catch (std::exception &e)
{
// TODO: Find a better solution.
const std::map<const string, const string> searchreplace =
{
{ " ", "-" }, { "§", "-" },
{ "$", "-" }, { "%", "-" },
{ "&", "-" }, { "/", "-" },
{ "=", "-" }, { "^", "-" },
{ "!", "-" }, { "?", "-" },
{ "", "0" }, { "", "0" },
{ "", "1" }, { "¹", "1" },
{ "", "2" }, { "²", "2" },
{ "", "3" }, { "³", "3" },
{ "", "4" }, { "", "4" },
{ "", "5" }, { "", "5" },
{ "", "6" }, { "", "6" },
{ "", "7" }, { "", "7" },
{ "", "8" }, { "", "8" },
{ "", "9" }, { "", "9" }
};
for (const std::pair<const string, const string> &sr : searchreplace)
{
size_t pos = 0;
while ((pos = text.find(sr.first, pos)) != std::string::npos)
{
text.replace(pos, sr.first.length(), sr.second);
}
}
return text;
cerr << "Error in " << __func__ << ": " << e.what() << endl;
}
}
void AsciiDoc::print_tags(
const std::map<string,vector<Database::entry>> &tags) const
const string Export::AsciiDoc::replace(string text,
const replacemap &replacements) const
{
for (const std::pair<const string, const string> &sr : replacements)
{
_out << "== Tags\n\n";
vector<tagpair> sortedtags(tags.size());
std::move(tags.begin(), tags.end(), sortedtags.begin());
std::sort(sortedtags.begin(), sortedtags.end(),
[](const tagpair &a, tagpair &b)
{
if (a.second.size() != b.second.size())
{ // Sort by number of occurrences if they are different.
return a.second.size() > b.second.size();
}
else
{ // Sort by tag names otherwise.
std::locale loc;
const std::collate<char> &coll =
std::use_facet<std::collate<char>>(loc);
return (coll.compare(
a.first.data(), a.first.data()
+ a.first.length(),
b.first.data(), b.first.data()
+ b.first.length()) == -1);
}
});
bool othertags = false;
for (const auto &tag : sortedtags)
size_t pos = 0;
while ((pos = text.find(sr.first, pos)) != std::string::npos)
{
if (tag.second.size() == 1)
{
if (!othertags)
{
_out << "=== Less used tags\n\n";
othertags = true;
}
_out << "=";
}
text.replace(pos, sr.first.length(), sr.second);
pos += sr.second.length();
}
}
return text;
}
const string Export::AsciiDoc::replace_in_tag(const string &text) const
{
// TODO: Find a better solution.
const replacemap replacements =
{
{ " ", "-" }, { "§", "-" },
{ "$", "-" }, { "%", "-" },
{ "&", "-" }, { "/", "-" },
{ "=", "-" }, { "^", "-" },
{ "!", "-" }, { "?", "-" },
{ "", "0" }, { "", "0" },
{ "", "1" }, { "¹", "1" },
{ "", "2" }, { "²", "2" },
{ "", "3" }, { "³", "3" },
{ "", "4" }, { "", "4" },
{ "", "5" }, { "", "5" },
{ "", "6" }, { "", "6" },
{ "", "7" }, { "", "7" },
{ "", "8" }, { "", "8" },
{ "", "9" }, { "", "9" }
};
_out << "=== [[t_" << replace_in_tags(tag.first) << "]]"
<< tag.first << endl;
for (const Database::entry &entry : tag.second)
return replace(text, replacements);
}
const string Export::AsciiDoc::replace_in_title(const string &text) const
{
// [ is implicitly escaped if the corresponding ] is.
return replace(text, {{ "]", "\\]" }});
}
void Export::AsciiDoc::print_tags(const tagmap &tags) const
{
_out << "== Tags\n\n";
vector<tagpair> sortedtags(tags.size());
std::move(tags.begin(), tags.end(), sortedtags.begin());
std::sort(sortedtags.begin(), sortedtags.end(),
[](const tagpair &a, tagpair &b)
{
if (a.second.size() != b.second.size())
{ // Sort by number of occurrences if they are different.
return a.second.size() > b.second.size();
}
else
{ // Sort by tag names otherwise.
std::locale loc;
const std::collate<char> &coll =
std::use_facet<std::collate<char>>(loc);
return (coll.compare(
a.first.data(), a.first.data()
+ a.first.length(),
b.first.data(), b.first.data()
+ b.first.length()) == -1);
}
});
bool othertags = false;
for (const auto &tag : sortedtags)
{
if (tag.second.size() == 1)
{
if (!othertags)
{
const string datetime = timepoint_to_string(entry.datetime);
const string date = datetime.substr(0, datetime.find('T'));
string title = entry.title;
if (title.empty())
{
title = "++" + entry.uri + "++";
}
_out << endl << "* xref:dt_" << datetime
<< '[' << title << "] _(" << date << ")_" << endl;
_out << "=== Less used tags\n\n";
othertags = true;
}
_out << endl;
_out << "=";
}
_out << "=== [[t_" << replace_in_tag(tag.first) << "]]"
<< tag.first << endl;
for (const Database::entry &entry : tag.second)
{
const string datetime = timepoint_to_string(entry.datetime);
const string date = datetime.substr(0, datetime.find('T'));
string title = replace_in_title(entry.title);
if (title.empty())
{
title = "++" + entry.uri + "++";
}
_out << endl << "* xref:dt_" << datetime
<< '[' << title << "] _(" << date << ")_" << endl;
}
_out << endl;
}
_out << endl;
}
const string Export::AsciiDoc::get_day(const Database::entry &entry) const
{
const string datetime = timepoint_to_string(entry.datetime);
return datetime.substr(0, datetime.find('T'));
}
const string Export::AsciiDoc::get_time(const Database::entry &entry) const
{
const string datetime = timepoint_to_string(entry.datetime);
return datetime.substr(datetime.find('T') + 1);
}

View File

@ -20,6 +20,7 @@
#include <vector>
#include <iostream>
#include <map>
#include "time.hpp"
#include "sqlite.hpp"
using std::vector;
@ -59,9 +60,17 @@ namespace Export
void print() const;
private:
using tagmap = std::map<string,vector<Database::entry>>;
using replacemap = const std::map<const string, const string>;
const string replace(string text, const replacemap &replacements) const;
//! Replaces characters in tags that asciidoctor doesn't like.
const string replace_in_tags(string text) const;
void print_tags(const std::map<string,vector<Database::entry>> &tags) const;
const string replace_in_tag(const string &text) const;
//! Replaces characters in title that asciidoctor doesn't like.
const string replace_in_title(const string &text) const;
void print_tags(const tagmap &tags) const;
const string get_day(const Database::entry &entry) const;
const string get_time(const Database::entry &entry) const;
};
}