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 |
14 | |
15 | #include "llvm/Support/raw_ostream.h" |
16 | #include <cstddef> |
17 | #include <memory> |
18 | #include <string> |
19 | #include <vector> |
20 | |
21 | namespace clang { |
22 | namespace clangd { |
23 | namespace 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. |
28 | class Block { |
29 | public: |
30 | virtual void renderMarkdown(llvm::raw_ostream &OS) const = 0; |
31 | virtual void renderPlainText(llvm::raw_ostream &OS) const = 0; |
32 | virtual std::unique_ptr<Block> clone() const = 0; |
33 | std::string asMarkdown() const; |
34 | std::string asPlainText() const; |
35 | |
36 | virtual bool isRuler() const { return false; } |
37 | virtual ~Block() = default; |
38 | }; |
39 | |
40 | /// Represents parts of the markup that can contain strings, like inline code, |
41 | /// code block or plain text. |
42 | /// One must introduce different paragraphs to create separate blocks. |
43 | class Paragraph : public Block { |
44 | public: |
45 | void renderMarkdown(llvm::raw_ostream &OS) const override; |
46 | void renderPlainText(llvm::raw_ostream &OS) const override; |
47 | std::unique_ptr<Block> clone() const override; |
48 | |
49 | /// Append plain text to the end of the string. |
50 | Paragraph &appendText(llvm::StringRef Text); |
51 | |
52 | /// Append inline code, this translates to the ` block in markdown. |
53 | /// \p Preserve indicates the code span must be apparent even in plaintext. |
54 | Paragraph &appendCode(llvm::StringRef Code, bool Preserve = false); |
55 | |
56 | /// Ensure there is space between the surrounding chunks. |
57 | /// Has no effect at the beginning or end of a paragraph. |
58 | Paragraph &appendSpace(); |
59 | |
60 | private: |
61 | struct Chunk { |
62 | enum { |
63 | PlainText, |
64 | InlineCode, |
65 | } Kind = PlainText; |
66 | // Preserve chunk markers in plaintext. |
67 | bool Preserve = false; |
68 | std::string Contents; |
69 | // Whether this chunk should be surrounded by whitespace. |
70 | // Consecutive SpaceAfter and SpaceBefore will be collapsed into one space. |
71 | // Code spans don't usually set this: their spaces belong "inside" the span. |
72 | bool SpaceBefore = false; |
73 | bool SpaceAfter = false; |
74 | }; |
75 | std::vector<Chunk> Chunks; |
76 | }; |
77 | |
78 | /// Represents a sequence of one or more documents. Knows how to print them in a |
79 | /// list like format, e.g. by prepending with "- " and indentation. |
80 | class BulletList : public Block { |
81 | public: |
82 | BulletList(); |
83 | ~BulletList(); |
84 | |
85 | void renderMarkdown(llvm::raw_ostream &OS) const override; |
86 | void renderPlainText(llvm::raw_ostream &OS) const override; |
87 | std::unique_ptr<Block> clone() const override; |
88 | |
89 | class Document &addItem(); |
90 | |
91 | private: |
92 | std::vector<class Document> Items; |
93 | }; |
94 | |
95 | /// A format-agnostic representation for structured text. Allows rendering into |
96 | /// markdown and plaintext. |
97 | class Document { |
98 | public: |
99 | Document() = default; |
100 | Document(const Document &Other) { *this = Other; } |
101 | Document &operator=(const Document &); |
102 | Document(Document &&) = default; |
103 | Document &operator=(Document &&) = default; |
104 | |
105 | void append(Document Other); |
106 | |
107 | /// Adds a semantical block that will be separate from others. |
108 | Paragraph &addParagraph(); |
109 | /// Inserts a horizontal separator to the document. |
110 | void addRuler(); |
111 | /// Adds a block of code. This translates to a ``` block in markdown. In plain |
112 | /// text representation, the code block will be surrounded by newlines. |
113 | void addCodeBlock(std::string Code, std::string Language = "cpp" ); |
114 | /// Heading is a special type of paragraph that will be prepended with \p |
115 | /// Level many '#'s in markdown. |
116 | Paragraph &addHeading(size_t Level); |
117 | |
118 | BulletList &addBulletList(); |
119 | |
120 | /// Doesn't contain any trailing newlines. |
121 | /// We try to make the markdown human-readable, e.g. avoid extra escaping. |
122 | /// At least one client (coc.nvim) displays the markdown verbatim! |
123 | std::string asMarkdown() const; |
124 | /// Doesn't contain any trailing newlines. |
125 | std::string asPlainText() const; |
126 | |
127 | private: |
128 | std::vector<std::unique_ptr<Block>> Children; |
129 | }; |
130 | } // namespace markup |
131 | } // namespace clangd |
132 | } // namespace clang |
133 | |
134 | #endif |
135 | |