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
@@ -37,7 +37,8 @@ module VER
37
37
  self.filename = path
38
38
 
39
39
  begin
40
- self.value = read_file(filename)
40
+ clear
41
+ insert 1.0, read_file(filename)
41
42
  message "Opened #{short_filename}"
42
43
  rescue Errno::ENOENT
43
44
  delete '1.0', :end
@@ -81,6 +82,8 @@ module VER
81
82
  mark_set :insert, "#{line.to_i}.0"
82
83
  @pristine = false
83
84
 
85
+ @undoer = VER::Undo::Tree.new(self)
86
+
84
87
  bind('<Map>') do
85
88
  defer do
86
89
  setup_highlight
@@ -152,9 +155,6 @@ module VER
152
155
  options[option] = value
153
156
  yield(value) if block_given?
154
157
  end
155
-
156
- require 'pp'
157
- pp options
158
158
  end
159
159
 
160
160
  def set_filetype(type)
@@ -167,42 +167,3 @@ module VER
167
167
  end
168
168
  end
169
169
  end
170
-
171
- =begin
172
- There are two forms of modelines. The first form:
173
- [text]{white}{vi:|vim:|ex:}[white]{options}
174
-
175
- [text] any text or empty
176
- {white} at least one blank character (<Space> or <Tab>)
177
- {vi:|vim:|ex:} the string "vi:", "vim:" or "ex:"
178
- [white] optional white space
179
- {options} a list of option settings, separated with white space or ':',
180
- where each part between ':' is the argument for a ":set"
181
- command (can be empty)
182
-
183
- Example:
184
- vi:noai:sw=3 ts=6 ~
185
-
186
- The second form (this is compatible with some versions of Vi):
187
-
188
- [text]{white}{vi:|vim:|ex:}[white]se[t] {options}:[text]
189
-
190
- [text] any text or empty
191
- {white} at least one blank character (<Space> or <Tab>)
192
- {vi:|vim:|ex:} the string "vi:", "vim:" or "ex:"
193
- [white] optional white space
194
- se[t] the string "set " or "se " (note the space)
195
- {options} a list of options, separated with white space, which is the
196
- argument for a ":set" command
197
- : a colon
198
- [text] any text or empty
199
-
200
- Example:
201
- /* vim: set ai tw=75: */ ~
202
-
203
- The white space before {vi:|vim:|ex:} is required. This minimizes the chance
204
- that a normal word like "lex:" is caught. There is one exception: "vi:" and
205
- "vim:" can also be at the start of the line (for compatibility with version
206
- 3.0). Using "ex:" at the start of the line will be ignored (this could be
207
- short for "example:").
208
- =end
@@ -7,40 +7,65 @@ module VER
7
7
  }
8
8
 
9
9
  def status_search_next
10
- status_search_common('/') do
11
- search_next
12
- end
10
+ status_search_common('/'){ search_next }
13
11
  end
14
12
 
15
13
  def status_search_prev
16
- status_search_common('?') do
17
- search_prev
18
- end
14
+ status_search_common('?'){ search_prev }
19
15
  end
20
16
 
21
17
  def status_search_common(question)
22
- status_ask question do |term|
23
- begin
24
- needle = Regexp.new(term)
25
- rescue RegexpError
26
- needle = Regexp.escape(term)
27
- end
18
+ status.bind('<<Modified>>') do
19
+ search_incremental(status.value)
20
+ end
28
21
 
29
- tag_all_matching(:search, needle, SEARCH_HIGHLIGHT)
22
+ status_ask question do |term|
23
+ status.bind('<<Modified>>'){ }
24
+ search_incremental(term, force = true)
25
+ search_prev
30
26
  yield
31
27
  end
32
28
  end
33
29
 
34
- def search_next
35
- from, to = tag_nextrange('search', 'insert + 1 chars', 'end')
30
+ def search_incremental(term, force = false)
31
+ return if !term || term.empty?
32
+ return if !force && term.size <= options.search_incremental_min
33
+
34
+ begin
35
+ needle = Regexp.new(term)
36
+ rescue RegexpError, SyntaxError
37
+ needle = Regexp.escape(term)
38
+ end
39
+
40
+ tag_all_matching(:search, needle, SEARCH_HIGHLIGHT)
41
+ from, to = tag_nextrange('search', '1.0', 'end')
42
+ see(from) if from
43
+ end
44
+
45
+ def search_first
46
+ from, to = tag_nextrange('search', '1.0', 'end')
36
47
  mark_set(:insert, from) if from
37
48
  end
38
49
 
39
- def search_prev
40
- from, to = tag_prevrange('search', 'insert - 1 chars', '1.0')
50
+ def search_last
51
+ from, to = tag_prevrange('search', 'end', '1.0')
41
52
  mark_set(:insert, from) if from
42
53
  end
43
54
 
55
+ def search_next(count = 1)
56
+ count.times do
57
+ from, to = tag_nextrange('search', 'insert + 1 chars', 'end')
58
+ mark_set(:insert, from) if from
59
+ end
60
+ end
61
+
62
+ def search_prev(count = 1)
63
+ count.times do
64
+ from, to = tag_prevrange('search', 'insert - 1 chars', '1.0')
65
+ mark_set(:insert, from) if from
66
+ end
67
+ end
68
+
44
69
  def search_next_word_under_cursor
45
70
  word = get('insert wordstart', 'insert wordend')
46
71
  return if word.squeeze == ' ' # we don't want to match space
@@ -78,6 +103,10 @@ module VER
78
103
  end
79
104
  end
80
105
  end
106
+
107
+ def search_clear
108
+ tag_remove('search', '1.0', 'end')
109
+ end
81
110
  end
82
111
  end
83
112
  end
@@ -48,6 +48,7 @@ module VER
48
48
  end
49
49
  end
50
50
 
51
+ # Delete selection without copying it.
51
52
  def delete_selection
52
53
  queue = tag_ranges(:sel).flatten
53
54
  delete(*queue)
@@ -56,22 +57,32 @@ module VER
56
57
  finish_selection
57
58
  end
58
59
 
60
+ # Copy selection and delete it.
61
+ def kill_selection
62
+ queue = tag_ranges(:sel).flatten
63
+ kill(*queue)
64
+ mark_set(:insert, queue.first)
65
+
66
+ finish_selection
67
+ end
68
+
59
69
  def indent_selection
60
- indent_size = VER.options.shiftwidth
70
+ indent_size = options.shiftwidth
61
71
  indent = ' ' * indent_size
62
72
 
63
- each_selected_line do |y, fx, tx|
64
- tx = fx + indent_size
65
- next if get("#{y}.#{fx}", "#{y}.#{tx}").empty?
66
- insert("#{y}.#{fx}", indent)
73
+ undo_record do |record|
74
+ each_selected_line do |y, fx, tx|
75
+ tx = fx + indent_size
76
+ next if get("#{y}.#{fx}", "#{y}.#{tx}").empty?
77
+ record.insert("#{y}.#{fx}", indent)
78
+ end
67
79
  end
68
80
 
69
- edit_separator
70
81
  refresh_selection
71
82
  end
72
83
 
73
84
  def unindent_selection
74
- indent_size = VER.options.shiftwidth
85
+ indent_size = options.shiftwidth
75
86
  indent = ' ' * indent_size
76
87
  queue = []
77
88
 
@@ -83,7 +94,6 @@ module VER
83
94
  end
84
95
 
85
96
  delete(*queue)
86
- edit_separator
87
97
  refresh_selection
88
98
  end
89
99
 
@@ -113,20 +123,46 @@ module VER
113
123
  end
114
124
 
115
125
  def comment_selection
126
+ comment = "#{options.comment_line} "
127
+ indent = nil
128
+ lines = []
129
+
116
130
  each_selected_line do |y, fx, tx|
117
- insert("#{y}.0 linestart", '# ')
131
+ lines << y
132
+
133
+ next if indent == 0 # can't get lower
134
+
135
+ line = get("#{y}.#{fx}", "#{y}.#{tx}")
136
+
137
+ next unless start = line =~ /\S/
138
+
139
+ indent ||= start
140
+ indent = start if start < indent
141
+ end
142
+
143
+ lines.each do |y|
144
+ insert("#{y}.#{indent}", comment)
118
145
  end
119
146
 
120
- edit_separator
147
+ undo_separator
121
148
  refresh_selection
122
149
  end
123
150
 
124
151
  def uncomment_selection
125
- each_selected_line do |y, fx, tx|
126
- delete("#{y}.0 linestart", "#{y}.0 linestart + 2 chars")
152
+ comment = "#{options.comment_line} "
153
+ regex = /#{Regexp.escape(comment)}/
154
+
155
+ undo_record do |record|
156
+ each_selected_line do |y, fx, tx|
157
+ from, to = "#{y}.#{fx}", "#{y}.#{tx}"
158
+ line = get(from, to)
159
+
160
+ if line.sub!(regex, '')
161
+ record.replace(from, to, line)
162
+ end
163
+ end
127
164
  end
128
165
 
129
- edit_separator
130
166
  refresh_selection
131
167
  end
132
168
 
@@ -152,30 +188,52 @@ module VER
152
188
  end
153
189
  end
154
190
 
191
+ def replace_selection_with_clipboard
192
+ string = clipboard_get
193
+ ranges = tag_ranges(:sel)
194
+ from, to = ranges.first.first, ranges.last.last
195
+ replace(from, to, string)
196
+ finish_selection
197
+ mark_set :insert, from
198
+ end
199
+
200
+ def refresh_selection
201
+ return unless start = selection_start
202
+
203
+ tag_remove :sel, 1.0, :end
204
+
205
+ case selection_mode
206
+ when :select_char; refresh_selection_char(start)
207
+ when :select_line; refresh_selection_line(start)
208
+ when :select_block; refresh_selection_block(start)
209
+ end
210
+ end
211
+
155
212
  private
156
213
 
157
214
  # TODO: find better name for +full+
158
215
  def replace_selection_with(string, full)
159
216
  origin = index(:insert)
160
217
 
161
- if full
162
- each_selected_line do |y, fx, tx|
163
- diff = tx - fx
164
- replace("#{y}.#{fx}", "#{y}.#{tx}", string * diff)
165
- end
166
- else
167
- string_size = string.size
168
- each_selected_line do |y, fx, tx|
169
- replace("#{y}.#{fx}", "#{y}.#{tx}", string)
218
+ undo_record do |record|
219
+ if full
220
+ each_selected_line do |y, fx, tx|
221
+ diff = tx - fx
222
+ record.replace("#{y}.#{fx}", "#{y}.#{tx}", string * diff)
223
+ end
224
+ else
225
+ string_size = string.size
226
+ each_selected_line do |y, fx, tx|
227
+ record.replace("#{y}.#{fx}", "#{y}.#{tx}", string)
228
+ end
170
229
  end
171
230
  end
172
231
 
173
- edit_separator
174
232
  mark_set :insert, origin
175
233
  end
176
234
 
177
235
  def finish_selection(mode = nil)
178
- edit_separator
236
+ undo_separator
179
237
  clear_selection
180
238
  mode ? self.mode = mode : keymap.use_previous_mode
181
239
  apply_mode_style(keymap.mode)
@@ -220,6 +278,45 @@ module VER
220
278
  insert(queue.first, output)
221
279
  end
222
280
  end
281
+
282
+ # FIXME: yes, i know i'm calling `tag add` for every line, which makes
283
+ # things slower, but it seems like there is a bug in the text widget.
284
+ # So we aggregate the information into a single eval.
285
+ def refresh_selection_block(start)
286
+ ly, lx, ry, rx =
287
+ if compare('insert', '>', start)
288
+ [*index('insert').split, *start.split]
289
+ else
290
+ [*start.split, *index('insert').split]
291
+ end
292
+
293
+ from_y, to_y = [ly, ry].sort
294
+ from_x, to_x = [lx, rx].sort
295
+
296
+ code = [%(set win "#{tk_pathname}")]
297
+
298
+ from_y.upto to_y do |y|
299
+ code << "$win tag add sel #{y}.#{from_x} #{y}.#{to_x + 1}"
300
+ end
301
+
302
+ Tk.execute_only(Tk::TclString.new(code.join("\n")))
303
+ end
304
+
305
+ def refresh_selection_char(start)
306
+ if compare('insert', '>', start)
307
+ tag_add :sel, start, "insert + 1 chars"
308
+ else
309
+ tag_add :sel, "insert", "#{start} + 1 chars"
310
+ end
311
+ end
312
+
313
+ def refresh_selection_line(start)
314
+ if compare('insert', '>', start)
315
+ tag_add :sel, "#{start} linestart", 'insert lineend'
316
+ else
317
+ tag_add :sel, 'insert linestart', "#{start} lineend"
318
+ end
319
+ end
223
320
  end
224
321
  end
225
322
  end
@@ -0,0 +1,23 @@
1
+ module VER
2
+ module Methods
3
+ module Undo
4
+ def redo
5
+ undoer.redo
6
+ end
7
+
8
+ def undo
9
+ undoer.undo
10
+ end
11
+
12
+ private
13
+
14
+ def undo_record(&block)
15
+ undoer.record_multi(&block)
16
+ end
17
+
18
+ def undo_separator
19
+ undoer.separate!
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,6 +1,11 @@
1
1
  module VER
2
2
  module Methods
3
3
  module Views
4
+ def view_focus(index = 0)
5
+ return unless found = layout.views[index - 1]
6
+ found.push_top
7
+ end
8
+
4
9
  def view_find_or_create(file)
5
10
  view.find_or_create(file)
6
11
  end
@@ -20,7 +20,7 @@ module VER
20
20
  @map = {}
21
21
  @ancestors = []
22
22
  @missing = nil
23
- @arguments = true
23
+ @arguments = callback.arguments
24
24
  end
25
25
 
26
26
  def inherits(*others)
@@ -58,7 +58,7 @@ module VER
58
58
  bind(keychain.flatten, sym)
59
59
  end
60
60
  end
61
- alias to map
61
+ alias key map
62
62
 
63
63
  def bind(keychain, action_name = nil, &block)
64
64
  keychain = keychain.dup
@@ -137,23 +137,24 @@ module VER
137
137
 
138
138
  case executable
139
139
  when nil
140
+ return false unless previous.respond_to?(:find)
141
+
140
142
  # FIXME: this allows only one mode
141
- if found = previous.find{|prev_key, prev_value| prev_key.is_a?(Mode) }
142
- mode, action = found
143
- looked = mode.attempt_execute([key, *stack], true)
144
-
145
- case looked
146
- when false
147
- return false
148
- when nil
149
- return nil
150
- else
151
- cmd, cmd_arg = looked
152
- return nil if cmd.is_a?(Hash)
153
- return execute(action, cmd, arg)
154
- end
155
- else
143
+ found = previous.find{|prev_key, prev_value| prev_key.is_a?(Mode) }
144
+ return false unless found
145
+
146
+ mode, action = found
147
+ looked = mode.attempt_execute([key, *stack], true)
148
+
149
+ case looked
150
+ when false
156
151
  return false
152
+ when nil
153
+ return nil
154
+ else
155
+ cmd, cmd_arg = looked
156
+ return nil if cmd.is_a?(Hash)
157
+ return execute(action, cmd, arg)
157
158
  end
158
159
  end
159
160
  end