| OLD | NEW |
| 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 | 6 |
| 7 import 'package:analysis_server/src/protocol_server.dart' hide Element; | 7 import 'package:analysis_server/src/protocol_server.dart' hide Element; |
| 8 import 'package:analysis_server/src/services/correction/util.dart'; | 8 import 'package:analysis_server/src/services/correction/util.dart'; |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/element/element.dart'; | 10 import 'package:analyzer/dart/element/element.dart'; |
| (...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 273 /** | 273 /** |
| 274 * The computer for Dart postfix completions. | 274 * The computer for Dart postfix completions. |
| 275 */ | 275 */ |
| 276 class PostfixCompletionProcessor { | 276 class PostfixCompletionProcessor { |
| 277 static final NO_COMPLETION = new PostfixCompletion( | 277 static final NO_COMPLETION = new PostfixCompletion( |
| 278 DartPostfixCompletion.NO_TEMPLATE, new SourceChange("", edits: [])); | 278 DartPostfixCompletion.NO_TEMPLATE, new SourceChange("", edits: [])); |
| 279 | 279 |
| 280 final PostfixCompletionContext completionContext; | 280 final PostfixCompletionContext completionContext; |
| 281 final AnalysisContext analysisContext; | 281 final AnalysisContext analysisContext; |
| 282 final CorrectionUtils utils; | 282 final CorrectionUtils utils; |
| 283 int fileStamp; | |
| 284 AstNode node; | 283 AstNode node; |
| 285 PostfixCompletion completion; | 284 PostfixCompletion completion; |
| 286 SourceChange change = new SourceChange('postfix-completion'); | 285 SourceChange change = new SourceChange('postfix-completion'); |
| 287 final Map<String, LinkedEditGroup> linkedPositionGroups = | 286 final Map<String, LinkedEditGroup> linkedPositionGroups = |
| 288 <String, LinkedEditGroup>{}; | 287 <String, LinkedEditGroup>{}; |
| 289 Position exitPosition = null; | 288 Position exitPosition = null; |
| 290 TypeProvider _typeProvider; | 289 TypeProvider _typeProvider; |
| 291 | 290 |
| 292 PostfixCompletionProcessor(this.completionContext) | 291 PostfixCompletionProcessor(this.completionContext) |
| 293 : analysisContext = completionContext.unitElement.context, | 292 : analysisContext = completionContext.unitElement.context, |
| 294 utils = new CorrectionUtils(completionContext.unit) { | 293 utils = new CorrectionUtils(completionContext.unit); |
| 295 fileStamp = _modificationStamp(file); | |
| 296 } | |
| 297 | 294 |
| 298 AnalysisDriver get driver => completionContext.driver; | 295 AnalysisDriver get driver => completionContext.driver; |
| 299 | 296 |
| 300 String get eol => utils.endOfLine; | 297 String get eol => utils.endOfLine; |
| 301 | 298 |
| 302 String get file => completionContext.file; | 299 String get file => completionContext.file; |
| 303 | 300 |
| 304 String get key => completionContext.key; | 301 String get key => completionContext.key; |
| 305 | 302 |
| 306 LineInfo get lineInfo => completionContext.lineInfo; | 303 LineInfo get lineInfo => completionContext.lineInfo; |
| 307 | 304 |
| 308 int get requestLine => lineInfo.getLocation(selectionOffset).lineNumber; | 305 int get requestLine => lineInfo.getLocation(selectionOffset).lineNumber; |
| 309 | 306 |
| 310 int get selectionOffset => completionContext.selectionOffset; | 307 int get selectionOffset => completionContext.selectionOffset; |
| 311 | 308 |
| 312 Source get source => completionContext.unitElement.source; | 309 Source get source => completionContext.unitElement.source; |
| 313 | 310 |
| 314 TypeProvider get typeProvider { | 311 TypeProvider get typeProvider { |
| 315 return _typeProvider ??= unitElement.context.typeProvider; | 312 return _typeProvider ??= unitElement.context.typeProvider; |
| 316 } | 313 } |
| 317 | 314 |
| 318 CompilationUnit get unit => completionContext.unit; | 315 CompilationUnit get unit => completionContext.unit; |
| 319 | 316 |
| 320 CompilationUnitElement get unitElement => completionContext.unitElement; | 317 CompilationUnitElement get unitElement => completionContext.unitElement; |
| 321 | 318 |
| 322 Future<PostfixCompletion> compute() async { | 319 Future<PostfixCompletion> compute() async { |
| 323 // If the source was changed between the constructor and running | |
| 324 // this asynchronous method, it is not safe to use the unit. | |
| 325 if (_modificationStamp(file) != fileStamp) { | |
| 326 return NO_COMPLETION; | |
| 327 } | |
| 328 node = _selectedNode(); | 320 node = _selectedNode(); |
| 329 if (node == null) { | 321 if (node == null) { |
| 330 return NO_COMPLETION; | 322 return NO_COMPLETION; |
| 331 } | 323 } |
| 332 PostfixCompletionKind completer = DartPostfixCompletion.forKey(key); | 324 PostfixCompletionKind completer = DartPostfixCompletion.forKey(key); |
| 333 return completer?.computer(this, completer) ?? NO_COMPLETION; | 325 return completer?.computer(this, completer) ?? NO_COMPLETION; |
| 334 } | 326 } |
| 335 | 327 |
| 336 Future<PostfixCompletion> expand( | 328 Future<PostfixCompletion> expand( |
| 337 PostfixCompletionKind kind, Function contexter, Function sourcer, | 329 PostfixCompletionKind kind, Function contexter, Function sourcer, |
| 338 {bool withBraces: true}) async { | 330 {bool withBraces: true}) async { |
| 339 AstNode expr = contexter(); | 331 AstNode expr = contexter(); |
| 340 if (expr == null) { | 332 if (expr == null) { |
| 341 return null; | 333 return null; |
| 342 } | 334 } |
| 343 | 335 |
| 344 DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); | 336 DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| 345 await changeBuilder.addFileEdit(file, fileStamp, | 337 await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| 346 (DartFileEditBuilder builder) { | |
| 347 builder.addReplacement(range.node(expr), (DartEditBuilder builder) { | 338 builder.addReplacement(range.node(expr), (DartEditBuilder builder) { |
| 348 String newSrc = sourcer(expr); | 339 String newSrc = sourcer(expr); |
| 349 if (newSrc == null) { | 340 if (newSrc == null) { |
| 350 return null; | 341 return null; |
| 351 } | 342 } |
| 352 builder.write(newSrc); | 343 builder.write(newSrc); |
| 353 if (withBraces) { | 344 if (withBraces) { |
| 354 builder.write(" {"); | 345 builder.write(" {"); |
| 355 builder.write(eol); | 346 builder.write(eol); |
| 356 String indent = utils.getNodePrefix(expr); | 347 String indent = utils.getNodePrefix(expr); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 370 } | 361 } |
| 371 | 362 |
| 372 Future<PostfixCompletion> expandTry( | 363 Future<PostfixCompletion> expandTry( |
| 373 PostfixCompletionKind kind, Function contexter, | 364 PostfixCompletionKind kind, Function contexter, |
| 374 {bool withOn: false}) async { | 365 {bool withOn: false}) async { |
| 375 AstNode stmt = contexter(); | 366 AstNode stmt = contexter(); |
| 376 if (stmt == null) { | 367 if (stmt == null) { |
| 377 return null; | 368 return null; |
| 378 } | 369 } |
| 379 DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); | 370 DartChangeBuilder changeBuilder = new DartChangeBuilder(driver); |
| 380 await changeBuilder.addFileEdit(file, fileStamp, | 371 await changeBuilder.addFileEdit(file, (DartFileEditBuilder builder) { |
| 381 (DartFileEditBuilder builder) { | |
| 382 // Embed the full line(s) of the statement in the try block. | 372 // Embed the full line(s) of the statement in the try block. |
| 383 var startLine = lineInfo.getLocation(stmt.offset).lineNumber - 1; | 373 var startLine = lineInfo.getLocation(stmt.offset).lineNumber - 1; |
| 384 var endLine = lineInfo.getLocation(stmt.end).lineNumber - 1; | 374 var endLine = lineInfo.getLocation(stmt.end).lineNumber - 1; |
| 385 if (stmt is ExpressionStatement && !stmt.semicolon.isSynthetic) { | 375 if (stmt is ExpressionStatement && !stmt.semicolon.isSynthetic) { |
| 386 endLine += 1; | 376 endLine += 1; |
| 387 } | 377 } |
| 388 var startOffset = lineInfo.getOffsetOfLine(startLine); | 378 var startOffset = lineInfo.getOffsetOfLine(startLine); |
| 389 var endOffset = lineInfo.getOffsetOfLine(endLine); | 379 var endOffset = lineInfo.getOffsetOfLine(endLine); |
| 390 var src = utils.getText(startOffset, endOffset - startOffset); | 380 var src = utils.getText(startOffset, endOffset - startOffset); |
| 391 String indent = utils.getLinePrefix(stmt.offset); | 381 String indent = utils.getLinePrefix(stmt.offset); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 470 return null; | 460 return null; |
| 471 } | 461 } |
| 472 return astNode; | 462 return astNode; |
| 473 } | 463 } |
| 474 astNode = astNode.parent; | 464 astNode = astNode.parent; |
| 475 } | 465 } |
| 476 return null; | 466 return null; |
| 477 } | 467 } |
| 478 | 468 |
| 479 Future<bool> isApplicable() async { | 469 Future<bool> isApplicable() async { |
| 480 if (_modificationStamp(file) != fileStamp) { | |
| 481 return false; | |
| 482 } | |
| 483 node = _selectedNode(); | 470 node = _selectedNode(); |
| 484 if (node == null) { | 471 if (node == null) { |
| 485 return false; | 472 return false; |
| 486 } | 473 } |
| 487 PostfixCompletionKind completer = DartPostfixCompletion.forKey(key); | 474 PostfixCompletionKind completer = DartPostfixCompletion.forKey(key); |
| 488 return completer?.selector(this); | 475 return completer?.selector(this); |
| 489 } | 476 } |
| 490 | 477 |
| 491 String makeNegatedBoolExpr(Expression expr) { | 478 String makeNegatedBoolExpr(Expression expr) { |
| 492 String originalSrc = utils.getNodeText(expr); | 479 String originalSrc = utils.getNodeText(expr); |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 553 }, orElse: () => null); | 540 }, orElse: () => null); |
| 554 if (expr is SimpleIdentifier && expr.parent is PropertyAccess) { | 541 if (expr is SimpleIdentifier && expr.parent is PropertyAccess) { |
| 555 expr = expr.parent; | 542 expr = expr.parent; |
| 556 } | 543 } |
| 557 if (expr?.parent is CascadeExpression) { | 544 if (expr?.parent is CascadeExpression) { |
| 558 expr = expr.parent; | 545 expr = expr.parent; |
| 559 } | 546 } |
| 560 return expr; | 547 return expr; |
| 561 } | 548 } |
| 562 | 549 |
| 563 int _modificationStamp(String filePath) { | |
| 564 // TODO(brianwilkerson) We have lost the ability for clients to know whether | |
| 565 // it is safe to apply an edit. | |
| 566 return driver.fsState.getFileForPath(filePath).exists ? 0 : -1; | |
| 567 } | |
| 568 | |
| 569 AstNode _selectedNode({int at: null}) => | 550 AstNode _selectedNode({int at: null}) => |
| 570 new NodeLocator(at == null ? selectionOffset : at).searchWithin(unit); | 551 new NodeLocator(at == null ? selectionOffset : at).searchWithin(unit); |
| 571 | 552 |
| 572 void _setCompletionFromBuilder( | 553 void _setCompletionFromBuilder( |
| 573 DartChangeBuilder builder, PostfixCompletionKind kind, | 554 DartChangeBuilder builder, PostfixCompletionKind kind, |
| 574 [List args]) { | 555 [List args]) { |
| 575 SourceChange change = builder.sourceChange; | 556 SourceChange change = builder.sourceChange; |
| 576 if (change.edits.isEmpty) { | 557 if (change.edits.isEmpty) { |
| 577 completion = null; | 558 completion = null; |
| 578 return; | 559 return; |
| 579 } | 560 } |
| 580 change.message = formatList(kind.message, args); | 561 change.message = formatList(kind.message, args); |
| 581 completion = new PostfixCompletion(kind, change); | 562 completion = new PostfixCompletion(kind, change); |
| 582 } | 563 } |
| 583 } | 564 } |
| OLD | NEW |