-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
context_root.dart
158 lines (136 loc) · 4.59 KB
/
context_root.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright (c) 2018, 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.
import 'package:analyzer/dart/analysis/context_root.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/workspace/workspace.dart';
import 'package:glob/glob.dart';
import 'package:path/path.dart';
/// An implementation of a context root.
class ContextRootImpl implements ContextRoot {
@override
final ResourceProvider resourceProvider;
@override
final Folder root;
@override
final Workspace workspace;
@override
final List<Resource> included = [];
@override
final List<Resource> excluded = [];
/// A list of the globs for excluded files that were read from the analysis
/// options file.
List<Glob> excludedGlobs = [];
@override
File? optionsFile;
@override
File? packagesFile;
/// Initialize a newly created context root.
ContextRootImpl(this.resourceProvider, this.root, this.workspace);
@override
Iterable<String> get excludedPaths =>
excluded.map((Resource folder) => folder.path);
@override
int get hashCode => root.path.hashCode;
@override
Iterable<String> get includedPaths =>
included.map((Resource folder) => folder.path);
@override
bool operator ==(Object other) {
return other is ContextRoot && root.path == other.root.path;
}
@override
Iterable<String> analyzedFiles() sync* {
var visited = <String>{};
for (var includedPath in includedPaths) {
var included = resourceProvider.getResource(includedPath);
if (included is File) {
yield includedPath;
} else if (included is Folder) {
yield* _includedFilesInFolder(visited, included, includedPath);
} else {
Type type = included.runtimeType;
throw StateError('Unknown resource at path "$includedPath" ($type)');
}
}
}
@override
bool isAnalyzed(String path) {
for (var includedPath in includedPaths) {
var included = resourceProvider.getResource(includedPath);
if (included is File) {
if (included.path == path) {
return true;
}
} else if (included is Folder) {
if (included.isOrContains(path)) {
if (!_isExcluded(path, included.path)) {
return true;
}
}
}
}
return false;
}
/// Return the absolute paths of all of the files that are included in the
/// given [folder]. Ignore globs that match the explicit [includedPath].
Iterable<String> _includedFilesInFolder(
Set<String> visited,
Folder folder,
String includedPath,
) sync* {
for (Resource resource in folder.getChildren()) {
String path = resource.path;
if (!_isExcluded(path, includedPath)) {
if (resource is File) {
yield path;
} else if (resource is Folder) {
var canonicalPath = resource.resolveSymbolicLinksSync().path;
if (visited.add(canonicalPath)) {
yield* _includedFilesInFolder(visited, resource, includedPath);
visited.remove(canonicalPath);
}
} else {
Type type = resource.runtimeType;
throw StateError('Unknown resource at path "$path" ($type)');
}
}
}
}
/// Return `true` if the given [path] is not excluded by one of the
/// [excludedPaths], or an applicable [excludedGlobs].
///
/// This method is invoked while processing an explicitly [includedPath],
/// and so we should ignore globs that would have excluded it.
bool _isExcluded(String path, String includedPath) {
Context context = resourceProvider.pathContext;
for (var current = path; root.contains(current);) {
if (context.basename(current).startsWith('.')) {
return true;
}
current = context.dirname(current);
}
for (String excludedPath in excludedPaths) {
if (context.isAbsolute(excludedPath)) {
if (path == excludedPath || context.isWithin(excludedPath, path)) {
return true;
}
} else {
// The documentation claims that [excludedPaths] only contains absolute
// paths, so we shouldn't be able to reach this point.
for (String includedPath in includedPaths) {
if (context.isWithin(
context.join(includedPath, excludedPath), path)) {
return true;
}
}
}
}
for (Glob pattern in excludedGlobs) {
if (!pattern.matches(includedPath) && pattern.matches(path)) {
return true;
}
}
return false;
}
}