Merge branch 'develop' into main
continuous-integration/drone/push Build is passing Details

This commit is contained in:
tastytea 2019-11-28 09:52:21 +01:00
commit 8b95b43c45
Signed by: tastytea
GPG Key ID: CFC39497F1B26E07
13 changed files with 263 additions and 29 deletions

View File

@ -20,6 +20,7 @@
:uri-ff-addon: https://addons.mozilla.org/firefox/addon/remwharead :uri-ff-addon: https://addons.mozilla.org/firefox/addon/remwharead
:uri-papirus: https://github.com/PapirusDevelopmentTeam/papirus-icon-theme :uri-papirus: https://github.com/PapirusDevelopmentTeam/papirus-icon-theme
:uri-hunter: https://github.com/cpp-pm/hunter :uri-hunter: https://github.com/cpp-pm/hunter
:uri-rofi: https://github.com/davatorium/rofi
*remwharead* saves URIs of things you want to remember in a database along with *remwharead* saves URIs of things you want to remember in a database along with
an URI to the archived version, the current date and time, title, description, an URI to the archived version, the current date and time, title, description,
@ -45,6 +46,12 @@ image::{uri-images-base}/example_tags.png[]
See {uri-branch-main}/man/remwharead.1.adoc[manpage] and/or read See {uri-branch-main}/man/remwharead.1.adoc[manpage] and/or read
{uri-blogpost}[the blogpost]. {uri-blogpost}[the blogpost].
=== With rofi
The link:{uri-rofi}[rofi] export makes integration with rofi simple. See
link:{uri-branch-main}/scripts/remwharead-rofi[scripts/remwharead-rofi] for an
example.
=== In your programs === In your programs
The complete functionality is implemented in a C++ library, libremwharead. Take The complete functionality is implemented in a C++ library, libremwharead. Take

39
include/export/rofi.hpp Normal file
View File

@ -0,0 +1,39 @@
/* This file is part of remwharead.
* Copyright © 2019 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef REMWHAREAD_EXPORT_ROFI_HPP
#define REMWHAREAD_EXPORT_ROFI_HPP
#include "export.hpp"
namespace remwharead::Export
{
/*!
* @brief Export title, tags and URL for consumption by rofi.
*
* @since 0.9.0
*
* @headerfile rofi.hpp remwharead/export/rofi.hpp
*/
class Rofi : protected ExportBase
{
public:
using ExportBase::ExportBase;
void print() const override;
};
} // namespace remwharead::Export
#endif // REMWHAREAD_EXPORT_ROFI_HPP

View File

@ -46,6 +46,8 @@
#include "export/json.hpp" #include "export/json.hpp"
#include "export/rss.hpp" #include "export/rss.hpp"
#include "export/simple.hpp" #include "export/simple.hpp"
#include "export/list.hpp"
#include "export/rofi.hpp"
#include "search.hpp" #include "search.hpp"
#include "sqlite.hpp" #include "sqlite.hpp"
#include "time.hpp" #include "time.hpp"

View File

@ -118,6 +118,14 @@ public:
*/ */
size_t remove(const string &uri); size_t remove(const string &uri);
/*!
* @brief Returns tags as comma separated string.
*
* @since 0.9.0
*/
[[nodiscard]]
static string tags_to_string(const vector<string> &tags);
private: private:
fs::path _dbpath; fs::path _dbpath;
std::unique_ptr<Session> _session; std::unique_ptr<Session> _session;

View File

@ -37,7 +37,8 @@ enum class export_format
simple, simple,
json, json,
rss, rss,
link link,
rofi
}; };
} // namespace remwharead } // namespace remwharead

View File

@ -26,7 +26,8 @@ remwharead - Saves URIs of things you want to remember in a database
the full text of the page and optional tags. the full text of the page and optional tags.
The database can be filtered by time, tags and full text and exported to CSV, The database can be filtered by time, tags and full text and exported to CSV,
AsciiDoc, a bookmarks file, JSON, RSS or a list of hyperlinks. AsciiDoc, a bookmarks file, JSON, RSS, a list of hyperlinks or a rofi-compatible
list.
Archiving is done using the Wayback machine from the Archiving is done using the Wayback machine from the
https://archive.org/[Internet Archive]. https://archive.org/[Internet Archive].
@ -38,7 +39,7 @@ Add tags to _URI_, delimited by commas.
*-e*=_format_, *--export*=_format_:: *-e*=_format_, *--export*=_format_::
Export to _format_. Possible values are _csv_, _asciidoc_, _bookmarks_, Export to _format_. Possible values are _csv_, _asciidoc_, _bookmarks_,
_simple_, _json_, _rss_ or _link_. See _FORMATS_. _simple_, _json_, _rss_, _link_ or _rofi_. See _FORMATS_.
*-f*=_file_, *--file*=_file_:: *-f*=_file_, *--file*=_file_::
Save output to _file_. Default is stdout. Save output to _file_. Default is stdout.
@ -77,32 +78,65 @@ Print version, copyright and license.
.Save a thing into the database, with tags. .Save a thing into the database, with tags.
==== ====
`remwharead -t=tag1,tag2 https://example.com/article.html` [source,shell]
----
remwharead -t=tag1,tag2 https://example.com/article.html
----
==== ====
.Export all things between and including 2019-04-01 and 2019-05-31 to a file. .Export all things between and including 2019-04-01 and 2019-05-31 to a file.
==== ====
`remwharead -e=asciidoc -f=out.adoc -T=2019-04-01,2019-05-31` [source,shell]
----
remwharead -e=asciidoc -f=out.adoc -T=2019-04-01,2019-05-31
----
==== ====
.Export all things to an HTML file. .Export all things to an HTML file.
==== ====
`remwharead -e=asciidoc | asciidoctor --backend=html5 --out-file=out.html -` [source,shell]
----
remwharead -e=asciidoc | asciidoctor --backend=html5 --out-file=out.html -
----
==== ====
.Export all things about GRUB the boot-loader, but nothing about caterpillars. .Export all things about GRUB the boot-loader, but nothing about caterpillars.
==== ====
`remwharead -e=csv -s="grub AND boot"` [source,shell]
----
remwharead -e=csv -s="grub AND boot"
----
==== ====
.Output all articles by Jan Müller, consider different spellings. .Output all articles by Jan Müller, consider different spellings.
==== ====
`remwharead -e=simple -S='Jan[\s]+M(ü|ue?)ller' -r` [source,shell]
----
remwharead -e=simple -S='Jan[\s]+M(ü|ue?)ller' -r
----
==== ====
.Export all things from the last week to an RSS feed. .Export all things from the last week to an RSS feed.
==== ====
`remwharead -e=rss -T=$(date -d "-1 week" -I),$(date -Iminutes) | sed 's|<link/>|<link>https://example.com/</link>|' > /var/www/feed.rss` [source,shell]
----
remwharead -e=rss -T=$(date -d "-1 week" -I),$(date -Iminutes) | sed 's|<link/>|<link>https://example.com/</link>|' > /var/www/feed.rss
----
====
.Remove all entries that are tagged with mountain
====
[source,shell]
----
OLDIFS=${IFS}
IFS=$'\n'
for uri in $(remwharead -e link -s mountain); do
remwharead -d "${uri}"
done
IFS=${OLDIFS}
----
==== ====
=== Display database === Display database
@ -158,6 +192,11 @@ specification (the element _link_ in _channel_ is empty).
Export as a plain list of links, separated by newlines. Export as a plain list of links, separated by newlines.
=== rofi
Export title, tags and URL for consumption by rofi. See the `scripts/` directory
on https://schlomp.space/tastytea/remwharead for an example.
== SEARCH EXPRESSIONS == SEARCH EXPRESSIONS
A search expression is either a single term, or several terms separated by _AND_ A search expression is either a single term, or several terms separated by _AND_

15
scripts/remwharead-rofi Executable file
View File

@ -0,0 +1,15 @@
#!/bin/sh
# Open the whole database in rofi. Searches in title, tags and URI.
# The selected entry will be opened with the default browser.
if [ -n "${2}" ]; then
uri=$(echo "${*}" | sed -E 's|^.+>([^><]+)</span>$|\1|')
xdg-open "${uri}"
exit 0
fi
if [ "${1}" = "runremwharead" ]; then
remwharead -e rofi
else
rofi -show remwharead -modi remwharead:"${0} runremwharead"
fi

View File

@ -20,6 +20,7 @@
#include "export/csv.hpp" #include "export/csv.hpp"
#include "export/json.hpp" #include "export/json.hpp"
#include "export/link.hpp" #include "export/link.hpp"
#include "export/rofi.hpp"
#include "export/rss.hpp" #include "export/rss.hpp"
#include "export/simple.hpp" #include "export/simple.hpp"
#include "search.hpp" #include "search.hpp"
@ -228,6 +229,19 @@ int App::main(const std::vector<std::string> &args)
} }
break; break;
} }
case export_format::rofi:
{
if (file.is_open())
{
Export::Rofi(entries, file).print();
file.close();
}
else
{
Export::Rofi(entries).print();
}
break;
}
default: default:
{ {
break; break;

View File

@ -156,6 +156,10 @@ void App::handle_options(const std::string &name, const std::string &value)
{ {
_format = export_format::link; _format = export_format::link;
} }
else if (value == "rofi")
{
_format = export_format::rofi;
}
else else
{ {
cerr << "Error: Unknown format.\n"; cerr << "Error: Unknown format.\n";

View File

@ -30,19 +30,10 @@ void Export::CSV::print() const
<< R"("Title","Description","Full text")" << "\r\n"; << R"("Title","Description","Full text")" << "\r\n";
for (const Database::entry &entry : _entries) for (const Database::entry &entry : _entries)
{ {
string strtags;
for (const string &tag : entry.tags)
{
strtags += tag;
if (tag != *(entry.tags.rbegin()))
{
strtags += ",";
}
}
_out << '"' << quote(entry.uri) << "\",\"" _out << '"' << quote(entry.uri) << "\",\""
<< quote(entry.archive_uri) << "\",\"" << quote(entry.archive_uri) << "\",\""
<< timepoint_to_string(entry.datetime) << "\",\"" << timepoint_to_string(entry.datetime) << "\",\""
<< quote(strtags) << "\",\"" << quote(Database::tags_to_string(entry.tags)) << "\",\""
<< quote(entry.title) << "\",\"" << quote(entry.title) << "\",\""
<< quote(entry.description) << "\",\"" << quote(entry.description) << "\",\""
<< quote(entry.fulltext_oneline()) << '"'<< "\r\n"; << quote(entry.fulltext_oneline()) << '"'<< "\r\n";

39
src/lib/export/rofi.cpp Normal file
View File

@ -0,0 +1,39 @@
/* This file is part of remwharead.
* Copyright © 2019 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "export/rofi.hpp"
#include "sqlite.hpp"
#include <string>
namespace remwharead
{
using std::string;
void Export::Rofi::print() const
{
_out << static_cast<char>(0x00) << "markup-rows"
<< static_cast<char>(0x1f) << "true\n";
for (const Database::entry & entry : _entries)
{
_out << entry.title
<< R"( <span size="small" weight="light" style="italic">()"
<< Database::tags_to_string(entry.tags) << ")</span> "
<< R"(<span size="xx-small" weight="ultralight">)"
<< entry.uri << "</span>\n";
}
}
} // namespace remwharead

View File

@ -84,18 +84,9 @@ void Database::store(const Database::entry &data) const
try try
{ {
const string strdatetime = timepoint_to_string(data.datetime, true); const string strdatetime = timepoint_to_string(data.datetime, true);
string strtags; string strtags = tags_to_string(data.tags);
Statement insert(*_session); Statement insert(*_session);
for (const string &tag : data.tags)
{
strtags += tag;
if (tag != *(data.tags.rbegin()))
{
strtags += ",";
}
}
// useRef() uses the const reference. // useRef() uses the const reference.
insert << "INSERT INTO remwharead " insert << "INSERT INTO remwharead "
"VALUES(?, ?, ?, ?, ?, ?, ?);", "VALUES(?, ?, ?, ?, ?, ?, ?);",
@ -175,6 +166,22 @@ size_t Database::remove(const string &uri)
return del.execute(); return del.execute();
} }
string Database::tags_to_string(const vector<string> &tags)
{
string strtags;
for (const string &tag : tags)
{
strtags += tag;
if (tag != *(tags.rbegin()))
{
strtags += ',';
}
}
return strtags;
}
fs::path Database::get_data_home() const fs::path Database::get_data_home() const
{ {
fs::path path; fs::path path;

68
tests/test_rofi.cpp Normal file
View File

@ -0,0 +1,68 @@
/* This file is part of remwharead.
* Copyright © 2019 tastytea <tastytea@tastytea.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <exception>
#include <string>
#include <catch.hpp>
#include "sqlite.hpp"
#include "export/rofi.hpp"
using namespace remwharead;
using std::string;
SCENARIO ("The Rofi export works correctly")
{
bool exception = false;
bool rofi_ok = true;
GIVEN ("One database entry")
{
Database::entry entry;
entry.uri = "https://example.com/page.html";
entry.title = "Thoughtful title";
entry.tags = { "tag1", "tag2" };
const string expected = static_cast<char>(0x00) + string("markup-rows")
+ static_cast<char>(0x1f) + "true\n"
"Thoughtful title "
R"(<span size="small" weight="light" style="italic">(tag1,tag2))"
R"(</span> <span size="xx-small" weight="ultralight">)"
"https://example.com/page.html</span>\n";
try
{
std::ostringstream output;
Export::Rofi({ entry }, output).print();
const string rofi = output.str();
if (rofi != expected)
{
rofi_ok = false;
}
}
catch (const std::exception &e)
{
exception = true;
}
THEN ("No exception is thrown")
AND_THEN ("Output looks okay")
{
REQUIRE_FALSE(exception);
REQUIRE(rofi_ok);
}
}
}