Skip to content

Instantly share code, notes, and snippets.

@dgfitch
Created September 1, 2009 14:44
Show Gist options
  • Save dgfitch/179128 to your computer and use it in GitHub Desktop.
Save dgfitch/179128 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# focus - convenient window switching in ratpoison
#
# Dan Fitch [email protected]
#
# based on select.sh - originally copyright 2003 <[email protected]>
#
# Example: focus cmus in a screen or start a new one containing it
#
# bind a exec focus 'cmus' 'screen cmus'
#
# USAGE:
#
# focus <operation> <type> <regex>
#
# Returns 1 if nothing matches.
#
# When operation is "last":
# re-run the last regex with the given type
# When operation is "select":
# toggle the two most recently accessed matching windows
# When operation is "cycle":
# go through all of the matching windows in order
# When operation is "cycleback":
# go through all of the matching windows in order, backwards
# When operation is "menu":
# show menu of all of the matching windows
#
# When type is "last":
# re-run the last regex with the given operation
# When type is "title":
# match by title
# When type is "class":
# match by class
#
# If the window has a screen session, attempt to select the first matching screen
#
# TODO:
# menu mode
# ordering is a little wacky on simple focus, like it's skipping when it shouldn't have to
# use knowledge of screens (the physical ones)
#
$: << File.dirname(__FILE__)
require 'ratpoison'
windows = Ratpoison.windows_array
operation = nil
type = nil
args = nil
if ARGV[0] == "last"
operation = Ratpoison.getenv "focus_last_operation"
args = Ratpoison.getenv "focus_last_args"
else
operation = ARGV[0]
Ratpoison.setenv "focus_last_operation #{operation}"
end
if ARGV[1] == "last"
type = Ratpoison.getenv "focus_last_type"
args = Ratpoison.getenv "focus_last_args"
else
type = ARGV[1]
Ratpoison.setenv "focus_last_type #{type}"
end
if not args
exit 2 if not ARGV[2]
args = ARGV[2..-1].join(' ')
Ratpoison.setenv "focus_last_args #{args}"
end
regex = /#{args}/i
current_window = windows.detect {|x| x.status == "*"}
filtered_windows = windows.find_all do |win|
case type
when /title/:
regex.match win.title
when /class/:
regex.match win.class
else
raise "Unknown match type #{type}"
end
end
if filtered_windows.length == 0
puts "no windows match #{type} #{regex}"
exit 1
end
# TODO: This should probably build a list of all screen titles that match so
# that cycling works at screen granularity instead of window granularity
window = case operation
when /cycle/:
# Cycle things that match
if filtered_windows.detect { |x| x.number == current_window.number }
# Select by number
cycle = filtered_windows.sort_by { |x| x.number }
current_index = cycle.index current_window
index =
if current_index
if operation[/back/]:
current_index + 1
else
current_index - 1
end
else
0
end
puts "selecting #{index} by number, current is #{current_index}" if $DEBUG
else
# Current window does not match -- select the most recent or least recent
cycle = filtered_windows.sort_by { |x| x.last_access }
index =
if operation[/back/]:
0
else
cycle.length - 1
end
puts "selecting #{index} by recent" if $DEBUG
end
index = cycle.length - 1 if index < 0
index = 0 if index > cycle.length - 1
puts "moving to #{index}" if $DEBUG
cycle[index]
when /menu/:
# Select via menu (TODO)
menu = filtered_windows.sort_by { |x| x.last_access }
if menu.length == 1 then
menu[0]
else
nil
end
else
# Select the most recent non-current window
select = filtered_windows.sort_by { |x| x.last_access }
if current_window
select.delete_if { |x| x.number == current_window.number }
end
if select.length == 0
nil
else
puts select.map { |x| "#{x.number} #{x.title}" } if $DEBUG
select[0]
end
end
Ratpoison.select window.number if window
window ||= current_window
if window and window.screen_session
screen = window.screen_session.detect { |x| regex.match x.title }
exit 1 if window == current_window and screen.number == window.screen_session.number
Ratpoison::ScreenSession.select screen.number if screen
else
exit 1 if window == current_window
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment