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

Side by Side Diff: pkg/analysis_server/lib/src/services/refactoring/extract_local.dart

Issue 1431673003: Compute covering offsets/lengths. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 5 years, 1 month 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) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, 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.refactoring.extract_local; 5 library services.src.refactoring.extract_local;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:collection'; 8 import 'dart:collection';
9 9
10 import 'package:analysis_server/src/protocol_server.dart' hide Element; 10 import 'package:analysis_server/src/protocol_server.dart' hide Element;
(...skipping 22 matching lines...) Expand all
33 final CompilationUnit unit; 33 final CompilationUnit unit;
34 final int selectionOffset; 34 final int selectionOffset;
35 final int selectionLength; 35 final int selectionLength;
36 CompilationUnitElement unitElement; 36 CompilationUnitElement unitElement;
37 String file; 37 String file;
38 SourceRange selectionRange; 38 SourceRange selectionRange;
39 CorrectionUtils utils; 39 CorrectionUtils utils;
40 40
41 String name; 41 String name;
42 bool extractAll = true; 42 bool extractAll = true;
43 final List<int> coveringExpressionOffsets = <int>[];
44 final List<int> coveringExpressionLengths = <int>[];
43 final List<String> names = <String>[]; 45 final List<String> names = <String>[];
44 final List<int> offsets = <int>[]; 46 final List<int> offsets = <int>[];
45 final List<int> lengths = <int>[]; 47 final List<int> lengths = <int>[];
46 48
47 Expression rootExpression; 49 Expression rootExpression;
48 Expression singleExpression; 50 Expression singleExpression;
49 bool wholeStatementExpression = false; 51 bool wholeStatementExpression = false;
50 String stringLiteralPart; 52 String stringLiteralPart;
51 final List<SourceRange> occurrences = <SourceRange>[]; 53 final List<SourceRange> occurrences = <SourceRange>[];
52 final Map<Element, int> elementIds = <Element, int>{}; 54 final Map<Element, int> elementIds = <Element, int>{};
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 } 173 }
172 // done 174 // done
173 return new Future.value(change); 175 return new Future.value(change);
174 } 176 }
175 177
176 @override 178 @override
177 bool requiresPreview() => false; 179 bool requiresPreview() => false;
178 180
179 /** 181 /**
180 * Checks if [selectionRange] selects [Expression] which can be extracted, and 182 * Checks if [selectionRange] selects [Expression] which can be extracted, and
181 * location of this [DartExpression] in AST allows extracting. 183 * location of this [Expression] in AST allows extracting.
182 */ 184 */
183 RefactoringStatus _checkSelection() { 185 RefactoringStatus _checkSelection() {
184 _ExtractExpressionAnalyzer _selectionAnalyzer = 186 String selectionStr;
185 new _ExtractExpressionAnalyzer(selectionRange); 187 // exclude whitespaces
186 unit.accept(_selectionAnalyzer);
187 AstNode coveringNode = _selectionAnalyzer.coveringNode;
188 // may be fatal error
189 { 188 {
190 RefactoringStatus status = _selectionAnalyzer.status; 189 selectionStr = utils.getRangeText(selectionRange);
191 if (status.hasFatalError) { 190 int numLeading = countLeadingWhitespaces(selectionStr);
192 return status; 191 int numTrailing = countTrailingWhitespaces(selectionStr);
192 int offset = selectionRange.offset + numLeading;
193 int end = selectionRange.end - numTrailing;
194 selectionRange = new SourceRange(offset, end - offset);
195 }
196 // get covering node
197 AstNode coveringNode = new NodeLocator(
198 selectionRange.offset, selectionRange.end).searchWithin(unit);
199 // compute covering expressions
200 for (AstNode node = coveringNode; node is Expression; node = node.parent) {
201 AstNode parent = node.parent;
202 // cannot extract the name part of a property access
203 if (parent is PrefixedIdentifier && parent.identifier == node ||
204 parent is PropertyAccess && parent.propertyName == node) {
205 continue;
193 } 206 }
207 // fatal selection problems
208 if (coveringExpressionOffsets.isEmpty) {
209 if (node is SimpleIdentifier) {
210 Element element = node.bestElement;
211 if (element is FunctionElement || element is MethodElement) {
212 return new RefactoringStatus.fatal(
213 'Cannot extract a single method name.',
214 newLocation_fromNode(node));
215 }
216 if (node.inDeclarationContext()) {
217 return new RefactoringStatus.fatal(
218 'Cannot extract the name part of a declaration.',
219 newLocation_fromNode(node));
220 }
221 }
222 if (parent is AssignmentExpression && parent.leftHandSide == node) {
223 return new RefactoringStatus.fatal(
224 'Cannot extract the left-hand side of an assignment.',
225 newLocation_fromNode(node));
226 }
227 }
228 // set selected expression
229 if (coveringExpressionOffsets.isEmpty) {
230 rootExpression = node;
231 }
232 // add the expression range
233 coveringExpressionOffsets.add(node.offset);
234 coveringExpressionLengths.add(node.length);
194 } 235 }
195 // we need enclosing block to add variable declaration statement 236 // we need enclosing block to add variable declaration statement
196 if (coveringNode == null || 237 if (coveringNode == null ||
197 coveringNode.getAncestor((node) => node is Block) == null) { 238 coveringNode.getAncestor((node) => node is Block) == null) {
198 return new RefactoringStatus.fatal( 239 return new RefactoringStatus.fatal(
199 'Expression inside of function must be selected ' 240 'Expression inside of function must be selected '
200 'to activate this refactoring.'); 241 'to activate this refactoring.');
201 } 242 }
202 // part of string literal 243 // part of string literal
203 if (coveringNode is StringLiteral) { 244 if (coveringNode is StringLiteral) {
204 stringLiteralPart = utils.getRangeText(selectionRange); 245 if (selectionRange.offset > coveringNode.offset &&
205 if (stringLiteralPart.startsWith("'") || 246 selectionRange.end < coveringNode.end) {
206 stringLiteralPart.startsWith('"') || 247 stringLiteralPart = selectionStr;
207 stringLiteralPart.endsWith("'") ||
208 stringLiteralPart.endsWith('"')) {
209 return new RefactoringStatus.fatal(
210 'Cannot extract only leading or trailing quote of string literal.');
211 }
212 return new RefactoringStatus();
213 }
214 // single node selected
215 if (_selectionAnalyzer.selectedNodes.length == 1 &&
216 !utils.selectionIncludesNonWhitespaceOutsideNode(
217 selectionRange, _selectionAnalyzer.firstSelectedNode)) {
218 AstNode selectedNode = _selectionAnalyzer.firstSelectedNode;
219 if (selectedNode is Expression) {
220 rootExpression = selectedNode;
221 singleExpression = rootExpression;
222 wholeStatementExpression =
223 singleExpression.parent is ExpressionStatement;
224 return new RefactoringStatus(); 248 return new RefactoringStatus();
225 } 249 }
226 } 250 }
227 // fragment of binary expression selected 251 // single node selected
228 if (coveringNode is BinaryExpression) { 252 if (rootExpression != null) {
229 BinaryExpression binaryExpression = coveringNode; 253 singleExpression = rootExpression;
230 if (utils.validateBinaryExpressionRange( 254 selectionRange = rangeNode(singleExpression);
231 binaryExpression, selectionRange)) { 255 wholeStatementExpression = singleExpression.parent is ExpressionStatement;
232 rootExpression = binaryExpression; 256 return new RefactoringStatus();
233 singleExpression = null;
234 return new RefactoringStatus();
235 }
236 } 257 }
237 // invalid selection 258 // invalid selection
238 return new RefactoringStatus.fatal( 259 return new RefactoringStatus.fatal(
239 'Expression must be selected to activate this refactoring.'); 260 'Expression must be selected to activate this refactoring.');
240 } 261 }
241 262
242 /** 263 /**
243 * Return an unique identifier for the given [Element], or `null` if [element] 264 * Return an unique identifier for the given [Element], or `null` if [element]
244 * is `null`. 265 * is `null`.
245 */ 266 */
246 int _encodeElement(Element element) { 267 int _encodeElement(Element element) {
247 if (element == null) { 268 if (element == null) {
248 return null; 269 return null;
249 } 270 }
250 int id = elementIds[element]; 271 int id = elementIds[element];
251 if (id == null) { 272 if (id == null) {
252 id = elementIds.length; 273 id = elementIds.length;
253 elementIds[element] = id; 274 elementIds[element] = id;
254 } 275 }
255 return id; 276 return id;
256 } 277 }
257 278
258 /** 279 /**
259 * Returns an [Element]-sensitive encoding of [tokens]. 280 * Returns an [Element]-sensitive encoding of [tokens].
260 * Each [Token] with a [LocalVariableElement] has a suffix of the element id. 281 * Each [Token] with a [LocalVariableElement] has a suffix of the element id.
261 * 282 *
262 * So, we can distingush different local variables with the same name, if 283 * So, we can distinguish different local variables with the same name, if
263 * there are multiple variables with the same name are declared in the 284 * there are multiple variables with the same name are declared in the
264 * function we are searching occurrences in. 285 * function we are searching occurrences in.
265 */ 286 */
266 String _encodeExpressionTokens(Expression expr, List<Token> tokens) { 287 String _encodeExpressionTokens(Expression expr, List<Token> tokens) {
267 // no expression, i.e. a part of a string 288 // no expression, i.e. a part of a string
268 if (expr == null) { 289 if (expr == null) {
269 return tokens.join(_TOKEN_SEPARATOR); 290 return tokens.join(_TOKEN_SEPARATOR);
270 } 291 }
271 // prepare Token -> LocalElement map 292 // prepare Token -> LocalElement map
272 Map<Token, Element> map = new HashMap<Token, Element>( 293 Map<Token, Element> map = new HashMap<Token, Element>(
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
468 invalidSelection('Cannot extract name part of a property access.'); 489 invalidSelection('Cannot extract name part of a property access.');
469 } 490 }
470 if (parent is PropertyAccess && identical(parent.propertyName, node)) { 491 if (parent is PropertyAccess && identical(parent.propertyName, node)) {
471 invalidSelection('Cannot extract name part of a property access.'); 492 invalidSelection('Cannot extract name part of a property access.');
472 } 493 }
473 } 494 }
474 return null; 495 return null;
475 } 496 }
476 497
477 /** 498 /**
478 * Records fatal error with given message and [Locatiom]. 499 * Records fatal error with given [message] and [location].
479 */ 500 */
480 void _invalidSelection(String message, Location location) { 501 void _invalidSelection(String message, Location location) {
481 status.addFatalError(message, location); 502 status.addFatalError(message, location);
482 reset(); 503 reset();
483 } 504 }
484 505
485 bool _isFirstSelectedNode(AstNode node) => node == firstSelectedNode; 506 bool _isFirstSelectedNode(AstNode node) => node == firstSelectedNode;
486 } 507 }
487 508
488 class _HasStatementVisitor extends GeneralizingAstVisitor { 509 class _HasStatementVisitor extends GeneralizingAstVisitor {
(...skipping 27 matching lines...) Expand all
516 Object visitExpression(Expression node) { 537 Object visitExpression(Expression node) {
517 if (ref._isExtractable(rangeNode(node))) { 538 if (ref._isExtractable(rangeNode(node))) {
518 _tryToFindOccurrence(node); 539 _tryToFindOccurrence(node);
519 } 540 }
520 return super.visitExpression(node); 541 return super.visitExpression(node);
521 } 542 }
522 543
523 @override 544 @override
524 Object visitStringLiteral(StringLiteral node) { 545 Object visitStringLiteral(StringLiteral node) {
525 if (ref.stringLiteralPart != null) { 546 if (ref.stringLiteralPart != null) {
526 int occuLength = ref.stringLiteralPart.length; 547 int length = ref.stringLiteralPart.length;
527 String value = ref.utils.getNodeText(node); 548 String value = ref.utils.getNodeText(node);
528 int lastIndex = 0; 549 int lastIndex = 0;
529 while (true) { 550 while (true) {
530 int index = value.indexOf(ref.stringLiteralPart, lastIndex); 551 int index = value.indexOf(ref.stringLiteralPart, lastIndex);
531 if (index == -1) { 552 if (index == -1) {
532 break; 553 break;
533 } 554 }
534 lastIndex = index + occuLength; 555 lastIndex = index + length;
535 int occuStart = node.offset + index; 556 int start = node.offset + index;
536 SourceRange occuRange = rangeStartLength(occuStart, occuLength); 557 SourceRange range = rangeStartLength(start, length);
537 occurrences.add(occuRange); 558 occurrences.add(range);
538 } 559 }
539 return null; 560 return null;
540 } 561 }
541 return visitExpression(node); 562 return visitExpression(node);
542 } 563 }
543 564
544 void _addOccurrence(SourceRange range) { 565 void _addOccurrence(SourceRange range) {
545 if (range.intersects(ref.selectionRange)) { 566 if (range.intersects(ref.selectionRange)) {
546 occurrences.add(ref.selectionRange); 567 occurrences.add(ref.selectionRange);
547 } else { 568 } else {
548 occurrences.add(range); 569 occurrences.add(range);
549 } 570 }
550 } 571 }
551 572
552 bool _hasStatements(AstNode root) { 573 bool _hasStatements(AstNode root) {
553 _HasStatementVisitor visitor = new _HasStatementVisitor(); 574 _HasStatementVisitor visitor = new _HasStatementVisitor();
554 root.accept(visitor); 575 root.accept(visitor);
555 return visitor.result; 576 return visitor.result;
556 } 577 }
557 578
558 void _tryToFindOccurrence(Expression node) { 579 void _tryToFindOccurrence(Expression node) {
559 String nodeSource = ref.utils.getNodeText(node); 580 String nodeSource = ref.utils.getNodeText(node);
560 List<Token> nodeTokens = TokenUtils.getTokens(nodeSource); 581 List<Token> nodeTokens = TokenUtils.getTokens(nodeSource);
561 nodeSource = ref._encodeExpressionTokens(node, nodeTokens); 582 nodeSource = ref._encodeExpressionTokens(node, nodeTokens);
562 if (nodeSource == selectionSource) { 583 if (nodeSource == selectionSource) {
563 SourceRange occuRange = rangeNode(node); 584 SourceRange range = rangeNode(node);
564 _addOccurrence(occuRange); 585 _addOccurrence(range);
565 } 586 }
566 } 587 }
567 588
568 void _tryToFindOccurrenceFragments(Expression node) { 589 void _tryToFindOccurrenceFragments(Expression node) {
569 int nodeOffset = node.offset; 590 int nodeOffset = node.offset;
570 String nodeSource = ref.utils.getNodeText(node); 591 String nodeSource = ref.utils.getNodeText(node);
571 List<Token> nodeTokens = TokenUtils.getTokens(nodeSource); 592 List<Token> nodeTokens = TokenUtils.getTokens(nodeSource);
572 nodeSource = ref._encodeExpressionTokens(node, nodeTokens); 593 nodeSource = ref._encodeExpressionTokens(node, nodeTokens);
573 // find "selection" in "node" tokens 594 // find "selection" in "node" tokens
574 int lastIndex = 0; 595 int lastIndex = 0;
575 while (true) { 596 while (true) {
576 // find next occurrence 597 // find next occurrence
577 int index = nodeSource.indexOf(selectionSource, lastIndex); 598 int index = nodeSource.indexOf(selectionSource, lastIndex);
578 if (index == -1) { 599 if (index == -1) {
579 break; 600 break;
580 } 601 }
581 lastIndex = index + selectionSource.length; 602 lastIndex = index + selectionSource.length;
582 // find start/end tokens 603 // find start/end tokens
583 int startTokenIndex = 604 int startTokenIndex =
584 countMatches(nodeSource.substring(0, index), _TOKEN_SEPARATOR); 605 countMatches(nodeSource.substring(0, index), _TOKEN_SEPARATOR);
585 int endTokenIndex = 606 int endTokenIndex =
586 countMatches(nodeSource.substring(0, lastIndex), _TOKEN_SEPARATOR); 607 countMatches(nodeSource.substring(0, lastIndex), _TOKEN_SEPARATOR);
587 Token startToken = nodeTokens[startTokenIndex]; 608 Token startToken = nodeTokens[startTokenIndex];
588 Token endToken = nodeTokens[endTokenIndex]; 609 Token endToken = nodeTokens[endTokenIndex];
589 // add occurrence range 610 // add occurrence range
590 int occuStart = nodeOffset + startToken.offset; 611 int start = nodeOffset + startToken.offset;
591 int occuEnd = nodeOffset + endToken.end; 612 int end = nodeOffset + endToken.end;
592 SourceRange occuRange = rangeStartEnd(occuStart, occuEnd); 613 SourceRange range = rangeStartEnd(start, end);
593 _addOccurrence(occuRange); 614 _addOccurrence(range);
594 } 615 }
595 } 616 }
596 } 617 }
597 618
598 class _TokenLocalElementVisitor extends RecursiveAstVisitor { 619 class _TokenLocalElementVisitor extends RecursiveAstVisitor {
599 final Map<Token, Element> map; 620 final Map<Token, Element> map;
600 621
601 _TokenLocalElementVisitor(this.map); 622 _TokenLocalElementVisitor(this.map);
602 623
603 visitSimpleIdentifier(SimpleIdentifier node) { 624 visitSimpleIdentifier(SimpleIdentifier node) {
604 Element element = node.staticElement; 625 Element element = node.staticElement;
605 if (element is LocalVariableElement) { 626 if (element is LocalVariableElement) {
606 map[node.token] = element; 627 map[node.token] = element;
607 } 628 }
608 } 629 }
609 } 630 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698