From 072bbe7e5d37433ff7f33ffde690e67c8cb839df Mon Sep 17 00:00:00 2001 From: David Adam Date: Wed, 14 Mar 2018 21:44:17 +0800 Subject: [PATCH 001/159] [cmake] add doc target to ALL Closes #4809. --- cmake/Docs.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Docs.cmake b/cmake/Docs.cmake index 14260bca7..39f3a0409 100644 --- a/cmake/Docs.cmake +++ b/cmake/Docs.cmake @@ -118,7 +118,7 @@ IF(BUILD_DOCS) # @echo " doxygen $(em)user_doc$(sgr0)" # $v (cat Doxyfile.user; echo INPUT_FILTER=./lexicon_filter; echo PROJECT_NUMBER=$(FISH_BUILD_VERSION) | $(SED) "s/-.*//") | doxygen - && touch user_doc # $v rm -f $(wildcard $(addprefix ./user_doc/html/,arrow*.png bc_s.png bdwn.png closed.png doc.png folder*.png ftv2*.png nav*.png open.png splitbar.png sync_*.png tab*.* doxygen.* dynsections.js jquery.js pages.html)) - ADD_CUSTOM_TARGET(doc + ADD_CUSTOM_TARGET(doc ALL COMMAND env `cat ${FBVF}` ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/build_user_doc.sh ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.user ./lexicon_filter From 625ef5d73431cb2fbbc582c726eebe8ce626d73b Mon Sep 17 00:00:00 2001 From: David Adam Date: Thu, 22 Mar 2018 15:34:33 +1100 Subject: [PATCH 002/159] [cmake] fix generation of fish pkgconfig file Use `printf` instead of the non-portable `echo -n`. --- cmake/Install.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 6aad5a4bc..0424afd8a 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -132,7 +132,7 @@ CONFIGURE_FILE(fish.pc.in fish.pc.noversion) ADD_CUSTOM_COMMAND(OUTPUT fish.pc COMMAND sed '/Version/d' fish.pc.noversion > fish.pc - COMMAND echo -n "Version: " >> fish.pc + COMMAND printf "Version: " >> fish.pc COMMAND sed 's/FISH_BUILD_VERSION=//\;s/\"//g' ${FBVF} >> fish.pc WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FBVF} ${CMAKE_CURRENT_BINARY_DIR}/fish.pc.noversion) From a6d2b06529942f8dfa9dbd3107b6420a61f24435 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 23 Mar 2018 13:34:16 +1100 Subject: [PATCH 003/159] [cmake] use full paths for extra configuration/function/completion dirs --- cmake/Install.cmake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 0424afd8a..94fb3465f 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -25,15 +25,15 @@ SET(configure_input DO NOT MANUALLY EDIT THIS FILE!") SET(extra_completionsdir - ${rel_datadir}/fish/vendor_completions.d + ${datadir}/fish/vendor_completions.d CACHE STRING "Path for extra completions") SET(extra_functionsdir - ${rel_datadir}/fish/vendor_functions.d + ${datadir}/fish/vendor_functions.d CACHE STRING "Path for extra completions") SET(extra_confdir - ${rel_datadir}/fish/vendor_conf.d + ${datadir}/fish/vendor_conf.d CACHE STRING "Path for extra configuration") # These are the man pages that go in system manpath. From b819f38e834bf804200377fe3cfd5942d7c44f0a Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 23 Mar 2018 19:03:02 +1100 Subject: [PATCH 004/159] [cmake] fix selection of manual pages for installation --- cmake/Install.cmake | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 94fb3465f..e53741821 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -36,19 +36,18 @@ SET(extra_confdir ${datadir}/fish/vendor_conf.d CACHE STRING "Path for extra configuration") -# These are the man pages that go in system manpath. +# These are the man pages that go in system manpath; all manpages go in the fish-specific manpath. SET(MANUALS ${CMAKE_CURRENT_BINARY_DIR}/share/man/man1/fish.1 ${CMAKE_CURRENT_BINARY_DIR}/share/man/man1/fish_indent.1 ${CMAKE_CURRENT_BINARY_DIR}/share/man/man1/fish_key_reader.1) -# These are the manpages that go in fish-specific manpath. -FILE(GLOB HELP_MANPAGES share/man/man1/*.1) - -# Determine which man pages we don't want to install. +# Determine which man page we don't want to install. # On OS X, don't install a man page for open, since we defeat fish's open # function on OS X. IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - LIST(REMOVE_ITEM HELP_MANPAGES share/man/man1/open.1) + SET(CONDEMNED_PAGE "open.1") +ELSE() + SET(CONDEMNED_PAGE "none") ENDIF() # Define a function to help us create directories. @@ -160,10 +159,13 @@ INSTALL(DIRECTORY share/groff DESTINATION ${rel_datadir}/fish) # $v test -z "$(wildcard share/man/man1/*.1)" || $(INSTALL) -m 644 $(filter-out $(addprefix share/man/man1/, $(CONDEMNED_PAGES)), $(wildcard share/man/man1/*.1)) $(DESTDIR)$(datadir)/fish/man/man1/ -# CONDEMNED_PAGES is managed by the LIST() function after the glob +# CONDEMNED_PAGE is managed by the conditional above # Building the man pages is optional: if doxygen isn't installed, they're not built -INSTALL(FILES ${HELP_MANPAGES} - DESTINATION ${rel_datadir}/fish/man/man1) +INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/share/man/man1/ + DESTINATION ${rel_datadir}/fish/man/man1 + FILES_MATCHING + PATTERN "*.1" + PATTERN ${CONDEMNED_PAGE} EXCLUDE) # @echo "Installing helper tools"; # $v $(INSTALL) -m 755 share/tools/*.py $(DESTDIR)$(datadir)/fish/tools/ From 0572b29f263c824488a78fcedbaff1eb1f019099 Mon Sep 17 00:00:00 2001 From: David Adam Date: Fri, 23 Mar 2018 21:12:36 +1100 Subject: [PATCH 005/159] [cmake] install manual from binary directory Fixes out-of-tree builds. --- cmake/Install.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index e53741821..6061d8280 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -206,7 +206,7 @@ INSTALL(FILES ${MANUALS} DESTINATION ${mandir}/man1/ OPTIONAL) # fi; \ # done; # Building the manual is optional -INSTALL(DIRECTORY user_doc/html/ # Trailing slash is important! +INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/user_doc/html/ # Trailing slash is important! DESTINATION ${docdir} OPTIONAL) INSTALL(FILES CHANGELOG.md DESTINATION ${docdir}) From 46e9bf86b87e73fd4f919fa692799197e099f750 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 24 Mar 2018 21:28:27 +0800 Subject: [PATCH 006/159] [cmake] fix dependencies in documentation build Closes two race conditions in parallel builds. --- cmake/Docs.cmake | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmake/Docs.cmake b/cmake/Docs.cmake index 39f3a0409..df5a91a66 100644 --- a/cmake/Docs.cmake +++ b/cmake/Docs.cmake @@ -68,8 +68,10 @@ IF(BUILD_DOCS) DEPENDS ${FUNCTIONS_DIR_FILES} ${COMPLETIONS_DIR_FILES} doc_src/commands.hdr ${CMAKE_CURRENT_SOURCE_DIR}/lexicon_filter.in share/functions/__fish_config_interactive.fish - build_tools/build_lexicon_filter.sh) + build_tools/build_lexicon_filter.sh command_list_toc.txt) + # Other targets should depend on this target, otherwise the lexicon + # filter can be built twice. ADD_CUSTOM_TARGET(build_lexicon_filter DEPENDS lexicon_filter) # @@ -127,7 +129,7 @@ IF(BUILD_DOCS) ADD_CUSTOM_COMMAND(OUTPUT share/man/ COMMAND env `cat ${FBVF} | tr -d '\"' ` INPUT_FILTER=lexicon_filter ${CMAKE_CURRENT_SOURCE_DIR}/build_tools/build_documentation.sh ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.help doc_src ./share - DEPENDS ${CFBVF} ${HELP_SRC} ${CMAKE_CURRENT_BINARY_DIR}/lexicon_filter) + DEPENDS ${CFBVF} ${HELP_SRC} build_lexicon_filter) ADD_CUSTOM_TARGET(BUILD_MANUALS ALL DEPENDS share/man/) From 0f59e4280269c44637f2c31f07b09c9740d5fe30 Mon Sep 17 00:00:00 2001 From: David Adam Date: Sat, 24 Mar 2018 22:34:30 +0800 Subject: [PATCH 007/159] [cmake] support prebuilt documentation in out-of-tree builds --- cmake/Docs.cmake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmake/Docs.cmake b/cmake/Docs.cmake index df5a91a66..54ff25e81 100644 --- a/cmake/Docs.cmake +++ b/cmake/Docs.cmake @@ -136,4 +136,15 @@ IF(BUILD_DOCS) # Group docs targets into a DocsTargets folder SET_PROPERTY(TARGET doc BUILD_MANUALS build_lexicon_filter PROPERTY FOLDER cmake/DocTargets) +ELSEIF(HAVE_PREBUILT_DOCS) + IF(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) + # Out of tree build - link the prebuilt documentation to the build tree + ADD_CUSTOM_TARGET(link_doc ALL) + ADD_CUSTOM_COMMAND(TARGET link_doc + COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/share/man ${CMAKE_CURRENT_BINARY_DIR}/share/man + POST_BUILD) + ADD_CUSTOM_COMMAND(TARGET link_doc + COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/user_doc ${CMAKE_CURRENT_BINARY_DIR}/user_doc + POST_BUILD) + ENDIF() ENDIF(BUILD_DOCS) From 6d80ab8d749553a8a61bcd97df3e8b964fa6c1e0 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 15 Mar 2018 18:31:15 -0500 Subject: [PATCH 008/159] =?UTF-8?q?Rename=20$pid=20(n=C3=A9e=20%self)=20to?= =?UTF-8?q?=20$fish=5Fpid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- po/de.po | 2 +- po/en.po | 2 +- po/fr.po | 4 ++-- po/nb.po | 2 +- po/nn.po | 2 +- po/pl.po | 4 ++-- po/pt_BR.po | 2 +- po/sv.po | 2 +- po/zh_CN.po | 2 +- share/functions/__fish_complete_pids.fish | 4 ++-- share/functions/edit_command_buffer.fish | 4 ++-- share/functions/suspend.fish | 6 +++--- src/builtin_jobs.cpp | 2 +- src/env.cpp | 6 +++--- src/parse_constants.h | 2 +- tests/exit.expect | 2 +- tests/signals.expect | 4 ++-- 18 files changed, 27 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67fe9ec34..d2ac6f694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ This section is for changes merged to the `major` branch that are not also merge - `for` loop control variables are no longer local to the `for` block (#1935). - A literal `{}` now expands to itself, rather than nothing. This makes working with `find -exec` easier. (#1109, #4632) - Successive commas in brace expansions are handled in less surprising manner (`{,,,}` expands to four empty strings rather than an empty string, a comma and an empty string again). (#3002, #4632). -- `%` is no longer used for process and job expansion. `$pid` and `$last_pid` have taken the place of `%self` and `%last` respectively. (#4230, #1202) +- `%` is no longer used for process and job expansion. `$fish_pid` and `$last_pid` have taken the place of `%self` and `%last` respectively. (#4230, #1202) - The new `math` builtin (see below) does not support logical expressions; `test` should be used instead (#4777). ## Notable fixes and improvements diff --git a/po/de.po b/po/de.po index 9825adc4c..7e83305d4 100644 --- a/po/de.po +++ b/po/de.po @@ -2118,7 +2118,7 @@ msgstr "" #: src/parse_constants.h:275 #, c-format -msgid "$$ is not the pid. In fish, please use $pid." +msgid "$$ is not the pid. In fish, please use $fish_pid." msgstr "" #: src/parse_constants.h:278 diff --git a/po/en.po b/po/en.po index e42c37dee..51d4f7f1c 100644 --- a/po/en.po +++ b/po/en.po @@ -2068,7 +2068,7 @@ msgstr "" #: src/parse_constants.h:275 #, c-format -msgid "$$ is not the pid. In fish, please use $pid." +msgid "$$ is not the pid. In fish, please use $fish_pid." msgstr "" #: src/parse_constants.h:278 diff --git a/po/fr.po b/po/fr.po index 3d6c85033..c3c90cc9a 100644 --- a/po/fr.po +++ b/po/fr.po @@ -2229,8 +2229,8 @@ msgstr "$? n’est pas le code de retour. Dans fish, veuillez utiliser $status." #: src/parse_constants.h:275 #, c-format -msgid "$$ is not the pid. In fish, please use $pid." -msgstr "$$ n’est pas le PID. Dans fish, veuillez utiliser $pid." +msgid "$$ is not the pid. In fish, please use $fish_pid." +msgstr "$$ n’est pas le PID. Dans fish, veuillez utiliser $fish_pid." #: src/parse_constants.h:278 msgid "$# is not supported. In fish, please use 'count $argv'." diff --git a/po/nb.po b/po/nb.po index 9112bcaba..52e244788 100644 --- a/po/nb.po +++ b/po/nb.po @@ -2027,7 +2027,7 @@ msgstr "" #: src/parse_constants.h:275 #, c-format -msgid "$$ is not the pid. In fish, please use $pid." +msgid "$$ is not the pid. In fish, please use $fish_pid." msgstr "" #: src/parse_constants.h:278 diff --git a/po/nn.po b/po/nn.po index 27cb7902d..1241996d8 100644 --- a/po/nn.po +++ b/po/nn.po @@ -2027,7 +2027,7 @@ msgstr "" #: src/parse_constants.h:275 #, c-format -msgid "$$ is not the pid. In fish, please use $pid." +msgid "$$ is not the pid. In fish, please use $fish_pid." msgstr "" #: src/parse_constants.h:278 diff --git a/po/pl.po b/po/pl.po index 9bb69f753..37c89891d 100644 --- a/po/pl.po +++ b/po/pl.po @@ -2051,9 +2051,9 @@ msgstr "" #: src/parse_constants.h:275 #, c-format -msgid "$$ is not the pid. In fish, please use $pid." +msgid "$$ is not the pid. In fish, please use $fish_pid." msgstr "" -"$$ nie jest numerem identyfikacyjnym procesu. W fish używane jest $pid." +"$$ nie jest numerem identyfikacyjnym procesu. W fish używane jest $fish_pid." #: src/parse_constants.h:278 msgid "$# is not supported. In fish, please use 'count $argv'." diff --git a/po/pt_BR.po b/po/pt_BR.po index 6973708be..4e3ce749c 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -2084,7 +2084,7 @@ msgstr "" #: src/parse_constants.h:275 #, c-format -msgid "$$ is not the pid. In fish, please use $pid." +msgid "$$ is not the pid. In fish, please use $fish_pid." msgstr "" #: src/parse_constants.h:278 diff --git a/po/sv.po b/po/sv.po index fa4458e04..ff9d90655 100644 --- a/po/sv.po +++ b/po/sv.po @@ -2031,7 +2031,7 @@ msgstr "" #: src/parse_constants.h:275 #, c-format -msgid "$$ is not the pid. In fish, please use $pid." +msgid "$$ is not the pid. In fish, please use $fish_pid." msgstr "" #: src/parse_constants.h:278 diff --git a/po/zh_CN.po b/po/zh_CN.po index 9e8b37ce1..8a09b58ee 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -2045,7 +2045,7 @@ msgstr "" #: src/parse_constants.h:275 #, c-format -msgid "$$ is not the pid. In fish, please use $pid." +msgid "$$ is not the pid. In fish, please use $fish_pid." msgstr "" #: src/parse_constants.h:278 diff --git a/share/functions/__fish_complete_pids.fish b/share/functions/__fish_complete_pids.fish index e352bb3fb..9036b0559 100644 --- a/share/functions/__fish_complete_pids.fish +++ b/share/functions/__fish_complete_pids.fish @@ -1,10 +1,10 @@ function __fish_complete_pids -d "Print a list of process identifiers along with brief descriptions" # This may be a bit slower, but it's nice - having the tty displayed is really handy # 'tail -n +2' deletes the first line, which contains the headers - # $pid is removed from output by string match -r -v + # $fish_pid is removed from output by string match -r -v # Display the tty if available # But not if it's just question marks, meaning no tty - ps axc -o pid,ucomm,tty | string match -r -v '^\s*'$pid'\s' | tail -n +2 | string replace -r ' *([0-9]+) +([^ ].*[^ ]|[^ ]) +([^ ]+) *$' '$1\t$2 [$3]' | string replace -r ' *\[\?*\] *$' '' + ps axc -o pid,ucomm,tty | string match -r -v '^\s*'$fish_pid'\s' | tail -n +2 | string replace -r ' *([0-9]+) +([^ ].*[^ ]|[^ ]) +([^ ]+) *$' '$1\t$2 [$3]' | string replace -r ' *\[\?*\] *$' '' end diff --git a/share/functions/edit_command_buffer.fish b/share/functions/edit_command_buffer.fish index 94fce6413..91ab89278 100644 --- a/share/functions/edit_command_buffer.fish +++ b/share/functions/edit_command_buffer.fish @@ -6,9 +6,9 @@ function edit_command_buffer --description 'Edit the command buffer in an extern else # We should never execute this block but better to be paranoid. if set -q TMPDIR - set f $TMPDIR/fish.$pid.fish + set f $TMPDIR/fish.$fish_pid.fish else - set f /tmp/fish.$pid.fish + set f /tmp/fish.$fish_pid.fish end touch $f or return 1 diff --git a/share/functions/suspend.fish b/share/functions/suspend.fish index a8803f670..0babd35c4 100644 --- a/share/functions/suspend.fish +++ b/share/functions/suspend.fish @@ -18,11 +18,11 @@ function suspend --description 'Suspend the current shell.' end if status is-interactive - echo -ns 'Suspending ' $pid ': run' - echo -n (set_color --bold) 'kill -CONT' $pid (set_color normal) + echo -ns 'Suspending ' $fish_pid ': run' + echo -n (set_color --bold) 'kill -CONT' $fish_pid (set_color normal) echo 'from another terminal to resume' end # XXX always causes a zombie until one fg's when we do this: - kill -STOP $pid + kill -STOP $fish_pid end diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index 019682c4e..d82bdb1a3 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -125,7 +125,7 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { {L"group", no_argument, NULL, 'g'}, {L"help", no_argument, NULL, 'h'}, {L"last", no_argument, NULL, 'l'}, - {L"pid", no_argument, NULL, 'p'}, + {L"fish_pid", no_argument, NULL, 'p'}, {L"quiet", no_argument, NULL, 'q'}, {nullptr, 0, NULL, 0}}; diff --git a/src/env.cpp b/src/env.cpp index 67cf0fbe7..2783b2eb0 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -322,7 +322,7 @@ bool string_set_contains(const T &set, const wchar_t *val) { /// Check if a variable may not be set using the set command. static bool is_read_only(const wchar_t *val) { - const string_set_t env_read_only = {L"PWD", L"SHLVL", L"history", L"status", L"version", L"pid", L"hostname", L"current_cmd"}; + const string_set_t env_read_only = {L"PWD", L"SHLVL", L"history", L"status", L"version", L"fish_pid", L"hostname", L"current_cmd"}; return string_set_contains(env_read_only, val); } @@ -956,8 +956,8 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { wcstring version = str2wcstring(get_fish_version()); env_set_one(L"version", ENV_GLOBAL, version); - // Set the $pid variable (%self replacement) - env_set_one(L"pid", ENV_GLOBAL, to_string(getpid())); + // Set the $fish_pid variable (%self replacement) + env_set_one(L"fish_pid", ENV_GLOBAL, to_string(getpid())); // Set the $hostname variable wcstring hostname = L"fish"; diff --git a/src/parse_constants.h b/src/parse_constants.h index b84de73e6..c83b548c6 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -260,7 +260,7 @@ void parse_error_offset_source_start(parse_error_list_t *errors, size_t amt); #define ERROR_NOT_STATUS _(L"$? is not the exit status. In fish, please use $status.") /// Error issued on $$. -#define ERROR_NOT_PID _(L"$$ is not the pid. In fish, please use $pid.") +#define ERROR_NOT_PID _(L"$$ is not the pid. In fish, please use $fish_pid.") /// Error issued on $#. #define ERROR_NOT_ARGV_COUNT _(L"$# is not supported. In fish, please use 'count $argv'.") diff --git a/tests/exit.expect b/tests/exit.expect index f6490d2ce..86d7f6510 100644 --- a/tests/exit.expect +++ b/tests/exit.expect @@ -38,7 +38,7 @@ send "exit\r" catch {expect default exp_continue} output wait -# Verify all child processes have been killed. We don't use `-p $pid` because +# Verify all child processes have been killed. We don't use `-p $fish_pid` because # if the shell has a bug the child processes might have been reparented to pid # 1 rather than killed. set status [catch {exec pgrep -l -f "sleep 11"} output] diff --git a/tests/signals.expect b/tests/signals.expect index dc7991b8b..a17ac8902 100644 --- a/tests/signals.expect +++ b/tests/signals.expect @@ -12,13 +12,13 @@ expect_prompt send "sleep 131 &\r" expect_prompt send "sleep 132\r" -exec -- kill -HUP $pid +exec -- kill -HUP $fish_pid # Verify the spawned fish shell has exited. catch {expect default exp_continue} output wait -# Verify all child processes have been killed. We don't use `-p $pid` because +# Verify all child processes have been killed. We don't use `-p $fish_pid` because # if the shell has a bug the child processes might have been reparented to pid # 1 rather than killed. set status [catch {exec pgrep -l -f "sleep 13"} output] From 515fc509ec2b7ead2f946df837f934f2dfeef451 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 24 Mar 2018 11:58:19 -0500 Subject: [PATCH 009/159] Deprecate $_ in favor of `(status current-command)` Closes #813. --- CHANGELOG.md | 1 + doc_src/index.hdr.in | 4 ++-- src/builtin_fg.cpp | 3 ++- src/builtin_status.cpp | 8 ++++++++ src/env.cpp | 2 +- src/reader.cpp | 11 +++++++---- 6 files changed, 21 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2ac6f694..9a7b89471 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ This section is for changes merged to the `major` branch that are not also merge ## Deprecations - The `IFS` variable is deprecated and will be removed in fish 4.0 (#4156). - The `function --on-process-exit` event will be removed in future (#4700). Use the `fish_exit` event instead. +- `$_` is deprecated and will removed in the future (#813). Use `status current-command` in a subshell instead. ## Notable non-backward compatible changes - `.` command no longer exists -- use `source` (#4294). diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index df1d01288..4ebb350c2 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1297,14 +1297,14 @@ To customize the syntax highlighting, you can set the environment variables list \subsection title Programmable title -When using most virtual terminals, it is possible to set the message displayed in the titlebar of the terminal window. This can be done automatically in fish by defining the `fish_title` function. The `fish_title` function is executed before and after a new command is executed or put into the foreground and the output is used as a titlebar message. The $_ environment variable will always contain the name of the job to be put into the foreground (Or 'fish' if control is returning to the shell) when the `fish_prompt` function is called. The first argument to fish_title will contain the most recently executed foreground command as a string, starting with fish 2.2. +When using most virtual terminals, it is possible to set the message displayed in the titlebar of the terminal window. This can be done automatically in fish by defining the `fish_title` function. The `fish_title` function is executed before and after a new command is executed or put into the foreground and the output is used as a titlebar message. The `status current-command` builtin will always return the name of the job to be put into the foreground (or 'fish' if control is returning to the shell) when the `fish_prompt` function is called. The first argument to fish_title will contain the most recently executed foreground command as a string, starting with fish 2.2. Examples: The default `fish` title is \fish function fish_title - echo $_ ' ' + echo (status current-command) ' ' pwd end \endfish diff --git a/src/builtin_fg.cpp b/src/builtin_fg.cpp index df9ce328e..2c838b949 100644 --- a/src/builtin_fg.cpp +++ b/src/builtin_fg.cpp @@ -102,7 +102,8 @@ int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } const wcstring ft = tok_first(j->command()); - if (!ft.empty()) env_set_one(L"current_cmd", ENV_EXPORT, ft); + //For compatibility with fish 2.0's $_, now replaced with `status current-command` + if (!ft.empty()) env_set_one(L"_", ENV_EXPORT, ft); reader_write_title(j->command()); job_promote(j); diff --git a/src/builtin_status.cpp b/src/builtin_status.cpp index 4b1bea72f..c498dd3bd 100644 --- a/src/builtin_status.cpp +++ b/src/builtin_status.cpp @@ -25,6 +25,7 @@ enum status_cmd_t { STATUS_IS_FULL_JOB_CTRL, STATUS_IS_INTERACTIVE_JOB_CTRL, STATUS_IS_NO_JOB_CTRL, + STATUS_CURRENT_CMD, STATUS_FILENAME, STATUS_FUNCTION, STATUS_LINE_NUMBER, @@ -35,6 +36,7 @@ enum status_cmd_t { // Must be sorted by string, not enum or random. const enum_map status_enum_map[] = { + {STATUS_CURRENT_CMD, L"current-command"}, {STATUS_FILENAME, L"current-filename"}, {STATUS_FUNCTION, L"current-function"}, {STATUS_LINE_NUMBER, L"current-line-number"}, @@ -374,6 +376,12 @@ int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { streams.out.append(parser.stack_trace()); break; } + case STATUS_CURRENT_CMD: { + CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) + streams.out.append(program_name); + streams.out.push_back(L'\n'); + break; + } } return retval; diff --git a/src/env.cpp b/src/env.cpp index 2783b2eb0..568d392af 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -322,7 +322,7 @@ bool string_set_contains(const T &set, const wchar_t *val) { /// Check if a variable may not be set using the set command. static bool is_read_only(const wchar_t *val) { - const string_set_t env_read_only = {L"PWD", L"SHLVL", L"history", L"status", L"version", L"fish_pid", L"hostname", L"current_cmd"}; + const string_set_t env_read_only = {L"PWD", L"SHLVL", L"history", L"status", L"version", L"fish_pid", L"hostname", L"_"}; return string_set_contains(env_read_only, val); } diff --git a/src/reader.cpp b/src/reader.cpp index 843530d1b..29cfe81c8 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -99,7 +99,7 @@ #define MODE_PROMPT_FUNCTION_NAME L"fish_mode_prompt" /// The default title for the reader. This is used by reader_readline. -#define DEFAULT_TITLE L"echo $_ \" \"; __fish_pwd" +#define DEFAULT_TITLE L"echo (status current-command) \" \"; __fish_pwd" /// The maximum number of characters to read from the keyboard without repainting. Note that this /// readahead will only occur if new characters are available for reading, fish will never block for @@ -1620,7 +1620,8 @@ static void reader_interactive_init() { invalidate_termsize(); - env_set_one(L"current_cmd", ENV_GLOBAL, L"fish"); + //For compatibility with fish 2.0's $_, now replaced with `status current-command` + env_set_one(L"_", ENV_GLOBAL, L"fish"); } /// Destroy data for interactive use. @@ -1897,7 +1898,8 @@ void reader_run_command(parser_t &parser, const wcstring &cmd) { wcstring ft = tok_first(cmd); - if (!ft.empty()) env_set_one(L"current_cmd", ENV_GLOBAL, ft); + //For compatibility with fish 2.0's $_, now replaced with `status current-command` + if (!ft.empty()) env_set_one(L"_", ENV_GLOBAL, ft); reader_write_title(cmd); @@ -1913,7 +1915,8 @@ void reader_run_command(parser_t &parser, const wcstring &cmd) { term_steal(); - env_set_one(L"current_cmd", ENV_GLOBAL, program_name); + //For compatibility with fish 2.0's $_, now replaced with `status current-command` + env_set_one(L"_", ENV_GLOBAL, program_name); #ifdef HAVE__PROC_SELF_STAT proc_update_jiffies(); From 8bbecb66cf07b145f0183ceb617d5959c6452366 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 24 Mar 2018 12:13:58 -0500 Subject: [PATCH 010/159] Fix accidental rename of --pid parameter to `jobs` builtin --- src/builtin_jobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index d82bdb1a3..019682c4e 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -125,7 +125,7 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { {L"group", no_argument, NULL, 'g'}, {L"help", no_argument, NULL, 'h'}, {L"last", no_argument, NULL, 'l'}, - {L"fish_pid", no_argument, NULL, 'p'}, + {L"pid", no_argument, NULL, 'p'}, {L"quiet", no_argument, NULL, 'q'}, {nullptr, 0, NULL, 0}}; From c0535b4e1349e8709f9de4e4420de9934385e968 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 24 Mar 2018 12:15:56 -0500 Subject: [PATCH 011/159] Fix $pid -> $fish_pid in signals.expect This was a expect variable, not a fish one. --- tests/signals.expect | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/signals.expect b/tests/signals.expect index a17ac8902..dc7991b8b 100644 --- a/tests/signals.expect +++ b/tests/signals.expect @@ -12,13 +12,13 @@ expect_prompt send "sleep 131 &\r" expect_prompt send "sleep 132\r" -exec -- kill -HUP $fish_pid +exec -- kill -HUP $pid # Verify the spawned fish shell has exited. catch {expect default exp_continue} output wait -# Verify all child processes have been killed. We don't use `-p $fish_pid` because +# Verify all child processes have been killed. We don't use `-p $pid` because # if the shell has a bug the child processes might have been reparented to pid # 1 rather than killed. set status [catch {exec pgrep -l -f "sleep 13"} output] From 517b77ca74821d2cd6c0fcd89f04aaaf8d3c76c7 Mon Sep 17 00:00:00 2001 From: Aaron Miller Date: Sat, 24 Mar 2018 15:37:15 -0400 Subject: [PATCH 012/159] Fix handling of signals (#4851) --- src/exec.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/exec.cpp b/src/exec.cpp index 2b13367ec..bb50371f2 100644 --- a/src/exec.cpp +++ b/src/exec.cpp @@ -360,10 +360,8 @@ static bool can_use_posix_spawn_for_job(const job_t *job, const process_t *proce void internal_exec(job_t *j, const io_chain_t &&all_ios) { // Do a regular launch - but without forking first... - signal_block(); - // setup_child_process makes sure signals are properly set up. It will also call - // signal_unblock. + // setup_child_process makes sure signals are properly set up. // PCA This is for handling exec. Passing all_ios here matches what fish 2.0.0 and 1.x did. // It's known to be wrong - for example, it means that redirections bound for subsequent From 669eafb55f1a6eff3b72bec73470bb31d4497cb3 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 24 Mar 2018 23:32:55 -0700 Subject: [PATCH 013/159] Stop exporting empty variables as ENV_NULL Localize the encoding of empty variables as ENV_NULL into the universal variables component, and ensure they are not exported as ENV_NULL. Fixes #4846 --- src/common.cpp | 27 ++++++++++++++++++ src/common.h | 6 ++++ src/env.cpp | 53 ++++-------------------------------- src/env.h | 9 ------ src/env_universal_common.cpp | 19 +++++++++++-- tests/set.err | 3 ++ tests/set.in | 6 ++++ tests/set.out | 6 ++++ 8 files changed, 71 insertions(+), 58 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index fbee108ff..f20f5206f 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1791,6 +1791,33 @@ bool contains(const wcstring_list_t &list, const wcstring &str) { return std::find(list.begin(), list.end(), str) != list.end(); } +wcstring_list_t split_string(const wcstring &val, wchar_t sep) { + wcstring_list_t out; + size_t pos = 0, end = val.size(); + while (pos <= end) { + size_t next_pos = val.find(sep, pos); + if (next_pos == wcstring::npos) { + next_pos = end; + } + out.emplace_back(val, pos, next_pos - pos); + pos = next_pos + 1; // skip the separator, or skip past the end + } + return out; +} + +wcstring join_strings(const wcstring_list_t &vals, wchar_t sep) { + wcstring result; + bool first = true; + for (const wcstring &s : vals) { + if (!first) { + result.push_back(sep); + } + result.append(s); + first = false; + } + return result; +} + int create_directory(const wcstring &d) { bool ok = false; struct stat buf; diff --git a/src/common.h b/src/common.h index 9b84f7625..765412ca5 100644 --- a/src/common.h +++ b/src/common.h @@ -326,6 +326,12 @@ bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &valu bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, const wcstring &value); +/// Split a string by a separator character. +wcstring_list_t split_string(const wcstring &val, wchar_t sep); + +/// Join a list of strings by a separator character. +wcstring join_strings(const wcstring_list_t &vals, wchar_t sep); + enum fuzzy_match_type_t { // We match the string exactly: FOOBAR matches FOOBAR. fuzzy_match_exact = 0, diff --git a/src/env.cpp b/src/env.cpp index 568d392af..a6189def6 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -109,30 +109,6 @@ static const wcstring_list_t curses_variables({L"TERM", L"TERMINFO", L"TERMINFO_ static void init_locale(); static void init_curses(); -/// This is used to convert a serialized env_var_t back into a list. It is used when reading legacy -/// (fish 2.x) encoded vars stored in the universal variable file and the environment. -static wcstring_list_t tokenize_variable_array(const wcstring &val) { - // Zero element arrays are externally encoded as this placeholder string. - if (val == ENV_NULL) return {}; - - wcstring_list_t out; - size_t pos = 0, end = val.size(); - while (pos <= end) { - size_t next_pos = val.find(ARRAY_SEP, pos); - if (next_pos == wcstring::npos) { - next_pos = end; - } - out.emplace_back(val, pos, next_pos - pos); - pos = next_pos + 1; // skip the separator, or skip past the end - } - return out; -} - -/// This is used to convert a serialized env_var_t back into a list. -wcstring_list_t decode_serialized(const wcstring &s) { - return tokenize_variable_array(s); -} - // Struct representing one level in the function variable stack. // Only our variable stack should create and destroy these class env_node_t { @@ -916,14 +892,8 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { key.assign(key_and_val, 0, eql); if (is_read_only(key) || is_electric(key)) continue; val.assign(key_and_val, eql + 1, wcstring::npos); - if (variable_is_colon_delimited_var(key)) { - std::replace(val.begin(), val.end(), L':', ARRAY_SEP); - wcstring_list_t values = decode_serialized(val); - env_set(key, ENV_EXPORT | ENV_GLOBAL, values); - } else { - wcstring_list_t values = decode_serialized(val); - env_set(key, ENV_EXPORT | ENV_GLOBAL, values); - } + wchar_t sep = variable_is_colon_delimited_var(key) ? L':' : ARRAY_SEP; + env_set(key, ENV_EXPORT | ENV_GLOBAL, split_string(val, sep)); } } @@ -1322,16 +1292,8 @@ const wcstring_list_t &env_var_t::as_list() const { return vals; } /// Return a string representation of the var. At the present time this uses the legacy 2.x /// encoding. wcstring env_var_t::as_string() const { - if (this->vals.empty()) return wcstring(ENV_NULL); - wchar_t sep = (flags & flag_colon_delimit) ? L':' : ARRAY_SEP; - auto it = this->vals.cbegin(); - wcstring result(*it); - while (++it != vals.end()) { - result.push_back(sep); - result.append(*it); - } - return result; + return join_strings(vals, sep); } void env_var_t::to_list(wcstring_list_t &out) const { @@ -1539,16 +1501,13 @@ static void export_func(const var_table_t &envs, std::vector &out) // Replace ARRAY_SEP with colon. std::replace(vs.begin(), vs.end(), (char)ARRAY_SEP, ':'); } - - // Put a string on the vector. - out.push_back(std::string()); - std::string &str = out.back(); + // Create and append a string of the form ks=vs + std::string str; str.reserve(ks.size() + 1 + vs.size()); - - // Append our environment variable data to it. str.append(ks); str.append("="); str.append(vs); + out.push_back(std::move(str)); } } diff --git a/src/env.h b/src/env.h index 543b4dd11..7ebafb51a 100644 --- a/src/env.h +++ b/src/env.h @@ -20,12 +20,6 @@ extern bool curses_initialized; /// that seems logical. #define ARRAY_SEP (wchar_t)0x1e -/// String containing the character for separating two array elements. -#define ARRAY_SEP_STR L"\x1e" - -/// Value denoting a null string. -#define ENV_NULL L"\x1d" - // Flags that may be passed as the 'mode' in env_set / env_get. enum { /// Default mode. Used with `env_get()` to indicate the caller doesn't care what scope the var @@ -122,9 +116,6 @@ class env_var_t { bool operator!=(const env_var_t &var) const { return vals != var.vals; } }; -/// This is used to convert a serialized env_var_t back into a list. -wcstring_list_t decode_serialized(const wcstring &s); - /// Gets the variable with the specified name, or none() if it does not exist. maybe_t env_get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT); diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 758c16725..643078f3e 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -255,6 +255,21 @@ static bool append_file_entry(fish_message_type_t type, const wcstring &key_in, return success; } +/// Encoding of a null string. +static const wchar_t *ENV_NULL = L"\x1d"; + +/// Decode a serialized universal variable value into a list. +static wcstring_list_t decode_serialized(const wcstring &val) { + if (val == ENV_NULL) return {}; + return split_string(val, ARRAY_SEP); +} + +/// Decode a a list into a serialized universal variable value. +static wcstring encode_serialized(const wcstring_list_t &vals) { + if (vals.empty()) return ENV_NULL; + return join_strings(vals, ARRAY_SEP); +} + env_universal_t::env_universal_t(wcstring path) : explicit_vars_path(std::move(path)), tried_renaming(false), last_read_file(kInvalidFileID) {} @@ -448,8 +463,8 @@ bool env_universal_t::write_to_fd(int fd, const wcstring &path) { // variable; soldier on. const wcstring &key = iter->first; const env_var_t &var = iter->second; - append_file_entry(var.exports() ? SET_EXPORT : SET, key, var.as_string(), &contents, - &storage); + append_file_entry(var.exports() ? SET_EXPORT : SET, key, encode_serialized(var.as_list()), + &contents, &storage); // Go to next. ++iter; diff --git a/tests/set.err b/tests/set.err index 9f4b92c4d..196a395a2 100644 --- a/tests/set.err +++ b/tests/set.err @@ -17,3 +17,6 @@ $argle bargle: invalid var name #################### # Setting local scope when no local scope of the var uses the closest scope + +#################### +# Exporting works diff --git a/tests/set.in b/tests/set.in index 8893210d3..0fdcbe2eb 100644 --- a/tests/set.in +++ b/tests/set.in @@ -54,3 +54,9 @@ begin set -l -a var6 mno set --show var6 end + +logmsg Exporting works +set -x TESTVAR0 +set -x TESTVAR1 a +set -x TESTVAR2 a b +env | grep TESTVAR | cat -v diff --git a/tests/set.out b/tests/set.out index 29e8bf82c..3d4535bab 100644 --- a/tests/set.out +++ b/tests/set.out @@ -100,3 +100,9 @@ $var6[1]: length=3 value=|ghi| $var6[2]: length=3 value=|jkl| $var6: not set in universal scope + +#################### +# Exporting works +TESTVAR0= +TESTVAR1=a +TESTVAR2=a^^b From ad5bbeb3c2556445105887861b0878dbcd62cc36 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sun, 25 Mar 2018 10:19:57 +0200 Subject: [PATCH 014/159] Make wcwidth configurable (#4816) * Make wcwidth configurable This adds the cmake option "INTERNAL_WCWIDTH" (to be set to "ON" or "OFF") and the configure option --[en,dis]able-internal-wcwidth. Both default to enabling our fallback, but can be set to use the system's wcwidth again. Sequel to #4554. See #4571, #4539, #4609. On my system, this would fix #4306. --- CMakeLists.txt | 7 +++++++ configure.ac | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73d162ffe..a01397a17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,13 @@ SET_SOURCE_FILES_PROPERTIES(src/fish_version.cpp PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FBVF}) +OPTION(INTERNAL_WCWIDTH "use fallback wcwidth" ON) +IF(INTERNAL_WCWIDTH) + add_definitions(-DHAVE_BROKEN_WCWIDTH=1) +ELSE() + add_definitions(-DHAVE_BROKEN_WCWIDTH=0) +ENDIF() + # Set up PCRE2 INCLUDE(cmake/PCRE2.cmake) diff --git a/configure.ac b/configure.ac index c41ee59f8..39e6de148 100644 --- a/configure.ac +++ b/configure.ac @@ -25,6 +25,7 @@ AC_SUBST(HAVE_DOXYGEN) AC_SUBST(LDFLAGS_FISH) AC_SUBST(WCHAR_T_BITS) AC_SUBST(EXTRA_PCRE2) +AC_SUBST(HAVE_BROKEN_WCWIDTH) # # If needed, run autoconf to regenerate the configure file @@ -599,6 +600,20 @@ AC_ARG_WITH( [included_pcre2=auto] ) +HAVE_BROKEN_WCWIDTH= +AC_ARG_ENABLE( + [wcwidth], + AS_HELP_STRING( + [--disable-internal-wcwidth], + [use system wcwidth instead of the bundled version] + )) + +if test "x$enable_wcwidth" != "xno"; then + AC_DEFINE([HAVE_BROKEN_WCWIDTH], [1], [banana]) +else + AC_DEFINE([HAVE_BROKEN_WCWIDTH], [0], [banana]) +fi + if test "x$included_pcre2" != "xyes"; then # test for pcre2-config From c116843611edb0daf0c0d3ac3349b04b52fc7219 Mon Sep 17 00:00:00 2001 From: "Luc J. Bourhis" Date: Mon, 26 Mar 2018 17:07:23 +0200 Subject: [PATCH 015/159] Completion for conda, the package manager (#4837) * Completion for conda, the package manager * Make the list of platforms a private variable * Add commands activate and deactivate * Avoid clobbering a user-defined function __ * Use Use __fish_seen_subcommand_from to identify subcommand And treat the case of the first argument as a special case with function __fish_conda_fist_arg * Factor out create from loop for option --name * Fix typo (missing parenthesis in description) * Start from a blank state by removing completions from conda configuration script --- share/completions/conda.fish | 296 +++++++++++++++++++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 share/completions/conda.fish diff --git a/share/completions/conda.fish b/share/completions/conda.fish new file mode 100644 index 000000000..6a15256ce --- /dev/null +++ b/share/completions/conda.fish @@ -0,0 +1,296 @@ +# Fish shell completions for conda, the package management system +# https://conda.io/docs +# Based on `conda help` as per version 4.4.11 +# Advanced completions: +# - completion of already defined keys for `conda config` +# - completion of already created environment where possible +# +# Note: conda ships with a completion for fish, automatically generated +# from `conda -h` but it is far cruder than the completions in this file, +# and it is by nature far too tricky to tweak to get the desired richness. +# Hence this effort. + +# First, as part of conda's configuration, some complete would have been defined +# Let's erase them, so that we start from a blank state +complete -c conda -e + +# Complete using -n to select the given conda subcommand +# and passing the rest of the arguments to `complete` +# The goal here is to reduce clutter in the definitions below +function __fish_conda -a cmd + complete -c conda -n "__fish_seen_subcommand_from $cmd" $argv[2..-1] +end + +# Complete for the first argument only +function __fish_conda_top + complete -c conda -n "test (count (commandline -opc)) -eq 1" $argv +end + +function __fish_conda_config_keys + conda config --show | string match -r '^\w+(?=:)' +end + +function __fish_conda_environments + conda env list | string match -rv '^#' | string match -r '^\w+' +end + +# common options +complete -c conda -f +complete -c conda -s h -l help -d "Show help and exit" + +# top-level options +__fish_conda_top -s V -l version -d "Show the conda version number and exit" + +# top-level commands +__fish_conda_top -a clean -d "Remove unused packages and caches" +__fish_conda_top -a config -d "Modify configuration values in .condarc" +__fish_conda_top -a create -d "Create a new conda environment from a list of specified packages" +__fish_conda_top -a help -d "Displays a list of available conda commands and their help strings" +__fish_conda_top -a info -d "Display information about current conda install" +__fish_conda_top -a install -d "Installs a list of packages into a specified conda environment" +__fish_conda_top -a list -d "List linked packages in a conda environment" +__fish_conda_top -a package -d "Low-level conda package utility (EXPERIMENTAL)" +__fish_conda_top -a remove -d "Remove a list of packages from a specified conda environment" +__fish_conda_top -a uninstall -d "Alias for conda remove" +__fish_conda_top -a search -d "Search for packages and display associated information" +__fish_conda_top -a update -d "Updates conda packages to the latest compatible version" +__fish_conda_top -a upgrade -d "Alias for conda update" + +# command added by sourcing ~/miniconda3/etc/fish/conf.d/conda.fish, +# which is the recommended way to use conda with fish +__fish_conda_top -a activate -d "Activate the given environment" +__fish_conda activate -x -a "(__fish_conda_environments)" +__fish_conda_top -a deactivate -d "Deactivate current environment, reactivating the previous one" + +# common to all top-level commands + +set -l __fish_conda_commands clean config create help info install list package remove uninstall search update upgrade +for cmd in $__fish_conda_commands + __fish_conda $cmd -l json -d "Report all output as json" + __fish_conda $cmd -l debug -d "Show debug output" + __fish_conda $cmd -l verbose -s v -d "Use once for info, twice for debug, three times for trace" +end + +# 'clean' command +__fish_conda clean -s y -l yes -d "Do not ask for confirmation" +__fish_conda clean -l dry-run -d "Only display what would have been done" +__fish_conda clean -s q -l quiet -d "Do not display progress bar" +__fish_conda clean -s a -l all -d "Remove all: same as -iltps" +__fish_conda clean -s i -l index-cache -d "Remove index cache" +__fish_conda clean -s l -l lock -d "Remove all conda lock files" +__fish_conda clean -s t -l tarballs -d "Remove cached package tarballs" +__fish_conda clean -s p -l packages -d "Remove unused cached packages (no check for symlinks)" +__fish_conda clean -s s -l source-cache -d "Remove files from the source cache of conda build" + +# 'config' command + +__fish_conda config -l system -d "Write to the system .condarc file" +__fish_conda config -l env -d "Write to the active conda environment .condarc file" +__fish_conda config -l file -d "Write to the given file" +__fish_conda config -l show -x -a "(__fish_conda_config_keys)" -d "Display configuration values" +__fish_conda config -l show-sources -d "Display all identified configuration sources" +__fish_conda config -l validate -d "Validate all configuration sources" +__fish_conda config -l describe -x -a "(__fish_conda_config_keys)" -d "Describe configuration parameters" +__fish_conda config -l write-default -d "Write the default configuration to a file" +__fish_conda config -l get -x -a "(__fish_conda_config_keys)" -d "Get a configuration value" +__fish_conda config -l append -d "Add one configuration value to the end of a list key" +__fish_conda config -l prepend -d "Add one configuration value to the beginning of a list key" +__fish_conda config -l add -d "Alias for --prepend" +__fish_conda config -l set -x -a "(__fish_conda_config_keys)" -d "Set a boolean or string key" +__fish_conda config -l remove -x -a "(__fish_conda_config_keys)" -d "Remove a configuration value from a list key" +__fish_conda config -l remove-key -x -a "(__fish_conda_config_keys)" -d "Remove a configuration key (and all its values)" +__fish_conda config -l stdin -d "Apply configuration given in yaml format from stdin" + +# 'help' command +__fish_conda "help" -d "Displays a list of available conda commands and their help strings" +__fish_conda "help" -x -a "$__fish_conda_commands" + +# 'info' command +__fish_conda info -l offline -d "Offline mode, don't connect to the Internet." +__fish_conda info -s a -l all -d "Show all information, (environments, license, and system information)" +__fish_conda info -s e -l envs -d "List all known conda environments" +__fish_conda info -s l -l license -d "Display information about the local conda licenses list" +__fish_conda info -s s -l system -d "List environment variables" +__fish_conda info -l base -d "Display base environment path" +__fish_conda info -l unsafe-channels -d "Display list of channels with tokens exposed" + +# The remaining commands share many options, so the definitions are written the other way around: +# the outer loop is on the options + +# Option channel +for cmd in create install remove search update + __fish_conda $cmd -s c -l channel -d 'Additional channel to search for packages' +end + +# Option channel-priority +for cmd in create install update + __fish_conda $cmd -l channel-priority -d 'Channel priority takes precedence over package version' +end + +# Option clobber +for cmd in create install update + __fish_conda $cmd -l clobber -d 'Allow clobbering of overlapping file paths (no warnings)' +end + +# Option clone +__fish_conda create -l clone -x -a "(__fish_conda_environments)" -d "Path to (or name of) existing local environment" + +# Option copy +for cmd in create install update + __fish_conda $cmd -l copy -d 'Install all packages using copies instead of hard- or soft-linking' +end + +# Option download-only +for cmd in create install update + __fish_conda $cmd -l download-only -d 'Solve an environment: populate caches but no linking/unlinking into prefix' +end + +# Option dry-run +for cmd in create install remove update + __fish_conda $cmd -l dry-run -d 'Only display what would have been done' +end + +# Option file +for cmd in create install update + __fish_conda $cmd -l file -d 'Read package versions from the given file' +end + +# Option force +for cmd in create install remove update + __fish_conda $cmd -l force -d 'Force install (even when package already installed)' +end + +# Option insecure +for cmd in create install remove search update + __fish_conda $cmd -l insecure -d 'Allow conda to perform "insecure" SSL connections and transfers' +end + +# Option mkdir +for cmd in create install update + __fish_conda $cmd -l mkdir -d 'Create the environment directory if necessary' +end + +# Option name +__fish_conda create -s n -l name -d "Name of new environment" +for cmd in install list remove search update + __fish_conda $cmd -s n -l name -x -a "(__fish_conda_environments)" -d "Name of existing environment" +end + +# Option no-channel-priority +for cmd in create install update + __fish_conda $cmd -l no-channel-priority -l no-channel-pri -l no-chan-pri -d 'Package version takes precedence over channel priority' +end + +# Option no-default-packages +__fish_conda create -l no-default-packages -d 'Ignore create_default_packages in the .condarc file' + +# Option no-deps +for cmd in create install update + __fish_conda $cmd -l no-deps -d 'Do not install, update, remove, or change dependencies' +end + +# Option no-pin +for cmd in create install remove update + __fish_conda $cmd -l no-pin -d 'Ignore pinned file' +end + +# Option no-show-channel-urls +for cmd in create install list update + __fish_conda $cmd -l no-show-channel-urls -d "Don't show channel urls" +end + +# Option no-update-dependencies +for cmd in create install update + __fish_conda $cmd -l no-update-dependencies -l no-update-deps -d "Don't update dependencies" +end + +# Option offline +for cmd in create install remove search update + __fish_conda $cmd -l offline -d "Offline mode, don't connect to the Internet" +end + +# Option only-deps +for cmd in create install update + __fish_conda $cmd -l only-deps -d "Only install dependencies" +end + +# Option override-channels +for cmd in create install remove search update + __fish_conda $cmd -l override-channels -d "Do not search default or .condarc channels" +end + +# Option prefix +for cmd in create install list remove search update + __fish_conda $cmd -s p -l prefix -d "Full path to environment prefix" +end + +# Option quiet +for cmd in create install remove update + __fish_conda $cmd -s q -l quiet -d "Do not display progress bar" +end + +# Option show-channel-urls +for cmd in create install list update + __fish_conda $cmd -l show-channel-urls -d "Show channel urls" +end + +# Option update-dependencies +for cmd in create install update + __fish_conda $cmd -l update-dependencies -l update-deps -d "Update dependencies" +end + +# Option use-index-cache +for cmd in create install remove search update + __fish_conda $cmd -s C -l use-index-cache -d "Use cache of channel index files, even if it has expired" +end + +# Option use-local +for cmd in create install remove search update + __fish_conda $cmd -l use-local -d "Use locally built packages" +end + +# Option yes +for cmd in create install remove update + __fish_conda $cmd -s y -l yes -d "Do not ask for confirmation" +end + +# Option revision +__fish_conda install -l revision -d "Revert to the specified REVISION" + +# Option canonical +__fish_conda list -s c -l canonical -d "Output canonical names of packages only. Implies --no-pip" + +# Option explicit +__fish_conda list -l explicit -d "List explicitly all installed conda with URL (output usable by conda create --file)" + +# Option export +__fish_conda list -s e -l export -d "Output requirement string only (output usable by conda create --file)" + +# Option full-name +__fish_conda list -s f -l full-name -d "Only search for full names, i.e., ^\$" + +# Option md5 +__fish_conda list -l md5 -d "Add MD5 hashsum when using --explicit" + +# Option no-pip +__fish_conda list -s c -l canonical -d "Output canonical names of packages only. Implies --no-pip" + +# Option revisions +__fish_conda list -s r -l revisions -d "List the revision history and exit" + +# Option all +__fish_conda remove -l all -d "Remove all packages, i.e., the entire environment" +__fish_conda update -l all -d "Update all installed packages in the environment" + +# Option features +__fish_conda remove -l features -d "Remove features (instead of packages)" + +# Option info +__fish_conda search -s i -l info -d "Provide detailed information about each package" + +# Option platform +set -l __fish_conda_platforms {osx,linux,win}-{32,64} +__fish_conda search -l platform -x -a "$__fish_conda_platforms" -d "Search the given platform" + +# Option reverse-dependency +__fish_conda search -l reverse-dependency -d "Perform a reverse dependency search" From adbaddfaf4ec3c1c6f3d149d595d15704a1a4ea6 Mon Sep 17 00:00:00 2001 From: Frederick Akalin Date: Tue, 27 Mar 2018 23:35:41 -0700 Subject: [PATCH 016/159] Fix reading of /etc/paths on OS X (and /etc/paths.d/*) Do so by emulating the behavior of /usr/libexec/path_helper for login shells, matching the behavior in /etc/profile. Also add a path_helper command to reproduce the behavior of /usr/libexec/path_helper for fish. This also handles setting MANPATH if necessary. Fixes issue #4336 --- share/config.fish | 91 +++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 54 deletions(-) diff --git a/share/config.fish b/share/config.fish index dddecae2a..c9a999fdf 100644 --- a/share/config.fish +++ b/share/config.fish @@ -126,59 +126,7 @@ if test -d /usr/xpg4/bin end end -# OS X-ism: Load the path files out of /etc/paths and /etc/paths.d/* -set -g __fish_tmp_path $PATH -function __fish_load_path_helper_paths - # We want to rearrange the path to reflect this order. Delete that path component if it exists and then prepend it. - # Since we are prepending but want to preserve the order of the input file, we reverse the array, append, and then reverse it again - set __fish_tmp_path $__fish_tmp_path[-1..1] - while read -l new_path_comp - if test -d $new_path_comp - set -l where (contains -i -- $new_path_comp $__fish_tmp_path) - and set -e __fish_tmp_path[$where] - set __fish_tmp_path $new_path_comp $__fish_tmp_path - end - end - set __fish_tmp_path $__fish_tmp_path[-1..1] -end -test -r /etc/paths -and __fish_load_path_helper_paths Date: Wed, 28 Mar 2018 14:27:25 -0500 Subject: [PATCH 017/159] Use alternative Unicode glyphs if compiled under Windows/WSL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two unicode glyphs used to represent missing new lines and redacted characters for secure entry are both not present in the glyph tables of the default font under Windows (Consolas and Lucida Console), use an alternative glyph instead. The "return" symbol is replaced with a pilcrow (¶) and the "redacted character" symbol is replaced with a bullet (•). Both of these are well-defined in almost all fonts as they're very old symbols. This change only takes place if -DWSL is supplied by the build toolchain. Note: this means a Windows SSH client connecting to a fish remote instance on a non-Windows machine will still use the (unavailable) default glyphs instead. --- src/common.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/common.cpp b/src/common.cpp index f20f5206f..d39a8a5cb 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -525,8 +525,16 @@ void fish_setlocale() { ellipsis_char = L'$'; // "horizontal ellipsis" ellipsis_str = L"..."; } - omitted_newline_char = can_be_encoded(L'\u23CE') ? L'\u23CE' : L'~'; // "return" - obfuscation_read_char = can_be_encoded(L'\u25CF') ? L'\u25CF' : L'#'; // "black circle" + if (is_windows_subsystem_for_linux()) { + //neither of \u23CE and \u25CF can be displayed in the default fonts on Windows, though + //they can be *encoded* just fine. Use alternative glyphs. + omitted_newline_char = can_be_encoded(L'\u00b6') ? L'\u00b6' : L'~'; // "pilcrow" + obfuscation_read_char = can_be_encoded(L'\u2022') ? L'\u2022' : L'*'; // "bullet" + } + else { + omitted_newline_char = can_be_encoded(L'\u23CE') ? L'\u23CE' : L'~'; // "return" + obfuscation_read_char = can_be_encoded(L'\u25CF') ? L'\u25CF' : L'#'; // "black circle" + } } long read_blocked(int fd, void *buf, size_t count) { From 999728670d7df3b0e6bad4af2a6117f4bdd88a89 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 29 Mar 2018 08:12:08 -0500 Subject: [PATCH 018/159] Default `string split` to keeping empty entries with option to remove The official fish documentation makes no mention of how `string split` treats empty tokens, e.g. splitting 'key1##key2' on '#' or (more confusingly) splitting '/path' on '/'. With this commit, `string split` now has an option to exclude zero-length substrings from the resulting array with a new `--no-empty/-n`. The default behavior of preserving empty entries is kept so as to avoid breakage. --- src/builtin_string.cpp | 14 +++++++++++--- src/wcstringutil.h | 7 +++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 0a86f71d1..3383ad632 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -147,6 +147,7 @@ typedef struct { //!OCLINT(too many fields) bool right_valid = false; bool start_valid = false; bool style_valid = false; + bool no_empty_valid = false; bool all = false; bool entire = false; @@ -160,6 +161,7 @@ typedef struct { //!OCLINT(too many fields) bool quiet = false; bool regex = false; bool right = false; + bool no_empty = false; long count = 0; long length = 0; @@ -314,6 +316,9 @@ static int handle_flag_n(wchar_t **argv, parser_t &parser, io_streams_t &streams } else if (opts->no_quoted_valid) { opts->no_quoted = true; return STATUS_CMD_OK; + } else if (opts->no_empty_valid) { + opts->no_empty = true; + return STATUS_CMD_OK; } string_unknown_option(parser, streams, argv[0], argv[w.woptind - 1]); return STATUS_INVALID_ARGS; @@ -391,6 +396,7 @@ static wcstring construct_short_opts(options_t *opts) { //!OCLINT(high npath co if (opts->regex_valid) short_opts.append(L"r"); if (opts->right_valid) short_opts.append(L"r"); if (opts->start_valid) short_opts.append(L"s:"); + if (opts->no_empty_valid) short_opts.append(L"n"); return short_opts; } @@ -407,6 +413,7 @@ static const struct woption long_options[] = {{L"all", no_argument, NULL, 'a'}, {L"left", no_argument, NULL, 'l'}, {L"length", required_argument, NULL, 'l'}, {L"max", required_argument, NULL, 'm'}, + {L"no-empty", no_argument, NULL, 'n'}, {L"no-newline", no_argument, NULL, 'N'}, {L"no-quoted", no_argument, NULL, 'n'}, {L"quiet", no_argument, NULL, 'q'}, @@ -420,7 +427,7 @@ static std::unordered_map flag_to_function = { {'N', handle_flag_N}, {'a', handle_flag_a}, {'c', handle_flag_c}, {'e', handle_flag_e}, {'f', handle_flag_f}, {'i', handle_flag_i}, {'l', handle_flag_l}, {'m', handle_flag_m}, {'n', handle_flag_n}, {'q', handle_flag_q}, {'r', handle_flag_r}, {'s', handle_flag_s}, - {'v', handle_flag_v}, {1, handle_flag_1}}; + {'v', handle_flag_v}, {1, handle_flag_1} }; /// Parse the arguments for flags recognized by a specific string subcommand. static int parse_opts(options_t *opts, int *optind, int n_req_args, int argc, wchar_t **argv, @@ -1129,6 +1136,7 @@ static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar opts.right_valid = true; opts.max_valid = true; opts.max = LONG_MAX; + opts.no_empty_valid = true; int optind; int retval = parse_opts(&opts, &optind, 1, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; @@ -1144,9 +1152,9 @@ static int string_split(parser_t &parser, io_streams_t &streams, int argc, wchar if (opts.right) { typedef std::reverse_iterator reverser; split_about(reverser(arg_end), reverser(arg), reverser(sep_end), reverser(sep), &splits, - opts.max); + opts.max, opts.no_empty); } else { - split_about(arg, arg_end, sep, sep_end, &splits, opts.max); + split_about(arg, arg_end, sep, sep_end, &splits, opts.max, opts.no_empty); } arg_count++; } diff --git a/src/wcstringutil.h b/src/wcstringutil.h index 75351e38f..907ffb8d7 100644 --- a/src/wcstringutil.h +++ b/src/wcstringutil.h @@ -28,7 +28,7 @@ wcstring_range wcstring_tok(wcstring& str, const wcstring& needle, /// If the needle is empty, split on individual elements (characters). template void split_about(ITER haystack_start, ITER haystack_end, ITER needle_start, ITER needle_end, - wcstring_list_t* output, long max) { + wcstring_list_t* output, long max, bool no_empty = false) { long remaining = max; ITER haystack_cursor = haystack_start; while (remaining > 0 && haystack_cursor != haystack_end) { @@ -41,7 +41,10 @@ void split_about(ITER haystack_start, ITER haystack_end, ITER needle_start, ITER if (split_point == haystack_end) { // not found break; } - output->push_back(wcstring(haystack_cursor, split_point)); + wcstring result = wcstring(haystack_cursor, split_point); + if (!no_empty || result.size() > 0) { + output->push_back(std::move(result)); + } remaining--; // Need to skip over the needle for the next search note that the needle may be empty. haystack_cursor = split_point + std::distance(needle_start, needle_end); From 04b8b35a5671e8ebcf88d1e5ae6ed0415f749f9d Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 29 Mar 2018 08:23:29 -0500 Subject: [PATCH 019/159] Document new `string split --no-empty` option --- CHANGELOG.md | 1 + doc_src/string.txt | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a7b89471..eb5b433f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ This section is for changes merged to the `major` branch that are not also merge - `bind` has a new `--silent` option to ignore bind requests for named keys not available under the current `$TERMINAL` (#4188, #4431) - Globs are faster (#4579) - `string` reads from stdin faster (#4610) +- `string split` supports `-n/--no-empty` to exclude empty strings from the result (#4779) - `cd` tab completions no longer descend into the deepest unambiguous path (#4649) - Setting `$PATH` no longer warns on non-existent directories, allowing for a single $PATH to be shared across machines (e.g. via dotfiles). - `funced` now has a `-s` and `--save` option to automatically save the edited function after successfully editing (#4668). diff --git a/doc_src/string.txt b/doc_src/string.txt index fb19dddc2..04b44884d 100644 --- a/doc_src/string.txt +++ b/doc_src/string.txt @@ -12,7 +12,7 @@ string repeat [(-n | --count) COUNT] [(-m | --max) MAX] [(-N | --no-newline)] [(-q | --quiet)] [STRING...] string replace [(-a | --all)] [(-f | --filter)] [(-i | --ignore-case)] [(-r | --regex)] [(-q | --quiet)] PATTERN REPLACEMENT [STRING...] -string split [(-m | --max) MAX] [(-r | --right)] [(-q | --quiet)] SEP +string split [(-m | --max) MAX] [(-n | --no-empty)] [(-q | --quiet)] [(-r | --right)] SEP [STRING...] string sub [(-s | --start) START] [(-l | --length) LENGTH] [(-q | --quiet)] [STRING...] @@ -89,7 +89,7 @@ Exit status: 0 if at least one replacement was performed, or 1 otherwise. \subsection string-split "split" subcommand -`string split` splits each STRING on the separator SEP, which can be an empty string. If `-m` or `--max` is specified, at most MAX splits are done on each STRING. If `-r` or `--right` is given, splitting is performed right-to-left. This is useful in combination with `-m` or `--max`. Exit status: 0 if at least one split was performed, or 1 otherwise. +`string split` splits each STRING on the separator SEP, which can be an empty string. If `-m` or `--max` is specified, at most MAX splits are done on each STRING. If `-r` or `--right` is given, splitting is performed right-to-left. This is useful in combination with `-m` or `--max`. With `-n` or `--no-empty`, empty results are excluded from consideration (e.g. `hello\n\nworld` would expand to two strings and not three). Exit status: 0 if at least one split was performed, or 1 otherwise. See also `read --delimiter`. From 535617623b92b1d48f8c4b67d49190b9411a5584 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Sat, 31 Mar 2018 12:41:16 +0200 Subject: [PATCH 020/159] Make pager background cover the entire candidate Currently, there are two possibilities for holes in the background: - When there are two candidates with the same meaning (a long and a short option or two candidates with the same description) - When a candidate does not have a description (meaning the color won't continue after it) This changes both so the background just goes on. In addition, it avoids making the background multiple times. Fixes #4866. --- src/pager.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/pager.cpp b/src/pager.cpp index a5f0bfcd3..dc6802308 100644 --- a/src/pager.cpp +++ b/src/pager.cpp @@ -143,21 +143,23 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s bg_color = highlight_spec_search_match; } + auto bg = highlight_make_background(bg_color); // Print the completion part size_t comp_remaining = comp_width; for (size_t i = 0; i < c->comp.size(); i++) { const wcstring &comp = c->comp.at(i); + highlight_spec_t packed_color = + highlight_spec_pager_prefix | bg; + if (i > 0) { - comp_remaining -= print_max(PAGER_SPACER_STRING, highlight_spec_normal, comp_remaining, + comp_remaining -= print_max(PAGER_SPACER_STRING, bg, comp_remaining, true /* has_more */, &line_data); } - highlight_spec_t packed_color = - highlight_spec_pager_prefix | highlight_make_background(bg_color); comp_remaining -= print_max(prefix, packed_color, comp_remaining, !comp.empty(), &line_data); - packed_color = highlight_spec_pager_completion | highlight_make_background(bg_color); + packed_color = highlight_spec_pager_completion | bg; comp_remaining -= print_max(comp, packed_color, comp_remaining, i + 1 < c->comp.size(), &line_data); } @@ -165,9 +167,9 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s size_t desc_remaining = width - comp_width + comp_remaining; if (c->desc_width > 0 && desc_remaining > 4) { highlight_spec_t desc_color = - highlight_spec_pager_description | highlight_make_background(bg_color); + highlight_spec_pager_description | bg; highlight_spec_t punct_color = - highlight_spec_pager_completion | highlight_make_background(bg_color); + highlight_spec_pager_completion | bg; // always have at least two spaces to separate completion and description desc_remaining -= print_max(L" ", punct_color, 2, false, &line_data); @@ -184,7 +186,7 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s desc_remaining -= print_max(L")", punct_color, 1, false, &line_data); } else { // No description, or it won't fit. Just add spaces. - print_max(wcstring(desc_remaining, L' '), 0, desc_remaining, false, &line_data); + print_max(wcstring(desc_remaining, L' '), bg, desc_remaining, false, &line_data); } return line_data; From c0f832a743ac9fba77443837f333e55988f3e863 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 31 Mar 2018 12:01:29 -0700 Subject: [PATCH 021/159] Invoke path_helper on macOS on all sessions, not just login fish reads paths out of /etc/paths.d. Prior to adbaddf it did this on every shell invocation; with adbaddf it does so on only login shells. This change wasn't justified so let's revert this behavior. --- share/config.fish | 68 ++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/share/config.fish b/share/config.fish index c9a999fdf..8c3779a3d 100644 --- a/share/config.fish +++ b/share/config.fish @@ -200,45 +200,47 @@ if not set -q __fish_init_2_3_0 set -U __fish_init_2_3_0 end +# macOS-ism: Emulate calling path_helper. +if command -sq /usr/libexec/path_helper + # Adapt construct_path from the macOS /usr/libexec/path_helper + # executable for fish; see + # https://opensource.apple.com/source/shell_cmds/shell_cmds-203/path_helper/path_helper.c.auto.html . + function __fish_macos_set_env -d "set an environment variable like path_helper does (macOS only)" + set -l result + + for path_file in $argv[2] $argv[3]/* + if test -f $path_file + while read -la entry + if not contains $entry $result + set result $result $entry + end + end <$path_file + end + end + + for entry in $$argv[1] + if not contains $entry $result + set result $result $entry + end + end + + set -xg $argv[1] $result + end + + __fish_macos_set_env 'PATH' '/etc/paths' '/etc/paths.d' + if [ -n "$MANPATH" ] + __fish_macos_set_env 'MANPATH' '/etc/manpaths' '/etc/manpaths.d' + end + functions -e __fish_macos_set_env +end + + # # Some things should only be done for login terminals # This used to be in etc/config.fish - keep it here to keep the semantics # -# Adapt construct_path from the macOS /usr/libexec/path_helper -# executable for fish; see -# https://opensource.apple.com/source/shell_cmds/shell_cmds-203/path_helper/path_helper.c.auto.html . -function __fish_macos_set_env -d "set an environment variable like path_helper does (macOS only)" - set -l result - - for path_file in $argv[2] $argv[3]/* - if test -f $path_file - while read -la entry - if not contains $entry $result - set result $result $entry - end - end <$path_file - end - end - - for entry in $$argv[1] - if not contains $entry $result - set result $result $entry - end - end - - set -xg $argv[1] $result -end - if status --is-login - # macOS-ism: Emulate calling path_helper. - if command -sq /usr/libexec/path_helper - __fish_macos_set_env 'PATH' '/etc/paths' '/etc/paths.d' - if [ -n "$MANPATH" ] - __fish_macos_set_env 'MANPATH' '/etc/manpaths' '/etc/manpaths.d' - end - end - # # Put linux consoles in unicode mode. # From d88866ccf74d2a0686e42bb24c462a022697256f Mon Sep 17 00:00:00 2001 From: slama Date: Sat, 24 Mar 2018 17:34:58 +0900 Subject: [PATCH 022/159] deleted no longer necessary codes due to removing process expansion. --- src/expand.cpp | 390 +------------------------------------------------ 1 file changed, 1 insertion(+), 389 deletions(-) diff --git a/src/expand.cpp b/src/expand.cpp index bf57451ff..38309345c 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -54,33 +54,9 @@ #include "tokenizer.h" #endif -/// Description for child process. -#define COMPLETE_CHILD_PROCESS_DESC _(L"Child process") - -/// Description for non-child process. -#define COMPLETE_PROCESS_DESC _(L"Process") - -/// Description for long job. -#define COMPLETE_JOB_DESC _(L"Job") - -/// Description for short job. The job command is concatenated. -#define COMPLETE_JOB_DESC_VAL _(L"Job: %ls") - -/// Description for the shells own pid. -#define COMPLETE_SELF_DESC _(L"Shell process") - -/// Description for the shells own pid. -#define COMPLETE_LAST_DESC _(L"Last background job") - -/// String in process expansion denoting ourself. -#define SELF_STR L"self" - -/// String in process expansion denoting last background job. -#define LAST_STR L"last" - /// Characters which make a string unclean if they are the first character of the string. See \c /// expand_is_clean(). -#define UNCLEAN_FIRST L"~%" +#define UNCLEAN_FIRST L"~" /// Unclean characters. See \c expand_is_clean(). #define UNCLEAN L"$*?\\\"'({})" @@ -201,370 +177,6 @@ wcstring expand_escape_variable(const env_var_t &var) { return buff; } -/// Tests if all characters in the wide string are numeric. -static int iswnumeric(const wchar_t *n) { - for (; *n; n++) { - if (*n < L'0' || *n > L'9') { - return 0; - } - } - return 1; -} - -/// See if the process described by \c proc matches the commandline \c cmd. -static bool match_pid(const wcstring &cmd, const wchar_t *proc, size_t *offset) { - // Test for a direct match. If the proc string is empty (e.g. the user tries to complete against - // %), then return an offset pointing at the base command. That ensures that you don't see a - // bunch of dumb paths when completing against all processes. - if (proc[0] != L'\0' && wcsncmp(cmd.c_str(), proc, wcslen(proc)) == 0) { - if (offset) *offset = 0; - return true; - } - - // Get the command to match against. We're only interested in the last path component. - const wcstring base_cmd = wbasename(cmd); - - bool result = string_prefixes_string(proc, base_cmd); - // It's a match. Return the offset within the full command. - if (result && offset) *offset = cmd.size() - base_cmd.size(); - return result; -} - -/// Helper class for iterating over processes. The names returned have been unescaped (e.g. may -/// include spaces). -#ifdef KERN_PROCARGS2 - -// BSD / OS X process completions. - -class process_iterator_t { - std::vector pids; - size_t idx; - - wcstring name_for_pid(pid_t pid); - - public: - process_iterator_t(); - bool next_process(wcstring *str, pid_t *pid); -}; - -wcstring process_iterator_t::name_for_pid(pid_t pid) { - wcstring result; - int mib[4], maxarg = 0, numArgs = 0; - size_t size = 0; - char *args = NULL, *stringPtr = NULL; - - mib[0] = CTL_KERN; - mib[1] = KERN_ARGMAX; - - size = sizeof(maxarg); - if (sysctl(mib, 2, &maxarg, &size, NULL, 0) == -1) { - return result; - } - - args = (char *)malloc(maxarg); - if (!args) return result; - - mib[0] = CTL_KERN; - mib[1] = KERN_PROCARGS2; - mib[2] = pid; - - size = (size_t)maxarg; - if (sysctl(mib, 3, args, &size, NULL, 0) == -1) { - free(args); - return result; - } - - memcpy(&numArgs, args, sizeof(numArgs)); - stringPtr = args + sizeof(numArgs); - result = str2wcstring(stringPtr); - free(args); - return result; -} - -bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) { - wcstring name; - pid_t pid = 0; - bool result = false; - while (idx < pids.size()) { - pid = pids.at(idx++); - name = name_for_pid(pid); - if (!name.empty()) { - result = true; - break; - } - } - if (result) { - *out_str = name; - *out_pid = pid; - } - return result; -} - -process_iterator_t::process_iterator_t() : idx(0) { - int err; - struct kinfo_proc *result; - bool done; - static const int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0}; - // Declaring name as const requires us to cast it when passing it to sysctl because the - // prototype doesn't include the const modifier. - size_t length; - - // We start by calling sysctl with result == NULL and length == 0. That will succeed, and set - // length to the appropriate length. We then allocate a buffer of that size and call sysctl - // again with that buffer. If that succeeds, we're done. If that fails with ENOMEM, we have to - // throw away our buffer and loop. Note that the loop causes use to call sysctl with NULL - // again; this is necessary because the ENOMEM failure case sets length to the amount of data - // returned, not the amount of data that could have been returned. - result = NULL; - done = false; - do { - assert(result == NULL); - - // Call sysctl with a NULL buffer. - length = 0; - err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, NULL, &length, NULL, 0); - if (err == -1) { - err = errno; - } - - // Allocate an appropriately sized buffer based on the results from the previous call. - if (err == 0) { - result = (struct kinfo_proc *)malloc(length); - if (result == NULL) { - err = ENOMEM; - } - } - - // Call sysctl again with the new buffer. If we get an ENOMEM error, toss away our buffer - // and start again. - if (err == 0) { - err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, result, &length, NULL, 0); - if (err == -1) { - err = errno; - } - if (err == 0) { - done = true; - } else if (err == ENOMEM) { - assert(result != NULL); - free(result); - result = NULL; - err = 0; - } - } - } while (err == 0 && !done); - - // Clean up and establish post conditions. - if (err == 0 && result != NULL) { - for (size_t idx = 0; idx < length / sizeof(struct kinfo_proc); idx++) - pids.push_back(result[idx].kp_proc.p_pid); - } - - if (result) free(result); -} - -#else - -/// /proc style process completions. -class process_iterator_t { - DIR *dir; - - public: - process_iterator_t(); - ~process_iterator_t(); - - bool next_process(wcstring *out_str, pid_t *out_pid); -}; - -process_iterator_t::process_iterator_t() { dir = opendir("/proc"); } - -process_iterator_t::~process_iterator_t() { - if (dir) closedir(dir); -} - -bool process_iterator_t::next_process(wcstring *out_str, pid_t *out_pid) { - wcstring cmd; - pid_t pid = 0; - while (cmd.empty()) { - wcstring name; - if (!dir || !wreaddir(dir, name)) break; - if (!iswnumeric(name.c_str())) continue; - - wcstring path = wcstring(L"/proc/") + name; - struct stat buf; - if (wstat(path, &buf)) continue; - - if (buf.st_uid != getuid()) continue; - - // Remember the pid. - pid = fish_wcstoi(name.c_str()); - if (errno || pid < 0) { - debug(1, _(L"Unexpected failure to convert pid '%ls' to integer\n"), name.c_str()); - } - - // The 'cmdline' file exists, it should contain the commandline. - FILE *cmdfile; - if ((cmdfile = wfopen(path + L"/cmdline", "r"))) { - wcstring full_command_line; - fgetws2(&full_command_line, cmdfile); - - // The command line needs to be escaped. - cmd = tok_first(full_command_line); - } -#ifdef SunOS - else if ((cmdfile = wfopen(path + L"/psinfo", "r"))) { - psinfo_t info; - if (fread(&info, sizeof(info), 1, cmdfile)) { - // The filename is unescaped. - cmd = str2wcstring(info.pr_fname); - } - } -#endif - if (cmdfile) fclose(cmdfile); - } - - bool result = !cmd.empty(); - if (result) { - *out_str = cmd; - *out_pid = pid; - } - return result; -} - -#endif - -/// The following function is invoked on the main thread, because the job list is not thread safe. -/// It should search the job list for something matching the given proc, and then return true to -/// stop the search, false to continue it. -static bool find_job(const wchar_t *proc, expand_flags_t flags, - std::vector *completions) { - ASSERT_IS_MAIN_THREAD(); - - bool found = false; - // If we are not doing tab completion, we first check for the single '%' character, because an - // empty string will pass the numeric check below. But if we are doing tab completion, we want - // all of the job IDs as completion options, not just the last job backgrounded, so we pass this - // first block in favor of the second. - if (wcslen(proc) == 0 && !(flags & EXPAND_FOR_COMPLETIONS)) { - // This is an empty job expansion: '%'. It expands to the last job backgrounded. - job_iterator_t jobs; - while (const job_t *j = jobs.next()) { - if (!j->command_is_empty()) { - append_completion(completions, to_string(j->pgid)); - break; - } - } - // You don't *really* want to flip a coin between killing the last process backgrounded and - // all processes, do you? Let's not try other match methods with the solo '%' syntax. - found = true; - } else if (iswnumeric(proc)) { - // This is a numeric job string, like '%2'. - if (flags & EXPAND_FOR_COMPLETIONS) { - job_iterator_t jobs; - while (const job_t *j = jobs.next()) { - wchar_t jid[16]; - if (j->command_is_empty()) continue; - - swprintf(jid, 16, L"%d", j->job_id); - - if (wcsncmp(proc, jid, wcslen(proc)) == 0) { - wcstring desc_buff = format_string(COMPLETE_JOB_DESC_VAL, j->command_wcstr()); - append_completion(completions, jid + wcslen(proc), desc_buff, 0); - } - } - } else { - int jid = fish_wcstoi(proc); - if (!errno && jid > 0) { - const job_t *j = job_get(jid); - if (j && !j->command_is_empty()) { - append_completion(completions, to_string(j->pgid)); - } - } - } - // Stop here so you can't match a random process name when you're just trying to use job - // control. - found = true; - } - - if (found) { - return found; - } - - job_iterator_t jobs; - while (const job_t *j = jobs.next()) { - if (j->command_is_empty()) continue; - - size_t offset = 0; - if (match_pid(j->command(), proc, &offset)) { - if (flags & EXPAND_FOR_COMPLETIONS) { - append_completion(completions, j->command_wcstr() + offset + wcslen(proc), - COMPLETE_JOB_DESC, 0); - } else { - append_completion(completions, to_string(j->pgid)); - found = 1; - } - } - } - - if (found) { - return found; - } - - jobs.reset(); - while (const job_t *j = jobs.next()) { - if (j->command_is_empty()) continue; - for (const process_ptr_t &p : j->processes) { - if (p->actual_cmd.empty()) continue; - - size_t offset = 0; - if (match_pid(p->actual_cmd, proc, &offset)) { - if (flags & EXPAND_FOR_COMPLETIONS) { - append_completion(completions, wcstring(p->actual_cmd, offset + wcslen(proc)), - COMPLETE_CHILD_PROCESS_DESC, 0); - } else { - append_completion(completions, to_string(p->pid), L"", 0); - found = 1; - } - } - } - } - - return found; -} - -/// Searches for a job with the specified job id, or a job or process which has the string \c proc -/// as a prefix of its commandline. Appends the name of the process as a completion in 'out'. -/// -/// Otherwise, any job matching the specified string is matched, and the job pgid is returned. If no -/// job matches, all child processes are searched. If no child processes match, and fish -/// can understand the contents of the /proc filesystem, all the users processes are searched for -/// matches. -static void find_process(const wchar_t *proc, expand_flags_t flags, - std::vector *out) { - if (!(flags & EXPAND_SKIP_JOBS)) { - bool found = false; - iothread_perform_on_main([&]() { found = find_job(proc, flags, out); }); - if (found) { - return; - } - } - - // Iterate over all processes. - wcstring process_name; - pid_t process_pid; - process_iterator_t iterator; - while (iterator.next_process(&process_name, &process_pid)) { - size_t offset = 0; - if (match_pid(process_name, proc, &offset)) { - if (flags & EXPAND_FOR_COMPLETIONS) { - append_completion(out, process_name.c_str() + offset + wcslen(proc), - COMPLETE_PROCESS_DESC, 0); - } else { - append_completion(out, to_string(process_pid)); - } - } - } -} - /// Parse an array slicing specification Returns 0 on success. If a parse error occurs, returns the /// index of the bad token. Note that 0 can never be a bad index because the string always starts /// with [. From f93d1c963f875d5fa9bef647f7e2462cea69c8d6 Mon Sep 17 00:00:00 2001 From: slama Date: Sun, 25 Mar 2018 11:53:59 +0900 Subject: [PATCH 023/159] removed explanation of process expansion in doc --- doc_src/bg.txt | 4 ---- doc_src/fg.txt | 4 +--- doc_src/index.hdr.in | 30 +----------------------------- 3 files changed, 2 insertions(+), 36 deletions(-) diff --git a/doc_src/bg.txt b/doc_src/bg.txt index 7195501ed..6d9e01f72 100644 --- a/doc_src/bg.txt +++ b/doc_src/bg.txt @@ -9,8 +9,6 @@ bg [PID...] `bg` sends jobs to the background, resuming them if they are stopped. A background job is executed simultaneously with fish, and does not have access to the keyboard. If no job is specified, the last job to be used is put in the background. If PID is specified, the jobs with the specified process group IDs are put in the background. -The PID of the desired process is usually found by using process expansion. - When at least one of the arguments isn't a valid job specifier (i.e. PID), `bg` will print an error without backgrounding anything. @@ -18,8 +16,6 @@ When all arguments are valid job specifiers, bg will background all matching job \subsection bg-example Example -`bg %1` will put the job with job ID 1 in the background. - `bg 123 456 789` will background 123, 456 and 789. If only 123 and 789 exist, it will still background them and print an error about 456. diff --git a/doc_src/fg.txt b/doc_src/fg.txt index 02562cd6b..a27a0ad6b 100644 --- a/doc_src/fg.txt +++ b/doc_src/fg.txt @@ -9,9 +9,7 @@ fg [PID] `fg` brings the specified job to the foreground, resuming it if it is stopped. While a foreground job is executed, fish is suspended. If no job is specified, the last job to be used is put in the foreground. If PID is specified, the job with the specified group ID is put in the foreground. -The PID of the desired process is usually found by using process expansion. Fish is capable of expanding far more than just the numeric PID, including referencing itself and finding PIDs by name. - \subsection fg-example Example -`fg %1` will put the job with job ID 1 in the foreground. +`fg` will put the last job in the foreground. diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 4ebb350c2..64b830703 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -94,7 +94,6 @@ Some characters can not be written directly on the command line. For these chara - '\\*' escapes the star character - '\\?' escapes the question mark character - '\\~' escapes the tilde character -- '\\%' escapes the percent character - '\\#' escapes the hash character - '\\(' escapes the left parenthesis character - '\\)' escapes the right parenthesis character @@ -333,8 +332,6 @@ These are the general purpose tab completions that `fish` provides: - Completion of filenames, even on strings with wildcards such as '`*`', '`**`' and '`?`'. -- Completion of job ID, job name and process names for process expansion. - `fish` provides a large number of program specific completions. Most of these completions are simple options like the `-l` option for `ls`, but some are more advanced. The latter include: - The programs `man` and `whatis` show all installed manual pages as completions. @@ -673,31 +670,6 @@ Note that variables can be used as indices for expansion of variables, but not c The `~` (tilde) character at the beginning of a parameter, followed by a username, is expanded into the home directory of the specified user. A lone `~`, or a `~` followed by a slash, is expanded into the home directory of the process owner. -\subsection expand-process Process expansion - -The `%` (percent) character at the beginning of a parameter followed by a string is expanded into a process ID (PID). The following expansions are performed: - -- If the string is the entire word `self`, the shell's PID is the result. - -- Otherwise, if the string is the entire word `last`, the last job's PID is the result. - -- Otherwise, if the string is the ID of a job, the result is the process group ID of the job. - -- Otherwise, if any child processes match the specified string, their PIDs are the result of the expansion. - -- Otherwise, if any processes owned by the user match the specified string, their PIDs are the result of the expansion. - -- If none of these matches apply, an error is produced. - -This form of expansion is useful for commands like kill and fg, which take process IDs as arguments. - -Example: - -`fg %%ema` will search for a process whose command line begins with the letters 'ema', such as emacs, and if found, put it in the foreground. - -`kill -s SIGINT %3` will send the SIGINT signal to the job with job ID 3. - - \subsection combine Combining different expansions All of the above expansions can be combined. If several expansions result in more than one parameter, all possible combinations are created. @@ -1270,7 +1242,7 @@ end If you want to run a set of commands when `fish` exits, use an event handler that is triggered by the exit of the shell: \fish -function on_exit --on-process %self +function on_exit --on-process $fish_pid echo fish is now exiting end \endfish From dae0dd513d18660de68635219f73c1c66ac6ad5e Mon Sep 17 00:00:00 2001 From: Samuel Gagnon Date: Fri, 30 Mar 2018 22:26:29 -0400 Subject: [PATCH 024/159] Fixes the FAQ questions in the sidebar of user_doc/html/index.html --- build_tools/build_toc_txt.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build_tools/build_toc_txt.sh b/build_tools/build_toc_txt.sh index 5c6f65c26..62a6fa910 100755 --- a/build_tools/build_toc_txt.sh +++ b/build_tools/build_toc_txt.sh @@ -12,5 +12,6 @@ for i in $@; do NAME=`basename $NAME .hdr.in` env sed <$i >>toc.txt -n \ -e 's,.*\\page *\([^ ]*\) *\(.*\)$,- \2,p' \ - -e 's,.*\\section *\([^ ]*\) *\([^-]*\)\(.*\)$, - \2,p' + -e 's,.*\\section *\([^ ]*\) *\(.*\) - .*$, - \2,p' \ + -e 's,.*\\section *\([^ ]*\) *\(.*\)$, - \2,p' done From ba06a899237d83db174ca99bb7bdb9f18c6716a1 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 31 Mar 2018 13:18:50 -0700 Subject: [PATCH 025/159] Update legacy Xcode build to use tinyexpr instead of muParser Fixes #4838 --- fish.xcodeproj/project.pbxproj | 248 ++------------------------------- 1 file changed, 10 insertions(+), 238 deletions(-) diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index d0156b45c..208842e22 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -203,8 +203,6 @@ D01243691CD4015C00C64313 /* util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0855E13B3ACEE0099B651 /* util.cpp */; }; D012436A1CD4018100C64313 /* fallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853E13B3ACEE0099B651 /* fallback.cpp */; }; D012436B1CD4019700C64313 /* fallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0A0853E13B3ACEE0099B651 /* fallback.cpp */; }; - D013CE371F52964A00AB1419 /* libmuparser.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D06821651F51490E00040321 /* libmuparser.a */; }; - D013CE381F52964D00AB1419 /* libmuparser.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D06821651F51490E00040321 /* libmuparser.a */; }; D01A2D24169B736200767098 /* man1 in Copy Files */ = {isa = PBXBuildFile; fileRef = D01A2D23169B730A00767098 /* man1 */; }; D01A2D25169B737700767098 /* man1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = D01A2D23169B730A00767098 /* man1 */; }; D02960E61FBD726200CA3985 /* builtin_wait.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D02960E51FBD726100CA3985 /* builtin_wait.cpp */; }; @@ -251,6 +249,10 @@ D033781115DC6D4C00A634BA /* completions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D025C02715D1FEA100B9DB63 /* completions */; }; D033781215DC6D5200A634BA /* functions in CopyFiles */ = {isa = PBXBuildFile; fileRef = D025C02815D1FEA100B9DB63 /* functions */; }; D033781315DC6D5400A634BA /* tools in CopyFiles */ = {isa = PBXBuildFile; fileRef = D025C02915D1FEA100B9DB63 /* tools */; }; + D046A0FA2070245000C8DFF7 /* tinyexpr.c in Sources */ = {isa = PBXBuildFile; fileRef = D046A0F92070245000C8DFF7 /* tinyexpr.c */; }; + D046A0FB2070245000C8DFF7 /* tinyexpr.c in Sources */ = {isa = PBXBuildFile; fileRef = D046A0F92070245000C8DFF7 /* tinyexpr.c */; }; + D046A0FC2070245000C8DFF7 /* tinyexpr.c in Sources */ = {isa = PBXBuildFile; fileRef = D046A0F92070245000C8DFF7 /* tinyexpr.c */; }; + D046A0FD2070245000C8DFF7 /* tinyexpr.c in Sources */ = {isa = PBXBuildFile; fileRef = D046A0F92070245000C8DFF7 /* tinyexpr.c */; }; D04F7FD51BA4E3AC00B0F227 /* pcre2_compile.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F8D1BA4DCD900B0F227 /* pcre2_compile.c */; }; D04F7FD61BA4E3AC00B0F227 /* pcre2_config.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F901BA4DCE900B0F227 /* pcre2_config.c */; }; D04F7FD71BA4E3AC00B0F227 /* pcre2_context.c in Sources */ = {isa = PBXBuildFile; fileRef = D04F7F931BA4DCFA00B0F227 /* pcre2_context.c */; }; @@ -375,13 +377,6 @@ D05F59CC1F041AE4003EE978 /* builtin_bg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596D1F041AE4003EE978 /* builtin_bg.cpp */; }; D05F59CD1F041AE4003EE978 /* builtin_bg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D05F596D1F041AE4003EE978 /* builtin_bg.cpp */; }; D06821601F5148AE00040321 /* builtin_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B51D811F5018B30051C61A /* builtin_math.cpp */; }; - D068222A1F5149B500040321 /* muParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D06822211F51498700040321 /* muParser.cpp */; }; - D068222B1F5149B500040321 /* muParserBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D06822221F51498700040321 /* muParserBase.cpp */; }; - D068222C1F5149B500040321 /* muParserBytecode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D06822231F51498700040321 /* muParserBytecode.cpp */; }; - D068222D1F5149B500040321 /* muParserCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D06822241F51498700040321 /* muParserCallback.cpp */; }; - D068222F1F5149B500040321 /* muParserError.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D06822261F51498700040321 /* muParserError.cpp */; }; - D06822301F5149B500040321 /* muParserInt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D06822271F51498700040321 /* muParserInt.cpp */; }; - D06822321F5149B500040321 /* muParserTokenReader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D06822291F51498700040321 /* muParserTokenReader.cpp */; }; D07B247315BCC15700D4ADB4 /* add-shell in Resources */ = {isa = PBXBuildFile; fileRef = D07B247215BCC15700D4ADB4 /* add-shell */; }; D07B247615BCC4BE00D4ADB4 /* install.sh in Resources */ = {isa = PBXBuildFile; fileRef = D07B247515BCC4BE00D4ADB4 /* install.sh */; }; D07D266A15E33B86009E43F6 /* config.fish in CopyFiles */ = {isa = PBXBuildFile; fileRef = D0C4FD9415A7D7EE00212EF1 /* config.fish */; }; @@ -389,8 +384,6 @@ D07D266D15E33B86009E43F6 /* functions in Copy Files */ = {isa = PBXBuildFile; fileRef = D025C02815D1FEA100B9DB63 /* functions */; }; D07D266E15E33B86009E43F6 /* tools in Copy Files */ = {isa = PBXBuildFile; fileRef = D025C02915D1FEA100B9DB63 /* tools */; }; D07D267215E34171009E43F6 /* config.fish in Copy Files */ = {isa = PBXBuildFile; fileRef = D0CBD580159EE48F0024809C /* config.fish */; }; - D08500A01F63A62C00C0E329 /* libmuparser.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D06821651F51490E00040321 /* libmuparser.a */; }; - D08500A11F63A63100C0E329 /* libmuparser.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D06821651F51490E00040321 /* libmuparser.a */; }; D08500A41F63A65800C0E329 /* builtin_argparse.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CB0F034A1F156FE3001827D3 /* builtin_argparse.cpp */; }; D08500A51F63A6EB00C0E329 /* builtin_math.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D0B51D811F5018B30051C61A /* builtin_math.cpp */; }; D0879AC816BF9AAB00E98E56 /* fish_term_icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = D0879AC616BF9A1A00E98E56 /* fish_term_icon.icns */; }; @@ -545,27 +538,6 @@ remoteGlobalIDString = D0D02ACF1598642A008E62BD; remoteInfo = fish_indent; }; - D085009C1F63A61B00C0E329 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D06821641F51490E00040321; - remoteInfo = muparser; - }; - D085009E1F63A61F00C0E329 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D06821641F51490E00040321; - remoteInfo = muparser; - }; - D08500A21F63A63600C0E329 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = D0A084F213B3AC130099B651 /* Project object */; - proxyType = 1; - remoteGlobalIDString = D06821641F51490E00040321; - remoteInfo = muparser; - }; D0A564EE168D09C000AF6161 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = D0A084F213B3AC130099B651 /* Project object */; @@ -714,6 +686,7 @@ D032388A1849D1980032CF2C /* pager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pager.h; sourceTree = ""; }; D03EE83814DF88B200FC7150 /* lru.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lru.h; sourceTree = ""; }; D043012D1F5350E400942A50 /* maybe.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = maybe.h; sourceTree = ""; }; + D046A0F92070245000C8DFF7 /* tinyexpr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tinyexpr.c; sourceTree = ""; }; D04F7F8D1BA4DCD900B0F227 /* pcre2_compile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_compile.c; sourceTree = ""; }; D04F7F901BA4DCE900B0F227 /* pcre2_config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_config.c; sourceTree = ""; }; D04F7F931BA4DCFA00B0F227 /* pcre2_context.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pcre2_context.c; sourceTree = ""; }; @@ -804,23 +777,6 @@ D05F596B1F041AE4003EE978 /* builtin_bind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_bind.cpp; sourceTree = ""; }; D05F596C1F041AE4003EE978 /* builtin_bg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtin_bg.h; sourceTree = ""; }; D05F596D1F041AE4003EE978 /* builtin_bg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_bg.cpp; sourceTree = ""; }; - D06821651F51490E00040321 /* libmuparser.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libmuparser.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D06821FB1F51498700040321 /* muParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = muParser.h; sourceTree = ""; }; - D06821FC1F51498700040321 /* muParserBase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = muParserBase.h; sourceTree = ""; }; - D06821FD1F51498700040321 /* muParserBytecode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = muParserBytecode.h; sourceTree = ""; }; - D06821FE1F51498700040321 /* muParserCallback.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = muParserCallback.h; sourceTree = ""; }; - D06821FF1F51498700040321 /* muParserDef.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = muParserDef.h; sourceTree = ""; }; - D06822031F51498700040321 /* muParserInt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = muParserInt.h; sourceTree = ""; }; - D06822061F51498700040321 /* muParserTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = muParserTest.h; sourceTree = ""; }; - D06822071F51498700040321 /* muParserToken.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = muParserToken.h; sourceTree = ""; }; - D06822081F51498700040321 /* muParserTokenReader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = muParserTokenReader.h; sourceTree = ""; }; - D06822211F51498700040321 /* muParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = muParser.cpp; sourceTree = ""; }; - D06822221F51498700040321 /* muParserBase.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = muParserBase.cpp; sourceTree = ""; }; - D06822231F51498700040321 /* muParserBytecode.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = muParserBytecode.cpp; sourceTree = ""; }; - D06822241F51498700040321 /* muParserCallback.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = muParserCallback.cpp; sourceTree = ""; }; - D06822261F51498700040321 /* muParserError.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = muParserError.cpp; sourceTree = ""; }; - D06822271F51498700040321 /* muParserInt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = muParserInt.cpp; sourceTree = ""; }; - D06822291F51498700040321 /* muParserTokenReader.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = muParserTokenReader.cpp; sourceTree = ""; }; D07B247215BCC15700D4ADB4 /* add-shell */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = "add-shell"; path = "build_tools/osx_package_scripts/add-shell"; sourceTree = ""; }; D07B247515BCC4BE00D4ADB4 /* install.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = install.sh; path = osx/install.sh; sourceTree = ""; }; D0879AC616BF9A1A00E98E56 /* fish_term_icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = fish_term_icon.icns; path = osx/fish_term_icon.icns; sourceTree = ""; }; @@ -932,7 +888,6 @@ files = ( 9C7A556C1DCD71330049C25D /* libncurses.dylib in Frameworks */, 9C7A556D1DCD71330049C25D /* libpcre2.a in Frameworks */, - D08500A01F63A62C00C0E329 /* libmuparser.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -942,7 +897,6 @@ files = ( D007693D1990137800CA4627 /* libncurses.dylib in Frameworks */, D04F7FFA1BA4E9A400B0F227 /* libpcre2.a in Frameworks */, - D08500A11F63A63100C0E329 /* libmuparser.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -953,20 +907,12 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - D06821621F51490E00040321 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; D0D02ACD1598642A008E62BD /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( D0D02ADC159864D5008E62BD /* libncurses.dylib in Frameworks */, D04F7FF21BA4E68A00B0F227 /* libpcre2.a in Frameworks */, - D013CE381F52964D00AB1419 /* libmuparser.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -976,7 +922,6 @@ files = ( D0D02A8D15983CFA008E62BD /* libncurses.dylib in Frameworks */, D04F7FF11BA4E68200B0F227 /* libpcre2.a in Frameworks */, - D013CE371F52964A00AB1419 /* libmuparser.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1039,45 +984,6 @@ path = "pcre2-10.22/src"; sourceTree = SOURCE_ROOT; }; - D06821C71F51498700040321 /* muparser-2.2.5 */ = { - isa = PBXGroup; - children = ( - D06821FA1F51498700040321 /* include */, - D06822201F51498700040321 /* src */, - ); - path = "muparser-2.2.5"; - sourceTree = SOURCE_ROOT; - }; - D06821FA1F51498700040321 /* include */ = { - isa = PBXGroup; - children = ( - D06821FB1F51498700040321 /* muParser.h */, - D06821FC1F51498700040321 /* muParserBase.h */, - D06821FD1F51498700040321 /* muParserBytecode.h */, - D06821FE1F51498700040321 /* muParserCallback.h */, - D06821FF1F51498700040321 /* muParserDef.h */, - D06822031F51498700040321 /* muParserInt.h */, - D06822061F51498700040321 /* muParserTest.h */, - D06822071F51498700040321 /* muParserToken.h */, - D06822081F51498700040321 /* muParserTokenReader.h */, - ); - path = include; - sourceTree = ""; - }; - D06822201F51498700040321 /* src */ = { - isa = PBXGroup; - children = ( - D06822211F51498700040321 /* muParser.cpp */, - D06822221F51498700040321 /* muParserBase.cpp */, - D06822231F51498700040321 /* muParserBytecode.cpp */, - D06822241F51498700040321 /* muParserCallback.cpp */, - D06822261F51498700040321 /* muParserError.cpp */, - D06822271F51498700040321 /* muParserInt.cpp */, - D06822291F51498700040321 /* muParserTokenReader.cpp */, - ); - path = src; - sourceTree = ""; - }; D08A328E17B4455100F3A533 /* fish_tests */ = { isa = PBXGroup; children = ( @@ -1259,6 +1165,7 @@ D0A0855A13B3ACEE0099B651 /* screen.cpp */, D0A0852413B3ACEE0099B651 /* signal.h */, D0A0855C13B3ACEE0099B651 /* signal.cpp */, + D046A0F92070245000C8DFF7 /* tinyexpr.c */, D0A0852513B3ACEE0099B651 /* tokenizer.h */, D0A0855D13B3ACEE0099B651 /* tokenizer.cpp */, 4F2D55CD2013ECDD00822920 /* tnode.h */, @@ -1275,7 +1182,6 @@ D0A0856013B3ACEE0099B651 /* wildcard.cpp */, D0A0852913B3ACEE0099B651 /* wutil.h */, D0A0856113B3ACEE0099B651 /* wutil.cpp */, - D06821C71F51498700040321 /* muparser-2.2.5 */, D04F7F8B1BA4DC7600B0F227 /* pcre */, ); name = Sources; @@ -1321,7 +1227,6 @@ D00769421990137800CA4627 /* fish_tests */, D04F7FD01BA4E29300B0F227 /* libpcre2.a */, 9C7A55721DCD71330049C25D /* fish_key_reader */, - D06821651F51490E00040321 /* libmuparser.a */, ); name = Products; sourceTree = ""; @@ -1336,13 +1241,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - D06821631F51490E00040321 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -1358,7 +1256,6 @@ dependencies = ( 9C7A55311DCD71330049C25D /* PBXTargetDependency */, 9C7A55331DCD71330049C25D /* PBXTargetDependency */, - D085009F1F63A61F00C0E329 /* PBXTargetDependency */, ); name = fish_key_reader; productName = fish_Xcode; @@ -1377,7 +1274,6 @@ dependencies = ( D008D0CF1BC58FE500841177 /* PBXTargetDependency */, D04F7FED1BA4E3DF00B0F227 /* PBXTargetDependency */, - D08500A31F63A63600C0E329 /* PBXTargetDependency */, ); name = fish_tests; productName = fish_Xcode; @@ -1402,23 +1298,6 @@ productReference = D04F7FD01BA4E29300B0F227 /* libpcre2.a */; productType = "com.apple.product-type.library.static"; }; - D06821641F51490E00040321 /* muparser */ = { - isa = PBXNativeTarget; - buildConfigurationList = D06821661F51490E00040321 /* Build configuration list for PBXNativeTarget "muparser" */; - buildPhases = ( - D06821611F51490E00040321 /* Sources */, - D06821621F51490E00040321 /* Frameworks */, - D06821631F51490E00040321 /* Headers */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = muparser; - productName = muparser; - productReference = D06821651F51490E00040321 /* libmuparser.a */; - productType = "com.apple.product-type.library.static"; - }; D0D02A9915985A75008E62BD /* fish.app */ = { isa = PBXNativeTarget; buildConfigurationList = D0D02AA415985A75008E62BD /* Build configuration list for PBXNativeTarget "fish.app" */; @@ -1466,7 +1345,6 @@ dependencies = ( D008D0CB1BC58FDD00841177 /* PBXTargetDependency */, D04F7FEB1BA4E3DB00B0F227 /* PBXTargetDependency */, - D085009D1F63A61B00C0E329 /* PBXTargetDependency */, ); name = fish_shell; productName = fish_Xcode; @@ -1487,9 +1365,6 @@ D04F7FCF1BA4E29300B0F227 = { CreatedOnToolsVersion = 6.4; }; - D06821641F51490E00040321 = { - CreatedOnToolsVersion = 9.0; - }; }; }; buildConfigurationList = D0A084F513B3AC130099B651 /* Build configuration list for PBXProject "fish" */; @@ -1514,7 +1389,6 @@ D04F7FCF1BA4E29300B0F227 /* pcre2 */, D008D0C41BC58F8800841177 /* generate-version-header */, 9C7A55301DCD71330049C25D /* fish_key_reader */, - D06821641F51490E00040321 /* muparser */, ); }; /* End PBXProject section */ @@ -1737,6 +1611,7 @@ D001B6141F041CBD000838CC /* builtin_exit.cpp in Sources */, D001B6161F041CBD000838CC /* builtin_emit.cpp in Sources */, D001B6181F041CBD000838CC /* builtin_echo.cpp in Sources */, + D046A0FD2070245000C8DFF7 /* tinyexpr.c in Sources */, D001B61A1F041CBD000838CC /* builtin_disown.cpp in Sources */, D001B61C1F041CBD000838CC /* builtin_contains.cpp in Sources */, D001B61E1F041CBD000838CC /* builtin_complete.cpp in Sources */, @@ -1802,6 +1677,7 @@ files = ( D02960E81FBD726200CA3985 /* builtin_wait.cpp in Sources */, 9C7A552F1DCD65820049C25D /* util.cpp in Sources */, + D046A0FC2070245000C8DFF7 /* tinyexpr.c in Sources */, D05F59971F041AE4003EE978 /* builtin_printf.cpp in Sources */, D05F59A31F041AE4003EE978 /* builtin_function.cpp in Sources */, 9C7A55271DCD651F0049C25D /* fallback.cpp in Sources */, @@ -1912,20 +1788,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - D06821611F51490E00040321 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - D068222A1F5149B500040321 /* muParser.cpp in Sources */, - D068222B1F5149B500040321 /* muParserBase.cpp in Sources */, - D068222C1F5149B500040321 /* muParserBytecode.cpp in Sources */, - D068222D1F5149B500040321 /* muParserCallback.cpp in Sources */, - D068222F1F5149B500040321 /* muParserError.cpp in Sources */, - D06822301F5149B500040321 /* muParserInt.cpp in Sources */, - D06822321F5149B500040321 /* muParserTokenReader.cpp in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; D0D02ACC1598642A008E62BD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1954,6 +1816,7 @@ D05F59A81F041AE4003EE978 /* builtin_exit.cpp in Sources */, D012436B1CD4019700C64313 /* fallback.cpp in Sources */, D05F59CC1F041AE4003EE978 /* builtin_bg.cpp in Sources */, + D046A0FB2070245000C8DFF7 /* tinyexpr.c in Sources */, D05F59AB1F041AE4003EE978 /* builtin_emit.cpp in Sources */, D05F59961F041AE4003EE978 /* builtin_printf.cpp in Sources */, D030FC001A4A38F300F7ADA0 /* function.cpp in Sources */, @@ -2095,6 +1958,7 @@ D0C52F371765284C00BFAB82 /* parse_tree.cpp in Sources */, D0FE8EE8179FB760008C9F21 /* parse_productions.cpp in Sources */, D01243681CD4015600C64313 /* util.cpp in Sources */, + D046A0FA2070245000C8DFF7 /* tinyexpr.c in Sources */, D05F59891F041AE4003EE978 /* builtin_realpath.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2165,21 +2029,6 @@ target = D0D02ACF1598642A008E62BD /* fish_indent */; targetProxy = D07D265E15E33B86009E43F6 /* PBXContainerItemProxy */; }; - D085009D1F63A61B00C0E329 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D06821641F51490E00040321 /* muparser */; - targetProxy = D085009C1F63A61B00C0E329 /* PBXContainerItemProxy */; - }; - D085009F1F63A61F00C0E329 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D06821641F51490E00040321 /* muparser */; - targetProxy = D085009E1F63A61F00C0E329 /* PBXContainerItemProxy */; - }; - D08500A31F63A63600C0E329 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = D06821641F51490E00040321 /* muparser */; - targetProxy = D08500A21F63A63600C0E329 /* PBXContainerItemProxy */; - }; D0A564EF168D09C000AF6161 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = D0A564E6168CFDD800AF6161 /* man_pages */; @@ -2317,74 +2166,6 @@ }; name = Release; }; - D06821671F51490E00040321 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = NO; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - EXECUTABLE_PREFIX = lib; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_NAME = "$(TARGET_NAME)"; - WARNING_CFLAGS = "-Wno-switch-enum"; - }; - name = Debug; - }; - D06821681F51490E00040321 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = NO; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - ENABLE_NS_ASSERTIONS = NO; - EXECUTABLE_PREFIX = lib; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_CHECK_SWITCH_STATEMENTS = NO; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNKNOWN_PRAGMAS = NO; - MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_NAME = "$(TARGET_NAME)"; - WARNING_CFLAGS = "-Wno-switch-enum"; - }; - name = Release; - }; D07D267015E33B86009E43F6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2676,15 +2457,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - D06821661F51490E00040321 /* Build configuration list for PBXNativeTarget "muparser" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - D06821671F51490E00040321 /* Debug */, - D06821681F51490E00040321 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; D07D266F15E33B86009E43F6 /* Build configuration list for PBXAggregateTarget "install_tree" */ = { isa = XCConfigurationList; buildConfigurations = ( From 4b079e16e5f208342f9ccc48b38b02653fb7717b Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 31 Mar 2018 14:57:24 -0700 Subject: [PATCH 026/159] Execute the conditions of if and while statements outside of their block Variables set in if and while conditions are in the enclosing block, not the if/while statement block. For example: if set -l var (somecommand) ; end echo $var will now work as expected. Fixes #4820. Fixes #1212. --- CHANGELOG.md | 1 + src/parse_execution.cpp | 64 ++++++++++++++++++++--------------------- src/parse_execution.h | 9 ++++-- tests/set.err | 3 ++ tests/set.in | 10 +++++++ tests/set.out | 23 +++++++++++++++ tests/test8.err | 3 ++ tests/test8.in | 10 +++++++ tests/test8.out | 3 ++ 9 files changed, 90 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb5b433f2..6c51b7852 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ This section is for changes merged to the `major` branch that are not also merge - fish now supports `&&`, `||`, and `!` (#4620). - The machine hostname, where available, is now exposed as `$hostname` which is now a reserved variable. This drops the dependency on the `hostname` executable (#4422). - `functions --handlers` can be used to show event handlers (#4694). +- Variables set in `if` and `while` conditions are available outside the block (#4820). ## Other significant changes - Command substitution output is now limited to 10 MB by default (#3822). diff --git a/src/parse_execution.cpp b/src/parse_execution.cpp index 17843b679..b728435ec 100644 --- a/src/parse_execution.cpp +++ b/src/parse_execution.cpp @@ -226,10 +226,7 @@ bool parse_execution_context_t::job_is_simple_block(tnode_t job_node) co } parse_execution_result_t parse_execution_context_t::run_if_statement( - tnode_t statement) { - // Push an if block. - if_block_t *ib = parser->push_block(); - + tnode_t statement, const block_t *associated_block) { parse_execution_result_t result = parse_execution_success; // We have a sequence of if clauses, with a final else, resulting in a single job list that we @@ -238,7 +235,7 @@ parse_execution_result_t parse_execution_context_t::run_if_statement( tnode_t if_clause = statement.child<0>(); tnode_t else_clause = statement.child<1>(); for (;;) { - if (should_cancel_execution(ib)) { + if (should_cancel_execution(associated_block)) { result = parse_execution_cancelled; break; } @@ -249,9 +246,9 @@ parse_execution_result_t parse_execution_context_t::run_if_statement( // Check the condition and the tail. We treat parse_execution_errored here as failure, in // accordance with historic behavior. - parse_execution_result_t cond_ret = run_job_conjunction(condition_head, ib); + parse_execution_result_t cond_ret = run_job_conjunction(condition_head, associated_block); if (cond_ret == parse_execution_success) { - cond_ret = run_job_list(condition_boolean_tail, ib); + cond_ret = run_job_list(condition_boolean_tail, associated_block); } const bool take_branch = (cond_ret == parse_execution_success) && proc_get_last_status() == EXIT_SUCCESS; @@ -284,20 +281,22 @@ parse_execution_result_t parse_execution_context_t::run_if_statement( // Execute any job list we got. if (job_list_to_execute) { + if_block_t *ib = parser->push_block(); run_job_list(job_list_to_execute, ib); + if (should_cancel_execution(ib)) { + result = parse_execution_cancelled; + } + parser->pop_block(ib); } else { // No job list means no sucessful conditions, so return 0 (issue #1443). proc_set_last_status(STATUS_CMD_OK); } // It's possible there's a last-minute cancellation (issue #1297). - if (should_cancel_execution(ib)) { + if (should_cancel_execution(associated_block)) { result = parse_execution_cancelled; } - // Done - parser->pop_block(ib); - // Otherwise, take the exit status of the job list. Reversal of issue #1061. return result; } @@ -337,7 +336,7 @@ parse_execution_result_t parse_execution_context_t::run_function_statement( } parse_execution_result_t parse_execution_context_t::run_block_statement( - tnode_t statement) { + tnode_t statement, const block_t *associated_block) { tnode_t bheader = statement.child<0>(); tnode_t contents = statement.child<1>(); @@ -345,7 +344,7 @@ parse_execution_result_t parse_execution_context_t::run_block_statement( if (auto header = bheader.try_get_child()) { ret = run_for_statement(header, contents); } else if (auto header = bheader.try_get_child()) { - ret = run_while_statement(header, contents); + ret = run_while_statement(header, contents, associated_block); } else if (auto header = bheader.try_get_child()) { ret = run_function_statement(header, contents); } else if (auto header = bheader.try_get_child()) { @@ -520,10 +519,8 @@ parse_execution_result_t parse_execution_context_t::run_switch_statement( } parse_execution_result_t parse_execution_context_t::run_while_statement( - tnode_t header, tnode_t contents) { - // Push a while block. - while_block_t *wb = parser->push_block(); - + tnode_t header, tnode_t contents, + const block_t *associated_block) { parse_execution_result_t ret = parse_execution_success; // The conditions of the while loop. @@ -533,9 +530,10 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( // Run while the condition is true. for (;;) { // Check the condition. - parse_execution_result_t cond_ret = this->run_job_conjunction(condition_head, wb); + parse_execution_result_t cond_ret = + this->run_job_conjunction(condition_head, associated_block); if (cond_ret == parse_execution_success) { - cond_ret = run_job_list(condition_boolean_tail, wb); + cond_ret = run_job_list(condition_boolean_tail, associated_block); } // We only continue on successful execution and EXIT_SUCCESS. @@ -544,21 +542,23 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( } // Check cancellation. - if (this->should_cancel_execution(wb)) { + if (this->should_cancel_execution(associated_block)) { ret = parse_execution_cancelled; break; } - // The block ought to go inside the loop (see issue #1212). + // Push a while block and then check its cancellation reason. + while_block_t *wb = parser->push_block(); this->run_job_list(contents, wb); + auto loop_status = wb->loop_status; + auto cancel_reason = this->cancellation_reason(wb); + parser->pop_block(wb); - if (this->cancellation_reason(wb) == execution_cancellation_loop_control) { + if (cancel_reason == execution_cancellation_loop_control) { // Handle break or continue. - if (wb->loop_status == LOOP_CONTINUE) { - // Reset the loop state. - wb->loop_status = LOOP_NORMAL; + if (loop_status == LOOP_CONTINUE) { continue; - } else if (wb->loop_status == LOOP_BREAK) { + } else if (loop_status == LOOP_BREAK) { break; } } @@ -569,9 +569,6 @@ parse_execution_result_t parse_execution_context_t::run_while_statement( break; } } - - // Done - parser->pop_block(wb); return ret; } @@ -1092,11 +1089,12 @@ parse_execution_result_t parse_execution_context_t::run_1_job(tnode_t jo assert(specific_statement_type_is_redirectable_block(specific_statement)); switch (specific_statement.type) { case symbol_block_statement: { - result = this->run_block_statement({&tree(), &specific_statement}); + result = + this->run_block_statement({&tree(), &specific_statement}, associated_block); break; } case symbol_if_statement: { - result = this->run_if_statement({&tree(), &specific_statement}); + result = this->run_if_statement({&tree(), &specific_statement}, associated_block); break; } case symbol_switch_statement: { @@ -1265,9 +1263,9 @@ parse_execution_result_t parse_execution_context_t::eval_node(tnode_t block_io_push(&block_io, io); enum parse_execution_result_t status = parse_execution_success; if (auto block = statement.try_get_child()) { - status = this->run_block_statement(block); + status = this->run_block_statement(block, associated_block); } else if (auto ifstat = statement.try_get_child()) { - status = this->run_if_statement(ifstat); + status = this->run_if_statement(ifstat, associated_block); } else if (auto switchstat = statement.try_get_child()) { status = this->run_switch_statement(switchstat); } else { diff --git a/src/parse_execution.h b/src/parse_execution.h index dceb87235..01a77b3ce 100644 --- a/src/parse_execution.h +++ b/src/parse_execution.h @@ -95,13 +95,16 @@ class parse_execution_context_t { tnode_t specific_statement); // These encapsulate the actual logic of various (block) statements. - parse_execution_result_t run_block_statement(tnode_t statement); + parse_execution_result_t run_block_statement(tnode_t statement, + const block_t *associated_block); parse_execution_result_t run_for_statement(tnode_t header, tnode_t contents); - parse_execution_result_t run_if_statement(tnode_t statement); + parse_execution_result_t run_if_statement(tnode_t statement, + const block_t *associated_block); parse_execution_result_t run_switch_statement(tnode_t statement); parse_execution_result_t run_while_statement(tnode_t statement, - tnode_t contents); + tnode_t contents, + const block_t *associated_block); parse_execution_result_t run_function_statement(tnode_t header, tnode_t body); parse_execution_result_t run_begin_statement(tnode_t contents); diff --git a/tests/set.err b/tests/set.err index 196a395a2..39e8350e7 100644 --- a/tests/set.err +++ b/tests/set.err @@ -20,3 +20,6 @@ $argle bargle: invalid var name #################### # Exporting works + +#################### +# if/for/while scope diff --git a/tests/set.in b/tests/set.in index 0fdcbe2eb..1db01f32b 100644 --- a/tests/set.in +++ b/tests/set.in @@ -60,3 +60,13 @@ set -x TESTVAR0 set -x TESTVAR1 a set -x TESTVAR2 a b env | grep TESTVAR | cat -v + +logmsg if/for/while scope +function test_ifforwhile_scope + if set -l ifvar1 (true && echo val1) ; end + if set -l ifvar2 (echo val2 && false) ; end + if false ; else if set -l ifvar3 (echo val3 && false) ; end + while set -l whilevar1 (echo val3 ; false) ; end + set --show ifvar1 ifvar2 ifvar3 whilevar1 +end +test_ifforwhile_scope diff --git a/tests/set.out b/tests/set.out index 3d4535bab..e429c2e66 100644 --- a/tests/set.out +++ b/tests/set.out @@ -106,3 +106,26 @@ $var6: not set in universal scope TESTVAR0= TESTVAR1=a TESTVAR2=a^^b + +#################### +# if/for/while scope +$ifvar1: set in local scope, unexported, with 1 elements +$ifvar1[1]: length=4 value=|val1| +$ifvar1: not set in global scope +$ifvar1: not set in universal scope + +$ifvar2: set in local scope, unexported, with 1 elements +$ifvar2[1]: length=4 value=|val2| +$ifvar2: not set in global scope +$ifvar2: not set in universal scope + +$ifvar3: set in local scope, unexported, with 1 elements +$ifvar3[1]: length=4 value=|val3| +$ifvar3: not set in global scope +$ifvar3: not set in universal scope + +$whilevar1: set in local scope, unexported, with 1 elements +$whilevar1[1]: length=4 value=|val3| +$whilevar1: not set in global scope +$whilevar1: not set in universal scope + diff --git a/tests/test8.err b/tests/test8.err index 279b7f5aa..46ecfda19 100644 --- a/tests/test8.err +++ b/tests/test8.err @@ -19,3 +19,6 @@ #################### # Catch this corner case, which should produce an error + +#################### +# Loop control in conditions diff --git a/tests/test8.in b/tests/test8.in index 2769765e6..19d645b1a 100644 --- a/tests/test8.in +++ b/tests/test8.in @@ -39,3 +39,13 @@ if false ; or true | false ; echo "failure5" ; end logmsg Catch this corner case, which should produce an error if false ; or --help ; end + +logmsg Loop control in conditions +for i in 1 2 3 + while break; end + echo $i +end +for i in 1 2 3 + while continue; end + echo $i +end diff --git a/tests/test8.out b/tests/test8.out index eb337f273..347669b16 100644 --- a/tests/test8.out +++ b/tests/test8.out @@ -37,3 +37,6 @@ success4 #################### # Catch this corner case, which should produce an error + +#################### +# Loop control in conditions From 6e56637cf0a6036fdea4105e9f33314a3d6e8e78 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 31 Mar 2018 16:48:57 -0700 Subject: [PATCH 027/159] Remove support for the ? wildcard Fixes #4520 --- CHANGELOG.md | 1 + doc_src/index.hdr.in | 9 ++------ share/completions/git.fish | 42 +++++++++++++++++--------------------- src/common.cpp | 13 ------------ src/expand.cpp | 8 ++------ src/fish_tests.cpp | 42 +++++++++++++++++++++----------------- src/highlight.cpp | 2 -- src/parse_util.cpp | 8 ++------ src/wildcard.cpp | 26 ++++++----------------- src/wildcard.h | 4 +--- tests/string.err | 8 ++++---- tests/string.in | 16 +++++++-------- tests/string.out | 8 ++++---- tests/test1.in | 2 +- tests/test5.in | 2 +- 15 files changed, 74 insertions(+), 117 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c51b7852..b1741ff32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,7 @@ This section is for changes merged to the `major` branch that are not also merge - The machine hostname, where available, is now exposed as `$hostname` which is now a reserved variable. This drops the dependency on the `hostname` executable (#4422). - `functions --handlers` can be used to show event handlers (#4694). - Variables set in `if` and `while` conditions are available outside the block (#4820). +- The `?` wildcard has been removed (#4520). ## Other significant changes - Command substitution output is now limited to 10 MB by default (#3822). diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 64b830703..d39a41bf1 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -92,7 +92,6 @@ Some characters can not be written directly on the command line. For these chara - '\\$' escapes the dollar character - '\\\\' escapes the backslash character - '\\*' escapes the star character -- '\\?' escapes the question mark character - '\\~' escapes the tilde character - '\\#' escapes the hash character - '\\(' escapes the left parenthesis character @@ -330,7 +329,7 @@ These are the general purpose tab completions that `fish` provides: - Completion of usernames for tilde expansion. -- Completion of filenames, even on strings with wildcards such as '`*`', '`**`' and '`?`'. +- Completion of filenames, even on strings with wildcards such as '`*`' and '`**`'. `fish` provides a large number of program specific completions. Most of these completions are simple options like the `-l` option for `ls`, but some are more advanced. The latter include: @@ -418,9 +417,7 @@ When an argument for a program is given on the commandline, it undergoes the pro \subsection expand-wildcard Wildcards -If a star (`*`) or a question mark (`?`) is present in the parameter, `fish` attempts to match the given parameter to any files in such a way that: - -- `?` can match any single character except '/'. +If a star (`*`) is present in the parameter, `fish` attempts to match the given parameter to any files in such a way that: - `*` can match any string of characters not containing '/'. This includes matching an empty string. @@ -446,8 +443,6 @@ Examples: - `a*` matches any files beginning with an 'a' in the current directory. -- `???` matches any file in the current directory whose name is exactly three characters long. - - `**` matches any files and directories in the current directory and all of its subdirectories. Note that for most commands, if any wildcard fails to expand, the command is not executed, `$status` is set to nonzero, and a warning is printed. This behavior is consistent with setting `shopt -s failglob` in bash. There are exactly 3 exceptions, namely `set`, `count` and `for`. Their globs are permitted to expand to zero arguments, as with `shopt -s nullglob` in bash. diff --git a/share/completions/git.fish b/share/completions/git.fish index 31997c5b5..9018c03a5 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -6,25 +6,18 @@ function __fish_git_commits # This allows filtering by subject with the new pager! # Because even subject lines can be quite long, # trim them (abbrev'd hash+tab+subject) to 73 characters - command git log --pretty=tformat:"%h"\t"%s" --all --max-count=1000 ^/dev/null \ - | string replace -r '(.{73}).+' '$1…' + command git log --pretty=tformat:"%h"\t"%s" --all --max-count=1000 ^/dev/null | string replace -r '(.{73}).+' '$1…' end function __fish_git_recent_commits # Like __fish_git_commits, but not on all branches and limited to # the last 50 commits. Used for fixup, where only the current branch # and the latest commits make sense. - command git log --pretty=tformat:"%h"\t"%s" --max-count=50 ^/dev/null \ - | string replace -r '(.{73}).+' '$1…' + command git log --pretty=tformat:"%h"\t"%s" --max-count=50 ^/dev/null | string replace -r '(.{73}).+' '$1…' end function __fish_git_branches - command git branch --no-color -a $argv ^/dev/null \ - # Filter out detached heads and such ("(HEAD detached at SOMESHA)", localized). - | string match -v '\* (*)' | string match -r -v ' -> ' | string trim -c "* " \ - # We assume anything that's not remote is a local branch. - | string replace -r '^(?!remotes/)(.*)' '$1\tLocal Branch' \ - | string replace -r "^remotes/(.*)" '$1\tRemote Branch' + command git branch --no-color -a $argv ^/dev/null # Filter out detached heads and such ("(HEAD detached at SOMESHA)", localized). | string match -v '\* (*)' | string match -r -v ' -> ' | string trim -c "* " # We assume anything that's not remote is a local branch. | string replace -r '^(?!remotes/)(.*)' '$1\tLocal Branch' | string replace -r "^remotes/(.*)" '$1\tRemote Branch' end function __fish_git_tags @@ -96,8 +89,7 @@ function __fish_git_files # E.g. `git reset $submodule` won't do anything (not even print an error). # --ignore-submodules=all was added in git 1.7.2, released July 2010. set -l use_next - command git status --porcelain -z --ignore-submodules=all \ - | while read -lz -d '' line + command git status --porcelain -z --ignore-submodules=all | while read -lz -d '' line # The entire line is the "from" from a rename. if set -q use_next[1] if contains -- $use_next $argv @@ -144,28 +136,31 @@ function __fish_git_files case 'A ' AM AD # Additions are only shown here if they are staged. # Otherwise it's an untracked file. - contains -- added $argv; or contains -- all-staged $argv + contains -- added $argv + or contains -- all-staged $argv and printf '%s\t%s\n' "$file" $added_desc - case '?M' + case '*M' # Modified contains -- modified $argv and printf '%s\t%s\n' "$file" $modified_desc - case 'M?' + case 'M*' # If the character is first ("M "), then that means it's "our" change, # which means it is staged. # This is useless for many commands - e.g. `checkout` won't do anything with this. # So it needs to be requested explicitly. - contains -- modified-staged $argv; or contains -- all-staged $argv + contains -- modified-staged $argv + or contains -- all-staged $argv and printf '%s\t%s\n' "$file" $staged_modified_desc - case '?D' + case '*D' contains -- deleted $argv and printf '%s\t%s\n' "$file" $deleted_desc - case 'D?' + case 'D*' # TODO: The docs are unclear on this. # There is both X unmodified and Y either M or D ("not updated") # and Y is D and X is unmodified or [MARC] ("deleted in work tree"). # For our purposes, we assume this is a staged deletion. - contains -- deleted-staged $argv; or contains -- all-staged $argv + contains -- deleted-staged $argv + or contains -- all-staged $argv and printf '%s\t%s\n' "$file" $staged_deleted_desc case '\?\?' # Untracked @@ -201,7 +196,8 @@ end function __fish_git_needs_command set cmd (commandline -opc) set -l skip_next 1 - set -q cmd[2]; or return 0 + set -q cmd[2] + or return 0 # Skip first word because it's "git" or a wrapper for c in $cmd[2..-1] test $skip_next -eq 0 @@ -555,7 +551,7 @@ complete -f -c git -n '__fish_git_needs_command' -a init -d 'Create an empty git # TODO options ### log -complete -c git -n '__fish_git_needs_command' -a shortlog -d 'Show commit shortlog' +complete -c git -n '__fish_git_needs_command' -a shortlog -d 'Show commit shortlog' complete -c git -n '__fish_git_needs_command' -a log -d 'Show commit logs' complete -c git -n '__fish_git_using_command log; and not contains -- -- (commandline -op)' -a '(__fish_git_refs) (__fish_git_ranges)' @@ -721,8 +717,8 @@ complete -c git -n '__fish_git_using_command log' -l irreversible-delete -s D complete -f -c git -n '__fish_git_using_command log' -s l function __fish__git_append_letters_nosep - set -l token (commandline -tc) - printf "%s\n" $token$argv + set -l token (commandline -tc) + printf "%s\n" $token$argv end complete -x -c git -n '__fish_git_using_command log' -l diff-filter -a '(__fish__git_append_letters_nosep a\tExclude\ added c\tExclude\ copied d\tExclude\ deleted m\tExclude\ modified r\tExclude\ renamed t\tExclude\ type\ changed u\tExclude\ unmerged x\tExclude\ unknown b\tExclude\ broken A\tAdded C\tCopied D\tDeleted M\tModified R\tRenamed T\tType\ Changed U\tUnmerged X\tUnknown B\tBroken)' diff --git a/src/common.cpp b/src/common.cpp index d39a8a5cb..a1612a06e 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -995,12 +995,6 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring out += *in; break; } - case ANY_CHAR: { - // Experimental fix for #1614. The hope is that any time these appear in a - // string, they came from wildcard expansion. - out += L'?'; - break; - } case ANY_STRING: { out += L'*'; break; @@ -1022,7 +1016,6 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring case L']': case L'{': case L'}': - case L'?': case L'*': case L'|': case L';': @@ -1356,12 +1349,6 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in } break; } - case L'?': { - if (unescape_special) { - to_append_or_none = ANY_CHAR; - } - break; - } case L'$': { if (unescape_special) { to_append_or_none = VARIABLE_EXPAND; diff --git a/src/expand.cpp b/src/expand.cpp index 38309345c..e3e4cbd01 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -823,15 +823,11 @@ static void remove_internal_separator(wcstring *str, bool conv) { // Remove all instances of INTERNAL_SEPARATOR. str->erase(std::remove(str->begin(), str->end(), (wchar_t)INTERNAL_SEPARATOR), str->end()); - // If conv is true, replace all instances of ANY_CHAR with '?', ANY_STRING with '*', + // If conv is true, replace all instances of ANY_STRING with '*', // ANY_STRING_RECURSIVE with '*'. if (conv) { for (size_t idx = 0; idx < str->size(); idx++) { switch (str->at(idx)) { - case ANY_CHAR: { - str->at(idx) = L'?'; - break; - } case ANY_STRING: case ANY_STRING_RECURSIVE: { str->at(idx) = L'*'; @@ -918,7 +914,7 @@ static expand_error_t expand_stage_wildcards(const wcstring &input, std::vector< wcstring path_to_expand = input; remove_internal_separator(&path_to_expand, flags & EXPAND_SKIP_WILDCARDS); - const bool has_wildcard = wildcard_has(path_to_expand, true /* internal, i.e. ANY_CHAR */); + const bool has_wildcard = wildcard_has(path_to_expand, true /* internal, i.e. ANY_STRING */); if (has_wildcard && (flags & EXECUTABLES_ONLY)) { ; // don't do wildcard expansion for executables, see issue #785 diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 71ee33a94..fb080f632 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -4120,15 +4120,16 @@ static void test_string() { {{L"string", L"match", 0}, STATUS_INVALID_ARGS, L""}, {{L"string", L"match", L"", 0}, STATUS_CMD_ERROR, L""}, {{L"string", L"match", L"", L"", 0}, STATUS_CMD_OK, L"\n"}, - {{L"string", L"match", L"?", L"a", 0}, STATUS_CMD_OK, L"a\n"}, + {{L"string", L"match", L"?", L"a", 0}, STATUS_CMD_ERROR, L""}, {{L"string", L"match", L"*", L"", 0}, STATUS_CMD_OK, L"\n"}, {{L"string", L"match", L"**", L"", 0}, STATUS_CMD_OK, L"\n"}, {{L"string", L"match", L"*", L"xyzzy", 0}, STATUS_CMD_OK, L"xyzzy\n"}, {{L"string", L"match", L"**", L"plugh", 0}, STATUS_CMD_OK, L"plugh\n"}, {{L"string", L"match", L"a*b", L"axxb", 0}, STATUS_CMD_OK, L"axxb\n"}, - {{L"string", L"match", L"a??b", L"axxb", 0}, STATUS_CMD_OK, L"axxb\n"}, - {{L"string", L"match", L"-i", L"a??B", L"axxb", 0}, STATUS_CMD_OK, L"axxb\n"}, - {{L"string", L"match", L"-i", L"a??b", L"Axxb", 0}, STATUS_CMD_OK, L"Axxb\n"}, + {{L"string", L"match", L"a??b", L"axxb", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"a??b", L"a??b", 0}, STATUS_CMD_OK, L"a??b\n"}, + {{L"string", L"match", L"-i", L"a??B", L"axxb", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"-i", L"a??b", L"Axxb", 0}, STATUS_CMD_ERROR, L""}, {{L"string", L"match", L"a*", L"axxb", 0}, STATUS_CMD_OK, L"axxb\n"}, {{L"string", L"match", L"*a", L"xxa", 0}, STATUS_CMD_OK, L"xxa\n"}, {{L"string", L"match", L"*a*", L"axa", 0}, STATUS_CMD_OK, L"axa\n"}, @@ -4137,14 +4138,14 @@ static void test_string() { {{L"string", L"match", L"*a", L"a", 0}, STATUS_CMD_OK, L"a\n"}, {{L"string", L"match", L"a*", L"a", 0}, STATUS_CMD_OK, L"a\n"}, {{L"string", L"match", L"a*b*c", L"axxbyyc", 0}, STATUS_CMD_OK, L"axxbyyc\n"}, - {{L"string", L"match", L"a*b?c", L"axxbyc", 0}, STATUS_CMD_OK, L"axxbyc\n"}, - {{L"string", L"match", L"*?", L"a", 0}, STATUS_CMD_OK, L"a\n"}, - {{L"string", L"match", L"*?", L"ab", 0}, STATUS_CMD_OK, L"ab\n"}, - {{L"string", L"match", L"?*", L"a", 0}, STATUS_CMD_OK, L"a\n"}, - {{L"string", L"match", L"?*", L"ab", 0}, STATUS_CMD_OK, L"ab\n"}, + {{L"string", L"match", L"a*b?c", L"axxb?c", 0}, STATUS_CMD_OK, L"axxb?c\n"}, + {{L"string", L"match", L"*?", L"a", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"*?", L"ab", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"?*", L"a", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"?*", L"ab", 0}, STATUS_CMD_ERROR, L""}, {{L"string", L"match", L"\\*", L"*", 0}, STATUS_CMD_OK, L"*\n"}, {{L"string", L"match", L"a*\\", L"abc\\", 0}, STATUS_CMD_OK, L"abc\\\n"}, - {{L"string", L"match", L"a*\\?", L"abc?", 0}, STATUS_CMD_OK, L"abc?\n"}, + {{L"string", L"match", L"a*\\?", L"abc?", 0}, STATUS_CMD_ERROR, L""}, {{L"string", L"match", L"?", L"", 0}, STATUS_CMD_ERROR, L""}, {{L"string", L"match", L"?", L"ab", 0}, STATUS_CMD_ERROR, L""}, @@ -4428,24 +4429,27 @@ static void test_illegal_command_exit_code() { }; const command_result_tuple_t tests[] = { - {L"echo -n", STATUS_CMD_OK}, {L"pwd", STATUS_CMD_OK}, - // a `)` without a matching `(` is now a tokenizer error, and cannot be executed even as an illegal command + {L"echo -n", STATUS_CMD_OK}, + {L"pwd", STATUS_CMD_OK}, + // a `)` without a matching `(` is now a tokenizer error, and cannot be executed even as an + // illegal command // {L")", STATUS_ILLEGAL_CMD}, {L") ", STATUS_ILLEGAL_CMD}, {L") ", STATUS_ILLEGAL_CMD} - {L"*", STATUS_ILLEGAL_CMD}, {L"**", STATUS_ILLEGAL_CMD}, - {L"?", STATUS_ILLEGAL_CMD}, {L"abc?def", STATUS_ILLEGAL_CMD}, + {L"*", STATUS_ILLEGAL_CMD}, + {L"**", STATUS_ILLEGAL_CMD}, + {L"?", STATUS_CMD_UNKNOWN}, + {L"abc?def", STATUS_CMD_UNKNOWN}, }; int res = 0; const io_chain_t empty_ios; parser_t &parser = parser_t::principal_parser(); - size_t i = 0; - for (i = 0; i < sizeof tests / sizeof *tests; i++) { - res = parser.eval(tests[i].txt, empty_ios, TOP); + for (const auto &test : tests) { + res = parser.eval(test.txt, empty_ios, TOP); int exit_status = res ? STATUS_CMD_UNKNOWN : proc_get_last_status(); - if (exit_status != tests[i].result) { - err(L"command '%ls': expected exit code %d , got %d", tests[i].txt, tests[i].result, + if (exit_status != test.result) { + err(L"command '%ls': expected exit code %d , got %d", test.txt, test.result, exit_status); } } diff --git a/src/highlight.cpp b/src/highlight.cpp index 1b94e87fa..8cb8b2f91 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -125,7 +125,6 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l case BRACE_BEGIN: case BRACE_END: case BRACE_SEP: - case ANY_CHAR: case ANY_STRING: case ANY_STRING_RECURSIVE: { has_magic = 1; @@ -549,7 +548,6 @@ static void color_argument_internal(const wcstring &buffstr, break; } case L'*': - case L'?': case L'(': case L')': { colors[in_pos] = highlight_spec_operator; diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 2fb17e94a..a40e9cc20 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -423,9 +423,7 @@ wcstring parse_util_unescape_wildcards(const wcstring &str) { for (size_t i = 0; cs[i] != L'\0'; i++) { if (cs[i] == L'*') { result.push_back(ANY_STRING); - } else if (cs[i] == L'?') { - result.push_back(ANY_CHAR); - } else if (cs[i] == L'\\' && (cs[i + 1] == L'*' || cs[i + 1] == L'?')) { + } else if (cs[i] == L'\\' && cs[i + 1] == L'*') { result.push_back(cs[i + 1]); i += 1; } else if (cs[i] == L'\\' && cs[i + 1] == L'\\') { @@ -892,9 +890,7 @@ void parse_util_expand_variable_error(const wcstring &token, size_t global_token default: { wchar_t token_stop_char = char_after_dollar; // Unescape (see issue #50). - if (token_stop_char == ANY_CHAR) - token_stop_char = L'?'; - else if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE) + if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE) token_stop_char = L'*'; // Determine which error message to use. The format string may not consume all the diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 48ce0671e..f67836800 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -51,7 +51,7 @@ /// Finds an internal (ANY_STRING, etc.) style wildcard, or wcstring::npos. static size_t wildcard_find(const wchar_t *wc) { for (size_t i = 0; wc[i] != L'\0'; i++) { - if (wc[i] == ANY_CHAR || wc[i] == ANY_STRING || wc[i] == ANY_STRING_RECURSIVE) { + if (wc[i] == ANY_STRING || wc[i] == ANY_STRING_RECURSIVE) { return i; } } @@ -64,13 +64,12 @@ static bool wildcard_has_impl(const wchar_t *str, size_t len, bool internal) { const wchar_t *end = str + len; if (internal) { for (; str < end; str++) { - if ((*str == ANY_CHAR) || (*str == ANY_STRING) || (*str == ANY_STRING_RECURSIVE)) - return true; + if (*str == ANY_STRING || *str == ANY_STRING_RECURSIVE) return true; } } else { wchar_t prev = 0; for (; str < end; str++) { - if (((*str == L'*') || (*str == L'?')) && (prev != L'\\')) return true; + if (*str == L'*' && prev != L'\\') return true; prev = *str; } } @@ -128,13 +127,6 @@ static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const restart_is_out_of_str = (*str_x == 0); wc_x++; continue; - } else if (*wc_x == ANY_CHAR && *str_x != 0) { - if (is_first && *str_x == L'.') { - return fuzzy_match_none; - } - wc_x++; - str_x++; - continue; } else if (*str_x != 0 && *str_x == *wc_x) { // ordinary character wc_x++; str_x++; @@ -212,7 +204,7 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, return false; } - // Locate the next wildcard character position, e.g. ANY_CHAR or ANY_STRING. + // Locate the next wildcard character position, e.g. ANY_STRING. const size_t next_wc_char_pos = wildcard_find(wc); // Maybe we have no more wildcards at all. This includes the empty string. @@ -265,12 +257,6 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, // Our first character is a wildcard. assert(next_wc_char_pos == 0); switch (wc[0]) { - case ANY_CHAR: { - if (str[0] == L'\0') { - return false; - } - return wildcard_complete_internal(str + 1, wc + 1, params, flags, out); - } case ANY_STRING: { // Hackish. If this is the last character of the wildcard, then just complete with // the empty string. This fixes cases like "f*" -> "f*o". @@ -789,7 +775,7 @@ void wildcard_expander_t::expand_last_segment(const wcstring &base_dir, DIR *bas /// /// Args: /// base_dir: the "working directory" against which the wildcard is to be resolved -/// wc: the wildcard string itself, e.g. foo*bar/baz (where * is acutally ANY_CHAR) +/// wc: the wildcard string itself, e.g. foo*bar/baz (where * is acutally ANY_STRING) /// prefix: the string that should be prepended for completions that replace their token. // This is usually the same thing as the original wildcard, but for fuzzy matching, we // expand intermediate segments. effective_prefix is always either empty, or ends with a slash @@ -810,7 +796,7 @@ void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc, const size_t wc_segment_len = next_slash ? next_slash - wc : wc_len; const wcstring wc_segment = wcstring(wc, wc_segment_len); const bool segment_has_wildcards = - wildcard_has(wc_segment, true /* internal, i.e. look for ANY_CHAR instead of ? */); + wildcard_has(wc_segment, true /* internal, i.e. look for ANY_STRING instead of * */); const wchar_t *const wc_remainder = next_slash ? next_slash + 1 : NULL; if (wc_segment.empty()) { diff --git a/src/wildcard.h b/src/wildcard.h index 5229ee027..614f28b83 100644 --- a/src/wildcard.h +++ b/src/wildcard.h @@ -11,10 +11,8 @@ // Enumeration of all wildcard types. enum { - /// Character representing any character except '/' (slash). - ANY_CHAR = WILDCARD_RESERVED_BASE, /// Character representing any character string not containing '/' (slash). - ANY_STRING, + ANY_STRING = WILDCARD_RESERVED_BASE, /// Character representing any character string. ANY_STRING_RECURSIVE, /// This is a special psuedo-char that is not used other than to mark the diff --git a/tests/string.err b/tests/string.err index 611df012b..4c6095d4f 100644 --- a/tests/string.err +++ b/tests/string.err @@ -117,16 +117,16 @@ # string unescape --style=var (string escape --style=var -- -) #################### -# string match "?" a +# string match "*" a #################### # string match "a*b" axxb #################### -# string match -i "a??B" Axxb +# string match -i "a**B" Axxb #################### -# echo "ok?" | string match "*\?" +# echo "ok?" | string match "*?" #################### # string match -r "cat|dog|fish" "nice dog" @@ -190,7 +190,7 @@ string invalidarg; and echo "unexpected exit 0" # string match -r -v "[dcantg].*" dog can cat diz #################### -# string match -v "???" dog can cat diz +# string match -v "*" dog can cat diz #################### # string match -rvn a bbb diff --git a/tests/string.in b/tests/string.in index 61020accc..78b85ea13 100644 --- a/tests/string.in +++ b/tests/string.in @@ -124,17 +124,17 @@ string unescape --style=var -- (string escape --style=var -- -) # The following tests verify that we can correctly match strings. -logmsg 'string match "?" a' -string match "?" a +logmsg 'string match "*" a' +string match "*" a logmsg 'string match "a*b" axxb' string match "a*b" axxb -logmsg 'string match -i "a??B" Axxb' -string match -i "a??B" Axxb +logmsg 'string match -i "a**B" Axxb' +string match -i "a**B" Axxb -logmsg 'echo "ok?" | string match "*\?"' -echo "ok?" | string match "*\?" +logmsg 'echo "ok?" | string match "*?"' +echo "ok?" | string match "*?" logmsg 'string match -r "cat|dog|fish" "nice dog"' string match -r "cat|dog|fish" "nice dog" @@ -199,8 +199,8 @@ string length; or echo "missing argument returns 1" logmsg 'string match -r -v "[dcantg].*" dog can cat diz' string match -r -v "[dcantg].*" dog can cat diz; or echo "no regexp invert match" -logmsg 'string match -v "???" dog can cat diz' -string match -v "???" dog can cat diz; or echo "no glob invert match" +logmsg 'string match -v "*" dog can cat diz' +string match -v "*" dog can cat diz; or echo "no glob invert match" logmsg 'string match -rvn a bbb' string match -rvn a bbb; or echo "exit 1" diff --git a/tests/string.out b/tests/string.out index b50416b04..68f060419 100644 --- a/tests/string.out +++ b/tests/string.out @@ -170,7 +170,7 @@ _a_b_c_ - #################### -# string match "?" a +# string match "*" a a #################### @@ -178,11 +178,11 @@ a axxb #################### -# string match -i "a??B" Axxb +# string match -i "a**B" Axxb Axxb #################### -# echo "ok?" | string match "*\?" +# echo "ok?" | string match "*?" ok? #################### @@ -272,7 +272,7 @@ missing argument returns 1 no regexp invert match #################### -# string match -v "???" dog can cat diz +# string match -v "*" dog can cat diz no glob invert match #################### diff --git a/tests/test1.in b/tests/test1.in index 980b3d889..d2910b6f4 100644 --- a/tests/test1.in +++ b/tests/test1.in @@ -65,7 +65,7 @@ for i in Test for continue break and switch builtins problems; switch $i case Test printf "%s " $i - case "f??" + case "for" printf "%s " 3b case "c*" echo pass diff --git a/tests/test5.in b/tests/test5.in index 3f99fa002..54b2b3693 100644 --- a/tests/test5.in +++ b/tests/test5.in @@ -20,7 +20,7 @@ end switch $smurf case cyan magenta yellow echo Test 3 fail - case "?????" + case "*" echo Test 3 pass end From abcc9647dad17d3828b8af58941c3eceb3afcb78 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 31 Mar 2018 17:06:13 -0700 Subject: [PATCH 028/159] Fix some unused variable warnings --- src/env.cpp | 2 ++ src/expand.cpp | 1 + src/fallback.cpp | 1 + src/input.cpp | 5 ++--- src/parse_productions.cpp | 1 + src/parse_tree.cpp | 7 ++++--- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index a6189def6..4fd100eff 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -779,6 +779,8 @@ static void handle_escape_delay_change(const wcstring &op, const wcstring &var_n } static void handle_change_emoji_width(const wcstring &op, const wcstring &var_name) { + (void)op; + (void)var_name; int new_width = 0; if (auto width_str = env_get(L"fish_emoji_width")) { new_width = fish_wcstol(width_str->as_string().c_str()); diff --git a/src/expand.cpp b/src/expand.cpp index e3e4cbd01..5c2dfc09a 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -898,6 +898,7 @@ static expand_error_t expand_stage_braces(const wcstring &input, std::vector *out, expand_flags_t flags, parse_error_list_t *errors) { + (void)errors; wcstring next = input; if (!(EXPAND_SKIP_HOME_DIRECTORIES & flags)) { diff --git a/src/fallback.cpp b/src/fallback.cpp index 2fc34c148..e2b2a0c2c 100644 --- a/src/fallback.cpp +++ b/src/fallback.cpp @@ -270,6 +270,7 @@ int g_fish_emoji_width = 0; int g_guessed_fish_emoji_width = 1; int fish_get_emoji_width(wchar_t c) { + (void)c; // Respect an explicit value. If we don't have one, use the guessed value. Do not try to fall // back to wcwidth(), it's hopeless. if (g_fish_emoji_width > 0) return g_fish_emoji_width; diff --git a/src/input.cpp b/src/input.cpp index ae3c865b5..abc766ad1 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -371,8 +371,7 @@ static bool input_mapping_is_match(const input_mapping_t &m) { debug(4, L"trying to match mapping %ls", escape_string(m.seq.c_str(), ESCAPE_ALL).c_str()); bool timed_first_char = iswcntrl(str[0]); - // i must be signed because we reverse direction below - for (ssize_t i = 0; i < str.size(); ++i) { + for (size_t i = 0; i < str.size(); ++i) { // Treat all strings beginning with control codes (0x00-0x1F) as timed characters, meaning they are assumed to be // their literal representation if not followed up with another character within the defined timeout. Obviously // we never time out on the first character in the sequence. @@ -383,7 +382,7 @@ static bool input_mapping_is_match(const input_mapping_t &m) { // We didn't match the bind sequence/input mapping, (it timed out or they entered something else) // Undo consumption of the read characters since we didn't match the bind sequence and abort. input_common_next_ch(read); - while (--i >= 0) { + while (i--) { input_common_next_ch(str[i]); } return false; diff --git a/src/parse_productions.cpp b/src/parse_productions.cpp index fefb0377b..d7f276446 100644 --- a/src/parse_productions.cpp +++ b/src/parse_productions.cpp @@ -337,6 +337,7 @@ RESOLVE(arguments_or_redirections_list) { RESOLVE(optional_newlines) { UNUSED(token2); + UNUSED(out_tag); if (token1.is_newline) return production_for(); return production_for(); } diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index db61b2a5f..2923b0dd2 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -461,7 +461,7 @@ class parse_ll_t { void accept_tokens(parse_token_t token1, parse_token_t token2); /// Report tokenizer errors. - void report_tokenizer_error(const tokenizer_t &tokenizer, const tok_t &tok); + void report_tokenizer_error(const tok_t &tok); /// Indicate if we hit a fatal error. bool has_fatal_error() const { return this->fatal_errored; } @@ -599,6 +599,7 @@ void parse_ll_t::parse_error(parse_token_t token, parse_error_code_t code, const void parse_ll_t::parse_error_at_location(size_t source_start, size_t source_length, size_t error_location, parse_error_code_t code, const wchar_t *fmt, ...) { + (void)error_location; this->fatal_errored = true; if (this->should_generate_error_messages) { // this->dump_stack(); @@ -667,7 +668,7 @@ void parse_ll_t::parse_error_failed_production(struct parse_stack_element_t &sta } } -void parse_ll_t::report_tokenizer_error(const tokenizer_t &tokenizer, const tok_t &tok) { +void parse_ll_t::report_tokenizer_error(const tok_t &tok) { parse_error_code_t parse_error_code = tok.error->parser_error; this->parse_error_at_location(tok.offset, tok.length, tok.offset + tok.error_offset, parse_error_code, L"%ls", @@ -1066,7 +1067,7 @@ bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags, // Handle tokenizer errors. This is a hack because really the parser should report this for // itself; but it has no way of getting the tokenizer message. if (queue[1].type == parse_special_type_tokenizer_error) { - parser.report_tokenizer_error(tok, tokenizer_token); + parser.report_tokenizer_error(tokenizer_token); } if (!parser.has_fatal_error()) { From 0e0168ef185743c2bcdba50333b8e4fb830feae2 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 31 Mar 2018 21:44:54 -0500 Subject: [PATCH 029/159] Display error message on `set -e PROTECTED` Previously unsetting a variable would fail silently. Now sharing error printing code from regular `set PROTECTED` call. --- src/builtin_set.cpp | 29 ++++++++++++++++++----------- src/env.cpp | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index ac064aaea..4386742c5 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -279,15 +279,8 @@ static bool validate_path_warning_on_colons(const wchar_t *cmd, return any_success; } -/// Call env_set. If this is a path variable, e.g. PATH, validate the elements. On error, print a -/// description of the problem to stderr. -static int my_env_set(const wchar_t *cmd, const wchar_t *key, int scope, wcstring_list_t &list, - io_streams_t &streams) { - if (is_path_variable(key) && !validate_path_warning_on_colons(cmd, key, list, streams)) { - return STATUS_CMD_ERROR; - } - - int retval = env_set(key, scope | ENV_USER, list); +static void handle_env_return(int retval, const wchar_t *cmd, const wchar_t *key, io_streams_t &streams) +{ switch (retval) { case ENV_OK: { retval = STATUS_CMD_OK; @@ -301,14 +294,14 @@ static int my_env_set(const wchar_t *cmd, const wchar_t *key, int scope, wcstrin } case ENV_SCOPE: { streams.err.append_format( - _(L"%ls: Tried to set the special variable '%ls' with the wrong scope\n"), cmd, + _(L"%ls: Tried to modify the special variable '%ls' with the wrong scope\n"), cmd, key); retval = STATUS_CMD_ERROR; break; } case ENV_INVALID: { streams.err.append_format( - _(L"%ls: Tried to set the special variable '%ls' to an invalid value\n"), cmd, key); + _(L"%ls: Tried to modify the special variable '%ls' to an invalid value\n"), cmd, key); retval = STATUS_CMD_ERROR; break; } @@ -317,6 +310,18 @@ static int my_env_set(const wchar_t *cmd, const wchar_t *key, int scope, wcstrin break; } } +} + +/// Call env_set. If this is a path variable, e.g. PATH, validate the elements. On error, print a +/// description of the problem to stderr. +static int my_env_set(const wchar_t *cmd, const wchar_t *key, int scope, wcstring_list_t &list, + io_streams_t &streams) { + if (is_path_variable(key) && !validate_path_warning_on_colons(cmd, key, list, streams)) { + return STATUS_CMD_ERROR; + } + + int retval = env_set(key, scope | ENV_USER, list); + handle_env_return(retval, cmd, key, streams); return retval; } @@ -626,6 +631,8 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, retval = my_env_set(cmd, dest, scope, result, streams); } + handle_env_return(retval, cmd, dest, streams); + if (retval != STATUS_CMD_OK) return retval; return check_global_scope_exists(cmd, opts, dest, streams); } diff --git a/src/env.cpp b/src/env.cpp index 4fd100eff..089875ce2 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1248,7 +1248,7 @@ int env_remove(const wcstring &key, int var_mode) { int erased = 0; if ((var_mode & ENV_USER) && is_read_only(key)) { - return 2; + return ENV_SCOPE; } first_node = vars_stack().top.get(); From 01452da5bf17224106b3b0117eb7bd978d75863d Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 31 Mar 2018 22:12:52 -0500 Subject: [PATCH 030/159] Add and use new exit code for env_remove() when var doesn't exist The previous commit caused the tests to fail since env_remove() was returning a blanket `!0` when a variable couldn't be unset because it didn't exist in the first place. This caused the wrong message to be emitted since the code clashed with a return code for `env_set()`. Added `ENV_NOT_FOUND` to signify that the variable requested unset didn't exist in the first place, but _not_ printing the error message currently so as not to break existing behavior before checking if this is something we want. --- src/builtin_set.cpp | 13 +++++++++++-- src/env.cpp | 2 +- src/env.h | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 4386742c5..aff84eec4 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -305,6 +305,12 @@ static void handle_env_return(int retval, const wchar_t *cmd, const wchar_t *key retval = STATUS_CMD_ERROR; break; } + case ENV_NOT_FOUND: { + streams.err.append_format( + _(L"%ls: The variable '%ls' does not exist\n"), cmd, key); + retval = STATUS_CMD_ERROR; + break; + } default: { DIE("unexpected env_set() ret val"); break; @@ -622,6 +628,11 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, if (idx_count == 0) { // unset the var retval = env_remove(dest, scope); + // Temporarily swallowing ENV_NOT_FOUND errors to prevent + // breaking all tests that unset variables that aren't set. + if (retval != ENV_NOT_FOUND) { + handle_env_return(retval, cmd, dest, streams); + } } else { // remove just the specified indexes of the var const auto dest_var = env_get(dest, scope); if (!dest_var) return STATUS_CMD_ERROR; @@ -631,8 +642,6 @@ static int builtin_set_erase(const wchar_t *cmd, set_cmd_opts_t &opts, int argc, retval = my_env_set(cmd, dest, scope, result, streams); } - handle_env_return(retval, cmd, dest, streams); - if (retval != STATUS_CMD_OK) return retval; return check_global_scope_exists(cmd, opts, dest, streams); } diff --git a/src/env.cpp b/src/env.cpp index 089875ce2..b156da24a 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1286,7 +1286,7 @@ int env_remove(const wcstring &key, int var_mode) { react_to_variable_change(L"ERASE", key); - return !erased; + return erased ? ENV_OK : ENV_NOT_FOUND; } const wcstring_list_t &env_var_t::as_list() const { return vals; } diff --git a/src/env.h b/src/env.h index 7ebafb51a..aac7b8f7a 100644 --- a/src/env.h +++ b/src/env.h @@ -44,7 +44,7 @@ enum { typedef uint32_t env_mode_flags_t; /// Return values for `env_set()`. -enum { ENV_OK, ENV_PERM, ENV_SCOPE, ENV_INVALID }; +enum { ENV_OK, ENV_PERM, ENV_SCOPE, ENV_INVALID, ENV_NOT_FOUND }; /// A struct of configuration directories, determined in main() that fish will optionally pass to /// env_init. From 95712125c9113d17bb27be46b2bd8a2bf980d41c Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 31 Mar 2018 22:21:22 -0500 Subject: [PATCH 031/159] Handle `set -e` result ENV_NOT_FOUND in tests --- tests/test4.out | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test4.out b/tests/test4.out index 3054a0162..752551259 100644 --- a/tests/test4.out +++ b/tests/test4.out @@ -36,7 +36,8 @@ Test 22 pass 4 0 5 0 6 0 -7 1 +# exit code 4 is ENV_NOT_FOUND, as foo has already been unset +7 4 8 0 9 121 10 0 A From 95e5af7814881d6cd48a72491629073f18c5fd7e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 31 Mar 2018 22:25:38 -0500 Subject: [PATCH 032/159] fixup! Handle `set -e` result ENV_NOT_FOUND in tests --- tests/test4.out | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test4.out b/tests/test4.out index 752551259..e0d388e26 100644 --- a/tests/test4.out +++ b/tests/test4.out @@ -36,7 +36,6 @@ Test 22 pass 4 0 5 0 6 0 -# exit code 4 is ENV_NOT_FOUND, as foo has already been unset 7 4 8 0 9 121 From 7659554deafd7a6f8c710fc42b52328be4561c05 Mon Sep 17 00:00:00 2001 From: Peter Ammon Date: Sun, 1 Apr 2018 13:42:38 -0700 Subject: [PATCH 033/159] Remove use of caret redirection from share/* This removes the use of caret redirections from share/completions and share/functions, in preparation for dropping support in fish. --- share/completions/ack.fish | 2 +- share/completions/adb.fish | 2 +- share/completions/apm.fish | 2 +- share/completions/apropos.fish | 2 +- share/completions/busctl.fish | 2 +- share/completions/cat.fish | 2 +- share/completions/chsh.fish | 2 +- share/completions/cower.fish | 2 +- share/completions/cp.fish | 2 +- share/completions/date.fish | 2 +- share/completions/dconf.fish | 2 +- share/completions/df.fish | 2 +- share/completions/duply.fish | 2 +- share/completions/eselect.fish | 4 +- share/completions/fossil.fish | 2 +- share/completions/git.fish | 22 +++++----- share/completions/gradle.fish | 2 +- share/completions/grunt.fish | 2 +- share/completions/gsettings.fish | 10 ++--- share/completions/head.fish | 2 +- share/completions/helm.fish | 6 +-- share/completions/hg.fish | 2 +- share/completions/iptables.fish | 2 +- share/completions/journalctl.fish | 4 +- share/completions/kill.fish | 2 +- share/completions/killall.fish | 2 +- share/completions/ls.fish | 2 +- share/completions/lua.fish | 2 +- share/completions/mkdir.fish | 4 +- share/completions/mktemp.fish | 2 +- share/completions/modinfo.fish | 2 +- share/completions/npm.fish | 2 +- share/completions/pactl.fish | 4 +- share/completions/passwd.fish | 2 +- share/completions/perl.fish | 2 +- share/completions/psql.fish | 4 +- share/completions/rm.fish | 2 +- share/completions/rsync.fish | 2 +- share/completions/sed.fish | 2 +- share/completions/seq.fish | 2 +- share/completions/ssh.fish | 2 +- share/completions/stat.fish | 2 +- share/completions/sysctl.fish | 6 +-- share/completions/tail.fish | 2 +- share/completions/tmux.fish | 10 ++--- share/completions/touch.fish | 2 +- share/completions/tr.fish | 2 +- share/completions/uniq.fish | 2 +- share/completions/valgrind.fish | 2 +- share/completions/vi.fish | 2 +- share/completions/which.fish | 2 +- .../functions/__fish_complete_lpr_option.fish | 4 +- share/functions/__fish_complete_man.fish | 2 +- .../__fish_complete_subcommand_root.fish | 2 +- .../__fish_complete_zfs_rw_properties.fish | 2 +- .../functions/__fish_config_interactive.fish | 18 ++++---- share/functions/__fish_describe_command.fish | 2 +- share/functions/__fish_git_prompt.fish | 42 +++++++++---------- share/functions/__fish_hg_prompt.fish | 4 +- share/functions/__fish_is_git_repository.fish | 2 +- .../__fish_make_completion_signals.fish | 4 +- share/functions/__fish_man_page.fish | 6 +-- share/functions/__fish_paginate.fish | 2 +- ...sh_portage_print_available_categories.fish | 2 +- share/functions/__fish_print_help.fish | 4 +- share/functions/__fish_print_lpr_options.fish | 2 +- .../functions/__fish_print_lpr_printers.fish | 2 +- .../functions/__fish_print_make_targets.fish | 6 +-- share/functions/__fish_print_modules.fish | 2 +- share/functions/__fish_print_packages.fish | 2 +- .../functions/__fish_print_service_names.fish | 2 +- .../functions/__fish_print_xdg_mimetypes.fish | 2 +- share/functions/__fish_svn_prompt.fish | 2 +- .../functions/__fish_systemctl_services.fish | 8 ++-- share/functions/__terlar_git_prompt.fish | 4 +- share/functions/_fish_systemctl.fish | 2 +- share/functions/fish_vi_cursor.fish | 6 +-- share/functions/fish_vi_key_bindings.fish | 2 +- share/functions/ls.fish | 4 +- share/functions/nextd.fish | 2 +- share/functions/prevd.fish | 2 +- share/functions/psub.fish | 2 +- 82 files changed, 152 insertions(+), 152 deletions(-) diff --git a/share/completions/ack.fish b/share/completions/ack.fish index 3c37be116..60db3d4c8 100644 --- a/share/completions/ack.fish +++ b/share/completions/ack.fish @@ -84,7 +84,7 @@ complete -c ack -l bar -d 'The warning admiral' # File types if type ack > /dev/null - for type in (ack --dump ^/dev/null | perl -lne 'print $1 if /^\s+--type-add=([^:]+)/' | uniq) + for type in (ack --dump 2>/dev/null | perl -lne 'print $1 if /^\s+--type-add=([^:]+)/' | uniq) complete -c ack -l $type -d "Allow $type file type" complete -c ack -l no$type -l no-$type -d "Don't allow $type file type" end diff --git a/share/completions/adb.fish b/share/completions/adb.fish index 2bdc4f350..f936100cf 100644 --- a/share/completions/adb.fish +++ b/share/completions/adb.fish @@ -49,7 +49,7 @@ function __fish_adb_run_command -d 'Runs adb with any -s parameters already give end function __fish_adb_list_packages - __fish_adb_run_command pm list packages ^/dev/null | string replace 'package:' '' + __fish_adb_run_command pm list packages 2>/dev/null | string replace 'package:' '' end diff --git a/share/completions/apm.fish b/share/completions/apm.fish index ab546493f..15622b417 100644 --- a/share/completions/apm.fish +++ b/share/completions/apm.fish @@ -69,7 +69,7 @@ function __fish_apm_list_packages apm list -b end -if apm -h ^| string match -q "*Atom Package Manager*" +if apm -h 2>| string match -q "*Atom Package Manager*" # Completions for Atom Package Manager ################## diff --git a/share/completions/apropos.fish b/share/completions/apropos.fish index 1b4eb9517..309358255 100644 --- a/share/completions/apropos.fish +++ b/share/completions/apropos.fish @@ -2,7 +2,7 @@ function __fish_complete_apropos if test (commandline -ct) set str (commandline -ct) - apropos $str ^/dev/null |sed -e "s/^\(.*$str\([^ ]*\).*\)\$/$str\2"\t"\1/" + apropos $str 2>/dev/null |sed -e "s/^\(.*$str\([^ ]*\).*\)\$/$str\2"\t"\1/" end end diff --git a/share/completions/busctl.fish b/share/completions/busctl.fish index b103612fa..dd575b15e 100644 --- a/share/completions/busctl.fish +++ b/share/completions/busctl.fish @@ -15,7 +15,7 @@ function __fish_busctl else set mode "--system" end - command busctl $mode $argv --no-legend --no-pager ^/dev/null + command busctl $mode $argv --no-legend --no-pager 2>/dev/null end # Only get the arguments to the actual command, skipping all options and arguments to options diff --git a/share/completions/cat.fish b/share/completions/cat.fish index b3a8e9c72..2a7bce379 100644 --- a/share/completions/cat.fish +++ b/share/completions/cat.fish @@ -1,4 +1,4 @@ -if cat --version ^ /dev/null > /dev/null # GNU +if cat --version 2>/dev/null > /dev/null # GNU complete -c cat -s A -l show-all -d "Escape all non-printing characters" complete -c cat -s b -l number-nonblank -d "Number nonblank lines" complete -c cat -s e -d "Escape non-printing characters except tab" diff --git a/share/completions/chsh.fish b/share/completions/chsh.fish index 3dbbdd7c9..2186ceb9b 100644 --- a/share/completions/chsh.fish +++ b/share/completions/chsh.fish @@ -10,6 +10,6 @@ complete -x -c chsh -a "(__fish_complete_users)" # util-linux's chsh also has a "-l"/"--list-shells" option. # While both FreeBSD and macOS also have "-l", it means something different and probably uninteresting. -if set -l chshversion (chsh --version ^/dev/null); and string match -qr '.*util-linux.*' -- $chshversion +if set -l chshversion (chsh --version 2>/dev/null); and string match -qr '.*util-linux.*' -- $chshversion complete -f -c chsh -s l -l list-shells -d "List available shells and exit" end diff --git a/share/completions/cower.fish b/share/completions/cower.fish index 83918d9cb..9b1c6269d 100644 --- a/share/completions/cower.fish +++ b/share/completions/cower.fish @@ -20,4 +20,4 @@ complete -c cower -f -s v -l 'verbose' -d 'Output more' # Complete with AUR packages: # If the search string is too short, cower prints an annoying message to stderr - ignore that -complete -c cower -f -n 'not string match -q -- "-*" (commandline --current-token)' -a '(cower --format="%n\t%d\n" --search (commandline --current-token) ^/dev/null)' +complete -c cower -f -n 'not string match -q -- "-*" (commandline --current-token)' -a '(cower --format="%n\t%d\n" --search (commandline --current-token) 2>/dev/null)' diff --git a/share/completions/cp.fish b/share/completions/cp.fish index 8bb316d32..178c0408e 100644 --- a/share/completions/cp.fish +++ b/share/completions/cp.fish @@ -1,4 +1,4 @@ -if cp --version ^ /dev/null > /dev/null # GNU +if cp --version 2>/dev/null > /dev/null # GNU complete -c cp -s a -l archive -d "Same as -dpR" complete -c cp -s b -l backup -d "Make backup of each existing destination file" -a "none off numbered t existing nil simple never" complete -c cp -l copy-contents -d "Copy contents of special files when recursive" diff --git a/share/completions/date.fish b/share/completions/date.fish index 62e6af0fa..d033932d8 100644 --- a/share/completions/date.fish +++ b/share/completions/date.fish @@ -1,4 +1,4 @@ -if date --version > /dev/null ^ /dev/null +if date --version > /dev/null 2>/dev/null complete -c date -s d -l date -d "Display date described by string" -x complete -c date -s f -l file -d "Display date for each line in file" -r complete -c date -s I -l iso-8601 -d "Output in ISO 8601 format" -x -a "date hours minutes seconds" diff --git a/share/completions/dconf.fish b/share/completions/dconf.fish index b24866d4e..8c014dd29 100644 --- a/share/completions/dconf.fish +++ b/share/completions/dconf.fish @@ -7,7 +7,7 @@ function __fish_dconf_keys # because it allows us to complete non-incrementally # i.e. to get the keys directly, without going through # `dconf list /`, `dconf list /org/` and so on. - dconf dump / ^/dev/null | while read -l line + dconf dump / 2>/dev/null | while read -l line if string match -q "[*]" -- $line # New directory - just save it for the keys set dir /(string trim -c "[]" -- $line) diff --git a/share/completions/df.fish b/share/completions/df.fish index 19cfdf4b4..5809ae91d 100644 --- a/share/completions/df.fish +++ b/share/completions/df.fish @@ -7,7 +7,7 @@ # set -l is_gnu -df --version >/dev/null ^/dev/null; and set is_gnu --is-gnu +df --version >/dev/null 2>/dev/null; and set is_gnu --is-gnu __fish_gnu_complete -c df -s h -l human-readable -d "Human readable sizes" $is_gnu __fish_gnu_complete -c df -s i -l inodes -d "List inode information" $is_gnu diff --git a/share/completions/duply.fish b/share/completions/duply.fish index eabbe2a89..22eb046fc 100644 --- a/share/completions/duply.fish +++ b/share/completions/duply.fish @@ -1,6 +1,6 @@ # First parameter is the profile name, or 'usage' -complete --command duply --no-files --condition '__fish_is_first_token' --arguments '(/bin/ls /etc/duply ^/dev/null) (/bin/ls ~/.duply ^/dev/null)' -d 'Profile' +complete --command duply --no-files --condition '__fish_is_first_token' --arguments '(/bin/ls /etc/duply 2>/dev/null) (/bin/ls ~/.duply 2>/dev/null)' -d 'Profile' complete --command duply --no-files --arguments 'usage' -d 'Get usage help text' # Second parameter is a duply command diff --git a/share/completions/eselect.fish b/share/completions/eselect.fish index 6ca000f60..d240a25c9 100644 --- a/share/completions/eselect.fish +++ b/share/completions/eselect.fish @@ -19,7 +19,7 @@ function __fish_complete_eselect_action_options # Alter further php completion if [ (__fish_print_cmd_args_without_options)[2] = 'php' ] - eselect php list-modules ^/dev/null | string split " " + eselect php list-modules 2>/dev/null | string split " " return end @@ -38,7 +38,7 @@ function __fish_complete_eselect_php_actions set -l sedregexp 's/^\s*\[([0-9]+)\]\s+([A-Za-z0-9\.]+).*/\1\t\2/' if test (__fish_print_cmd_args_without_options)[3] = 'set' - eselect php list (__fish_print_cmd_args_without_options)[-1] ^/dev/null | sed -r $sedregexp + eselect php list (__fish_print_cmd_args_without_options)[-1] 2>/dev/null | sed -r $sedregexp end end diff --git a/share/completions/fossil.fish b/share/completions/fossil.fish index e21c8e7c7..6b07c7a24 100644 --- a/share/completions/fossil.fish +++ b/share/completions/fossil.fish @@ -2,7 +2,7 @@ # http://www.fossil-scm.org/ function __fish_fossil - command fossil $argv ^/dev/null + command fossil $argv 2>/dev/null end function __fish_fossil_needs_command diff --git a/share/completions/git.fish b/share/completions/git.fish index 9018c03a5..cde489e63 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -6,26 +6,26 @@ function __fish_git_commits # This allows filtering by subject with the new pager! # Because even subject lines can be quite long, # trim them (abbrev'd hash+tab+subject) to 73 characters - command git log --pretty=tformat:"%h"\t"%s" --all --max-count=1000 ^/dev/null | string replace -r '(.{73}).+' '$1…' + command git log --pretty=tformat:"%h"\t"%s" --all --max-count=1000 2>/dev/null | string replace -r '(.{73}).+' '$1…' end function __fish_git_recent_commits # Like __fish_git_commits, but not on all branches and limited to # the last 50 commits. Used for fixup, where only the current branch # and the latest commits make sense. - command git log --pretty=tformat:"%h"\t"%s" --max-count=50 ^/dev/null | string replace -r '(.{73}).+' '$1…' + command git log --pretty=tformat:"%h"\t"%s" --max-count=50 2>/dev/null | string replace -r '(.{73}).+' '$1…' end function __fish_git_branches - command git branch --no-color -a $argv ^/dev/null # Filter out detached heads and such ("(HEAD detached at SOMESHA)", localized). | string match -v '\* (*)' | string match -r -v ' -> ' | string trim -c "* " # We assume anything that's not remote is a local branch. | string replace -r '^(?!remotes/)(.*)' '$1\tLocal Branch' | string replace -r "^remotes/(.*)" '$1\tRemote Branch' + command git branch --no-color -a $argv 2>/dev/null # Filter out detached heads and such ("(HEAD detached at SOMESHA)", localized). | string match -v '\* (*)' | string match -r -v ' -> ' | string trim -c "* " # We assume anything that's not remote is a local branch. | string replace -r '^(?!remotes/)(.*)' '$1\tLocal Branch' | string replace -r "^remotes/(.*)" '$1\tRemote Branch' end function __fish_git_tags - command git tag --sort=-creatordate ^/dev/null + command git tag --sort=-creatordate 2>/dev/null end function __fish_git_dir - command git rev-parse --git-dir ^/dev/null + command git rev-parse --git-dir 2>/dev/null end function __fish_git_heads @@ -44,7 +44,7 @@ function __fish_git_refs end function __fish_git_remotes - command git remote ^/dev/null + command git remote 2>/dev/null end function __fish_git_files @@ -65,7 +65,7 @@ function __fish_git_files # to get _all_ kinds of staged files. # Save the repo root to remove it from the path later. - set -l root (command git rev-parse --show-toplevel ^/dev/null) + set -l root (command git rev-parse --show-toplevel 2>/dev/null) # Cache the translated descriptions so we don't have to get it # once per file. @@ -288,11 +288,11 @@ function __fish_git_stash_not_using_subcommand end function __fish_git_complete_stashes - command git stash list --format=%gd:%gs ^/dev/null | string replace ":" \t + command git stash list --format=%gd:%gs 2>/dev/null | string replace ":" \t end function __fish_git_aliases - command git config -z --get-regexp '^alias\.' ^/dev/null | while read -lz key value + command git config -z --get-regexp '^alias\.' 2>/dev/null | while read -lz key value begin set -l name (string replace -r '^.*\.' '' -- $key) printf "%s\t%s\n" $name "Alias for $value" @@ -345,14 +345,14 @@ function __fish_git_possible_commithash end function __fish_git_reflog - command git reflog --no-decorate ^/dev/null | string replace -r '[0-9a-f]* (.+@\{[0-9]+\}): (.*)$' '$1\t$2' + command git reflog --no-decorate 2>/dev/null | string replace -r '[0-9a-f]* (.+@\{[0-9]+\}): (.*)$' '$1\t$2' end # general options complete -f -c git -l help -d 'Display the manual of a git command' complete -f -c git -n '__fish_git_needs_command' -l version -d 'Display version' complete -x -c git -n '__fish_git_needs_command' -s C -a '(__fish_complete_directories)' -d 'Run as if git was started in this directory' -complete -x -c git -n '__fish_git_needs_command' -s c -a '(command git config -l ^/dev/null | string replace = \t)' -d 'Set a configuration option' +complete -x -c git -n '__fish_git_needs_command' -s c -a '(command git config -l 2>/dev/null | string replace = \t)' -d 'Set a configuration option' complete -x -c git -n '__fish_git_needs_command' -l exec-path -a '(__fish_git_complete_directories)' -d 'Get or set the path to the git programs' complete -f -c git -n '__fish_git_needs_command' -l html-path -d 'Print the path to the html documentation' complete -f -c git -n '__fish_git_needs_command' -l man-path -d 'Print the path to the man documentation' diff --git a/share/completions/gradle.fish b/share/completions/gradle.fish index 7adc60e02..bf5343d1b 100644 --- a/share/completions/gradle.fish +++ b/share/completions/gradle.fish @@ -48,7 +48,7 @@ function __cache_or_get_gradle_completion set -l hashed_pwd (fish_md5 -s $PWD) set -l gradle_cache_file $XDG_CACHE_HOME/gradle-completions/$hashed_pwd if not test -f $gradle_cache_file; or command test build.gradle -nt $gradle_cache_file - command gradle -q tasks ^/dev/null | string match -r '^[[:alnum:]]+ - .*' | string replace ' - ' \t > $gradle_cache_file + command gradle -q tasks 2>/dev/null | string match -r '^[[:alnum:]]+ - .*' | string replace ' - ' \t > $gradle_cache_file end cat $gradle_cache_file end diff --git a/share/completions/grunt.fish b/share/completions/grunt.fish index f02578ea1..828e8841c 100644 --- a/share/completions/grunt.fish +++ b/share/completions/grunt.fish @@ -8,7 +8,7 @@ # https://github.com/gruntjs/grunt/blob/master/LICENSE-MIT function __grunt_print_tasks - set -l tasks (grunt --version --verbose ^/dev/null | awk '/Available tasks: / {$1=$2=""; print $0}' | awk '{$1=$1}1' | tr ' ' '\n') + set -l tasks (grunt --version --verbose 2>/dev/null | awk '/Available tasks: / {$1=$2=""; print $0}' | awk '{$1=$1}1' | tr ' ' '\n') for t in $tasks echo $t end diff --git a/share/completions/gsettings.fish b/share/completions/gsettings.fish index 0dbe4bb0a..67d7575ef 100644 --- a/share/completions/gsettings.fish +++ b/share/completions/gsettings.fish @@ -27,11 +27,11 @@ function __fish_complete_gsettings_args if not set -q cmd[2] # Non-relocatable schemas - gsettings $schemadir list-schemas ^/dev/null + gsettings $schemadir list-schemas 2>/dev/null # Relocatable schemas require a dconf path, but there is no universal way of # finding the right paths to suggest here. Just default to '/'. - gsettings $schemadir list-relocatable-schemas ^/dev/null | string replace -r \$ ':/' + gsettings $schemadir list-relocatable-schemas 2>/dev/null | string replace -r \$ ':/' return 0 end @@ -42,7 +42,7 @@ function __fish_complete_gsettings_args set -l schema $cmd[2] if not set -q cmd[3] - gsettings $schemadir list-keys $schema ^/dev/null + gsettings $schemadir list-keys $schema 2>/dev/null return 0 end @@ -52,7 +52,7 @@ function __fish_complete_gsettings_args if not set -q cmd[4] set -l key $cmd[3] - set -l range (gsettings $schemadir range $schema $key ^/dev/null) + set -l range (gsettings $schemadir range $schema $key 2>/dev/null) set -l key_type $range[1] set -e range[1] switch $key_type @@ -64,7 +64,7 @@ function __fish_complete_gsettings_args case '*' # If no sensible suggestions can be made, just use the current value. # It gives a good indication on the expected format and is likely already close to what the user wants. - gsettings $schemadir get $schema $key ^/dev/null + gsettings $schemadir get $schema $key 2>/dev/null end return 0 end diff --git a/share/completions/head.fish b/share/completions/head.fish index 7e397d6a5..9539a392c 100644 --- a/share/completions/head.fish +++ b/share/completions/head.fish @@ -1,4 +1,4 @@ -if head --version >/dev/null ^/dev/null +if head --version >/dev/null 2>/dev/null complete -c head -s c -l bytes -d 'Print the first N bytes; Leading '-', truncate the last N bytes' -r complete -c head -s n -l lines -d 'Print the first N lines; Leading '-', truncate the last N lines' -r complete -c head -s q -l quiet -l silent -d 'Never print file names' diff --git a/share/completions/helm.fish b/share/completions/helm.fish index b2bf7ec8d..8e908b161 100644 --- a/share/completions/helm.fish +++ b/share/completions/helm.fish @@ -68,7 +68,7 @@ function __helm_subcommands -a cmd end function __helm_kube_contexts - kubectl config get-contexts -o name ^/dev/null + kubectl config get-contexts -o name 2>/dev/null end function __helm_kube_namespaces @@ -76,11 +76,11 @@ function __helm_kube_namespaces end function __helm_releases - helm ls --short ^/dev/null + helm ls --short 2>/dev/null end function __helm_release_completions - helm ls ^/dev/null | awk 'NR >= 2 { print $1"\tRelease of "$NF }' + helm ls 2>/dev/null | awk 'NR >= 2 { print $1"\tRelease of "$NF }' end function __helm_release_revisions diff --git a/share/completions/hg.fish b/share/completions/hg.fish index 19b722eb5..b82bbd555 100644 --- a/share/completions/hg.fish +++ b/share/completions/hg.fish @@ -34,7 +34,7 @@ function __fish_hg if set -l cwd (__fish_hg_get_cwd) set argv $argv --cwd $cwd end - command hg $argv ^ /dev/null + command hg $argv 2>/dev/null end function __fish_hg_commands diff --git a/share/completions/iptables.fish b/share/completions/iptables.fish index 843057191..c28f184c8 100644 --- a/share/completions/iptables.fish +++ b/share/completions/iptables.fish @@ -29,7 +29,7 @@ function __fish_iptables_user_chains set tablearg "--table=$table" end # This only works as root, so ignore errors - iptables $tablearg -L ^/dev/null | grep Chain | while read a b c + iptables $tablearg -L 2>/dev/null | grep Chain | while read a b c echo $b end end diff --git a/share/completions/journalctl.fish b/share/completions/journalctl.fish index f582f4e54..b8ce1068f 100644 --- a/share/completions/journalctl.fish +++ b/share/completions/journalctl.fish @@ -17,13 +17,13 @@ end function __fish_journalctl_field_values set -l token (commandline -t | cut -d"=" -f 1) - command journalctl -F $token ^ /dev/null | while read value + command journalctl -F $token 2>/dev/null | while read value echo $token=$value end end complete -c journalctl -n "not __fish_journalctl_is_field" -a '(__fish_journalctl_fields)' -d "Journal field" -f -complete -c journalctl -n "not __fish_journalctl_is_field" -a '(command journalctl -F _EXE ^/dev/null)' -d "Executable" +complete -c journalctl -n "not __fish_journalctl_is_field" -a '(command journalctl -F _EXE 2>/dev/null)' -d "Executable" complete -c journalctl -n "not __fish_journalctl_is_field" -a '+' -d "OR" complete -c journalctl -n "__fish_journalctl_is_field" -a '(__fish_journalctl_field_values)' -f -r diff --git a/share/completions/kill.fish b/share/completions/kill.fish index 81eff965c..c128aacd6 100644 --- a/share/completions/kill.fish +++ b/share/completions/kill.fish @@ -10,7 +10,7 @@ end complete -c kill -xa '(__fish_complete_pids)' -if kill -L > /dev/null ^ /dev/null +if kill -L > /dev/null 2>/dev/null complete -c kill -s s -l signal -d "Signal to send" complete -c kill -s l -l list -d "Printf list of signal names, or name of given SIG NUMBER" complete -c kill -s L -l table -d " Print signal names and their corresponding numbers" diff --git a/share/completions/killall.fish b/share/completions/killall.fish index b94fa2b03..4fd7bd802 100644 --- a/share/completions/killall.fish +++ b/share/completions/killall.fish @@ -21,7 +21,7 @@ end complete -c killall -xa '(__fish_complete_proc)' -if killall --version >/dev/null ^/dev/null # GNU +if killall --version >/dev/null 2>/dev/null # GNU complete -c killall -s e -l exact -d 'Require an exact match for very long names' complete -c killall -s I -l ignore-case -d 'Do case insensitive process name match' complete -c killall -s g -l process-group -d 'Kill the process group to which the process belongs. The kill signal is only sent once per group, even if multiple processes belonging to the same process group were found' diff --git a/share/completions/ls.fish b/share/completions/ls.fish index 27002768e..418a67a90 100644 --- a/share/completions/ls.fish +++ b/share/completions/ls.fish @@ -17,7 +17,7 @@ complete -c ls -s x -d "List entries by lines" complete -c ls -s 1 -d "List one file per line" # Test if we are using GNU ls -if command ls --version >/dev/null ^/dev/null +if command ls --version >/dev/null 2>/dev/null complete -c ls -s a -l all -d "Show hidden" complete -c ls -s A -l almost-all -d "Show hidden except . and .." complete -c ls -s F -l classify -d "Append filetype indicator" diff --git a/share/completions/lua.fish b/share/completions/lua.fish index 1bfc1257d..046fbaa9b 100644 --- a/share/completions/lua.fish +++ b/share/completions/lua.fish @@ -1,7 +1,7 @@ complete -c lua -s e -d 'Execute string' -x # Try the most common lib directories, silencing errors in case they don't exist. -complete -c lua -s l -d 'Require library' -xa "(find /usr/lib{,32,64}/lua/ -name \*.so -printf '%f\n' ^/dev/null | string replace -r '.so\$' '')" +complete -c lua -s l -d 'Require library' -xa "(find /usr/lib{,32,64}/lua/ -name \*.so -printf '%f\n' 2>/dev/null | string replace -r '.so\$' '')" complete -c lua -s i -d 'Enter interactive mode after executing script' complete -c lua -s v -d 'Show version' complete -c lua -s h -l help -d 'Print help and exit' diff --git a/share/completions/mkdir.fish b/share/completions/mkdir.fish index 638994852..1e863244b 100644 --- a/share/completions/mkdir.fish +++ b/share/completions/mkdir.fish @@ -1,6 +1,6 @@ # Checks if we are using GNU tools -if mkdir --version > /dev/null ^ /dev/null +if mkdir --version > /dev/null 2>/dev/null complete -c mkdir -l version -d 'Output version' complete -c mkdir -s m -l mode -d 'Set file mode (as in chmod)' -x complete -c mkdir -s p -l parents -d 'Make parent directories as needed' @@ -14,6 +14,6 @@ else end # Checks if SELinux is installed -if command -s sestatus > /dev/null ^ /dev/null +if command -s sestatus > /dev/null 2>/dev/null complete -c mkdir -l context -s Z -d 'Set SELinux security context of each created directory to the default type' end diff --git a/share/completions/mktemp.fish b/share/completions/mktemp.fish index 786359005..11dceb1a7 100644 --- a/share/completions/mktemp.fish +++ b/share/completions/mktemp.fish @@ -1,4 +1,4 @@ -if mktemp --version >/dev/null ^/dev/null # GNU +if mktemp --version >/dev/null 2>/dev/null # GNU complete -c mktemp -s d -l directory -d 'create a directory, not a file' complete -c mktemp -s u -l dry-run -d 'do not create anything; merely print a name (unsafe)' complete -c mktemp -s q -l quiet -d 'suppress diagnostics about file/dir-creation failure' diff --git a/share/completions/modinfo.fish b/share/completions/modinfo.fish index eca5014d2..765ab2846 100644 --- a/share/completions/modinfo.fish +++ b/share/completions/modinfo.fish @@ -1,4 +1,4 @@ -if command -s uname > /dev/null ^/dev/null +if command -s uname > /dev/null 2>/dev/null if test (uname) = "Linux" complete -c modinfo -a "(__fish_print_modules)" complete -c modinfo -l author -s a -d "Print only 'author'" diff --git a/share/completions/npm.fish b/share/completions/npm.fish index a04a50500..ad8d2b253 100644 --- a/share/completions/npm.fish +++ b/share/completions/npm.fish @@ -59,7 +59,7 @@ function __fish_complete_npm -d "Complete the commandline using npm's 'completio set COMP_CWORD (math $COMP_CWORD + 1) set COMP_LINE $COMP_LINE "" end - command npm completion -- $COMP_LINE ^/dev/null + command npm completion -- $COMP_LINE 2>/dev/null end end diff --git a/share/completions/pactl.fish b/share/completions/pactl.fish index ae6dec43b..499c162e0 100644 --- a/share/completions/pactl.fish +++ b/share/completions/pactl.fish @@ -27,11 +27,11 @@ end function __fish_pa_list_ports # Yes, this is localized - env LC_ALL=C pactl list cards ^/dev/null | sed -n -e '/Ports:/,$p' | string match -r '^\t\t\w.*$' | string replace -r '\s+([-+\w:]+): (\w.+)' '$1\t$2' + env LC_ALL=C pactl list cards 2>/dev/null | sed -n -e '/Ports:/,$p' | string match -r '^\t\t\w.*$' | string replace -r '\s+([-+\w:]+): (\w.+)' '$1\t$2' end function __fish_pa_list_profiles - env LC_ALL=C pactl list cards ^/dev/null | sed -n -e '/Profiles:/,/Active Profile/p' | string match -r '\t\t.*' | string replace -r '\s+([-+\w:]+): (\w.+)' '$1\t$2' + env LC_ALL=C pactl list cards 2>/dev/null | sed -n -e '/Profiles:/,/Active Profile/p' | string match -r '\t\t.*' | string replace -r '\s+([-+\w:]+): (\w.+)' '$1\t$2' end # This is needed to filter out loaded modules diff --git a/share/completions/passwd.fish b/share/completions/passwd.fish index 2ba059a3a..3d1052f07 100644 --- a/share/completions/passwd.fish +++ b/share/completions/passwd.fish @@ -7,7 +7,7 @@ function __fish_passwd_darwin_infosystem echo -e "nis\tRemote NIS server" end -if passwd --help >/dev/null ^&1 +if passwd --help >/dev/null 2>&1 complete -c passwd -n '__fish_contains_opt -s S status' -s a -l all -f -d "Display password state for all users" complete -c passwd -s d -l delete -f -d "Delete user password" complete -c passwd -s e -l expire -f -d "Immediately obsolete user password" diff --git a/share/completions/perl.fish b/share/completions/perl.fish index 19e8fd47a..b9af0d2d4 100644 --- a/share/completions/perl.fish +++ b/share/completions/perl.fish @@ -8,7 +8,7 @@ begin set -l unicode 'commandline | string match -qr -- "-[a-zA-Z]*C[a-zA-Z]*\$"' set -l noopt 'commandline | not string match -qr -- "-[a-zA-Z]*C[a-zA-Z]*\$"' - set -l modules "(find (perl -lE'print for @INC') -name '*.pm' -printf '%P\n' ^/dev/null \ + set -l modules "(find (perl -lE'print for @INC') -name '*.pm' -printf '%P\n' 2>/dev/null \ | sed -e 's,/,::,g; s,\.pm\$,,' | sort -u)" complete -c perl -s 0 -n $noopt -d 'Specify record separator' complete -c perl -s a -n $noopt -d 'Turn on autosplit mode' diff --git a/share/completions/psql.fish b/share/completions/psql.fish index f9dee6cd9..e5b6b4656 100644 --- a/share/completions/psql.fish +++ b/share/completions/psql.fish @@ -1,10 +1,10 @@ function __fish_complete_pg_database - psql -AtqwlF \t ^/dev/null | awk 'NF > 1 { print $1 }' + psql -AtqwlF \t 2>/dev/null | awk 'NF > 1 { print $1 }' end function __fish_complete_pg_user - psql -Atqwc 'select usename from pg_user' template1 ^/dev/null + psql -Atqwc 'select usename from pg_user' template1 2>/dev/null end complete -c psql --no-files -a '(__fish_complete_pg_database)' diff --git a/share/completions/rm.fish b/share/completions/rm.fish index 9cff038ae..15f68ae13 100644 --- a/share/completions/rm.fish +++ b/share/completions/rm.fish @@ -1,5 +1,5 @@ #Completions for rm -if rm --version >/dev/null ^/dev/null # GNU +if rm --version >/dev/null 2>/dev/null # GNU complete -c rm -s d -l directory -d "Unlink directory (Only by superuser)" complete -c rm -s f -l force -d "Never prompt before removal" complete -c rm -s i -l interactive -d "Prompt before removal" diff --git a/share/completions/rsync.fish b/share/completions/rsync.fish index dc866c4ba..80f2d2685 100644 --- a/share/completions/rsync.fish +++ b/share/completions/rsync.fish @@ -130,6 +130,6 @@ complete -c rsync -d "Remote path" -n "commandline -ct | string match -q '*:*'" __rsync_remote_target )( # Get the list of remote files from the specified rsync server. - rsync --list-only (__rsync_remote_target) ^/dev/null | string replace -r '^d.*' '\$0/' | tr -s ' ' | cut -d' ' -f 5- + rsync --list-only (__rsync_remote_target) 2>/dev/null | string replace -r '^d.*' '\$0/' | tr -s ' ' | cut -d' ' -f 5- ) " diff --git a/share/completions/sed.fish b/share/completions/sed.fish index 657ead2fe..e4dd44c9c 100644 --- a/share/completions/sed.fish +++ b/share/completions/sed.fish @@ -5,7 +5,7 @@ # Test if we are using GNU sed set -l is_gnu -sed --version >/dev/null ^/dev/null; and set is_gnu --is-gnu +sed --version >/dev/null 2>/dev/null; and set is_gnu --is-gnu # Shared ls switches diff --git a/share/completions/seq.fish b/share/completions/seq.fish index 69760099d..e7fe74cae 100644 --- a/share/completions/seq.fish +++ b/share/completions/seq.fish @@ -1,4 +1,4 @@ -if seq --version ^ /dev/null > /dev/null #GNU +if seq --version 2>/dev/null > /dev/null #GNU complete -c seq -s f -l format -d 'Use printf style floating-point FORMAT' complete -c seq -s s -l separator -d 'Use STRING to separate numbers' complete -c seq -s w -l equal-width -d 'Equalize width with leading zeroes' diff --git a/share/completions/ssh.fish b/share/completions/ssh.fish index b90c7117e..4950b2082 100644 --- a/share/completions/ssh.fish +++ b/share/completions/ssh.fish @@ -19,7 +19,7 @@ complete -c ssh -s a -d "Disables forwarding of the authentication agent" complete -c ssh -s A -d "Enables forwarding of the authentication agent" # TODO: Improve this since /proc/net/arp is not POSIX compliant. complete -x -c ssh -s b -d "Interface to transmit from" -a " -(cut -d ' ' -f 1 /proc/net/arp ^/dev/null | string match -r -v '^IP') +(cut -d ' ' -f 1 /proc/net/arp 2>/dev/null | string match -r -v '^IP') " complete -x -c ssh -s e -d "Escape character" -a "\^ none" diff --git a/share/completions/stat.fish b/share/completions/stat.fish index a87ca02a1..9a540feb1 100644 --- a/share/completions/stat.fish +++ b/share/completions/stat.fish @@ -1,4 +1,4 @@ -if stat --version ^ /dev/null > /dev/null # GNU +if stat --version 2>/dev/null > /dev/null # GNU complete -c stat -s L -l dereference -d 'follow links' complete -c stat -s f -l file-system -d 'display file system status instead of file status' complete -c stat -s c -l format -x -d 'use the specified FORMAT instead of the default; output a newline after each use of FORMAT' diff --git a/share/completions/sysctl.fish b/share/completions/sysctl.fish index aaa32f04c..5736d93b6 100644 --- a/share/completions/sysctl.fish +++ b/share/completions/sysctl.fish @@ -1,9 +1,9 @@ if type -q -f sysctl # Only GNU and BSD sysctl seem to know "-h", so others should exit non-zero - if sysctl -h >/dev/null ^/dev/null + if sysctl -h >/dev/null 2>/dev/null # Print sysctl keys and values, separated by a tab function __fish_sysctl_values - sysctl -a ^/dev/null | string replace -a " = " \t + sysctl -a 2>/dev/null | string replace -a " = " \t end complete -c sysctl -a '(__fish_sysctl_values)' -f @@ -32,7 +32,7 @@ if type -q -f sysctl else # OSX sysctl function __fish_sysctl_values - sysctl -a ^/dev/null | string replace -a ":" \t + sysctl -a 2>/dev/null | string replace -a ":" \t end complete -c sysctl -a '(__fish_sysctl_values)' -f diff --git a/share/completions/tail.fish b/share/completions/tail.fish index 72a6793c4..b3daaae61 100644 --- a/share/completions/tail.fish +++ b/share/completions/tail.fish @@ -1,4 +1,4 @@ -if tail --version > /dev/null ^ /dev/null +if tail --version > /dev/null 2>/dev/null complete -c tail -s c -l bytes -x -d 'output the last K bytes; alternatively, use -c +K to output bytes starting with the Kth of each file' complete -c tail -s f -l follow -a 'name descriptor' -d 'output appended data as the file grows; -f -l follow, and --follow=descriptor are equivalent' complete -c tail -s F -d 'same as --follow=name --retry' diff --git a/share/completions/tmux.fish b/share/completions/tmux.fish index 2072ccf9b..9a5e3569e 100644 --- a/share/completions/tmux.fish +++ b/share/completions/tmux.fish @@ -1,20 +1,20 @@ function __fish_tmux_sessions -d 'available sessions' - tmux list-sessions -F "#S #{session_windows} windows created: #{session_created_string} [#{session_width}x#{session_height}]#{session_attached}" | sed 's/0$//;s/1$/ (attached)/' ^/dev/null + tmux list-sessions -F "#S #{session_windows} windows created: #{session_created_string} [#{session_width}x#{session_height}]#{session_attached}" | sed 's/0$//;s/1$/ (attached)/' 2>/dev/null end function __fish_tmux_clients -d 'connected clients' - tmux list-clients -F "#{client_tty} #S: Created: #{client_created_string} [#{client_width}x#{client_height} #{client_termname}]" ^/dev/null + tmux list-clients -F "#{client_tty} #S: Created: #{client_created_string} [#{client_width}x#{client_height} #{client_termname}]" 2>/dev/null end function __fish_tmux_panes -d 'window panes' #fully qualified pane names - tmux list-panes -F '#S:#W.#P session:window.pane' ^/dev/null + tmux list-panes -F '#S:#W.#P session:window.pane' 2>/dev/null #panes by themselves - tmux list-panes -F '#P pane' ^/dev/null + tmux list-panes -F '#P pane' 2>/dev/null #windows by themselves - tmux list-panes -F '#W window' ^/dev/null + tmux list-panes -F '#W window' 2>/dev/null end #don't allow dirs in the completion list... diff --git a/share/completions/touch.fish b/share/completions/touch.fish index fb2a1f513..8dc69477e 100644 --- a/share/completions/touch.fish +++ b/share/completions/touch.fish @@ -1,4 +1,4 @@ -if touch --version ^ /dev/null > /dev/null # GNU +if touch --version 2>/dev/null > /dev/null # GNU complete -c touch -s a -d "Change access time" complete -c touch -s B -l backward -x -d "Set date back" complete -c touch -s c -l no-create -d "Do not create file" diff --git a/share/completions/tr.fish b/share/completions/tr.fish index 5d247c81d..cc450897d 100644 --- a/share/completions/tr.fish +++ b/share/completions/tr.fish @@ -3,7 +3,7 @@ # # Test if we are using GNU tr -if command tr --version >/dev/null ^/dev/null +if command tr --version >/dev/null 2>/dev/null complete -c tr -x complete -c tr -s c -s C -l complement -d 'use the complement of SET1' complete -c tr -s d -l delete -d 'delete characters in SET1, do not translate' diff --git a/share/completions/uniq.fish b/share/completions/uniq.fish index c03d14a24..e02062571 100644 --- a/share/completions/uniq.fish +++ b/share/completions/uniq.fish @@ -1,4 +1,4 @@ -if uniq --version > /dev/null ^ /dev/null +if uniq --version > /dev/null 2>/dev/null complete -c uniq -s c -l count -d "Print number of occurences" complete -c uniq -s d -l repeated -d "Only print duplicates" complete -c uniq -s D -l all-repeated -d "Remove non-duplicate lines" -f -x -a " diff --git a/share/completions/valgrind.fish b/share/completions/valgrind.fish index bc6614114..eabcaf135 100644 --- a/share/completions/valgrind.fish +++ b/share/completions/valgrind.fish @@ -1,7 +1,7 @@ # Don't go invoking valgrind unless it is installed set -l skin tool -if type -q valgrind; and valgrind --version ^/dev/null | string match -qr -- '-2\.[012]\.' +if type -q valgrind; and valgrind --version 2>/dev/null | string match -qr -- '-2\.[012]\.' # In older versions of Valgrind, the skin selection option was # '--skin' # But someone decided that it would be fun to change this to diff --git a/share/completions/vi.fish b/share/completions/vi.fish index 52390c178..74efc1028 100644 --- a/share/completions/vi.fish +++ b/share/completions/vi.fish @@ -6,7 +6,7 @@ # Check if vi exists at all ( needed for vi --version ) if type -q vi # Check if vi is really vim - if vi --version > /dev/null ^ /dev/null + if vi --version > /dev/null 2>/dev/null complete -c vi -w vim else complete -c vi -s s -d 'Suppress all interactive user feedback' diff --git a/share/completions/which.fish b/share/completions/which.fish index d94683688..aaca38272 100644 --- a/share/completions/which.fish +++ b/share/completions/which.fish @@ -1,4 +1,4 @@ -if which -v > /dev/null ^ /dev/null # GNU +if which -v > /dev/null 2>/dev/null # GNU complete -c which -s a -l all -d "Print all matching executables in PATH, not just the first" complete -c which -s i -l read-alias -d "Read aliases from stdin, reporting matching ones on stdout" complete -c which -l skip-alias -d "Ignore option '--read-alias'" diff --git a/share/functions/__fish_complete_lpr_option.fish b/share/functions/__fish_complete_lpr_option.fish index eccb6bdb6..637923d03 100644 --- a/share/functions/__fish_complete_lpr_option.fish +++ b/share/functions/__fish_complete_lpr_option.fish @@ -4,7 +4,7 @@ function __fish_complete_lpr_option --description 'Complete lpr option' case '*=*' string split -m1 = -- "$optstr" | read -l opt val set -l descr - for l in (lpoptions -l ^/dev/null | string match -- "*$opt*" | string replace -r '.*/(.*):\s*(.*)$' '$1 $2' | string split " ") + for l in (lpoptions -l 2>/dev/null | string match -- "*$opt*" | string replace -r '.*/(.*):\s*(.*)$' '$1 $2' | string split " ") if not set -q descr[1] set descr $l continue @@ -17,7 +17,7 @@ function __fish_complete_lpr_option --description 'Complete lpr option' echo $opt=$l\t$default$descr end case '*' - lpoptions -l ^/dev/null | string replace -r '(.*)/(.*):.*$' '$1=\t$2' + lpoptions -l 2>/dev/null | string replace -r '(.*)/(.*):.*$' '$1=\t$2' end diff --git a/share/functions/__fish_complete_man.fish b/share/functions/__fish_complete_man.fish index 5061e5271..e65d61c07 100644 --- a/share/functions/__fish_complete_man.fish +++ b/share/functions/__fish_complete_man.fish @@ -30,7 +30,7 @@ function __fish_complete_man if test -n "$token" # Do the actual search - apropos $token ^/dev/null | awk ' + apropos $token 2>/dev/null | awk ' BEGIN { FS="[\t ]- "; OFS="\t"; } # BSD/Darwin /^[^( \t]+\('$section'\)/ { diff --git a/share/functions/__fish_complete_subcommand_root.fish b/share/functions/__fish_complete_subcommand_root.fish index 736abeeff..d434d0cbb 100644 --- a/share/functions/__fish_complete_subcommand_root.fish +++ b/share/functions/__fish_complete_subcommand_root.fish @@ -1,4 +1,4 @@ function __fish_complete_subcommand_root -d "Run the __fish_complete_subcommand function using a PATH containing /sbin and /usr/sbin" - set -lx -p PATH /sbin /usr/sbin ^/dev/null + set -lx -p PATH /sbin /usr/sbin 2>/dev/null __fish_complete_subcommand $argv end diff --git a/share/functions/__fish_complete_zfs_rw_properties.fish b/share/functions/__fish_complete_zfs_rw_properties.fish index b90cd809b..ebc8572e9 100644 --- a/share/functions/__fish_complete_zfs_rw_properties.fish +++ b/share/functions/__fish_complete_zfs_rw_properties.fish @@ -93,5 +93,5 @@ function __fish_complete_zfs_rw_properties -d "Completes with ZFS read-write pro echo -e "volmode\t"(_ "How to expose volumes to OS")" (default, geom, dev, none)" end # User properties; the /dev/null redirection masks the possible "no datasets available" - zfs list -o all ^/dev/null | head -n 1 | string replace -a -r "\s+" "\n" | string match -e ":" | string lower + zfs list -o all 2>/dev/null | head -n 1 | string replace -a -r "\s+" "\n" | string match -e ":" | string lower end diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 44f49b284..7f0a0a6a4 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -133,11 +133,11 @@ function __fish_config_interactive -d "Initializations that should be performed # c_m_p.py should work with any python version. set -l update_args -B $__fish_data_dir/tools/create_manpage_completions.py --manpath --cleanup-in '~/.config/fish/completions' --cleanup-in '~/.config/fish/generated_completions' if command -qs python3 - python3 $update_args >/dev/null ^/dev/null & + python3 $update_args >/dev/null 2>/dev/null & else if command -qs python2 - python2 $update_args >/dev/null ^/dev/null & + python2 $update_args >/dev/null 2>/dev/null & else if command -qs python - python $update_args >/dev/null ^/dev/null & + python $update_args >/dev/null 2>/dev/null & end end end @@ -165,14 +165,14 @@ function __fish_config_interactive -d "Initializations that should be performed function __fish_repaint --on-variable fish_color_cwd --description "Event handler, repaints the prompt when fish_color_cwd changes" if status --is-interactive set -e __fish_prompt_cwd - commandline -f repaint ^/dev/null + commandline -f repaint 2>/dev/null end end function __fish_repaint_root --on-variable fish_color_cwd_root --description "Event handler, repaints the prompt when fish_color_cwd_root changes" if status --is-interactive set -e __fish_prompt_cwd - commandline -f repaint ^/dev/null + commandline -f repaint 2>/dev/null end end @@ -231,13 +231,13 @@ function __fish_config_interactive -d "Initializations that should be performed set -g fish_bind_mode default if test "$fish_key_bindings" = fish_default_key_bindings # Redirect stderr per #1155 - fish_default_key_bindings ^/dev/null + fish_default_key_bindings 2>/dev/null else - eval $fish_key_bindings ^/dev/null + eval $fish_key_bindings 2>/dev/null end # Load user key bindings if they are defined if functions --query fish_user_key_bindings >/dev/null - fish_user_key_bindings ^/dev/null + fish_user_key_bindings 2>/dev/null end end @@ -326,7 +326,7 @@ function __fish_config_interactive -d "Initializations that should be performed # it ships with example handlers for bash and zsh, so we'll follow that format else if type -p -q pkgfile function __fish_command_not_found_handler --on-event fish_command_not_found - set -l __packages (pkgfile --binaries --verbose -- $argv[1] ^/dev/null) + set -l __packages (pkgfile --binaries --verbose -- $argv[1] 2>/dev/null) if test $status -eq 0 printf "%s may be found in the following packages:\n" "$argv[1]" printf " %s\n" $__packages diff --git a/share/functions/__fish_describe_command.fish b/share/functions/__fish_describe_command.fish index df2efc8d5..43936c4be 100644 --- a/share/functions/__fish_describe_command.fish +++ b/share/functions/__fish_describe_command.fish @@ -3,7 +3,7 @@ # function __fish_describe_command -d "Command used to find descriptions for commands" - apropos $argv ^/dev/null | awk -v FS=" +- +" '{ + apropos $argv 2>/dev/null | awk -v FS=" +- +" '{ split($1, names, ", "); for (name in names) if (names[name] ~ /^'"$argv"'.* *\([18]\)/ ) { diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index 8ad0b56cb..e70753445 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -193,7 +193,7 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi set -l svn_remote # get some config options from git-config - command git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' ^/dev/null | while read -lz key value + command git config -z --get-regexp '^(svn-remote\..*\.url|bash\.showupstream)$' 2>/dev/null | while read -lz key value switch $key case bash.showupstream set show_upstream $value @@ -241,7 +241,7 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi case svn\* # get the upstream from the 'git-svn-id: …' in a commit message # (git-svn uses essentially the same procedure internally) - set -l svn_upstream (git log --first-parent -1 --grep="^git-svn-id: \($svn_url_pattern\)" ^/dev/null) + set -l svn_upstream (git log --first-parent -1 --grep="^git-svn-id: \($svn_url_pattern\)" 2>/dev/null) if test (count $svn_upstream) -ne 0 echo $svn_upstream[-1] | read -l __ svn_upstream __ set svn_upstream (string replace -r '@.*' '' -- $svn_upstream) @@ -280,7 +280,7 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi end # Find how many commits we are ahead/behind our upstream - set count (command git rev-list --count --left-right $upstream...HEAD ^/dev/null) + set count (command git rev-list --count --left-right $upstream...HEAD 2>/dev/null) # calculate the result if test -n "$verbose" @@ -304,7 +304,7 @@ function __fish_git_prompt_show_upstream --description "Helper function for __fi echo "$prefix$___fish_git_prompt_char_upstream_diverged$ahead-$behind" end if test -n "$count" -a -n "$name" - echo " "(command git rev-parse --abbrev-ref "$upstream" ^/dev/null) + echo " "(command git rev-parse --abbrev-ref "$upstream" 2>/dev/null) end else if test -n "$informative" echo $count | read -l behind ahead @@ -339,7 +339,7 @@ function __fish_git_prompt --description "Prompt function for Git" if not command -sq git return 1 end - set -l repo_info (command git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD ^/dev/null) + set -l repo_info (command git rev-parse --git-dir --is-inside-git-dir --is-bare-repository --is-inside-work-tree HEAD 2>/dev/null) test -n "$repo_info" or return @@ -387,7 +387,7 @@ function __fish_git_prompt --description "Prompt function for Git" if set -q __fish_git_prompt_showuntrackedfiles set -l config (command git config --bool bash.showUntrackedFiles) if test "$config" != false - if command git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- '*' >/dev/null ^/dev/null + if command git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- '*' >/dev/null 2>/dev/null set u $___fish_git_prompt_char_untrackedfiles end end @@ -463,7 +463,7 @@ function __fish_git_prompt_staged --description "__fish_git_prompt helper, tells set -l staged if test -n "$sha" - command git diff-index --cached --quiet HEAD -- ^/dev/null + command git diff-index --cached --quiet HEAD -- 2>/dev/null or set staged $___fish_git_prompt_char_stagedstate else set staged $___fish_git_prompt_char_invalidstate @@ -475,7 +475,7 @@ function __fish_git_prompt_dirty --description "__fish_git_prompt helper, tells set -l dirty set -l os - command git diff --no-ext-diff --quiet --exit-code ^/dev/null + command git diff --no-ext-diff --quiet --exit-code 2>/dev/null set os $status if test $os -ne 0 set dirty $___fish_git_prompt_char_dirtystate @@ -487,7 +487,7 @@ set -g ___fish_git_prompt_status_order stagedstate invalidstate dirtystate untra function __fish_git_prompt_informative_status - set -l changedFiles (command git diff --name-status ^/dev/null | string match -r \\w) + set -l changedFiles (command git diff --name-status 2>/dev/null | string match -r \\w) set -l stagedFiles (command git diff --staged --name-status | string match -r \\w) set -l x (count $changedFiles) @@ -501,7 +501,7 @@ function __fish_git_prompt_informative_status set -l info # If `math` fails for some reason, assume the state is clean - it's the simpler path - set -l state (math $dirtystate + $invalidstate + $stagedstate + $untrackedfiles ^/dev/null) + set -l state (math $dirtystate + $invalidstate + $stagedstate + $untrackedfiles 2>/dev/null) if test -z "$state" or test "$state" = 0 set info $___fish_git_prompt_color_cleanstate$___fish_git_prompt_char_cleanstate$___fish_git_prompt_color_cleanstate_done @@ -549,9 +549,9 @@ function __fish_git_prompt_operation_branch_bare --description "__fish_git_promp set -l os if test -d $git_dir/rebase-merge - set branch (cat $git_dir/rebase-merge/head-name ^/dev/null) - set step (cat $git_dir/rebase-merge/msgnum ^/dev/null) - set total (cat $git_dir/rebase-merge/end ^/dev/null) + set branch (cat $git_dir/rebase-merge/head-name 2>/dev/null) + set step (cat $git_dir/rebase-merge/msgnum 2>/dev/null) + set total (cat $git_dir/rebase-merge/end 2>/dev/null) if test -f $git_dir/rebase-merge/interactive set operation "|REBASE-i" else @@ -559,10 +559,10 @@ function __fish_git_prompt_operation_branch_bare --description "__fish_git_promp end else if test -d $git_dir/rebase-apply - set step (cat $git_dir/rebase-apply/next ^/dev/null) - set total (cat $git_dir/rebase-apply/last ^/dev/null) + set step (cat $git_dir/rebase-apply/next 2>/dev/null) + set total (cat $git_dir/rebase-apply/last 2>/dev/null) if test -f $git_dir/rebase-apply/rebasing - set branch (cat $git_dir/rebase-apply/head-name ^/dev/null) + set branch (cat $git_dir/rebase-apply/head-name 2>/dev/null) set operation "|REBASE" else if test -f $git_dir/rebase-apply/applying set operation "|AM" @@ -585,7 +585,7 @@ function __fish_git_prompt_operation_branch_bare --description "__fish_git_promp end if test -z "$branch" - set branch (command git symbolic-ref HEAD ^/dev/null; set os $status) + set branch (command git symbolic-ref HEAD 2>/dev/null; set os $status) if test $os -ne 0 set detached yes set branch (switch "$__fish_git_prompt_describe_style" @@ -597,7 +597,7 @@ function __fish_git_prompt_operation_branch_bare --description "__fish_git_promp command git describe HEAD case default '*' command git describe --tags --exact-match HEAD - end ^/dev/null; set os $status) + end 2>/dev/null; set os $status) if test $os -ne 0 # Shorten the sha ourselves to 8 characters - this should be good for most repositories, # and even for large ones it should be good for most commits @@ -750,7 +750,7 @@ function __fish_git_prompt_repaint $varargs --description "Event handler, repain end end - commandline -f repaint ^/dev/null + commandline -f repaint 2>/dev/null end end @@ -771,7 +771,7 @@ function __fish_git_prompt_repaint_color $varargs --description "Event handler, set -e ___fish_git_prompt_color_{$name}_done end end - commandline -f repaint ^/dev/null + commandline -f repaint 2>/dev/null end end @@ -782,6 +782,6 @@ end function __fish_git_prompt_repaint_char $varargs --description "Event handler, repaints prompt when any char changes" if status --is-interactive set -e _$argv[3] - commandline -f repaint ^/dev/null + commandline -f repaint 2>/dev/null end end diff --git a/share/functions/__fish_hg_prompt.fish b/share/functions/__fish_hg_prompt.fish index 0ab444514..9e7663053 100644 --- a/share/functions/__fish_hg_prompt.fish +++ b/share/functions/__fish_hg_prompt.fish @@ -45,8 +45,8 @@ function __fish_hg_prompt --description 'Write out the hg prompt' end # Read branch and bookmark - set -l branch (cat $root/branch ^/dev/null; or echo default) - if set -l bookmark (cat $root/bookmarks.current ^/dev/null) + set -l branch (cat $root/branch 2>/dev/null; or echo default) + if set -l bookmark (cat $root/bookmarks.current 2>/dev/null) set branch "$branch|$bookmark" end diff --git a/share/functions/__fish_is_git_repository.fish b/share/functions/__fish_is_git_repository.fish index 8c7558278..57046b890 100644 --- a/share/functions/__fish_is_git_repository.fish +++ b/share/functions/__fish_is_git_repository.fish @@ -1,3 +1,3 @@ function __fish_is_git_repository --description 'Check if the current directory is a git repository' - git rev-parse --is-inside-work-tree ^/dev/null >/dev/null + git rev-parse --is-inside-work-tree 2>/dev/null >/dev/null end diff --git a/share/functions/__fish_make_completion_signals.fish b/share/functions/__fish_make_completion_signals.fish index cac6e135f..8c6c930c0 100644 --- a/share/functions/__fish_make_completion_signals.fish +++ b/share/functions/__fish_make_completion_signals.fish @@ -13,8 +13,8 @@ function __fish_make_completion_signals --description 'Make list of kill signals # The procps `kill -L` produces a more compact table. We can distinguish the two cases by # testing whether it supports `kill -t`; in which case it is the coreutils `kill` command. # Darwin doesn't have kill -t or kill -L - if kill -t ^/dev/null >/dev/null - or not kill -L ^/dev/null >/dev/null + if kill -t 2>/dev/null >/dev/null + or not kill -L 2>/dev/null >/dev/null # Posix systems print out the name of a signal using 'kill -l SIGNUM'. complete -c kill -s l --description "List names of available signals" for i in (seq 31) diff --git a/share/functions/__fish_man_page.fish b/share/functions/__fish_man_page.fish index 8bc32b76f..5c490cc6a 100644 --- a/share/functions/__fish_man_page.fish +++ b/share/functions/__fish_man_page.fish @@ -12,11 +12,11 @@ function __fish_man_page # Try "man first-second" and fall back to "man first" if that doesn't work out. set -l maincmd (basename $args[1]) if set -q args[2] - man "$maincmd-$args[2]" ^/dev/null - or man "$maincmd" ^/dev/null + man "$maincmd-$args[2]" 2>/dev/null + or man "$maincmd" 2>/dev/null or printf \a else - man "$maincmd" ^/dev/null + man "$maincmd" 2>/dev/null or printf \a end diff --git a/share/functions/__fish_paginate.fish b/share/functions/__fish_paginate.fish index 848392b4e..937c5c6de 100644 --- a/share/functions/__fish_paginate.fish +++ b/share/functions/__fish_paginate.fish @@ -11,7 +11,7 @@ function __fish_paginate -d "Paginate the current command using the users defaul if commandline -j | string match -q -r -v "$cmd *\$" - commandline -aj " ^&1 | $cmd;" + commandline -aj " 2>&1 | $cmd;" end end diff --git a/share/functions/__fish_portage_print_available_categories.fish b/share/functions/__fish_portage_print_available_categories.fish index ad3121cf1..21734c0ed 100644 --- a/share/functions/__fish_portage_print_available_categories.fish +++ b/share/functions/__fish_portage_print_available_categories.fish @@ -1,3 +1,3 @@ function __fish_portage_print_available_categories --description 'Print all available categories' - cat (__fish_portage_print_repository_paths)/profiles/categories ^ /dev/null + cat (__fish_portage_print_repository_paths)/profiles/categories 2>/dev/null end diff --git a/share/functions/__fish_print_help.fish b/share/functions/__fish_print_help.fish index 5f51b46e4..ff7668912 100644 --- a/share/functions/__fish_print_help.fish +++ b/share/functions/__fish_print_help.fish @@ -43,9 +43,9 @@ function __fish_print_help --description "Print help message for the specified f set mfish -mfish end if test -e "$__fish_data_dir/man/man1/$item.1" - set help (nroff -c -man $mfish -t $rLL "$__fish_data_dir/man/man1/$item.1" ^/dev/null) + set help (nroff -c -man $mfish -t $rLL "$__fish_data_dir/man/man1/$item.1" 2>/dev/null) else if test -e "$__fish_data_dir/man/man1/$item.1.gz" - set help (gunzip -c "$__fish_data_dir/man/man1/$item.1.gz" ^/dev/null | nroff -c -man $mfish -t $rLL ^/dev/null) + set help (gunzip -c "$__fish_data_dir/man/man1/$item.1.gz" 2>/dev/null | nroff -c -man $mfish -t $rLL 2>/dev/null) end # The original implementation trimmed off the top 5 lines and bottom 3 lines diff --git a/share/functions/__fish_print_lpr_options.fish b/share/functions/__fish_print_lpr_options.fish index 1e5ba97a1..9227031f9 100644 --- a/share/functions/__fish_print_lpr_options.fish +++ b/share/functions/__fish_print_lpr_options.fish @@ -1,4 +1,4 @@ function __fish_print_lpr_options --description 'Print lpr options' - lpoptions -l ^/dev/null | sed 's+\(.*\)/\(.*\):.*$+\1\t\2+' + lpoptions -l 2>/dev/null | sed 's+\(.*\)/\(.*\):.*$+\1\t\2+' end diff --git a/share/functions/__fish_print_lpr_printers.fish b/share/functions/__fish_print_lpr_printers.fish index a6fbb0c43..b070a16b2 100644 --- a/share/functions/__fish_print_lpr_printers.fish +++ b/share/functions/__fish_print_lpr_printers.fish @@ -1,5 +1,5 @@ function __fish_print_lpr_printers --description 'Print lpr printers' - lpstat -p ^/dev/null | sed 's/^\S*\s\(\S*\)\s\(.*\)$/\1\t\2/' + lpstat -p 2>/dev/null | sed 's/^\S*\s\(\S*\)\s\(.*\)$/\1\t\2/' end diff --git a/share/functions/__fish_print_make_targets.fish b/share/functions/__fish_print_make_targets.fish index c38e1b130..acb1a9fcf 100644 --- a/share/functions/__fish_print_make_targets.fish +++ b/share/functions/__fish_print_make_targets.fish @@ -17,7 +17,7 @@ function __fish_print_make_targets --argument-names directory file end set -l bsd_make - if make -C $directory -pn >/dev/null ^/dev/null + if make -C $directory -pn >/dev/null 2>/dev/null set bsd_make 0 else set bsd_make 1 @@ -25,9 +25,9 @@ function __fish_print_make_targets --argument-names directory file if test "$bsd_make" = 0 # https://stackoverflow.com/a/26339924 - make -C "$directory" -f "$file" -pRrq : ^/dev/null | awk -v RS= -F: '/^# Files/,/^# Finished Make data base/ {if ($1 !~ "^[#.]") {print $1}}' ^/dev/null + make -C "$directory" -f "$file" -pRrq : 2>/dev/null | awk -v RS= -F: '/^# Files/,/^# Finished Make data base/ {if ($1 !~ "^[#.]") {print $1}}' 2>/dev/null else - make -C "$directory" -f "$file" -d g1 -rn >/dev/null ^| awk -F, '/^#\*\*\* Input graph:/,/^$/ {if ($1 !~ "^#... ") {gsub(/# /,"",$1); print $1}}' ^/dev/null + make -C "$directory" -f "$file" -d g1 -rn >/dev/null 2>| awk -F, '/^#\*\*\* Input graph:/,/^$/ {if ($1 !~ "^#... ") {gsub(/# /,"",$1); print $1}}' 2>/dev/null end end diff --git a/share/functions/__fish_print_modules.fish b/share/functions/__fish_print_modules.fish index b52dd1535..388072e93 100644 --- a/share/functions/__fish_print_modules.fish +++ b/share/functions/__fish_print_modules.fish @@ -1,4 +1,4 @@ # Helper function for completions that need to enumerate Linux modules function __fish_print_modules - find /lib/modules/(uname -r)/{kernel,misc} -type f ^/dev/null | sed -e 's$/.*/\([^/.]*\).*$\1$' + find /lib/modules/(uname -r)/{kernel,misc} -type f 2>/dev/null | sed -e 's$/.*/\([^/.]*\).*$\1$' end diff --git a/share/functions/__fish_print_packages.fish b/share/functions/__fish_print_packages.fish index e4a7c3ff9..9c8e489ed 100644 --- a/share/functions/__fish_print_packages.fish +++ b/share/functions/__fish_print_packages.fish @@ -21,7 +21,7 @@ function __fish_print_packages if type -q -f apt-cache # Do not generate the cache as apparently sometimes this is slow. # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=547550 - apt-cache --no-generate pkgnames (commandline -ct) ^/dev/null | sed -e 's/$/'\t$package'/' + apt-cache --no-generate pkgnames (commandline -ct) 2>/dev/null | sed -e 's/$/'\t$package'/' return end diff --git a/share/functions/__fish_print_service_names.fish b/share/functions/__fish_print_service_names.fish index 70519859f..134dc0b93 100644 --- a/share/functions/__fish_print_service_names.fish +++ b/share/functions/__fish_print_service_names.fish @@ -3,7 +3,7 @@ function __fish_print_service_names -d 'All services known to the system' # For the `service` command, this needs to be without the type suffix # on Debian at least __fish_systemctl_services | string replace -r '.service$' '' - else if type -f rc-service ^/dev/null # OpenRC (Gentoo) + else if type -f rc-service 2>/dev/null # OpenRC (Gentoo) command rc-service -l else if test -d /etc/init.d # SysV on Debian and other linuxen string replace '/etc/init.d/' '' -- /etc/init.d/* diff --git a/share/functions/__fish_print_xdg_mimetypes.fish b/share/functions/__fish_print_xdg_mimetypes.fish index ec7252f44..97070e042 100644 --- a/share/functions/__fish_print_xdg_mimetypes.fish +++ b/share/functions/__fish_print_xdg_mimetypes.fish @@ -1,3 +1,3 @@ function __fish_print_xdg_mimetypes --description 'Print XDG mime types' - cat (__fish_print_xdg_applications_directories)/mimeinfo.cache ^/dev/null | string match -v '[MIME Cache]' | string replace = \t + cat (__fish_print_xdg_applications_directories)/mimeinfo.cache 2>/dev/null | string match -v '[MIME Cache]' | string replace = \t end diff --git a/share/functions/__fish_svn_prompt.fish b/share/functions/__fish_svn_prompt.fish index 5c649c2cd..0cae12cf4 100644 --- a/share/functions/__fish_svn_prompt.fish +++ b/share/functions/__fish_svn_prompt.fish @@ -96,7 +96,7 @@ function __fish_svn_prompt --description "Prompt function for svn" end # make sure that this is a svn repo - set -l checkout_info (command svn info ^/dev/null) + set -l checkout_info (command svn info 2>/dev/null) if [ $status -ne 0 ] return diff --git a/share/functions/__fish_systemctl_services.fish b/share/functions/__fish_systemctl_services.fish index c80f7333d..57251a20e 100644 --- a/share/functions/__fish_systemctl_services.fish +++ b/share/functions/__fish_systemctl_services.fish @@ -1,13 +1,13 @@ function __fish_systemctl_services if type -q systemctl if __fish_contains_opt user - systemctl --user list-unit-files --no-legend --type=service ^/dev/null $argv | cut -f 1 -d ' ' - systemctl --user list-units --state=loaded --no-legend --type=service ^/dev/null | cut -f 1 -d ' ' + systemctl --user list-unit-files --no-legend --type=service 2>/dev/null $argv | cut -f 1 -d ' ' + systemctl --user list-units --state=loaded --no-legend --type=service 2>/dev/null | cut -f 1 -d ' ' else # list-unit-files will also show disabled units - systemctl list-unit-files --no-legend --type=service ^/dev/null $argv | cut -f 1 -d ' ' + systemctl list-unit-files --no-legend --type=service 2>/dev/null $argv | cut -f 1 -d ' ' # list-units will not show disabled units but will show instances (like wpa_supplicant@wlan0.service) - systemctl list-units --state=loaded --no-legend --type=service ^/dev/null | cut -f 1 -d ' ' + systemctl list-units --state=loaded --no-legend --type=service 2>/dev/null | cut -f 1 -d ' ' end end end diff --git a/share/functions/__terlar_git_prompt.fish b/share/functions/__terlar_git_prompt.fish index f1fcedf33..170f8e08c 100644 --- a/share/functions/__terlar_git_prompt.fish +++ b/share/functions/__terlar_git_prompt.fish @@ -26,14 +26,14 @@ function __terlar_git_prompt --description 'Write out the git prompt' if not command -sq git return 1 end - set -l branch (git rev-parse --abbrev-ref HEAD ^/dev/null) + set -l branch (git rev-parse --abbrev-ref HEAD 2>/dev/null) if test -z $branch return end echo -n '|' - set -l index (git status --porcelain ^/dev/null|cut -c 1-2|sort -u) + set -l index (git status --porcelain 2>/dev/null|cut -c 1-2|sort -u) if test -z "$index" set_color $fish_color_git_clean diff --git a/share/functions/_fish_systemctl.fish b/share/functions/_fish_systemctl.fish index 0baee21ca..669bd41e5 100644 --- a/share/functions/_fish_systemctl.fish +++ b/share/functions/_fish_systemctl.fish @@ -15,7 +15,7 @@ function _fish_systemctl --description 'Call systemctl with some options from th set -l args $argv set -l cmdline (commandline -opc) (commandline -ct) set -e cmdline[1] - argparse $opts -- $cmdline ^/dev/null + argparse $opts -- $cmdline 2>/dev/null or return # If no subcommand has been given, return so this can be used as a condition. diff --git a/share/functions/fish_vi_cursor.fish b/share/functions/fish_vi_cursor.fish index 356cebf03..b667c5464 100644 --- a/share/functions/fish_vi_cursor.fish +++ b/share/functions/fish_vi_cursor.fish @@ -20,14 +20,14 @@ function fish_vi_cursor -d 'Set cursor shape for different vi modes' # vte-based terms set $TERM = xterm*, but only gained support relatively recently. # From https://bugzilla.gnome.org/show_bug.cgi?id=720821, it appears it was version 0.40.0 if set -q VTE_VERSION - and test "$VTE_VERSION" -lt 4000 ^/dev/null + and test "$VTE_VERSION" -lt 4000 2>/dev/null return end # We use the `tput` here just to see if terminfo thinks we can change the cursor. # We cannot use that sequence directly as it's not the correct one for konsole and iTerm, # and because we may want to change the cursor even though terminfo says we can't (tmux). - if not tput Ss >/dev/null ^/dev/null + if not tput Ss >/dev/null 2>/dev/null # Whitelist tmux... and not begin set -q TMUX @@ -57,7 +57,7 @@ function fish_vi_cursor -d 'Set cursor shape for different vi modes' or set -q ITERM_PROFILE or set -q VTE_VERSION # which version is already checked above # If $XTERM_VERSION is undefined, this will return 1 and print an error. Silence it. - or test (string replace -r "XTerm\((\d+)\)" '$1' -- $XTERM_VERSION) -ge 280 ^/dev/null + or test (string replace -r "XTerm\((\d+)\)" '$1' -- $XTERM_VERSION) -ge 280 2>/dev/null end end diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 6a65f33d7..e281e74a1 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -186,7 +186,7 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind gU upcase-word bind J end-of-line delete-char - bind K 'man (commandline -t) ^/dev/null; or echo -n \a' + bind K 'man (commandline -t) 2>/dev/null; or echo -n \a' bind yy kill-whole-line yank bind Y kill-whole-line yank diff --git a/share/functions/ls.fish b/share/functions/ls.fish index 003fc20c0..79c20237e 100644 --- a/share/functions/ls.fish +++ b/share/functions/ls.fish @@ -1,7 +1,7 @@ # # Make ls use colors if we are on a system that supports that feature and writing to stdout. # -if command ls --version >/dev/null ^/dev/null +if command ls --version >/dev/null 2>/dev/null # This appears to be GNU ls. function ls --description "List contents of directory" set -l param --color=auto @@ -29,7 +29,7 @@ if command ls --version >/dev/null ^/dev/null end end end -else if command ls -G / >/dev/null ^/dev/null +else if command ls -G / >/dev/null 2>/dev/null # It looks like BSD, OS X and a few more which support colors through the -G switch instead. function ls --description "List contents of directory" command ls -G $argv diff --git a/share/functions/nextd.fish b/share/functions/nextd.fish index 366cdd7c0..3d3d3cdfb 100644 --- a/share/functions/nextd.fish +++ b/share/functions/nextd.fish @@ -10,7 +10,7 @@ function nextd --description "Move forward in the directory history" set -l times 1 if set -q argv[1] - if test $argv[1] -ge 0 ^/dev/null + if test $argv[1] -ge 0 2>/dev/null set times $argv[1] else printf (_ "%s: The number of positions to skip must be a non-negative integer\n") nextd diff --git a/share/functions/prevd.fish b/share/functions/prevd.fish index 1d02036ea..acb119705 100644 --- a/share/functions/prevd.fish +++ b/share/functions/prevd.fish @@ -10,7 +10,7 @@ function prevd --description "Move back in the directory history" set -l times 1 if set -q argv[1] - if test $argv[1] -ge 0 ^/dev/null + if test $argv[1] -ge 0 2>/dev/null set times $argv[1] else printf (_ "%s: The number of positions to skip must be a non-negative integer\n") nextd diff --git a/share/functions/psub.fish b/share/functions/psub.fish index 61d8cf4cc..b764d6f35 100644 --- a/share/functions/psub.fish +++ b/share/functions/psub.fish @@ -50,7 +50,7 @@ function psub --description "Read from stdin into a file and output the filename # Find unique function name while true set funcname __fish_psub_(random) - if not functions $funcname >/dev/null ^/dev/null + if not functions $funcname >/dev/null 2>/dev/null break end end From 5b489ca30f3205b03b23dff6bedbc59df499e92b Mon Sep 17 00:00:00 2001 From: Peter Ammon Date: Sun, 1 Apr 2018 13:43:05 -0700 Subject: [PATCH 034/159] Remove caret redirection This removes the caret as a shorthand for redirecting stderr. Note that stderr may be redirected to a file via 2>/some/path... and may be redirected with a pipe via 2>|. Fixes #4394 --- CHANGELOG.md | 3 ++- src/common.cpp | 1 - src/fish_tests.cpp | 4 +--- src/tokenizer.cpp | 38 ++++++++++---------------------------- tests/gen_output.fish | 10 +++++----- tests/interactive.fish | 7 ++++--- tests/jobs.in | 2 +- tests/printf.in | 2 +- tests/read.in | 4 ++-- tests/test.fish | 2 +- tests/test6.in | 2 +- 11 files changed, 28 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1741ff32..de7c11d61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ This section is for changes merged to the `major` branch that are not also merge - Successive commas in brace expansions are handled in less surprising manner (`{,,,}` expands to four empty strings rather than an empty string, a comma and an empty string again). (#3002, #4632). - `%` is no longer used for process and job expansion. `$fish_pid` and `$last_pid` have taken the place of `%self` and `%last` respectively. (#4230, #1202) - The new `math` builtin (see below) does not support logical expressions; `test` should be used instead (#4777). +- The `?` wildcard has been removed (#4520). +- The `^` caret redirection for stderr has been removed (#4394). To redirect stderr, `2>/some/path` may be used, or `2>|` as a pipe. ## Notable fixes and improvements - `wait` builtin is added for waiting on processes (#4498). @@ -51,7 +53,6 @@ This section is for changes merged to the `major` branch that are not also merge - The machine hostname, where available, is now exposed as `$hostname` which is now a reserved variable. This drops the dependency on the `hostname` executable (#4422). - `functions --handlers` can be used to show event handlers (#4694). - Variables set in `if` and `while` conditions are available outside the block (#4820). -- The `?` wildcard has been removed (#4520). ## Other significant changes - Command substitution output is now limited to 10 MB by default (#3822). diff --git a/src/common.cpp b/src/common.cpp index a1612a06e..f5fcf65c3 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1007,7 +1007,6 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring case L'$': case L' ': case L'#': - case L'^': case L'<': case L'>': case L'(': diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index fb080f632..d748e6e27 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -534,7 +534,7 @@ static void test_tokenizer() { const wchar_t *str = L"string &1 'nested \"quoted\" '(string containing subshells " - L"){and,brackets}$as[$well (as variable arrays)] not_a_redirect^ ^ ^^is_a_redirect " + L"){and,brackets}$as[$well (as variable arrays)] not_a_redirect^ 2> 2>^is_a_redirect " L"&&& ||| " L"&& || & |" L"Compress_Newlines\n \n\t\n \nInto_Just_One"; @@ -608,8 +608,6 @@ static void test_tokenizer() { // Test redirection_type_for_string. if (redirection_type_for_string(L"<") != redirection_type_t::input) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); - if (redirection_type_for_string(L"^") != redirection_type_t::overwrite) - err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); if (redirection_type_for_string(L">") != redirection_type_t::overwrite) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); if (redirection_type_for_string(L"2>") != redirection_type_t::overwrite) diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index afd0903da..75d4ce71f 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -70,10 +70,8 @@ bool tokenizer_t::next(struct tok_t *result) { return true; } -/// Tests if this character can be a part of a string. The redirect ^ is allowed unless it's the -/// first character. Hash (#) starts a comment if it's the first character in a token; otherwise it -/// is considered a string character. See issue #953. -static bool tok_is_string_character(wchar_t c, bool is_first) { +/// Tests if this character can be a part of a string. +static bool tok_is_string_character(wchar_t c) { switch (c) { case L'\0': case L' ': @@ -84,15 +82,9 @@ static bool tok_is_string_character(wchar_t c, bool is_first) { case L'\r': case L'<': case L'>': - case L'&': { - // Unconditional separators. + case L'&': return false; - } - case L'^': { - // Conditional separator. - return !is_first; - } - default: { return true; } + default: return true; } } @@ -117,7 +109,6 @@ tok_t tokenizer_t::read_string() { std::vector expecting; int slice_offset = 0; const wchar_t *const buff_start = this->buff; - bool is_first = true; while (true) { wchar_t c = *this->buff; @@ -219,7 +210,7 @@ tok_t tokenizer_t::read_string() { break; } } - else if (mode == tok_mode::regular_text && !tok_is_string_character(c, is_first)) { + else if (mode == tok_mode::regular_text && !tok_is_string_character(c)) { break; } @@ -233,7 +224,6 @@ tok_t tokenizer_t::read_string() { #endif this->buff++; - is_first = false; } if ((!this->accept_unfinished) && (mode != tok_mode::regular_text)) { @@ -292,7 +282,7 @@ static maybe_t read_redirection_or_fd_pipe(const wchar_t size_t idx = 0; // Determine the fd. This may be specified as a prefix like '2>...' or it may be implicit like - // '>' or '^'. Try parsing out a number; if we did not get any digits then infer it from the + // '>'. Try parsing out a number; if we did not get any digits then infer it from the // first character. Watch out for overflow. long long big_fd = 0; for (; iswdigit(buff[idx]); idx++) { @@ -313,10 +303,6 @@ static maybe_t read_redirection_or_fd_pipe(const wchar_t result.fd = STDIN_FILENO; break; } - case L'^': { - result.fd = STDERR_FILENO; - break; - } default: { errored = true; break; @@ -325,12 +311,11 @@ static maybe_t read_redirection_or_fd_pipe(const wchar_t } // Either way we should have ended on the redirection character itself like '>'. - // Don't allow an fd with a caret redirection - see #1873 wchar_t redirect_char = buff[idx++]; // note increment of idx - if (redirect_char == L'>' || (redirect_char == L'^' && idx == 1)) { + if (redirect_char == L'>') { result.redirection_mode = redirection_type_t::overwrite; if (buff[idx] == redirect_char) { - // Doubled up like ^^ or >>. That means append. + // Doubled up like >>. That means append. result.redirection_mode = redirection_type_t::append; idx++; } @@ -505,8 +490,7 @@ maybe_t tokenizer_t::tok_next() { break; } case L'>': - case L'<': - case L'^': { + case L'<': { // There's some duplication with the code in the default case below. The key difference // here is that we must never parse these as a string; a failed redirection is an error! auto redir_or_pipe = read_redirection_or_fd_pipe(this->buff); @@ -615,9 +599,7 @@ bool move_word_state_machine_t::consume_char_punctuation(wchar_t c) { } bool move_word_state_machine_t::is_path_component_character(wchar_t c) { - // Always treat separators as first. All this does is ensure that we treat ^ as a string - // character instead of as stderr redirection, which I hypothesize is usually what is desired. - return tok_is_string_character(c, true) && !wcschr(L"/={,}'\"", c); + return tok_is_string_character(c) && !wcschr(L"/={,}'\"", c); } bool move_word_state_machine_t::consume_char_path_components(wchar_t c) { diff --git a/tests/gen_output.fish b/tests/gen_output.fish index 7bf7f3ec3..788e1a421 100755 --- a/tests/gen_output.fish +++ b/tests/gen_output.fish @@ -8,10 +8,10 @@ # for i in $argv - set template_out (basename $i .in).out - set template_err (basename $i .in).err - set template_status (basename $i .in).status + set template_out (basename $i .in).out + set template_err (basename $i .in).err + set template_status (basename $i .in).status - fish <$i >$template_out ^$template_err - echo $status >$template_status + fish <$i >$template_out 2>$template_err + echo $status >$template_status end diff --git a/tests/interactive.fish b/tests/interactive.fish index 41552f615..ad10cbf54 100644 --- a/tests/interactive.fish +++ b/tests/interactive.fish @@ -32,8 +32,9 @@ else set files_to_test *.expect end -source test_util.fish (status -f) $argv; or exit -cat interactive.config >> $XDG_CONFIG_HOME/fish/config.fish +source test_util.fish (status -f) $argv +or exit +cat interactive.config >>$XDG_CONFIG_HOME/fish/config.fish say -o cyan "Testing interactive functionality" if not type -q expect @@ -46,7 +47,7 @@ function test_file echo -n "Testing file $file ... " begin set -lx TERM dumb - expect -n -c 'source interactive.expect.rc' -f $file >$file.tmp.out ^$file.tmp.err + expect -n -c 'source interactive.expect.rc' -f $file >$file.tmp.out 2>$file.tmp.err end set -l exit_status $status set -l res ok diff --git a/tests/jobs.in b/tests/jobs.in index 9b2ba6c22..326f20e59 100644 --- a/tests/jobs.in +++ b/tests/jobs.in @@ -3,7 +3,7 @@ sleep 5 & sleep 5 & jobs -c jobs -q; echo $status -bg -23 1 ^/dev/null +bg -23 1 2>/dev/null or echo bg: invalid option -23 >&2 fg 3 bg 3 diff --git a/tests/printf.in b/tests/printf.in index 366b34921..4ee7e95cd 100644 --- a/tests/printf.in +++ b/tests/printf.in @@ -28,7 +28,7 @@ printf 'a\cb' echo # Bogus printf specifier, should produce no stdout -printf "%5" 10 ^/dev/null +printf "%5" 10 2>/dev/null # Octal escapes produce literal bytes, not characters # \376 is 0xFE diff --git a/tests/read.in b/tests/read.in index fcd2e071d..7230e4cfc 100644 --- a/tests/read.in +++ b/tests/read.in @@ -141,7 +141,7 @@ set line abcdefghijklmnopqrstuvwxyz # Ensure the `read` command terminates if asked to read too much data. The var # should be empty. We throw away any data we read if it exceeds the limit on # what we consider reasonable. -yes $line | dd bs=1024 count=(math "1 + $fish_read_limit / 1024") ^/dev/null | read --null x +yes $line | dd bs=1024 count=(math "1 + $fish_read_limit / 1024") 2>/dev/null | read --null x if test $status -ne 122 echo reading too much data did not terminate with failure status end @@ -188,7 +188,7 @@ end # Same as previous test but limit the amount of data fed to `read` rather than # using the `--nchars` flag. -yes $line | dd bs=1024 count=(math "$fish_read_limit / 1024") ^/dev/null | read --null x +yes $line | dd bs=1024 count=(math "$fish_read_limit / 1024") 2>/dev/null | read --null x if test $status -ne 0 echo the read of the max amount of data failed unexpectedly end diff --git a/tests/test.fish b/tests/test.fish index c42e4a08e..0a0dd5605 100644 --- a/tests/test.fish +++ b/tests/test.fish @@ -35,7 +35,7 @@ function test_file echo -n "Testing file $file ... " - ../test/root/bin/fish <$file >$base.tmp.out ^$base.tmp.err + ../test/root/bin/fish <$file >$base.tmp.out 2>$base.tmp.err set -l exit_status $status set -l res ok diff --git a/tests/test6.in b/tests/test6.in index 0ea772bb9..b64d2048b 100644 --- a/tests/test6.in +++ b/tests/test6.in @@ -88,7 +88,7 @@ if begin; rm -rf test6.tmp.dir; and mkdir test6.tmp.dir; end end popd if begin - set -l PATH $PWD/test6.tmp.dir $PATH ^/dev/null + set -l PATH $PWD/test6.tmp.dir $PATH 2>/dev/null complete -C$dir | grep "^$dir/.*Directory" >/dev/null end echo "incorrect implicit cd from PATH" From 222a45f07a7aa2d4ace50bb508935bc225e1d142 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 1 Apr 2018 16:10:59 -0700 Subject: [PATCH 035/159] Add acquire() to maybe_t Easy way to pull the value out. --- src/fish_tests.cpp | 6 ++++++ src/maybe.h | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index d748e6e27..340d78b74 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -4491,6 +4491,12 @@ void test_maybe() { do_test(m4 && *m4 == "hi"); maybe_t m5 = m0; do_test(!m5); + + maybe_t acquire_test("def"); + do_test(acquire_test); + std::string res = acquire_test.acquire(); + do_test(!acquire_test); + do_test(res == "def"); } void test_layout_cache() { diff --git a/src/maybe.h b/src/maybe.h index 898731f9d..9e8301ed5 100644 --- a/src/maybe.h +++ b/src/maybe.h @@ -64,6 +64,14 @@ class maybe_t { return *reinterpret_cast(storage); } + // Transfer the value to the caller. + T acquire() { + assert(filled && "maybe_t does not have a value"); + T res = std::move(value()); + reset(); + return res; + } + // Clear the value. void reset() { if (filled) { From ca13e816cef35d7cec70d2ba899bd2d5911a87d0 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 1 Apr 2018 16:11:12 -0700 Subject: [PATCH 036/159] Make fish more resilient to empty key bindings If fish_key_bindings gets set to empty, fish will become unusable. In this case reset it to fish_default_key_bindings. --- share/functions/__fish_config_interactive.fish | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 7f0a0a6a4..59b52f4b6 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -188,13 +188,14 @@ function __fish_config_interactive -d "Initializations that should be performed complete -x -p "/etc/init.d/*" -a reload --description 'Reload service configuration' end - # Make sure some key bindings are set - if not set -q fish_key_bindings - set -U fish_key_bindings fish_default_key_bindings - end # Reload key bindings when binding variable change function __fish_reload_key_bindings -d "Reload key bindings when binding variable change" --on-variable fish_key_bindings + # Make sure some key bindings are set + if not set -q fish_key_bindings + set -U fish_key_bindings fish_default_key_bindings + end + # Do nothing if the key bindings didn't actually change. # This could be because the variable was set to the existing value # or because it was a local variable. @@ -297,7 +298,8 @@ function __fish_config_interactive -d "Initializations that should be performed # First check if we are on OpenSUSE since SUSE's handler has no options # but the same name and path as Ubuntu's. - if contains -- suse $os; or contains -- sles $os + if contains -- suse $os + or contains -- sles $os and type -q command-not-found function __fish_command_not_found_handler --on-event fish_command_not_found /usr/bin/command-not-found $argv[1] From ff10e504a132750d64282987bfdd6f386b6a38b3 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 1 Apr 2018 17:43:12 -0700 Subject: [PATCH 037/159] Remove MAC address from universal variables file This switches the universal variables file from a machine-specific name to the fixed '.config/fish/fish_universal_variables'. The old file name is migrated if necessary. Fixes #1912 --- src/env.cpp | 2 +- src/env_universal_common.cpp | 83 ++++++++++++++++++++++-------------- src/env_universal_common.h | 11 ++--- src/fish_tests.cpp | 2 +- 4 files changed, 60 insertions(+), 38 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index b156da24a..af2f8e451 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -1008,7 +1008,7 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { assert(s_universal_variables == NULL); s_universal_variables = new env_universal_t(L""); callback_data_list_t callbacks; - s_universal_variables->load(callbacks); + s_universal_variables->initialize(callbacks); env_universal_callbacks(callbacks); // Now that the global scope is fully initialized, add a toplevel local scope. This same local diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index 643078f3e..d926a1620 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -78,19 +78,29 @@ static wcstring get_machine_identifier(); -static wcstring vars_filename_in_directory(const wcstring &wdir) { - if (wdir.empty()) return L""; - - wcstring result = wdir; - result.append(L"/fishd."); - result.append(get_machine_identifier()); +/// return a list of paths where the uvars file has been historically stored. +static wcstring_list_t get_legacy_paths(const wcstring &wdir) { + wcstring_list_t result; + result.push_back(wdir + L"/fishd." + get_machine_identifier()); + wcstring hostname_id; + if (get_hostname_identifier(hostname_id)) { + result.push_back(wdir + L'/' + hostname_id); + } return result; } -static const wcstring default_vars_path() { +static maybe_t default_vars_path_directory() { wcstring path; - path_get_config(path); - return vars_filename_in_directory(path); + if (!path_get_config(path)) return none(); + return path; +} + +static maybe_t default_vars_path() { + if (auto path = default_vars_path_directory()) { + path->append(L"/fish_universal_variables"); + return path; + } + return none(); } #if !defined(__APPLE__) && !defined(__CYGWIN__) @@ -270,8 +280,7 @@ static wcstring encode_serialized(const wcstring_list_t &vals) { return join_strings(vals, ARRAY_SEP); } -env_universal_t::env_universal_t(wcstring path) - : explicit_vars_path(std::move(path)), tried_renaming(false), last_read_file(kInvalidFileID) {} +env_universal_t::env_universal_t(wcstring path) : explicit_vars_path(std::move(path)) {} maybe_t env_universal_t::get(const wcstring &name) const { var_table_t::const_iterator where = vars.find(name); @@ -499,26 +508,35 @@ bool env_universal_t::move_new_vars_file_into_place(const wcstring &src, const w return ret == 0; } -bool env_universal_t::load(callback_data_list_t &callbacks) { - const wcstring vars_path = - explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path; +bool env_universal_t::initialize(callback_data_list_t &callbacks) { scoped_lock locker(lock); - bool success = load_from_path(vars_path, callbacks); - if (!success && !tried_renaming && errno == ENOENT) { - // We failed to load, because the file was not found. Older fish used the hostname only. Try - // moving the filename based on the hostname into place; if that succeeds try again. - // Silently "upgraded." - tried_renaming = true; - wcstring hostname_id; - if (get_hostname_identifier(hostname_id)) { - const wcstring hostname_path = wdirname(vars_path) + L'/' + hostname_id; - if (0 == wrename(hostname_path, vars_path)) { - // We renamed - try again. - success = this->load(callbacks); + if (!explicit_vars_path.empty()) { + return load_from_path(explicit_vars_path, callbacks); + } + + // Get the variables path; if there is none (e.g. HOME is bogus) it's hopeless. + auto vars_path = default_vars_path(); + if (!vars_path) return false; + + bool success = load_from_path(*vars_path, callbacks); + if (!success && errno == ENOENT) { + // We failed to load, because the file was not found. Attempt to load from our legacy paths. + if (auto dir = default_vars_path_directory()) { + for (const wcstring &path : get_legacy_paths(*dir)) { + if (load_from_path(path, callbacks)) { + // Mark every variable as modified. + // This tells the uvars to write out the values loaded from the legacy path; + // otherwise it will conclude that the values have been deleted since they + // aren't present. + for (const auto &kv : vars) { + modified.insert(kv.first); + } + success = true; + break; + } } } } - return success; } @@ -669,9 +687,12 @@ bool env_universal_t::sync(callback_data_list_t &callbacks) { // instances of fish will not be able to obtain it. This seems to be a greater risk than that of // data loss on lockless NFS. Users who put their home directory on lockless NFS are playing // with fire anyways. - const wcstring vars_path = - explicit_vars_path.empty() ? default_vars_path() : explicit_vars_path; - + wcstring vars_path = explicit_vars_path; + if (vars_path.empty()) { + if (auto default_path = default_vars_path()) { + vars_path = default_path.acquire(); + } + } if (vars_path.empty()) { debug(2, L"No universal variable path available"); return false; @@ -941,7 +962,7 @@ bool get_hostname_identifier(wcstring &result) { /// Get a sort of unique machine identifier. Prefer the MAC address; if that fails, fall back to the /// hostname; if that fails, pick something. -wcstring get_machine_identifier() { +static wcstring get_machine_identifier() { wcstring result; unsigned char mac_addr[MAC_ADDRESS_MAX_LEN] = {}; if (get_mac_address(mac_addr)) { diff --git a/src/env_universal_common.h b/src/env_universal_common.h index d1521cdd3..7c2d08f88 100644 --- a/src/env_universal_common.h +++ b/src/env_universal_common.h @@ -31,7 +31,9 @@ typedef std::vector callback_data_list_t; bool get_hostname_identifier(wcstring &result); /// Class representing universal variables. class env_universal_t { - var_table_t vars; // current values + // The table of variables. Note this is sorted; this ensures that the output file is in sorted + // order. + var_table_t vars; // Keys that have been modified, and need to be written. A value here that is not present in // vars indicates a deleted value. @@ -41,7 +43,6 @@ class env_universal_t { const wcstring explicit_vars_path; mutable fish_mutex_t lock; - bool tried_renaming; bool load_from_path(const wcstring &path, callback_data_list_t &callbacks); void load_from_fd(int fd, callback_data_list_t &callbacks); @@ -55,7 +56,7 @@ class env_universal_t { bool move_new_vars_file_into_place(const wcstring &src, const wcstring &dst); // File id from which we last read. - file_id_t last_read_file; + file_id_t last_read_file = kInvalidFileID; // Given a variable table, generate callbacks representing the difference between our vars and // the new vars. @@ -86,8 +87,8 @@ class env_universal_t { // Gets variable names. wcstring_list_t get_names(bool show_exported, bool show_unexported) const; - /// Loads variables at the correct path. - bool load(callback_data_list_t &callbacks); + /// Loads variables at the correct path, optionally migrating from a legacy path. + bool initialize(callback_data_list_t &callbacks); /// Reads and writes variables at the correct path. Returns true if modified variables were /// written. diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 340d78b74..8795fab54 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -2753,7 +2753,7 @@ static void test_universal() { env_universal_t uvars(UVARS_TEST_PATH); callback_data_list_t callbacks; - bool loaded = uvars.load(callbacks); + bool loaded = uvars.initialize(callbacks); if (!loaded) { err(L"Failed to load universal variables"); } From 0d0a65dc87ca5a38dc387b1bf22bfa4397d8f3f0 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 1 Apr 2018 17:46:02 -0700 Subject: [PATCH 038/159] CHANGELOG removal of MAC address from uvars file Part of #1912 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de7c11d61..2e7262781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,7 @@ This section is for changes merged to the `major` branch that are not also merge - The machine hostname, where available, is now exposed as `$hostname` which is now a reserved variable. This drops the dependency on the `hostname` executable (#4422). - `functions --handlers` can be used to show event handlers (#4694). - Variables set in `if` and `while` conditions are available outside the block (#4820). +- The universal variables file no longer contains the MAC address. It is now at the fixed location `.config/fish/fish_universal_variables`. ## Other significant changes - Command substitution output is now limited to 10 MB by default (#3822). From ba8bdbb3ae821c416cb76a174fa000fb3f784349 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 1 Apr 2018 17:47:43 -0700 Subject: [PATCH 039/159] Reference issue for removing uvars MAC address in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e7262781..b2cdb4fae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,7 +53,7 @@ This section is for changes merged to the `major` branch that are not also merge - The machine hostname, where available, is now exposed as `$hostname` which is now a reserved variable. This drops the dependency on the `hostname` executable (#4422). - `functions --handlers` can be used to show event handlers (#4694). - Variables set in `if` and `while` conditions are available outside the block (#4820). -- The universal variables file no longer contains the MAC address. It is now at the fixed location `.config/fish/fish_universal_variables`. +- The universal variables file no longer contains the MAC address. It is now at the fixed location `.config/fish/fish_universal_variables` (#1912). ## Other significant changes - Command substitution output is now limited to 10 MB by default (#3822). From a98cc75f9e86075617dc68c35794c16270b59265 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 1 Apr 2018 17:50:08 -0700 Subject: [PATCH 040/159] Reword warning inside fish_universal_variables file Prior to this fix, the fish universal variables file claimed that changes to it would be overwritten. This no longer true and has not been true for a long time. Remove that warning. --- src/env_universal_common.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index d926a1620..c8649167e 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -72,9 +72,7 @@ #define PARSE_ERR L"Unable to parse universal variable message: '%ls'" /// Small note about not editing ~/.fishd manually. Inserted at the top of all .fishd files. -#define SAVE_MSG \ - "# This file is automatically generated by the fish.\n# Do NOT edit it directly, your " \ - "changes will be overwritten.\n" +#define SAVE_MSG "# This file contains fish universal variable definitions.\n" static wcstring get_machine_identifier(); From f922875dbc7b3f2d39f5388954301ca13de0c88e Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sun, 1 Apr 2018 17:55:21 -0700 Subject: [PATCH 041/159] Fix the Linux build --- src/env_universal_common.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/env_universal_common.cpp b/src/env_universal_common.cpp index c8649167e..e8d24e547 100644 --- a/src/env_universal_common.cpp +++ b/src/env_universal_common.cpp @@ -162,8 +162,11 @@ static wcstring get_runtime_path() { /// Returns a "variables" file in the appropriate runtime directory. This is called infrequently and /// so does not need to be cached. static wcstring default_named_pipe_path() { - // Note that vars_filename_in_directory returns empty string when passed the empty string. - return vars_filename_in_directory(get_runtime_path()); + wcstring result = get_runtime_path(); + if (!result.empty()) { + result.append(L"/fish_universal_variables"); + } + return result; } #endif From 5c56765d120132e4c6e6a4e30708116c5849a64f Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 2 Apr 2018 21:24:50 +0800 Subject: [PATCH 042/159] hg prompt: fix infinite loop due to incorrect scoping 4b079e16e5f208342f9ccc48b38b02653fb7717b fixed some unintended behaviour which the hg prompt was apparently relying upon, producing an infinite loop whenever called. --- share/functions/__fish_hg_prompt.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_hg_prompt.fish b/share/functions/__fish_hg_prompt.fish index 9e7663053..6a4a3e721 100644 --- a/share/functions/__fish_hg_prompt.fish +++ b/share/functions/__fish_hg_prompt.fish @@ -37,7 +37,7 @@ function __fish_hg_prompt --description 'Write out the hg prompt' break end # Go up one directory - set -l dir (string replace -r '[^/]*/?$' '' $dir) + set dir (string replace -r '[^/]*/?$' '' $dir) end if test -z "$root" From 61ab3aea8c0f4e3c5ba6de12d1153e077c0f7aac Mon Sep 17 00:00:00 2001 From: David Adam Date: Mon, 2 Apr 2018 21:35:10 +0800 Subject: [PATCH 043/159] webconfig: import webbrowser with empty TERM Re-introduces the changes from 168d25e7806a147027a38c4e3135594c52bb7c2b which were inadvertently removed in 20bcbcc2528c63a2309b045266f4a33dfff0a298. Work on #4299. Re-commit of #1132. --- share/tools/web_config/webconfig.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 3d392867f..30af43436 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -17,7 +17,6 @@ import socket import string import subprocess import sys -import webbrowser FISH_BIN_PATH = False # will be set later IS_PY2 = sys.version_info[0] == 2 @@ -39,6 +38,9 @@ def isMacOS10_12_5_OrLater(): # Disable CLI web browsers term = os.environ.pop('TERM', None) +# This import must be done with an empty $TERM, otherwise a command-line browser may be started +# which will block the whole process - see https://docs.python.org/3/library/webbrowser.html +import webbrowser if term: os.environ['TERM'] = term From 358e9def5bfd513d0ad243f744c55318e95d56a4 Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 2 Apr 2018 10:26:40 -0700 Subject: [PATCH 044/159] Fix the git completion Commit 6e56637cf ran fish_indent on the git completion and mangled some of it. Manually revert the non-essential changes. --- share/completions/git.fish | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index cde489e63..6721c879a 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -6,18 +6,25 @@ function __fish_git_commits # This allows filtering by subject with the new pager! # Because even subject lines can be quite long, # trim them (abbrev'd hash+tab+subject) to 73 characters - command git log --pretty=tformat:"%h"\t"%s" --all --max-count=1000 2>/dev/null | string replace -r '(.{73}).+' '$1…' + command git log --pretty=tformat:"%h"\t"%s" --all --max-count=1000 2>/dev/null \ + | string replace -r '(.{73}).+' '$1…' end function __fish_git_recent_commits # Like __fish_git_commits, but not on all branches and limited to # the last 50 commits. Used for fixup, where only the current branch # and the latest commits make sense. - command git log --pretty=tformat:"%h"\t"%s" --max-count=50 2>/dev/null | string replace -r '(.{73}).+' '$1…' + command git log --pretty=tformat:"%h"\t"%s" --max-count=50 2>/dev/null \ + | string replace -r '(.{73}).+' '$1…' end function __fish_git_branches - command git branch --no-color -a $argv 2>/dev/null # Filter out detached heads and such ("(HEAD detached at SOMESHA)", localized). | string match -v '\* (*)' | string match -r -v ' -> ' | string trim -c "* " # We assume anything that's not remote is a local branch. | string replace -r '^(?!remotes/)(.*)' '$1\tLocal Branch' | string replace -r "^remotes/(.*)" '$1\tRemote Branch' + command git branch --no-color -a $argv 2>/dev/null \ + # Filter out detached heads and such ("(HEAD detached at SOMESHA)", localized). + | string match -v '\* (*)' | string match -r -v ' -> ' | string trim -c "* " \ + # We assume anything that's not remote is a local branch. + | string replace -r '^(?!remotes/)(.*)' '$1\tLocal Branch' \ + | string replace -r "^remotes/(.*)" '$1\tRemote Branch' end function __fish_git_tags @@ -89,7 +96,8 @@ function __fish_git_files # E.g. `git reset $submodule` won't do anything (not even print an error). # --ignore-submodules=all was added in git 1.7.2, released July 2010. set -l use_next - command git status --porcelain -z --ignore-submodules=all | while read -lz -d '' line + command git status --porcelain -z --ignore-submodules=all \ + | while read -lz -d '' line # The entire line is the "from" from a rename. if set -q use_next[1] if contains -- $use_next $argv @@ -136,8 +144,7 @@ function __fish_git_files case 'A ' AM AD # Additions are only shown here if they are staged. # Otherwise it's an untracked file. - contains -- added $argv - or contains -- all-staged $argv + contains -- added $argv; or contains -- all-staged $argv and printf '%s\t%s\n' "$file" $added_desc case '*M' # Modified @@ -148,8 +155,7 @@ function __fish_git_files # which means it is staged. # This is useless for many commands - e.g. `checkout` won't do anything with this. # So it needs to be requested explicitly. - contains -- modified-staged $argv - or contains -- all-staged $argv + contains -- modified-staged $argv; or contains -- all-staged $argv and printf '%s\t%s\n' "$file" $staged_modified_desc case '*D' contains -- deleted $argv @@ -159,8 +165,7 @@ function __fish_git_files # There is both X unmodified and Y either M or D ("not updated") # and Y is D and X is unmodified or [MARC] ("deleted in work tree"). # For our purposes, we assume this is a staged deletion. - contains -- deleted-staged $argv - or contains -- all-staged $argv + contains -- deleted-staged $argv; or contains -- all-staged $argv and printf '%s\t%s\n' "$file" $staged_deleted_desc case '\?\?' # Untracked @@ -196,8 +201,7 @@ end function __fish_git_needs_command set cmd (commandline -opc) set -l skip_next 1 - set -q cmd[2] - or return 0 + set -q cmd[2]; or return 0 # Skip first word because it's "git" or a wrapper for c in $cmd[2..-1] test $skip_next -eq 0 @@ -551,7 +555,7 @@ complete -f -c git -n '__fish_git_needs_command' -a init -d 'Create an empty git # TODO options ### log -complete -c git -n '__fish_git_needs_command' -a shortlog -d 'Show commit shortlog' +complete -c git -n '__fish_git_needs_command' -a shortlog -d 'Show commit shortlog' complete -c git -n '__fish_git_needs_command' -a log -d 'Show commit logs' complete -c git -n '__fish_git_using_command log; and not contains -- -- (commandline -op)' -a '(__fish_git_refs) (__fish_git_ranges)' From 99ecaec17501542b3627f531fc8570d467319bbf Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 2 Apr 2018 18:36:11 -0500 Subject: [PATCH 045/159] Use system web browser under WSL Launch `cmd.exe /c "start URL"` under WSL for both `fish_config` and `help`. This works around #4299 but does not address the underlying issue (#1132). --- share/functions/help.fish | 9 ++++++++- share/tools/web_config/webconfig.py | 10 ++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/share/functions/help.fish b/share/functions/help.fish index e04aed808..e42ad81b7 100644 --- a/share/functions/help.fish +++ b/share/functions/help.fish @@ -119,6 +119,11 @@ function help --description 'Show help for the fish shell' set fish_help_page "index.html" end + set -l wsl 0 + if uname -a | string match -qr Microsoft + set wsl 1 + end + set -l page_url if test -f $__fish_help_dir/index.html # Help is installed, use it @@ -150,8 +155,10 @@ function help --description 'Show help for the fish shell' end end + if test $wsl -eq 1 + cmd.exe /c "start $page_url" # If browser is known to be graphical, put into background - if contains -- $fish_browser[1] $graphical_browsers + else if contains -- $fish_browser[1] $graphical_browsers switch $fish_browser[1] case 'htmlview' 'x-www-browser' printf (_ 'help: Help is being displayed in your default browser.\n') diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 30af43436..b8c0f8007 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -35,6 +35,14 @@ def isMacOS10_12_5_OrLater(): version = platform.mac_ver()[0] return version and LooseVersion(version) >= LooseVersion('10.12.5') +def is_wsl(): + """ Return whether we are running under the Windows Subsystem for Linux """ + if 'linux' in platform.system().lower(): + with open('/proc/version', 'r') as f: + if 'Microsoft' in f.read(): + return True + return False + # Disable CLI web browsers term = os.environ.pop('TERM', None) @@ -1139,6 +1147,8 @@ fileurl = 'file://' + filename print("Web config started at '%s'. Hit enter to stop." % fileurl) if isMacOS10_12_5_OrLater(): subprocess.check_call(['open', fileurl]) +elif is_wsl(): + subprocess.call(['cmd.exe', '/c', "start %s" % url]) else: webbrowser.open(fileurl) From cc50103e5343953df20ac93eb6ab6591a6c6d30e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 3 Apr 2018 14:05:07 -0500 Subject: [PATCH 046/159] Unblock builtins from completions The `head_exists` value was being reset after being set to true for most builtins, causing completions to not trigger. --- src/complete.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/complete.cpp b/src/complete.cpp index dbff9065c..acf9ea477 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -870,12 +870,12 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop run_on_main_thread([&completion_snapshot] () { completion_snapshot = std::move(env_vars_snapshot_t( (wchar_t const * const []) { L"fish_function_path", nullptr } )); }); - } - head_exists = function_exists_no_autoload(cmd.c_str(), completion_snapshot); - // While it may seem like first testing `path_get_path` before resorting to an env lookup may be faster, path_get_path can potentially - // do a lot of FS/IO access, so env.get() + function_exists() should still be faster. - head_exists = head_exists || path_get_path(cmd, nullptr); + head_exists = function_exists_no_autoload(cmd.c_str(), completion_snapshot); + // While it may seem like first testing `path_get_path` before resorting to an env lookup may be faster, path_get_path can potentially + // do a lot of FS/IO access, so env.get() + function_exists() should still be faster. + head_exists = head_exists || path_get_path(cmd, nullptr); + } if (!head_exists) { //Do not load custom completions if the head does not exist From c492d03f5133902669252ba4a0dcfb607c167fce Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 3 Apr 2018 14:40:51 -0500 Subject: [PATCH 047/159] Overhaul completions for `set` and add new completions for `set -e` Now the description includes the variable scope, `set [-e] -[Ugl]` completions only provide variables matching that scope, and completions that shouldn't be modified are hidden from the user. Completions that are often modified but rarely unset (`fish_*` variables) are omitted from `set -e` completions. A new helper function `__fish_seen_argument` has been added that makes it easy to only provied completions for a specific flag. --- share/completions/set.fish | 22 +++++++++++++++++++--- share/functions/__fish_seen_argument.fish | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 share/functions/__fish_seen_argument.fish diff --git a/share/completions/set.fish b/share/completions/set.fish index b684ae679..e0e5d3f7d 100644 --- a/share/completions/set.fish +++ b/share/completions/set.fish @@ -40,6 +40,7 @@ function __fish_set_is_locale -d 'Test if We are specifying a locale value for t return 0 case '-*' + continue case '*' return 1 @@ -65,9 +66,25 @@ complete -c set -n '__fish_is_first_token' -s q -l query -d "Test if variable is complete -c set -n '__fish_is_first_token' -s h -l help -d "Display help and exit" complete -c set -n '__fish_is_first_token' -s n -l names -d "List the names of the variables, but not their value" +#TODO: add CPP code to generate list of read-only variables and exclude them from the following completions # Complete using preexisting variable names -complete -c set -n '__fish_is_first_token' -x -a "(set|sed -e 's/ /'\t'Variable: /')" +complete -c set -n '__fish_is_first_token; and not __fish_seen_argument -s e -l erase; and not __fish_seen_argument -s l -s g -s U -l local -l global -l universal' -x -a "(set -l | string match -rv '^__' | string replace ' ' \t'Local Variable: ')" +complete -c set -n '__fish_is_first_token; and not __fish_seen_argument -s e -l erase; and not __fish_seen_argument -s l -s g -s U -l local -l global -l universal' -x -a "(set -g | string match -rv '^__' | string replace ' ' \t'Global Variable: ')" +complete -c set -n '__fish_is_first_token; and not __fish_seen_argument -s e -l erase; and not __fish_seen_argument -s l -s g -s U -l local -l global -l universal' -x -a "(set -U | string match -rv '^__' | string replace ' ' \t'Universal Variable: ')" +# Complete scope-specific variables +complete -c set -n '__fish_is_first_token; and __fish_seen_argument -s l -l local' -x -a "(set -l | string match -rv '^__' | string replace ' ' \t'Local Variable: ')" +complete -c set -n '__fish_is_first_token; and __fish_seen_argument -s g -l global' -x -a "(set -g | string match -rv '^__' | string replace ' ' \t'Global Variable: ')" +complete -c set -n '__fish_is_first_token; and __fish_seen_argument -s U -l universal' -x -a "(set -U | string match -rv '^__' | string replace ' ' \t'Universal Variable: ')" + +# Complete using preexisting variable names for `set --erase` +complete -c set -n '__fish_seen_argument -s e -l erase; and not __fish_seen_argument -s l -s U -s g -l local -l global -l Universal' -f -a "(set -g | string match -rv '^_|^fish_' | string replace ' ' \tGlobal\ Variable:\ )" +complete -c set -n '__fish_seen_argument -s e -l erase; and not __fish_seen_argument -s l -s U -s g -l local -l global -l Universal' -f -a "(set -l | string match -rv '^_|^fish_' | string replace ' ' \tLocal\ Variable:\ )" +complete -c set -n '__fish_seen_argument -s e -l erase; and not __fish_seen_argument -s l -s U -s g -l local -l global -l Universal' -f -a "(set -U | string match -rv '^_|^fish_' | string replace ' ' \tUniversal\ Variable:\ )" +# Complete scope-specific variables for `set --erase` +complete -c set -n '__fish_seen_argument -s e -l erase; and __fish_seen_argument -s g -l global' -f -a "(set -g | string match -rv '^_|^fish_' | string replace ' ' \t'Global Variable: ')" +complete -c set -n '__fish_seen_argument -s e -l erase; and __fish_seen_argument -s U -l universal' -f -a "(set -u | string match -rv '^_|^fish_' | string replace ' ' \t'Universal Variable: ')" +complete -c set -n '__fish_seen_argument -s e -l erase; and __fish_seen_argument -s l -l local' -f -a "(set -l | string match -rv '^_|^fish_' | string replace ' ' \t'Local Variable: ')" # Color completions complete -c set -n '__fish_set_is_color' -x -a '(set_color --print-colors)' -d Color @@ -75,6 +92,5 @@ complete -c set -n '__fish_set_is_color' -s b -l background -x -a '(set_color -- complete -c set -n '__fish_set_is_color' -s o -l bold -d 'Make font bold' # Locale completions -complete -c set -n '__fish_is_first_token' -x -a '$__fish_locale_vars' -d 'Locale variable' -complete -c set -n '__fish_set_is_locale' -x -a '(locale -a)' -d (_ "Locale") +complete -c set -n '__fish_set_is_locale; and not __fish_seen_argument -s e -l erase' -x -a '(locale -a)' -d (_ "Locale") complete -c set -s L -l long -d 'Do not truncate long lines' diff --git a/share/functions/__fish_seen_argument.fish b/share/functions/__fish_seen_argument.fish new file mode 100644 index 000000000..dd9a6dd2b --- /dev/null +++ b/share/functions/__fish_seen_argument.fish @@ -0,0 +1,22 @@ +function __fish_seen_argument + argparse 's/short=+' 'l/long=+' -- $argv + + set cmd (commandline -co) + set -e cmd[1] + for t in $cmd + for s in $_flag_s + if string match -qr "^-[A-z0-9]*"$s"[A-z0-9]*\$" -- $t + return 0 + end + end + + for l in $_flag_l + if string match -q "--$l" -- $t + return 0 + end + end + end + + return 1 +end + From d8a1928c243b35fd399a56d69ed526f8c2b64a0a Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 3 Apr 2018 14:57:02 -0500 Subject: [PATCH 048/159] Convert list of builtins from sorted array to unordered_set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The order of this list does not need to be strictly maintained any longer. Benchmarked with `hyperfine` as follows, where `bench1` is the existing approach of binary search and `bench2` is the new unordered_set code, (executed under bash because fish would always return non-zero). The benchmark code checks each argv to see if it is a builtin keyword (both return the same result): ``` hyperfine './bench1 $(shuf /usr/share/dict/words)' './bench2 $(shuf /usr/share/dict/words)' Benchmark #1: ./bench1 $(shuf /usr/share/dict/words) Time (mean ± σ): 68.4 ms ± 3.0 ms [User: 28.8 ms, System: 38.9 ms] Range (min … max): 60.4 ms … 75.4 ms Benchmark #2: ./bench2 $(shuf /usr/share/dict/words) Time (mean ± σ): 61.4 ms ± 2.3 ms [User: 23.1 ms, System: 39.8 ms] Range (min … max): 58.1 ms … 67.1 ms Summary './bench2 $(shuf /usr/share/dict/words)' ran 1.11x faster than './bench1 $(shuf /usr/share/dict/words)' ``` --- src/builtin.cpp | 50 +++++++++++++++++++------------------------------ src/builtin.h | 20 +++++++++++++++----- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/builtin.cpp b/src/builtin.cpp index 53484f842..8bf723a68 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "builtin.h" #include "builtin_argparse.h" @@ -75,14 +76,6 @@ #include "wgetopt.h" #include "wutil.h" // IWYU pragma: keep -bool builtin_data_t::operator<(const wcstring &other) const { - return wcscmp(this->name, other.c_str()) < 0; -} - -bool builtin_data_t::operator<(const builtin_data_t *other) const { - return wcscmp(this->name, other->name) < 0; -} - /// Counts the number of arguments in the specified null-terminated array int builtin_count_args(const wchar_t *const *argv) { int argc; @@ -401,12 +394,10 @@ int builtin_false(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // END OF BUILTIN COMMANDS // Below are functions for handling the builtin commands. -// THESE MUST BE SORTED BY NAME! Completion lookup uses binary search. // Data about all the builtin commands in fish. // Functions that are bound to builtin_generic are handled directly by the parser. -// NOTE: These must be kept in sorted order! -static const builtin_data_t builtin_datas[] = { +static const std::unordered_set builtin_datas = { {L"[", &builtin_test, N_(L"Test a condition")}, {L"and", &builtin_generic, N_(L"Execute command if previous command suceeded")}, {L"argparse", &builtin_argparse, N_(L"Parse options in fish script")}, @@ -415,8 +406,7 @@ static const builtin_data_t builtin_datas[] = { {L"bind", &builtin_bind, N_(L"Handle fish key bindings")}, {L"block", &builtin_block, N_(L"Temporarily block delivery of events")}, {L"break", &builtin_break_continue, N_(L"Stop the innermost loop")}, - {L"breakpoint", &builtin_breakpoint, - N_(L"Temporarily halt execution of a script and launch an interactive debug prompt")}, + {L"breakpoint", &builtin_breakpoint, N_(L"Temporarily halt execution of a script and launch an interactive debug prompt")}, {L"builtin", &builtin_builtin, N_(L"Run a builtin command instead of a function")}, {L"case", &builtin_generic, N_(L"Conditionally execute a block of commands")}, {L"cd", &builtin_cd, N_(L"Change working directory")}, @@ -424,8 +414,7 @@ static const builtin_data_t builtin_datas[] = { {L"commandline", &builtin_commandline, N_(L"Set or get the commandline")}, {L"complete", &builtin_complete, N_(L"Edit command specific completions")}, {L"contains", &builtin_contains, N_(L"Search for a specified string in a list")}, - {L"continue", &builtin_break_continue, - N_(L"Skip the rest of the current lap of the innermost loop")}, + {L"continue", &builtin_break_continue, N_(L"Skip the rest of the current lap of the innermost loop")}, {L"count", &builtin_count, N_(L"Count the number of arguments")}, {L"disown", &builtin_disown, N_(L"Remove job from job list")}, {L"echo", &builtin_echo, N_(L"Print arguments")}, @@ -463,8 +452,6 @@ static const builtin_data_t builtin_datas[] = { {L"wait", &builtin_wait, N_(L"Wait for background processes completed")}, {L"while", &builtin_generic, N_(L"Perform a command multiple times")}}; -#define BUILTIN_COUNT (sizeof builtin_datas / sizeof *builtin_datas) - /// Look up a builtin_data_t for a specified builtin /// /// @param name @@ -474,18 +461,19 @@ static const builtin_data_t builtin_datas[] = { /// Pointer to a builtin_data_t /// static const builtin_data_t *builtin_lookup(const wcstring &name) { - const builtin_data_t *array_end = builtin_datas + BUILTIN_COUNT; - const builtin_data_t *found = std::lower_bound(builtin_datas, array_end, name); - if (found != array_end && name == found->name) { - return found; + auto search = builtin_data_t { name }; + auto result = builtin_datas.find(search); + if (result == builtin_datas.end()) { + return NULL; } - return NULL; + + return &*result; } /// Initialize builtin data. void builtin_init() { - for (size_t i = 0; i < BUILTIN_COUNT; i++) { - intern_static(builtin_datas[i].name); + for (auto &bi : builtin_datas) { + intern_static(bi.name.c_str()); } } @@ -529,9 +517,9 @@ int builtin_run(parser_t &parser, const wchar_t *const *argv, io_streams_t &stre /// Returns a list of all builtin names. wcstring_list_t builtin_get_names() { wcstring_list_t result; - result.reserve(BUILTIN_COUNT); - for (size_t i = 0; i < BUILTIN_COUNT; i++) { - result.push_back(builtin_datas[i].name); + result.reserve(builtin_datas.size()); + for (auto &bi : builtin_datas) { + result.push_back(bi.name); } return result; } @@ -539,9 +527,9 @@ wcstring_list_t builtin_get_names() { /// Insert all builtin names into list. void builtin_get_names(std::vector *list) { assert(list != NULL); - list->reserve(list->size() + BUILTIN_COUNT); - for (size_t i = 0; i < BUILTIN_COUNT; i++) { - append_completion(list, builtin_datas[i].name); + list->reserve(list->size() + builtin_datas.size()); + for (auto &bi : builtin_datas) { + append_completion(list, bi.name); } } @@ -550,7 +538,7 @@ wcstring builtin_get_desc(const wcstring &name) { wcstring result; const builtin_data_t *builtin = builtin_lookup(name); if (builtin) { - result = _(builtin->desc); + result = _(builtin->desc.c_str()); } return result; } diff --git a/src/builtin.h b/src/builtin.h index 58434d7fa..9dd03abe8 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -16,16 +16,26 @@ struct io_streams_t; /// Data structure to describe a builtin. struct builtin_data_t { // Name of the builtin. - const wchar_t *name; - // Function pointer tothe builtin implementation. + const wcstring name; + // Function pointer to the builtin implementation. int (*func)(parser_t &parser, io_streams_t &streams, wchar_t **argv); // Description of what the builtin does. - const wchar_t *desc; + const wcstring desc; - bool operator<(const wcstring &) const; - bool operator<(const builtin_data_t *) const; + bool operator == (const builtin_data_t &other) const { + return name == other.name; + } }; +namespace std { + template<> + struct hash { + std::size_t operator()(const builtin_data_t &data) const { + return std::hash()(data.name); + } + }; +} + /// The default prompt for the read command. #define DEFAULT_READ_PROMPT L"set_color green; echo -n read; set_color normal; echo -n \"> \"" From 366933413bb448cbc9ceda668e5b727f0c3a5789 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 3 Apr 2018 21:13:11 -0500 Subject: [PATCH 049/159] Fix string match argument parsing in __fish_seen_argument --- share/functions/__fish_seen_argument.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_seen_argument.fish b/share/functions/__fish_seen_argument.fish index dd9a6dd2b..20fed3279 100644 --- a/share/functions/__fish_seen_argument.fish +++ b/share/functions/__fish_seen_argument.fish @@ -11,7 +11,7 @@ function __fish_seen_argument end for l in $_flag_l - if string match -q "--$l" -- $t + if string match -q -- "--$l" $t return 0 end end From 01f24f2df6c3a89abe19b0b7960df208fbe56972 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Mon, 9 Apr 2018 00:46:09 +0200 Subject: [PATCH 050/159] Fix typo in emacs completions Fixes #4885. [ci skip] --- share/completions/emacs.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/emacs.fish b/share/completions/emacs.fish index 78ec60eb8..2f77c1108 100644 --- a/share/completions/emacs.fish +++ b/share/completions/emacs.fish @@ -11,7 +11,7 @@ complete -c emacs -l no-site-file -d "do not load site-start.el" complete -c emacs -l no-site-lisp -o nsl -d "do not add site-lisp directories to load-path" complete -c emacs -l no-splash -d "do not display a splash screen on startup" complete -c emacs -l no-window-system -o nw -d "do not communicate with X, ignoring $DISPLAY" -complete -c emacs -l quick -s Q -d "equivalent to: emacs -l --no-site-file --no-site-lisp --no-splash" +complete -c emacs -l quick -s Q -d "equivalent to: emacs -q --no-site-file --no-splash" complete -c emacs -l script -d "run FILE as an Emacs Lisp script" complete -c emacs -l terminal -s t -d "use DEVICE for terminal I/O" complete -c emacs -l user -s u -d "load ~USER/.emacs instead of your own" From c6e7b7ef00cf51582bad16cf7b782a9a52b382cd Mon Sep 17 00:00:00 2001 From: George Christou Date: Mon, 9 Apr 2018 00:05:03 +0100 Subject: [PATCH 051/159] prompt: Show untracked Git files relative to root directory (#4874) * prompt/git: Match untracked files relative to root directory * prompt/git: Move untracked file logic to a separate function --- share/functions/__fish_git_prompt.fish | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index e70753445..cc8525a63 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -387,9 +387,7 @@ function __fish_git_prompt --description "Prompt function for Git" if set -q __fish_git_prompt_showuntrackedfiles set -l config (command git config --bool bash.showUntrackedFiles) if test "$config" != false - if command git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- '*' >/dev/null 2>/dev/null - set u $___fish_git_prompt_char_untrackedfiles - end + set u (__fish_git_prompt_untracked) end end end @@ -471,6 +469,14 @@ function __fish_git_prompt_staged --description "__fish_git_prompt helper, tells echo $staged end +function __fish_git_prompt_untracked --description "__fish_git_prompt helper, tells whether or not the current repository has untracked files" + set -l untracked + if command git ls-files --others --exclude-standard --directory --no-empty-directory --error-unmatch -- :/ >/dev/null 2>&1 + set untracked $___fish_git_prompt_char_untrackedfiles + end + echo $untracked +end + function __fish_git_prompt_dirty --description "__fish_git_prompt helper, tells whether or not the current branch has tracked, modified files" set -l dirty From 6c0f31d622c9dbc5d4855faa76cab53e10961768 Mon Sep 17 00:00:00 2001 From: George Christou Date: Mon, 2 Apr 2018 21:51:07 +0100 Subject: [PATCH 052/159] completions: [git] Use builtin git truncation --- share/completions/git.fish | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 6721c879a..e4115f1c4 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -6,16 +6,14 @@ function __fish_git_commits # This allows filtering by subject with the new pager! # Because even subject lines can be quite long, # trim them (abbrev'd hash+tab+subject) to 73 characters - command git log --pretty=tformat:"%h"\t"%s" --all --max-count=1000 2>/dev/null \ - | string replace -r '(.{73}).+' '$1…' + command git log --pretty=tformat:"%h"\t"%<(64,trunc)%s" --all --max-count=1000 2>/dev/null end function __fish_git_recent_commits # Like __fish_git_commits, but not on all branches and limited to # the last 50 commits. Used for fixup, where only the current branch # and the latest commits make sense. - command git log --pretty=tformat:"%h"\t"%s" --max-count=50 2>/dev/null \ - | string replace -r '(.{73}).+' '$1…' + command git log --pretty=tformat:"%h"\t"%<(64,trunc)%s" --max-count=50 2>/dev/null end function __fish_git_branches From 25169a44ed9108fd4e0a437cd39a54ab4968790c Mon Sep 17 00:00:00 2001 From: Thom Chiovoloni Date: Fri, 6 Apr 2018 10:27:40 -0700 Subject: [PATCH 053/159] Add `alias -s/--save`, which saves the alias. Also updates the `alias` documentation to mention the `-h`/`--help` option, which was previously undocumented. --- CHANGELOG.md | 1 + doc_src/alias.txt | 10 ++++++++-- share/functions/alias.fish | 5 ++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2cdb4fae..581f8aea4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ This section is for changes merged to the `major` branch that are not also merge - `functions --handlers` can be used to show event handlers (#4694). - Variables set in `if` and `while` conditions are available outside the block (#4820). - The universal variables file no longer contains the MAC address. It is now at the fixed location `.config/fish/fish_universal_variables` (#1912). +- `alias` now has a `-s` and `--save` option to save the function generated by the alias using `funcsave` (#4878). ## Other significant changes - Command substitution output is now limited to 10 MB by default (#3822). diff --git a/doc_src/alias.txt b/doc_src/alias.txt index fc36fbbac..606a05ec7 100644 --- a/doc_src/alias.txt +++ b/doc_src/alias.txt @@ -3,8 +3,8 @@ \subsection alias-synopsis Synopsis \fish{synopsis} alias -alias NAME DEFINITION -alias NAME=DEFINITION +alias [OPTIONS] NAME DEFINITION +alias [OPTIONS] NAME=DEFINITION \endfish \subsection alias-description Description @@ -18,6 +18,12 @@ alias NAME=DEFINITION You cannot create an alias to a function with the same name. Note that spaces need to be escaped in the call to `alias` just like at the command line, _even inside quoted parts_. +The following options are available: + +- `-h` or `--help` displays help about using this command. + +- `-s` or `--save` Automatically save the function created by the alias into your fish configuration directory using funcsave. + \subsection alias-example Example The following code will create `rmi`, which runs `rm` with additional arguments on every invocation. diff --git a/share/functions/alias.fish b/share/functions/alias.fish index 3cedfe24d..0651cc6ac 100644 --- a/share/functions/alias.fish +++ b/share/functions/alias.fish @@ -1,5 +1,5 @@ function alias --description 'Creates a function wrapping a command' - set -l options 'h/help' + set -l options 'h/help' 's/save' argparse -n alias --max-args=2 $options -- $argv or return @@ -69,5 +69,8 @@ function alias --description 'Creates a function wrapping a command' set -l cmd_string (string escape -- "alias $argv") set wrapped_cmd (string join ' ' -- $first_word $body | string escape) echo "function $name --wraps $wrapped_cmd --description $cmd_string; $prefix $first_word $body \$argv; end" | source + if set -q _flag_save + funcsave $name + end #echo "function $name --wraps $wrapped_cmd --description $cmd_string; $prefix $first_word $body \$argv; end" end From bd24e8662e8faafd9f276772b76d8dc73db29059 Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop Date: Sun, 25 Mar 2018 17:23:01 +0200 Subject: [PATCH 054/159] fix 'sort | uniq' --- build_tools/list_committers_since.fish | 4 ++-- share/completions/zfs.fish | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build_tools/list_committers_since.fish b/build_tools/list_committers_since.fish index 64c9ae3a0..e5c51c423 100755 --- a/build_tools/list_committers_since.fish +++ b/build_tools/list_committers_since.fish @@ -20,8 +20,8 @@ set committers_from_tag (mktemp) # Unicode collation tables mean that this is fraught with danger; for example, the # "“" character will not case-fold in UTF-8 locales. sort suggests using the C locale! -git log "$TAG" --format="%aN" --reverse | sort | uniq > $committers_to_tag -git log "$TAG".. --format="%aN" --reverse | sort | uniq > $committers_from_tag +git log "$TAG" --format="%aN" --reverse | sort -u > $committers_to_tag +git log "$TAG".. --format="%aN" --reverse | sort -u > $committers_from_tag echo New committers: echo (comm -13 $committers_to_tag $committers_from_tag)',' diff --git a/share/completions/zfs.fish b/share/completions/zfs.fish index 44cd83beb..4b8227231 100644 --- a/share/completions/zfs.fish +++ b/share/completions/zfs.fish @@ -203,7 +203,7 @@ function __fish_zfs_list_permissions echo -e "casesensitivity\t"(_ "Case sensitivity")" (sensitive, insensitive, mixed)" end # Permissions set; if none are found, or if permission sets are not supported, no output is expected, even an error - for i in (zpool list -o name -H); zfs allow $i; end | grep -o '@[[:alnum:]]*' | sort | uniq + for i in (zpool list -o name -H); zfs allow $i; end | grep -o '@[[:alnum:]]*' | sort -u end complete -c zfs -f -n '__fish_zfs_needs_command' -s '?' -a '?' -d 'Display a help message' From 8ae9b716a06626798a30497794386ff5cc9e1bef Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop Date: Mon, 26 Mar 2018 19:49:02 +0200 Subject: [PATCH 055/159] fix 'grep ... | sed' --- share/completions/lpadmin.fish | 3 +-- share/completions/mvn.fish | 15 ++++++--------- .../__fish_complete_convert_options.fish | 6 ++---- share/functions/__fish_print_xwindows.fish | 3 +-- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/share/completions/lpadmin.fish b/share/completions/lpadmin.fish index 099309b9b..578b158f9 100644 --- a/share/completions/lpadmin.fish +++ b/share/completions/lpadmin.fish @@ -23,10 +23,9 @@ complete -c lpadmin -s o -d 'Sets the binary communications program to use when complete -c lpadmin -s o -d 'Sets the error policy to be used when the printer backend is unable to send the job to the printer. ' -xa 'printer-error-policy=abort-job printer-error-policy=retry-job printer-error-policy=retry-current-job printer-error-policy=stop-printer' complete -c lpadmin -s o -xa printer-is-shared=true -d 'Sets the destination to shared/published or unshared/unpublished' complete -c lpadmin -s o -xa printer-is-shared=false -d 'Sets the destination to shared/published or unshared/unpublished' -complete -c lpadmin -s o -d 'Sets the IPP operation policy associated with the destination' -xa "printer-policy=(cat /etc/cups/cupsd.conf | grep \/\1/')" +complete -c lpadmin -s o -d 'Sets the IPP operation policy associated with the destination' -xa "printer-policy=(string replace -r --filter '' '$1' < /etc/cups/cupsd.conf)" complete -c lpadmin -s u -xa 'allow:all allow:none (__fish_complete_list , __fish_complete_users allow:)' -d 'Sets user-level access control on a destination. Names starting with "@" are interpreted as UNIX group' complete -c lpadmin -s u -xa '(__fish_complete_list , __fish_complete_groups allow: @)' -d 'Sets user-level access control on a destination. Names starting with "@" are interpreted as UNIX group' complete -c lpadmin -s u -xa 'deny:all deny:none (__fish_complete_list , __fish_complete_users deny:)' -d 'Sets user-level access control on a destination. Names starting with "@" are interpreted as UNIX group' complete -c lpadmin -s u -xa '(__fish_complete_list , __fish_complete_groups deny: @)' -d 'Sets user-level access control on a destination. Names starting with "@" are interpreted as UNIX group' - diff --git a/share/completions/mvn.fish b/share/completions/mvn.fish index 1e99a40bc..a3d50ffb1 100644 --- a/share/completions/mvn.fish +++ b/share/completions/mvn.fish @@ -63,21 +63,18 @@ complete -c mvn -f -o V -l show-version -d "Display version complete -c mvn -f -o v -l version -d "Display version information" complete -c mvn -f -o X -l debug -d "Produce execution debug output" -# # # Profiles # -# -function __fish_mvn_profiles_from_settings - grep -e "" -A 1 ~/.m2/settings.xml | grep -e ".*" | sed 's/.*//' | sed 's/<\/id>.*//g' -end - #TODO search pom.xml hierarchy -function __fish_mvn_profiles_from_pom - [ -e pom.xml ]; and grep -e "" -A 1 pom.xml | grep -e ".*" | sed 's/.*//' | sed 's/<\/id>.*//g' +function __fish_mvn_profiles + # find line opening the profile-tag + # read next line + # extract contents of id-tag + sed -n -e '//{n; s!^.*\([^<]*\).*$!\1!; p}' ~/.m2/settings.xml pom.xml ^/dev/null end -complete -c mvn -f -r -o P -l activate-profiles -a "(__fish_mvn_profiles_from_pom) (__fish_mvn_profiles_from_settings)" -d "Comma-delimited list of profiles to activate" +complete -c mvn -f -r -o P -l activate-profiles -a "(__fish_mvn_profiles)" -d "Comma-delimited list of profiles to activate" #default properties for some plugins / profiles diff --git a/share/functions/__fish_complete_convert_options.fish b/share/functions/__fish_complete_convert_options.fish index 863c83c7f..b32a55120 100644 --- a/share/functions/__fish_complete_convert_options.fish +++ b/share/functions/__fish_complete_convert_options.fish @@ -5,12 +5,10 @@ function __fish_complete_convert_options --description 'Complete Convert options case color Color convert -list color | awk '{ print $1"\t"$2" "$3} ' | sed '1,/----/d' case family - convert -list Font | grep family | sed 's/^\s*.\+: //' | sort -u + convert -list Font | sed '/family/!d; s/^\s*.\+: //' | sort -u case font Font - convert -list Font | grep Font | sed 's/^\s*.\+: //' | sort -u + convert -list Font | sed '/Font/!d; s/^\s*.\+: //' | sort -u case '*' convert -list $what end - - end diff --git a/share/functions/__fish_print_xwindows.fish b/share/functions/__fish_print_xwindows.fish index 58fbecc25..7eef1a6b1 100644 --- a/share/functions/__fish_print_xwindows.fish +++ b/share/functions/__fish_print_xwindows.fish @@ -1,4 +1,3 @@ function __fish_print_xwindows --description 'Print X windows' - xwininfo -root -children | grep '^\s\+0x' | sed '/(has no name)/d; s/^\s*\(\S\+\)\s\+"\(.\+\)":\s\+(\(.*\)).*$/\1\t\2 (\3)/' - + xwininfo -root -children | sed '/^\s\+0x/!d; /(has no name)/d; s/^\s*\(\S\+\)\s\+"\(.\+\)":\s\+(\(.*\)).*$/\1\t\2 (\3)/' end From 5d01399586f1a98dc048bc817af3f3f1ab70d0b9 Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop Date: Mon, 26 Mar 2018 21:37:05 +0200 Subject: [PATCH 056/159] add file readability-test --- share/completions/lpadmin.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/lpadmin.fish b/share/completions/lpadmin.fish index 578b158f9..acfc57eeb 100644 --- a/share/completions/lpadmin.fish +++ b/share/completions/lpadmin.fish @@ -23,7 +23,7 @@ complete -c lpadmin -s o -d 'Sets the binary communications program to use when complete -c lpadmin -s o -d 'Sets the error policy to be used when the printer backend is unable to send the job to the printer. ' -xa 'printer-error-policy=abort-job printer-error-policy=retry-job printer-error-policy=retry-current-job printer-error-policy=stop-printer' complete -c lpadmin -s o -xa printer-is-shared=true -d 'Sets the destination to shared/published or unshared/unpublished' complete -c lpadmin -s o -xa printer-is-shared=false -d 'Sets the destination to shared/published or unshared/unpublished' -complete -c lpadmin -s o -d 'Sets the IPP operation policy associated with the destination' -xa "printer-policy=(string replace -r --filter '' '$1' < /etc/cups/cupsd.conf)" +complete -c lpadmin -s o -d 'Sets the IPP operation policy associated with the destination' -xa "printer-policy=(test -r /etc/cups/cupsd.conf; and string replace -r --filter '' '$1' < /etc/cups/cupsd.conf)" complete -c lpadmin -s u -xa 'allow:all allow:none (__fish_complete_list , __fish_complete_users allow:)' -d 'Sets user-level access control on a destination. Names starting with "@" are interpreted as UNIX group' complete -c lpadmin -s u -xa '(__fish_complete_list , __fish_complete_groups allow: @)' -d 'Sets user-level access control on a destination. Names starting with "@" are interpreted as UNIX group' From f7ba2a6a00ab127eadd3af99ab16d3a089c1d7f2 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sun, 8 Apr 2018 12:19:26 -0700 Subject: [PATCH 057/159] Don't corrupt memory when setting a slice with wrong # of args Fixes #4881. --- src/builtin_set.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index aff84eec4..f6cc9ddf9 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -689,6 +689,7 @@ static int set_var_slices(const wchar_t *cmd, set_cmd_opts_t &opts, const wchar_ if (indexes.size() != static_cast(argc)) { streams.err.append_format(BUILTIN_SET_MISMATCHED_ARGS, cmd, indexes.size(), argc); + return STATUS_INVALID_ARGS; } int scope = compute_scope(opts); // calculate the variable scope based on the provided options From a89e9e7ba3ea7a591ca8ef1ba10fd6f7f7edd777 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 10 Apr 2018 21:23:47 +0200 Subject: [PATCH 058/159] Improve read docs re splitting Intentionally make no mention of $IFS, because it is deprecated. Fixes #4861. --- doc_src/read.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc_src/read.txt b/doc_src/read.txt index 3d01333ca..b661b368a 100644 --- a/doc_src/read.txt +++ b/doc_src/read.txt @@ -7,13 +7,13 @@ read [OPTIONS] VARIABLES... \subsection read-description Description -`read` reads from standard input and either writes the result back to the terminal for use in command substitution or stores the result in one or more shell variables. By default, one line (terminated by a newline) is read into each variable. Alternatively, a null character or a maximum number of characters can be used to terminate the input. Unlike other shells, there is no default variable (such as `REPLY`) for storing the result. +`read` reads from standard input and either writes the result back to the terminal for use in command substitution or stores the result in one or more shell variables. By default, `read` reads up to the next newline and splits it into the given variables on space, tab and newline. Alternatively, a null character or a maximum number of characters can be used to terminate the input, and other delimiters can be given. Unlike other shells, there is no default variable (such as `REPLY`) for storing the result. Instead, it is printed on stdout. The following options are available: - `-c CMD` or `--command=CMD` sets the initial string in the interactive mode command buffer to `CMD`. -- `-d DELIMITER` or `--delimiter=DELIMITER` splits on DELIMITER. +- `-d DELIMITER` or `--delimiter=DELIMITER` splits on DELIMITER. DELIMITER will be used as an entire string to split on, not a set of characters. - `-g` or `--global` makes the variables global. From 701259d372b0cf0f431ccde20ca0baf495067398 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 10 Apr 2018 21:24:38 +0200 Subject: [PATCH 059/159] Remove ":" from argparse docs Also improve some of the wording. Fixes #4871. [ci skip] --- doc_src/argparse.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc_src/argparse.txt b/doc_src/argparse.txt index d210c1689..b71f43d85 100644 --- a/doc_src/argparse.txt +++ b/doc_src/argparse.txt @@ -40,7 +40,7 @@ or return If `$argv` is empty then there is nothing to parse and `argparse` returns zero to indicate success. If `$argv` is not empty then it is checked for flags `-h`, `--help`, `-n` and `--name`. If they are found they are removed from the arguments and local variables (more on this below) are set so the script can determine which options were seen. Assuming `$argv` doesn't have any errors, such as a missing mandatory value for an option, then `argparse` exits with status zero. Otherwise it writes appropriate error messages to stderr and exits with a status of one. -Not including a `--` argument is an error condition. You do not have to include any arguments after the `--` but you must include the `--`. For example, this is acceptable: +The `--` argument is required. You do not have to include any arguments after the `--` but you must include the `--`. For example, this is acceptable: \fish set -l argv @@ -72,7 +72,7 @@ Each option specification is a string composed of - `=?` it takes an optional value and only the last instance of the flag is saved, else -- `=+` if it requires a value each instance of the flag is saved. +- `=+` if it requires a value and each instance of the flag is saved. - Optionally a `!` followed by fish script to validate the value. Typically this will be a function to run. If the return status is zero the value for the flag is valid. If non-zero the value is invalid. Any error messages should be written to stdout (not stderr). See the section on Flag Value Validation for more information. @@ -114,7 +114,7 @@ Some OPTION_SPEC examples: - `x=`, `x=?`, and `x=+` are similar to the n/name examples above but there is no long flag alternative to the short flag `-x`. -- `x-` is not valid since there is no long flag name and therefore the short flag, `-x`, has to be usable. This is obviously true whether or not the specification also includes one of `:`, `::`, `+`. +- `x-` is not valid since there is no long flag name and therefore the short flag, `-x`, has to be usable. - `#-max` means that flags matching the regex "^--?\d+$" are valid. When seen they are assigned to the variable `_flag_max`. This allows any valid positive or negative integer to be specified by prefixing it with a single "-". Many commands support this idiom. For example `head -3 /a/file` to emit only the first three lines of /a/file. @@ -122,7 +122,7 @@ Some OPTION_SPEC examples: After parsing the arguments the `argv` var is set with local scope to any values not already consumed during flag processing. If there are not unbound values the var is set but `count $argv` will be zero. -If an error occurs during argparse processing it will exit with a non-zero status and appropriate error messages are written to stderr. +If an error occurs during argparse processing it will exit with a non-zero status and print error messages to stderr. \subsection argparse-notes Notes From ea49c14c62e66d5cfa8b827c2ccb625b1b142c06 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 10 Apr 2018 21:45:28 +0200 Subject: [PATCH 060/159] Fix read to stdout output appearing first echo banana (read) will output whatever read reads _first_ because it uses a direct write_loop(). This also removes some duplicate code. --- src/builtin_read.cpp | 7 +------ tests/read.in | 4 ++++ tests/read.out | 2 ++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 9b7586f2a..453a9df64 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -394,11 +394,6 @@ static int validate_read_args(const wchar_t *cmd, read_cmd_opts_t &opts, int arg return STATUS_CMD_OK; } -void write_stdout(wcstring val) { - const std::string &out = wcs2string(val); - write_loop(STDOUT_FILENO, out.c_str(), out.size()); -} - /// The read builtin. Reads from stdin and stores the values in environment variables. int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { wchar_t *cmd = argv[0]; @@ -446,7 +441,7 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (opts.to_stdout) { - write_stdout(buff); + streams.out.append(buff); return exit_res; } diff --git a/tests/read.in b/tests/read.in index 7230e4cfc..59eddcdfd 100644 --- a/tests/read.in +++ b/tests/read.in @@ -247,3 +247,7 @@ begin echo a...b...c | read -l a b c echo $a; echo $b; echo $c end +echo + +# At one point, whatever was read was printed _before_ banana +echo banana (echo sausage | read) diff --git a/tests/read.out b/tests/read.out index 5dd8f66a2..d486f4af9 100644 --- a/tests/read.out +++ b/tests/read.out @@ -130,3 +130,5 @@ Multi-char delimiters with IFS a b ..c + +banana sausage From d53750bee62a14b9e9dde5a53dd04de2d91d8499 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 10 Apr 2018 21:46:43 +0200 Subject: [PATCH 061/159] Let `read -s` also output to stdout This was caused by "to_stdout" being automatically enabled if argc was 0 _before_ removing options. Fixes #4859. --- src/builtin_read.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 453a9df64..4a3afa050 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -73,11 +73,6 @@ static const struct woption long_options[] = {{L"export", no_argument, NULL, 'x' static int parse_cmd_opts(read_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss method) int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) { - if (argc == 1) { - opts.to_stdout = true; - return STATUS_CMD_OK; - } - wchar_t *cmd = argv[0]; int opt; wgetopter_t w; @@ -372,7 +367,7 @@ static int validate_read_args(const wchar_t *cmd, read_cmd_opts_t &opts, int arg return STATUS_INVALID_ARGS; } - if (!opts.array && argc < 1) { + if (!opts.array && argc < 1 && !opts.to_stdout) { streams.err.append_format(BUILTIN_ERR_MIN_ARG_COUNT1, cmd, 1, argc); return STATUS_INVALID_ARGS; } @@ -382,6 +377,11 @@ static int validate_read_args(const wchar_t *cmd, read_cmd_opts_t &opts, int arg return STATUS_INVALID_ARGS; } + if (opts.to_stdout && argc > 0) { + streams.err.append_format(BUILTIN_ERR_MAX_ARG_COUNT1, cmd, 0, argc); + return STATUS_INVALID_ARGS; + } + // Verify all variable names. for (int i = 0; i < argc; i++) { if (!valid_var_name(argv[i])) { @@ -410,6 +410,10 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { argv += optind; } + if (argc == 0) { + opts.to_stdout = true; + } + if (opts.print_help) { builtin_print_help(parser, streams, cmd, streams.out); return STATUS_CMD_OK; From 60c4a66c571adc3f56099ae8d79f213ff2025735 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 10 Apr 2018 21:53:38 +0200 Subject: [PATCH 062/159] Fix crash in `wait` in english Wrong specifier in the localization again. Note that this was already alleviated by not using fuzzy translations in the cmake build. Fixes #4847. --- po/en.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/po/en.po b/po/en.po index 51d4f7f1c..f1474f970 100644 --- a/po/en.po +++ b/po/en.po @@ -118,7 +118,7 @@ msgstr "%ls: '%ls' is not a valid variable name\n" #: src/builtin_bg.cpp:95 src/builtin_disown.cpp:89 src/builtin_wait.cpp:163 #, fuzzy, c-format msgid "%ls: Could not find job '%d'\n" -msgstr "%s: Could not find “%s”\n" +msgstr "%s: Could not find “%d”\n" #: src/builtin_bind.cpp:141 #, c-format From 14d59c53f63075aa405551533ae9ac08dc9e25a9 Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Tue, 10 Apr 2018 21:56:37 +0200 Subject: [PATCH 063/159] Stop using fuzzy translations in autotools build We've already stopped doing this for cmake, and as we've seen in issue #4847, it can cause _crashes_. --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 53b2a6239..21e018dad 100644 --- a/Makefile.in +++ b/Makefile.in @@ -467,7 +467,7 @@ doc.h: $(HDR_FILES) # %.gmo: @echo " msgfmt $(em)$@$(sgr0)" - $v msgfmt --use-fuzzy -o $@ $*.po + $v msgfmt -o $@ $*.po # # Update existing po file or copy messages.pot From facdc88c0cda8a93134f43c794659b2f2fe6fa7c Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 12 Apr 2018 21:40:10 -0500 Subject: [PATCH 064/159] Improve completions for `ssh` by completing from history --- CHANGELOG.md | 1 + share/completions/ssh.fish | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 581f8aea4..42c1d56fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,7 @@ This section is for changes merged to the `major` branch that are not also merge - `brew` - `diskutil` - `yarn` + - `ssh` (#4344) -- diff --git a/share/completions/ssh.fish b/share/completions/ssh.fish index 4950b2082..6d8249df1 100644 --- a/share/completions/ssh.fish +++ b/share/completions/ssh.fish @@ -41,3 +41,14 @@ complete -c ssh -s X -d "Enable X11 forwarding" complete -c ssh -s L -d "Locally forwarded ports" complete -c ssh -s R -d "Remotely forwarded ports" complete -c ssh -s D -d "Dynamic port forwarding" + +# Also look up hosts from the history +function __ssh_history_completions --argument limit + if string match -q "" + set limit 100 + end + + history --prefix ssh | sed -n "s/.* \([A-Za-z0-9._:-]\+@[A-Za-z0-9._:-]\+\).*/\1/p" | head -n $limit +end + +complete -k -c ssh -a '(__ssh_history_completions 100)' -f From 1c8bbfdb6d8eab6cb3e355f1cd32afd835cec961 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 14 Apr 2018 16:47:05 -0500 Subject: [PATCH 065/159] Support job expansion for `jobs` builtin This brings back expansion of `%n` where `n` is a job id, but not as a general parser syntax. This makes `jobs -p %n` work, which can be used as part of the job control command chain, i.e. ``` cat & fg (jobs -p %1) ``` fg/bg/wait can either be wrapped in a function to call `jobs -p` for `%n` arguments, or they can be updated to take `%n` arguments themselves. --- src/builtin_jobs.cpp | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index 019682c4e..8b05fbaf0 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -188,19 +188,31 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { int i; for (i = w.woptind; i < argc; i++) { - int pid = fish_wcstoi(argv[i]); - if (errno || pid < 0) { - streams.err.append_format(_(L"%ls: '%ls' is not a job\n"), cmd, argv[i]); - return STATUS_INVALID_ARGS; + const job_t *j = nullptr; + + if (argv[i][0] == L'%') { + int jobId = -1; + jobId = fish_wcstoi(argv[i] + 1); + if (errno || jobId < -1) { + streams.err.append_format(_(L"%ls: '%ls' is not a valid job id"), cmd, argv[i]); + return STATUS_INVALID_ARGS; + } + j = job_get(jobId); + } + else { + int pid = fish_wcstoi(argv[i]); + if (errno || pid < 0) { + streams.err.append_format(_(L"%ls: '%ls' is not a valid process id\n"), cmd, argv[i]); + return STATUS_INVALID_ARGS; + } + j = job_get_from_pid(pid); } - const job_t *j = job_get_from_pid(pid); - - if (j && !job_is_completed(j)) { + if (j && !job_is_completed(j) && (j->flags & JOB_CONSTRUCTED)) { builtin_jobs_print(j, mode, false, streams); found = 1; } else { - streams.err.append_format(_(L"%ls: No suitable job: %d\n"), cmd, pid); + streams.err.append_format(_(L"%ls: No suitable job: %ls\n"), cmd, argv[i]); return STATUS_CMD_ERROR; } } From 9f3059b7f4f3aabe5e6b5037908227afca1eb756 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 14 Apr 2018 17:11:04 -0500 Subject: [PATCH 066/159] Add wrappers for fg/bg/wait to support job expansion See commit 1c8bbfdb6d8eab6cb3e355f1cd32afd835cec961 for an alternative approach, if this isn't desired. --- share/config.fish | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/share/config.fish b/share/config.fish index 8c3779a3d..0196b7582 100644 --- a/share/config.fish +++ b/share/config.fish @@ -256,3 +256,35 @@ end # Invoke this here to apply the current value of fish_user_path after # PATH is possibly set above. __fish_reconstruct_path + +# Allow %n job expansion to be used with fg/bg/wait +# `jobs` is the only one that natively supports job expansion +function __fish_expand_pid_args + for arg in $argv + if string match -qr '^%\d+$' -- $arg + # set newargv $newargv (jobs -p $arg) + jobs -p $arg + if not test $status -eq 0 + return 1 + end + else + echo $arg + end + end +end + +function __fish_expand_fg + builtin fg (__fish_expand_pid_args $argv) +end + +function __fish_expand_bg + builtin bg (__fish_expand_pid_args $argv) +end + +function __fish_expand_wait + builtin wait (__fish_expand_pid_args $argv) +end + +alias fg __fish_expand_fg +alias bg __fish_expand_bg +alias wait __fish_expand_wait From e35983438ef36fef579d8247a515da59cee07d95 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 14 Apr 2018 21:01:52 -0500 Subject: [PATCH 067/159] Blow away existing $fish_data_dir on (re)install Fixes #4314 (under cmake, at least) --- cmake/Install.cmake | 20 ++++++++++---------- share/config.fish | 10 +++------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/cmake/Install.cmake b/cmake/Install.cmake index 6061d8280..add0b84fb 100644 --- a/cmake/Install.cmake +++ b/cmake/Install.cmake @@ -59,6 +59,14 @@ FUNCTION(FISH_CREATE_DIRS) ENDFOREACH(dir) ENDFUNCTION(FISH_CREATE_DIRS) +FUNCTION(FISH_TRY_REMOVE) + FOREACH(dir ${ARGV}) + IF(EXISTS ${CMAKE_INSTALL_PREFIX}/${dir}) + FILE(REMOVE_RECURSE ${CMAKE_INSTALL_PREFIX}/${dir}) + ENDIF() + ENDFOREACH() +ENDFUNCTION(FISH_TRY_REMOVE) + FUNCTION(FISH_TRY_CREATE_DIRS) FOREACH(dir ${ARGV}) IF(NOT IS_ABSOLUTE ${dir}) @@ -90,16 +98,8 @@ INSTALL(TARGETS ${PROGRAMS} FISH_CREATE_DIRS(${sysconfdir}/fish/conf.d) INSTALL(FILES etc/config.fish DESTINATION ${sysconfdir}/fish/) -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/completions -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/functions -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/groff -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/man/man1 -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config/js -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config/partials -# $v $(INSTALL) -m 755 -d $(DESTDIR)$(datadir)/fish/tools/web_config/sample_prompts +FISH_TRY_REMOVE(${rel_datadir}/fish) + FISH_CREATE_DIRS(${rel_datadir}/fish ${rel_datadir}/fish/completions ${rel_datadir}/fish/functions ${rel_datadir}/fish/groff ${rel_datadir}/fish/man/man1 ${rel_datadir}/fish/tools diff --git a/share/config.fish b/share/config.fish index 0196b7582..23350ffcf 100644 --- a/share/config.fish +++ b/share/config.fish @@ -273,18 +273,14 @@ function __fish_expand_pid_args end end -function __fish_expand_fg +function fg --wraps fg builtin fg (__fish_expand_pid_args $argv) end -function __fish_expand_bg +function bg --wraps bg builtin bg (__fish_expand_pid_args $argv) end -function __fish_expand_wait +function wait --wraps wait builtin wait (__fish_expand_pid_args $argv) end - -alias fg __fish_expand_fg -alias bg __fish_expand_bg -alias wait __fish_expand_wait From 9a9238a253e57ffa08f71e39b2a87a3034f92071 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 14 Apr 2018 21:17:23 -0500 Subject: [PATCH 068/159] Add job expansion wrapper for kill --- share/config.fish | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/share/config.fish b/share/config.fish index 23350ffcf..a9898a083 100644 --- a/share/config.fish +++ b/share/config.fish @@ -273,12 +273,16 @@ function __fish_expand_pid_args end end +function bg --wraps bg + builtin bg (__fish_expand_pid_args $argv) +end + function fg --wraps fg builtin fg (__fish_expand_pid_args $argv) end -function bg --wraps bg - builtin bg (__fish_expand_pid_args $argv) +function kill --wraps kill + command kill (__fish_expand_pid_args $argv) end function wait --wraps wait From 7ec761fc75d24ca1810292089aa23fed5fdd7a10 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 14 Apr 2018 21:42:57 -0500 Subject: [PATCH 069/159] Revert "Fix wait test with no process expansion" This reverts commit b38ac1e35df5c8588a8ee1aa01912f32e1492037 as wait now supports process expansion (via the wait wrapper). --- tests/wait.expect | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/wait.expect b/tests/wait.expect index 4eb6ec74b..94870f517 100644 --- a/tests/wait.expect +++ b/tests/wait.expect @@ -25,14 +25,14 @@ send_line "jobs" expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # one job id specified -set error_msg "one job id specified: Fail" +set error_msg "one job id specified" -send_line "sleep 3 &; set pid1 \$last_pid; sleep 1 &; set pid2 \$last_pid; sleep 2 &; set pid3 \$last_pid;" +send_line "sleep 3 &; sleep 1 &; sleep 2 &" expect_prompt -send_line "wait \$pid3" -expect "Job 3, 'sleep 1 &' has ended" {} timeout { puts stderr $error_msg } -expect_prompt "Job 4, 'sleep 2 &' has ended" {} unmatched { puts stderr $error_msg } -send_line "wait \$pid1" +send_line "wait %3" +expect "Job 2, 'sleep 1 &' has ended" {} timeout { puts stderr $error_msg } +expect_prompt "Job 3, 'sleep 2 &' has ended" {} unmatched { puts stderr $error_msg } +send_line "wait %1" expect_prompt "Job 1, 'sleep 3 &' has ended" {} unmatched { puts stderr $error_msg } send_line "jobs" expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } @@ -40,25 +40,25 @@ expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # three job ids specified set error_msg "three job ids specified: Fail" -send_line "sleep 3 &; set pid1 \$last_pid; sleep 1 &; set pid2 \$last_pid; sleep 2 &; set pid3 \$last_pid; sleep 4 &; set pid4 \$last_pid;" +send_line "sleep 3 &; sleep 1 &; sleep 2 &; sleep 4 &;" expect_prompt -send_line "wait \$pid1 \$pid3 \$pid4" -expect "Job 3, 'sleep 1 &' has ended" {} timeout { puts stderr $error_msg } -expect "Job 4, 'sleep 2 &' has ended" {} timeout { puts stderr $error_msg } +send_line "wait %1 %3 %4" +expect "Job 2, 'sleep 1 &' has ended" {} timeout { puts stderr $error_msg } +expect "Job 3, 'sleep 2 &' has ended" {} timeout { puts stderr $error_msg } expect "Job 1, 'sleep 3 &' has ended" {} timeout { puts stderr $error_msg } -expect_prompt "Job 5, 'sleep 4 &' has ended" {} unmatched { puts stderr $error_msg } +expect_prompt "Job 4, 'sleep 4 &' has ended" {} unmatched { puts stderr $error_msg } send_line "jobs" expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # wait with -n option set error_msg "wait with -n option: Fail" -send_line "sleep 3 &; set pid1 \$last_pid; sleep 1 &; set pid2 \$last_pid; sleep 2 &; set pid3 \$last_pid;" +send_line "sleep 3 &; sleep 1 &; sleep 2 &" expect_prompt send_line "wait -n" -expect_prompt "Job 3, 'sleep 1 &' has ended" {} unmatched { puts stderr $error_msg } +expect_prompt "Job 2, 'sleep 1 &' has ended" {} unmatched { puts stderr $error_msg } send_line "wait -n" -expect_prompt "Job 4, 'sleep 2 &' has ended" {} unmatched { puts stderr $error_msg } +expect_prompt "Job 3, 'sleep 2 &' has ended" {} unmatched { puts stderr $error_msg } send_line "wait -n" expect_prompt "Job 1, 'sleep 3 &' has ended" {} unmatched { puts stderr $error_msg } send_line "jobs" @@ -67,12 +67,12 @@ expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } # specify job ids with -n option set error_msg "specify job ids with -n option: Fail" -send_line "sleep 3 &; set pid1 \$last_pid; sleep 1 &; set pid2 \$last_pid; sleep 2 &; set pid3 \$last_pid;" +send_line "sleep 3 &; sleep 1 &; sleep 2 &" expect_prompt -send_line "wait -n \$pid1 \$pid3" -expect "Job 3, 'sleep 1 &' has ended" {} timeout { puts stderr $error_msg } -expect_prompt "Job 4, 'sleep 2 &' has ended" {} unmatched { puts stderr $error_msg } -send_line "wait -n \$pid1" +send_line "wait -n %1 %3" +expect "Job 2, 'sleep 1 &' has ended" {} timeout { puts stderr $error_msg } +expect_prompt "Job 3, 'sleep 2 &' has ended" {} unmatched { puts stderr $error_msg } +send_line "wait -n %1" expect_prompt "Job 1, 'sleep 3 &' has ended" {} unmatched { puts stderr $error_msg } send_line "jobs" expect_prompt "jobs: There are no jobs" {} unmatched { puts stderr $error_msg } @@ -82,15 +82,15 @@ set error_msg "don't wait stopped jobs: Fail" send_line "sleep 3 &" expect_prompt -send_line "kill -STOP \$last_pid" +send_line "kill -STOP %1" expect_prompt send_line "wait" expect_prompt -send_line "wait \$last_pid" +send_line "wait %1" expect_prompt send_line "wait -n" expect_prompt -send_line "bg \$last_pid" +send_line "bg %1" expect_prompt send_line "wait" expect_prompt From 2961dcc4be9569b95d2fe46c1cff1da8fd532ddb Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 14 Apr 2018 23:19:25 -0500 Subject: [PATCH 070/159] Stop `echo` from swallowing passthrough arguments The job expansion wrapper was swallowing `-n` (and presumably `-e` and others) when that was the literal argument we needed to emit. Using `printf %s ...` instead. --- share/config.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/config.fish b/share/config.fish index a9898a083..ed6dd459c 100644 --- a/share/config.fish +++ b/share/config.fish @@ -268,7 +268,7 @@ function __fish_expand_pid_args return 1 end else - echo $arg + printf "%s\n" $arg end end end From a2cb9cceaf021f482ea87908d8477df91844c064 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 14 Apr 2018 23:23:33 -0500 Subject: [PATCH 071/159] Erase default autojump completions in j.fish completions The default completions that autojump ships with for fish are broken (emitting output like "1\___\#...") as they use hackes to work around the previous lack of `complete -k`. The history-based autojump completions fully replace it. --- share/completions/j.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/completions/j.fish b/share/completions/j.fish index 7d8a995aa..689e338f8 100644 --- a/share/completions/j.fish +++ b/share/completions/j.fish @@ -7,4 +7,6 @@ function __history_completions --argument limit history --prefix (commandline) | string replace -r \^$tokens[1]\\s\* "" | head -n$limit end +# erase the stock autojump completions, which are no longer needed with this +complete -c j -e complete -k -c j -a '(__history_completions 25)' -f From 92165444377d18a4f003c8193487b904e7d4b3f3 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sat, 14 Apr 2018 21:45:25 -0700 Subject: [PATCH 072/159] Add completion for --case-sensitive flag to history --- share/completions/history.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/completions/history.fish b/share/completions/history.fish index f4d9b9114..98d665d71 100644 --- a/share/completions/history.fish +++ b/share/completions/history.fish @@ -10,6 +10,8 @@ complete -c history -n '__fish_seen_subcommand_from search delete' \ -s e -l exact -d "Match items identical to the string" complete -c history -n '__fish_seen_subcommand_from search delete' \ -s t -l show-time -d "Output with timestamps" +complete -c history -n '__fish_seen_subcommand_from search delete' \ + -s C -l case-sensitive -d "Match items in a case-sensitive manner" complete -c history -n '__fish_seen_subcommand_from search' \ -s n -l max -d "Limit output to the first 'n' matches" From a9b582d2a806a7ded75adc004e90cb378a17c82b Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Sat, 14 Apr 2018 23:55:35 -0500 Subject: [PATCH 073/159] Block .dll files from completion as potential heads under WSL --- src/common.cpp | 6 ++++++ src/common.h | 1 + src/complete.cpp | 10 ++++++++++ src/wildcard.cpp | 6 +++++- 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/common.cpp b/src/common.cpp index f5fcf65c3..ac2492805 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1693,6 +1693,12 @@ bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &valu value.compare(value.size() - suffix_size, suffix_size, proposed_suffix) == 0; } +bool string_suffixes_string_case_insensitive(const wcstring &proposed_suffix, const wcstring &value) { + size_t suffix_size = proposed_suffix.size(); + return suffix_size <= value.size() && + wcsncasecmp(value.c_str() + (value.size() - suffix_size), proposed_suffix.c_str(), suffix_size) == 0; +} + /// Returns true if seq, represented as a subsequence, is contained within string. static bool subsequence_in_string(const wcstring &seq, const wcstring &str) { // Impossible if seq is larger than string. diff --git a/src/common.h b/src/common.h index 765412ca5..1428afe3f 100644 --- a/src/common.h +++ b/src/common.h @@ -321,6 +321,7 @@ bool string_prefixes_string(const wchar_t *proposed_prefix, const wchar_t *value /// Test if a string is a suffix of another. bool string_suffixes_string(const wcstring &proposed_suffix, const wcstring &value); bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &value); +bool string_suffixes_string_case_insensitive(const wcstring &proposed_suffix, const wcstring &value); /// Test if a string prefixes another without regard to case. Returns true if a is a prefix of b. bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, diff --git a/src/complete.cpp b/src/complete.cpp index acf9ea477..cb0ef776b 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -642,6 +642,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool std::vector possible_comp; if (use_command) { + // Append all possible executables expand_error_t result = expand_string(str_cmd, &this->completions, EXPAND_SPECIAL_FOR_COMMAND | EXPAND_FOR_COMPLETIONS | EXECUTABLES_ONLY | this->expand_flags(), @@ -655,6 +656,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool // We don't really care if this succeeds or fails. If it succeeds this->completions will be // updated with choices for the user. expand_error_t ignore = + // Append all matching directories expand_string(str_cmd, &this->completions, EXPAND_FOR_COMPLETIONS | DIRECTORIES_ONLY | this->expand_flags(), NULL); UNUSED(ignore); @@ -664,6 +666,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool if (use_function) { wcstring_list_t names = function_get_names(str_cmd.at(0) == L'_'); for (size_t i = 0; i < names.size(); i++) { + // Append all known matching functions append_completion(&possible_comp, names.at(i)); } @@ -673,6 +676,7 @@ void completer_t::complete_cmd(const wcstring &str_cmd, bool use_function, bool possible_comp.clear(); if (use_builtin) { + // Append all matching builtins builtin_get_names(&possible_comp); this->complete_strings(str_cmd, 0, &builtin_get_desc, possible_comp, 0); } @@ -990,6 +994,7 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop if (short_ok(str, o, options)) { // It's a match. const wcstring desc = o->localized_desc(); + // Append a short-style option append_completion(&this->completions, o->option, desc, 0); } @@ -1031,9 +1036,11 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop // switch-arg', since it is more commonly supported by homebrew getopt-like // functions. wcstring completion = format_string(L"%ls=", whole_opt.c_str() + offset); + // Append a long-style option with a mandatory trailing equal sign append_completion(&this->completions, completion, C_(o->desc), flags); } + // Append a long-style option append_completion(&this->completions, whole_opt.c_str() + offset, C_(o->desc), flags); } } @@ -1138,6 +1145,7 @@ bool completer_t::complete_variable(const wcstring &str, size_t start_offset) { } } + // Append matching environment variables append_completion(&this->completions, comp, desc, flags, match); res = true; @@ -1240,11 +1248,13 @@ bool completer_t::try_complete_user(const wcstring &str) { const wchar_t *pw_name = pw_name_str.c_str(); if (wcsncmp(user_name, pw_name, name_len) == 0) { wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); + // Append a user name append_completion(&this->completions, &pw_name[name_len], desc, COMPLETE_NO_SPACE); result = true; } else if (wcsncasecmp(user_name, pw_name, name_len) == 0) { wcstring name = format_string(L"~%ls", pw_name); wcstring desc = format_string(COMPLETE_USER_DESC, pw_name); + // Append a user name append_completion(&this->completions, name, desc, COMPLETE_REPLACES_TOKEN | COMPLETE_DONT_ESCAPE | COMPLETE_NO_SPACE); result = true; diff --git a/src/wildcard.cpp b/src/wildcard.cpp index f67836800..27d115b93 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -100,7 +100,7 @@ static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const // The string is '.' or '..'. Return true if the wildcard exactly matches. return wcscmp(str, wc) ? fuzzy_match_none : fuzzy_match_exact; } - + // Near Linear implementation as proposed here https://research.swtch.com/glob. const wchar_t *wc_x = wc; const wchar_t *str_x = str; @@ -411,6 +411,10 @@ static bool wildcard_test_flags_then_complete(const wcstring &filepath, const wc return false; } + if (is_windows_subsystem_for_linux() && string_suffixes_string_case_insensitive(L".dll", filename)) { + return false; + } + // Compute the description. wcstring desc; if (!(expand_flags & EXPAND_NO_DESCRIPTIONS)) { From afc5c0a6e71cab9b527e0531dda1ba0023400910 Mon Sep 17 00:00:00 2001 From: Kevin Ballard Date: Sat, 14 Apr 2018 21:59:18 -0700 Subject: [PATCH 074/159] Tweak the error message for `history delete --exact foo` The old message made it sound like the `--exact` flag wasn't supported. --- src/builtin_history.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/builtin_history.cpp b/src/builtin_history.cpp index d5ed1f399..30b613ece 100644 --- a/src/builtin_history.cpp +++ b/src/builtin_history.cpp @@ -264,7 +264,7 @@ int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (!opts.case_sensitive) { streams.err.append_format( - _(L"builtin history delete only supports --case-sensitive\n")); + _(L"builtin history delete --exact requires --case-sensitive\n")); status = STATUS_INVALID_ARGS; break; } From 7a07e9fc76aefba6d715d8e7ba52ec128ddff572 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 17 Apr 2018 17:11:04 -0500 Subject: [PATCH 075/159] Stop ignoring .out files in tests/ directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7fb439054..e62cb2ef3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ *.o *.obj *.orig +!tests/*.out *.out *.pch *.slo From d00474f0fcdd7cc7361a9d5584d9c69a45c16e5e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Mon, 16 Apr 2018 21:49:26 -0500 Subject: [PATCH 076/159] Optimize split_about --- src/wcstringutil.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wcstringutil.h b/src/wcstringutil.h index 907ffb8d7..ad9404ede 100644 --- a/src/wcstringutil.h +++ b/src/wcstringutil.h @@ -41,16 +41,17 @@ void split_about(ITER haystack_start, ITER haystack_end, ITER needle_start, ITER if (split_point == haystack_end) { // not found break; } - wcstring result = wcstring(haystack_cursor, split_point); - if (!no_empty || result.size() > 0) { - output->push_back(std::move(result)); + if (!no_empty || haystack_cursor != split_point) { + output->emplace_back(haystack_cursor, split_point); } remaining--; // Need to skip over the needle for the next search note that the needle may be empty. haystack_cursor = split_point + std::distance(needle_start, needle_end); } // Trailing component, possibly empty. - output->push_back(wcstring(haystack_cursor, haystack_end)); + if (!no_empty || haystack_cursor != haystack_end) { + output->emplace_back(haystack_cursor, haystack_end); + } } enum class ellipsis_type { From bd8c8ceb5994ba0945fbe331574f68c6d214408b Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 17 Apr 2018 06:57:33 -0500 Subject: [PATCH 077/159] Add line-delimited read presets with --line and --all-lines Refer to changes in doc_src/read.txt for more info. Closes #4861. --- doc_src/read.txt | 6 +- src/builtin_read.cpp | 171 +++++++++++++++++++++++++++---------------- src/wcstringutil.h | 3 +- 3 files changed, 116 insertions(+), 64 deletions(-) diff --git a/doc_src/read.txt b/doc_src/read.txt index b661b368a..f17aa2c89 100644 --- a/doc_src/read.txt +++ b/doc_src/read.txt @@ -7,7 +7,7 @@ read [OPTIONS] VARIABLES... \subsection read-description Description -`read` reads from standard input and either writes the result back to the terminal for use in command substitution or stores the result in one or more shell variables. By default, `read` reads up to the next newline and splits it into the given variables on space, tab and newline. Alternatively, a null character or a maximum number of characters can be used to terminate the input, and other delimiters can be given. Unlike other shells, there is no default variable (such as `REPLY`) for storing the result. Instead, it is printed on stdout. +`read` reads from standard input and either writes the result back to the terminal for use in command substitution or stores the result in one or more shell variables. By default, `read` reads up to the next newline and splits it into given variables on spaces or tabs. Alternatively, a null character or a maximum number of characters can be used to terminate the input, and other delimiters can be given. Unlike other shells, there is no default variable (such as `REPLY`) for storing the result. Instead, it is printed on stdout. The following options are available: @@ -43,6 +43,10 @@ The following options are available: - `-z` or `--null` marks the end of the line with the NUL character, instead of newline. This also disables interactive mode. +- `-L` or `--line` reads a single line at a time from the input stream and stores it in the `N` given variable. No more than `N` lines are consumed (one line per variable) from the input stream. + +- `-A` or `--all-lines` splits input into the given variables, separated by line breaks. The entire input stream is consumed and interactive mode is disabled. Probably only useful with `-a` to read all lines into a single array variable. Where possible, ` | while read --line` should be preferred over ` | read --all-lines` as the latter will block until the input stream has been consumed, leading to latency and decreased responsiveness. + `read` reads a single line of input from stdin, breaks it into tokens based on the delimiter set via `-d`/`--delimiter` as a complete string (like `string split` or, if that has not been given the (deprecated) `IFS` shell variable as a set of characters, and then assigns one token to each variable specified in `VARIABLES`. If there are more tokens than variables, the complete remainder is assigned to the last variable. As a special case, if `IFS` is set to the empty string, each character of the input is considered a separate token. If no parameters are provided, `read` enters a special case that simply provides redirection from `stdin` to `stdout`, useful for command substitution. For instance, the fish shell command below can be used to read data that should be provided via a command line argument from the console instead of hardcoding it in the command itself, allowing the command to both be reused as-is in various contexts with different input values and preventing possibly sensitive text from being included in the shell history: diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 4a3afa050..37edcb548 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -49,27 +49,33 @@ struct read_cmd_opts_t { bool split_null = false; bool to_stdout = false; int nchars = 0; + bool all_lines = false; + bool one_line = false; }; -static const wchar_t *short_options = L":ac:ghilm:n:p:d:suxzP:UR:"; -static const struct woption long_options[] = {{L"export", no_argument, NULL, 'x'}, - {L"global", no_argument, NULL, 'g'}, - {L"local", no_argument, NULL, 'l'}, - {L"universal", no_argument, NULL, 'U'}, - {L"unexport", no_argument, NULL, 'u'}, - {L"prompt", required_argument, NULL, 'p'}, - {L"prompt-str", required_argument, NULL, 'P'}, - {L"right-prompt", required_argument, NULL, 'R'}, - {L"command", required_argument, NULL, 'c'}, - {L"mode-name", required_argument, NULL, 'm'}, - {L"silent", no_argument, NULL, 's'}, - {L"nchars", required_argument, NULL, 'n'}, - {L"delimiter", required_argument, NULL, 'd'}, - {L"shell", no_argument, NULL, 'S'}, - {L"array", no_argument, NULL, 'a'}, - {L"null", no_argument, NULL, 'z'}, - {L"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0}}; +static const wchar_t *short_options = L":Aac:d:ghiLlm:n:p:suxzP:UR:LB"; +static const struct woption long_options[] = { + {L"array", no_argument, NULL, 'a'}, + {L"all-lines", no_argument, NULL, 'A'}, + {L"command", required_argument, NULL, 'c'}, + {L"delimiter", required_argument, NULL, 'd'}, + {L"export", no_argument, NULL, 'x'}, + {L"global", no_argument, NULL, 'g'}, + {L"help", no_argument, NULL, 'h'}, + {L"line", no_argument, NULL, 'L'}, + {L"local", no_argument, NULL, 'l'}, + {L"mode-name", required_argument, NULL, 'm'}, + {L"nchars", required_argument, NULL, 'n'}, + {L"null", no_argument, NULL, 'z'}, + {L"prompt", required_argument, NULL, 'p'}, + {L"prompt-str", required_argument, NULL, 'P'}, + {L"right-prompt", required_argument, NULL, 'R'}, + {L"shell", no_argument, NULL, 'S'}, + {L"silent", no_argument, NULL, 's'}, + {L"unexport", no_argument, NULL, 'u'}, + {L"universal", no_argument, NULL, 'U'}, + {NULL, 0, NULL, 0} +}; static int parse_cmd_opts(read_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss method) int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) { @@ -78,42 +84,44 @@ static int parse_cmd_opts(read_cmd_opts_t &opts, int *optind, //!OCLINT(high nc wgetopter_t w; while ((opt = w.wgetopt_long(argc, argv, short_options, long_options, NULL)) != -1) { switch (opt) { - case L'x': { - opts.place |= ENV_EXPORT; + case 'a': { + opts.array = true; break; } + case L'A': { + opts.all_lines = true; + break; + } + case L'c': { + opts.commandline = w.woptarg; + break; + } + case 'd': { + opts.have_delimiter = true; + opts.delimiter = w.woptarg; + break; + } + case 'i': { + streams.err.append_format(_(L"%ls: usage of -i for --silent is deprecated. Please use -s or --silent instead.\n"), + cmd); + return STATUS_INVALID_ARGS; + } case L'g': { opts.place |= ENV_GLOBAL; break; } + case 'h': { + opts.print_help = true; + break; + } + case L'L': { + opts.one_line = true; + break; + } case L'l': { opts.place |= ENV_LOCAL; break; } - case L'U': { - opts.place |= ENV_UNIVERSAL; - break; - } - case L'u': { - opts.place |= ENV_UNEXPORT; - break; - } - case L'p': { - opts.prompt = w.woptarg; - break; - } - case L'P': { - opts.prompt_str = w.woptarg; - break; - } - case L'R': { - opts.right_prompt = w.woptarg; - break; - } - case L'c': { - opts.commandline = w.woptarg; - break; - } case L'm': { streams.err.append_format(_(L"%ls: flags '--mode-name' / '-m' are now ignored. " L"Set fish_history instead.\n"), @@ -137,34 +145,40 @@ static int parse_cmd_opts(read_cmd_opts_t &opts, int *optind, //!OCLINT(high nc } break; } - case 'd': { - opts.have_delimiter = true; - opts.delimiter = w.woptarg; + case L'P': { + opts.prompt_str = w.woptarg; break; } - case 'i': { - streams.err.append_format(_(L"%ls: usage of -i for --silent is deprecated. Please use -s or --silent instead.\n"), - cmd); - return STATUS_INVALID_ARGS; + case L'p': { + opts.prompt = w.woptarg; + break; + } + case L'R': { + opts.right_prompt = w.woptarg; + break; } case 's': { opts.silent = true; break; } - case 'a': { - opts.array = true; - break; - } case L'S': { opts.shell = true; break; } - case L'z': { - opts.split_null = true; + case L'U': { + opts.place |= ENV_UNIVERSAL; break; } - case 'h': { - opts.print_help = true; + case L'u': { + opts.place |= ENV_UNEXPORT; + break; + } + case L'x': { + opts.place |= ENV_EXPORT; + break; + } + case L'z': { + opts.split_null = true; break; } case ':': { @@ -341,11 +355,28 @@ static int read_one_char_at_a_time(int fd, wcstring &buff, int nchars, bool spli static int validate_read_args(const wchar_t *cmd, read_cmd_opts_t &opts, int argc, const wchar_t *const *argv, parser_t &parser, io_streams_t &streams) { if (opts.prompt && opts.prompt_str) { - streams.err.append_format(_(L"%ls: You can't specify both -p and -P\n"), cmd); + streams.err.append_format(_(L"%ls: Options %ls and %ls cannot be used together\n"), cmd, L"-p", L"-P"); builtin_print_help(parser, streams, cmd, streams.err); return STATUS_INVALID_ARGS; } + if (opts.have_delimiter && opts.all_lines) { + streams.err.append_format(_(L"%ls: Options %ls and %ls cannot be used together\n"), cmd, L"--delimiter", L"--all-lines"); + return STATUS_INVALID_ARGS; + } + if (opts.have_delimiter && opts.one_line) { + streams.err.append_format(_(L"%ls: Options %ls and %ls cannot be used together\n"), cmd, L"--delimiter", L"--line"); + return STATUS_INVALID_ARGS; + } + if (opts.one_line && opts.all_lines) { + streams.err.append_format(_(L"%ls: Options %ls and %ls cannot be used together\n"), cmd, L"--all-lines", L"--line"); + return STATUS_INVALID_ARGS; + } + if (opts.one_line && opts.split_null) { + streams.err.append_format(_(L"%ls: Options %ls and %ls cannot be used together\n"), cmd, L"-z", L"--line"); + return STATUS_INVALID_ARGS; + } + if (opts.prompt_str) { opts.prompt_cmd = L"echo " + escape_string(opts.prompt_str, ESCAPE_ALL); opts.prompt = opts.prompt_cmd.c_str(); @@ -422,6 +453,21 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { retval = validate_read_args(cmd, opts, argc, argv, parser, streams); if (retval != STATUS_CMD_OK) return retval; + if (opts.all_lines) { + // --all-lines is the same as read -d \n -z + opts.have_delimiter = true; + opts.delimiter = L"\n"; + opts.split_null = true; + opts.shell = false; + } + else if (opts.one_line) { + // --line is the same as read -d \n + opts.have_delimiter = true; + opts.delimiter = L"\n"; + opts.split_null = false; + opts.shell = false; + } + // TODO: Determine if the original set of conditions for interactive reads should be reinstated: // if (isatty(0) && streams.stdin_fd == STDIN_FILENO && !split_null) { int stream_stdin_is_a_tty = isatty(streams.stdin_fd); @@ -517,7 +563,8 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { // We're using a delimiter provided by the user so use the `string split` behavior. wcstring_list_t splits; split_about(buff.begin(), buff.end(), opts.delimiter.begin(), opts.delimiter.end(), - &splits, LONG_MAX); + &splits); + env_set(argv[0], opts.place, splits); } } else { diff --git a/src/wcstringutil.h b/src/wcstringutil.h index ad9404ede..483817bff 100644 --- a/src/wcstringutil.h +++ b/src/wcstringutil.h @@ -26,9 +26,10 @@ wcstring_range wcstring_tok(wcstring& str, const wcstring& needle, /// If the iterators are forward, this does the normal thing. /// If the iterators are backward, this returns reversed strings, in reversed order! /// If the needle is empty, split on individual elements (characters). +/// Max output entries will be max + 1 (after max splits) template void split_about(ITER haystack_start, ITER haystack_end, ITER needle_start, ITER needle_end, - wcstring_list_t* output, long max, bool no_empty = false) { + wcstring_list_t* output, long max = LONG_MAX, bool no_empty = false) { long remaining = max; ITER haystack_cursor = haystack_start; while (remaining > 0 && haystack_cursor != haystack_end) { From 3742a7827f8f7b9e5bd67bc597ae742e5b1ea152 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 17 Apr 2018 13:28:56 -0500 Subject: [PATCH 078/159] Support multiple `read --line` variable outputs ref #4861. Also closes #4917. --- src/builtin_read.cpp | 224 ++++++++++++++++++++++--------------------- src/wcstringutil.cpp | 2 +- 2 files changed, 115 insertions(+), 111 deletions(-) diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 37edcb548..4dc957448 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -461,137 +462,140 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { opts.shell = false; } else if (opts.one_line) { - // --line is the same as read -d \n + // --line is the same as read -d \n repeated N times opts.have_delimiter = true; opts.delimiter = L"\n"; opts.split_null = false; opts.shell = false; } - // TODO: Determine if the original set of conditions for interactive reads should be reinstated: - // if (isatty(0) && streams.stdin_fd == STDIN_FILENO && !split_null) { - int stream_stdin_is_a_tty = isatty(streams.stdin_fd); - if (stream_stdin_is_a_tty && !opts.split_null) { - // We should read interactively using reader_readline(). This does not support splitting on - // null. - exit_res = read_interactive(buff, opts.nchars, opts.shell, opts.silent, opts.prompt, - opts.right_prompt, opts.commandline); - } else if (!opts.nchars && !stream_stdin_is_a_tty && - lseek(streams.stdin_fd, 0, SEEK_CUR) != -1) { - exit_res = read_in_chunks(streams.stdin_fd, buff, opts.split_null); - } else { - exit_res = read_one_char_at_a_time(streams.stdin_fd, buff, opts.nchars, opts.split_null); - } + wchar_t * const *var_ptr = argv; + auto vars_left = [&] () { return argv + argc - var_ptr; }; + auto clear_remaining_vars = [&] () { + while (vars_left()) { + env_set_empty(*var_ptr, opts.place); + // env_set_one(*var_ptr, opts.place, L""); + ++var_ptr; + } + }; - if (exit_res != STATUS_CMD_OK) { - // Define the var(s) without any data. We do this because when this happens we want the user - // to be able to use the var but have it expand to nothing. - for (int i = 0; i < argc; i++) env_set_empty(argv[i], opts.place); - return exit_res; - } + // Normally, we either consume a line of input or all available input. But if we are reading a line at + // a time, we need a middle ground where we only consume as many lines as we need to fill the given vars. + do { + buff.clear(); - if (opts.to_stdout) { - streams.out.append(buff); - return exit_res; - } - - if (!opts.have_delimiter) { - auto ifs = env_get(L"IFS"); - if (!ifs.missing_or_empty()) opts.delimiter = ifs->as_string(); - } - - if (opts.delimiter.empty()) { - // Every character is a separate token with one wrinkle involving non-array mode where the - // final var gets the remaining characters as a single string. - size_t x = std::max(static_cast(1), buff.size()); - size_t n_splits = (opts.array || static_cast(argc) > x) ? x : argc; - wcstring_list_t chars; - chars.reserve(n_splits); - x = x - n_splits + 1; - int i = 0; - for (auto it = buff.begin(), end = buff.end(); it != end; ++i, ++it) { - if (opts.array || i < argc) { - chars.emplace_back(wcstring(1, *it)); - } else { - if (x) { - chars.back().reserve(x); - x = 0; - } - chars.back().push_back(*it); - } + // TODO: Determine if the original set of conditions for interactive reads should be reinstated: + // if (isatty(0) && streams.stdin_fd == STDIN_FILENO && !split_null) { + int stream_stdin_is_a_tty = isatty(streams.stdin_fd); + if (stream_stdin_is_a_tty && !opts.split_null) { + // Read interactively using reader_readline(). This does not support splitting on null. + exit_res = read_interactive(buff, opts.nchars, opts.shell, opts.silent, opts.prompt, + opts.right_prompt, opts.commandline); + } else if (!opts.nchars && !stream_stdin_is_a_tty && + lseek(streams.stdin_fd, 0, SEEK_CUR) != -1) { + exit_res = read_in_chunks(streams.stdin_fd, buff, opts.split_null); + } else { + exit_res = read_one_char_at_a_time(streams.stdin_fd, buff, opts.nchars, opts.split_null); } - if (opts.array) { - // Array mode: assign each char as a separate element of the sole var. - env_set(argv[0], opts.place, chars); - } else { - // Not array mode: assign each char to a separate var with the remainder being assigned - // to the last var. + if (exit_res != STATUS_CMD_OK) { + clear_remaining_vars(); + return exit_res; + } + + if (opts.to_stdout) { + streams.out.append(buff); + return exit_res; + } + + if (!opts.have_delimiter) { + auto ifs = env_get(L"IFS"); + if (!ifs.missing_or_empty()) opts.delimiter = ifs->as_string(); + } + + if (opts.delimiter.empty()) { + // Every character is a separate token with one wrinkle involving non-array mode where the + // final var gets the remaining characters as a single string. + size_t x = std::max(static_cast(1), buff.size()); + size_t n_splits = (opts.array || static_cast(vars_left()) > x) ? x : vars_left(); + wcstring_list_t chars; + chars.reserve(n_splits); + x = x - n_splits + 1; int i = 0; - size_t j = 0; - for (; i + 1 < argc; ++i) { - if (j < chars.size()) { - env_set_one(argv[i], opts.place, chars[j]); - j++; + for (auto it = buff.begin(), end = buff.end(); it != end; ++i, ++it) { + if (opts.array || i + 1 < vars_left()) { + chars.emplace_back(1, *it); } else { - env_set_one(argv[i], opts.place, L""); + chars.emplace_back(it, buff.end()); + break; } } - if (i < argc) { - wcstring val = chars.size() == static_cast(argc) ? chars[i] : L""; - env_set_one(argv[i], opts.place, val); + if (opts.array) { + // Array mode: assign each char as a separate element of the sole var. + env_set(*var_ptr++, opts.place, chars); } else { - env_set_empty(argv[i], opts.place); - } - } - } else if (opts.array) { - // The user has requested the input be split into a sequence of tokens and all the tokens - // assigned to a single var. How we do the tokenizing depends on whether the user specified - // the delimiter string or we're using IFS. - if (!opts.have_delimiter) { - // We're using IFS, so tokenize the buffer using each IFS char. This is for backward - // compatibility with old versions of fish. - wcstring_list_t tokens; - - for (wcstring_range loc = wcstring_tok(buff, opts.delimiter); - loc.first != wcstring::npos; loc = wcstring_tok(buff, opts.delimiter, loc)) { - tokens.emplace_back(wcstring(buff, loc.first, loc.second)); - } - env_set(argv[0], opts.place, tokens); - } else { - // We're using a delimiter provided by the user so use the `string split` behavior. - wcstring_list_t splits; - split_about(buff.begin(), buff.end(), opts.delimiter.begin(), opts.delimiter.end(), - &splits); - - env_set(argv[0], opts.place, splits); - } - } else { - // Not array mode. Split the input into tokens and assign each to the vars in sequence. - if (!opts.have_delimiter) { - // We're using IFS, so tokenize the buffer using each IFS char. This is for backward - // compatibility with old versions of fish. - wcstring_range loc = wcstring_range(0, 0); - for (int i = 0; i < argc; i++) { - wcstring substr; - loc = wcstring_tok(buff, (i + 1 < argc) ? opts.delimiter : wcstring(), loc); - if (loc.first != wcstring::npos) { - substr = wcstring(buff, loc.first, loc.second); + // Not array mode: assign each char to a separate var with the remainder being assigned + // to the last var. + auto c = chars.begin(); + for (; c != chars.end() && vars_left(); ++c) { + env_set_one(*var_ptr++, opts.place, *c); } - env_set_one(argv[i], opts.place, substr); + } + } else if (opts.array) { + // The user has requested the input be split into a sequence of tokens and all the tokens + // assigned to a single var. How we do the tokenizing depends on whether the user specified + // the delimiter string or we're using IFS. + if (!opts.have_delimiter) { + // We're using IFS, so tokenize the buffer using each IFS char. This is for backward + // compatibility with old versions of fish. + wcstring_list_t tokens; + + for (wcstring_range loc = wcstring_tok(buff, opts.delimiter); + loc.first != wcstring::npos; loc = wcstring_tok(buff, opts.delimiter, loc)) { + tokens.emplace_back(wcstring(buff, loc.first, loc.second)); + } + env_set(*var_ptr++, opts.place, tokens); + } else { + // We're using a delimiter provided by the user so use the `string split` behavior. + wcstring_list_t splits; + split_about(buff.begin(), buff.end(), opts.delimiter.begin(), opts.delimiter.end(), + &splits); + + env_set(*var_ptr++, opts.place, splits); } } else { - // We're using a delimiter provided by the user so use the `string split` behavior. - wcstring_list_t splits; - // We're making at most argc - 1 splits so the last variable - // is set to the remaining string. - split_about(buff.begin(), buff.end(), opts.delimiter.begin(), opts.delimiter.end(), - &splits, argc - 1); - for (size_t i = 0; i < (size_t)argc && i < splits.size(); i++) { - env_set_one(argv[i], opts.place, splits[i]); + // Not array mode. Split the input into tokens and assign each to the vars in sequence. + if (!opts.have_delimiter) { + // We're using IFS, so tokenize the buffer using each IFS char. This is for backward + // compatibility with old versions of fish. + wcstring_range loc = wcstring_range(0, 0); + while (vars_left()) { + wcstring substr; + loc = wcstring_tok(buff, (vars_left() > 1) ? opts.delimiter : wcstring(), loc); + if (loc.first != wcstring::npos) { + substr = wcstring(buff, loc.first, loc.second); + } + env_set_one(*var_ptr++, opts.place, substr); + } + } else { + // We're using a delimiter provided by the user so use the `string split` behavior. + wcstring_list_t splits; + // We're making at most argc - 1 splits so the last variable + // is set to the remaining string. + split_about(buff.begin(), buff.end(), opts.delimiter.begin(), opts.delimiter.end(), + &splits, argc - 1); + assert(splits.size() <= (size_t) vars_left()); + for (const auto &split : splits) { + env_set_one(*var_ptr++, opts.place, split); + } } } + } while (opts.one_line && vars_left()); + + if (!opts.array) { + // In case there were more args than splits + clear_remaining_vars(); } return exit_res; diff --git a/src/wcstringutil.cpp b/src/wcstringutil.cpp index 348257443..d4904584d 100644 --- a/src/wcstringutil.cpp +++ b/src/wcstringutil.cpp @@ -31,7 +31,7 @@ wcstring_range wcstring_tok(wcstring& str, const wcstring& needle, wcstring_rang } wcstring truncate(const wcstring &input, int max_len, ellipsis_type etype) { - if (input.size() <= max_len) { + if (input.size() <= (size_t) max_len) { return input; } From c2cfc65cf219498848ac6a6303ecf7823fbb8270 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 17 Apr 2018 21:20:57 -0500 Subject: [PATCH 079/159] Correct read behavior for unset values and update tests accordingly `read` with IFS empty was expected to set all parameters after the first n filled variables to an empty string, but that was inconsistent with the behavior of `read` everywhere else. I'm not sure why fish differed from the spec with regards to the behavior in the event of an empty IFS: we eschew IFS where possible, yet here we adopt non-standard behavior splitting on every (unicode) character instead of not splitting at all with IFS empty. We still do that, but now the unset variables are treated as they normally would be, i.e. cleared and not set to an empty string (which is what an empty value between two IFS separators would contain). --- tests/read.out | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/read.out b/tests/read.out index d486f4af9..f29e2e1b9 100644 --- a/tests/read.out +++ b/tests/read.out @@ -35,9 +35,9 @@ two 1 'hello' 1 'h' 1 'ello' 1 'h' 1 'e' 1 'llo' -1 '' -1 't' 1 '' -1 't' 1 '' 1 '' +0 +1 't' 0 +1 't' 0 0 1 ' ' 1 't' 2 'hello' 'there' From c8af566330968a491f024d4ff62b3c25bcbb31da Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 17 Apr 2018 14:53:34 -0500 Subject: [PATCH 080/159] Stop completion skipping in case of valid ./command complete.cpp strips the path from commands before parsing for completions, meaning that when we called `path_get_path()` against `cmd`, if `./cmd` were typed in at the command line but `cmd` does not exist in the PATH, then the command would incorrectly be flagged as not present and the completions would be skipped. This is also faster when an absolute/relative path is used for a command, as we now search with the original path which skips searching PATH directories unnecessarily. Found when debugging why completions for `./configure` wouldn't work. --- src/complete.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/complete.cpp b/src/complete.cpp index cb0ef776b..1df55e5a6 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -868,6 +868,7 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop // after a command, so the overhead of the additional env lookup should be negligible. env_vars_snapshot_t completion_snapshot; + // debug(0, L"\nThinking about looking up completions for %ls\n", cmd.c_str()); bool head_exists = builtin_exists(cmd); // Only reload environment variables if builtin_exists returned false, as an optimization if (head_exists == false) { @@ -878,7 +879,7 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop head_exists = function_exists_no_autoload(cmd.c_str(), completion_snapshot); // While it may seem like first testing `path_get_path` before resorting to an env lookup may be faster, path_get_path can potentially // do a lot of FS/IO access, so env.get() + function_exists() should still be faster. - head_exists = head_exists || path_get_path(cmd, nullptr); + head_exists = head_exists || path_get_path(cmd_orig, nullptr); //use cmd_orig here as it is potentially pathed } if (!head_exists) { @@ -886,7 +887,7 @@ bool completer_t::complete_param(const wcstring &scmd_orig, const wcstring &spop //This prevents errors caused during the execution of completion providers for //tools that do not exist. Applies to both manual completions ("cm", "cmd ") //and automatic completions ("gi" autosuggestion provider -> git) - // debug(0, "Skipping completions for non-existent head\n"); + debug(4, "Skipping completions for non-existent head\n"); } else { run_on_main_thread([&]() { From 6c5e5d35a9fa22474930144457e8842ef071cbeb Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 17 Apr 2018 14:56:42 -0500 Subject: [PATCH 081/159] Add real completions for ./configure This relies on the new `read --line/-L` support as an entire parser for the output of `./configure --help` was written in fishscript. Also doesn't work without 72f32e6d8a7905b064680ec4b578c41dea62bf84. The completion script is slow... a function of both the autotools configure script itself being written in a shell script combined with a fishscript output parser. fish's own `./configure --help` takes around 350ms to execute, while `__fish_parse_configure ./configure` (which runs that behind the scenes) takes around 660ms to run, all-in-all - a not insignificant overhead. Output can be cached (based off of ./configure hash or mtime) in the future if this is a big deal. --- CHANGELOG.md | 7 ++- share/completions/configure.fish | 4 ++ share/functions/__fish_parse_configure.fish | 62 +++++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 share/functions/__fish_parse_configure.fish diff --git a/CHANGELOG.md b/CHANGELOG.md index 42c1d56fa..0c374498c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,17 +59,18 @@ This section is for changes merged to the `major` branch that are not also merge ## Other significant changes - Command substitution output is now limited to 10 MB by default (#3822). - Added completions for - - `j` (autojump #4344) - `bd` (#4472) + - `configure` + - `j` (autojump #4344) - `jhipster` (#4472) - `ngrok` (#4642)  - `port` - Improved completions for - - `git` (#4395, #4396, #4592) - `brew` - `diskutil` - - `yarn` + - `git` (#4395, #4396, #4592) - `ssh` (#4344) + - `yarn` -- diff --git a/share/completions/configure.fish b/share/completions/configure.fish index cd85dc6d8..f431f7db1 100644 --- a/share/completions/configure.fish +++ b/share/completions/configure.fish @@ -10,3 +10,7 @@ complete -c configure -l exec-prefix -d "Architecture-dependent install director complete -c configure -l build -d "Configure for building on BUILD" -x complete -c configure -l host -d "Cross-compile to build programs to run on HOST" -x complete -c configure -l target -d "Configure for building compilers for TARGET" -x + +# use autoconf's --help to generate completions: +echo "sourcing configure completions" +complete -c 'configure' -a '(__fish_parse_configure (commandline | string replace -r "(.*configure) .*" "\$1"))' diff --git a/share/functions/__fish_parse_configure.fish b/share/functions/__fish_parse_configure.fish new file mode 100644 index 000000000..7fcb44dfe --- /dev/null +++ b/share/functions/__fish_parse_configure.fish @@ -0,0 +1,62 @@ +function __fish_parse_configure + if test (count $argv) -ne 1 + echo "Usage: parse_configure path/to/configure" 1>&2 + return 1 + end + + # `complete` parses `./configure` as `configure` so we have to handle all paths, not just ./ + if not test -x $argv[1] + printf "Cannot find or execute '%s'\n" $argv[1] 1>&2 + return 1 + end + +# Must support output along the lines of +# -h, --help display this help and exit +# --help=short display options specific to this package +# --help=recursive display the short help of all the included packages +# -V, --version display version information and exit +# -q, --quiet, --silent do not print `checking ...' messages + + set -l next_line + set -l line + set -l buffer + eval $argv[1] --help 2>/dev/null | while test (string length -- "$next_line") -gt 0 || read -lL next_line + # In autoconfigure scripts, the first column wraps at 26 chars + # echo next_line: $next_line + # echo old_line: $line + if test (string length -- "$line") -eq 0 + set line $next_line + set next_line "" # mark it as consumed + continue + else if string match -qr '^( |\t){2,}[^-]\S*' -- $next_line + # echo "continuation line found. Old value of line: " \"$line\" + set line "$line "(string trim $next_line) + set next_line "" # mark it as consumed + continue + end + + # echo line: $line + + # Search for one or more strings starting with `-` separated by commas + if string replace -fr '^\s+(-.*?)\s+([^\s\-].*)' '$1\n$2' -- $line | read -lL opts description + for opt in (string split -n , -- $opts | string trim) + + if string match -qr -- '--.*=\[.*\]' $opt + # --option=[OPTIONAL_VALUE] + string replace -r -- '(--.*)=.*' '$1' $opt | read opt + else if string match -qr -- '--.*=[A-Z]+' $opt + # --option=CLASS_OF_VALUE (eg FILE or DIR) + string replace -r -- '(--.*)=.*' '$1' $opt | read opt + else if string match -qr -- '--.*=\S+' $opt + # --option=literal_value, leave as-is + else if string match -qr -- '-[^-]\b' $opt + # short option, leave as-is + end + + echo "$opt"\t"$description" # parsed by `complete` as value and description + end + end + + set line "" + end +end From 9d9afd8264cac86d57a64a0d119896ff23a07c7d Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 18 Apr 2018 15:40:52 -0500 Subject: [PATCH 082/159] Speed up ./configure completion by not running `./configure --help` Instead, attempt to extract the message that _would_ be displayed on execution of `./configure --help` by relying on some markers present in autoconf-generated configure files. As measured with 'hyperfine' on a laptop running in reduced frequency power savings mode, `fish -c "__fish_parse_configure ./configure"` runtime dropped from ~1.25s to ~0.8ms, which is inline with the previously observed ~350ms execution time for `./configure --help`. fish's own startup time is approximately 75ms before parsing begins. Still very slow, but much better. --- share/functions/__fish_parse_configure.fish | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/share/functions/__fish_parse_configure.fish b/share/functions/__fish_parse_configure.fish index 7fcb44dfe..aa8b94415 100644 --- a/share/functions/__fish_parse_configure.fish +++ b/share/functions/__fish_parse_configure.fish @@ -20,11 +20,15 @@ function __fish_parse_configure set -l next_line set -l line set -l buffer - eval $argv[1] --help 2>/dev/null | while test (string length -- "$next_line") -gt 0 || read -lL next_line + # eval $argv[1] --help 2>/dev/null | + # Just fish's `./configure --help` takes ~350ms to run, before parsing + # The following chain attempts to extract the help message: + cat $argv[1] | tr \n \u0e | sed -n 's/.*Report the --help message\(.*\?\)ac_status.*/\1/; s/ac_status.*//p' | tr \u0e \n | + while test "$next_line" != "" || read -lL next_line # In autoconfigure scripts, the first column wraps at 26 chars # echo next_line: $next_line # echo old_line: $line - if test (string length -- "$line") -eq 0 + if test "$line" = "" set line $next_line set next_line "" # mark it as consumed continue From c2e66b002a667bbf62b20a6dc3e807e2892d279c Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 18 Apr 2018 21:43:39 -0500 Subject: [PATCH 083/159] Dynamically invoke yarn completions so they aren't cached Correct `-a (something)` to `-a '(something)'` --- share/completions/yarn.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index d4e4d3c09..0556821a4 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -49,7 +49,7 @@ function __yarn_list_packages end -complete -f -c yarn -n '__fish_seen_subcommand_from remove' -a (set -l packages (__yarn_list_packages); echo $packages) +complete -f -c yarn -n '__fish_seen_subcommand_from remove' -a '(__yarn_list_packages)' complete -f -c yarn -n '__fish_use_subcommand' -a help From 156d4fb9b9b83546ff891e14216330648cd71a07 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Wed, 18 Apr 2018 22:42:46 -0500 Subject: [PATCH 084/159] Complete npm package names for `npm install` via `all-the-package-names` `npm search` was _way_ too slow to be used for dynamic completions, so using a cached list of all avaialable NPM packages to match against. This is a bit brave for a fish completion, but the npm package `all-the-package-names` has a list of, well, all the package names avaialable for installation via the default npm registry. Installing a copy locally to $HOME/.cache/fish/npm_completions and using that to search for packages matching the tokenized command line. Preference would be to call `__update_atpm` in the background, but that emits an ugly "job has completed" message.. Should also use this for completions for `yarn add`. --- share/completions/npm.fish | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/share/completions/npm.fish b/share/completions/npm.fish index ad8d2b253..0ae9dcdc2 100644 --- a/share/completions/npm.fish +++ b/share/completions/npm.fish @@ -4,6 +4,39 @@ # see also Fish's large set of completions for examples: # https://github.com/fish-shell/fish-shell/tree/master/share/completions +set -g __fish_atpm_cache $HOME/.cache/fish/npm_completions + +function __install_atpm + if not test -d $__fish_atpm_cache + mkdir -p $__fish_atpm_cache + fish -c "cd $HOME/.cache/fish/npm_completions; npm init -y >/dev/null; npm install all-the-package-names >/dev/null; touch .last_update" >/dev/null 2>/dev/null + end +end + +function __update_atpm + if test \"(find $__fish_atpm_cache/ -name .last_update -mtime +1 -print)\" + # all-the-package-names is out of date + fish -c "cd $__fish_atpm_cache; npm update; touch .last_update" >/dev/null 2>/dev/null + end +end + +function __npm_list_packages + if not test -x $__fish_atpm_cache/node_modules/.bin/all-the-package-names + if not __install_atpm >/dev/null + return + end + end + + __update_atpm >/dev/null + eval $__fish_atpm_cache/node_modules/.bin/all-the-package-names +end + +# Entire list of packages is too long to be used in a `complete` subcommand +# Search it for matches instead +function __npm_filtered_list_packages + __npm_list_packages | grep (commandline -ct) | head -n 50 +end + function __fish_npm_needs_command set cmd (commandline -opc) @@ -192,3 +225,4 @@ complete -f -c npm -n '__fish_npm_needs_command' -a 'unpublish' -d 'Remove a pac complete -f -c npm -n '__fish_npm_needs_command' -a 'unstar' -d 'Remove star from a package' complete -f -c npm -n '__fish_npm_needs_command' -a 'version' -d 'Bump a package version' complete -f -c npm -n '__fish_npm_needs_command' -a 'whoami' -d 'Display npm username' +complete -f -c npm -n '__fish_seen_subcommand_from install' -a '(__npm_filtered_list_packages)' From beba9df4060cdfa26af5f41d8d47f34fbbe3a35e Mon Sep 17 00:00:00 2001 From: slama Date: Tue, 17 Apr 2018 00:01:34 +0900 Subject: [PATCH 085/159] add job expansion wrapper for disown --- share/config.fish | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/share/config.fish b/share/config.fish index ed6dd459c..9f08d0ca9 100644 --- a/share/config.fish +++ b/share/config.fish @@ -288,3 +288,8 @@ end function wait --wraps wait builtin wait (__fish_expand_pid_args $argv) end + +function disown --wraps disown + builtin disown (__fish_expand_pid_args $argv) +end + From 408cdba1bed438c87d9c6fe13ce59dcd880aa9aa Mon Sep 17 00:00:00 2001 From: Sam Yu Date: Fri, 13 Apr 2018 23:19:01 +0800 Subject: [PATCH 086/159] Update tar completion * Fix gzip archive files completion * Let tar auto detect file type --- share/functions/__fish_complete_tar.fish | 49 +++++++----------------- 1 file changed, 14 insertions(+), 35 deletions(-) diff --git a/share/functions/__fish_complete_tar.fish b/share/functions/__fish_complete_tar.fish index f7f654a0e..3b90bfa67 100644 --- a/share/functions/__fish_complete_tar.fish +++ b/share/functions/__fish_complete_tar.fish @@ -1,42 +1,21 @@ function __fish_complete_tar -d "Peek inside of archives and list all files" - set -l cmd (commandline -poc) - set -e cmd[1] - for i in $cmd - switch $i - case '-*' + set -l args (commandline -poc) + while count $args >/dev/null + switch $args[1] + case '-*f' '--file' + set -e args[1] + if test -f $args[1] + set -l file_list (tar -atf $args[1] ^ /dev/null) + if test -n "$file_list" + printf (_ "%s\tArchived file\n") $file_list + end + return + end + case '*' + set -e args[1] continue - - case '*.tar.bz' '*.tar.bz2' '*.tbz' '*.tbz2' - if test -f $i - set -l file_list (tar -jt <$i) - printf (_ "%s\tArchived file\n") $file_list - end - return - - case '*.tar.gz' '*.tgz' - if test -f $i - set -l file_list (tar -it <$i) - printf (_ "%s\tArchived file\n") $file_list - end - return - - case '*.tar.xz' - if test -f $i - set -l file_list (tar -Jt <$i) - printf (_ "%s\tArchived file\n") $file_list - end - return - - case '*.tar' - if test -f $i - set -l file_list (tar -t <$i) - printf (_ "%s\tArchived file\n") $file_list - end - return end end end - - From 97f2eca5718030567985cbef12c40497022fb498 Mon Sep 17 00:00:00 2001 From: George Christou Date: Mon, 16 Apr 2018 09:55:11 +0100 Subject: [PATCH 087/159] completions: [git] Add common options for `show` and `diff` --- share/completions/git.fish | 143 +++++++++++++++++++++++++++++++------ 1 file changed, 120 insertions(+), 23 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index e4115f1c4..9f11967ca 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -350,6 +350,54 @@ function __fish_git_reflog command git reflog --no-decorate 2>/dev/null | string replace -r '[0-9a-f]* (.+@\{[0-9]+\}): (.*)$' '$1\t$2' end +function __fish_git_diff_opt -a option + switch $option + case diff-algorithm + printf "%b" " +default\tBasic greedy diff algorithm +myers\tBasic greedy diff algorithm +minimal\tMake smallest diff possible +patience\tPatience diff algorithm +histogram\tPatience algorithm with low-occurrence common elements" + case diff-filter + printf "%b" " +A\tAdded files +C\tCopied files +D\tDeleted files +M\tModified files +R\tRenamed files +T\tType changed files +U\tUnmerged files +X\tUnknown files +B\tBroken pairing files" + case dirstat + printf "%b" " +changes\tCount lines that have been removed from the source / added to the destination +lines\tRegular line-based diff analysis +files\tCount the number of files changed +cumulative\tCount changes in a child directory for the parent directory as well" + case ignore-submodules + printf "%b" " +none\tUntracked/modified files +untracked\tNot considered dirty when they only contain untracked content +dirty\tIgnore all changes to the work tree of submodules +all\tHide all changes to submodules (default)" + case submodule + printf "%b" " +short\tShow the name of the commits at the beginning and end of the range +log\tList the commits in the range +diff\tShow an inline diff of the changes" + case ws-error-highlight + printf "%b" " +context\tcontext lines of the diff +old\told lines of the diff +new\tnew lines of the diff +none\treset previous values +default\treset the list to 'new' +all\tShorthand for 'old,new,context'" + end +end + # general options complete -f -c git -l help -d 'Display the manual of a git command' complete -f -c git -n '__fish_git_needs_command' -l version -d 'Display version' @@ -374,6 +422,71 @@ complete -f -c git -n '__fish_git_needs_command' -l icase-pathspecs -d 'Match pa # Options shared between multiple commands complete -f -c git -n '__fish_git_using_command log show diff-tree rev-list' -l pretty -a 'oneline short medium full fuller email raw format:' +complete -c git -n '__fish_git_using_command diff show' -l abbrev -d 'Show only a partial prefix instead of the full 40-byte hexadecimal object name' +complete -c git -n '__fish_git_using_command diff show' -l binary -d 'Output a binary diff that can be applied with "git-apply"' +complete -c git -n '__fish_git_using_command diff show' -l check -d 'Warn if changes introduce conflict markers or whitespace errors' +complete -c git -n '__fish_git_using_command diff show' -l color -d 'Show colored diff' +complete -c git -n '__fish_git_using_command diff show' -l color-moved -d 'Moved lines of code are colored differently' +complete -c git -n '__fish_git_using_command diff show' -l color-words -d 'Equivalent to --word-diff=color plus --word-diff-regex=' +complete -c git -n '__fish_git_using_command diff show' -l compact-summary -d 'Output a condensed summary of extended header information' +complete -c git -n '__fish_git_using_command diff show' -l dst-prefix -d 'Show the given destination prefix instead of "b/"' +complete -c git -n '__fish_git_using_command diff show' -l ext-diff -d 'Allow an external diff helper to be executed' +complete -c git -n '__fish_git_using_command diff show' -l find-copies-harder -d 'Inspect unmodified files as candidates for the source of copy' +complete -c git -n '__fish_git_using_command diff show' -l find-object -d 'Look for differences that change the number of occurrences of the specified object' +complete -c git -n '__fish_git_using_command diff show' -l full-index -d 'Show the full pre- and post-image blob object names on the "index" line' +complete -c git -n '__fish_git_using_command diff show' -l histogram -d 'Generate a diff using the "histogram diff" algorithm' +complete -c git -n '__fish_git_using_command diff show' -l ignore-blank-lines -d 'Ignore changes whose lines are all blank' +complete -c git -n '__fish_git_using_command diff show' -l ignore-cr-at-eol -d 'Ignore carrige-return at the end of line when doing a comparison' +complete -c git -n '__fish_git_using_command diff show' -l ignore-space-at-eol -d 'Ignore changes in whitespace at EOL' +complete -c git -n '__fish_git_using_command diff show' -l indent-heuristic -d 'Enable the heuristic that shift diff hunk boundaries' +complete -c git -n '__fish_git_using_command diff show' -l inter-hunk-context -d 'Show the context between diff hunks, up to the specified number of lines' +complete -c git -n '__fish_git_using_command diff show' -l ita-invisible-in-index -d 'Make the entry appear as a new file in "git diff" and non-existent in "git diff -l cached"' +complete -c git -n '__fish_git_using_command diff show' -l line-prefix -d 'Prepend an additional prefix to every line of output' +complete -c git -n '__fish_git_using_command diff show' -l minimal -d 'Spend extra time to make sure the smallest possible diff is produced' +complete -c git -n '__fish_git_using_command diff show' -l name-only -d 'Show only names of changed files' +complete -c git -n '__fish_git_using_command diff show' -l name-status -d 'Show only names and status of changed files' +complete -c git -n '__fish_git_using_command diff show' -l no-color -d 'Turn off colored diff' +complete -c git -n '__fish_git_using_command diff show' -l no-ext-diff -d 'Disallow external diff drivers' +complete -c git -n '__fish_git_using_command diff show' -l no-indent-heuristic -d 'Disable the indent heuristic' +complete -c git -n '__fish_git_using_command diff show' -l no-prefix -d 'Do not show any source or destination prefix' +complete -c git -n '__fish_git_using_command diff show' -l no-renames -d 'Turn off rename detection' +complete -c git -n '__fish_git_using_command diff show' -l no-textconv -d 'Disallow external text conversion filters to be run when comparing binary files' +complete -c git -n '__fish_git_using_command diff show' -l numstat -d 'Shows number of added/deleted lines in decimal notation' +complete -c git -n '__fish_git_using_command diff show' -l patch-with-raw -d 'Synonym for -p --raw' +complete -c git -n '__fish_git_using_command diff show' -l patch-with-stat -d 'Synonym for -p --stat' +complete -c git -n '__fish_git_using_command diff show' -l patience -d 'Generate a diff using the "patience diff" algorithm' +complete -c git -n '__fish_git_using_command diff show' -l pickaxe-all -d 'When -S or -G finds a change, show all the changes in that changeset' +complete -c git -n '__fish_git_using_command diff show' -l pickaxe-regex -d 'Treat the given to -S as an extended POSIX regular expression to match' +complete -c git -n '__fish_git_using_command diff show' -l relative -d 'Exclude changes outside the directory and show relative pathnames' +complete -c git -n '__fish_git_using_command diff show' -l shortstat -d 'Output only the last line of the --stat format containing total number of modified files' +complete -c git -n '__fish_git_using_command diff show' -l src-prefix -d 'Show the given source prefix instead of "a/"' +complete -c git -n '__fish_git_using_command diff show' -l stat -d 'Generate a diffstat' +complete -c git -n '__fish_git_using_command diff show' -l summary -d 'Output a condensed summary of extended header information' +complete -c git -n '__fish_git_using_command diff show' -l textconv -d 'Allow external text conversion filters to be run when comparing binary files' +complete -c git -n '__fish_git_using_command diff show' -l word-diff -d 'Show a word diff' +complete -c git -n '__fish_git_using_command diff show' -l word-diff-regex -d 'Use to decide what a word is' +complete -c git -n '__fish_git_using_command diff show' -s a -l text -d 'Treat all files as text' +complete -c git -n '__fish_git_using_command diff show' -s B -l break-rewrites -d 'Break complete rewrite changes into pairs of delete and create' +complete -c git -n '__fish_git_using_command diff show' -s b -l ignore-space-change -d 'Ignore changes in amount of whitespace' +complete -c git -n '__fish_git_using_command diff show' -s C -l find-copies -d 'Detect copies as well as renames' +complete -c git -n '__fish_git_using_command diff show' -s D -l irreversible-delete -d 'Omit the preimage for deletes' +complete -c git -n '__fish_git_using_command diff show' -s G -d 'Look for differences whose patch text contains added/removed lines that match ' +complete -c git -n '__fish_git_using_command diff show' -s M -l find-renames -d 'Detect and report renames' +complete -c git -n '__fish_git_using_command diff show' -s R -d 'Show differences from index or on-disk file to tree contents' +complete -c git -n '__fish_git_using_command diff show' -s S -d 'Look for differences that change the number of occurrences of the specified string' +complete -c git -n '__fish_git_using_command diff show' -s W -l function-context -d 'Show whole surrounding functions of changes' +complete -c git -n '__fish_git_using_command diff show' -s w -l ignore-all-space -d 'Ignore whitespace when comparing lines' +complete -c git -n '__fish_git_using_command diff show' -s z -d 'Use NULs as output field/commit terminators' +complete -r -c git -n '__fish_git_using_command diff show' -s O -d 'Control the order in which files appear in the output' +complete -f -c git -n '__fish_git_using_command diff show' -l anchored -d 'Generate a diff using the "anchored diff" algorithm' +complete -x -c git -n '__fish_git_using_command diff show' -s l -d 'Prevents rename/copy detection if the number of rename/copy targets exceeds the specified number' +complete -x -c git -n '__fish_git_using_command diff show' -l diff-filter -a '(__fish_git_diff_opt diff-filter)' -d 'Choose diff filters' +complete -x -c git -n '__fish_git_using_command diff log show' -l diff-algorithm -a '(__fish_git_diff_opt diff-algorithm)' -d 'Choose a diff algorithm' +complete -x -c git -n '__fish_git_using_command diff log show' -l dirstat -a '(__fish_git_diff_opt dirstat)' -d 'Output the distribution of relative amount of changes for each sub-directory' +complete -x -c git -n '__fish_git_using_command diff log show' -l ignore-submodules -a '(__fish_git_diff_opt ignore-submodules)' -d 'Ignore changes to submodules in the diff generation' +complete -x -c git -n '__fish_git_using_command diff log show' -l submodule -a '(__fish_git_diff_opt submodule)' -d 'Specify how differences in submodules are shown' +complete -x -c git -n '__fish_git_using_command diff log show' -l ws-error-highlight -a '(__fish_git_diff_opt ws-error-highlight)' -d 'Highlight whitespace errors in lines of the diff' + #### fetch complete -f -c git -n '__fish_git_needs_command' -a fetch -d 'Download objects and refs from another repository' # Suggest "repository", then "refspec" - this also applies to e.g. push/pull @@ -389,13 +502,13 @@ complete -f -c git -n '__fish_git_using_command fetch' -s f -l force -d 'Force u #### filter-branch complete -f -c git -n '__fish_git_needs_command' -a filter-branch -d 'Rewrite branches' complete -f -c git -n '__fish_git_using_command filter-branch' -l env-filter -d 'This filter may be used if you only need to modify the environment' -complete -f -c git -n '__fish_git_using_command filter-branch' -l tree-filter -d 'This is the filter for rewriting the tree and its contents.' -complete -f -c git -n '__fish_git_using_command filter-branch' -l index-filter -d 'This is the filter for rewriting the index.' -complete -f -c git -n '__fish_git_using_command filter-branch' -l parent-filter -d 'This is the filter for rewriting the commit\\(cqs parent list.' -complete -f -c git -n '__fish_git_using_command filter-branch' -l msg-filter -d 'This is the filter for rewriting the commit messages.' -complete -f -c git -n '__fish_git_using_command filter-branch' -l commit-filter -d 'This is the filter for performing the commit.' -complete -f -c git -n '__fish_git_using_command filter-branch' -l tag-name-filter -d 'This is the filter for rewriting tag names.' -complete -f -c git -n '__fish_git_using_command filter-branch' -l subdirectory-filter -d 'Only look at the history which touches the given subdirectory.' +complete -f -c git -n '__fish_git_using_command filter-branch' -l tree-filter -d 'This is the filter for rewriting the tree and its contents' +complete -f -c git -n '__fish_git_using_command filter-branch' -l index-filter -d 'This is the filter for rewriting the index' +complete -f -c git -n '__fish_git_using_command filter-branch' -l parent-filter -d 'This is the filter for rewriting the commit' +complete -f -c git -n '__fish_git_using_command filter-branch' -l msg-filter -d 'This is the filter for rewriting the commit messages' +complete -f -c git -n '__fish_git_using_command filter-branch' -l commit-filter -d 'This is the filter for performing the commit' +complete -f -c git -n '__fish_git_using_command filter-branch' -l tag-name-filter -d 'This is the filter for rewriting tag names' +complete -f -c git -n '__fish_git_using_command filter-branch' -l subdirectory-filter -d 'Only look at the history which touches the given subdirectory' complete -f -c git -n '__fish_git_using_command filter-branch' -l prune-empty -d 'Ignore empty commits generated by filters' complete -f -c git -n '__fish_git_using_command filter-branch' -l original -d 'Use this option to set the namespace where the original commits will be stored' complete -r -c git -n '__fish_git_using_command filter-branch' -s d -d 'Use this option to set the path to the temporary directory used for rewriting' @@ -679,23 +792,14 @@ complete -c git -n '__fish_git_using_command log' -l no-compaction-heuristic complete -c git -n '__fish_git_using_command log' -l minimal complete -c git -n '__fish_git_using_command log' -l patience complete -c git -n '__fish_git_using_command log' -l histogram -complete -x -c git -n '__fish_git_using_command log' -l diff-algorithm -a ' - default\tBasic\ greedy\ diff\ algorithm - myers\tBasic\ greedy\ diff\ algorithm - minimal\tMake\ smallest\ diff\ possible - patience\tPatience\ diff\ algorithm - histogram\tPatience\ algorithm\ with\ low-occurrence\ common\ elements -' complete -f -x -c git -n '__fish_git_using_command log' -l stat complete -c git -n '__fish_git_using_command log' -l numstat complete -c git -n '__fish_git_using_command log' -l shortstat -complete -f -c git -n '__fish_git_using_command log' -l dirstat -a '(__fish_append , changes lines files cumulative)' complete -c git -n '__fish_git_using_command log' -l summary complete -c git -n '__fish_git_using_command log' -l patch-with-stat complete -c git -n '__fish_git_using_command log' -s z complete -c git -n '__fish_git_using_command log' -l name-only complete -c git -n '__fish_git_using_command log' -l name-status -complete -f -c git -n '__fish_git_using_command log' -l submodule -a 'short diff log' complete -f -c git -n '__fish_git_using_command log' -l color -a 'always never auto' complete -c git -n '__fish_git_using_command log' -l no-color complete -f -c git -n '__fish_git_using_command log' -l word-diff -a ' @@ -707,7 +811,6 @@ complete -f -c git -n '__fish_git_using_command log' -l word-diff -a ' complete -f -c git -n '__fish_git_using_command log' -l color-words complete -c git -n '__fish_git_using_command log' -l no-renames complete -c git -n '__fish_git_using_command log' -l check -complete -f -c git -n '__fish_git_using_command log' -l ws-error-highlight -a '(__fish_append , old new context)' complete -c git -n '__fish_git_using_command log' -l full-index complete -c git -n '__fish_git_using_command log' -l binary complete -f -c git -n '__fish_git_using_command log' -l abbrev @@ -742,12 +845,6 @@ complete -c git -n '__fish_git_using_command log' -l ext-diff complete -c git -n '__fish_git_using_command log' -l no-ext-diff complete -c git -n '__fish_git_using_command log' -l textconv complete -c git -n '__fish_git_using_command log' -l no-textconv -complete -f -c git -n '__fish_git_using_command log' -l ignore-submodules -a ' - none - untracked - dirty - all -' complete -x -c git -n '__fish_git_using_command log' -l src-prefix complete -x -c git -n '__fish_git_using_command log' -l dst-prefix complete -c git -n '__fish_git_using_command log' -l no-prefix From 54959a8248b6f547ca13168220a84531aebdbc2d Mon Sep 17 00:00:00 2001 From: J Delaney Date: Mon, 16 Apr 2018 12:34:14 -0700 Subject: [PATCH 088/159] Completions for merging --- share/completions/git.fish | 43 ++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 9f11967ca..7319f59a2 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -856,25 +856,34 @@ complete -f -c git -n '__fish_git_needs_command' -a merge -d 'Join two or more d complete -f -c git -n '__fish_git_using_command merge' -a '(__fish_git_branches)' complete -f -c git -n '__fish_git_using_command merge' -l commit -d "Autocommit the merge" complete -f -c git -n '__fish_git_using_command merge' -l no-commit -d "Don't autocommit the merge" -complete -f -c git -n '__fish_git_using_command merge' -l edit -d 'Edit auto-generated merge message' +complete -f -c git -n '__fish_git_using_command merge' -s e -l edit -d 'Edit auto-generated merge message' complete -f -c git -n '__fish_git_using_command merge' -l no-edit -d "Don't edit auto-generated merge message" complete -f -c git -n '__fish_git_using_command merge' -l ff -d "Don't generate a merge commit if merge is fast-forward" complete -f -c git -n '__fish_git_using_command merge' -l no-ff -d "Generate a merge commit even if merge is fast-forward" complete -f -c git -n '__fish_git_using_command merge' -l ff-only -d 'Refuse to merge unless fast-forward possible' +complete -f -c git -n '__fish_git_using_command merge' -s S -l gpg-sign -d 'GPG-sign the merge commit' complete -f -c git -n '__fish_git_using_command merge' -l log -d 'Populate the log message with one-line descriptions' complete -f -c git -n '__fish_git_using_command merge' -l no-log -d "Don't populate the log message with one-line descriptions" +complete -f -c git -n '__fish_git_using_command merge' -l signoff -d 'Add Signed-off-by line at the end of the merge commit message' +complete -f -c git -n '__fish_git_using_command merge' -l no-signoff -d 'Do not add a Signed-off-by line at the end of the merge commit message' complete -f -c git -n '__fish_git_using_command merge' -l stat -d "Show diffstat of the merge" complete -f -c git -n '__fish_git_using_command merge' -s n -l no-stat -d "Don't show diffstat of the merge" complete -f -c git -n '__fish_git_using_command merge' -l squash -d "Squash changes from other branch as a single commit" complete -f -c git -n '__fish_git_using_command merge' -l no-squash -d "Don't squash changes" +complete -x -c git -n '__fish_git_using_command merge' -s s -l strategy -d 'Use the given merge strategy' +complete -r -c git -n '__fish_git_using_command merge' -s X -l strategy-option -d 'Pass given option to the merge strategy' +complete -f -c git -n '__fish_git_using_command merge' -l verify-signatures -d 'Abort merge if other branch tip commit is not signed with a valid key' +complete -f -c git -n '__fish_git_using_command merge' -l no-verify-signatures -d 'Do not abort merge if other branch tip commit is not signed with a valid key' complete -f -c git -n '__fish_git_using_command merge' -s q -l quiet -d 'Be quiet' complete -f -c git -n '__fish_git_using_command merge' -s v -l verbose -d 'Be verbose' complete -f -c git -n '__fish_git_using_command merge' -l progress -d 'Force progress status' complete -f -c git -n '__fish_git_using_command merge' -l no-progress -d 'Force no progress status' -complete -f -c git -n '__fish_git_using_command merge' -s m -d 'Set the commit message' +complete -f -c git -n '__fish_git_using_command merge' -l allow-unrelated-histories -d 'Allow merging even when branches do not share a common history' +complete -x -c git -n '__fish_git_using_command merge' -s m -d 'Set the commit message' +complete -f -c git -n '__fish_git_using_command merge' -s rerere-autoupdate -d 'If possible, use previous conflict resolutions' +complete -f -c git -n '__fish_git_using_command merge' -s no-rerere-autoupdate -d 'Do not use previous conflict resolutions' complete -f -c git -n '__fish_git_using_command merge' -l abort -d 'Abort the current conflict resolution process' - -# TODO options +complete -f -c git -n '__fish_git_using_command merge' -l continue -d 'Conclude current conflict resolution process' ### mergetool @@ -914,6 +923,32 @@ complete -f -c git -n '__fish_git_using_command pull' -l no-tags -d 'Disable aut complete -f -c git -n '__fish_git_using_command pull' -l progress -d 'Force progress status' complete -f -c git -n '__fish_git_using_command pull; and not __fish_git_branch_for_remote' -a '(__fish_git_remotes)' -d 'Remote alias' complete -f -c git -n '__fish_git_using_command pull; and __fish_git_branch_for_remote' -a '(__fish_git_branch_for_remote)' +# Options related to merging +complete -f -c git -n '__fish_git_using_command pull' -l commit -d "Autocommit the merge" +complete -f -c git -n '__fish_git_using_command pull' -l no-commit -d "Don't autocommit the merge" +complete -f -c git -n '__fish_git_using_command pull' -s e -l edit -d 'Edit auto-generated merge message' +complete -f -c git -n '__fish_git_using_command pull' -l no-edit -d "Don't edit auto-generated merge message" +complete -f -c git -n '__fish_git_using_command pull' -l ff -d "Don't generate a merge commit if merge is fast-forward" +complete -f -c git -n '__fish_git_using_command pull' -l no-ff -d "Generate a merge commit even if merge is fast-forward" +complete -f -c git -n '__fish_git_using_command pull' -l ff-only -d 'Refuse to merge unless fast-forward possible' +complete -f -c git -n '__fish_git_using_command pull' -s S -l gpg-sign -d 'GPG-sign the merge commit' +complete -f -c git -n '__fish_git_using_command pull' -l log -d 'Populate the log message with one-line descriptions' +complete -f -c git -n '__fish_git_using_command pull' -l no-log -d "Don't populate the log message with one-line descriptions" +complete -f -c git -n '__fish_git_using_command pull' -l signoff -d 'Add Signed-off-by line at the end of the merge commit message' +complete -f -c git -n '__fish_git_using_command pull' -l no-signoff -d 'Do not add a Signed-off-by line at the end of the merge commit message' +complete -f -c git -n '__fish_git_using_command pull' -l stat -d "Show diffstat of the merge" +complete -f -c git -n '__fish_git_using_command pull' -s n -l no-stat -d "Don't show diffstat of the merge" +complete -f -c git -n '__fish_git_using_command pull' -l squash -d "Squash changes from upstream branch as a single commit" +complete -f -c git -n '__fish_git_using_command pull' -l no-squash -d "Don't squash changes" +complete -x -c git -n '__fish_git_using_command pull' -s s -l strategy -d 'Use the given merge strategy' +complete -r -c git -n '__fish_git_using_command pull' -s X -l strategy-option -d 'Pass given option to the merge strategy' +complete -f -c git -n '__fish_git_using_command pull' -l verify-signatures -d 'Abort merge if upstream branch tip commit is not signed with a valid key' +complete -f -c git -n '__fish_git_using_command pull' -l no-verify-signatures -d 'Do not abort merge if upstream branch tip commit is not signed with a valid key' +complete -f -c git -n '__fish_git_using_command pull' -l allow-unrelated-histories -d 'Allow merging even when branches do not share a common history' +complete -f -c git -n '__fish_git_using_command pull' -s r -l rebase -d 'Rebase the current branch on top of the upstream branch' +complete -f -c git -n '__fish_git_using_command pull' -l no-rebase -d 'Do not rebase the current branch on top of the upstream branch' +complete -f -c git -n '__fish_git_using_command pull' -l autostash -d 'Before starting rebase, stash local changes, and apply stash when done' +complete -f -c git -n '__fish_git_using_command pull' -l no-autostash -d 'Do not stash local changes before starting rebase' # TODO other options ### push From 503427255e6a4abade0b3490f25bd0b323d2429a Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 19 Apr 2018 08:15:50 -0500 Subject: [PATCH 089/159] Drop automation of `all-the-package-names` install for npm completions Selectively reverts 156d4fb9b9b83546ff891e14216330648cd71a07. `all-the-package-names` is still used to generate completions for `npm` if it is installed, but it is not manually installed nor updated. It is now the user's responsibility to do both, and it must be installed globally. --- CHANGELOG.md | 5 ++++- share/completions/npm.fish | 27 +++++---------------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c374498c..249c60d8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,8 +69,11 @@ This section is for changes merged to the `major` branch that are not also merge - `brew` - `diskutil` - `git` (#4395, #4396, #4592) + - `npm` - `ssh` (#4344) - - `yarn` + - `yarn` + +_† for autocompletion of available packages for installation via `npm` or `yarn`, make sure `all-the-package-names` is installed (typically: `sudo npm install -g all-the-package-names`)._ -- diff --git a/share/completions/npm.fish b/share/completions/npm.fish index 0ae9dcdc2..ff9731c8d 100644 --- a/share/completions/npm.fish +++ b/share/completions/npm.fish @@ -4,31 +4,14 @@ # see also Fish's large set of completions for examples: # https://github.com/fish-shell/fish-shell/tree/master/share/completions -set -g __fish_atpm_cache $HOME/.cache/fish/npm_completions - -function __install_atpm - if not test -d $__fish_atpm_cache - mkdir -p $__fish_atpm_cache - fish -c "cd $HOME/.cache/fish/npm_completions; npm init -y >/dev/null; npm install all-the-package-names >/dev/null; touch .last_update" >/dev/null 2>/dev/null - end -end - -function __update_atpm - if test \"(find $__fish_atpm_cache/ -name .last_update -mtime +1 -print)\" - # all-the-package-names is out of date - fish -c "cd $__fish_atpm_cache; npm update; touch .last_update" >/dev/null 2>/dev/null - end -end - +# If all-the-package-names is installed, it will be used to generate npm completions. +# Install globally with `sudo npm install -g all-the-package-names`. Keep it up to date. function __npm_list_packages - if not test -x $__fish_atpm_cache/node_modules/.bin/all-the-package-names - if not __install_atpm >/dev/null - return - end + if not type -q all-the-package-names + return end - __update_atpm >/dev/null - eval $__fish_atpm_cache/node_modules/.bin/all-the-package-names + all-the-package-names end # Entire list of packages is too long to be used in a `complete` subcommand From 77134fc49d68984cf12974d02d45f39edd313109 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 19 Apr 2018 08:24:20 -0500 Subject: [PATCH 090/159] Use `all-the-package-names` to complete `yarn add` --- share/completions/yarn.fish | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index 0556821a4..52eee5a1f 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -2,6 +2,22 @@ # see https://github.com/fish-shell/fish-shell/blob/master/share/functions/__fish_seen_subcommand_from.fish # and https://github.com/fish-shell/fish-shell/blob/master/share/functions/__fish_use_subcommand.fish +# If all-the-package-names is installed, it will be used to generate npm completions. +# Install globally with `sudo npm install -g all-the-package-names`. Keep it up to date. +function __yarn_list_packages + if not type -q all-the-package-names + return + end + + all-the-package-names +end + +# Entire list of packages is too long to be used in a `complete` subcommand +# Search it for matches instead +function __yarn_filtered_list_packages + __yarn_list_packages | grep (commandline -ct) | head -n 50 +end + function __yarn_find_package_json set parents (__fish_parent_directories (pwd)) @@ -15,7 +31,7 @@ function __yarn_find_package_json return 1 end -function __yarn_list_packages +function __yarn_installed_packages set -l package_json (__yarn_find_package_json) if not test $status -eq 0 # no package.json in tree @@ -49,7 +65,8 @@ function __yarn_list_packages end -complete -f -c yarn -n '__fish_seen_subcommand_from remove' -a '(__yarn_list_packages)' +complete -f -c yarn -n '__fish_seen_subcommand_from remove' -a '(__yarn_installed_packages)' +complete -f -c yarn -n '__fish_seen_subcommand_from add' -a '(__yarn_filtered_list_packages)' complete -f -c yarn -n '__fish_use_subcommand' -a help From c31861fdd1467721400c98c3201e1b35744f1b7f Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 19 Apr 2018 08:37:43 -0500 Subject: [PATCH 091/159] Add note about `read -L` to CHANGELOG.md` --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 249c60d8b..331dac0c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ This section is for changes merged to the `major` branch that are not also merge - `wait` builtin is added for waiting on processes (#4498). - `read` has a new `--delimiter` option as a better alternative to the `IFS` variable (#4256). - `read` writes directly to stdout if called without arguments (#4407) +- `read` can now read one or more individual lines from the input stream without consuming the input in its entirety via `read -L/--line`. Refer to the `read` documentation for more info. - `set` has a new `--append` and `--prepend` option (#1326). - `set` has a new `--show` option to show lots of information about variables (#4265). - `complete` now has a `-k` and `--keep-order` option to keep the order of the `OPTION_ARGUMENTS` (#361). From 342d0644acd170793724b91ef47fd711007f4332 Mon Sep 17 00:00:00 2001 From: George Christou Date: Thu, 19 Apr 2018 14:00:35 +0100 Subject: [PATCH 092/159] completions: [git] Don't try to complete files when not in a repo --- share/completions/git.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/completions/git.fish b/share/completions/git.fish index 7319f59a2..424de7129 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -71,6 +71,8 @@ function __fish_git_files # Save the repo root to remove it from the path later. set -l root (command git rev-parse --show-toplevel 2>/dev/null) + # Do not continue if not inside a Git repository + or return # Cache the translated descriptions so we don't have to get it # once per file. From 7db09588046a07fbf8e8c25e5dcc3317b32c7842 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 19 Apr 2018 17:52:20 -0500 Subject: [PATCH 093/159] Add `__fish_can_complete_switches` & `__fish_should_complete_switches` To be used by completions to directly determine whether it is either possible or preferable to complete a switch (instead of a subcommand), (presuming that switches must come before subcommands). * __fish_can_complete_switches: we are in a position where a switch may be placed. * __fish_should_complete_switches: we're in a position to accept a switch and the current token starts with `-` so we have no choice but to do so. --- share/functions/__fish_can_complete_switches.fish | 14 ++++++++++++++ .../functions/__fish_should_complete_switches.fish | 12 ++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 share/functions/__fish_can_complete_switches.fish create mode 100644 share/functions/__fish_should_complete_switches.fish diff --git a/share/functions/__fish_can_complete_switches.fish b/share/functions/__fish_can_complete_switches.fish new file mode 100644 index 000000000..9354f0892 --- /dev/null +++ b/share/functions/__fish_can_complete_switches.fish @@ -0,0 +1,14 @@ +# Returns whether it is at all possible (even if not recommended) +# to complete a -s or --long argument. +function __fish_can_complete_switches + # Search backwards + for arg in (commandline -ct)[-1..1] + if test "$arg" = "" + continue + else if not string match -qr -- "^-\S*\$" "$arg" + return 1 + end + end + + return 0 +end diff --git a/share/functions/__fish_should_complete_switches.fish b/share/functions/__fish_should_complete_switches.fish new file mode 100644 index 000000000..876a6a673 --- /dev/null +++ b/share/functions/__fish_should_complete_switches.fish @@ -0,0 +1,12 @@ +# Returns whether we *should* complete a -s or --long argument. +# The preference is NOT to do so, i.e. prefer subcommands over switches. +function __fish_should_complete_switches + if not __fish_can_complete_switches + return 1 + end + if string match -qr -- "^-" (commandline -ct)[-1] + return 0 + end + + return 1 +end From e48722f0bf6eb91055d451fb6c6b9655ff7a2aca Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 19 Apr 2018 18:08:22 -0500 Subject: [PATCH 094/159] Add completions for `bower` These are "true" completions, with dynamic completion of available packages to be installed or removed. --- CHANGELOG.md | 6 ++-- share/completions/bower.fish | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 share/completions/bower.fish diff --git a/CHANGELOG.md b/CHANGELOG.md index 331dac0c4..33de6cd20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ This section is for changes merged to the `major` branch that are not also merge - Command substitution output is now limited to 10 MB by default (#3822). - Added completions for - `bd` (#4472) + - `bower` - `configure` - `j` (autojump #4344) - `jhipster` (#4472) @@ -72,9 +73,10 @@ This section is for changes merged to the `major` branch that are not also merge - `git` (#4395, #4396, #4592) - `npm` - `ssh` (#4344) - - `yarn` + - `yarn` -_† for autocompletion of available packages for installation via `npm` or `yarn`, make sure `all-the-package-names` is installed (typically: `sudo npm install -g all-the-package-names`)._ +∗ _`jq` must be installed to complete the list of currently installed `bower` or `yarn` packages_.
+† _to autocomplete the list of packages available for installation with `npm` or `yarn`, `all-the-package-names` must be installed (typically: `sudo npm install -g all-the-package-names`)._ -- diff --git a/share/completions/bower.fish b/share/completions/bower.fish new file mode 100644 index 000000000..e695f6b01 --- /dev/null +++ b/share/completions/bower.fish @@ -0,0 +1,63 @@ +function __bower_cmds + echo -en "cache\tManage bower cache +help\tDisplay help information about Bower +home\tOpens a package homepage into your favorite browser +info\tInfo of a particular package +init\tInteractively create a bower.json file +install\tInstall a package locally +link\tSymlink a package folder +list\tList local packages - and possible updates +login\tAuthenticate with GitHub and store credentials +lookup\tLook up a single package URL by name +prune\tRemoves local extraneous packages +register\tRegister a package +search\tSearch for packages by name +update\tUpdate a local package +uninstall\tRemove a local package +unregister\tRemove a package from the registry +version\tBump a package version +" +end + +function __bower_args + echo -en "-f\tMakes various commands more forceful +--force\tMakes various commands more forceful +-j\tOutput consumable JSON +--json\tOutput consumable JSON +-l\tWhat level of logs to report +--loglevel\tWhat level of logs to report +-o\tDo not hit the network +--offline\tDo not hit the network +-q\tOnly output important information +--quiet\tOnly output important information +-s\tDo not output anything, besides errors +--silent\tDo not output anything, besides errors +-V\tMakes output more verbose +--verbose\tMakes output more verbose +--allow-root\tAllows running commands as root +-v\tOutput Bower version +--version\tOutput Bower version +--no-color\tDisable colors" +end + +function __bower_matching_pkgs + bower search (commandline -ct) | string match -r "\S+[^\s]" | string match -v "Search" +end + +# Output of `bower list` is a) slow, b) convoluted. Use `jq` instead. +function __bower_list_installed + if not type -q jq + return 1 + end + + if not test -e bower.json + return 1 + end + + jq -r '.dependencies | to_entries[] | .key' bower.json +end + +complete -c bower -n "__fish_is_first_token" -x -a '(__bower_cmds)' +complete -c bower -n "__fish_should_complete_args" -x -a '(__bower_args)' +complete -c bower -n "__fish_seen_subcommand_from install" -x -a '(__bower_matching_pkgs)' +complete -c bower -n "__fish_seen_subcommand_from uninstall" -x -a '(__bower_list_installed)' From 5f82cb5ca4aab947c4f776279a39462c776683cf Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 19 Apr 2018 18:15:33 -0500 Subject: [PATCH 095/159] Use `jq` to iterate over installed dependencies for `yarn` completions Only if available, else fall back to fragile package.json parsing. --- share/completions/yarn.fish | 45 ++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index 52eee5a1f..e1732f61a 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -38,34 +38,37 @@ function __yarn_installed_packages return 1 end - # todo: if jq exists, use it instead and use what's below as a fallback only - set -l depsFound 0 - for line in (cat $package_json) - # echo "evaluating $line" - if test $depsFound -eq 0 - # echo "mode: noDeps" - if string match -qr '(devD|d)ependencies"' -- $line - # echo "switching to mode: deps" - set depsFound 1 + if type -q jq + jq -r '.dependencies | to_entries[] | .key' bower.json + else + set -l depsFound 0 + for line in (cat $package_json) + # echo "evaluating $line" + if test $depsFound -eq 0 + # echo "mode: noDeps" + if string match -qr '(devD|d)ependencies"' -- $line + # echo "switching to mode: deps" + set depsFound 1 + continue + end continue end - continue + + if string match -qr '\}' -- $line + # echo "switching to mode: noDeps" + set depsFound 0 + continue + end + + # echo "mode: deps" + + string replace -r '^\s*"([^"]+)".*' '$1' -- $line end - - if string match -qr '\}' -- $line - # echo "switching to mode: noDeps" - set depsFound 0 - continue - end - - # echo "mode: deps" - - string replace -r '^\s*"([^"]+)".*' '$1' -- $line end end -complete -f -c yarn -n '__fish_seen_subcommand_from remove' -a '(__yarn_installed_packages)' +complete -f -c yarn -n '__fish_seen_subcommand_from remove' -xa '(__yarn_installed_packages)' complete -f -c yarn -n '__fish_seen_subcommand_from add' -a '(__yarn_filtered_list_packages)' complete -f -c yarn -n '__fish_use_subcommand' -a help From b619f34777efe9e7cee55876738ff65e89088bc6 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Thu, 19 Apr 2018 18:26:47 -0500 Subject: [PATCH 096/159] Fix `ngrok` and `port` appearing on same line in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33de6cd20..544cecbfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,7 +66,7 @@ This section is for changes merged to the `major` branch that are not also merge - `j` (autojump #4344) - `jhipster` (#4472) - `ngrok` (#4642) -  - `port` + - `port` - Improved completions for - `brew` - `diskutil` From a73329698092ca610813da81dac1c595e26ff26e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Fri, 20 Apr 2018 09:12:34 -0500 Subject: [PATCH 097/159] Correct bower and yarn completions bower was calling `__fish_should_complete_args`, the old name for `__fish_should_complete_switches.` yarn was parsing bower.json instead of package.json. --- share/completions/bower.fish | 2 +- share/completions/yarn.fish | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/completions/bower.fish b/share/completions/bower.fish index e695f6b01..d4c45c949 100644 --- a/share/completions/bower.fish +++ b/share/completions/bower.fish @@ -58,6 +58,6 @@ function __bower_list_installed end complete -c bower -n "__fish_is_first_token" -x -a '(__bower_cmds)' -complete -c bower -n "__fish_should_complete_args" -x -a '(__bower_args)' +complete -c bower -n "__fish_should_complete_switches" -x -a '(__bower_args)' complete -c bower -n "__fish_seen_subcommand_from install" -x -a '(__bower_matching_pkgs)' complete -c bower -n "__fish_seen_subcommand_from uninstall" -x -a '(__bower_list_installed)' diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index e1732f61a..1564ad644 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -39,7 +39,7 @@ function __yarn_installed_packages end if type -q jq - jq -r '.dependencies | to_entries[] | .key' bower.json + jq -r '.dependencies | to_entries[] | .key' $package_json else set -l depsFound 0 for line in (cat $package_json) From a9e9af5c5d97fae689dcdf9f3bab9689b22805a2 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Fri, 20 Apr 2018 23:05:31 -0500 Subject: [PATCH 098/159] Include devDependencies in yarn and npm completions --- share/completions/yarn.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/yarn.fish b/share/completions/yarn.fish index 1564ad644..70e4acd2a 100644 --- a/share/completions/yarn.fish +++ b/share/completions/yarn.fish @@ -39,7 +39,7 @@ function __yarn_installed_packages end if type -q jq - jq -r '.dependencies | to_entries[] | .key' $package_json + jq -r '.dependencies as $a1 | .devDependencies as $a2 | ($a1 + $a2) | to_entries[] | .key' $package_json else set -l depsFound 0 for line in (cat $package_json) From 8f526c0876f4740c202b026e47d1d172ee9530dc Mon Sep 17 00:00:00 2001 From: Pavel Sviderski Date: Sun, 8 Apr 2018 22:52:00 +0300 Subject: [PATCH 099/159] add try in browser button to launch fish playground --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 610f377b1..9be873761 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ fish generally works like other shells, like bash or zsh. A few important differ Detailed user documentation is available by running `help` within fish, and also at +You can quickly play with fish right in your browser by clicking the button below: + +[![Try in browser](https://cdn.rawgit.com/rootnroll/library/assets/try.svg)](https://rootnroll.com/d/fish-shell/) + ## Getting fish ### macOS From 0fdc51beede0a75e1d1385e48c255e928487c5f3 Mon Sep 17 00:00:00 2001 From: George Christou Date: Sun, 22 Apr 2018 13:03:20 +0100 Subject: [PATCH 100/159] completions: [git] Add options for `diff` and `show` --- share/completions/git.fish | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 424de7129..368bc0655 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -400,6 +400,21 @@ all\tShorthand for 'old,new,context'" end end +function __fish_git_show_opt -a option + switch $option + case format pretty + printf "%b" " +oneline\t +short\t<sha1> / <author> / <title line> +medium\t<sha1> / <author> / <author date> / <title> / <commit msg> +full\t<sha1> / <author> / <committer> / <title> / <commit msg> +fuller\t<sha1> / <author> / <author date> / <committer> / <committer date> / <title> / <commit msg> +email\t<sha1> <date> / <author> / <author date> / <title> / <commit msg> +raw\tShow the entire commit exactly as stored in the commit object +format:\tSpecify which information to show" + end +end + # general options complete -f -c git -l help -d 'Display the manual of a git command' complete -f -c git -n '__fish_git_needs_command' -l version -d 'Display version' @@ -422,7 +437,7 @@ complete -f -c git -n '__fish_git_needs_command' -l noglob-pathspecs -d "Don't t complete -f -c git -n '__fish_git_needs_command' -l icase-pathspecs -d 'Match pathspecs case-insensitively' # Options shared between multiple commands -complete -f -c git -n '__fish_git_using_command log show diff-tree rev-list' -l pretty -a 'oneline short medium full fuller email raw format:' +complete -f -c git -n '__fish_git_using_command log show diff-tree rev-list' -l pretty -a '(__fish_git_show_opt pretty)' complete -c git -n '__fish_git_using_command diff show' -l abbrev -d 'Show only a partial prefix instead of the full 40-byte hexadecimal object name' complete -c git -n '__fish_git_using_command diff show' -l binary -d 'Output a binary diff that can be applied with "git-apply"' @@ -546,8 +561,17 @@ complete -f -c git -n '__fish_git_needs_command' -a show -d 'Shows the last comm complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_branches)' complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_tags)' -d 'Tag' complete -f -c git -n '__fish_git_using_command show' -a '(__fish_git_commits)' -complete -f -c git -n '__fish_git_using_command show' -l stat -d 'Generate a diffstat, showing the number of changed lines of each file' -# TODO options +complete -f -c git -n '__fish_git_using_command show' -l format -d 'Pretty-print the contents of the commit logs in a given format' -a '(__fish_git_show_opt format)' +complete -f -c git -n '__fish_git_using_command show' -l abbrev-commit -d 'Show only a partial hexadecimal commit object name' +complete -f -c git -n '__fish_git_using_command show' -l no-abbrev-commit -d 'Show the full 40-byte hexadecimal commit object name' +complete -f -c git -n '__fish_git_using_command show' -l oneline -d 'Shorthand for "--pretty=oneline --abbrev-commit"' +complete -f -c git -n '__fish_git_using_command show' -l encoding -d 'Re-code the commit log message in the encoding' +complete -f -c git -n '__fish_git_using_command show' -l expand-tabs -d 'Perform a tab expansion in the log message' +complete -f -c git -n '__fish_git_using_command show' -l no-expand-tabs -d 'Do not perform a tab expansion in the log message' +complete -f -c git -n '__fish_git_using_command show' -l notes -a '(__fish_git_refs)' -d 'Show the notes that annotate the commit' +complete -f -c git -n '__fish_git_using_command show' -l no-notes -d 'Do not show notes' +complete -f -c git -n '__fish_git_using_command show' -l show-signature -d 'Check the validity of a signed commit object' + ### show-branch complete -f -c git -n '__fish_git_needs_command' -a show-branch -d 'Shows the commits on branches' @@ -648,8 +672,13 @@ complete -c git -n '__fish_git_needs_command' -a diff -d 'Show changes between c complete -c git -n '__fish_git_using_command diff' -a '(__fish_git_ranges)' complete -c git -n '__fish_git_using_command diff' -l cached -d 'Show diff of changes in the index' complete -c git -n '__fish_git_using_command diff' -l no-index -d 'Compare two paths on the filesystem' +complete -c git -n '__fish_git_using_command diff' -l exit-code -d 'Exit with 1 if there were differences or 0 if no differences' +complete -c git -n '__fish_git_using_command diff' -s q -l quiet -d 'Disable all output of the program, implies --exit-code' +complete -c git -n '__fish_git_using_command diff' -s 1 -l base -d 'Compare the working tree with the "base" version' +complete -c git -n '__fish_git_using_command diff' -s 2 -l ours -d 'Compare the working tree with the "our branch"' +complete -c git -n '__fish_git_using_command diff' -s 3 -l theirs -d 'Compare the working tree with the "their branch"' +complete -c git -n '__fish_git_using_command diff' -s 0 -d 'Omit diff output for unmerged entries and just show "Unmerged"' complete -f -c git -n '__fish_git_using_command diff' -a '(__fish_git_files modified deleted)' -# TODO options ### difftool complete -c git -n '__fish_git_needs_command' -a difftool -d 'Open diffs in a visual tool' From 4d864aea1c6fbd0e8e25055c06e4b9449708f14b Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Sun, 15 Apr 2018 18:58:53 +0200 Subject: [PATCH 101/159] remove sed from __fish_print_addresses.fish --- share/functions/__fish_print_addresses.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_print_addresses.fish b/share/functions/__fish_print_addresses.fish index 356c8559c..1bb6cd7a7 100644 --- a/share/functions/__fish_print_addresses.fish +++ b/share/functions/__fish_print_addresses.fish @@ -1,6 +1,6 @@ function __fish_print_addresses --description "Print a list of known network addresses" if command -sq ip - command ip --oneline address | cut -d" " -f7 | sed "s:\(.*\)/.*:\1:" + command ip --oneline address | string replace -r '(\S+\s+){3}(.+)/.*' '$2' else if command -sq ifconfig # This is for OSX/BSD # There's also linux ifconfig but that has at least two different output formats From 83637e2178e996fcf30358bc4b982e314db4368d Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Sun, 15 Apr 2018 18:59:35 +0200 Subject: [PATCH 102/159] remove sed from __fish_complete_groups.fish --- share/functions/__fish_complete_groups.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/__fish_complete_groups.fish b/share/functions/__fish_complete_groups.fish index 051f66663..523412d5a 100644 --- a/share/functions/__fish_complete_groups.fish +++ b/share/functions/__fish_complete_groups.fish @@ -1,8 +1,8 @@ function __fish_complete_groups --description "Print a list of local groups, with group members as the description" if command -sq getent - getent group | cut -d ':' -f 1,4 | sed 's/:/\t/' + getent group | cut -d ':' -f 1,4 | string replace : \t else - cut -d ':' -f 1,4 /etc/group | sed 's/:/\t/' + cut -d ':' -f 1,4 /etc/group | string replace : \t end end From 374772e539b1bd7818ac3247c277e419ad30937b Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Sun, 15 Apr 2018 18:59:57 +0200 Subject: [PATCH 103/159] remove sed from __fish_print_xrandr_outputs.fish --- share/functions/__fish_print_xrandr_outputs.fish | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/share/functions/__fish_print_xrandr_outputs.fish b/share/functions/__fish_print_xrandr_outputs.fish index 61bb73628..43614a996 100644 --- a/share/functions/__fish_print_xrandr_outputs.fish +++ b/share/functions/__fish_print_xrandr_outputs.fish @@ -1,5 +1,3 @@ function __fish_print_xrandr_outputs --description 'Print xrandr outputs' - xrandr | sed '/^Screen\|^ /d; s/^\(\S\+\) \+\(.\+\)/\1\t\2/' - - + xrandr | string replace -r --filter '^(\S+)\s+(.*)$' '$1\t$2' | string match -v -e Screen end From 70c80c9d0e8559986c63c0b0d2130313311e0967 Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Sun, 15 Apr 2018 20:08:59 +0200 Subject: [PATCH 104/159] fix and remove sed from __fish_print_xrandr_modes.fish --- .../functions/__fish_print_xrandr_modes.fish | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/share/functions/__fish_print_xrandr_modes.fish b/share/functions/__fish_print_xrandr_modes.fish index dd5ebb70b..0023abe54 100644 --- a/share/functions/__fish_print_xrandr_modes.fish +++ b/share/functions/__fish_print_xrandr_modes.fish @@ -1,13 +1,11 @@ function __fish_print_xrandr_modes --description 'Print xrandr modes' - set -l out - xrandr | sed '/^Screen/d; s/^ \+/mode: /' | while read -l output mode misc - switch $output - case mode: - echo $mode\t(echo $misc | sed 's/\(^ \+\)\|\( *$\)//') [$out] - case '*' - set out $output - end - end - - + set -l output + xrandr | string match -v -r '^(Screen|\s{4,})' | while read line + switch $line + case ' *' + string trim $line | string replace -r '^(\S+)\s+(.*)$' "\$1\t\$2 [$output]" + case '*' + set output (string match -r '^\S+' $line) + end + end end From 02ae926c8e29ed55ab0ff156a75aed9e0b7d26da Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Sun, 15 Apr 2018 20:52:51 +0200 Subject: [PATCH 105/159] remove sed (and awk) from gentoo-portage completions --- share/functions/__fish_portage_print_repository_names.fish | 2 +- share/functions/__fish_portage_print_repository_paths.fish | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/share/functions/__fish_portage_print_repository_names.fish b/share/functions/__fish_portage_print_repository_names.fish index 47e204570..336567ff4 100644 --- a/share/functions/__fish_portage_print_repository_names.fish +++ b/share/functions/__fish_portage_print_repository_names.fish @@ -1,4 +1,4 @@ function __fish_portage_print_repository_names --description 'Print the names of all configured repositories' # repos.conf may be a file or a directory - find /etc/portage/repos.conf -type f -exec sed -ne '/^[[:blank:]]*\[[[:alnum:]_][[:alnum:]_-]*\]/{s!^.*\[\(.*\)\].*$!\1!; /DEFAULT/!p}' '{}' + + find /etc/portage/repos.conf -type f -exec cat '{}' + | string replace -r --filter '^\s*\[([[:alnum:]_][[:alnum:]_-]*)\]' '$1' | string match -v -e DEFAULT end diff --git a/share/functions/__fish_portage_print_repository_paths.fish b/share/functions/__fish_portage_print_repository_paths.fish index cfe23f715..48dd738a5 100644 --- a/share/functions/__fish_portage_print_repository_paths.fish +++ b/share/functions/__fish_portage_print_repository_paths.fish @@ -1,4 +1,4 @@ function __fish_portage_print_repository_paths --description 'Print the paths of all configured repositories' # repos.conf may be a file or a directory - find /etc/portage/repos.conf -type f -exec awk 'BEGIN { FS="[[:blank:]]*=[[:blank:]]*" } $1 == "location" { print $2 }' '{}' + + find /etc/portage/repos.conf -type f -exec cat '{}' + | string replace -r --filter '^\s*location\s*=\s*(\S+)' '$1' end From 809b2cd792a87f572f586584965b8186da76c966 Mon Sep 17 00:00:00 2001 From: nblock <nblock@users.noreply.github.com> Date: Wed, 25 Apr 2018 15:45:48 +0200 Subject: [PATCH 106/159] Add completion for MkDocs (#4906) * Add completion for MkDocs * Use __fish_seen_subcommand_from --- share/completions/mkdocs.fish | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 share/completions/mkdocs.fish diff --git a/share/completions/mkdocs.fish b/share/completions/mkdocs.fish new file mode 100644 index 000000000..182330cda --- /dev/null +++ b/share/completions/mkdocs.fish @@ -0,0 +1,38 @@ +# completion for mkdocs + +complete -f -c mkdocs -s h -l help -d 'Show help and exit' +complete -f -c mkdocs -s v -l verbose -d 'Enable verbose output' +complete -f -c mkdocs -s q -l quiet -d 'Silence warnings' +complete -n 'not __fish_seen_subcommand_from build gh-deploy new serve' -f -c mkdocs -s V -l version -d 'Show the version and exit' + +## build +complete -n 'not __fish_seen_subcommand_from build gh-deploy new serve' -f -c mkdocs -a 'build' -d 'Build the MkDocs documentation' +complete -n 'contains build (commandline -poc)' -f -c mkdocs -s c -l clean -d 'Remove old site_dir before building (the default)' +complete -n 'contains build (commandline -poc)' -c mkdocs -s f -l config-file -r -d 'Provide a specific MkDocs config' +complete -n 'contains build (commandline -poc)' -f -c mkdocs -s s -l strict -d 'Enable strict mode. This will cause MkDocs to abort the build on any warnings' +complete -n 'contains build (commandline -poc)' -c mkdocs -s t -l theme -d 'The theme to use when building your documentation' -xa 'mkdocs readthedocs material' +complete -n 'contains build (commandline -poc)' -c mkdocs -s e -l theme-dir -r -d 'The theme directory to use when building your documentation' +complete -n 'contains build (commandline -poc)' -c mkdocs -s d -l site-dir -r -d 'The directory to output the result of the documentation build' + +## gh-deploy +complete -n 'not __fish_seen_subcommand_from build gh-deploy new serve' -f -c mkdocs -a 'gh-deploy' -d 'Deploy your documentation to GitHub Pages' +complete -n 'contains gh-deploy (commandline -poc)' -f -c mkdocs -s c -l clean -d 'Remove old site_dir before building (the default)' +complete -n 'contains gh-deploy (commandline -poc)' -c mkdocs -s f -l config-file -r -d 'Provide a specific MkDocs config' +complete -n 'contains gh-deploy (commandline -poc)' -f -c mkdocs -s m -l message -r -d 'A commit message to use when commiting to the Github Pages remote branch' +complete -n 'contains gh-deploy (commandline -poc)' -f -c mkdocs -s b -l remote-branch -r -d 'The remote branch to commit to for Github Pages' +complete -n 'contains gh-deploy (commandline -poc)' -f -c mkdocs -s r -l remote-name -r -d 'The remote name to commit to for Github Pages' +complete -n 'contains gh-deploy (commandline -poc)' -f -c mkdocs -l force -d 'Force the push to the repository' + +## new +complete -n 'not __fish_seen_subcommand_from build gh-deploy new serve' -f -c mkdocs -a 'new' -r -d 'Create a new MkDocs project' + +## serve +complete -n 'not __fish_seen_subcommand_from build gh-deploy new serve' -f -c mkdocs -a 'serve' -d 'Run the builtin development server' +complete -n 'contains serve (commandline -poc)' -c mkdocs -s f -l config-file -r -d 'Provide a specific MkDocs config' +complete -n 'contains serve (commandline -poc)' -c mkdocs -s a -l dev-addr -r -d 'IP address and port to serve documentation locally (default: localhost:8000)' +complete -n 'contains serve (commandline -poc)' -f -c mkdocs -s s -l strict -d 'Enable strict mode. This will cause MkDocs to abort the build on any warnings' +complete -n 'contains serve (commandline -poc)' -c mkdocs -s t -l theme -d 'The theme to use when building your documentation' -xa 'mkdocs readthedocs material' +complete -n 'contains serve (commandline -poc)' -c mkdocs -s e -l theme-dir -r -d 'The theme directory to use when building your documentation' +complete -n 'contains serve (commandline -poc)' -f -c mkdocs -l livereload -d 'Enable the live reloading in the development server (this is the default)' +complete -n 'contains serve (commandline -poc)' -f -c mkdocs -l no-livereload -d 'Disable the live reloading in the development server' +complete -n 'contains serve (commandline -poc)' -f -c mkdocs -l dirtyreload -d 'Enable the live reloading in the development server, but only re-build files that have changed' From 92fa94aa099ea9e61ed4a1b86dfd59d0ebe1886d Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Fri, 6 Apr 2018 12:10:44 +0200 Subject: [PATCH 107/159] Improve equery completion - fix capitalization - shorten descriptions - implement subcommand shortcuts - add arg completion for 'limit' and 'depth' switches - improve arg completion for list subcommand in case of -p switch --- share/completions/equery.fish | 159 ++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 75 deletions(-) diff --git a/share/completions/equery.fish b/share/completions/equery.fish index f04c6fab4..53afc1b94 100644 --- a/share/completions/equery.fish +++ b/share/completions/equery.fish @@ -1,18 +1,18 @@ # TODO unused function __fish_equery_print_format printf "%s\t%s\n" \ - '$cp' "contains the category and the package name only (e.g 'app-portage/gentoolkit')" \ - '$cpv' "contains the category, the package name and the full version (e.g. 'app-portage/gentoolkit-0.3.0_rc10-r1')" \ - '$category' "contains just the category (e.g. 'app-portage')" \ - '$name' "contains just the package name (e.g. 'gentoolkit')" \ - '$version' "contains the package version (without the revision) (e.g. '0.3.0_rc10')" \ - '$revision' "contains the package revision (e.g. 'r1')" \ - '$fullversion' "contains the package version including its revision (e.g. '0.3.0_rc10-r1')" \ - '$slot' "contains the package's slot" \ - '$repo' "contains the name of the package's repository (e.g. 'gentoo')" \ - '$mask' "the mask-status field (~M-??)" \ - '$mask2' "contains a verbose description of the packages masking status" \ - '$location' "the location field (IPO-)" + '$cp' "Category and package name (e.g 'app-portage/gentoolkit')" \ + '$cpv' "Category, package name and version (e.g. 'app-portage/gentoolkit-0.3.0_rc10-r1')" \ + '$category' "Category (e.g. 'app-portage')" \ + '$name' "Package name (e.g. 'gentoolkit')" \ + '$version' "Version (without the revision) (e.g. '0.3.0_rc10')" \ + '$revision' "Revision (e.g. 'r1')" \ + '$fullversion' "Version including revision (e.g. '0.3.0_rc10-r1')" \ + '$slot' "Slot" \ + '$repo' "Repository (e.g. 'gentoo')" \ + '$mask' "Mask-status field (~M-??)" \ + '$mask2' "Verbose description of the masking status" \ + '$location' "Location field (IPO-)" end ## Global Opts @@ -23,101 +23,110 @@ complete -c equery -s N -l no-pipe -d "Turns off pipe detection" complete -c equery -s V -l version -d "Display version information" ## Subcommands -complete -c equery -n '__fish_use_subcommand' -xa 'belongs' -d "List all packages owning file(s)" +complete -c equery -n '__fish_use_subcommand' -xa 'belongs' -d "List all pkgs owning file(s)" complete -c equery -n '__fish_use_subcommand' -xa 'changes' -d "List changelog entries for ATOM" -complete -c equery -n '__fish_use_subcommand' -xa 'check' -d "Check MD5sums and timestamps of package" -complete -c equery -n '__fish_use_subcommand' -xa 'depends' -d "List all packages depending on specified package" -complete -c equery -n '__fish_use_subcommand' -xa 'depgraph' -d "Display a dependency tree for package" -complete -c equery -n '__fish_use_subcommand' -xa 'files' -d "List files owned by package" +complete -c equery -n '__fish_use_subcommand' -xa 'check' -d "Check pkg's MD5sums and timestamps" +complete -c equery -n '__fish_use_subcommand' -xa 'depends' -d "List all pkgs depending on specified pkg" +complete -c equery -n '__fish_use_subcommand' -xa 'depgraph' -d "Display pkg's dependency tree" +complete -c equery -n '__fish_use_subcommand' -xa 'files' -d "List files owned by pkg" complete -c equery -n '__fish_use_subcommand' -xa 'has' -d "List pkgs for matching ENVIRONMENT data" complete -c equery -n '__fish_use_subcommand' -xa 'hasuse' -d "List pkgs with specified useflag" -complete -c equery -n '__fish_use_subcommand' -xa 'keywords' -d "Display keywords for specified PKG" -complete -c equery -n '__fish_use_subcommand' -xa 'list' -d "List all packages matching pattern" -complete -c equery -n '__fish_use_subcommand' -xa 'meta' -d "Display metadata about PKG" -complete -c equery -n '__fish_use_subcommand' -xa 'size' -d "Print size of files contained in package" -complete -c equery -n '__fish_use_subcommand' -xa 'uses' -d "Display USE flags for package" -complete -c equery -n '__fish_use_subcommand' -xa 'which' -d "Print full path to ebuild for package" +complete -c equery -n '__fish_use_subcommand' -xa 'keywords' -d "Display pkg's keywords" +complete -c equery -n '__fish_use_subcommand' -xa 'list' -d "List all pkgs matching pattern" +complete -c equery -n '__fish_use_subcommand' -xa 'meta' -d "Display pkg's metadata" +complete -c equery -n '__fish_use_subcommand' -xa 'size' -d "Print size of files contained in pkg" +complete -c equery -n '__fish_use_subcommand' -xa 'uses' -d "Display pkg's USE flags" +complete -c equery -n '__fish_use_subcommand' -xa 'which' -d "Print full path to ebuild for pkg" ## Arguments -complete -c equery -n '__fish_seen_subcommand_from changes depends depgraph meta which' -xa '(__fish_portage_print_available_pkgs)' -complete -c equery -n '__fish_seen_subcommand_from check files uses size' -xa '(__fish_portage_print_installed_pkgs)' +complete -c equery -n '__fish_seen_subcommand_from c changes d depends g depgraph m meta w which' \ + -xa '(__fish_portage_print_available_pkgs)' +complete -c equery -n '__fish_seen_subcommand_from k check f files u uses s size' \ + -xa '(__fish_portage_print_installed_pkgs)' ## Local opts # belongs -complete -c equery -n '__fish_seen_subcommand_from belongs' -s f -l full-regex -d "Supplied query is a regex" -complete -c equery -n '__fish_seen_subcommand_from belongs' -s e -l early-out -d "Stop when first match is found" -complete -c equery -n '__fish_seen_subcommand_from belongs' -s n -l name-only -d "Don't print the version" +complete -c equery -n '__fish_seen_subcommand_from b belongs' -s f -l full-regex -d "Supplied query is a regex" +complete -c equery -n '__fish_seen_subcommand_from b belongs' -s e -l early-out -d "Stop after first match" +complete -c equery -n '__fish_seen_subcommand_from b belongs' -s n -l name-only -d "Omit version" # changes -complete -c equery -n '__fish_seen_subcommand_from changes' -s l -l latest -d "Display only the latest ChangeLog entry" -complete -c equery -n '__fish_seen_subcommand_from changes' -s f -l full -d "Display the full ChangeLog" -#complete -c equery -n '__fish_seen_subcommand_from changes' -l limit=NUM -d "Limit the number of entries displayed (with --full)" -#complete -c equery -n '__fish_seen_subcommand_from changes' -l from=VER -d "Set which version to display from" -#complete -c equery -n '__fish_seen_subcommand_from changes' -l to=VER -d "Set which version to display to" +complete -c equery -n '__fish_seen_subcommand_from c changes' -s l -l latest -d "Display only latest ChangeLog entry" +complete -c equery -n '__fish_seen_subcommand_from c changes' -s f -l full -d "Display full ChangeLog" +complete -c equery -n '__fish_seen_subcommand_from c changes' -l limit -d "Limit number of entries displayed (with --full)" \ + -xa "(seq 99)" +#complete -c equery -n '__fish_seen_subcommand_from c changes' -l from=VER -d "Set which version to display from" +#complete -c equery -n '__fish_seen_subcommand_from c changes' -l to=VER -d "Set which version to display to" # check -complete -c equery -n '__fish_seen_subcommand_from check' -s f -l full-regex -d "Query is a regular expression" -complete -c equery -n '__fish_seen_subcommand_from check' -s o -l only-failures -d "Only display packages that do not pass" +complete -c equery -n '__fish_seen_subcommand_from k check' -s f -l full-regex -d "Query is a regular expression" +complete -c equery -n '__fish_seen_subcommand_from k check' -s o -l only-failures -d "Only display pkgs that do not pass" # depends -complete -c equery -n '__fish_seen_subcommand_from depends' -s a -l all-packages -d "Include dependencies that are not installed (slow)" -complete -c equery -n '__fish_seen_subcommand_from depends' -s D -l indirect -d "Search both direct and indirect dependencies" -#complete -c equery -n '__fish_seen_subcommand_from depends' -l depth=N -d "Limit indirect dependency tree to specified depth" +complete -c equery -n '__fish_seen_subcommand_from d depends' -s a -l all-packages -d "Include dependencies that are not installed (slow)" +complete -c equery -n '__fish_seen_subcommand_from d depends' -s D -l indirect -d "Search both direct and indirect dependencies" +complete -c equery -n '__fish_seen_subcommand_from d depends' -l depth -d "Limit indirect dependency tree to specified depth" \ + -xa "(seq 9)" # depgraph -complete -c equery -n '__fish_seen_subcommand_from depgraph' -s A -l no-atom -d "Do not show dependency atom" -complete -c equery -n '__fish_seen_subcommand_from depgraph' -s M -l no-mask -d "Do not show masking status" -complete -c equery -n '__fish_seen_subcommand_from depgraph' -s U -l no-useflags -d "Do not show USE flags" -complete -c equery -n '__fish_seen_subcommand_from depgraph' -s l -l linear -d "Do not format the graph by indenting dependencies" -#complete -c equery -n '__fish_seen_subcommand_from depgraph' -l depth=N -d "Limit dependency graph to specified depth" +complete -c equery -n '__fish_seen_subcommand_from g depgraph' -s A -l no-atom -d "Don't show dependency atom" +complete -c equery -n '__fish_seen_subcommand_from g depgraph' -s M -l no-mask -d "Don't show masking status" +complete -c equery -n '__fish_seen_subcommand_from g depgraph' -s U -l no-useflags -d "Don't show USE flags" +complete -c equery -n '__fish_seen_subcommand_from g depgraph' -s l -l linear -d "Don't indent dependencies" +complete -c equery -n '__fish_seen_subcommand_from g depgraph' -l depth -d "Limit dependency graph to specified depth" \ + -xa "(seq 9)" # files -complete -c equery -n '__fish_seen_subcommand_from files' -s m -l md5sum -d "Include MD5 sum in output" -complete -c equery -n '__fish_seen_subcommand_from files' -s s -l timestamp -d "Include timestamp in output" -complete -c equery -n '__fish_seen_subcommand_from files' -s t -l type -d "Include file type in output" -complete -c equery -n '__fish_seen_subcommand_from files' -l tree -d "Display results in a tree (turns off other options)" +complete -c equery -n '__fish_seen_subcommand_from f files' -s m -l md5sum -d "Include MD5 sum in output" +complete -c equery -n '__fish_seen_subcommand_from f files' -s s -l timestamp -d "Include timestamp in output" +complete -c equery -n '__fish_seen_subcommand_from f files' -s t -l type -d "Include file type in output" +complete -c equery -n '__fish_seen_subcommand_from f files' -l tree -d "Display results in a tree" # TODO comma separated list -complete -c equery -n '__fish_seen_subcommand_from files' -s f -l filter -d "Filter output by file type (comma separated list)" \ +complete -c equery -n '__fish_seen_subcommand_from f files' -s f -l filter -d "Filter output by file type (comma separated list)" \ -xa "dir obj sym dev fifo path conf cmd doc man info" # has + hasuse -complete -c equery -n '__fish_seen_subcommand_from has hasuse' -s I -l exclude-installed -d "Exclude installed packages from search path" -complete -c equery -n '__fish_seen_subcommand_from has hasuse' -s o -l overlay-tree -d "Include overlays in search path" -complete -c equery -n '__fish_seen_subcommand_from has hasuse' -s p -l portage-tree -d "Include entire portage tree in search path" -#complete -c equery -n '__fish_seen_subcommand_from has hasuse' -s F -l format=TMPL -d "Specify a custom output format" +complete -c equery -n '__fish_seen_subcommand_from a has h hasuse' -s I -l exclude-installed -d "Exclude installed pkgs from search path" +complete -c equery -n '__fish_seen_subcommand_from a has h hasuse' -s o -l overlay-tree -d "Include overlays in search path" +complete -c equery -n '__fish_seen_subcommand_from a has h hasuse' -s p -l portage-tree -d "Include entire portage tree in search path" +#complete -c equery -n '__fish_seen_subcommand_from a has h hasuse' -s F -l format=TMPL -d "Specify a custom output format" # keywords # TODO # list -complete -c equery -n '__fish_seen_subcommand_from list' -s d -l duplicates -d "List only installed duplicate packages" -complete -c equery -n '__fish_seen_subcommand_from list' -s b -l binpkgs-missing -d "List only installed packages without a corresponding binary package" -complete -c equery -n '__fish_seen_subcommand_from list' -s f -l full-regex -d "Query is a regular expression" -complete -c equery -n '__fish_seen_subcommand_from list' -s m -l mask-reason -d "Include reason for package mask" -complete -c equery -n '__fish_seen_subcommand_from list' -s I -l exclude-installed -d "Exclude installed packages from output" -complete -c equery -n '__fish_seen_subcommand_from list' -s o -l overlay-tree -d "List packages in overlays" -complete -c equery -n '__fish_seen_subcommand_from list' -s p -l portage-tree -d "List packages in the main portage tree" -#complete -c equery -n '__fish_seen_subcommand_from list' -s F -l format=TMPL -d "Specify a custom output format" +complete -c equery -n '__fish_seen_subcommand_from l list' -s d -l duplicates -d "List only installed duplicate pkgs" +complete -c equery -n '__fish_seen_subcommand_from l list' -s b -l binpkgs-missing -d "List only installed pkgs without a corresponding binary pkg" +complete -c equery -n '__fish_seen_subcommand_from l list' -s f -l full-regex -d "Query is a regular expression" +complete -c equery -n '__fish_seen_subcommand_from l list' -s m -l mask-reason -d "Include reason for pkg mask" +complete -c equery -n '__fish_seen_subcommand_from l list' -s I -l exclude-installed -d "Exclude installed pkgs from output" +complete -c equery -n '__fish_seen_subcommand_from l list' -s o -l overlay-tree -d "List pkgs in overlays" +complete -c equery -n '__fish_seen_subcommand_from l list' -s p -l portage-tree -d "List pkgs in the main portage tree" +#complete -c equery -n '__fish_seen_subcommand_from l list' -s F -l format=TMPL -d "Specify a custom output format" +complete -c equery -n '__fish_seen_subcommand_from l list; and not __fish_contains_opt -s p portage-tree' \ + -xa "(__fish_portage_print_installed_pkgs)" +complete -c equery -n '__fish_seen_subcommand_from l list; and __fish_contains_opt -s p portage-tree' \ + -xa "(__fish_portage_print_available_pkgs)" # meta -complete -c equery -n '__fish_seen_subcommand_from meta' -s d -l description -d "Show an extended package description" -complete -c equery -n '__fish_seen_subcommand_from meta' -s H -l herd -d "Show the herd(s) for the package" -complete -c equery -n '__fish_seen_subcommand_from meta' -s k -l keywords -d "Show keywords for all matching package versions" -complete -c equery -n '__fish_seen_subcommand_from meta' -s l -l license -d "Show licenses for the best maching version" -complete -c equery -n '__fish_seen_subcommand_from meta' -s m -l maintainer -d "Show the maintainer(s) for the package" -complete -c equery -n '__fish_seen_subcommand_from meta' -s S -l stablreq -d "Show STABLEREQ arches (cc's) for all matching package versions" -complete -c equery -n '__fish_seen_subcommand_from meta' -s u -l useflags -d "Show per-package USE flag descriptions" -complete -c equery -n '__fish_seen_subcommand_from meta' -s U -l upstream -d "Show package's upstream information" -complete -c equery -n '__fish_seen_subcommand_from meta' -s x -l xml -d "Show the plain metadata.xml file" +complete -c equery -n '__fish_seen_subcommand_from m meta' -s d -l description -d "Show an extended pkg description" +complete -c equery -n '__fish_seen_subcommand_from m meta' -s H -l herd -d "Show pkg's herd(s)" +complete -c equery -n '__fish_seen_subcommand_from m meta' -s k -l keywords -d "Show keywords for all matching pkg versions" +complete -c equery -n '__fish_seen_subcommand_from m meta' -s l -l license -d "Show licenses for the best maching version" +complete -c equery -n '__fish_seen_subcommand_from m meta' -s m -l maintainer -d "Show the maintainer(s) for the pkg" +complete -c equery -n '__fish_seen_subcommand_from m meta' -s S -l stablreq -d "Show STABLEREQ arches (cc's) for all matching pkg versions" +complete -c equery -n '__fish_seen_subcommand_from m meta' -s u -l useflags -d "Show per-pkg USE flag descriptions" +complete -c equery -n '__fish_seen_subcommand_from m meta' -s U -l upstream -d "Show pkg's upstream information" +complete -c equery -n '__fish_seen_subcommand_from m meta' -s x -l xml -d "Show the plain metadata.xml file" # size -complete -c equery -n '__fish_seen_subcommand_from size' -s b -l bytes -d "Report size in bytes" -complete -c equery -n '__fish_seen_subcommand_from size' -s f -l full-regex -d "Query is a regular expression" +complete -c equery -n '__fish_seen_subcommand_from s size' -s b -l bytes -d "Report size in bytes" +complete -c equery -n '__fish_seen_subcommand_from s size' -s f -l full-regex -d "Query is a regular expression" # uses -complete -c equery -n '__fish_seen_subcommand_from uses' -s a -l all -d "Include all package versions" -complete -c equery -n '__fish_seen_subcommand_from uses' -s i -l ignore-l10n -d "Don't show l10n USE flags" +complete -c equery -n '__fish_seen_subcommand_from u uses' -s a -l all -d "Include all pkg versions" +complete -c equery -n '__fish_seen_subcommand_from u uses' -s i -l ignore-l10n -d "Don't show l10n USE flags" # which -complete -c equery -n '__fish_seen_subcommand_from which' -s m -l include-masked -d "Return highest version ebuild available" -complete -c equery -n '__fish_seen_subcommand_from which' -s e -l ebuild -d "Print the ebuild" +complete -c equery -n '__fish_seen_subcommand_from w which' -s m -l include-masked -d "Return highest version ebuild available" +complete -c equery -n '__fish_seen_subcommand_from w which' -s e -l ebuild -d "Print the ebuild" From 06cda3f0b00e1f4bba421eaf3094567d3de1d2ef Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Sun, 15 Apr 2018 16:17:11 +0200 Subject: [PATCH 108/159] fix 'equery uses' completion --- share/completions/equery.fish | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/completions/equery.fish b/share/completions/equery.fish index 53afc1b94..3b30bff95 100644 --- a/share/completions/equery.fish +++ b/share/completions/equery.fish @@ -39,9 +39,9 @@ complete -c equery -n '__fish_use_subcommand' -xa 'uses' -d "Display pkg' complete -c equery -n '__fish_use_subcommand' -xa 'which' -d "Print full path to ebuild for pkg" ## Arguments -complete -c equery -n '__fish_seen_subcommand_from c changes d depends g depgraph m meta w which' \ +complete -c equery -n '__fish_seen_subcommand_from c changes d depends g depgraph m meta u uses w which' \ -xa '(__fish_portage_print_available_pkgs)' -complete -c equery -n '__fish_seen_subcommand_from k check f files u uses s size' \ +complete -c equery -n '__fish_seen_subcommand_from k check f files s size' \ -xa '(__fish_portage_print_installed_pkgs)' ## Local opts From 80e30bc1444ecbba29f7aab829d7bebbc4ac7c53 Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Sun, 15 Apr 2018 18:12:36 +0200 Subject: [PATCH 109/159] add argument completion for 'equery keywords' --- share/completions/equery.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/completions/equery.fish b/share/completions/equery.fish index 3b30bff95..0e1cf634c 100644 --- a/share/completions/equery.fish +++ b/share/completions/equery.fish @@ -39,7 +39,7 @@ complete -c equery -n '__fish_use_subcommand' -xa 'uses' -d "Display pkg' complete -c equery -n '__fish_use_subcommand' -xa 'which' -d "Print full path to ebuild for pkg" ## Arguments -complete -c equery -n '__fish_seen_subcommand_from c changes d depends g depgraph m meta u uses w which' \ +complete -c equery -n '__fish_seen_subcommand_from c changes d depends g depgraph y keywords m meta u uses w which' \ -xa '(__fish_portage_print_available_pkgs)' complete -c equery -n '__fish_seen_subcommand_from k check f files s size' \ -xa '(__fish_portage_print_installed_pkgs)' From 9d9966b15627a9789095ae4b9ead055465a14ed7 Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Sun, 15 Apr 2018 18:14:17 +0200 Subject: [PATCH 110/159] add completion for 'equery files' filter switch --- share/completions/equery.fish | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/share/completions/equery.fish b/share/completions/equery.fish index 0e1cf634c..add3e4e49 100644 --- a/share/completions/equery.fish +++ b/share/completions/equery.fish @@ -77,13 +77,15 @@ complete -c equery -n '__fish_seen_subcommand_from g depgraph' -l depth -xa "(seq 9)" # files +function __fish_equery_files_filter_args + printf "%s\n" dir obj sym dev fifo path conf cmd doc man info +end complete -c equery -n '__fish_seen_subcommand_from f files' -s m -l md5sum -d "Include MD5 sum in output" complete -c equery -n '__fish_seen_subcommand_from f files' -s s -l timestamp -d "Include timestamp in output" complete -c equery -n '__fish_seen_subcommand_from f files' -s t -l type -d "Include file type in output" complete -c equery -n '__fish_seen_subcommand_from f files' -l tree -d "Display results in a tree" -# TODO comma separated list -complete -c equery -n '__fish_seen_subcommand_from f files' -s f -l filter -d "Filter output by file type (comma separated list)" \ - -xa "dir obj sym dev fifo path conf cmd doc man info" +complete -c equery -n '__fish_seen_subcommand_from f files' -s f -l filter -d "Filter output by file type" \ + -xa "(__fish_complete_list , __fish_equery_files_filter_args)" # has + hasuse complete -c equery -n '__fish_seen_subcommand_from a has h hasuse' -s I -l exclude-installed -d "Exclude installed pkgs from search path" From 29b67030b8ab5f54957359853ff470af6cf624b7 Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Sun, 15 Apr 2018 18:21:30 +0200 Subject: [PATCH 111/159] add completion for 'ebuild' command --- share/completions/ebuild.fish | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 share/completions/ebuild.fish diff --git a/share/completions/ebuild.fish b/share/completions/ebuild.fish new file mode 100644 index 000000000..d09d1d2a8 --- /dev/null +++ b/share/completions/ebuild.fish @@ -0,0 +1,43 @@ +function __fish_seen_ebuild_arg -d "Test if an ebuild-argument has been given in the current commandline" + set -l cmd (commandline -poc) + set -e cmd[1] + for i in $cmd + switch $i + case '*.ebuild' + return 0 + end + end + return 1 +end + +## Opts +complete -c ebuild -l debug -d "Run bash with the -x option" +complete -c ebuild -l color -d "Enable color" \ + -xa "y n" +complete -c ebuild -l force -d "Force regeneration of digests" +complete -c ebuild -l ignore-default-opts -d "Ignore EBUILD_DEFAULT_OPTS" +complete -c ebuild -l skip-manifest -d "Skip all manifest checks" + +## Subcommands +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'help' -d "Show help" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'setup' -d "Run setup and system checks" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'clean' -d "Clean build dir" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'fetch' -d "Fetches all files from SRC_URI" +#complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'digest' -d "Deprecared: see manifest" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'manifest' -d "Update pkg manifest" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'unpack' -d "Extracts sources" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'prepare' -d "Run src_prepare()" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'configure' -d "Run src_configure()" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'compile' -d "Run src_compile()" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'test' -d "Run tests" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'preinst' -d "Run pkg_preinst()" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'install' -d "Run src_install()" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'postinst' -d "Run pkg_postinst()" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'qmerge' -d "Install files to live filesystem" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'merge' -d "Run fetch, unpack, compile, install and qmerge" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'unmerge' -d "Uninstall files from live filesystem" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'prerm' -d "Run pkg_prerm()" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'postrm' -d "Run pkg_postrm()" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'config' -d "Run post-install configuration" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'package' -d "Create a binpkg in PKGDIR" +complete -c ebuild -n '__fish_seen_ebuild_arg' -xa 'rpm' -d "Builds a RedHat RPM pkg" From 0b0e65a8a4a3a74880085efc25453191cb7fa786 Mon Sep 17 00:00:00 2001 From: Wilke Schwiedop <drwilly@drwilly.de> Date: Thu, 19 Apr 2018 21:41:25 +0200 Subject: [PATCH 112/159] Update ebuild.fish greatly simplify __fish_seen_ebuild_arg --- share/completions/ebuild.fish | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/share/completions/ebuild.fish b/share/completions/ebuild.fish index d09d1d2a8..4ef230e8f 100644 --- a/share/completions/ebuild.fish +++ b/share/completions/ebuild.fish @@ -1,13 +1,5 @@ function __fish_seen_ebuild_arg -d "Test if an ebuild-argument has been given in the current commandline" - set -l cmd (commandline -poc) - set -e cmd[1] - for i in $cmd - switch $i - case '*.ebuild' - return 0 - end - end - return 1 + commandline -opc | string match -q '*.ebuild' end ## Opts From f3f2d2d191de65dd226681c8e8f8f1e7f44cd32c Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Wed, 25 Apr 2018 23:04:45 +0200 Subject: [PATCH 113/159] [git completions] Speed up branch completion Using `git for-each-ref` both simplifies the code (no need to deal with detached heads anymore) and speeds it up. With 1600 branches, the time goes from ~48ms to ~16ms. --- share/completions/git.fish | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 368bc0655..5f0dcc653 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -17,12 +17,11 @@ function __fish_git_recent_commits end function __fish_git_branches - command git branch --no-color -a $argv 2>/dev/null \ - # Filter out detached heads and such ("(HEAD detached at SOMESHA)", localized). - | string match -v '\* (*)' | string match -r -v ' -> ' | string trim -c "* " \ - # We assume anything that's not remote is a local branch. - | string replace -r '^(?!remotes/)(.*)' '$1\tLocal Branch' \ - | string replace -r "^remotes/(.*)" '$1\tRemote Branch' + # This is much faster than using `git branch`, + # and avoids having to deal with localized "detached HEAD" messages. + command git for-each-ref --format='%(refname)' refs/heads/ refs/remotes/ \ + | string replace -r '^refs/heads/(.*)$' '$1\tLocal Branch' \ + | string replace -r '^refs/remotes/(.*)$' '$1\tRemote Branch' end function __fish_git_tags From 2fd15e7c6c09f11b8aeeb5c972b29cdf99070cf9 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Wed, 25 Apr 2018 23:10:58 +0200 Subject: [PATCH 114/159] Silence xsel errors Fixes #4923. [ci skip] --- share/functions/fish_clipboard_copy.fish | 4 +++- share/functions/fish_clipboard_paste.fish | 5 ++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/share/functions/fish_clipboard_copy.fish b/share/functions/fish_clipboard_copy.fish index a973e4437..b747799ca 100644 --- a/share/functions/fish_clipboard_copy.fish +++ b/share/functions/fish_clipboard_copy.fish @@ -2,6 +2,8 @@ function fish_clipboard_copy if type -q pbcopy commandline | pbcopy else if type -q xsel - commandline | xsel --clipboard + # Silence error so no error message shows up + # if e.g. X isn't running. + commandline | xsel --clipboard 2>/dev/null end end diff --git a/share/functions/fish_clipboard_paste.fish b/share/functions/fish_clipboard_paste.fish index 232a0a80a..f82c56e57 100644 --- a/share/functions/fish_clipboard_paste.fish +++ b/share/functions/fish_clipboard_paste.fish @@ -4,10 +4,9 @@ function fish_clipboard_paste set data (pbpaste) else if type -q xsel # Return if `xsel` failed. - # That way any xsel error is printed (to show e.g. a non-functioning X connection), - # but we don't print the redundant (and overly verbose for this) commandline error. + # That way we don't print the redundant (and overly verbose for this) commandline error. # Also require non-empty contents to not clear the buffer. - if not set data (xsel --clipboard) + if not set data (xsel --clipboard 2>/dev/null) return 1 end end From e598cb235ae903f0bf704edab9bf1866efca71b6 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Wed, 25 Apr 2018 23:37:39 +0200 Subject: [PATCH 115/159] Fix case matching literal `?` Fixes #4896. --- share/completions/git.fish | 5 ++++- share/functions/__fish_hg_prompt.fish | 4 +++- share/functions/__terlar_git_prompt.fish | 4 +++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 5f0dcc653..49ce8da59 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -125,6 +125,9 @@ function __fish_git_files # A " " means it's unmodified. # # Be careful about the ordering here! + # + # HACK: To allow this to work both with and without '?' globs + set -l dq '??' switch "$stat" case DD AU UD UA DU AA UU # Unmerged @@ -166,7 +169,7 @@ function __fish_git_files # For our purposes, we assume this is a staged deletion. contains -- deleted-staged $argv; or contains -- all-staged $argv and printf '%s\t%s\n' "$file" $staged_deleted_desc - case '\?\?' + case "$dq" # a literal '??' # Untracked contains -- untracked $argv and printf '%s\t%s\n' "$file" $untracked_desc diff --git a/share/functions/__fish_hg_prompt.fish b/share/functions/__fish_hg_prompt.fish index 6a4a3e721..b59cf203b 100644 --- a/share/functions/__fish_hg_prompt.fish +++ b/share/functions/__fish_hg_prompt.fish @@ -68,6 +68,8 @@ function __fish_hg_prompt --description 'Write out the hg prompt' for line in $repo_status # Add a character for each file status if we have one + # HACK: To allow this to work both with and without '?' globs + set -l q '?' switch $line case 'A ' set -a hg_statuses added @@ -77,7 +79,7 @@ function __fish_hg_prompt --description 'Write out the hg prompt' set -a hg_statuses copied case 'D ' ' D' set -a hg_statuses deleted - case '\? ' + case "$dq " set -a hg_statuses untracked case 'U*' '*U' 'DD' 'AA' set -a hg_statuses unmerged diff --git a/share/functions/__terlar_git_prompt.fish b/share/functions/__terlar_git_prompt.fish index 170f8e08c..1068944fb 100644 --- a/share/functions/__terlar_git_prompt.fish +++ b/share/functions/__terlar_git_prompt.fish @@ -50,6 +50,8 @@ function __terlar_git_prompt --description 'Write out the git prompt' set staged 1 end + # HACK: To allow matching a literal `??` both with and without `?` globs. + set -l dq '??' switch $i case 'A ' set -a gs added @@ -61,7 +63,7 @@ function __terlar_git_prompt --description 'Write out the git prompt' set -a gs copied case 'D ' ' D' set -a gs deleted - case '\?\?' + case "$dq" set -a gs untracked case 'U*' '*U' 'DD' 'AA' set -a gs unmerged From f479e4a830964ee029762dffbf8ba17aa48728c2 Mon Sep 17 00:00:00 2001 From: Andrew Lobos <andrew@lobos.me> Date: Sat, 28 Apr 2018 11:59:53 -0400 Subject: [PATCH 116/159] Remove caret redirection from tar completion --- share/functions/__fish_complete_tar.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_complete_tar.fish b/share/functions/__fish_complete_tar.fish index 3b90bfa67..a44620e58 100644 --- a/share/functions/__fish_complete_tar.fish +++ b/share/functions/__fish_complete_tar.fish @@ -7,7 +7,7 @@ function __fish_complete_tar -d "Peek inside of archives and list all files" case '-*f' '--file' set -e args[1] if test -f $args[1] - set -l file_list (tar -atf $args[1] ^ /dev/null) + set -l file_list (tar -atf $args[1] 2> /dev/null) if test -n "$file_list" printf (_ "%s\tArchived file\n") $file_list end From 376a4eca58fd0c16545548c4c4b87569521eeccc Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Sat, 28 Apr 2018 18:15:52 +0200 Subject: [PATCH 117/159] Remove ^ redirection from sample prompts A bunch of these did ^/dev/null. Replace it with 2>/dev/null. [ci skip] --- share/tools/web_config/sample_prompts/acidhub.fish | 4 ++-- .../web_config/sample_prompts/classic_vcs.fish | 8 ++++---- .../web_config/sample_prompts/robbyrussell.fish | 14 +++++++------- share/tools/web_config/sample_prompts/sorin.fish | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/share/tools/web_config/sample_prompts/acidhub.fish b/share/tools/web_config/sample_prompts/acidhub.fish index 24d675396..6d00ef901 100644 --- a/share/tools/web_config/sample_prompts/acidhub.fish +++ b/share/tools/web_config/sample_prompts/acidhub.fish @@ -4,10 +4,10 @@ function fish_prompt -d "Write out the prompt" set laststatus $status function _git_branch_name - echo (git symbolic-ref HEAD ^/dev/null | sed -e 's|^refs/heads/||') + echo (git symbolic-ref HEAD 2>/dev/null | sed -e 's|^refs/heads/||') end function _is_git_dirty - echo (git status -s --ignore-submodules=dirty ^/dev/null) + echo (git status -s --ignore-submodules=dirty 2>/dev/null) end if [ (_git_branch_name) ] set -l git_branch (set_color -o blue)(_git_branch_name) diff --git a/share/tools/web_config/sample_prompts/classic_vcs.fish b/share/tools/web_config/sample_prompts/classic_vcs.fish index 92e75346f..68e663552 100644 --- a/share/tools/web_config/sample_prompts/classic_vcs.fish +++ b/share/tools/web_config/sample_prompts/classic_vcs.fish @@ -12,25 +12,25 @@ function fish_prompt --description 'Write out the prompt' function __fish_repaint_user --on-variable fish_color_user --description "Event handler, repaint when fish_color_user changes" if status --is-interactive - commandline -f repaint ^/dev/null + commandline -f repaint 2>/dev/null end end function __fish_repaint_host --on-variable fish_color_host --description "Event handler, repaint when fish_color_host changes" if status --is-interactive - commandline -f repaint ^/dev/null + commandline -f repaint 2>/dev/null end end function __fish_repaint_status --on-variable fish_color_status --description "Event handler; repaint when fish_color_status changes" if status --is-interactive - commandline -f repaint ^/dev/null + commandline -f repaint 2>/dev/null end end function __fish_repaint_bind_mode --on-variable fish_key_bindings --description "Event handler; repaint when fish_key_bindings changes" if status --is-interactive - commandline -f repaint ^/dev/null + commandline -f repaint 2>/dev/null end end diff --git a/share/tools/web_config/sample_prompts/robbyrussell.fish b/share/tools/web_config/sample_prompts/robbyrussell.fish index 1da972182..f539027df 100644 --- a/share/tools/web_config/sample_prompts/robbyrussell.fish +++ b/share/tools/web_config/sample_prompts/robbyrussell.fish @@ -7,36 +7,36 @@ function fish_prompt if not set -q -g __fish_robbyrussell_functions_defined set -g __fish_robbyrussell_functions_defined function _git_branch_name - set -l branch (git symbolic-ref --quiet HEAD ^/dev/null) + set -l branch (git symbolic-ref --quiet HEAD 2>/dev/null) if set -q branch[1] echo (string replace -r '^refs/heads/' '' $branch) else - echo (git rev-parse --short HEAD ^/dev/null) + echo (git rev-parse --short HEAD 2>/dev/null) end end function _is_git_dirty - echo (git status -s --ignore-submodules=dirty ^/dev/null) + echo (git status -s --ignore-submodules=dirty 2>/dev/null) end function _is_git_repo type -q git or return 1 - git status -s >/dev/null ^/dev/null + git status -s >/dev/null 2>/dev/null end function _hg_branch_name - echo (hg branch ^/dev/null) + echo (hg branch 2>/dev/null) end function _is_hg_dirty - echo (hg status -mard ^/dev/null) + echo (hg status -mard 2>/dev/null) end function _is_hg_repo type -q hg or return 1 - hg summary >/dev/null ^/dev/null + hg summary >/dev/null 2>/dev/null end function _repo_branch_name diff --git a/share/tools/web_config/sample_prompts/sorin.fish b/share/tools/web_config/sample_prompts/sorin.fish index 5246aaf02..3c0c20895 100644 --- a/share/tools/web_config/sample_prompts/sorin.fish +++ b/share/tools/web_config/sample_prompts/sorin.fish @@ -16,7 +16,7 @@ function fish_right_prompt test $status != 0 and printf (set_color red)"⏎ " - if git rev-parse ^/dev/null + if git rev-parse 2>/dev/null # Magenta if branch detached else green git branch -qv | grep "\*" | string match -rq detached and set_color brmagenta @@ -26,7 +26,7 @@ function fish_right_prompt git name-rev --name-only HEAD # Merging state - git merge -q ^/dev/null + git merge -q 2>/dev/null or printf ':'(set_color red)'merge' printf ' ' From 4962244161d3908e75ffb35551fe67608e28525e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi <mqudsi@neosmart.net> Date: Sun, 29 Apr 2018 11:17:35 -0500 Subject: [PATCH 118/159] Fix __fish_complete_suffix for arguments starting with './' The previous completion generation was broken for several reasons: * ./foo would break detection of suffix due to the leading . being interpreted an extension marker, * ./foo would be completed as foo, which would be excluded from matching inrcomplete.cpp --- share/functions/__fish_complete_suffix.fish | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/share/functions/__fish_complete_suffix.fish b/share/functions/__fish_complete_suffix.fish index 7c5ad2e91..cde966d90 100644 --- a/share/functions/__fish_complete_suffix.fish +++ b/share/functions/__fish_complete_suffix.fish @@ -33,10 +33,22 @@ function __fish_complete_suffix -d "Complete using files" end - # Perform the completion + # Strip leading ./ as it confuses the detection of base and suffix + # It is conditionally re-added below. + set -l base_temp (string replace -r '^\./' '' -- $comp) - set base (string replace -r '\.[^.]*$' '' -- $comp | string trim -c '\'"') # " make emacs syntax highlighting happy - eval "set files $base*$suff" + set base (string replace -r '\.[^.]*$' '' -- $base_temp | string trim -c '\'"') # " make emacs syntax highlighting happy + # echo "base: $base" > /dev/tty + # echo "suffix: $suff" > /dev/tty + + # If $comp is "./ma" and the file is "main.py", we'll catch that case here, + # but complete.cpp will not consider it a match, so we have to output the + # correct form. + if string match -qr '^\./' -- $comp + eval "set files ./$base*$suff" + else + eval "set files $base*$suff" + end if test $files[1] printf "%s\t$desc\n" $files From e02e485cc6e712879d10fc99034543b7424567fb Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Tue, 1 May 2018 19:28:37 +0200 Subject: [PATCH 119/159] command-not-found: Split os-release' stuff on space ID_LIKE is defined as a space-separated list and ID can't have spaces. Fixes it for "openSUSE Tumblewee". --- share/functions/__fish_config_interactive.fish | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/functions/__fish_config_interactive.fish b/share/functions/__fish_config_interactive.fish index 59b52f4b6..f57314543 100644 --- a/share/functions/__fish_config_interactive.fish +++ b/share/functions/__fish_config_interactive.fish @@ -293,7 +293,7 @@ function __fish_config_interactive -d "Initializations that should be performed set -l os if test -r /etc/os-release set os (string match -r '^ID(?:_LIKE)?\s*=.*' < /etc/os-release | \ - string replace -r '^ID(?:_LIKE)?\s*=(.*)' '$1' | string trim -c '\'"') + string replace -r '^ID(?:_LIKE)?\s*=(.*)' '$1' | string trim -c '\'"' | string split " ") end # First check if we are on OpenSUSE since SUSE's handler has no options From baeeef3233d80e4fa69bc6168ab318d4afc2c55d Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Wed, 2 May 2018 16:33:28 +0200 Subject: [PATCH 120/159] Remove triggerable assert in unescape_string_internal Fixes #4954 - in a hacky way. --- src/common.cpp | 6 +++++- src/complete.cpp | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/common.cpp b/src/common.cpp index ac2492805..d7182e6b6 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -1363,7 +1363,11 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in } case L'}': { if (unescape_special) { - assert(brace_count > 0 && "imbalanced brackets are a tokenizer error, we shouldn't be able to get here"); + // HACK: The completion machinery sometimes hands us partial tokens. + // We can't parse them properly, but it shouldn't hurt, + // so we don't assert here. + // See #4954. + // assert(brace_count > 0 && "imbalanced brackets are a tokenizer error, we shouldn't be able to get here"); brace_count--; brace_text_start = brace_text_start && brace_count > 0; to_append_or_none = BRACE_END; diff --git a/src/complete.cpp b/src/complete.cpp index 1df55e5a6..c256a0558 100644 --- a/src/complete.cpp +++ b/src/complete.cpp @@ -1078,6 +1078,9 @@ void completer_t::complete_param_expand(const wcstring &str, bool do_file, bool complete_from_start = !complete_from_separator || !string_prefixes_string(L"-", str); if (complete_from_separator) { + // FIXME: This just cuts the token, + // so any quoting or braces gets lost. + // See #4954. const wcstring sep_string = wcstring(str, sep_index + 1); std::vector<completion_t> local_completions; if (expand_string(sep_string, &local_completions, flags, NULL) == EXPAND_ERROR) { From d8b1f0715f2e3927efb782dcdf7994d665b09d21 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Thu, 3 May 2018 11:32:18 +0200 Subject: [PATCH 121/159] [pip* completions] Silence stderr For some reason, these tools print an upgrade message to stderr, even when stdout goes somewhere else. --- share/completions/pip.fish | 2 +- share/completions/pip2.fish | 2 +- share/completions/pip3.fish | 2 +- share/completions/pipenv.fish | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/share/completions/pip.fish b/share/completions/pip.fish index 381d3feaa..6280623cb 100644 --- a/share/completions/pip.fish +++ b/share/completions/pip.fish @@ -1,4 +1,4 @@ if command -sq pip - pip completion --fish | source + pip completion --fish 2>/dev/null | source end diff --git a/share/completions/pip2.fish b/share/completions/pip2.fish index af3ef5630..c798fbb65 100644 --- a/share/completions/pip2.fish +++ b/share/completions/pip2.fish @@ -3,5 +3,5 @@ if command -sq pip2 # See discussion at https://github.com/fish-shell/fish-shell/pull/4448 # and pip bug at https://github.com/pypa/pip/pull/4755 # Keep this even after pip fix is upstreamed for users not on the latest pip - pip2 completion --fish | string replace -r -- " -c\s+pip\b" " -c pip2" | source + pip2 completion --fish 2>/dev/null | string replace -r -- " -c\s+pip\b" " -c pip2" | source end diff --git a/share/completions/pip3.fish b/share/completions/pip3.fish index 1542a5528..11c8dd12d 100644 --- a/share/completions/pip3.fish +++ b/share/completions/pip3.fish @@ -3,5 +3,5 @@ if command -sq pip3 # See discussion at https://github.com/fish-shell/fish-shell/pull/4448 # and pip bug at https://github.com/pypa/pip/pull/4755 # Keep this even after pip fix is upstreamed for users not on the latest pip - pip3 completion --fish | string replace -r -- " -c\s+pip\b" " -c pip3" | source + pip3 completion --fish 2>/dev/null | string replace -r -- " -c\s+pip\b" " -c pip3" | source end diff --git a/share/completions/pipenv.fish b/share/completions/pipenv.fish index 83f93f3ce..943b44d2b 100644 --- a/share/completions/pipenv.fish +++ b/share/completions/pipenv.fish @@ -1,3 +1,3 @@ if command -sq pipenv - pipenv --completion | source + pipenv --completion 2>/dev/null | source end From 2a616698b371627cea91588ab772285cbde068ff Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Thu, 3 May 2018 12:42:09 +0200 Subject: [PATCH 122/159] Just define `:` compatibility function directly There really is no need for that indirection. --- share/config.fish | 12 ++++++------ share/functions/colon.fish | 5 ----- 2 files changed, 6 insertions(+), 11 deletions(-) delete mode 100644 share/functions/colon.fish diff --git a/share/config.fish b/share/config.fish index 9f08d0ca9..59fa7f505 100644 --- a/share/config.fish +++ b/share/config.fish @@ -105,12 +105,12 @@ if not contains -- $__fish_data_dir/completions $fish_complete_path set fish_complete_path $fish_complete_path $__fish_data_dir/completions end -# : is a sh/bash-compatibility function, but because its name can cause problems on many -# systems, it is saved as colon.fish and not :.fish, which means that it's never autoloaded -# since the name of the file and the name of the function differ. Force evaluation of colon.fish -# as a function by simply trying to load the non-existent function colon, which will pull in -# colon.fish and lead to `:` being recognized. Sourced up here so it can be used later safely. -type -q colon; or true # just to reset $status +# This cannot be in an autoload-file because `:.fish` is an invalid filename on windows. +function : + # no-op function for compatibility with sh, bash, and others. + # Often used to insert a comment into a chain of commands without having + # it eat up the remainder of the line, handy in Makefiles. +end # # This is a Solaris-specific test to modify the PATH so that diff --git a/share/functions/colon.fish b/share/functions/colon.fish deleted file mode 100644 index b41ad1e4e..000000000 --- a/share/functions/colon.fish +++ /dev/null @@ -1,5 +0,0 @@ -function : - # no-op function for compatibility with sh, bash, and others. - # Often used to insert a comment into a chain of commands without having - # it eat up the remainder of the line, handy in Makefiles. -end From b0368fd85b18faa70717a1720c2e3448e193ea57 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Thu, 3 May 2018 12:49:21 +0200 Subject: [PATCH 123/159] Set $IFS in C++ This was done in share/config.fish, but leads to surprising results if that isn't read - e.g. because someone just built fish in the git directory to test it without installing. It's also not something that is any more or less complicated. For compatibility, keep it in config.fish as well for the time being. --- src/env.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/env.cpp b/src/env.cpp index af2f8e451..1bd267708 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -924,6 +924,9 @@ void env_init(const struct config_paths_t *paths /* or NULL */) { uid_t uid = getuid(); setup_user(uid == 0); + // Set up $IFS - this used to be in share/config.fish, but really breaks if it isn't done. + env_set_one(L"IFS", ENV_GLOBAL, L"\n \t"); + // Set up the version variable. wcstring version = str2wcstring(get_fish_version()); env_set_one(L"version", ENV_GLOBAL, version); From ba68efd5438e47459876bbc170f1d7e269cafa1a Mon Sep 17 00:00:00 2001 From: Ray Juang <rjuang@github.com> Date: Wed, 2 May 2018 22:06:32 -0700 Subject: [PATCH 124/159] Updating adb completion to enable tab-completing file paths for adb shell and pull commands --- share/completions/adb.fish | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/share/completions/adb.fish b/share/completions/adb.fish index f936100cf..7684d0ef2 100644 --- a/share/completions/adb.fish +++ b/share/completions/adb.fish @@ -58,6 +58,21 @@ function __fish_adb_list_uninstallable_packages __fish_adb_run_command pm list packages -3 | string replace 'package:' '' end +function __fish_adb_list_files + set -l token (commandline -ct) + + # Have tab complete show initial / if nothing on current token + if test -z "$token" + set token "/" + end + + # Return list of directories suffixed with '/' + __fish_adb_run_command find -H "$token*" -maxdepth 0 -type d 2>/dev/null | awk '{print $1"/"}' + # Return list of files + __fish_adb_run_command find -H "$token*" -maxdepth 0 -type f 2>/dev/null +end + + # Generic options, must come before command complete -n '__fish_adb_no_subcommand' -c adb -s s -x -a "(__fish_adb_get_devices)" -d 'Device to communicate with' complete -n '__fish_adb_no_subcommand' -c adb -s d -d 'Communicate with first USB device' @@ -143,3 +158,7 @@ complete -n '__fish_seen_subcommand_from sideload' -c adb -xa '(__fish_complete_ # reconnect complete -n '__fish_seen_subcommand_from reconnect' -c adb -x -a 'device' -d 'Kick current connection from device side and make it reconnect.' + +# commands that accept listing device files +complete -n '__fish_seen_subcommand_from shell' -c adb -f -a "(__fish_adb_list_files)" -d 'File on device' +complete -n '__fish_seen_subcommand_from pull' -c adb -f -a "(__fish_adb_list_files)" -d 'File on device' From b5d6c1102e73c6b7377a1f51b71f6874cb72cf0d Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Thu, 3 May 2018 15:19:06 +0200 Subject: [PATCH 125/159] [git prompt] Only set chars/colors when necessary Either on start or when something has changed. This saves about 8ms on every call except the first. --- share/functions/__fish_git_prompt.fish | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/share/functions/__fish_git_prompt.fish b/share/functions/__fish_git_prompt.fish index cc8525a63..ef4165edc 100644 --- a/share/functions/__fish_git_prompt.fish +++ b/share/functions/__fish_git_prompt.fish @@ -362,8 +362,13 @@ function __fish_git_prompt --description "Prompt function for Git" set -l p #upstream set -l informative_status - __fish_git_prompt_validate_chars - __fish_git_prompt_validate_colors + if not set -q ___fish_git_prompt_init + # This takes a while, so it only needs to be done once, + # and then whenever the configuration changes. + __fish_git_prompt_validate_chars + __fish_git_prompt_validate_colors + set -g ___fish_git_prompt_init + end set -l space "$___fish_git_prompt_color$___fish_git_prompt_char_stateseparator$___fish_git_prompt_color_done" @@ -767,6 +772,7 @@ end set -a varargs --on-variable __fish_git_prompt_showcolorhints function __fish_git_prompt_repaint_color $varargs --description "Event handler, repaints prompt when any color changes" if status --is-interactive + set -e ___fish_git_prompt_init set -l var $argv[3] set -e _$var set -e _{$var}_done @@ -787,6 +793,7 @@ for var in cleanstate dirtystate invalidstate stagedstate stashstate statesepara end function __fish_git_prompt_repaint_char $varargs --description "Event handler, repaints prompt when any char changes" if status --is-interactive + set -e ___fish_git_prompt_init set -e _$argv[3] commandline -f repaint 2>/dev/null end From f533189a28c04ad847c59e9049329171e6c2b3d1 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Fri, 4 May 2018 12:14:49 +0200 Subject: [PATCH 126/159] [default bindings] Bind \e\b as well As it turns out, for some terminals backspace is \b but only when preceded by \e. All this makes about as much sense as the english language. Fixes #4955. --- share/functions/fish_default_key_bindings.fish | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index 7c587cfb5..34e6c4df3 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -72,7 +72,9 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis # This clashes with __fish_list_current_token # bind $argv \el downcase-word bind $argv \ec capitalize-word + # One of these is alt+backspace. bind $argv \e\x7f backward-kill-word + bind $argv \e\b backward-kill-word bind $argv \eb backward-word bind $argv \ef forward-word bind $argv \e\[1\;5C forward-word From 1a1ee352ff582bc2595e06a3247f0f88ab9df0d3 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Sat, 5 May 2018 21:41:03 +0200 Subject: [PATCH 127/159] [commandline] Change "--selection" to "--current-selection" Plus documentation. Work towards #4255. --- doc_src/commandline.txt | 4 +++- src/builtin_commandline.cpp | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/doc_src/commandline.txt b/doc_src/commandline.txt index 54869fa96..ce054737a 100644 --- a/doc_src/commandline.txt +++ b/doc_src/commandline.txt @@ -35,7 +35,9 @@ The following options change what part of the commandline is printed or updated: - `-p` or `--current-process` select the current process -- `-t` or `--current-token` select the current token. +- `-s` or `--current-selection` selects the current selection + +- `-t` or `--current-token` select the current token The following options change the way `commandline` prints the current commandline buffer: diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp index 161470f46..e392f37ea 100644 --- a/src/builtin_commandline.cpp +++ b/src/builtin_commandline.cpp @@ -213,10 +213,11 @@ int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv) static const struct woption long_options[] = {{L"append", no_argument, NULL, 'a'}, {L"insert", no_argument, NULL, 'i'}, {L"replace", no_argument, NULL, 'r'}, + {L"current-buffer", no_argument, NULL, 'b'}, {L"current-job", no_argument, NULL, 'j'}, {L"current-process", no_argument, NULL, 'p'}, + {L"current-selection", no_argument, NULL, 's'}, {L"current-token", no_argument, NULL, 't'}, - {L"current-buffer", no_argument, NULL, 'b'}, {L"cut-at-cursor", no_argument, NULL, 'c'}, {L"function", no_argument, NULL, 'f'}, {L"tokenize", no_argument, NULL, 'o'}, @@ -225,7 +226,6 @@ int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv) {L"cursor", no_argument, NULL, 'C'}, {L"line", no_argument, NULL, 'L'}, {L"search-mode", no_argument, NULL, 'S'}, - {L"selection", no_argument, NULL, 's'}, {L"paging-mode", no_argument, NULL, 'P'}, {NULL, 0, NULL, 0}}; From 7cbc0c371ad1f2ff9afce080e2c78f5ff6f4d680 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Sun, 6 May 2018 11:07:27 -0700 Subject: [PATCH 128/159] Remove a "common fish problems" section in the docs This wasn't really that common. --- doc_src/index.hdr.in | 5 ----- share/completions/help.fish | 1 - 2 files changed, 6 deletions(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index d39a41bf1..70757d7ab 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1327,11 +1327,6 @@ To start a debug session simply run the builtin command `breakpoint` at the poin Note: At the moment the debug prompt is identical to your normal fish prompt. This can make it hard to recognize that you've entered a debug session. <a hread="https://github.com/fish-shell/fish-shell/issues/1310">Issue 1310</a> is open to improve this. -\section issues Common issues with fish - -If you install fish in your home directory, fish will not work correctly for any other user than yourself. This is because fish needs its initialization files to function properly. To solve this problem, either copy the initialization files to each fish users home directory, or install them in `/etc`. - - \section more-help Further help and development If you have a question not answered by this documentation, there are several avenues for help: diff --git a/share/completions/help.fish b/share/completions/help.fish index 4f5547a8c..2ff41eb76 100644 --- a/share/completions/help.fish +++ b/share/completions/help.fish @@ -38,7 +38,6 @@ complete -c help -x -a history-search -d 'Searchable history' complete -c help -x -a identifiers -d 'Shell variable and function names' complete -c help -x -a initialization -d 'Initialization files' complete -c help -x -a introduction -d 'Introduction' -complete -c help -x -a issues -d 'Common issues with fish' complete -c help -x -a job-control -d 'Running multiple programs' complete -c help -x -a killring -d 'Copy and paste (Kill Ring)' complete -c help -x -a more-help -d 'Further help and development' From 14f766b66d23f422795239e083247f6deb2b8605 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Tue, 24 Apr 2018 12:09:13 -0700 Subject: [PATCH 129/159] Add support for feature flags This introduces a new type features_t that exposes feature flags. The intent is to allow a deprecation/incremental adoption path. This is not a general purpose configuration mechanism, but instead allows for compatibility during the transition as features are added/removed. Each feature has a user-presentable short name and a short description. Their values are tracked in a struct features_t. We start with one feature stderr_nocaret, but it's not hooked up yet. --- CMakeLists.txt | 1 + Makefile.in | 3 +- src/fish_tests.cpp | 25 ++++++++++++++ src/future_feature_flags.cpp | 23 +++++++++++++ src/future_feature_flags.h | 63 ++++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 src/future_feature_flags.cpp create mode 100644 src/future_feature_flags.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a01397a17..b6514c4c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ SET(FISH_SRCS src/postfork.cpp src/proc.cpp src/reader.cpp src/sanity.cpp src/screen.cpp src/signal.cpp src/tnode.cpp src/tokenizer.cpp src/utf8.cpp src/util.cpp src/wcstringutil.cpp src/wgetopt.cpp src/wildcard.cpp src/wutil.cpp + src/future_feature_flags.cpp ) # Header files are just globbed. diff --git a/Makefile.in b/Makefile.in index 21e018dad..620d224b1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -118,7 +118,8 @@ FISH_OBJS := obj/autoload.o obj/builtin.o obj/builtin_bg.o obj/builtin_bind.o ob obj/parse_productions.o obj/parse_tree.o obj/parse_util.o obj/parser.o \ obj/parser_keywords.o obj/path.o obj/postfork.o obj/proc.o obj/reader.o \ obj/sanity.o obj/screen.o obj/signal.o obj/tinyexpr.o obj/tokenizer.o obj/tnode.o obj/utf8.o \ - obj/util.o obj/wcstringutil.o obj/wgetopt.o obj/wildcard.o obj/wutil.o + obj/util.o obj/wcstringutil.o obj/wgetopt.o obj/wildcard.o obj/wutil.o \ + obj/future_feature_flags.o FISH_INDENT_OBJS := obj/fish_indent.o obj/print_help.o $(FISH_OBJS) diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 8795fab54..e022fa166 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -45,6 +45,7 @@ #include "expand.h" #include "fallback.h" // IWYU pragma: keep #include "function.h" +#include "future_feature_flags.h" #include "highlight.h" #include "history.h" #include "input.h" @@ -1352,6 +1353,29 @@ static void test_utf8() { #endif } +static void test_feature_flags() { + say(L"Testing future feature flags"); + using ft = features_t; + ft f; + do_test(!f.test(ft::stderr_nocaret)); + f.set(ft::stderr_nocaret, true); + do_test(f.test(ft::stderr_nocaret)); + f.set(ft::stderr_nocaret, false); + do_test(!f.test(ft::stderr_nocaret)); + + // Ensure every metadata is represented once. + size_t counts[ft::flag_count] = {}; + for (const auto &md : ft::metadata) { + counts[md.flag]++; + } + for (size_t c : counts) { + do_test(c == 1); + } + do_test(ft::metadata[ft::stderr_nocaret].name == wcstring(L"stderr-nocaret")); + do_test(ft::metadata_for(L"stderr-nocaret") == &ft::metadata[ft::stderr_nocaret]); + do_test(ft::metadata_for(L"not-a-flag") == nullptr); +} + static void test_escape_sequences() { say(L"Testing escape_sequences"); if (escape_code_length(L"") != 0) err(L"test_escape_sequences failed on line %d\n", __LINE__); @@ -4611,6 +4635,7 @@ int main(int argc, char **argv) { if (should_test_function("cancellation")) test_cancellation(); if (should_test_function("indents")) test_indents(); if (should_test_function("utf8")) test_utf8(); + if (should_test_function("feature_flags")) test_feature_flags(); if (should_test_function("escape_sequences")) test_escape_sequences(); if (should_test_function("lru")) test_lru(); if (should_test_function("expand")) test_expand(); diff --git a/src/future_feature_flags.cpp b/src/future_feature_flags.cpp new file mode 100644 index 000000000..26b854975 --- /dev/null +++ b/src/future_feature_flags.cpp @@ -0,0 +1,23 @@ +#include "config.h" // IWYU pragma: keep + +#include <wchar.h> +#include "future_feature_flags.h" + +/// The set of features applying to this instance. +static features_t global_features; + +const features_t &fish_features() { return global_features; } + +features_t &mutable_fish_features() { return global_features; } + +const features_t::metadata_t features_t::metadata[features_t::flag_count] = { + {stderr_nocaret, L"stderr-nocaret", L"3.0", L"^ no longer redirects stderr"}, +}; + +const struct features_t::metadata_t *features_t::metadata_for(const wchar_t *name) { + assert(name && "null flag name"); + for (const auto &md : metadata) { + if (!wcscmp(name, md.name)) return &md; + } + return nullptr; +} diff --git a/src/future_feature_flags.h b/src/future_feature_flags.h new file mode 100644 index 000000000..d8373e064 --- /dev/null +++ b/src/future_feature_flags.h @@ -0,0 +1,63 @@ +// Flags to enable upcoming features +#ifndef FISH_FUTURE_FEATURE_FLAGS_H +#define FISH_FUTURE_FEATURE_FLAGS_H + +#include <assert.h> + +class features_t { +public: + /// The list of flags. + enum flag_t { + /// Whether ^ is supported for stderr redirection. + stderr_nocaret, + + /// The number of flags. + flag_count + }; + + /// Return whether a flag is set. + bool test(flag_t f) const { + assert(f >= 0 && f < flag_count && "Invalid flag"); + return values[f]; + } + + /// Set a flag. + void set(flag_t f, bool value) { + assert(f >= 0 && f < flag_count && "Invalid flag"); + values[f] = value; + } + + /// Metadata about feature flags. + struct metadata_t { + /// The flag itself. + features_t::flag_t flag; + + /// User-presentable short name of the feature flag. + const wchar_t *name; + + /// Comma-separated list of feature groups. + const wchar_t *groups; + + /// User-presentable description of the feature flag. + const wchar_t *description; + }; + + /// The metadata, indexed by flag. + static const metadata_t metadata[flag_count]; + + /// Return the metadata for a particular name, or nullptr if not found. + static const struct metadata_t *metadata_for(const wchar_t *name); + + private: + /// Values for the flags. + bool values[flag_count] = {}; +}; + +/// Return the global set of features for fish. This is const to prevent accidental mutation. +const features_t &fish_features(); + +/// Return the global set of features for fish, but mutable. In general fish features should be set +/// at startup only. +features_t &mutable_fish_features(); + +#endif From 782cae2d212a914a599de081bd4bec5704b13e76 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Tue, 24 Apr 2018 14:02:15 -0700 Subject: [PATCH 130/159] Add status subcomannds: features and test-feature This teaches the status command to work with features. 'status features' will show a table listing all known features and whether they are currently on or off. `status test-feature` will test an individual feature, setting the exit status to 0 if the feature is on, 1 if off, 2 if unknown. --- src/builtin_status.cpp | 36 ++++++++++++++++++++++++++++++++++++ tests/status.err | 3 +++ tests/status.in | 7 ++++++- tests/status.out | 6 ++++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/builtin_status.cpp b/src/builtin_status.cpp index c498dd3bd..1aecc9621 100644 --- a/src/builtin_status.cpp +++ b/src/builtin_status.cpp @@ -10,6 +10,7 @@ #include "builtin_status.h" #include "common.h" #include "fallback.h" // IWYU pragma: keep +#include "future_feature_flags.h" #include "io.h" #include "parser.h" #include "proc.h" @@ -31,6 +32,8 @@ enum status_cmd_t { STATUS_LINE_NUMBER, STATUS_SET_JOB_CONTROL, STATUS_STACK_TRACE, + STATUS_FEATURES, + STATUS_TEST_FEATURE, STATUS_UNDEF }; @@ -40,6 +43,7 @@ const enum_map<status_cmd_t> status_enum_map[] = { {STATUS_FILENAME, L"current-filename"}, {STATUS_FUNCTION, L"current-function"}, {STATUS_LINE_NUMBER, L"current-line-number"}, + {STATUS_FEATURES, L"features"}, {STATUS_FILENAME, L"filename"}, {STATUS_FUNCTION, L"function"}, {STATUS_IS_BLOCK, L"is-block"}, @@ -54,6 +58,7 @@ const enum_map<status_cmd_t> status_enum_map[] = { {STATUS_LINE_NUMBER, L"line-number"}, {STATUS_STACK_TRACE, L"print-stack-trace"}, {STATUS_STACK_TRACE, L"stack-trace"}, + {STATUS_TEST_FEATURE, L"test-feature"}, {STATUS_UNDEF, NULL}}; #define status_enum_map_len (sizeof status_enum_map / sizeof *status_enum_map) @@ -66,6 +71,9 @@ const enum_map<status_cmd_t> status_enum_map[] = { break; \ } +/// Values that may be returned from the test-feature option to status. +enum { TEST_FEATURE_ON, TEST_FEATURE_OFF, TEST_FEATURE_NOT_RECOGNIZED }; + int job_control_str_to_mode(const wchar_t *mode, wchar_t *cmd, io_streams_t &streams) { if (wcscmp(mode, L"full") == 0) { return JOB_CONTROL_ALL; @@ -82,6 +90,7 @@ struct status_cmd_opts_t { bool print_help = false; int level = 1; int new_job_control_mode = -1; + const wchar_t *feature_name; status_cmd_t status_cmd = STATUS_UNDEF; }; @@ -126,6 +135,15 @@ static bool set_status_cmd(wchar_t *const cmd, status_cmd_opts_t &opts, status_c return true; } +/// Print the features and their values. +static void print_features(io_streams_t &streams) { + for (const auto &md : features_t::metadata) { + int set = fish_features().test(md.flag); + streams.out.append_format(L"%ls\t%s\t%ls\t%ls\n", md.name, set ? "on" : "off", md.groups, + md.description); + } +} + static int parse_cmd_opts(status_cmd_opts_t &opts, int *optind, //!OCLINT(high ncss method) int argc, wchar_t **argv, parser_t &parser, io_streams_t &streams) { wchar_t *cmd = argv[0]; @@ -307,6 +325,24 @@ int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { job_control_mode = opts.new_job_control_mode; break; } + case STATUS_FEATURES: { + print_features(streams); + break; + } + case STATUS_TEST_FEATURE: { + if (args.size() != 1) { + const wchar_t *subcmd_str = enum_to_str(opts.status_cmd, status_enum_map); + streams.err.append_format(BUILTIN_ERR_ARG_COUNT2, cmd, subcmd_str, 1, args.size()); + return STATUS_INVALID_ARGS; + } + const auto *metadata = features_t::metadata_for(args.front().c_str()); + if (!metadata) { + retval = TEST_FEATURE_NOT_RECOGNIZED; + } else { + retval = fish_features().test(metadata->flag) ? TEST_FEATURE_ON : TEST_FEATURE_OFF; + } + break; + } case STATUS_FILENAME: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) const wchar_t *fn = parser.current_filename(); diff --git a/tests/status.err b/tests/status.err index 2dc6956a2..855655277 100644 --- a/tests/status.err +++ b/tests/status.err @@ -6,3 +6,6 @@ status: Invalid combination of options, you cannot do both 'is-block' and 'is-interactive' in the same invocation status: Invalid job control mode 'full1' status: Invalid job control mode '1none' + +#################### +# Future Feature Flags diff --git a/tests/status.in b/tests/status.in index ee16ecdad..008255027 100644 --- a/tests/status.in +++ b/tests/status.in @@ -48,4 +48,9 @@ function test_function end test_function -eval test_function \ No newline at end of file +eval test_function + +logmsg Future Feature Flags +status features +status test-feature stderr-nocaret ; echo $status +status test-feature not-a-feature ; echo $status diff --git a/tests/status.out b/tests/status.out index 989264396..1553d674a 100644 --- a/tests/status.out +++ b/tests/status.out @@ -1,3 +1,9 @@ Not a function test_function test_function + +#################### +# Future Feature Flags +stderr-nocaret off 3.0 ^ no longer redirects stderr +1 +2 From 8a96f283ba56673acc7bd088d48fdbdd56f84d31 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Tue, 24 Apr 2018 14:32:06 -0700 Subject: [PATCH 131/159] Allow setting feature flags on the command line This introduces a new command line option --features which can be used for enabling or disabling features for a particular fish session. Examples: fish --features stderr-nocaret fish --features 3.0,no-stderr-nocaret fish --features all Note that the feature set cannot be changed in an existing session. --- src/fish.cpp | 12 +++++++- src/fish_tests.cpp | 5 ++++ src/future_feature_flags.cpp | 34 +++++++++++++++++++++++ src/future_feature_flags.h | 9 ++++++ tests/invocation/features-nocaret1.invoke | 1 + tests/invocation/features-nocaret1.out | 1 + tests/invocation/features-nocaret2.invoke | 1 + tests/invocation/features-nocaret2.out | 1 + 8 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/invocation/features-nocaret1.invoke create mode 100644 tests/invocation/features-nocaret1.out create mode 100644 tests/invocation/features-nocaret2.invoke create mode 100644 tests/invocation/features-nocaret2.out diff --git a/src/fish.cpp b/src/fish.cpp index 5568086b7..431739928 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -44,6 +44,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "fallback.h" // IWYU pragma: keep #include "fish_version.h" #include "function.h" +#include "future_feature_flags.h" #include "history.h" #include "io.h" #include "parser.h" @@ -61,6 +62,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA // container to hold the options specified within the command line class fish_cmd_opts_t { public: + // Future feature flags values string + wcstring features; // Commands to be executed in place of interactive shell. std::vector<std::string> batch_cmds; // Commands to execute after the shell's config has been read. @@ -238,9 +241,10 @@ int run_command_list(std::vector<std::string> *cmds, const io_chain_t &io) { /// Parse the argument list, return the index of the first non-flag arguments. static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) { - static const char *short_opts = "+hilnvc:C:p:d:D:"; + static const char *short_opts = "+hilnvc:C:p:d:f:D:"; static const struct option long_opts[] = {{"command", required_argument, NULL, 'c'}, {"init-command", required_argument, NULL, 'C'}, + {"features", required_argument, NULL, 'f'}, {"debug-level", required_argument, NULL, 'd'}, {"debug-stack-frames", required_argument, NULL, 'D'}, {"interactive", no_argument, NULL, 'i'}, @@ -277,6 +281,10 @@ static int fish_parse_opt(int argc, char **argv, fish_cmd_opts_t *opts) { } break; } + case 'f': { + opts->features = str2wcstring(optarg); + break; + } case 'h': { opts->batch_cmds.push_back("__fish_print_help fish"); break; @@ -375,6 +383,8 @@ int main(int argc, char **argv) { const struct config_paths_t paths = determine_config_directory_paths(argv[0]); env_init(&paths); + // Set features early in case other initialization depends on them. + mutable_fish_features().set_from_string(opts.features); proc_init(); builtin_init(); misc_init(); diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index e022fa166..8a97ea4a4 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -1363,6 +1363,11 @@ static void test_feature_flags() { f.set(ft::stderr_nocaret, false); do_test(!f.test(ft::stderr_nocaret)); + f.set_from_string(L"stderr-nocaret,nonsense"); + do_test(f.test(ft::stderr_nocaret)); + f.set_from_string(L"stderr-nocaret,no-stderr-nocaret,nonsense"); + do_test(!f.test(ft::stderr_nocaret)); + // Ensure every metadata is represented once. size_t counts[ft::flag_count] = {}; for (const auto &md : ft::metadata) { diff --git a/src/future_feature_flags.cpp b/src/future_feature_flags.cpp index 26b854975..276ea32f2 100644 --- a/src/future_feature_flags.cpp +++ b/src/future_feature_flags.cpp @@ -21,3 +21,37 @@ const struct features_t::metadata_t *features_t::metadata_for(const wchar_t *nam } return nullptr; } + +void features_t::set_from_string(const wcstring &str) { + wcstring_list_t entries = split_string(str, L','); + const wchar_t *whitespace = L"\t\n\v\f\r "; + for (wcstring entry : entries) { + if (entry.empty()) continue; + + // Trim leading and trailing whitespace + entry.erase(0, entry.find_first_not_of(whitespace)); + entry.erase(entry.find_last_not_of(whitespace) + 1); + + const wchar_t *name = entry.c_str(); + bool value = true; + // A "no-" prefix inverts the sense. + if (string_prefixes_string(L"no-", name)) { + value = false; + name += 3; // wcslen(L"no-") + } + // Look for a feature with this name. If we don't find it, assume it's a group name and set + // all features whose group contain it. Do nothing even if the string is unrecognized; this + // is to allow uniform invocations of fish (e.g. disable a feature that is only present in + // future versions). + // The special name 'all' may be used for those who like to live on the edge. + if (const metadata_t *md = metadata_for(name)) { + this->set(md->flag, value); + } else { + for (const metadata_t &md : metadata) { + if (wcsstr(md.groups, name) || !wcscmp(name, L"all")) { + this->set(md.flag, value); + } + } + } + } +} diff --git a/src/future_feature_flags.h b/src/future_feature_flags.h index d8373e064..6765252e9 100644 --- a/src/future_feature_flags.h +++ b/src/future_feature_flags.h @@ -3,6 +3,9 @@ #define FISH_FUTURE_FEATURE_FLAGS_H #include <assert.h> +#include <unordered_map> + +#include "common.h" class features_t { public: @@ -27,6 +30,12 @@ class features_t { values[f] = value; } + /// Parses a comma-separated feature-flag string, updating ourselves with the values. + /// Feature names or group names may be prefixed with "no-" to disable them. + /// The special group name "all" may be used for those who like to live on the edge. + /// Unknown features are silently ignored. + void set_from_string(const wcstring &str); + /// Metadata about feature flags. struct metadata_t { /// The flag itself. diff --git a/tests/invocation/features-nocaret1.invoke b/tests/invocation/features-nocaret1.invoke new file mode 100644 index 000000000..91243cd8e --- /dev/null +++ b/tests/invocation/features-nocaret1.invoke @@ -0,0 +1 @@ +--features 'no-stderr-nocaret' -c 'status test-feature stderr-nocaret; echo nocaret: $status' diff --git a/tests/invocation/features-nocaret1.out b/tests/invocation/features-nocaret1.out new file mode 100644 index 000000000..057e3967b --- /dev/null +++ b/tests/invocation/features-nocaret1.out @@ -0,0 +1 @@ +nocaret: 1 diff --git a/tests/invocation/features-nocaret2.invoke b/tests/invocation/features-nocaret2.invoke new file mode 100644 index 000000000..4a20f5f95 --- /dev/null +++ b/tests/invocation/features-nocaret2.invoke @@ -0,0 +1 @@ +--features 'stderr-nocaret' -c 'status test-feature stderr-nocaret; echo nocaret: $status' diff --git a/tests/invocation/features-nocaret2.out b/tests/invocation/features-nocaret2.out new file mode 100644 index 000000000..2b8a87b31 --- /dev/null +++ b/tests/invocation/features-nocaret2.out @@ -0,0 +1 @@ +nocaret: 0 From 902af26253905e0cb24a0cd6388962349f6abd28 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Tue, 24 Apr 2018 15:53:30 -0700 Subject: [PATCH 132/159] Bring back caret redirections under a feature flag This partially reverts 5b489ca30f3205b03b23dff6bedbc59df499e92b, with carets acting as redirections unless the stderr-nocaret flag is set. This flag is off by default but may be enabled on the command line: fish --features stderr-nocaret --- CHANGELOG.md | 3 +- src/common.cpp | 13 +++++-- src/fish_tests.cpp | 14 ++++++- src/tokenizer.cpp | 46 +++++++++++++++++------ tests/invocation/features-nocaret3.invoke | 1 + tests/invocation/features-nocaret3.out | 1 + tests/invocation/features-nocaret4.invoke | 1 + tests/invocation/features-nocaret4.out | 1 + 8 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 tests/invocation/features-nocaret3.invoke create mode 100644 tests/invocation/features-nocaret3.out create mode 100644 tests/invocation/features-nocaret4.invoke create mode 100644 tests/invocation/features-nocaret4.out diff --git a/CHANGELOG.md b/CHANGELOG.md index 544cecbfe..2ff721e82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,6 @@ This section is for changes merged to the `major` branch that are not also merge - Successive commas in brace expansions are handled in less surprising manner (`{,,,}` expands to four empty strings rather than an empty string, a comma and an empty string again). (#3002, #4632). - `%` is no longer used for process and job expansion. `$fish_pid` and `$last_pid` have taken the place of `%self` and `%last` respectively. (#4230, #1202) - The new `math` builtin (see below) does not support logical expressions; `test` should be used instead (#4777). -- The `?` wildcard has been removed (#4520). -- The `^` caret redirection for stderr has been removed (#4394). To redirect stderr, `2>/some/path` may be used, or `2>|` as a pipe. ## Notable fixes and improvements - `wait` builtin is added for waiting on processes (#4498). @@ -56,6 +54,7 @@ This section is for changes merged to the `major` branch that are not also merge - Variables set in `if` and `while` conditions are available outside the block (#4820). - The universal variables file no longer contains the MAC address. It is now at the fixed location `.config/fish/fish_universal_variables` (#1912). - `alias` now has a `-s` and `--save` option to save the function generated by the alias using `funcsave` (#4878). +- The `?` wildcard has been removed (#4520). ## Other significant changes - Command substitution output is now limited to 10 MB by default (#3822). diff --git a/src/common.cpp b/src/common.cpp index d7182e6b6..cedf0fc72 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -38,6 +38,7 @@ #include "env.h" #include "expand.h" #include "fallback.h" // IWYU pragma: keep +#include "future_feature_flags.h" #include "proc.h" #include "wildcard.h" #include "wutil.h" // IWYU pragma: keep @@ -928,9 +929,10 @@ static bool unescape_string_var(const wchar_t *in, wcstring *out) { static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring &out, escape_flags_t flags) { const wchar_t *in = orig_in; - bool escape_all = static_cast<bool>(flags & ESCAPE_ALL); - bool no_quoted = static_cast<bool>(flags & ESCAPE_NO_QUOTED); - bool no_tilde = static_cast<bool>(flags & ESCAPE_NO_TILDE); + const bool escape_all = static_cast<bool>(flags & ESCAPE_ALL); + const bool no_quoted = static_cast<bool>(flags & ESCAPE_NO_QUOTED); + const bool no_tilde = static_cast<bool>(flags & ESCAPE_NO_TILDE); + const bool no_caret = fish_features().test(features_t::stderr_nocaret); int need_escape = 0; int need_complex_escape = 0; @@ -1003,10 +1005,12 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring out += L"**"; break; } + case L'&': case L'$': case L' ': case L'#': + case L'^': case L'<': case L'>': case L'(': @@ -1020,7 +1024,8 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring case L';': case L'"': case L'~': { - if (!no_tilde || c != L'~') { + bool char_is_normal = (c == L'~' && no_tilde) || (c == L'^' && no_caret); + if (!char_is_normal) { need_escape = 1; if (escape_all) out += L'\\'; } diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index 8a97ea4a4..debff8791 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -535,7 +535,7 @@ static void test_tokenizer() { const wchar_t *str = L"string <redirection 2>&1 'nested \"quoted\" '(string containing subshells " - L"){and,brackets}$as[$well (as variable arrays)] not_a_redirect^ 2> 2>^is_a_redirect " + L"){and,brackets}$as[$well (as variable arrays)] not_a_redirect^ ^ ^^is_a_redirect " L"&&& ||| " L"&& || & |" L"Compress_Newlines\n \n\t\n \nInto_Just_One"; @@ -609,6 +609,8 @@ static void test_tokenizer() { // Test redirection_type_for_string. if (redirection_type_for_string(L"<") != redirection_type_t::input) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + if (redirection_type_for_string(L"^") != redirection_type_t::overwrite) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); if (redirection_type_for_string(L">") != redirection_type_t::overwrite) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); if (redirection_type_for_string(L"2>") != redirection_type_t::overwrite) @@ -625,6 +627,16 @@ static void test_tokenizer() { err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); if (redirection_type_for_string(L"2>|")) err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + + // Test ^ with our feature flag on and off. + auto saved_flags = fish_features(); + mutable_fish_features().set(features_t::stderr_nocaret, false); + if (redirection_type_for_string(L"^") != redirection_type_t::overwrite) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + mutable_fish_features().set(features_t::stderr_nocaret, true); + if (redirection_type_for_string(L"^") != none()) + err(L"redirection_type_for_string failed on line %ld", (long)__LINE__); + mutable_fish_features() = saved_flags; } // Little function that runs in a background thread, bouncing to the main. diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 75d4ce71f..1bbc784f0 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -13,6 +13,7 @@ #include "common.h" #include "fallback.h" // IWYU pragma: keep +#include "future_feature_flags.h" #include "tokenizer.h" #include "wutil.h" // IWYU pragma: keep @@ -34,6 +35,9 @@ const wchar_t *tokenizer_error::Message() const { return _(_message); } +// Whether carets redirect stderr. +static bool caret_redirs() { return !fish_features().test(features_t::stderr_nocaret); } + /// Return an error token and mark that we no longer have a next token. tok_t tokenizer_t::call_error(tokenizer_error *error_type, const wchar_t *token_start, const wchar_t *error_loc) { @@ -70,8 +74,10 @@ bool tokenizer_t::next(struct tok_t *result) { return true; } -/// Tests if this character can be a part of a string. -static bool tok_is_string_character(wchar_t c) { +/// Tests if this character can be a part of a string. The redirect ^ is allowed unless it's the +/// first character. Hash (#) starts a comment if it's the first character in a token; otherwise it +/// is considered a string character. See issue #953. +static bool tok_is_string_character(wchar_t c, bool is_first) { switch (c) { case L'\0': case L' ': @@ -82,9 +88,15 @@ static bool tok_is_string_character(wchar_t c) { case L'\r': case L'<': case L'>': - case L'&': + case L'&': { + // Unconditional separators. return false; - default: return true; + } + case L'^': { + // Conditional separator. + return !caret_redirs() || !is_first; + } + default: { return true; } } } @@ -109,6 +121,7 @@ tok_t tokenizer_t::read_string() { std::vector<char> expecting; int slice_offset = 0; const wchar_t *const buff_start = this->buff; + bool is_first = true; while (true) { wchar_t c = *this->buff; @@ -209,8 +222,7 @@ tok_t tokenizer_t::read_string() { } break; } - } - else if (mode == tok_mode::regular_text && !tok_is_string_character(c)) { + } else if (mode == tok_mode::regular_text && !tok_is_string_character(c, is_first)) { break; } @@ -224,6 +236,7 @@ tok_t tokenizer_t::read_string() { #endif this->buff++; + is_first = false; } if ((!this->accept_unfinished) && (mode != tok_mode::regular_text)) { @@ -282,7 +295,7 @@ static maybe_t<parsed_redir_or_pipe_t> read_redirection_or_fd_pipe(const wchar_t size_t idx = 0; // Determine the fd. This may be specified as a prefix like '2>...' or it may be implicit like - // '>'. Try parsing out a number; if we did not get any digits then infer it from the + // '>' or '^'. Try parsing out a number; if we did not get any digits then infer it from the // first character. Watch out for overflow. long long big_fd = 0; for (; iswdigit(buff[idx]); idx++) { @@ -303,6 +316,14 @@ static maybe_t<parsed_redir_or_pipe_t> read_redirection_or_fd_pipe(const wchar_t result.fd = STDIN_FILENO; break; } + case L'^': { + if (caret_redirs()) { + result.fd = STDERR_FILENO; + } else { + errored = true; + } + break; + } default: { errored = true; break; @@ -311,11 +332,12 @@ static maybe_t<parsed_redir_or_pipe_t> read_redirection_or_fd_pipe(const wchar_t } // Either way we should have ended on the redirection character itself like '>'. + // Don't allow an fd with a caret redirection - see #1873 wchar_t redirect_char = buff[idx++]; // note increment of idx - if (redirect_char == L'>') { + if (redirect_char == L'>' || (redirect_char == L'^' && idx == 1 && caret_redirs())) { result.redirection_mode = redirection_type_t::overwrite; if (buff[idx] == redirect_char) { - // Doubled up like >>. That means append. + // Doubled up like ^^ or >>. That means append. result.redirection_mode = redirection_type_t::append; idx++; } @@ -507,7 +529,7 @@ maybe_t<tok_t> tokenizer_t::tok_next() { // Maybe a redirection like '2>&1', maybe a pipe like 2>|, maybe just a string. const wchar_t *error_location = this->buff; maybe_t<parsed_redir_or_pipe_t> redir_or_pipe; - if (iswdigit(*this->buff)) { + if (iswdigit(*this->buff) || (*this->buff == L'^' && caret_redirs())) { redir_or_pipe = read_redirection_or_fd_pipe(this->buff); } @@ -599,7 +621,9 @@ bool move_word_state_machine_t::consume_char_punctuation(wchar_t c) { } bool move_word_state_machine_t::is_path_component_character(wchar_t c) { - return tok_is_string_character(c) && !wcschr(L"/={,}'\"", c); + // Always treat separators as first. All this does is ensure that we treat ^ as a string + // character instead of as stderr redirection, which I hypothesize is usually what is desired. + return tok_is_string_character(c, true) && !wcschr(L"/={,}'\"", c); } bool move_word_state_machine_t::consume_char_path_components(wchar_t c) { diff --git a/tests/invocation/features-nocaret3.invoke b/tests/invocation/features-nocaret3.invoke new file mode 100644 index 000000000..ede2fb3d4 --- /dev/null +++ b/tests/invocation/features-nocaret3.invoke @@ -0,0 +1 @@ +--features 'no-stderr-nocaret' -c 'echo -n careton:; echo ^/dev/null' diff --git a/tests/invocation/features-nocaret3.out b/tests/invocation/features-nocaret3.out new file mode 100644 index 000000000..7a1fba14c --- /dev/null +++ b/tests/invocation/features-nocaret3.out @@ -0,0 +1 @@ +careton: diff --git a/tests/invocation/features-nocaret4.invoke b/tests/invocation/features-nocaret4.invoke new file mode 100644 index 000000000..bf38ca7ee --- /dev/null +++ b/tests/invocation/features-nocaret4.invoke @@ -0,0 +1 @@ +--features ' stderr-nocaret' -c 'echo -n "caretoff: "; echo ^/dev/null' diff --git a/tests/invocation/features-nocaret4.out b/tests/invocation/features-nocaret4.out new file mode 100644 index 000000000..dfd96bbf7 --- /dev/null +++ b/tests/invocation/features-nocaret4.out @@ -0,0 +1 @@ +caretoff: ^/dev/null From d3201ad8872d9c9c0e64bf1d624974488a8018fd Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Tue, 24 Apr 2018 16:48:13 -0700 Subject: [PATCH 133/159] Set features from the environment This enables users to opt in (or out) of specific features by setting the fish_features environment variable. For example `set -U fish_features stderr-nocaret` to opt into removing the caret redirection. --- src/fish.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/fish.cpp b/src/fish.cpp index 431739928..baeab0dd0 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -384,6 +384,13 @@ int main(int argc, char **argv) { const struct config_paths_t paths = determine_config_directory_paths(argv[0]); env_init(&paths); // Set features early in case other initialization depends on them. + // Start with the ones set in the environment, then those set on the command line (so the + // command line takes precedence). + if (auto features_var = env_get(L"fish_features")) { + for (const wcstring &s : features_var->as_list()) { + mutable_fish_features().set_from_string(s); + } + } mutable_fish_features().set_from_string(opts.features); proc_init(); builtin_init(); From dc8d603f988218b6e4fe9dba77f3b62323883182 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Sun, 29 Apr 2018 01:57:45 -0700 Subject: [PATCH 134/159] Add a qmark-noglob feature flag This adds a feature flag for controlling whether question marks are globs. It is not yet hooked up. --- src/future_feature_flags.cpp | 1 + src/future_feature_flags.h | 23 +++++++++++++---------- tests/status.out | 1 + 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/future_feature_flags.cpp b/src/future_feature_flags.cpp index 276ea32f2..2cafa4533 100644 --- a/src/future_feature_flags.cpp +++ b/src/future_feature_flags.cpp @@ -12,6 +12,7 @@ features_t &mutable_fish_features() { return global_features; } const features_t::metadata_t features_t::metadata[features_t::flag_count] = { {stderr_nocaret, L"stderr-nocaret", L"3.0", L"^ no longer redirects stderr"}, + {qmark_noglob, L"qmark-noglob", L"3.0", L"? no longer globs"}, }; const struct features_t::metadata_t *features_t::metadata_for(const wchar_t *name) { diff --git a/src/future_feature_flags.h b/src/future_feature_flags.h index 6765252e9..2f9aec788 100644 --- a/src/future_feature_flags.h +++ b/src/future_feature_flags.h @@ -10,18 +10,21 @@ class features_t { public: /// The list of flags. - enum flag_t { - /// Whether ^ is supported for stderr redirection. - stderr_nocaret, + enum flag_t { + /// Whether ^ is supported for stderr redirection. + stderr_nocaret, - /// The number of flags. - flag_count - }; + /// Whether ? is supported as a glob. + qmark_noglob, - /// Return whether a flag is set. - bool test(flag_t f) const { - assert(f >= 0 && f < flag_count && "Invalid flag"); - return values[f]; + /// The number of flags. + flag_count + }; + + /// Return whether a flag is set. + bool test(flag_t f) const { + assert(f >= 0 && f < flag_count && "Invalid flag"); + return values[f]; } /// Set a flag. diff --git a/tests/status.out b/tests/status.out index 1553d674a..ea2ab205e 100644 --- a/tests/status.out +++ b/tests/status.out @@ -5,5 +5,6 @@ test_function #################### # Future Feature Flags stderr-nocaret off 3.0 ^ no longer redirects stderr +qmark-noglob off 3.0 ? no longer globs 1 2 From 762c31be87a951d601700d4e64ce9e5ef73c8798 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Sat, 5 May 2018 19:11:57 -0700 Subject: [PATCH 135/159] Feature flag support for ? wildcard This partially reverts 6e56637cf0a6036fdea4105e9f33314a3d6e8e78 and #4520 by bringing back the ? wildcard, guarded by the qmark-noglob feature flag. --- doc_src/index.hdr.in | 9 +++- share/completions/git.fish | 6 ++- src/common.cpp | 15 +++++- src/expand.cpp | 4 ++ src/fish_tests.cpp | 69 ++++++++++++++----------- src/highlight.cpp | 8 +++ src/parse_util.cpp | 11 +++- src/wildcard.cpp | 28 +++++++--- src/wildcard.h | 4 +- tests/invocation/features-qmark1.invoke | 1 + tests/invocation/features-qmark1.out | 1 + tests/invocation/features-qmark2.invoke | 1 + tests/invocation/features-qmark2.out | 1 + 13 files changed, 117 insertions(+), 41 deletions(-) create mode 100644 tests/invocation/features-qmark1.invoke create mode 100644 tests/invocation/features-qmark1.out create mode 100644 tests/invocation/features-qmark2.invoke create mode 100644 tests/invocation/features-qmark2.out diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 70757d7ab..f37259e74 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -92,6 +92,7 @@ Some characters can not be written directly on the command line. For these chara - '<code>\\$</code>' escapes the dollar character - '<code>\\\\</code>' escapes the backslash character - '<code>\\*</code>' escapes the star character +- '<code>\\?</code>' escapes the question mark character - '<code>\\~</code>' escapes the tilde character - '<code>\\#</code>' escapes the hash character - '<code>\\(</code>' escapes the left parenthesis character @@ -329,7 +330,7 @@ These are the general purpose tab completions that `fish` provides: - Completion of usernames for tilde expansion. -- Completion of filenames, even on strings with wildcards such as '`*`' and '`**`'. +- Completion of filenames, even on strings with wildcards such as '`*`', '`**`' and '`?`'. `fish` provides a large number of program specific completions. Most of these completions are simple options like the `-l` option for `ls`, but some are more advanced. The latter include: @@ -417,7 +418,9 @@ When an argument for a program is given on the commandline, it undergoes the pro \subsection expand-wildcard Wildcards -If a star (`*`) is present in the parameter, `fish` attempts to match the given parameter to any files in such a way that: +If a star (`*`) or a question mark (`?`) is present in the parameter, `fish` attempts to match the given parameter to any files in such a way that: + +- `?` can match any single character except '/'. - `*` can match any string of characters not containing '/'. This includes matching an empty string. @@ -443,6 +446,8 @@ Examples: - `a*` matches any files beginning with an 'a' in the current directory. +- `???` matches any file in the current directory whose name is exactly three characters long. + - `**` matches any files and directories in the current directory and all of its subdirectories. Note that for most commands, if any wildcard fails to expand, the command is not executed, <a href='#variables-status'>`$status`</a> is set to nonzero, and a warning is printed. This behavior is consistent with setting `shopt -s failglob` in bash. There are exactly 3 exceptions, namely <a href="commands.html#set">`set`</a>, <a href="commands.html#count">`count`</a> and <a href="commands.html#for">`for`</a>. Their globs are permitted to expand to zero arguments, as with `shopt -s nullglob` in bash. diff --git a/share/completions/git.fish b/share/completions/git.fish index 49ce8da59..8608808e1 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -127,7 +127,11 @@ function __fish_git_files # Be careful about the ordering here! # # HACK: To allow this to work both with and without '?' globs - set -l dq '??' + set -l dq '\\?\\?' + if status test-feature qmark-noglob + # ? is not a glob + set dq '??' + end switch "$stat" case DD AU UD UA DU AA UU # Unmerged diff --git a/src/common.cpp b/src/common.cpp index cedf0fc72..83f2fae04 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -933,6 +933,7 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring const bool no_quoted = static_cast<bool>(flags & ESCAPE_NO_QUOTED); const bool no_tilde = static_cast<bool>(flags & ESCAPE_NO_TILDE); const bool no_caret = fish_features().test(features_t::stderr_nocaret); + const bool no_qmark = fish_features().test(features_t::qmark_noglob); int need_escape = 0; int need_complex_escape = 0; @@ -997,6 +998,11 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring out += *in; break; } + case ANY_CHAR: { + // See #1614 + out += L'?'; + break; + } case ANY_STRING: { out += L'*'; break; @@ -1019,12 +1025,13 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring case L']': case L'{': case L'}': + case L'?': case L'*': case L'|': case L';': case L'"': case L'~': { - bool char_is_normal = (c == L'~' && no_tilde) || (c == L'^' && no_caret); + bool char_is_normal = (c == L'~' && no_tilde) || (c == L'^' && no_caret) || (c == L'?' && no_qmark); if (!char_is_normal) { need_escape = 1; if (escape_all) out += L'\\'; @@ -1353,6 +1360,12 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in } break; } + case L'?': { + if (unescape_special && !fish_features().test(features_t::qmark_noglob)) { + to_append_or_none = ANY_CHAR; + } + break; + } case L'$': { if (unescape_special) { to_append_or_none = VARIABLE_EXPAND; diff --git a/src/expand.cpp b/src/expand.cpp index 5c2dfc09a..80a94c066 100644 --- a/src/expand.cpp +++ b/src/expand.cpp @@ -828,6 +828,10 @@ static void remove_internal_separator(wcstring *str, bool conv) { if (conv) { for (size_t idx = 0; idx < str->size(); idx++) { switch (str->at(idx)) { + case ANY_CHAR: { + str->at(idx) = L'?'; + break; + } case ANY_STRING: case ANY_STRING_RECURSIVE: { str->at(idx) = L'*'; diff --git a/src/fish_tests.cpp b/src/fish_tests.cpp index debff8791..e70f4eeb1 100644 --- a/src/fish_tests.cpp +++ b/src/fish_tests.cpp @@ -4093,7 +4093,7 @@ static void test_wcstring_tok() { } int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv); -static void run_one_string_test(const wchar_t **argv, int expected_rc, +static void run_one_string_test(const wchar_t *const *argv, int expected_rc, const wchar_t *expected_out) { parser_t parser; io_streams_t streams(0); @@ -4115,7 +4115,7 @@ static void run_one_string_test(const wchar_t **argv, int expected_rc, } static void test_string() { - static struct string_test { + const struct string_test { const wchar_t *argv[15]; int expected_rc; const wchar_t *expected_out; @@ -4159,16 +4159,15 @@ static void test_string() { {{L"string", L"match", 0}, STATUS_INVALID_ARGS, L""}, {{L"string", L"match", L"", 0}, STATUS_CMD_ERROR, L""}, {{L"string", L"match", L"", L"", 0}, STATUS_CMD_OK, L"\n"}, - {{L"string", L"match", L"?", L"a", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"?", L"a", 0}, STATUS_CMD_OK, L"a\n"}, {{L"string", L"match", L"*", L"", 0}, STATUS_CMD_OK, L"\n"}, {{L"string", L"match", L"**", L"", 0}, STATUS_CMD_OK, L"\n"}, {{L"string", L"match", L"*", L"xyzzy", 0}, STATUS_CMD_OK, L"xyzzy\n"}, {{L"string", L"match", L"**", L"plugh", 0}, STATUS_CMD_OK, L"plugh\n"}, {{L"string", L"match", L"a*b", L"axxb", 0}, STATUS_CMD_OK, L"axxb\n"}, - {{L"string", L"match", L"a??b", L"axxb", 0}, STATUS_CMD_ERROR, L""}, - {{L"string", L"match", L"a??b", L"a??b", 0}, STATUS_CMD_OK, L"a??b\n"}, - {{L"string", L"match", L"-i", L"a??B", L"axxb", 0}, STATUS_CMD_ERROR, L""}, - {{L"string", L"match", L"-i", L"a??b", L"Axxb", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"a??b", L"axxb", 0}, STATUS_CMD_OK, L"axxb\n"}, + {{L"string", L"match", L"-i", L"a??B", L"axxb", 0}, STATUS_CMD_OK, L"axxb\n"}, + {{L"string", L"match", L"-i", L"a??b", L"Axxb", 0}, STATUS_CMD_OK, L"Axxb\n"}, {{L"string", L"match", L"a*", L"axxb", 0}, STATUS_CMD_OK, L"axxb\n"}, {{L"string", L"match", L"*a", L"xxa", 0}, STATUS_CMD_OK, L"xxa\n"}, {{L"string", L"match", L"*a*", L"axa", 0}, STATUS_CMD_OK, L"axa\n"}, @@ -4177,14 +4176,9 @@ static void test_string() { {{L"string", L"match", L"*a", L"a", 0}, STATUS_CMD_OK, L"a\n"}, {{L"string", L"match", L"a*", L"a", 0}, STATUS_CMD_OK, L"a\n"}, {{L"string", L"match", L"a*b*c", L"axxbyyc", 0}, STATUS_CMD_OK, L"axxbyyc\n"}, - {{L"string", L"match", L"a*b?c", L"axxb?c", 0}, STATUS_CMD_OK, L"axxb?c\n"}, - {{L"string", L"match", L"*?", L"a", 0}, STATUS_CMD_ERROR, L""}, - {{L"string", L"match", L"*?", L"ab", 0}, STATUS_CMD_ERROR, L""}, - {{L"string", L"match", L"?*", L"a", 0}, STATUS_CMD_ERROR, L""}, - {{L"string", L"match", L"?*", L"ab", 0}, STATUS_CMD_ERROR, L""}, {{L"string", L"match", L"\\*", L"*", 0}, STATUS_CMD_OK, L"*\n"}, {{L"string", L"match", L"a*\\", L"abc\\", 0}, STATUS_CMD_OK, L"abc\\\n"}, - {{L"string", L"match", L"a*\\?", L"abc?", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"a*\\?", L"abc?", 0}, STATUS_CMD_OK, L"abc?\n"}, {{L"string", L"match", L"?", L"", 0}, STATUS_CMD_ERROR, L""}, {{L"string", L"match", L"?", L"ab", 0}, STATUS_CMD_ERROR, L""}, @@ -4403,15 +4397,36 @@ static void test_string() { {{L"string", L"trim", L"-c", L"\\/", L"/a\\"}, STATUS_CMD_OK, L"a\n"}, {{L"string", L"trim", L"-c", L"\\/", L"a/"}, STATUS_CMD_OK, L"a\n"}, {{L"string", L"trim", L"-c", L"\\/", L"\\a/"}, STATUS_CMD_OK, L"a\n"}, - {{L"string", L"trim", L"-c", L"", L".a."}, STATUS_CMD_ERROR, L".a.\n"}, - - {{NULL}, STATUS_CMD_ERROR, NULL}}; - - struct string_test *t = string_tests; - while (t->argv[0]) { - run_one_string_test(t->argv, t->expected_rc, t->expected_out); - t++; + {{L"string", L"trim", L"-c", L"", L".a."}, STATUS_CMD_ERROR, L".a.\n"}}; + for (const auto &t : string_tests) { + run_one_string_test(t.argv, t.expected_rc, t.expected_out); } + + const auto saved_flags = fish_features(); + const struct string_test qmark_noglob_tests[] = { + {{L"string", L"match", L"a*b?c", L"axxb?c", 0}, STATUS_CMD_OK, L"axxb?c\n"}, + {{L"string", L"match", L"*?", L"a", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"*?", L"ab", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"?*", L"a", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"?*", L"ab", 0}, STATUS_CMD_ERROR, L""}, + {{L"string", L"match", L"a*\\?", L"abc?", 0}, STATUS_CMD_ERROR, L""}}; + mutable_fish_features().set(features_t::qmark_noglob, true); + for (const auto &t : qmark_noglob_tests) { + run_one_string_test(t.argv, t.expected_rc, t.expected_out); + } + + const struct string_test qmark_glob_tests[] = { + {{L"string", L"match", L"a*b?c", L"axxbyc", 0}, STATUS_CMD_OK, L"axxbyc\n"}, + {{L"string", L"match", L"*?", L"a", 0}, STATUS_CMD_OK, L"a\n"}, + {{L"string", L"match", L"*?", L"ab", 0}, STATUS_CMD_OK, L"ab\n"}, + {{L"string", L"match", L"?*", L"a", 0}, STATUS_CMD_OK, L"a\n"}, + {{L"string", L"match", L"?*", L"ab", 0}, STATUS_CMD_OK, L"ab\n"}, + {{L"string", L"match", L"a*\\?", L"abc?", 0}, STATUS_CMD_OK, L"abc?\n"}}; + mutable_fish_features().set(features_t::qmark_noglob, false); + for (const auto &t : qmark_glob_tests) { + run_one_string_test(t.argv, t.expected_rc, t.expected_out); + } + mutable_fish_features() = saved_flags; } /// Helper for test_timezone_env_vars(). @@ -4468,15 +4483,11 @@ static void test_illegal_command_exit_code() { }; const command_result_tuple_t tests[] = { - {L"echo -n", STATUS_CMD_OK}, - {L"pwd", STATUS_CMD_OK}, - // a `)` without a matching `(` is now a tokenizer error, and cannot be executed even as an - // illegal command + {L"echo -n", STATUS_CMD_OK}, {L"pwd", STATUS_CMD_OK}, + // a `)` without a matching `(` is now a tokenizer error, and cannot be executed even as an illegal command // {L")", STATUS_ILLEGAL_CMD}, {L") ", STATUS_ILLEGAL_CMD}, {L") ", STATUS_ILLEGAL_CMD} - {L"*", STATUS_ILLEGAL_CMD}, - {L"**", STATUS_ILLEGAL_CMD}, - {L"?", STATUS_CMD_UNKNOWN}, - {L"abc?def", STATUS_CMD_UNKNOWN}, + {L"*", STATUS_ILLEGAL_CMD}, {L"**", STATUS_ILLEGAL_CMD}, + {L"?", STATUS_ILLEGAL_CMD}, {L"abc?def", STATUS_ILLEGAL_CMD}, }; int res = 0; diff --git a/src/highlight.cpp b/src/highlight.cpp index 8cb8b2f91..782725b1f 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -23,6 +23,7 @@ #include "expand.h" #include "fallback.h" // IWYU pragma: keep #include "function.h" +#include "future_feature_flags.h" #include "highlight.h" #include "history.h" #include "output.h" @@ -125,6 +126,7 @@ bool is_potential_path(const wcstring &potential_path_fragment, const wcstring_l case BRACE_BEGIN: case BRACE_END: case BRACE_SEP: + case ANY_CHAR: case ANY_STRING: case ANY_STRING_RECURSIVE: { has_magic = 1; @@ -547,6 +549,12 @@ static void color_argument_internal(const wcstring &buffstr, in_pos -= 1; break; } + case L'?': { + if (!fish_features().test(features_t::qmark_noglob)) { + colors[in_pos] = highlight_spec_operator; + } + break; + } case L'*': case L'(': case L')': { diff --git a/src/parse_util.cpp b/src/parse_util.cpp index a40e9cc20..4fb9e79d8 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -16,6 +16,7 @@ #include "common.h" #include "expand.h" #include "fallback.h" // IWYU pragma: keep +#include "future_feature_flags.h" #include "parse_constants.h" #include "parse_util.h" #include "tnode.h" @@ -418,14 +419,20 @@ void parse_util_token_extent(const wchar_t *buff, size_t cursor_pos, const wchar wcstring parse_util_unescape_wildcards(const wcstring &str) { wcstring result; result.reserve(str.size()); + bool unesc_qmark = !fish_features().test(features_t::qmark_noglob); const wchar_t *const cs = str.c_str(); for (size_t i = 0; cs[i] != L'\0'; i++) { if (cs[i] == L'*') { result.push_back(ANY_STRING); + } else if (cs[i] == L'?' && unesc_qmark) { + result.push_back(ANY_CHAR); } else if (cs[i] == L'\\' && cs[i + 1] == L'*') { result.push_back(cs[i + 1]); i += 1; + } else if (cs[i] == L'\\' && cs[i + 1] == L'?' && unesc_qmark) { + result.push_back(cs[i + 1]); + i += 1; } else if (cs[i] == L'\\' && cs[i + 1] == L'\\') { // Not a wildcard, but ensure the next iteration doesn't see this escaped backslash. result.append(L"\\\\"); @@ -890,7 +897,9 @@ void parse_util_expand_variable_error(const wcstring &token, size_t global_token default: { wchar_t token_stop_char = char_after_dollar; // Unescape (see issue #50). - if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE) + if (token_stop_char == ANY_CHAR) + token_stop_char = L'?'; + else if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE) token_stop_char = L'*'; // Determine which error message to use. The format string may not consume all the diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 27d115b93..05f551f1a 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -19,6 +19,7 @@ #include "complete.h" #include "expand.h" #include "fallback.h" // IWYU pragma: keep +#include "future_feature_flags.h" #include "reader.h" #include "wildcard.h" #include "wutil.h" // IWYU pragma: keep @@ -51,7 +52,7 @@ /// Finds an internal (ANY_STRING, etc.) style wildcard, or wcstring::npos. static size_t wildcard_find(const wchar_t *wc) { for (size_t i = 0; wc[i] != L'\0'; i++) { - if (wc[i] == ANY_STRING || wc[i] == ANY_STRING_RECURSIVE) { + if (wc[i] == ANY_CHAR || wc[i] == ANY_STRING || wc[i] == ANY_STRING_RECURSIVE) { return i; } } @@ -61,15 +62,17 @@ static size_t wildcard_find(const wchar_t *wc) { /// Implementation of wildcard_has. Needs to take the length to handle embedded nulls (issue #1631). static bool wildcard_has_impl(const wchar_t *str, size_t len, bool internal) { assert(str != NULL); + bool qmark_is_wild = !fish_features().test(features_t::qmark_noglob); const wchar_t *end = str + len; if (internal) { for (; str < end; str++) { - if (*str == ANY_STRING || *str == ANY_STRING_RECURSIVE) return true; + if ((*str == ANY_CHAR) || (*str == ANY_STRING) || (*str == ANY_STRING_RECURSIVE)) + return true; } } else { wchar_t prev = 0; for (; str < end; str++) { - if (*str == L'*' && prev != L'\\') return true; + if (((*str == L'*') || (*str == L'?' && qmark_is_wild)) && (prev != L'\\')) return true; prev = *str; } } @@ -127,6 +130,13 @@ static enum fuzzy_match_type_t wildcard_match_internal(const wchar_t *str, const restart_is_out_of_str = (*str_x == 0); wc_x++; continue; + } else if (*wc_x == ANY_CHAR && *str_x != 0) { + if (is_first && *str_x == L'.') { + return fuzzy_match_none; + } + wc_x++; + str_x++; + continue; } else if (*str_x != 0 && *str_x == *wc_x) { // ordinary character wc_x++; str_x++; @@ -204,7 +214,7 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, return false; } - // Locate the next wildcard character position, e.g. ANY_STRING. + // Locate the next wildcard character position, e.g. ANY_CHAR or ANY_STRING. const size_t next_wc_char_pos = wildcard_find(wc); // Maybe we have no more wildcards at all. This includes the empty string. @@ -257,6 +267,12 @@ static bool wildcard_complete_internal(const wchar_t *str, const wchar_t *wc, // Our first character is a wildcard. assert(next_wc_char_pos == 0); switch (wc[0]) { + case ANY_CHAR: { + if (str[0] == L'\0') { + return false; + } + return wildcard_complete_internal(str + 1, wc + 1, params, flags, out); + } case ANY_STRING: { // Hackish. If this is the last character of the wildcard, then just complete with // the empty string. This fixes cases like "f*<tab>" -> "f*o". @@ -779,7 +795,7 @@ void wildcard_expander_t::expand_last_segment(const wcstring &base_dir, DIR *bas /// /// Args: /// base_dir: the "working directory" against which the wildcard is to be resolved -/// wc: the wildcard string itself, e.g. foo*bar/baz (where * is acutally ANY_STRING) +/// wc: the wildcard string itself, e.g. foo*bar/baz (where * is acutally ANY_CHAR) /// prefix: the string that should be prepended for completions that replace their token. // This is usually the same thing as the original wildcard, but for fuzzy matching, we // expand intermediate segments. effective_prefix is always either empty, or ends with a slash @@ -800,7 +816,7 @@ void wildcard_expander_t::expand(const wcstring &base_dir, const wchar_t *wc, const size_t wc_segment_len = next_slash ? next_slash - wc : wc_len; const wcstring wc_segment = wcstring(wc, wc_segment_len); const bool segment_has_wildcards = - wildcard_has(wc_segment, true /* internal, i.e. look for ANY_STRING instead of * */); + wildcard_has(wc_segment, true /* internal, i.e. look for ANY_CHAR instead of ? */); const wchar_t *const wc_remainder = next_slash ? next_slash + 1 : NULL; if (wc_segment.empty()) { diff --git a/src/wildcard.h b/src/wildcard.h index 614f28b83..5229ee027 100644 --- a/src/wildcard.h +++ b/src/wildcard.h @@ -11,8 +11,10 @@ // Enumeration of all wildcard types. enum { + /// Character representing any character except '/' (slash). + ANY_CHAR = WILDCARD_RESERVED_BASE, /// Character representing any character string not containing '/' (slash). - ANY_STRING = WILDCARD_RESERVED_BASE, + ANY_STRING, /// Character representing any character string. ANY_STRING_RECURSIVE, /// This is a special psuedo-char that is not used other than to mark the diff --git a/tests/invocation/features-qmark1.invoke b/tests/invocation/features-qmark1.invoke new file mode 100644 index 000000000..25b7db2d0 --- /dev/null +++ b/tests/invocation/features-qmark1.invoke @@ -0,0 +1 @@ +--features '' -c 'string match --quiet "??" ab ; echo "qmarkon: $status"' diff --git a/tests/invocation/features-qmark1.out b/tests/invocation/features-qmark1.out new file mode 100644 index 000000000..5a27d467d --- /dev/null +++ b/tests/invocation/features-qmark1.out @@ -0,0 +1 @@ +qmarkon: 0 diff --git a/tests/invocation/features-qmark2.invoke b/tests/invocation/features-qmark2.invoke new file mode 100644 index 000000000..5db93ac03 --- /dev/null +++ b/tests/invocation/features-qmark2.invoke @@ -0,0 +1 @@ +--features 'qmark-noglob' -C 'string match --quiet "??" ab ; echo "qmarkoff: $status"' diff --git a/tests/invocation/features-qmark2.out b/tests/invocation/features-qmark2.out new file mode 100644 index 000000000..57cedbb0b --- /dev/null +++ b/tests/invocation/features-qmark2.out @@ -0,0 +1 @@ +qmarkoff: 1 From 4194b4efee5ae2943dd7ff8e2f0df47e4d7d44b2 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Sat, 5 May 2018 19:44:57 -0700 Subject: [PATCH 136/159] Add a feature_test() function This is a convenience over fish_features().test() --- src/builtin_status.cpp | 4 ++-- src/common.cpp | 6 +++--- src/future_feature_flags.h | 3 +++ src/highlight.cpp | 2 +- src/parse_util.cpp | 2 +- src/tokenizer.cpp | 2 +- src/wildcard.cpp | 2 +- 7 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/builtin_status.cpp b/src/builtin_status.cpp index 1aecc9621..f2f2ed17b 100644 --- a/src/builtin_status.cpp +++ b/src/builtin_status.cpp @@ -138,7 +138,7 @@ static bool set_status_cmd(wchar_t *const cmd, status_cmd_opts_t &opts, status_c /// Print the features and their values. static void print_features(io_streams_t &streams) { for (const auto &md : features_t::metadata) { - int set = fish_features().test(md.flag); + int set = feature_test(md.flag); streams.out.append_format(L"%ls\t%s\t%ls\t%ls\n", md.name, set ? "on" : "off", md.groups, md.description); } @@ -339,7 +339,7 @@ int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (!metadata) { retval = TEST_FEATURE_NOT_RECOGNIZED; } else { - retval = fish_features().test(metadata->flag) ? TEST_FEATURE_ON : TEST_FEATURE_OFF; + retval = feature_test(metadata->flag) ? TEST_FEATURE_ON : TEST_FEATURE_OFF; } break; } diff --git a/src/common.cpp b/src/common.cpp index 83f2fae04..6462ee811 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -932,8 +932,8 @@ static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring const bool escape_all = static_cast<bool>(flags & ESCAPE_ALL); const bool no_quoted = static_cast<bool>(flags & ESCAPE_NO_QUOTED); const bool no_tilde = static_cast<bool>(flags & ESCAPE_NO_TILDE); - const bool no_caret = fish_features().test(features_t::stderr_nocaret); - const bool no_qmark = fish_features().test(features_t::qmark_noglob); + const bool no_caret = feature_test(features_t::stderr_nocaret); + const bool no_qmark = feature_test(features_t::qmark_noglob); int need_escape = 0; int need_complex_escape = 0; @@ -1361,7 +1361,7 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in break; } case L'?': { - if (unescape_special && !fish_features().test(features_t::qmark_noglob)) { + if (unescape_special && !feature_test(features_t::qmark_noglob)) { to_append_or_none = ANY_CHAR; } break; diff --git a/src/future_feature_flags.h b/src/future_feature_flags.h index 2f9aec788..7ccc7f570 100644 --- a/src/future_feature_flags.h +++ b/src/future_feature_flags.h @@ -68,6 +68,9 @@ class features_t { /// Return the global set of features for fish. This is const to prevent accidental mutation. const features_t &fish_features(); +/// Perform a feature test on the global set of features. +inline bool feature_test(features_t::flag_t f) { return fish_features().test(f); } + /// Return the global set of features for fish, but mutable. In general fish features should be set /// at startup only. features_t &mutable_fish_features(); diff --git a/src/highlight.cpp b/src/highlight.cpp index 782725b1f..5bf286422 100644 --- a/src/highlight.cpp +++ b/src/highlight.cpp @@ -550,7 +550,7 @@ static void color_argument_internal(const wcstring &buffstr, break; } case L'?': { - if (!fish_features().test(features_t::qmark_noglob)) { + if (!feature_test(features_t::qmark_noglob)) { colors[in_pos] = highlight_spec_operator; } break; diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 4fb9e79d8..eb60f25e5 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -419,7 +419,7 @@ void parse_util_token_extent(const wchar_t *buff, size_t cursor_pos, const wchar wcstring parse_util_unescape_wildcards(const wcstring &str) { wcstring result; result.reserve(str.size()); - bool unesc_qmark = !fish_features().test(features_t::qmark_noglob); + bool unesc_qmark = !feature_test(features_t::qmark_noglob); const wchar_t *const cs = str.c_str(); for (size_t i = 0; cs[i] != L'\0'; i++) { diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 1bbc784f0..7f1f86a20 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -36,7 +36,7 @@ const wchar_t *tokenizer_error::Message() const { } // Whether carets redirect stderr. -static bool caret_redirs() { return !fish_features().test(features_t::stderr_nocaret); } +static bool caret_redirs() { return !feature_test(features_t::stderr_nocaret); } /// Return an error token and mark that we no longer have a next token. tok_t tokenizer_t::call_error(tokenizer_error *error_type, const wchar_t *token_start, diff --git a/src/wildcard.cpp b/src/wildcard.cpp index 05f551f1a..e12307495 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -62,7 +62,7 @@ static size_t wildcard_find(const wchar_t *wc) { /// Implementation of wildcard_has. Needs to take the length to handle embedded nulls (issue #1631). static bool wildcard_has_impl(const wchar_t *str, size_t len, bool internal) { assert(str != NULL); - bool qmark_is_wild = !fish_features().test(features_t::qmark_noglob); + bool qmark_is_wild = !feature_test(features_t::qmark_noglob); const wchar_t *end = str + len; if (internal) { for (; str < end; str++) { From 87eb073ff9227d7e0fe1ddfd46dbfbcc9eac9a37 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Sun, 6 May 2018 11:53:14 -0700 Subject: [PATCH 137/159] Remove some references to ^ redirection from the docs Replace these with 2> --- doc_src/index.hdr.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index f37259e74..6ac315435 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -142,11 +142,11 @@ An example of a file redirection is `echo hello > output.txt`, which directs the - To read standard input from a file, write `<SOURCE_FILE` - To write standard output to a file, write `>DESTINATION` -- To write standard error to a file, write `^DESTINATION` +- To write standard error to a file, write `2>DESTINATION` - To append standard output to a file, write `>>DESTINATION_FILE` -- To append standard error to a file, write `^^DESTINATION_FILE` +- To append standard error to a file, write `2>>DESTINATION_FILE` -- To not overwrite ("clobber") an existing file, write '>?DESTINATION' or '^?DESTINATION' +- To not overwrite ("clobber") an existing file, write '>?DESTINATION' or '2>?DESTINATION' `DESTINATION` can be one of the following: @@ -158,7 +158,7 @@ An example of a file redirection is `echo hello > output.txt`, which directs the Example: -To redirect both standard output and standard error to the file 'all_output.txt', you can write `echo Hello > all_output.txt ^&1`. +To redirect both standard output and standard error to the file 'all_output.txt', you can write `echo Hello > all_output.txt 2>&1`. Any file descriptor can be redirected in an arbitrary way by prefixing the redirection with the file descriptor. @@ -166,7 +166,7 @@ Any file descriptor can be redirected in an arbitrary way by prefixing the redir - To redirect output of FD N, write `N>DESTINATION` - To append the output of FD N to a file, write `N>>DESTINATION_FILE` -Example: `echo Hello 2>output.stderr` and `echo Hello ^output.stderr` are equivalent, and write the standard error (file descriptor 2) of the target program to `output.stderr`. +Example: `echo Hello 2>output.stderr` and `echo Hello 2>output.stderr` are equivalent, and write the standard error (file descriptor 2) of the target program to `output.stderr`. \subsection piping Piping From d623ac5040e9bbb83e94d6d8d83b062c2dd8030b Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Sun, 6 May 2018 12:06:06 -0700 Subject: [PATCH 138/159] Add future feature flags to the documentation --- doc_src/index.hdr.in | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/doc_src/index.hdr.in b/doc_src/index.hdr.in index 6ac315435..513b5e3ef 100644 --- a/doc_src/index.hdr.in +++ b/doc_src/index.hdr.in @@ -1247,6 +1247,29 @@ function on_exit --on-process $fish_pid end \endfish +\section featureflags Future feature flags + +Feature flags are how fish stages changes that might break scripts. Breaking changes are introduced as opt-in, in a few releases they become opt-out, and eventually the old behavior is removed. + +You can see the current list of features via `status features`: + +\fish +> status features +stderr-nocaret on 3.0 ^ no longer redirects stderr +qmark-noglob off 3.0 ? no longer globs +\endfish + +There are two breaking changes in fish 3.0: caret `^` no longer redirects stderr, and question mark `?` is no longer a glob. These changes are off by default. They can be enabled on a per session basis: + +\fish +> fish --features qmark-noglob,stderr-nocaret +\endfish + +or opted into globally for a user: + +\fish +> set -U fish_features stderr-nocaret qmark-noglob +\endfish \section other Other features From 060643a3b0561936dcdc5cc3d5b4fbd3cdfd3ab7 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Sun, 6 May 2018 12:31:32 -0700 Subject: [PATCH 139/159] Changelog feature flags --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ff721e82..d31b80e4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This section is for changes merged to the `major` branch that are not also merge - The `IFS` variable is deprecated and will be removed in fish 4.0 (#4156). - The `function --on-process-exit` event will be removed in future (#4700). Use the `fish_exit` event instead. - `$_` is deprecated and will removed in the future (#813). Use `status current-command` in a subshell instead. +- `^` as a redirection deprecated and will be removed in the future. (#4394). Use `2>` to redirect stderr. This is controlled by the `stderr-nocaret` feature flag. +- `?` as a glob is deprecated and will be removed in the future. (#4520). This is controlled by the `qmark-noglob` feature flag. ## Notable non-backward compatible changes - `.` command no longer exists -- use `source` (#4294). @@ -17,6 +19,7 @@ This section is for changes merged to the `major` branch that are not also merge - The new `math` builtin (see below) does not support logical expressions; `test` should be used instead (#4777). ## Notable fixes and improvements +- A new feature flags mechanism is added for staging deprecations and breaking changes. (#4940) - `wait` builtin is added for waiting on processes (#4498). - `read` has a new `--delimiter` option as a better alternative to the `IFS` variable (#4256). - `read` writes directly to stdout if called without arguments (#4407) @@ -54,7 +57,6 @@ This section is for changes merged to the `major` branch that are not also merge - Variables set in `if` and `while` conditions are available outside the block (#4820). - The universal variables file no longer contains the MAC address. It is now at the fixed location `.config/fish/fish_universal_variables` (#1912). - `alias` now has a `-s` and `--save` option to save the function generated by the alias using `funcsave` (#4878). -- The `?` wildcard has been removed (#4520). ## Other significant changes - Command substitution output is now limited to 10 MB by default (#3822). From d7a9e25e17d55eea4377b6fa097afb5a3c036df4 Mon Sep 17 00:00:00 2001 From: "Luc J. Bourhis" <luc_j_bourhis@mac.com> Date: Sun, 6 May 2018 19:06:53 +0200 Subject: [PATCH 140/159] Completion of git difftool and git mergetool: add missing options --- share/completions/git.fish | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 8608808e1..d7a14ca85 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -691,6 +691,15 @@ complete -c git -n '__fish_git_needs_command' -a difftool -d 'Open diffs in a vi complete -c git -n '__fish_git_using_command difftool' -a '(__fish_git_ranges)' complete -c git -n '__fish_git_using_command difftool' -l cached -d 'Visually show diff of changes in the index' complete -f -c git -n '__fish_git_using_command difftool' -a '(__fish_git_files modified deleted)' +complete -f -c git -n '__fish_git_using_command difftool' -s g -l gui -d 'Use `diff.guitool` instead of `diff.tool`' +complete -f -c git -n '__fish_git_using_command difftool' -s d -l dir-diff -d 'Perform a full-directory diff' +complete -c git -n '__fish_git_using_command difftool' -l prompt -d 'Prompt before each invocation of the diff tool' +complete -f -c git -n '__fish_git_using_command difftool' -s y -l no-prompt -d 'Do not prompt before launching a diff tool' +complete -f -c git -n '__fish_git_using_command difftool' -l symlinks -d 'Use symlinks in dir-diff mode' +complete -f -c git -n '__fish_git_using_command difftool' -s t -l tool -d 'Use the specified diff tool' +complete -f -c git -n '__fish_git_using_command difftool' -l tool-help -d 'Print a list of diff tools that may be used with `--tool`' +complete -f -c git -n '__fish_git_using_command difftool' -l trust-exit-code -d 'Exit when an invoked diff tool returns a non-zero exit code' +complete -f -c git -n '__fish_git_using_command difftool' -s x -l extcmd -d 'Specify a custom command for viewing diffs' # TODO options @@ -935,8 +944,11 @@ end complete -f -c git -n '__fish_git_needs_command' -a mergetool -d 'Run merge conflict resolution tools to resolve merge conflicts' complete -f -c git -n '__fish_git_using_command mergetool' -s t -l tool -d "Use specific merge resolution program" -a "(__fish_git_mergetools)" +complete -f -c git -n '__fish_git_using_command mergetool' -l tool-help -d 'Print a list of merge tools that may be used with `--tool`' complete -f -c git -n '__fish_git_using_command mergetool' -a "(__fish_git_files unmerged)" - +complete -f -c git -n '__fish_git_using_command mergetool' -s y -l no-prompt -d 'Do not prompt before launching a diff tool' +complete -f -c git -n '__fish_git_using_command mergetool' -l 'prompt' -d 'Prompt before each invocation of the merge resolution program' +complete -c git -n '__fish_git_using_command mergetool' -s O -d 'Process files in the order specified in the file passed as argument' ### mv complete -c git -n '__fish_git_needs_command' -a mv -d 'Move or rename a file, a directory, or a symlink' From d652b9b606104e26ef2d8763ff4a75275b5affe6 Mon Sep 17 00:00:00 2001 From: "Luc J. Bourhis" <luc_j_bourhis@mac.com> Date: Sun, 6 May 2018 19:53:56 +0200 Subject: [PATCH 141/159] Use --tool-help to find available tools for difftool/mergetool --- share/completions/git.fish | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index d7a14ca85..8a982f326 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -686,6 +686,17 @@ complete -c git -n '__fish_git_using_command diff' -s 3 -l theirs -d 'Compare th complete -c git -n '__fish_git_using_command diff' -s 0 -d 'Omit diff output for unmerged entries and just show "Unmerged"' complete -f -c git -n '__fish_git_using_command diff' -a '(__fish_git_files modified deleted)' +### Function to list available tools for git difftool and mergetool + +function __fish_git_diffmerge_tools -a cmd + git $cmd --tool-help | \ + while read -l line + string match -q 'The following tools are valid, but not currently available:' -- $line + and break + string replace -f -r '^\t\t(\w+).*$' '$1' -- $line + end +end + ### difftool complete -c git -n '__fish_git_needs_command' -a difftool -d 'Open diffs in a visual tool' complete -c git -n '__fish_git_using_command difftool' -a '(__fish_git_ranges)' @@ -696,7 +707,7 @@ complete -f -c git -n '__fish_git_using_command difftool' -s d -l dir-diff -d 'P complete -c git -n '__fish_git_using_command difftool' -l prompt -d 'Prompt before each invocation of the diff tool' complete -f -c git -n '__fish_git_using_command difftool' -s y -l no-prompt -d 'Do not prompt before launching a diff tool' complete -f -c git -n '__fish_git_using_command difftool' -l symlinks -d 'Use symlinks in dir-diff mode' -complete -f -c git -n '__fish_git_using_command difftool' -s t -l tool -d 'Use the specified diff tool' +complete -f -c git -n '__fish_git_using_command difftool' -s t -l tool -d 'Use the specified diff tool' -a "(__fish_git_diffmerge_tools difftool)" complete -f -c git -n '__fish_git_using_command difftool' -l tool-help -d 'Print a list of diff tools that may be used with `--tool`' complete -f -c git -n '__fish_git_using_command difftool' -l trust-exit-code -d 'Exit when an invoked diff tool returns a non-zero exit code' complete -f -c git -n '__fish_git_using_command difftool' -s x -l extcmd -d 'Specify a custom command for viewing diffs' @@ -933,17 +944,8 @@ complete -f -c git -n '__fish_git_using_command merge' -l continue -d 'Conclude ### mergetool -function __fish_git_mergetools - set -l tools diffuse diffmerge ecmerge emerge kdiff3 meld opendiff tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare - for tool in $tools - if command -sq $tool - echo "$tool" - end - end -end - complete -f -c git -n '__fish_git_needs_command' -a mergetool -d 'Run merge conflict resolution tools to resolve merge conflicts' -complete -f -c git -n '__fish_git_using_command mergetool' -s t -l tool -d "Use specific merge resolution program" -a "(__fish_git_mergetools)" +complete -f -c git -n '__fish_git_using_command mergetool' -s t -l tool -d "Use specific merge resolution program" -a "(__fish_git_diffmerge_tools mergetool)" complete -f -c git -n '__fish_git_using_command mergetool' -l tool-help -d 'Print a list of merge tools that may be used with `--tool`' complete -f -c git -n '__fish_git_using_command mergetool' -a "(__fish_git_files unmerged)" complete -f -c git -n '__fish_git_using_command mergetool' -s y -l no-prompt -d 'Do not prompt before launching a diff tool' From 6dc74d3b6cae9178c3707b060712a5b35824a0b5 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi <mqudsi@neosmart.net> Date: Sun, 6 May 2018 18:46:56 -0500 Subject: [PATCH 142/159] New helper functions `__fish_is_first_arg` and `__fish_prev_arg_in` For usage in completion scripts. Unlike `__fish_is_first_token` (which is probably not correctly named), `__fish_is_first_arg` returns true regardless of whether existing tokens start with `-` or not, to be used when an arg cannot be used with any other argument. `__fish_prev_arg_in` is similar to `__fish_seen_...` but it explicitly tests the preceding token only, for arguments that take only a single parameter. --- share/functions/__fish_is_first_arg.fish | 5 +++++ share/functions/__fish_prev_arg_in.fish | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 share/functions/__fish_is_first_arg.fish create mode 100644 share/functions/__fish_prev_arg_in.fish diff --git a/share/functions/__fish_is_first_arg.fish b/share/functions/__fish_is_first_arg.fish new file mode 100644 index 000000000..b026a11df --- /dev/null +++ b/share/functions/__fish_is_first_arg.fish @@ -0,0 +1,5 @@ +# determine if this is the very first argument (regardless if switch or not) +function __fish_is_first_arg + set -l tokens (commandline -co) + test (count $tokens) -eq 1 +end diff --git a/share/functions/__fish_prev_arg_in.fish b/share/functions/__fish_prev_arg_in.fish new file mode 100644 index 000000000..1fd1662bd --- /dev/null +++ b/share/functions/__fish_prev_arg_in.fish @@ -0,0 +1,16 @@ +# returns 0 only if previous argument is one of the supplied arguments +function __fish_prev_arg_in + set -l tokens (commandline -co) + set -l tokenCount (count $tokens) + if test $tokenCount -lt 2 + # need at least cmd and prev argument + return 1 + end + for arg in $argv + if string match -q -- $tokens[-1] $arg + return 0 + end + end + + return 1 +end From 91f75d84d3e2651d4d77f1f14990ec7a2e8ed464 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi <mqudsi@neosmart.net> Date: Sun, 6 May 2018 18:55:02 -0500 Subject: [PATCH 143/159] Completions for optipng --- CHANGELOG.md | 1 + share/completions/optipng.fish | 41 ++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 share/completions/optipng.fish diff --git a/CHANGELOG.md b/CHANGELOG.md index d31b80e4c..d549b4063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ This section is for changes merged to the `major` branch that are not also merge - `j` (autojump #4344) - `jhipster` (#4472) - `ngrok` (#4642) + - `optipng` - `port` - Improved completions for - `brew` diff --git a/share/completions/optipng.fish b/share/completions/optipng.fish new file mode 100644 index 000000000..475b2410b --- /dev/null +++ b/share/completions/optipng.fish @@ -0,0 +1,41 @@ +complete -x -c optipng +complete -x -c optipng -n '__fish_should_complete_switches; and __fish_is_first_arg' -a '-?\t"show help"' +complete -x -c optipng -n '__fish_should_complete_switches; and __fish_is_first_arg' -a '-h\t"show help"' +complete -x -c optipng -n '__fish_should_complete_switches; and __fish_is_first_arg' -a '-help\t"show help"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-v\t"run in verbose mode"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-backup\t"keep a backup of the modified files"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-keep\t"keep a backup of the modified files"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-clobber\t"overwrite existing files"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-fix\t"enable error recovery"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-force\t"enforce writing of a new output file"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-preserve\t"preserve file attributes if possible"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-quiet\t"run in quiet mode"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-silent\t"run in quiet mode"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-simulate\t"run in simulation mode"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-full\t"produce a full IDAT report (might reduce speed)"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-nb\t"no bit depth reduction"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-nc\t"no color type reduction"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-np\t"no palette reduction"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-nx\t"no reductions"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-nz\t"no IDAT recoding"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-snip\t"cut one image out of multi-image or animation files"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-strip\t"strip specified metadata objects (e.g. "all")"' + +for n in (seq 0 9) + complete -x -c optipng -n '__fish_should_complete_switches' -a "-o$n\t\"PNG optimization level $n\"" +end + +complete -x -c optipng -n '__fish_should_complete_switches' -a '-i0\t"PNG interlace type 0"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-i1\t"PNG interlace type 1"' + +for n in (seq 0 5) + complete -x -c optipng -n '__fish_should_complete_switches' -a "-f$n\t'PNG delta filters $n'" +end + +complete -x -c optipng -n '__fish_should_complete_switches' -a '-out\t"write output file to <file>"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-dir\t"write output file(s) to <directory>"' +complete -x -c optipng -n '__fish_should_complete_switches' -a '-log\t"log messages to <file>"' + +complete -x -c optipng -n 'not __fish_prev_arg_in -out -dir -log' \ + -a '(__fish_complete_suffix ".{png,PNG,pnm,PNM,tiff,TIFF,bmp,BMP}")' +complete -x -c optipng -n '__fish_prev_arg_in -dir' -a '(__fish_complete_suffix \'{}\')' # hack to list directories only From 5f787cfe5599d00433147054fb470683b346b289 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Mon, 7 May 2018 13:44:26 -0700 Subject: [PATCH 144/159] Correct format string in dump_tree_recursive This was passing two unused arguments to the format string. Use one and drop the other. --- src/parse_tree.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 2923b0dd2..b4851ed60 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -284,8 +284,7 @@ static void dump_tree_recursive(const parse_node_tree_t &nodes, const wcstring & append_format(*result, L" [%ld, %ld]", (long)node.source_start, (long)node.source_length); } else { - append_format(*result, L" [no src]", (long)node.source_start, - (long)node.source_length); + append_format(*result, L" [%ld, no src]", (long)node.source_start); } } From 678fd861079392d26ad9e58aa2d39fbe215db717 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Mon, 7 May 2018 14:20:45 -0700 Subject: [PATCH 145/159] Minor cleanup of parse_ll_t::accept_tokens --- src/parse_tree.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index b4851ed60..72ab0549d 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -842,14 +842,8 @@ bool parse_ll_t::top_node_handle_terminal_types(parse_token_t token) { } void parse_ll_t::accept_tokens(parse_token_t token1, parse_token_t token2) { - bool logit = false; - if (logit) { - fwprintf(stderr, L"Accept token %ls\n", token1.describe().c_str()); - } PARSE_ASSERT(token1.type >= FIRST_PARSE_TOKEN_TYPE); - bool consumed = false; - // Handle special types specially. Note that these are the only types that can be pushed if the // symbol stack is empty. if (token1.type == parse_special_type_parse_error || @@ -865,7 +859,6 @@ void parse_ll_t::accept_tokens(parse_token_t token1, parse_token_t token2) { special_node.source_start = token1.source_start; special_node.source_length = token1.source_length; nodes.push_back(special_node); - consumed = true; // Mark special flags. if (token1.type == parse_special_type_comment) { @@ -874,16 +867,14 @@ void parse_ll_t::accept_tokens(parse_token_t token1, parse_token_t token2) { // Tokenizer errors are fatal. if (token1.type == parse_special_type_tokenizer_error) this->fatal_errored = true; + return; } - while (!consumed && !this->fatal_errored) { + // It's not a special type. + while (!this->fatal_errored) { PARSE_ASSERT(!symbol_stack.empty()); //!OCLINT(multiple unary operator) if (top_node_handle_terminal_types(token1)) { - if (logit) { - fwprintf(stderr, L"Consumed token %ls\n", token1.describe().c_str()); - } - // consumed = true; break; } From 6f57fef8f8a956e7dfd91bb01175d79caa69a171 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Mon, 7 May 2018 15:22:09 -0700 Subject: [PATCH 146/159] Teach the tokenizer to report escaped newlines Add fields and flags so that escaped newlines can be reported, for the benefit of fish_indent. --- src/parse_tree.cpp | 34 +++++++++++++++++----------------- src/parse_tree.h | 27 ++++++++++++++++++++------- src/tokenizer.cpp | 4 ++++ src/tokenizer.h | 3 +++ 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/parse_tree.cpp b/src/parse_tree.cpp index 72ab0549d..5293544e3 100644 --- a/src/parse_tree.cpp +++ b/src/parse_tree.cpp @@ -270,7 +270,10 @@ static void dump_tree_recursive(const parse_node_tree_t &nodes, const wcstring & append_format(*result, L" <%lu children>", node.child_count); } if (node.has_comments()) { - append_format(*result, L" <has_comments>", node.child_count); + append_format(*result, L" <has_comments>"); + } + if (node.has_preceding_escaped_newline()) { + append_format(*result, L" <preceding_esc_nl>"); } if (node.has_source() && node.type == parse_token_type_string) { @@ -357,7 +360,7 @@ class parse_ll_t { parse_error_list_t errors; // The symbol stack can contain terminal types or symbols. Symbols go on to do productions, but // terminal types are just matched against input tokens. - bool top_node_handle_terminal_types(parse_token_t token); + bool top_node_handle_terminal_types(const parse_token_t &token); void parse_error_unexpected_token(const wchar_t *expected, parse_token_t token); void parse_error(parse_token_t token, parse_error_code_t code, const wchar_t *format, ...); @@ -758,7 +761,7 @@ bool parse_ll_t::report_error_for_unclosed_block() { return reported_error; } -bool parse_ll_t::top_node_handle_terminal_types(parse_token_t token) { +bool parse_ll_t::top_node_handle_terminal_types(const parse_token_t &token) { PARSE_ASSERT(!symbol_stack.empty()); //!OCLINT(multiple unary operator) PARSE_ASSERT(token.type >= FIRST_PARSE_TOKEN_TYPE); parse_stack_element_t &stack_top = symbol_stack.back(); @@ -791,6 +794,8 @@ bool parse_ll_t::top_node_handle_terminal_types(parse_token_t token) { node.keyword = token.keyword; node.source_start = token.source_start; node.source_length = token.source_length; + if (token.preceding_escaped_nl) + node.flags |= parse_node_flag_preceding_escaped_nl; } else { // Failure if (stack_top.type == parse_token_type_string && token.type == parse_token_type_string) { @@ -858,6 +863,8 @@ void parse_ll_t::accept_tokens(parse_token_t token1, parse_token_t token2) { special_node.parent = symbol_stack.back().node_idx; special_node.source_start = token1.source_start; special_node.source_length = token1.source_length; + if (token1.preceding_escaped_nl) + special_node.flags |= parse_node_flag_preceding_escaped_nl; nodes.push_back(special_node); // Mark special flags. @@ -969,12 +976,10 @@ static parse_keyword_t keyword_for_token(token_type tok, const wcstring &token) } /// Placeholder invalid token. -static constexpr parse_token_t kInvalidToken = { - token_type_invalid, parse_keyword_none, false, false, false, SOURCE_OFFSET_INVALID, 0}; +static constexpr parse_token_t kInvalidToken{token_type_invalid}; /// Terminal token. -static constexpr parse_token_t kTerminalToken = { - parse_token_type_terminate, parse_keyword_none, false, false, false, SOURCE_OFFSET_INVALID, 0}; +static constexpr parse_token_t kTerminalToken = {parse_token_type_terminate}; static inline bool is_help_argument(const wcstring &txt) { return txt == L"-h" || txt == L"--help"; @@ -986,19 +991,18 @@ static inline parse_token_t next_parse_token(tokenizer_t *tok, tok_t *token, wcs return kTerminalToken; } - parse_token_t result; - // Set the type, keyword, and whether there's a dash prefix. Note that this is quite sketchy, // because it ignores quotes. This is the historical behavior. For example, `builtin --names` // lists builtins, but `builtin "--names"` attempts to run --names as a command. Amazingly as of // this writing (10/12/13) nobody seems to have noticed this. Squint at it really hard and it // even starts to look like a feature. - result.type = parse_token_type_from_tokenizer_token(token->type); + parse_token_t result{parse_token_type_from_tokenizer_token(token->type)}; const wcstring &text = tok->copy_text_of(*token, storage); result.keyword = keyword_for_token(token->type, text); result.has_dash_prefix = !text.empty() && text.at(0) == L'-'; result.is_help_argument = result.has_dash_prefix && is_help_argument(text); result.is_newline = (result.type == parse_token_type_end && text == L"\n"); + result.preceding_escaped_nl = token->preceding_escaped_nl; // These assertions are totally bogus. Basically our tokenizer works in size_t but we work in // uint32_t to save some space. If we have a source file larger than 4 GB, we'll probably just @@ -1079,13 +1083,9 @@ bool parse_tree_from_string(const wcstring &str, parse_tree_flags_t parse_flags, } // Mark a special error token, and then keep going. - const parse_token_t token = {parse_special_type_parse_error, - parse_keyword_none, - false, - false, - false, - queue[error_token_idx].source_start, - queue[error_token_idx].source_length}; + parse_token_t token = {parse_special_type_parse_error}; + token.source_start = queue[error_token_idx].source_start; + token.source_length = queue[error_token_idx].source_length; parser.accept_tokens(token, kInvalidToken); parser.reset_symbols(goal); } diff --git a/src/parse_tree.h b/src/parse_tree.h index 184037f1c..9e032f11d 100644 --- a/src/parse_tree.h +++ b/src/parse_tree.h @@ -28,15 +28,18 @@ constexpr source_offset_t SOURCE_OFFSET_INVALID = static_cast<source_offset_t>(- /// A struct representing the token type that we use internally. struct parse_token_t { enum parse_token_type_t type; // The type of the token as represented by the parser - enum parse_keyword_t keyword; // Any keyword represented by this token - bool has_dash_prefix; // Hackish: whether the source contains a dash prefix - bool is_help_argument; // Hackish: whether the source looks like '-h' or '--help' - bool is_newline; // Hackish: if TOK_END, whether the source is a newline. - source_offset_t source_start; - source_offset_t source_length; + enum parse_keyword_t keyword{parse_keyword_none}; // Any keyword represented by this token + bool has_dash_prefix{false}; // Hackish: whether the source contains a dash prefix + bool is_help_argument{false}; // Hackish: whether the source looks like '-h' or '--help' + bool is_newline{false}; // Hackish: if TOK_END, whether the source is a newline. + bool preceding_escaped_nl{false}; // Whether there was an escaped newline preceding this token. + source_offset_t source_start{SOURCE_OFFSET_INVALID}; + source_offset_t source_length{0}; wcstring describe() const; wcstring user_presentable_description() const; + + constexpr parse_token_t(parse_token_type_t type) : type(type) {} }; enum { @@ -66,6 +69,11 @@ const wchar_t *keyword_description(parse_keyword_t type); enum { /// Flag indicating that the node has associated comment nodes. parse_node_flag_has_comments = 1 << 0, + + /// Flag indicating that the token was preceded by an escaped newline, e.g. + /// echo abc | \ + /// cat + parse_node_flag_preceding_escaped_nl = 1 << 1, }; typedef uint8_t parse_node_flags_t; @@ -123,7 +131,12 @@ class parse_node_t { /// Indicate if the node has comment nodes. bool has_comments() const { - return static_cast<bool>(this->flags & parse_node_flag_has_comments); + return this->flags & parse_node_flag_has_comments; + } + + /// Indicates if we have a preceding escaped newline. + bool has_preceding_escaped_newline() const { + return this->flags & parse_node_flag_preceding_escaped_nl; } /// Gets source for the node, or the empty string if it has no source. diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 7f1f86a20..fe9d229b9 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -428,10 +428,12 @@ maybe_t<tok_t> tokenizer_t::tok_next() { } // Consume non-newline whitespace. If we get an escaped newline, mark it and continue past it. + bool preceding_escaped_nl = false; for (;;) { if (this->buff[0] == L'\\' && this->buff[1] == L'\n') { this->buff += 2; this->continue_line_after_comment = true; + preceding_escaped_nl = true; } else if (iswspace_not_nl(this->buff[0])) { this->buff++; } else { @@ -454,6 +456,7 @@ maybe_t<tok_t> tokenizer_t::tok_next() { result.type = TOK_COMMENT; result.offset = comment_start - this->start; result.length = comment_len; + result.preceding_escaped_nl = preceding_escaped_nl; return result; } while (iswspace_not_nl(this->buff[0])) this->buff++; @@ -551,6 +554,7 @@ maybe_t<tok_t> tokenizer_t::tok_next() { break; } } + result.preceding_escaped_nl = preceding_escaped_nl; return result; } diff --git a/src/tokenizer.h b/src/tokenizer.h index f44ea2815..10f600737 100644 --- a/src/tokenizer.h +++ b/src/tokenizer.h @@ -82,6 +82,9 @@ struct tok_t { // If an error, this is the error code. tokenizer_error *error { TOK_ERROR_NONE }; + // Whether the token was preceded by an escaped newline. + bool preceding_escaped_nl{false}; + // If an error, this is the offset of the error within the token. A value of 0 means it occurred // at 'offset'. size_t error_offset{size_t(-1)}; From 98d736f916fcafc18f89adb889ba26b945eb1b61 Mon Sep 17 00:00:00 2001 From: ridiculousfish <corydoras@ridiculousfish.com> Date: Mon, 7 May 2018 15:52:04 -0700 Subject: [PATCH 147/159] Teach fish_indent about escaped newlines Correctly indents code like: alpha | \ # comment beta --- src/fish_indent.cpp | 105 +++++++++++++++++++++++++++++++------------- tests/indent.in | 19 ++++++++ tests/indent.out | 20 +++++++++ 3 files changed, 113 insertions(+), 31 deletions(-) diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 12545786d..59e560bd5 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -65,16 +65,59 @@ static wcstring read_file(FILE *f) { return result; } -// Append whitespace as necessary. If we have a newline, append the appropriate indent. Otherwise, -// append a space. -static void append_whitespace(indent_t node_indent, bool do_indent, bool has_new_line, - wcstring *out_result) { - if (!has_new_line) { - out_result->push_back(L' '); - } else if (do_indent) { - out_result->append(node_indent * SPACES_PER_INDENT, L' '); +struct prettifier_t { + // Original source. + const wcstring &source; + + // The prettifier output. + wcstring output; + + // Whether to indent, or just insert spaces. + const bool do_indent; + + // Whether we are at the beginning of a new line. + bool has_new_line = true; + + // Whether we need to append a continuation new line before continuing. + bool needs_continuation_newline = false; + + // Additional indentation due to line continuation (escaped newline) + uint32_t line_continuation_indent = 0; + + prettifier_t(const wcstring &source, bool do_indent) : source(source), do_indent(do_indent) {} + + void prettify_node_recursive(const parse_node_tree_t &tree, + node_offset_t node_idx, indent_t node_indent, + parse_token_type_t parent_type); + + void maybe_prepend_escaped_newline(const parse_node_t &node) { + if (node.has_preceding_escaped_newline()) { + output.append(L" \\"); + append_newline(true); + } } -} + + void append_newline(bool is_continuation = false) { + output.push_back('\n'); + has_new_line = true; + needs_continuation_newline = false; + line_continuation_indent = is_continuation ? 1 : 0; + } + + // Append whitespace as necessary. If we have a newline, append the appropriate indent. Otherwise, + // append a space. + void append_whitespace(indent_t node_indent) { + if (needs_continuation_newline) { + append_newline(true); + } + if (!has_new_line) { + output.push_back(L' '); + } else if (do_indent) { + output.append((node_indent + line_continuation_indent) * SPACES_PER_INDENT, L' '); + } + } + +}; // Dump a parse tree node in a form helpful to someone debugging the behavior of this program. static void dump_node(indent_t node_indent, const parse_node_t &node, const wcstring &source) { @@ -106,10 +149,9 @@ static void dump_node(indent_t node_indent, const parse_node_t &node, const wcst token_type_description(node.type), prevc_str, source_txt.c_str(), nextc_str); } -static void prettify_node_recursive(const wcstring &source, const parse_node_tree_t &tree, +void prettifier_t::prettify_node_recursive(const parse_node_tree_t &tree, node_offset_t node_idx, indent_t node_indent, - parse_token_type_t parent_type, bool *has_new_line, - wcstring *out_result, bool do_indent) { + parse_token_type_t parent_type) { const parse_node_t &node = tree.at(node_idx); const parse_token_type_t node_type = node.type; const parse_token_type_t prev_node_type = @@ -130,32 +172,36 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre if (dump_parse_tree) dump_node(node_indent, node, source); - if (node.has_comments()) // handle comments, which come before the text - { + // Prepend any escaped newline. + maybe_prepend_escaped_newline(node); + + // handle comments, which come before the text + if (node.has_comments()) { auto comment_nodes = tree.comment_nodes_for_node(node); for (const auto &comment : comment_nodes) { - append_whitespace(node_indent, do_indent, *has_new_line, out_result); + maybe_prepend_escaped_newline(*comment.node()); + append_whitespace(node_indent); auto source_range = comment.source_range(); - out_result->append(source, source_range->start, source_range->length); + output.append(source, source_range->start, source_range->length); + needs_continuation_newline = true; } } if (node_type == parse_token_type_end) { - out_result->push_back(L'\n'); - *has_new_line = true; + append_newline(); } else if ((node_type >= FIRST_PARSE_TOKEN_TYPE && node_type <= LAST_PARSE_TOKEN_TYPE) || node_type == parse_special_type_parse_error) { if (node.keyword != parse_keyword_none) { - append_whitespace(node_indent, do_indent, *has_new_line, out_result); - out_result->append(keyword_description(node.keyword)); - *has_new_line = false; + append_whitespace(node_indent); + output.append(keyword_description(node.keyword)); + has_new_line = false; } else if (node.has_source()) { // Some type representing a particular token. if (prev_node_type != parse_token_type_redirection) { - append_whitespace(node_indent, do_indent, *has_new_line, out_result); + append_whitespace(node_indent); } - out_result->append(source, node.source_start, node.source_length); - *has_new_line = false; + output.append(source, node.source_start, node.source_length); + has_new_line = false; } } @@ -164,8 +210,7 @@ static void prettify_node_recursive(const wcstring &source, const parse_node_tre // Note: We pass our type to our child, which becomes its parent node type. // Note: While node.child_start could be -1 (NODE_OFFSET_INVALID) the addition is safe // because we won't execute this call in that case since node.child_count should be zero. - prettify_node_recursive(source, tree, node.child_start + idx, node_indent, node_type, - has_new_line, out_result, do_indent); + prettify_node_recursive(tree, node.child_start + idx, node_indent, node_type); } } @@ -185,17 +230,15 @@ static wcstring prettify(const wcstring &src, bool do_indent) { // We may have a forest of disconnected trees on a parse failure. We have to handle all nodes // that have no parent, and all parse errors. - bool has_new_line = true; - wcstring result; + prettifier_t prettifier{src, do_indent}; for (node_offset_t i = 0; i < parse_tree.size(); i++) { const parse_node_t &node = parse_tree.at(i); if (node.parent == NODE_OFFSET_INVALID || node.type == parse_special_type_parse_error) { // A root node. - prettify_node_recursive(src, parse_tree, i, 0, symbol_job_list, &has_new_line, &result, - do_indent); + prettifier.prettify_node_recursive(parse_tree, i, 0, symbol_job_list); } } - return result; + return std::move(prettifier.output); } // Helper for output_set_writer diff --git a/tests/indent.in b/tests/indent.in index 2d98dd8eb..0bc64947f 100644 --- a/tests/indent.in +++ b/tests/indent.in @@ -103,4 +103,23 @@ d e" true "builtin" yes en"d" + +alpha | \ + beta + +gamma | \ +# comment3 +delta + +if true +echo abc +end + +if false # comment4 +and true && false +echo abc;end + +echo hi | + +echo bye ' | ../test/root/bin/fish_indent diff --git a/tests/indent.out b/tests/indent.out index 5a4d9dc0f..9819b89a0 100644 --- a/tests/indent.out +++ b/tests/indent.out @@ -105,3 +105,23 @@ end while true builtin yes end + +alpha | \ + beta + +gamma | \ + # comment3 + delta + +if true + echo abc +end + +if false # comment4 + and true && false + echo abc +end + +echo hi | + +echo bye From 21890ccac73990042ae3cd0414e99f7e6317d0f6 Mon Sep 17 00:00:00 2001 From: David Adam <zanchey@ucc.gu.uwa.edu.au> Date: Sun, 29 Apr 2018 18:31:47 +0800 Subject: [PATCH 148/159] function: restore '%self' functionality for --on-process-exit One key use of process expansion, used in currently-shipped code, is for running a function on current shell exit. Restore the use of %self as a valid argument (and add `self`) and document this change. (faho: Remove bare "self") --- doc_src/function.txt | 5 ++++- src/builtin_function.cpp | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/doc_src/function.txt b/doc_src/function.txt index d9aad268b..b6db339e9 100644 --- a/doc_src/function.txt +++ b/doc_src/function.txt @@ -25,7 +25,10 @@ The following options are available: - `-j PGID` or `--on-job-exit PGID` tells fish to run this function when the job with group ID PGID exits. Instead of PGID, the string 'caller' can be specified. This is only legal when in a command substitution, and will result in the handler being triggered by the exit of the job which created this command substitution. -- `-p PID` or `--on-process-exit PID` tells fish to run this function when the fish child process with process ID PID exits. +- `-p PID` or `--on-process-exit PID` tells fish to run this function when the fish child process + with process ID PID exits. Instead of a PID, for backward compatibility, + "`%self`" can be specified as an alias for `$fish_pid`, and the function will be run when the + current fish instance exits. - `-s` or `--on-signal SIGSPEC` tells fish to run this function when the signal SIGSPEC is delivered. SIGSPEC can be a signal number, or the signal name, such as SIGHUP (or just HUP). diff --git a/src/builtin_function.cpp b/src/builtin_function.cpp index ca66b301c..7794dc307 100644 --- a/src/builtin_function.cpp +++ b/src/builtin_function.cpp @@ -115,6 +115,10 @@ static int parse_cmd_opts(function_cmd_opts_t &opts, int *optind, //!OCLINT(hig } e.type = EVENT_JOB_ID; e.param1.job_id = job_id; + } else if ((opt == 'p') && (wcscasecmp(w.woptarg, L"%self") == 0)) { + pid = getpid(); + e.type = EVENT_EXIT; + e.param1.pid = pid; } else { pid = fish_wcstoi(w.woptarg); if (errno || pid < 0) { From e35d248f6408b6d6d9fbdeafa901a112a6d4e642 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Tue, 8 May 2018 12:39:58 +0200 Subject: [PATCH 149/159] Fix clearing abandoned line with VTE (#4243) Turns out the segfaults we've been getting in our tests are because we set $TERM to "dumb". So we only clear the line if the terminal isn't dumb. This reverts commit 745a88f2f6119f28ed951be7711ec9774d59b683. Fixes #2320. --- src/screen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/screen.cpp b/src/screen.cpp index 6e565dcf9..da5c2803d 100644 --- a/src/screen.cpp +++ b/src/screen.cpp @@ -1196,7 +1196,9 @@ void s_reset(screen_t *s, screen_reset_mode_t mode) { // line above your prompt. This doesn't make a difference in normal usage, but copying and // pasting your terminal log becomes a pain. This commit clears that line, making it an // actual empty line. - abandon_line_string.append(L"\e[2K"); + if (!is_dumb()) { + abandon_line_string.append(str2wcstring(clr_eol)); + } const std::string narrow_abandon_line_string = wcs2string(abandon_line_string); write_loop(STDOUT_FILENO, narrow_abandon_line_string.c_str(), From 5b1731331eb2f80b7ed9ef7814cdb0566f576110 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Tue, 8 May 2018 17:11:48 +0200 Subject: [PATCH 150/159] [math] Add subsection headers to the docs Otherwise this is printed as "math-syntax" in the man page. --- doc_src/math.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc_src/math.txt b/doc_src/math.txt index 9e62272b2..e57be5e03 100644 --- a/doc_src/math.txt +++ b/doc_src/math.txt @@ -19,13 +19,13 @@ The following options are available: If the expression is successfully evaluated and doesn't over/underflow or return NaN the return `status` is zero (success) else one. -\subsection math-syntax +\subsection math-syntax Syntax `math` knows some operators, constants, functions and can (obviously) read numbers. For numbers, `.` is always the radix character regardless of locale - `2.5`, not `2,5`. Scientific notation (`10e5`) is also available. -\subsection math-operators +\subsection math-operators Operators `math` knows the following operators: @@ -41,7 +41,7 @@ For numbers, `.` is always the radix character regardless of locale - `2.5`, not They are all used in an infix manner - `5 + 2`, not `+ 5 2`. -\subsection math-constants +\subsection math-constants Constants `math` knows the following constants: @@ -50,7 +50,7 @@ They are all used in an infix manner - `5 + 2`, not `+ 5 2`. Use them without a leading `$`. -\subsection math-functions +\subsection math-functions Functions `math` supports the following functions: From 2c312d05df1d9bfcde83d6374b5a0361a5d8bedd Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Tue, 8 May 2018 17:26:56 +0200 Subject: [PATCH 151/159] [docs] Add more subsections They are great! --- doc_src/argparse.txt | 2 ++ doc_src/bind.txt | 1 + 2 files changed, 3 insertions(+) diff --git a/doc_src/argparse.txt b/doc_src/argparse.txt index b71f43d85..6be1411d8 100644 --- a/doc_src/argparse.txt +++ b/doc_src/argparse.txt @@ -15,6 +15,8 @@ Each option that is seen in the ARG list will result in a var name of the form ` For example `_flag_h` and `_flag_help` if `-h` or `--help` is seen. The var will be set with local scope (i.e., as if the script had done `set -l _flag_X`). If the flag is a boolean (that is, does not have an associated value) the values are the short and long flags seen. If the option is not a boolean flag the values will be zero or more values corresponding to the values collected when the ARG list is processed. If the flag was not seen the flag var will not be set. +\subsection argparse-options Options + The following `argparse` options are available. They must appear before all OPTION_SPECs: - `-n` or `--name` is the command name to insert into any error messages. If you don't provide this value `argparse` will be used. diff --git a/doc_src/bind.txt b/doc_src/bind.txt index 94ee19b6d..f6ec1403b 100644 --- a/doc_src/bind.txt +++ b/doc_src/bind.txt @@ -54,6 +54,7 @@ The following parameters are available: - `-a` or `--all` See `--erase` and `--key-names` +\subsection bind-functions Special input functions The following special input functions are available: - `accept-autosuggestion`, accept the current autosuggestion completely From 88b688c54413dfc6263bcad7aea119a840606921 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Tue, 8 May 2018 22:52:16 +0200 Subject: [PATCH 152/159] [hg prompt] Use `hg status -q` For some reason, `hg status -q` prints the exact same output as `hg status`, but about 20% faster. --- share/functions/__fish_hg_prompt.fish | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/share/functions/__fish_hg_prompt.fish b/share/functions/__fish_hg_prompt.fish index b59cf203b..21f6eaf7a 100644 --- a/share/functions/__fish_hg_prompt.fish +++ b/share/functions/__fish_hg_prompt.fish @@ -52,7 +52,9 @@ function __fish_hg_prompt --description 'Write out the hg prompt' echo -n '|' - set -l repo_status (hg status | string sub -l 2 | sort -u) + # For some reason, "-q" still prints the same output, but ~20% faster. + # Disabling color and pager is always a good idea. + set -l repo_status (hg status -q --color never --pager never | string sub -l 2 | sort -u) # Show nice color for a clean repo if test -z "$repo_status" From 34fc390e138f57deaa798b2e0d3ec2eee5a7707e Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Tue, 8 May 2018 23:12:13 +0200 Subject: [PATCH 153/159] [git completions] Shorten commit SHA ourselves This is much quicker - on the order of 100ms vs 50ms. We shorten to 10 characters, which is statistically suitable - 3 out of 600k commits in the linux kernel need 11 characters. --- share/completions/git.fish | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 8a982f326..dd827c884 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -6,7 +6,15 @@ function __fish_git_commits # This allows filtering by subject with the new pager! # Because even subject lines can be quite long, # trim them (abbrev'd hash+tab+subject) to 73 characters - command git log --pretty=tformat:"%h"\t"%<(64,trunc)%s" --all --max-count=1000 2>/dev/null + # + # Hashes we just truncate ourselves to 10 characters, without disambiguating. + # That technically means that sometimes we don't give usable SHAs, + # but according to https://stackoverflow.com/a/37403152/3150338, + # that happens for 3 commits out of 600k. + # For fish, at the time of writing, out of 12200 commits, 7 commits need 8 characters. + # And since this takes about 1/3rd of the time that disambiguating takes... + command git log --pretty=tformat:"%H"\t"%<(64,trunc)%s" --all --max-count=1000 2>/dev/null \ + | string replace -r '^([0-9a-f]{10})[0-9a-f]*\t(.*)' '$1\t$2' end function __fish_git_recent_commits From 39e3c3c0b12a6cc458703653fcf4aad306618f1d Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Tue, 8 May 2018 23:14:49 +0200 Subject: [PATCH 154/159] [git completions] Readd "unique remote branches" This is based on what the official git completions do, and it's quite fast. Also only complete files after a "--" separator for `checkout`. Fixes #4858. --- share/completions/git.fish | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index dd827c884..8e4c7e1b0 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -32,6 +32,17 @@ function __fish_git_branches | string replace -r '^refs/remotes/(.*)$' '$1\tRemote Branch' end +function __fish_git_unique_remote_branches + # `git checkout` accepts remote branches without the remote part + # if they are unambiguous. + # E.g. if only alice has a "frobulate" branch + # `git checkout frobulate` is equivalent to `git checkout -b frobulate --track alice/frobulate`. + command git for-each-ref --format="%(refname:strip=3)" \ + --sort="refname:strip=3" \ + "refs/remotes/*/$match*" "refs/remotes/*/*/**" | \ + uniq -u +end + function __fish_git_tags command git tag --sort=-creatordate 2>/dev/null end @@ -613,9 +624,10 @@ complete -f -c git -n '__fish_git_using_command add' -a '(__fish_git_files modif ### checkout complete -f -c git -n '__fish_git_needs_command' -a checkout -d 'Checkout and switch to a branch' -complete -k -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_branches)' -complete -k -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_heads)' -d 'Head' -complete -k -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_tags)' -d 'Tag' +complete -k -f -c git -n '__fish_git_using_command checkout; and not contains -- -- (commandline -op)' -a '(__fish_git_branches)' +complete -k -f -c git -n '__fish_git_using_command checkout; and not contains -- -- (commandline -op)' -a '(__fish_git_heads)' -d 'Head' +complete -k -f -c git -n '__fish_git_using_command checkout; and not contains -- -- (commandline -op)' -a '(__fish_git_tags)' -d 'Tag' +complete -k -f -c git -n '__fish_git_using_command checkout; and not contains -- -- (commandline -op)' -a '(__fish_git_unique_remote_branches)' -d 'Unique Remote Branch' complete -k -f -c git -n '__fish_git_using_command checkout' -a '(__fish_git_files modified deleted)' complete -f -c git -n '__fish_git_using_command checkout' -s b -d 'Create a new branch' complete -f -c git -n '__fish_git_using_command checkout' -s t -l track -d 'Track a new branch' From 183b7f701734661806007a6d539f44a6e3c8f5d8 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Tue, 8 May 2018 23:16:10 +0200 Subject: [PATCH 155/159] [git completions] Complete deleted files after "--" for "reset" --- share/completions/git.fish | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index 8e4c7e1b0..c6d4d243f 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -1080,9 +1080,10 @@ complete -f -c git -n '__fish_git_using_command reset' -l hard -d 'Reset files i complete -c git -n '__fish_git_using_command reset' -a '(__fish_git_branches)' # reset can either undo changes to versioned modified files, # or remove files from the staging area. -# TODO: Deleted files seem to need a "--" separator. -complete -f -c git -n '__fish_git_using_command reset' -a '(__fish_git_files all-staged modified)' -complete -f -c git -n '__fish_git_using_command reset' -a '(__fish_git_reflog)' -d 'Reflog' +# Deleted files seem to need a "--" separator. +complete -f -c git -n '__fish_git_using_command reset; and not contains -- -- (commandline -op)' -a '(__fish_git_files all-staged modified)' +complete -f -c git -n '__fish_git_using_command reset; and contains -- -- (commandline -op)' -a '(__fish_git_files all-staged deleted modified)' +complete -f -c git -n '__fish_git_using_command reset; and not contains -- -- (commandline -op)' -a '(__fish_git_reflog)' -d 'Reflog' # TODO options ### revert From 1e3d26f744674eae8a0c4cd388b2ba1a6b59d021 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Tue, 8 May 2018 23:16:39 +0200 Subject: [PATCH 156/159] [git completions] Remove impossible error message We already read the rest into a "_" garbage variable. --- share/completions/git.fish | 3 --- 1 file changed, 3 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index c6d4d243f..d0593df70 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -270,9 +270,6 @@ end # but a command can be aliased multiple times) git config -z --get-regexp 'alias\..*' | while read -lz alias command _ # Git aliases can contain chars that variable names can't - escape them. - if test (count $command) -ne 1 - printf (_ "Warning: alias '%s' has more than one command: '%s'") $alias "$command" >&2 - end set alias (string replace 'alias.' '' -- $alias | string escape --style=var) set -g __fish_git_alias_$alias $command end From ceddd1e6845ebaa6d69e00d59a1666dc6ff523ec Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Wed, 9 May 2018 16:46:17 +0200 Subject: [PATCH 157/159] Restore `.` alias for `source` The breakage is just too annoying. This reverts 55bef3cd2ee53b48704dace006932057db6352ab. --- CHANGELOG.md | 1 - share/config.fish | 12 ++++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d549b4063..12805a6f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,6 @@ This section is for changes merged to the `major` branch that are not also merge - `?` as a glob is deprecated and will be removed in the future. (#4520). This is controlled by the `qmark-noglob` feature flag. ## Notable non-backward compatible changes -- `.` command no longer exists -- use `source` (#4294). - `read` now uses `-s` as short for `--silent` (à la `bash`); `--shell`'s abbreviation (formerly `-s`) is now `-S` instead (#4490). - `set x[1] x[2] a b` is no longer valid syntax (#4236). - `for` loop control variables are no longer local to the `for` block (#1935). diff --git a/share/config.fish b/share/config.fish index 59fa7f505..c6ccfc4e4 100644 --- a/share/config.fish +++ b/share/config.fish @@ -172,6 +172,18 @@ end # in UTF-8 (with non-ASCII characters). __fish_set_locale +# "." command for compatibility with old fish versions. +function . --description 'Evaluate contents of file (deprecated, see "source")' --no-scope-shadowing + if test (count $argv) -eq 0 + # Uses tty directly, as isatty depends on "." + and tty 0>&0 >/dev/null + echo "source: '.' command is deprecated, and doesn't work with STDIN anymore. Did you mean 'source' or './'?" >&2 + return 1 + else + source $argv + end +end + # As last part of initialization, source the conf directories. # Implement precedence (User > Admin > Extra (e.g. vendors) > Fish) by basically doing "basename". set -l sourcelist From 1f8e4dad9fb6d7de35ddd6856101118a4159066e Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi <mqudsi@neosmart.net> Date: Thu, 10 May 2018 14:04:20 -0500 Subject: [PATCH 158/159] Use `git ls-files` to generate completions for `git` This uses the same logic that git uses to determine the satus of files and doesn't require any parsing on our end. Brings in support for relative paths (such as `git add ../f<TAB>`). Should be faster and more reliable than manually parsing porcelain status. This doesn't support as many cases as the old `__git_ls_files` function did (e.g. `renamed` is not supported, nor is `added`), both of which _can_ be implemented on top of the new logic - but neither of which were actually being used, anyway. Usefulness is decreased by #4970, speed still bottlenecked by #4969. cc @faho --- share/completions/git.fish | 168 ++++++++++++------------------------- 1 file changed, 55 insertions(+), 113 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index d0593df70..f057a280c 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -72,31 +72,21 @@ end function __fish_git_files # A function to show various kinds of files git knows about, - # by parsing `git status --porcelain`. + # now supporting relative paths. # # This accepts arguments to denote the kind of files: - # - added: Staged added files (unstaged adds are untracked) - # - copied # - deleted - # - deleted-staged # - ignored - # - modified: Files that have been modified (but aren't staged) - # - modified-staged: Staged modified files - # - renamed - # - untracked - # and as a convenience "all-staged" + # - staged (changes staged but not committed) + # - modified: Files that have been modified (but aren't yet staged) + # - untracked (not in index) + # - staged (all changes pending commit) # to get _all_ kinds of staged files. - # Save the repo root to remove it from the path later. - set -l root (command git rev-parse --show-toplevel 2>/dev/null) - # Do not continue if not inside a Git repository - or return - # Cache the translated descriptions so we don't have to get it # once per file. # This is slightly slower for < 8 files, but that is fast enough anyway. set -l unmerged_desc (_ "Unmerged File") - set -l added_desc (_ "Added file") set -l modified_desc (_ "Modified file") set -l staged_modified_desc (_ "Staged modified file") set -l deleted_desc (_ "Deleted file") @@ -104,103 +94,50 @@ function __fish_git_files set -l untracked_desc (_ "Untracked file") set -l ignored_desc (_ "Ignored file") - # git status --porcelain gives us all the info we need, in a format we don't. - # The v2 format has better documentation and doesn't use " " to denote anything, - # but it's only been added in git 2.11.0, which was released November 2016. - # Instead, we use the v1 format, without explicitly specifying it (since that errors out as well). - # - # Also, we ignore submodules because they aren't useful as arguments (generally), - # and they slow things down quite significantly. - # E.g. `git reset $submodule` won't do anything (not even print an error). - # --ignore-submodules=all was added in git 1.7.2, released July 2010. - set -l use_next - command git status --porcelain -z --ignore-submodules=all \ - | while read -lz -d '' line - # The entire line is the "from" from a rename. - if set -q use_next[1] - if contains -- $use_next $argv - string replace -f -- "$PWD/" "" "$root/$line" - or string replace -- "$root/" ":/" "$root/$line" - end - set -e use_next[1] - continue - end + # arguments to be passed to all `git ls-files` calls + set -l ls_args --directory --no-empty-directory --exclude-standard - # The format is two characters for status, then a space and then - # up to a NUL for the filename. - # - # Use IFS to handle newlines in filenames. - set -l IFS - set -l stat (string sub -l 2 -- $line) - set -l file (string sub -s 4 -- $line) - # Print files from the current $PWD as-is, prepend all others with ":/" (relative to toplevel in git-speak) - # This is a bit simplistic but finding the lowest common directory - # and then replacing everything else in $PWD with ".." is a bit annoying - set file (string replace -f -- "$PWD/" "" "$root/$file"; or string replace -- "$root/" ":/" "$root/$file") - set -e IFS - # The basic status format is "XY", where X is "our" state (meaning the staging area), - # and "Y" is "their" state (meaning the work tree). - # A " " means it's unmodified. - # - # Be careful about the ordering here! - # - # HACK: To allow this to work both with and without '?' globs - set -l dq '\\?\\?' - if status test-feature qmark-noglob - # ? is not a glob - set dq '??' - end - switch "$stat" - case DD AU UD UA DU AA UU - # Unmerged - # TODO: It might be useful to split this up. - contains -- unmerged $argv - and printf '%s\t%s\n' "$file" $unmerged_desc - case 'R ' RM RD - # Renamed/Copied - # These have the "from" name as the next batch. - # TODO: Do we care about the new name? - set use_next renamed - continue - case 'C ' CM CD - set use_next copied - continue - case 'A ' AM AD - # Additions are only shown here if they are staged. - # Otherwise it's an untracked file. - contains -- added $argv; or contains -- all-staged $argv - and printf '%s\t%s\n' "$file" $added_desc - case '*M' - # Modified - contains -- modified $argv - and printf '%s\t%s\n' "$file" $modified_desc - case 'M*' - # If the character is first ("M "), then that means it's "our" change, - # which means it is staged. - # This is useless for many commands - e.g. `checkout` won't do anything with this. - # So it needs to be requested explicitly. - contains -- modified-staged $argv; or contains -- all-staged $argv - and printf '%s\t%s\n' "$file" $staged_modified_desc - case '*D' - contains -- deleted $argv - and printf '%s\t%s\n' "$file" $deleted_desc - case 'D*' - # TODO: The docs are unclear on this. - # There is both X unmodified and Y either M or D ("not updated") - # and Y is D and X is unmodified or [MARC] ("deleted in work tree"). - # For our purposes, we assume this is a staged deletion. - contains -- deleted-staged $argv; or contains -- all-staged $argv - and printf '%s\t%s\n' "$file" $staged_deleted_desc - case "$dq" # a literal '??' - # Untracked - contains -- untracked $argv - and printf '%s\t%s\n' "$file" $untracked_desc - case '!!' - # Ignored - contains -- ignored $argv - and printf '%s\t%s\n' "$file" $ignored_desc - end + # `git ls-files` does not take a general "filter" as an argument, but rather + # a list of files or paths. i.e. if user wants to complete ../foo and has typed + # in ../f<TAB>, `git ls-files ../f` won't return any results because that isn't + # a path in the index. So we need to parse the final token (if any) into a + # directory and a file fragment, pass in the directory to `git ls-files`, and then + # filter (literally) the results based on the file fragment. + + set dst (commandline -ct | string match -r '^(..$|.*/)')[1] + set -l filter (string replace -- "$dst" "" (commandline -ct)) + + # target to `git ls-files` when no token is passed before <TAB> + if string match -qr -- '^[-.]?$' "$dst" + # search top-level directory files by default + set dst ":/" + end + + # printf "\ndst: \"$dst\"\n" > /dev/tty + # printf "filter: \"$filter\"\n" > /dev/tty + + if contains "delete" $argv + set ls_args $ls_args -d + end + if contains "modified" $argv + set ls_args $ls_args -m + end + if contains "unmerged" $argv + set ls_args $ls_args -o + end + if contains "ignored" $argv + set ls_args $ls_args -i + end + if contains "staged" $argv + set ls_args $ls_args -s + end + + # Needed until #4971 is figured out + if string match -- "" $filter + git ls-files $ls_args $dst + else + git ls-files $ls_args $dst | string match -e -- "$filter" end end @@ -275,6 +212,11 @@ git config -z --get-regexp 'alias\..*' | while read -lz alias command _ end function __fish_git_using_command + # speed up simple (common) case + set -l tokens (commandline -co) + if string match -q -- $tokens[1] $argv[1] + return 0 + end set -l cmd (__fish_git_needs_command) test -z "$cmd" and return 1 @@ -1078,8 +1020,8 @@ complete -c git -n '__fish_git_using_command reset' -a '(__fish_git_branches)' # reset can either undo changes to versioned modified files, # or remove files from the staging area. # Deleted files seem to need a "--" separator. -complete -f -c git -n '__fish_git_using_command reset; and not contains -- -- (commandline -op)' -a '(__fish_git_files all-staged modified)' -complete -f -c git -n '__fish_git_using_command reset; and contains -- -- (commandline -op)' -a '(__fish_git_files all-staged deleted modified)' +complete -f -c git -n '__fish_git_using_command reset; and not contains -- -- (commandline -op)' -a '(__fish_git_files staged)' +complete -f -c git -n '__fish_git_using_command reset; and contains -- -- (commandline -op)' -a '(__fish_git_files staged)' complete -f -c git -n '__fish_git_using_command reset; and not contains -- -- (commandline -op)' -a '(__fish_git_reflog)' -d 'Reflog' # TODO options @@ -1091,7 +1033,7 @@ complete -f -c git -n '__fish_git_using_command revert' -a '(__fish_git_commits) ### rm complete -c git -n '__fish_git_needs_command' -a rm -d 'Remove files from the working tree and the index' complete -c git -n '__fish_git_using_command rm' -l cached -d 'Unstage files from the index' -complete -c git -n '__fish_git_using_command rm; and __fish_contains_opt cached' -f -a '(__fish_git_files all-staged)' +complete -c git -n '__fish_git_using_command rm; and __fish_contains_opt cached' -f -a '(__fish_git_files staged)' complete -c git -n '__fish_git_using_command rm' -l ignore-unmatch -d 'Exit with a zero status even if no files matched' complete -c git -n '__fish_git_using_command rm' -s r -d 'Allow recursive removal' complete -c git -n '__fish_git_using_command rm' -s q -l quiet -d 'Be quiet' From 9929acd9d8d472c7bd763a59cf19f71d8597e4d0 Mon Sep 17 00:00:00 2001 From: Fabian Homborg <FHomborg@gmail.com> Date: Fri, 11 May 2018 16:00:45 +0200 Subject: [PATCH 159/159] Revert "Use `git ls-files` to generate completions for `git`" While this is a bit faster (mostly because it needs less processing on fish's side), it lacks the neat description bit and the ":/" stuff doesn't work. The boost is also not large in absolute terms (a few milliseconds). This reverts commit 1f8e4dad9fb6d7de35ddd6856101118a4159066e. --- share/completions/git.fish | 168 +++++++++++++++++++++++++------------ 1 file changed, 113 insertions(+), 55 deletions(-) diff --git a/share/completions/git.fish b/share/completions/git.fish index f057a280c..d0593df70 100644 --- a/share/completions/git.fish +++ b/share/completions/git.fish @@ -72,21 +72,31 @@ end function __fish_git_files # A function to show various kinds of files git knows about, - # now supporting relative paths. + # by parsing `git status --porcelain`. # # This accepts arguments to denote the kind of files: + # - added: Staged added files (unstaged adds are untracked) + # - copied # - deleted + # - deleted-staged # - ignored - # - staged (changes staged but not committed) - # - modified: Files that have been modified (but aren't yet staged) - # - untracked (not in index) - # - staged (all changes pending commit) + # - modified: Files that have been modified (but aren't staged) + # - modified-staged: Staged modified files + # - renamed + # - untracked + # and as a convenience "all-staged" # to get _all_ kinds of staged files. + # Save the repo root to remove it from the path later. + set -l root (command git rev-parse --show-toplevel 2>/dev/null) + # Do not continue if not inside a Git repository + or return + # Cache the translated descriptions so we don't have to get it # once per file. # This is slightly slower for < 8 files, but that is fast enough anyway. set -l unmerged_desc (_ "Unmerged File") + set -l added_desc (_ "Added file") set -l modified_desc (_ "Modified file") set -l staged_modified_desc (_ "Staged modified file") set -l deleted_desc (_ "Deleted file") @@ -94,50 +104,103 @@ function __fish_git_files set -l untracked_desc (_ "Untracked file") set -l ignored_desc (_ "Ignored file") - # arguments to be passed to all `git ls-files` calls - set -l ls_args --directory --no-empty-directory --exclude-standard + # git status --porcelain gives us all the info we need, in a format we don't. + # The v2 format has better documentation and doesn't use " " to denote anything, + # but it's only been added in git 2.11.0, which was released November 2016. + # Instead, we use the v1 format, without explicitly specifying it (since that errors out as well). + # + # Also, we ignore submodules because they aren't useful as arguments (generally), + # and they slow things down quite significantly. + # E.g. `git reset $submodule` won't do anything (not even print an error). + # --ignore-submodules=all was added in git 1.7.2, released July 2010. + set -l use_next + command git status --porcelain -z --ignore-submodules=all \ + | while read -lz -d '' line + # The entire line is the "from" from a rename. + if set -q use_next[1] + if contains -- $use_next $argv + string replace -f -- "$PWD/" "" "$root/$line" + or string replace -- "$root/" ":/" "$root/$line" + end + set -e use_next[1] + continue + end + # The format is two characters for status, then a space and then + # up to a NUL for the filename. + # + # Use IFS to handle newlines in filenames. + set -l IFS + set -l stat (string sub -l 2 -- $line) + set -l file (string sub -s 4 -- $line) + # Print files from the current $PWD as-is, prepend all others with ":/" (relative to toplevel in git-speak) + # This is a bit simplistic but finding the lowest common directory + # and then replacing everything else in $PWD with ".." is a bit annoying + set file (string replace -f -- "$PWD/" "" "$root/$file"; or string replace -- "$root/" ":/" "$root/$file") + set -e IFS - # `git ls-files` does not take a general "filter" as an argument, but rather - # a list of files or paths. i.e. if user wants to complete ../foo and has typed - # in ../f<TAB>, `git ls-files ../f` won't return any results because that isn't - # a path in the index. So we need to parse the final token (if any) into a - # directory and a file fragment, pass in the directory to `git ls-files`, and then - # filter (literally) the results based on the file fragment. - - set dst (commandline -ct | string match -r '^(..$|.*/)')[1] - set -l filter (string replace -- "$dst" "" (commandline -ct)) - - # target to `git ls-files` when no token is passed before <TAB> - if string match -qr -- '^[-.]?$' "$dst" - # search top-level directory files by default - set dst ":/" - end - - # printf "\ndst: \"$dst\"\n" > /dev/tty - # printf "filter: \"$filter\"\n" > /dev/tty - - if contains "delete" $argv - set ls_args $ls_args -d - end - if contains "modified" $argv - set ls_args $ls_args -m - end - if contains "unmerged" $argv - set ls_args $ls_args -o - end - if contains "ignored" $argv - set ls_args $ls_args -i - end - if contains "staged" $argv - set ls_args $ls_args -s - end - - # Needed until #4971 is figured out - if string match -- "" $filter - git ls-files $ls_args $dst - else - git ls-files $ls_args $dst | string match -e -- "$filter" + # The basic status format is "XY", where X is "our" state (meaning the staging area), + # and "Y" is "their" state (meaning the work tree). + # A " " means it's unmodified. + # + # Be careful about the ordering here! + # + # HACK: To allow this to work both with and without '?' globs + set -l dq '\\?\\?' + if status test-feature qmark-noglob + # ? is not a glob + set dq '??' + end + switch "$stat" + case DD AU UD UA DU AA UU + # Unmerged + # TODO: It might be useful to split this up. + contains -- unmerged $argv + and printf '%s\t%s\n' "$file" $unmerged_desc + case 'R ' RM RD + # Renamed/Copied + # These have the "from" name as the next batch. + # TODO: Do we care about the new name? + set use_next renamed + continue + case 'C ' CM CD + set use_next copied + continue + case 'A ' AM AD + # Additions are only shown here if they are staged. + # Otherwise it's an untracked file. + contains -- added $argv; or contains -- all-staged $argv + and printf '%s\t%s\n' "$file" $added_desc + case '*M' + # Modified + contains -- modified $argv + and printf '%s\t%s\n' "$file" $modified_desc + case 'M*' + # If the character is first ("M "), then that means it's "our" change, + # which means it is staged. + # This is useless for many commands - e.g. `checkout` won't do anything with this. + # So it needs to be requested explicitly. + contains -- modified-staged $argv; or contains -- all-staged $argv + and printf '%s\t%s\n' "$file" $staged_modified_desc + case '*D' + contains -- deleted $argv + and printf '%s\t%s\n' "$file" $deleted_desc + case 'D*' + # TODO: The docs are unclear on this. + # There is both X unmodified and Y either M or D ("not updated") + # and Y is D and X is unmodified or [MARC] ("deleted in work tree"). + # For our purposes, we assume this is a staged deletion. + contains -- deleted-staged $argv; or contains -- all-staged $argv + and printf '%s\t%s\n' "$file" $staged_deleted_desc + case "$dq" # a literal '??' + # Untracked + contains -- untracked $argv + and printf '%s\t%s\n' "$file" $untracked_desc + case '!!' + # Ignored + contains -- ignored $argv + and printf '%s\t%s\n' "$file" $ignored_desc + end end end @@ -212,11 +275,6 @@ git config -z --get-regexp 'alias\..*' | while read -lz alias command _ end function __fish_git_using_command - # speed up simple (common) case - set -l tokens (commandline -co) - if string match -q -- $tokens[1] $argv[1] - return 0 - end set -l cmd (__fish_git_needs_command) test -z "$cmd" and return 1 @@ -1020,8 +1078,8 @@ complete -c git -n '__fish_git_using_command reset' -a '(__fish_git_branches)' # reset can either undo changes to versioned modified files, # or remove files from the staging area. # Deleted files seem to need a "--" separator. -complete -f -c git -n '__fish_git_using_command reset; and not contains -- -- (commandline -op)' -a '(__fish_git_files staged)' -complete -f -c git -n '__fish_git_using_command reset; and contains -- -- (commandline -op)' -a '(__fish_git_files staged)' +complete -f -c git -n '__fish_git_using_command reset; and not contains -- -- (commandline -op)' -a '(__fish_git_files all-staged modified)' +complete -f -c git -n '__fish_git_using_command reset; and contains -- -- (commandline -op)' -a '(__fish_git_files all-staged deleted modified)' complete -f -c git -n '__fish_git_using_command reset; and not contains -- -- (commandline -op)' -a '(__fish_git_reflog)' -d 'Reflog' # TODO options @@ -1033,7 +1091,7 @@ complete -f -c git -n '__fish_git_using_command revert' -a '(__fish_git_commits) ### rm complete -c git -n '__fish_git_needs_command' -a rm -d 'Remove files from the working tree and the index' complete -c git -n '__fish_git_using_command rm' -l cached -d 'Unstage files from the index' -complete -c git -n '__fish_git_using_command rm; and __fish_contains_opt cached' -f -a '(__fish_git_files staged)' +complete -c git -n '__fish_git_using_command rm; and __fish_contains_opt cached' -f -a '(__fish_git_files all-staged)' complete -c git -n '__fish_git_using_command rm' -l ignore-unmatch -d 'Exit with a zero status even if no files matched' complete -c git -n '__fish_git_using_command rm' -s r -d 'Allow recursive removal' complete -c git -n '__fish_git_using_command rm' -s q -l quiet -d 'Be quiet'