Skip to content

Commit

Permalink
Complete Flutter's setState() as a whole statement, with a closure, a…
Browse files Browse the repository at this point in the history
…nd the caret inside the closure.

[email protected], [email protected]

Bug: flutter/flutter-intellij#1916
Change-Id: I90b8c11c5435e1981fb334a4762bbe2131f3064f
Reviewed-on: https://dart-review.googlesource.com/48526
Reviewed-by: Brian Wilkerson <[email protected]>
Commit-Queue: Konstantin Shcheglov <[email protected]>
  • Loading branch information
scheglov authored and [email protected] committed Mar 28, 2018
1 parent b82e5b4 commit 59de971
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'dart:async';

import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
import 'package:analysis_server/src/services/completion/dart/suggestion_builder.dart';
import 'package:analysis_server/src/utilities/flutter.dart' as flutter;
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/standard_resolution_map.dart';
import 'package:analyzer/dart/element/element.dart';
Expand Down Expand Up @@ -81,16 +82,17 @@ class InheritedReferenceContributor extends DartCompletionContributor
skipChildClass: skipChildClass);
}

_addSuggestionsForType(InterfaceType type, OpType optype,
_addSuggestionsForType(InterfaceType type, DartCompletionRequest request,
{bool isFunctionalArgument: false}) {
OpType opType = request.opType;
if (!isFunctionalArgument) {
for (PropertyAccessorElement elem in type.accessors) {
if (elem.isGetter) {
if (optype.includeReturnValueSuggestions) {
if (opType.includeReturnValueSuggestions) {
addSuggestion(elem);
}
} else {
if (optype.includeVoidReturnSuggestions) {
if (opType.includeVoidReturnSuggestions) {
addSuggestion(elem);
}
}
Expand All @@ -100,12 +102,13 @@ class InheritedReferenceContributor extends DartCompletionContributor
if (elem.returnType == null) {
addSuggestion(elem);
} else if (!elem.returnType.isVoid) {
if (optype.includeReturnValueSuggestions) {
if (opType.includeReturnValueSuggestions) {
addSuggestion(elem);
}
} else {
if (optype.includeVoidReturnSuggestions) {
addSuggestion(elem);
if (opType.includeVoidReturnSuggestions) {
CompletionSuggestion suggestion = addSuggestion(elem);
_updateFlutterSuggestions(request, elem, suggestion);
}
}
}
Expand All @@ -118,17 +121,58 @@ class InheritedReferenceContributor extends DartCompletionContributor
kind = isFunctionalArgument
? CompletionSuggestionKind.IDENTIFIER
: CompletionSuggestionKind.INVOCATION;
OpType optype = request.opType;

if (!skipChildClass) {
_addSuggestionsForType(classElement.type, optype,
_addSuggestionsForType(classElement.type, request,
isFunctionalArgument: isFunctionalArgument);
}

for (InterfaceType type in classElement.allSupertypes) {
_addSuggestionsForType(type, optype,
_addSuggestionsForType(type, request,
isFunctionalArgument: isFunctionalArgument);
}
return suggestions;
}

void _updateFlutterSuggestions(DartCompletionRequest request, Element element,
CompletionSuggestion suggestion) {
if (suggestion == null) {
return;
}
if (element is MethodElement &&
element.name == 'setState' &&
flutter.isExactState(element.enclosingElement)) {
// Find the line indentation.
String content = request.result.content;
int lineStartOffset = request.offset;
int notWhitespaceOffset = request.offset;
for (; lineStartOffset > 0; lineStartOffset--) {
var char = content.substring(lineStartOffset - 1, lineStartOffset);
if (char == '\n') {
break;
}
if (char != ' ' && char != '\t') {
notWhitespaceOffset = lineStartOffset - 1;
}
}
String indent = content.substring(lineStartOffset, notWhitespaceOffset);

// Let the user know that we are going to insert a complete statement.
suggestion.displayText = 'setState(() {});';

// Build the completion and the selection offset.
var buffer = new StringBuffer();
buffer.writeln('setState(() {');
buffer.write('$indent ');
suggestion.selectionOffset = buffer.length;
buffer.writeln();
buffer.write('$indent});');
suggestion.completion = buffer.toString();

// There are no arguments to fill.
suggestion.parameterNames = null;
suggestion.parameterTypes = null;
suggestion.requiredParameterCount = null;
suggestion.hasNamedParameters = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,13 @@ abstract class ElementSuggestionBuilder {
/**
* Add a suggestion based upon the given element.
*/
void addSuggestion(Element element,
CompletionSuggestion addSuggestion(Element element,
{String prefix,
int relevance: DART_RELEVANCE_DEFAULT,
String elementCompletion}) {
if (element.isPrivate) {
if (element.library != containingLibrary) {
return;
return null;
}
}
String completion = elementCompletion ?? element.displayName;
Expand All @@ -162,7 +162,7 @@ abstract class ElementSuggestionBuilder {
}
}
if (completion == null || completion.length <= 0) {
return;
return null;
}
CompletionSuggestion suggestion = createSuggestion(element,
completion: completion, kind: kind, relevance: relevance);
Expand Down Expand Up @@ -195,7 +195,7 @@ abstract class ElementSuggestionBuilder {
typeParameters: getter.element.typeParameters,
parameters: null,
returnType: getter.returnType);
return;
return existingSuggestion;
}

// Cache lone getter/setter so that it can be paired
Expand All @@ -206,6 +206,7 @@ abstract class ElementSuggestionBuilder {
suggestions.add(suggestion);
}
}
return suggestion;
}
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/analysis_server/lib/src/utilities/flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ bool isExactlyStatelessWidgetType(DartType type) {
_isExactWidget(type.element, _STATELESS_WIDGET_NAME, _WIDGET_URI);
}

/// Return `true` if the given [element] is the Flutter class `State`.
bool isExactState(ClassElement element) {
return _isExactWidget(element, _STATE_NAME, _WIDGET_URI);
}

/**
* Return `true` if the given [type] is the Flutter class `Center`.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) 2014, 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 'dart:async';

import 'package:analysis_server/src/protocol_server.dart';
import 'package:analysis_server/src/provisional/completion/dart/completion_dart.dart';
Expand Down Expand Up @@ -125,6 +126,42 @@ class A extends E implements I with M {a() {^}}''');
assertSuggestMethod('m2', 'M', 'int');
}

test_flutter_setState_hasPrefix() async {
var spaces_4 = ' ' * 4;
var spaces_6 = ' ' * 6;
await _check_flutter_setState(
' setSt',
'''
setState(() {
$spaces_6
$spaces_4});''',
20);
}

test_flutter_setState_longPrefix() async {
var spaces_6 = ' ' * 6;
var spaces_8 = ' ' * 8;
await _check_flutter_setState(
' setSt',
'''
setState(() {
$spaces_8
$spaces_6});''',
22);
}

test_flutter_setState_noPrefix() async {
var spaces_4 = ' ' * 4;
var spaces_6 = ' ' * 6;
await _check_flutter_setState(
' ',
'''
setState(() {
$spaces_6
$spaces_4});''',
20);
}

test_inherited() async {
resolveSource('/testB.dart', '''
lib libB;
Expand Down Expand Up @@ -604,4 +641,37 @@ class B extends A1 with A2 {
assertNotSuggested('x2');
assertNotSuggested('y2');
}

Future<void> _check_flutter_setState(
String line, String completion, int selectionOffset) async {
addFlutterPackage();
addTestSource('''
import 'package:flutter/widgets.dart';
class TestWidget extends StatefulWidget {
@override
TestWidgetState createState() {
return new TestWidgetState();
}
}
class TestWidgetState extends State<TestWidget> {
@override
Widget build(BuildContext context) {
$line^
}
}
''');
await computeSuggestions();
CompletionSuggestion cs =
assertSuggest(completion, selectionOffset: selectionOffset);
expect(cs.selectionLength, 0);

// It is an invocation, but we don't need any additional info for it.
// So, all parameter information is absent.
expect(cs.parameterNames, isNull);
expect(cs.parameterTypes, isNull);
expect(cs.requiredParameterCount, isNull);
expect(cs.hasNamedParameters, isNull);
}
}

0 comments on commit 59de971

Please sign in to comment.