brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.6 KiB · 219a7da Raw
240 lines · c
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-exception6//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_H13#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_MARKUP_H14 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 to26/// form a document. Blocks include their own trailing newlines, container27/// 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 help88  /// 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 break97  /// (.:,;!?).98  ///99  /// If \p IsMarkdown is false, lines ending with 2 spaces are also considered100  /// as indicating a line break. This is not needed for markdown because the101  /// 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 newlines117  /// 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 \\ (escaped122  /// markdown/doxygen command) or @ (doxygen command)123  ///124  /// This newline handling is only used when the client requests markdown125  /// for hover/signature help content.126  /// Markdown does not add any newlines inside paragraphs unless the user127  /// explicitly adds them. For hover/signature help content, we still want to128  /// add newlines in some cases to improve readability, especially when doxygen129  /// 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 configure133  /// clangd to treat documentation comments as plain text, while the client134  /// requests markdown.135  /// In this case, all markdown syntax is escaped and will136  /// 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 the139  /// '\\' new line indicator.140  ///141  /// FIXME: The heuristic fails e.g. for lists starting with '*' because it is142  /// 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 newlines150  /// and required spacing correctly for plain text rendering.151  ///152  /// Newlines are added if:153  /// - the line ends with 2 spaces and a newline follows154  /// - the line ends with punctuation that indicates a line break (.:,;!?)155  /// - the next line starts with a hard line break indicator (-@>#`\\ or a156  ///   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 only used when the client requests plain165  /// text for hover/signature help content.166  /// Therefore with this approach we mimic the behavior of markdown rendering167  /// for these clients.168  ///169  /// \param OS The stream to render to.170  /// \param ParagraphText The text of the paragraph to render.171  void renderNewlinesPlaintext(llvm::raw_ostream &OS,172                               llvm::StringRef ParagraphText) const;173};174 175/// Represents a sequence of one or more documents. Knows how to print them in a176/// list like format, e.g. by prepending with "- " and indentation.177class BulletList : public Block {178public:179  BulletList();180  ~BulletList();181 182  // A BulletList rendered in markdown is a tight list if it is not a nested183  // list and no item contains multiple paragraphs. Otherwise, it is a loose184  // list.185  void renderEscapedMarkdown(llvm::raw_ostream &OS) const override;186  void renderMarkdown(llvm::raw_ostream &OS) const override;187  void renderPlainText(llvm::raw_ostream &OS) const override;188  std::unique_ptr<Block> clone() const override;189 190  class Document &addItem();191 192private:193  std::vector<class Document> Items;194};195 196/// A format-agnostic representation for structured text. Allows rendering into197/// markdown and plaintext.198class Document {199public:200  Document() = default;201  Document(const Document &Other) { *this = Other; }202  Document &operator=(const Document &);203  Document(Document &&) = default;204  Document &operator=(Document &&) = default;205 206  void append(Document Other);207 208  /// Adds a semantical block that will be separate from others.209  Paragraph &addParagraph();210  /// Inserts a horizontal separator to the document.211  void addRuler();212  /// Adds a block of code. This translates to a ``` block in markdown. In plain213  /// text representation, the code block will be surrounded by newlines.214  void addCodeBlock(std::string Code, std::string Language = "cpp");215  /// Heading is a special type of paragraph that will be prepended with \p216  /// Level many '#'s in markdown.217  Paragraph &addHeading(size_t Level);218 219  BulletList &addBulletList();220 221  /// Doesn't contain any trailing newlines and escaped markdown syntax.222  /// It is expected that the result of this function223  /// is rendered as markdown.224  std::string asEscapedMarkdown() const;225  /// Doesn't contain any trailing newlines.226  /// It is expected that the result of this function227  /// is rendered as markdown.228  std::string asMarkdown() const;229  /// Doesn't contain any trailing newlines.230  std::string asPlainText() const;231 232private:233  std::vector<std::unique_ptr<Block>> Children;234};235} // namespace markup236} // namespace clangd237} // namespace clang238 239#endif240