diff --git a/scripts/tmux-jump.rb b/scripts/tmux-jump.rb index b78dcbe..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'] @@ -85,26 +86,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 +111,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 +134,27 @@ 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 + 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_char + if (char =~ /\w/).nil? \ + && screen_chars[i+1] && screen_chars[i+1].downcase == jump_to_chars[0] \ + && (jump_to_chars.size == 1 || (screen_chars[i+2] && screen_chars[i+2].downcase == jump_to_chars[1])) positions << i+1 end end @@ -166,19 +184,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 @@ -186,20 +209,31 @@ def prompt_position_index!(positions, screen_chars) # raises Timeout::Error end def main - begin - jump_to_char = read_char_from_file! File.new(Config.tmp_file) - rescue Timeout::Error - Kernel.exit - 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 + + # 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 end + Kernel.exit 0 if position_index.nil? jump_to = positions[position_index] `tmux copy-mode -t #{Config.pane_nr}` @@ -212,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] @@ -226,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 57f140c..098a63e 100755 --- a/scripts/tmux-jump.sh +++ b/scripts/tmux-jump.sh @@ -1,11 +1,8 @@ #!/usr/bin/env bash -tmp_file="$(mktemp)" -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"