diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart index 5f026fa287..a3a8b853df 100644 --- a/lib/src/element_type.dart +++ b/lib/src/element_type.dart @@ -11,7 +11,7 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/nullability_suffix.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:dartdoc/src/model/model.dart'; -import 'package:dartdoc/src/model_utils.dart'; +import 'package:dartdoc/src/render/parameter_renderer.dart'; /// Base class representing a type in Dartdoc. It wraps a [DartType], and /// may link to a [ModelElement]. @@ -147,7 +147,7 @@ class FunctionTypeElementType extends UndefinedElementType { buf.write('${returnType.linkedName} '); buf.write('${nameWithGenerics}'); buf.write(''); - buf.write('(${linkedParams(parameters)})'); + buf.write('(${ParameterRendererHtml().renderLinkedParams(parameters)})'); buf.write(''); _linkedName = buf.toString(); } @@ -418,7 +418,7 @@ class CallableElementType extends ParameterizedElementType @override String get linkedName { if (name != null && name.isNotEmpty) return super.linkedName; - return '${nameWithGenerics}(${linkedParams(element.parameters, showNames: false).trim()}) → ${returnType.linkedName}'; + return '${nameWithGenerics}(${ParameterRendererHtml(showNames: false).renderLinkedParams(element.parameters).trim()}) → ${returnType.linkedName}'; } } @@ -435,7 +435,7 @@ class CallableAnonymousElementType extends CallableElementType { String get linkedName { if (_linkedName == null) { _linkedName = - '${returnType.linkedName} ${super.linkedName}(${linkedParams(element.parameters)})'; + '${returnType.linkedName} ${super.linkedName}(${ParameterRendererHtml().renderLinkedParams(element.parameters)})'; } return _linkedName; } diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index 29e54b5dfd..e09ba79d4f 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -26,6 +26,7 @@ import 'package:dartdoc/src/logging.dart'; import 'package:dartdoc/src/markdown_processor.dart' show Documentation; import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/model_utils.dart' as utils; +import 'package:dartdoc/src/render/parameter_renderer.dart'; import 'package:dartdoc/src/source_linker.dart'; import 'package:dartdoc/src/tuple.dart'; import 'package:dartdoc/src/utils.dart'; @@ -928,18 +929,18 @@ abstract class ModelElement extends Canonicalization return _linkedName; } - String get linkedParams => utils.linkedParams(parameters); + String get linkedParams => + ParameterRendererHtml().renderLinkedParams(parameters); String get linkedParamsLines => - utils.linkedParams(parameters, asList: true).trim(); + ParameterRendererHtmlList().renderLinkedParams(parameters).trim(); String get linkedParamsNoMetadata => - utils.linkedParams(parameters, showMetadata: false); + ParameterRendererHtml(showMetadata: false).renderLinkedParams(parameters); - String get linkedParamsNoMetadataOrNames { - return utils.linkedParams(parameters, - showMetadata: false, showNames: false); - } + String get linkedParamsNoMetadataOrNames => + ParameterRendererHtml(showMetadata: false, showNames: false) + .renderLinkedParams(parameters); ElementType get modelType { if (_modelType == null) { diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 22d9afe794..8c88c724ea 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -9,193 +9,11 @@ import 'dart:io'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/src/dart/ast/utilities.dart'; import 'package:dartdoc/src/model/model.dart'; -import 'package:dartdoc/src/element_type.dart'; final Map _fileContents = {}; -/// Render HTML in an extended vertical format using
    tag. -class ParameterRendererHtmlList extends ParameterRendererHtml { - ParameterRendererHtmlList({bool showMetadata = true, bool showNames = true}) - : super(showMetadata: showMetadata, showNames: showNames); - @override - String listItem(String listItem) => '
  1. $listItem
  2. \n'; - @override - // TODO(jcollins-g): consider comma separated lists and more advanced css. - String orderedList(String listItems) => - '
      $listItems
    \n'; -} - -/// Render HTML suitable for a single, wrapped line. -class ParameterRendererHtml extends ParameterRenderer { - @override - final bool showMetadata; - @override - final bool showNames; - ParameterRendererHtml({this.showMetadata = true, this.showNames = true}); - - @override - String listItem(String listItem) => '${listItem}'; - @override - String orderedList(String listItems) => listItems; - @override - String annotation(String annotation) => '$annotation'; - @override - String covariant(String covariant) => '$covariant'; - @override - String defaultValue(String defaultValue) => - '$defaultValue'; - @override - String parameter(String parameter, String htmlId) => - '$parameter'; - @override - String parameterName(String parameterName) => - '$parameterName'; - @override - String typeName(String typeName) => - '$typeName'; - @override - String required(String required) => '$required'; -} - -abstract class ParameterRenderer { - bool get showMetadata; - bool get showNames; - - String listItem(String item); - String orderedList(String listItems); - String annotation(String annotation); - String covariant(String covariant); - String defaultValue(String defaultValue); - String parameter(String parameter, String id); - String parameterName(String parameterName); - String typeName(String typeName); - String required(String required); - - String _linkedParameterSublist(List parameters, bool trailingComma, - {String thisOpenBracket = '', String thisCloseBracket = ''}) { - StringBuffer builder = StringBuffer(); - parameters.forEach((p) { - String prefix = ''; - String suffix = ''; - if (identical(p, parameters.first)) { - prefix = thisOpenBracket; - } - if (identical(p, parameters.last)) { - suffix += thisCloseBracket; - if (trailingComma) suffix += ', '; - } else { - suffix += ', '; - } - builder.write( - listItem(parameter(prefix + renderParam(p) + suffix, p.htmlId))); - }); - return builder.toString(); - } - - String linkedParams(List parameters) { - List positionalParams = - parameters.where((Parameter p) => p.isRequiredPositional).toList(); - List optionalPositionalParams = - parameters.where((Parameter p) => p.isOptionalPositional).toList(); - List namedParams = - parameters.where((Parameter p) => p.isNamed).toList(); - - String positional = '', optional = '', named = ''; - if (positionalParams.isNotEmpty) { - positional = _linkedParameterSublist(positionalParams, - optionalPositionalParams.isNotEmpty || namedParams.isNotEmpty); - } - if (optionalPositionalParams.isNotEmpty) { - optional = _linkedParameterSublist( - optionalPositionalParams, namedParams.isNotEmpty, - thisOpenBracket: '[', thisCloseBracket: ']'); - } - if (namedParams.isNotEmpty) { - named = _linkedParameterSublist(namedParams, false, - thisOpenBracket: '{', thisCloseBracket: '}'); - } - return (orderedList(positional + optional + named)); - } - - String renderParam(Parameter param) { - StringBuffer buf = StringBuffer(); - ElementType paramModelType = param.modelType; - - if (showMetadata && param.hasAnnotations) { - buf.write(param.annotations.map(annotation).join(' ') + ' '); - } - if (param.isRequiredNamed) { - buf.write(required('required') + ' '); - } - if (param.isCovariant) { - buf.write(covariant('covariant') + ' '); - } - if (paramModelType is CallableElementTypeMixin || - paramModelType.type is FunctionType) { - String returnTypeName; - if (paramModelType.isTypedef) { - returnTypeName = paramModelType.linkedName; - } else { - returnTypeName = paramModelType.createLinkedReturnTypeName(); - } - buf.write(typeName(returnTypeName)); - if (showNames) { - buf.write(' ${parameterName(param.name)}'); - } else if (paramModelType.isTypedef || - paramModelType is CallableAnonymousElementType || - paramModelType.type is FunctionType) { - buf.write(' ${parameterName(paramModelType.name)}'); - } - if (!paramModelType.isTypedef && paramModelType is DefinedElementType) { - buf.write('('); - buf.write(linkedParams(paramModelType.element.parameters)); - buf.write(')'); - } - if (!paramModelType.isTypedef && paramModelType.type is FunctionType) { - buf.write('('); - buf.write( - linkedParams((paramModelType as UndefinedElementType).parameters)); - buf.write(')'); - } - } else if (param.modelType != null) { - String linkedTypeName = paramModelType.linkedName; - if (linkedTypeName.isNotEmpty) { - buf.write(typeName(linkedTypeName)); - if (showNames && param.name.isNotEmpty) { - buf.write(' '); - } - } - if (showNames && param.name.isNotEmpty) { - buf.write(parameterName(param.name)); - } - } - - if (param.hasDefaultValue) { - if (param.isNamed) { - buf.write(': '); - } else { - buf.write(' = '); - } - buf.write(defaultValue(param.defaultValue)); - } - return buf.toString(); - } -} - -String linkedParams(List parameters, - {showMetadata = true, showNames = true, asList = false}) { - if (asList) { - return ParameterRendererHtmlList( - showMetadata: showMetadata, showNames: showNames) - .linkedParams(parameters); - } - return ParameterRendererHtml(showMetadata: showMetadata, showNames: showNames) - .linkedParams(parameters); -} - /// Returns the [AstNode] for a given [Element]. /// /// Uses a precomputed map of [element.source.fullName] to [CompilationUnit] diff --git a/lib/src/render/parameter_renderer.dart b/lib/src/render/parameter_renderer.dart new file mode 100644 index 0000000000..f4998c9f41 --- /dev/null +++ b/lib/src/render/parameter_renderer.dart @@ -0,0 +1,176 @@ +// Copyright (c) 2019, 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:analyzer/dart/element/type.dart'; +import 'package:dartdoc/src/element_type.dart'; +import 'package:dartdoc/src/model/parameter.dart'; + +/// Render HTML in an extended vertical format using
      tag. +class ParameterRendererHtmlList extends ParameterRendererHtml { + ParameterRendererHtmlList({bool showMetadata = true, bool showNames = true}) + : super(showMetadata: showMetadata, showNames: showNames); + @override + String listItem(String listItem) => '
    1. $listItem
    2. \n'; + @override + // TODO(jcollins-g): consider comma separated lists and more advanced css. + String orderedList(String listItems) => + '
        $listItems
      \n'; +} + +/// Render HTML suitable for a single, wrapped line. +class ParameterRendererHtml extends ParameterRenderer { + @override + final bool showMetadata; + @override + final bool showNames; + ParameterRendererHtml({this.showMetadata = true, this.showNames = true}); + + @override + String listItem(String listItem) => '${listItem}'; + @override + String orderedList(String listItems) => listItems; + @override + String annotation(String annotation) => '$annotation'; + @override + String covariant(String covariant) => '$covariant'; + @override + String defaultValue(String defaultValue) => + '$defaultValue'; + @override + String parameter(String parameter, String htmlId) => + '$parameter'; + @override + String parameterName(String parameterName) => + '$parameterName'; + @override + String typeName(String typeName) => + '$typeName'; + @override + String required(String required) => '$required'; +} + +abstract class ParameterRenderer { + bool get showMetadata; + bool get showNames; + + String listItem(String item); + String orderedList(String listItems); + String annotation(String annotation); + String covariant(String covariant); + String defaultValue(String defaultValue); + String parameter(String parameter, String id); + String parameterName(String parameterName); + String typeName(String typeName); + String required(String required); + + String _linkedParameterSublist(List parameters, bool trailingComma, + {String thisOpenBracket = '', String thisCloseBracket = ''}) { + StringBuffer builder = StringBuffer(); + parameters.forEach((p) { + String prefix = ''; + String suffix = ''; + if (identical(p, parameters.first)) { + prefix = thisOpenBracket; + } + if (identical(p, parameters.last)) { + suffix += thisCloseBracket; + if (trailingComma) suffix += ', '; + } else { + suffix += ', '; + } + builder.write( + listItem(parameter(prefix + renderParam(p) + suffix, p.htmlId))); + }); + return builder.toString(); + } + + String renderLinkedParams(List parameters) { + List positionalParams = + parameters.where((Parameter p) => p.isRequiredPositional).toList(); + List optionalPositionalParams = + parameters.where((Parameter p) => p.isOptionalPositional).toList(); + List namedParams = + parameters.where((Parameter p) => p.isNamed).toList(); + + String positional = '', optional = '', named = ''; + if (positionalParams.isNotEmpty) { + positional = _linkedParameterSublist(positionalParams, + optionalPositionalParams.isNotEmpty || namedParams.isNotEmpty); + } + if (optionalPositionalParams.isNotEmpty) { + optional = _linkedParameterSublist( + optionalPositionalParams, namedParams.isNotEmpty, + thisOpenBracket: '[', thisCloseBracket: ']'); + } + if (namedParams.isNotEmpty) { + named = _linkedParameterSublist(namedParams, false, + thisOpenBracket: '{', thisCloseBracket: '}'); + } + return (orderedList(positional + optional + named)); + } + + String renderParam(Parameter param) { + StringBuffer buf = StringBuffer(); + ElementType paramModelType = param.modelType; + + if (showMetadata && param.hasAnnotations) { + buf.write(param.annotations.map(annotation).join(' ') + ' '); + } + if (param.isRequiredNamed) { + buf.write(required('required') + ' '); + } + if (param.isCovariant) { + buf.write(covariant('covariant') + ' '); + } + if (paramModelType is CallableElementTypeMixin || + paramModelType.type is FunctionType) { + String returnTypeName; + if (paramModelType.isTypedef) { + returnTypeName = paramModelType.linkedName; + } else { + returnTypeName = paramModelType.createLinkedReturnTypeName(); + } + buf.write(typeName(returnTypeName)); + if (showNames) { + buf.write(' ${parameterName(param.name)}'); + } else if (paramModelType.isTypedef || + paramModelType is CallableAnonymousElementType || + paramModelType.type is FunctionType) { + buf.write(' ${parameterName(paramModelType.name)}'); + } + if (!paramModelType.isTypedef && paramModelType is DefinedElementType) { + buf.write('('); + buf.write(renderLinkedParams(paramModelType.element.parameters)); + buf.write(')'); + } + if (!paramModelType.isTypedef && paramModelType.type is FunctionType) { + buf.write('('); + buf.write(renderLinkedParams( + (paramModelType as UndefinedElementType).parameters)); + buf.write(')'); + } + } else if (param.modelType != null) { + String linkedTypeName = paramModelType.linkedName; + if (linkedTypeName.isNotEmpty) { + buf.write(typeName(linkedTypeName)); + if (showNames && param.name.isNotEmpty) { + buf.write(' '); + } + } + if (showNames && param.name.isNotEmpty) { + buf.write(parameterName(param.name)); + } + } + + if (param.hasDefaultValue) { + if (param.isNamed) { + buf.write(': '); + } else { + buf.write(' = '); + } + buf.write(defaultValue(param.defaultValue)); + } + return buf.toString(); + } +} diff --git a/test/model_test.dart b/test/model_test.dart index 55fc10e654..ccc39702a8 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -8,8 +8,8 @@ import 'dart:io'; import 'package:dartdoc/dartdoc.dart'; import 'package:dartdoc/src/model/model.dart'; -import 'package:dartdoc/src/model_utils.dart'; import 'package:dartdoc/src/render/category_renderer.dart'; +import 'package:dartdoc/src/render/parameter_renderer.dart'; import 'package:dartdoc/src/warnings.dart'; import 'package:test/test.dart'; @@ -2084,7 +2084,8 @@ void main() { }); test('handles dynamic parameters correctly', () { - expect(linkedParams(f1.parameters), contains('lastParam')); + expect(ParameterRendererHtml().renderLinkedParams(f1.parameters), + contains('lastParam')); }); test('async function', () { @@ -2123,7 +2124,8 @@ void main() { test('function with a parameter having type FutureOr', () { expect( - linkedParams(paramOfFutureOrNull.parameters), + ParameterRendererHtml() + .renderLinkedParams(paramOfFutureOrNull.parameters), equals( 'FutureOr<Null> future')); }); @@ -2151,7 +2153,8 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, test('typedef params have proper signature', () { ModelFunction function = fakeLibrary.functions.firstWhere((f) => f.name == 'addCallback'); - String params = linkedParams(function.parameters); + String params = + ParameterRendererHtml().renderLinkedParams(function.parameters); expect( params, '' @@ -2160,7 +2163,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, function = fakeLibrary.functions.firstWhere((f) => f.name == 'addCallback2'); - params = linkedParams(function.parameters); + params = ParameterRendererHtml().renderLinkedParams(function.parameters); expect( params, '' @@ -2174,7 +2177,8 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, }); test('can resolve functions as parameters', () { - String params = linkedParams(doAComplicatedThing.parameters); + String params = ParameterRendererHtml() + .renderLinkedParams(doAComplicatedThing.parameters); expect(params, 'int x, {void doSomething(int aThingParameter, String anotherThing), void doSomethingElse(int aThingParameter, double somethingElse)}'); }); @@ -2292,7 +2296,9 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, .singleWhere((m) => m.name == 'operator +'); expect(aInheritedAdditionOperator.linkedReturnType, 'ParameterizedClass<List<int>>'); - expect(linkedParams(aInheritedAdditionOperator.parameters), + expect( + ParameterRendererHtml() + .renderLinkedParams(aInheritedAdditionOperator.parameters), 'ParameterizedClass<List<int>> other'); }); @@ -3258,8 +3264,8 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, test('a function requiring a Future parameter', () { expect( - linkedParams(aVoidParameter.parameters, - showMetadata: true, showNames: true), + ParameterRendererHtml(showMetadata: true, showNames: true) + .renderLinkedParams(aVoidParameter.parameters), equals( 'Future<void> p1')); }); @@ -3341,7 +3347,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, () { Constructor theConstructor = TypedefUsingClass.constructors.first; expect( - linkedParams(theConstructor.parameters), + ParameterRendererHtml().renderLinkedParams(theConstructor.parameters), equals( 'ParameterizedTypedef<double> x')); }); @@ -3488,21 +3494,24 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, }); test('param with generics', () { - var params = linkedParams(methodWithGenericParam.parameters); + var params = ParameterRendererHtml() + .renderLinkedParams(methodWithGenericParam.parameters); expect(params.contains('List') && params.contains('Apple'), isTrue); }); test('commas on same param line', () { ModelFunction method = fakeLibrary.functions.firstWhere((f) => f.name == 'paintImage1'); - String params = linkedParams(method.parameters); + String params = + ParameterRendererHtml().renderLinkedParams(method.parameters); expect(params, contains(', ')); }); test('param with annotations', () { ModelFunction method = fakeLibrary.functions.firstWhere((f) => f.name == 'paintImage1'); - String params = linkedParams(method.parameters); + String params = + ParameterRendererHtml().renderLinkedParams(method.parameters); expect(params, contains('@required')); }); @@ -3513,7 +3522,8 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, }); test('typedef param is linked and does not include types', () { - var params = linkedParams(methodWithTypedefParam.parameters); + var params = ParameterRendererHtml() + .renderLinkedParams(methodWithTypedefParam.parameters); expect( params, equals(