blob: 0d1ec0c19c5fcf43c5cd9b8e80e8c6b687db42e7 [file]
// Copyright (c) 2025, 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.
import 'package:markdown/markdown.dart';
import 'utils.dart';
// todo: sanitize html (see lib/src/render/documentation_renderer.dart from
// dartdoc)
// todo: link resolver
// todo: markdown files can create id attributes
// todo: ensure that element docs cannot create id attributes
class MarkdownResults {
final String html;
final Outline outline;
MarkdownResults(this.html, this.outline);
}
typedef MarkdownLinkResolver = String? Function(String reference);
String convertMarkdown(String markdown, {MarkdownLinkResolver? linkResolver}) {
return markdownToHtml(
markdown,
extensionSet: ExtensionSet.gitHubWeb,
enableTagfilter: true,
linkResolver: (String name, [String? title]) {
String? href;
if (linkResolver != null) {
href = linkResolver(name);
}
if (href == null) {
return Element.text('span', name)..attributes['class'] = 'code';
} else {
return Element.text('a', name)
..attributes['href'] = href
..attributes['class'] = 'code';
}
},
);
}
String firstSentence(String markdown) {
return markdown
.split('\n')
.takeWhile((line) => line.trim().isNotEmpty && !line.startsWith('```'))
.join('\n');
}
String markdownToText(String markdown) {
final visitor = _TextVisitor();
final document = Document(
extensionSet: ExtensionSet.gitHubWeb,
encodeHtml: false,
linkResolver: (name, [text]) => Element.text('span', name),
);
for (final node in document.parse(markdown)) {
node.accept(visitor);
}
return visitor.toString();
}
MarkdownResults convertMarkdownWithOutline(String markdown) {
final document = Document(extensionSet: ExtensionSet.gitHubWeb);
final nodes = document.parse(markdown);
var contents = '${renderToHtml(nodes, enableTagfilter: true)}\n';
var elements = nodes.whereType<Element>().where(
(element) => element.tag == 'h2' || element.tag == 'h3',
);
return MarkdownResults(contents, _toOutline(elements));
}
Outline _toOutline(Iterable<Element> elements) {
var outline = Outline();
for (var element in elements) {
var level = int.parse(element.tag.substring(1));
outline.add(
Heading(element.textContent, level: level, id: element.generatedId),
);
}
return outline;
}
class _TextVisitor implements NodeVisitor {
final StringBuffer buf = StringBuffer();
@override
String toString() => buf.toString();
@override
bool visitElementBefore(Element element) {
return true;
}
@override
void visitText(Text text) {
buf.write(text.text);
}
@override
void visitElementAfter(Element element) {}
}