clang-tools 24.0.0git
Markup.h
Go to the documentation of this file.
1//===--- Markup.h -------------------------------------------*- C++-*------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// A model of formatted text that can be rendered to plaintext or markdown.
10//
11//===----------------------------------------------------------------------===//
12#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MARKUP_H
13#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MARKUP_H
14
15#include "llvm/Support/raw_ostream.h"
16#include <cstddef>
17#include <memory>
18#include <string>
19#include <vector>
20
21namespace clang {
22namespace clangd {
23namespace markup {
24
25/// Holds text and knows how to lay it out. Multiple blocks can be grouped to
26/// form a document. Blocks include their own trailing newlines, container
27/// should trim them if need be.
28class Block {
29public:
30 virtual void renderEscapedMarkdown(llvm::raw_ostream &OS) const = 0;
31 virtual void renderMarkdown(llvm::raw_ostream &OS) const = 0;
32 virtual void renderPlainText(llvm::raw_ostream &OS) const = 0;
33 virtual std::unique_ptr<Block> clone() const = 0;
34 std::string asEscapedMarkdown() const;
35 std::string asMarkdown() const;
36 std::string asPlainText() const;
37
38 virtual bool isRuler() const { return false; }
39 virtual ~Block() = default;
40};
41
42/// Represents parts of the markup that can contain strings, like inline code,
43/// code block or plain text.
44/// One must introduce different paragraphs to create separate blocks.
45class Paragraph : public Block {
46public:
47 void renderEscapedMarkdown(llvm::raw_ostream &OS) const override;
48 void renderMarkdown(llvm::raw_ostream &OS) const override;
49 void renderPlainText(llvm::raw_ostream &OS) const override;
50 std::unique_ptr<Block> clone() const override;
51
52 /// Append plain text to the end of the string.
53 Paragraph &appendText(llvm::StringRef Text);
54
55 /// Append emphasized text, this translates to the * block in markdown.
56 Paragraph &appendEmphasizedText(llvm::StringRef Text);
57
58 /// Append bold text, this translates to the ** block in markdown.
59 Paragraph &appendBoldText(llvm::StringRef Text);
60
61 /// Append inline code, this translates to the ` block in markdown.
62 /// \p Preserve indicates the code span must be apparent even in plaintext.
63 Paragraph &appendCode(llvm::StringRef Code, bool Preserve = false);
64
65 /// Ensure there is space between the surrounding chunks.
66 /// Has no effect at the beginning or end of a paragraph.
67 Paragraph &appendSpace();
68
69private:
70 enum ChunkKind { PlainText, InlineCode, Bold, Emphasized };
71 struct Chunk {
72 ChunkKind Kind = PlainText;
73 // Preserve chunk markers in plaintext.
74 bool Preserve = false;
75 std::string Contents;
76 // Whether this chunk should be surrounded by whitespace.
77 // Consecutive SpaceAfter and SpaceBefore will be collapsed into one space.
78 // Code spans don't usually set this: their spaces belong "inside" the span.
79 bool SpaceBefore = false;
80 bool SpaceAfter = false;
81 };
82 std::vector<Chunk> Chunks;
83
84 /// Estimated size of the string representation of this paragraph.
85 /// Used to reserve space in the output string.
86 /// Each time paragraph content is added, this value is updated.
87 /// This is an estimate, so it may not be accurate but can help
88 /// reducing dynamically reallocating string memory.
89 unsigned EstimatedStringSize = 0;
90
91 Paragraph &appendChunk(llvm::StringRef Contents, ChunkKind K);
92
93 llvm::StringRef chooseMarker(llvm::ArrayRef<llvm::StringRef> Options,
94 llvm::StringRef Text) const;
95
96 /// Checks if the given line ends with punctuation that indicates a line break
97 /// (.:,;!?).
98 ///
99 /// If \p IsMarkdown is false, lines ending with 2 spaces are also considered
100 /// as indicating a line break. This is not needed for markdown because the
101 /// client renderer will handle this case.
102 bool punctuationIndicatesLineBreak(llvm::StringRef Line,
103 bool IsMarkdown) const;
104
105 /// Checks if the given line starts with a hard line break indicator.
106 ///
107 /// If \p IsMarkdown is true, only '@' and '\' are considered as indicators.
108 /// Otherwise, '-', '*', '@', '\', '>', '#', '`' and a digit followed by '.'
109 /// or ')' are also considered as indicators.
110 bool isHardLineBreakIndicator(llvm::StringRef Rest, bool IsMarkdown) const;
111
112 /// Checks if a hard line break should be added after the given line.
113 bool isHardLineBreakAfter(llvm::StringRef Line, llvm::StringRef Rest,
114 bool IsMarkdown) const;
115
116 /// \brief Go through the contents line by line to handle the newlines
117 /// and required spacing correctly for markdown rendering.
118 ///
119 /// Newlines are added if:
120 /// - the line ends with punctuation that indicates a line break (.:,;!?)
121 /// - the next line starts with a hard line break indicator \\ (escaped
122 /// markdown/doxygen command) or @ (doxygen command)
123 ///
124 /// This newline handling is only used when the client requests markdown
125 /// for hover/signature help content.
126 /// Markdown does not add any newlines inside paragraphs unless the user
127 /// explicitly adds them. For hover/signature help content, we still want to
128 /// add newlines in some cases to improve readability, especially when doxygen
129 /// parsing is disabled or not implemented (like for signature help).
130 /// Therefore we add newlines in the above mentioned cases.
131 ///
132 /// In addition to that, we need to consider that the user can configure
133 /// clangd to treat documentation comments as plain text, while the client
134 /// requests markdown.
135 /// In this case, all markdown syntax is escaped and will
136 /// not be rendered as expected by markdown.
137 /// Examples are lists starting with '-' or headings starting with '#'.
138 /// With the above next line heuristics, these cases are also covered by the
139 /// '\\' new line indicator.
140 ///
141 /// FIXME: The heuristic fails e.g. for lists starting with '*' because it is
142 /// also used for emphasis in markdown and should not be treated as a newline.
143 ///
144 /// \param OS The stream to render to.
145 /// \param ParagraphText The text of the paragraph to render.
146 void renderNewlinesMarkdown(llvm::raw_ostream &OS,
147 llvm::StringRef ParagraphText) const;
148
149 /// \brief Go through the contents line by line to handle the newlines
150 /// and required spacing correctly for plain text rendering.
151 ///
152 /// Newlines are added if:
153 /// - the line ends with 2 spaces and a newline follows
154 /// - the line ends with punctuation that indicates a line break (.:,;!?)
155 /// - the next line starts with a hard line break indicator (-@>#`\\ or a
156 /// digit followed by '.' or ')'), ignoring leading whitespace.
157 ///
158 /// Otherwise, newlines in the input are replaced with a single space.
159 ///
160 /// Multiple spaces are collapsed into a single space.
161 ///
162 /// Lines containing only whitespace are ignored.
163 ///
164 /// This newline handling is used when the server is configured to provide
165 /// Plaintext hover content.
166 ///
167 /// In case the client requests Markdown content:
168 /// - the newline "character" needs to be a Markdown line break (two spaces
169 /// followed by `\n`).
170 /// Otherwise the linebreak will not be rendered correctly.
171 /// This can be controlled by the \p UseMarkdownLinebreaks parameter.
172 /// - verbatim newlines are replaced with a space to replicate Markdown render
173 /// behaviour inside paragraphs.
174 ///
175 /// \param OS The stream to render to.
176 /// \param ParagraphText The text of the paragraph to render.
177 /// \param UseMarkdownLinebreaks Indicates whether to use markdown linebreaks
178 void renderNewlinesPlaintext(llvm::raw_ostream &OS,
179 llvm::StringRef ParagraphText,
180 bool UseMarkdownLinebreaks) const;
181};
182
183/// Represents a sequence of one or more documents. Knows how to print them in a
184/// list like format, e.g. by prepending with "- " and indentation.
185class BulletList : public Block {
186public:
187 BulletList();
188 ~BulletList();
189
190 // A BulletList rendered in markdown is a tight list if it is not a nested
191 // list and no item contains multiple paragraphs. Otherwise, it is a loose
192 // list.
193 void renderEscapedMarkdown(llvm::raw_ostream &OS) const override;
194 void renderMarkdown(llvm::raw_ostream &OS) const override;
195 void renderPlainText(llvm::raw_ostream &OS) const override;
196 std::unique_ptr<Block> clone() const override;
197
198 class Document &addItem();
199
200private:
201 std::vector<class Document> Items;
202};
203
204/// A format-agnostic representation for structured text. Allows rendering into
205/// markdown and plaintext.
206class Document {
207public:
208 Document() = default;
209 Document(const Document &Other) { *this = Other; }
210 Document &operator=(const Document &);
211 Document(Document &&) = default;
212 Document &operator=(Document &&) = default;
213
214 void append(Document Other);
215
216 /// Adds a semantical block that will be separate from others.
217 Paragraph &addParagraph();
218 /// Inserts a horizontal separator to the document.
219 void addRuler();
220 /// Adds a block of code. This translates to a ``` block in markdown. In plain
221 /// text representation, the code block will be surrounded by newlines.
222 void addCodeBlock(std::string Code, std::string Language = "cpp");
223 /// Heading is a special type of paragraph that will be prepended with \p
224 /// Level many '#'s in markdown.
225 Paragraph &addHeading(size_t Level);
226
227 BulletList &addBulletList();
228
229 /// Doesn't contain any trailing newlines and escaped markdown syntax.
230 /// It is expected that the result of this function
231 /// is rendered as markdown.
232 std::string asEscapedMarkdown() const;
233 /// Doesn't contain any trailing newlines.
234 /// It is expected that the result of this function
235 /// is rendered as markdown.
236 std::string asMarkdown() const;
237 /// Doesn't contain any trailing newlines.
238 std::string asPlainText() const;
239
240private:
241 std::vector<std::unique_ptr<Block>> Children;
242};
243} // namespace markup
244} // namespace clangd
245} // namespace clang
246
247#endif
Holds text and knows how to lay it out.
Definition Markup.h:28
std::string asEscapedMarkdown() const
Definition Markup.cpp:457
virtual bool isRuler() const
Definition Markup.h:38
virtual std::unique_ptr< Block > clone() const =0
virtual void renderPlainText(llvm::raw_ostream &OS) const =0
virtual ~Block()=default
virtual void renderMarkdown(llvm::raw_ostream &OS) const =0
virtual void renderEscapedMarkdown(llvm::raw_ostream &OS) const =0
std::string asMarkdown() const
Definition Markup.cpp:464
std::string asPlainText() const
Definition Markup.cpp:471
Represents parts of the markup that can contain strings, like inline code, code block or plain text.
Definition Markup.h:45
void renderPlainText(llvm::raw_ostream &OS) const override
Definition Markup.cpp:703
Paragraph & appendEmphasizedText(llvm::StringRef Text)
Append emphasized text, this translates to the * block in markdown.
Definition Markup.cpp:801
std::string asMarkdown() const
Doesn't contain any trailing newlines.
Paragraph & appendText(llvm::StringRef Text)
Append plain text to the end of the string.
Definition Markup.cpp:792
std::string asEscapedMarkdown() const
Doesn't contain any trailing newlines and escaped markdown syntax.
void addCodeBlock(std::string Code, std::string Language="cpp")
Go through the contents line by line to handle the newlines and required spacing correctly for plain ...
std::string asPlainText() const
Doesn't contain any trailing newlines.
void renderEscapedMarkdown(llvm::raw_ostream &OS) const override
Definition Markup.cpp:506
void renderNewlinesMarkdown(llvm::raw_ostream &OS, llvm::StringRef ParagraphText) const
Go through the contents line by line to handle the newlines and required spacing correctly for markdo...
Definition Markup.cpp:478
Paragraph & addHeading(size_t Level)
Heading is a special type of paragraph that will be prepended with Level many '#'s in markdown.
std::unique_ptr< Block > clone() const override
Definition Markup.cpp:598
bool isHardLineBreakAfter(llvm::StringRef Line, llvm::StringRef Rest, bool IsMarkdown) const
Checks if a hard line break should be added after the given line.
Definition Markup.cpp:651
void renderMarkdown(llvm::raw_ostream &OS) const override
Definition Markup.cpp:546
Paragraph & appendBoldText(llvm::StringRef Text)
Append bold text, this translates to the ** block in markdown.
Definition Markup.cpp:806
bool isHardLineBreakIndicator(llvm::StringRef Rest, bool IsMarkdown) const
Append inline code, this translates to the ` block in markdown.
Definition Markup.cpp:622
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//