Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ jobs:
- uses: actions/checkout@v6
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~3.25.0"
ninjaVersion: "^1.11.1"
cmakeVersion: "~4.2.0" # <--= optional, use most recent 4.2.x version
ninjaVersion: "^1.11.1" # <--= optional, use most recent 1.x version
- name: build
run: |
mkdir tmp
Expand All @@ -39,13 +39,13 @@ jobs:
- uses: actions/checkout@v6
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~3.25.0"
ninjaVersion: "^1.11.1"
cmakeVersion: "~4.2.0" # <--= optional, use most recent 4.2.x version
ninjaVersion: "^1.11.1" # <--= optional, use most recent 1.x version
- name: build
run: |
mkdir tmp
cd tmp
cmake -G "Visual Studio 17 2022" -A x64 -DMADDY_BUILD_WITH_TESTS=ON ..
cmake -G "Visual Studio 18 2026" -A x64 -DMADDY_BUILD_WITH_TESTS=ON ..
cmake --build . --config Debug
- name: run tests
run: |
Expand All @@ -57,8 +57,8 @@ jobs:
- uses: actions/checkout@v6
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~3.25.0"
ninjaVersion: "^1.11.1"
cmakeVersion: "~4.2.0" # <--= optional, use most recent 4.2.x version
ninjaVersion: "^1.11.1" # <--= optional, use most recent 1.x version
- name: build
run: |
mkdir tmp
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ maddy uses [semver versioning](https://semver.org/).

## Upcoming

* ![**ADDED**](https://img.shields.io/badge/-ADDED-%23099) New `maddy::types::MADDY_SPECIFIC_PARSER` config flag (on by default, keeping current behavior). Turning it off switches `TableParser` from maddy's own `|table>` sigil syntax to standard GitHub-flavored-Markdown pipe tables (which have no footer concept).
* ![**FIXED**](https://img.shields.io/badge/-FIXED-%23090) Only create strong and emphasis tags at word boundaries, i.e. `not only_internal_underscores`.
* ![**FIXED**](https://img.shields.io/badge/-FIXED-%23090) Correctly leave any leftover strong or emphasis delimiters outside the tag on either side, however many there are, i.e. `___text__` becomes `_<strong>text</strong>` and `_text_______` becomes `<em>text</em>______`.
* ...

## version 1.6.0 2025-07-26
Expand Down
47 changes: 47 additions & 0 deletions docs/definitions.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,53 @@ becomes
```
table header and footer are optional

This is maddy's own historical table syntax, and it's used by default
(`maddy::types::MADDY_SPECIFIC_PARSER`). If you need standard
GitHub-flavored-Markdown pipe tables instead, disable it in config:

```cpp
std::shared_ptr<maddy::ParserConfig> config = std::make_shared<maddy::ParserConfig>();
config->enabledParsers &= ~maddy::types::MADDY_SPECIFIC_PARSER;

std::shared_ptr<maddy::Parser> parser = std::make_shared<maddy::Parser>(config);
std::string htmlOutput = parser->Parse(markdownInput);
```

After this, tables look like:

```
| Left header | middle header | last header |
| --- | --- | --- |
| cell 1 | cell 2 | cell 3 |
| cell 4 | cell 5 | cell 6 |
```
becomes
```html
<table>
<thead>
<tr>
<th>Left header</th>
<th>middle header</th>
<th>last header</th>
</tr>
</thead>
<tbody>
<tr>
<td>cell 1</td>
<td>cell 2</td>
<td>cell 3</td>
</tr>
<tr>
<td>cell 4</td>
<td>cell 5</td>
<td>cell 6</td>
</tr>
</tbody>
</table>
```
GFM pipe tables have no footer concept, so this mode never produces a
`<tfoot>`.

## LaTeX(MathJax) block support

To turn on the LaTeX support - which basically is only a
Expand Down
7 changes: 5 additions & 2 deletions include/maddy/emphasizedparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,13 @@ class EmphasizedParser : public LineParser
*/
void Parse(std::string& line) override
{
// The leading and trailing `(_*)` groups absorb any leftover underscores
// from an unbalanced run (e.g. `__foo_` or `_foo____`), re-emitted
// outside the <em> tag instead of into its content.
static std::regex re(
R"((?!.*`.*|.*<code>.*)_(?!.*`.*|.*<\/code>.*)([^_]*)_(?!.*`.*|.*<\/code>.*))"
R"((?!.*`.*|.*<code>.*)\b(_*)_(?![\s_])(?!.*`.*|.*<\/code>.*)(.*?[^\s])_(_*)\b(?!.*`.*|.*<\/code>.*))"
);
static std::string replacement = "<em>$1</em>";
static std::string replacement = "$1<em>$2</em>$3";

line = std::regex_replace(line, re, replacement);
}
Expand Down
13 changes: 10 additions & 3 deletions include/maddy/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class Parser
*/
static const std::string& version()
{
static const std::string v = "1.6.0"; // MADDY_VERSION_LINE_REPLACEMENT
static const std::string v = "1.5.0";
return v;
}

Expand Down Expand Up @@ -278,10 +278,17 @@ class Parser
}
else if ((!this->config || (this->config->enabledParsers &
maddy::types::TABLE_PARSER) != 0) &&
maddy::TableParser::IsStartingLine(line))
maddy::TableParser::IsStartingLine(
line,
!this->config || (this->config->enabledParsers &
maddy::types::MADDY_SPECIFIC_PARSER) != 0
))
{
parser = std::make_shared<maddy::TableParser>(
[this](std::string& line) { this->runLineParser(line); }, nullptr
[this](std::string& line) { this->runLineParser(line); },
nullptr,
!this->config || (this->config->enabledParsers &
maddy::types::MADDY_SPECIFIC_PARSER) != 0
);
}
else if ((!this->config || (this->config->enabledParsers &
Expand Down
9 changes: 7 additions & 2 deletions include/maddy/parserconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,13 @@ enum PARSER_TYPE : uint32_t
UNORDERED_LIST_PARSER = 0b100000000000000000,
LATEX_BLOCK_PARSER = 0b1000000000000000000,

DEFAULT = 0b0111111111110111111,
ALL = 0b1111111111111111111,
// Not a parser of its own: gates maddy's own historical markdown dialect
// wherever a parser supports both that and a more standard alternative
// (currently just TableParser's `|table>` syntax vs. GFM pipe tables).
MADDY_SPECIFIC_PARSER = 0b10000000000000000000,

DEFAULT = 0b10111111111110111111,
ALL = 0b11111111111111111111,
};
// clang-format on

Expand Down
26 changes: 14 additions & 12 deletions include/maddy/strongparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,21 @@ class StrongParser : public LineParser
*/
void Parse(std::string& line) override
{
static std::vector<std::regex> res{
std::regex{
R"((?!.*`.*|.*<code>.*)\*\*(?!.*`.*|.*<\/code>.*)([^\*\*]*)\*\*(?!.*`.*|.*<\/code>.*))"
},
std::regex{
R"((?!.*`.*|.*<code>.*)__(?!.*`.*|.*<\/code>.*)([^__]*)__(?!.*`.*|.*<\/code>.*))"
}
// `*` is not a word character, so `\b` next to it does not mean
// "edge of a delimiter run" the way it does for `_`; the asterisk
// variant is left without a word-boundary anchor.
static std::regex reAsterisk{
R"((?!.*`.*|.*<code>.*)\*\*(?![\s])(?!.*`.*|.*<\/code>.*)(.*?[^\s])\*\*(?!.*`.*|.*<\/code>.*))"
};
static std::string replacement = "<strong>$1</strong>";
for (const auto& re : res)
{
line = std::regex_replace(line, re, replacement);
}
// The leading and trailing `(_*)` groups absorb any leftover underscores
// from an unbalanced run on either side (e.g. `___text__` or
// `__text_______`), re-emitted outside the <strong> tag by the caller
// instead of being swallowed into its content.
static std::regex reUnderscore{
R"((?!.*`.*|.*<code>.*)\b(_*)__(?![\s_])(?!.*`.*|.*<\/code>.*)(.*?[^\s])__(_*)\b(?!.*`.*|.*<\/code>.*))"
};
line = std::regex_replace(line, reAsterisk, "<strong>$1</strong>");
line = std::regex_replace(line, reUnderscore, "$1<strong>$2</strong>$3");
}
}; // class StrongParser

Expand Down
Loading