Skip to content

Commit 7b87236

Browse files
authored
Separate documentation parsing and rendering (dart-lang#2081)
- Move Documentation out of markdown_processor and into model package - Create a renderer class for Documentation
1 parent 0a0135a commit 7b87236

File tree

7 files changed

+173
-160
lines changed

7 files changed

+173
-160
lines changed

lib/src/markdown_processor.dart

Lines changed: 47 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import 'package:dartdoc/src/element_type.dart';
1313
import 'package:dartdoc/src/model/model.dart';
1414
import 'package:dartdoc/src/tuple.dart';
1515
import 'package:dartdoc/src/warnings.dart';
16-
import 'package:html/parser.dart' show parse;
1716
import 'package:markdown/markdown.dart' as md;
1817

1918
const validHtmlTags = [
@@ -757,23 +756,23 @@ class _MarkdownCommentReference {
757756
}
758757
}
759758

760-
String _linkDocReference(String codeRef, Warnable warnable,
759+
md.Node _makeLinkNode(String codeRef, Warnable warnable,
761760
List<ModelCommentReference> commentRefs) {
762-
MatchingLinkResult result;
763-
result = _getMatchingLinkElement(codeRef, warnable, commentRefs);
761+
MatchingLinkResult result =
762+
_getMatchingLinkElement(codeRef, warnable, commentRefs);
763+
final textContent = htmlEscape.convert(codeRef);
764764
final ModelElement linkedElement = result.element;
765765
if (linkedElement != null) {
766-
var classContent = '';
767-
if (linkedElement.isDeprecated) {
768-
classContent = 'class="deprecated" ';
769-
}
770-
// This would be linkedElement.linkedName, but link bodies are slightly
771-
// different for doc references.
772-
if (linkedElement.href == null) {
773-
return '<code>${htmlEscape.convert(codeRef)}</code>';
774-
} else {
775-
return '<a ${classContent}href="${linkedElement.href}">${htmlEscape.convert(codeRef)}</a>';
766+
if (linkedElement.href != null) {
767+
var anchor = md.Element.text('a', textContent);
768+
if (linkedElement.isDeprecated) {
769+
anchor.attributes['class'] = 'deprecated';
770+
}
771+
anchor.attributes['href'] = linkedElement.href;
772+
return anchor;
776773
}
774+
// else this would be linkedElement.linkedName, but link bodies are slightly
775+
// different for doc references, so fall out.
777776
} else {
778777
if (result.warn) {
779778
// Avoid claiming documentation is inherited when it comes from the
@@ -784,8 +783,9 @@ String _linkDocReference(String codeRef, Warnable warnable,
784783
? null
785784
: warnable.documentationFrom);
786785
}
787-
return '<code>${htmlEscape.convert(codeRef)}</code>';
788786
}
787+
788+
return md.Element.text('code', textContent);
789789
}
790790

791791
// Maximum number of characters to display before a suspected generic.
@@ -800,7 +800,7 @@ final RegExp allAfterLastNewline = RegExp(r'\n.*$', multiLine: true);
800800
// (like, [Apple<int>]). @Hixie asked for a warning when there's something, that looks
801801
// like a non HTML tag (a generic?) outside of a `[]` block.
802802
// https://github.com/dart-lang/dartdoc/issues/1250#issuecomment-269257942
803-
void _showWarningsForGenericsOutsideSquareBracketsBlocks(
803+
void showWarningsForGenericsOutsideSquareBracketsBlocks(
804804
String text, Warnable element) {
805805
List<int> tagPositions = findFreeHangingGenericsPositions(text);
806806
if (tagPositions.isNotEmpty) {
@@ -851,6 +851,21 @@ List<int> findFreeHangingGenericsPositions(String string) {
851851
}
852852

853853
class MarkdownDocument extends md.Document {
854+
factory MarkdownDocument.withElementLinkResolver(
855+
Canonicalization element, List<ModelCommentReference> commentRefs) {
856+
md.Node linkResolver(String name, [String _]) {
857+
if (name.isEmpty) {
858+
return null;
859+
}
860+
return _makeLinkNode(name, element, commentRefs);
861+
}
862+
863+
return MarkdownDocument(
864+
inlineSyntaxes: _markdown_syntaxes,
865+
blockSyntaxes: _markdown_block_syntaxes,
866+
linkResolver: linkResolver);
867+
}
868+
854869
MarkdownDocument(
855870
{Iterable<md.BlockSyntax> blockSyntaxes,
856871
Iterable<md.InlineSyntax> inlineSyntaxes,
@@ -864,43 +879,24 @@ class MarkdownDocument extends md.Document {
864879
linkResolver: linkResolver,
865880
imageLinkResolver: imageLinkResolver);
866881

867-
/// Returns a tuple of longHtml, shortHtml. longHtml is NULL if [processFullDocs] is true.
868-
static Tuple2<String, String> _renderNodesToHtml(
869-
List<md.Node> nodes, bool processFullDocs) {
870-
var rawHtml = md.HtmlRenderer().render(nodes);
871-
var asHtmlDocument = parse(rawHtml);
872-
for (var s in asHtmlDocument.querySelectorAll('script')) {
873-
s.remove();
874-
}
875-
for (var pre in asHtmlDocument.querySelectorAll('pre')) {
876-
if (pre.children.isNotEmpty &&
877-
pre.children.length != 1 &&
878-
pre.children.first.localName != 'code') {
879-
continue;
880-
}
881-
882-
if (pre.children.isNotEmpty && pre.children.first.localName == 'code') {
883-
var code = pre.children.first;
884-
pre.classes
885-
.addAll(code.classes.where((name) => name.startsWith('language-')));
882+
/// Returns a tuple of List<md.Node> and hasExtendedContent
883+
Tuple2<List<md.Node>, bool> parseMarkdownText(
884+
String text, bool processFullText) {
885+
bool hasExtendedContent = false;
886+
List<String> lines = LineSplitter.split(text).toList();
887+
md.Node firstNode;
888+
List<md.Node> nodes = [];
889+
for (md.Node node
890+
in IterableBlockParser(lines, this).parseLinesGenerator()) {
891+
if (firstNode != null) {
892+
hasExtendedContent = true;
893+
if (!processFullText) break;
886894
}
887-
888-
bool specifiesLanguage = pre.classes.isNotEmpty;
889-
// Assume the user intended Dart if there are no other classes present.
890-
if (!specifiesLanguage) pre.classes.add('language-dart');
891-
}
892-
String asHtml;
893-
String asOneLiner;
894-
895-
if (processFullDocs) {
896-
// `trim` fixes issue with line ending differences between mac and windows.
897-
asHtml = asHtmlDocument.body.innerHtml?.trim();
895+
firstNode ??= node;
896+
nodes.add(node);
898897
}
899-
asOneLiner = asHtmlDocument.body.children.isEmpty
900-
? ''
901-
: asHtmlDocument.body.children.first.innerHtml;
902-
903-
return Tuple2(asHtml, asOneLiner);
898+
_parseInlineContent(nodes);
899+
return Tuple2(nodes, hasExtendedContent);
904900
}
905901

906902
// From package:markdown/src/document.dart
@@ -919,112 +915,6 @@ class MarkdownDocument extends md.Document {
919915
}
920916
}
921917
}
922-
923-
/// Returns a tuple of longHtml, shortHtml (longHtml is NULL if !processFullDocs)
924-
Tuple3<String, String, bool> renderLinesToHtml(
925-
List<String> lines, bool processFullDocs) {
926-
bool hasExtendedDocs = false;
927-
md.Node firstNode;
928-
List<md.Node> nodes = [];
929-
for (md.Node node
930-
in IterableBlockParser(lines, this).parseLinesGenerator()) {
931-
if (firstNode != null) {
932-
hasExtendedDocs = true;
933-
if (!processFullDocs) break;
934-
}
935-
firstNode ??= node;
936-
nodes.add(node);
937-
}
938-
_parseInlineContent(nodes);
939-
940-
String shortHtml;
941-
String longHtml;
942-
if (processFullDocs) {
943-
Tuple2 htmls = _renderNodesToHtml(nodes, processFullDocs);
944-
longHtml = htmls.item1;
945-
shortHtml = htmls.item2;
946-
} else {
947-
if (firstNode != null) {
948-
Tuple2 htmls = _renderNodesToHtml([firstNode], processFullDocs);
949-
shortHtml = htmls.item2;
950-
} else {
951-
shortHtml = '';
952-
}
953-
}
954-
return Tuple3<String, String, bool>(longHtml, shortHtml, hasExtendedDocs);
955-
}
956-
}
957-
958-
class Documentation {
959-
final Canonicalization _element;
960-
961-
Documentation.forElement(this._element);
962-
963-
bool _hasExtendedDocs;
964-
965-
bool get hasExtendedDocs {
966-
if (_hasExtendedDocs == null) {
967-
_renderHtmlForDartdoc(_element.isCanonical && _asHtml == null);
968-
}
969-
return _hasExtendedDocs;
970-
}
971-
972-
String _asHtml;
973-
974-
String get asHtml {
975-
if (_asHtml == null) {
976-
assert(_asOneLiner == null || _element.isCanonical);
977-
_renderHtmlForDartdoc(true);
978-
}
979-
return _asHtml;
980-
}
981-
982-
String _asOneLiner;
983-
984-
String get asOneLiner {
985-
if (_asOneLiner == null) {
986-
assert(_asHtml == null);
987-
_renderHtmlForDartdoc(_element.isCanonical);
988-
}
989-
return _asOneLiner;
990-
}
991-
992-
List<ModelCommentReference> get commentRefs => _element.commentRefs;
993-
994-
void _renderHtmlForDartdoc(bool processAllDocs) {
995-
Tuple3<String, String, bool> renderResults =
996-
_renderMarkdownToHtml(processAllDocs);
997-
if (processAllDocs) {
998-
_asHtml = renderResults.item1;
999-
}
1000-
if (_asOneLiner == null) {
1001-
_asOneLiner = renderResults.item2;
1002-
}
1003-
if (_hasExtendedDocs != null) {
1004-
assert(_hasExtendedDocs == renderResults.item3);
1005-
}
1006-
_hasExtendedDocs = renderResults.item3;
1007-
}
1008-
1009-
/// Returns a tuple of longHtml, shortHtml, hasExtendedDocs
1010-
/// (longHtml is NULL if !processFullDocs)
1011-
Tuple3<String, String, bool> _renderMarkdownToHtml(bool processFullDocs) {
1012-
md.Node _linkResolver(String name, [String _]) {
1013-
if (name.isEmpty) {
1014-
return null;
1015-
}
1016-
return md.Text(_linkDocReference(name, _element, commentRefs));
1017-
}
1018-
1019-
String text = _element.documentation;
1020-
_showWarningsForGenericsOutsideSquareBracketsBlocks(text, _element);
1021-
MarkdownDocument document = MarkdownDocument(
1022-
inlineSyntaxes: _markdown_syntaxes,
1023-
blockSyntaxes: _markdown_block_syntaxes,
1024-
linkResolver: _linkResolver);
1025-
List<String> lines = LineSplitter.split(text).toList();
1026-
return document.renderLinesToHtml(lines, processFullDocs);
1027-
}
1028918
}
1029919

1030920
class _InlineCodeSyntax extends md.InlineSyntax {

lib/src/model/documentable.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'package:dartdoc/src/dartdoc_options.dart';
6-
import 'package:dartdoc/src/markdown_processor.dart';
76
import 'package:dartdoc/src/package_meta.dart';
87
import 'package:path/path.dart' as path;
98

lib/src/model/documentation.dart

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:dartdoc/src/markdown_processor.dart';
6+
import 'package:dartdoc/src/model/model.dart';
7+
import 'package:dartdoc/src/render/documentation_renderer.dart';
8+
import 'package:dartdoc/src/tuple.dart';
9+
import 'package:markdown/markdown.dart' as md;
10+
11+
class Documentation {
12+
final Canonicalization _element;
13+
14+
Documentation.forElement(this._element);
15+
16+
bool _hasExtendedDocs;
17+
18+
bool get hasExtendedDocs {
19+
if (_hasExtendedDocs == null) {
20+
_renderDocumentation(_element.isCanonical && _asHtml == null);
21+
}
22+
return _hasExtendedDocs;
23+
}
24+
25+
String _asHtml;
26+
27+
String get asHtml {
28+
if (_asHtml == null) {
29+
assert(_asOneLiner == null || _element.isCanonical);
30+
_renderDocumentation(true);
31+
}
32+
return _asHtml;
33+
}
34+
35+
String _asOneLiner;
36+
37+
String get asOneLiner {
38+
if (_asOneLiner == null) {
39+
assert(_asHtml == null);
40+
_renderDocumentation(_element.isCanonical);
41+
}
42+
return _asOneLiner;
43+
}
44+
45+
List<ModelCommentReference> get commentRefs => _element.commentRefs;
46+
47+
void _renderDocumentation(bool processAllDocs) {
48+
Tuple2<List<md.Node>, bool> parseResult =
49+
_parseDocumentation(processAllDocs);
50+
if (_hasExtendedDocs != null) {
51+
assert(_hasExtendedDocs == parseResult.item2);
52+
}
53+
_hasExtendedDocs = parseResult.item2;
54+
55+
Tuple2<String, String> renderResult =
56+
DocumentationRendererHtml().render(parseResult.item1, processAllDocs);
57+
58+
if (processAllDocs) {
59+
_asHtml = renderResult.item1;
60+
}
61+
if (_asOneLiner == null) {
62+
_asOneLiner = renderResult.item2;
63+
}
64+
}
65+
66+
/// Returns a tuple of List<md.Node> and hasExtendedDocs
67+
Tuple2<List<md.Node>, bool> _parseDocumentation(bool processFullDocs) {
68+
String text = _element.documentation;
69+
showWarningsForGenericsOutsideSquareBracketsBlocks(text, _element);
70+
MarkdownDocument document =
71+
MarkdownDocument.withElementLinkResolver(_element, commentRefs);
72+
return document.parseMarkdownText(text, processFullDocs);
73+
}
74+
}

lib/src/model/model.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export 'constructor.dart';
1111
export 'container.dart';
1212
export 'container_member.dart';
1313
export 'documentable.dart';
14+
export 'documentation.dart';
1415
export 'dynamic.dart';
1516
export 'enclosed_element.dart';
1617
export 'enum.dart';

lib/src/model/model_element.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import 'package:crypto/crypto.dart';
2323
import 'package:dartdoc/src/dartdoc_options.dart';
2424
import 'package:dartdoc/src/element_type.dart';
2525
import 'package:dartdoc/src/logging.dart';
26-
import 'package:dartdoc/src/markdown_processor.dart' show Documentation;
2726
import 'package:dartdoc/src/model/model.dart';
2827
import 'package:dartdoc/src/model_utils.dart' as utils;
2928
import 'package:dartdoc/src/render/parameter_renderer.dart';

lib/src/model/package.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'dart:io';
66

77
import 'package:analyzer/dart/element/element.dart';
88
import 'package:dartdoc/src/dartdoc_options.dart';
9-
import 'package:dartdoc/src/markdown_processor.dart';
109
import 'package:dartdoc/src/model/model.dart';
1110
import 'package:dartdoc/src/package_meta.dart';
1211
import 'package:dartdoc/src/warnings.dart';

0 commit comments

Comments
 (0)