Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(37)

Unified Diff: pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart

Issue 2868383002: Recover from merge problem (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
diff --git a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
index 167efb932403aa14c5b416bcd1713a19cd3c0690..8326733fac1f314befa75f4faaa67a010bb06351 100644
--- a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
+++ b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
@@ -5,6 +5,7 @@
library services.src.completion.statement;
import 'dart:async';
+import 'dart:math';
import 'package:analysis_server/protocol/protocol_generated.dart';
import 'package:analysis_server/src/protocol_server.dart' hide Element;
@@ -18,6 +19,7 @@ import 'package:analyzer/error/error.dart' as engine;
import 'package:analyzer/src/dart/ast/utilities.dart';
import 'package:analyzer/src/dart/error/hint_codes.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
+import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/java_core.dart';
import 'package:analyzer/src/generated/source.dart';
@@ -201,6 +203,7 @@ class StatementCompletionProcessor {
}
}
+ _checkExpressions();
if (node is Statement) {
if (errors.isEmpty) {
if (_complete_ifStatement() ||
@@ -285,6 +288,102 @@ class StatementCompletionProcessor {
return text;
}
+ void _checkExpressions() {
+ // Note: This may queue edits that have to be accounted for later.
+ // See _lengthOfInsertions().
+ AstNode errorMatching(errorCode, {partialMatch = null}) {
+ var error = _findError(errorCode, partialMatch: partialMatch);
+ if (error == null) {
+ return null;
+ }
+ AstNode expr = _selectedNode();
+ return (expr.getAncestor((n) => n is StringInterpolation) == null)
+ ? expr
+ : null;
+ }
+
+ var expr = errorMatching(ScannerErrorCode.UNTERMINATED_STRING_LITERAL);
+ if (expr != null) {
+ String source = utils.getNodeText(expr);
+ String content = source;
+ int char = content.codeUnitAt(0);
+ if (char == 'r'.codeUnitAt(0)) {
+ content = source.substring(1);
+ char = content.codeUnitAt(0);
+ }
+ String delimiter;
+ int loc;
+ if (content.length >= 3 &&
+ char == content.codeUnitAt(1) &&
+ char == content.codeUnitAt(2)) {
+ // multi-line string
+ delimiter = content.substring(0, 3);
+ int newlineLoc = source.indexOf(eol, selectionOffset - expr.offset);
+ if (newlineLoc < 0) {
+ newlineLoc = source.length;
+ }
+ loc = newlineLoc + expr.offset;
+ } else {
+ // add first char of src
+ delimiter = content.substring(0, 1);
+ loc = expr.offset + source.length;
+ }
+ _removeError(ScannerErrorCode.UNTERMINATED_STRING_LITERAL);
+ _addInsertEdit(loc, delimiter);
+ }
+ expr = errorMatching(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "']'");
+ if (expr != null) {
+ expr = expr.getAncestor((n) => n is ListLiteral);
+ if (expr != null) {
+ ListLiteral lit = expr;
+ if (lit.rightBracket.isSynthetic) {
+ String src = utils.getNodeText(expr).trim();
+ int loc = expr.offset + src.length;
+ if (src.contains(eol)) {
+ String indent = utils.getNodePrefix(node);
+ _addInsertEdit(loc, ',' + eol + indent + ']');
+ } else {
+ _addInsertEdit(loc, ']');
+ }
+ _removeError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "']'");
+ var ms =
+ _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
+ if (ms != null) {
+ // Ensure the semicolon gets inserted in the correct location.
+ ms.offset = loc - 1;
+ }
+ }
+ }
+ }
+ // The following code is similar to the code for ']' but does not work well.
+ // A closing brace is recognized as belong to the map even if it is intended
+ // to close a block of code.
+ /*
+ expr = errorMatching(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "'}'");
+ if (expr != null) {
+ expr = expr.getAncestor((n) => n is MapLiteral);
+ if (expr != null) {
+ MapLiteral lit = expr;
+ String src = utils.getNodeText(expr).trim();
+ int loc = expr.offset + src.length;
+ if (lit.entries.last.separator.isSynthetic) {
+ _addInsertEdit(loc, ': ');
+ }
+ if (!src.endsWith('}')/*lit.rightBracket.isSynthetic*/) {
+ _addInsertEdit(loc, '}');
+ }
+ _removeError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "'}'");
+ var ms =
+ _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
+ if (ms != null) {
+ // Ensure the semicolon gets inserted in the correct location.
+ ms.offset = loc - 1;
+ }
+ }
+ }
+ */
+ }
+
bool _complete_classDeclaration() {
if (node is! ClassDeclaration) {
return false;
@@ -322,6 +421,7 @@ class StatementCompletionProcessor {
outer is WhileStatement)) {
return false;
}
+ int previousInsertions = _lengthOfInsertions();
int delta = 0;
if (errors.isNotEmpty) {
var error =
@@ -349,7 +449,7 @@ class StatementCompletionProcessor {
}
}
int offset = _appendNewlinePlusIndentAt(node.parent.end);
- exitPosition = new Position(file, offset + delta);
+ exitPosition = new Position(file, offset + delta + previousInsertions);
_setCompletion(DartStatementCompletion.COMPLETE_CONTROL_FLOW_BLOCK);
return true;
}
@@ -486,7 +586,7 @@ class StatementCompletionProcessor {
}
ForStatement forNode = node;
SourceBuilder sb;
- int delta = 0;
+ int replacementLength = 0;
if (forNode.leftParenthesis.isSynthetic) {
if (!forNode.rightParenthesis.isSynthetic) {
return false;
@@ -499,31 +599,40 @@ class StatementCompletionProcessor {
} else {
if (!forNode.rightSeparator.isSynthetic) {
// Fully-defined init, cond, updaters so nothing more needed here.
- // emptyParts
+ // emptyParts, noError
sb = new SourceBuilder(file, forNode.rightParenthesis.offset + 1);
} else if (!forNode.leftSeparator.isSynthetic) {
if (_isSyntheticExpression(forNode.condition)) {
- exitPosition = _newPosition(forNode.leftSeparator.offset + 1);
String text = utils
.getNodeText(forNode)
.substring(forNode.leftSeparator.offset - forNode.offset);
- if (text.startsWith(new RegExp(r';\s*\)'))) {
- // emptyCondition
- int end = text.indexOf(')');
+ Match match =
+ new RegExp(r';\s*(/\*.*\*/\s*)?\)[ \t]*').matchAsPrefix(text);
+ if (match != null) {
+ // emptyCondition, emptyInitializersEmptyCondition
+ replacementLength = match.end - match.start;
sb = new SourceBuilder(file, forNode.leftSeparator.offset);
- _addReplaceEdit(new SourceRange(sb.offset, end), '; ; ');
- delta = end - '; '.length;
+ sb.append('; ${match.group(1) == null ? '' : match.group(1)}; )');
+ String suffix = text.substring(match.end);
+ if (suffix.trim().isNotEmpty) {
+ sb.append(' ');
+ sb.append(suffix.trim());
+ replacementLength += suffix.length;
+ if (suffix.endsWith(eol)) {
+ // emptyCondition
+ replacementLength -= eol.length;
+ }
+ }
+ exitPosition = _newPosition(forNode.leftSeparator.offset + 2);
} else {
- // emptyInitializersEmptyCondition
- exitPosition = _newPosition(forNode.rightParenthesis.offset);
- sb = new SourceBuilder(file, forNode.rightParenthesis.offset);
+ return false; // Line comment in condition
}
} else {
// emptyUpdaters
- exitPosition = _newPosition(forNode.rightSeparator.offset);
- sb = new SourceBuilder(file, forNode.rightSeparator.offset);
- _addReplaceEdit(new SourceRange(sb.offset, 0), '; ');
- delta = -'; '.length;
+ sb = new SourceBuilder(file, forNode.rightParenthesis.offset);
+ replacementLength = 1;
+ sb.append('; )');
+ exitPosition = _newPosition(forNode.rightSeparator.offset + 2);
}
} else if (_isSyntheticExpression(forNode.initialization)) {
// emptyInitializers
@@ -537,9 +646,8 @@ class StatementCompletionProcessor {
// missingLeftSeparator
int end = text.indexOf(')');
sb = new SourceBuilder(file, start);
- _addReplaceEdit(new SourceRange(start, end), '; ');
- delta = end - '; '.length;
- exitPosition = new Position(file, start);
+ _addReplaceEdit(new SourceRange(start, end), '; ; ');
+ exitPosition = new Position(file, start - (end - '; '.length));
} else {
// Not possible; any comment following init is attached to init.
exitPosition = _newPosition(forNode.rightParenthesis.offset);
@@ -550,13 +658,16 @@ class StatementCompletionProcessor {
if (!_statementHasValidBody(forNode.forKeyword, forNode.body)) {
// keywordOnly, noError
sb.append(' ');
- _appendEmptyBraces(sb, exitPosition == null);
- }
- if (delta != 0 && exitPosition != null) {
- // missingLeftSeparator
- exitPosition = new Position(file, exitPosition.offset - delta);
+ _appendEmptyBraces(sb, true /*exitPosition == null*/);
+ } else if (forNode.body is Block) {
+ Block body = forNode.body;
+ if (body.rightBracket.end <= selectionOffset) {
+ // emptyInitializersAfterBody
+ errors = []; // Ignore errors; they are for previous statement.
+ return false; // If cursor is after closing brace just add newline.
+ }
}
- _insertBuilder(sb);
+ _insertBuilder(sb, replacementLength);
_setCompletion(DartStatementCompletion.COMPLETE_FOR_STMT);
return true;
}
@@ -565,7 +676,29 @@ class StatementCompletionProcessor {
if (node is! MethodDeclaration && node is! FunctionDeclaration) {
return false;
}
- SourceBuilder sb = new SourceBuilder(file, node.end - 1);
+ bool needsParen = false;
+ int computeExitPos(FormalParameterList parameters) {
+ if (needsParen = parameters.rightParenthesis.isSynthetic) {
+ var error = _findError(ParserErrorCode.MISSING_CLOSING_PARENTHESIS);
+ if (error != null) {
+ return error.offset - 1;
+ }
+ }
+ return node.end - 1;
+ }
+
+ int paramListEnd;
+ if (node is FunctionDeclaration) {
+ FunctionDeclaration func = node;
+ paramListEnd = computeExitPos(func.functionExpression.parameters);
+ } else {
+ MethodDeclaration meth = node;
+ paramListEnd = computeExitPos(meth.parameters);
+ }
+ SourceBuilder sb = new SourceBuilder(file, paramListEnd);
+ if (needsParen) {
+ sb.append(')');
+ }
sb.append(' ');
_appendEmptyBraces(sb, true);
_insertBuilder(sb);
@@ -621,9 +754,13 @@ class StatementCompletionProcessor {
if (sb == null) {
return false;
}
+ int overshoot = _lengthOfDeletions();
sb.append(' ');
_appendEmptyBraces(sb, exitPosition == null);
_insertBuilder(sb);
+ if (overshoot != 0) {
+ exitPosition = _newPosition(exitPosition.offset - overshoot);
+ }
_setCompletion(kind);
return true;
}
@@ -634,6 +771,20 @@ class StatementCompletionProcessor {
}
IfStatement ifNode = node;
if (ifNode.elseKeyword != null) {
+ if (selectionOffset >= ifNode.elseKeyword.end &&
+ ifNode.elseStatement is EmptyStatement) {
+ SourceBuilder sb = new SourceBuilder(file, selectionOffset);
+ String src = utils.getNodeText(ifNode);
+ if (!src
+ .substring(ifNode.elseKeyword.end - node.offset)
+ .startsWith(new RegExp(r'[ \t]'))) {
+ sb.append(' ');
+ }
+ _appendEmptyBraces(sb, true);
+ _insertBuilder(sb);
+ _setCompletion(DartStatementCompletion.COMPLETE_IF_STMT);
+ return true;
+ }
return false;
}
var stmt = new _KeywordConditionBlockStructure(
@@ -663,7 +814,16 @@ class StatementCompletionProcessor {
exitPosition = _newPosition(statement.leftParenthesis.offset + 1);
sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1);
} else {
- sb = new SourceBuilder(file, statement.rightParenthesis.offset + 1);
+ int afterParen = statement.rightParenthesis.offset + 1;
+ if (utils
+ .getNodeText(node)
+ .substring(afterParen - node.offset)
+ .startsWith(new RegExp(r'[ \t]'))) {
+ _addReplaceEdit(new SourceRange(afterParen, 1), '');
+ sb = new SourceBuilder(file, afterParen + 1);
+ } else {
+ sb = new SourceBuilder(file, afterParen);
+ }
}
}
return sb;
@@ -675,12 +835,17 @@ class StatementCompletionProcessor {
if (parenError == null) {
return false;
}
- AstNode argList = _selectedNode(at: parenError.offset - 1)
+ AstNode argList = _selectedNode(at: selectionOffset)
.getAncestor((n) => n is ArgumentList);
+ if (argList == null) {
+ argList = _selectedNode(at: parenError.offset)
+ .getAncestor((n) => n is ArgumentList);
+ }
if (argList?.getAncestor((n) => n == node) == null) {
return false;
}
- int loc = argList.end - 1;
+ int previousInsertions = _lengthOfInsertions();
+ int loc = min(selectionOffset, argList.end - 1);
int delta = 1;
var semicolonError =
_findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
@@ -695,7 +860,7 @@ class StatementCompletionProcessor {
String indent = utils.getLinePrefix(selectionOffset);
int exit = utils.getLineNext(selectionOffset);
_addInsertEdit(exit, indent + eol);
- exit += indent.length + eol.length;
+ exit += indent.length + eol.length + previousInsertions;
_setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, exit + delta);
return true;
@@ -709,7 +874,7 @@ class StatementCompletionProcessor {
String indent = utils.getLinePrefix(selectionOffset);
int loc = utils.getLineNext(selectionOffset);
_addInsertEdit(loc, indent + eol);
- offset = loc + indent.length + eol.length;
+ offset = loc + indent.length;
}
_setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, offset);
return true;
@@ -721,10 +886,11 @@ class StatementCompletionProcessor {
}
var error = _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
if (error != null) {
+ int previousInsertions = _lengthOfInsertions();
// TODO(messick) Fix this to find the correct place in all cases.
int insertOffset = error.offset + error.length;
_addInsertEdit(insertOffset, ';');
- int offset = _appendNewlinePlusIndent() + 1 /* ';' */;
+ int offset = _appendNewlinePlusIndent() + 1 /*';'*/ + previousInsertions;
_setCompletionAt(DartStatementCompletion.SIMPLE_SEMICOLON, offset);
return true;
}
@@ -808,7 +974,10 @@ class StatementCompletionProcessor {
} else if ((catchNode = _findInvalidElement(tryNode.catchClauses)) !=
null) {
if (catchNode.onKeyword != null) {
- if (catchNode.exceptionType.length == 0) {
+ if (catchNode.exceptionType.length == 0 ||
+ null !=
+ _findError(StaticWarningCode.NON_TYPE_IN_CATCH_CLAUSE,
+ partialMatch: "name 'catch")) {
String src = utils.getNodeText(catchNode);
if (src.startsWith(new RegExp(r'on[ \t]+'))) {
if (src.startsWith(new RegExp(r'on[ \t][ \t]+'))) {
@@ -969,10 +1138,52 @@ class StatementCompletionProcessor {
return expr is SimpleIdentifier && expr.isSynthetic;
}
+ int _lengthOfDeletions() {
+ if (change.edits.isEmpty) {
+ return 0;
+ }
+ int length = 0;
+ for (SourceFileEdit edit in change.edits) {
+ for (SourceEdit srcEdit in edit.edits) {
+ if (srcEdit.length > 0) {
+ length += srcEdit.length - srcEdit.replacement.length;
+ }
+ }
+ }
+ return length;
+ }
+
+ int _lengthOfInsertions() {
+ // Any _complete_*() that may follow changes made by _checkExpressions()
+ // must cache the result of this method and add that value to its
+ // exit position. That's assuming all edits are done in increasing position.
+ // There are currently no editing sequences that produce both insertions and
+ // deletions, but if there were this approach would have to be generalized.
+ if (change.edits.isEmpty) {
+ return 0;
+ }
+ int length = 0;
+ for (SourceFileEdit edit in change.edits) {
+ for (SourceEdit srcEdit in edit.edits) {
+ if (srcEdit.length == 0) {
+ length += srcEdit.replacement.length;
+ }
+ }
+ }
+ return length;
+ }
+
Position _newPosition(int offset) {
return new Position(file, offset);
}
+ void _removeError(errorCode, {partialMatch = null}) {
+ var error = _findError(errorCode, partialMatch: partialMatch);
+ if (error != null) {
+ errors.remove(error);
+ }
+ }
+
AstNode _selectedNode({int at: null}) =>
new NodeLocator(at == null ? selectionOffset : at).searchWithin(unit);
@@ -995,8 +1206,8 @@ class StatementCompletionProcessor {
String text = _baseNodeText(node);
text = text.substring(keyword.offset - node.offset);
int len = keyword.length;
- if (text.length == len ||
- !text.substring(len, len + 1).contains(new RegExp(r'\s'))) {
+ if (text.length == len || // onCatchComment
+ !text.substring(len, len + 1).contains(new RegExp(r'[ \t]'))) {
sb = new SourceBuilder(file, keyword.offset + len);
sb.append(' ');
} else {

Powered by Google App Engine
This is Rietveld 408576698