blob: 34a7f0a87e24b48f49db968e2169f36b9a4c8ae4 [file] [log] [blame]
// Copyright (c) 2021, 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_selection_ranges.dart'
hide SelectionRange;
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
import 'package:analysis_server/src/lsp/mapping.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/ast/ast.dart';
class SelectionRangeHandler
extends MessageHandler<SelectionRangeParams, List<SelectionRange>?> {
SelectionRangeHandler(super.server);
@override
Method get handlesMessage => Method.textDocument_selectionRange;
@override
LspJsonHandler<SelectionRangeParams> get jsonHandler =>
SelectionRangeParams.jsonHandler;
@override
Future<ErrorOr<List<SelectionRange>?>> handle(SelectionRangeParams params,
MessageInfo message, CancellationToken token) async {
if (!isDartDocument(params.textDocument)) {
return success(null);
}
final path = pathOfDoc(params.textDocument);
return path.mapResult((path) async {
final unit = await requireUnresolvedUnit(path);
final positions = params.positions;
final offsets = await unit.mapResult((unit) =>
ErrorOr.all(positions.map((pos) => toOffset(unit.lineInfo, pos))));
final allRanges = await offsets.mapResult(
(offsets) => success(_getSelectionRangesForOffsets(offsets, unit)));
return allRanges;
});
}
SelectionRange _getSelectionRangesForOffset(
CompilationUnit unit, int offset) {
final lineInfo = unit.lineInfo;
final computer = DartSelectionRangeComputer(unit, offset);
final ranges = computer.compute();
// Loop through the items starting at the end (the outermost range), using
// each item as the parent for the next item.
SelectionRange? last;
for (var i = ranges.length - 1; i >= 0; i--) {
final range = ranges[i];
last = SelectionRange(
range: toRange(lineInfo, range.offset, range.length),
parent: last,
);
}
// When there is no range for a given position, return an empty range at the
// requested position. From the LSP spec:
//
// "To allow for results where some positions have selection ranges and
// others do not, result[i].range is allowed to be the empty range at
// positions[i]."
return last ?? SelectionRange(range: toRange(lineInfo, offset, 0));
}
List<SelectionRange> _getSelectionRangesForOffsets(
List<int> offsets, ErrorOr<ParsedUnitResult> unit) {
return offsets
.map((offset) => _getSelectionRangesForOffset(unit.result.unit, offset))
.toList();
}
}