lib/ver/methods/move.rb in ver-2009.12.14 vs lib/ver/methods/move.rb in ver-2010.02
- old
+ new
@@ -7,295 +7,366 @@
'[' => ']',
'<' => '>',
}
GO_MATCHING_LEFT = GO_MATCHING_RIGHT.invert
- def matching_brace(count = nil)
- opening = get(:insert)
+ module_function
+ def prefix_arg_sol(text)
+ return if text.update_prefix_arg(text)
+ start_of_line(text)
+ end
+
+ def matching_brace(text)
+ if pos = matching_brace_pos(text, :insert)
+ text.mark_set(:insert, pos)
+ else
+ VER.warn "No matching brace"
+ end
+ end
+
+ def matching_brace_pos(text, index = :insert)
+ opening = text.get(index)
+
if closing = GO_MATCHING_RIGHT[opening]
- search = method(:search_all)
- level = 1
- start = 'insert + 1 chars'
+ start = "#{index} + 1 chars"
+ search = text.method(:search_all)
elsif closing = GO_MATCHING_LEFT[opening]
- search = method(:rsearch_all)
- level = 1
- start = 'insert'
+ start = index
+ search = text.method(:rsearch_all)
else
return
end
+ balance = 1
needle = Regexp.union(opening, closing)
-
- search.call(needle, start) do |match, pos, from|
- if match == opening
- level += 1
- elsif match == closing
- level -= 1
+ search.call needle, start do |match, from, to|
+ case match
+ when opening
+ balance += 1
+ when closing
+ balance -= 1
end
- if level < 1
- mark_set :insert, pos
- return
+ if balance == 0
+ return from
end
end
end
# Move cursor +count+ characters left.
- def backward_char(count = 1)
- mark_set :insert, "insert - #{count} displaychars"
+ def prev_char(text, count = text.prefix_count)
+ text.mark_set(:insert, "insert - #{count} displaychars")
end
# Move cursor +count+ characters right.
- def forward_char(count = 1)
- mark_set :insert, "insert + #{count} displaychars"
+ def next_char(text, count = text.prefix_count)
+ text.mark_set(:insert, "insert + #{count} displaychars")
end
- # Move to the beginning of the line where insert mark is located.
+ # Move to the beginning of the line in which 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)
+ def start_of_line(text, count = text.prefix_count)
if count
- mark_set :insert, 'insert display linestart'
+ text.mark_set(:insert, 'insert display linestart')
else
- mark_set :insert, 'insert linestart'
+ text.mark_set(:insert, 'insert linestart')
end
end
+ # Move to the first character of the line in which insert mark is located.
+ #
+ # With +count+ it will move to the linestart of the displayed, taking
+ # linewraps into account.
+ def home_of_line(text, count = text.prefix_arg)
+ if count
+ start_of_line(text, true)
+ else
+ x = text.get('insert linestart', 'insert lineend').index(/\S/) || 0
+ y = text.index('insert').y
+ text.mark_set(:insert, "#{y}.#{x}")
+ 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)
- if count
- mark_set :insert, 'insert display lineend'
+ def end_of_line(text, count_or_mode = nil)
+ case count_or_mode
+ when Symbol
+ text.mark_set(:insert, 'insert display lineend')
+ text.minor_mode(:control, count_or_mode)
+ when nil
+ text.mark_set(:insert, 'insert lineend')
else
- mark_set :insert, 'insert lineend'
+ text.mark_set(:insert, 'insert display lineend')
end
end
- def eol_then_insert_mode(count = nil)
- end_of_line(count)
- start_insert_mode
+ def go_line(text, number = text.prefix_count)
+ text.mark_set(:insert, "#{number}.0")
end
- def sol_then_insert_mode(count = nil)
- beginning_of_line(count)
- start_insert_mode
+ def ask_go_line(text)
+ initial = $1 if text.event.unicode =~ /^(\d+)$/
+ question = 'Go to [line number|+lines][,column number]: '
+ text.ask question, value: initial do |answer, action|
+ case action
+ when :attempt
+ parse_go_line text, answer do |index|
+ text.mark_set(:insert, index)
+ :abort
+ end
+ when :modified
+ parse_go_line text, answer do |index|
+ text.tag_configure(Search::TAG, Search::HIGHLIGHT)
+ text.tag_add(Search::TAG, index, index.lineend)
+ text.see(index)
+ Tk::After.ms(3000){
+ text.tag_remove(Search::TAG, index, index.lineend)
+ text.see(:insert)
+ }
+ end
+ end
+ end
end
- def forward_char_then_insert_mode(count = 1)
- forward_char(count)
- start_insert_mode
+ def parse_go_line(text, input)
+ case input.to_s.strip
+ when /^\+(\d+)(?:,(\d+))?$/
+ line, column = $1.to_i, $2.to_i
+ current = text.index(:insert)
+ yield text.index("#{current.line + line}.#{column}")
+ when /^(\d+)(?:,(\d+))?$/
+ line, column = $1.to_i, $2.to_i
+ yield text.index("#{line}.#{column}")
+ end
end
- def go_line(number = 0)
- mark_set :insert, "#{number}.0"
+ def go_column(text, number = text.prefix_count)
+ text.mark_set(:insert, "insert linestart + #{number} chars")
end
- def end_of_file(count = nil)
+ # Basically like [go_line] without arguments, but much nicer name.
+ def start_of_text(text)
+ text.mark_set(:insert, "1.0")
+ end
+
+ def end_of_text(text, count = text.prefix_arg)
if count
- mark_set :insert, "#{count}.0"
+ text.mark_set(:insert, "#{count}.0")
else
- mark_set :insert, :end
+ text.mark_set(:insert, :end)
end
end
- def virtual_movement(name, count = 1)
- pos = index(:insert)
- __send__(name, count)
- mark = index(:insert)
- mark_set :insert, pos
+ def end_of_sentence(text, count = text.prefix_count)
+ text.search_all(/\.\s/, 'insert') do |match, from, to|
+ p match: match, from: from, to: to
+ text.mark_set(:insert, "#{to} - 1 chars")
+ count -= 1
+ return if count <= 0
+ end
+ end
+
+ def virtual(text, action, count = text.prefix_count)
+ pos = text.index(:insert)
+
+ if action.respond_to?(:call)
+ action.call(text, count)
+ else
+ send(action, text, count)
+ end
+
+ mark = text.index(:insert)
+ text.mark_set(:insert, pos)
return [pos, mark].sort
rescue => ex
VER.error(ex)
end
- # HACK: but it's just too good to do it manually
+ def prev_line(text, count = text.prefix_count)
+ up_down_line(text, -count.abs)
+ end
- def page_up(count = 1)
- mark_set :insert, tk_prev_page_pos(count)
+ def next_line(text, count = text.prefix_count)
+ up_down_line(text, count.abs)
end
- def page_down(count = 1)
- mark_set :insert, tk_next_page_pos(count)
+ def forward_scroll(text, count = text.prefix_count)
+ count_abs = count.abs
+ text.yview_scroll(count_abs, :units)
+ next_line(text, count_abs)
end
- def previous_line(count = 1)
- up_down_line(-count.abs)
- refresh_selection
+ def backward_scroll(text, count = text.prefix_count)
+ count_abs = count.abs
+ text.yview_scroll(-count_abs, :units)
+ prev_line(text, count_abs)
end
- def next_line(count = 1)
- up_down_line(count.abs)
- refresh_selection
+ def next_word(text, count = text.prefix_count)
+ forward_jump(text, count, &method(:word_char_type))
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)
+ def next_chunk(text, count = text.prefix_count)
+ forward_jump(text, count, &method(:chunk_char_type))
+ end
- @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
+ # Jump to the last character of the word the insert cursor is over currently.
+ def next_word_end(text, count = text.prefix_count)
+ text.mark_set(:insert, index_at_word_right_end(text, count))
end
- def forward_scroll(count = 1)
- count_abs = count.abs
- yview_scroll(count_abs, :units)
- next_line(count_abs)
+ def prev_word(text, count = text.prefix_count)
+ backward_jump(text, count, &method(:word_char_type))
end
- def backward_scroll(count = 1)
- count_abs = count.abs
- yview_scroll(-count_abs, :units)
- previous_line(count_abs)
+ def prev_chunk(text, count = text.prefix_count)
+ backward_jump(text, count, &method(:chunk_char_type))
end
- def forward_word(count = 1)
- forward_jump(count, &method(:word_char_type))
+ # HACK: but it's just too good to do it manually
+
+ def prev_page(text, count = text.prefix_count)
+ text.mark_set(:insert, text.tk_prev_page_pos(count))
end
- def forward_chunk(count = 1)
- forward_jump(count, &method(:chunk_char_type))
+ def next_page(text, count = text.prefix_count)
+ text.mark_set(:insert, text.tk_next_page_pos(count))
end
- def word_right_end(count = 1)
+ def index_at_word_right_end(text, count = text.prefix_count)
+ offset = 1
+ last = text.index('end')
+
count.times do
- mark_set :insert, tk_next_word_pos_end('insert')
- end
- end
+ pos = text.index("insert + #{offset} chars")
- def backward_word(count = 1)
- backward_jump(count, &method(:word_char_type))
- end
+ return if pos == last
- def backward_chunk(count = 1)
- backward_jump(count, &method(:chunk_char_type))
- end
+ type = word_char_type(text.get(pos))
- private
+ while type == :space
+ offset += 1
+ pos = text.index("insert + #{offset} chars")
+ break if pos == last
+ type = word_char_type(text.get(pos))
+ end
+ lock = type
+
+ while type == lock && type != :space
+ offset += 1
+ pos = text.index("insert + #{offset} chars")
+ break if pos == last
+ type = word_char_type(text.get(pos))
+ end
+ end
+
+ text.index("insert + #{offset - 1} chars")
+ rescue => ex
+ VER.error(ex)
+ end
+
def word_char_type(char)
case char
when /\w/; :word
when /\S/; :special
when /\s/; :space
- else; raise "You cannot get here"
+ else
+ Kernel.raise "No matching char type for: %p" % [char]
end
end
def chunk_char_type(char)
case char
when /\S/; :nonspace
when /\s/; :space
- else; raise "You cannot get here"
+ else
+ Kernel.raise "No matching chunk type for: %p " % [char]
end
end
- def forward_jump(count)
+ def forward_jump(text, count)
count.times do
- original_type = type = yield(get(:insert))
+ original_type = type = yield(text.get(:insert))
changed = 0
begin
- original_pos = index(:insert)
- execute :mark, :set, :insert, 'insert + 1 chars'
- break if original_pos == index(:insert)
+ original_pos = text.index(:insert)
+ text.execute_only(:mark, :set, :insert, 'insert + 1 chars')
+ break if original_pos == text.index(:insert)
- type = yield(get(:insert))
+ type = yield(text.get(:insert))
changed += 1 if type != original_type
original_type = type
end until changed > 0 && type != :space
end
- Tk::Event.generate(self, '<<Movement>>')
+ Tk::Event.generate(text, '<<Movement>>')
rescue => ex
VER.error(ex)
end
- def backward_jump(count = 1)
+ def backward_jump(text, count)
count.times do
- original_type = type = yield(get(:insert))
+ original_type = type = yield(text.get(:insert))
changed = 0
begin
- original_pos = index(:insert)
- execute :mark, :set, :insert, 'insert - 1 chars'
- break if index(:insert) == original_pos
+ original_pos = text.index(:insert)
+ text.execute_only(:mark, :set, :insert, 'insert - 1 chars')
+ break if text.index(:insert) == original_pos
- type = yield(get(:insert))
+ type = yield(text.get(:insert))
changed += 1 if type != original_type
original_type = type
end until changed > 0 && type != :space
- type = yield(get('insert - 1 chars'))
+ type = yield(text.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
+ original_pos = text.index(:insert)
+ text.execute_only(:mark, :set, :insert, 'insert - 1 chars')
+ break if text.index(:insert) == original_pos
- type = yield(get('insert - 1 chars'))
+ type = yield(text.get('insert - 1 chars'))
end
end
- Tk::Event.generate(self, '<<Movement>>')
+ Tk::Event.generate(text, '<<Movement>>')
rescue => ex
VER.error(ex)
end
- def tk_prev_word_pos(start)
- Tk.execute('tk::TextPrevPos', tk_pathname, start, 'tcl_startOfPreviousWord').to_s
- end
-
- def tk_next_word_pos(start)
- Tk.execute('tk::TextNextPos', tk_pathname, start, 'tcl_startOfNextWord').to_s
- end
-
- def tk_next_word_pos_end(start)
- Tk.execute('tk::TextNextWord', tk_pathname, start).to_s
- end
-
- def tk_prev_line_pos(count)
- Tk.execute('tk::TextUpDownLine', tk_pathname, -count.abs).to_s
- end
-
- def tk_next_line_pos(count)
- Tk.execute('tk::TextUpDownLine', tk_pathname, count).to_s
- end
-
- def tk_prev_page_pos(count)
- Tk.execute('tk::TextScrollPages', tk_pathname, -count.abs).to_s
- end
-
- def tk_next_page_pos(count)
- Tk.execute('tk::TextScrollPages', tk_pathname, count).to_s
+ # 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(text, count)
+ target = text.up_down_line(count)
+ text.mark_set(:insert, target)
end
end
end
end