blob: fd4f8efd9b833ba52ffd395200b0514a2483f635 [file] [log] [blame] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// Given markdown formatted text [data] and a line [width], return a
/// line-wrapped dartdoc comment accounting for any [leadingWhitespace] the
/// comment should have.
List<String> formatDocs(String data, int width, [int leadingWhitespace = 0]) {
// TODO(devoncarew): Look at combining soft line breaks in the markdown in
// order to better reflow the returned dartdoc comments (i.e., only have line
// breaks for markdown paragraphs).
final lines = data.split('\n');
final output = <String>[];
width -= leadingWhitespace;
width -= '/// '.length;
var inCodeFence = false;
for (var line in lines) {
if (inCodeFence) {
output.add(line);
if (line.startsWith('```')) {
inCodeFence = false;
}
} else {
final ltrim = line.trimLeft();
if (line.startsWith('> ')) {
final wrapped = _wrap(line.substring(2), width - 2);
output.addAll(wrapped.map((l) => '> $l'));
} else if (line.startsWith('- ')) {
final wrapped = _wrap(line.substring(2), width - 2);
output.add('- ${wrapped.first}');
output.addAll(wrapped.skip(1).map((l) => ' $l'));
} else if (ltrim.startsWith('- ') || ltrim.startsWith('<')) {
output.add(line);
} else if (ltrim.startsWith('| ')) {
// Don't wrap lines in a markdown table.
output.add(line);
} else {
output.addAll(_wrap(line, width));
if (line.startsWith('```')) {
inCodeFence = true;
}
}
}
}
return output.map((line) => line.isEmpty ? '///' : '/// $line').toList();
}
/// Identify `[foo](bar)` and `[foo]` patterns.
final RegExp _linksRegEx = RegExp(r'\[([\w `-]+?)\](\(\S+\))?');
/// Wrap the given [line] to [width], breaking at whitespace.
Iterable<String> _wrap(String line, int width) sync* {
if (line.isEmpty) {
yield line;
return;
}
const markerChar = '\u009e';
// handle [foo bar](link)
line = line.replaceAllMapped(_linksRegEx, (match) {
const urlBase = 'https://developer.mozilla.org/';
var text = match.group(1)!;
text = text.replaceAll(' ', markerChar);
final link = match.group(2);
if (link == null) {
return '[$text]';
} else {
if (link.startsWith('(/')) {
return '[$text]($urlBase${link.substring(2)}';
} else {
return '[$text]$link';
}
}
});
final words = line.split(' ');
final buf = StringBuffer();
for (var word in words) {
if (buf.isEmpty) {
buf.write(word);
} else {
if (buf.length + 1 + word.length > width) {
yield buf.toString().replaceAll(markerChar, ' ');
buf.clear();
buf.write(word);
} else {
buf.write(' $word');
}
}
}
if (buf.isNotEmpty) {
yield buf.toString().replaceAll(markerChar, ' ');
}
}