mirror of
https://github.com/junegunn/fzf.git
synced 2026-10-26 20:16:32 +00:00
fix(nushell): adapt the completer to the 0.116 contract (#4934)
* fix(nushell): adapt the completer to the 0.116 contract Nushell 0.116 runs an external completer on a background worker without the terminal unless the registered closure reaches a command carrying the @interactive attribute. Interactivity is seen through the closure to the command it calls, so wrap the completion logic in an @interactive entry point and register a closure that ends with the call to it. Read the named `place` input instead of the deprecated positional `spans`; $place.command carries the same token list. An @interactive completer cannot decline with null (null reads as a dismissed picker), so request the fallback explicitly when the `**` trigger is absent, keeping plain file completion working. The previous external completer is still called when the trigger is absent. The script now requires Nushell 0.116+, since @interactive is a parse error on older versions. Fixes #4932 * fix(nushell): retry the previous completer with the legacy spans input The fallback to the previously configured external completer always handed it the new `place` input. A completer that still declares the legacy `spans` parameter errors on a record, so the fallback silently produced no completions for pre-0.116 completers (e.g. older Carapace installs). Try `place` first, then retry with the token list, which is what the old positional bridge handed such completers. * docs(nushell): note the 0.116 requirement and load order Requires Nushell 0.116.1+, where a completion can take the terminal through the @interactive attribute, and the wrapper captures the external completer present at load time.
This commit is contained in:
1 parent
c8c2d0828a
commit
55c122f1a0
4 files changed
+67
-23
No files matched your search
@@ -38,9 +38,11 @@ jobs:
|
||||
wget -qO- https://apt.fury.io/nushell/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/fury-nushell.gpg
|
||||
echo "deb [signed-by=/etc/apt/keyrings/fury-nushell.gpg] https://apt.fury.io/nushell/ /" | sudo tee /etc/apt/sources.list.d/fury-nushell.list
|
||||
sudo apt-get update
|
||||
# Pinned: 0.115.0 runs external completers on a background thread, so
|
||||
# Pinned: the Nushell scripts now require 0.116+, where an external
|
||||
# completer reaches the terminal through the @interactive attribute.
|
||||
# Before that (e.g. 0.115.0), completers run on a background thread, so
|
||||
# fzf never receives keystrokes. https://github.com/nushell/nushell/issues/18771
|
||||
sudo apt-get install --yes zsh fish tmux shfmt nushell=0.114.1-0
|
||||
sudo apt-get install --yes zsh fish tmux shfmt nushell=0.116.1-0
|
||||
|
||||
- name: Install Ruby gems
|
||||
run: bundle install
|
||||
|
||||
@@ -5,6 +5,8 @@ CHANGELOG
|
||||
------
|
||||
- Fixed `--with-shell` on Windows not detecting cmd, PowerShell, and pwsh, which made fzf quote commands and entries for a POSIX shell (#4936) (@vimkim)
|
||||
- e.g. With `--with-shell "cmd.exe /d /c"`, `--preview '"C:\Program Files\nu\bin\nu.exe" --version'` failed
|
||||
- nushell
|
||||
- The completion integration now requires nushell 0.116.1 or above, where an interactive completer can take the terminal through the `@interactive` attribute (#4934) (@sim590)
|
||||
|
||||
0.74.5
|
||||
------
|
||||
|
||||
@@ -841,6 +841,13 @@ Fuzzy completion in Nushell works via the
|
||||
[external completer](https://www.nushell.sh/cookbook/external_completers.html)
|
||||
mechanism. There are some differences compared to bash and zsh:
|
||||
|
||||
- Nushell 0.116.1 or newer is required. The completion runs on the line-editor
|
||||
thread so fzf can take the terminal; a previously configured external
|
||||
completer (e.g. Carapace) is therefore invoked inline when the `**` trigger
|
||||
is absent.
|
||||
- The script wraps whichever external completer is configured when it loads,
|
||||
so load it after any other external completer setup. The autoload setup
|
||||
described above runs after `config.nu`, so it already does this.
|
||||
- On Nushell >= 0.103.0, the external completer is no longer called for
|
||||
built-in commands (e.g. `cd`, `ls`). Fuzzy completion with `**<TAB>` only
|
||||
works for external commands.
|
||||
|
||||
+54
-21
@@ -8,6 +8,15 @@
|
||||
# An implementation of completion.nu
|
||||
# This loads FZF as a Nushell External Completer
|
||||
# https://www.nushell.sh/cookbook/external_completers.html
|
||||
#
|
||||
# Requires Nushell 0.116.0 or newer.
|
||||
#
|
||||
# On 0.116+, the registered completer closure must reach a command carrying the
|
||||
# `@interactive` attribute: interactivity is seen through the closure to that
|
||||
# command (a closure cannot carry the attribute itself), and that is what makes
|
||||
# the completion run on the REPL thread with the terminal, so fzf can take it.
|
||||
# A closure that does not reach such a command runs on a background worker
|
||||
# without the terminal, where fzf cannot run.
|
||||
|
||||
|
||||
# --- Default Environment Variables ---
|
||||
@@ -367,9 +376,9 @@ def _fzf_complete_kill [query: string] {
|
||||
|
||||
# --- Main FZF External Completer ---
|
||||
|
||||
# This function is registered with Nushell's external completion system.
|
||||
# It gets called when Tab is pressed.
|
||||
let fzf_external_completer = {|spans|
|
||||
# Main completion logic. It receives the token list of the command being
|
||||
# completed (the legacy `spans`). Called directly, so no attribute is needed.
|
||||
def __fzf_completer_logic [spans] {
|
||||
let trigger: string = $env.FZF_COMPLETION_TRIGGER? | default '**'
|
||||
|
||||
if ($trigger | is-empty) { return null } # Cannot work with empty trigger
|
||||
@@ -446,6 +455,40 @@ let fzf_external_completer = {|spans|
|
||||
}
|
||||
}
|
||||
|
||||
# Nushell 0.116+ entry point. The registered closure must END with a call to
|
||||
# this command for the completion to run on the REPL thread with the terminal.
|
||||
#
|
||||
# It declares `place` (a named input) rather than the legacy `spans`, so the
|
||||
# completer never goes through the deprecated positional bridge. `$place.command`
|
||||
# is the token list of the command being completed, the same value the old
|
||||
# `spans` held (with a trailing "" when the cursor sits on an empty slot).
|
||||
@interactive
|
||||
def __fzf_external_completer [place] {
|
||||
let result = (__fzf_completer_logic $place.command)
|
||||
if $result != null {
|
||||
$result
|
||||
} else {
|
||||
# Fall back to the previously configured external completer, if any. It is
|
||||
# handed the new `place` input first. A completer that still declares the
|
||||
# legacy `spans` parameter errors on a record, so it is retried with the
|
||||
# token list, which is what the old positional bridge handed it.
|
||||
let previous = ($env.__fzf_previous_completer? | default null)
|
||||
let previous_result = if $previous != null {
|
||||
try {
|
||||
do $previous $place
|
||||
} catch {
|
||||
try { do $previous $place.command } catch { null }
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
# An @interactive completer cannot decline with null: null reads as a
|
||||
# dismissed picker with an empty, final answer. Request the fallback
|
||||
# explicitly so file completion still runs when the trigger is absent.
|
||||
if $previous_result != null { $previous_result } else { {completions: [], fallback: true} }
|
||||
}
|
||||
}
|
||||
|
||||
# --- WRAPPER AND REGISTRATION ---
|
||||
|
||||
# Guard against re-sourcing: wrapping the completer multiple times would
|
||||
@@ -455,25 +498,15 @@ if ($env.__fzf_completer_registered? | default false) != true {
|
||||
# Get the currently configured external completer, if any exists
|
||||
let previous_external_completer = $env.config? | get completions? | get external? | get completer?
|
||||
|
||||
# Define the new wrapper completer
|
||||
let fzf_wrapper_completer = {|spans|
|
||||
# 1. Try the FZF completer logic first
|
||||
let fzf_result = do $fzf_external_completer $spans
|
||||
# Stash it for `__fzf_external_completer`: a def cannot capture a local
|
||||
# variable, and the fallback to the previous completer happens inside it.
|
||||
$env.__fzf_previous_completer = $previous_external_completer
|
||||
|
||||
# 2. If FZF returned a result (a list, even an empty one), return it.
|
||||
# `null` means FZF didn't handle it because the trigger wasn't present.
|
||||
if $fzf_result != null {
|
||||
$fzf_result
|
||||
} else {
|
||||
# 3. FZF didn't handle it, so call the previous completer (if it exists).
|
||||
if $previous_external_completer != null {
|
||||
do $previous_external_completer $spans
|
||||
} else {
|
||||
# 4. No previous completer, and FZF didn't handle it. Return null.
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
# The registered closure must END with a call to an `@interactive` command so
|
||||
# that Nushell 0.116+ runs the completion on the REPL thread with the terminal
|
||||
# (it inspects the last pipeline element of the closure). Declaring `place`
|
||||
# instead of `spans` keeps it off the deprecated positional bridge.
|
||||
let fzf_wrapper_completer = {|place| __fzf_external_completer $place }
|
||||
|
||||
# Register the new wrapper completer
|
||||
# This ensures external completions are enabled and sets our wrapper.
|
||||
|
||||
Reference in new issue
Block a user