diff --git a/lib/src/model/package_builder.dart b/lib/src/model/package_builder.dart index 572faedaad..e3da440664 100644 --- a/lib/src/model/package_builder.dart +++ b/lib/src/model/package_builder.dart @@ -42,8 +42,8 @@ class PackageBuilder { if (config.topLevelPackageMeta.needsPubGet) { config.topLevelPackageMeta.runPubGet(); } - // TODO(jdkoren): change factory for other formats based on config options - RendererFactory rendererFactory = HtmlRenderFactory(); + + RendererFactory rendererFactory = RendererFactory.forFormat(config.format); PackageGraph newGraph = PackageGraph.UninitializedPackageGraph( config, driver, sdk, hasEmbedderSdkFiles, rendererFactory); diff --git a/lib/src/render/category_renderer.dart b/lib/src/render/category_renderer.dart index 7b0b803c9e..d8e5f0598d 100644 --- a/lib/src/render/category_renderer.dart +++ b/lib/src/render/category_renderer.dart @@ -31,7 +31,7 @@ class CategoryRendererHtml extends CategoryRenderer { StringBuffer buf = StringBuffer(); buf.write(''); - buf.write(category.linkedName); + buf.write(renderLinkedName(category)); buf.write(''); return buf.toString(); } @@ -46,3 +46,17 @@ class CategoryRendererHtml extends CategoryRenderer { } } } + +class CategoryRendererMd extends CategoryRenderer { + @override + String renderCategoryLabel(Category category) => renderLinkedName(category); + + @override + String renderLinkedName(Category category) { + String name = category.name; + if (category.isDocumented) { + return '[$name](${category.href})'; + } + return name; + } +} diff --git a/lib/src/render/element_type_renderer.dart b/lib/src/render/element_type_renderer.dart index cc4be7a4fb..af03ac7be9 100644 --- a/lib/src/render/element_type_renderer.dart +++ b/lib/src/render/element_type_renderer.dart @@ -11,6 +11,8 @@ abstract class ElementTypeRenderer { String renderNameWithGenerics(T elementType) => ''; } +// Html implementations + class FunctionTypeElementTypeRendererHtml extends ElementTypeRenderer { @override @@ -93,3 +95,83 @@ class CallableElementTypeRendererHtml return buf.toString(); } } + +// Markdown implementations + +class FunctionTypeElementTypeRendererMd + extends ElementTypeRenderer { + @override + String renderLinkedName(FunctionTypeElementType elementType) { + StringBuffer buf = StringBuffer(); + buf.write('${elementType.returnType.linkedName} '); + buf.write('${elementType.nameWithGenerics}'); + buf.write('('); + buf.write(ParameterRendererMd().renderLinkedParams(elementType.parameters)); + buf.write(')'); + return buf.toString(); + } + + @override + String renderNameWithGenerics(FunctionTypeElementType elementType) { + StringBuffer buf = StringBuffer(); + buf.write(elementType.name); + if (elementType.typeFormals.isNotEmpty) { + if (!elementType.typeFormals.every((t) => t.name == 'dynamic')) { + buf.write('<'); + buf.writeAll(elementType.typeFormals.map((t) => t.name), ', '); + buf.write('>'); + } + } + return buf.toString(); + } +} + +class ParameterizedElementTypeRendererMd + extends ElementTypeRenderer { + @override + String renderLinkedName(ParameterizedElementType elementType) { + StringBuffer buf = StringBuffer(); + buf.write(elementType.element.linkedName); + if (elementType.typeArguments.isNotEmpty && + !elementType.typeArguments.every((t) => t.name == 'dynamic')) { + buf.write('<'); + buf.writeAll(elementType.typeArguments.map((t) => t.linkedName), ', '); + buf.write('>'); + } + return buf.toString(); + } + + @override + String renderNameWithGenerics(ParameterizedElementType elementType) { + StringBuffer buf = StringBuffer(); + buf.write(elementType.element.name); + if (elementType.typeArguments.isNotEmpty && + !elementType.typeArguments.every((t) => t.name == 'dynamic')) { + buf.write('<'); + buf.writeAll( + elementType.typeArguments.map((t) => t.nameWithGenerics), ', '); + buf.write('>'); + } + return buf.toString(); + } +} + +class CallableElementTypeRendererMd + extends ElementTypeRenderer { + @override + String renderLinkedName(CallableElementType elementType) { + if (elementType.name != null && elementType.name.isNotEmpty) { + return elementType.superLinkedName; + } + + StringBuffer buf = StringBuffer(); + buf.write(elementType.nameWithGenerics); + buf.write('('); + buf.write(ParameterRendererMd() + .renderLinkedParams(elementType.element.parameters, showNames: false) + .trim()); + buf.write(') → '); + buf.write(elementType.returnType.linkedName); + return buf.toString(); + } +} diff --git a/lib/src/render/enum_field_renderer.dart b/lib/src/render/enum_field_renderer.dart index c9096b0b26..4a635909e7 100644 --- a/lib/src/render/enum_field_renderer.dart +++ b/lib/src/render/enum_field_renderer.dart @@ -18,3 +18,14 @@ class EnumFieldRendererHtml extends EnumFieldRenderer { } } } + +class EnumFieldRendererMd extends EnumFieldRenderer { + @override + String renderValue(EnumField field) { + if (field.name == 'values') { + return 'const List<${field.enclosingElement.name}>'; + } else { + return 'const ${field.enclosingElement.name}(${field.index})'; + } + } +} diff --git a/lib/src/render/model_element_renderer.dart b/lib/src/render/model_element_renderer.dart index 9d274d4fe4..d7755d4851 100644 --- a/lib/src/render/model_element_renderer.dart +++ b/lib/src/render/model_element_renderer.dart @@ -95,3 +95,17 @@ class ModelElementRendererHtml extends ModelElementRenderer { '''; // Must end at start of line, or following inline text will be indented. } } + +class ModelElementRendererMd extends ModelElementRendererHtml { + @override + String renderLinkedName(ModelElement modelElement) { + if (modelElement.isDeprecated) { + return '[~~${modelElement.name}~~](${modelElement.href})'; + } + return '[${modelElement.name}](${modelElement.href})'; + } + + @override + String renderExtendedDocLink(ModelElement modelElement) => + '[...](${modelElement.href})'; +} diff --git a/lib/src/render/parameter_renderer.dart b/lib/src/render/parameter_renderer.dart index 3e7a895c3a..fab430d259 100644 --- a/lib/src/render/parameter_renderer.dart +++ b/lib/src/render/parameter_renderer.dart @@ -42,6 +42,35 @@ class ParameterRendererHtml extends ParameterRenderer { String required(String required) => '$required'; } +class ParameterRendererMd extends ParameterRenderer { + @override + String annotation(String annotation) => annotation; + + @override + String covariant(String covariant) => covariant; + + @override + String defaultValue(String defaultValue) => defaultValue; + + @override + String listItem(String item) => item; + + @override + String orderedList(String listItems) => listItems; + + @override + String parameter(String parameter, String id) => parameter; + + @override + String parameterName(String parameterName) => parameterName; + + @override + String required(String required) => required; + + @override + String typeName(String typeName) => typeName; +} + abstract class ParameterRenderer { String listItem(String item); String orderedList(String listItems); diff --git a/lib/src/render/renderer_factory.dart b/lib/src/render/renderer_factory.dart index f8787228a8..eaead50507 100644 --- a/lib/src/render/renderer_factory.dart +++ b/lib/src/render/renderer_factory.dart @@ -14,6 +14,17 @@ import 'package:dartdoc/src/render/type_parameters_renderer.dart'; import 'package:dartdoc/src/render/typedef_renderer.dart'; abstract class RendererFactory { + static RendererFactory forFormat(String format) { + switch (format) { + case 'html': + return HtmlRenderFactory(); + case 'md': + return MdRenderFactory(); + default: + throw ArgumentError('Unsupported format: $format'); + } + } + TemplateRenderer get templateRenderer; CategoryRenderer get categoryRenderer; @@ -86,3 +97,50 @@ class HtmlRenderFactory extends RendererFactory { @override TypedefRenderer get typedefRenderer => TypedefRendererHtml(); } + +class MdRenderFactory extends RendererFactory { + @override + TemplateRenderer get templateRenderer => MdTemplateRenderer(); + + @override + CategoryRenderer get categoryRenderer => CategoryRendererMd(); + + // We render documentation as HTML for now. + // TODO(jdkoren): explore using documentation directly in the output file. + @override + DocumentationRenderer get documentationRenderer => + DocumentationRendererHtml(); + + @override + ElementTypeRenderer get callableElementTypeRenderer => + CallableElementTypeRendererMd(); + + @override + ElementTypeRenderer + get functionTypeElementTypeRenderer => + FunctionTypeElementTypeRendererMd(); + + @override + ElementTypeRenderer + get parameterizedElementTypeRenderer => + ParameterizedElementTypeRendererMd(); + + @override + EnumFieldRenderer get enumFieldRenderer => EnumFieldRendererMd(); + + @override + ModelElementRenderer get modelElementRenderer => ModelElementRendererMd(); + + @override + ParameterRenderer get parameterRenderer => ParameterRendererMd(); + + @override + ParameterRenderer get parameterRendererDetailed => parameterRenderer; + + @override + TypeParametersRenderer get typeParametersRenderer => + TypeParametersRendererMd(); + + @override + TypedefRenderer get typedefRenderer => TypedefRendererMd(); +} diff --git a/lib/src/render/template_renderer.dart b/lib/src/render/template_renderer.dart index 3e0ed15391..e18ab4ac41 100644 --- a/lib/src/render/template_renderer.dart +++ b/lib/src/render/template_renderer.dart @@ -16,3 +16,14 @@ class HtmlTemplateRenderer implements TemplateRenderer { } } } + +class MdTemplateRenderer implements TemplateRenderer { + @override + String composeLayoutTitle(String name, String kind, bool isDeprecated) { + if (isDeprecated) { + return '~~${name}~~ ${kind}'; + } else { + return '${name} ${kind}'; + } + } +} diff --git a/lib/src/render/type_parameters_renderer.dart b/lib/src/render/type_parameters_renderer.dart index 879ca0ffaa..09ac2eeda0 100644 --- a/lib/src/render/type_parameters_renderer.dart +++ b/lib/src/render/type_parameters_renderer.dart @@ -6,6 +6,7 @@ import 'package:dartdoc/src/model/type_parameter.dart'; abstract class TypeParametersRenderer { String renderGenericParameters(TypeParameters typeParameters); + String renderLinkedGenericParameters(TypeParameters typeParameters); } @@ -32,3 +33,22 @@ class TypeParametersRendererHtml extends TypeParametersRenderer { return '<${joined}>'; } } + +class TypeParametersRendererMd extends TypeParametersRenderer { + @override + String renderGenericParameters(TypeParameters typeParameters) => + _compose(typeParameters.typeParameters, (t) => t.name); + + @override + String renderLinkedGenericParameters(TypeParameters typeParameters) => + _compose(typeParameters.typeParameters, (t) => t.linkedName); + + String _compose(List typeParameters, + String Function(TypeParameter) mapfn) { + if (typeParameters.isEmpty) { + return ''; + } + var joined = typeParameters.map(mapfn).join(', '); + return '<${joined}>'; + } +} diff --git a/lib/src/render/typedef_renderer.dart b/lib/src/render/typedef_renderer.dart index 385225b87b..b79e3b3091 100644 --- a/lib/src/render/typedef_renderer.dart +++ b/lib/src/render/typedef_renderer.dart @@ -20,3 +20,14 @@ class TypedefRendererHtml extends TypedefRenderer { return '<${joined}>'; } } + +class TypedefRendererMd extends TypedefRenderer { + @override + String renderGenericParameters(Typedef typedef) { + if (typedef.genericTypeParameters.isEmpty) { + return ''; + } + var joined = typedef.genericTypeParameters.map((t) => t.name).join(', '); + return '<{$joined}>'; + } +} diff --git a/lib/templates/md/_callable.md b/lib/templates/md/_callable.md index 49d5fdb5f3..9cc868478b 100644 --- a/lib/templates/md/_callable.md +++ b/lib/templates/md/_callable.md @@ -1,3 +1,3 @@ -{{#isDeprecated}}~~{{/isDeprecated}}{{{linkedName}}}{{{linkedGenericParameters}}}({{{ linkedParamsNoMetadata }}}) {{{ linkedReturnType }}} {{>categorization}}{{#isDeprecated}}~~{{/isDeprecated}} +{{{linkedName}}}{{{linkedGenericParameters}}}({{{ linkedParamsNoMetadata }}}) {{{ linkedReturnType }}} {{>categorization}} : {{{ oneLineDoc }}} {{{ extendedDocLink }}} {{>features}} diff --git a/lib/templates/md/_class.md b/lib/templates/md/_class.md index 90721f1898..7659fa0e6e 100644 --- a/lib/templates/md/_class.md +++ b/lib/templates/md/_class.md @@ -1,2 +1,2 @@ -{{#isDeprecated}}~~{{/isDeprecated}}{{{linkedName}}}{{{linkedGenericParameters}}} {{>categorization}}{{#isDeprecated}}~~{{/isDeprecated}} +{{{linkedName}}}{{{linkedGenericParameters}}} {{>categorization}} : {{{ oneLineDoc }}} {{{ extendedDocLink }}} diff --git a/lib/templates/md/_constant.md b/lib/templates/md/_constant.md index 54d040ba0d..6367e59957 100644 --- a/lib/templates/md/_constant.md +++ b/lib/templates/md/_constant.md @@ -1,3 +1,3 @@ -{{#isDeprecated}}~~{{/isDeprecated}}{{{ linkedName }}} const {{{ linkedReturnType }}} {{>categorization}}{{#isDeprecated}}~~{{/isDeprecated}} +{{{ linkedName }}} const {{{ linkedReturnType }}} {{>categorization}} : {{{ oneLineDoc }}} {{{ extendedDocLink }}} {{>features}} diff --git a/lib/templates/md/_extension.md b/lib/templates/md/_extension.md index 097235ead6..84737c4d56 100644 --- a/lib/templates/md/_extension.md +++ b/lib/templates/md/_extension.md @@ -1,2 +1,2 @@ -{{#isDeprecated}}~~{{/isDeprecated}}{{{linkedName}}} {{>categorization}}{{#isDeprecated}}~~{{/isDeprecated}} +{{{linkedName}}} {{>categorization}} : {{{ oneLineDoc }}} {{{ extendedDocLink }}} diff --git a/lib/templates/md/_mixin.md b/lib/templates/md/_mixin.md index 90721f1898..7659fa0e6e 100644 --- a/lib/templates/md/_mixin.md +++ b/lib/templates/md/_mixin.md @@ -1,2 +1,2 @@ -{{#isDeprecated}}~~{{/isDeprecated}}{{{linkedName}}}{{{linkedGenericParameters}}} {{>categorization}}{{#isDeprecated}}~~{{/isDeprecated}} +{{{linkedName}}}{{{linkedGenericParameters}}} {{>categorization}} : {{{ oneLineDoc }}} {{{ extendedDocLink }}} diff --git a/lib/templates/md/constructor.md b/lib/templates/md/constructor.md index bd50ad17a1..ce897718a4 100644 --- a/lib/templates/md/constructor.md +++ b/lib/templates/md/constructor.md @@ -14,7 +14,7 @@ {{/hasAnnotations}} {{#isConst}}const{{/isConst}} -{{#isDeprecated}}~~{{/isDeprecated}}{{{nameWithGenerics}}}({{#hasParameters}}{{{linkedParamsLines}}}{{/hasParameters}}){{#isDeprecated}}~~{{/isDeprecated}} +{{{nameWithGenerics}}}({{#hasParameters}}{{{linkedParamsLines}}}{{/hasParameters}}) {{>documentation}} diff --git a/test/model_test.dart b/test/model_test.dart index 5f4efdef7b..9fc8dbda34 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -373,6 +373,7 @@ void main() { equals(utils.kTestPackagePublicLibraries - 5)); }); + // TODO consider moving these to a separate suite test('CategoryRendererHtml renders category label', () { Category category = packageGraph.publicPackages.first.categories.first; CategoryRendererHtml renderer = CategoryRendererHtml(); @@ -388,6 +389,20 @@ void main() { expect(renderer.renderLinkedName(category), 'Superb'); }); + + test('CategoryRendererMd renders category label', () { + Category category = packageGraph.publicPackages.first.categories.first; + CategoryRendererMd renderer = CategoryRendererMd(); + expect(renderer.renderCategoryLabel(category), + '[Superb](${HTMLBASE_PLACEHOLDER}topics/Superb-topic.html)'); + }); + + test('CategoryRendererMd renders linkedName', () { + Category category = packageGraph.publicPackages.first.categories.first; + CategoryRendererMd renderer = CategoryRendererMd(); + expect(renderer.renderLinkedName(category), + '[Superb](${HTMLBASE_PLACEHOLDER}topics/Superb-topic.html)'); + }); }); group('LibraryContainer', () { diff --git a/test/html/html_template_renderer_test.dart b/test/render/template_renderer_test.dart similarity index 63% rename from test/html/html_template_renderer_test.dart rename to test/render/template_renderer_test.dart index e0611b9b4d..577a3b4a4a 100644 --- a/test/html/html_template_renderer_test.dart +++ b/test/render/template_renderer_test.dart @@ -23,4 +23,22 @@ void main() { expect(test, equals('Banana Fruit')); }); }); + + group('MdTemplateRenderer', () { + MdTemplateRenderer renderer; + + setUpAll(() { + renderer = MdTemplateRenderer(); + }); + + test('composeLayoutTitle', () { + String test = renderer.composeLayoutTitle('Banana', 'Fruit', false); + expect(test, equals('Banana Fruit')); + }); + + test('composeLayoutTitle deprecated', () { + String test = renderer.composeLayoutTitle('Banana', 'Fruit', true); + expect(test, equals('~~Banana~~ Fruit')); + }); + }); }