blob: 33f8affc34acd63193c127cc1802466187576912 [file] [log] [blame]
// Copyright (c) 2017, 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 'dart:async';
import 'package:analysis_server/src/analysis_server.dart';
import 'package:analysis_server/src/plugin/plugin_manager.dart';
import 'package:analysis_server/src/status/diagnostics.dart';
import 'package:analysis_server/src/status/utilities/string_extensions.dart';
/// The page that displays information about _new_ (not legacy) analyzer
/// plugins.
class PluginsPage extends DiagnosticPageWithNav {
@override
AnalysisServer server;
PluginsPage(DiagnosticsSite site, this.server)
: super(site, 'plugins', 'Plugins', description: 'Plugins in use.');
@override
Future<void> generateContent(Map<String, String> params) async {
var pluginIsolates = server.pluginManager.newPluginIsolates;
if (pluginIsolates.isEmpty) {
blankslate('No known analysis plugins.');
return;
}
pluginIsolates.sort(
(first, second) => first.pluginId.compareTo(second.pluginId),
);
for (var isolate in pluginIsolates) {
var id = isolate.pluginId;
var data = isolate.data;
p(
'The following plugins are running from a single bootstrapped '
'location:',
);
_emitTable([
['Bootstrap package path:', id],
if (isolate.executionPath case var executionPath?)
['Execution path:', executionPath.wordBreakOnSlashes],
if (isolate.packageConfigPath case var packageConfigPath?)
['Package config path', packageConfigPath.wordBreakOnSlashes],
]);
if (data.name == null) {
if (isolate.exception != null) {
p('Not running due to:');
pre(() {
buf.write(isolate.exception);
});
} else {
p(
'Not running for unknown reason (no exception was caught while '
'starting).',
);
}
continue;
}
p('Associated contexts:');
var contexts = isolate.contextRoots;
if (contexts.isEmpty) {
blankslate('none');
} else {
ul(contexts.toList(), (root) {
buf.writeln(root.root);
});
}
var details = await isolate.requestDetails();
if (details == null) {
// Either the plugin is not alive, or the plugin did not respond in
// time.
// TODO(srawlins): Distinguish between the two.
p('Plugin isolate did not respond with plugin details.');
} else {
for (var plugin in details.plugins) {
h3(plugin.name);
if (plugin.lintRules.isNotEmpty) {
p('Lint rules:');
ul(plugin.lintRules, (rule) {
buf.writeln(rule);
});
}
if (plugin.warningRules.isNotEmpty) {
p('Warning rules:');
ul(plugin.warningRules, (rule) {
buf.writeln(rule);
});
}
if (plugin.fixes.isNotEmpty) {
p('Quick fixes:');
ul(plugin.fixes, (fix) {
var codes = fix.codes.join(', ');
buf.writeln('${fix.id}: "${fix.message}" to fix $codes');
});
}
if (plugin.assists.isNotEmpty) {
p('Assists:');
ul(plugin.assists, (assist) {
buf.writeln('${assist.id}: "${assist.message}"');
});
}
}
}
p('Performance:');
var responseTimes = PluginManager.pluginResponseTimes[isolate] ?? {};
var entries = responseTimes.entries.toList();
entries.sort((first, second) => first.key.compareTo(second.key));
for (var entry in entries) {
var requestName = entry.key;
var data = entry.value;
// TODO(brianwilkerson): Consider displaying these times as a graph,
// similar to the one in CompletionPage.generateContent.
var buffer = StringBuffer();
buffer.write(requestName);
buffer.write(' ');
buffer.write(data.toAnalyticsString());
p(buffer.toString());
}
}
}
void _emitTable(List<List<String>> data) {
buf.writeln('<table>');
for (var row in data) {
buf.writeln('<tr>');
for (var value in row) {
buf.writeln('<td>$value</td>');
}
buf.writeln('</tr>');
}
buf.writeln('</table>');
}
}