Skip to content
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 395 lines (332 sloc) 10.8 KB
#!/usr/bin/env ruby
# [Gedit Tool]
# Name=Jump
# Shortcut=F3
# Applicability=all
# Output=output-panel
# Input=nothing
# Save-files=nothing
# (c) 2011 Alexander Adam
# you will need exuberant-ctags and zenity for this
# https://github.com/antifarben/kaese-und-toast-gedit
#
# Jump tool; currently it is trying naively to jump betweern rails
# parts. Later it could be use support for other languages.
# Source is very messy, Sorry for that :)
require 'find'
require 'pp'
def document_type
@document_type ||= case ENV['GEDIT_CURRENT_DOCUMENT_TYPE']
when /\/x-ruby/
:ruby
when /\/rhtml/
:rhtml
else
puts "Unknown document type: #{ENV["GEDIT_CURRENT_DOCUMENT_TYPE"]}"
:unknown
end
end
def word_character
case document_type
when :ruby
'[a-zA-Z0-9_!\?]'
when :rhtml
'[a-zA-Z0-9_!\?]'
else
'[a-zA-Z0-9_]'
end
end
class String
def underscore
self.gsub(/::/, '/').
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
gsub(/([a-z\d])([A-Z])/,'\1_\2').
tr("-", "_").
downcase
end
end
def selected_word( different_word_chars = nil )
return @selected_word if @selected_word and !different_word_chars
if ENV['GEDIT_SELECTED_TEXT'] and (ENV['GEDIT_SELECTED_TEXT'] != '') and !ENV['GEDIT_SELECTED_TEXT'].include?("\n")
return (@selected_word = ENV['GEDIT_SELECTED_TEXT'])
end
@current_word = ENV['GEDIT_CURRENT_WORD']
if @current_word and (@current_word != '')
chars = different_word_chars || word_character
if chars.nil? or chars == ''
puts 'empty character set'
else
ENV['GEDIT_CURRENT_LINE'] =~ /(#{chars}*#{@current_word}#{chars}*)/
@current_word = $1
end
end
@current_word
end
def get_file_roots( custom_dirs = nil )
return @file_roots if @file_roots and !@file_roots.nil? and @file_roots != ''
rails_dirs = ['app','config','test','vendor']
results = []
limit = 2000
(custom_dirs || ENV['GEDIT_DOCUMENTS_PATH']).split.each do |path|
dir = File.expand_path File.dirname(path)
while dir and dir.size > 2 and (limit -= 1) > 0
dir = '/' if results.include? dir
if (Dir.glob("#{dir}/{#{rails_dirs*','}}").count == rails_dirs.count) or # rails root
(Dir.glob("#{dir}/.git/").count > 0) # .git-repository root
results << dir
dir = '/'
end
dir = File.dirname(dir)
end
end
@file_roots = results.uniq
@file_roots.any? ? @file_roots : raise('unable to find root directory.')
end
def find_files_by_filename( filter )
result = []
get_file_roots.each do |root_dir|
Find.find( root_dir ) do |path|
result << path if path =~ filter
end
end
result
end
def find_class_file_in_rails_root( class_name )
all_results = []
expected_filename = class_name.underscore.split('/').last+'.rb'
class_name = class_name.gsub(/^::/,'')
puts 'looking for ' + class_name + ' in '+ expected_filename
classes_from_current_doc = get_class_and_module_names_from_file
limit = 2000
get_file_roots.each do |root_dir|
Find.find(root_dir) do |path|
p = path.gsub(root_dir, '').gsub(/^\//,'')
Find.prune if p =~ /^app\/views/ or p =~ /^config/ or
p =~ /^db/ or p =~ /^doc/ or
p =~ /^log/ or p =~ /^public/ or
p =~ /^script/ or p =~ /^tmp/
next unless File.extname(p) == '.rb'
if p.end_with? expected_filename
if get_class_and_module_names_from_file(path).include?(class_name)
return [path]
elsif get_class_and_module_names_from_file(path).map{|c|c.split('::').last}.include?(class_name)
all_results << path
end
classes_from_current_doc.each do |w|
cp_name = class_name.split('::')
cp_name.each_with_index do |class_part_name,n|
class_paths = w.split('::')
class_paths.each_with_index do |c,i|
class_part = (class_paths[0..i] + [class_part_name]) * '::'
if get_class_and_module_names_from_file(path).include? class_part
return [path]
end
if get_class_and_module_names_from_file(path).include? class_part_name
return [path]
end
end
end
end # each
else # p.end_with? expected_filename # document name seems not to be related to the token?
all_results << path
end
end
if ((limit -= 1) < 0)
puts 'limit reached'
return []
end
end # find
# all_results.each do |path|
# if get_class_and_module_names_from_file(path).include?(class_name)
# return [path]
# end
# end
[]
end
def string_to_regex( str )
str.gsub!(/#\{[^\}]/,'.*') if str.start_with? '"'
str = str.gsub(/^(["'])/,'').gsub('.','\.').gsub('_','\_').gsub(/(\/)([^\/]*)$/,'/.?\2').gsub(/^\//,'') + '.*'
str.gsub('/','\/')
Regexp.new(str)
end
def routes_hash
return @routes_hash if @routes_hash
@routes_hash = {}
routes = `rake routes 2> /dev/null`.split("\n")
routes.each do |l|
l =~ /^\s*([a-z_]+)\s.*\s(\{[^\}]*\})\s*$/
if $1 and $2
@routes_hash[$1] = eval($2)
end
end
@routes_hash
end
def open_file( file )
if file.is_a? Array
`gedit "#{file.first}" +#{file.last}`
else
`gedit "#{file}"`
end
end
def get_line_no( file, regex )
line_no = nil
File.open(file).each_with_index do |l,i|
if l =~ regex
line_no = i+1
break
end
end
line_no
end
def search_definition( defintion = nil, where = get_file_roots )
results = []
[where].flatten.each do |root_dir|
tmp = `ctags-exuberant -xR --format=2 #{root_dir} 2> /dev/null | grep "#{defintion}"`
tmp.strip.split("\n").each do |r|
s = r.strip.split(/(\d+)/)
if defintion and defintion.strip == s.delete_at(0).strip.split.first
line_no, file = (s*'').split
results << [file.strip, line_no.strip]
end
end
end
results
end
def get_class_and_module_names_from_file( filename = nil, return_classes = true, return_modules = true )
filename ||= ENV['GEDIT_CURRENT_DOCUMENT_PATH']
@classnames ||= {}
@modulenames ||= {}
if @classnames[filename]
if return_classes and return_modules
return @classnames[filename] + @modulenames[filename]
elsif return_classes
@classnames[filename]
elsif return_modules
@modulenames[filename]
else
[]
end
end
reg_class = /^\s*class\s*(([A-Z][A-z]*::)*([A-Z][A-z]*))/
reg_module = /^\s*module\s*(([A-Z][A-z]*::)*([A-Z][A-z]*))/
n = -1
buff, modules, classes = [], [], []
File.open(filename).each_with_index do |l,i|
if l =~ reg_class
clazz = $1
if buff.size > 0
i = buff.size
while ((i-=1) >= 0) and (buff[i] =~ reg_module or (buff[i] == ''))
clazz = "#{$1}::#{clazz}" unless (buff[i] == '')
end
end
classes << clazz
end # class
if l =~ reg_module
module_name = $1
if buff.size > 0
i = buff.size
while ((i-=1) >= 0) and (buff[i] =~ reg_module or (buff[i] == ''))
if buff[i] != ''
if modules.any? and modules[-1].end_with?($1)
module_name = "#{modules.delete_at(-1)}::#{module_name}"
end
end
end
end
modules << module_name
end # module
buff.delete_at 0 if buff.size > 15
buff << l.strip
end # File.open
@classnames[filename], @modulenames[filename] = classes, modules
classes + modules
end
def functional_test_action( selected_word = nil )
if ENV['GEDIT_CURRENT_DOCUMENT_PATH'] =~ /test\/functional\/([\/a-z0-9_]+_controller)_test/
controller = $1
matches = find_files_by_filename /#{controller}.rb/
if selected_word.nil? or selected_word == ''
return matches
elsif matches.any? and ENV['GEDIT_CURRENT_LINE'] =~ /(get|post|put|delete)\s*:?([^,\n]*)[,\n]/
http_verb, action = $1, $2
return [] unless http_verb.include?(selected_word) or action.include?(selected_word)
results = matches.map{|path| search_definition action, path }.flatten(1)
results.any? ? results : matches
else
[]
end
else
[]
end
end
alias :functional_test_to_corresponding_controllers :functional_test_action
def controller_to_corresponding_functional_tests
if ENV['GEDIT_CURRENT_DOCUMENT_PATH'] =~ /app\/controllers\/([\/a-z0-9_]+_controller)\.rb$/
controller = $1
matches = find_files_by_filename /#{controller}_test.rb/
else
[]
end
end
result = case ENV['GEDIT_CURRENT_LINE']
when /render\s*.*:partial\s*=>\s*("[^"]*|'[^']*)/
find_files_by_filename(string_to_regex($1))
when /render\s*.*:template\s*=>\s*("[^"]*|'[^']*)/
find_files_by_filename(string_to_regex($1))
else
[]
end
result = result + case selected_word
when /_(url|path)$/ # jump to action by path-Method
controller_hash = routes_hash[selected_word.gsub(/_(url|path)$/,'')]
files = controller_hash ? find_files_by_filename(/controllers\/#{controller_hash[:controller]}/) : []
files.map do |f|
line_no = get_line_no(f, /^\s*def\s+#{controller_hash[:action]}($|\(|\s)/)
line_no ? [f, line_no] : f
end
when /^([A-Z_]+)$/ # constant
search_definition $1
when /^[A-Z][A-Za-z]*$/ # some module or class
if [:ruby, :rhtml].include? document_type
find_class_file_in_rails_root(selected_word('[:a-zA-Z0-9]').gsub(/::[a-z0-9]+/,''))
else
[]
end
when '' # switch to corresponding test if possible
case ENV['GEDIT_CURRENT_DOCUMENT_PATH']
when /test\/functional\/([\/a-z0-9_]+_controller)_test/
functional_test_to_corresponding_controllers
when /app\/([\/a-z0-9_]+_controller)\.rb$/
controller_to_corresponding_functional_tests
else
[]
end
else
higher_priorised_matches = []
if ENV['GEDIT_CURRENT_DOCUMENT_PATH'] =~ /test\/functional\/([\/a-z0-9_]+_controller)_test/
higher_priorised_matches = functional_test_action(selected_word)
end
higher_priorised_matches.any? ? higher_priorised_matches : search_definition(selected_word)
end
exit if !result or result.size == 0
result.uniq!
if result.size == 1
open_file result.first
else
zenity_installed = !(`whereis zenity`.split(":").last.strip.empty?)
if zenity_installed
options, translator = [], {}
result.uniq.sort{|a,b| (a.is_a?(Array) ? a.first : a) <=> (b.is_a?(Array) ? b.first : b) }.each do |e|
key = (e.is_a?(Array) ? (e.first+' in line '+e.last) : e)
get_file_roots.each{|r| key.gsub! r, '' }
options << "FALSE \"#{key}\""
translator[key] = e
end
selected_file = `zenity --list --text "Choose destination" --height=700 --width=950 --radiolist --column "Pick" --column "Destination" #{options*' '}`.strip
open_file translator[selected_file] if selected_file.strip != ''
elsif ARGV.count < 2
puts "please install zenity"
exit 0
end
end
Jump to Line
Something went wrong with that request. Please try again.