Cancel key reading when 'execute' triggered via a server request#4653
Conversation
Fix #4524 (except on Windows)
There was a problem hiding this comment.
Pull request overview
This PR implements the ability to cancel blocking key reading operations when an 'execute' action is triggered via a server request, addressing issue #4524. The implementation is complete for Unix-like systems but remains unimplemented for Windows (as noted in the TODO comments).
- Added
CancelGetChar()method to the Renderer interface - Implemented cancellation mechanism for Unix using pipes and select system call
- Updated return types from bool to
getCharResultenum to handle cancellation state - Modified terminal event loop to call
CancelGetChar()when execute actions arrive from server
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/tui/tui.go | Added CancelGetChar() method to Renderer interface |
| src/tui/tcell.go | Added stub CancelGetChar() implementation with TODO for future work |
| src/tui/light_windows.go | Updated getch() return type from bool to getCharResult enum |
| src/tui/light_unix.go | Implemented getch() cancellation using pipes and select, updated return type to getCharResult enum |
| src/tui/light.go | Added getCharResult type, cancel function field, and CancelGetChar() implementation; updated all related functions to handle cancellation |
| src/tui/dummy.go | Added empty CancelGetChar() stub for dummy renderer |
| src/terminal.go | Added logic to call CancelGetChar() when execute action is received from server |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
47df19c to
57f2ed4
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| func (r *FullscreenRenderer) CancelGetChar() { | ||
| // TODO |
There was a problem hiding this comment.
The CancelGetChar method for tcell renderer is not implemented (marked as TODO). This means that when using the tcell-based renderer, the execute action triggered via server requests will not be able to cancel a blocking GetChar call. Consider implementing this using tcell's event posting mechanism or document this limitation.
| // TODO | |
| // Use tcell's event posting mechanism to interrupt a blocking PollEvent/GetChar. | |
| if _screen != nil { | |
| _screen.PostEvent(tcell.NewEventInterrupt(nil)) | |
| } |
| } | ||
|
|
||
| func (r *FullscreenRenderer) GetChar() Event { | ||
| func (r *FullscreenRenderer) GetChar(cancellable bool) Event { |
There was a problem hiding this comment.
The cancellable parameter is not used in this implementation. Since CancelGetChar is not implemented for tcell (marked as TODO), this parameter should either be documented as unused or the cancellation functionality should be implemented.
| func (r *FullscreenRenderer) GetChar(cancellable bool) Event { | |
| // GetChar blocks until an event is available. The cancellable parameter is | |
| // ignored in the tcell implementation; cancellation is not supported here. | |
| func (r *FullscreenRenderer) GetChar(_ bool) Event { |
| showCursor bool | ||
| mutex sync.Mutex | ||
|
|
||
| // Windows only |
There was a problem hiding this comment.
The comment "// Windows only" is now misleading since the mutex field (line 135) is used on all platforms for protecting the cancel function. Consider updating this comment to reflect that only the fields from ttyinChannel onwards are Windows-specific.
| // Windows only | |
| // Windows-specific fields (from ttyinChannel onwards) |
| return getter() | ||
| } | ||
| r.setCancel(func() { | ||
| wpipe.Write([]byte{0}) |
There was a problem hiding this comment.
The write to wpipe in the cancel function does not check for errors. While this is generally acceptable for a cancellation signal, if the write fails (e.g., pipe buffer full or pipe closed due to a race condition), it will silently fail. Consider ignoring the error explicitly with a blank identifier to make the intention clear.
| wpipe.Write([]byte{0}) | |
| _, _ = wpipe.Write([]byte{0}) |
| go func() { | ||
| for { | ||
| light_renderer.mutex.Lock() | ||
| ready := light_renderer.cancel != nil | ||
| light_renderer.mutex.Unlock() | ||
|
|
||
| if ready { | ||
| light_renderer.CancelGetChar() | ||
| break | ||
| } | ||
| } |
There was a problem hiding this comment.
This test uses a busy-wait loop that repeatedly locks and unlocks the mutex, which is inefficient and could cause unnecessary CPU usage. Consider using a synchronization primitive like a channel or sync.WaitGroup to signal when the cancel function is ready, or add a small sleep in the loop to reduce CPU usage.
This MR contains the following updates: | Package | Update | Change | |---|---|---| | [junegunn/fzf](https://github.com/junegunn/fzf) | minor | `v0.67.0` → `v0.68.0` | MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot). **Proposed changes to behavior should be submitted there as MRs.** --- ### Release Notes <details> <summary>junegunn/fzf (junegunn/fzf)</summary> ### [`v0.68.0`](https://github.com/junegunn/fzf/releases/tag/v0.68.0): 0.68.0 [Compare Source](junegunn/fzf@v0.67.0...v0.68.0) <https://github.com/user-attachments/assets/8d9e6e29-1bde-49d3-b580-c560da394a5d> - Implemented word wrapping in the list section - Added `--wrap=word` (or `--wrap-word`) option and `toggle-wrap-word` action for word-level line wrapping in the list section - Changed default binding of `ctrl-/` and `alt-/` from `toggle-wrap` to `toggle-wrap-word` ```sh fzf --wrap=word ``` - Implemented word wrapping in the preview window - Added `wrap-word` flag for `--preview-window` to enable word-level wrapping - Added `toggle-preview-wrap-word` action ```sh fzf --preview 'bat --style=plain --color=always {}' \ --preview-window wrap-word \ --bind space:toggle-preview-wrap-word ``` - Added support for underline style variants in `--color`: `underline-double`, `underline-curly`, `underline-dotted`, `underline-dashed` ```sh fzf --color 'fg:underline-curly,current-fg:underline-dashed' ``` - Added support for underline styles (`4:N`) and underline colors (SGR 58/59) ```sh # In the list section printf '\e[4:3;58;2;255;0;0mRed curly underline\e[0m\n' | fzf --ansi # In the preview window fzf --preview "printf '\e[4:3;58;2;255;0;0mRed curly underline\e[0m\n'" ``` - Added `--preview-wrap-sign` to set a different wrap indicator for the preview window - Added `alt-gutter` color option ([#​4602](junegunn/fzf#4602)) ([@​hedgieinsocks](https://github.com/hedgieinsocks)) - Added `$FZF_WRAP` environment variable to child processes (`char` or `word` when wrapping is enabled) ([#​4672](junegunn/fzf#4672)) ([@​bitraid](https://github.com/bitraid)) - fish: Improved command history (CTRL-R) ([#​4672](junegunn/fzf#4672)) ([@​bitraid](https://github.com/bitraid)) - Enabled syntax highlighting in the list on fish 4.3.3+ - Added syntax-highlighted preview window that auto-shows for long or multi-line commands - Added `ALT-ENTER` to reformat and insert selected commands - Improved handling of bulk deletion of selected history entries (`SHIFT-DELETE`) - Added fish completion support ([#​4605](junegunn/fzf#4605)) ([@​lalvarezt](https://github.com/lalvarezt)) - zsh: Handle multi-line history selection ([#​4595](junegunn/fzf#4595)) ([@​LangLangBart](https://github.com/LangLangBart)) - Bug fixes - Fixed `_fzf_compgen_{path,dir}` to respect `FZF_COMPLETION_{PATH,DIR}_OPTS` ([#​4592](junegunn/fzf#4592)) ([@​shtse8](https://github.com/shtse8), [@​LangLangBart](https://github.com/LangLangBart)) - Fixed `--preview-window follow` not working correctly with wrapping ([#​3243](junegunn/fzf#3243), [#​4258](junegunn/fzf#4258)) - Fixed symlinks to directories being returned as files ([#​4676](junegunn/fzf#4676)) ([@​skk64](https://github.com/skk64)) - Fixed SIGHUP signal handling ([#​4668](junegunn/fzf#4668)) ([@​LangLangBart](https://github.com/LangLangBart)) - Fixed preview process not killed on exit ([#​4667](junegunn/fzf#4667)) - Fixed coloring of items with zero-width characters ([#​4620](junegunn/fzf#4620)) - Fixed `track-current` unset after a combined movement action ([#​4649](junegunn/fzf#4649)) - Fixed `--accept-nth` being ignored in filter mode ([#​4636](junegunn/fzf#4636)) ([@​charemma](https://github.com/charemma)) - Fixed display width calculation with `maxWidth` ([#​4596](junegunn/fzf#4596)) ([@​LangLangBart](https://github.com/LangLangBart)) - Fixed clearing of the rest of the current line on start ([#​4652](junegunn/fzf#4652)) - Fixed `x-api-key` header not required for GET requests ([#​4627](junegunn/fzf#4627)) - Fixed key reading not cancelled when `execute` triggered via a server request ([#​4653](junegunn/fzf#4653)) - Fixed rebind of readline command `redraw-current-line` ([#​4635](junegunn/fzf#4635)) ([@​jameslazo](https://github.com/jameslazo)) - Fixed `fzf-tmux` `TERM` quoting and added `mktemp` usage ([#​4664](junegunn/fzf#4664)) ([@​Goofygiraffe06](https://github.com/Goofygiraffe06)) - Do not allow very long queries in `FuzzyMatchV2` ([#​4608](junegunn/fzf#4608)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this MR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box --- This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNS44IiwidXBkYXRlZEluVmVyIjoiNDMuMjYuNCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiUmVub3ZhdGUgQm90IiwiYXV0b21hdGlvbjpib3QtYXV0aG9yZWQiLCJkZXBlbmRlbmN5LXR5cGU6Om1pbm9yIl19-->
Fix #4524
Close #4648
Note:
Using poll() would simplify the code, but on macOS it just returns
POLLNVALfor/dev/tty.