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

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

Issue 2834913002: Add special completion for control-flow block exits (Closed)
Patch Set: Created 3 years, 8 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 unified diff | Download patch
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library services.src.completion.statement; 5 library services.src.completion.statement;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'package:analysis_server/plugin/protocol/protocol.dart'; 9 import 'package:analysis_server/plugin/protocol/protocol.dart';
10 import 'package:analysis_server/src/protocol_server.dart' hide Element; 10 import 'package:analysis_server/src/protocol_server.dart' hide Element;
11 import 'package:analysis_server/src/services/correction/source_buffer.dart'; 11 import 'package:analysis_server/src/services/correction/source_buffer.dart';
12 import 'package:analysis_server/src/services/correction/source_range.dart'; 12 import 'package:analysis_server/src/services/correction/source_range.dart';
13 import 'package:analysis_server/src/services/correction/util.dart'; 13 import 'package:analysis_server/src/services/correction/util.dart';
14 import 'package:analyzer/dart/ast/ast.dart'; 14 import 'package:analyzer/dart/ast/ast.dart';
15 import 'package:analyzer/dart/ast/token.dart'; 15 import 'package:analyzer/dart/ast/token.dart';
16 import 'package:analyzer/dart/ast/visitor.dart';
17 import 'package:analyzer/dart/element/element.dart'; 16 import 'package:analyzer/dart/element/element.dart';
18 import 'package:analyzer/error/error.dart'; 17 import 'package:analyzer/error/error.dart';
19 import 'package:analyzer/error/error.dart' as engine; 18 import 'package:analyzer/error/error.dart' as engine;
20 import 'package:analyzer/src/dart/ast/utilities.dart'; 19 import 'package:analyzer/src/dart/ast/utilities.dart';
21 import 'package:analyzer/src/dart/error/hint_codes.dart'; 20 import 'package:analyzer/src/dart/error/hint_codes.dart';
22 import 'package:analyzer/src/dart/error/syntactic_errors.dart'; 21 import 'package:analyzer/src/dart/error/syntactic_errors.dart';
23 import 'package:analyzer/src/generated/engine.dart'; 22 import 'package:analyzer/src/generated/engine.dart';
24 import 'package:analyzer/src/generated/java_core.dart'; 23 import 'package:analyzer/src/generated/java_core.dart';
25 import 'package:analyzer/src/generated/source.dart'; 24 import 'package:analyzer/src/generated/source.dart';
26 25
27 /** 26 /**
28 * An enumeration of possible statement completion kinds. 27 * An enumeration of possible statement completion kinds.
29 */ 28 */
30 class DartStatementCompletion { 29 class DartStatementCompletion {
31 static const NO_COMPLETION = 30 static const NO_COMPLETION =
32 const StatementCompletionKind('No_COMPLETION', 'No completion available'); 31 const StatementCompletionKind('No_COMPLETION', 'No completion available');
33 static const SIMPLE_ENTER = const StatementCompletionKind( 32 static const SIMPLE_ENTER = const StatementCompletionKind(
34 'SIMPLE_ENTER', "Insert a newline at the end of the current line"); 33 'SIMPLE_ENTER', "Insert a newline at the end of the current line");
35 static const SIMPLE_SEMICOLON = const StatementCompletionKind( 34 static const SIMPLE_SEMICOLON = const StatementCompletionKind(
36 'SIMPLE_SEMICOLON', "Add a semicolon and newline"); 35 'SIMPLE_SEMICOLON', "Add a semicolon and newline");
36 static const COMPLETE_CONTROL_FLOW_BLOCK = const StatementCompletionKind(
37 'COMPLETE_CONTROL_FLOW_BLOCK', "Complete control flow block");
37 static const COMPLETE_DO_STMT = const StatementCompletionKind( 38 static const COMPLETE_DO_STMT = const StatementCompletionKind(
38 'COMPLETE_DO_STMT', "Complete do-statement"); 39 'COMPLETE_DO_STMT', "Complete do-statement");
39 static const COMPLETE_IF_STMT = const StatementCompletionKind( 40 static const COMPLETE_IF_STMT = const StatementCompletionKind(
40 'COMPLETE_IF_STMT', "Complete if-statement"); 41 'COMPLETE_IF_STMT', "Complete if-statement");
41 static const COMPLETE_FOR_STMT = const StatementCompletionKind( 42 static const COMPLETE_FOR_STMT = const StatementCompletionKind(
42 'COMPLETE_FOR_STMT', "Complete for-statement"); 43 'COMPLETE_FOR_STMT', "Complete for-statement");
43 static const COMPLETE_FOR_EACH_STMT = const StatementCompletionKind( 44 static const COMPLETE_FOR_EACH_STMT = const StatementCompletionKind(
44 'COMPLETE_FOR_EACH_STMT', "Complete for-each-statement"); 45 'COMPLETE_FOR_EACH_STMT', "Complete for-each-statement");
45 static const COMPLETE_SWITCH_STMT = const StatementCompletionKind( 46 static const COMPLETE_SWITCH_STMT = const StatementCompletionKind(
46 'COMPLETE_SWITCH_STMT', "Complete switch-statement"); 47 'COMPLETE_SWITCH_STMT', "Complete switch-statement");
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
179 for (engine.AnalysisError error in statementContext.errors) { 180 for (engine.AnalysisError error in statementContext.errors) {
180 if (error.offset >= node.offset && 181 if (error.offset >= node.offset &&
181 error.offset <= node.offset + node.length) { 182 error.offset <= node.offset + node.length) {
182 if (error.errorCode is! HintCode) { 183 if (error.errorCode is! HintCode) {
183 errors.add(error); 184 errors.add(error);
184 } 185 }
185 } 186 }
186 } 187 }
187 188
188 if (errors.isEmpty) { 189 if (errors.isEmpty) {
189 if (_complete_simpleEnter()) { 190 if (_complete_controlFlowBlock() || _complete_simpleEnter()) {
190 return completion; 191 return completion;
191 } 192 }
192 } else { 193 } else {
193 if (_complete_ifStatement() || 194 if (_complete_ifStatement() ||
194 _complete_doStatement() || 195 _complete_doStatement() ||
195 _complete_forStatement() || 196 _complete_forStatement() ||
196 _complete_forEachStatement() || 197 _complete_forEachStatement() ||
197 _complete_switchStatement() || 198 _complete_switchStatement() ||
198 _complete_tryStatement() || 199 _complete_tryStatement() ||
199 _complete_whileStatement() || 200 _complete_whileStatement() ||
201 _complete_controlFlowBlock() ||
200 _complete_simpleSemicolon() || 202 _complete_simpleSemicolon() ||
201 _complete_controlFlowBlock() ||
202 _complete_simpleEnter()) { 203 _complete_simpleEnter()) {
203 return completion; 204 return completion;
204 } 205 }
205 } 206 }
206 return NO_COMPLETION; 207 return NO_COMPLETION;
207 } 208 }
208 209
209 void _addInsertEdit(int offset, String text) { 210 void _addInsertEdit(int offset, String text) {
210 SourceEdit edit = new SourceEdit(offset, 0, text); 211 SourceEdit edit = new SourceEdit(offset, 0, text);
211 doSourceChange_addElementEdit(change, unitElement, edit); 212 doSourceChange_addElementEdit(change, unitElement, edit);
(...skipping 12 matching lines...) Expand all
224 sb.append(utils.getIndent(1)); 225 sb.append(utils.getIndent(1));
225 if (needsExitMark && sb.exitOffset == null) { 226 if (needsExitMark && sb.exitOffset == null) {
226 sb.setExitOffset(); 227 sb.setExitOffset();
227 } 228 }
228 sb.append(eol); 229 sb.append(eol);
229 sb.append(indent); 230 sb.append(indent);
230 sb.append('}'); 231 sb.append('}');
231 } 232 }
232 233
233 int _appendNewlinePlusIndent() { 234 int _appendNewlinePlusIndent() {
235 return _appendNewlinePlusIndentAt(selectionOffset);
236 }
237
238 int _appendNewlinePlusIndentAt(int offset) {
Brian Wilkerson 2017/04/21 21:45:45 I don't feel strongly about it, but in case you di
234 // Append a newline plus proper indent and another newline. 239 // Append a newline plus proper indent and another newline.
235 // Return the position before the second newline. 240 // Return the position before the second newline.
236 String indent = utils.getLinePrefix(selectionOffset); 241 String indent = utils.getLinePrefix(offset);
237 int loc = utils.getLineNext(selectionOffset); 242 int loc = utils.getLineNext(offset);
238 _addInsertEdit(loc, indent + eol); 243 _addInsertEdit(loc, indent + eol);
239 return loc + indent.length; 244 return loc + indent.length;
240 } 245 }
241 246
242 String _baseNodeText(AstNode astNode) { 247 String _baseNodeText(AstNode astNode) {
243 String text = utils.getNodeText(astNode); 248 String text = utils.getNodeText(astNode);
244 if (text.endsWith(eol)) { 249 if (text.endsWith(eol)) {
245 text = text.substring(0, text.length - eol.length); 250 text = text.substring(0, text.length - eol.length);
246 } 251 }
247 return text; 252 return text;
248 } 253 }
249 254
250 bool _complete_controlFlowBlock() { 255 bool _complete_controlFlowBlock() {
251 //TODO(messick) Implement _complete_controlFlowBlock 256 Expression expr = (node is ExpressionStatement)
252 // Use statement completion to move the cursor to a new line outside the 257 ? (node as ExpressionStatement).expression
253 // current block. The statement has no errors in this case. Used to jump 258 : (node is ReturnStatement
254 // out of do/for/if/while blocks. 259 ? (node as ReturnStatement).expression
255 return false; 260 : null);
261 if (!(node is ReturnStatement || expr is ThrowExpression)) {
262 return false;
263 }
264 if (node.parent is! Block) {
265 return false;
266 }
267 AstNode outer = node.parent.parent;
268 if (!(outer is DoStatement ||
269 outer is ForStatement ||
270 outer is ForEachStatement ||
271 outer is IfStatement ||
272 outer is WhileStatement)) {
273 return false;
274 }
275 int delta = 0;
276 if (errors.isNotEmpty) {
277 var error =
278 _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
279 if (error != null) {
280 int insertOffset;
281 if (expr == null || expr.isSynthetic) {
282 if (node is ReturnStatement) {
283 insertOffset = (node as ReturnStatement).returnKeyword.end;
284 } else if (node is ExpressionStatement) {
285 insertOffset =
286 ((node as ExpressionStatement).expression as ThrowExpression)
287 .throwKeyword
288 .end;
289 } else {
290 insertOffset = node.end; // Not reached.
291 }
292 } else {
293 insertOffset = expr.end;
294 }
295 //TODO(messick) Uncomment the following line when error location is fixe d.
296 //insertOffset = error.offset + error.length;
297 _addInsertEdit(insertOffset, ';');
298 delta = 1;
299 }
300 }
301 int offset = _appendNewlinePlusIndentAt(node.parent.end);
302 exitPosition = new Position(file, offset + delta);
303 _setCompletion(DartStatementCompletion.COMPLETE_CONTROL_FLOW_BLOCK);
304 return true;
256 } 305 }
257 306
258 bool _complete_doStatement() { 307 bool _complete_doStatement() {
259 if (node is! DoStatement) { 308 if (node is! DoStatement) {
260 return false; 309 return false;
261 } 310 }
262 DoStatement statement = node; 311 DoStatement statement = node;
263 SourceBuilder sb = _sourceBuilderAfterKeyword(statement.doKeyword); 312 SourceBuilder sb = _sourceBuilderAfterKeyword(statement.doKeyword);
264 bool hasWhileKeyword = statement.whileKeyword.lexeme == "while"; 313 bool hasWhileKeyword = statement.whileKeyword.lexeme == "while";
265 int exitDelta = 0; 314 int exitDelta = 0;
(...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after
533 _setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, offset); 582 _setCompletionAt(DartStatementCompletion.SIMPLE_ENTER, offset);
534 return true; 583 return true;
535 } 584 }
536 585
537 bool _complete_simpleSemicolon() { 586 bool _complete_simpleSemicolon() {
538 if (errors.length != 1) { 587 if (errors.length != 1) {
539 return false; 588 return false;
540 } 589 }
541 var error = _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'"); 590 var error = _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
542 if (error != null) { 591 if (error != null) {
592 // TODO(messick) Fix this to find the correct place in all cases.
543 int insertOffset = error.offset + error.length; 593 int insertOffset = error.offset + error.length;
544 _addInsertEdit(insertOffset, ';'); 594 _addInsertEdit(insertOffset, ';');
545 int offset = _appendNewlinePlusIndent() + 1 /* ';' */; 595 int offset = _appendNewlinePlusIndent() + 1 /* ';' */;
546 _setCompletionAt(DartStatementCompletion.SIMPLE_SEMICOLON, offset); 596 _setCompletionAt(DartStatementCompletion.SIMPLE_SEMICOLON, offset);
547 return true; 597 return true;
548 } 598 }
549 return false; 599 return false;
550 } 600 }
551 601
552 bool _complete_switchStatement() { 602 bool _complete_switchStatement() {
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after
806 final Token keyword; 856 final Token keyword;
807 final Token leftParenthesis, rightParenthesis; 857 final Token leftParenthesis, rightParenthesis;
808 final Expression condition; 858 final Expression condition;
809 final Statement block; 859 final Statement block;
810 860
811 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis, 861 _KeywordConditionBlockStructure(this.keyword, this.leftParenthesis,
812 this.condition, this.rightParenthesis, this.block); 862 this.condition, this.rightParenthesis, this.block);
813 863
814 int get offset => keyword.offset; 864 int get offset => keyword.offset;
815 } 865 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698