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

Side by Side Diff: pkg/analysis_server/lib/src/context_directory_manager.dart

Issue 450643005: Churn-only CL for ContextDirectoryManager. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/analysis_server/test/context_directory_manager_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 context.directory.manager; 5 library context.directory.manager;
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/package_map_provider.dart';
10 import 'package:analyzer/file_system/file_system.dart'; 11 import 'package:analyzer/file_system/file_system.dart';
11 import 'package:analysis_server/src/package_map_provider.dart';
12 import 'package:analyzer/src/generated/engine.dart'; 12 import 'package:analyzer/src/generated/engine.dart';
13 import 'package:analyzer/src/generated/source.dart'; 13 import 'package:analyzer/src/generated/source.dart';
14 import 'package:watcher/watcher.dart'; 14 import 'package:watcher/watcher.dart';
15 15
16 /** 16 /**
17 * Information tracked by the [ContextDirectoryManager] for each context.
18 */
19 class _ContextDirectoryInfo {
20 /**
21 * Stream subscription we are using to watch the context's directory for
22 * changes.
23 */
24 StreamSubscription<WatchEvent> changeSubscription;
25
26 /**
27 * Map from full path to the [Source] object, for each source that has been
28 * added to the context.
29 */
30 Map<String, Source> sources = new HashMap<String, Source>();
31
32 /**
33 * Dependencies of the context's package map. If any of these files changes,
34 * the package map needs to be recomputed.
35 */
36 Set<String> packageMapDependencies;
37 }
38
39 /**
40 * Class that maintains a mapping from included/excluded paths to a set of 17 * Class that maintains a mapping from included/excluded paths to a set of
41 * folders that should correspond to analysis contexts. 18 * folders that should correspond to analysis contexts.
42 */ 19 */
43 abstract class ContextDirectoryManager { 20 abstract class ContextDirectoryManager {
44 /** 21 /**
45 * File name of pubspec files. 22 * File name of pubspec files.
46 */ 23 */
47 static const String PUBSPEC_NAME = 'pubspec.yaml'; 24 static const String PUBSPEC_NAME = 'pubspec.yaml';
48 25
49 /** 26 /**
(...skipping 10 matching lines...) Expand all
60 37
61 /** 38 /**
62 * Provider which is used to determine the mapping from package name to 39 * Provider which is used to determine the mapping from package name to
63 * package folder. 40 * package folder.
64 */ 41 */
65 final PackageMapProvider packageMapProvider; 42 final PackageMapProvider packageMapProvider;
66 43
67 ContextDirectoryManager(this.resourceProvider, this.packageMapProvider); 44 ContextDirectoryManager(this.resourceProvider, this.packageMapProvider);
68 45
69 /** 46 /**
47 * Called when a new context needs to be created.
48 */
49 void addContext(Folder folder, Map<String, List<Folder>> packageMap);
50
51 /**
52 * Called when the set of files associated with a context have changed (or
53 * some of those files have been modified). [changeSet] is the set of
54 * changes that need to be applied to the context.
55 */
56 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet);
57
58 /**
59 * Returns `true` if the given absolute [path] is in one of the current
60 * root folders and is not excluded.
61 */
62 bool isInAnalysisRoot(String path) {
63 // TODO(scheglov) check for excluded paths
64 for (Folder root in _currentDirectoryInfo.keys) {
65 if (root.contains(path)) {
66 return true;
67 }
68 }
69 return false;
70 }
71
72 /**
73 * Remove the context associated with the given [folder].
74 */
75 void removeContext(Folder folder);
76
77 /**
70 * Change the set of paths which should be used as starting points to 78 * Change the set of paths which should be used as starting points to
71 * determine the context directories. 79 * determine the context directories.
72 */ 80 */
73 void setRoots(List<String> includedPaths, 81 void setRoots(List<String> includedPaths, List<String> excludedPaths) {
74 List<String> excludedPaths) {
75 // included 82 // included
76 Set<Folder> includedFolders = new HashSet<Folder>(); 83 Set<Folder> includedFolders = new HashSet<Folder>();
77 for (int i = 0; i < includedPaths.length; i++) { 84 for (int i = 0; i < includedPaths.length; i++) {
78 String path = includedPaths[i]; 85 String path = includedPaths[i];
79 Resource resource = resourceProvider.getResource(path); 86 Resource resource = resourceProvider.getResource(path);
80 if (resource is Folder) { 87 if (resource is Folder) {
81 includedFolders.add(resource); 88 includedFolders.add(resource);
82 } else { 89 } else {
83 // TODO(scheglov) implemented separate files analysis 90 // TODO(scheglov) implemented separate files analysis
84 throw new UnimplementedError( 91 throw new UnimplementedError(
85 '$path is not a folder. ' 92 '$path is not a folder. '
86 'Only support for folder analysis is implemented currently.'); 93 'Only support for folder analysis is implemented currently.');
87 } 94 }
88 } 95 }
89 // excluded 96 // excluded
90 // TODO(scheglov) remove when implemented 97 // TODO(scheglov) remove when implemented
91 if (excludedPaths.isNotEmpty) { 98 if (excludedPaths.isNotEmpty) {
92 throw new UnimplementedError( 99 throw new UnimplementedError('Excluded paths are not supported yet');
93 'Excluded paths are not supported yet');
94 } 100 }
95 Set<Folder> excludedFolders = new HashSet<Folder>(); 101 Set<Folder> excludedFolders = new HashSet<Folder>();
96 // diff 102 // diff
97 Set<Folder> currentFolders = _currentDirectoryInfo.keys.toSet(); 103 Set<Folder> currentFolders = _currentDirectoryInfo.keys.toSet();
98 Set<Folder> newFolders = includedFolders.difference(currentFolders); 104 Set<Folder> newFolders = includedFolders.difference(currentFolders);
99 Set<Folder> oldFolders = currentFolders.difference(includedFolders); 105 Set<Folder> oldFolders = currentFolders.difference(includedFolders);
100 // destroy old contexts 106 // destroy old contexts
101 for (Folder folder in oldFolders) { 107 for (Folder folder in oldFolders) {
102 _destroyContext(folder); 108 _destroyContext(folder);
103 } 109 }
104 // create new contexts 110 // create new contexts
105 for (Folder folder in newFolders) { 111 for (Folder folder in newFolders) {
106 _createContext(folder); 112 _createContext(folder);
107 } 113 }
108 } 114 }
109 115
110 /** 116 /**
117 * Called when the package map for a context has changed.
118 */
119 void updateContextPackageMap(Folder contextFolder, Map<String,
120 List<Folder>> packageMap);
121
122 /**
111 * Create a new context associated with the given folder. 123 * Create a new context associated with the given folder.
112 */ 124 */
113 void _createContext(Folder folder) { 125 void _createContext(Folder folder) {
114 _ContextDirectoryInfo info = new _ContextDirectoryInfo(); 126 _ContextDirectoryInfo info = new _ContextDirectoryInfo();
115 _currentDirectoryInfo[folder] = info; 127 _currentDirectoryInfo[folder] = info;
116 info.changeSubscription = folder.changes.listen((WatchEvent event) { 128 info.changeSubscription = folder.changes.listen((WatchEvent event) {
117 _handleWatchEvent(folder, info, event); 129 _handleWatchEvent(folder, info, event);
118 }); 130 });
119 File pubspecFile = folder.getChild(PUBSPEC_NAME); 131 File pubspecFile = folder.getChild(PUBSPEC_NAME);
120 PackageMapInfo packageMapInfo = packageMapProvider.computePackageMap(folder) ; 132 PackageMapInfo packageMapInfo =
133 packageMapProvider.computePackageMap(folder);
121 info.packageMapDependencies = packageMapInfo.dependencies; 134 info.packageMapDependencies = packageMapInfo.dependencies;
122 // TODO(paulberry): if any of the dependencies is outside of [folder], 135 // TODO(paulberry): if any of the dependencies is outside of [folder],
123 // we'll need to watch their parent folders as well. 136 // we'll need to watch their parent folders as well.
124 addContext(folder, packageMapInfo.packageMap); 137 addContext(folder, packageMapInfo.packageMap);
125 ChangeSet changeSet = new ChangeSet(); 138 ChangeSet changeSet = new ChangeSet();
126 _addSourceFiles(changeSet, folder, info); 139 _addSourceFiles(changeSet, folder, info);
127 applyChangesToContext(folder, changeSet); 140 applyChangesToContext(folder, changeSet);
128 } 141 }
129 142
130 /** 143 /**
131 * Clean up and destroy the context associated with the given folder. 144 * Clean up and destroy the context associated with the given folder.
132 */ 145 */
133 void _destroyContext(Folder folder) { 146 void _destroyContext(Folder folder) {
134 _currentDirectoryInfo[folder].changeSubscription.cancel(); 147 _currentDirectoryInfo[folder].changeSubscription.cancel();
135 _currentDirectoryInfo.remove(folder); 148 _currentDirectoryInfo.remove(folder);
136 removeContext(folder); 149 removeContext(folder);
137 } 150 }
138 151
139 void _handleWatchEvent(Folder folder, _ContextDirectoryInfo info, WatchEvent e vent) { 152 void _handleWatchEvent(Folder folder, _ContextDirectoryInfo info,
153 WatchEvent event) {
140 switch (event.type) { 154 switch (event.type) {
141 case ChangeType.ADD: 155 case ChangeType.ADD:
142 if (_isInPackagesDir(event.path, folder)) { 156 if (_isInPackagesDir(event.path, folder)) {
143 // TODO(paulberry): perhaps we should only skip packages dirs if 157 // TODO(paulberry): perhaps we should only skip packages dirs if
144 // there is a pubspec.yaml? 158 // there is a pubspec.yaml?
145 break; 159 break;
146 } 160 }
147 Resource resource = resourceProvider.getResource(event.path); 161 Resource resource = resourceProvider.getResource(event.path);
148 // If the file went away and was replaced by a folder before we 162 // If the file went away and was replaced by a folder before we
149 // had a chance to process the event, resource might be a Folder. In 163 // had a chance to process the event, resource might be a Folder. In
150 // that case don't add it. 164 // that case don't add it.
151 if (resource is File) { 165 if (resource is File) {
152 File file = resource; 166 File file = resource;
153 if (_shouldFileBeAnalyzed(file)) { 167 if (_shouldFileBeAnalyzed(file)) {
154 ChangeSet changeSet = new ChangeSet(); 168 ChangeSet changeSet = new ChangeSet();
155 Source source = file.createSource(); 169 Source source = file.createSource();
156 changeSet.addedSource(source); 170 changeSet.addedSource(source);
157 applyChangesToContext(folder, changeSet); 171 applyChangesToContext(folder, changeSet);
158 info.sources[event.path]= source; 172 info.sources[event.path] = source;
159 } 173 }
160 } 174 }
161 break; 175 break;
162 case ChangeType.REMOVE: 176 case ChangeType.REMOVE:
163 Source source = info.sources[event.path]; 177 Source source = info.sources[event.path];
164 if (source != null) { 178 if (source != null) {
165 ChangeSet changeSet = new ChangeSet(); 179 ChangeSet changeSet = new ChangeSet();
166 changeSet.removedSource(source); 180 changeSet.removedSource(source);
167 applyChangesToContext(folder, changeSet); 181 applyChangesToContext(folder, changeSet);
168 info.sources.remove(event.path); 182 info.sources.remove(event.path);
169 } 183 }
170 break; 184 break;
171 case ChangeType.MODIFY: 185 case ChangeType.MODIFY:
172 Source source = info.sources[event.path]; 186 Source source = info.sources[event.path];
173 if (source != null) { 187 if (source != null) {
174 ChangeSet changeSet = new ChangeSet(); 188 ChangeSet changeSet = new ChangeSet();
175 changeSet.changedSource(source); 189 changeSet.changedSource(source);
176 applyChangesToContext(folder, changeSet); 190 applyChangesToContext(folder, changeSet);
177 } 191 }
178 break; 192 break;
179 } 193 }
180 194
181 if (info.packageMapDependencies.contains(event.path)) { 195 if (info.packageMapDependencies.contains(event.path)) {
182 // TODO(paulberry): when computePackageMap is changed into an 196 // TODO(paulberry): when computePackageMap is changed into an
183 // asynchronous API call, we'll want to suspend analysis for this context 197 // asynchronous API call, we'll want to suspend analysis for this context
184 // while we're rerunning "pub list", since any analysis we complete while 198 // while we're rerunning "pub list", since any analysis we complete while
185 // "pub list" is in progress is just going to get thrown away anyhow. 199 // "pub list" is in progress is just going to get thrown away anyhow.
186 PackageMapInfo packageMapInfo = packageMapProvider.computePackageMap(folde r); 200 PackageMapInfo packageMapInfo =
201 packageMapProvider.computePackageMap(folder);
187 info.packageMapDependencies = packageMapInfo.dependencies; 202 info.packageMapDependencies = packageMapInfo.dependencies;
188 updateContextPackageMap(folder, packageMapInfo.packageMap); 203 updateContextPackageMap(folder, packageMapInfo.packageMap);
189 } 204 }
190 } 205 }
191 206
192 /** 207 /**
193 * Determine if the path from [folder] to [path] contains a 'packages' 208 * Determine if the path from [folder] to [path] contains a 'packages'
194 * directory. 209 * directory.
195 */ 210 */
196 bool _isInPackagesDir(String path, Folder folder) { 211 bool _isInPackagesDir(String path, Folder folder) {
197 String relativePath = resourceProvider.pathContext.relative(path, from: fold er.path); 212 String relativePath =
213 resourceProvider.pathContext.relative(path, from: folder.path);
198 List<String> pathParts = resourceProvider.pathContext.split(relativePath); 214 List<String> pathParts = resourceProvider.pathContext.split(relativePath);
199 for (int i = 0; i < pathParts.length - 1; i++) { 215 for (int i = 0; i < pathParts.length - 1; i++) {
200 if (pathParts[i] == 'packages') { 216 if (pathParts[i] == 'packages') {
201 return true; 217 return true;
202 } 218 }
203 } 219 }
204 return false; 220 return false;
205 } 221 }
206 222
207 /** 223 /**
208 * Resursively adds all Dart and HTML files to the [changeSet]. 224 * Resursively adds all Dart and HTML files to the [changeSet].
209 */ 225 */
210 static void _addSourceFiles(ChangeSet changeSet, Folder folder, _ContextDirect oryInfo info) { 226 static void _addSourceFiles(ChangeSet changeSet, Folder folder,
227 _ContextDirectoryInfo info) {
211 List<Resource> children = folder.getChildren(); 228 List<Resource> children = folder.getChildren();
212 for (Resource child in children) { 229 for (Resource child in children) {
213 if (child is File) { 230 if (child is File) {
214 if (_shouldFileBeAnalyzed(child)) { 231 if (_shouldFileBeAnalyzed(child)) {
215 Source source = child.createSource(); 232 Source source = child.createSource();
216 changeSet.addedSource(source); 233 changeSet.addedSource(source);
217 info.sources[child.path] = source; 234 info.sources[child.path] = source;
218 } 235 }
219 } else if (child is Folder) { 236 } else if (child is Folder) {
220 if (child.shortName == 'packages') { 237 if (child.shortName == 'packages') {
221 // TODO(paulberry): perhaps we should only skip packages dirs if 238 // TODO(paulberry): perhaps we should only skip packages dirs if
222 // there is a pubspec.yaml? 239 // there is a pubspec.yaml?
223 continue; 240 continue;
224 } 241 }
225 _addSourceFiles(changeSet, child, info); 242 _addSourceFiles(changeSet, child, info);
226 } 243 }
227 } 244 }
228 } 245 }
229 246
230 static bool _shouldFileBeAnalyzed(File file) { 247 static bool _shouldFileBeAnalyzed(File file) {
231 if (!(AnalysisEngine.isDartFileName(file.path) 248 if (!(AnalysisEngine.isDartFileName(file.path) ||
232 || AnalysisEngine.isHtmlFileName(file.path))) { 249 AnalysisEngine.isHtmlFileName(file.path))) {
233 return false; 250 return false;
234 } 251 }
235 // Emacs creates dummy links to track the fact that a file is open for 252 // Emacs creates dummy links to track the fact that a file is open for
236 // editing and has unsaved changes (e.g. having unsaved changes to 253 // editing and has unsaved changes (e.g. having unsaved changes to
237 // 'foo.dart' causes a link '.#foo.dart' to be created, which points to the 254 // 'foo.dart' causes a link '.#foo.dart' to be created, which points to the
238 // non-existent file 'username@hostname.pid'. To avoid these dummy links 255 // non-existent file 'username@hostname.pid'. To avoid these dummy links
239 // causing the analyzer to thrash, just ignore links to non-existent files. 256 // causing the analyzer to thrash, just ignore links to non-existent files.
240 return file.exists; 257 return file.exists;
241 } 258 }
259 }
260
261 /**
262 * Information tracked by the [ContextDirectoryManager] for each context.
263 */
264 class _ContextDirectoryInfo {
265 /**
266 * Stream subscription we are using to watch the context's directory for
267 * changes.
268 */
269 StreamSubscription<WatchEvent> changeSubscription;
242 270
243 /** 271 /**
244 * Returns `true` if the given absolute [path] is in one of the current 272 * Map from full path to the [Source] object, for each source that has been
245 * root folders and is not excluded. 273 * added to the context.
246 */ 274 */
247 bool isInAnalysisRoot(String path) { 275 Map<String, Source> sources = new HashMap<String, Source>();
248 // TODO(scheglov) check for excluded paths
249 for (Folder root in _currentDirectoryInfo.keys) {
250 if (root.contains(path)) {
251 return true;
252 }
253 }
254 return false;
255 }
256 276
257 /** 277 /**
258 * Called when a new context needs to be created. 278 * Dependencies of the context's package map.
279 * If any of these files changes, the package map needs to be recomputed.
259 */ 280 */
260 void addContext(Folder folder, Map<String, List<Folder>> packageMap); 281 Set<String> packageMapDependencies;
261
262 /**
263 * Called when the set of files associated with a context have changed (or
264 * some of those files have been modified). [changeSet] is the set of
265 * changes that need to be applied to the context.
266 */
267 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet);
268
269 /**
270 * Remove the context associated with the given [folder].
271 */
272 void removeContext(Folder folder);
273
274 /**
275 * Called when the package map for a context has changed.
276 */
277 void updateContextPackageMap(Folder contextFolder,
278 Map<String, List<Folder>> packageMap);
279 } 282 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/test/context_directory_manager_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698