Skip to content

Commit 741047c

Browse files
committed
support inline styled text
#feat
1 parent d104e7c commit 741047c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+953
-825
lines changed

include/mrdocs/Support/String.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ rtrim(std::string_view s) noexcept
4545
{
4646
auto it = s.end() - 1;
4747
while(it > s.begin() && std::isspace(*it))
48+
{
4849
--it;
49-
return s.substr(0, it - s.begin());
50+
}
51+
return s.substr(0, it - s.begin() + 1);
5052
}
5153

5254
/** Return the substring without leading and trailing horizontal whitespace.

src/lib/AST/ASTVisitor.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1754,13 +1754,6 @@ generateJavadoc(
17541754
{
17551755
return false;
17561756
}
1757-
// KRYSTIAN FIXME: clang ignores documentation comments
1758-
// when there is a preprocessor directive between the end
1759-
// of the comment and the declaration location. there are two
1760-
// ways to fix this: either set the declaration begin location
1761-
// to be before and preprocessor directives, or submit a patch
1762-
// which disables this behavior (it's not entirely clear why
1763-
// this check occurs anyways, so some investigation is needed)
17641757
parseJavadoc(javadoc, FC, D, config_, diags_);
17651758
return true;
17661759
}

src/lib/AST/ParseJavadoc.cpp

Lines changed: 173 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <mrdocs/Support/Error.hpp>
1717
#include <mrdocs/Support/Path.hpp>
1818
#include <mrdocs/Support/String.hpp>
19+
#include <mrdocs/Support/ScopeExit.hpp>
1920
#include <clang/AST/CommentCommandTraits.h>
2021
#include <clang/AST/ASTContext.h>
2122
#include <clang/AST/RawCommentList.h>
@@ -397,22 +398,183 @@ ensureUTF8(
397398
return s;
398399
}
399400

401+
/* Parse the inline content of a text
402+
403+
This function takes a string from a comment
404+
and parses it into a sequence of styled text
405+
nodes.
406+
407+
The string may contain inline commands that
408+
change the style of the text:
409+
410+
Regular text is stored as a doc::Text.
411+
Styled text is stored as a doc::Styled.
412+
413+
The styles can be one of: mono, bold, or italic.
414+
415+
The tags "`", "*", and "_" are used to indicate
416+
the start and end of styled text. They can be
417+
escaped by prefixing them with a backslash.
418+
419+
*/
420+
doc::List<doc::Text>
421+
parseStyled(StringRef s)
422+
{
423+
doc::List<doc::Text> result;
424+
std::string currentText;
425+
doc::Style currentStyle = doc::Style::none;
426+
bool escapeNext = false;
427+
428+
auto isStyleMarker = [](char c) {
429+
return c == '`' || c == '*' || c == '_';
430+
};
431+
432+
auto flushCurrentText = [&]() {
433+
if (!currentText.empty()) {
434+
if (currentStyle == doc::Style::none) {
435+
bool const lastIsSame =
436+
!result.empty() &&
437+
result.back()->kind == doc::Kind::text;
438+
if (lastIsSame)
439+
{
440+
auto& lastText = static_cast<doc::Text&>(*result.back());
441+
lastText.string.append(currentText);
442+
}
443+
else
444+
{
445+
result.emplace_back(std::make_unique<doc::Text>(std::move(currentText)));
446+
}
447+
} else {
448+
bool const lastIsSame =
449+
!result.empty() &&
450+
result.back()->kind == doc::Kind::styled &&
451+
static_cast<doc::Styled&>(*result.back()).style == currentStyle;
452+
if (lastIsSame)
453+
{
454+
auto& lastStyled = static_cast<doc::Styled&>(*result.back());
455+
lastStyled.string.append(currentText);
456+
}
457+
else
458+
{
459+
result.emplace_back(std::make_unique<doc::Styled>(std::move(currentText), currentStyle));
460+
}
461+
}
462+
currentText.clear();
463+
}
464+
};
465+
466+
auto isPunctuationOrSpace = [](char c) {
467+
return std::isspace(c) || std::ispunct(c);
468+
};
469+
470+
for (std::size_t i = 0; i < s.size(); ++i) {
471+
char c = s[i];
472+
if (escapeNext) {
473+
currentText.push_back(c);
474+
escapeNext = false;
475+
} else if (c == '\\') {
476+
escapeNext = true;
477+
} else if (isStyleMarker(c)) {
478+
bool const atWordBoundary =
479+
(currentStyle == doc::Style::none && ((i == 0) || isPunctuationOrSpace(s[i - 1]))) ||
480+
(currentStyle != doc::Style::none && ((i == s.size() - 1) || isPunctuationOrSpace(s[i + 1])));
481+
if (atWordBoundary) {
482+
flushCurrentText();
483+
if (c == '`') {
484+
currentStyle = (currentStyle == doc::Style::mono) ? doc::Style::none : doc::Style::mono;
485+
} else if (c == '*') {
486+
currentStyle = (currentStyle == doc::Style::bold) ? doc::Style::none : doc::Style::bold;
487+
} else if (c == '_') {
488+
currentStyle = (currentStyle == doc::Style::italic) ? doc::Style::none : doc::Style::italic;
489+
}
490+
} else {
491+
currentText.push_back(c);
492+
}
493+
} else {
494+
currentText.push_back(c);
495+
}
496+
}
497+
498+
// Whatever style we started, we should end it because
499+
// we reached the end of the string without a closing
500+
// marker.
501+
currentStyle = doc::Style::none;
502+
flushCurrentText();
503+
504+
return result;
505+
}
506+
400507
void
401508
JavadocVisitor::
402509
visitChildren(
403510
Comment const* C)
404511
{
405-
auto const it0 = it_;
406-
auto const end0 = end_;
407-
it_ = C->child_begin();
408-
end_ = C->child_end();
512+
ScopeExitRestore s1(it_, C->child_begin());
513+
ScopeExitRestore s2(end_, C->child_end());
409514
while(it_ != end_)
410515
{
411516
visit(*it_);
412517
++it_; // must happen after
413518
}
414-
it_ = it0;
415-
end_ = end0;
519+
520+
if (!block_)
521+
{
522+
return;
523+
}
524+
525+
bool const isVerbatim = block_->kind == doc::Kind::code;
526+
if (isVerbatim)
527+
{
528+
return;
529+
}
530+
531+
// Merge consecutive plain text nodes in the current block
532+
auto it = block_->children.begin();
533+
while(it != block_->children.end())
534+
{
535+
auto& child = *it;
536+
if (child.get()->kind == doc::Kind::text)
537+
{
538+
auto* text = dynamic_cast<doc::Text*>(child.get());
539+
MRDOCS_ASSERT(text);
540+
auto next = std::next(it);
541+
if(next != block_->children.end())
542+
{
543+
if(next->get()->kind == doc::Kind::text)
544+
{
545+
auto* next_text = dynamic_cast<doc::Text*>(next->get());
546+
MRDOCS_ASSERT(next_text);
547+
text->string.append(next_text->string);
548+
it = block_->children.erase(next);
549+
continue;
550+
}
551+
}
552+
}
553+
++it;
554+
}
555+
556+
// Parse any Text nodes for styled text
557+
for (auto it = block_->children.begin(); it != block_->children.end();)
558+
{
559+
MRDOCS_ASSERT(it->get());
560+
if (it->get()->kind == doc::Kind::text)
561+
{
562+
auto* text = dynamic_cast<doc::Text*>(it->get());
563+
auto styledText = parseStyled(text->string);
564+
std::size_t const offset = std::distance(block_->children.begin(), it);
565+
std::size_t const n = styledText.size();
566+
block_->children.erase(it);
567+
block_->children.insert(
568+
block_->children.begin() + offset,
569+
std::make_move_iterator(styledText.begin()),
570+
std::make_move_iterator(styledText.end()));
571+
it = block_->children.begin() + offset + n;
572+
}
573+
else
574+
{
575+
++it;
576+
}
577+
}
416578
}
417579

418580
//------------------------------------------------
@@ -462,16 +624,18 @@ visitTextComment(
462624
// If this is the first text comment in the
463625
// paragraph then remove all the leading space.
464626
// Otherwise, just remove the trailing space.
465-
if(block_->children.empty())
627+
if (block_->children.empty())
628+
{
466629
s = s.ltrim();
467-
else
468-
s = s.rtrim();
630+
}
469631

470632
// Only insert non-empty text nodes
471633
if(! s.empty())
634+
{
472635
emplaceText<doc::Text>(
473636
C->hasTrailingNewline(),
474637
ensureUTF8(s.str()));
638+
}
475639
}
476640

477641
Expected<JavadocVisitor::TagComponents>

src/lib/Gen/adoc/DocVisitor.cpp

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <llvm/Support/raw_ostream.h>
1919
#include <mrdocs/Support/RangeFor.hpp>
2020
#include <mrdocs/Support/String.hpp>
21+
#include <ranges>
2122

2223
namespace clang::mrdocs::adoc {
2324

@@ -65,7 +66,7 @@ operator()(
6566
doc::visit(*it.value,
6667
[&]<class T>(T const& text)
6768
{
68-
if constexpr(std::is_same_v<T, doc::Text>)
69+
if constexpr(std::derived_from<T, doc::Text>)
6970
{
7071
if(! text.string.empty())
7172
{
@@ -89,7 +90,7 @@ DocVisitor::
8990
operator()(
9091
doc::Heading const& I) const
9192
{
92-
fmt::format_to(ins_, "\n=== {}\n", AdocEscape(I.string));
93+
fmt::format_to(ins_, "\n=== {}\n\n", AdocEscape(I.string));
9394
}
9495

9596
// Also handles doc::Brief
@@ -103,15 +104,23 @@ operator()(
103104
{
104105
return;
105106
}
106-
bool non_empty = write(*children.front(), *this);
107-
for(auto const& child : children.subspan(1))
107+
108+
std::size_t i = 0;
109+
for (auto it = children.begin(); it != children.end(); ++it)
108110
{
109-
if (non_empty)
111+
auto& child = *it;
112+
if (i == 0)
110113
{
111-
dest_.push_back('\n');
114+
child->string = ltrim(child->string);
112115
}
113-
non_empty = write(*child, *this);
116+
if (i == children.size() - 1)
117+
{
118+
child->string = rtrim(child->string);
119+
}
120+
write(*child, *this);
121+
i = i + 1;
114122
}
123+
115124
dest_.push_back('\n');
116125
dest_.push_back('\n');
117126
}
@@ -188,37 +197,32 @@ void
188197
DocVisitor::
189198
operator()(doc::Text const& I) const
190199
{
191-
// Asciidoc text must not have leading
192-
// else they can be rendered up as code.
193-
std::string_view s = trim(I.string);
194-
// Render empty lines as paragraph delimiters.
195-
if (s.empty())
200+
if (I.string.empty())
201+
{
202+
dest_.append("\n\n");
203+
} else
196204
{
197-
s = "\n";
205+
dest_.append(AdocEscape(I.string));
198206
}
199-
dest_.append(AdocEscape(s));
200207
}
201208

202209
void
203210
DocVisitor::
204211
operator()(doc::Styled const& I) const
205212
{
206-
// VFALCO We need to apply Asciidoc escaping
207-
// depending on the contents of the string.
208-
std::string_view s = trim(I.string);
209213
switch(I.style)
210214
{
211215
case doc::Style::none:
212-
dest_.append(s);
216+
dest_.append(AdocEscape(I.string));
213217
break;
214218
case doc::Style::bold:
215-
fmt::format_to(std::back_inserter(dest_), "*{}*", s);
219+
fmt::format_to(std::back_inserter(dest_), "*{}*", AdocEscape(I.string));
216220
break;
217221
case doc::Style::mono:
218-
fmt::format_to(std::back_inserter(dest_), "`{}`", s);
222+
fmt::format_to(std::back_inserter(dest_), "`{}`", AdocEscape(I.string));
219223
break;
220224
case doc::Style::italic:
221-
fmt::format_to(std::back_inserter(dest_), "_{}_", s);
225+
fmt::format_to(std::back_inserter(dest_), "_{}_", AdocEscape(I.string));
222226
break;
223227
default:
224228
MRDOCS_UNREACHABLE();

0 commit comments

Comments
 (0)