lib/ver/methods/move.rb in ver-2009.11.29 vs lib/ver/methods/move.rb in ver-2009.12.14

- old
+ new

@@ -40,38 +40,55 @@ end end # Move cursor +count+ characters left. def backward_char(count = 1) - mark_set :insert, "insert - #{count} char" + mark_set :insert, "insert - #{count} displaychars" end # Move cursor +count+ characters right. def forward_char(count = 1) - mark_set :insert, "insert + #{count} char" + mark_set :insert, "insert + #{count} displaychars" end + # Move to the beginning of the line where insert mark is located. + # + # With +count+ it will move to the beginning of the display line, which + # takes line wraps into account. def beginning_of_line(count = nil) - mark_set :insert, 'insert display linestart' + if count + mark_set :insert, 'insert display linestart' + else + mark_set :insert, 'insert linestart' + end end + # Move to the end of the line where insert mark is located. + # + # With +count+ it moves to the end of the display line, so when there is + # a line wrap it will move to the place where the line wraps instead of the + # real end of the line. def end_of_line(count = nil) - mark_set :insert, 'insert display lineend' + if count + mark_set :insert, 'insert display lineend' + else + mark_set :insert, 'insert lineend' + end end def eol_then_insert_mode(count = nil) - end_of_line + end_of_line(count) start_insert_mode end - def sol_then_insert_mode - beginning_of_line + def sol_then_insert_mode(count = nil) + beginning_of_line(count) start_insert_mode end - def forward_char_then_insert_mode - forward_char + def forward_char_then_insert_mode(count = 1) + forward_char(count) start_insert_mode end def go_line(number = 0) mark_set :insert, "#{number}.0" @@ -104,83 +121,153 @@ def page_down(count = 1) mark_set :insert, tk_next_page_pos(count) end def previous_line(count = 1) - mark_set :insert, tk_prev_line_pos(count) + up_down_line(-count.abs) + refresh_selection end def next_line(count = 1) - mark_set :insert, tk_next_line_pos(count) + up_down_line(count.abs) + refresh_selection end + # OK, finally found the issue. + # + # the implementation of tk::TextUpDownLine is smart, but not smart enough. + # It doesn't assume large deltas between the start of up/down movement and + # the last other modification of the insert mark. + # + # This means that, after scrolling with up/down for a few hundred lines, + # it has to calculate the amount of display lines in between, which is a + # very expensive calculation and time increases O(delta_lines). + # + # We'll try to solve this another way, by assuming that there are at least + # a few other lines of same or greater length in between, we simply + # compare against a closer position and make delta_lines as small as + # possible. + # + # Now, if you go to, like column 100 of a line, and there is never a line + # as long for the rest of the file, the scrolling will still slow down a + # lot. This is an issue we can fix if we "forget" the @udl_pos_orig after + # a user-defined maximum delta (something around 200 should do), will + # implement that on demand. + def up_down_line(count) + insert = index(:insert) + + @udl_pos_orig = insert if @udl_pos_prev != insert + + lines = count(@udl_pos_orig, insert, :displaylines) + target = index("#@udl_pos_orig + #{lines + count} displaylines") + @udl_pos_prev = target + mark_set :insert, target + @udl_pos_orig = target if target.x == @udl_pos_orig.x + end + + def forward_scroll(count = 1) + count_abs = count.abs + yview_scroll(count_abs, :units) + next_line(count_abs) + end + + def backward_scroll(count = 1) + count_abs = count.abs + yview_scroll(-count_abs, :units) + previous_line(count_abs) + end + def forward_word(count = 1) + forward_jump(count, &method(:word_char_type)) + end + + def forward_chunk(count = 1) + forward_jump(count, &method(:chunk_char_type)) + end + + def word_right_end(count = 1) count.times do - original_type = type = char_type(get(:insert)) + mark_set :insert, tk_next_word_pos_end('insert') + end + end + + def backward_word(count = 1) + backward_jump(count, &method(:word_char_type)) + end + + def backward_chunk(count = 1) + backward_jump(count, &method(:chunk_char_type)) + end + + private + + def word_char_type(char) + case char + when /\w/; :word + when /\S/; :special + when /\s/; :space + else; raise "You cannot get here" + end + end + + def chunk_char_type(char) + case char + when /\S/; :nonspace + when /\s/; :space + else; raise "You cannot get here" + end + end + + def forward_jump(count) + count.times do + original_type = type = yield(get(:insert)) changed = 0 begin original_pos = index(:insert) execute :mark, :set, :insert, 'insert + 1 chars' break if original_pos == index(:insert) - type = char_type(get(:insert)) + type = yield(get(:insert)) changed += 1 if type != original_type original_type = type end until changed > 0 && type != :space end Tk::Event.generate(self, '<<Movement>>') rescue => ex VER.error(ex) end - def word_right_end(count = 1) + def backward_jump(count = 1) count.times do - mark_set :insert, tk_next_word_pos_end('insert') - end - end - - def backward_word(count = 1) - count.times do - original_type = type = char_type(get(:insert)) + original_type = type = yield(get(:insert)) changed = 0 begin original_pos = index(:insert) execute :mark, :set, :insert, 'insert - 1 chars' break if index(:insert) == original_pos - type = char_type(get(:insert)) + type = yield(get(:insert)) changed += 1 if type != original_type original_type = type end until changed > 0 && type != :space - type = char_type(get('insert - 1 chars')) + type = yield(get('insert - 1 chars')) while type == original_type original_pos = index(:insert) execute :mark, :set, :insert, 'insert - 1 chars' break if index(:insert) == original_pos - type = char_type(get('insert - 1 chars')) + type = yield(get('insert - 1 chars')) end end Tk::Event.generate(self, '<<Movement>>') rescue => ex VER.error(ex) - end - - private - - def char_type(char) - case char - when /\w/; :word - when /\S/; :special - when /\s/; :space - else; raise "You cannot get here" - end end def tk_prev_word_pos(start) Tk.execute('tk::TextPrevPos', tk_pathname, start, 'tcl_startOfPreviousWord').to_s end