02ea7d0b14
This adds support for keywords like `return`, `yield`, `break`, `continue` to Document Highlights. For break/continue, the matching loop keyword (`do`/`while`/`for`) is also highlighted (and this works in both directions). This behaviour matches what I see for TypeScript in VS Code. This is only supported for LSP because the legacy protocol groups these by "Elements" which we don't have for loops (though since IntelliJ uses its own data for occurrences, it's not clear to me if anyone is using Occurrences over the legacy protocol). Fixes https://github.com/dart-lang/sdk/issues/61170 Change-Id: I5563ec0a91a6fe33d10a6317d33e9256b3c50209 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/442061 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Samuel Rawlins <srawlins@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
97 lines
3.6 KiB
Dart
97 lines
3.6 KiB
Dart
// Copyright (c) 2022, 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.dart'
|
|
show Position, Range;
|
|
import 'package:analysis_server/src/lsp/mapping.dart';
|
|
import 'package:analyzer/source/line_info.dart';
|
|
import 'package:analyzer/src/test_utilities/test_code_format.dart';
|
|
import 'package:collection/collection.dart';
|
|
import 'package:test/test.dart' show expect;
|
|
|
|
extension ListTestCodePositionExtension on List<TestCodePosition> {
|
|
/// Return the LSP [Position]s of the markers.
|
|
///
|
|
/// Positions are based on [TestCode.code], with all parsed markers removed.
|
|
List<Position> get positions => map((position) => position.position).toList();
|
|
}
|
|
|
|
extension ListTestCodeRangeExtension on List<TestCodeRange> {
|
|
/// The LSP [Range]s indicated by the markers.
|
|
///
|
|
/// Ranges are based on [TestCode.code], with all parsed markers removed.
|
|
List<Range> get ranges => map((range) => range.range).toList();
|
|
}
|
|
|
|
extension TestCodeExtension on TestCode {
|
|
/// Verifies that [actualRanges] match with the marked ranges in [code].
|
|
///
|
|
/// This is done by taking the resulting code (without markers) and then
|
|
/// inserting the range markers from [actualRanges] and performing a string
|
|
/// comparison.
|
|
///
|
|
/// This can result in a simpler failure message/diff than having the JSON of
|
|
/// a large [List<Range>] dumped.
|
|
void verifyRanges(Iterable<Range> actualRanges) {
|
|
// First rewrite the marked code to have only ranges, and using the indexed
|
|
// markers. This removes any `^` we won't be verifing and changes any
|
|
// range shorthand to indexed tokens.
|
|
var expected = _withRanges(ranges.map((range) => range.range));
|
|
|
|
// Now build the same with the actual result ranges.
|
|
var actual = _withRanges(actualRanges);
|
|
|
|
expect(actual, expected);
|
|
}
|
|
|
|
/// Replaces all marked positions and ranges with [ranges].
|
|
String _withRanges(Iterable<Range> newRanges) {
|
|
var insertedCharacters = 0;
|
|
var rangeIndex = 0;
|
|
var newContent = code; // Start from the unmarked code.
|
|
var lineInfo = LineInfo.fromContent(newContent);
|
|
|
|
/// Helper to insert the markers for [range] into the content.
|
|
void markRange(Range range) {
|
|
/// Helper to insert [text] at [offset] in the content.
|
|
void insertText(int offset, String text) {
|
|
var adjustedOffset = insertedCharacters + offset;
|
|
newContent = newContent.replaceRange(
|
|
adjustedOffset,
|
|
adjustedOffset,
|
|
text,
|
|
);
|
|
insertedCharacters += text.length;
|
|
}
|
|
|
|
var startOffset = toOffset(lineInfo, range.start).result;
|
|
var endOffset = toOffset(lineInfo, range.end).result;
|
|
insertText(startOffset, '/*[$rangeIndex*/');
|
|
insertText(endOffset, '/*$rangeIndex]*/');
|
|
rangeIndex++;
|
|
}
|
|
|
|
var sortedRanges = newRanges.sortedBy(
|
|
(range) => toOffset(lineInfo, range.start).result,
|
|
);
|
|
sortedRanges.forEach(markRange);
|
|
|
|
return newContent;
|
|
}
|
|
}
|
|
|
|
extension TestCodePositionExtension on TestCodePosition {
|
|
/// Return the LSP [Position] of the marker.
|
|
///
|
|
/// Positions are based on [TestCode.code], with all parsed markers removed.
|
|
Position get position => toPosition(lineInfo.getLocation(offset));
|
|
}
|
|
|
|
extension TestCodeRangeExtension on TestCodeRange {
|
|
/// The LSP [Range] indicated by the markers.
|
|
///
|
|
/// Ranges are based on [TestCode.code], with all parsed markers removed.
|
|
Range get range => toRange(lineInfo, sourceRange.offset, sourceRange.length);
|
|
}
|