blob: 0c1141deabe42095c9859143285f88954ed50066 [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 'package:analysis_server/lsp_protocol/protocol_generated.dart';
import 'package:analysis_server/lsp_protocol/protocol_special.dart';
import 'package:analysis_server/src/computer/computer_outline.dart';
import 'package:analysis_server/src/lsp/client_capabilities.dart';
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/lsp_analysis_server.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analysis_server/src/protocol_server.dart' show Outline;
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/source/line_info.dart';
class DocumentSymbolHandler extends MessageHandler<DocumentSymbolParams,
Either2<List<DocumentSymbol>, List<SymbolInformation>>?> {
DocumentSymbolHandler(LspAnalysisServer server) : super(server);
@override
Method get handlesMessage => Method.textDocument_documentSymbol;
@override
LspJsonHandler<DocumentSymbolParams> get jsonHandler =>
DocumentSymbolParams.jsonHandler;
@override
Future<ErrorOr<Either2<List<DocumentSymbol>, List<SymbolInformation>>?>>
handle(DocumentSymbolParams params, CancellationToken token) async {
final clientCapabilities = server.clientCapabilities;
if (clientCapabilities == null || !isDartDocument(params.textDocument)) {
return success(
Either2<List<DocumentSymbol>, List<SymbolInformation>>.t2([]),
);
}
final path = pathOfDoc(params.textDocument);
final unit = await path.mapResult(requireResolvedUnit);
return unit.mapResult(
(unit) => _getSymbols(clientCapabilities, path.result, unit));
}
DocumentSymbol _asDocumentSymbol(
Set<SymbolKind> supportedKinds,
LineInfo lineInfo,
Outline outline,
) {
final codeRange = toRange(lineInfo, outline.codeOffset, outline.codeLength);
final nameLocation = outline.element.location;
final nameRange = nameLocation != null
? toRange(lineInfo, nameLocation.offset, nameLocation.length)
: null;
return DocumentSymbol(
name: toElementName(outline.element),
detail: outline.element.parameters,
kind: elementKindToSymbolKind(supportedKinds, outline.element.kind),
deprecated: outline.element.isDeprecated,
range: codeRange,
selectionRange: nameRange ?? codeRange,
children: outline.children
?.map((child) => _asDocumentSymbol(supportedKinds, lineInfo, child))
.toList(),
);
}
SymbolInformation? _asSymbolInformation(
String? containerName,
Set<SymbolKind> supportedKinds,
String documentUri,
LineInfo lineInfo,
Outline outline,
) {
final location = outline.element.location;
if (location == null) {
return null;
}
return SymbolInformation(
name: toElementName(outline.element),
kind: elementKindToSymbolKind(supportedKinds, outline.element.kind),
deprecated: outline.element.isDeprecated,
location: Location(
uri: documentUri,
range: toRange(lineInfo, location.offset, location.length),
),
containerName: containerName,
);
}
ErrorOr<Either2<List<DocumentSymbol>, List<SymbolInformation>>?> _getSymbols(
LspClientCapabilities capabilities,
String path,
ResolvedUnitResult unit,
) {
final computer = DartUnitOutlineComputer(unit);
final outline = computer.compute();
if (capabilities.hierarchicalSymbols) {
// Return a tree of DocumentSymbol only if the client shows explicit support
// for it.
final children = outline.children;
if (children == null) {
return success(null);
}
return success(
Either2<List<DocumentSymbol>, List<SymbolInformation>>.t1(
children
.map((child) => _asDocumentSymbol(
capabilities.documentSymbolKinds, unit.lineInfo, child))
.toList(),
),
);
} else {
// Otherwise, we need to use the original flat SymbolInformation.
final allSymbols = <SymbolInformation>[];
final documentUri = Uri.file(path).toString();
// Adds a symbol and it's children recursively, supplying the parent
// name as required by SymbolInformation.
void addSymbol(Outline outline, {String? parentName}) {
final symbol = _asSymbolInformation(
parentName,
capabilities.documentSymbolKinds,
documentUri,
unit.lineInfo,
outline,
);
if (symbol != null) {
allSymbols.add(symbol);
}
outline.children?.forEach(
(c) => addSymbol(c, parentName: outline.element.name),
);
}
outline.children?.forEach(addSymbol);
return success(
Either2<List<DocumentSymbol>, List<SymbolInformation>>.t2(allSymbols),
);
}
}
}