From 4af3eb82d66f51b654500191dd86c184dc62ca27 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Fri, 29 Mar 2024 23:26:46 +0100 Subject: [PATCH 1/2] feat: Add support for two-characters typing --- scripts/tmux-jump.rb | 59 +++++++++++++++++++++++++++++++------------- scripts/tmux-jump.sh | 1 + 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/scripts/tmux-jump.rb b/scripts/tmux-jump.rb index b78dcbe..9623bfb 100755 --- a/scripts/tmux-jump.rb +++ b/scripts/tmux-jump.rb @@ -85,26 +85,24 @@ def prompt_char! # raises Timeout::Error tmp_file = Tempfile.new 'tmux-jump' Kernel.spawn( 'tmux', 'command-prompt', '-1', '-p', 'char:', - "run-shell \"printf '%1' >> #{tmp_file.path}\"") + "run-shell \"printf '%1' >> #{tmp_file.path}\"" + ) + result_queue = Queue.new thread_0 = async_read_char_from_file! tmp_file, result_queue thread_1 = async_detect_user_escape result_queue + char = result_queue.pop + thread_0.exit thread_1.exit - char -end -def async_read_char_from_file!(tmp_file, result_queue) - thread = Thread.new do - result_queue.push read_char_from_file! tmp_file - end - thread.abort_on_exception = true - thread + char end -def read_char_from_file!(tmp_file) # raises Timeout::Error +def read_char_from_file(tmp_file) # raises Timeout::Error char = nil + Timeout.timeout(10) do begin loop do # busy waiting with files :/ @@ -112,10 +110,21 @@ def read_char_from_file!(tmp_file) # raises Timeout::Error end end end - File.delete tmp_file + char end +def async_read_char_from_file!(tmp_file, result_queue) + thread = Thread.new do + char = read_char_from_file tmp_file + File.delete tmp_file + result_queue.push char + end + + thread.abort_on_exception = true + thread +end + def async_detect_user_escape(result_queue) Thread.new do last_activity = @@ -124,19 +133,23 @@ def async_detect_user_escape(result_queue) new_activity = Open3.capture2 'tmux', 'display-message', '-p', '#{session_activity}' sleep 0.05 + if last_activity != new_activity - result_queue.push nil + # TODO: Disabled during testing + # result_queue.push nil end end end end -def positions_of(jump_to_char, screen_chars) +def positions_of(jump_to_chars, screen_chars) positions = [] - positions << 0 if screen_chars[0] =~ /\w/ && screen_chars[0].downcase == jump_to_char + positions << 0 if screen_chars[0] =~ /\w/ && screen_chars[0].downcase == jump_to_chars[0].downcase && screen_chars[1].downcase == jump_to_chars[1].downcase screen_chars.each_char.with_index do |char, i| - if (char =~ /\w/).nil? && screen_chars[i+1] && screen_chars[i+1].downcase == jump_to_char + if (char =~ /\w/).nil? \ + && screen_chars[i+1] && screen_chars[i+1].downcase == jump_to_chars[0] \ + && screen_chars[i+2] && screen_chars[i+2].downcase == jump_to_chars[1] positions << i+1 end end @@ -166,19 +179,24 @@ def keys_for(position_count, keys = KEYS) def prompt_position_index!(positions, screen_chars) # raises Timeout::Error return nil if positions.size == 0 + return 0 if positions.size == 1 + keys = keys_for positions.size key_len = keys.first.size draw_keys_onto_tty screen_chars, positions, keys, key_len key_index = KEYS.index(prompt_char!) + if !key_index.nil? && key_len > 1 magnitude = KEYS.size ** (key_len - 1) range_beginning = key_index * magnitude # p.e. 2 * 22^1 range_ending = range_beginning + magnitude - 1 remaining_positions = positions[range_beginning..range_ending] return nil if remaining_positions.nil? + lower_index = prompt_position_index!(remaining_positions, screen_chars) return nil if lower_index.nil? + range_beginning + lower_index else key_index @@ -187,19 +205,26 @@ def prompt_position_index!(positions, screen_chars) # raises Timeout::Error def main begin - jump_to_char = read_char_from_file! File.new(Config.tmp_file) + chars_read_file = File.new(Config.tmp_file) + jump_to_chars = '' + jump_to_chars << (read_char_from_file chars_read_file) + jump_to_chars << (read_char_from_file chars_read_file) rescue Timeout::Error Kernel.exit + ensure + File.delete chars_read_file end `tmux send-keys -X -t #{Config.pane_nr} cancel` if Config.pane_mode == '1' start = -Config.scroll_position ending = -Config.scroll_position + Config.pane_height - 1 screen_chars = `tmux capture-pane -p -t #{Config.pane_nr} -S #{start} -E #{ending}`[0..-2].gsub("︎", '') # without colors - positions = positions_of jump_to_char, screen_chars + + positions = positions_of jump_to_chars, screen_chars position_index = recover_screen_after do prompt_position_index! positions, screen_chars end + Kernel.exit 0 if position_index.nil? jump_to = positions[position_index] `tmux copy-mode -t #{Config.pane_nr}` diff --git a/scripts/tmux-jump.sh b/scripts/tmux-jump.sh index 57f140c..a31c9aa 100755 --- a/scripts/tmux-jump.sh +++ b/scripts/tmux-jump.sh @@ -2,6 +2,7 @@ tmp_file="$(mktemp)" tmux command-prompt -1 -p 'char:' "run-shell \"printf '%1' >> $tmp_file\"" +tmux command-prompt -1 -p 'char:' "run-shell \"printf '%1' >> $tmp_file\"" current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $current_dir/utils.sh From 0510f87b9455f10681961a41244af2e5a5b0d560 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 30 Mar 2024 18:10:57 +0100 Subject: [PATCH 2/2] fix: Already show labels on first input --- scripts/tmux-jump.rb | 52 ++++++++++++++++++++++++++++++++------------ scripts/tmux-jump.sh | 6 +---- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/scripts/tmux-jump.rb b/scripts/tmux-jump.rb index 9623bfb..609ea30 100755 --- a/scripts/tmux-jump.rb +++ b/scripts/tmux-jump.rb @@ -12,6 +12,7 @@ RESET_COLORS = "\e[0m" ENTER_ALTERNATE_SCREEN = "\e[?1049h" RESTORE_NORMAL_SCREEN = "\e[?1049l" +CLEAR_SCREEN = "\033[H\033[J" # CONFIG KEYS_POSITION = ENV['JUMP_KEYS_POSITION'] @@ -145,11 +146,15 @@ def async_detect_user_escape(result_queue) def positions_of(jump_to_chars, screen_chars) positions = [] - positions << 0 if screen_chars[0] =~ /\w/ && screen_chars[0].downcase == jump_to_chars[0].downcase && screen_chars[1].downcase == jump_to_chars[1].downcase + if screen_chars[0] =~ /\w/ \ + && screen_chars[0].downcase == jump_to_chars[0].downcase \ + && (jump_to_chars.size == 1 || (screen_chars[1].downcase == jump_to_chars[1].downcase)) + positions << 0 + end screen_chars.each_char.with_index do |char, i| if (char =~ /\w/).nil? \ && screen_chars[i+1] && screen_chars[i+1].downcase == jump_to_chars[0] \ - && screen_chars[i+2] && screen_chars[i+2].downcase == jump_to_chars[1] + && (jump_to_chars.size == 1 || (screen_chars[i+2] && screen_chars[i+2].downcase == jump_to_chars[1])) positions << i+1 end end @@ -204,22 +209,26 @@ def prompt_position_index!(positions, screen_chars) # raises Timeout::Error end def main - begin - chars_read_file = File.new(Config.tmp_file) - jump_to_chars = '' - jump_to_chars << (read_char_from_file chars_read_file) - jump_to_chars << (read_char_from_file chars_read_file) - rescue Timeout::Error - Kernel.exit - ensure - File.delete chars_read_file - end `tmux send-keys -X -t #{Config.pane_nr} cancel` if Config.pane_mode == '1' start = -Config.scroll_position ending = -Config.scroll_position + Config.pane_height - 1 screen_chars = `tmux capture-pane -p -t #{Config.pane_nr} -S #{start} -E #{ending}`[0..-2].gsub("︎", '') # without colors + # puts "screen_chars: #{screen_chars}; #{screen_chars.size} chars" + + first_char = prompt_char! + # Preview first char + positions = positions_of first_char, screen_chars + keys = keys_for positions.size + key_len = keys.first.size + + second_char = recover_screen_after do + draw_keys_onto_tty screen_chars, positions, keys, key_len + prompt_char! + end + jump_to_chars = "#{first_char}#{second_char}" + positions = positions_of jump_to_chars, screen_chars position_index = recover_screen_after do prompt_position_index! positions, screen_chars @@ -237,12 +246,25 @@ def main `tmux send-keys -X -t #{Config.pane_nr} top-line` `tmux send-keys -X -t #{Config.pane_nr} -N #{Config.scroll_position} cursor-up` `tmux send-keys -X -t #{Config.pane_nr} -N #{jump_to} cursor-right` + # + # + # `tmux copy-mode -t #{Config.pane_nr}` + # # begin: tmux weirdness when 1st line is empty + # `tmux send-keys -X -t #{Config.pane_nr} start-of-line` + # `tmux send-keys -X -t #{Config.pane_nr} top-line` + # `tmux send-keys -X -t #{Config.pane_nr} -N 200 cursor-right` + # # end + # `tmux send-keys -X -t #{Config.pane_nr} start-of-line` + # `tmux send-keys -X -t #{Config.pane_nr} top-line` + # `tmux send-keys -X -t #{Config.pane_nr} -N #{Config.scroll_position} cursor-up` + # `tmux send-keys -X -t #{Config.pane_nr} -N #{jump_to} cursor-right` end if $PROGRAM_NAME == __FILE__ Config.pane_nr = `tmux display-message -p "\#{pane_id}"`.strip - format = '#{pane_id};#{pane_tty};#{pane_in_mode};#{cursor_y};#{cursor_x};'\ - '#{alternate_on};#{scroll_position};#{pane_height}' + format = '#{pane_id};#{pane_tty};#{pane_in_mode};#{cursor_y};#{cursor_x};' \ + '#{alternate_on};#{scroll_position};#{pane_height};' \ + '#{copy_cursor_x};#{copy_cursor_y};' tmux_data = `tmux display-message -p -t #{Config.pane_nr} -F "#{format}"`.strip.split(';') Config.pane_tty_file = tmux_data[1] Config.pane_mode = tmux_data[2] @@ -251,6 +273,8 @@ def main Config.alternate_on = tmux_data[5] Config.scroll_position = tmux_data[6].to_i Config.pane_height = tmux_data[7].to_i + # puts "Cursor X: #{Config.cursor_x}, Cursor Y: #{Config.cursor_y}, Copy X: #{tmux_data[8]}, Copy Y: #{tmux_data[9]}" Config.tmp_file = ARGV[0] + main end diff --git a/scripts/tmux-jump.sh b/scripts/tmux-jump.sh index a31c9aa..098a63e 100755 --- a/scripts/tmux-jump.sh +++ b/scripts/tmux-jump.sh @@ -1,12 +1,8 @@ #!/usr/bin/env bash -tmp_file="$(mktemp)" -tmux command-prompt -1 -p 'char:' "run-shell \"printf '%1' >> $tmp_file\"" -tmux command-prompt -1 -p 'char:' "run-shell \"printf '%1' >> $tmp_file\"" - current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $current_dir/utils.sh export JUMP_BACKGROUND_COLOR=$(get_tmux_option "@jump-bg-color" "\e[0m\e[32m") export JUMP_FOREGROUND_COLOR=$(get_tmux_option "@jump-fg-color" "\e[1m\e[31m") export JUMP_KEYS_POSITION=$(get_tmux_option "@jump-keys-position" "left") -ruby "$current_dir/tmux-jump.rb" "$tmp_file" +ruby "$current_dir/tmux-jump.rb"