analyze method

Implementation

Future<CockpitAnalyzeFilesResult> analyze(
  CockpitAnalyzeFilesRequest request,
) async {
  if (request.paths.isEmpty) {
    throw const CockpitApplicationServiceException(
      code: 'analysisPathsRequired',
      message: 'paths must contain at least one file or directory.',
    );
  }

  final workspaceRoot = assertWorkspaceRootAllowed(
    request.workspaceRoot,
    request.allowedRoots,
    pathContext: _fileSystem.pathContext,
  );
  final resolvedPaths = request.paths
      .map((path) => _resolveWorkspacePath(workspaceRoot, path))
      .toList(growable: false);
  final relativePaths = resolvedPaths
      .map(
        (path) => _fileSystem.pathContext.relative(path, from: workspaceRoot),
      )
      .toList(growable: false);
  final toolchain = detectWorkspaceToolchain(_fileSystem, workspaceRoot);
  final executable = toolchain == CockpitWorkspaceToolchain.flutter
      ? _sdkEnvironment.flutterExecutable
      : _sdkEnvironment.dartExecutable;
  final arguments = <String>[
    'analyze',
    if (toolchain == CockpitWorkspaceToolchain.flutter) '--no-pub',
    ...relativePaths,
  ];
  final commandResult = await runWorkspaceProcess(
    processManager: _processManager,
    executable: executable,
    arguments: arguments,
    workingDirectory: workspaceRoot,
    timeout: request.timeout,
  );
  final stdout = commandResult.stdout;
  final stderr = commandResult.stderr;
  final allDiagnostics = _diagnosticsFromAnalyzerOutput(
    stdout: stdout,
    stderr: stderr,
    workspaceRoot: workspaceRoot,
    pathContext: _fileSystem.pathContext,
  );
  if (!commandResult.success && allDiagnostics.isEmpty) {
    throw CockpitApplicationServiceException(
      code: 'analyzerDiagnosticsUnavailable',
      message: 'Analyzer failed without parsable diagnostics.',
      details: <String, Object?>{
        'command': commandResult.command.toJson(),
        'exitCode': commandResult.exitCode,
        'stdoutPreview': _preview(stdout, maxChars: 400),
        'stderrPreview': _preview(stderr, maxChars: 400),
      },
    );
  }
  final displayedDiagnostics = allDiagnostics
      .take(request.maxDiagnostics)
      .toList(growable: false);
  final severityCounts = <String, int>{};
  for (final diagnostic in allDiagnostics) {
    severityCounts.update(
      diagnostic.severity,
      (value) => value + 1,
      ifAbsent: () => 1,
    );
  }
  final stdoutExcerpt = _analyzerExcerpt(
    stdout,
    maxChars: request.maxOutputChars,
  );
  final stderrExcerpt = _analyzerExcerpt(
    stderr,
    maxChars: request.maxOutputChars,
  );
  return CockpitAnalyzeFilesResult(
    workspaceRoot: workspaceRoot,
    toolchain: toolchain,
    paths: relativePaths,
    command: commandResult.command,
    exitCode: commandResult.exitCode,
    success: commandResult.success,
    clean: allDiagnostics.isEmpty,
    summary: _analysisSummary(allDiagnostics, severityCounts),
    totalDiagnostics: allDiagnostics.length,
    diagnostics: displayedDiagnostics,
    diagnosticsTruncated: allDiagnostics.length > displayedDiagnostics.length,
    severityCounts: Map<String, int>.unmodifiable(severityCounts),
    stdoutPreview: stdoutExcerpt.preview,
    stdoutTruncated: stdoutExcerpt.truncated,
    stderrPreview: stderrExcerpt.preview,
    stderrTruncated: stderrExcerpt.truncated,
  );
}