begin
require 'rubygems'
require_gem 'fxruby', '>= 1.2.0'
rescue LoadError
require 'fox12'
end
require 'fox12/responder'
require 'rrb/script'
require 'rrb/node'
require 'rrb/completion'
require 'rrb/rename_local_var'
require 'rrb/rename_instance_var'
require 'rrb/rename_class_var'
require 'rrb/rename_global_var'
require 'rrb/rename_method'
require 'rrb/rename_method_all'
require 'rrb/rename_constant'
require 'rrb/extract_method'
require 'rrb/move_method'
require 'rrb/pullup_method'
require 'rrb/pushdown_method'
require 'rrb/remove_parameter'
require 'rrb/extract_superclass'
require 'rrb/default_value'
module FreeRIDE
module RRB
include Fox
class RRB
extend FreeBASE::StandardPlugin
def RRB.start(plugin)
@@plugin = plugin
register_command
register_menu
plugin.transition(FreeBASE::RUNNING)
end
def self.register_command
cmd_manager = @@plugin['/system/ui/commands'].manager
cmd_manager.add("Refactor/ExperimentalWarning", "### Refactoring Support is Experimental ###") do |cmd_slot|
cmd_manager.command('App/Services/MessageBox').invoke("Experimental RRB Support",
"These are commands that invoke the experimental RRB refactoring support. Please see the detailed info at: http://freeride.rubyforge.org/wiki/wiki.pl?RefactoringSupport")
end
cmd = cmd_manager.add('Refactor/RenameLocalVariable','&Rename Local Variable') do |slot|
RenameLocalVariableDialog.new(@@plugin)
end
cmd = cmd_manager.add('Refactor/ExtractMethod','&Extract Method') do |slot|
ExtractMethodDialog.new(@@plugin)
end
cmd = cmd_manager.add('Refactor/RenameInstanceVariable','&Rename Instance Variable') do |slot|
RenameInstanceVariableDialog.new(@@plugin)
end
cmd = cmd_manager.add('Refactor/RenameClassVariable','&Rename Class Variable') do |slot|
RenameClassVariableDialog.new(@@plugin)
end
cmd = cmd_manager.add('Refactor/RenameGlobalVariable','&Rename Global Variable') do |slot|
RenameGlobalVariableDialog.new(@@plugin)
end
cmd = cmd_manager.add('Refactor/RenameMethod','&Rename Method') do |slot|
RenameMethodDialog.new(@@plugin)
end
cmd = cmd_manager.add('Refactor/RenameConstant','&Rename Constant') do |slot|
RenameConstantDialog.new(@@plugin)
end
cmd = cmd_manager.add('Refactor/PushdownMethod','&Push Down Method') do |slot|
PushdownMethodDialog.new(@@plugin)
end
cmd = cmd_manager.add('Refactor/PullupMethod','&Pull Up Method') do |slot|
PullupMethodDialog.new(@@plugin)
end
end
def self.register_menu
refactor_menu = @@plugin["/system/ui/components/MenuPane"].manager.add("Refactor_menu")
refactor_menu.data = "Refactor"
refactor_menu.attr_visible = true
refactor_menu.manager.add_command("Refactor/ExperimentalWarning")
refactor_menu.manager.add_command("Refactor/RenameLocalVariable")
refactor_menu.manager.add_command("Refactor/RenameInstanceVariable")
refactor_menu.manager.add_command("Refactor/RenameClassVariable")
refactor_menu.manager.add_command("Refactor/RenameGlobalVariable")
refactor_menu.manager.add_command("Refactor/RenameMethod")
refactor_menu.manager.add_command("Refactor/RenameConstant")
refactor_menu.manager.add_command("Refactor/ExtractMethod")
refactor_menu.manager.add_command("Refactor/PushdownMethod")
refactor_menu.manager.add_command("Refactor/PullupMethod")
# Things will be more better if menubar.manager#menuPanes exists...
menus = []
menubar = @@plugin["/system/ui/components/MenuBar/1"]
menubar.each_slot do |slot|
menus << slot.data
end
menus = menus[0..1] + [refactor_menu.path] + menus[2..-1]
menubar.manager.menuPanes = menus
end
end
def self.new_script(plugin)
editpanes = plugin['/system/ui/components/EditPane']
script_files = []
syntax_errors = SyntaxErrorList.new()
editpanes.each_slot do |editpane|
path = editpane.data
text = editpane['actions/get_text'].invoke()
begin
script_files << ::RRB::ScriptFile.new(text, path)
rescue ArgumentError => error
# this is the exception raised by ripper when a parsed file has a
# syntax error in it. In which case it cannot be refactored of course.
if error.to_s =~ /:(\d+):/m # regexp for line number extraction
syntax_errors.add(path,$1)
end
end
end
return ::RRB::Script.new(script_files)
end
def self.rewrite_script(plugin, script)
editpanes = plugin['/system/ui/components/EditPane']
script.files.each do |script_file|
editpanes.each_slot do |editpane|
if editpane.data == script_file.path
ext_object = editpane['actions/get_ext_object'].invoke()
ext_object.begin_undo_action()
ext_object.set_text(script_file.new_script)
ext_object.end_undo_action()
end
end
end
end
class RefactorDialog < FXDialogBox
include Fox
include Responder
def initialize(plugin, title)
owner = plugin["/system/ui/fox/FXMainWindow"].data
super(owner, title, DECOR_TITLE|DECOR_BORDER|DECOR_CLOSE)
@plugin = plugin
@app = plugin["/system/ui/fox/FXApp"].data
@current_pane = plugin['/system/ui/current/EditPane']
@filename = @current_pane.data
@cursor_line = @current_pane['actions/get_cursor_line'].invoke
@script = FreeRIDE::RRB.new_script(@plugin)
FXMAPFUNC(SEL_COMMAND, ID_ACCEPT, :onCmdAccept)
hfr_buttons = FXHorizontalFrame.new(self, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH|PACK_UNIFORM_HEIGHT)
cmd_cancel = FXButton.new(hfr_buttons, "&Cancel", nil, self, ID_CANCEL, FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_Y|LAYOUT_RIGHT)
cmd_ok = FXButton.new(hfr_buttons, "&OK", nil, self, ID_ACCEPT, FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_Y|LAYOUT_RIGHT)
FXHorizontalSeparator.new(self, LAYOUT_SIDE_BOTTOM|SEPARATOR_GROOVE|LAYOUT_FILL_X)
end
def enable_refactor?
end
def refactor
end
def onCmdAccept(sender, sel, ptr)
begin
if enable_refactor?
refactor
FreeRIDE::RRB.rewrite_script(@plugin, @script)
unless SyntaxErrorList.empty?
message_box = FXMessageBox.warning(self, MBOX_OK, 'Warning', "The following files have syntax errors and could not be inspected:\n#{SyntaxErrorList.message()}\n")
end
else
message_box = FXMessageBox.error(self, MBOX_OK, 'Error', @script.error_message)
end
rescue
ensure
onCmdCancel(sender, sel, ptr)
end
end
def onCmdCancel(sender, sel, ptr)
@app.stopModal(self)
self.destroy
end
end
class RenameDialog < RefactorDialog
def initialize(plugin, title)
super(plugin, title)
old_txt_field = FXHorizontalFrame.new(self, LAYOUT_FILL_X)
FXLabel.new(old_txt_field, "Old name: ", nil, JUSTIFY_LEFT|LAYOUT_CENTER_Y)
@txt_old_value = FXTextField.new(old_txt_field, 12, nil, 0, (FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_CENTER_Y))
new_txt_field = FXHorizontalFrame.new(self, LAYOUT_FILL_X)
FXLabel.new(new_txt_field, "New name: ", nil, JUSTIFY_LEFT|LAYOUT_CENTER_Y)
@txt_new_value = FXTextField.new(new_txt_field, 12, nil, 0, (FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_CENTER_Y))
@txt_new_value.setFocus
@old_value = get_word_on_cursor
@txt_old_value.text = @old_value
@txt_new_value.text = @old_value
self.create
self.show(PLACEMENT_OWNER)
@app.runModalFor(self)
end
def is_delimiter(char)
if char =~ /\s/ || "(){}\n\r-+=.#%^&*-|?<>,~`".include?(char)
return true
else
return false
end
end
def get_word_on_cursor
ext_obj = @current_pane['actions/get_ext_object'].invoke
current_pos = ext_obj.current_pos
text_length = ext_obj.get_text_length
buffer = ext_obj.get_text(text_length + 1)
left = current_pos
loop do
left_char = buffer[left - 1, 1]
break if left <= 0 || is_delimiter(left_char)
left -= 1
end
if current_pos == 0
right = 0
else
right = current_pos - 1
end
loop do
right_char = buffer[right + 1, 1]
break if right >= text_length || is_delimiter(right_char)
right += 1
end
buffer[left..right]
end
end
class RenameLocalVariableDialog < RenameDialog
def initialize(plugin)
super(plugin, "Rename Local Variable")
end
def setup_args
method = @script.get_method_on_cursor(@filename, @cursor_line).name
method_name = ::RRB::Method[method]
return [method_name, @txt_old_value.text, @txt_new_value.text]
end
def enable_refactor?
return @script.rename_local_var?(*setup_args)
end
def refactor
@script.rename_local_var(*setup_args)
end
end
class RenameInstanceVariableDialog < RenameDialog
def initialize(plugin)
super(plugin, "Rename Instance Variable")
end
def setup_args
namespace = @script.get_class_on_cursor(@filename, @cursor_line)
return [namespace, @txt_old_value.text, @txt_new_value.text]
end
def enable_refactor?
return @script.rename_instance_var?(*setup_args)
end
def refactor
@script.rename_instance_var(*setup_args)
end
end
class RenameClassVariableDialog < RenameDialog
def initialize(plugin)
super(plugin, "Rename Class Variable")
end
def setup_args
namespace = @script.get_class_on_cursor(@filename, @cursor_line)
return [namespace, @txt_old_value.text, @txt_new_value.text]
end
def enable_refactor?
return @script.rename_class_var?(*setup_args)
end
def refactor
@script.rename_class_var(*setup_args)
end
end
class RenameGlobalVariableDialog < RenameDialog
def initialize(plugin)
super(plugin, "Rename Global Variable")
end
def setup_args
[@txt_old_value.text, @txt_new_value.text]
end
def enable_refactor?
return @script.rename_global_var?(*setup_args)
end
def refactor
@script.rename_global_var(*setup_args)
end
end
class RenameMethodDialog < RenameDialog
def initialize(plugin)
super(plugin, "Rename Method")
end
def setup_args
namespace = @script.get_class_on_cursor(@filename, @cursor_line)
old_methods = [::RRB::Method.new(namespace, @txt_old_value.text)]
return [old_methods, @txt_new_value.text]
end
def enable_refactor?
return @script.rename_method?(*setup_args)
end
def refactor
@script.rename_method(*setup_args)
end
end
class RenameConstantDialog < RenameDialog
def initialize(plugin)
super(plugin, "Rename Constant")
end
def setup_args
namespace = @script.get_class_on_cursor(@filename, @cursor_line)
old_const = namespace.name + '::' + @txt_old_value.text
return [old_const, @txt_new_value.text]
end
def enable_refactor?
return @script.rename_constant?(*setup_args)
end
def refactor
@script.rename_constant(*setup_args)
end
end
class ExtractMethodDialog < RefactorDialog
def initialize(plugin)
super(plugin, "Extract Method")
hfr_txt_field = FXHorizontalFrame.new(self, LAYOUT_FILL_X)
FXLabel.new(hfr_txt_field, "Method name: ", nil, JUSTIFY_LEFT|LAYOUT_CENTER_Y)
@txt_new_method = FXTextField.new(hfr_txt_field, 12, nil, 0, (FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_CENTER_Y))
@txt_new_method.setFocus
self.create
self.show(PLACEMENT_OWNER)
@app.runModalFor(self)
end
def setup_args
ext_obj = @current_pane['actions/get_ext_object'].invoke
start_line = ext_obj.line_from_position(ext_obj.selection_start) + 1
end_line = ext_obj.line_from_position(ext_obj.selection_end) + 1
new_method = @txt_new_method.text
return [@filename, new_method, start_line, end_line]
end
def enable_refactor?
return @script.extract_method?(*setup_args)
end
def refactor
@script.extract_method(*setup_args)
end
end
class MoveMethodDialog < RefactorDialog
def initialize(plugin, text)
super(plugin, "Extract Method")
matrix = FXMatrix.new(self, 2, MATRIX_BY_COLUMNS|LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)
begin
method_candidates = @script.refactable_methods
destination_candidates = @script.refactable_classes
rescue
return
end
FXLabel.new(matrix, "Select Target Method: ", nil, JUSTIFY_RIGHT|LAYOUT_CENTER_Y|LAYOUT_FILL_ROW)
@cmb_target_method = FXComboBox.new(matrix,method_candidates.size,nil,0,COMBOBOX_INSERT_FIRST|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_ROW|LAYOUT_FILL_COLUMN)
@cmb_target_method.setNumVisible(method_candidates.size)
method_candidates.map{|method| method.name}.each do |candidate|
@cmb_target_method.appendItem(candidate)
end
FXLabel.new(matrix, "Select Destination class: ", nil, JUSTIFY_RIGHT|LAYOUT_CENTER_Y|LAYOUT_FILL_ROW)
@cmb_destination = FXComboBox.new(matrix,destination_candidates.size,nil,0,COMBOBOX_INSERT_FIRST|FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_ROW|LAYOUT_FILL_COLUMN)
@cmb_destination.setNumVisible(destination_candidates.size)
destination_candidates.each do |candidate|
@cmb_destination.appendItem(candidate)
end
self.create
self.show(PLACEMENT_OWNER)
@app.runModalFor(self)
end
end
class PushdownMethodDialog < MoveMethodDialog
def initialize(plugin)
super(plugin, "Push Down Method")
end
def setup_args
method_name = ::RRB::Method[@cmb_target_method.text]
new_namespace = ::RRB::Namespace.new(@cmb_destination.text)
return [method_name, new_namespace, @filename, @cursor_line]
end
def enable_refactor?
return @script.pushdown_method?(*setup_args)
end
def refactor
@script.pushdown_method(*setup_args)
end
end
class PullupMethodDialog < MoveMethodDialog
def initialize(plugin)
super(plugin, "Pull Up Method")
end
def setup_args
method_name = ::RRB::Method[@cmb_target_method.text]
new_namespace = ::RRB::Namespace.new(@cmb_destination.text)
return [method_name, new_namespace, @filename, @cursor_line]
end
def enable_refactor?
return @script.pullup_method?(*setup_args)
end
def refactor
@script.pullup_method(*setup_args)
end
end
class SyntaxErrorList
def initialize
@@syntax_errors = Hash.new
end
def self.message
msg = String.new
@@syntax_errors.each do |k,v|
msg << "#{k}:#{v}\n"
end
return msg
end
def self.empty?
return @@syntax_errors.empty?
end
def add(path, line)
@@syntax_errors[path] = line
end
end
end
end
syntax highlighted by Code2HTML, v. 0.9.1