start method
Loads plugin configuration (enabled rules, severity overrides, etc.) from analysis_options / SAROPA env vars before rules are registered.
Logs the start event via PluginLogger — the entry will be buffered
in memory until loadNativePluginConfigFromProjectRoot runs and
PluginLogger.setProjectRoot is called on the first analyzed file, then
flushed to reports/.saropa_lints/plugin.log so users have a visible
surface confirming the plugin actually started.
Implementation
@override
FutureOr<void> start() {
PluginLogger.log('Plugin.start() — loading initial config');
// Arm the rapid-edit gate: this runs ONLY inside the interactive analysis
// server (no bin/ CLI instantiates the plugin), so it is the reliable signal
// that in-flux relief is safe. Batch runners (scan/baseline/health, `dart
// analyze`) leave this false and therefore report every rule at full
// fidelity. See SaropaLintRule.deferForRapidEdit / isAnalysisServer.
SaropaLintRule.isAnalysisServer = true;
// Mark the plugin as started BEFORE the cwd check so the essential-tier
// default applies on the lazy config reload from the real project root.
markNativePluginStarted();
// Skip initial config load when cwd is not a Dart project (e.g. when the
// analysis server sets cwd to the VS Code install dir). The real config
// will load from the project root on the first analyzed file via
// SaropaContext._ensureConfigLoadedFromProjectRoot(). Loading from a
// non-project cwd produces a noisy 0-rules phase in the log.
final sep = Platform.pathSeparator;
final cwdHasPubspec = File(
'${Directory.current.path}${sep}pubspec.yaml',
).existsSync();
if (cwdHasPubspec) {
try {
loadNativePluginConfig();
} on Object catch (e, st) {
PluginLogger.error(
'loadNativePluginConfig failed in Plugin.start()',
error: e,
stackTrace: st,
);
// Defensive: plugin still registers with defaults
}
} else {
PluginLogger.debug(
'Plugin.start() — cwd is not a Dart project '
'(${Directory.current.path}), deferring config to project root',
);
}
// Arm the memory-relief subsystem. Before this call,
// initializeCacheManagement() was defined but never invoked anywhere, so
// MemoryPressureHandler stayed disabled: none of the plugin's own caches
// (compilation-unit, file-content, metrics, source-location, semantic
// tokens, import-graph, string interner, …) were ever registered for
// eviction, and auto-relief never armed. They therefore grew for the
// entire analysis-server process lifetime. Wiring it here bounds the
// plugin's OWN footprint and sheds it under pressure.
//
// Scope honesty: this caps the plugin's caches only (sub-GB on a large
// project). It does NOT bound the analyzer's resolved element/AST model,
// which is the dominant cost when many element-resolving rules run over a
// large codebase under strict modes — that is reduced by doing less
// resolution, not by cache relief.
try {
initializeCacheManagement();
// initializeCacheManagement registers the project_context caches but not
// the report-layer ImportGraphTracker, which holds a per-file set of
// import/export URIs for every analyzed file and is never evicted across
// the server lifetime. Register it for pressure relief at clear-late
// priority (rebuilding the graph requires re-walking files, so shed it
// only after cheaper caches). Non-destructive in normal operation — it
// is cleared only when the memory estimate crosses the threshold, and
// repopulates as files are re-analyzed.
MemoryPressureHandler.registerCache(
'importGraphTracker',
ImportGraphTracker.reset,
priority: 85,
);
// Register saropa_lint_rule.dart caches (separate library, not
// reachable from initializeCacheManagement's project_context library).
//
// NOTE: ProgressTracker, ImpactTracker, ReportWriter, and
// SuppressionTracker are NOT registered — they are forward-accumulating
// session counters (issue counts, severity totals, suppression records),
// not recomputable caches. Clearing them mid-session silently truncates
// the analysis summary.
//
// NOTE: BaselineManager is NOT registered — its reset() nulls _config,
// permanently disabling baseline suppression with no lazy re-init path.
MemoryPressureHandler.registerCache(
'ruleTimingTracker',
RuleTimingTracker.reset,
priority: 55,
);
// Baseline date cache and config caches are recomputable.
MemoryPressureHandler.registerCache(
'baselineDate',
BaselineDate.clearCache,
priority: 55,
);
MemoryPressureHandler.registerCache(
'runtimeTierCap',
RuntimeTierCap.clearCache,
priority: 60,
);
} on Object catch (e, st) {
PluginLogger.error(
'initializeCacheManagement failed in Plugin.start()',
error: e,
stackTrace: st,
);
}
}