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
@@ -65,7 +65,7 @@ module VER
65
65
 
66
66
  def update
67
67
  self.from, self.to, self.choices = completer.call
68
- # @longest_choice = choices.map{|choice| choice.size }.max
68
+ @longest_choice = choices.map{|choice| choice.size }.max
69
69
 
70
70
  if choices && choices.size > 0
71
71
  list.value = choices
@@ -91,10 +91,9 @@ module VER
91
91
  x, y, caret_height =
92
92
  parent.tk_caret.values_at(:x, :y, :height)
93
93
 
94
- height, width= parent.winfo_height, parent.winfo_width
94
+ height, width = parent.winfo_height, parent.winfo_width
95
95
  height -= parent.status.winfo_height
96
96
 
97
-
98
97
  # use side with most space, east or west
99
98
  if x > (width / 2)
100
99
  side = 'e'
@@ -115,10 +114,18 @@ module VER
115
114
  height -= (height - y)
116
115
  end
117
116
 
118
- list.configure height: -1 # fit as many into the list as possible
119
-
120
- # let's place it so we can see how big it is.
121
- list.place x: x, y: y, height: height, in: parent, anchor: "#{hemisphere}#{side}"
117
+ list.configure width: @longest_choice + 2, height: -1
118
+ height = [height, list.winfo_reqheight].min
119
+ width = [width, list.winfo_reqwidth].min
120
+
121
+ list.place(
122
+ x: x,
123
+ y: y,
124
+ height: height,
125
+ width: width,
126
+ in: parent,
127
+ anchor: "#{hemisphere}#{side}"
128
+ )
122
129
  end
123
130
 
124
131
  def cancel
@@ -20,7 +20,8 @@ module VER
20
20
  end
21
21
  end
22
22
 
23
- attr_accessor :modes, :callback, :widget, :tag, :previous_mode
23
+ attr_accessor :modes, :callback, :widget, :tag, :previous_mode, :last_send,
24
+ :ignore_sends, :accumulate_sends, :history, :arguments
24
25
  attr_reader :mode
25
26
 
26
27
  def initialize(options)
@@ -28,6 +29,11 @@ module VER
28
29
  self.widget = options.fetch(:widget, callback)
29
30
  self.previous_mode = nil
30
31
  self.modes = {}
32
+ self.history = SizedArray.new(50)
33
+ self.last_send = nil
34
+ self.ignore_sends ||= []
35
+ self.accumulate_sends ||= []
36
+ self.arguments = true
31
37
 
32
38
  prepare_tag
33
39
  prepare_default_binds
@@ -39,13 +45,32 @@ module VER
39
45
 
40
46
  def send(*args)
41
47
  callback.send(*args)
48
+ ensure
49
+ name = args.first
50
+
51
+ if accumulate_sends.include?(name)
52
+ if last_send && last_send.first == name
53
+ arg = (last_send[1..-1] + args[1..-1]).join
54
+ self.last_send = [name, arg]
55
+ else
56
+ self.last_send = args
57
+ end
58
+ else
59
+ if ignore_sends.include?(name)
60
+ # self.last_send = nil
61
+ else
62
+ self.last_send = args
63
+ end
64
+ end
42
65
  end
43
66
 
44
67
  def enter_key(key)
68
+ @history << key
45
69
  modes[mode].enter_key key
46
70
  end
47
71
 
48
72
  def enter_missing(key)
73
+ @history << key
49
74
  modes[mode].enter_missing key
50
75
  end
51
76
 
@@ -112,6 +137,10 @@ module VER
112
137
  mode
113
138
  end
114
139
 
140
+ def in_mode(name, &block)
141
+ add_mode(name){|mode| mode.instance_eval(&block) }
142
+ end
143
+
115
144
  def use_previous_mode
116
145
  return unless mode = previous_mode
117
146
  self.mode = previous_mode
@@ -1,13 +1,17 @@
1
1
  module VER
2
2
  class Layout < Tk::Tile::Frame
3
- attr_reader :strategy, :views, :options
3
+ attr_reader :strategy, :views, :stack, :options
4
4
 
5
5
  def initialize(parent, options = {})
6
6
  super
7
7
 
8
8
  pack(fill: :both, expand: true)
9
9
 
10
- @views = []
10
+ # These two have to be kept in sync
11
+ # @views contains the views in the order they were opened
12
+ # @stack contains the views in the order they are displayed
13
+ @views, @stack = [], []
14
+
11
15
  @options = {}
12
16
  self.strategy = Layout::VerticalTiling
13
17
  end
@@ -20,14 +24,16 @@ module VER
20
24
  def create_view
21
25
  view = View.new(self)
22
26
  yield view
23
- @views.unshift view
27
+ @views.push(view)
28
+ @stack.unshift(view)
24
29
 
25
30
  apply
26
31
  view.focus
27
32
  end
28
33
 
29
34
  def close_view(view)
30
- @views.delete view
35
+ @views.delete(view)
36
+ @stack.delete(view)
31
37
  view.destroy
32
38
 
33
39
  if previous = @views.first
@@ -62,10 +68,10 @@ module VER
62
68
  # | | 3 | > | | 2 | > | | 2 | > | | 2 |
63
69
  # +-----+-----+ > +-----+-----+ > +-----+-----+ > +-----+-----+
64
70
  def push_up(current)
65
- return unless index = @views.index(current)
66
- previous = @views[index - 1]
71
+ return unless index = @stack.index(current)
72
+ previous = @stack[index - 1]
67
73
  current.raise(previous)
68
- @views[index - 1], @views[index] = current, previous
74
+ @stack[index - 1], @stack[index] = current, previous
69
75
 
70
76
  apply
71
77
  end
@@ -77,21 +83,21 @@ module VER
77
83
  # | | 3 | > | | 2 | > | | 2 | > | | 3 |
78
84
  # +-----+-----+ > +-----+-----+ > +-----+-----+ > +-----+-----+
79
85
  def push_down(current)
80
- return unless index = @views.index(current)
81
- following = @views[index + 1] || @views[0]
86
+ return unless index = @stack.index(current)
87
+ following = @stack[index + 1] || @stack[0]
82
88
  current.raise(following)
83
- @views[@views.index(following)], @views[index] = current, following
89
+ @stack[@stack.index(following)], @stack[index] = current, following
84
90
 
85
91
  apply
86
92
  end
87
93
 
88
94
  def push_top(current)
89
- @views.unshift(@views.delete(current))
95
+ @stack.unshift(@stack.delete(current))
90
96
  apply
91
97
  end
92
98
 
93
99
  def push_bottom(view)
94
- @views.push(@views.delete(view))
100
+ @stack.push(@stack.delete(view))
95
101
  apply
96
102
  end
97
103
 
@@ -109,7 +115,7 @@ module VER
109
115
  DEFAULT = { master: 1, stacking: 3 }
110
116
 
111
117
  def prepare(layout, options)
112
- slaves = layout.views
118
+ slaves = layout.stack
113
119
  master, stacking = DEFAULT.merge(options).values_at(:master, :stacking)
114
120
  options.merge! master: master, stacking: stacking
115
121
  head, tail, hidden = slaves[0...master], slaves[master..stacking], slaves[stacking..-1]
@@ -168,4 +174,4 @@ module VER
168
174
  end
169
175
  end
170
176
  end
171
- end
177
+ end
@@ -1,5 +1,6 @@
1
- require 'ver/methods/completion'
1
+ require 'ver/methods/bookmark'
2
2
  require 'ver/methods/clipboard'
3
+ require 'ver/methods/completion'
3
4
  require 'ver/methods/control'
4
5
  require 'ver/methods/ctags'
5
6
  require 'ver/methods/delete'
@@ -11,25 +12,15 @@ require 'ver/methods/preview'
11
12
  require 'ver/methods/save'
12
13
  require 'ver/methods/search'
13
14
  require 'ver/methods/select'
15
+ require 'ver/methods/undo'
14
16
  require 'ver/methods/views'
15
17
  require 'ver/methods/shortcuts'
16
18
 
17
19
  module VER
18
20
  module Methods
19
- include Completion
20
- include Control
21
- include Ctags
22
- include Delete
23
- include Help
24
- include Insert
25
- include Move
26
- include Open
27
- include Preview
28
- include Save
29
- include Search
30
- include Select
21
+ include(Completion, Control, Ctags, Delete, Help, Insert, Move, Open,
22
+ Preview, Save, Search, Select, Clipboard, Bookmarks::Methods,
23
+ Views, Undo)
31
24
  include Shortcuts
32
- include Clipboard
33
- include Views
34
25
  end
35
26
  end
@@ -0,0 +1,189 @@
1
+ module VER
2
+ class Bookmarks
3
+ include Enumerable
4
+
5
+ def initialize
6
+ @bm = []
7
+ @idx = nil
8
+ end
9
+
10
+ def each(&block)
11
+ @bm.each(&block)
12
+ end
13
+
14
+ def add_named(name, value)
15
+ if value.is_a?(Bookmark)
16
+ bm = value.dup
17
+ else
18
+ bm = Bookmark.new(name, *value.to_a)
19
+ end
20
+
21
+ @bm << bm
22
+ @bm.uniq!
23
+ @bm.sort!
24
+ bm
25
+ end
26
+ alias []= add_named
27
+
28
+ def add_unnamed(value)
29
+ if value.is_a?(Bookmark)
30
+ bm = value
31
+ else
32
+ bm = Bookmark.new(nil, *value.to_a)
33
+ end
34
+
35
+ @bm << bm
36
+ @bm.uniq!
37
+ @bm.sort!
38
+ bm
39
+ end
40
+
41
+ def <<(value)
42
+ add_unnamed(value)
43
+ self
44
+ end
45
+
46
+ def [](name)
47
+ @bm.find{|bm| bm.name == name }
48
+ end
49
+
50
+ def key?(name)
51
+ @bm.any?{|bm| bm.name == name }
52
+ end
53
+
54
+ def delete(name)
55
+ if found = @bm.find{|bm| bm.name == name }
56
+ @bm.delete(found)
57
+ end
58
+ end
59
+
60
+ def next_from(pos)
61
+ needle = Bookmark.new(nil, *pos)
62
+ @bm.find{|bm| bm > needle }
63
+ end
64
+
65
+ def prev_from(pos)
66
+ needle = Bookmark.new(nil, *pos)
67
+ @bm.reverse.find{|bm| needle > bm }
68
+ end
69
+
70
+ def at(pos)
71
+ needle = Bookmark.new(nil, *pos)
72
+ @bm.find{|bm| needle == bm }
73
+ end
74
+
75
+ class Bookmark < Struct.new(:name, :file, :index)
76
+ include Comparable
77
+
78
+ def <=>(other)
79
+ [file, index] <=> [other.file, other.index]
80
+ end
81
+
82
+ def to_a
83
+ [name, file, index.y, index.x]
84
+ end
85
+ end
86
+
87
+ module Methods
88
+ def char_bookmark_visit(name = nil)
89
+ if name
90
+ named_bookmark_visit(name)
91
+ else
92
+ status_ask 'Bookmark name: ', take: 1 do |bm_name|
93
+ named_bookmark_visit(bm_name)
94
+ end
95
+ end
96
+ end
97
+
98
+ def char_bookmark_add(name = nil)
99
+ if name
100
+ named_bookmark_add(name)
101
+ else
102
+ status_ask 'Bookmark name: ', take: 1 do |bm_name|
103
+ named_bookmark_add(bm_name)
104
+ end
105
+ end
106
+ end
107
+
108
+ def named_bookmark_add(name = nil)
109
+ if name
110
+ bm = bookmarks.add_named(name, bookmark_value)
111
+ message("Added bookmark [%s|%s:%d,%d]." % bm.to_a)
112
+ else
113
+ status_ask 'Bookmark name: ' do |bm_name|
114
+ named_bookmark_add(bm_name)
115
+ end
116
+ end
117
+ end
118
+
119
+ def named_bookmark_remove(name = nil)
120
+ if name
121
+ if bm = bookmarks.delete(name)
122
+ message("Removed bookmark [%s|%s:%d,%d]." % bm.to_a)
123
+ else
124
+ message("No Bookmark named %p." % [name])
125
+ end
126
+ else
127
+ status_ask 'Bookmark name: ' do |bm_name|
128
+ named_bookmark_remove(bm_name)
129
+ end
130
+ end
131
+ end
132
+
133
+ def named_bookmark_visit(name = nil)
134
+ if name
135
+ if bm = bookmarks[name]
136
+ bookmark_open(bm)
137
+ else
138
+ message("No Bookmark named %p." % [name])
139
+ end
140
+ else
141
+ status_ask 'Bookmark name: ' do |bm_name|
142
+ named_bookmark_visit(bm_name)
143
+ end
144
+ end
145
+ end
146
+
147
+ def bookmark_toggle
148
+ pos = bookmark_value
149
+
150
+ if bm = bookmarks.at(pos)
151
+ bookmarks.delete(bm)
152
+ message("Removed bookmark [%s|%s:%d,%d]." % bm.to_a)
153
+ else
154
+ bm = bookmarks.add_unnamed(bookmark_value)
155
+ message("Added bookmark [%s|%s:%d,%d]." % bm.to_a)
156
+ end
157
+ rescue => ex
158
+ VER.error(ex)
159
+ end
160
+
161
+ def next_bookmark
162
+ bookmark_open(bookmarks.next_from(bookmark_value))
163
+ end
164
+
165
+ def prev_bookmark
166
+ bookmark_open(bookmarks.prev_from(bookmark_value))
167
+ end
168
+
169
+ private
170
+
171
+ def bookmarks
172
+ VER.bookmarks
173
+ end
174
+
175
+ def bookmark_value
176
+ [filename, index(:insert)]
177
+ end
178
+
179
+ def bookmark_open(bookmark, use_x = true)
180
+ return unless bookmark.respond_to?(:file) && bookmark.respond_to?(:index)
181
+
182
+ view.find_or_create(bookmark.file) do |view|
183
+ y, x = use_x ? bookmark.index.split : [bookmark.index.y, 0]
184
+ view.text.mark_set(:insert, "#{y}.#{x}")
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -13,7 +13,7 @@ module VER
13
13
  complete_word
14
14
  end
15
15
  else
16
- indent = ' ' * VER.options.shiftwidth
16
+ indent = ' ' * options.shiftwidth
17
17
  insert :insert, indent
18
18
  end
19
19
  end
@@ -183,7 +183,7 @@ module VER
183
183
  end
184
184
 
185
185
  def complete(options = {}, &block)
186
- edit_separator
186
+ undo_separator
187
187
  HoverCompletion.new(self, options, &block)
188
188
  end
189
189
  end
@@ -1,6 +1,84 @@
1
1
  module VER
2
2
  module Methods
3
3
  module Control
4
+ def repeat_command(count = 1)
5
+ return unless command = keymap.last_send
6
+ return if command.first == __method__
7
+ count.times{ send(*command) }
8
+ keymap.last_send = command
9
+ end
10
+
11
+ # Assigns env variables used in the given command.
12
+ # - $f: The current buffer's filename
13
+ # - $d: The current buffer's directory
14
+ # - $F: A space-separated list of all buffer filenames
15
+ # - $i: A string acquired from the user with a prompt
16
+ # - $c: The current clipboard text
17
+ # - $s: The currently selected text
18
+ #
19
+ # @param [String] command
20
+ # The string containing the command executed
21
+ def prepare_exec(command)
22
+ prepare_exec_f if command =~ /\$f/
23
+ prepare_exec_d if command =~ /\$d/
24
+ prepare_exec_F if command =~ /\$F/
25
+ prepare_exec_i if command =~ /\$i/
26
+ prepare_exec_c if command =~ /\$c/
27
+ prepare_exec_s if command =~ /\$s/
28
+ end
29
+
30
+ def prepare_exec_f
31
+ p f: (ENV['f'] = filename.to_s)
32
+ end
33
+
34
+ def prepare_exec_d
35
+ p d: (ENV['d'] = filename.directory.to_s)
36
+ end
37
+
38
+ def prepare_exec_F
39
+ p F: (ENV['F'] = layout.views.map{|v| v.text.filename }.join(' '))
40
+ end
41
+
42
+ def prepare_exec_i
43
+ raise NotImplementedError
44
+ end
45
+
46
+ def prepare_exec_c
47
+ p c: (ENV['c'] = clipboard_get)
48
+ end
49
+
50
+ def prepare_exec_s
51
+ content = []
52
+
53
+ each_selected_line do |y, fx, tx|
54
+ content << get("#{y}.#{fx}", "#{y}.#{tx}")
55
+ end
56
+
57
+ ENV['s'] = content.join("\n")
58
+ end
59
+
60
+ def exec_into_new(command = nil)
61
+ if command
62
+ target = options.home_conf_dir/'shell-result.txt'
63
+ prepare_exec(command)
64
+ p command
65
+ system(command)
66
+ target.open('w+'){|io| io.write(`#{command}`) }
67
+ view.find_or_create(target)
68
+ else
69
+ status_ask 'Command: ' do |command|
70
+ exec_into_new(command)
71
+ end
72
+ end
73
+ end
74
+
75
+ def exec_into_void
76
+ status_ask 'Command: ' do |command|
77
+ system(command)
78
+ message("Exit code: #{$?}")
79
+ end
80
+ end
81
+
4
82
  def tags_at(index = :insert)
5
83
  index = index(index)
6
84
  tags = tag_names(index)
@@ -47,6 +125,20 @@ module VER
47
125
  end
48
126
  end
49
127
 
128
+ def grep_buffer
129
+ View::List::Grep.new self, filename do |file, line|
130
+ view.find_or_create(file, line)
131
+ end
132
+ end
133
+
134
+ def grep_buffers
135
+ glob = '{' << layout.views.map{|v| v.text.filename }.join(',') << '}'
136
+
137
+ View::List::Grep.new self, glob do |file, line|
138
+ view.find_or_create(file, line)
139
+ end
140
+ end
141
+
50
142
  def open_method_list
51
143
  View::List::Methods.new self do |file, line|
52
144
  view.find_or_create(file, line)
@@ -224,28 +316,33 @@ module VER
224
316
  end
225
317
  end
226
318
 
227
- def indent_line
228
- insert('insert linestart', ' ')
319
+ def indent_line(count = 1)
320
+ indent = (' ' * options[:shiftwidth] * count)
321
+ insert('insert linestart', indent)
229
322
  end
230
323
 
231
- def unindent_line
232
- line = get('insert linestart', 'insert lineend')
324
+ def unindent_line(count = 1)
325
+ indent = ' ' * options[:shiftwidth]
326
+ replace_from = 'insert linestart'
327
+ replace_to = "insert linestart + #{indent.size} chars"
233
328
 
234
- return unless line =~ /^(\s\s?)/
329
+ undo_record do |record|
330
+ count.times do
331
+ line = get('insert linestart', 'insert lineend')
235
332
 
236
- replace(
237
- 'insert linestart',
238
- "insert linestart + #{$1.size} chars",
239
- ''
240
- )
333
+ return unless line.start_with?(indent)
334
+
335
+ record.replace(replace_from, replace_to, '')
336
+ end
337
+ end
241
338
  end
242
339
 
243
- def clean_line(index)
340
+ def clean_line(index, record = self)
244
341
  index = index(index)
245
342
  from, to = index.linestart, index.lineend
246
343
  line = get(from, to)
247
344
  bare = line.rstrip
248
- replace(from, to, bare) if bare.empty?
345
+ record.replace(from, to, bare) if bare.empty?
249
346
  end
250
347
 
251
348
  def start_insert_mode
@@ -257,20 +354,6 @@ module VER
257
354
  self.mode = :control
258
355
  end
259
356
 
260
- def undo
261
- edit_undo
262
- touch!
263
- rescue RuntimeError => ex
264
- status.value = ex.message
265
- end
266
-
267
- def redo
268
- edit_redo
269
- touch!
270
- rescue RuntimeError => ex
271
- status.value = ex.message
272
- end
273
-
274
357
  private
275
358
 
276
359
  def wrap_lines_of(text, wrap = 80)