Repository navigation
feat(hook): let programs replace the typed line before it runs (CODIGEST_REWRITERS) - #54
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A program that turns a typed line into another one, such as
hffrom hanfix (it re-runs the previous command, fixed), runs that command from inside its own function today. Everything that watches the Enter key then seeshf, not the command that ran: shell history, PSReadLine and engine history, preexec hooks (atuin, starship),$?, and in bash/zsh adeclarein the fixed line stays local to the function. This adds a contract so the replacement happens before the shell accepts the line, and the new line then goes on as if typed.Contract (
CODIGEST_REWRITERS)$global:CODIGEST_REWRITERS += 'name'; the function gets the line and returns$null(not its line), the new line, or@{ Line = <new>; Confirm = $true }to ask first.CODIGEST_REWRITERS="${CODIGEST_REWRITERS-} name"; the function setsREPLYand returns 0 (run) or 2 (ask), or 1 (not its line).[Enter / Ctrl+C]; Enter runs it, any other key or Ctrl+C drops it (nothing runs, no history entry,$?unchanged).CODIGEST_RUNNERSand__cdg_runstay as they are: they coverhfinside a longer line, scripts, and programs that do not rewrite. With an empty or unsetCODIGEST_REWRITERSnothing changes.README (English and Korean), "Programs that run other commands".
How
AddToHistoryHandlerholds the line back, and the hook callsAddToHistoryfor the line that runs. The handler that was set before still decides for that line: PSReadLine's own (aFunc<string, object>that leaves out lines that look like secrets) or a profile's script block. An error in that handler keeps the line instead of bringing PSReadLine down. Loading the hook again does not chain the handler twice.bind -x) setsREADLINE_LINEbefore accept-line, so bash records the new line only. The question is printed and read there.__cdg_acceptsetsBUFFERbefore the original accept-line; the question useszle -Mandread -k.Design and the spike behind it: the codigest/hanfix design note (Option B), checked before this code: PSReadLine history held back and re-added, and printing/reading inside
bind -xwith Enter, another key and Ctrl+C.Also:
$?inside a captured line (#55)In PowerShell a captured line that read
$?always sawTrue:__cdgruns a few statements of its own before the line, and each resets$?. The read-line now passes the status it saw at the prompt, and when that was a failure__cdgruns a function that sets$?to False (an error record written withErrorAction Ignore: nothing printed, nothing in$Error) right before the line. Found in a live check of hanfix 0.2.7 with the real profile:ㅗㄹ -yre-ran a failing command and the next line printedTrue, whilehfitself had set it to False (a line with a leading space, not captured, showed False). Separate commit, fixes #55.Tests (
tests/test_hook_terminal.py)New
test_pwsh_rewriter_replaces_the_line_before_it_runsandtest_sh_rewriter_replaces_the_line_before_it_runs(bash, zsh), each with the rewriter loaded before and after the hook. A stand-in rewriter maps tags to rules:fc -l(bash/zsh) hold the new line once and never the typed one;nruns nothing and leaves no history entry;test_pwsh_captured_line_sees_the_previous_status(PowerShell hook: a captured line sees $? as True after a failed command #55): after a failing typed command, and after a rewriter's failing command, a captured line sees$?False and the exit code; after a line that succeeded, True. With the hook from main the same sequence gives True.local-ci: b790706 | docs and comments only since 325835e (README, README.ko.md, hook comments: the 200 ms check is not a timeout); at 325835e: Windows, ConPTY: pwsh rewriter tests 2 passed and the #55 test passed (at 325835e), existing pwsh terminal tests 5 passed (at 325835e); Git Bash 5.3 rewriter tests 2 passed and sh runner 2 passed + 1 xfailed (#52) (at 68f3d15; 325835e touches only hook.ps1); rest of the suite 296 passed, 1 skipped; same sequence as the live check, with hanfix 0.2.7 and this hook:
$?False in all five cases (main's hook: True for captured lines); ruff 0.17.1 check and format; bash -n hook.sh; PowerShell parser on hook.ps1: 0 errors; conventions.py check: 0 findings | zsh (not installed here), pwsh/bash on Linux and macOS only in CI; Ctrl+C at the question not in the suite (checked in the spike, bash only); hanfix's rewriter is the next PR