Skip to content

Commit

Permalink
Make doc comment alignment group @params, @return, and @throws
Browse files Browse the repository at this point in the history
see #134
  • Loading branch information
jcberquist committed Dec 29, 2021
1 parent e0a7fce commit 574e607
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 65 deletions.
166 changes: 103 additions & 63 deletions models/Alignment.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ component accessors="true" {
'(?:"[^"]*"|''[^'']*''|#identifier#)', // attribute value
')?' // attribute value is optional
];
variables.docParamRegex = [
'^([ \t]*\*\s*)', // leading indentation and *
'(?!(?i:@throws|@return))', // not @throws or @return
'(@#identifier#)', // param name
'([^\r\n]*)\r?\n' // param description (rest of the line)
];
variables.docThrowsRegex = [
'^([ \t]*\*\s*)', // leading indentation and *
'(?i:(@throws\s+#identifier#))',
'([^\r\n]*)\r?\n' // throws description (rest of the line)
variables.docLineRegex = [
'^([ \t]*\*)', // leading indentation and *
'[ \t]*', // any whitespace
'(',
'(?:@throws #identifier#)', // throws
'|',
'@#identifier#', // param or return
')?', // this is optional
'[ \t]*', // any whitespace
'([^\r\n]*\r?\n)' // rest of the line
];

function init() {
Expand All @@ -56,9 +56,7 @@ component accessors="true" {
variables.propertiesPattern = patternClass.compile(propertiesRegex.toList(''), 8);
variables.paramsPattern = patternClass.compile(propertiesRegex.toList('').replace('property', 'param'), 8);
variables.attributePattern = patternClass.compile(attributeRegex.toList(''), 8);
variables.docParamPattern = patternClass.compile(docParamRegex.toList(''), 8);
variables.docThrowsPattern = patternClass.compile(docThrowsRegex.toList(''), 8);

variables.docLinePattern = patternClass.compile(docLineRegex.toList(''), 8);
return this;
}

Expand Down Expand Up @@ -166,53 +164,115 @@ component accessors="true" {
}

string function alignDocComments(required string src) {
var replacements = [];
var ranges = stringRanges.walk(src);
var replacements = [];

for (var range in ranges) {
if (range.name != 'doc_comment') {
continue;
}

var docComment = src.substring(range.start, range.end);

if (!docComment.find(chr(10))) {
continue;
}

for (var matcher in [docParamPattern.matcher(src), docThrowsPattern.matcher(src)]) {
var lf = docComment.find(chr(13)) ? chr(13) & chr(10) : chr(10);
var matcher = docLinePattern.matcher(docComment);
var index = 0;
var strRanges = {index: 1, ranges: ranges};
var indent = '';
var emptyLine = '';

var lines = {
docs: [],
params: [],
return: [],
throws: [],
maxThrowLen: 0,
maxParamLen: 0
}

while (matcher.find(index)) {
index = matcher.end();
indent = matcher.group(1);
emptyLine = indent & lf;
var restOfLine = (matcher.group(3) ?: '');
if (restOfLine.trim().len()) {
restOfLine = ' ' & restOfLine;
}

if (!inDocRange(matcher.start(2), strRanges)) {
continue;
if (isNull(matcher.group(2))) {
// this is a regular line
if (
matcher.group(0) != emptyLine ||
!lines.docs.len() ||
lines.docs.last() != emptyLine
) {
lines.docs.append(indent & restOfLine);
}
} else {
var tag = matcher.group(2);

if (tag == '@return' || tag == '@returns') {
lines.return.append(indent & ' @return' & restOfLine);
} else if (tag.startswith('@throws')) {
lines.maxThrowLen = max(lines.maxThrowLen, tag.len());
lines.throws.append([tag, restOfLine]);
} else {
lines.maxParamLen = max(lines.maxParamLen, tag.len());
lines.params.append([tag, restOfLine]);
}
}
}

var group = [matcher.toMatchResult()];
var indent = matcher.group(1);
// build the comment
var formattedLines = [];

while (true) {
matcher.region(index, len(src));
formattedLines.append(lines.docs, true);

if (matcher.lookingAt()) {
if (
inDocRange(matcher.start(2), strRanges) &&
len(indent) == len(matcher.group(1))
) {
group.append(matcher.toMatchResult());
index = matcher.end();
continue;
}
}
// params
if (lines.params.len()) {
if (formattedLines.len() && formattedLines.last() != emptyLine) {
formattedLines.append(emptyLine);
}
for (var line in lines.params) {
var formattedLine = indent & ' ' & line[1];
formattedLine &= repeatString(' ', lines.maxParamLen - line[1].len());
formattedLine &= line[2];
formattedLines.append(formattedLine)
}
}

if (arrayLen(group) > 1) {
replacements.append(parseDocParamGroup(group), true);
}
break;
// return
if (lines.return.len()) {
if (formattedLines.len() && formattedLines.last() != emptyLine) {
formattedLines.append(emptyLine);
}
formattedLines.append(lines.return, true);
}

// throws
if (lines.throws.len()) {
if (formattedLines.len() && formattedLines.last() != emptyLine) {
formattedLines.append(emptyLine);
}
for (var line in lines.throws) {
var formattedLine = indent & ' ' & line[1];
formattedLine &= repeatString(' ', lines.maxThrowLen - line[1].len());
formattedLine &= line[2];
formattedLines.append(formattedLine)
}
}
}

replacements.sort(function(a, b) {
if (a.start > b.start) return 1;
if (a.start < b.start) return -1;
return 0;
});
var formatted = docComment.listFirst(chr(10)) & chr(10);
formatted &= formattedLines.toList('');
formatted &= docComment.listLast(chr(10));
replacements.append({start: range.start, end: range.end, docComment: formatted});
}

for (var replacement in replacements.reverse()) {
src = src.substring(0, replacement.start) & replacement.line & src.substring(replacement.end);
src = src.substring(0, replacement.start) & replacement.docComment & src.substring(replacement.end);
}

return src;
Expand Down Expand Up @@ -281,26 +341,6 @@ component accessors="true" {
return longestValues;
}

private function parseDocParamGroup(group) {
var longestName = getLongestDocParamName(group);
var output = [];
for (var match in group) {
var line = match.group(2);
line &= repeatString(' ', longestName - line.len());
line &= ' ' & match.group(3).ltrim();
output.append({start: match.start(2), end: match.end(3), line: line.trim()});
}
return output;
}

private function getLongestDocParamName(group) {
var longest = 0;
for (var m in group) {
longest = max(longest, m.end(2) - m.start(2));
}
return longest;
}

private function inStringRange(idx, strRanges) {
while (
strRanges.ranges.len() >= strRanges.index &&
Expand Down
4 changes: 2 additions & 2 deletions tests/data/alignDocComments/formatted.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ component {
* @storeInContext By default, the token will be stored in the request context
* @authenticate By default, the token will be authenticated, you can disable it and do manual authentication.
*
* @return The payload for convenience
*
* @throws TokenExpiredException If the token has expired or no longer in the storage (invalidated)
* @throws TokenInvalidException If the token doesn't verify decoding
* @throws TokenNotFoundException If the token cannot be found in the headers
*
* @returns The payload for convenience
*/
struct function parseToken(
string token = discoverToken(),
Expand Down

0 comments on commit 574e607

Please sign in to comment.