Version 2.18.0-219.0.dev
Merge commit '14796fafa867d6db4bf3bc8307c82e02fcb0d45f' into 'dev'
diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart
index 04b8ac1..5fa0b4a 100644
--- a/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart
+++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_available_refactorings.dart
@@ -61,10 +61,9 @@
if (element != null) {
// try CONVERT_METHOD_TO_GETTER
if (element is ExecutableElement) {
- Refactoring refactoring = ConvertMethodToGetterRefactoring(
- searchEngine, resolvedUnit.session, element);
- var status = await refactoring.checkInitialConditions();
- if (!status.hasFatalError) {
+ if (ConvertMethodToGetterRefactoring(
+ searchEngine, resolvedUnit.session, element)
+ .isAvailable()) {
kinds.add(RefactoringKind.CONVERT_METHOD_TO_GETTER);
}
}
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
index b34d459..d37962b 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_code_actions.dart
@@ -612,7 +612,10 @@
final node = NodeLocator(offset).searchWithin(unit.unit);
final element = server.getElementOfNode(node);
// Getter to Method
- if (element is PropertyAccessorElement) {
+ if (element is PropertyAccessorElement &&
+ ConvertGetterToMethodRefactoring(
+ server.searchEngine, unit.session, element)
+ .isAvailable()) {
refactorActions.add(createRefactor(
CodeActionKind.RefactorRewrite,
'Convert Getter to Method',
@@ -621,7 +624,9 @@
// Method to Getter
if (element is ExecutableElement &&
- element is! PropertyAccessorElement) {
+ ConvertMethodToGetterRefactoring(
+ server.searchEngine, unit.session, element)
+ .isAvailable()) {
refactorActions.add(createRefactor(
CodeActionKind.RefactorRewrite,
'Convert Method to Getter',
diff --git a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
index ccea475..07ab252 100644
--- a/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
+++ b/pkg/analysis_server/lib/src/lsp/handlers/handler_completion_resolve.dart
@@ -85,7 +85,9 @@
return cancelled();
}
- var newInsertText = item.insertText ?? item.label;
+ var newInsertText = item.textEdit
+ ?.map((edit) => edit.newText, (edit) => edit.newText) ??
+ item.label;
final builder = ChangeBuilder(session: session);
await builder.addDartFileEdit(file, (builder) {
final result = builder.importLibraryElement(libraryUri);
@@ -151,7 +153,6 @@
preselect: item.preselect,
sortText: item.sortText,
filterText: item.filterText,
- insertText: newInsertText,
insertTextFormat: item.insertTextFormat,
insertTextMode: item.insertTextMode,
textEdit: supportsInsertReplace && insertionRange != replacementRange
@@ -262,7 +263,6 @@
preselect: item.preselect,
sortText: item.sortText,
filterText: item.filterText,
- insertText: item.insertText,
insertTextFormat: item.insertTextFormat,
textEdit: item.textEdit,
additionalTextEdits: item.additionalTextEdits,
diff --git a/pkg/analysis_server/lib/src/lsp/mapping.dart b/pkg/analysis_server/lib/src/lsp/mapping.dart
index 518bf16..77a4353 100644
--- a/pkg/analysis_server/lib/src/lsp/mapping.dart
+++ b/pkg/analysis_server/lib/src/lsp/mapping.dart
@@ -360,9 +360,6 @@
filterText: completion != label
? completion
: null, // filterText uses label if not set
- insertText: insertText != label
- ? insertText
- : null, // insertText uses label if not set
insertTextFormat: insertTextFormat != lsp.InsertTextFormat.PlainText
? insertTextFormat
: null, // Defaults to PlainText if not supplied
@@ -1124,8 +1121,8 @@
LspClientCapabilities capabilities,
server.LineInfo lineInfo,
server.CompletionSuggestion suggestion, {
- Range? replacementRange,
- Range? insertionRange,
+ required Range replacementRange,
+ required Range insertionRange,
required bool includeCommitCharacters,
required bool completeFunctionCalls,
CompletionItemResolutionInfo? resolutionData,
@@ -1234,31 +1231,26 @@
filterText: filterText != label
? filterText
: null, // filterText uses label if not set
- insertText: insertText != label
- ? insertText
- : null, // insertText uses label if not set
insertTextFormat: insertTextFormat != lsp.InsertTextFormat.PlainText
? insertTextFormat
: null, // Defaults to PlainText if not supplied
insertTextMode: supportsAsIsInsertMode && isMultilineCompletion
? InsertTextMode.asIs
: null,
- textEdit: (insertionRange == null || replacementRange == null)
- ? null
- : supportsInsertReplace && insertionRange != replacementRange
- ? Either2<InsertReplaceEdit, TextEdit>.t1(
- InsertReplaceEdit(
- insert: insertionRange,
- replace: replacementRange,
- newText: insertText,
- ),
- )
- : Either2<InsertReplaceEdit, TextEdit>.t2(
- TextEdit(
- range: replacementRange,
- newText: insertText,
- ),
- ),
+ textEdit: supportsInsertReplace && insertionRange != replacementRange
+ ? Either2<InsertReplaceEdit, TextEdit>.t1(
+ InsertReplaceEdit(
+ insert: insertionRange,
+ replace: replacementRange,
+ newText: insertText,
+ ),
+ )
+ : Either2<InsertReplaceEdit, TextEdit>.t2(
+ TextEdit(
+ range: replacementRange,
+ newText: insertText,
+ ),
+ ),
);
}
diff --git a/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart b/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
index 5aaf8ad..e226e3e 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
@@ -70,7 +70,13 @@
return change;
}
- RefactoringStatus _checkInitialConditions() {
+ @override
+ bool isAvailable() {
+ return !_checkElement().hasFatalError;
+ }
+
+ /// Checks if [element] is valid to perform this refactor.
+ RefactoringStatus _checkElement() {
if (!element.isGetter || element.isSynthetic) {
return RefactoringStatus.fatal(
'Only explicit getters can be converted to methods.');
@@ -78,6 +84,10 @@
return RefactoringStatus();
}
+ RefactoringStatus _checkInitialConditions() {
+ return _checkElement();
+ }
+
Future<void> _updateElementDeclaration(
PropertyAccessorElement element) async {
// prepare "get" keyword
diff --git a/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart b/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart
index 95ce6f0..dd6724c 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart
@@ -39,6 +39,37 @@
@override
Future<RefactoringStatus> checkInitialConditions() async {
+ return _checkElement();
+ }
+
+ @override
+ Future<SourceChange> createChange() async {
+ change = SourceChange(refactoringName);
+ // FunctionElement
+ final element = this.element;
+ if (element is FunctionElement) {
+ await _updateElementDeclaration(element);
+ await _updateElementReferences(element);
+ }
+ // MethodElement
+ if (element is MethodElement) {
+ var elements = await getHierarchyMembers(searchEngine, element);
+ await Future.forEach(elements, (Element element) async {
+ await _updateElementDeclaration(element);
+ return _updateElementReferences(element);
+ });
+ }
+ // done
+ return change;
+ }
+
+ @override
+ bool isAvailable() {
+ return !_checkElement().hasFatalError;
+ }
+
+ /// Checks if [element] is valid to perform this refactor.
+ RefactoringStatus _checkElement() {
// check Element type
if (element is FunctionElement) {
if (element.enclosingElement is! CompilationUnitElement) {
@@ -63,27 +94,6 @@
return RefactoringStatus();
}
- @override
- Future<SourceChange> createChange() async {
- change = SourceChange(refactoringName);
- // FunctionElement
- final element = this.element;
- if (element is FunctionElement) {
- await _updateElementDeclaration(element);
- await _updateElementReferences(element);
- }
- // MethodElement
- if (element is MethodElement) {
- var elements = await getHierarchyMembers(searchEngine, element);
- await Future.forEach(elements, (Element element) async {
- await _updateElementDeclaration(element);
- return _updateElementReferences(element);
- });
- }
- // done
- return change;
- }
-
Future<void> _updateElementDeclaration(Element element) async {
// prepare parameters
FormalParameterList? parameters;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
index 08e544a..7d76a4a 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/refactoring.dart
@@ -39,6 +39,16 @@
AnalysisSession session, PropertyAccessorElement element) {
return ConvertGetterToMethodRefactoringImpl(searchEngine, session, element);
}
+
+ /// Return `true` if refactoring is available, possibly without checking all
+ /// initial conditions.
+ ///
+ /// This value may be used to control the visibility of the refactor in the UI
+ /// so that it doesn't show up in locations that are obviously not
+ /// appropriate. Initial conditions may perform additional checks (and provide
+ /// user-friendly messages) for locations where a user might reasonably expect
+ /// to see the refactor but it's not valid for a less obvious reason.
+ bool isAvailable();
}
/// [Refactoring] to convert normal [MethodDeclaration]s into getters.
@@ -49,6 +59,16 @@
AnalysisSession session, ExecutableElement element) {
return ConvertMethodToGetterRefactoringImpl(searchEngine, session, element);
}
+
+ /// Return `true` if refactoring is available, possibly without checking all
+ /// initial conditions.
+ ///
+ /// This value may be used to control the visibility of the refactor in the UI
+ /// so that it doesn't show up in locations that are obviously not
+ /// appropriate. Initial conditions may perform additional checks (and provide
+ /// user-friendly messages) for locations where a user might reasonably expect
+ /// to see the refactor but it's not valid for a less obvious reason.
+ bool isAvailable();
}
/// [Refactoring] to extract an expression into a local variable declaration.
@@ -100,6 +120,12 @@
/// Return `true` if refactoring is available, possibly without checking all
/// initial conditions.
+ ///
+ /// This value may be used to control the visibility of the refactor in the UI
+ /// so that it doesn't show up in locations that are obviously not
+ /// appropriate. Initial conditions may perform additional checks (and provide
+ /// user-friendly messages) for locations where a user might reasonably expect
+ /// to see the refactor but it's not valid for a less obvious reason.
bool isAvailable();
}
@@ -168,6 +194,12 @@
/// Return `true` if refactoring is available, possibly without checking all
/// initial conditions.
+ ///
+ /// This value may be used to control the visibility of the refactor in the UI
+ /// so that it doesn't show up in locations that are obviously not
+ /// appropriate. Initial conditions may perform additional checks (and provide
+ /// user-friendly messages) for locations where a user might reasonably expect
+ /// to see the refactor but it's not valid for a less obvious reason.
bool isAvailable();
}
@@ -195,6 +227,12 @@
/// Return `true` if refactoring is available, possibly without checking all
/// initial conditions.
+ ///
+ /// This value may be used to control the visibility of the refactor in the UI
+ /// so that it doesn't show up in locations that are obviously not
+ /// appropriate. Initial conditions may perform additional checks (and provide
+ /// user-friendly messages) for locations where a user might reasonably expect
+ /// to see the refactor but it's not valid for a less obvious reason.
bool isAvailable();
}
@@ -214,6 +252,12 @@
/// Return `true` if refactoring is available, possibly without checking all
/// initial conditions.
+ ///
+ /// This value may be used to control the visibility of the refactor in the UI
+ /// so that it doesn't show up in locations that are obviously not
+ /// appropriate. Initial conditions may perform additional checks (and provide
+ /// user-friendly messages) for locations where a user might reasonably expect
+ /// to see the refactor but it's not valid for a less obvious reason.
bool isAvailable();
}
@@ -246,6 +290,12 @@
/// Return `true` if refactoring is available, possibly without checking all
/// initial conditions.
+ ///
+ /// This value may be used to control the visibility of the refactor in the UI
+ /// so that it doesn't show up in locations that are obviously not
+ /// appropriate. Initial conditions may perform additional checks (and provide
+ /// user-friendly messages) for locations where a user might reasonably expect
+ /// to see the refactor but it's not valid for a less obvious reason.
bool isAvailable();
}
diff --git a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
index 029f542..9a6951c 100644
--- a/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
+++ b/pkg/analysis_server/test/lsp/code_actions_refactor_test.dart
@@ -57,12 +57,45 @@
await verifyCodeActionEdits(
codeAction, withoutMarkers(content), expectedContent);
}
+
+ Future<void> test_setter_notAvailable() async {
+ const content = '''
+set ^a(String value) {}
+
+''';
+ newFile(mainFilePath, withoutMarkers(content));
+ await initialize();
+
+ final codeActions = await getCodeActions(mainFileUri.toString(),
+ position: positionFromMarker(content));
+ final codeAction =
+ findCommand(codeActions, Commands.performRefactor, refactorTitle);
+
+ expect(codeAction, isNull);
+ }
}
@reflectiveTest
class ConvertMethodToGetterCodeActionsTest extends AbstractCodeActionsTest {
final refactorTitle = 'Convert Method to Getter';
+ Future<void> test_constructor_notAvailable() async {
+ const content = '''
+class A {
+ ^A();
+}
+''';
+ newFile(mainFilePath, withoutMarkers(content));
+ await initialize();
+
+ final codeActions = await getCodeActions(mainFileUri.toString(),
+ position: positionFromMarker(content));
+ final codeAction =
+ findCommand(codeActions, Commands.performRefactor, refactorTitle);
+
+ expect(codeAction, isNull);
+ }
+
Future<void> test_refactor() async {
const content = '''
int ^test() => 42;
diff --git a/pkg/analysis_server/test/lsp/completion_dart_test.dart b/pkg/analysis_server/test/lsp/completion_dart_test.dart
index f55c2bd..9010d16 100644
--- a/pkg/analysis_server/test/lsp/completion_dart_test.dart
+++ b/pkg/analysis_server/test/lsp/completion_dart_test.dart
@@ -32,7 +32,7 @@
with CompletionTestMixin {
Future<void> checkCompleteFunctionCallInsertText(
String content, String completion,
- {required String? insertText, InsertTextFormat? insertTextFormat}) async {
+ {required String? editText, InsertTextFormat? insertTextFormat}) async {
await provideConfig(
() => initialize(
textDocumentCapabilities: withCompletionItemSnippetSupport(
@@ -51,12 +51,13 @@
);
expect(item.insertTextFormat, equals(insertTextFormat));
- expect(item.insertText, equals(insertText));
+ // We always expect `insertText` to be `null` now, as we always use
+ // `textEdit`.
+ expect(item.insertText, isNull);
+ // And the expected text should be in the `textEdit`.
final textEdit = toTextEdit(item.textEdit!);
- // newText in the edit will always be set, so if insertText is null we need
- // fall back to item.label for the expected value.
- expect(textEdit.newText, equals(item.insertText ?? item.label));
+ expect(textEdit.newText, equals(editText));
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
@@ -188,7 +189,7 @@
''',
'Aaaaa(…)',
insertTextFormat: InsertTextFormat.Snippet,
- insertText: r'Aaaaa(${0:a})',
+ editText: r'Aaaaa(${0:a})',
);
Future<void> test_completeFunctionCalls_escapesDollarArgs() =>
@@ -201,7 +202,7 @@
'myFunction(…)',
insertTextFormat: InsertTextFormat.Snippet,
// The dollar should have been escaped.
- insertText: r'myFunction(${1:a\$a}, ${2:b})',
+ editText: r'myFunction(${1:a\$a}, ${2:b})',
);
Future<void> test_completeFunctionCalls_escapesDollarName() =>
@@ -214,7 +215,7 @@
r'myFunc$tion(…)',
insertTextFormat: InsertTextFormat.Snippet,
// The dollar should have been escaped.
- insertText: r'myFunc\$tion(${1:a}, ${2:b})',
+ editText: r'myFunc\$tion(${1:a}, ${2:b})',
);
Future<void> test_completeFunctionCalls_existingArgList_constructor() =>
@@ -228,7 +229,7 @@
}
''',
'Aaaaa(…)',
- insertText: 'Aaaaa',
+ editText: 'Aaaaa',
);
Future<void> test_completeFunctionCalls_existingArgList_expression() =>
@@ -239,7 +240,7 @@
}
''',
'myFunction(…)',
- insertText: 'myFunction',
+ editText: 'myFunction',
);
Future<void> test_completeFunctionCalls_existingArgList_member_noPrefix() =>
@@ -254,7 +255,7 @@
}
''',
'foo(…)',
- insertText: 'foo',
+ editText: 'foo',
);
Future<void> test_completeFunctionCalls_existingArgList_namedConstructor() =>
@@ -268,7 +269,7 @@
}
''',
'foo(…)',
- insertText: 'foo',
+ editText: 'foo',
);
Future<void> test_completeFunctionCalls_existingArgList_statement() =>
@@ -279,7 +280,7 @@
}
''',
'f(…)',
- insertText: 'f',
+ editText: 'f',
);
Future<void> test_completeFunctionCalls_existingArgList_suggestionSets() =>
@@ -290,7 +291,7 @@
}
''',
'print(…)',
- insertText: 'print',
+ editText: 'print',
);
Future<void> test_completeFunctionCalls_existingPartialArgList() =>
@@ -304,7 +305,7 @@
}
''',
'Aaaaa(…)',
- insertText: 'Aaaaa',
+ editText: 'Aaaaa',
);
Future<void> test_completeFunctionCalls_expression() =>
@@ -316,7 +317,7 @@
''',
'myFunction(…)',
insertTextFormat: InsertTextFormat.Snippet,
- insertText: r'myFunction(${1:a}, ${2:b})',
+ editText: r'myFunction(${1:a}, ${2:b})',
);
Future<void> test_completeFunctionCalls_flutterSetState() async {
@@ -360,9 +361,9 @@
// Ensure the snippet comes through in the expected format with the expected
// placeholders.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
- expect(item.insertText, equals('setState(() {\n \$0\n });'));
+ expect(item.insertText, isNull);
final textEdit = toTextEdit(item.textEdit!);
- expect(textEdit.newText, equals(item.insertText));
+ expect(textEdit.newText, 'setState(() {\n \$0\n });');
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
@@ -378,7 +379,7 @@
''',
'foo(…)',
insertTextFormat: InsertTextFormat.Snippet,
- insertText: r'foo(${0:a})',
+ editText: r'foo(${0:a})',
);
Future<void> test_completeFunctionCalls_noParameters() async {
@@ -393,10 +394,7 @@
await checkCompleteFunctionCallInsertText(
content,
'myFunction()',
- // With no params, we don't put a tab stop inside the parens. This results
- // in the insertText being the same as the label, which means it will be
- // set to null so that it falls back without needing to repeat the value.
- insertText: null,
+ editText: 'myFunction()',
insertTextFormat: InsertTextFormat.Snippet,
);
}
@@ -414,7 +412,7 @@
content,
'myFunction(…)',
// With optional params, there should still be parens/tab stop inside.
- insertText: r'myFunction($0)',
+ editText: r'myFunction($0)',
insertTextFormat: InsertTextFormat.Snippet,
);
}
@@ -443,9 +441,9 @@
// Ensure the snippet comes through in the expected format with the expected
// placeholders.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
- expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b}, c: ${3:c})'));
+ expect(item.insertText, isNull);
final textEdit = toTextEdit(item.textEdit!);
- expect(textEdit.newText, equals(item.insertText));
+ expect(textEdit.newText, r'myFunction(${1:a}, ${2:b}, c: ${3:c})');
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
@@ -479,19 +477,67 @@
// Ensure the snippet comes through in the expected format with the expected
// placeholders.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
- expect(item.insertText, equals(r'myFunction(${1:a}, ${2:b}, c: ${3:c})'));
+ expect(item.insertText, isNull);
expect(item.textEdit, isNotNull);
final originalTextEdit = item.textEdit;
// Ensure the item can be resolved and retains the correct textEdit (since
// textEdit may be recomputed during resolve).
final resolved = await resolveCompletion(item);
+ expect(resolved.insertText, isNull);
expect(resolved.textEdit, originalTextEdit);
final textEdit = toTextEdit(resolved.textEdit!);
- expect(textEdit.newText, equals(item.insertText));
+ expect(textEdit.newText, r'myFunction(${1:a}, ${2:b}, c: ${3:c})');
expect(textEdit.range, equals(rangeFromMarkers(content)));
}
+ Future<void>
+ test_completeFunctionCalls_resolve_producesCorrectEditWithoutInsertText() async {
+ // Ensure our `resolve` call does not rely on the presence of `insertText`
+ // to compute the correct edits. This is something we did incorrectly in the
+ // past and broke with
+ // https://github.com/dart-lang/sdk/commit/40e25ebad0bd008615b1c1d8021cb27839f00dcd
+ // because the way these are combined in the VS Code LSP client means we are
+ // not provided both `insertText` and `textEdit` back in the resolve call.
+ //
+ // Now, we never supply `insertText` and always use `textEdit`.
+ final content = '''
+final a = Stri^
+ ''';
+
+ /// Helper to verify a completion is as expected.
+ void expectCorrectCompletion(CompletionItem item) {
+ // Ensure this completion looks as we'd expect.
+ expect(item.label, 'String.fromCharCode(…)');
+ expect(item.insertText, isNull);
+ expect(
+ item.textEdit!.map((edit) => edit.newText, (edit) => edit.newText),
+ r'String.fromCharCode(${0:charCode})',
+ );
+ }
+
+ final initialAnalysis = waitForAnalysisComplete();
+ await provideConfig(
+ () => initialize(
+ textDocumentCapabilities: withCompletionItemSnippetSupport(
+ emptyTextDocumentClientCapabilities),
+ workspaceCapabilities:
+ withConfigurationSupport(emptyWorkspaceClientCapabilities),
+ ),
+ {'completeFunctionCalls': true},
+ );
+ await openFile(mainFileUri, withoutMarkers(content));
+ await initialAnalysis;
+ final res = await getCompletion(mainFileUri, positionFromMarker(content));
+
+ final completion =
+ res.singleWhere((c) => c.label == 'String.fromCharCode(…)');
+ expectCorrectCompletion(completion);
+
+ final resolved = await resolveCompletion(completion);
+ expectCorrectCompletion(resolved);
+ }
+
Future<void> test_completeFunctionCalls_show() async {
final content = '''
import 'dart:math' show mi^
@@ -512,9 +558,9 @@
// The insert text should be a simple string with no parens/args and
// no need for snippets.
expect(item.insertTextFormat, isNull);
- expect(item.insertText, equals(r'min'));
+ expect(item.insertText, isNull);
final textEdit = toTextEdit(item.textEdit!);
- expect(textEdit.newText, equals(item.insertText));
+ expect(textEdit.newText, r'min');
}
Future<void> test_completeFunctionCalls_statement() =>
@@ -526,7 +572,7 @@
''',
'f(…)',
insertTextFormat: InsertTextFormat.Snippet,
- insertText: r'f(${0:a})',
+ editText: r'f(${0:a})',
);
Future<void> test_completeFunctionCalls_suggestionSets() =>
@@ -538,7 +584,7 @@
''',
'print(…)',
insertTextFormat: InsertTextFormat.Snippet,
- insertText: r'print(${0:object})',
+ editText: r'print(${0:object})',
);
Future<void> test_completionKinds_default() async {
@@ -726,7 +772,9 @@
expect(item, isNotNull);
expect(item!.label, equals('name => …'));
expect(item.filterText, isNull); // Falls back to label
- expect(item.insertText, equals('''@override
+ expect(item.insertText, isNull);
+ final textEdit = toTextEdit(item.textEdit!);
+ expect(textEdit.newText, equals('''@override
// TODO: implement name
String get name => throw UnimplementedError();'''));
}
@@ -936,7 +984,9 @@
final item = res.singleWhere((c) => c.label.startsWith('setState'));
// Multiline completions should always set insertTextMode.asIs.
- expect(item.insertText, contains('\n'));
+ expect(item.insertText, isNull);
+ final textEdit = toTextEdit(item.textEdit!);
+ expect(textEdit.newText, contains('\n'));
expect(item.insertTextMode, equals(InsertTextMode.asIs));
}
@@ -956,7 +1006,9 @@
// Single line completions should never set insertTextMode.asIs to
// avoid bloating payload size where it wouldn't matter.
- expect(item.insertText, isNot(contains('\n')));
+ expect(item.insertText, isNull);
+ final textEdit = toTextEdit(item.textEdit!);
+ expect(textEdit.newText, isNot(contains('\n')));
expect(item.insertTextMode, isNull);
}
@@ -1386,7 +1438,9 @@
final item = res.singleWhere((c) => c.label == 'one: ');
expect(item.insertTextFormat,
anyOf(equals(InsertTextFormat.PlainText), isNull));
- expect(item.insertText, anyOf(equals('test'), isNull));
+ expect(item.insertText, isNull);
+ final textEdit = toTextEdit(item.textEdit!);
+ expect(textEdit.newText, item.label);
final updated = applyTextEdits(
withoutMarkers(content),
[toTextEdit(item.textEdit!)],
@@ -1444,7 +1498,7 @@
// Ensure the snippet comes through in the expected format with the expected
// placeholder.
expect(item.insertTextFormat, equals(InsertTextFormat.Snippet));
- expect(item.insertText, equals(r'one: $0,'));
+ expect(item.insertText, isNull);
final textEdit = toTextEdit(item.textEdit!);
expect(textEdit.newText, equals(r'one: $0,'));
expect(
@@ -1483,7 +1537,7 @@
expect(completion.detail, '(int? a, [int b = 1]) → String?');
}
- Future<void> test_parensNotInFilterTextInsertText() async {
+ Future<void> test_parensNotInFilterTextOrEditText() async {
final content = '''
class MyClass {}
@@ -1497,8 +1551,10 @@
final res = await getCompletion(mainFileUri, positionFromMarker(content));
expect(res.any((c) => c.label == 'MyClass()'), isTrue);
final item = res.singleWhere((c) => c.label == 'MyClass()');
- expect(item.filterText, equals('MyClass'));
- expect(item.insertText, equals('MyClass'));
+ expect(item.filterText, 'MyClass');
+ expect(item.insertText, isNull);
+ final textEdit = toTextEdit(item.textEdit!);
+ expect(textEdit.newText, 'MyClass');
}
Future<void> test_plainText() async {
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index 8b6c799..2b021a0 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -193,7 +193,7 @@
}
CollectGarbage(thread, GCType::kMarkSweep, GCReason::kExternal);
} else {
- CheckConcurrentMarking(thread, GCReason::kExternal);
+ CheckConcurrentMarking(thread, GCReason::kExternal, 0);
}
}
@@ -459,7 +459,7 @@
CollectOldSpaceGarbage(thread, GCType::kMarkSweep,
GCReason::kPromotion);
} else {
- CheckConcurrentMarking(thread, GCReason::kPromotion);
+ CheckConcurrentMarking(thread, GCReason::kPromotion, 0);
}
}
}
@@ -561,7 +561,9 @@
WaitForSweeperTasks(thread);
}
-void Heap::CheckConcurrentMarking(Thread* thread, GCReason reason) {
+void Heap::CheckConcurrentMarking(Thread* thread,
+ GCReason reason,
+ intptr_t size) {
PageSpace::Phase phase;
{
MonitorLocker ml(old_space_.tasks_lock());
@@ -570,11 +572,9 @@
switch (phase) {
case PageSpace::kMarking:
- // TODO(rmacnak): Make the allocator of a large page mark size equals to
- // the large page?
- COMPILE_ASSERT(kOldPageSize == 512 * KB);
- COMPILE_ASSERT(kNewPageSize == 512 * KB);
- old_space_.IncrementalMarkWithSizeBudget(512 * KB);
+ if (size != 0) {
+ old_space_.IncrementalMarkWithSizeBudget(size);
+ }
return;
case PageSpace::kSweepingLarge:
case PageSpace::kSweepingRegular:
diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h
index 6a0dc2a..f4fb748 100644
--- a/runtime/vm/heap/heap.h
+++ b/runtime/vm/heap/heap.h
@@ -125,7 +125,7 @@
void CollectAllGarbage(GCReason reason = GCReason::kFull,
bool compact = false);
- void CheckConcurrentMarking(Thread* thread, GCReason reason);
+ void CheckConcurrentMarking(Thread* thread, GCReason reason, intptr_t size);
void StartConcurrentMarking(Thread* thread, GCReason reason);
void WaitForMarkerTasks(Thread* thread);
void WaitForSweeperTasks(Thread* thread);
diff --git a/runtime/vm/heap/pages.cc b/runtime/vm/heap/pages.cc
index eb2bbd8..1d66e97 100644
--- a/runtime/vm/heap/pages.cc
+++ b/runtime/vm/heap/pages.cc
@@ -473,7 +473,8 @@
if (growth_policy != kForceGrowth) {
ASSERT(GrowthControlState());
if (heap_ != nullptr) { // Some unit tests.
- heap_->CheckConcurrentMarking(Thread::Current(), GCReason::kOldSpace);
+ heap_->CheckConcurrentMarking(Thread::Current(), GCReason::kOldSpace,
+ kOldPageSize);
}
}
@@ -514,7 +515,8 @@
if (growth_policy != kForceGrowth) {
ASSERT(GrowthControlState());
if (heap_ != nullptr) { // Some unit tests.
- heap_->CheckConcurrentMarking(Thread::Current(), GCReason::kOldSpace);
+ heap_->CheckConcurrentMarking(Thread::Current(), GCReason::kOldSpace,
+ size);
}
}
diff --git a/runtime/vm/heap/safepoint.cc b/runtime/vm/heap/safepoint.cc
index 9933bed..d75d06a 100644
--- a/runtime/vm/heap/safepoint.cc
+++ b/runtime/vm/heap/safepoint.cc
@@ -65,7 +65,7 @@
if (heap->old_space()->ReachedHardThreshold()) {
heap->CollectGarbage(T, GCType::kMarkSweep, GCReason::kOldSpace);
} else {
- heap->CheckConcurrentMarking(T, GCReason::kOldSpace);
+ heap->CheckConcurrentMarking(T, GCReason::kOldSpace, 0);
}
}
}
diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc
index 17155e2..5bce434 100644
--- a/runtime/vm/heap/scavenger.cc
+++ b/runtime/vm/heap/scavenger.cc
@@ -1680,7 +1680,7 @@
if (can_safepoint) {
ASSERT(thread->no_safepoint_scope_depth() == 0);
- heap_->CheckConcurrentMarking(thread, GCReason::kNewSpace);
+ heap_->CheckConcurrentMarking(thread, GCReason::kNewSpace, kNewPageSize);
}
MutexLocker ml(&space_lock_);
diff --git a/tools/VERSION b/tools/VERSION
index 927f53e..5848a35 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 18
PATCH 0
-PRERELEASE 218
+PRERELEASE 219
PRERELEASE_PATCH 0
\ No newline at end of file