gitGenerators top-level property
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,
),
};