gitGenerators top-level property

Map<String, FigGenerator> gitGenerators
final

Git generators map. Use in git spec as generators: gitGenerators'commits', etc.

Implementation

final Map<String, FigGenerator> gitGenerators = {
  'commits': FigGenerator(
    script: ['git', '--no-optional-locks', 'log', '--oneline'],
    postProcess: (out, [tokens]) {
      final output = filterMessages(out);
      if (output.startsWith('fatal:')) return [];
      return output.split('\n').map((line) {
        return FigSuggestion(
          name: line.length >= 7 ? line.substring(0, 7) : line,
          icon: 'fig://icon?type=node',
          description: line.length > 7 ? line.substring(7) : '',
        );
      }).toList();
    },
  ),
  'aliases': FigGenerator(
    script: [
      'git',
      '--no-optional-locks',
      'config',
      '--get-regexp',
      r'^alias.'
    ],
    cache: const FigCache(strategy: 'stale-while-revalidate'),
    postProcess: (out, [tokens]) {
      final suggestions = out.split('\n').map((aliasLine) {
        final rest = aliasLine.length > 6 ? aliasLine.substring(6) : '';
        final idx = rest.indexOf(' ');
        final name = idx != -1 ? rest.substring(0, idx) : rest;
        final value = idx != -1 ? rest.substring(idx + 1) : '';
        return FigSuggestion(
          name: name,
          description: "Alias for '$value'",
          icon: 'fig://icon?type=commandkey',
        );
      }).toList();
      final seen = <String>{};
      return suggestions.where((s) {
        final n = s.nameSingle ?? '';
        if (seen.contains(n)) return false;
        seen.add(n);
        return true;
      }).toList();
    },
  ),
  'revs': FigGenerator(
    script: ['git', 'rev-list', '--all', '--oneline'],
    postProcess: (out, [tokens]) {
      final output = filterMessages(out);
      if (output.startsWith('fatal:')) return [];
      return output.split('\n').map((line) {
        return FigSuggestion(
          name: line.length >= 7 ? line.substring(0, 7) : line,
          icon: 'fig://icon?type=node',
          description: line.length > 7 ? line.substring(7) : '',
        );
      }).toList();
    },
  ),
  'stashes': FigGenerator(
    script: ['git', '--no-optional-locks', 'stash', 'list'],
    postProcess: (out, [tokens]) {
      final output = filterMessages(out);
      if (output.startsWith('fatal:')) return [];
      return output.split('\n').map((line) {
        final parts = line.split(':');
        final insertValue = parts.isNotEmpty ? parts[0] : '';
        final name = parts.length > 2 ? parts.sublist(2).join(':') : line;
        return FigSuggestion(
          name: name,
          insertValue: insertValue,
          icon: 'fig://icon?type=node',
        );
      }).toList();
    },
  ),
  'treeish': FigGenerator(
    script: ['git', '--no-optional-locks', 'diff', '--cached', '--name-only'],
    postProcess: (out, [tokens]) {
      final output = filterMessages(out);
      if (output.startsWith('fatal:')) return [];
      final hasDoubleDash = tokens != null && tokens.contains('--');
      return output.split('\n').map((file) {
        return FigSuggestion(
          name: file,
          insertValue: (!hasDoubleDash ? '-- ' : '') + file,
          icon: 'fig://icon?type=file',
          description: 'Staged file',
        );
      }).toList();
    },
  ),
  'remoteLocalBranches': FigGenerator(
    script: [
      'git',
      '--no-optional-locks',
      'branch',
      '-a',
      '--no-color',
      '--sort=-committerdate',
    ],
    postProcess: postProcessBranches(
        const PostProcessBranchesOptions(insertWithoutRemotes: true)),
  ),
  'localBranches': FigGenerator(
    script: [
      'git',
      '--no-optional-locks',
      'branch',
      '--no-color',
      '--sort=-committerdate',
    ],
    postProcess: postProcessBranches(
        const PostProcessBranchesOptions(insertWithoutRemotes: true)),
  ),
  // TS has custom async that switches on -r; Dart uses local branches only.
  // For remote branches use remoteLocalBranches or a separate arg.
  'localOrRemoteBranches': FigGenerator(
    script: [
      'git',
      '--no-optional-locks',
      'branch',
      '--no-color',
      '--sort=-committerdate',
    ],
    postProcess: postProcessBranches(
        const PostProcessBranchesOptions(insertWithoutRemotes: true)),
  ),
  'remotes': FigGenerator(
    script: ['git', '--no-optional-locks', 'remote', '-v'],
    postProcess: (out, [tokens]) {
      final remoteURLs = <String, String>{};
      for (final line in out.split('\n')) {
        final pair = line.split('\t');
        if (pair.length >= 2) {
          final remote = pair[0];
          final url = pair[1].split(' ')[0];
          remoteURLs[remote] = url;
        }
      }
      return remoteURLs.keys.map((remote) {
        final url = remoteURLs[remote] ?? '';
        var icon = 'box';
        if (url.contains('github.com')) icon = 'github';
        if (url.contains('gitlab.com')) icon = 'gitlab';
        if (url.contains('heroku.com')) icon = 'heroku';
        return FigSuggestion(
          name: remote,
          icon: 'fig://icon?type=$icon',
          description: 'Remote',
        );
      }).toList();
    },
  ),
  'tags': FigGenerator(
    script: [
      'git',
      '--no-optional-locks',
      'tag',
      '--list',
      '--sort=-committerdate',
    ],
    postProcess: (out, [tokens]) {
      return out
          .split('\n')
          .map((tag) => FigSuggestion(name: tag, icon: '🏷️'))
          .toList();
    },
  ),
  'files_for_staging': FigGenerator(
    script: ['git', '--no-optional-locks', 'status', '--short'],
    postProcess: (out, [context]) {
      final output = filterMessages(out);
      if (output.startsWith('fatal:')) return [];

      var files = output.split('\n').map((line) {
        final alreadyAdded = line.isNotEmpty && ['M', 'A'].contains(line[0]);
        final trimmed = line.trim();
        final arr = trimmed.split(' ');
        return _StagingFile(
          working: arr.isNotEmpty ? arr[0] : '',
          file: arr.length > 1 ? arr.sublist(1).join(' ').trim() : '',
          alreadyAdded: alreadyAdded,
        );
      }).toList();

      final paths = output.split('\n').map((line) {
        final lastSlash = line.lastIndexOf('/');
        final segment =
            lastSlash >= 0 ? line.substring(0, lastSlash + 1).trim() : line;
        final arr = segment.split(' ');
        return arr.length > 1 ? arr.sublist(1).join(' ').trim() : '';
      }).toList();

      final dirArr = <String>[];
      if (paths.length >= 2) {
        var currentDir = paths[0];
        var count = 1;
        for (var i = 1; i < paths.length; i++) {
          if (paths[i].contains(currentDir) && i + 1 != paths.length) {
            count++;
          } else {
            if (count >= 2) dirArr.add(currentDir);
            count = 1;
          }
          currentDir = paths[i];
        }
      }

      final ctx = context ?? [];
      files = files.where((item) {
        final file = item.file.replaceAll(RegExp(r'^"|"$'), '');
        return !ctx.any((c) =>
            c == file ||
            (c.endsWith('*') &&
                file.startsWith(c.substring(0, c.length - 1))) ||
            (c.startsWith('*') && file.endsWith(c.substring(1))));
      }).toList();

      return [
        ...dirArr.map((name) => FigSuggestion(
              name: '$name*',
              description: 'Wildcard',
              icon: 'fig://icon?type=asterisk',
            )),
        ...files.map((item) {
          final file = item.file.replaceAll(RegExp(r'^"|"$'), '');
          var ext = '';
          try {
            final parts = file.split('.');
            ext = parts.isEmpty ? '' : parts.last;
          } catch (_) {}
          if (file.endsWith('/')) ext = 'folder';
          final priority = item.alreadyAdded ? 50 : 100;
          return FigSuggestion(
            name: file,
            icon: 'fig://icon?type=$ext&color=ff0000&badge=${item.working}',
            description: 'Changed file',
            priority: priority,
          );
        }),
      ];
    },
  ),
  'getStagedFiles': FigGenerator(
    script: [
      'bash',
      '-c',
      "git --no-optional-locks status --short | sed -ne '/^M /p' -e '/A /p'",
    ],
    postProcess: postProcessTrackedFiles,
  ),
  'getUnstagedFiles': FigGenerator(
    script: ['git', '--no-optional-locks', 'diff', '--name-only'],
    splitOn: '\n',
  ),
  // TS script depends on context (--staged/--cached); Dart uses unstaged script.
  'getChangedTrackedFiles': FigGenerator(
    script: [
      'bash',
      '-c',
      "git --no-optional-locks status --short | sed -ne '/M /p' -e '/A /p'",
    ],
    postProcess: postProcessTrackedFiles,
  ),
};