ver 2009.11.29 → 2009.12.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/AUTHORS +6 -0
  2. data/CHANGELOG +353 -1
  3. data/LICENSE +18 -0
  4. data/MANIFEST +11 -1
  5. data/Rakefile +2 -1
  6. data/bin/ver +3 -12
  7. data/config/detect.rb +1 -1
  8. data/config/keymap/diakonos.rb +181 -0
  9. data/config/keymap/emacs.rb +24 -24
  10. data/config/keymap/vim.rb +162 -127
  11. data/config/rc.rb +29 -14
  12. data/config/syntax/Nemerle.json +3 -3
  13. data/lib/ver.rb +88 -134
  14. data/lib/ver/entry.rb +5 -0
  15. data/lib/ver/exception_view.rb +97 -0
  16. data/lib/ver/hover_completion.rb +14 -7
  17. data/lib/ver/keymap.rb +30 -1
  18. data/lib/ver/layout.rb +20 -14
  19. data/lib/ver/methods.rb +6 -15
  20. data/lib/ver/methods/bookmark.rb +189 -0
  21. data/lib/ver/methods/completion.rb +2 -2
  22. data/lib/ver/methods/control.rb +109 -26
  23. data/lib/ver/methods/ctags.rb +28 -4
  24. data/lib/ver/methods/delete.rb +85 -4
  25. data/lib/ver/methods/insert.rb +73 -52
  26. data/lib/ver/methods/move.rb +122 -35
  27. data/lib/ver/methods/open.rb +4 -43
  28. data/lib/ver/methods/search.rb +46 -17
  29. data/lib/ver/methods/select.rb +121 -24
  30. data/lib/ver/methods/undo.rb +23 -0
  31. data/lib/ver/methods/views.rb +5 -0
  32. data/lib/ver/mode.rb +18 -17
  33. data/lib/ver/status.rb +2 -2
  34. data/lib/ver/status/context.rb +166 -0
  35. data/lib/ver/text.rb +43 -81
  36. data/lib/ver/text/index.rb +24 -7
  37. data/lib/ver/undo.rb +289 -0
  38. data/lib/ver/vendor/sized_array.rb +70 -0
  39. data/lib/ver/vendor/textpow.rb +6 -1
  40. data/lib/ver/version.rb +3 -0
  41. data/lib/ver/view.rb +11 -8
  42. data/lib/ver/view/list/grep.rb +15 -4
  43. data/lib/ver/view/term.rb +9 -3
  44. data/spec/helper.rb +94 -0
  45. data/ver.gemspec +9 -6
  46. metadata +25 -5
  47. data/spec/keymap.rb +0 -224
@@ -6,6 +6,19 @@ module VER
6
6
  ctags_find(word)
7
7
  end
8
8
 
9
+ def ctags_go(name = nil)
10
+ if name
11
+ ctags_content do |tag_name, file_name, ex_cmd|
12
+ next unless tag_name == name
13
+ return ctags_execute(file_name, ex_cmd)
14
+ end
15
+ else
16
+ status_ask 'Tag name: ' do |tag_name|
17
+ ctags_go(tag_name)
18
+ end
19
+ end
20
+ end
21
+
9
22
  def ctags_find(needle)
10
23
  ctags_content do |tag_name, file_name, ex_cmd|
11
24
  next unless tag_name == needle
@@ -13,15 +26,26 @@ module VER
13
26
  end
14
27
  end
15
28
 
29
+ def ctags_prev
30
+ if bm = VER.ctag_stack.pop
31
+ bookmark_open(bm)
32
+ else
33
+ message("Tag stack empty.")
34
+ end
35
+ end
36
+
16
37
  def ctags_execute(file_name, ex_cmd)
17
38
  case ex_cmd
18
39
  when /^\d+$/
19
- view.find_or_create(file_name) do |view|
20
- view.text.go_line(ex_cmd.to_i)
21
- end
40
+ VER.ctag_stack << Bookmarks::Bookmark.new(nil, *bookmark_value)
41
+
42
+ view.find_or_create(file_name, ex_cmd.to_i)
22
43
  when /^\/(.*)\/$/
23
- source = $1.gsub!(/(?!\\)([()])/, '\\\\\\1')
44
+ VER.ctag_stack << Bookmarks::Bookmark.new(nil, *bookmark_value)
45
+
46
+ source = $1.gsub(/(?!\\)([()])/, '\\\\\\1')
24
47
  regexp = Regexp.new(source)
48
+
25
49
  self.view.find_or_create(file_name) do |view|
26
50
  view.text.tag_all_matching(:search, regexp, Search::SEARCH_HIGHLIGHT)
27
51
  view.text.search_next
@@ -6,25 +6,106 @@ module VER
6
6
  start_insert_mode
7
7
  end
8
8
 
9
+ # Given a +motion+, this method will execute a virtual movement with the
10
+ # +count+ argument and [delete] the corresponding indices.
11
+ #
12
+ # @param [Symbol String] motion name of a method acceptable for [virtual_movement]
13
+ # @param [#to_i] count
14
+ #
15
+ # @see delete
16
+ # @see VER::Move#virtual_movement
9
17
  def delete_motion(motion, count = 1)
10
18
  delete(*virtual_movement(motion, count))
11
19
  end
12
20
 
21
+ # Given a +motion+, this method will execute a virtual movement with the
22
+ # +count+ argument and [kill] the corresponding indices.
23
+ #
24
+ # @param [Symbol String] motion name of a method acceptable for [virtual_movement]
25
+ # @param [#to_i] count
26
+ #
27
+ # @see kill
28
+ # @see VER::Move#virtual_movement
29
+ def kill_motion(motion, count = 1)
30
+ kill(*virtual_movement(motion, count))
31
+ end
32
+
33
+ # Delete current line and upto +count+ subsequent lines.
34
+ #
35
+ # @param [#to_i] count Number of lines to delete
36
+ #
37
+ # @see delete
38
+ # @see kill_line
13
39
  def delete_line(count = 1)
14
- count.times do
15
- delete 'insert linestart', 'insert lineend + 1 char'
16
- end
40
+ from = index('insert linestart')
41
+ count = count.abs - 1
42
+ to = index("#{from.y + count}.#{from.x} lineend + 1 char")
43
+ delete(from, to)
17
44
  end
18
45
 
46
+ # Copy current line and upto +count+ subsequent lines and delete them.
47
+ #
48
+ # @param [#to_i] count Number of lines to kill
49
+ #
50
+ # @see kill
51
+ # @see delete_line
52
+ def kill_line(count = 1)
53
+ from = index('insert linestart')
54
+ count = count.abs - 1
55
+ to = index("#{from.y + count.to_i}.#{from.x} lineend + 1 char")
56
+ kill(from, to)
57
+ end
58
+
59
+ # Wrapper for [kill_line] that starts insert mode after killing +count+
60
+ # lines.
61
+ #
62
+ # @param [#to_i] count Number of lines to kill
63
+ #
64
+ # @see kill_line
65
+ # @see start_insert_mode
19
66
  def change_line(count = 1)
20
- delete_line(count)
67
+ kill_line(count)
21
68
  start_insert_mode
22
69
  end
23
70
 
71
+ # Tag and delete all trailing whitespace in the current buffer.
24
72
  def delete_trailing_whitespace
25
73
  tag_all_trailing_whitespace
26
74
  execute :delete, *tag_ranges('invalid.trailing-whitespace').flatten
27
75
  end
76
+
77
+ # Delete text between +indices+
78
+ def delete(*indices)
79
+ indices_size = indices.size
80
+ return if indices_size == 0
81
+
82
+ undo_record do |record|
83
+ if indices_size == 1
84
+ record.delete(indices.first)
85
+ else
86
+ indices.each_slice(2) do |from, to|
87
+ next if from == to
88
+ record.delete(from, to)
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ # Copy text between +indices+ and delete it.
95
+ #
96
+ # @param [Array<Text::Index, String, Symbol>] indices
97
+ # one or more indices within the buffer, must be an even number of
98
+ # indices if more than one.
99
+ def kill(*indices)
100
+ if indices.size > 2
101
+ deleted = indices.each_slice(2).map{|left, right| get(left, right) }
102
+ else
103
+ deleted = get(*indices)
104
+ end
105
+
106
+ copy(deleted)
107
+ delete(*indices)
108
+ end
28
109
  end
29
110
  end
30
111
  end
@@ -17,32 +17,35 @@ module VER
17
17
  end
18
18
 
19
19
  def insert_indented_newline_below
20
- if VER.options.autoindent
21
- line = get('insert linestart', 'insert lineend')
20
+ undo_record do |record|
21
+ if options.autoindent
22
+ line = get('insert linestart', 'insert lineend')
22
23
 
23
- indent = line.empty? ? "" : (line[/^\s+/] || '')
24
- mark_set :insert, 'insert lineend'
25
- insert :insert, "\n#{indent}"
26
- else
27
- mark_set :insert, 'insert lineend'
28
- insert :insert, "\n"
29
- end
24
+ indent = line.empty? ? "" : (line[/^\s+/] || '')
25
+ mark_set :insert, 'insert lineend'
26
+ record.insert :insert, "\n#{indent}"
27
+ else
28
+ mark_set :insert, 'insert lineend'
29
+ record.insert :insert, "\n"
30
+ end
30
31
 
31
- clean_line('insert - 1 line')
32
- start_insert_mode
32
+ clean_line('insert - 1 line', record)
33
+ start_insert_mode
34
+ end
33
35
  end
34
36
 
35
37
  def insert_indented_newline_above
36
- if index(:insert).y > 1
37
- previous_line
38
- insert_indented_newline_below
39
- else
40
- insert('insert linestart', "\n")
41
- mark_set(:insert, 'insert - 1 line')
38
+ undo_record do |record|
39
+ if index(:insert).y > 1
40
+ previous_line
41
+ insert_indented_newline_below
42
+ else
43
+ record.insert('insert linestart', "\n")
44
+ mark_set(:insert, 'insert - 1 line')
45
+ clean_line('insert - 1 line', record)
46
+ start_insert_mode
47
+ end
42
48
  end
43
-
44
- clean_line('insert + 1 line')
45
- start_insert_mode
46
49
  end
47
50
 
48
51
  def insert_newline
@@ -50,7 +53,7 @@ module VER
50
53
  end
51
54
 
52
55
  def insert_indented_newline
53
- if VER.options.autoindent
56
+ if options.autoindent
54
57
  fallback_insert_indented_newline
55
58
  else
56
59
  insert_newline
@@ -136,26 +139,28 @@ module VER
136
139
  end
137
140
 
138
141
  def fallback_insert_indented_newline
139
- line1 = get('insert linestart', 'insert lineend')
140
- indentation1 = line1[/^\s+/] || ''
141
- insert :insert, "\n"
142
+ undo_record do |record|
143
+ line1 = get('insert linestart', 'insert lineend')
144
+ indentation1 = line1[/^\s+/] || ''
145
+ record.insert :insert, "\n"
142
146
 
143
- line2 = get('insert linestart', 'insert lineend')
144
- indentation2 = line2[/^\s+/] || ''
147
+ line2 = get('insert linestart', 'insert lineend')
148
+ indentation2 = line2[/^\s+/] || ''
145
149
 
146
- replace(
147
- 'insert linestart',
148
- "insert linestart + #{indentation2.size} chars",
149
- indentation1
150
- )
150
+ record.replace(
151
+ 'insert linestart',
152
+ "insert linestart + #{indentation2.size} chars",
153
+ indentation1
154
+ )
151
155
 
152
- clean_line('insert - 1 line')
156
+ clean_line('insert - 1 line', record)
157
+ end
153
158
  end
154
159
 
155
160
  # Most of the input will be in US-ASCII, but an encoding can be set per view for the input.
156
161
  # For just about all purposes, UTF-8 should be what you want to input, and it's what Tk
157
162
  # can handle best.
158
- def insert_string(string)
163
+ def insert_string(string, record = self)
159
164
  return if string.empty?
160
165
 
161
166
  if !string.frozen? && string.encoding == Encoding::ASCII_8BIT
@@ -167,7 +172,20 @@ module VER
167
172
  end
168
173
 
169
174
  # puts "Insert %p in mode %p" % [string, keymap.mode]
170
- insert :insert, string
175
+ record.insert(:insert, string)
176
+ end
177
+
178
+ def start_replace_mode
179
+ self.mode = :replace
180
+ end
181
+
182
+ def replace_string(string)
183
+ return if string.empty?
184
+
185
+ undo_record do |record|
186
+ record.delete(:insert, 'insert + 1 chars')
187
+ insert_string(string, record)
188
+ end
171
189
  end
172
190
 
173
191
  def auto_indent_line
@@ -212,24 +230,27 @@ module VER
212
230
 
213
231
  empty_line = /^\s*$/
214
232
  indent = 0
215
-
216
- index('1.0').upto(index('end')) do |pos|
217
- pos_lineend = pos.lineend
218
- line = get(pos, pos_lineend).strip
219
-
220
- if increase && decrease && line =~ increase && line =~ decrease
221
- indent -= 1
222
- replace(pos, pos_lineend, (' ' * indent) << line)
223
- indent += 1
224
- elsif decrease && line =~ decrease
225
- indent -= 1
226
- replace(pos, pos_lineend, (' ' * indent) << line)
227
- elsif increase && line =~ increase
228
- replace(pos, pos_lineend, (' ' * indent) << line)
229
- indent += 1
230
- elsif line =~ empty_line
231
- else
232
- replace(pos, pos_lineend, (' ' * indent) << line)
233
+ indent_token = ' ' * options.shiftwidth
234
+
235
+ undo_record do |record|
236
+ index('1.0').upto(index('end')) do |pos|
237
+ pos_lineend = pos.lineend
238
+ line = get(pos, pos_lineend).strip
239
+
240
+ if increase && decrease && line =~ increase && line =~ decrease
241
+ indent -= 1
242
+ record.replace(pos, pos_lineend, (indent_token * indent) << line)
243
+ indent += 1
244
+ elsif decrease && line =~ decrease
245
+ indent -= 1
246
+ record.replace(pos, pos_lineend, (indent_token * indent) << line)
247
+ elsif increase && line =~ increase
248
+ record.replace(pos, pos_lineend, (indent_token * indent) << line)
249
+ indent += 1
250
+ elsif line =~ empty_line
251
+ else
252
+ record.replace(pos, pos_lineend, (indent_token * indent) << line)
253
+ end
233
254
  end
234
255
  end
235
256
  end
@@ -42,34 +42,51 @@ module VER
42
42
 
43
43
  # Move cursor +count+ characters left.
44
44
  def backward_char(count = 1)
45
- mark_set :insert, "insert - #{count} char"
45
+ mark_set :insert, "insert - #{count} displaychars"
46
46
  end
47
47
 
48
48
  # Move cursor +count+ characters right.
49
49
  def forward_char(count = 1)
50
- mark_set :insert, "insert + #{count} char"
50
+ mark_set :insert, "insert + #{count} displaychars"
51
51
  end
52
52
 
53
+ # Move to the beginning of the line where insert mark is located.
54
+ #
55
+ # With +count+ it will move to the beginning of the display line, which
56
+ # takes line wraps into account.
53
57
  def beginning_of_line(count = nil)
54
- mark_set :insert, 'insert display linestart'
58
+ if count
59
+ mark_set :insert, 'insert display linestart'
60
+ else
61
+ mark_set :insert, 'insert linestart'
62
+ end
55
63
  end
56
64
 
65
+ # Move to the end of the line where insert mark is located.
66
+ #
67
+ # With +count+ it moves to the end of the display line, so when there is
68
+ # a line wrap it will move to the place where the line wraps instead of the
69
+ # real end of the line.
57
70
  def end_of_line(count = nil)
58
- mark_set :insert, 'insert display lineend'
71
+ if count
72
+ mark_set :insert, 'insert display lineend'
73
+ else
74
+ mark_set :insert, 'insert lineend'
75
+ end
59
76
  end
60
77
 
61
78
  def eol_then_insert_mode(count = nil)
62
- end_of_line
79
+ end_of_line(count)
63
80
  start_insert_mode
64
81
  end
65
82
 
66
- def sol_then_insert_mode
67
- beginning_of_line
83
+ def sol_then_insert_mode(count = nil)
84
+ beginning_of_line(count)
68
85
  start_insert_mode
69
86
  end
70
87
 
71
- def forward_char_then_insert_mode
72
- forward_char
88
+ def forward_char_then_insert_mode(count = 1)
89
+ forward_char(count)
73
90
  start_insert_mode
74
91
  end
75
92
 
@@ -106,16 +123,103 @@ module VER
106
123
  end
107
124
 
108
125
  def previous_line(count = 1)
109
- mark_set :insert, tk_prev_line_pos(count)
126
+ up_down_line(-count.abs)
127
+ refresh_selection
110
128
  end
111
129
 
112
130
  def next_line(count = 1)
113
- mark_set :insert, tk_next_line_pos(count)
131
+ up_down_line(count.abs)
132
+ refresh_selection
133
+ end
134
+
135
+ # OK, finally found the issue.
136
+ #
137
+ # the implementation of tk::TextUpDownLine is smart, but not smart enough.
138
+ # It doesn't assume large deltas between the start of up/down movement and
139
+ # the last other modification of the insert mark.
140
+ #
141
+ # This means that, after scrolling with up/down for a few hundred lines,
142
+ # it has to calculate the amount of display lines in between, which is a
143
+ # very expensive calculation and time increases O(delta_lines).
144
+ #
145
+ # We'll try to solve this another way, by assuming that there are at least
146
+ # a few other lines of same or greater length in between, we simply
147
+ # compare against a closer position and make delta_lines as small as
148
+ # possible.
149
+ #
150
+ # Now, if you go to, like column 100 of a line, and there is never a line
151
+ # as long for the rest of the file, the scrolling will still slow down a
152
+ # lot. This is an issue we can fix if we "forget" the @udl_pos_orig after
153
+ # a user-defined maximum delta (something around 200 should do), will
154
+ # implement that on demand.
155
+ def up_down_line(count)
156
+ insert = index(:insert)
157
+
158
+ @udl_pos_orig = insert if @udl_pos_prev != insert
159
+
160
+ lines = count(@udl_pos_orig, insert, :displaylines)
161
+ target = index("#@udl_pos_orig + #{lines + count} displaylines")
162
+ @udl_pos_prev = target
163
+ mark_set :insert, target
164
+ @udl_pos_orig = target if target.x == @udl_pos_orig.x
165
+ end
166
+
167
+ def forward_scroll(count = 1)
168
+ count_abs = count.abs
169
+ yview_scroll(count_abs, :units)
170
+ next_line(count_abs)
171
+ end
172
+
173
+ def backward_scroll(count = 1)
174
+ count_abs = count.abs
175
+ yview_scroll(-count_abs, :units)
176
+ previous_line(count_abs)
114
177
  end
115
178
 
116
179
  def forward_word(count = 1)
180
+ forward_jump(count, &method(:word_char_type))
181
+ end
182
+
183
+ def forward_chunk(count = 1)
184
+ forward_jump(count, &method(:chunk_char_type))
185
+ end
186
+
187
+ def word_right_end(count = 1)
188
+ count.times do
189
+ mark_set :insert, tk_next_word_pos_end('insert')
190
+ end
191
+ end
192
+
193
+ def backward_word(count = 1)
194
+ backward_jump(count, &method(:word_char_type))
195
+ end
196
+
197
+ def backward_chunk(count = 1)
198
+ backward_jump(count, &method(:chunk_char_type))
199
+ end
200
+
201
+ private
202
+
203
+ def word_char_type(char)
204
+ case char
205
+ when /\w/; :word
206
+ when /\S/; :special
207
+ when /\s/; :space
208
+ else; raise "You cannot get here"
209
+ end
210
+ end
211
+
212
+ def chunk_char_type(char)
213
+ case char
214
+ when /\S/; :nonspace
215
+ when /\s/; :space
216
+ else; raise "You cannot get here"
217
+ end
218
+ end
219
+
220
+ def forward_jump(count)
117
221
  count.times do
118
- original_type = type = char_type(get(:insert))
222
+ original_type = type = yield(get(:insert))
119
223
  changed = 0
120
224
 
121
225
  begin
@@ -123,7 +227,7 @@ module VER
123
227
  execute :mark, :set, :insert, 'insert + 1 chars'
124
228
  break if original_pos == index(:insert)
125
229
 
126
- type = char_type(get(:insert))
230
+ type = yield(get(:insert))
127
231
  changed += 1 if type != original_type
128
232
  original_type = type
129
233
  end until changed > 0 && type != :space
@@ -134,15 +238,9 @@ module VER
134
238
  VER.error(ex)
135
239
  end
136
240
 
137
- def word_right_end(count = 1)
138
- count.times do
139
- mark_set :insert, tk_next_word_pos_end('insert')
140
- end
141
- end
142
-
143
- def backward_word(count = 1)
241
+ def backward_jump(count = 1)
144
242
  count.times do
145
- original_type = type = char_type(get(:insert))
243
+ original_type = type = yield(get(:insert))
146
244
  changed = 0
147
245
 
148
246
  begin
@@ -150,19 +248,19 @@ module VER
150
248
  execute :mark, :set, :insert, 'insert - 1 chars'
151
249
  break if index(:insert) == original_pos
152
250
 
153
- type = char_type(get(:insert))
251
+ type = yield(get(:insert))
154
252
  changed += 1 if type != original_type
155
253
  original_type = type
156
254
  end until changed > 0 && type != :space
157
255
 
158
- type = char_type(get('insert - 1 chars'))
256
+ type = yield(get('insert - 1 chars'))
159
257
 
160
258
  while type == original_type
161
259
  original_pos = index(:insert)
162
260
  execute :mark, :set, :insert, 'insert - 1 chars'
163
261
  break if index(:insert) == original_pos
164
262
 
165
- type = char_type(get('insert - 1 chars'))
263
+ type = yield(get('insert - 1 chars'))
166
264
  end
167
265
  end
168
266
 
@@ -171,17 +269,6 @@ module VER
171
269
  VER.error(ex)
172
270
  end
173
271
 
174
- private
175
-
176
- def char_type(char)
177
- case char
178
- when /\w/; :word
179
- when /\S/; :special
180
- when /\s/; :space
181
- else; raise "You cannot get here"
182
- end
183
- end
184
-
185
272
  def tk_prev_word_pos(start)
186
273
  Tk.execute('tk::TextPrevPos', tk_pathname, start, 'tcl_startOfPreviousWord').to_s
187
274
  end