utils 0.0.0

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 (75) hide show
  1. data/Rakefile +68 -0
  2. data/VERSION +1 -0
  3. data/bin/chroot-exec +12 -0
  4. data/bin/chroot-libs +18 -0
  5. data/bin/classify +37 -0
  6. data/bin/discover +137 -0
  7. data/bin/edit +74 -0
  8. data/bin/errf +32 -0
  9. data/bin/git-empty +8 -0
  10. data/bin/myex +90 -0
  11. data/bin/number_files +26 -0
  12. data/bin/same_files +37 -0
  13. data/bin/search +205 -0
  14. data/bin/sedit +3 -0
  15. data/bin/sshscreen +68 -0
  16. data/bin/term +21 -0
  17. data/bin/unquarantine_apps +8 -0
  18. data/bin/untest +17 -0
  19. data/bin/utils-install-config +10 -0
  20. data/bin/vacuum_firefox_sqlite +22 -0
  21. data/bin/xmp +74 -0
  22. data/lib/utils.rb +8 -0
  23. data/lib/utils/config.rb +23 -0
  24. data/lib/utils/config/gdb/asm +179 -0
  25. data/lib/utils/config/gdb/ruby +528 -0
  26. data/lib/utils/config/gdbinit +8 -0
  27. data/lib/utils/config/irbrc +455 -0
  28. data/lib/utils/config/rdebugrc +2 -0
  29. data/lib/utils/config/screenrc +143 -0
  30. data/lib/utils/config/vim/autoload/Align.vim +1029 -0
  31. data/lib/utils/config/vim/autoload/AlignMaps.vim +330 -0
  32. data/lib/utils/config/vim/autoload/rails.vim +4744 -0
  33. data/lib/utils/config/vim/autoload/rubycomplete.vim +801 -0
  34. data/lib/utils/config/vim/autoload/sqlcomplete.vim +741 -0
  35. data/lib/utils/config/vim/autoload/vimball.vim +750 -0
  36. data/lib/utils/config/vim/colors/flori.vim +113 -0
  37. data/lib/utils/config/vim/compiler/eruby.vim +40 -0
  38. data/lib/utils/config/vim/compiler/ruby.vim +67 -0
  39. data/lib/utils/config/vim/compiler/rubyunit.vim +34 -0
  40. data/lib/utils/config/vim/ftdetect/ragel.vim +2 -0
  41. data/lib/utils/config/vim/ftdetect/ruby.vim +17 -0
  42. data/lib/utils/config/vim/ftplugin/eruby.vim +100 -0
  43. data/lib/utils/config/vim/ftplugin/ruby.vim +260 -0
  44. data/lib/utils/config/vim/ftplugin/xml.vim +941 -0
  45. data/lib/utils/config/vim/indent/IndentAnything_html.vim +35 -0
  46. data/lib/utils/config/vim/indent/eruby.vim +77 -0
  47. data/lib/utils/config/vim/indent/javascript.vim +116 -0
  48. data/lib/utils/config/vim/indent/ruby.vim +377 -0
  49. data/lib/utils/config/vim/plugin/AlignMapsPlugin.vim +242 -0
  50. data/lib/utils/config/vim/plugin/AlignPlugin.vim +41 -0
  51. data/lib/utils/config/vim/plugin/Decho.vim +592 -0
  52. data/lib/utils/config/vim/plugin/IndentAnything.vim +675 -0
  53. data/lib/utils/config/vim/plugin/bufexplorer.vim +1144 -0
  54. data/lib/utils/config/vim/plugin/cecutil.vim +482 -0
  55. data/lib/utils/config/vim/plugin/fugitive.vim +1703 -0
  56. data/lib/utils/config/vim/plugin/lusty-explorer.vim +1509 -0
  57. data/lib/utils/config/vim/plugin/rails.vim +340 -0
  58. data/lib/utils/config/vim/plugin/rubyextra.vim +193 -0
  59. data/lib/utils/config/vim/plugin/surround.vim +628 -0
  60. data/lib/utils/config/vim/plugin/taglist.vim +4546 -0
  61. data/lib/utils/config/vim/plugin/test/IndentAnything/test.js +131 -0
  62. data/lib/utils/config/vim/plugin/vimballPlugin.vim +40 -0
  63. data/lib/utils/config/vim/syntax/Decho.vim +101 -0
  64. data/lib/utils/config/vim/syntax/eruby.vim +73 -0
  65. data/lib/utils/config/vim/syntax/javascript.vim +246 -0
  66. data/lib/utils/config/vim/syntax/ragel.vim +165 -0
  67. data/lib/utils/config/vim/syntax/ruby.vim +367 -0
  68. data/lib/utils/config/vimrc +461 -0
  69. data/lib/utils/file.rb +49 -0
  70. data/lib/utils/find.rb +54 -0
  71. data/lib/utils/md5.rb +23 -0
  72. data/lib/utils/patterns.rb +34 -0
  73. data/lib/utils/version.rb +8 -0
  74. data/utils.gemspec +33 -0
  75. metadata +183 -0
@@ -0,0 +1,1509 @@
1
+ " Copyright: Copyright (C) 2007-2010 Stephen Bach
2
+ " Permission is hereby granted to use and distribute this code,
3
+ " with or without modifications, provided that this copyright
4
+ " notice is copied with it. Like anything else that's free,
5
+ " lusty-explorer.vim is provided *as is* and comes with no
6
+ " warranty of any kind, either expressed or implied. In no
7
+ " event will the copyright holder be liable for any damages
8
+ " resulting from the use of this software.
9
+ "
10
+ " Name Of File: lusty-explorer.vim
11
+ " Description: Dynamic Filesystem and Buffer Explorer Vim Plugin
12
+ " Maintainers: Stephen Bach <this-file@sjbach.com>
13
+ " Matt Tolton <matt-lusty-explorer@tolton.com>
14
+ " Contributors: Raimon Grau, Sergey Popov, Yuichi Tateno, Bernhard Walle,
15
+ " Rajendra Badapanda, cho45, Simo Salminen, Sami Samhuri,
16
+ " Matt Tolton, Björn Winckler, sowill
17
+ "
18
+ " Release Date: March 3, 2010
19
+ " Version: 2.1.3
20
+ " Inspired by Viewglob, Emacs, and by Jeff Lanzarotta's Buffer
21
+ " Explorer plugin.
22
+ "
23
+ " Usage: To launch the explorers:
24
+ "
25
+ " <Leader>lf - Opens the filesystem explorer.
26
+ " <Leader>lr - Opens the filesystem explorer from the parent
27
+ " directory of the current file.
28
+ " <Leader>lb - Opens the buffer explorer.
29
+ "
30
+ " You can also use the commands:
31
+ "
32
+ " ":FilesystemExplorer"
33
+ " ":FilesystemExplorerFromHere"
34
+ " ":BufferExplorer"
35
+ "
36
+ " (Personally, I map these to ,f and ,r and ,b)
37
+ "
38
+ " The interface is intuitive. When one of the explorers is
39
+ " launched, a new window appears at bottom presenting a list of
40
+ " files/dirs or buffers, and in the status bar is a prompt:
41
+ "
42
+ " >>
43
+ "
44
+ " As you type, the list updates for possible matches using a
45
+ " fuzzy matching algorithm. Special keys include:
46
+ "
47
+ " <Enter> open the selected match
48
+ " <Tab> open the selected match
49
+ " <Esc> cancel
50
+ " <C-c> cancel
51
+ " <C-g> cancel
52
+ "
53
+ " <C-t> open the selected match in a new tab
54
+ " <C-n> select the next match
55
+ " <C-p> select the previous match
56
+ " <C-w> ascend one directory at prompt
57
+ " <C-u> clear the prompt
58
+ "
59
+ " Additional shortcuts for the filesystem explorer:
60
+ "
61
+ " <C-r> refresh directory contents
62
+ " <C-a> open all files in the current list
63
+ " <C-e> create a new file with the given name
64
+ "
65
+ " Buffer Explorer:
66
+ " - The currently active buffer is highlighted.
67
+ " - Buffers are listed without path unless needed to differentiate buffers of
68
+ " the same name.
69
+ "
70
+ " Filesystem Explorer:
71
+ " - Directory contents are memoized.
72
+ " - You can recurse into and out of directories by typing the directory name
73
+ " and a slash, e.g. "stuff/" or "../".
74
+ " - Variable expansion, e.g. "$D" -> "/long/dir/path/".
75
+ " - Tilde (~) expansion, e.g. "~/" -> "/home/steve/".
76
+ " - Dotfiles are hidden by default, but are shown if the current search term
77
+ " begins with a '.'. To show these file at all times, set this option:
78
+ "
79
+ " let g:LustyExplorerAlwaysShowDotFiles = 1
80
+ "
81
+ " You can prevent certain files from appearing in the directory listings with
82
+ " the following variable:
83
+ "
84
+ " set wildignore=*.o,*.fasl,CVS
85
+ "
86
+ " The above example will mask all object files, compiled lisp files, and
87
+ " files/directories named CVS from appearing in the filesystem explorer.
88
+ " Note that they can still be opened by being named explicitly.
89
+ "
90
+ " See :help 'wildignore' for more information.
91
+ "
92
+ "
93
+ " Install Details:
94
+ "
95
+ " Copy this file into your $HOME/.vim/plugin directory so that it will be
96
+ " sourced on startup automatically.
97
+ "
98
+ " Note! This plugin requires Vim be compiled with Ruby interpretation. If you
99
+ " don't know if your build of Vim has this functionality, you can check by
100
+ " running "vim --version" from the command line and looking for "+ruby".
101
+ " Alternatively, just try sourcing this script.
102
+ "
103
+ " If your version of Vim does not have "+ruby" but you would still like to
104
+ " use this plugin, you can fix it. See the "Check for Ruby functionality"
105
+ " comment below for instructions.
106
+ "
107
+ " If you are using the same Vim configuration and plugins for multiple
108
+ " machines, some of which have Ruby and some of which don't, you may want to
109
+ " turn off the "Sorry, LustyExplorer requires ruby" warning. You can do so
110
+ " like this (in .vimrc):
111
+ "
112
+ " let g:LustyExplorerSuppressRubyWarning = 1
113
+ "
114
+ " GetLatestVimScripts: 1890 1 :AutoInstall: lusty-explorer.vim
115
+ "
116
+ " TODO:
117
+ " - when an edited file is in nowrap mode and the explorer is called while the
118
+ " current window is scrolled to the right, name truncation occurs.
119
+ " - bug: NO ENTRIES is not red when input is a space
120
+ " - happens because LustyExpMatch declares after LustyExpNoEntries.
121
+ " - if new_hash == previous_hash, don't bother 'repainting'.
122
+
123
+ " Exit quickly when already loaded.
124
+ if exists("g:loaded_lustyexplorer")
125
+ finish
126
+ endif
127
+
128
+ if exists("g:FuzzyFinderMode.TextMate")
129
+ echohl WarningMsg
130
+ echo "Warning: LustyExplorer detects the presence of fuzzyfinder_textmate;"
131
+ echo "that plugin sometimes interacts poorly with other Ruby plugins."
132
+ echohl none
133
+ endif
134
+
135
+ " Check for Ruby functionality.
136
+ if !has("ruby") || version < 700
137
+ if !exists("g:LustyExplorerSuppressRubyWarning") ||
138
+ \ g:LustyExplorerSuppressRubyWarning == "0"
139
+ if !exists("g:LustyJugglerSuppressRubyWarning") ||
140
+ \ g:LustyJugglerSuppressRubyWarning == "0"
141
+ echohl ErrorMsg
142
+ echon "Sorry, LustyExplorer requires ruby. "
143
+ echon "Here are some tips for adding it:\n"
144
+
145
+ echo "Debian / Ubuntu:"
146
+ echo " # apt-get install vim-ruby\n"
147
+
148
+ echo "Fedora:"
149
+ echo " # yum install vim-enhanced\n"
150
+
151
+ echo "Gentoo:"
152
+ echo " # USE=\"ruby\" emerge vim\n"
153
+
154
+ echo "FreeBSD:"
155
+ echo " # pkg_add -r vim+ruby\n"
156
+
157
+ echo "Windows:"
158
+ echo " 1. Download and install Ruby from here:"
159
+ echo " http://www.ruby-lang.org/"
160
+ echo " 2. Install a Vim binary with Ruby support:"
161
+ echo " http://segfault.hasno.info/vim/gvim72.zip\n"
162
+
163
+ echo "Manually (including Cygwin):"
164
+ echo " 1. Install Ruby."
165
+ echo " 2. Download the Vim source package (say, vim-7.0.tar.bz2)"
166
+ echo " 3. Build and install:"
167
+ echo " # tar -xvjf vim-7.0.tar.bz2"
168
+ echo " # ./configure --enable-rubyinterp"
169
+ echo " # make && make install"
170
+
171
+ echo "(If you just wish to stifle this message, set the following option:"
172
+ echo " let g:LustyJugglerSuppressRubyWarning = 1)"
173
+ echohl none
174
+ endif
175
+ endif
176
+ finish
177
+ endif
178
+
179
+ let g:loaded_lustyexplorer = "yep"
180
+
181
+ " Commands.
182
+ command BufferExplorer :call <SID>BufferExplorerStart()
183
+ command FilesystemExplorer :call <SID>FilesystemExplorerStart()
184
+ command FilesystemExplorerFromHere :call <SID>FilesystemExplorerStartFromHere()
185
+
186
+ " Default mappings.
187
+ nmap <silent> <Leader>lf :FilesystemExplorer<CR>
188
+ nmap <silent> <Leader>lr :FilesystemExplorerFromHere<CR>
189
+ nmap <silent> <Leader>lb :BufferExplorer<CR>
190
+
191
+ " Old mappings (from DynamicExplorer).
192
+ nmap <silent> <Leader>df :FilesystemExplorer<CR>
193
+ nmap <silent> <Leader>db :BufferExplorer<CR>
194
+
195
+ " Vim-to-ruby function calls.
196
+ function! s:FilesystemExplorerStart()
197
+ ruby $filesystem_explorer.run_from_wd
198
+ endfunction
199
+
200
+ function! s:FilesystemExplorerStartFromHere()
201
+ ruby $filesystem_explorer.run_from_here
202
+ endfunction
203
+
204
+ function! s:BufferExplorerStart()
205
+ ruby $buffer_explorer.run
206
+ endfunction
207
+
208
+ function! FilesystemExplorerCancel()
209
+ ruby $filesystem_explorer.cancel
210
+ endfunction
211
+
212
+ function! BufferExplorerCancel()
213
+ ruby $buffer_explorer.cancel
214
+ endfunction
215
+
216
+ function! FilesystemExplorerKeyPressed(code_arg)
217
+ ruby $filesystem_explorer.key_pressed
218
+ endfunction
219
+
220
+ function! BufferExplorerKeyPressed(code_arg)
221
+ ruby $buffer_explorer.key_pressed
222
+ endfunction
223
+
224
+ " Setup the autocommands that handle buffer MRU ordering.
225
+ "augroup LustyExplorer
226
+ " autocmd!
227
+ " autocmd BufEnter * ruby Window.buffer_stack.push
228
+ " autocmd BufDelete * ruby Window.buffer_stack.pop
229
+ " autocmd BufWipeout * ruby Window.buffer_stack.pop
230
+ "augroup End
231
+
232
+ ruby << EOF
233
+ require 'pathname'
234
+ # Needed for String#each_char in Ruby 1.8 on some platforms
235
+ require 'jcode' unless "".respond_to? :each_char
236
+
237
+ $PROFILING = false
238
+
239
+ if $PROFILING
240
+ require 'rubygems'
241
+ require 'ruby-prof'
242
+ end
243
+
244
+ class String
245
+ def ends_with?(s)
246
+ tail = self[-s.length, s.length]
247
+ tail == s
248
+ end
249
+
250
+ def starts_with?(s)
251
+ head = self[0, s.length]
252
+ head == s
253
+ end
254
+ end
255
+
256
+ class IO
257
+ def ready_for_read?
258
+ result = IO.select([self], nil, nil, 0)
259
+ result && (result.first.first == self)
260
+ end
261
+ end
262
+
263
+ class File
264
+ def self.simplify_path(s)
265
+ begin
266
+ if s[0] == '~'[0]
267
+ s = File.expand_path(s.sub(/\/.*/,'')) + \
268
+ s.sub(/^[^\/]+/,'')
269
+ end
270
+
271
+ if s.ends_with?(File::SEPARATOR)
272
+ File.expand_path(s) + File::SEPARATOR
273
+ else
274
+ File.expand_path(File.dirname(s)) + File::SEPARATOR + File.basename(s)
275
+ end
276
+ rescue ArgumentError
277
+ s
278
+ end
279
+ end
280
+ end
281
+
282
+ module VIM
283
+ def self.zero?(var)
284
+ # In Vim 7.2 and older, VIM::evaluate returns Strings for boolean
285
+ # expressions; in later versions, Fixnums.
286
+ case var
287
+ when String
288
+ var == "0"
289
+ when Fixnum
290
+ var == 0
291
+ else
292
+ assert(false, "unexpected type: #{var.class}")
293
+ end
294
+ end
295
+
296
+ def self.nonzero?(var)
297
+ not(self.zero? var)
298
+ end
299
+
300
+ def self.has_syntax?
301
+ VIM::nonzero? eva('has("syntax")')
302
+ end
303
+
304
+ def self.columns
305
+ eva("&columns").to_i
306
+ end
307
+
308
+ def self.lines
309
+ eva("&lines").to_i
310
+ end
311
+
312
+ def self.getcwd
313
+ eva("getcwd()")
314
+ end
315
+
316
+ def self.single_quote_escape(s)
317
+ # Everything in a Vim single quoted string is literal, except single quotes.
318
+ # Single quotes are escaped by doubling them.
319
+ s.gsub("'", "''")
320
+ end
321
+
322
+ def self.filename_escape(s)
323
+ # Escape slashes, open square braces, spaces, sharps, and double quotes.
324
+ s.gsub(/\\/, '\\\\\\').gsub(/[\[ #"]/, '\\\\\0')
325
+ end
326
+
327
+ def self.regex_escape(s)
328
+ s.gsub(/[\]\[.~"^$\\*]/,'\\\\\0')
329
+ end
330
+
331
+ class Buffer
332
+ def modified?
333
+ VIM::nonzero? eva("getbufvar(#{number()}, '&modified')")
334
+ end
335
+ end
336
+ end
337
+
338
+ def lusty_option_set?(opt_name)
339
+ opt_name = "g:LustyExplorer" + opt_name
340
+ VIM::nonzero? eva("exists('#{opt_name}') && #{opt_name} != '0'")
341
+ end
342
+
343
+ # Port of Ryan McGeary's LiquidMetal fuzzy matching algorithm found at:
344
+ # http://github.com/rmm5t/liquidmetal/tree/master.
345
+ class LiquidMetal
346
+ @@SCORE_NO_MATCH = 0.0
347
+ @@SCORE_MATCH = 1.0
348
+ @@SCORE_TRAILING = 0.8
349
+ @@SCORE_TRAILING_BUT_STARTED = 0.90
350
+ @@SCORE_BUFFER = 0.85
351
+
352
+ def self.score(string, abbrev)
353
+
354
+ return @@SCORE_TRAILING if abbrev.empty?
355
+ return @@SCORE_NO_MATCH if abbrev.length > string.length
356
+
357
+ scores = buildScoreArray(string, abbrev)
358
+
359
+ sum = scores.inject { |a, b| a + b }
360
+
361
+ return sum / scores.length;
362
+ end
363
+
364
+ def self.buildScoreArray(string, abbrev)
365
+ scores = Array.new(string.length)
366
+ lower = string.downcase()
367
+
368
+ lastIndex = -1
369
+ started = false
370
+
371
+ abbrev.downcase().each_char do |c|
372
+ index = lower.index(c, lastIndex + 1)
373
+ return scores.fill(@@SCORE_NO_MATCH, 0..-1) if index.nil?
374
+ started = true if index == 0
375
+
376
+ if index > 0 and " \t/._-".include?(string[index - 1])
377
+ scores[index - 1] = @@SCORE_MATCH
378
+ scores.fill(@@SCORE_BUFFER, (lastIndex + 1)...(index - 1))
379
+ elsif string[index] >= "A"[0] and string[index] <= "Z"[0]
380
+ scores.fill(@@SCORE_BUFFER, (lastIndex + 1)...index)
381
+ else
382
+ scores.fill(@@SCORE_NO_MATCH, (lastIndex + 1)...index)
383
+ end
384
+
385
+ scores[index] = @@SCORE_MATCH
386
+ lastIndex = index
387
+ end
388
+
389
+ trailing_score = started ? @@SCORE_TRAILING_BUT_STARTED : @@SCORE_TRAILING
390
+ scores.fill(trailing_score, lastIndex + 1)
391
+ return scores
392
+ end
393
+ end
394
+
395
+ # Used in FilesystemExplorer
396
+ class Entry
397
+ attr_accessor :name, :current_score
398
+ def initialize(name)
399
+ @name = name
400
+ @current_score = 0.0
401
+ end
402
+ end
403
+
404
+ # Used in BufferExplorer
405
+ class BufferEntry < Entry
406
+ attr_accessor :full_name, :vim_buffer
407
+ def initialize(vim_buffer)
408
+ @full_name = vim_buffer.name
409
+ @vim_buffer = vim_buffer
410
+ @name = "::UNSET::"
411
+ @current_score = 0.0
412
+ end
413
+ end
414
+
415
+ class LustyExplorer
416
+ public
417
+ def initialize
418
+ @settings = SavedSettings.new
419
+ @displayer = Displayer.new title()
420
+ @prompt = nil
421
+ @ordered_matching_entries = []
422
+ @running = false
423
+ end
424
+
425
+ def run
426
+ return if @running
427
+
428
+ if $PROFILING
429
+ RubyProf.measure_mode = RubyProf::WALL_TIME
430
+ end
431
+
432
+ @settings.save
433
+ @running = true
434
+ @calling_window = $curwin
435
+ @saved_alternate_bufnum = if VIM::nonzero? eva("expand('#') == ''")
436
+ nil
437
+ else
438
+ eva("bufnr(expand('#'))")
439
+ end
440
+ @selected_index = 0
441
+ create_explorer_window()
442
+ refresh(:full)
443
+ end
444
+
445
+ def key_pressed()
446
+ # Grab argument from the Vim function.
447
+ i = eva("a:code_arg").to_i
448
+ refresh_mode = :full
449
+
450
+ case i
451
+ when 32..126 # Printable characters
452
+ c = i.chr
453
+ @prompt.add! c
454
+ @selected_index = 0
455
+ when 8 # Backspace/Del/C-h
456
+ @prompt.backspace!
457
+ @selected_index = 0
458
+ when 9, 13 # Tab and Enter
459
+ choose(:current_tab)
460
+ @selected_index = 0
461
+ when 23 # C-w (delete 1 dir backward)
462
+ @prompt.up_one_dir!
463
+ @selected_index = 0
464
+ when 14 # C-n (select next)
465
+ @selected_index = \
466
+ (@selected_index + 1) % @ordered_matching_entries.size
467
+ refresh_mode = :no_recompute
468
+ when 16 # C-p (select previous)
469
+ @selected_index = \
470
+ (@selected_index - 1) % @ordered_matching_entries.size
471
+ refresh_mode = :no_recompute
472
+ when 20 # C-t choose in new tab
473
+ choose(:new_tab)
474
+ @selected_index = 0
475
+ when 21 # C-u clear prompt
476
+ @prompt.clear!
477
+ @selected_index = 0
478
+ end
479
+
480
+ refresh(refresh_mode)
481
+ end
482
+
483
+ def cancel
484
+ if @running
485
+ cleanup()
486
+ # fix alternate file
487
+ if @saved_alternate_bufnum
488
+ cur = $curbuf
489
+ exe "silent b #{@saved_alternate_bufnum}"
490
+ exe "silent b #{cur.number}"
491
+ end
492
+
493
+ if $PROFILING
494
+ #outfile = File.new('rbprof.txt', 'a')
495
+ #RubyProf::CallTreePrinter.new(RubyProf.stop).print(outfile)
496
+ outfile = File.new('rbprof.html', 'a')
497
+ RubyProf::GraphHtmlPrinter.new(RubyProf.stop).print(outfile)
498
+ end
499
+ end
500
+ end
501
+
502
+ private
503
+ def refresh(mode)
504
+ return if not @running
505
+
506
+ if mode == :full
507
+ @ordered_matching_entries = compute_ordered_matching_entries()
508
+ end
509
+
510
+ on_refresh()
511
+ highlight_selected_index()
512
+ @displayer.print @ordered_matching_entries.map { |x| x.name }
513
+ @prompt.print
514
+ end
515
+
516
+ def create_explorer_window
517
+
518
+ @displayer.create
519
+
520
+ # Setup key mappings to reroute user input.
521
+
522
+ # Non-special printable characters.
523
+ printables = '/!"#$%&\'()*+,-.0123456789:<=>?#@"' \
524
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \
525
+ '[]^_`abcdefghijklmnopqrstuvwxyz{}~'
526
+
527
+ map_command = "noremap <silent> <buffer> "
528
+
529
+ printables.each_byte do |b|
530
+ exe "#{map_command} <Char-#{b}> :call #{self.class}KeyPressed(#{b})<CR>"
531
+ end
532
+
533
+ # Special characters
534
+ exe "#{map_command} <Tab> :call #{self.class}KeyPressed(9)<CR>"
535
+ exe "#{map_command} <Bslash> :call #{self.class}KeyPressed(92)<CR>"
536
+ exe "#{map_command} <Space> :call #{self.class}KeyPressed(32)<CR>"
537
+ exe "#{map_command} \026| :call #{self.class}KeyPressed(124)<CR>"
538
+
539
+ exe "#{map_command} <BS> :call #{self.class}KeyPressed(8)<CR>"
540
+ exe "#{map_command} <Del> :call #{self.class}KeyPressed(8)<CR>"
541
+ exe "#{map_command} <C-h> :call #{self.class}KeyPressed(8)<CR>"
542
+
543
+ exe "#{map_command} <CR> :call #{self.class}KeyPressed(13)<CR>"
544
+ exe "#{map_command} <S-CR> :call #{self.class}KeyPressed(10)<CR>"
545
+ exe "#{map_command} <C-a> :call #{self.class}KeyPressed(1)<CR>"
546
+
547
+ exe "#{map_command} <Esc> :call #{self.class}Cancel()<CR>"
548
+ exe "#{map_command} <C-c> :call #{self.class}Cancel()<CR>"
549
+ exe "#{map_command} <C-g> :call #{self.class}Cancel()<CR>"
550
+
551
+ exe "#{map_command} <C-w> :call #{self.class}KeyPressed(23)<CR>"
552
+ exe "#{map_command} <C-n> :call #{self.class}KeyPressed(14)<CR>"
553
+ exe "#{map_command} <C-p> :call #{self.class}KeyPressed(16)<CR>"
554
+ exe "#{map_command} <C-t> :call #{self.class}KeyPressed(20)<CR>"
555
+ exe "#{map_command} <C-e> :call #{self.class}KeyPressed(5)<CR>"
556
+ exe "#{map_command} <C-r> :call #{self.class}KeyPressed(18)<CR>"
557
+ exe "#{map_command} <C-u> :call #{self.class}KeyPressed(21)<CR>"
558
+ end
559
+
560
+ def highlight_selected_index
561
+ return unless VIM::has_syntax?
562
+
563
+ entry = @ordered_matching_entries[@selected_index]
564
+ return if entry.nil?
565
+
566
+ exe "syn clear LustyExpSelected"
567
+ exe "syn match LustyExpSelected " \
568
+ "\"#{Displayer.vim_match_string(entry.name, false)}\" "
569
+ end
570
+
571
+ def compute_ordered_matching_entries
572
+ abbrev = current_abbreviation()
573
+ unordered = matching_entries()
574
+
575
+ # Sort alphabetically if there's just a dot or we have no abbreviation,
576
+ # otherwise it just looks weird.
577
+ if abbrev.length == 0 or abbrev == '.'
578
+ unordered.sort! { |x, y| x.name <=> y.name }
579
+ else
580
+ # Sort by score.
581
+ unordered.sort! { |x, y| y.current_score <=> x.current_score }
582
+ end
583
+ end
584
+
585
+ def matching_entries
586
+ all_entries().select { |x|
587
+ x.current_score = LiquidMetal.score(x.name, current_abbreviation())
588
+ x.current_score != 0.0
589
+ }
590
+ end
591
+
592
+ def choose(open_mode)
593
+ entry = @ordered_matching_entries[@selected_index]
594
+ return if entry.nil?
595
+ @selected_index = 0
596
+ open_entry(entry, open_mode)
597
+ end
598
+
599
+ def cleanup
600
+ @displayer.close
601
+ Window.select @calling_window
602
+ @settings.restore
603
+ @running = false
604
+ msg ""
605
+ assert(@calling_window == $curwin)
606
+ end
607
+ end
608
+
609
+
610
+ class BufferExplorer < LustyExplorer
611
+ public
612
+ def initialize
613
+ super
614
+ @prompt = Prompt.new
615
+ @buffer_entries = []
616
+ end
617
+
618
+ def run
619
+ unless @running
620
+ @prompt.clear!
621
+ @curbuf_at_start = VIM::Buffer.current
622
+ fill_buffer_entries()
623
+ super
624
+ end
625
+ end
626
+
627
+ private
628
+ def title
629
+ '[LustyExplorer-Buffers]'
630
+ end
631
+
632
+ def curbuf_match_string
633
+ curbuf = @buffer_entries.find { |x| x.vim_buffer == @curbuf_at_start }
634
+ if curbuf
635
+ Displayer.vim_match_string(curbuf.name, @prompt.insensitive?)
636
+ else
637
+ ""
638
+ end
639
+ end
640
+
641
+ def on_refresh
642
+ # Highlighting for the current buffer name.
643
+ if VIM::has_syntax?
644
+ exe 'syn clear LustyExpCurrentBuffer'
645
+ exe "syn match LustyExpCurrentBuffer \"#{curbuf_match_string()}\" " \
646
+ 'contains=LustyExpModified'
647
+ end
648
+ end
649
+
650
+ def common_prefix(entries)
651
+ prefix = entries[0].full_name
652
+ entries.each do |entry|
653
+ full_name = entry.full_name
654
+ for i in 0...prefix.length
655
+ if full_name.length <= i or prefix[i] != full_name[i]
656
+ prefix = prefix[0...i]
657
+ prefix = prefix[0..(prefix.rindex('/') or -1)]
658
+ break
659
+ end
660
+ end
661
+ end
662
+ return prefix
663
+ end
664
+
665
+ def fill_buffer_entries
666
+ @buffer_entries.clear
667
+ (0..VIM::Buffer.count-1).each do |i|
668
+ @buffer_entries << BufferEntry.new(VIM::Buffer[i])
669
+ end
670
+
671
+ # Shorten each buffer name by removing all path elements which are not
672
+ # needed to differentiate a given name from other names. This usually
673
+ # results in only the basename shown, but if several buffers of the
674
+ # same basename are opened, there will be more.
675
+
676
+ # Group the buffers by common basename
677
+ common_base = Hash.new { |hash, k| hash[k] = [] }
678
+ @buffer_entries.each do |entry|
679
+ if entry.full_name
680
+ basename = Pathname.new(entry.full_name).basename.to_s
681
+ common_base[basename] << entry
682
+ end
683
+ end
684
+
685
+ # Determine the longest common prefix for each basename group.
686
+ basename_to_prefix = {}
687
+ common_base.each do |base, entries|
688
+ if entries.length > 1
689
+ basename_to_prefix[base] = common_prefix(entries)
690
+ end
691
+ end
692
+
693
+ # Compute shortened buffer names by removing prefix, if possible.
694
+ @buffer_entries.each do |entry|
695
+ full_name = entry.full_name
696
+
697
+ short_name = if full_name.nil?
698
+ '[No Name]'
699
+ elsif full_name.starts_with?("scp://")
700
+ full_name
701
+ else
702
+ base = Pathname.new(full_name).basename.to_s
703
+ prefix = basename_to_prefix[base]
704
+
705
+ prefix ? full_name[prefix.length..-1] \
706
+ : base
707
+ end
708
+
709
+ # Disabled: show buffer number next to name
710
+ #short_name += ' ' + buffer.number.to_s
711
+
712
+ # Show modification indicator
713
+ short_name += entry.vim_buffer.modified? ? " [+]" : ""
714
+
715
+ entry.name = short_name
716
+ end
717
+ end
718
+
719
+ def current_abbreviation
720
+ @prompt.input
721
+ end
722
+
723
+ def all_entries
724
+ @buffer_entries
725
+ end
726
+
727
+ def open_entry(entry, open_mode)
728
+ cleanup()
729
+ assert($curwin == @calling_window)
730
+
731
+ number = entry.vim_buffer.number
732
+ assert(number)
733
+
734
+ cmd = case open_mode
735
+ when :current_tab
736
+ "b"
737
+ when :new_tab
738
+ # For some reason just using tabe or e gives an error when
739
+ # the alternate-file isn't set.
740
+ "tab split | b"
741
+ else
742
+ assert(false, "bad open mode")
743
+ end
744
+
745
+ exe "silent #{cmd} #{number}"
746
+ end
747
+ end
748
+
749
+ def time
750
+ if $PROFILING
751
+ RubyProf.resume
752
+ end
753
+ begin
754
+ yield
755
+ rescue Exception => e
756
+ puts e
757
+ puts e.backtrace
758
+ end
759
+ if $PROFILING
760
+ RubyProf.pause
761
+ end
762
+ end
763
+
764
+ class FilesystemExplorer < LustyExplorer
765
+ public
766
+ def initialize
767
+ super
768
+ @prompt = FilesystemPrompt.new
769
+ @memoized_entries = {}
770
+ end
771
+
772
+ def run
773
+ FileMasks.create_glob_masks()
774
+ @vim_swaps = VimSwaps.new
775
+ super
776
+ end
777
+
778
+ def run_from_here
779
+ start_path = if $curbuf.name.nil?
780
+ VIM::getcwd()
781
+ else
782
+ eva("expand('%:p:h')")
783
+ end
784
+
785
+ @prompt.set!(start_path + File::SEPARATOR)
786
+ run()
787
+ end
788
+
789
+ def run_from_wd
790
+ @prompt.set!(VIM::getcwd() + File::SEPARATOR)
791
+ run()
792
+ end
793
+
794
+ def key_pressed()
795
+ time do
796
+ i = eva("a:code_arg").to_i
797
+
798
+ case i
799
+ when 1, 10 # <C-a>, <Shift-Enter>
800
+ cleanup()
801
+ # Open all non-directories currently in view.
802
+ @ordered_matching_entries.each do |e|
803
+ path_str = \
804
+ if @prompt.at_dir?
805
+ @prompt.input + e.name
806
+ else
807
+ @prompt.dirname + File::SEPARATOR + e.name
808
+ end
809
+
810
+ load_file(path_str, :current_tab) unless File.directory?(path_str)
811
+ end
812
+ when 5 # <C-e> edit file, create it if necessary
813
+ if not @prompt.at_dir?
814
+ cleanup()
815
+ # Force a reread of this directory so that the new file will
816
+ # show up (as long as it is saved before the next run).
817
+ @memoized_entries.delete(view_path())
818
+ load_file(@prompt.input, :current_tab)
819
+ end
820
+ when 18 # <C-r> refresh
821
+ @memoized_entries.delete(view_path())
822
+ refresh(:full)
823
+ else
824
+ super
825
+ end
826
+ end
827
+ end
828
+
829
+ private
830
+ def title
831
+ '[LustyExplorer-Files]'
832
+ end
833
+
834
+ def on_refresh
835
+ if VIM::has_syntax?
836
+ exe 'syn clear LustyExpFileWithSwap'
837
+
838
+ view = view_path()
839
+ @vim_swaps.file_names.each do |file_with_swap|
840
+ if file_with_swap.dirname == view
841
+ base = file_with_swap.basename
842
+ match_str = Displayer.vim_match_string(base.to_s, false)
843
+ exe "syn match LustyExpFileWithSwap \"#{match_str}\""
844
+ end
845
+ end
846
+ end
847
+
848
+ # TODO: restore highlighting for open buffers?
849
+ end
850
+
851
+ def current_abbreviation
852
+ if @prompt.at_dir?
853
+ ""
854
+ else
855
+ File.basename(@prompt.input)
856
+ end
857
+ end
858
+
859
+ def view_path
860
+ input = @prompt.input
861
+
862
+ path = \
863
+ if @prompt.at_dir? and \
864
+ input.length > 1 # Not root
865
+ # The last element in the path is a directory + '/' and we want to
866
+ # see what's in it instead of what's in its parent directory.
867
+
868
+ Pathname.new(input[0..-2]) # Canonicalize by removing trailing '/'
869
+ else
870
+ Pathname.new(input).dirname
871
+ end
872
+
873
+ return path
874
+ end
875
+
876
+ def all_entries
877
+ view = view_path()
878
+
879
+ if not view.exist?
880
+ return []
881
+ elsif not view.readable?
882
+ # TODO: show "-- PERMISSION DENIED --"
883
+ return []
884
+ end
885
+
886
+ unless @memoized_entries.has_key?(view)
887
+ # Generate an array of the files
888
+ entries = []
889
+ view.each_entry do |file|
890
+ name = file.basename.to_s
891
+ next if name == "." # Skip pwd
892
+ next if name == ".." and lusty_option_set?("AlwaysShowDotFiles")
893
+
894
+ # Hide masked files.
895
+ next if FileMasks.masked?(name)
896
+
897
+ if (view + file).directory?
898
+ # ^^ bug in Pathname.each_entry -- block variable has no dir.
899
+ name += File::SEPARATOR
900
+ end
901
+ entries << Entry.new(name)
902
+ end
903
+ @memoized_entries[view] = entries
904
+ end
905
+
906
+ all = @memoized_entries[view]
907
+
908
+ if lusty_option_set?("AlwaysShowDotFiles") or \
909
+ current_abbreviation()[0] == '.'[0]
910
+ all
911
+ else
912
+ # Filter out dotfiles if the current abbreviation doesn't start with
913
+ # '.'.
914
+ all.select { |x| x.name[0] != '.'[0] }
915
+ end
916
+ end
917
+
918
+ def open_entry(entry, open_mode)
919
+ path = view_path() + entry.name
920
+
921
+ if File.directory?(path)
922
+ # Recurse into the directory instead of opening it.
923
+ @prompt.set!(path.to_s)
924
+ elsif entry.name.include?(File::SEPARATOR)
925
+ # Don't open a fake file/buffer with "/" in its name.
926
+ return
927
+ else
928
+ cleanup()
929
+ load_file(path.to_s, open_mode)
930
+ end
931
+ end
932
+
933
+ def load_file(path_str, open_mode)
934
+ assert($curwin == @calling_window)
935
+ # Escape for Vim and remove leading ./ for files in pwd.
936
+ escaped = VIM::filename_escape(path_str).sub(/^\.\//,"")
937
+ sanitized = eva "fnamemodify('#{escaped}', ':.')"
938
+ cmd = case open_mode
939
+ when :current_tab
940
+ "e"
941
+ when :new_tab
942
+ "tabe"
943
+ else
944
+ assert(false, "bad open mode")
945
+ end
946
+
947
+ exe "silent #{cmd} #{sanitized}"
948
+ end
949
+ end
950
+
951
+
952
+ class Prompt
953
+ private
954
+ @@PROMPT = ">> "
955
+
956
+ public
957
+ def initialize
958
+ clear!
959
+ end
960
+
961
+ def clear!
962
+ @input = ""
963
+ end
964
+
965
+ def print
966
+ pretty_msg("Comment", @@PROMPT, "None", @input, "Underlined", " ")
967
+ end
968
+
969
+ def set!(s)
970
+ @input = s
971
+ end
972
+
973
+ def input
974
+ @input
975
+ end
976
+
977
+ def insensitive?
978
+ @input == @input.downcase
979
+ end
980
+
981
+ def ends_with?(c)
982
+ @input.ends_with? c
983
+ end
984
+
985
+ def add!(s)
986
+ @input += s
987
+ end
988
+
989
+ def backspace!
990
+ @input.chop!
991
+ end
992
+
993
+ def up_one_dir!
994
+ @input.chop!
995
+ while !@input.empty? and @input[-1] != '/'[0]
996
+ @input.chop!
997
+ end
998
+ end
999
+ end
1000
+
1001
+ class FilesystemPrompt < Prompt
1002
+
1003
+ def initialize
1004
+ super
1005
+ @memoized = nil
1006
+ @dirty = true
1007
+ end
1008
+
1009
+ def clear!
1010
+ @dirty = true
1011
+ super
1012
+ end
1013
+
1014
+ def set!(s)
1015
+ @dirty = true
1016
+ # On Windows, Vim will return paths with a '\' separator, but
1017
+ # we want to use '/'.
1018
+ super(s.gsub('\\', '/'))
1019
+ end
1020
+
1021
+ def backspace!
1022
+ @dirty = true
1023
+ super
1024
+ end
1025
+
1026
+ def up_one_dir!
1027
+ @dirty = true
1028
+ super
1029
+ end
1030
+
1031
+ def at_dir?
1032
+ # We have not typed anything yet or have just typed the final '/' on a
1033
+ # directory name in pwd. This check is interspersed throughout
1034
+ # FilesystemExplorer because of the conventions of basename and dirname.
1035
+ input().empty? or input().ends_with?(File::SEPARATOR)
1036
+ # Don't think the File.directory? call is necessary, but leaving this
1037
+ # here as a reminder.
1038
+ #(File.directory?(input()) and input().ends_with?(File::SEPARATOR))
1039
+ end
1040
+
1041
+ def insensitive?
1042
+ at_dir? or (basename() == basename().downcase)
1043
+ end
1044
+
1045
+ def add!(s)
1046
+ # Assumption: add!() will only receive enough chars at a time to complete
1047
+ # a single directory level, e.g. foo/, not foo/bar/
1048
+
1049
+ @input += s
1050
+ @dirty = true
1051
+ end
1052
+
1053
+ def input
1054
+ if @dirty
1055
+ @memoized = variable_expansion(File.simplify_path(@input))
1056
+ @dirty = false
1057
+ end
1058
+
1059
+ @memoized
1060
+ end
1061
+
1062
+ def basename
1063
+ File.basename input()
1064
+ end
1065
+
1066
+ def dirname
1067
+ File.dirname input()
1068
+ end
1069
+
1070
+ private
1071
+ def variable_expansion (input_str)
1072
+ # FIXME does this still work?
1073
+ strings = input_str.split('$', -1)
1074
+ return "" if strings.nil? or strings.length == 0
1075
+
1076
+ first = strings.shift
1077
+
1078
+ # Try to expand each instance of $<word>.
1079
+ strings.inject(first) { |str, s|
1080
+ if s =~ /^(\w+)/ and ENV[$1]
1081
+ str + s.sub($1, ENV[$1])
1082
+ else
1083
+ str + "$" + s
1084
+ end
1085
+ }
1086
+ end
1087
+ end
1088
+
1089
+ # Simplify switching between windows.
1090
+ class Window
1091
+ def Window.select(window)
1092
+ return true if window == $curwin
1093
+
1094
+ start = $curwin
1095
+
1096
+ # Try to select the given window.
1097
+ begin
1098
+ exe "wincmd w"
1099
+ end while ($curwin != window) and ($curwin != start)
1100
+
1101
+ if $curwin == window
1102
+ return true
1103
+ else
1104
+ # Failed -- re-select the starting window.
1105
+ exe("wincmd w") while $curwin != start
1106
+ pretty_msg("ErrorMsg", "Cannot find the correct window!")
1107
+ return false
1108
+ end
1109
+ end
1110
+ end
1111
+
1112
+ # Save and restore settings when creating the explorer buffer.
1113
+ class SavedSettings
1114
+ def initialize
1115
+ save()
1116
+ end
1117
+
1118
+ def save
1119
+ @timeoutlen = eva "&timeoutlen"
1120
+
1121
+ @splitbelow = VIM::nonzero? eva("&splitbelow")
1122
+ @insertmode = VIM::nonzero? eva("&insertmode")
1123
+ @showcmd = VIM::nonzero? eva("&showcmd")
1124
+ @list = VIM::nonzero? eva("&list")
1125
+
1126
+ @report = eva "&report"
1127
+ @sidescroll = eva "&sidescroll"
1128
+ @sidescrolloff = eva "&sidescrolloff"
1129
+ end
1130
+
1131
+ def restore
1132
+ set "timeoutlen=#{@timeoutlen}"
1133
+
1134
+ if @splitbelow
1135
+ set "splitbelow"
1136
+ else
1137
+ set "nosplitbelow"
1138
+ end
1139
+
1140
+ if @insertmode
1141
+ set "insertmode"
1142
+ else
1143
+ set "noinsertmode"
1144
+ end
1145
+
1146
+ if @showcmd
1147
+ set "showcmd"
1148
+ else
1149
+ set "noshowcmd"
1150
+ end
1151
+
1152
+ if @list
1153
+ set "list"
1154
+ else
1155
+ set "nolist"
1156
+ end
1157
+
1158
+ exe "set report=#{@report}"
1159
+ exe "set sidescroll=#{@sidescroll}"
1160
+ exe "set sidescrolloff=#{@sidescrolloff}"
1161
+ end
1162
+ end
1163
+
1164
+ # Manage the explorer buffer.
1165
+ class Displayer
1166
+ private
1167
+ @@COLUMN_SEPARATOR = " "
1168
+ @@NO_ENTRIES_STRING = "-- NO ENTRIES --"
1169
+ @@TRUNCATED_STRING = "-- TRUNCATED --"
1170
+
1171
+ public
1172
+ def Displayer.vim_match_string(s, case_insensitive)
1173
+ # Create a match regex string for the given s. This is for a Vim regex,
1174
+ # not for a Ruby regex.
1175
+
1176
+ str = '\%(^\|' + @@COLUMN_SEPARATOR + '\)' \
1177
+ '\zs' + VIM::regex_escape(s) + '\%( \[+\]\)\?' + '\ze' \
1178
+ '\%(\s*$\|' + @@COLUMN_SEPARATOR + '\)'
1179
+
1180
+ str += '\c' if case_insensitive
1181
+
1182
+ return str
1183
+ end
1184
+
1185
+ def initialize(title)
1186
+ @title = title
1187
+ @window = nil
1188
+ @buffer = nil
1189
+ end
1190
+
1191
+ def create
1192
+ # Make a window for the displayer and move there.
1193
+ exe "silent! botright split #{@title}"
1194
+
1195
+ @window = $curwin
1196
+ @buffer = $curbuf
1197
+
1198
+ # Displayer buffer is special.
1199
+ exe "setlocal bufhidden=delete"
1200
+ exe "setlocal buftype=nofile"
1201
+ exe "setlocal nomodifiable"
1202
+ exe "setlocal noswapfile"
1203
+ exe "setlocal nowrap"
1204
+ exe "setlocal nonumber"
1205
+ exe "setlocal foldcolumn=0"
1206
+ exe "setlocal nocursorline"
1207
+ exe "setlocal nospell"
1208
+ exe "setlocal nobuflisted"
1209
+ exe "setlocal textwidth=0"
1210
+
1211
+ # (Update SavedSettings if adding to below.)
1212
+ set "timeoutlen=0"
1213
+ set "noinsertmode"
1214
+ set "noshowcmd"
1215
+ set "nolist"
1216
+ set "report=9999"
1217
+ set "sidescroll=0"
1218
+ set "sidescrolloff=0"
1219
+
1220
+ # TODO -- cpoptions?
1221
+
1222
+ if VIM::has_syntax?
1223
+ exe 'syn match LustyExpSlash "/" contained'
1224
+ exe 'syn match LustyExpDir "\zs\%(\S\+ \)*\S\+/\ze" ' \
1225
+ 'contains=LustyExpSlash'
1226
+
1227
+ exe 'syn match LustyExpModified " \[+\]"'
1228
+
1229
+ exe 'syn match LustyExpNoEntries "\%^\s*' \
1230
+ "#{@@NO_ENTRIES_STRING}" \
1231
+ '\s*\%$"'
1232
+
1233
+ exe 'syn match LustyExpTruncated "^\s*' \
1234
+ "#{@@TRUNCATED_STRING}" \
1235
+ '\s*$"'
1236
+
1237
+ exe 'highlight link LustyExpDir Directory'
1238
+ exe 'highlight link LustyExpSlash Function'
1239
+ exe 'highlight link LustyExpSelected Type'
1240
+ exe 'highlight link LustyExpModified Special'
1241
+ exe 'highlight link LustyExpCurrentBuffer Constant'
1242
+ exe 'highlight link LustyExpOpenedFile PreProc'
1243
+ exe 'highlight link LustyExpFileWithSwap WarningMsg'
1244
+ exe 'highlight link LustyExpNoEntries ErrorMsg'
1245
+ exe 'highlight link LustyExpTruncated Visual'
1246
+ end
1247
+ end
1248
+
1249
+ def print(strings)
1250
+ Window.select(@window) || return
1251
+
1252
+ if strings.empty?
1253
+ print_no_entries()
1254
+ return
1255
+ end
1256
+
1257
+ # Perhaps truncate the results to just over the upper bound of
1258
+ # displayable strings. This isn't exact, but it's close enough.
1259
+ max = VIM::lines * (VIM::columns / (1 + @@COLUMN_SEPARATOR.length))
1260
+ if strings.length > max
1261
+ strings.slice!(max, strings.length - max)
1262
+ end
1263
+
1264
+ # Get a high upper bound on the number of columns to display to optimize
1265
+ # the following algorithm a little.
1266
+ col_count = column_count_upper_bound(strings)
1267
+
1268
+ # Figure out the actual number of columns to use (yuck)
1269
+ cols = nil
1270
+ widths = nil
1271
+ while col_count > 1 do
1272
+
1273
+ cols = columnize(strings, col_count);
1274
+
1275
+ widths = cols.map { |col|
1276
+ col.max { |a, b| a.length <=> b.length }.length
1277
+ }
1278
+
1279
+ full_width = widths.inject { |sum, n| sum + n }
1280
+ full_width += @@COLUMN_SEPARATOR.length * (col_count - 1)
1281
+
1282
+ if full_width <= $curwin.width
1283
+ break
1284
+ end
1285
+
1286
+ col_count -= 1
1287
+ end
1288
+
1289
+ if col_count <= 1
1290
+ cols = [strings]
1291
+ widths = [0]
1292
+ end
1293
+
1294
+ print_columns(cols, widths)
1295
+ end
1296
+
1297
+ def close
1298
+ # Only wipe the buffer if we're *sure* it's the explorer.
1299
+ if Window.select @window and \
1300
+ $curbuf == @buffer and \
1301
+ $curbuf.name =~ /#{Regexp.escape(@title)}$/
1302
+ exe "bwipeout!"
1303
+ @window = nil
1304
+ @buffer = nil
1305
+ end
1306
+ end
1307
+
1308
+ private
1309
+ def print_columns(cols, widths)
1310
+ unlock_and_clear()
1311
+
1312
+ # Set the height to the height of the longest column.
1313
+ $curwin.height = cols.max { |a, b| a.length <=> b.length }.length
1314
+
1315
+ (0..$curwin.height-1).each do |i|
1316
+
1317
+ string = ""
1318
+ (0..cols.length-1).each do |j|
1319
+ break if cols[j][i].nil?
1320
+ string += cols[j][i]
1321
+ string += " " * [(widths[j] - cols[j][i].length), 0].max
1322
+ string += @@COLUMN_SEPARATOR
1323
+ end
1324
+
1325
+ # Stretch the line to the length of the window with whitespace so that
1326
+ # we can "hide" the cursor in the corner.
1327
+ string += " " * [($curwin.width - string.length), 0].max
1328
+
1329
+ $curwin.cursor = [i+1, 1]
1330
+ $curbuf.append(i, string)
1331
+ end
1332
+
1333
+ # Check for result truncation.
1334
+ if cols[0][$curwin.height]
1335
+ # Show a truncation indicator.
1336
+ $curbuf.delete($curbuf.count - 1)
1337
+ $curwin.cursor = [$curbuf.count, 1]
1338
+ $curbuf.append($curbuf.count - 1, \
1339
+ @@TRUNCATED_STRING.center($curwin.width, " "))
1340
+ end
1341
+
1342
+ # There's a blank line at the end of the buffer because of how
1343
+ # VIM::Buffer.append works.
1344
+ $curbuf.delete $curbuf.count
1345
+ lock()
1346
+ end
1347
+
1348
+ def print_no_entries
1349
+ unlock_and_clear()
1350
+ $curwin.height = 1
1351
+
1352
+ $curbuf[1] = @@NO_ENTRIES_STRING.center($curwin.width, " ")
1353
+ lock()
1354
+ end
1355
+
1356
+ def unlock_and_clear
1357
+ exe "setlocal modifiable"
1358
+
1359
+ # Clear the explorer (black hole register)
1360
+ exe "silent %d _"
1361
+ end
1362
+
1363
+ def lock
1364
+ exe "setlocal nomodifiable"
1365
+
1366
+ # Hide the cursor
1367
+ exe "normal! Gg$"
1368
+ end
1369
+
1370
+ # Get a starting upper bound on the number of columns
1371
+ def column_count_upper_bound(strings)
1372
+ column_count = 0
1373
+ length = 0
1374
+
1375
+ sorted_by_length = strings.sort {|x, y| x.length <=> y.length }
1376
+
1377
+ sorted_by_length.each do |e|
1378
+ length += e.length
1379
+ break unless length < $curwin.width
1380
+
1381
+ column_count += 1
1382
+ length += @@COLUMN_SEPARATOR.length
1383
+ end
1384
+
1385
+ return column_count
1386
+ end
1387
+
1388
+ def columnize(strings, n_cols)
1389
+ n_rows = (strings.length.to_f / n_cols).ceil
1390
+
1391
+ # Break the array into sub arrays representing columns
1392
+ cols = []
1393
+ 0.step(strings.size-1, n_rows) do |i|
1394
+ cols << strings[i..(i + n_rows - 1)]
1395
+ end
1396
+ return cols
1397
+ end
1398
+ end
1399
+
1400
+
1401
+ class FileMasks
1402
+ private
1403
+ @@glob_masks = []
1404
+
1405
+ public
1406
+ def FileMasks.create_glob_masks
1407
+ @@glob_masks = \
1408
+ if VIM::nonzero? eva('exists("g:LustyExplorerFileMasks")')
1409
+ # Note: this variable deprecated.
1410
+ eva("g:LustyExplorerFileMasks").split(',')
1411
+ elsif VIM::nonzero? eva('exists("&wildignore")')
1412
+ eva("&wildignore").split(',')
1413
+ else
1414
+ []
1415
+ end
1416
+ end
1417
+
1418
+ def FileMasks.masked?(str)
1419
+ @@glob_masks.each do |mask|
1420
+ return true if File.fnmatch(mask, str)
1421
+ end
1422
+
1423
+ return false
1424
+ end
1425
+ end
1426
+
1427
+ class VimSwaps
1428
+ def initialize
1429
+ if VIM::has_syntax?
1430
+ # FIXME: vvv disabled
1431
+ # @vim_r = IO.popen("vim -r 2>&1")
1432
+ # @files_with_swaps = nil
1433
+ @files_with_swaps = []
1434
+ else
1435
+ @files_with_swaps = []
1436
+ end
1437
+ end
1438
+
1439
+ def file_names
1440
+ if @files_with_swaps.nil?
1441
+ if @vim_r.ready_for_read?
1442
+ @files_with_swaps = []
1443
+ @vim_r.each_line do |line|
1444
+ if line =~ /^ +file name: (.*)$/
1445
+ file = $1.chomp
1446
+ @files_with_swaps << Pathname.new(File.simplify_path(file))
1447
+ end
1448
+ end
1449
+ else
1450
+ return []
1451
+ end
1452
+ end
1453
+
1454
+ @files_with_swaps
1455
+ end
1456
+ end
1457
+
1458
+
1459
+ def d(s)
1460
+ # (Debug print)
1461
+ $stderr.puts s
1462
+ end
1463
+
1464
+ # Simple mappings to decrease typing.
1465
+ def exe(s)
1466
+ VIM.command s
1467
+ end
1468
+
1469
+ def eva(s)
1470
+ VIM.evaluate s
1471
+ end
1472
+
1473
+ def set(s)
1474
+ VIM.set_option s
1475
+ end
1476
+
1477
+ def msg(s)
1478
+ VIM.message s
1479
+ end
1480
+
1481
+ def pretty_msg(*rest)
1482
+ return if rest.length == 0
1483
+ return if rest.length % 2 != 0
1484
+
1485
+ exe "redraw" # see :help echo-redraw
1486
+ i = 0
1487
+ while i < rest.length do
1488
+ exe "echohl #{rest[i]}"
1489
+ exe "echon '#{rest[i+1]}'"
1490
+ i += 2
1491
+ end
1492
+
1493
+ exe 'echohl None'
1494
+ end
1495
+
1496
+ class AssertionError < StandardError
1497
+ end
1498
+
1499
+ def assert(condition, message = 'assertion failure')
1500
+ raise AssertionError.new(message) unless condition
1501
+ end
1502
+
1503
+
1504
+ $buffer_explorer = BufferExplorer.new
1505
+ $filesystem_explorer = FilesystemExplorer.new
1506
+
1507
+ EOF
1508
+
1509
+ " vim: set sts=2 sw=2: