ver 2009.10.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 (188) hide show
  1. data/CHANGELOG +1404 -0
  2. data/MANIFEST +187 -0
  3. data/Rakefile +30 -0
  4. data/TODO +169 -0
  5. data/bin/ver +19 -0
  6. data/config/detect.rb +109 -0
  7. data/config/keymap/vim.rb +239 -0
  8. data/config/rc.rb +0 -0
  9. data/config/scratch +3 -0
  10. data/config/syntax/ANTLR.json +275 -0
  11. data/config/syntax/ASP VB.net.json +228 -0
  12. data/config/syntax/ASP.json +135 -0
  13. data/config/syntax/ActionScript.json +168 -0
  14. data/config/syntax/Ada.json +108 -0
  15. data/config/syntax/Apache.json +342 -0
  16. data/config/syntax/AppleScript.json +721 -0
  17. data/config/syntax/Bibtex.json +253 -0
  18. data/config/syntax/Blog (HTML).json +47 -0
  19. data/config/syntax/Blog (Markdown).json +50 -0
  20. data/config/syntax/Blog (Text).json +45 -0
  21. data/config/syntax/Blog (Textile).json +45 -0
  22. data/config/syntax/Bulletin Board.json +428 -0
  23. data/config/syntax/C++.json +323 -0
  24. data/config/syntax/C.json +694 -0
  25. data/config/syntax/CSS.json +346 -0
  26. data/config/syntax/DOT.json +79 -0
  27. data/config/syntax/Diff.json +136 -0
  28. data/config/syntax/Dylan.json +104 -0
  29. data/config/syntax/Eiffel.json +128 -0
  30. data/config/syntax/Erlang.json +1636 -0
  31. data/config/syntax/F-Script.json +137 -0
  32. data/config/syntax/FXScript.json +260 -0
  33. data/config/syntax/Gri.json +141 -0
  34. data/config/syntax/HTML (Mason).json +207 -0
  35. data/config/syntax/HTML (Rails).json +52 -0
  36. data/config/syntax/HTML (Tcl).json +42 -0
  37. data/config/syntax/HTML for ASP.net.json +736 -0
  38. data/config/syntax/HTML-ASP.json +45 -0
  39. data/config/syntax/HTML.json +614 -0
  40. data/config/syntax/Haskell.json +407 -0
  41. data/config/syntax/Inform.json +80 -0
  42. data/config/syntax/Ini.json +91 -0
  43. data/config/syntax/Io.json +142 -0
  44. data/config/syntax/Java.json +745 -0
  45. data/config/syntax/JavaProperties.json +42 -0
  46. data/config/syntax/JavaScript.json +446 -0
  47. data/config/syntax/LaTeX Beamer.json +65 -0
  48. data/config/syntax/LaTeX Log.json +88 -0
  49. data/config/syntax/LaTeX.json +962 -0
  50. data/config/syntax/Lighttpd.json +93 -0
  51. data/config/syntax/Lisp.json +101 -0
  52. data/config/syntax/Literate Haskell.json +55 -0
  53. data/config/syntax/Logtalk.json +289 -0
  54. data/config/syntax/Lua.json +146 -0
  55. data/config/syntax/M.json +744 -0
  56. data/config/syntax/MEL.json +161 -0
  57. data/config/syntax/MIPS.json +114 -0
  58. data/config/syntax/Mail.json +224 -0
  59. data/config/syntax/Makefile.json +66 -0
  60. data/config/syntax/Markdown.json +644 -0
  61. data/config/syntax/Modula-3.json +80 -0
  62. data/config/syntax/Movable Type.json +348 -0
  63. data/config/syntax/OCaml.json +1391 -0
  64. data/config/syntax/Objective-C++.json +21 -0
  65. data/config/syntax/OpenGL.json +24 -0
  66. data/config/syntax/PHP.json +2184 -0
  67. data/config/syntax/Pascal.json +128 -0
  68. data/config/syntax/Perl.json +2091 -0
  69. data/config/syntax/Plain text.json +49 -0
  70. data/config/syntax/Processing.json +188 -0
  71. data/config/syntax/Quake3 Config.json +54 -0
  72. data/config/syntax/R.json +157 -0
  73. data/config/syntax/Rez.json +137 -0
  74. data/config/syntax/Ruby on Rails.json +170 -0
  75. data/config/syntax/Ruby.json +1753 -0
  76. data/config/syntax/SQL (Rails).json +31 -0
  77. data/config/syntax/SQL.json +435 -0
  78. data/config/syntax/SWIG.json +96 -0
  79. data/config/syntax/Scheme.json +359 -0
  80. data/config/syntax/Shell-Unix-Generic.json +1198 -0
  81. data/config/syntax/Slate.json +265 -0
  82. data/config/syntax/Smarty.json +110 -0
  83. data/config/syntax/Standard ML.json +322 -0
  84. data/config/syntax/Subversion commit message.json +62 -0
  85. data/config/syntax/Tcl.json +278 -0
  86. data/config/syntax/TeX Math.json +83 -0
  87. data/config/syntax/TeX.json +157 -0
  88. data/config/syntax/Textile.json +273 -0
  89. data/config/syntax/Twiki.json +436 -0
  90. data/config/syntax/Vectorscript.json +97 -0
  91. data/config/syntax/XML strict.json +148 -0
  92. data/config/syntax/XML.json +301 -0
  93. data/config/syntax/XSL.json +96 -0
  94. data/config/syntax/YAML.json +293 -0
  95. data/config/syntax/iCalendar.json +51 -0
  96. data/config/syntax/reStructuredText.json +403 -0
  97. data/config/theme/Active4D.json +260 -0
  98. data/config/theme/All Hallow's Eve.json +171 -0
  99. data/config/theme/Amy.json +359 -0
  100. data/config/theme/BBEdit.json +269 -0
  101. data/config/theme/Bespin.json +322 -0
  102. data/config/theme/Blackboard.json +215 -0
  103. data/config/theme/BoysAndGirls01.json +156 -0
  104. data/config/theme/Brilliance Black.json +1695 -0
  105. data/config/theme/Brilliance Dull.json +1451 -0
  106. data/config/theme/Classic Modified.json +288 -0
  107. data/config/theme/Cobalt.json +345 -0
  108. data/config/theme/Cool Glow.json +215 -0
  109. data/config/theme/Dawn.json +258 -0
  110. data/config/theme/Eiffel.json +270 -0
  111. data/config/theme/Espresso Libre.json +247 -0
  112. data/config/theme/Fluidvision.json +272 -0
  113. data/config/theme/IDLE.json +159 -0
  114. data/config/theme/LAZY.json +178 -0
  115. data/config/theme/Mac Classic.json +277 -0
  116. data/config/theme/MagicWB (Amiga).json +231 -0
  117. data/config/theme/Merbivore Soft.json +181 -0
  118. data/config/theme/Merbivore.json +181 -0
  119. data/config/theme/Monokai.json +177 -0
  120. data/config/theme/Notepad2.json +166 -0
  121. data/config/theme/Pastels on Dark.json +437 -0
  122. data/config/theme/RubyBlue.json +226 -0
  123. data/config/theme/Sin City 2.json +361 -0
  124. data/config/theme/Slate.json +270 -0
  125. data/config/theme/Slush & Poppies.json +232 -0
  126. data/config/theme/SpaceCadet.json +143 -0
  127. data/config/theme/Sunburst.json +415 -0
  128. data/config/theme/Twilight BG FG.json +633 -0
  129. data/config/theme/Twilight.json +321 -0
  130. data/config/theme/Whys Poignant.json +119 -0
  131. data/config/theme/Zenburnesque.json +237 -0
  132. data/config/theme/barf.json +155 -0
  133. data/config/theme/fake.json +418 -0
  134. data/config/theme/happydeluxe.json +114 -0
  135. data/config/theme/iLife 05.json +393 -0
  136. data/config/theme/iPlastic.json +177 -0
  137. data/config/theme/mintBlue Dark.json +414 -0
  138. data/config/theme/mintBlue.json +415 -0
  139. data/config/theme/monoindustrial.json +276 -0
  140. data/config/theme/starlight.json +67 -0
  141. data/config/tutorial +74 -0
  142. data/config/welcome +115 -0
  143. data/help/index.verh +14 -0
  144. data/lib/ver.rb +156 -0
  145. data/lib/ver/entry.rb +97 -0
  146. data/lib/ver/keymap.rb +96 -0
  147. data/lib/ver/layout.rb +107 -0
  148. data/lib/ver/methods.rb +19 -0
  149. data/lib/ver/methods/completion.rb +116 -0
  150. data/lib/ver/methods/control.rb +340 -0
  151. data/lib/ver/methods/insert.rb +6 -0
  152. data/lib/ver/methods/move.rb +65 -0
  153. data/lib/ver/methods/search.rb +33 -0
  154. data/lib/ver/methods/select.rb +145 -0
  155. data/lib/ver/methods/views.rb +21 -0
  156. data/lib/ver/mode.rb +160 -0
  157. data/lib/ver/options.rb +207 -0
  158. data/lib/ver/plist.rb +106 -0
  159. data/lib/ver/status.rb +67 -0
  160. data/lib/ver/syntax.rb +68 -0
  161. data/lib/ver/syntax/detector.rb +53 -0
  162. data/lib/ver/syntax/processor.rb +48 -0
  163. data/lib/ver/text.rb +374 -0
  164. data/lib/ver/textpow.rb +357 -0
  165. data/lib/ver/theme.rb +162 -0
  166. data/lib/ver/vendor/fuzzy_file_finder.rb +340 -0
  167. data/lib/ver/view.rb +163 -0
  168. data/lib/ver/view/entry.rb +28 -0
  169. data/lib/ver/view/list.rb +137 -0
  170. data/lib/ver/view/list/buffer.rb +27 -0
  171. data/lib/ver/view/list/fuzzy_file_finder.rb +44 -0
  172. data/lib/ver/view/list/syntax.rb +13 -0
  173. data/lib/ver/view/list/theme.rb +13 -0
  174. data/spec/keymap.rb +224 -0
  175. data/tasks/bacon.rake +49 -0
  176. data/tasks/changelog.rake +18 -0
  177. data/tasks/gem.rake +22 -0
  178. data/tasks/gem_installer.rake +76 -0
  179. data/tasks/grancher.rake +12 -0
  180. data/tasks/install_dependencies.rake +6 -0
  181. data/tasks/manifest.rake +4 -0
  182. data/tasks/plist2json.rake +35 -0
  183. data/tasks/rcov.rake +18 -0
  184. data/tasks/release.rake +12 -0
  185. data/tasks/reversion.rake +8 -0
  186. data/tasks/syntax_list.rake +31 -0
  187. data/ver.gemspec +29 -0
  188. metadata +241 -0
@@ -0,0 +1,28 @@
1
+ module VER
2
+ class View
3
+ # This sucks, should we go method_missing?
4
+ class Entry < VER::Entry
5
+ attr_accessor :list_view
6
+
7
+ def pick_first
8
+ list_view.pick_first
9
+ end
10
+
11
+ def pick_selection
12
+ list_view.pick_selection
13
+ end
14
+
15
+ def cancel
16
+ list_view.cancel
17
+ end
18
+
19
+ def go_line_up
20
+ list_view.go_line_up
21
+ end
22
+
23
+ def go_line_down
24
+ list_view.go_line_down
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,137 @@
1
+ module VER
2
+ class View::List < Struct.new(:parent, :frame, :list, :entry, :tag, :callback)
3
+ autoload :Buffer, 'ver/view/list/buffer'
4
+ autoload :Window, 'ver/view/list/buffer'
5
+ autoload :FuzzyFileFinder, 'ver/view/list/fuzzy_file_finder'
6
+ autoload :Syntax, 'ver/view/list/syntax'
7
+ autoload :Theme, 'ver/view/list/theme'
8
+
9
+ def initialize(parent, &block)
10
+ self.parent, self.callback = parent, block
11
+
12
+ setup_widgets
13
+ setup_keymap
14
+ setup_events
15
+ on_update
16
+ end
17
+
18
+ def setup_widgets
19
+ self.frame = TkFrame.new{
20
+ pack fill: :both, expand: true
21
+ }
22
+
23
+ self.list = Tk::Listbox.new(frame){
24
+ setgrid 'yes'
25
+ width 0
26
+ background '#000'
27
+ foreground '#fff'
28
+ selectforeground '#000'
29
+ selectbackground '#fff'
30
+ font VER.options[:font]
31
+ pack fill: :both, expand: true
32
+ }
33
+
34
+ self.entry = View::Entry.new(frame){
35
+ pack fill: :x, expand: false
36
+ font VER.options[:font]
37
+ focus
38
+ }
39
+ entry.list_view = self
40
+ end
41
+
42
+ def setup_keymap
43
+ keymap_name = VER.options.fetch(:keymap)
44
+
45
+ @list_keymap = Keymap.get(
46
+ name: keymap_name, receiver: self, widget: list, mode: :list_view_list)
47
+
48
+ @entry_keymap = Keymap.get(
49
+ name: keymap_name, receiver: entry, widget: entry, mode: :list_view_entry)
50
+ end
51
+
52
+ # Setup this event, because Keymap gets very confused when you bind 'Key' and
53
+ # we don't want to break the event-chain anyway
54
+ def setup_events
55
+ entry.bind('<Modified>'){ on_update }
56
+ end
57
+
58
+ def on_update
59
+ update
60
+ list.selection_set(0)
61
+ list.activate(0)
62
+ end
63
+
64
+ def quit
65
+ Tk.exit
66
+ end
67
+
68
+ def go_line_up
69
+ index = list.curselection.first - 1
70
+
71
+ if index >= 0
72
+ list.selection_clear(0, 'end')
73
+ select_index(index)
74
+ end
75
+ end
76
+
77
+ def go_line_down
78
+ index = list.curselection.first + 1
79
+ max = list.size
80
+
81
+ if index < max
82
+ list.selection_clear(0, 'end')
83
+ select_index(index)
84
+ end
85
+ end
86
+
87
+ def select_index(index)
88
+ list.selection_set(index)
89
+ list.activate(index)
90
+ list.see(index)
91
+ end
92
+
93
+ def cancel
94
+ destroy
95
+ end
96
+
97
+ def destroy
98
+ entry.destroy
99
+ list.destroy
100
+ frame.destroy
101
+ parent.focus
102
+ end
103
+
104
+ def sublist(list, input = entry.value)
105
+ if input == input.downcase
106
+ list.select{|item| item.to_s.downcase.include?(input) }
107
+ else
108
+ list.select{|item| item.to_s.include?(input) }
109
+ end
110
+ end
111
+
112
+ def pick(item)
113
+ if list.size > 0
114
+ pick_action(item)
115
+ destroy
116
+ else
117
+ message "VER is confused, what did you actually want to do?"
118
+ end
119
+ end
120
+
121
+ def pick_first
122
+ pick list.get(0)
123
+ end
124
+
125
+ def pick_selection
126
+ pick TkSelection.get
127
+ end
128
+
129
+ def pick_action(item)
130
+ callback.call(item) if callback
131
+ end
132
+
133
+ def message(string)
134
+ parent.status.message(string)
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,27 @@
1
+ module VER
2
+ class View::List::Window < View::List
3
+ def update
4
+ list.value = sublist(windows.keys)
5
+ end
6
+
7
+ def windows
8
+ path_view = parent.layout.views.map{|view| [view.filename.to_s, view] }
9
+ Hash[path_view]
10
+ end
11
+
12
+ def pick_action(filename)
13
+ view = windows[filename]
14
+ callback.call(view) if callback && view
15
+ end
16
+ end
17
+
18
+ class View::List::Buffer < View::List
19
+ def update
20
+ list.value = sublist(buffers)
21
+ end
22
+
23
+ def buffers
24
+ VER.paths.map(&:to_s).select {|b| ! b.empty? }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,44 @@
1
+ require 'ver/vendor/fuzzy_file_finder'
2
+
3
+ module VER
4
+ class View::List::FuzzyFileFinder < View::List
5
+ FFF = ::FuzzyFileFinder
6
+
7
+ def fffinder
8
+ @fffinder ||= FFF.new(Dir.pwd)
9
+ rescue FFF::TooManyEntries
10
+ message "The FuzzyFileFinder is overwhelmed by the amount of files"
11
+ destroy
12
+ end
13
+
14
+ def update
15
+ choices = fffinder.find(entry.value.chomp('/'))
16
+ choices = choices.sort_by{|m| [-m[:score], m[:path]] }
17
+
18
+ list.clear
19
+
20
+ pwd = Dir.pwd + '/'
21
+ choices.each do |choice|
22
+ choice[:path].sub!(pwd, '')
23
+ insert_choice(choice)
24
+ end
25
+ end
26
+
27
+ def insert_choice(choice)
28
+ path, score = choice.values_at(:path, :score)
29
+ list.insert(:end, path)
30
+
31
+ foreground, background =
32
+ case score
33
+ when 0 ; ['white', 'black']
34
+ when 0 ..0.25 ; ['red', 'black']
35
+ when 0.25..0.75 ; ['yellow', 'black']
36
+ when 0.75..1 ; ['green', 'black']
37
+ end
38
+
39
+ list.itemconfigure(:end,
40
+ foreground: foreground, background: background,
41
+ selectforeground: background, selectbackground: foreground)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ module VER
2
+ class View::List::Syntax < View::List
3
+ def update
4
+ list.value = sublist(syntaxes)
5
+ end
6
+
7
+ def syntaxes
8
+ VER::Syntax.list.map do |fullpath|
9
+ File.basename(fullpath, File.extname(fullpath))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module VER
2
+ class View::List::Theme < View::List
3
+ def update
4
+ list.value = sublist(themes)
5
+ end
6
+
7
+ def themes
8
+ VER::Theme.list.map do |fullpath|
9
+ File.basename(fullpath, File.extname(fullpath))
10
+ end
11
+ end
12
+ end
13
+ end
data/spec/keymap.rb ADDED
@@ -0,0 +1,224 @@
1
+ require 'bacon'
2
+ Bacon.summary_on_exit
3
+
4
+ module VER
5
+ class Keyboard
6
+ end
7
+
8
+ class Keymap
9
+ attr_accessor :modes, :current_mode
10
+
11
+ def initialize
12
+ @modes = {}
13
+ end
14
+
15
+ def send(*args)
16
+ @callback.__send__(*args)
17
+ end
18
+
19
+ def enter_key(key)
20
+ modes[current_mode].enter_key key
21
+ end
22
+
23
+ # TODO: callbacks
24
+ def current_mode=(cm)
25
+ @current_mode = cm.to_sym
26
+ end
27
+
28
+ def add_mode(name)
29
+ @modes[name.to_sym] = mode = Mode.new(self)
30
+ yield mode if block_given?
31
+ mode
32
+ end
33
+ end
34
+
35
+ class Mode
36
+ def initialize(callback = nil)
37
+ @callback = callback
38
+ @stack = []
39
+ @map = {}
40
+ @ancestors = []
41
+ end
42
+
43
+ def inherit(other)
44
+ @ancestors.delete other
45
+ @ancestors.unshift other
46
+ end
47
+
48
+ def ancestors(&block)
49
+ ([self] + @ancestors).find(&block)
50
+ end
51
+
52
+ def map(sym, *keychain)
53
+ bind(*keychain.flatten){|*arg| @callback.send(sym, *arg) }
54
+ end
55
+
56
+ def bind(*keychain, &block)
57
+ keychain = keychain.dup
58
+ total = hash = {}
59
+
60
+ while key = keychain.shift
61
+ if keychain.empty?
62
+ hash[key] = block
63
+ else
64
+ hash = hash[key] = {}
65
+ end
66
+ end
67
+
68
+ @map.replace @map.merge(total, &MERGER)
69
+ end
70
+
71
+ def enter_keys(*keys)
72
+ keys.flatten.each{|key| enter_key(key) }
73
+ end
74
+
75
+ def enter_key(key)
76
+ @stack << key
77
+ ancestors{|ancestor| ancestor.attempt_execute(@stack) }
78
+ end
79
+
80
+ def attempt_execute(original_stack)
81
+ stack, arg = Mode.split_stack(original_stack)
82
+ return false if stack.empty?
83
+
84
+ executable = stack.inject(@map){|keys, key| keys.fetch(key) }
85
+
86
+ if execute(executable, *arg)
87
+ original_stack.clear
88
+ return true
89
+ end
90
+
91
+ false
92
+ rescue KeyError
93
+ false
94
+ end
95
+
96
+ def execute(executable, *arg)
97
+ case executable
98
+ when Proc
99
+ executable.call(*arg)
100
+ true
101
+ when Symbol
102
+ @callback.send(executable, *arg)
103
+ else
104
+ false
105
+ end
106
+ end
107
+
108
+ def self.split_stack(stack)
109
+ return stack, nil if stack[0] == '0'
110
+
111
+ pivot = stack.index{|c| c !~ /\d/ }
112
+
113
+ if pivot == 0
114
+ return stack, nil
115
+ elsif pivot
116
+ return stack[pivot..-1], stack[0..pivot].join.to_i
117
+ else
118
+ return [], nil
119
+ end
120
+ end
121
+
122
+ MERGER = proc{|key,v1,v2|
123
+ Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
124
+ }
125
+ end
126
+ end
127
+
128
+ describe Mode = VER::Mode do
129
+ it 'splits stack into argument and keychain' do
130
+ Mode.split_stack(['4', 'a']).should == [['a'], 4]
131
+ Mode.split_stack(['4', '2', 'a']).should == [['a'], 42]
132
+ Mode.split_stack(['4', '2', 'a', 'b']).should == [['a', 'b'], 42]
133
+ Mode.split_stack(['a', 'b']).should == [['a', 'b'], nil]
134
+ end
135
+
136
+ it 'executes action assigned to key' do
137
+ done = false
138
+ mode = Mode.new
139
+ mode.bind('a'){ done = true }
140
+ mode.enter_keys 'a'
141
+ done.should == true
142
+ end
143
+
144
+ it 'executes action assigned to key chain (immediately)' do
145
+ done = false
146
+ mode = Mode.new
147
+ mode.bind('a', 'b', 'c'){ done = true }
148
+ mode.enter_keys 'a', 'b', 'c'
149
+ done.should == true
150
+ end
151
+
152
+ it 'executes action assigned to key chain (delayed)' do
153
+ done = false
154
+ mode = Mode.new
155
+ mode.bind('a', 'b', 'c'){ done = true }
156
+ mode.enter_key 'a'
157
+ mode.enter_key 'b'
158
+ mode.enter_key 'c'
159
+ done.should == true
160
+ end
161
+
162
+ it 'executes action with numeric argument' do
163
+ done = false
164
+ mode = Mode.new
165
+ mode.bind('a', 'b', 'c'){|arg| done = arg }
166
+ mode.enter_keys '4', '2', 'a', 'b', 'c'
167
+ done.should == 42
168
+ end
169
+
170
+ it 'executes action on callback' do
171
+ done = false
172
+ callback = Class.new{
173
+ attr_accessor :done
174
+
175
+ def foo
176
+ @done = true
177
+ end
178
+ }.new
179
+
180
+ mode = Mode.new(callback)
181
+ mode.map :foo, %w[a b c]
182
+ mode.enter_keys %w[a b c]
183
+ callback.done.should == true
184
+ end
185
+
186
+ it 'inherits another mode but executes actions itself' do
187
+ first_done = second_done = false
188
+ first, second = Mode.new, Mode.new
189
+ second.inherit(first)
190
+ second.bind('a', 'b', 'c'){|arg| second_done = arg }
191
+ second.enter_keys %w[4 2 a b c]
192
+ second_done.should == 42
193
+ end
194
+
195
+ it 'inherits another mode that also executes actions' do
196
+ first_done = second_done = false
197
+ first, second = Mode.new, Mode.new
198
+ second.inherit(first)
199
+
200
+ first.bind('first-key'){|arg| first_done = arg }
201
+ second.bind('second-key'){|arg| second_done = arg }
202
+
203
+ second.enter_keys %w[4 2 first-key]
204
+ first_done.should == 42
205
+
206
+ second.enter_keys %w[2 4 second-key]
207
+ second_done.should == 24
208
+ end
209
+ end
210
+
211
+ describe Keymap = VER::Keymap do
212
+ it 'adds a mode into the keymap' do
213
+ done = false
214
+
215
+ keymap = Keymap.new
216
+ keymap.add_mode :test do |mode|
217
+ mode.bind('a'){ done = true }
218
+ end
219
+ keymap.current_mode = :test
220
+
221
+ keymap.enter_key 'a'
222
+ done.should == true
223
+ end
224
+ end