Skip to content

Commit a6f550e

Browse files
committed
[clangd] Add a ruler after header in hover
Reviewers: sammccall Subscribers: ilya-biryukov, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D72622 (cherry picked from commit d74a3d4)
1 parent ef91746 commit a6f550e

File tree

5 files changed

+103
-15
lines changed

5 files changed

+103
-15
lines changed

clang-tools-extra/clangd/FormattedString.cpp

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "llvm/Support/FormatVariadic.h"
1717
#include "llvm/Support/raw_ostream.h"
1818
#include <cstddef>
19+
#include <iterator>
1920
#include <memory>
2021
#include <string>
2122
#include <vector>
@@ -117,16 +118,52 @@ std::string renderBlocks(llvm::ArrayRef<std::unique_ptr<Block>> Children,
117118
void (Block::*RenderFunc)(llvm::raw_ostream &) const) {
118119
std::string R;
119120
llvm::raw_string_ostream OS(R);
120-
for (auto &C : Children)
121+
122+
// Trim rulers.
123+
Children = Children.drop_while(
124+
[](const std::unique_ptr<Block> &C) { return C->isRuler(); });
125+
auto Last = llvm::find_if(
126+
llvm::reverse(Children),
127+
[](const std::unique_ptr<Block> &C) { return !C->isRuler(); });
128+
Children = Children.drop_back(Children.end() - Last.base());
129+
130+
bool LastBlockWasRuler = true;
131+
for (const auto &C : Children) {
132+
if (C->isRuler() && LastBlockWasRuler)
133+
continue;
134+
LastBlockWasRuler = C->isRuler();
121135
((*C).*RenderFunc)(OS);
122-
return llvm::StringRef(OS.str()).trim().str();
136+
}
137+
138+
// Get rid of redundant empty lines introduced in plaintext while imitating
139+
// padding in markdown.
140+
std::string AdjustedResult;
141+
llvm::StringRef TrimmedText(OS.str());
142+
TrimmedText = TrimmedText.trim();
143+
144+
llvm::copy_if(TrimmedText, std::back_inserter(AdjustedResult),
145+
[&TrimmedText](const char &C) {
146+
return !llvm::StringRef(TrimmedText.data(),
147+
&C - TrimmedText.data() + 1)
148+
// We allow at most two newlines.
149+
.endswith("\n\n\n");
150+
});
151+
152+
return AdjustedResult;
123153
}
124154

125-
// Puts a vertical space between blocks inside a document.
126-
class Spacer : public Block {
155+
// Seperates two blocks with extra spacing. Note that it might render strangely
156+
// in vscode if the trailing block is a codeblock, see
157+
// https://github.com/microsoft/vscode/issues/88416 for details.
158+
class Ruler : public Block {
127159
public:
128-
void renderMarkdown(llvm::raw_ostream &OS) const override { OS << '\n'; }
160+
void renderMarkdown(llvm::raw_ostream &OS) const override {
161+
// Note that we need an extra new line before the ruler, otherwise we might
162+
// make previous block a title instead of introducing a ruler.
163+
OS << "\n---\n";
164+
}
129165
void renderPlainText(llvm::raw_ostream &OS) const override { OS << '\n'; }
166+
bool isRuler() const override { return true; }
130167
};
131168

132169
class CodeBlock : public Block {
@@ -272,7 +309,7 @@ Paragraph &Document::addParagraph() {
272309
return *static_cast<Paragraph *>(Children.back().get());
273310
}
274311

275-
void Document::addSpacer() { Children.push_back(std::make_unique<Spacer>()); }
312+
void Document::addRuler() { Children.push_back(std::make_unique<Ruler>()); }
276313

277314
void Document::addCodeBlock(std::string Code, std::string Language) {
278315
Children.emplace_back(

clang-tools-extra/clangd/FormattedString.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class Block {
3333
std::string asMarkdown() const;
3434
std::string asPlainText() const;
3535

36+
virtual bool isRuler() const { return false; }
3637
virtual ~Block() = default;
3738
};
3839

@@ -82,8 +83,8 @@ class Document {
8283
public:
8384
/// Adds a semantical block that will be separate from others.
8485
Paragraph &addParagraph();
85-
/// Inserts a vertical space into the document.
86-
void addSpacer();
86+
/// Inserts a horizontal separator to the document.
87+
void addRuler();
8788
/// Adds a block of code. This translates to a ``` block in markdown. In plain
8889
/// text representation, the code block will be surrounded by newlines.
8990
void addCodeBlock(std::string Code, std::string Language = "cpp");

clang-tools-extra/clangd/Hover.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,8 @@ markup::Document HoverInfo::present() const {
532532
Header.appendCode(*Type);
533533
}
534534

535+
// Put a linebreak after header to increase readability.
536+
Output.addRuler();
535537
// For functions we display signature in a list form, e.g.:
536538
// - `bool param1`
537539
// - `int param2 = 5`
@@ -555,6 +557,7 @@ markup::Document HoverInfo::present() const {
555557
Output.addParagraph().appendText(Documentation);
556558

557559
if (!Definition.empty()) {
560+
Output.addRuler();
558561
std::string ScopeComment;
559562
// Drop trailing "::".
560563
if (!LocalScope.empty()) {

clang-tools-extra/clangd/unittests/FormattedStringTests.cpp

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,37 @@ bar)pt";
136136
EXPECT_EQ(D.asPlainText(), ExpectedText);
137137
}
138138

139-
TEST(Document, Spacer) {
139+
TEST(Document, Ruler) {
140140
Document D;
141141
D.addParagraph().appendText("foo");
142-
D.addSpacer();
142+
D.addRuler();
143+
144+
// Ruler followed by paragraph.
143145
D.addParagraph().appendText("bar");
144-
EXPECT_EQ(D.asMarkdown(), "foo \n\nbar");
146+
EXPECT_EQ(D.asMarkdown(), "foo \n\n---\nbar");
147+
EXPECT_EQ(D.asPlainText(), "foo\n\nbar");
148+
149+
D = Document();
150+
D.addParagraph().appendText("foo");
151+
D.addRuler();
152+
D.addCodeBlock("bar");
153+
// Ruler followed by a codeblock.
154+
EXPECT_EQ(D.asMarkdown(), "foo \n\n---\n```cpp\nbar\n```");
145155
EXPECT_EQ(D.asPlainText(), "foo\n\nbar");
156+
157+
// Ruler followed by another ruler
158+
D = Document();
159+
D.addParagraph().appendText("foo");
160+
D.addRuler();
161+
D.addRuler();
162+
EXPECT_EQ(D.asMarkdown(), "foo");
163+
EXPECT_EQ(D.asPlainText(), "foo");
164+
165+
// Multiple rulers between blocks
166+
D.addRuler();
167+
D.addParagraph().appendText("foo");
168+
EXPECT_EQ(D.asMarkdown(), "foo \n\n---\nfoo");
169+
EXPECT_EQ(D.asPlainText(), "foo\n\nfoo");
146170
}
147171

148172
TEST(Document, Heading) {
@@ -182,15 +206,11 @@ foo
182206
foo
183207
```)md";
184208
EXPECT_EQ(D.asMarkdown(), ExpectedMarkdown);
185-
// FIXME: we shouldn't have 2 empty lines in between. A solution might be
186-
// having a `verticalMargin` method for blocks, and let container insert new
187-
// lines according to that before/after blocks.
188209
ExpectedPlainText =
189210
R"pt(foo
190211
bar
191212
baz
192213
193-
194214
foo)pt";
195215
EXPECT_EQ(D.asPlainText(), ExpectedPlainText);
196216
}

clang-tools-extra/clangd/unittests/HoverTests.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,7 @@ TEST(Hover, Present) {
16991699
HI.NamespaceScope.emplace();
17001700
},
17011701
R"(class foo
1702+
17021703
documentation
17031704
17041705
template <typename T, typename C = bool> class Foo {})",
@@ -1722,6 +1723,7 @@ template <typename T, typename C = bool> class Foo {})",
17221723
HI.Definition = "ret_type foo(params) {}";
17231724
},
17241725
R"(function foo → ret_type
1726+
17251727
-
17261728
- type
17271729
- type foo
@@ -1740,6 +1742,7 @@ ret_type foo(params) {})",
17401742
HI.Definition = "def";
17411743
},
17421744
R"(variable foo : type
1745+
17431746
Value = value
17441747
17451748
// In test::bar
@@ -1765,6 +1768,30 @@ TEST(Hover, PresentHeadings) {
17651768
EXPECT_EQ(HI.present().asMarkdown(), "### variable `foo` \\: `type`");
17661769
}
17671770

1771+
// This is a separate test as rulers behave differently in markdown vs
1772+
// plaintext.
1773+
TEST(Hover, PresentRulers) {
1774+
HoverInfo HI;
1775+
HI.Kind = index::SymbolKind::Variable;
1776+
HI.Name = "foo";
1777+
HI.Value = "val";
1778+
HI.Definition = "def";
1779+
1780+
EXPECT_EQ(HI.present().asMarkdown(), R"md(### variable `foo`
1781+
1782+
---
1783+
Value \= `val`
1784+
1785+
---
1786+
```cpp
1787+
def
1788+
```)md");
1789+
EXPECT_EQ(HI.present().asPlainText(), R"pt(variable foo
1790+
1791+
Value = val
1792+
1793+
def)pt");
1794+
}
17681795
} // namespace
17691796
} // namespace clangd
17701797
} // namespace clang

0 commit comments

Comments
 (0)