diff --git a/Base/etc/shellrc b/Base/etc/shellrc index b917ae3ae8..2db6d78453 100644 --- a/Base/etc/shellrc +++ b/Base/etc/shellrc @@ -43,3 +43,5 @@ export HISTORY_AUTOSAVE_TIME_MS=10000 export TMPDIR=/tmp PROGRAMS_ALLOWED_TO_MODIFY_DEFAULT_TERMIOS=(stty) + +for /usr/share/shell/completion/*.sh { source $it } diff --git a/Base/usr/share/shell/completion/builtin.sh b/Base/usr/share/shell/completion/builtin.sh new file mode 100644 index 0000000000..d7725b47ff --- /dev/null +++ b/Base/usr/share/shell/completion/builtin.sh @@ -0,0 +1,76 @@ +#!/bin/Shell + +_complete_unalias() { + shift 2 + argsparser_parse \ + --add-positional-argument names --help-string _ --value-name _ --short-name '' --min 0 --max 9999 \ + -- $* + name='' + if test ${length $names} -ne 0 { + name="$names[-1]" + } + invariant="${length $name}" + for $(alias | grep "^$name") { + n=${regex_replace '"' '\"' ${regex_replace '\\([^\\])' '\1' ${regex_replace '=.*' '' "$it"}}} + v=${regex_replace '"' '\"' ${regex_replace '\\([^\\])' '\1' ${regex_replace '[^=]*=' '' "$it"}}} + echo '{"kind":"plain","completion":"'"$n"'", "trailing_trivia":" ", "display_trivia":"'"$v"'", "invariant_offset": '$invariant'}' + } +} + +__complete_job_spec() { + match $1 as hint { + %?* as (name) { + for $(jobs | grep "$name") { + id='' + match $it { + [*]\ * as (i _) { id=$i } + * { continue } + } + echo '{"kind":"plain","static_offset":'"${length "?$name"}"',"invariant_offset":0,"completion":"'"$id"'"}' + } + } + %* as (id) { + invariant=${length $id} + for $(jobs | grep "^\\[$id\\d+\\]") { + id='' + match $it { + [*]\ * as (i _) { id=$i } + * { continue } + } + echo '{"kind":"plain","static_offset":0,"invariant_offset":'"$invariant"',"completion":"'"$id"'"}' + } + } + (?^\d+$) { + invariant=${length $pid} + for $(ps -e | grep "^ *$pid") { + id='' + description='' + match $it { + "*$pid* *" as (_ i rest) { id="$pid$i" description="$rest" } + * { continue } + } + echo '{"kind":"plain","static_offset":0,"invariant_offset":'"$invariant"',"completion":"'"$id"'","display_trivia":"'"$description"'"}' + } + } + * as (name) { + static="${length $name}" + for $(ps -e | grep "$name") { + id='' + description='' + match $it { + (?: *(?\d+) (?.*)) { id="$pid" description="$rest" } + * { continue } + } + echo '{"kind":"plain","static_offset":'"$static"',"invariant_offset":0,"completion":"'"$id"'","display_trivia":"'"$description"'","allow_commit_without_listing":false}' + } + } + } +} + +_complete_kill() { + if test $*[-1] = '--' { + __complete_job_spec '' + } else { + __complete_job_spec $*[-1] + } +} diff --git a/Base/usr/share/shell/completion/proxy.sh b/Base/usr/share/shell/completion/proxy.sh new file mode 100644 index 0000000000..2179cd42ba --- /dev/null +++ b/Base/usr/share/shell/completion/proxy.sh @@ -0,0 +1,30 @@ +#!/bin/Shell + +__proxy() { + echo '{"kind":"proxy","argv":"'"${regex_replace '"' '\"' "$*"}"'"}' +} + +# Builtins +_complete_time() { + shift 2 + argsparser_parse \ + --add-option _ --help-string "Number of iterations" \ + --long-name iterations --short-name n --value-name iterations --type u32 \ + --add-positional-argument argv --help-string _ \ + --value-name _ --min 0 --max 9999999 \ + --stop-on-first-non-option \ + -- $* + __proxy $argv +} + +# Utilities +_complete_pls() { + shift 2 + argsparser_parse \ + --add-option _ --help-string "User to execute as" --short-name u --value-name UID \ + --add-positional-argument argv --help-string "Command to run at elevated privilege level" \ + --value-name command --min 0 --max 999999 \ + --stop-on-first-non-option \ + -- $* + __proxy $argv +} diff --git a/Meta/lint-shell-scripts.sh b/Meta/lint-shell-scripts.sh index b59409f598..b7e17e5d6f 100755 --- a/Meta/lint-shell-scripts.sh +++ b/Meta/lint-shell-scripts.sh @@ -12,7 +12,9 @@ if [ "$#" -eq "0" ]; then ':!:Ports' \ ':!:Userland/Shell/Tests' \ ':!:Base/home/anon/Tests' \ - ':!:Base/root/generate_manpages.sh' + ':!:Base/root/generate_manpages.sh' \ + ':!:Base/usr/share/shell' \ + ':!:Base/etc/shellrc' \ ) else files=()