timeouts for dynamic completions
This commit is contained in:
parent
8f92bb86db
commit
73904c036f
5 changed files with 122 additions and 8 deletions
17
doc/nixos.md
17
doc/nixos.md
|
|
@ -73,6 +73,14 @@ programs.inshellah = {
|
|||
# default of 200ms)
|
||||
timeoutMs = null;
|
||||
|
||||
# timeout in ms for live dynamic completions at tab-completion time
|
||||
# set to 0 to disable the runtime timeout
|
||||
dynamicTimeoutMs = 5000;
|
||||
|
||||
# result cap requested from live providers that support native limits
|
||||
# set to 0 to omit native result-limit flags
|
||||
dynamicLimit = 200;
|
||||
|
||||
# worker-thread count for the parallel scrape
|
||||
workers = null;
|
||||
};
|
||||
|
|
@ -109,6 +117,14 @@ $env.config.completions.external = { ... }
|
|||
the snippet provides both static lookups against the system index and
|
||||
runtime fallbacks for cases the static index can't cover:
|
||||
|
||||
runtime fallbacks have a default 5s timeout, controlled by
|
||||
`programs.inshellah.dynamicTimeoutMs` or `INSHELLAH_DYNAMIC_TIMEOUT_MS`
|
||||
when sourcing the snippet manually. providers with native result caps use
|
||||
`programs.inshellah.dynamicLimit` or `INSHELLAH_DYNAMIC_LIMIT`, defaulting
|
||||
to 200. set either value to 0 to disable that guard. on timeout the
|
||||
completer returns `null` so nushell can fall back to its normal completion
|
||||
behavior.
|
||||
|
||||
| command | dynamic source |
|
||||
|---|---|
|
||||
| `nix` | flake refs via `NIX_GET_COMPLETIONS`, with optional `meta.description` |
|
||||
|
|
@ -120,6 +136,7 @@ runtime fallbacks for cases the static index can't cover:
|
|||
| `docker` / `podman` | containers + image refs by subcommand |
|
||||
| `kubectl` | resource names from the live cluster |
|
||||
| `git` | refs + worktree paths |
|
||||
| `jj` | revisions, operations, bookmarks, remotes, files, and workspaces |
|
||||
| `npm` / `pnpm` / `yarn` | scripts from package.json |
|
||||
| `make` / `just` | targets / recipes |
|
||||
| `cargo` | workspace targets behind `--bin` / `--example` / etc. |
|
||||
|
|
|
|||
27
flake.nix
27
flake.nix
|
|
@ -68,6 +68,9 @@
|
|||
fakeNix = pkgs.writeShellScriptBin "nix" ''
|
||||
if [ "''${1:-}" = eval ]; then
|
||||
printf 'raw package description\n'
|
||||
elif [ "''${1:-}" = slow ]; then
|
||||
sleep 1
|
||||
printf 'header\nslow-package\n'
|
||||
else
|
||||
printf 'header\nbuild\nflake#pkg\n'
|
||||
fi
|
||||
|
|
@ -101,6 +104,9 @@
|
|||
printf 'origin\nupstream\n'
|
||||
;;
|
||||
for-each-ref)
|
||||
if [ -n "''${INSHELLAH_GIT_ARGS_FILE:-}" ]; then
|
||||
printf '%s\n' "$*" > "$INSHELLAH_GIT_ARGS_FILE"
|
||||
fi
|
||||
case "$*" in
|
||||
*"refs/heads refs/remotes refs/tags"*)
|
||||
printf 'main\tcommit\tMain branch\norigin/main\tcommit\tRemote main\nv1.0\tcommit\tRelease 1\n'
|
||||
|
|
@ -210,9 +216,30 @@
|
|||
echo "running nushell shim checks"
|
||||
export PATH="${fakeCompletionBackends}/bin:$PATH"
|
||||
export KUBECTL_ARGS_FILE="$TMPDIR/kubectl.args"
|
||||
export INSHELLAH_GIT_ARGS_FILE="$TMPDIR/git.args"
|
||||
export INSHELLAH_STATIC_FILE="$TMPDIR/inshellah-static.json"
|
||||
export INSHELLAH_DYNAMIC_TIMEOUT_MS=50
|
||||
: > "$INSHELLAH_STATIC_FILE"
|
||||
nu --no-config-file -c 'source ${./nix/inshellah-completer.nu}; source ${./tests/nushell-completer.nu}'
|
||||
INSHELLAH_DYNAMIC_TIMEOUT_MS=0 nu --no-config-file -c '
|
||||
source ${./nix/inshellah-completer.nu}
|
||||
"" | save --force $env.INSHELLAH_STATIC_FILE
|
||||
let completer = $env.config.completions.external.completer
|
||||
let slow = do $completer [nix slow ""]
|
||||
if (($slow | get 0.value) != "slow-package") {
|
||||
error make {msg: "dynamic timeout 0 should disable timeout"}
|
||||
}
|
||||
'
|
||||
INSHELLAH_DYNAMIC_LIMIT=0 nu --no-config-file -c '
|
||||
source ${./nix/inshellah-completer.nu}
|
||||
"[]" | save --force $env.INSHELLAH_STATIC_FILE
|
||||
"" | save --force $env.INSHELLAH_GIT_ARGS_FILE
|
||||
let completer = $env.config.completions.external.completer
|
||||
do $completer [git fetch origin ""]
|
||||
if ((open $env.INSHELLAH_GIT_ARGS_FILE) | str contains "--count") {
|
||||
error make {msg: "dynamic limit 0 should omit provider limit flags"}
|
||||
}
|
||||
'
|
||||
cat > "$TMPDIR/config-load.nu" <<'EOF'
|
||||
source ${./nix/inshellah-completer.nu}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,47 @@ let inshellah_nonempty = { |items|
|
|||
if ($result | is-empty) { null } else { $result }
|
||||
}
|
||||
|
||||
let inshellah_default_dynamic_timeout_ms = 5000
|
||||
|
||||
let inshellah_dynamic_timeout_ms = do {
|
||||
let raw = (try {
|
||||
$env.INSHELLAH_DYNAMIC_TIMEOUT_MS? | default $inshellah_default_dynamic_timeout_ms | into int
|
||||
} catch { $inshellah_default_dynamic_timeout_ms })
|
||||
if $raw >= 0 { $raw } else { $inshellah_default_dynamic_timeout_ms }
|
||||
}
|
||||
|
||||
let inshellah_dynamic_timeout = ($inshellah_dynamic_timeout_ms * 1ms)
|
||||
|
||||
let inshellah_default_dynamic_limit = 200
|
||||
|
||||
let inshellah_dynamic_limit = do {
|
||||
let raw = (try {
|
||||
$env.INSHELLAH_DYNAMIC_LIMIT? | default $inshellah_default_dynamic_limit | into int
|
||||
} catch { $inshellah_default_dynamic_limit })
|
||||
if $raw >= 0 { $raw } else { $inshellah_default_dynamic_limit }
|
||||
}
|
||||
|
||||
let inshellah_limit_args = { |flag|
|
||||
if $inshellah_dynamic_limit == 0 { [] } else { [$flag $inshellah_dynamic_limit] }
|
||||
}
|
||||
|
||||
let inshellah_with_timeout = { |body|
|
||||
if $inshellah_dynamic_timeout_ms == 0 {
|
||||
try { do $body } catch { null }
|
||||
} else {
|
||||
let tag = random int 1..2147483647
|
||||
let job_id = job spawn {
|
||||
do $body | job send --tag $tag 0
|
||||
}
|
||||
try {
|
||||
job recv --tag $tag --timeout $inshellah_dynamic_timeout
|
||||
} catch {
|
||||
try { job kill $job_id } catch { null }
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let inshellah_fuzzy_score = { |needle, haystack|
|
||||
let needle = $needle | default "" | into string
|
||||
let haystack = $haystack | default "" | into string
|
||||
|
|
@ -189,7 +230,7 @@ let inshellah_kubectl_names = { |kind, spans|
|
|||
|
||||
let inshellah_git_refs = { ||
|
||||
try {
|
||||
^git for-each-ref --format='%(refname:short)%09%(objecttype)%09%(contents:subject)' refs/heads refs/remotes refs/tags
|
||||
^git for-each-ref ...(do $inshellah_limit_args "--count") --format='%(refname:short)%09%(objecttype)%09%(contents:subject)' refs/heads refs/remotes refs/tags
|
||||
| lines
|
||||
| each { |l|
|
||||
let p = $l | split row "\t"
|
||||
|
|
@ -200,7 +241,7 @@ let inshellah_git_refs = { ||
|
|||
|
||||
let inshellah_git_branches = { ||
|
||||
try {
|
||||
^git for-each-ref --format='%(refname:short)%09%(contents:subject)' refs/heads
|
||||
^git for-each-ref ...(do $inshellah_limit_args "--count") --format='%(refname:short)%09%(contents:subject)' refs/heads
|
||||
| lines
|
||||
| each { |l|
|
||||
let p = $l | split row "\t"
|
||||
|
|
@ -211,7 +252,7 @@ let inshellah_git_branches = { ||
|
|||
|
||||
let inshellah_git_tags = { ||
|
||||
try {
|
||||
^git for-each-ref --format='%(refname:short)%09%(contents:subject)' refs/tags
|
||||
^git for-each-ref ...(do $inshellah_limit_args "--count") --format='%(refname:short)%09%(contents:subject)' refs/tags
|
||||
| lines
|
||||
| each { |l|
|
||||
let p = $l | split row "\t"
|
||||
|
|
@ -232,7 +273,7 @@ let inshellah_git_remotes = { ||
|
|||
|
||||
let inshellah_git_stashes = { ||
|
||||
try {
|
||||
^git stash list
|
||||
^git stash list ...(do $inshellah_limit_args "-n")
|
||||
| lines
|
||||
| each { |l|
|
||||
let m = $l | parse -r '^(?P<stash>stash@\{[0-9]+\}):\s*(?P<desc>.*)$'
|
||||
|
|
@ -289,7 +330,7 @@ let inshellah_git_worktrees = { ||
|
|||
|
||||
let inshellah_jj_revs = { ||
|
||||
try {
|
||||
^jj log --ignore-working-copy --no-graph -r 'all()' -T 'change_id.shortest() ++ "\t" ++ description.first_line() ++ "\n"' err> /dev/null
|
||||
^jj log --ignore-working-copy --no-graph ...(do $inshellah_limit_args "-n") -r 'all()' -T 'change_id.shortest() ++ "\t" ++ description.first_line() ++ "\n"' err> /dev/null
|
||||
| lines
|
||||
| each { |l|
|
||||
let p = $l | split row "\t"
|
||||
|
|
@ -331,7 +372,7 @@ let inshellah_jj_remotes = { ||
|
|||
|
||||
let inshellah_jj_ops = { ||
|
||||
try {
|
||||
^jj op log --ignore-working-copy --no-graph -T 'id.short() ++ "\t" ++ description.first_line() ++ "\n"' err> /dev/null
|
||||
^jj op log --ignore-working-copy --no-graph ...(do $inshellah_limit_args "-n") -T 'id.short() ++ "\t" ++ description.first_line() ++ "\n"' err> /dev/null
|
||||
| lines
|
||||
| each { |l|
|
||||
let p = $l | split row "\t"
|
||||
|
|
@ -368,6 +409,7 @@ let inshellah_complete = { |spans|
|
|||
let sub = if $span_len >= 2 { $spans | get 1 } else { "" }
|
||||
|
||||
let additional = if ($completions == null and $span_len > 0) {
|
||||
do $inshellah_with_timeout {
|
||||
match $spans.0 {
|
||||
"nix" => {
|
||||
if $span_len < 2 {
|
||||
|
|
@ -425,7 +467,7 @@ let inshellah_complete = { |spans|
|
|||
if (($sub in $unit_verbs) and $span_len >= 3) {
|
||||
let units = (do $inshellah_unit_candidates [] $last_span | default [])
|
||||
let pids = (try {
|
||||
^coredumpctl list --no-pager --no-legend
|
||||
^coredumpctl list ...(do $inshellah_limit_args "-n") --no-pager --no-legend
|
||||
| lines
|
||||
| each { |l|
|
||||
let p = $l | split row -r '\s+'
|
||||
|
|
@ -502,7 +544,7 @@ let inshellah_complete = { |spans|
|
|||
let need_image = ["run" "rmi" "tag" "push" "pull" "history" "save" "create"]
|
||||
if ($sub in $need_container) {
|
||||
try {
|
||||
^($spans.0) ps -a --format '{{.Names}}\t{{.Image}}'
|
||||
^($spans.0) ps ...(do $inshellah_limit_args "--last") --format '{{.Names}}\t{{.Image}}'
|
||||
| lines | each { |l|
|
||||
let p = $l | split row "\t"
|
||||
if ($p | length) >= 2 { {value: $p.0, description: $p.1} }
|
||||
|
|
@ -801,6 +843,7 @@ let inshellah_complete = { |spans|
|
|||
}
|
||||
_ => { null }
|
||||
}
|
||||
}
|
||||
} else { null }
|
||||
|
||||
if $completions == null {
|
||||
|
|
|
|||
|
|
@ -110,6 +110,28 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
dynamicTimeoutMs = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 5000;
|
||||
example = 2000;
|
||||
description = ''
|
||||
timeout in milliseconds for live dynamic completions in the nushell
|
||||
shim. this bounds runtime calls such as nix, jj, kubectl, and systemctl.
|
||||
set to 0 to disable the runtime timeout.
|
||||
'';
|
||||
};
|
||||
|
||||
dynamicLimit = lib.mkOption {
|
||||
type = lib.types.int;
|
||||
default = 200;
|
||||
example = 100;
|
||||
description = ''
|
||||
maximum number of results requested from live dynamic completion
|
||||
providers when they expose a native result limit. set to 0 to omit
|
||||
native result-limit flags.
|
||||
'';
|
||||
};
|
||||
|
||||
workers = lib.mkOption {
|
||||
type = lib.types.nullOr lib.types.int;
|
||||
default = null;
|
||||
|
|
@ -131,6 +153,9 @@ in
|
|||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
environment.variables.INSHELLAH_DYNAMIC_TIMEOUT_MS = toString cfg.dynamicTimeoutMs;
|
||||
environment.variables.INSHELLAH_DYNAMIC_LIMIT = toString cfg.dynamicLimit;
|
||||
|
||||
environment.systemPackages =
|
||||
let
|
||||
systemDir = "/run/current-system/sw${cfg.completionsPath}";
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ let nix_commands = do $completer [nix ""]
|
|||
assert-eq ($nix_commands | get 0.value) "build" "nix command completion uses NIX_GET_COMPLETIONS"
|
||||
let nix_pkg = do $completer [nix "flake#pkg"]
|
||||
assert-eq ($nix_pkg | get 0.description) "raw package description" "nix descriptions are raw strings"
|
||||
let nix_slow = do $completer [nix slow ""]
|
||||
assert-eq $nix_slow null "slow dynamic completions time out"
|
||||
|
||||
let systemctl_empty = do $completer [systemctl daemon-reload ""]
|
||||
assert-eq $systemctl_empty null "systemctl does not offer units for non-unit verbs"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue