xiki 0.5.0a

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 (252) hide show
  1. data/Gemfile +11 -0
  2. data/LICENSE +22 -0
  3. data/README.markdown +83 -0
  4. data/Rakefile +8 -0
  5. data/bin/xiki +46 -0
  6. data/etc/command/xiki_command.rb +203 -0
  7. data/etc/command/xiki_process.rb +52 -0
  8. data/etc/command/xiki_wrapper +2 -0
  9. data/etc/js/menu1.js +55 -0
  10. data/etc/js/xiki.js +259 -0
  11. data/etc/logo.png +0 -0
  12. data/etc/presentations/bootstrap.deck +5 -0
  13. data/etc/presentations/databases.deck +41 -0
  14. data/etc/presentations/diffs.deck +23 -0
  15. data/etc/presentations/documentation.deck +14 -0
  16. data/etc/presentations/effects.deck +5 -0
  17. data/etc/presentations/face.deck +297 -0
  18. data/etc/presentations/files.deck +79 -0
  19. data/etc/presentations/icons.deck +22 -0
  20. data/etc/presentations/images.deck +24 -0
  21. data/etc/presentations/key_shortcuts.deck +16 -0
  22. data/etc/presentations/macros.deck +18 -0
  23. data/etc/presentations/menu_classes.deck +44 -0
  24. data/etc/presentations/menu_directories.deck +30 -0
  25. data/etc/presentations/notes.deck +19 -0
  26. data/etc/presentations/other_languages.deck +55 -0
  27. data/etc/presentations/other_wiki_syntaxes.deck +4 -0
  28. data/etc/presentations/piano.deck +5 -0
  29. data/etc/presentations/potential/diffs.deck +38 -0
  30. data/etc/presentations/potential/evolution.deck +18 -0
  31. data/etc/presentations/potential/intro.deck +711 -0
  32. data/etc/presentations/potential/intro1.deck +711 -0
  33. data/etc/presentations/potential/intro2.deck +97 -0
  34. data/etc/presentations/potential/make_mysql_menu.deck +36 -0
  35. data/etc/presentations/potential/ruby_development.deck +17 -0
  36. data/etc/presentations/potential/ui_prototyping.deck +50 -0
  37. data/etc/presentations/potential/web_dev.deck +4 -0
  38. data/etc/presentations/potential/web_development.deck +10 -0
  39. data/etc/presentations/potential/wiki.deck +45 -0
  40. data/etc/presentations/presentations.deck +24 -0
  41. data/etc/presentations/rails_development.deck +29 -0
  42. data/etc/presentations/search_key_shortcuts.deck +37 -0
  43. data/etc/presentations/simplest_possible_ui.deck +37 -0
  44. data/etc/presentations/svg.deck +5 -0
  45. data/etc/presentations/testing.deck +28 -0
  46. data/etc/presentations/the_end.deck +13 -0
  47. data/etc/presentations/type_something_and_double_click.deck +57 -0
  48. data/etc/presentations/type_the_acronym.deck +144 -0
  49. data/etc/presentations/web_browser.deck +56 -0
  50. data/etc/presentations/xiki_command.deck +20 -0
  51. data/etc/presentations/xiki_url.deck +14 -0
  52. data/etc/shark.icns +0 -0
  53. data/etc/shark_script.icns +0 -0
  54. data/etc/snippets/html.notes +7 -0
  55. data/etc/snippets/notes.notes +20 -0
  56. data/etc/snippets/rb.notes +38 -0
  57. data/etc/templates/menu_template.menu +2 -0
  58. data/etc/templates/menu_template.rb +8 -0
  59. data/etc/templates/template.rb +5 -0
  60. data/etc/themes/Dark_Metal.notes +28 -0
  61. data/etc/themes/Orange_Path.notes +15 -0
  62. data/etc/themes/Shiny_Blue.notes +27 -0
  63. data/etc/themes/Shiny_Green.notes +27 -0
  64. data/etc/wrappers/wrapper.js +17 -0
  65. data/etc/wrappers/wrapper.py +20 -0
  66. data/etc/wrappers/wrapper.rb +25 -0
  67. data/lib/block.rb +72 -0
  68. data/lib/bookmarks.rb +352 -0
  69. data/lib/buffers.rb +170 -0
  70. data/lib/clipboard.rb +333 -0
  71. data/lib/code.rb +860 -0
  72. data/lib/code_tree.rb +476 -0
  73. data/lib/color.rb +274 -0
  74. data/lib/console.rb +557 -0
  75. data/lib/control_lock.rb +9 -0
  76. data/lib/control_tab.rb +176 -0
  77. data/lib/core_ext.rb +31 -0
  78. data/lib/cursor.rb +111 -0
  79. data/lib/deletes.rb +65 -0
  80. data/lib/diff_log.rb +297 -0
  81. data/lib/effects.rb +145 -0
  82. data/lib/environment.rb +5 -0
  83. data/lib/file_tree.rb +1875 -0
  84. data/lib/files.rb +334 -0
  85. data/lib/hide.rb +259 -0
  86. data/lib/history.rb +286 -0
  87. data/lib/image.rb +51 -0
  88. data/lib/incrementer.rb +15 -0
  89. data/lib/insert.rb +7 -0
  90. data/lib/irc.rb +22 -0
  91. data/lib/key_bindings.rb +658 -0
  92. data/lib/keys.rb +754 -0
  93. data/lib/launcher.rb +1351 -0
  94. data/lib/line.rb +429 -0
  95. data/lib/links.rb +6 -0
  96. data/lib/location.rb +175 -0
  97. data/lib/macros.rb +48 -0
  98. data/lib/man.rb +19 -0
  99. data/lib/menu.rb +708 -0
  100. data/lib/merb.rb +259 -0
  101. data/lib/message.rb +5 -0
  102. data/lib/meths.rb +56 -0
  103. data/lib/mode.rb +34 -0
  104. data/lib/move.rb +248 -0
  105. data/lib/notes.rb +1000 -0
  106. data/lib/numbers.rb +45 -0
  107. data/lib/ol.rb +203 -0
  108. data/lib/ol_helper.rb +44 -0
  109. data/lib/overlay.rb +167 -0
  110. data/lib/pause_means_space.rb +68 -0
  111. data/lib/php.rb +22 -0
  112. data/lib/projects.rb +21 -0
  113. data/lib/relinquish_exception.rb +2 -0
  114. data/lib/remote.rb +206 -0
  115. data/lib/requirer.rb +46 -0
  116. data/lib/rest_tree.rb +108 -0
  117. data/lib/ruby.rb +57 -0
  118. data/lib/ruby_console.rb +165 -0
  119. data/lib/search.rb +1572 -0
  120. data/lib/search_term.rb +40 -0
  121. data/lib/snippet.rb +68 -0
  122. data/lib/specs.rb +229 -0
  123. data/lib/styles.rb +274 -0
  124. data/lib/svn.rb +682 -0
  125. data/lib/text_util.rb +110 -0
  126. data/lib/tree.rb +1871 -0
  127. data/lib/tree_cursor.rb +87 -0
  128. data/lib/trouble_shooting.rb +27 -0
  129. data/lib/url_tree.rb +11 -0
  130. data/lib/view.rb +1474 -0
  131. data/lib/window.rb +133 -0
  132. data/lib/xiki.rb +404 -0
  133. data/menus/accounts.rb +5 -0
  134. data/menus/address_book.rb +21 -0
  135. data/menus/agenda.rb +28 -0
  136. data/menus/all.rb +5 -0
  137. data/menus/amazon.rb +16 -0
  138. data/menus/app.rb +16 -0
  139. data/menus/applescript.rb +46 -0
  140. data/menus/as.rb +15 -0
  141. data/menus/bookmarklet.rb +63 -0
  142. data/menus/bootstrap.rb +568 -0
  143. data/menus/browse.rb +13 -0
  144. data/menus/browser.rb +78 -0
  145. data/menus/cassandra_db.rb +36 -0
  146. data/menus/chmod.rb +27 -0
  147. data/menus/classes.rb +5 -0
  148. data/menus/coffee_script.rb +35 -0
  149. data/menus/computer.rb +24 -0
  150. data/menus/contacts.rb +5 -0
  151. data/menus/cookies.rb +25 -0
  152. data/menus/couch.rb +184 -0
  153. data/menus/crop.rb +45 -0
  154. data/menus/css.rb +55 -0
  155. data/menus/current.rb +5 -0
  156. data/menus/db.rb +12 -0
  157. data/menus/deck.rb +219 -0
  158. data/menus/dictionary.rb +9 -0
  159. data/menus/dir.rb +8 -0
  160. data/menus/disk.rb +5 -0
  161. data/menus/do.rb +13 -0
  162. data/menus/docs.rb +58 -0
  163. data/menus/dotsies.rb +107 -0
  164. data/menus/edited.rb +18 -0
  165. data/menus/emacs.rb +17 -0
  166. data/menus/enter.rb +13 -0
  167. data/menus/eval.rb +17 -0
  168. data/menus/executable.rb +16 -0
  169. data/menus/filter.rb +46 -0
  170. data/menus/firefox.rb +607 -0
  171. data/menus/foo.rb +30 -0
  172. data/menus/french.rb +7 -0
  173. data/menus/git.rb +185 -0
  174. data/menus/gito.rb +785 -0
  175. data/menus/google.rb +35 -0
  176. data/menus/google_images.rb +11 -0
  177. data/menus/google_patents.rb +10 -0
  178. data/menus/gutenberg.rb +13 -0
  179. data/menus/head.rb +10 -0
  180. data/menus/headings.rb +39 -0
  181. data/menus/html.rb +61 -0
  182. data/menus/icon.rb +40 -0
  183. data/menus/images.menu +2 -0
  184. data/menus/img.rb +15 -0
  185. data/menus/info.rb +9 -0
  186. data/menus/ip.rb +10 -0
  187. data/menus/iterm.rb +36 -0
  188. data/menus/itunes.rb +78 -0
  189. data/menus/javascript.rb +74 -0
  190. data/menus/layout.rb +18 -0
  191. data/menus/local_storage.rb +67 -0
  192. data/menus/ls.rb +19 -0
  193. data/menus/mac.rb +87 -0
  194. data/menus/maps.rb +18 -0
  195. data/menus/matches.rb +18 -0
  196. data/menus/memcache.rb +117 -0
  197. data/menus/mkdir.rb +23 -0
  198. data/menus/mongo.rb +83 -0
  199. data/menus/mysql.rb +294 -0
  200. data/menus/node.rb +88 -0
  201. data/menus/open.rb +19 -0
  202. data/menus/outline.rb +24 -0
  203. data/menus/piano.rb +746 -0
  204. data/menus/postgres.rb +34 -0
  205. data/menus/pre.rb +5 -0
  206. data/menus/python.rb +39 -0
  207. data/menus/rails.rb +160 -0
  208. data/menus/rake.rb +12 -0
  209. data/menus/redmine.rb +168 -0
  210. data/menus/riak_tree.rb +204 -0
  211. data/menus/rss.rb +15 -0
  212. data/menus/safari.rb +11 -0
  213. data/menus/sass.rb +15 -0
  214. data/menus/say.rb +6 -0
  215. data/menus/scale.rb +49 -0
  216. data/menus/serve.rb +78 -0
  217. data/menus/shuffle.rb +24 -0
  218. data/menus/spanish.rb +7 -0
  219. data/menus/standalone.rb +57 -0
  220. data/menus/tail.rb +41 -0
  221. data/menus/technologies.rb +19 -0
  222. data/menus/themes.rb +32 -0
  223. data/menus/thesaurus.rb +13 -0
  224. data/menus/to.rb +24 -0
  225. data/menus/twitter.rb +57 -0
  226. data/menus/wikipedia.rb +34 -0
  227. data/menus/words.rb +11 -0
  228. data/spec/code_tree_spec.rb +59 -0
  229. data/spec/diff_log_spec.rb +40 -0
  230. data/spec/file_tree_spec.rb +102 -0
  231. data/spec/keys_spec.rb +24 -0
  232. data/spec/line_spec.rb +68 -0
  233. data/spec/menu_spec.rb +50 -0
  234. data/spec/ol_spec.rb +98 -0
  235. data/spec/remote_spec.rb +43 -0
  236. data/spec/search_spec.rb +162 -0
  237. data/spec/text_util_spec.rb +119 -0
  238. data/spec/tree_cursor_spec.rb +91 -0
  239. data/spec/tree_spec.rb +955 -0
  240. data/tests/console_test.rb +11 -0
  241. data/tests/couch_db_test.rb +12 -0
  242. data/tests/diff_log_test.rb +43 -0
  243. data/tests/el_mixin.rb +16 -0
  244. data/tests/git_test.rb +95 -0
  245. data/tests/keys_test.rb +19 -0
  246. data/tests/line_test.rb +38 -0
  247. data/tests/merb_test.rb +11 -0
  248. data/tests/redmine_test.rb +50 -0
  249. data/tests/remote_test.rb +31 -0
  250. data/tests/rest_tree_test.rb +70 -0
  251. data/xiki.gemspec +37 -0
  252. metadata +332 -0
@@ -0,0 +1,754 @@
1
+ require 'pause_means_space'
2
+ require 'line'
3
+ require 'text_util'
4
+ require 'launcher'
5
+
6
+ # Methods for defining keyboard shortcuts
7
+ class Keys
8
+
9
+ @@key_queue =[] # For defining menus (must be done in reverse)
10
+
11
+ def self.menu
12
+ %`
13
+ - .history/
14
+ - .api/
15
+ > Map C-c C-a
16
+ | Keys.CA { View.insert "foo" }
17
+
18
+ > Map C-d C-h
19
+ | Keys.do_hi { Line << "hey there" }
20
+
21
+ > Map M-x (in shell mode)
22
+ | Keys._X(:shell_mode_map) { View.insert "foooo" }
23
+
24
+ > Get user input
25
+ | - A String
26
+ | - puts Keys.input(:prompt => "Name: ")
27
+ | - Just one char
28
+ | - puts Keys.input :chars=>1
29
+ - docs/
30
+ - Summary/
31
+ > Xiki Shortcuts
32
+ | Xiki has keyboard shortcuts predefined for doing all kinds of things.
33
+ | Each keyboard shortcut has a mnemonic. Check out the "Keys" menu bar
34
+ | menu for a quick look at them.
35
+
36
+ | And Xiki lets you define your own keyboard shortcuts. This line makes
37
+ | Control-e Control-n insert "Steve".
38
+
39
+ | Keys.EN { View << "Steve" }
40
+
41
+ | For more about defining your own keyboard shortcuts see:
42
+ - @keys/api/
43
+
44
+ > Xiki's "type the acronym" approach
45
+ | Each xiki keyboard shortcut has a mnemonic that helps you
46
+ | simultaneously remember what it does and how to type it.
47
+
48
+ | For example, given this mnemonic:
49
+
50
+ | layout_create
51
+
52
+ | you type:
53
+
54
+ | Control-l Control-c (l for "layout" and c for "create")
55
+
56
+ > Reasons for this appoarch
57
+ - More possible shortcuts/
58
+ | The approach of having single character key shortcuts (e.g. Control-a)
59
+ | works nicely for apps that have a small number of shortcuts. But it
60
+ | becomes less elegant when more shortcuts are used (Ctrl-a, Alt-a,
61
+ | Ctrl-Shift-a).
62
+
63
+ | The "type the acronym" approach with just the Control
64
+ | key allows for very a large number of key shortcuts that are less
65
+ | prone to get confused with one another.
66
+
67
+ - Less to remember/
68
+ | A mnemonic clues you into what the keyboard shortcut does and how to type it,
69
+ | so it's all you need to remember. There's no need to separately remember a keyboard shortcut and what it does (a
70
+ | challenging part of most keyboard shortcut schemes having a large number of
71
+ | shortcuts, which xiki attempts to avoid).
72
+
73
+ | Doesn't sound like standard emacs shortcuts? Here's an explanation about
74
+ | how xiki deals with existing emacs shortcuts.
75
+
76
+ - emacs_shortcuts/
77
+ | TODO add stuff about how C-a turns into C-a C-a, etc.
78
+ | Mention how this lets a large number of key shortcuts without interfering
79
+ | with emacs shortcuts.
80
+ | But an admitted downside is it affects 6 existing emacs shortcuts
81
+ | and makes you type them twice.
82
+ | In practice the annoyance caused by this isn't as bad as it initially may seem
83
+ | Consider using to_axis instead of C-a C-a and to_end instead of C-e C-e.
84
+
85
+ > Six categories
86
+ | As you can see by looking at the "Keys" menu in the menu bar, there are
87
+ | six main categories of key shortcuts.
88
+
89
+ - Descriptions of each category/
90
+ - to: jumping to specific points
91
+ - open: opening things
92
+ - layout: views and windows
93
+ - as: remembering things
94
+ - enter: inserting things
95
+ - do: executing things
96
+
97
+ > Examples
98
+ | Here are some of the most commonly used shortcuts in each category.
99
+ | (Double-click a category to see them.)
100
+
101
+ - to/
102
+ | to+highest: Jump to top of file
103
+ | to+lowest: Jump to bottom of file
104
+ | to+axis: Jump to beginning of line
105
+ | to+end: Jump to end of line
106
+ - open/
107
+ | open+bookmark: view a bookmark
108
+ | open+tree: view a tree of a directory
109
+ | open+current: shows currently open files
110
+ | open+edited: shows recently edited files
111
+ | open+history: shows recently viewed files
112
+ | open+menu: opens view that lets you type a menu (type "-" to see all)
113
+ - layout/
114
+ | layout+create: Create a new view
115
+ | layout+hide: Hide this view
116
+ | layout+next: Go to next view
117
+ | layout+previous: Go to previous view
118
+ | layout+kill: Close the current file
119
+ - as/
120
+ | as+clipboard: Copy (after doing Control-space on the other side)
121
+ | as+kill: Cut (after doing Control-space on the other side)
122
+ | as+bookmark: remember this file as a bookmark
123
+ - enter/
124
+ | enter+clipboard: Paste
125
+ - do/
126
+ | do+tree: view an expanded tree of a directory
127
+ - miscellaneous/
128
+ | Control-tab: cycles through files
129
+
130
+ | For all keyboard shortcuts, see where they're key_bindings.rb, where they're
131
+ | defined:
132
+ - @$xiki/key_bindings.rb
133
+
134
+ > Keyboard shortcuts while searching
135
+ | The seventh category, "search" has special behavior. See:
136
+ - @search/docs/
137
+ `
138
+ end
139
+
140
+ def self.api
141
+ '
142
+ > Summary
143
+ | Ways to use the Keys class, to define keyboard shortcuts etc. Remember
144
+ | that with xiki shortcuts you hold down the control key and "type the
145
+ | acronym" (the first letter in each word) while holding down the control
146
+ | key.
147
+ |
148
+ > Define keyboard shortcuts
149
+ | Defines the key Control-e Control-y
150
+ |
151
+ | Keys.enter_name { View << "Steve" }
152
+ |
153
+ | Defines the key Control-e Control-y (with no mnemonic)
154
+ |
155
+ | Keys.EN { View << "Steve again" }
156
+ |
157
+ > Where to put them
158
+ | You can put keyboard shortcuts into any file that gets required by xiki.
159
+ | For example, you could create a file like this:
160
+ |
161
+ - @~/my_xiki_stuff/
162
+ - keys.rb
163
+ | # My shortcuts
164
+ | Keys.enter_name { View << "Steve" }
165
+ | Keys.enter_yay { View << "Yay" }
166
+ |
167
+ | Then you could require this file when xiki loads by adding this line:
168
+ |
169
+ - @~/.el4r/
170
+ - init.rb
171
+ | require "~/my_xiki_stuff/keys"
172
+ '
173
+ end
174
+
175
+
176
+ # Handles Keys.to_foo etc.
177
+
178
+ def self.method_missing(meth, *args, &block)
179
+
180
+ return if ! $el
181
+
182
+ # Accept it if block but no args
183
+ meth = meth.to_s
184
+
185
+ meth_orig = meth.dup
186
+
187
+
188
+ # Delegate to super unless arg is single symbol
189
+ unless args.size == 0 or (args.size == 1 and args[0].is_a? Symbol)
190
+ return super(meth, *args, &block)
191
+ end
192
+
193
+ meth_title = meth.gsub('_', ' ').gsub(/\b\w/) {|s| s.upcase}
194
+ menu, item = meth_title.match(/(.+?) (.+)/)[1..2] if meth_title =~ /. ./
195
+
196
+ # If 1st word is 'isearch', use it as map
197
+ if meth =~ /^search_/
198
+ @@key_queue << ["Search", item]
199
+ meth.sub! /^search_/, ''
200
+ meth = self.words_to_letters meth
201
+ args = [:isearch_mode_map]
202
+ elsif meth =~ /[A-Z]/ # If capital letters
203
+ # Don't convert
204
+ elsif meth =~ /_/ # Add menu item, if more than one word
205
+
206
+ if args.size == 0 # If global keymap
207
+ # Make lisp function
208
+ $el.defun(meth.to_sym, :interactive=>true) do
209
+ block.call
210
+ end
211
+ @@key_queue << [menu, item]
212
+ end
213
+
214
+ # Change 'to_foo' to 'TF' etc
215
+ meth = self.words_to_letters meth
216
+ end
217
+
218
+ keys_raw = self.translate_keys meth # Translate to 'C-t C-f' etc
219
+
220
+ keys = $el.kbd keys_raw # Translate to actual control keys
221
+
222
+ map = :global_map # Default to global keymap
223
+
224
+ # Use keymap if symbol passed as 1st arg
225
+ map = args.shift if args[0] and args[0].class == Symbol
226
+ # If they just passed a string, use it as code
227
+ if args and (args.size == 1) and !block
228
+ self.map_to_eval keys_raw, args[0]
229
+ return
230
+ end
231
+
232
+ # # Add menus for key shortcuts - seems kind of pointless in retrospect
233
+ # if map == :global_map && meth_orig =~ /^[a-z]/
234
+ # Launcher.add(meth.downcase, &block)
235
+ # Launcher.add(meth_orig, &block) # if meth_orig =~ /^[a-z]/
236
+ # end
237
+
238
+ # Define key
239
+ begin
240
+ $el.define_key map, keys, &block
241
+ "- key was defined: #{keys_raw}"
242
+ rescue Exception => e
243
+ return if map != :global_map || meth !~ /([A-Z])([A-Z]?)./ # Only global map and 2 control keys
244
+ message = e.message
245
+ prefix = message[/"Key sequence .+? starts with non-prefix key (.+?)"/, 1]
246
+ return if prefix.nil?
247
+
248
+ prefix = $el.kbd(prefix)
249
+
250
+ begin # If it appears to be a prefix key (already defined)
251
+ $el.global_unset_key(prefix)
252
+ $el.define_key map, keys, &block
253
+
254
+ rescue Exception => e
255
+ Ol << "e (inner): #{e.inspect}"
256
+ end
257
+
258
+ end
259
+ end
260
+
261
+ def self.map_to_eval keys_raw, code
262
+ $el.el4r_lisp_eval"
263
+ (global-set-key (kbd \"#{keys_raw}\") (lambda () (interactive)
264
+ (el4r-ruby-eval \"#{code}\" )
265
+ ))
266
+ "
267
+ end
268
+
269
+ def self.translate_keys txt
270
+ l = txt.scan(/_?\w/)
271
+ l.collect! { |b|
272
+ case b
273
+ when /^_([A-Z])/
274
+ "M-" + $1.downcase
275
+ when /^([a-z])$/
276
+ $1
277
+ else
278
+ "C-" + b.downcase
279
+ end
280
+ }
281
+ l.join " "
282
+ end
283
+
284
+ def self.set *args, &block
285
+
286
+ # Keys is always first arg
287
+ keys = args.shift
288
+ if args.size > 0 # If 2nd arg, use it
289
+ self.map_to_eval keys, args[0]
290
+ else # Otherwise, use block
291
+ $el.define_key :global_map, $el.kbd(keys), &block
292
+ end
293
+ end
294
+
295
+ #
296
+ # Gets input from user.
297
+ #
298
+ # Sample usages:
299
+ # Keys.input # Terminated by enter
300
+ # Keys.input :chars=>1 # Just one char
301
+ # Keys.input :control=>1 # One char if control
302
+ # Keys.input :timed=>1 # Terminated by pause
303
+ # Keys.input :optional=>1 # Terminated by pause
304
+ # - A pause at the beginning will result in no input (nil)
305
+ #
306
+ def self.input options={}
307
+
308
+ return self.input_with_choices(options) if options[:choices]
309
+
310
+ Cursor.remember :before_input
311
+ Cursor.green
312
+ Cursor.hollow
313
+
314
+ prompt = options[:prompt] || "Input: "
315
+
316
+ # Not completely implemented
317
+ if options[:control]
318
+ prompt = "todo - implement this: "
319
+
320
+ $el.elvar.inhibit_quit = true
321
+ # Maybe use this?
322
+ # Or call self.char?
323
+ # char = $el.char_to_string(Keys.remove_control($el.read_char(prompt))).to_s
324
+ c = read_char(prompt)
325
+ $el.elvar.inhibit_quit = nil
326
+ if c == 7
327
+ Cursor.restore :before_input
328
+ keyboard_quit
329
+ end
330
+
331
+ Cursor.restore :before_input
332
+ return c
333
+ end
334
+
335
+ if options[:chars]
336
+ char = $el.char_to_string(
337
+ self.remove_control($el.read_char(prompt))).to_s
338
+ Cursor.restore :before_input
339
+ return char
340
+ end
341
+
342
+ # If simple un-timed input, just get string and return it
343
+ unless options[:timed] || options[:optional]
344
+ Cursor.restore :before_input
345
+ c = $el.read_string(prompt, options[:initial_input])
346
+ return c
347
+ end
348
+
349
+ keys = ""
350
+
351
+ $el.elvar.inhibit_quit = true
352
+ c = nil
353
+
354
+ # If not optional, wait for input initially
355
+ unless options[:optional]
356
+ c = $el.read_char(prompt)
357
+ keys = self.to_letter(c)
358
+ end
359
+
360
+ if c == 7
361
+ Cursor.restore :before_input
362
+ $el.elvar.inhibit_quit = nil
363
+ $el.keyboard_quit
364
+ end
365
+
366
+ while(c = $el.read_char("#{prompt}#{keys}", nil, 0.35))
367
+ keys += self.to_letter(c)
368
+ if c == 7
369
+ Cursor.restore :before_input
370
+ $el.elvar.inhibit_quit = nil
371
+ $el.keyboard_quit
372
+ end
373
+ end
374
+ $el.elvar.inhibit_quit = nil
375
+ Cursor.restore :before_input
376
+
377
+ $el.message ""
378
+
379
+ # If nothing, return nil
380
+ keys == "" ? nil : keys
381
+ end
382
+
383
+ # TODO: finish - look at spec
384
+ def self.input_with_choices options
385
+ prompt = options[:prompt] ? "#{options[:prompt]} " : ""
386
+ prompt << options[:choices].map{|i|
387
+ "[#{i.first[/./]}]#{i.first[/.(.+)/,1]}"}.
388
+ join(', ')
389
+ c = Keys.input :chars=>1, :prompt=>prompt
390
+ options[:choices].find{|i| i.first =~ /^#{c}/}[1]
391
+ end
392
+
393
+ def self.to_letter ch
394
+ return nil if ch.nil?
395
+ if ch < 27
396
+ ch += 96
397
+
398
+ elsif 67108896 <= ch and ch <= 67108921
399
+ ch -= 67108864
400
+ end
401
+ ch.chr
402
+ end
403
+
404
+ # Converts any control keys in input to normal keys.
405
+ # Example: "\C-x" => "x"
406
+ def self.remove_control ch
407
+ ch += 96 if ch < 27
408
+ ch
409
+ end
410
+
411
+ def self.read_char_maybe
412
+ loc = $el.read_char("Optionally type a char:", nil, 0.35)
413
+ $el.message ""
414
+ return if loc.nil?
415
+
416
+ # Convert control chars to the corresponding letters
417
+ loc += 96 if(1 <= loc and loc <= 26)
418
+ loc = self.remove_control loc
419
+ loc
420
+ end
421
+
422
+ def self.insert_code
423
+ keys = $el.read_key_sequence("Type some keys, to insert the corresponding code: ")
424
+
425
+ # If C-n or C-p, pretend like they were mapped to xiki functions
426
+
427
+ if keys == "\cn"
428
+ return View << 'Line.next'
429
+ elsif keys == "\cp"
430
+ return View << 'Line.previous'
431
+ end
432
+
433
+ proc = self.proc_from_key keys
434
+
435
+ # If lisp, enter lisp?
436
+ if proc.nil?
437
+ keys = $el.key_binding(keys)
438
+ if keys
439
+ return View.insert($el.prin1_to_string(keys))
440
+ else
441
+ $el.beep
442
+ return View.message("Key is unmapped")
443
+ end
444
+ end
445
+
446
+ code = Code.to_ruby(proc)
447
+ code.gsub! 'proc { ', ''
448
+ code.gsub! ' }', ''
449
+
450
+ code.gsub! '(:blink => (true))', ''
451
+
452
+ View << code
453
+ end
454
+
455
+ def self.jump_to_code
456
+ keys = $el.read_key_sequence("Type some keys, to jump to the corresponding code: ")
457
+ proc = self.proc_from_key keys
458
+ if proc.nil?
459
+ $el.beep
460
+ return View.message("Key isn't mapped in Xiki")
461
+ end
462
+
463
+ file, line = Code.location_from_proc proc
464
+ file = "#{XIKI_ROOT}/#{file}" unless file =~ /^\//
465
+ Location.go(file)
466
+ View.to_line line.to_i
467
+ Effects.blink(:what=>:line)
468
+ end
469
+
470
+ def self.proc_from_key keys
471
+ code = $el.prin1_to_string($el.key_binding(keys))
472
+ # If it is a call to elisp
473
+ id = code[/el4r-ruby-call-proc-by-id.+?([_0-9]+)/, 1]
474
+ return nil if id.nil?
475
+
476
+ ObjectSpace._id2ref(id.to_i)
477
+ end
478
+
479
+ def self.timed_insert options={}
480
+ prefix = Keys.prefix
481
+ # If prefix of 0, insert in a way that works with macros
482
+ case prefix
483
+ when nil # Do nothing
484
+ when :u # Do pause for space
485
+ PauseMeansSpace.go
486
+ return
487
+ when 0
488
+ View.insert Keys.input(:prompt => "Insert text to insert: ")
489
+ return
490
+ else # If other prefix, insert single char n times
491
+ c = View.read_char("Insert single char to insert #{prefix} times: ").chr
492
+ prefix.times do
493
+ View.insert c
494
+ end
495
+ return
496
+ end
497
+
498
+ Cursor.remember :before_q
499
+ Cursor.green
500
+
501
+ # Get first char and insert
502
+ c = $el.read_char("insert text (pause to exit): ").chr
503
+ inserted = "#{c}"
504
+
505
+ View.insert c
506
+
507
+ # While no pause, insert more chars
508
+ while(c = $el.read_char("insert text (pause to exit): ", nil, 0.36))
509
+ inserted += c.chr
510
+ View.insert c.chr
511
+ end
512
+
513
+ $el.elvar.qinserted = inserted
514
+ $el.message "input ended"
515
+
516
+ Cursor.restore :before_q
517
+
518
+ # Store in hash by first letter for use by enter_yank
519
+
520
+ Clipboard.save_by_first_letter inserted # Store for retrieval with enter_yank
521
+
522
+ end
523
+
524
+ def self.as name
525
+ Clipboard.copy("#{name}")
526
+ Bookmarks.save("$#{name}")
527
+ Bookmarks.save("$_#{name}")
528
+ View.save("#{name}")
529
+ end
530
+
531
+ def self.insert_from_q
532
+ ($el.elvar.current_prefix_arg || 1).times do
533
+ View.insert($el.elvar.qinserted)
534
+ end
535
+ end
536
+
537
+ def self.prefix_or_0 options={}
538
+ pre = Keys.prefix
539
+ pre.is_a?(Fixnum) ? pre : 0
540
+ end
541
+
542
+ #
543
+ # Set prefix, or append it (space-delimited) if one already there.
544
+ #
545
+ def self.add_prefix new_prefix
546
+ prefix = self.prefix
547
+
548
+ return self.prefix = new_prefix if ! prefix # If none there already, just set it
549
+
550
+ self.prefix = "#{self.prefix} #{new_prefix}"
551
+ end
552
+
553
+ def self.prefix options={}
554
+ pre = $el.elvar.current_prefix_arg
555
+ return nil unless pre
556
+
557
+ # Clear prefix if :clear
558
+ $el.elvar.current_prefix_arg = nil if options[:clear]
559
+ str = pre.to_s
560
+
561
+ return :u if str == "u"
562
+
563
+ if str =~ /^\(/
564
+ return :uu if str == "(16)"
565
+ return :uuu if str == "(64)"
566
+ return :u
567
+ end
568
+
569
+ return :- if "#{pre}" == "-"
570
+ return pre
571
+ end
572
+
573
+ def self.prefix= to
574
+ $el.elvar.current_prefix_arg = to
575
+ end
576
+
577
+ # Whether C-u was held down before this
578
+ def self.prefix_u?
579
+ self.prefix == :u
580
+ end
581
+
582
+ # Whether C-u was held down before this
583
+ # Deprecated
584
+ def self.prefix_u options={}
585
+ result = self.prefix == :u
586
+ self.clear_prefix if options[:clear]
587
+ result
588
+ end
589
+
590
+ def self.up? options={}
591
+ self.prefix_u options
592
+ end
593
+
594
+ def self.update? options={}
595
+ # TODO update so prefix can have multiple values, like C-u and "update"
596
+ # for when C-u as+update
597
+ # (space-separated list)?
598
+ # "u update"
599
+ self.prefix == "update"
600
+ end
601
+
602
+ def self.delete? options={}
603
+ self.prefix == "delete"
604
+ end
605
+
606
+ def self.open? options={}
607
+ self.prefix == "open"
608
+ end
609
+
610
+ def self.prefix_n options={}
611
+ pre = self.prefix(options)
612
+ return pre if pre.is_a?(Fixnum)
613
+ return $&.to_i if pre.is_a?(String) && pre =~ /\d+/
614
+ nil
615
+ end
616
+
617
+ def self.prefix_uu
618
+ self.prefix == :uu
619
+ end
620
+
621
+ def self.clear_prefix
622
+ $el.elvar.current_prefix_arg = nil
623
+ end
624
+
625
+ def self.bookmark_as_path options={}
626
+ bm = options[:bm] || Keys.input(:timed=>true, :prompt=>options[:prompt]||"Enter a bookmark: ")
627
+ if bm == " " # If space, return special token
628
+ return :space
629
+ elsif bm == "/" # If slash, return special token
630
+ return :slash
631
+ elsif bm == "x" # If slash, return special token
632
+ return Xiki.dir
633
+ elsif bm == "," # If slash, return special token
634
+ return :comma
635
+ elsif bm =~ /^\.+$/ # If .+ do tree in current dir
636
+ dir = View.dir :force_slash
637
+ (bm.size - 1).times do
638
+ dir.sub! /\/$/, '' # Remove / on end if there
639
+ dir.sub! /[^\/]+$/, '' # Remove dir
640
+ end
641
+ dir.sub! /^$/, '/' # If nothing left, use root (/)
642
+ return dir
643
+ end
644
+
645
+ dir = Bookmarks.expand bm, :just_bookmark=>true
646
+ if dir.nil? # If no dir, return nil
647
+ View.beep "- Bookmark '#{bm}' doesn't exist."
648
+ return :bookmark_doesnt_exist
649
+ end
650
+
651
+ unless options[:include_file]
652
+ dir = Bookmarks.dir_only dir
653
+ dir << "/" unless dir =~ /\/$/
654
+ end
655
+ dir
656
+
657
+ end
658
+
659
+ def self.prefix_times
660
+ prefix = self.prefix
661
+ case prefix
662
+ when nil, :u, :uu, :uuu
663
+ 1
664
+ else
665
+ prefix
666
+ end
667
+ end
668
+
669
+ def self.add_menu_items
670
+ @@key_queue.reverse.each do |i|
671
+ Menu.add_item [Menu::ROOT_MENU, i[0]], i[1], "#{i[0].downcase}-#{i[1].downcase.gsub(' ', '-')}"
672
+ end
673
+ @@key_queue = []
674
+ end
675
+
676
+ def self.char
677
+
678
+ $el.elvar.inhibit_quit = true
679
+ ch_initial = $el.read_event.to_s
680
+ $el.elvar.inhibit_quit = nil
681
+
682
+ if ch_initial =~ /^\d+$/ # If a number, assign it to raw
683
+ ch_raw = ch_initial.to_i
684
+ if 134217825 <= ch_raw and ch_raw <= 134217850 # If meta (out of elisp range)
685
+ return ["meta_#{(ch_raw - 134217728).chr}".to_sym, nil]
686
+ end
687
+
688
+ # If char is over the elisp max, try to interpret it as Meta
689
+ ch = $el.char_to_string(ch_raw)
690
+ # Special check for C-. and other sequences
691
+ ch = :control_period if ch_raw == 67108910
692
+ ch = :control_slash if ch_raw == 67108911
693
+ return [ch, ch_raw]
694
+
695
+ elsif ['left', 'right', 'up', 'down', ].member?(ch_initial)
696
+ return [ch_initial.to_sym, 0] # Arbitrary indicator for arrow keys
697
+
698
+ elsif ch_initial == "C-return"
699
+ return [:control_return, 13]
700
+
701
+ elsif ch_initial == "return"
702
+ return [:return, 13]
703
+
704
+ elsif ch_initial == "backspace"
705
+ return [:backspace, 127]
706
+
707
+ elsif ch_initial == "tab"
708
+ return ["\t", 9]
709
+
710
+ else # Probably a mouse event
711
+ return [nil, nil]
712
+ end
713
+
714
+ end
715
+
716
+ def self.words_to_letters txt
717
+ TextUtil.camel_case(txt).gsub(/[a-z]/, '')
718
+ end
719
+
720
+ def self.last nth=1
721
+ $el.el4r_lisp_eval("(elt (recent-keys) (- (length (recent-keys)) #{nth}))").to_s
722
+ end
723
+
724
+ def self.before_last
725
+ $el.el4r_lisp_eval("(elt (recent-keys) (- (length (recent-keys)) 2))").to_s
726
+ end
727
+
728
+ def self.history
729
+ $el.view_lossage
730
+ View.flash "- Showed recently-typed keys in other view!", :times=>4
731
+ end
732
+
733
+ def self.isearch_prefix shortcut_length=2
734
+ # TODO Make search.stop set Keys.prefix (call .isearch_prefix)
735
+ # Keys.prefix = self.isearch_prefix
736
+ # and don't call .isearch_prefix
737
+
738
+ # What about shortcut_length though? Would we get weird results for search_foo_foo shortcuts? - just try it for now
739
+
740
+ # Return it if character before key shortcut was C-u or C-0 - C-9...
741
+ char = Keys.last(shortcut_length+1).to_i
742
+ return :u if char == 21
743
+ return :- if char == 67108909
744
+ return (char - 67108912) if char >= 67108912 && char <= 67108921
745
+
746
+ nil
747
+ end
748
+
749
+ def self.human_readable txt
750
+ txt.split(/[^a-z]/i).map{|o| "Control-#{o[/./].upcase}"}.join(" ")
751
+ end
752
+
753
+ end
754
+