blob: 808a4f9524f8d326f3e31af79ead73255f334894 [file] [log] [blame]
// 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/file_system/file_system.dart';
import 'package:analyzer/src/context/builder.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer_cli/src/options.dart';
import 'package:path/path.dart' as path;
/// Cache of [AnalysisOptionsImpl] objects that correspond to directories
/// with analyzed files, used to reduce searching for `analysis_options.yaml`
/// files.
class ContextCache {
final ResourceProvider resourceProvider;
final CommandLineOptions clOptions;
final void Function(String) verbosePrint;
/// A mapping from normalized paths (currently, directories) to cache entries
/// which include builder, analysis_options paths, and [AnalysisOptionImpl]s.
final Map<String, ContextCacheEntry> _byDirectory = {};
ContextCache(this.resourceProvider, this.clOptions, this.verbosePrint);
/// Look up info about a context from the cache. You can pass in any [path],
/// and it will try to provide an existing [ContextCacheEntry] that is
/// suitable for that [path] if one exists.
ContextCacheEntry forSource(String path) {
path = _normalizeSourcePath(path);
return _byDirectory.putIfAbsent(path, () {
final builder = new ContextBuilder(resourceProvider, null, null,
options: clOptions.contextBuilderOptions);
return new ContextCacheEntry(builder, path, clOptions, verbosePrint);
});
}
/// Cheaply normalize source paths so we can increase cache performance.
/// Getting the location of an analysis_options.yaml file for a given source
/// can be expensive, so we want to reduce extra lookups where possible. We
/// know that two files in the same directory share an analysis options file,
/// so that's the normalization we perform currently. However, this could be
/// any form of performance-increasing cache key normalization.
String _normalizeSourcePath(String sourcePath) {
if (!resourceProvider.pathContext.isAbsolute(sourcePath)) {
// TODO(mfairhurst) Use resourceProvider.pathContext.absolute(). For the
// moment, we get an issues where pathContext.current is `.`, which causes
// pathContext.absolute() to produce `./foo` instead of `/absolute/foo`.
sourcePath = path.absolute(sourcePath);
}
sourcePath = resourceProvider.pathContext.normalize(sourcePath);
// Prepare the directory which is, or contains, the context root.
if (resourceProvider.getFolder(sourcePath).exists) {
return sourcePath;
}
return resourceProvider.pathContext.dirname(sourcePath);
}
}
/// Each entry of the [ContextCache] caches three things: the [ContextBuilder],
/// the analysis_options.yaml path of the context, and the [AnalysisOptionsImpl]
/// of the context.
class ContextCacheEntry {
final CommandLineOptions clOptions;
final ContextBuilder builder;
final String requestedSourceDirectory;
final void Function(String) verbosePrint;
AnalysisOptionsImpl _analysisOptions;
String _analysisRoot;
ContextCacheEntry(this.builder, this.requestedSourceDirectory, this.clOptions,
this.verbosePrint);
/// Get the fully parsed [AnalysisOptionsImpl] for this entry.
AnalysisOptionsImpl get analysisOptions =>
_analysisOptions ??= _getAnalysisOptions();
/// Find the root path from which excludes should be considered due to where
/// the analysis_options.yaml was defined.
String get analysisRoot => _analysisRoot ??= _getAnalysisRoot();
/// The actual calculation to get the [AnalysisOptionsImpl], with no caching.
/// This should not be used except behind the getter which caches this result
/// automatically.
AnalysisOptionsImpl _getAnalysisOptions() {
AnalysisOptionsImpl contextOptions = builder.getAnalysisOptions(
requestedSourceDirectory,
verbosePrint: clOptions.verbose ? verbosePrint : null)
as AnalysisOptionsImpl;
contextOptions.trackCacheDependencies = false;
contextOptions.disableCacheFlushing = clOptions.disableCacheFlushing;
contextOptions.enabledExperiments = clOptions.enabledExperiments;
contextOptions.hint = !clOptions.disableHints;
contextOptions.generateImplicitErrors = clOptions.showPackageWarnings;
contextOptions.generateSdkErrors = clOptions.showSdkWarnings;
contextOptions.useFastaParser = clOptions.useFastaParser;
return contextOptions;
}
/// The actual calculation to get the analysis root, with no caching. This
/// should not be used except behind the getter which caches this result
/// automatically.
String _getAnalysisRoot() {
// The analysis yaml defines the root, if it exists.
var analysisOptionsPath = builder
.getOptionsFile(requestedSourceDirectory, forceSearch: true)
?.path;
if (analysisOptionsPath == null) {
return requestedSourceDirectory;
}
return path.dirname(analysisOptionsPath);
}
}