blob: 9558b4bd9acd1c5c010e21d0289a86c5b7835f23 [file] [log] [blame]
// Copyright (c) 2025, 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:analysis_server_client/protocol.dart';
import 'package:analysis_server_client/server.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/utilities/extensions/object.dart';
import 'package:analyzer/src/utilities/extensions/string.dart';
import 'package:analyzer_testing/package_root.dart' as pkg_root;
import 'package:analyzer_utilities/tools.dart';
import 'package:path/path.dart';
void main() async {
await GeneratedContent.generateAll(pkg_root.packageRoot, await allTargets);
}
Future<List<GeneratedContent>> get allTargets async {
var astLibrary = await _getElementLibrary();
return <GeneratedContent>[
GeneratedFile('analyzer/lib/src/dart/element/element.g.dart', (_) async {
var generator = _ElementGenerator(astLibrary);
return await generator.generate();
}),
];
}
String get _analyzerPath => normalize(join(pkg_root.packageRoot, 'analyzer'));
Future<String> _formatSortCode(String path, String code) async {
var server = Server();
await server.start();
server.listenToOutput();
await server.send('analysis.setAnalysisRoots', {
'included': [path],
'excluded': [],
});
Future<void> updateContent() async {
await server.send('analysis.updateContent', {
'files': {
path: {'type': 'add', 'content': code},
},
});
}
await updateContent();
var formatResponse = await server.send('edit.format', {
'file': path,
'selectionOffset': 0,
'selectionLength': code.length,
});
var formatResult = EditFormatResult.fromJson(
ResponseDecoder(null),
'result',
formatResponse,
);
code = SourceEdit.applySequence(code, formatResult.edits);
await updateContent();
var sortResponse = await server.send('edit.sortMembers', {'file': path});
var sortResult = EditSortMembersResult.fromJson(
ResponseDecoder(null),
'result',
sortResponse,
);
code = SourceEdit.applySequence(code, sortResult.edit.edits);
await server.kill();
return code;
}
Future<LibraryElement> _getElementLibrary() async {
var collection = AnalysisContextCollection(includedPaths: [_analyzerPath]);
var analysisContext = collection.contextFor(_analyzerPath);
var analysisSession = analysisContext.currentSession;
var libraryResult = await analysisSession.getLibraryByUri(
'package:analyzer/src/dart/element/element.dart',
);
libraryResult as LibraryElementResult;
return libraryResult.element;
}
class _ElementGenerator {
final LibraryElement astLibrary;
final StringBuffer out = StringBuffer('''
// Copyright (c) 2025, 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.
// THIS FILE IS GENERATED. DO NOT EDIT.
//
// Run 'dart pkg/analyzer/tool/element/generate.dart' to update.
part of 'element.dart';
''');
_ElementGenerator(this.astLibrary);
Future<String> generate() async {
_writeMixins();
var resultPath = normalize(
join(_analyzerPath, 'lib', 'src', 'dart', 'element', 'element.g.dart'),
);
return _formatSortCode(resultPath, out.toString());
}
void _writeMixins() {
for (var generateFragment in astLibrary.generateFragments) {
var fragmentName = generateFragment.element.name;
out.write('''
mixin _${fragmentName}Mixin {
bool hasModifier(Modifier modifier);
void setModifier(Modifier modifier, bool value);
''');
for (var modifier in generateFragment.modifiers) {
var name = modifier.name;
var constName = name.removePrefixOrSelf('is').toScreamingSnake();
out.write('''
${modifier.documentationComment ?? ''}
bool get $name {
return hasModifier(Modifier.$constName);
}
set $name(bool value) {
setModifier(Modifier.$constName, value);
}
''');
}
out.writeln('}\n');
}
}
}
class _GenerateElementModifier {
final String name;
final String? documentationComment;
_GenerateElementModifier({
required this.name,
required this.documentationComment,
});
}
class _GenerateFragment {
final ClassElement element;
final List<_GenerateElementModifier> modifiers;
_GenerateFragment(this.element, this.modifiers);
bool get isConcrete => !element.isAbstract;
_GenerateFragment? get superNode {
return element.supertype?.element
.ifTypeOrNull<ClassElement>()
?.asGenerateFragment;
}
}
extension on ClassElement {
_GenerateFragment? get asGenerateFragment {
var generateObject = metadata.annotations
.map((annotation) {
var generateObject = annotation.computeConstantValue();
var generateObjectType = generateObject?.type;
if (generateObjectType?.element?.name != 'GenerateFragmentImpl') {
return null;
}
return generateObject;
})
.nonNulls
.firstOrNull;
if (generateObject == null) {
return null;
}
var modifiersField = generateObject.getField('modifiers')!;
var modifiers = modifiersField.toListValue()!.map((modifier) {
var variable = modifier.variable!;
return _GenerateElementModifier(
name: variable.name!,
documentationComment: variable.documentationComment,
);
}).toList();
return _GenerateFragment(this, modifiers);
}
}
extension on LibraryElement {
List<_GenerateFragment> get generateFragments {
return classes
.map((element) => element.asGenerateFragment)
.nonNulls
.toList();
}
}