blob: 41962830fb64df447324cc23a2c734fe68dfe534 [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/src/protocol_server.dart';
import 'package:analyzer_utilities/check/check.dart';
import 'package:meta/meta.dart';
class CompletionResponseForTesting {
final int requestOffset;
final int replacementOffset;
final int replacementLength;
final bool isIncomplete;
final List<CompletionSuggestion> suggestions;
CompletionResponseForTesting({
required this.requestOffset,
required this.replacementOffset,
required this.replacementLength,
required this.isIncomplete,
required this.suggestions,
});
factory CompletionResponseForTesting.legacy(
int requestOffset,
CompletionResultsParams parameters,
) {
return CompletionResponseForTesting(
requestOffset: requestOffset,
replacementOffset: parameters.replacementOffset,
replacementLength: parameters.replacementLength,
isIncomplete: false,
suggestions: parameters.results,
);
}
}
/// A completion suggestion with the response for context.
class CompletionSuggestionForTesting {
final CompletionResponseForTesting response;
final CompletionSuggestion suggestion;
CompletionSuggestionForTesting({
required this.response,
required this.suggestion,
});
/// Return the effective replacement length.
int get replacementLength =>
suggestion.replacementLength ?? response.replacementLength;
/// Return the effective replacement offset.
int get replacementOffset =>
suggestion.replacementOffset ?? response.replacementOffset;
@override
String toString() => '(completion: ${suggestion.completion})';
}
extension CompletionResponseExtension
on CheckTarget<CompletionResponseForTesting> {
CheckTarget<bool> get isIncomplete {
return nest(
value.isIncomplete,
(selected) => 'has isIncomplete ${valueStr(selected)}',
);
}
CheckTarget<int> get replacementLength {
return nest(
value.replacementLength,
(selected) => 'has replacementLength ${valueStr(selected)}',
);
}
CheckTarget<int> get replacementOffset {
return nest(
value.replacementOffset,
(selected) => 'has replacementOffset ${valueStr(selected)}',
);
}
CheckTarget<List<CompletionSuggestionForTesting>> get suggestions {
var suggestions = value.suggestions.map((e) {
return CompletionSuggestionForTesting(
response: value,
suggestion: e,
);
}).toList();
return nest(
suggestions,
(selected) => 'suggestions ${valueStr(selected)}',
);
}
void assertComplete() {
isIncomplete.isFalse;
}
void assertIncomplete() {
isIncomplete.isTrue;
}
/// Check that the replacement offset is the completion request offset,
/// and the length of the replacement is zero.
void hasEmptyReplacement() {
hasReplacement(left: 0, right: 0);
}
/// Check that the replacement offset is the completion request offset
/// minus [left], and the length of the replacement is `left + right`.
void hasReplacement({int left = 0, int right = 0}) {
replacementOffset.isEqualTo(value.requestOffset - left);
replacementLength.isEqualTo(left + right);
}
}
extension CompletionSuggestionExtension
on CheckTarget<CompletionSuggestionForTesting> {
CheckTarget<String> get completion {
return nest(
value.suggestion.completion,
(selected) => 'has completion ${valueStr(selected)}',
);
}
CheckTarget<String?> get defaultArgumentListString {
return nest(
value.suggestion.defaultArgumentListString,
(selected) => 'has defaultArgumentListString ${valueStr(selected)}',
);
}
CheckTarget<List<int>?> get defaultArgumentListTextRanges {
return nest(
value.suggestion.defaultArgumentListTextRanges,
(selected) => 'has defaultArgumentListTextRanges ${valueStr(selected)}',
);
}
CheckTarget<String?> get docComplete {
return nest(
value.suggestion.docComplete,
(selected) => 'has docComplete ${valueStr(selected)}',
);
}
CheckTarget<String?> get docSummary {
return nest(
value.suggestion.docSummary,
(selected) => 'has docSummary ${valueStr(selected)}',
);
}
CheckTarget<Element?> get element {
return nest(
value.suggestion.element,
(selected) => 'has element ${valueStr(selected)}',
);
}
void get isClass {
kind.isIdentifier;
element.isNotNull.kind.isClass;
}
void get isConstructorInvocation {
kind.isInvocation;
element.isNotNull.kind.isConstructor;
}
void get isField {
kind.isIdentifier;
element.isNotNull.kind.isField;
}
void get isGetter {
kind.isIdentifier;
element.isNotNull.kind.isGetter;
}
void get isMethodInvocation {
kind.isInvocation;
element.isNotNull.kind.isMethod;
}
@useResult
CheckTarget<bool?> get isNotImported {
return nest(
value.suggestion.isNotImported,
(selected) => 'has isNotImported ${valueStr(selected)}',
);
}
void get isParameter {
kind.isIdentifier;
element.isNotNull.kind.isParameter;
}
void get isSetter {
kind.isIdentifier;
element.isNotNull.kind.isSetter;
}
void get isTopLevelVariable {
kind.isIdentifier;
element.isNotNull.kind.isTopLevelVariable;
}
CheckTarget<CompletionSuggestionKind> get kind {
return nest(
value.suggestion.kind,
(selected) => 'has kind ${valueStr(selected)}',
);
}
CheckTarget<String?> get libraryUriToImport {
return nest(
value.suggestion.isNotImported == true
? value.suggestion.element?.libraryUri
: null,
(selected) => 'has libraryUriToImport ${valueStr(selected)}',
);
}
CheckTarget<String?> get parameterType {
return nest(
value.suggestion.parameterType,
(selected) => 'has parameterType ${valueStr(selected)}',
);
}
/// Return the effective replacement length.
CheckTarget<int> get replacementLength {
return nest(
value.replacementLength,
(selected) => 'has replacementLength ${valueStr(selected)}',
);
}
/// Return the effective replacement offset.
CheckTarget<int> get replacementOffset {
return nest(
value.replacementOffset,
(selected) => 'has replacementOffset ${valueStr(selected)}',
);
}
CheckTarget<String?> get returnType {
return nest(
value.suggestion.returnType,
(selected) => 'has returnType ${valueStr(selected)}',
);
}
CheckTarget<int> get selectionLength {
return nest(
value.suggestion.selectionLength,
(selected) => 'has selectionLength ${valueStr(selected)}',
);
}
CheckTarget<int> get selectionOffset {
return nest(
value.suggestion.selectionOffset,
(selected) => 'has selectionOffset ${valueStr(selected)}',
);
}
/// Check that the effective replacement offset is the completion request
/// offset, and the length of the replacement is zero.
void hasEmptyReplacement() {
hasReplacement(left: 0, right: 0);
}
/// Check that the effective replacement offset is the completion request
/// offset minus [left], and the length of the replacement is `left + right`.
void hasReplacement({int left = 0, int right = 0}) {
replacementOffset.isEqualTo(value.response.requestOffset - left);
replacementLength.isEqualTo(left + right);
}
void hasSelection({required int offset, int length = 0}) {
selectionOffset.isEqualTo(offset);
selectionLength.isEqualTo(length);
}
}
extension CompletionSuggestionKindExtension
on CheckTarget<CompletionSuggestionKind> {
void get isIdentifier {
isEqualTo(CompletionSuggestionKind.IDENTIFIER);
}
void get isInvocation {
isEqualTo(CompletionSuggestionKind.INVOCATION);
}
}
extension CompletionSuggestionListExtension
on CheckTarget<List<CompletionSuggestionForTesting>> {
CheckTarget<Iterable<String>> get completions {
return nest(
value.map((e) => e.suggestion.completion).toList(),
(selected) => 'has completions ${valueStr(selected)}',
);
}
CheckTarget<Iterable<CompletionSuggestionForTesting>> get withElementClass {
return nest(
value.where((e) {
return e.suggestion.element?.kind == ElementKind.CLASS;
}).toList(),
(selected) => 'withElementClass ${valueStr(selected)}',
);
}
}
extension CompletionSuggestionsExtension
on CheckTarget<Iterable<CompletionSuggestionForTesting>> {
CheckTarget<List<String>> get completions {
return nest(
value.map((e) => e.suggestion.completion).toList(),
(selected) => 'completions ${valueStr(selected)}',
);
}
CheckTarget<Iterable<CompletionSuggestionForTesting>> get namedArguments {
var result = value
.where((suggestion) =>
suggestion.suggestion.kind ==
CompletionSuggestionKind.NAMED_ARGUMENT)
.toList();
return nest(
result,
(selected) => 'named arguments ${valueStr(selected)}',
);
}
}
extension ElementExtension on CheckTarget<Element> {
CheckTarget<ElementKind> get kind {
return nest(
value.kind,
(selected) => 'has kind ${valueStr(selected)}',
);
}
CheckTarget<String> get name {
return nest(
value.name,
(selected) => 'has name ${valueStr(selected)}',
);
}
}
extension ElementKindExtension on CheckTarget<ElementKind> {
void get isClass {
isEqualTo(ElementKind.CLASS);
}
void get isConstructor {
isEqualTo(ElementKind.CONSTRUCTOR);
}
void get isField {
isEqualTo(ElementKind.FIELD);
}
void get isGetter {
isEqualTo(ElementKind.GETTER);
}
void get isMethod {
isEqualTo(ElementKind.METHOD);
}
void get isParameter {
isEqualTo(ElementKind.PARAMETER);
}
void get isSetter {
isEqualTo(ElementKind.SETTER);
}
void get isTopLevelVariable {
isEqualTo(ElementKind.TOP_LEVEL_VARIABLE);
}
}