Skip to content

Commit 785f08c

Browse files
committed
feat: Improve search input handling that should not missing a keystroke
1 parent 39dd9b0 commit 785f08c

File tree

3 files changed

+49
-23
lines changed

3 files changed

+49
-23
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.aider*
22
.env
33
__pycache__
4+
.keystroke

easymotion.py

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414

1515
# Configuration from environment
1616
HINTS = os.environ.get('TMUX_EASYMOTION_HINTS', 'asdghklqwertyuiopzxcvbnmfj;')
17-
CASE_SENSITIVE = os.environ.get('TMUX_EASYMOTION_CASE_SENSITIVE', 'false').lower() == 'true'
18-
SMARTSIGN = os.environ.get('TMUX_EASYMOTION_SMARTSIGN', 'false').lower() == 'true'
17+
CASE_SENSITIVE = os.environ.get(
18+
'TMUX_EASYMOTION_CASE_SENSITIVE', 'false').lower() == 'true'
19+
SMARTSIGN = os.environ.get(
20+
'TMUX_EASYMOTION_SMARTSIGN', 'false').lower() == 'true'
1921

2022
# Smartsign mapping table
2123
SMARTSIGN_TABLE = {
@@ -338,15 +340,33 @@ def get_terminal_size():
338340
return width, height - 1 # Subtract 1 from height
339341

340342

341-
def getch():
342-
"""Get a single character from terminal"""
343-
fd = sys.stdin.fileno()
344-
old_settings = termios.tcgetattr(fd)
345-
try:
346-
tty.setraw(fd)
347-
ch = sys.stdin.read(1)
348-
finally:
349-
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
343+
def getch(input_file=None):
344+
"""Get a single character from terminal or file
345+
346+
Args:
347+
input_file: Optional filename to read from. If None, read from stdin.
348+
File will be deleted after reading if specified.
349+
"""
350+
if input_file is None:
351+
# Read from stdin
352+
fd = sys.stdin.fileno()
353+
old_settings = termios.tcgetattr(fd)
354+
try:
355+
tty.setraw(fd)
356+
ch = sys.stdin.read(1)
357+
finally:
358+
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
359+
else:
360+
# Read from file and delete it
361+
try:
362+
with open(input_file, 'r') as f:
363+
ch = f.read(1)
364+
except FileNotFoundError:
365+
return '\x03' # Return Ctrl+C if file not found
366+
except Exception as e:
367+
logging.error(f"Error reading from file: {str(e)}")
368+
return '\x03'
369+
350370
return ch
351371

352372

@@ -442,8 +462,8 @@ def generate_hints(keys: str, needed_count: Optional[int] = None) -> List[str]:
442462
hints.extend(single_char_hints)
443463

444464
# Filter out double char hints that start with any single char hint
445-
filtered_doubles = [h for h in double_char_hints
446-
if h[0] not in single_char_hints]
465+
filtered_doubles = [h for h in double_char_hints
466+
if h[0] not in single_char_hints]
447467

448468
# Take needed doubles
449469
needed_doubles = needed_count - single_chars
@@ -513,12 +533,12 @@ def draw_all_panes(panes, max_x, padding_cache, terminal_height, screen):
513533
def find_matches(panes, search_ch):
514534
"""Find all matches and return match list"""
515535
matches = []
516-
536+
517537
# If smartsign is enabled, add corresponding symbol
518538
search_chars = [search_ch]
519539
if SMARTSIGN and search_ch in SMARTSIGN_TABLE:
520540
search_chars.append(SMARTSIGN_TABLE[search_ch])
521-
541+
522542
for pane in panes:
523543
for line_num, line in enumerate(pane.lines):
524544
pos = 0
@@ -545,7 +565,8 @@ def find_matches(panes, search_ch):
545565
def update_hints_display(screen, positions, current_key):
546566
"""Update hint display based on current key sequence"""
547567
for screen_y, screen_x, pane_right_edge, char, next_char, hint in positions:
548-
logging.debug(f'{screen_x} {pane_right_edge} {char} {next_char} {hint}')
568+
logging.debug(f'{screen_x} {pane_right_edge} {
569+
char} {next_char} {hint}')
549570
if hint.startswith(current_key):
550571
next_x = screen_x + get_char_width(char)
551572
if next_x < pane_right_edge:
@@ -600,13 +621,14 @@ def main(screen: Screen):
600621
setup_logging()
601622
terminal_width, terminal_height = get_terminal_size()
602623
panes, max_x, padding_cache = init_panes()
603-
624+
current_pane = next(p for p in panes if p.active)
604625
draw_all_panes(panes, max_x, padding_cache, terminal_height, screen)
605-
sh(['tmux', 'select-window', '-t', '{end}'])
606626

607-
search_ch = getch()
608-
if search_ch =='\x03':
627+
# Read character from temporary file
628+
search_ch = getch(sys.argv[1])
629+
if search_ch == '\x03':
609630
return
631+
sh(['tmux', 'select-window', '-t', '{end}'])
610632
matches = find_matches(panes, search_ch)
611633

612634
# If only one match, jump directly
@@ -617,7 +639,6 @@ def main(screen: Screen):
617639
return
618640

619641
# Get cursor position from current pane
620-
current_pane = next(p for p in panes if p.active)
621642
cursor_y = current_pane.cursor_y
622643
cursor_x = current_pane.cursor_x
623644
logging.debug(f"Cursor position: {current_pane.pane_id}, {

easymotion.tmux

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@ PERF=$(get_tmux_option "@easymotion-perf" "false")
2323
CASE_SENSITIVE=$(get_tmux_option "@easymotion-case-sensitive" "false")
2424
SMARTSIGN=$(get_tmux_option "@easymotion-smartsign" "false")
2525

26+
tmp_file=$CURRENT_DIR/.keystroke
2627
# Execute Python script with environment variables
27-
tmux bind $(get_tmux_option "@easymotion-key" "s") run-shell "tmux neww -d 'TMUX_EASYMOTION_HINTS=$HINTS \
28+
tmux bind $(get_tmux_option "@easymotion-key" "s") run-shell "\
29+
printf '\x03' > $tmp_file && tmux command-prompt -1 -p 'easymotion:' 'run-shell \"printf '%1' > $tmp_file\"' \; \
30+
neww -d '\
31+
TMUX_EASYMOTION_HINTS=$HINTS \
2832
TMUX_EASYMOTION_VERTICAL_BORDER=$VERTICAL_BORDER \
2933
TMUX_EASYMOTION_HORIZONTAL_BORDER=$HORIZONTAL_BORDER \
3034
TMUX_EASYMOTION_USE_CURSES=$USE_CURSES \
3135
TMUX_EASYMOTION_DEBUG=$DEBUG \
3236
TMUX_EASYMOTION_PERF=$PERF \
3337
TMUX_EASYMOTION_CASE_SENSITIVE=$CASE_SENSITIVE \
3438
TMUX_EASYMOTION_SMARTSIGN=$SMARTSIGN \
35-
$CURRENT_DIR/easymotion.py'"
39+
$CURRENT_DIR/easymotion.py $tmp_file'"

0 commit comments

Comments
 (0)