diff --git a/lib/src/model/enum.dart b/lib/src/model/enum.dart index 0ff67e468a..91c2e68a91 100644 --- a/lib/src/model/enum.dart +++ b/lib/src/model/enum.dart @@ -5,6 +5,7 @@ // TODO(jcollins-g): Consider Enum as subclass of Container? import 'package:analyzer/dart/element/element.dart'; import 'package:dartdoc/src/model/model.dart'; +import 'package:dartdoc/src/render/enum_field_renderer.dart'; class Enum extends Class { Enum(ClassElement element, Library library, PackageGraph packageGraph) @@ -32,24 +33,18 @@ class Enum extends Class { /// Enum's fields are virtual, so we do a little work to create /// usable values for the docs. class EnumField extends Field { - int _index; + int index; EnumField(FieldElement element, Library library, PackageGraph packageGraph, Accessor getter, Accessor setter) : super(element, library, packageGraph, getter, setter); - EnumField.forConstant(this._index, FieldElement element, Library library, + EnumField.forConstant(this.index, FieldElement element, Library library, PackageGraph packageGraph, Accessor getter) : super(element, library, packageGraph, getter, null); @override - String get constantValueBase { - if (name == 'values') { - return 'const List<${field.enclosingElement.name}>'; - } else { - return 'const ${field.enclosingElement.name}($_index)'; - } - } + String get constantValueBase => EnumFieldRendererHtml().renderValue(this); @override List get documentationFrom { @@ -87,7 +82,7 @@ class EnumField extends Field { if (name == 'index') return false; // If this is something inherited from Object, e.g. hashCode, let the // normal rules apply. - if (_index == null) { + if (index == null) { return super.isCanonical; } // TODO(jcollins-g): We don't actually document this as a separate entity; diff --git a/lib/src/model/model_element.dart b/lib/src/model/model_element.dart index e09ba79d4f..e2e0f4f4c6 100644 --- a/lib/src/model/model_element.dart +++ b/lib/src/model/model_element.dart @@ -27,6 +27,7 @@ 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/render/model_element_renderer.dart'; import 'package:dartdoc/src/source_linker.dart'; import 'package:dartdoc/src/tuple.dart'; import 'package:dartdoc/src/utils.dart'; @@ -1144,8 +1145,7 @@ abstract class ModelElement extends Canonicalization return htmlEscape.convert(name); } - var classContent = isDeprecated ? ' class="deprecated"' : ''; - return '$name'; + return ModelElementRendererHtml().renderLinkedName(this); } /// Replace {@example ...} in API comments with the content of named file. @@ -1370,37 +1370,14 @@ abstract class ModelElement extends Canonicalization warn(PackageWarning.invalidParameter, message: 'A @youtube directive has an invalid URL: ' '"${positionalArgs[2]}". Supported YouTube URLs have the ' - 'follwing format: https://www.youtube.com/watch?v=oHg5SJYRHA0.'); + 'following format: https://www.youtube.com/watch?v=oHg5SJYRHA0.'); return ''; } final String youTubeId = url.group(url.groupCount); final String aspectRatio = (height / width * 100).toStringAsFixed(2); - // Blank lines before and after, and no indenting at the beginning and end - // is needed so that Markdown doesn't confuse this with code, so be - // careful of whitespace here. - return ''' - -

- -

- -'''; // String must end at beginning of line, or following inline text will be - // indented. + return ModelElementRendererHtml() + .renderYoutubeUrl(youTubeId, aspectRatio); }); } @@ -1531,45 +1508,8 @@ abstract class ModelElement extends Canonicalization 'parameter)'); } - // Blank lines before and after, and no indenting at the beginning and end - // is needed so that Markdown doesn't confuse this with code, so be - // careful of whitespace here. - return ''' - -
-
-
- -
- -'''; // String must end at beginning of line, or following inline text will be - // indented. + return ModelElementRendererHtml() + .renderAnimation(uniqueId, width, height, movieUrl, overlayId); }); } diff --git a/lib/src/model/type_parameter.dart b/lib/src/model/type_parameter.dart index fc5d0aeb1f..158095ddaa 100644 --- a/lib/src/model/type_parameter.dart +++ b/lib/src/model/type_parameter.dart @@ -5,6 +5,7 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:dartdoc/src/element_type.dart'; import 'package:dartdoc/src/model/model.dart'; +import 'package:dartdoc/src/render/type_parameters_renderer.dart'; class TypeParameter extends ModelElement { TypeParameter( @@ -76,21 +77,11 @@ abstract class TypeParameters implements ModelElement { bool get hasGenericParameters => typeParameters.isNotEmpty; String get genericParameters { - if (typeParameters.isEmpty) return ''; - - var joined = typeParameters - .map((t) => t.name) - .join(', '); - return '<${joined}>'; + return TypeParametersRendererHtml().renderGenericParameters(this); } String get linkedGenericParameters { - if (typeParameters.isEmpty) return ''; - - var joined = typeParameters - .map((t) => t.linkedName) - .join(', '); - return '<${joined}>'; + return TypeParametersRendererHtml().renderLinkedGenericParameters(this); } @override diff --git a/lib/src/model/typedef.dart b/lib/src/model/typedef.dart index 15a168e4a8..3a134474fd 100644 --- a/lib/src/model/typedef.dart +++ b/lib/src/model/typedef.dart @@ -5,6 +5,7 @@ import 'package:analyzer/dart/element/element.dart'; import 'package:dartdoc/src/element_type.dart'; import 'package:dartdoc/src/model/model.dart'; +import 'package:dartdoc/src/render/typedef_renderer.dart'; class Typedef extends ModelElement with SourceCodeMixin, TypeParameters, Categorization @@ -20,18 +21,14 @@ class Typedef extends ModelElement String get nameWithGenerics => '$name${super.genericParameters}'; @override - String get genericParameters { + String get genericParameters => + TypedefRendererHtml().renderGenericParameters(this); + + List get genericTypeParameters { if (element is GenericTypeAliasElement) { - List genericTypeParameters = - (element as GenericTypeAliasElement).function.typeParameters; - if (genericTypeParameters.isNotEmpty) { - var joined = genericTypeParameters - .map((t) => t.name) - .join(', '); - return '<${joined}>'; - } - } // else, all types are resolved. - return ''; + return (element as GenericTypeAliasElement).function.typeParameters; + } + return Iterable.empty(); } @override diff --git a/lib/src/render/enum_field_renderer.dart b/lib/src/render/enum_field_renderer.dart new file mode 100644 index 0000000000..c9096b0b26 --- /dev/null +++ b/lib/src/render/enum_field_renderer.dart @@ -0,0 +1,20 @@ +// 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:dartdoc/src/model/enum.dart'; + +abstract class EnumFieldRenderer { + String renderValue(EnumField field); +} + +class EnumFieldRendererHtml 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 new file mode 100644 index 0000000000..700e3f2dae --- /dev/null +++ b/lib/src/render/model_element_renderer.dart @@ -0,0 +1,90 @@ +// 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:dartdoc/src/model/model_element.dart'; + +abstract class ModelElementRenderer { + String renderLinkedName(ModelElement modelElement); + + String renderYoutubeUrl(String youTubeId, String aspectRatio); + + String renderAnimation( + String uniqueId, int width, int height, Uri movieUrl, String overlayId); +} + +class ModelElementRendererHtml extends ModelElementRenderer { + @override + String renderLinkedName(ModelElement modelElement) { + var cssClass = modelElement.isDeprecated ? ' class="deprecated"' : ''; + return '${modelElement.name}'; + } + + @override + String renderYoutubeUrl(String youTubeId, String aspectRatio) { + // Blank lines before and after, and no indenting at the beginning and end + // is needed so that Markdown doesn't confuse this with code, so be + // careful of whitespace here. + return ''' + +

+ +

+ +'''; // Must end at start of line, or following inline text will be indented. + } + + @override + String renderAnimation( + String uniqueId, int width, int height, Uri movieUrl, String overlayId) { + return ''' + +
+
+
+ +
+ +'''; // Must end at start of line, or following inline text will be indented. + } +} diff --git a/lib/src/render/type_parameters_renderer.dart b/lib/src/render/type_parameters_renderer.dart new file mode 100644 index 0000000000..879ca0ffaa --- /dev/null +++ b/lib/src/render/type_parameters_renderer.dart @@ -0,0 +1,34 @@ +// 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:dartdoc/src/model/type_parameter.dart'; + +abstract class TypeParametersRenderer { + String renderGenericParameters(TypeParameters typeParameters); + String renderLinkedGenericParameters(TypeParameters typeParameters); +} + +class TypeParametersRendererHtml extends TypeParametersRenderer { + @override + String renderGenericParameters(TypeParameters typeParameters) { + if (typeParameters.typeParameters.isEmpty) { + return ''; + } + var joined = typeParameters.typeParameters + .map((t) => t.name) + .join('
, '); + return '<${joined}>'; + } + + @override + String renderLinkedGenericParameters(TypeParameters typeParameters) { + if (typeParameters.typeParameters.isEmpty) { + return ''; + } + var joined = typeParameters.typeParameters + .map((t) => t.linkedName) + .join(', '); + return '<${joined}>'; + } +} diff --git a/lib/src/render/typedef_renderer.dart b/lib/src/render/typedef_renderer.dart new file mode 100644 index 0000000000..385225b87b --- /dev/null +++ b/lib/src/render/typedef_renderer.dart @@ -0,0 +1,22 @@ +// 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:dartdoc/src/model/typedef.dart'; + +abstract class TypedefRenderer { + String renderGenericParameters(Typedef typedef); +} + +class TypedefRendererHtml 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/test/model_special_cases_test.dart b/test/model_special_cases_test.dart index 782e33f0bb..8abf5af3ed 100644 --- a/test/model_special_cases_test.dart +++ b/test/model_special_cases_test.dart @@ -459,7 +459,7 @@ void main() { PackageWarning.invalidParameter, 'A @youtube directive has an invalid URL: ' '"http://host/path/to/video.mp4". Supported YouTube URLs have ' - 'the follwing format: ' + 'the following format: ' 'https://www.youtube.com/watch?v=oHg5SJYRHA0.'), isTrue); }); @@ -470,7 +470,7 @@ void main() { PackageWarning.invalidParameter, 'A @youtube directive has an invalid URL: ' '"https://www.youtube.com/watch?v=yI-8QHpGIP4&list=PLjxrf2q8roU23XGwz3Km7sQZFTdB996iG&index=5". ' - 'Supported YouTube URLs have the follwing format: ' + 'Supported YouTube URLs have the following format: ' 'https://www.youtube.com/watch?v=oHg5SJYRHA0.'), isTrue); }); diff --git a/test/model_test.dart b/test/model_test.dart index ccc39702a8..58572a73ca 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -10,6 +10,8 @@ import 'package:dartdoc/dartdoc.dart'; import 'package:dartdoc/src/model/model.dart'; import 'package:dartdoc/src/render/category_renderer.dart'; import 'package:dartdoc/src/render/parameter_renderer.dart'; +import 'package:dartdoc/src/render/enum_field_renderer.dart'; +import 'package:dartdoc/src/render/typedef_renderer.dart'; import 'package:dartdoc/src/warnings.dart'; import 'package:test/test.dart'; @@ -1984,20 +1986,19 @@ void main() { }); test("has a (synthetic) values constant", () { - var values = animal.constants.firstWhere((f) => f.name == 'values'); - expect(values, isNotNull); - expect( - values.constantValue, - equals( - 'const List<Animal>')); - expect(values.documentation, startsWith('A constant List')); + var valuesField = animal.constants.firstWhere((f) => f.name == 'values'); + expect(valuesField, isNotNull); + expect(valuesField.constantValue, + equals(EnumFieldRendererHtml().renderValue(valuesField))); + expect(valuesField.documentation, startsWith('A constant List')); }); test('has a constant that does not link anywhere', () { var dog = animal.constants.firstWhere((f) => f.name == 'DOG'); expect(dog.linkedName, equals('DOG')); expect(dog.isConst, isTrue); - expect(dog.constantValue, equals('const Animal(1)')); + expect( + dog.constantValue, equals(EnumFieldRendererHtml().renderValue(dog))); }); test('constants have correct indicies', () { @@ -3410,9 +3411,12 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, 'NewGenericTypedef<T>')); }); - test("generic parameters", () { - expect(t.genericParameters, equals('')); - expect(generic.genericParameters, + // TODO(jdkoren): Not easy to call TypedefRenderer directly because Typedef + // inspects its element member. Find a better way when we start to isolate + // renderer tests. + test("TypedefRendererHtml renders genericParameters", () { + expect(TypedefRendererHtml().renderGenericParameters(t), equals('')); + expect(TypedefRendererHtml().renderGenericParameters(generic), equals('<S>')); }); });