Skip to content

Instantly share code, notes, and snippets.

@cryzed
Last active May 17, 2021 18:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cryzed/a9a2a714bf3b78f2b0e671469054dca0 to your computer and use it in GitHub Desktop.
Save cryzed/a9a2a714bf3b78f2b0e671469054dca0 to your computer and use it in GitHub Desktop.
.xonshrc with fzf history and last command output search on Ctrl+F and Ctrl+G
import collections.abc as abc
import itertools
import re
import shlex
import subprocess
import typing as T
try:
from prompt_toolkit.key_binding.key_bindings import KeyBindings
from prompt_toolkit.key_binding.key_processor import KeyPressEvent
from prompt_toolkit.keys import Keys
except ImportError:
has_prompt_toolkit = False
else:
has_prompt_toolkit = True
_ANSI_SEQUENCE_REGEX = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
_NULL_CHARACTER = "\x00"
_previous_command_output = None
def _deduplicate(sequence: abc.Sequence[T.Hashable]) -> T.Generator[T.Hashable, None, None]:
seen = set()
for item in sequence:
if item not in seen:
yield item
seen.add(item)
def _remove_ansi_escape_sequences(text: str) -> str:
return _ANSI_SEQUENCE_REGEX.sub("", text)
def _fzf(lines: abc.Iterator[str], *arguments: str) -> T.Optional[str]:
process = subprocess.Popen(["fzf", *arguments], stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
mapping = {}
for line in lines:
key = line.replace("\n", " ")
print(key, file=process.stdin)
mapping[key] = line
process.stdin.close()
key = process.stdout.read().strip()
return mapping.get(key)
if has_prompt_toolkit:
@events.on_postcommand
def _cache_command_output(cmd: str, rtn: int, out: T.Optional[str], ts: list) -> None:
global _previous_command_output
if out is None or not (out := out.strip()):
return
_previous_command_output = out
@events.on_ptk_create
def _custom_keybindings(bindings: KeyBindings, **kwargs: dict[str, T.Any]) -> None:
@bindings.add(Keys.ControlF)
def _fzf_history(event: KeyPressEvent) -> None:
# Trying to use $(history show all | fzf) directly creates problems
history = _deduplicate(entry.strip() for entry in $(history show all -0).split(_NULL_CHARACTER))
session_history = $(history show -0).split(_NULL_CHARACTER)
if selected := _fzf(itertools.chain(history, session_history), "--exact", "--no-sort", "--tac"):
event.current_buffer.insert_text(selected)
@bindings.add(Keys.ControlG)
def _fzf_output(event: KeyPressEvent) -> None:
if not _previous_command_output:
return
parts = _deduplicate(shlex.split(_remove_ansi_escape_sequences(_previous_command_output)))
if selected := _fzf(parts, "--exact", "--no-sort", "--tac"):
event.current_buffer.insert_text(repr(selected))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment