blob: 0ca98c374e5ea496ae224cad11c05c8a22c157bd [file] [log] [blame]
// Copyright (c) 2018, 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 'typescript_parser.dart';
/// Removes types that are in the spec that we don't want in other signatures.
bool allowTypeInSignatures(TypeBase type) {
// Don't allow arrays of MarkedStrings, but do allow simple MarkedStrings.
// The only place that uses these are Hovers and we only send one value
// (to match the MarkupString equiv) so the array just makes the types
// unnecessarily complicated.
if (type is ArrayType) {
final elementType = type.elementType;
if (elementType is Type && elementType.name == 'MarkedString') {
return false;
}
}
return true;
}
String cleanComment(String comment) {
// Remove the start/end comment markers.
if (comment.startsWith('/**') && comment.endsWith('*/')) {
comment = comment.substring(3, comment.length - 2);
} else if (comment.startsWith('//')) {
comment = comment.substring(2);
}
final commentLinePrefixes = RegExp(r'\n\s*\* ?');
final nonConcurrentNewlines = RegExp(r'\n(?![\n\s\-*])');
final newLinesThatRequireReinserting = RegExp(r'\n (\w)');
// Remove any Windows newlines from the source.
comment = comment.replaceAll('\r', '');
// Remove the * prefixes.
comment = comment.replaceAll(commentLinePrefixes, '\n');
// Remove and newlines that look like wrapped text.
comment = comment.replaceAll(nonConcurrentNewlines, ' ');
// The above will remove one of the newlines when there are two, so we need
// to re-insert newlines for any block that starts immediately after a newline.
comment = comment.replaceAllMapped(
newLinesThatRequireReinserting, (m) => '\n\n${m.group(1)}');
return comment.trim();
}
/// Improves types in generated code, including:
///
/// - Fixes up some enum types that are not as specific as they could be in the
/// spec. For example, Diagnostic.severity is typed "number" but can be mapped
/// to the DiagnosticSeverity enum class.
///
/// - Narrows unions to single types where they're only generated on the server
/// and we know we always use a specific type. This avoids wrapping a lot
/// of code in `EitherX<Y,Z>.tX()` and simplifies the testing of them.
String? getImprovedType(String interfaceName, String? fieldName) {
const improvedTypeMappings = <String, Map<String, String>>{
'Diagnostic': {
'severity': 'DiagnosticSeverity',
'code': 'String',
'data': 'object',
},
'TextDocumentSyncOptions': {
'change': 'TextDocumentSyncKind',
},
'TextDocumentChangeRegistrationOptions': {
'syncKind': 'TextDocumentSyncKind',
},
'FileSystemWatcher': {
'kind': 'WatchKind',
},
'CompletionItem': {
'kind': 'CompletionItemKind',
'data': 'CompletionItemResolutionInfo',
},
'CallHierarchyItem': {
'data': 'object',
},
'DocumentHighlight': {
'kind': 'DocumentHighlightKind',
},
'FoldingRange': {
'kind': 'FoldingRangeKind',
},
'SymbolInformation': {
'kind': 'SymbolKind',
},
'ParameterInformation': {
'label': 'String',
},
'ProgressParams': {
'value': 'object',
},
'ServerCapabilities': {
'changeNotifications': 'bool',
},
'TextDocumentEdit': {
'edits': 'TextDocumentEditEdits',
}
};
final interface = improvedTypeMappings[interfaceName];
return interface != null ? interface[fieldName] : null;
}
/// Removes types that are in the spec that we don't want to emit.
bool includeTypeDefinitionInOutput(AstNode node) {
const ignoredTypes = {
// InitializeError is not used for v3.0 (Feb 2017) and by dropping it we don't
// have to handle any cases where both a namespace and interfaces are declared
// with the same name.
'InitializeError',
// We don't use `InitializeErrorCodes` as it contains only one error code
// that has been deprecated and we've never used.
'InitializeErrorCodes',
// Handled in custom classes now in preperation for JSON meta model which
// does not specify them.
'Message',
'RequestMessage',
'NotificationMessage',
'ResponseMessage',
'ResponseError',
};
const ignoredPrefixes = {
// We don't emit MarkedString because it gets mapped to a simple String
// when getting the .dartType for it.
'MarkedString'
};
final shouldIgnore = ignoredTypes.contains(node.name) ||
ignoredPrefixes.any((ignore) => node.name.startsWith(ignore));
return !shouldIgnore;
}