Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for two-characters typing + overall improvements #43

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 72 additions & 23 deletions scripts/tmux-jump.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down Expand Up @@ -85,37 +86,46 @@ 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 :/
break if char = tmp_file.getc
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 =
Expand All @@ -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
Expand Down Expand Up @@ -166,40 +184,56 @@ 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
end
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}`
Expand All @@ -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]
Expand All @@ -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
5 changes: 1 addition & 4 deletions scripts/tmux-jump.sh
Original file line number Diff line number Diff line change
@@ -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"