@@ -520,6 +520,49 @@ llvm::Optional<HoverInfo> getHoverContents(const Expr *E, ParsedAST &AST) {
520
520
}
521
521
return llvm::None;
522
522
}
523
+
524
+ bool isParagraphLineBreak (llvm::StringRef Str, size_t LineBreakIndex) {
525
+ return Str.substr (LineBreakIndex + 1 )
526
+ .drop_while ([](auto C) { return C == ' ' || C == ' \t ' ; })
527
+ .startswith (" \n " );
528
+ };
529
+
530
+ bool isPunctuationLineBreak (llvm::StringRef Str, size_t LineBreakIndex) {
531
+ constexpr llvm::StringLiteral Punctuation = R"txt( .:,;!?)txt" ;
532
+
533
+ return LineBreakIndex > 0 && Punctuation.contains (Str[LineBreakIndex - 1 ]);
534
+ };
535
+
536
+ bool isFollowedByHardLineBreakIndicator (llvm::StringRef Str,
537
+ size_t LineBreakIndex) {
538
+ // '-'/'*' md list, '@'/'\' documentation command, '>' md blockquote,
539
+ // '#' headings, '`' code blocks
540
+ constexpr llvm::StringLiteral LinbreakIdenticators = R"txt( -*@\>#`)txt" ;
541
+
542
+ auto NextNonSpaceCharIndex = Str.find_first_not_of (' ' , LineBreakIndex + 1 );
543
+
544
+ if (NextNonSpaceCharIndex == llvm::StringRef::npos) {
545
+ return false ;
546
+ }
547
+
548
+ auto FollowedBySingleCharIndicator =
549
+ LinbreakIdenticators.find (Str[NextNonSpaceCharIndex]) !=
550
+ llvm::StringRef::npos;
551
+
552
+ auto FollowedByNumberedListIndicator =
553
+ llvm::isDigit (Str[NextNonSpaceCharIndex]) &&
554
+ NextNonSpaceCharIndex + 1 < Str.size () &&
555
+ (Str[NextNonSpaceCharIndex + 1 ] == ' .' ||
556
+ Str[NextNonSpaceCharIndex + 1 ] == ' )' );
557
+
558
+ return FollowedBySingleCharIndicator || FollowedByNumberedListIndicator;
559
+ };
560
+
561
+ bool isHardLineBreak (llvm::StringRef Str, size_t LineBreakIndex) {
562
+ return isPunctuationLineBreak (Str, LineBreakIndex) ||
563
+ isFollowedByHardLineBreakIndicator (Str, LineBreakIndex);
564
+ }
565
+
523
566
} // namespace
524
567
525
568
llvm::Optional<HoverInfo> getHover (ParsedAST &AST, Position Pos,
@@ -652,7 +695,7 @@ markup::Document HoverInfo::present() const {
652
695
}
653
696
654
697
if (!Documentation.empty ())
655
- Output. addParagraph (). appendText ( Documentation);
698
+ parseDocumentation ( Documentation, Output );
656
699
657
700
if (!Definition.empty ()) {
658
701
Output.addRuler ();
@@ -675,6 +718,45 @@ markup::Document HoverInfo::present() const {
675
718
return Output;
676
719
}
677
720
721
+ void parseDocumentation (llvm::StringRef Input, markup::Document &Output) {
722
+
723
+ constexpr auto WhiteSpaceChars = " \t\n\v\f\r " ;
724
+
725
+ auto TrimmedInput = Input.trim ();
726
+
727
+ std::string CurrentLine;
728
+
729
+ for (size_t CharIndex = 0 ; CharIndex < TrimmedInput.size ();) {
730
+ if (TrimmedInput[CharIndex] == ' \n ' ) {
731
+ // Trim whitespace infront of linebreak
732
+ const auto LastNonSpaceCharIndex =
733
+ CurrentLine.find_last_not_of (WhiteSpaceChars) + 1 ;
734
+ CurrentLine.erase (LastNonSpaceCharIndex);
735
+
736
+ if (isParagraphLineBreak (TrimmedInput, CharIndex) ||
737
+ isHardLineBreak (TrimmedInput, CharIndex)) {
738
+ // FIXME: maybe distinguish between line breaks and paragraphs
739
+ Output.addParagraph ().appendText (CurrentLine);
740
+ CurrentLine = " " ;
741
+ } else {
742
+ // Ommit linebreak
743
+ CurrentLine += ' ' ;
744
+ }
745
+
746
+ CharIndex++;
747
+ // After a linebreak always remove spaces to avoid 4 space markdown code
748
+ // blocks, also skip all additional linebreaks since they have no effect
749
+ CharIndex = TrimmedInput.find_first_not_of (WhiteSpaceChars, CharIndex);
750
+ } else {
751
+ CurrentLine += TrimmedInput[CharIndex];
752
+ CharIndex++;
753
+ }
754
+ }
755
+ if (!CurrentLine.empty ()) {
756
+ Output.addParagraph ().appendText (CurrentLine);
757
+ }
758
+ }
759
+
678
760
llvm::raw_ostream &operator <<(llvm::raw_ostream &OS,
679
761
const HoverInfo::Param &P) {
680
762
std::vector<llvm::StringRef> Output;
0 commit comments