blob: 7274d6545dd1faa2d0c9f76ff7cadf9f1f19c126 [file] [log] [blame]
// Copyright (c) 2015, 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 '../js_ast/js_ast.dart' as JS show TypeRef, ClosureTypePrinter;
/// Set of closure annotations that can be [toString]ed to a single JsDoc comment.
/// See https://developers.google.com/closure/compiler/docs/js-for-compiler
///
/// TODO(ochafik): Support inclusion of 'normal' comments (including @param comments).
class ClosureAnnotation {
final String comment;
final bool isConst;
final bool isConstructor;
final bool isFinal;
final bool isNoCollapse;
final bool isNoSideEffects;
final bool isOverride;
final bool isPrivate;
final bool isProtected;
final bool isStruct;
final bool isTypedef;
final JS.TypeRef lendsToType;
final JS.TypeRef returnType;
final JS.TypeRef superType;
final JS.TypeRef thisType;
final JS.TypeRef throwsType;
final JS.TypeRef type;
final List<JS.TypeRef> interfaces;
final List<String> templates;
final Map<String, JS.TypeRef> paramTypes;
ClosureAnnotation(
{this.comment,
this.interfaces = const [],
this.isConst = false,
this.isConstructor = false,
this.isFinal = false,
this.isNoCollapse = false,
this.isNoSideEffects = false,
this.isOverride = false,
this.isPrivate = false,
this.isProtected = false,
this.isStruct = false,
this.isTypedef = false,
this.lendsToType,
this.paramTypes = const {},
this.returnType,
this.superType,
this.templates = const [],
this.thisType,
this.throwsType,
this.type});
@override
int get hashCode => _cachedString.hashCode;
@override
bool operator ==(other) =>
other is ClosureAnnotation && _cachedString == other._cachedString;
@override
String toString([String indent = '']) =>
_cachedString.replaceAll('\n', '\n$indent');
String _print(JS.TypeRef t) => (JS.ClosureTypePrinter()..visit(t)).toString();
String __cachedString;
String get _cachedString {
if (__cachedString == null) {
bool isNonWildcard(JS.TypeRef t) => t != null && !t.isAny && !t.isUnknown;
var lines = <String>[];
if (comment != null) lines.addAll(comment.split('\n'));
if (templates != null && templates.isNotEmpty) {
lines.add('@template ${templates.join(', ')}');
}
if (thisType != null) lines.add('@this {${_print(thisType)}}');
if (isOverride) lines.add('@override');
if (isNoSideEffects) lines.add('@nosideeffects');
if (isNoCollapse) lines.add('@nocollapse');
if (lendsToType != null) lines.add('@lends {${_print(lendsToType)}}');
{
var typeHolders = <String>[];
if (isPrivate) typeHolders.add('@private');
if (isProtected) typeHolders.add('@protected');
if (isFinal) typeHolders.add('@final');
if (isConst) typeHolders.add('@const');
if (isTypedef) typeHolders.add('@typedef');
if (isNonWildcard(type)) {
if (typeHolders.isEmpty) typeHolders.add('@type');
typeHolders.add('{${_print(type)}}');
}
if (!typeHolders.isEmpty) lines.add(typeHolders.join(' '));
}
{
List constructorLine = [];
if (isConstructor) constructorLine.add('@constructor');
if (isStruct) constructorLine.add('@struct');
if (isNonWildcard(superType)) {
constructorLine.add('@extends {${_print(superType)}}');
}
if (constructorLine.isNotEmpty) lines.add(constructorLine.join(' '));
}
if (interfaces != null) {
for (var interface in interfaces) {
if (isNonWildcard(interface))
lines.add('@implements {${_print(interface)}}');
}
}
if (paramTypes != null) {
paramTypes.forEach((String paramName, JS.TypeRef paramType) {
// Must output params even with wildcard type.
lines.add('@param {${_print(paramType)}} $paramName');
});
}
if (isNonWildcard(returnType))
lines.add('@return {${_print(returnType)}}');
if (isNonWildcard(throwsType))
lines.add('@throws {${_print(throwsType)}}');
if (lines.length == 0) return '';
if (lines.length == 1) return '/** ${lines.single} */';
__cachedString = '/**\n' + lines.map((l) => ' * $l').join('\n') + '\n */';
}
return __cachedString;
}
}