execute method

  1. @override
Future<ToolResult> execute(
  1. Map<String, dynamic> input
)
override

Execute the tool with the given input.

Implementation

@override
Future<ToolResult> execute(Map<String, dynamic> input) async {
  final parsed = LspToolInput.fromJson(input);
  final absolutePath = _expandPath(parsed.filePath);

  // Wait for initialization.
  final manager = _manager;
  if (manager == null) {
    return ToolResult.success(
      'LSP server manager not initialized. This may indicate a startup issue.',
      metadata: LspToolOutput(
        operation: parsed.operation.name,
        result: 'LSP server manager not initialized.',
        filePath: parsed.filePath,
      ).toJson(),
    );
  }

  if (manager.initStatus == LspInitStatus.pending) {
    await manager.waitForInitialization();
  }

  try {
    // Ensure file is open.
    if (!manager.isFileOpen(absolutePath)) {
      final file = File(absolutePath);
      final stat = await file.stat();
      if (stat.size > maxLspFileSizeBytes) {
        final sizeMb = (stat.size / 1000000).ceil();
        return ToolResult.success(
          'File too large for LSP analysis (${sizeMb}MB exceeds 10MB limit)',
          metadata: LspToolOutput(
            operation: parsed.operation.name,
            result:
                'File too large for LSP analysis (${sizeMb}MB exceeds 10MB limit)',
            filePath: parsed.filePath,
          ).toJson(),
        );
      }
      final content = await file.readAsString();
      await manager.openFile(absolutePath, content);
    }

    // Build params.
    final uri = Uri.file(absolutePath).toString();
    final position = parsed.lspPosition;
    final method = parsed.operation.method;
    final params = _buildParams(parsed.operation, uri, position);

    // Send request.
    var result = await manager.sendRequest(absolutePath, method, params);

    if (result == null) {
      final ext = absolutePath.split('.').last;
      return ToolResult.success(
        'No LSP server available for file type: .$ext',
        metadata: LspToolOutput(
          operation: parsed.operation.name,
          result: 'No LSP server available for file type: .$ext',
          filePath: parsed.filePath,
        ).toJson(),
      );
    }

    // For call hierarchy, do the two-step process.
    if (parsed.operation == LspOperation.incomingCalls ||
        parsed.operation == LspOperation.outgoingCalls) {
      if (result is! List || result.isEmpty) {
        return ToolResult.success(
          'No call hierarchy item found at this position',
          metadata: LspToolOutput(
            operation: parsed.operation.name,
            result: 'No call hierarchy item found at this position',
            filePath: parsed.filePath,
            resultCount: 0,
            fileCount: 0,
          ).toJson(),
        );
      }

      final callMethod = parsed.operation == LspOperation.incomingCalls
          ? 'callHierarchy/incomingCalls'
          : 'callHierarchy/outgoingCalls';

      result = await manager.sendRequest(absolutePath, callMethod, {
        'item': result[0],
      });
    }

    // Format result string.
    final formatted = _formatResult(parsed.operation, result);
    return ToolResult.success(
      formatted,
      metadata: LspToolOutput(
        operation: parsed.operation.name,
        result: formatted,
        filePath: parsed.filePath,
      ).toJson(),
    );
  } catch (e) {
    final errorMsg = 'Error performing ${parsed.operation.name}: $e';
    return ToolResult.success(
      errorMsg,
      metadata: LspToolOutput(
        operation: parsed.operation.name,
        result: errorMsg,
        filePath: parsed.filePath,
      ).toJson(),
    );
  }
}