Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 96 additions & 21 deletions src/dparse/trivia.d
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,12 @@ unittest
"/**b1\n*b2\n*b3*/",
"/**c1\n *c2\n *c3*/",
"/**d1\n *d2\n *d3\n*/",
"///a\fbc\n///def"
"///a\fbc\n///def",
// code fence directly before the closing delimiter (Phobos writeln
// style): the fence lines and their content are not decorated
"/**\n * a\n * Example:\n * c.\n---\ncode\n---\n */",
"/**\n * a\n---\ncode\n---\n * b\n */",
"/++\n + a\n + Example:\n + c.\n---\ncode\n---\n +/"
];
string[] outputs = [
"",
Expand All @@ -167,7 +172,10 @@ unittest
"b1\nb2\nb3",
"c1\nc2\nc3",
"d1\nd2\nd3",
"a\fbc\ndef"
"a\fbc\ndef",
"a\nExample:\n c.\n---\ncode\n---",
"a\n---\ncode\n---\nb",
"a\nExample:\n c.\n---\ncode\n---"
];

// tests where * and + are not interchangeable
Expand Down Expand Up @@ -331,33 +339,100 @@ private:
const size_t hi = (lines[$-1].length > 1 &&
(lines[$-1][0] == commentChar || lines[$-1][0..2] == pattern[patternIndex]))
? lines.length : lines.length-1;
// deco with a leading white
foreach (const i; lo .. hi)
// DDoc code fences (lines of `---`) and their content are not
// decorated even in an otherwise fully decorated comment (e.g.
// Phobos writeln). Lines inside a fence must not participate in
// the allDecorated detection, and must not have decoration
// stripped (there is none to strip).
// Returns true when every line in [lo, hi) is decorated (with the
// given leading pattern), a fence line, or content inside a fence.
// `pattern2` is the two-char leading pattern (" *" / "++"), while
// `pattern1` is the single-char decoration ("*" / "+").
static bool allLinesDecorated(in char[][] lines, size_t lo, size_t hi,
const char[2] pattern2, char pattern1) @safe @nogc pure nothrow
{
if (lines[i].length < 2)
break;
else if (lines[i][0..2] != pattern[patternIndex])
break;
else if (i == hi-1)
allDecorated = true;
}
// deco w/o leading white
if (!allDecorated)
bool inFence;
foreach (const i; lo .. hi)
{
if (lines[i].length == 0)
break;
if (lines[i][0] != commentChar)
break;
else if (i == hi-1)
allDecorated = true;
{
if (lines[i].length == 0)
{
// empty lines are valid inside a fence; outside one
// they end the decoration scan (like the original
// break-on-empty behavior)
if (inFence)
continue;
return false;
}
else if (lines[i][0] == pattern1
|| lines[i].length >= 2 && lines[i][0..2] == pattern2)
continue;
else if (isFenceLine(lines[i]))
inFence = !inFence;
else if (!inFence)
return false;
// decorated content inside a fence: keep scanning
}
return !inFence;
}

// deco with a leading white
allDecorated = allLinesDecorated(lines, lo, hi,
pattern[patternIndex], commentChar);
if (!allDecorated)
return;

const size_t indexToChange = (lines[lo][0] == commentChar) ? 0 : 1;
// Does the comment contain a code fence? Fence lines and their
// content are undecorated, which changes how the decoration must
// be removed (see below).
bool hasFence;
foreach (ref line; lines[lo .. hi])
line[indexToChange] = ' ';
if (isFenceLine(line))
hasFence = true;
bool inFence;
foreach (ref line; lines[lo .. hi])
{
if (isFenceLine(line))
inFence = !inFence;
else if (!inFence)
{
if (!hasFence)
{
line[indexToChange] = ' ';
continue;
}
// The comment mixes decorated lines with undecorated
// fence content, so stripLeft cannot remove the decoration
// column (the fence lines have no leading whitespace).
// Remove the leading whitespace, the decoration marker and
// ONE separator whitespace by shifting the content left in
// place; relative indentation beyond that is preserved.
size_t start;
while (start < line.length && line[start].isWhite)
start++;
if (start < line.length && line[start] == commentChar)
start++;
if (start < line.length && line[start].isWhite)
start++;
foreach (const j; start .. line.length)
line[j - start] = line[j];
line = line[0 .. $ - start];
}
}
}

/// A DDoc code fence line: `---` possibly indented, with optional
/// trailing whitespace.
static bool isFenceLine(const char[] line) @safe @nogc pure nothrow
{
size_t i;
while (i < line.length && line[i].isWhite)
i++;
size_t dashes;
while (i + dashes < line.length && line[i + dashes] == '-')
dashes++;
return dashes >= 3
&& i + dashes == line.length;
}

void stripLeft() @safe @nogc pure nothrow
Expand Down
Loading