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

Unified Diff: pkg/analysis_server/lib/src/services/correction/organize_directives.dart

Issue 1266393002: Organize directives implementation. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: renames Created 5 years, 4 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
« no previous file with comments | « no previous file | pkg/analysis_server/test/edit/sort_members_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analysis_server/lib/src/services/correction/organize_directives.dart
diff --git a/pkg/analysis_server/lib/src/services/correction/organize_directives.dart b/pkg/analysis_server/lib/src/services/correction/organize_directives.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d3de3ea62748220f583aece528ff848f713802b5
--- /dev/null
+++ b/pkg/analysis_server/lib/src/services/correction/organize_directives.dart
@@ -0,0 +1,254 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library services.src.refactoring.organize_directives;
+
+import 'package:analysis_server/src/protocol.dart' hide AnalysisError, Element;
+import 'package:analysis_server/src/services/correction/strings.dart';
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+
+/**
+ * Organizer of directives in the [unit].
+ */
+class DirectiveOrganizer {
+ final String initialCode;
+ final CompilationUnit unit;
+ final List<AnalysisError> errors;
+ final bool removeUnresolved;
+ final bool removeUnused;
+ String code;
+ String endOfLine;
+
+ DirectiveOrganizer(this.initialCode, this.unit, this.errors,
+ {this.removeUnresolved: true, this.removeUnused: true}) {
+ this.code = initialCode;
+ this.endOfLine = getEOL(code);
+ }
+
+ /**
+ * Return the [SourceEdit]s that organize directives in the [unit].
+ */
+ List<SourceEdit> organize() {
+ _organizeDirectives();
+ // prepare edits
+ List<SourceEdit> edits = <SourceEdit>[];
+ if (code != initialCode) {
+ int prefixLength = findCommonPrefix(initialCode, code);
+ int suffixLength = findCommonSuffix(initialCode, code);
+ String prefix = code.substring(0, prefixLength);
+ String suffix = code.substring(code.length - suffixLength, code.length);
+ int commonLength = findCommonOverlap(prefix, suffix);
+ suffixLength -= commonLength;
+ SourceEdit edit = new SourceEdit(
+ prefixLength,
+ initialCode.length - suffixLength - prefixLength,
+ code.substring(prefixLength, code.length - suffixLength));
+ edits.add(edit);
+ }
+ return edits;
+ }
+
+ bool _isUnresolvedUri(UriBasedDirective directive) {
+ for (AnalysisError error in errors) {
+ if (error.errorCode == CompileTimeErrorCode.URI_DOES_NOT_EXIST &&
+ directive.uri.offset == error.offset) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool _isUnusedImport(UriBasedDirective directive) {
+ for (AnalysisError error in errors) {
+ if (error.errorCode == HintCode.UNUSED_IMPORT &&
+ directive.uri.offset == error.offset) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Oraganize all [Directive]s.
+ */
+ void _organizeDirectives() {
+ List<_DirectiveInfo> directives = [];
+ for (Directive directive in unit.directives) {
+ if (directive is UriBasedDirective) {
+ _DirectivePriority priority = getDirectivePriority(directive);
+ if (priority != null) {
+ int offset = directive.offset;
+ int length = directive.length;
+ String text = code.substring(offset, offset + length);
+ String uriContent = directive.uri.stringValue;
+ directives
+ .add(new _DirectiveInfo(directive, priority, uriContent, text));
+ }
+ }
+ }
+ // nothing to do
+ if (directives.isEmpty) {
+ return;
+ }
+ int firstDirectiveOffset = directives.first.directive.offset;
+ int lastDirectiveEnd = directives.last.directive.end;
+ // sort
+ directives.sort();
+ // append directives with grouping
+ String directivesCode;
+ {
+ StringBuffer sb = new StringBuffer();
+ _DirectivePriority currentPriority = null;
+ for (_DirectiveInfo directiveInfo in directives) {
+ if (removeUnresolved && _isUnresolvedUri(directiveInfo.directive)) {
+ continue;
+ }
+ if (removeUnused && _isUnusedImport(directiveInfo.directive)) {
+ continue;
+ }
+ if (currentPriority != directiveInfo.priority) {
+ if (sb.length != 0) {
+ sb.write(endOfLine);
+ }
+ currentPriority = directiveInfo.priority;
+ }
+ sb.write(directiveInfo.text);
+ sb.write(endOfLine);
+ }
+ directivesCode = sb.toString();
+ directivesCode = directivesCode.trimRight();
+ }
+ // append comment tokens which otherwise would be removed completely
+ {
+ bool firstCommentToken = true;
+ Token token = unit.beginToken;
+ while (token != null &&
+ token.type != TokenType.EOF &&
+ token.end < lastDirectiveEnd) {
+ Token commentToken = token.precedingComments;
+ while (commentToken != null) {
+ int offset = commentToken.offset;
+ int end = commentToken.end;
+ if (offset > firstDirectiveOffset && offset < lastDirectiveEnd) {
+ if (firstCommentToken) {
+ directivesCode += endOfLine;
+ firstCommentToken = false;
+ }
+ directivesCode += code.substring(offset, end) + endOfLine;
+ }
+ commentToken = commentToken.next;
+ }
+ token = token.next;
+ }
+ }
+ // prepare code
+ String beforeDirectives = code.substring(0, firstDirectiveOffset);
+ String afterDirectives = code.substring(lastDirectiveEnd);
+ code = beforeDirectives + directivesCode + afterDirectives;
+ }
+
+ static _DirectivePriority getDirectivePriority(UriBasedDirective directive) {
+ String uriContent = directive.uri.stringValue;
+ if (directive is ImportDirective) {
+ if (uriContent.startsWith("dart:")) {
+ return _DirectivePriority.IMPORT_SDK;
+ } else if (uriContent.startsWith("package:")) {
+ return _DirectivePriority.IMPORT_PKG;
+ } else if (uriContent.contains('://')) {
+ return _DirectivePriority.IMPORT_OTHER;
+ } else {
+ return _DirectivePriority.IMPORT_REL;
+ }
+ }
+ if (directive is ExportDirective) {
+ if (uriContent.startsWith("dart:")) {
+ return _DirectivePriority.EXPORT_SDK;
+ } else if (uriContent.startsWith("package:")) {
+ return _DirectivePriority.EXPORT_PKG;
+ } else if (uriContent.contains('://')) {
+ return _DirectivePriority.EXPORT_OTHER;
+ } else {
+ return _DirectivePriority.EXPORT_REL;
+ }
+ }
+ if (directive is PartDirective) {
+ return _DirectivePriority.PART;
+ }
+ return null;
+ }
+
+ /**
+ * Return the EOL to use for [code].
+ */
+ static String getEOL(String code) {
+ if (code.contains('\r\n')) {
+ return '\r\n';
+ } else {
+ return '\n';
+ }
+ }
+}
+
+class _DirectiveInfo implements Comparable<_DirectiveInfo> {
+ final UriBasedDirective directive;
+ final _DirectivePriority priority;
+ final String uri;
+ final String text;
+
+ _DirectiveInfo(this.directive, this.priority, this.uri, this.text);
+
+ @override
+ int compareTo(_DirectiveInfo other) {
+ if (priority == other.priority) {
+ return _compareUri(uri, other.uri);
+ }
+ return priority.ordinal - other.priority.ordinal;
+ }
+
+ @override
+ String toString() => '(priority=$priority; text=$text)';
+
+ static int _compareUri(String a, String b) {
+ List<String> aList = _splitUri(a);
+ List<String> bList = _splitUri(b);
+ int result;
+ if ((result = aList[0].compareTo(bList[0])) != 0) return result;
+ if ((result = aList[1].compareTo(bList[1])) != 0) return result;
+ return 0;
+ }
+
+ /**
+ * Split the given [uri] like `package:some.name/and/path.dart` into a list
+ * like `[package:some.name, and/path.dart]`.
+ */
+ static List<String> _splitUri(String uri) {
+ int index = uri.indexOf('/');
+ if (index == -1) {
+ return <String>[uri, ''];
+ }
+ return <String>[uri.substring(0, index), uri.substring(index + 1)];
+ }
+}
+
+class _DirectivePriority {
+ static const IMPORT_SDK = const _DirectivePriority('IMPORT_SDK', 0);
+ static const IMPORT_PKG = const _DirectivePriority('IMPORT_PKG', 1);
+ static const IMPORT_OTHER = const _DirectivePriority('IMPORT_OTHER', 2);
+ static const IMPORT_REL = const _DirectivePriority('IMPORT_REL', 3);
+ static const EXPORT_SDK = const _DirectivePriority('EXPORT_SDK', 4);
+ static const EXPORT_PKG = const _DirectivePriority('EXPORT_PKG', 5);
+ static const EXPORT_OTHER = const _DirectivePriority('EXPORT_OTHER', 6);
+ static const EXPORT_REL = const _DirectivePriority('EXPORT_REL', 7);
+ static const PART = const _DirectivePriority('PART', 8);
+
+ final String name;
+ final int ordinal;
+
+ const _DirectivePriority(this.name, this.ordinal);
+
+ @override
+ String toString() => name;
+}
« no previous file with comments | « no previous file | pkg/analysis_server/test/edit/sort_members_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698