5 Commits

Author SHA1 Message Date
Petra Baranski
9ec4777a7b Add benchmark
Some checks failed
run-checks / run-clang-format (push) Has been cancelled
run-tests / test-on-ubuntu (push) Has been cancelled
run-tests / test-on-windows (push) Has been cancelled
run-tests / test-on-osx (push) Has been cancelled
* add benchmark option
* updated changelog
* added benchmark info to readme
* add bench folder to format.py
2025-04-21 01:02:07 +02:00
Lucian Smith
2a00c9fb0b Add regex for title text version of links
* Add regex for title text version of links

Original parser matched:
  [name](http:://link)

Add match for:
  [name](http:://link "title text")

* Add tests and improve regex's
* Several new tests in test_maddy_linkparser.cpp  (Some with paths for future improvement, and one to ensure an overzealous future update doesn't disallow actually-used special characters like o-umlaut).
* URLs now ignore leading/trailing spaces.
* URLs now don't match on internal spaces or quotes.
* Small grammar fix in CONTRIBUTING.md
* Updated changelog.
2025-04-20 18:45:27 +02:00
Petra Baranski
af59cd13d4 Update pull_request_template.md
Some checks failed
run-checks / run-clang-format (push) Has been cancelled
run-tests / test-on-ubuntu (push) Has been cancelled
run-tests / test-on-windows (push) Has been cancelled
run-tests / test-on-osx (push) Has been cancelled
2025-04-18 12:52:51 +02:00
Petra Baranski
f7d2e69e44 Create pull_request_template.md 2025-04-18 12:49:42 +02:00
Petra Baranski
261e75f22f Release should use the actual annotated tag message
Some checks failed
run-checks / run-clang-format (push) Has been cancelled
run-tests / test-on-ubuntu (push) Has been cancelled
run-tests / test-on-windows (push) Has been cancelled
run-tests / test-on-osx (push) Has been cancelled
Update dependency versions / update-fetch-content (push) Has been cancelled
2025-03-28 05:22:54 +01:00
14 changed files with 329 additions and 8 deletions

11
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,11 @@
### Description of the change
*Please describe what has changed and why. If it fixes an open issue, mention it here.*
### Checklist for contributor
* [ ] if you want to be mentioned in the [AUTHORS](https://github.com/progsource/maddy/blob/master/AUTHORS) file, you added yourself
* [ ] added an entry to [CHANGELOG.md](https://github.com/progsource/maddy/blob/master/CHANGELOG.md) at "Upcoming"
* [ ] if any Markdown definition changed, you updated the [definition docs](https://github.com/progsource/maddy/blob/master/docs/definitions.md)
* [ ] your C++ code change is accommodated with a unit/integration test where it makes sense
* [ ] your code meets the code format style (clang-format) of the project (`tools/format.py`)

View File

@@ -29,7 +29,7 @@ jobs:
id: tag-message id: tag-message
run: | run: |
TAG_NAME=${GITHUB_REF#refs/tags/} TAG_NAME=${GITHUB_REF#refs/tags/}
TAG_MESSAGE=$(git for-each-ref refs/tags/$TAG_NAME --format='%(contents)') TAG_MESSAGE=$(git tag -l --format='%(contents)' $TAG_NAME)
echo "message<<EOF" >> $GITHUB_OUTPUT echo "message<<EOF" >> $GITHUB_OUTPUT
echo "$TAG_MESSAGE" >> $GITHUB_OUTPUT echo "$TAG_MESSAGE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT

View File

@@ -11,3 +11,4 @@ Andrew Mettlach (dmmettlach@gmail.com)
Evan Klitzke (evan@eklitzke.org) Evan Klitzke (evan@eklitzke.org)
Albert Schwarzkopf (dev-maddy@quitesimple.org) Albert Schwarzkopf (dev-maddy@quitesimple.org)
Ivans Saponenko (ivans.saponenko+maddy@gmail.com) Ivans Saponenko (ivans.saponenko+maddy@gmail.com)
Lucian Smith (lpsmith@uw.edu)

View File

@@ -14,6 +14,11 @@ maddy uses [semver versioning](https://semver.org/).
## Upcoming ## Upcoming
* ![**ADDED**](https://img.shields.io/badge/-ADDED-%23099) Correctly parse links with title text, i.e. `[link](http://example.com "example")`.
* ![**FIXED**](https://img.shields.io/badge/-FIXED-%23090) Do not create invalid URLs from links with spaces, i.e. `[link](/ABC/some file)`.
* ![**FIXED**](https://img.shields.io/badge/-FIXED-%23090) Do not create invalid HTML from links with quotes, i.e. `[link](/ABC/some"file)`.
* ![**ADDED**](https://img.shields.io/badge/-ADDED-%23099) benchmarks.
## version 1.4.0 2025-03-28 ## version 1.4.0 2025-03-28
* ![**CHANGED**](https://img.shields.io/badge/-CHANGED-%23e90) Updated google test to v1.16.0. * ![**CHANGED**](https://img.shields.io/badge/-CHANGED-%23e90) Updated google test to v1.16.0.

View File

@@ -19,6 +19,7 @@ if(${MADDY_BUILD_WITH_TESTS})
enable_testing() enable_testing()
endif() endif()
option(MADDY_BUILD_WITH_BENCH "enable benchmarks" OFF)
option(MADDY_CREATE_PACKAGE "create a package for a version release" OFF) option(MADDY_CREATE_PACKAGE "create a package for a version release" OFF)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@@ -49,10 +50,14 @@ if(${MADDY_BUILD_WITH_TESTS})
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
if(${MADDY_BUILD_WITH_BENCH})
add_subdirectory(bench)
endif()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
if(${MADDY_CREATE_PACKAGE}) if(${MADDY_CREATE_PACKAGE})
set(MADDY_PACKAGE_FILES include/ CMakeLists.txt LICENSE) set(MADDY_PACKAGE_FILES include/ CMakeLists.txt LICENSE AUTHORS)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-src.zip add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-src.zip
COMMAND ${CMAKE_COMMAND} -E tar c ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-src.zip --format=zip -- ${MADDY_PACKAGE_FILES} COMMAND ${CMAKE_COMMAND} -E tar c ${CMAKE_BINARY_DIR}/${PROJECT_NAME}-src.zip --format=zip -- ${MADDY_PACKAGE_FILES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}

View File

@@ -16,4 +16,4 @@ improve the code? Then [create a GitHub issue](https://github.com/progsource/mad
* Explain for what your PR is for - like providing a use-case or something similar. * Explain for what your PR is for - like providing a use-case or something similar.
* Update documentation of the Markdown syntax if anything changed there. (`docs/definitions.md`) * Update documentation of the Markdown syntax if anything changed there. (`docs/definitions.md`)
* Add a changelog entry at "Upcoming" inside of `CHANGELOG.md` * Add a changelog entry at "Upcoming" inside of `CHANGELOG.md`
* Make sure, that the tests are successful and if you wrote a bugfix, to have a test, that highlights the issue. * Make sure that the tests are successful and if you wrote a bugfix, to have a test that highlights the issue.

View File

@@ -98,6 +98,21 @@ make
make test # or run the executable in ../build/MaddyTests make test # or run the executable in ../build/MaddyTests
``` ```
## How to run the benchmarks
To get proper test results, the benchmarks should always be compiled as
release build.
```shell
git clone https://github.com/progsource/maddy.git
cd maddy
mkdir tmp
cd tmp
cmake -DMADDY_BUILD_WITH_BENCH=ON -DCMAKE_BUILD_TYPE=Release ..
make BUILD_TYPE=Release
../build/maddy_benchmark
```
## How to contribute ## How to contribute
There are different possibilities: There are different possibilities:

50
bench/CMakeLists.txt Normal file
View File

@@ -0,0 +1,50 @@
# This project is licensed under the MIT license. For more information see the
# LICENSE file.
if (UNIX AND NOT APPLE)
execute_process(COMMAND ${CMAKE_CXX_COMPILER}
-fuse-ld=gold -Wl,--version
ERROR_QUIET OUTPUT_VARIABLE ld_version)
if ("${ld_version}" MATCHES "GNU gold")
message(STATUS "Found Gold linker, use faster linker")
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=gold")
set(CMAKE_SHARED_LINKER_FLAGS
"${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=gold ")
endif()
endif()
# ------------------------------------------------------------------------------
include(FetchContent)
FetchContent_Declare(
nanobench
GIT_REPOSITORY https://github.com/martinus/nanobench.git
GIT_TAG v4.3.11
GIT_SHALLOW TRUE
)
FetchContent_MakeAvailable(nanobench)
# ------------------------------------------------------------------------------
file(GLOB_RECURSE MADDY_BENCHMARK_FILES
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)
# ------------------------------------------------------------------------------
add_executable(
maddy_benchmark
${MADDY_BENCHMARK_FILES}
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
)
target_include_directories(maddy_benchmark PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(maddy_benchmark maddy nanobench::nanobench)
set_target_properties(maddy_benchmark PROPERTIES
CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -O2 -Wall -Wno-ignored-qualifiers -Wpedantic -Wextra -Wno-deprecated -fno-exceptions -fno-rtti"
)

14
bench/README.md Normal file
View File

@@ -0,0 +1,14 @@
# maddy benchmarks
## How to run
The benchmarks have to be run in release mode to give proper results.
```shell
# in main folder
mkdir tmp
cd tmp
cmake -DMADDY_BUILD_WITH_BENCH=ON -DCMAKE_BUILD_TYPE=Release ..
make BUILD_TYPE=Release
../build/maddy_benchmark
```

63
bench/main.cpp Normal file
View File

@@ -0,0 +1,63 @@
/*
* This project is licensed under the MIT license. For more information see the
* LICENSE file.
*/
#include <fstream>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#define ANKERL_NANOBENCH_IMPLEMENT
#include <nanobench.h>
#include "maddy/parser.h"
int main()
{
static const std::string markdownFile = "../docs/definitions.md";
std::stringstream buffer;
{
std::ifstream file(markdownFile);
if (!file.good() || !file.is_open())
{
std::cout << "could not read file at " << markdownFile << std::endl;
return 1;
}
buffer << file.rdbuf();
file.close();
}
if (!buffer.good() || buffer.str().empty())
{
std::cout << "buffer is invalid" << std::endl;
return 2;
}
// maddy 1.*
std::shared_ptr<maddy::Parser> parser = std::make_shared<maddy::Parser>();
// This is the place in the future to compare maddy with other libraries.
// For now it can be used to check if changes in maddy code result in better
// performance.
ankerl::nanobench::Bench()
.title("maddy test")
.warmup(100)
.relative(true)
.run(
"maddy 1.x",
[&]()
{
buffer.clear(); // clear any error flags
buffer.seekg(0, buffer.beg);
ankerl::nanobench::doNotOptimizeAway(parser->Parse(buffer));
}
);
return 0;
}

View File

@@ -41,6 +41,14 @@ results in
<a href="http://example.com">Text of the link</a> <a href="http://example.com">Text of the link</a>
``` ```
```
[Text of the link](http://example.com "title text")
```
results in
```html
<a href="http://example.com" title="title text">Text of the link</a>
```
## Lists ## Lists
### unordered ### unordered

View File

@@ -40,10 +40,17 @@ public:
*/ */
void Parse(std::string& line) override void Parse(std::string& line) override
{ {
static std::regex re(R"(\[([^\]]*)\]\(([^)]*)\))"); // Match [name](http:://link "title text")
static std::string replacement = "<a href=\"$2\">$1</a>"; // NOTE: the 'no quote' bit at the beginning (^") is a hack for now:
// there should eventually be something that replaces it with '%22'.
static std::regex re(R"(\[([^\]]*)\]\( *([^)^ ^"]*) *\"([^\"]*)\" *\))");
static std::string replacement = "<a href=\"$2\" title=\"$3\">$1</a>";
line = std::regex_replace(line, re, replacement); line = std::regex_replace(line, re, replacement);
// Match [name](http:://link)
static std::regex re2(R"(\[([^\]]*)\]\( *([^)^ ^"]*) *\))");
static std::string replacement2 = "<a href=\"$2\">$1</a>";
line = std::regex_replace(line, re2, replacement2);
} }
}; // class LinkParser }; // class LinkParser

View File

@@ -37,14 +37,47 @@ TEST(MADDY_LINKPARSER, ItReplacesMarkdownWithLinks)
ASSERT_EQ(expected, text); ASSERT_EQ(expected, text);
} }
TEST(MADDY_LINKPARSER, ItReplacesMarkdownWithSpacesAfterLink)
{
std::string text =
"Some text [Link Title](http://example.com ) bla [Link "
"Title](http://example.com)";
std::string expected =
"Some text <a href=\"http://example.com\">Link Title</a> bla <a "
"href=\"http://example.com\">Link Title</a>";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_LINKPARSER, ItHandlesURLsWithOfficiallyIllegalCharacters)
{
// Some links in the real world have characters that are not
// 'official' characters that are supposedly allowed in URLs.
std::string text =
"Wikipedia's [Möbius strip]"
"(https://en.wikipedia.org/wiki/Möbius_strip) link.";
std::string expected =
"Wikipedia's <a "
"href=\"https://en.wikipedia.org/wiki/Möbius_strip\">"
"Möbius strip</a> link.";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST( TEST(
MADDY_LINKPARSER, ItReplacesMarkdownProperlyEvenWithMultipleParenthesisInLine MADDY_LINKPARSER, ItReplacesMarkdownProperlyEvenWithMultipleParenthesisInLine
) )
{ {
std::string text = std::string text =
"(This is a [link](/ABC/some file) (the URL will include this).)"; "(This is a [link](/ABC/some_file) (the URL will not include this).)";
std::string expected = std::string expected =
"(This is a <a href=\"/ABC/some file\">link</a> (the URL will include " "(This is a <a href=\"/ABC/some_file\">link</a> (the URL will not include "
"this).)"; "this).)";
auto linkParser = std::make_shared<maddy::LinkParser>(); auto linkParser = std::make_shared<maddy::LinkParser>();
@@ -53,6 +86,99 @@ TEST(
ASSERT_EQ(expected, text); ASSERT_EQ(expected, text);
} }
TEST(MADDY_LINKPARSER, ItDoesntReplaceMarkdownWithSpaceInURL)
{
// Spaces are not allowed in URLs, so don't match them.
std::string text = "This is an invalid [link](/ABC/some file)";
std::string expected = "This is an invalid [link](/ABC/some file)";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_LINKPARSER, ItReplacesMarkdownWithTitleText)
{
std::string text = "Link to [name](http:://example.com \"title text\")";
std::string expected =
"Link to <a href=\"http:://example.com\" title=\"title text\">name</a>";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_LINKPARSER, ItReplacesMarkdownWithSpacesWithTitleText)
{
std::string text = "Link to [name](http:://example.com \"title text\")";
std::string expected =
"Link to <a href=\"http:://example.com\" title=\"title text\">name</a>";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_LINKPARSER, ItReplacesMarkdownWithMoreSpacesWithTitleText)
{
std::string text =
"Link to [name](http:://example.com \"title text\" )";
std::string expected =
"Link to <a href=\"http:://example.com\" title=\"title text\">name</a>";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_LINKPARSER, ItReplacesMarkdownWithParentheticalText)
{
std::string text = "Link to [name](http:://example.com \"title (text)\")";
std::string expected =
"Link to <a href=\"http:://example.com\" title=\"title (text)\">name</a>";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_LINKPARSER, ItDoesntReplaceMarkdownWithTooManyQuotes)
{
// If you have too many quotation marks, don't match:
std::string text =
"This is an invalid [link](/ABC/some_file \"title \" text \")";
std::string expected =
"This is an invalid [link](/ABC/some_file \"title \" text \")";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}
TEST(MADDY_LINKPARSER, ItDoesntReplaceMarkdownWithQuoteInLink)
{
// This is actually legal markdown, but hard to parse with regexes;
// See disabled 'ItReplacesMarkdownWithQuoteInLink' below.
//
// For now, don't try to translate it; it would produce invalid HTML.
std::string text = "Some text [Link Title](http://example.com/\"foo ) bla.";
std::string current_expected =
"Some text [Link Title](http://example.com/\"foo ) bla.";
std::string correct_expected =
"Some text <a href=\"http://example.com/%22foo\">Link Title</a> bla.";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(current_expected, text);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
class DISABLED_MADDY_LINKPARSER : public ::testing::Test class DISABLED_MADDY_LINKPARSER : public ::testing::Test
@@ -70,3 +196,17 @@ TEST_F(DISABLED_MADDY_LINKPARSER, ItReplacesNoImageMarkdownWithLinks)
ASSERT_EQ(expected, text); ASSERT_EQ(expected, text);
} }
TEST(DISABLED_MADDY_LINKPARSER, ItReplacesMarkdownWithQuoteInLink)
{
// This is legal markdown, but hard to parse with regexes; dropping it
// here for a future update.
std::string text = "Some text [Link Title](http://example.com/\"foo ) bla.";
std::string expected =
"Some text <a href=\"http://example.com/%22foo\">Link Title</a> bla.";
auto linkParser = std::make_shared<maddy::LinkParser>();
linkParser->Parse(text);
ASSERT_EQ(expected, text);
}

View File

@@ -60,6 +60,8 @@ def format_files(dry_run):
during the actual formatting process. during the actual formatting process.
""" """
patterns = [ patterns = [
"bench/**/*.h",
"bench/**/*.cpp",
"include/**/*.h", "include/**/*.h",
"tests/**/*.h", "tests/**/*.h",
"tests/**/*.cpp", "tests/**/*.cpp",