Strange Find and Replace Bug - r906
Summary:
Doing a selection based find and replace consistently inserts many lines of code when it should be only replace one word with another single word.
Reproducing The Bug:
* Open the attached (.rb) file * Switch to Ruby on Rails editor mode * Do a full line selection on line #39 * Hit cmd+f to go into the finder window * Replace the word ":now" with "now" while using the shift button to do a selection only replace
Results:
* Replaces the entire line #39 with only the word "now" * Performs a selection from line #39 down to #110 * Duplicates a bunch of code under line #110 (copies and pastes the new selection)
Details:
* Ignore and wrap are checked. Reg exp. is unchecked. * Restarting Textmate doesn't seem to fix the issue. * Soyru in IRC helped me confirm the bug, so it is not specific to my situation.
Environment:
* I don't use manager hacksies. * I haven't edited the default bundle editor commands. * I do use quicksilver, starting it is ctrl+alt+cmd+space; Everything else is at defaults
class TimerController < ApplicationController
# def index unless @user.master_timer_is_stopped?
# initialize current task timer
# initialize task list @upcoming_tasks = @user.upcoming_tasks @recent_tasks = @user.recent_tasks @favorite_tasks = @user.favorite_tasks end end
# Start a master timer def start_clock @user.start_master_timer redirect_to :action => 'index' end
# Pause a running master timer def pause_clock @user.pause_master_timer redirect_to :action => 'index' end
# Stop a running or paused master timer def stop_clock @user.stop_master_timer redirect_to :action => 'index' end
# Start a task def start_task
# capture the time now so all timers match up :now = Time.now # find currently selected task :selected_task = @user.selected_task
# if a selected task exists if :selected_task # if the recently started task is already the selected task if :selected_task.id == params[:id] if :selected_task.status == 2 # notice - can't start a timer that is already running elsif :selected_task.status == 1 # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = :selected_task.id # change status of the this task :selected_task.status = 2 :selected_task.save else # error - stopped tasks can't be selected end # if the recently started task is not already the selected task else # stop the task timer if it's still running if :selected_task.status == 2 :task_timer = TaskTimer.find(:first, :condition => "ended_at is null and " + "user_id = #{@user.id} and " + "task_id = #{:selected_task.id}") :task_timer.ended_at = :now :task_timer.save end #change status of selected task to stopped :selected_task.status = 0 :selected_task.save # find the newly started task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end # if no selected task exists else # find the newly selected task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end
redirect_to :action => 'index' end
private
before_filter :find_user
# # def find_user @user = session[:user] end
end
class TimerController < ApplicationController
# def index unless @user.master_timer_is_stopped?
# initialize current task timer
# initialize task list @upcoming_tasks = @user.upcoming_tasks @recent_tasks = @user.recent_tasks @favorite_tasks = @user.favorite_tasks end end
# Start a master timer def start_clock @user.start_master_timer redirect_to :action => 'index' end
# Pause a running master timer def pause_clock @user.pause_master_timer redirect_to :action => 'index' end
# Stop a running or paused master timer def stop_clock @user.stop_master_timer redirect_to :action => 'index' end
# Start a task def start_task
# capture the time now so all timers match up now # capture the time now so all timers match up :now = Time.now # find currently selected task :selected_task = @user.selected_task
# if a selected task exists if :selected_task # if the recently started task is already the selected task if :selected_task.id == params[:id] if :selected_task.status == 2 # notice - can't start a timer that is already running elsif :selected_task.status == 1 # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = :selected_task.id # change status of the this task :selected_task.status = 2 :selected_task.save else # error - stopped tasks can't be selected end # if the recently started task is not already the selected task else # stop the task timer if it's still running if :selected_task.status == 2 :task_timer = TaskTimer.find(:first, :condition => "ended_at is null and " + "user_id = #{@user.id} and " + "task_id = #{:selected_task.id}") :task_timer.ended_at = :now :task_timer.save end #change status of selected task to stopped :selected_task.status = 0 :selected_task.save # find the newly started task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end # if no selected task exists else # find the newly selected task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end = Time.now # find currently selected task :selected_task = @user.selected_task
# if a selected task exists if :selected_task # if the recently started task is already the selected task if :selected_task.id == params[:id] if :selected_task.status == 2 # notice - can't start a timer that is already running elsif :selected_task.status == 1 # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = :selected_task.id # change status of the this task :selected_task.status = 2 :selected_task.save else # error - stopped tasks can't be selected end # if the recently started task is not already the selected task else # stop the task timer if it's still running if :selected_task.status == 2 :task_timer = TaskTimer.find(:first, :condition => "ended_at is null and " + "user_id = #{@user.id} and " + "task_id = #{:selected_task.id}") :task_timer.ended_at = :now :task_timer.save end #change status of selected task to stopped :selected_task.status = 0 :selected_task.save # find the newly started task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end # if no selected task exists else # find the newly selected task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end
redirect_to :action => 'index' end
private
before_filter :find_user
# # def find_user @user = session[:user] end
end
On Feb 16, 2006, at 12:32 PM, Benjamin Linton wrote:
Strange Find and Replace Bug - r906
Summary:
Doing a selection based find and replace consistently inserts many lines of code when it should be only replace one word with another single word.
In my system, this happens only if you press the "replace" button, instead of the "In selection" button. Pressing the "In selection" button does what it should do. Pressing the "replace" button, or the "replace & find" button produce this effect, regardless of what the search string is. They just replace the selected text with whatever the find window tells them to replace it by.
So this is a problem with the behavior of "replace" when there is already a selection in place. Not sure I would call it a bug actually. Ok, maybe I would. But it is not a bug with "Replace in selection". There is no "replace once in selection" option. There is only "replace all in selection", and that works fine, at least over here.
Haris
Sorry Haris,
but that's not it. It happens for him when he *IS* pressing shift to do a selection based replacement.
Just to clarify, this does not happen for me. My TextMate does not insert any text at the end of the document or other funny stuff. I'll attach the file we troubleshooted in irc. It makes things a little clearer than the inline code in the original email.
Soryu.
Here's a solution that might help you solve my issue:
<Soryu> ok try the following <Soryu> select the text to find and press cmd-e <Soryu> select the text to replace it with and cmd-shift-e <Soryu> make the replacment selection <Soryu> and invoke from the menu: edit->find->replace all in selection <Soryu> does this still bring the bug? <blinton> the bug does not occur <Soryu> interesting, can you make a followup mail? <blinton> sure
The dirty details:
Original text block, a block section of my code:
# Start a task def start_task # capture the time now so all timers match up (8) :now = Time.now # find currently selected task :selected_task = @user.selected_task # if a selected task exists if :selected_task # if the recently started task is already the selected task if :selected_task.id == params[:id] if :selected_task.status == 2 # notice - can't start a timer that is already running elsif :selected_task.status == 1 # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = :selected_task.id # change status of the this task :selected_task.status = 2 :selected_task.save else # error - stopped tasks can't be selected end # if the recently started task is not already the selected task else # stop the task timer if it's still running if :selected_task.status == 2 :task_timer = TaskTimer.find(:first, :condition => "ended_at is null and " + "user_id = #{@user.id} and " + "task_id = #{:selected_task.id}") :task_timer.ended_at = :now :task_timer.save end #change status of selected task to stopped :selected_task.status = 0 :selected_task.save # find the newly started task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end # if no selected task exists else # find the newly selected task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end
redirect_to :action => 'index'
end
I make a full line selection on line #8. I hit CMD+F and hold shift to do a selection only find/replace.
I replace the word ":now" with "now" and here is what happens. Notice that it not only screws up line #8, but also duplicates text down below line #164. Strange!
# Start a task def start_task # capture the time now so all timers match up now # capture the time now so all timers match up (8) :now = Time.now # find currently selected task :selected_task = @user.selected_task # if a selected task exists if :selected_task # if the recently started task is already the selected task if :selected_task.id == params[:id] if :selected_task.status == 2 # notice - can't start a timer that is already running elsif :selected_task.status == 1 # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = :selected_task.id # change status of the this task :selected_task.status = 2 :selected_task.save else # error - stopped tasks can't be selected end # if the recently started task is not already the selected task else # stop the task timer if it's still running if :selected_task.status == 2 :task_timer = TaskTimer.find(:first, :condition => "ended_at is null and " + "user_id = #{@user.id} and " + "task_id = #{:selected_task.id}") :task_timer.ended_at = :now :task_timer.save end #change status of selected task to stopped :selected_task.status = 0 :selected_task.save # find the newly started task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end # if no selected task exists else # find the newly selected task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end (164) = Time.now # find currently selected task :selected_task = @user.selected_task # if a selected task exists if :selected_task # if the recently started task is already the selected task if :selected_task.id == params[:id] if :selected_task.status == 2 # notice - can't start a timer that is already running elsif :selected_task.status == 1 # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = :selected_task.id # change status of the this task :selected_task.status = 2 :selected_task.save else # error - stopped tasks can't be selected end # if the recently started task is not already the selected task else # stop the task timer if it's still running if :selected_task.status == 2 :task_timer = TaskTimer.find(:first, :condition => "ended_at is null and " + "user_id = #{@user.id} and " + "task_id = #{:selected_task.id}") :task_timer.ended_at = :now :task_timer.save end #change status of selected task to stopped :selected_task.status = 0 :selected_task.save # find the newly started task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end # if no selected task exists else # find the newly selected task :selected_task = Task.find(params[:id]) # start the master timer, if necessary unless @user.master_timer_is_running? @user.start_master_timer end # start a new task timer timer TaskTimer.create :started_at => :now, :user_id => @user.id, :task_id = params[:id] # change status of the this task :selected_task.status = 2 :selected_task.save end
redirect_to :action => 'index'
end
I am using r906 (latest). Ignore and wrap are on. Reg exp. is off.
Soryu.
For new threads USE THIS: textmate@lists.macromates.com (threading gets destroyed and the universe will collapse if you don't) http://lists.macromates.com/mailman/listinfo/textmate
Latest Info:
These errors below occurred over a day ago, but they might be the cause of the issue I ran into. I have had my powerbook on for the last few days I do believe.
Another helpful bit of info, is that I recently deleted the file in question. Completely restarted textmate (which i did before to no prevail). Downloaded my email-attached filed from the mailing list. And now I cannot reproduce the bug. So that is good news, but it might help you find a deeper issue?
Good luck and thanks for all your hard / excellent work!
The Errors From The Console:
2006-02-15 12:07:46.752 TextMate[6157] -[NSBigMutableString characterAtIndex:] called with out-of-bounds index. For apps linked on Tiger this will raise an exception. For earlier apps it will produce this one-time warning and continue with existing behavior (which is undefined).
2006-02-15 12:07:46.753 TextMate[6157] *** -[NSBigMutableString characterAtIndex:]: Range or index out of bounds