| 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 | |