Skip to content

Commit 1916873

Browse files
committed
Prototype fix to convert URI to the shorthand syntax proposed by:
dart-lang/language#649 There are some differences between the semantics in the issue and in the proposal: https://github.com/dart-lang/language/tree/master/working/0649%20-%20Import%20shorthand This branch follows the issue's proposal because I think a shorthand for relative imports is worth doing.
1 parent 61a6636 commit 1916873

File tree

5 files changed

+108
-6
lines changed

5 files changed

+108
-6
lines changed

lib/src/io.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,45 @@ import 'dart:async';
55
import 'dart:convert';
66
import 'dart:io';
77

8+
import 'package:package_config/package_config.dart';
89
import 'package:path/path.dart' as p;
910

1011
import 'cli/formatter_options.dart';
1112
import 'dart_formatter.dart';
1213
import 'exceptions.dart';
1314
import 'source_code.dart';
1415

16+
/// Gets the name of the package that contains [uri] or `null` if it is not in
17+
/// a package.
18+
String? packageForUri(String? uri) {
19+
if (uri == null) return null;
20+
21+
if (!Uri.parse(uri).isAbsolute) {
22+
uri = p.url.normalize(p.url.join(Directory.current.uri.toString(), uri));
23+
}
24+
25+
var dir = p.url.dirname(p.fromUri(uri));
26+
27+
// TODO: Could cache some of this work for performance.
28+
29+
// Find the package config containing this file in order to determine what
30+
// package the file is in.
31+
while (Directory(p.fromUri(dir)).existsSync()) {
32+
var configFile =
33+
File(p.join(p.fromUri(dir), '.dart_tool', 'package_config.json'));
34+
if (configFile.existsSync()) {
35+
var packageConfig = PackageConfig.parseBytes(configFile.readAsBytesSync(),
36+
Uri.file(p.fromUri(p.url.join(dir, '.dart_tool'))));
37+
38+
return packageConfig.packageOf(Uri.parse(uri))?.name;
39+
}
40+
41+
dir = p.url.dirname(dir);
42+
}
43+
44+
return null;
45+
}
46+
1547
/// Reads and formats input from stdin until closed.
1648
Future<void> formatStdin(
1749
FormatterOptions options, List<int>? selection, String name) async {

lib/src/source_visitor.dart

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:analyzer/dart/ast/standard_ast_factory.dart';
66
import 'package:analyzer/dart/ast/token.dart';
77
import 'package:analyzer/dart/ast/visitor.dart';
88
import 'package:analyzer/src/generated/source.dart';
9+
import 'package:dart_style/src/io.dart';
910

1011
import 'argument_list_visitor.dart';
1112
import 'call_chain_visitor.dart';
@@ -192,6 +193,8 @@ class SourceVisitor extends ThrowingAstVisitor {
192193
/// from the output.
193194
final Set<Token> _suppressPrecedingCommentsAndNewLines = {};
194195

196+
late final String? _currentPackage = packageForUri(_source.uri);
197+
195198
/// Initialize a newly created visitor to write source code representing
196199
/// the visited nodes to the given [writer].
197200
SourceVisitor(this._formatter, this._lineInfo, this._source)
@@ -931,7 +934,7 @@ class SourceVisitor extends ThrowingAstVisitor {
931934

932935
token(node.rightParenthesis);
933936
space();
934-
visit(node.uri);
937+
_visitUri(node.uri);
935938
}
936939

937940
@override
@@ -1190,7 +1193,7 @@ class SourceVisitor extends ThrowingAstVisitor {
11901193
_simpleStatement(node, () {
11911194
token(node.keyword);
11921195
space();
1193-
visit(node.uri);
1196+
_visitUri(node.uri);
11941197

11951198
_visitConfigurations(node.configurations);
11961199

@@ -2082,7 +2085,7 @@ class SourceVisitor extends ThrowingAstVisitor {
20822085
_simpleStatement(node, () {
20832086
token(node.keyword);
20842087
space();
2085-
visit(node.uri);
2088+
_visitUri(node.uri);
20862089

20872090
_visitConfigurations(node.configurations);
20882091

@@ -2100,6 +2103,68 @@ class SourceVisitor extends ThrowingAstVisitor {
21002103
});
21012104
}
21022105

2106+
void _visitUri(StringLiteral uriLiteral) {
2107+
if (_formatter.fixes.contains(StyleFix.uriShorthand)) {
2108+
var uri = Uri.parse(uriLiteral.stringValue!);
2109+
2110+
String? result;
2111+
if (uri.scheme == 'package' &&
2112+
uri.pathSegments.length > 1 &&
2113+
uri.path.endsWith('.dart')) {
2114+
var packageName = uri.pathSegments.first;
2115+
var packageShortName = packageName.split('.').last;
2116+
2117+
var path = uri.pathSegments.skip(1).join('/');
2118+
// Remove ".dart".
2119+
path = path.substring(0, path.length - 5);
2120+
2121+
if (packageShortName == path) {
2122+
// package:name/name.dart -> name
2123+
// package:dotted.name/name.dart -> name
2124+
result = packageName;
2125+
} else if (packageName == _currentPackage) {
2126+
// TODO: The issue uses "/" here but the proposal says ":". Using
2127+
// "/" to match relative paths below.
2128+
result = '/$path';
2129+
} else {
2130+
// package:name/path.dart -> name:path
2131+
result = '$packageName:$path';
2132+
}
2133+
2134+
// TODO: Detect if package is same as current package and use ":path".
2135+
// package:current_package/path.dart -> :path
2136+
} else if (uri.scheme == 'dart') {
2137+
result = 'dart:${uri.path}';
2138+
} else if (uri.scheme == '' && uri.path.endsWith('.dart')) {
2139+
// Shorthand for relative imports.
2140+
2141+
// TODO: This is described in the issue, but not the proposal.
2142+
2143+
var path = uri.path;
2144+
// Remove ".dart".
2145+
path = path.substring(0, path.length - 5);
2146+
2147+
if (!path.startsWith('.')) {
2148+
// TODO: If path is in root directory, should just do "/".
2149+
path = './$path';
2150+
}
2151+
2152+
result = path;
2153+
} else {
2154+
// No shorthand.
2155+
}
2156+
2157+
if (result != null) {
2158+
writePrecedingCommentsAndNewlines(uriLiteral.beginToken);
2159+
_writeText(result, uriLiteral.offset);
2160+
} else {
2161+
visit(uriLiteral);
2162+
}
2163+
} else {
2164+
visit(uriLiteral);
2165+
}
2166+
}
2167+
21032168
@override
21042169
void visitIndexExpression(IndexExpression node) {
21052170
builder.nestExpression();
@@ -2411,7 +2476,7 @@ class SourceVisitor extends ThrowingAstVisitor {
24112476
_simpleStatement(node, () {
24122477
token(node.keyword);
24132478
space();
2414-
visit(node.uri);
2479+
_visitUri(node.uri);
24152480
});
24162481
}
24172482

@@ -2427,7 +2492,7 @@ class SourceVisitor extends ThrowingAstVisitor {
24272492
// Part-of may have either a name or a URI. Only one of these will be
24282493
// non-null. We visit both since visit() ignores null.
24292494
visit(node.libraryName);
2430-
visit(node.uri);
2495+
if (node.uri != null) _visitUri(node.uri!);
24312496
});
24322497
}
24332498

lib/src/style_fix.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,17 @@ class StyleFix {
2424
static const singleCascadeStatements = StyleFix._('single-cascade-statements',
2525
'Remove unnecessary single cascades from expression statements.');
2626

27+
static const uriShorthand = StyleFix._('uri-shorthand',
28+
'Convert URIs in directives to shorthand syntax.');
29+
2730
static const all = [
2831
docComments,
2932
functionTypedefs,
3033
namedDefaultSeparator,
3134
optionalConst,
3235
optionalNew,
3336
singleCascadeStatements,
37+
uriShorthand,
3438
];
3539

3640
final String name;

pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ packages:
170170
source: hosted
171171
version: "2.0.1"
172172
package_config:
173-
dependency: transitive
173+
dependency: "direct main"
174174
description:
175175
name: package_config
176176
url: "https://pub.dartlang.org"

pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ environment:
1212
dependencies:
1313
analyzer: '>=2.0.0 <3.0.0'
1414
args: '>=1.0.0 <3.0.0'
15+
package_config: ^2.0.2
1516
path: ^1.0.0
1617
pub_semver: '>=1.4.4 <3.0.0'
1718
source_span: ^1.4.0

0 commit comments

Comments
 (0)