searchGenerator top-level property

FigGenerator searchGenerator
final

Implementation

final FigGenerator searchGenerator = FigGenerator(
  trigger: (oldTokens, newTokens) {
    // Basic trigger logic: trigger if @ count changes
    // Simplified for Dart as dynamic trigger function is tricky,
    // but the spec supports 'trigger' string.
    // For now we will rely on default behavior or implement custom trigger if needed.
    // The TS code uses a function to detect @ changes.
    return '@';
  },
  getQueryTerm: '@',
  custom: (tokens, executeShellCommand, context) async {
    if (executeShellCommand == null || context == null) return [];

    // Intl number format simple implementation
    String formatNumber(int n) {
      if (n >= 1000000) return '${(n / 1000000).toStringAsFixed(1)}M';
      if (n >= 1000) return '${(n / 1000).toStringAsFixed(1)}K';
      return n.toString();
    }

    // Last token from context logic is tricky because tokens passed here are usually the full line
    // but 'context' in TS had specific meaning.
    // Here `tokens` are usually the arguments.
    // Let's assume the last token is what we are typing.
    // In Fig, `context` argument to script is an array of tokens.
    // In Dart spec, `tokens` is the array of tokens.

    if (tokens.isEmpty) return [];
    final lastToken = tokens.last;

    if (lastToken.contains('@') && !lastToken.startsWith('@')) {
      final parts = lastToken.split('@');
      final crate = parts[0];
      // final version = parts.length > 1 ? parts[1] : '';

      final query = Uri.encodeComponent(crate);
      final output = await executeShellCommand(ExecuteCommandInput(
        command: 'curl',
        args: ['-sfL', 'https://crates.io/api/v1/crates/$query/versions'],
      ));

      try {
        final json = jsonDecode(output.stdout);
        final versions =
            (json['versions'] as List).map((e) => Version.fromJson(e)).toList();

        return versions
            .map((version) => FigSuggestion(
                  name: '$crate@${version.num}',
                  insertValue: version.num,
                  description:
                      '${formatNumber(version.downloads)} downloads - ${version.createdAt.split('T')[0]}',
                  hidden: version.yanked,
                ))
            .toList();
      } catch (e) {
        return [];
      }
    } else if (lastToken.isNotEmpty) {
      final query = Uri.encodeComponent(lastToken);

      // Parallel execution
      final results = await Future.wait<ExecuteCommandOutput>([
        executeShellCommand(ExecuteCommandInput(
          command: 'curl',
          args: [
            '-sfL',
            'https://crates.io/api/v1/crates?q=$query&per_page=60',
          ],
        )),
        executeShellCommand(ExecuteCommandInput(
          command: 'cargo',
          args: ['metadata', '--format-version', '1', '--no-deps'],
        )),
      ]);

      final remoteStdout = results[0].stdout;
      final localStdout = results[1].stdout;

      List<FigSuggestion> remoteSuggestions = [];
      try {
        final remoteJson = jsonDecode(remoteStdout);
        final crates = (remoteJson['crates'] as List)
            .map((e) => Crate.fromJson(e))
            .toList();

        crates.sort((a, b) => b.recentDownloads.compareTo(a.recentDownloads));

        remoteSuggestions = crates
            .map((crate) => FigSuggestion(
                  icon: '📦',
                  displayName: '${crate.name}@${crate.newestVersion}',
                  name: crate.name,
                  description:
                      '${formatNumber(crate.recentDownloads)}${crate.description != null ? ' - ${crate.description}' : ''}',
                ))
            .toList();
      } catch (e) {
        // Ignore parsing errors
      }

      List<FigSuggestion> localSuggestions = [];
      if (localStdout.trim().isNotEmpty) {
        try {
          final localJson = Metadata.fromJson(jsonDecode(localStdout));
          localSuggestions = localJson.packages
              .where((pkg) => pkg.source == null)
              .map((pkg) => FigSuggestion(
                    icon: '📦',
                    displayName: '${pkg.name}@${pkg.version}',
                    name: pkg.name,
                    description:
                        'Local Crate ${pkg.version}${pkg.description != null ? ' - ${pkg.description}' : ''}',
                  ))
              .toList();
        } catch (e) {
          // Ignore
        }
      }

      return [...remoteSuggestions, ...localSuggestions];
    } else {
      return [];
    }
  },
);