Files
swift-mirror/lib/Markup/LineList.cpp
David Farler 09b486dbcd ObjC Header missing comment markers for some newline cases
Printing Doxygen isn't inside an XML tag, so we can't just print
code block content, which may have new lines without a doc comment
marker. Also, be more robust with indentation.

+ Tests.

rdar://problem/20703026

Swift SVN r27851
2015-04-28 01:32:54 +00:00

184 lines
5.8 KiB
C++

//===--- LineList.h - Data structures for Markup parsing ------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/AST/RawComment.h"
#include "swift/Basic/PrimitiveParsing.h"
#include "swift/Markup/LineList.h"
#include "swift/Markup/Markup.h"
using namespace llvm;
using namespace markup;
std::string LineList::str() const {
std::string Result;
raw_string_ostream Stream(Result);
if (Lines.empty())
return "";
auto FirstLine = Lines.begin();
while (FirstLine->Text.empty() && FirstLine != Lines.end())
++FirstLine;
if (FirstLine == Lines.end())
return "";
auto InitialIndentation = measureIndentation(FirstLine->Text);
for (auto Line = FirstLine; Line != Lines.end(); ++Line) {
auto Drop = std::min(InitialIndentation, Line->FirstNonspaceOffset);
Stream << Line->Text.drop_front(Drop) << "\n";
}
Stream.flush();
return Result;
}
size_t llvm::markup::measureIndentation(StringRef Text) {
size_t Col = 0;
for (size_t i = 0, e = Text.size(); i != e; ++i) {
if (Text[i] == ' ' || Text[i] == '\v' || Text[i] == '\f') {
Col++;
continue;
}
if (Text[i] == '\t') {
Col += ((i + 8) / 8) * 8;
}
return i;
}
return Text.size();
}
void LineListBuilder::addLine(llvm::StringRef Text, swift::SourceRange Range) {
Lines.push_back({Text, Range});
}
LineList LineListBuilder::takeLineList() const {
return LineList(Context.allocateCopy(ArrayRef<Line>(Lines)));
}
static unsigned measureASCIIArt(StringRef S, unsigned NumLeadingSpaces) {
StringRef Spaces = S.substr(0, NumLeadingSpaces);
if (Spaces.size() != NumLeadingSpaces)
return 0;
if (Spaces.find_first_not_of(' ') != StringRef::npos)
return 0;
S = S.drop_front(NumLeadingSpaces);
if (S.startswith(" * "))
return NumLeadingSpaces + 3;
if (S.startswith(" *\n") || S.startswith(" *\n\r"))
return NumLeadingSpaces + 2;
return 0;
}
LineList MarkupContext::getLineList(swift::RawComment RC) {
LineListBuilder Builder(*this);
for (const auto &C : RC.Comments) {
if (C.isLine()) {
// Skip comment marker.
unsigned CommentMarkerBytes = 2 + (C.isOrdinary() ? 0 : 1);
StringRef Cleaned = C.RawText.drop_front(CommentMarkerBytes);
// Drop trailing newline.
Cleaned = Cleaned.rtrim("\n\r");
auto CleanedStartLoc =
C.Range.getStart().getAdvancedLocOrInvalid(CommentMarkerBytes);
auto CleanedEndLoc =
C.Range.getStart().getAdvancedLocOrInvalid(Cleaned.size());
Builder.addLine(Cleaned, { CleanedStartLoc, CleanedEndLoc });
} else {
// Skip comment markers at the beginning and at the end.
unsigned CommentMarkerBytes = 2 + (C.isOrdinary() ? 0 : 1);
StringRef Cleaned = C.RawText.drop_front(CommentMarkerBytes).drop_back(2);
swift::SourceLoc CleanedStartLoc =
C.Range.getStart().getAdvancedLocOrInvalid(CommentMarkerBytes);
// Determine if we have leading decorations in this block comment.
bool HasASCIIArt = false;
if (swift::startsWithNewline(Cleaned)) {
Builder.addLine(Cleaned.substr(0, 0), { C.Range.getStart(),
C.Range.getStart() });
unsigned NewlineBytes = swift::measureNewline(Cleaned);
Cleaned = Cleaned.drop_front(NewlineBytes);
CleanedStartLoc = CleanedStartLoc.getAdvancedLocOrInvalid(NewlineBytes);
HasASCIIArt = measureASCIIArt(Cleaned, C.StartColumn - 1) != 0;
}
while (!Cleaned.empty()) {
size_t Pos = Cleaned.find_first_of("\n\r");
if (Pos == StringRef::npos)
Pos = Cleaned.size();
// Skip over ASCII art, if present.
if (HasASCIIArt)
if (unsigned ASCIIArtBytes =
measureASCIIArt(Cleaned, C.StartColumn - 1)) {
Cleaned = Cleaned.drop_front(ASCIIArtBytes);
CleanedStartLoc =
CleanedStartLoc.getAdvancedLocOrInvalid(ASCIIArtBytes);
Pos -= ASCIIArtBytes;
}
StringRef Line = Cleaned.substr(0, Pos);
auto CleanedEndLoc = CleanedStartLoc.getAdvancedLocOrInvalid(Pos);
Cleaned = Cleaned.drop_front(Pos);
unsigned NewlineBytes = swift::measureNewline(Cleaned);
Cleaned = Cleaned.drop_front(NewlineBytes);
Pos += NewlineBytes;
CleanedStartLoc = CleanedStartLoc.getAdvancedLocOrInvalid(Pos);
Builder.addLine(Line, { CleanedStartLoc, CleanedEndLoc });
}
}
}
return Builder.takeLineList();
}
LineList LineList::subListWithRange(MarkupContext &MC, size_t StartLine,
size_t EndLine, size_t StartColumn,
size_t EndColumn) const {
auto TrimmedLines = ArrayRef<Line>(Lines.begin() + StartLine,
Lines.begin() + EndLine);
LineListBuilder Builder(MC);
if (TrimmedLines.empty())
return LineList();
auto FirstLine = TrimmedLines.begin();
auto End = TrimmedLines.end();
auto LastLine = End - 1;
for (auto Line = FirstLine; Line != End; ++Line) {
auto T = Line->Text;
auto RangeStart = Line->Range.Start;
auto RangeEnd = Line->Range.End;
if (Line == LastLine) {
T = T.drop_back(T.size() - EndColumn);
RangeEnd = RangeStart.getAdvancedLocOrInvalid(EndColumn);
}
if (Line == FirstLine) {
T = T.drop_front(StartColumn);
RangeStart = RangeStart.getAdvancedLocOrInvalid(StartColumn);
}
Builder.addLine(T, {RangeStart, RangeEnd});
}
return Builder.takeLineList();
}