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,274 @@
1
+ require 'effects'
2
+ require 'styles'
3
+
4
+ # Colors lines, and shows only colored lines
5
+ class Color
6
+
7
+ def self.menu
8
+ "
9
+ > Make current line be red, yellow, or blue
10
+ @Color.colorize :r
11
+ @Color.colorize :y
12
+ @Color.colorize :b
13
+
14
+ > See
15
+ << themes/
16
+ << styles/
17
+ "
18
+ end
19
+
20
+
21
+ @@colors = {
22
+ "r"=>:color_rb_red, "y"=>:color_rb_yellow,
23
+ "t"=>:color_rb_orange,
24
+ "e"=>:color_rb_green, "b"=>:color_rb_blue, "u"=>:color_rb_purple,
25
+ "l"=>:color_rb_light,
26
+ "w"=>:color_rb_white,
27
+ }
28
+
29
+ def self.colorize char=nil
30
+ char ||= Keys.input(:chars=>1, :prompt=>'Enter first letter of color: ')
31
+ char = char.to_s
32
+ # If h, just show all colors
33
+ case char
34
+ when "l"
35
+ # We want there to be only one "light" line per file, so delete existing
36
+ overlays = $el.overlays_in(View.top, View.bottom) # Get all overlays
37
+ overlays.to_a.reverse.each do |o| # Loop through and copy all
38
+ if $el.overlay_get(o, :face).to_s == "color-rb-light"
39
+ $el.delete_overlay(o)
40
+ end
41
+ end
42
+
43
+ when "s" # Search in all buffers for marked lines
44
+
45
+ prefix = Keys.prefix
46
+
47
+ if prefix.nil?
48
+ light = $el.overlays_in(View.top, View.bottom).to_a.find{|o| $el.overlay_get(o, :face).to_s == "color-rb-light"}
49
+ return View.to($el.overlay_start(light)) if light
50
+ # Else, contine on and do Color.search
51
+ end
52
+
53
+ return Launcher.open("- Color.search/") if Keys.prefix == 8
54
+
55
+ # Presumably :u
56
+ Launcher.open("- Color.search 'light'/")
57
+ return
58
+
59
+ when "o" # Outline of marked lines
60
+ res = self.get_marked_lines
61
+ res.gsub! /^/, " | "
62
+
63
+ file = View.file
64
+ path = file ?
65
+ "- #{File.expand_path(file).sub(/(.+)\//, "\\1/\n - ")}\n" :
66
+ "- buffer #{View.name}/\n"
67
+
68
+ View.to_buffer("*outline of marked in #{path}")
69
+ View.clear; Notes.mode
70
+ View.insert path
71
+ if res == " | "
72
+ return View.insert " - Nothing was marked in this file!"
73
+ end
74
+ View.insert res
75
+ Keys.clear_prefix
76
+ View.to_line 3
77
+ Line.to_beginning
78
+
79
+ Tree.search :left=> Line.left, :number_means_enter=>true
80
+
81
+ return
82
+
83
+ when "c" # Copy marked lines
84
+
85
+ res = self.get_marked_lines
86
+
87
+ Clipboard['0'] = res
88
+
89
+ when "h" # Hilight
90
+ prefix = Keys.prefix :clear=>1
91
+
92
+ ignore, left, right = View.txt_per_prefix prefix, :just_positions=>1, :default_is_line=>1
93
+
94
+ prefix == :u ?
95
+ Effects.glow(:color=>:rainbow, :times=>4) :
96
+ Effects.glow(:what=>[left, right], :times=>4)
97
+
98
+ # when "h" # Hide
99
+ # Hide.hide_unless_block { |l, bol, eol|
100
+ # # Whether current line contains an overlay of this color
101
+ # $el.overlays_in(bol, eol).to_a.find{ |o|
102
+ # $el.overlay_get(o, :face).to_s =~ /^color-rb-/
103
+ # }
104
+ # }
105
+ # recenter(-3)
106
+ # Hide.search
107
+ # return
108
+ when "n" # to next marker
109
+ # Keys.prefix_times do
110
+ pos = $el.next_overlay_change(View.cursor)
111
+ # end
112
+ # If no overlay, may be at end, so continue on
113
+ pos = $el.next_overlay_change(pos) unless $el.overlays_at(pos)
114
+ return View.to(pos)
115
+ when "p" # to next marker
116
+ pos = $el.previous_overlay_change(View.cursor)
117
+ pos = $el.previous_overlay_change(pos-2) if $el.overlays_at(pos-2)
118
+ return View.to pos
119
+ when "d"
120
+ overlays = $el.overlays_at($el.next_overlay_change($el.point_at_bol - 1))
121
+ return View.beep "- No highlights after cursor!" if ! overlays
122
+ return $el.delete_overlay(overlays[0])
123
+ when "k"
124
+
125
+ if Keys.prefix_u # Don't delete map mark
126
+ return $el.remove_overlays
127
+ end
128
+ overlays = $el.overlays_in(View.top, View.bottom) # Get all overlays
129
+ overlays.to_a.reverse.each do |o| # Loop through and copy all
130
+ if $el.overlay_get(o, :face).to_s != "color-rb-light"
131
+ $el.delete_overlay(o)
132
+ end
133
+ end
134
+ return
135
+
136
+ when "a"
137
+ return self.alternating
138
+ end
139
+
140
+ case Keys.prefix
141
+ when :u # If C-u, use region
142
+ left, right = View.range
143
+ when nil # If nothing, do line
144
+ left, right = Line.left, Line.right+1
145
+ else # Else, get N lines
146
+ txt, left, right = View.txt_per_prefix
147
+ end
148
+
149
+ if ! @@colors[char]
150
+ return View.message "No char color for '#{char}'"
151
+ end
152
+
153
+ over = $el.make_overlay(left, right)
154
+ $el.overlay_put over, :face, @@colors[char]
155
+ end
156
+
157
+ def self.alternating
158
+ orig = View.cursor
159
+ # Get region to cover
160
+ txt, left, right = View.txt_per_prefix
161
+ View.cursor = left
162
+ while(View.cursor < right)
163
+ Color.colorize_line :color_rb_light
164
+ Line.next 2
165
+ end
166
+ View.cursor = orig
167
+ end
168
+
169
+ def self.colorize_line face
170
+ over = $el.make_overlay(Line.left, Line.right+1)
171
+ $el.overlay_put over, :face, face
172
+ end
173
+
174
+ def self.define_styles # For Keys.layout_kolor_light, etc.
175
+
176
+ return if ! $el
177
+
178
+ # Orange path in mode line
179
+ # Blue path in mode line
180
+ # Styles.define :mode_line_dir, :fg=>"7ce", :size=>"0", :face=>"arial", :bold=>false
181
+
182
+ Styles.define :mode_line_file, :fg=>"fff", :size=>"0", :face=>"arial", :bold=>false
183
+
184
+ if Styles.inverse
185
+ Styles.define :mode_line_dir, :fg=>"d93", :size=>"0", :face=>"arial", :bold=>false # Brighter
186
+
187
+ Styles.define :color_rb_red, :bg => "500"
188
+ Styles.define :color_rb_orange, :bg => "442500"
189
+ Styles.define :color_rb_yellow, :bg => "440"
190
+ Styles.define :color_rb_green, :bg => "131"
191
+ Styles.define :color_rb_white, :fg=>'222', :bg=>'fff', :border=>['fff', -1]
192
+
193
+ Styles.define :color_rb_light, :bg => "252525"
194
+
195
+ Styles.define :color_rb_blue, :bg => "005"
196
+ Styles.define :color_rb_purple, :bg => "203"
197
+
198
+ else
199
+
200
+ Styles.define :mode_line_dir, :fg=>"ea4", :size=>"0", :face=>"arial", :bold=>false # Brighter
201
+
202
+ Styles.define :color_rb_red, :bg => "ffd5d5"
203
+ Styles.define :color_rb_orange, :bg => "ffe5bb"
204
+ Styles.define :color_rb_yellow, :bg => "f9f9aa"
205
+ Styles.define :color_rb_green, :bg => "e0ffcc"
206
+ Styles.define :color_rb_light, :bg => "ddd"
207
+ Styles.define :color_rb_white, :fg=>'222', :bg=>'666', :border=>['666', -1]
208
+
209
+ Styles.define :color_rb_blue, :bg => "dde5ff"
210
+ Styles.define :color_rb_purple, :bg => "f2ddff"
211
+
212
+ end
213
+
214
+ Styles.define :fade7, :fg => "333"
215
+ Styles.define :fade6, :fg => "555"
216
+ Styles.define :fade5, :fg => "777"
217
+ Styles.define :fade4, :fg => "999"
218
+ Styles.define :fade3, :fg => "bbb"
219
+ Styles.define :fade2, :fg => "ddd"
220
+ Styles.define :fade1, :fg => "fff"
221
+
222
+ end
223
+
224
+ def self.get_marked_lines label=nil
225
+ overlays = $el.overlays_in(View.top, View.bottom) # Get all overlays
226
+ res = ""
227
+ overlays.to_a.reverse.each do |o| # Loop through and copy all
228
+ if label
229
+ next if $el.overlay_get(o, :face).to_s != label
230
+ end
231
+ line = View.txt($el.overlay_start(o), overlay_end(o))
232
+ line << "\n" unless line =~ /\n$/
233
+ res << line
234
+ end
235
+ res
236
+ end
237
+
238
+ def self.search label=nil
239
+ label = "color-rb-#{label}" if label
240
+
241
+ orig = View.buffer
242
+ txt = ""
243
+ Buffers.list.to_a.each do |b| # Each buffer open
244
+
245
+ $el.set_buffer b
246
+ res = self.get_marked_lines label
247
+ next if res.empty?
248
+
249
+ file = View.file
250
+ next unless file
251
+ path = file ?
252
+ "@#{File.expand_path(file).sub(/(.+)\//, "\\1/\n - ")}\n" :
253
+ "- buffer #{View.name}/\n"
254
+
255
+ txt << path
256
+ txt << res.gsub!(/^/, " | ")
257
+
258
+ end
259
+ View.to_buffer orig
260
+ txt
261
+ end
262
+
263
+
264
+ # Returns list of colors at cursor
265
+ # Color.at_cursor
266
+ def self.at_cursor
267
+ overlays = $el.overlays_in(Line.left, Line.right)
268
+ overlays = overlays.to_a.reverse.inject([]) do |a, o| # Loop through and copy all
269
+ a.push $el.overlay_get(o, :face).to_s
270
+ end
271
+ end
272
+
273
+ end
274
+ Color.define_styles
@@ -0,0 +1,557 @@
1
+ require 'open3'
2
+
3
+ class Console
4
+
5
+ @@log = File.expand_path("~/.emacs.d/console_log.notes")
6
+
7
+ def self.menu
8
+ %`
9
+ - .log/
10
+ - .tree/
11
+ - .history/
12
+ - api/
13
+ | In console (asynchronously)
14
+ @ Console.run "ls"
15
+ @ Console.run "ls", :dir=>"/tmp"
16
+
17
+ | Inline (synchronously)
18
+ @ Console.sync "ls"
19
+ @ Console.sync "ls", :dir=>"/etc"
20
+ - docs/
21
+ You can run shell commands by typing things like this...
22
+
23
+ > In current dir
24
+ @ $ ls
25
+
26
+ > In other dir
27
+ @ /tmp/
28
+ $ ls
29
+
30
+ > Async, in any open console view
31
+ @ /tmp/
32
+ % ls
33
+
34
+ > Async, in other dir
35
+ @ /tmp/
36
+ % ls
37
+
38
+ > Async, in iTerm
39
+ @ /tmp/
40
+ & ls
41
+
42
+ > Commands you've run recently
43
+ << log/
44
+
45
+ > Commands from currently open consoles
46
+ << tree/
47
+ `
48
+ end
49
+
50
+ def self.log
51
+ View.open @@log
52
+ end
53
+
54
+ # Run the command in a console
55
+ def self.[] command
56
+ self.run command, :sync=>1
57
+ end
58
+
59
+ def self.sync command, options={}
60
+ self.run command, options.merge(:sync=>1)
61
+ end
62
+
63
+ #
64
+ # Runs shell command asynchronously.
65
+ #
66
+ # Console.run "ls"
67
+ # Console.run "ls", :dir=>"/tmp/"
68
+ #
69
+ def self.run command, options={}
70
+
71
+ dir = options[:dir]
72
+ sync = options[:sync]
73
+ buffer = options[:buffer]
74
+ reuse_buffer = options[:reuse_buffer]
75
+
76
+ # Nil out dir if blank
77
+ dir = nil if dir && dir.length == 0
78
+
79
+ if dir
80
+ dir = Bookmarks.expand(dir)
81
+ # If relative dir, make current dir be on end of current
82
+ dir = "#{$el.elvar.default_directory}/#{dir}" unless dir =~ /^\//
83
+ dir.gsub!(/\/\/+/, '/')
84
+
85
+ # If file, but not dir, try backing up to the dir
86
+ raise "- Directory '#{dir}' doesn't exist!" if ! File.exists? dir
87
+
88
+ dir.sub!(/[^\/]+$/, '') if ! File.directory?(dir)
89
+
90
+ # If dir exists, continue
91
+ if File.directory?(dir)
92
+ # Put slash on end if not there
93
+ dir = "#{dir}/" unless dir =~ /\/$/
94
+ else # Otherwise, exit
95
+ return puts("#{dir} is not a dir")
96
+ end
97
+ else
98
+ dir = $el ? $el.elvar.default_directory : "/tmp/"
99
+ end
100
+
101
+ if sync
102
+
103
+ return command if options[:no_enter]
104
+ profile = File.exists?(File.expand_path('~/.profile')) ? '. ~/.profile;' : ''
105
+ stdin, stdout, stderr = Open3.popen3("#{profile}cd \"#{dir}\";#{command}")
106
+ result = ""
107
+ result << stdout.readlines.join('')
108
+ result << stderr.readlines.join('')
109
+ result.gsub!("\c@", '.') # Replace out characters that el4r can't handle
110
+ return result
111
+
112
+ else
113
+ if View.in_bar? and ! options[:dont_leave_bar]
114
+ View.to_after_bar
115
+ end
116
+ buffer ||= "*console #{dir}"
117
+
118
+ if ! reuse_buffer
119
+ buffer = $el.generate_new_buffer(buffer)
120
+ end
121
+ View.to_buffer buffer
122
+ $el.erase_buffer if reuse_buffer
123
+ $el.elvar.default_directory = dir if dir
124
+ $el.shell $el.current_buffer
125
+ Move.bottom
126
+ if command # If nil, just open console
127
+ $el.insert command
128
+ Console.enter unless options[:no_enter]
129
+ end
130
+ end
131
+
132
+ nil
133
+ end
134
+
135
+ def self.open dir=nil
136
+ View.handle_bar
137
+ dir ||= $el.elvar.default_directory
138
+ dir = File.expand_path(dir)+"/"
139
+ View.to_buffer $el.generate_new_buffer("shell")
140
+ raise "dir '#{dir}' doesn't exist" unless File.directory?(dir)
141
+ $el.elvar.default_directory = dir
142
+ $el.shell $el.current_buffer
143
+ end
144
+
145
+ def self.enter command=nil
146
+ View.insert command if command
147
+
148
+ begin
149
+ $el.comint_send_input
150
+ rescue
151
+ # Ol << "Console.enter error here!"
152
+ end
153
+ end
154
+
155
+ def self.console?
156
+ View.mode == :shell_mode
157
+ end
158
+
159
+ def self.to_shell_buffer dir=nil, options={}
160
+ if dir
161
+ dir = "#{dir}/" unless dir =~ /\/$/
162
+ pattern = /^\*console #{Regexp.quote(dir)}(<| |$)/
163
+ else
164
+ # If already in a shell (regardless of buffer name)
165
+ return true if View.mode == :shell_mode
166
+ pattern = /^\*console/
167
+ end
168
+
169
+ return true if View.name =~ pattern # If already there, do nothing
170
+
171
+ if ! dir
172
+
173
+ # Try to find visible shell buffer in same dir and with prompt
174
+
175
+ View.list.each do |w|
176
+ $el.set_buffer $el.window_buffer(w)
177
+ next if View.mode != :shell_mode || ! Console.prompt?
178
+ next if View.cursor != View.bottom
179
+ View.to_window(w)
180
+ return true
181
+ end
182
+ end
183
+
184
+ if dir && dir !~ /@/ # If local dir
185
+
186
+ # TODO Make sure there's no ssh or cd in history!
187
+ View.list.each do |w|
188
+ $el.set_buffer $el.window_buffer(w)
189
+ next if View.mode != :shell_mode || ! Console.prompt?
190
+ next if Tree.slashless(dir) != Tree.slashless(View.dir)
191
+ next if View.cursor != View.bottom
192
+
193
+ View.to_window(w)
194
+ return true
195
+ end
196
+
197
+ # TODO: implement similar finding a dir for remote
198
+ # else
199
+
200
+ end
201
+
202
+ # Deprecated:
203
+ # Try to find visible shell buffer with matching name
204
+ View.list.each do |w|
205
+ next if $el.buffer_name($el.window_buffer w) !~ pattern
206
+ View.to_window(w)
207
+ return true
208
+ end
209
+
210
+ if Keys.prefix_u(:clear=>true)
211
+
212
+ found = Buffers.list.find do |b|
213
+ name = Buffers.name b
214
+ next false unless name =~ pattern
215
+
216
+ view = nil
217
+ $el.with(:save_window_excursion) do
218
+ View.to_buffer name
219
+
220
+ next false unless Console.prompt?
221
+
222
+ cd_dir = View.dir
223
+ cd_dir = "#{cd_dir}/" unless cd_dir =~ /\/$/
224
+ next false unless cd_dir == dir
225
+ next false if Console.commands.join("\n") =~ /^(ssh|ftp) /
226
+ true
227
+ end
228
+ end
229
+
230
+ if found
231
+ View.to_upper
232
+ return View.to_buffer(found)
233
+ end
234
+ end
235
+
236
+ # Wasn't found among visible, so create new buffer
237
+
238
+ return false if options[:no_create] # Don't create it if option says not to
239
+
240
+ if dir =~ /@/ # If there's a @, it's remote
241
+ View.handle_bar
242
+ View.to_buffer $el.generate_new_buffer("*console #{dir}")
243
+ $el.elvar.default_directory = "/tmp"
244
+ $el.shell $el.current_buffer
245
+ if dir =~ /(.+?)(\/.+)/ # Split off dir if there
246
+ line = self.ssh_line($1)
247
+ Console.enter line
248
+ options[:cd_and_wait] ?
249
+ View.insert("cd #{$2} && ") :
250
+ Console.enter("cd #{$2}")
251
+ else
252
+ line = self.ssh_line(dir)
253
+ Console.enter line
254
+ end
255
+ else
256
+ Console.open dir
257
+ end
258
+ return true
259
+ end
260
+
261
+ def self.do_last_command
262
+ # Code.open_log_view if Keys.prefix_u
263
+
264
+ orig = View.index
265
+
266
+ found = self.to_shell_buffer(nil, :no_create=>true) # If not in shell buffer, go to it
267
+
268
+ return View.message("No *console buffer was visible") unless found
269
+
270
+ $el.erase_buffer
271
+ $el.comint_previous_input(1)
272
+ self.enter
273
+ View.to_nth orig
274
+ end
275
+
276
+ def self.launch_async options={}
277
+
278
+ orig = Location.new
279
+ orig_view = View.index
280
+ path = Tree.construct_path(:list=>true)
281
+
282
+ path[0] = Bookmarks[path[0]] if path[0] =~ /^(\.\/|\$[\w-])/ # Expand out bookmark or ./, if there
283
+ if path[0] =~ /^\// # If has dir (local or remote)
284
+ line = path.join('')
285
+ dir, command = line.match(/(.+?)[%&] (.*)/)[1..2]
286
+ self.append_log "#{command}", dir, '% '
287
+
288
+ return Iterm.run("cd #{dir}\n#{command}", :activate=>1) if Keys.prefix_uu
289
+ return Iterm.run("cd #{dir}\n#{command}") if Keys.prefix_u || options[:iterm]
290
+
291
+ Console.to_shell_buffer dir, :cd_and_wait=>true
292
+ else # Otherwise, if by itself - meaning on own line?
293
+ command = Line.without_label.match(/.*?[%&] ?(.*)/)[1]
294
+ self.append_log("#{command}", dir, '% ') if command.present?
295
+
296
+ return Iterm.run("#{command}", :activate=>1) if Keys.prefix_uu
297
+ return Iterm.run("#{command}", :activate=>1) if Keys.prefix_u && options[:iterm]
298
+ return Iterm.run("#{command}") if Keys.prefix_u || options[:iterm]
299
+
300
+ self.to_shell_buffer # Go to shell if one is visible
301
+ end
302
+ return if command.empty?
303
+
304
+ View.to_bottom
305
+
306
+ View.insert command
307
+ Console.enter
308
+
309
+ orig.go unless orig_view == View.index
310
+ end
311
+
312
+ # Synchronous - mapped to $ launcher
313
+ def self.launch options={}
314
+ trunk = Xiki.trunk # use Tree.path instead
315
+
316
+ # If it looks like error output, just jump to it
317
+ if trunk[-1] =~ /[^\/+]\/\| \s+from / || trunk[-1] =~ /[^\/+]\/\| +\//
318
+ return Code.open_as_file
319
+ end
320
+
321
+ # if not under file
322
+ # raise RelinquishException.new
323
+
324
+ # Run in current dir if no parent or @$
325
+ if trunk[-1] =~ /^\$ /
326
+ # TODO Run in current dir?
327
+ # return
328
+ end
329
+
330
+ # There's a dir in our chunk, so relinquish control if not fire tree
331
+
332
+ # Handle if
333
+ # no parent in our chunk
334
+ # parent in our chunk is file tree
335
+
336
+ line = Line.without_label :leave_indent=>true
337
+ # If indented, check whether file tree, extracting if yes
338
+ if line =~ /^\s+\$/
339
+ orig = View.cursor
340
+ path = Tree.construct_path(:list=>true)
341
+ if path[0] =~ /@/ # If there's a @, it's remote
342
+ self.append_log path[1], path[0]
343
+ return Remote.command path
344
+ end
345
+ if FileTree.handles?(path)
346
+ while(path.last =~ /^\$/) do # Remove all !foo lines from path
347
+ path.pop
348
+ end
349
+ dir = path.join('')
350
+
351
+ # If starts with ./, replace with current dir
352
+ dir.sub! /^\.\//, "#{View.dir}/"
353
+
354
+ end
355
+ View.to orig
356
+ end
357
+ line =~ / *@? ?(.*?)\$+ ?(.+)/
358
+ dir ||= $1 unless $1.empty?
359
+ command = $2
360
+
361
+ return Tree.<<("- Directory doesn't exist) #{dir}") if dir && ! File.exists?(dir)
362
+
363
+ if options[:sync]
364
+ output = Console.run command, :dir=>dir, :sync=>true
365
+ output.sub!(/\A\z/, "\n") # Add linebreak if blank
366
+ Keys.prefix == 1 ? output.gsub!(/^/, '|') : output.gsub!(/^/, '| ').gsub!(/^\| +$/, '|')
367
+
368
+ # Expose "!" and "- label: !" lines as commands
369
+ output.gsub!(/^\| \$/, '$')
370
+ output.gsub!(/^\| (- [\w ,-]+: \$)/, "\\1")
371
+ output.sub! /\n*\z/, "\n" # Guarantee exactly 1 linebreak at end
372
+ Tree.indent(output)
373
+
374
+ Tree.insert_quoted_and_search output
375
+ else
376
+ View.handle_bar
377
+ Console.run command, :dir=>dir #, :buffer=>"*console #{dir}"
378
+ end
379
+
380
+ self.append_log command, dir, '$ '
381
+
382
+ end
383
+
384
+ def self.append_log command, dir, prefix=''
385
+ return if View.name =~ /_log.notes$/
386
+ if dir.nil?
387
+ dir ||= View.dir
388
+ dir = "#{dir}/" if dir !~ /\/$/
389
+ end
390
+
391
+ command = command.dup
392
+ command.gsub!(/^/, prefix) unless command =~ /^ *!/
393
+ command.gsub!(/^/, ' ')
394
+
395
+ txt = "- #{dir}\n#{command}\n"
396
+ File.open(@@log, "a") { |f| f << txt } rescue nil
397
+ end
398
+
399
+ def self.ssh_line path
400
+ path = path.sub /^\//, ''
401
+ path.sub! /\/$/, ''
402
+
403
+ if path =~ /(.+):(.+)/ # If port exists (colon)
404
+ "ssh -p #{$2} #{$1}"
405
+ # Pull out and pass with -p
406
+ else
407
+ "ssh -A #{path}"
408
+ end
409
+ end
410
+
411
+ def self.do_as_execute options={}
412
+
413
+ if FileTree.handles? && ! Line.matches(/^\s*\|/) # If we're in a file tree
414
+ path = Tree.construct_path
415
+
416
+ # # Run command inside of dir
417
+ # if Line.matches(/\/$/) # If a dir
418
+ # command = Keys.input :prompt=>"Do shell command on '#{file}': "
419
+ # output = Console.run(command, :dir=>path, :sync=>true)
420
+ # FileTree.insert_under(output) if options[:insert]
421
+ # return View.message "Command ran with output: #{output.strip}."
422
+ # elsif Keys.prefix_n
423
+ # View.message "Running command on multiple files isn't implemented yet."
424
+ # return
425
+ # end
426
+
427
+ file = Line.without_label
428
+ command = Keys.input :prompt=>"Shell command on this file (_ means the filename): "
429
+ command = command =~ /\b_\b/ ? command.gsub(/\b_\b/, "\"#{file}\"") : "#{command} \"#{file}\""
430
+
431
+ output = Console.run(command, :dir=>File.dirname(path), :sync=>true)
432
+ Tree.under(output, :escape=>'| ') if options[:insert]
433
+
434
+ return View.message "Command ran with output: #{output.strip}."
435
+ end
436
+
437
+ command = Keys.input :prompt=>"Do shell command on '#{View.file_name}': "
438
+ command = "#{command} #{View.file_name}"
439
+ output = Console.run(command, :dir=>View.dir, :sync=>true)
440
+ View.insert(output) if options[:insert]
441
+
442
+ return View.message "Command ran with output: #{output.strip}."
443
+
444
+ end
445
+
446
+ # Whether buffer ends with shell prompt "...$ "
447
+ def self.prompt?
448
+ right = View.bottom
449
+ left = right - 10
450
+ left = 1 if left < 1
451
+ txt = View.txt left, right
452
+
453
+ txt =~ /[>#%$] \z/
454
+ end
455
+
456
+ def self.history bm
457
+
458
+ dir = Bookmarks[bm]
459
+ dir = Files.dir_of dir
460
+
461
+ console_log = IO.read(@@log)
462
+
463
+ result = []
464
+ match = false
465
+ console_log.split("\n").each do |l|
466
+ if l =~ /^[+-] /
467
+ next match = l =~ /\A- #{Regexp.escape dir}/
468
+ end
469
+
470
+ result << "#{l}" if match
471
+ end
472
+
473
+ "@ #{dir}\n"+result.reverse.uniq.join("\n")+"\n"
474
+
475
+ end
476
+
477
+ def self.commands
478
+ matches = $el.elvar.comint_input_ring.to_s.scan(/#\("(.+?)" /).flatten
479
+
480
+ matches.map!{|o| o.gsub '\\"', '"'}
481
+ matches
482
+ end
483
+
484
+ def self.custom_history
485
+ dir = View.dir
486
+ history = Console.commands
487
+ history.uniq! unless Keys.prefix_u
488
+ history = history.join("\n").gsub(/^/, '% ')
489
+ View.create :u if ! View.list_names.member?("*shell history")
490
+ View.to_buffer "*shell history"
491
+ View.kill_all
492
+ Notes.mode
493
+
494
+ View.insert "#{history}\n"
495
+ View.to_highest
496
+ Tree.search
497
+ end
498
+
499
+ def self.search_last_commands
500
+ bm = Keys.input(:timed => true, :prompt => "bookmark to show commands for (space for currently open): ")
501
+ return Launcher.open("- console/tree/") if bm == " "
502
+ if bm == "8"
503
+ Console.log; View.to_bottom; Search.isearch nil, :reverse=>true
504
+ return
505
+ end
506
+
507
+ Launcher.open("- console/history/$#{bm}/")
508
+ end
509
+
510
+ def self.tree *args
511
+ command = args.pop if args[-1] =~ /^\|/
512
+ console = args.any? ? args.join("/") : nil
513
+
514
+ if console
515
+ View.to_buffer console#.sub /\/$/, ''
516
+ return
517
+ end
518
+
519
+ txt = ""
520
+
521
+ $el.with(:save_excursion) do
522
+
523
+ Buffers.list.each do |b|
524
+ next if $el.buffer_file_name b
525
+ name = $el.buffer_name b
526
+ $el.set_buffer b
527
+ next if $el.elvar.major_mode.to_s != 'shell-mode'
528
+
529
+ next if name == "*ol"
530
+
531
+ txt << "- #{name}/\n"
532
+ self.commands.reverse.each do |h|
533
+ txt << " | $ #{h}\n"
534
+ end
535
+ end
536
+ end
537
+
538
+ txt
539
+ end
540
+
541
+ def self.exit # Kills running server or process in shell
542
+ $el.comint_interrupt_subjob
543
+ end
544
+
545
+ def self.wait_until buffer, options={}
546
+ max = options[:max] || 10
547
+ message = options[:message] || "Launching..."
548
+ while View.txt(:buffer=>buffer) !~ options[:contains]
549
+ View.flash message, :times=>1
550
+ max -= 1
551
+ break if max < 0
552
+ end
553
+ end
554
+
555
+ end
556
+
557
+ Keys.custom_history(:shell_mode_map) { Console.custom_history }