Skip to content

Commit 3cd3dd4

Browse files
authored
Cargo completions (#274)
1 parent 7b59fdb commit 3cd3dd4

3 files changed

Lines changed: 316 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- CLI completions for `cargo`: subcommands (including installed third-party
13+
ones via `cargo --list`), per-subcommand options, and dynamic values for
14+
`--target`, `--features`, `-p`/`--package`, `--bin`/`--example`/`--test`/`--bench`
15+
target names, and installed crates for `cargo uninstall`.
1216
- Match arms may list several literals in a row, matching if the subject equals
1317
any of them (OR), e.g. `'-h' '--help' : ...`. All alternatives in one arm
1418
must be the same literal kind: all strings, all integers, or all paths.

doc/mshell.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,9 @@ def mshCompletion { 'complete': ['msh' 'mshell'] } ([str] -- [str])
352352
end
353353
```
354354

355-
The standard library includes `__gitCompletion` for git argument completion.
355+
The standard library includes completion definitions for
356+
`git`, `fd`, `ssh`, `rg`, `sudo`, `systemctl`, `journalctl`, `cargo`,
357+
and `msh` itself (e.g. `__gitCompletion`, `__cargoCompletion`).
356358

357359
### Shell completions
358360

lib/std.msh

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,4 +1103,313 @@ def __journalctlCompletion { 'complete': ['journalctl'] } ([str] -- [str])
11031103
end
11041104
end
11051105
# }}}
1106+
# cargo {{{
1107+
# Options valid both at the top level and after any subcommand.
1108+
def __cargoCommonOptions ( -- [str])
1109+
['-h' '--help' '-V' '--version' '-v' '--verbose' '-q' '--quiet'
1110+
'--color' '--config' '-Z' '--frozen' '--locked' '--offline']
1111+
end
1112+
1113+
# Parse `cargo --list` for the available subcommands (includes installed third-party ones).
1114+
def __cargoSubcommands ( -- [str])
1115+
['cargo' '--list'] * ^ ; drop lines
1116+
(' ' startsWith) filter
1117+
(trim) map
1118+
(len 0 >) filter
1119+
(wsplit :0:) map
1120+
end
1121+
1122+
# Installed targets from rustup if available, otherwise all targets known to rustc.
1123+
def __cargoTargets ( -- [str])
1124+
['rustup' 'target' 'list'] * ^ ? code! drop out!
1125+
@code 0 = if
1126+
@out lines ('(installed)' in) filter (wsplit :0:) map
1127+
else
1128+
['rustc' '--print' 'target-list'] * ^ ; drop lines (len 0 >) filter
1129+
end
1130+
end
1131+
1132+
# Feature names from the current crate's manifest.
1133+
def __cargoFeatures ( -- [str])
1134+
['cargo' 'read-manifest'] * ^ ; drop out!
1135+
@out trim len 0 > if
1136+
@out parseJson match
1137+
dict d : @d 'features' {} getDef match
1138+
dict features : @features keys,
1139+
_ : [] as [str],
1140+
end,
1141+
_ : [] as [str],
1142+
end
1143+
else
1144+
[] as [str]
1145+
end
1146+
end
1147+
1148+
# Workspace package names from cargo metadata.
1149+
def __cargoPackages ( -- [str])
1150+
['cargo' 'metadata' '--no-deps' '--format-version' '1'] * ^ ; drop out!
1151+
@out trim len 0 > if
1152+
@out parseJson match
1153+
dict d : @d 'packages' [] getDef match
1154+
list packages :
1155+
@packages
1156+
(pkg! @pkg match
1157+
dict p : @p 'name' '' getDef match str name : @name, _ : '', end,
1158+
_ : '',
1159+
end) map
1160+
(len 0 >) filter,
1161+
_ : [] as [str],
1162+
end,
1163+
_ : [] as [str],
1164+
end
1165+
else
1166+
[] as [str]
1167+
end
1168+
end
1169+
1170+
# cargo prints the available target names to stderr when the flag is given no value,
1171+
# e.g. `cargo run --bin` lists "Available binaries:" with one indented name per line.
1172+
def __cargoTargetNames (str -- [str])
1173+
flag!
1174+
{ 'bin': 'run', 'example': 'run', 'test': 'test', 'bench': 'bench' } @flag 'run' getDef subcmd!
1175+
['cargo' @subcmd $"--{@flag}"] * ^ ; err! drop
1176+
@err lines (' ' startsWith) filter (trim) map (len 0 >) filter
1177+
end
1178+
1179+
# Crates listed by `cargo install --list`; unindented lines are "name vX.Y.Z:".
1180+
def __cargoInstalled ( -- [str])
1181+
['cargo' 'install' '--list'] * ^ ; drop lines
1182+
(len 0 >) filter
1183+
(' ' startsWith not) filter
1184+
(wsplit :0:) map
1185+
end
1186+
1187+
def __cargoOptionsForSubcmd (str -- [str])
1188+
cmd!
1189+
{
1190+
'add': [
1191+
'--dev' '--build' '--dry-run' '--optional' '--no-optional' '--rename'
1192+
'--features' '--default-features' '--no-default-features'
1193+
'--path' '--git' '--branch' '--tag' '--rev' '--registry'
1194+
'-p' '--package' '--manifest-path'
1195+
],
1196+
'bench': [
1197+
'-p' '--package' '--exclude' '-j' '--jobs' '--lib' '--bin' '--bins'
1198+
'--example' '--examples' '--test' '--tests' '--bench' '--benches'
1199+
'--all-targets' '--no-run' '--no-fail-fast' '--workspace'
1200+
'--features' '--all-features' '--no-default-features'
1201+
'--target' '--target-dir' '--manifest-path' '--message-format'
1202+
'--profile' '--ignore-rust-version' '--unit-graph'
1203+
],
1204+
'build': [
1205+
'-p' '--package' '--exclude' '-j' '--jobs' '--lib' '--bin' '--bins'
1206+
'--example' '--examples' '--test' '--tests' '--bench' '--benches'
1207+
'--all-targets' '--workspace' '--release' '--profile'
1208+
'--features' '--all-features' '--no-default-features'
1209+
'--target' '--target-dir' '--out-dir' '--manifest-path' '--message-format'
1210+
'--build-plan' '--unit-graph' '--future-incompat-report' '--ignore-rust-version'
1211+
],
1212+
'check': [
1213+
'-p' '--package' '--exclude' '-j' '--jobs' '--lib' '--bin' '--bins'
1214+
'--example' '--examples' '--test' '--tests' '--bench' '--benches'
1215+
'--all-targets' '--workspace' '--release' '--profile'
1216+
'--features' '--all-features' '--no-default-features'
1217+
'--target' '--target-dir' '--manifest-path' '--message-format'
1218+
'--unit-graph' '--future-incompat-report' '--ignore-rust-version'
1219+
],
1220+
'clean': [
1221+
'-p' '--package' '--manifest-path' '--target' '--target-dir'
1222+
'--profile' '--release' '--doc' '--dry-run'
1223+
],
1224+
'doc': [
1225+
'-p' '--package' '--exclude' '-j' '--jobs' '--lib' '--bin' '--bins'
1226+
'--open' '--no-deps' '--document-private-items' '--workspace'
1227+
'--release' '--profile' '--features' '--all-features' '--no-default-features'
1228+
'--target' '--target-dir' '--manifest-path' '--message-format'
1229+
'--ignore-rust-version' '--unit-graph'
1230+
],
1231+
'fetch': [
1232+
'--manifest-path' '--target'
1233+
],
1234+
'fix': [
1235+
'-p' '--package' '--exclude' '-j' '--jobs' '--lib' '--bin' '--bins'
1236+
'--example' '--examples' '--test' '--tests' '--bench' '--benches'
1237+
'--all-targets' '--workspace' '--release' '--profile'
1238+
'--features' '--all-features' '--no-default-features'
1239+
'--target' '--target-dir' '--manifest-path' '--message-format'
1240+
'--broken-code' '--edition' '--edition-idioms'
1241+
'--allow-no-vcs' '--allow-dirty' '--allow-staged' '--ignore-rust-version'
1242+
],
1243+
'generate-lockfile': [
1244+
'--manifest-path'
1245+
],
1246+
'init': [
1247+
'--registry' '--vcs' '--edition' '--name' '--bin' '--lib'
1248+
],
1249+
'install': [
1250+
'--version' '--git' '--branch' '--tag' '--rev' '--path'
1251+
'-j' '--jobs' '--features' '--all-features' '--no-default-features'
1252+
'--profile' '--bin' '--bins' '--example' '--examples'
1253+
'--target' '--target-dir' '--root' '--index' '--registry'
1254+
'--list' '-f' '--force' '--no-track' '--debug'
1255+
],
1256+
'locate-project': [
1257+
'--manifest-path' '--message-format' '--workspace'
1258+
],
1259+
'login': [
1260+
'--registry'
1261+
],
1262+
'logout': [
1263+
'--registry'
1264+
],
1265+
'metadata': [
1266+
'--features' '--all-features' '--no-default-features'
1267+
'--filter-platform' '--manifest-path' '--format-version' '--no-deps'
1268+
],
1269+
'new': [
1270+
'--registry' '--vcs' '--edition' '--name' '--bin' '--lib'
1271+
],
1272+
'owner': [
1273+
'-a' '--add' '-r' '--remove' '-l' '--list' '--index' '--token' '--registry'
1274+
],
1275+
'package': [
1276+
'-l' '--list' '--no-verify' '--no-metadata' '--allow-dirty'
1277+
'--target' '--target-dir' '--features' '--all-features' '--no-default-features'
1278+
'--manifest-path' '-j' '--jobs'
1279+
],
1280+
'pkgid': [
1281+
'-p' '--package' '--manifest-path'
1282+
],
1283+
'publish': [
1284+
'--index' '--token' '--no-verify' '--allow-dirty' '--dry-run' '--registry'
1285+
'--target' '--target-dir' '--manifest-path'
1286+
'--features' '--all-features' '--no-default-features' '-j' '--jobs'
1287+
],
1288+
'remove': [
1289+
'--dev' '--build' '--dry-run' '-p' '--package' '--manifest-path'
1290+
],
1291+
'run': [
1292+
'--bin' '--example' '-p' '--package' '-j' '--jobs' '--release' '--profile'
1293+
'--features' '--all-features' '--no-default-features'
1294+
'--target' '--target-dir' '--manifest-path' '--message-format'
1295+
'--unit-graph' '--ignore-rust-version'
1296+
],
1297+
'rustc': [
1298+
'-p' '--package' '-j' '--jobs' '--lib' '--bin' '--bins'
1299+
'--example' '--examples' '--test' '--tests' '--bench' '--benches'
1300+
'--all-targets' '--release' '--profile'
1301+
'--features' '--all-features' '--no-default-features'
1302+
'--target' '--target-dir' '--print' '--manifest-path' '--message-format'
1303+
'--unit-graph' '--ignore-rust-version' '--future-incompat-report'
1304+
],
1305+
'rustdoc': [
1306+
'-p' '--package' '-j' '--jobs' '--lib' '--bin' '--bins'
1307+
'--example' '--examples' '--test' '--tests' '--bench' '--benches'
1308+
'--all-targets' '--open' '--release' '--profile'
1309+
'--features' '--all-features' '--no-default-features'
1310+
'--target' '--target-dir' '--manifest-path' '--message-format'
1311+
'--unit-graph' '--ignore-rust-version'
1312+
],
1313+
'search': [
1314+
'--index' '--limit' '--registry'
1315+
],
1316+
'test': [
1317+
'--lib' '--bin' '--bins' '--example' '--examples' '--test' '--tests'
1318+
'--bench' '--benches' '--all-targets' '--doc'
1319+
'-p' '--package' '--exclude' '-j' '--jobs' '--no-run' '--no-fail-fast'
1320+
'--workspace' '--release' '--profile'
1321+
'--features' '--all-features' '--no-default-features'
1322+
'--target' '--target-dir' '--manifest-path' '--message-format'
1323+
'--ignore-rust-version' '--unit-graph' '--future-incompat-report'
1324+
],
1325+
'tree': [
1326+
'-p' '--package' '--exclude' '--features' '--all-features' '--no-default-features'
1327+
'--target' '-e' '--edges' '-i' '--invert' '--prefix' '--charset'
1328+
'-f' '--format' '--workspace' '--no-dedupe' '-d' '--duplicates' '--manifest-path'
1329+
],
1330+
'uninstall': [
1331+
'-p' '--package' '--bin' '--root'
1332+
],
1333+
'update': [
1334+
'-p' '--package' '--precise' '-w' '--workspace' '--aggressive' '--dry-run'
1335+
'--manifest-path' '--ignore-rust-version'
1336+
],
1337+
'vendor': [
1338+
'-s' '--sync' '--no-delete' '--respect-source-config' '--versioned-dirs'
1339+
'--manifest-path'
1340+
],
1341+
'verify-project': [
1342+
'--manifest-path'
1343+
],
1344+
'yank': [
1345+
'--vers' '--undo' '--index' '--token' '--registry'
1346+
]
1347+
}
1348+
@cmd [] getDef
1349+
end
1350+
1351+
# Default completions after a subcommand: common options plus the subcommand-specific ones.
1352+
def __cargoSubcmdCompletions (str -- [str])
1353+
cmd!
1354+
__cargoCommonOptions @cmd __cargoOptionsForSubcmd extend
1355+
end
1356+
1357+
def __cargoCompletion { 'complete': ['cargo'] } ([str] -- [str])
1358+
input!
1359+
@input len 0 > if
1360+
@input :0: rawSubcmd!
1361+
@rawSubcmd match
1362+
'b' : 'build',
1363+
'c' : 'check',
1364+
'r' : 'run',
1365+
't' : 'test',
1366+
'd' : 'doc',
1367+
_ : @rawSubcmd,
1368+
end subcmd!
1369+
@input :-1: last!
1370+
1371+
# Subcommands where --bin/--example (and --test/--bench) take a target name.
1372+
['bench' 'build' 'check' 'run' 'rustc' 'test'] binExampleCmds!
1373+
['bench' 'build' 'check' 'rustc' 'test'] testBenchCmds!
1374+
1375+
@last match
1376+
'-p' '--package' '--exclude' : __cargoPackages,
1377+
'--target' : __cargoTargets,
1378+
'--features' : __cargoFeatures,
1379+
'--color' : ['auto' 'always' 'never'],
1380+
'--vcs' : ['git' 'hg' 'pijul' 'fossil' 'none'],
1381+
'--edition' : ['2015' '2018' '2021' '2024'],
1382+
'--message-format' : ['human' 'short' 'json' 'json-diagnostic-short' 'json-diagnostic-rendered-ansi' 'json-render-diagnostics'],
1383+
'--profile' : ['dev' 'release' 'test' 'bench'],
1384+
'--bin' '--example' :
1385+
@binExampleCmds (@subcmd =) any
1386+
(@last 2: __cargoTargetNames)
1387+
(@subcmd __cargoSubcmdCompletions) iff,
1388+
'--test' '--bench' :
1389+
@testBenchCmds (@subcmd =) any
1390+
(@last 2: __cargoTargetNames)
1391+
(@subcmd __cargoSubcmdCompletions) iff,
1392+
'-e' '--edges' :
1393+
@subcmd 'tree' =
1394+
(['features' 'normal' 'build' 'dev' 'all' 'no-dev' 'no-build' 'no-normal'])
1395+
(@subcmd __cargoSubcmdCompletions) iff,
1396+
'--prefix' :
1397+
@subcmd 'tree' =
1398+
(['depth' 'indent' 'none'])
1399+
(@subcmd __cargoSubcmdCompletions) iff,
1400+
'--charset' :
1401+
@subcmd 'tree' =
1402+
(['utf8' 'ascii'])
1403+
(@subcmd __cargoSubcmdCompletions) iff,
1404+
_ : @subcmd match
1405+
'help' : __cargoSubcommands,
1406+
'uninstall' : __cargoInstalled @subcmd __cargoSubcmdCompletions extend,
1407+
_ : @subcmd __cargoSubcmdCompletions,
1408+
end,
1409+
end
1410+
else
1411+
__cargoSubcommands ['--list' '--explain' '-C'] extend __cargoCommonOptions extend
1412+
end
1413+
end
1414+
# }}}
11061415
# }}}

0 commit comments

Comments
 (0)