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 |