Highlight links in descriptions

This commit is contained in:
blankie 2023-04-10 00:08:01 +07:00
parent e69a00f446
commit b749d8d58a
Signed by: blankie
GPG Key ID: CC15FC822C7F61F5
3 changed files with 45 additions and 9 deletions

View File

@ -12,7 +12,9 @@
#define USERINFO "((?:" UNRESERVED "|" PCT_ENCODED "|" SUB_DELIMS "|:)*)" #define USERINFO "((?:" UNRESERVED "|" PCT_ENCODED "|" SUB_DELIMS "|:)*)"
// A lot looser than RFC 3986, but things will go very wrong very quickly if I comply // A lot looser than RFC 3986, but things will go very wrong very quickly if I comply
#define HOST "([^/?#]+?)" // Hostname does not allow for stuff like "//The", but it is not important here
#define HOSTCHAR "[\\w\\d\\-.]"
#define HOST "(\\[[\\da-f:.]+\\]|" HOSTCHAR "+(?:\\." HOSTCHAR "+)+)"
#define PORT "(\\d*)" #define PORT "(\\d*)"
#define AUTHORITY "(?:" USERINFO "@)?" HOST "(?::" PORT ")?" #define AUTHORITY "(?:" USERINFO "@)?" HOST "(?::" PORT ")?"
@ -22,20 +24,27 @@
#define QUERY "((?:" PCHAR "|[/?])*)" #define QUERY "((?:" PCHAR "|[/?])*)"
// Looser than RFC 3986, but fragments might as well own everything // Looser than RFC 3986, but fragments might as well own everything
#define FRAGMENT "(.*)" #define FRAGMENT "([^ ]*)"
#define HTTP_HTTPS_URL \ #define FULL_HTTP_HTTPS_REGEX \
"(?:(https?)?:)?//" AUTHORITY \
PATH_ABEMPTY \
"(?:\\?" QUERY ")?" \
"(?:#" FRAGMENT ")?"
#define HTTP_HTTPS_REGEX \
"(?:(https?)?:)?(?://" AUTHORITY ")?" \ "(?:(https?)?:)?(?://" AUTHORITY ")?" \
PATH_ABEMPTY \ PATH_ABEMPTY \
"(?:\\?" QUERY ")?" \ "(?:\\?" QUERY ")?" \
"(?:#" FRAGMENT ")?" "(?:#" FRAGMENT ")?"
static std::regex url_regex(HTTP_HTTPS_URL, std::regex::icase);
static void handle_segment(std::vector<std::string>& segments, const std::string& str, size_t offset, size_t length); static void handle_segment(std::vector<std::string>& segments, const std::string& str, size_t offset, size_t length);
namespace blankie { namespace blankie {
namespace murl { namespace murl {
std::regex full_url_regex(FULL_HTTP_HTTPS_REGEX, std::regex::icase);
std::regex url_regex(HTTP_HTTPS_REGEX, std::regex::icase);
Url::Url(const std::string& str) { Url::Url(const std::string& str) {
std::smatch sm; std::smatch sm;
if (!std::regex_match(str, sm, url_regex)) { if (!std::regex_match(str, sm, url_regex)) {

View File

@ -1,10 +1,13 @@
#pragma once #pragma once
#include <regex>
#include <string> #include <string>
namespace blankie { namespace blankie {
namespace murl { namespace murl {
extern std::regex full_url_regex;
struct Url { struct Url {
std::string scheme; std::string scheme;
std::string userinfo; std::string userinfo;

View File

@ -1,16 +1,21 @@
#include <regex>
#include "routes.h" #include "routes.h"
#include "../blankie/murl.h"
#include "../servehelper.h" #include "../servehelper.h"
#include "../numberhelper.h" #include "../numberhelper.h"
#include "../pixivclient.h" #include "../pixivclient.h"
static inline bool is_true(const std::string& str);
static inline std::string time_to_string(time_t time);
static inline Element generate_user_link(const httplib::Request& req, const Config& config, const Illust& illust); static inline Element generate_user_link(const httplib::Request& req, const Config& config, const Illust& illust);
static inline Element generate_images(const httplib::Request& req, const Config& config, const Illust& illust); static inline Element generate_images(const httplib::Request& req, const Config& config, const Illust& illust);
static inline Element generate_preview_images(const httplib::Request& req, const Config& config, const Illust& illust); static inline Element generate_preview_images(const httplib::Request& req, const Config& config, const Illust& illust);
static inline std::vector<blankie::html::Node> parse_description_line(std::string str);
static inline Element generate_description(const std::string& description); static inline Element generate_description(const std::string& description);
static inline Element generate_illust_tags(const Illust& illust); static inline Element generate_illust_tags(const Illust& illust);
static inline bool is_true(const std::string& str);
static inline std::string time_to_string(time_t time);
void artworks_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client) { void artworks_route(const httplib::Request& req, httplib::Response& res, const Config& config, PixivClient& pixiv_client) {
uint64_t illust_id = to_ull(req.matches.str(1)); uint64_t illust_id = to_ull(req.matches.str(1));
bool preview = is_true(req.get_param_value("preview")); bool preview = is_true(req.get_param_value("preview"));
@ -107,7 +112,25 @@ static inline Element generate_preview_images(const httplib::Request& req, const
return div; return div;
} }
// TODO auto-detect links // TODO proxy urls
static inline std::vector<blankie::html::Node> parse_description_line(std::string str) {
std::vector<blankie::html::Node> nodes;
std::smatch sm;
while (std::regex_search(str, sm, blankie::murl::full_url_regex)) {
if (sm.prefix().length()) {
nodes.push_back(sm.prefix());
}
nodes.push_back(Element("a", {{"href", sm.str(0)}}, {sm.str(0)}));
str = sm.suffix();
}
if (!str.empty()) {
nodes.push_back(std::move(str));
}
return nodes;
}
static inline Element generate_description(const std::string& description) { static inline Element generate_description(const std::string& description) {
Element p("p"); Element p("p");
size_t pos = 0; size_t pos = 0;
@ -116,11 +139,12 @@ static inline Element generate_description(const std::string& description) {
if (!p.nodes.empty()) { if (!p.nodes.empty()) {
p.nodes.push_back(Element("br")); p.nodes.push_back(Element("br"));
} }
p.nodes.push_back(std::move(str)); std::vector<blankie::html::Node> nodes = parse_description_line(std::move(str));
p.nodes.insert(p.nodes.end(), nodes.begin(), nodes.end());
}; };
while ((pos = description.find('\n', pos)) != std::string::npos) { while ((pos = description.find('\n', pos)) != std::string::npos) {
add(description.substr(last_pos, pos)); add(description.substr(last_pos, pos - last_pos));
last_pos = ++pos; last_pos = ++pos;
} }
if (description.size() > last_pos) { if (description.size() > last_pos) {