Module: VER::Methods::Move
- Defined in:
- lib/ver/methods/move.rb
Constant Summary collapse
- GO_MATCHING_RIGHT =
{ '(' => ')', '{' => '}', '[' => ']', '<' => '>', }
- GO_MATCHING_LEFT =
GO_MATCHING_RIGHT.invert
Class Method Summary collapse
- .ask_go_line(text) ⇒ Object
- .backward_jump(text, count) ⇒ Object
- .backward_scroll(text, count = text.prefix_count) ⇒ Object
- .chunk_char_type(char) ⇒ Object
-
.end_of_line(text, count_or_mode = nil) ⇒ Object
Move to the end of the line where insert mark is located.
- .end_of_sentence(text, count = text.prefix_count) ⇒ Object
- .end_of_text(text, count = text.prefix_arg) ⇒ Object
- .forward_jump(text, count) ⇒ Object
- .forward_scroll(text, count = text.prefix_count) ⇒ Object
- .go_column(text, number = text.prefix_count) ⇒ Object
- .go_line(text, number = text.prefix_count) ⇒ Object
-
.home_of_line(text, count = text.prefix_arg) ⇒ Object
Move to the first character of the line in which insert mark is located.
- .index_at_word_right_end(text, count = text.prefix_count) ⇒ Object
- .matching_brace(text) ⇒ Object
- .matching_brace_pos(text, index = :insert) ⇒ Object
-
.next_char(text, count = text.prefix_count) ⇒ Object
Move cursor
count
characters right. - .next_chunk(text, count = text.prefix_count) ⇒ Object
- .next_line(text, count = text.prefix_count) ⇒ Object
- .next_page(text, count = text.prefix_count) ⇒ Object
- .next_word(text, count = text.prefix_count) ⇒ Object
-
.next_word_end(text, count = text.prefix_count) ⇒ Object
Jump to the last character of the word the insert cursor is over currently.
- .parse_go_line(text, input) ⇒ Object
- .prefix_arg_sol(text) ⇒ Object
-
.prev_char(text, count = text.prefix_count) ⇒ Object
Move cursor
count
characters left. - .prev_chunk(text, count = text.prefix_count) ⇒ Object
- .prev_line(text, count = text.prefix_count) ⇒ Object
-
.prev_page(text, count = text.prefix_count) ⇒ Object
HACK: but it’s just too good to do it manually.
- .prev_word(text, count = text.prefix_count) ⇒ Object
-
.start_of_line(text, count = text.prefix_count) ⇒ Object
Move to the beginning of the line in which insert mark is located.
-
.start_of_text(text) ⇒ Object
Basically like [go_line] without arguments, but much nicer name.
-
.up_down_line(text, count) ⇒ Object
OK, finally found the issue.
- .virtual(text, action, count = text.prefix_count) ⇒ Object
- .word_char_type(char) ⇒ Object
Class Method Details
.ask_go_line(text) ⇒ Object
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/ver/methods/move.rb', line 113 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 |
.backward_jump(text, count) ⇒ Object
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
# File 'lib/ver/methods/move.rb', line 315 def backward_jump(text, count) count.times do original_type = type = yield(text.get(:insert)) changed = 0 begin original_pos = text.index(:insert) text.execute_only(:mark, :set, :insert, 'insert - 1 chars') break if text.index(:insert) == original_pos type = yield(text.get(:insert)) changed += 1 if type != original_type original_type = type end until changed > 0 && type != :space type = yield(text.get('insert - 1 chars')) while type == original_type original_pos = text.index(:insert) text.execute_only(:mark, :set, :insert, 'insert - 1 chars') break if text.index(:insert) == original_pos type = yield(text.get('insert - 1 chars')) end end Tk::Event.generate(text, '<<Movement>>') rescue => ex VER.error(ex) end |
.backward_scroll(text, count = text.prefix_count) ⇒ Object
205 206 207 208 209 |
# File 'lib/ver/methods/move.rb', line 205 def backward_scroll(text, count = text.prefix_count) count_abs = count.abs text.yview_scroll(-count_abs, :units) prev_line(text, count_abs) end |
.chunk_char_type(char) ⇒ Object
285 286 287 288 289 290 291 292 |
# File 'lib/ver/methods/move.rb', line 285 def chunk_char_type(char) case char when /\S/; :nonspace when /\s/; :space else Kernel.raise "No matching chunk type for: %p " % [char] end end |
.end_of_line(text, count_or_mode = nil) ⇒ Object
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.
97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/ver/methods/move.rb', line 97 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 text.mark_set(:insert, 'insert display lineend') end end |
.end_of_sentence(text, count = text.prefix_count) ⇒ Object
166 167 168 169 170 171 172 173 |
# File 'lib/ver/methods/move.rb', line 166 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 |
.end_of_text(text, count = text.prefix_arg) ⇒ Object
158 159 160 161 162 163 164 |
# File 'lib/ver/methods/move.rb', line 158 def end_of_text(text, count = text.prefix_arg) if count text.mark_set(:insert, "#{count}.0") else text.mark_set(:insert, :end) end end |
.forward_jump(text, count) ⇒ Object
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/ver/methods/move.rb', line 294 def forward_jump(text, count) count.times do original_type = type = yield(text.get(:insert)) changed = 0 begin original_pos = text.index(:insert) text.execute_only(:mark, :set, :insert, 'insert + 1 chars') break if original_pos == text.index(: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(text, '<<Movement>>') rescue => ex VER.error(ex) end |
.forward_scroll(text, count = text.prefix_count) ⇒ Object
199 200 201 202 203 |
# File 'lib/ver/methods/move.rb', line 199 def forward_scroll(text, count = text.prefix_count) count_abs = count.abs text.yview_scroll(count_abs, :units) next_line(text, count_abs) end |
.go_column(text, number = text.prefix_count) ⇒ Object
149 150 151 |
# File 'lib/ver/methods/move.rb', line 149 def go_column(text, number = text.prefix_count) text.mark_set(:insert, "insert linestart + #{number} chars") end |
.go_line(text, number = text.prefix_count) ⇒ Object
109 110 111 |
# File 'lib/ver/methods/move.rb', line 109 def go_line(text, number = text.prefix_count) text.mark_set(:insert, "#{number}.0") end |
.home_of_line(text, count = text.prefix_arg) ⇒ Object
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.
82 83 84 85 86 87 88 89 90 |
# File 'lib/ver/methods/move.rb', line 82 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 |
.index_at_word_right_end(text, count = text.prefix_count) ⇒ Object
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/ver/methods/move.rb', line 242 def index_at_word_right_end(text, count = text.prefix_count) offset = 1 last = text.index('end') count.times do pos = text.index("insert + #{offset} chars") return if pos == last type = word_char_type(text.get(pos)) 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 |
.matching_brace(text) ⇒ Object
19 20 21 22 23 24 25 |
# File 'lib/ver/methods/move.rb', line 19 def matching_brace(text) if pos = matching_brace_pos(text, :insert) text.mark_set(:insert, pos) else VER.warn "No matching brace" end end |
.matching_brace_pos(text, index = :insert) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/ver/methods/move.rb', line 27 def matching_brace_pos(text, index = :insert) opening = text.get(index) if closing = GO_MATCHING_RIGHT[opening] start = "#{index} + 1 chars" search = text.method(:search_all) elsif closing = GO_MATCHING_LEFT[opening] start = index search = text.method(:rsearch_all) else return end balance = 1 needle = Regexp.union(opening, closing) search.call needle, start do |match, from, to| case match when opening balance += 1 when closing balance -= 1 end if balance == 0 return from end end end |
.next_char(text, count = text.prefix_count) ⇒ Object
Move cursor count
characters right.
62 63 64 |
# File 'lib/ver/methods/move.rb', line 62 def next_char(text, count = text.prefix_count) text.mark_set(:insert, "insert + #{count} displaychars") end |
.next_chunk(text, count = text.prefix_count) ⇒ Object
215 216 217 |
# File 'lib/ver/methods/move.rb', line 215 def next_chunk(text, count = text.prefix_count) forward_jump(text, count, &method(:chunk_char_type)) end |
.next_line(text, count = text.prefix_count) ⇒ Object
195 196 197 |
# File 'lib/ver/methods/move.rb', line 195 def next_line(text, count = text.prefix_count) up_down_line(text, count.abs) end |
.next_page(text, count = text.prefix_count) ⇒ Object
238 239 240 |
# File 'lib/ver/methods/move.rb', line 238 def next_page(text, count = text.prefix_count) text.mark_set(:insert, text.tk_next_page_pos(count)) end |
.next_word(text, count = text.prefix_count) ⇒ Object
211 212 213 |
# File 'lib/ver/methods/move.rb', line 211 def next_word(text, count = text.prefix_count) forward_jump(text, count, &method(:word_char_type)) end |
.next_word_end(text, count = text.prefix_count) ⇒ Object
Jump to the last character of the word the insert cursor is over currently.
220 221 222 |
# File 'lib/ver/methods/move.rb', line 220 def next_word_end(text, count = text.prefix_count) text.mark_set(:insert, index_at_word_right_end(text, count)) end |
.parse_go_line(text, input) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/ver/methods/move.rb', line 137 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 |
.prefix_arg_sol(text) ⇒ Object
14 15 16 17 |
# File 'lib/ver/methods/move.rb', line 14 def prefix_arg_sol(text) return if text.update_prefix_arg(text) start_of_line(text) end |
.prev_char(text, count = text.prefix_count) ⇒ Object
Move cursor count
characters left.
57 58 59 |
# File 'lib/ver/methods/move.rb', line 57 def prev_char(text, count = text.prefix_count) text.mark_set(:insert, "insert - #{count} displaychars") end |
.prev_chunk(text, count = text.prefix_count) ⇒ Object
228 229 230 |
# File 'lib/ver/methods/move.rb', line 228 def prev_chunk(text, count = text.prefix_count) backward_jump(text, count, &method(:chunk_char_type)) end |
.prev_line(text, count = text.prefix_count) ⇒ Object
191 192 193 |
# File 'lib/ver/methods/move.rb', line 191 def prev_line(text, count = text.prefix_count) up_down_line(text, -count.abs) end |
.prev_page(text, count = text.prefix_count) ⇒ Object
HACK: but it’s just too good to do it manually
234 235 236 |
# File 'lib/ver/methods/move.rb', line 234 def prev_page(text, count = text.prefix_count) text.mark_set(:insert, text.tk_prev_page_pos(count)) end |
.prev_word(text, count = text.prefix_count) ⇒ Object
224 225 226 |
# File 'lib/ver/methods/move.rb', line 224 def prev_word(text, count = text.prefix_count) backward_jump(text, count, &method(:word_char_type)) end |
.start_of_line(text, count = text.prefix_count) ⇒ Object
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.
70 71 72 73 74 75 76 |
# File 'lib/ver/methods/move.rb', line 70 def start_of_line(text, count = text.prefix_count) if count text.mark_set(:insert, 'insert display linestart') else text.mark_set(:insert, 'insert linestart') end end |
.start_of_text(text) ⇒ Object
Basically like [go_line] without arguments, but much nicer name.
154 155 156 |
# File 'lib/ver/methods/move.rb', line 154 def start_of_text(text) text.mark_set(:insert, "1.0") end |
.up_down_line(text, count) ⇒ Object
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.
366 367 368 369 |
# File 'lib/ver/methods/move.rb', line 366 def up_down_line(text, count) target = text.up_down_line(count) text.mark_set(:insert, target) end |
.virtual(text, action, count = text.prefix_count) ⇒ Object
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ver/methods/move.rb', line 175 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 |
.word_char_type(char) ⇒ Object
275 276 277 278 279 280 281 282 283 |
# File 'lib/ver/methods/move.rb', line 275 def word_char_type(char) case char when /\w/; :word when /\S/; :special when /\s/; :space else Kernel.raise "No matching char type for: %p" % [char] end end |