cygnus 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cygnus/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cygnus"
8
+ spec.version = Cygnus::VERSION
9
+ spec.authors = ["Rahul Kumar"]
10
+ spec.email = ["sentinel1879@gmail.com"]
11
+ spec.description = %q{best code browser eva}
12
+ spec.summary = %q{the finest code browser with exactly what you need and no more}
13
+ spec.homepage = "https://github.com/rkumar/cygnus"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,1520 @@
1
+ require "cygnus/version"
2
+ require 'rbcurse/core/util/app'
3
+
4
+ module Cygnus
5
+ # Your code goes here...
6
+ # mmm what code ?
7
+ # http://www.ruby-doc.org/stdlib-1.9.3/libdoc/shellwords/rdoc/Shellwords.html
8
+ require 'shellwords'
9
+ require 'fileutils'
10
+ CONFIG_FILE="~/.lyrainfo"
11
+
12
+
13
+ $selected_files = Array.new
14
+ $bookmarks = {}
15
+ $mode = nil
16
+ $glines=%x(tput lines).to_i
17
+ $gcols=%x(tput cols).to_i
18
+ # grows depends on size of textpad @cols, not screen size, since this is no longer cli
19
+ $grows = $glines - 2
20
+ $pagesize = 60
21
+ $gviscols = 3
22
+ $pagesize = $grows * $gviscols
23
+ $stact = 0
24
+ $editor_mode = true
25
+ $enhanced_mode = true
26
+ $visual_block_start = nil
27
+ $pager_command = {
28
+ :text => 'most',
29
+ :image => 'open',
30
+ :zip => 'tar ztvf %% | most',
31
+ :unknown => 'open'
32
+ }
33
+ $dir_position = {}
34
+ ## CONSTANTS
35
+ GMARK='*'
36
+ CURMARK='>'
37
+ MSCROLL = 10
38
+ SPACE=" "
39
+ #CLEAR = "\e[0m"
40
+ #BOLD = "\e[1m"
41
+ #BOLD_OFF = "\e[22m"
42
+ #RED = "\e[31m"
43
+ #ON_RED = "\e[41m"
44
+ #GREEN = "\e[32m"
45
+ #YELLOW = "\e[33m"
46
+ #BLUE = "\e[1;34m"
47
+ #
48
+ ON_BLUE = "\e[44m"
49
+ #REVERSE = "\e[7m"
50
+ CURSOR_COLOR = ""
51
+ CLEAR = ""
52
+ $patt=nil
53
+ $ignorecase = true
54
+ $quitting = false
55
+ $modified = $writing = false
56
+ $visited_files = []
57
+ ## dir stack for popping
58
+ $visited_dirs = []
59
+ ## dirs where some work has been done, for saving and restoring
60
+ $used_dirs = []
61
+ $default_sort_order = "om"
62
+ $sorto = $default_sort_order
63
+ $viewctr = 0
64
+ $history = []
65
+ $sta = $cursor = 0
66
+ $visual_mode = false
67
+
68
+ ## main loop which calls all other programs
69
+
70
+ ## code related to long listing of files
71
+ GIGA_SIZE = 1073741824.0
72
+ MEGA_SIZE = 1048576.0
73
+ KILO_SIZE = 1024.0
74
+
75
+ # Return the file size with a readable style.
76
+ def readable_file_size(size, precision)
77
+ case
78
+ #when size == 1 : "1 B"
79
+ when size < KILO_SIZE then "%d B" % size
80
+ when size < MEGA_SIZE then "%.#{precision}f K" % (size / KILO_SIZE)
81
+ when size < GIGA_SIZE then "%.#{precision}f M" % (size / MEGA_SIZE)
82
+ else "%.#{precision}f G" % (size / GIGA_SIZE)
83
+ end
84
+ end
85
+ ## format date for file given stat
86
+ def date_format t
87
+ t.strftime "%Y/%m/%d"
88
+ end
89
+ ##
90
+ #
91
+ # print in columns
92
+ # ary - array of data
93
+ # sz - lines in one column
94
+ #
95
+ def columnate_with_indexing ary, sz
96
+ buff=Array.new
97
+ $log.warn "columnate_with_indexing got nil list " unless ary
98
+ return buff if ary.nil? || ary.size == 0
99
+
100
+ # determine width based on number of files to show
101
+ # if less than sz then 1 col and full width
102
+ #
103
+ wid = 30
104
+ ars = ary.size
105
+ ars = [$pagesize, ary.size].min
106
+ # 2 maybe for borders also
107
+ d = 0
108
+ if ars <= sz
109
+ wid = $gcols - d
110
+ else
111
+ tmp = (ars * 1.000/ sz).ceil
112
+ wid = $gcols / tmp - d
113
+ end
114
+
115
+ # ix refers to the index in the complete file list, wherease we only show 60 at a time
116
+ ix=0
117
+ while true
118
+ ## ctr refers to the index in the column
119
+ ctr=0
120
+ while ctr < sz
121
+
122
+ cur=SPACE
123
+ cur = CURMARK if ix + $sta == $cursor
124
+ f = ary[ix]
125
+
126
+ if $long_listing
127
+ begin
128
+ # one of zsh's flags adds not just / but @ * and a space, so the a FNF error comes
129
+ unless File.exist? f
130
+ last = f[-1]
131
+ if last == " " || last == "@" || last == '*'
132
+ stat = File.stat(f.chop)
133
+ end
134
+ else
135
+ stat = File.stat(f)
136
+ end
137
+ f = "%10s %s %s" % [readable_file_size(stat.size,1), date_format(stat.mtime), f]
138
+ rescue Exception => e
139
+ f = "%10s %s %s" % ["?", "??????????", f]
140
+ end
141
+ end
142
+
143
+ # be careful of modifying f or original array gets modified XXX
144
+ k = get_shortcut ix
145
+ isdir = f[-1] == "/"
146
+ fsz = f.size + k.to_s.size + 0
147
+ fsz = f.size + 1
148
+ if fsz > wid
149
+ # truncated since longer
150
+ f = f[0, wid-2]+"$ "
151
+ ## we do the coloring after trunc so ANSI escpe seq does not get get
152
+ #if ix + $sta == $cursor
153
+ #f = "#{CURSOR_COLOR}#{f}#{CLEAR}"
154
+ #end
155
+ else
156
+ ## we do the coloring before padding so the entire line does not get padded, only file name
157
+ #if ix + $sta == $cursor
158
+ #f = "#{CURSOR_COLOR}#{f}#{CLEAR}"
159
+ #end
160
+ f = f.ljust(wid)
161
+ # pad with spaces
162
+ #f << " " * (wid-fsz)
163
+ #f = f + " " * (wid-fsz)
164
+ end
165
+ # now we add the shortcut with the coloring (we need to adjust the space of the shortcut)
166
+ #
167
+ colr = "white"
168
+ colr = "blue, bold" if isdir
169
+ # this directly modified the damned index resulting in searches failing
170
+ #k << " " if k.length == 1
171
+ k = k + " " if k.length == 1
172
+
173
+ f = "#{cur}#[fg=yellow, bold]#{k}#[end] #[fg=#{colr}]#{f}#[end]"
174
+
175
+ if buff[ctr]
176
+ buff[ctr] += f
177
+ else
178
+ buff[ctr] = f
179
+ end
180
+
181
+ ctr+=1
182
+ ix+=1
183
+ break if ix >= ary.size
184
+ end
185
+ break if ix >= ary.size
186
+ end
187
+ return buff
188
+ end
189
+ ## formats the data with number, mark and details
190
+ def format ary
191
+ #buff = Array.new
192
+ buff = Array.new(ary.size)
193
+ return buff if ary.nil? || ary.size == 0
194
+
195
+ # determine width based on number of files to show
196
+ # if less than sz then 1 col and full width
197
+ #
198
+ # ix refers to the index in the complete file list, wherease we only show 60 at a time
199
+ ix=0
200
+ ctr=0
201
+ ary.each do |f|
202
+ ## ctr refers to the index in the column
203
+ ind = get_shortcut(ix)
204
+ mark=SPACE
205
+ cur=SPACE
206
+ cur = CURMARK if ix + $sta == $cursor
207
+ mark=GMARK if $selected_files.index(ary[ix])
208
+
209
+ if $long_listing
210
+ begin
211
+ unless File.exist? f
212
+ last = f[-1]
213
+ if last == " " || last == "@" || last == '*'
214
+ stat = File.stat(f.chop)
215
+ end
216
+ else
217
+ stat = File.stat(f)
218
+ end
219
+ f = "%10s %s %s" % [readable_file_size(stat.size,1), date_format(stat.mtime), f]
220
+ rescue Exception => e
221
+ f = "%10s %s %s" % ["?", "??????????", f]
222
+ end
223
+ end
224
+
225
+ s = "#{ind}#{mark}#{cur}#{f}"
226
+ # I cannot color the current line since format does the chopping
227
+ # so not only does the next lines alignment get skeweed, but also if the line is truncated
228
+ # then the color overflows.
229
+ #if ix + $sta == $cursor
230
+ #s = "#{RED}#{s}#{CLEAR}"
231
+ #end
232
+
233
+ buff[ctr] = s
234
+
235
+ ctr+=1
236
+ ix+=1
237
+ end
238
+ return buff
239
+ end
240
+ ## select file based on key pressed
241
+ def select_hint view, ch
242
+ # a to y is direct
243
+ # if z or Z take a key IF there are those many
244
+ #
245
+ ix = get_index(ch, view.size)
246
+ if ix
247
+ f = view[ix]
248
+ return unless f
249
+ $cursor = $sta + ix
250
+
251
+ if $mode == 'SEL'
252
+ toggle_select f
253
+ elsif $mode == 'COM'
254
+ run_command f
255
+ else
256
+ open_file f
257
+ end
258
+ #selectedix=ix
259
+ end
260
+ end
261
+ ## toggle selection state of file
262
+ def toggle_select f
263
+ if $selected_files.index f
264
+ $selected_files.delete f
265
+ else
266
+ $selected_files.push f
267
+ end
268
+ end
269
+
270
+ ## run command on given file/s
271
+ # Accepts command from user
272
+ # After putting readline in place of gets, pressing a C-c has a delayed effect. It goes intot
273
+ # exception bloack after executing other commands and still does not do the return !
274
+ def run_command f
275
+ files=nil
276
+ case f
277
+ when Array
278
+ # escape the contents and create a string
279
+ files = Shellwords.join(f)
280
+ when String
281
+ files = Shellwords.escape(f)
282
+ end
283
+ begin
284
+ # TODO put all this get_line stuff into field history
285
+ command = get_line "Run a command on #{files}: "
286
+ return if command.size == 0
287
+ command2 = get_line "Second part of command: "
288
+ # FIXME we may need to go into cooked mode and all that for this
289
+ # cat and most mess with the output using system
290
+ c_system "#{command} #{files} #{command2}"
291
+ rescue Exception => ex
292
+ perror "Canceled command, (#{ex}) press a key"
293
+ return
294
+ end
295
+
296
+ c_refresh
297
+ push_used_dirs Dir.pwd
298
+ end
299
+ def c_system command
300
+ w = @window || @form.window
301
+ w.hide
302
+ Ncurses.endwin
303
+ ret = system command
304
+ Ncurses.refresh
305
+ w.show
306
+ return ret
307
+ end
308
+
309
+ ## clear sort order and refresh listing, used typically if you are in some view
310
+ # such as visited dirs or files
311
+ def escape
312
+ $sorto = nil
313
+ $sorto = $default_sort_order
314
+ $viewctr = 0
315
+ $title = nil
316
+ $filterstr = "M"
317
+ visual_block_clear
318
+ c_refresh
319
+ end
320
+
321
+ ## refresh listing after some change like option change, or toggle
322
+ # I think NCurses has a refresh which when called internally results in this chap
323
+ # getting called since both are included. or maybe App or somehting has a refresh
324
+ def c_refresh
325
+ $filterstr ||= "M"
326
+ #$files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}#{$filterstr})'`.split("\n")
327
+ $patt=nil
328
+ $title = nil
329
+ display_dir
330
+ end
331
+ #
332
+ ## unselect all files
333
+ def unselect_all
334
+ $selected_files = []
335
+ $visual_mode = nil
336
+ end
337
+
338
+ ## select all files
339
+ def select_all
340
+ $selected_files = $view.dup
341
+ end
342
+
343
+ ## accept dir to goto and change to that ( can be a file too)
344
+ def goto_dir
345
+ begin
346
+ path = get_line "Enter path: "
347
+ return if path.nil? || path == ""
348
+ rescue Exception => ex
349
+ perror "Cancelled cd, press a key"
350
+ return
351
+ end
352
+ f = File.expand_path(path)
353
+ unless File.directory? f
354
+ ## check for env variable
355
+ tmp = ENV[path]
356
+ if tmp.nil? || !File.directory?( tmp )
357
+ ## check for dir in home
358
+ tmp = File.expand_path("~/#{path}")
359
+ if File.directory? tmp
360
+ f = tmp
361
+ end
362
+ else
363
+ f = tmp
364
+ end
365
+ end
366
+
367
+ open_file f
368
+ end
369
+
370
+ ## toggle mode to selection or not
371
+ # In selection, pressed hotkey selects a file without opening, one can keep selecting
372
+ # (or deselecting).
373
+ #
374
+ def selection_mode_toggle
375
+ if $mode == 'SEL'
376
+ # we seem to be coming out of select mode with some files
377
+ if $selected_files.size > 0
378
+ run_command $selected_files
379
+ end
380
+ $mode = nil
381
+ else
382
+ #$selection_mode = !$selection_mode
383
+ $mode = 'SEL'
384
+ end
385
+ end
386
+ ## toggle command mode
387
+ def command_mode
388
+ if $mode == 'COM'
389
+ $mode = nil
390
+ return
391
+ end
392
+ $mode = 'COM'
393
+ end
394
+ def goto_parent_dir
395
+ change_dir ".."
396
+ end
397
+ ## This actually filters, in zfm it goes to that entry since we have a cursor there
398
+ #
399
+ def goto_entry_starting_with fc=nil
400
+ unless fc
401
+ fc = get_single "Entries starting with: "
402
+ #fc = get_char
403
+ end
404
+ return if fc.size != 1
405
+ ## this is wrong and duplicates the functionality of /
406
+ # It shoud go to cursor of item starting with fc
407
+ $patt = "^#{fc}"
408
+ end
409
+
410
+
411
+ ## take regex from user, to run on files on screen, user can filter file names
412
+ def enter_regex
413
+ patt = get_line "Enter (regex) pattern: "
414
+ #$patt = gets().chomp
415
+ #$patt = Readline::readline('>', true)
416
+ $patt = patt
417
+ return patt
418
+ end
419
+ def next_page
420
+ # FIXME cursor position, take logic from zfm page calc
421
+ $sta += $pagesize
422
+ $cursor = $sta if $cursor < $sta
423
+ end
424
+ def prev_page
425
+ # FIXME cursor position, take logic from zfm page calc
426
+ $sta -= $pagesize
427
+ $cursor = $sta
428
+ end
429
+ def show_marks
430
+ list = []
431
+ $bookmarks.each_pair { |k, v| list << " #[fg=yellow, bold]#{k}#[/end] #[fg=green]#{v}#[/end]" }
432
+ # s="#[fg=green]hello there#[fg=yellow, bg=black, dim]"
433
+ config = {}
434
+ longestval = $bookmarks.values.max_by(&:length)
435
+ config[:title] = "Bookmarks"
436
+ config[:width] = [longestval.length + 5, FFI::NCurses.COLS - 5].min
437
+ $log.debug "XXX: LONGEST #{longestval}, #{longestval.length}"
438
+ ch = padpopup list, config
439
+ return unless ch
440
+ #$bookmarks.each_pair { |k, v| puts "#{k.ljust(7)} => #{v}" }
441
+ #puts
442
+ #print "Enter bookmark to goto: "
443
+ #ch = get_char
444
+ goto_bookmark(ch) if ch =~ /^[0-9A-Z]$/
445
+ end
446
+ # MENU MAIN -- keep consistent with zfm
447
+ def main_menu
448
+ h = {
449
+ :a => :ack,
450
+ "/" => :ffind,
451
+ :l => :locate,
452
+ :v => :viminfo,
453
+ :z => :z_interface,
454
+ :d => :child_dirs,
455
+ :r => :recent_files,
456
+ :t => :dirtree,
457
+ "4" => :tree,
458
+ :s => :sort_menu,
459
+ :F => :filter_menu,
460
+ :c => :command_menu ,
461
+ :B => :bindkey_ext_command,
462
+ :M => :newdir,
463
+ "%" => :newfile,
464
+ :x => :extras
465
+ }
466
+ menu "Main Menu", h
467
+ end
468
+
469
+ def toggle_menu
470
+ h = { :h => :toggle_hidden, :c => :toggle_case, :l => :toggle_long_list , "1" => :toggle_columns,
471
+ :p => :toggle_pager_mode, :e => :toggle_enhanced_list}
472
+ ch, menu_text = menu "Toggle Menu", h
473
+ case menu_text
474
+ when :toggle_hidden
475
+ #$hidden = $hidden ? nil : "D"
476
+ $hidden = !$hidden
477
+ c_refresh
478
+ when :toggle_case
479
+ #$ignorecase = $ignorecase ? "" : "i"
480
+ $ignorecase = !$ignorecase
481
+ c_refresh
482
+ when :toggle_columns
483
+ $gviscols = 3 if $gviscols == 1
484
+ #$long_listing = false if $gviscols > 1
485
+ x = $grows * $gviscols
486
+ $pagesize = $pagesize==x ? $grows : x
487
+ when :toggle_pager_mode
488
+ $editor_mode = !$editor_mode
489
+ if $editor_mode
490
+ $default_command = nil
491
+ else
492
+ $default_command = ENV['MANPAGER'] || ENV['PAGER']
493
+ end
494
+ when :toggle_enhanced_list
495
+ $enhanced_mode = !$enhanced_mode
496
+
497
+ when :toggle_long_list
498
+ $long_listing = !$long_listing
499
+ if $long_listing
500
+ $gviscols = 1
501
+ $pagesize = $grows
502
+ else
503
+ x = $grows * $gviscols
504
+ $pagesize = $pagesize==x ? $grows : x
505
+ end
506
+ c_refresh
507
+ end
508
+ end
509
+
510
+ def sort_menu
511
+ lo = nil
512
+ h = { :n => :newest, :a => :accessed, :o => :oldest,
513
+ :l => :largest, :s => :smallest , :m => :name , :r => :rname, :d => :dirs, :c => :clear }
514
+ ch, menu_text = menu "Sort Menu", h
515
+ case menu_text
516
+ when :newest
517
+ lo="om"
518
+ when :accessed
519
+ lo="oa"
520
+ when :oldest
521
+ lo="Om"
522
+ when :largest
523
+ lo="OL"
524
+ when :smallest
525
+ lo="oL"
526
+ when :name
527
+ lo="on"
528
+ when :rname
529
+ lo="On"
530
+ when :dirs
531
+ lo="/"
532
+ when :clear
533
+ lo=""
534
+ end
535
+ ## This needs to persist and be a part of all listings, put in change_dir.
536
+ $sorto = lo
537
+ $files = `zsh -c 'print -rl -- *(#{lo}#{$hidden}M)'`.split("\n") if lo
538
+ $title = nil
539
+ #$files =$(eval "print -rl -- ${pattern}(${MFM_LISTORDER}$filterstr)")
540
+ end
541
+
542
+ def command_menu
543
+ ##
544
+ # since these involve full paths, we need more space, like only one column
545
+ #
546
+ ## in these cases, getting back to the earlier dir, back to earlier listing
547
+ # since we've basically overlaid the old listing
548
+ #
549
+ # should be able to sort THIS listing and not rerun command. But for that I'd need to use
550
+ # xargs ls -t etc rather than the zsh sort order. But we can run a filter using |.
551
+ #
552
+ h = { :t => :today, :D => :default_command , :R => :remove_from_list}
553
+ if $editor_mode
554
+ h[:e] = :pager_mode
555
+ else
556
+ h[:e] = :editor_mode
557
+ end
558
+ ch, menu_text = menu "Command Menu", h
559
+ case menu_text
560
+ when :pager_mode
561
+ $editor_mode = false
562
+ $default_command = ENV['MANPAGER'] || ENV['PAGER']
563
+ when :editor_mode
564
+ $editor_mode = true
565
+ $default_command = nil
566
+ when :ffind
567
+ ffind
568
+ when :locate
569
+ locate
570
+ when :today
571
+ $files = `zsh -c 'print -rl -- *(#{$hidden}Mm0)'`.split("\n")
572
+ $title = "Today's files"
573
+ when :default_command
574
+ print "Selecting a file usually invokes $EDITOR, what command do you want to use repeatedly on selected files: "
575
+ $default_command = gets().chomp
576
+ if $default_command != ""
577
+ print "Second part of command (maybe blank): "
578
+ $default_command2 = gets().chomp
579
+ else
580
+ print "Cleared default command, will default to $EDITOR"
581
+ $default_command2 = nil
582
+ $default_command = nil
583
+ end
584
+ end
585
+ end
586
+ def extras
587
+ h = { "1" => :one_column, "2" => :multi_column, :c => :columns, :r => :config_read , :w => :config_write}
588
+ ch, menu_text = menu "Extras Menu", h
589
+ case menu_text
590
+ when :one_column
591
+ $pagesize = $grows
592
+ when :multi_column
593
+ #$pagesize = 60
594
+ $pagesize = $grows * $gviscols
595
+ when :columns
596
+ ch = get_single "How many columns to show: 1-6 [current #{$gviscols}]? "
597
+ #ch = get_char
598
+ ch = ch.to_i
599
+ if ch > 0 && ch < 7
600
+ $gviscols = ch.to_i
601
+ $pagesize = $grows * $gviscols
602
+ end
603
+ end
604
+ end
605
+ def filter_menu
606
+ h = { :d => :dirs, :f => :files, :e => :emptydirs , "0" => :emptyfiles}
607
+ ch, menu_text = menu "Filter Menu", h
608
+ files = nil
609
+ case menu_text
610
+ when :dirs
611
+ $filterstr = "/M"
612
+ files = `zsh -c 'print -rl -- *(#{$sorto}/M)'`.split("\n")
613
+ $title = "Filter: directories only"
614
+ when :files
615
+ $filterstr = "."
616
+ files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}.)'`.split("\n")
617
+ $title = "Filter: files only"
618
+ when :emptydirs
619
+ $filterstr = "/D^F"
620
+ files = `zsh -c 'print -rl -- *(#{$sorto}/D^F)'`.split("\n")
621
+ $title = "Filter: empty directories"
622
+ when :emptyfiles
623
+ $filterstr = ".L0"
624
+ files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}.L0)'`.split("\n")
625
+ $title = "Filter: empty files"
626
+ end
627
+ if files
628
+ $files = files
629
+ show_list
630
+ $stact = 0
631
+ end
632
+ end
633
+ def select_used_dirs
634
+ $title = "Used Directories"
635
+ $files = $used_dirs.uniq
636
+ #show_list
637
+ end
638
+ def select_visited_files
639
+ # not yet a unique list, needs to be unique and have latest pushed to top
640
+ $title = "Visited Files"
641
+ files = $visited_files.uniq
642
+ show_list files
643
+ $title = nil
644
+ end
645
+ def select_bookmarks
646
+ $title = "Bookmarks"
647
+ $files = $bookmarks.values.collect do |x|
648
+ if x.include? ":"
649
+ ix = x.index ":"
650
+ x[0,ix]
651
+ else
652
+ x
653
+ end
654
+ end
655
+ #show_list files
656
+ end
657
+
658
+ ## part copied and changed from change_dir since we don't dir going back on top
659
+ # or we'll be stuck in a cycle
660
+ def pop_dir
661
+ # the first time we pop, we need to put the current on stack
662
+ if !$visited_dirs.index(Dir.pwd)
663
+ $visited_dirs.push Dir.pwd
664
+ end
665
+ ## XXX make sure thre is something to pop
666
+ d = $visited_dirs.delete_at 0
667
+ ## XXX make sure the dir exists, cuold have been deleted. can be an error or crash otherwise
668
+ $visited_dirs.push d
669
+ Dir.chdir d
670
+ display_dir
671
+
672
+ return
673
+ # old stuff with zsh
674
+ $filterstr ||= "M"
675
+ $files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}#{$filterstr})'`.split("\n")
676
+ post_cd
677
+ end
678
+ # TODO
679
+ def TODOpost_cd
680
+ $patt=nil
681
+ $sta = $cursor = 0
682
+ $title = nil
683
+ if $selected_files.size > 0
684
+ $selected_files = []
685
+ end
686
+ $visual_block_start = nil
687
+ $stact = 0
688
+ screen_settings
689
+ # i think this will screw with the dir_pos since it is not filename based.
690
+ enhance_file_list
691
+ revert_dir_pos
692
+ end
693
+ #
694
+ ## read dirs and files and bookmarks from file
695
+ def config_read
696
+ #f = File.expand_path("~/.zfminfo")
697
+ f = File.expand_path(CONFIG_FILE)
698
+ if File.readable? f
699
+ load f
700
+ # maybe we should check for these existing else crash will happen.
701
+ $used_dirs.push(*(DIRS.split ":"))
702
+ $used_dirs.concat get_env_paths
703
+ $visited_files.push(*(FILES.split ":"))
704
+ #$bookmarks.push(*bookmarks) if bookmarks
705
+ chars = ('A'..'Z').to_a
706
+ chars.concat( ('0'..'9').to_a )
707
+ chars.each do |ch|
708
+ if Kernel.const_defined? "BM_#{ch}"
709
+ $bookmarks[ch] = Kernel.const_get "BM_#{ch}"
710
+ end
711
+ end
712
+ end
713
+ end
714
+ def get_env_paths
715
+ files = []
716
+ %w{ GEM_HOME PYTHONHOME}.each do |p|
717
+ d = ENV[p]
718
+ files.push d if d
719
+ end
720
+ %w{ RUBYLIB RUBYPATH GEM_PATH PYTHONPATH }.each do |p|
721
+ d = ENV[p]
722
+ files.concat d.split(":") if d
723
+ end
724
+ return files
725
+ end
726
+
727
+ ## save dirs and files and bookmarks to a file
728
+ def config_write
729
+ # Putting it in a format that zfm can also read and write
730
+ f1 = File.expand_path("~/.cygnusinfo")
731
+ #f1 = File.expand_path(CONFIG_FILE)
732
+ d = $used_dirs.join ":"
733
+ f = $visited_files.join ":"
734
+ File.open(f1, 'w+') do |f2|
735
+ # use "\n" for two lines of text
736
+ f2.puts "DIRS=\"#{d}\""
737
+ f2.puts "FILES=\"#{f}\""
738
+ $bookmarks.each_pair { |k, val|
739
+ f2.puts "BM_#{k}=\"#{val}\""
740
+ #f2.puts "BOOKMARKS[\"#{k}\"]=\"#{val}\""
741
+ }
742
+ end
743
+ $writing = $modified = false
744
+ end
745
+
746
+ ## accept a character to save this dir as a bookmark
747
+ def create_bookmark
748
+ ch = get_single "Enter A to Z or 0-9 for bookmark: "
749
+ #ch = get_char
750
+ if ch =~ /^[0-9A-Z]$/
751
+ #$bookmarks[ch] = "#{Dir.pwd}:#{$cursor}"
752
+ #
753
+ # The significance of putting a : and not a / is that with a
754
+ # : the dir will be opened with cursor on same object it was on, and not
755
+ # go into the dir. e.g, If bookmark is created with cursor on a dir, we don't want
756
+ # it to go into the dir.
757
+ $bookmarks[ch] = "#{Dir.pwd}:#{$view[$cursor]}"
758
+ $modified = true
759
+ else
760
+ perror "Bookmark must be upper-case character or number."
761
+ end
762
+ end
763
+ def subcommand
764
+ begin
765
+ command = get_line "Enter command: "
766
+ #command = gets().chomp
767
+ #command = Readline::readline('>', true)
768
+ return if command == ""
769
+ rescue Exception => ex
770
+ return
771
+ end
772
+ if command == "q"
773
+ if $modified
774
+ ch = get_single "Do you want to save bookmarks? (y/n): "
775
+ #ch = get_char
776
+ if ch == "y"
777
+ $writing = true
778
+ $quitting = true
779
+ elsif ch == "n"
780
+ $quitting = true
781
+ print "Quitting without saving bookmarks"
782
+ else
783
+ perror "No action taken."
784
+ end
785
+ else
786
+ $quitting = true
787
+ end
788
+ elsif command == "wq"
789
+ $quitting = true
790
+ $writing = true
791
+ elsif command == "x"
792
+ $quitting = true
793
+ $writing = true if $modified
794
+ elsif command == "p"
795
+ system "echo $PWD | pbcopy"
796
+ get_single "Stored PWD in clipboard (using pbcopy)"
797
+ end
798
+ end
799
+ def quit_command
800
+ if $modified
801
+ s = ""
802
+ s << "Press w to save bookmarks before quitting. " if $modified
803
+ s << "Press another q to quit "
804
+ ch = get_single s
805
+ #ch = get_char
806
+ else
807
+ $quitting = true
808
+ end
809
+ $quitting = true if ch == "q"
810
+ $quitting = $writing = true if ch == "w"
811
+ end
812
+
813
+ def views
814
+ views=%w[/ om oa Om OL oL On on]
815
+ viewlabels=%w[Dirs Newest Accessed Oldest Largest Smallest Reverse Name]
816
+ $sorto = views[$viewctr]
817
+ $title = viewlabels[$viewctr]
818
+ $viewctr += 1
819
+ $viewctr = 0 if $viewctr > views.size
820
+
821
+ $files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}M)'`.split("\n")
822
+
823
+ end
824
+ def child_dirs
825
+ $title = "Child directories"
826
+ $files = `zsh -c 'print -rl -- *(/#{$sorto}#{$hidden}M)'`.split("\n")
827
+ end
828
+ def dirtree
829
+ $title = "Child directories"
830
+ $files = `zsh -c 'print -rl -- **/*(/#{$sorto}#{$hidden}M)'`.split("\n")
831
+ end
832
+ #
833
+ # Get a full recursive listing of what's in this dir - useful for small projects with more
834
+ # structure than files.
835
+ def tree
836
+ # Caution: use only for small projects, don't use in root.
837
+ $title = "Full Tree"
838
+ $files = `zsh -c 'print -rl -- **/*(#{$sorto}#{$hidden}M)'`.split("\n")
839
+ end
840
+ def recent_files
841
+ # print -rl -- **/*(Dom[1,10])
842
+ $title = "Recent files"
843
+ $files = `zsh -c 'print -rl -- **/*(Dom[1,15])'`.split("\n")
844
+ end
845
+ def select_current
846
+ ## vp is local there, so i can do $vp[0]
847
+ #open_file $view[$sta] if $view[$sta]
848
+ open_file $view[$cursor] if $view[$cursor]
849
+ end
850
+
851
+ ## create a list of dirs in which some action has happened, for saving
852
+ def push_used_dirs d=Dir.pwd
853
+ $used_dirs.index(d) || $used_dirs.push(d)
854
+ end
855
+ ## I thin we need to make this like the command line one TODO
856
+ def get_char
857
+ c = @window.getchar
858
+ case c
859
+ when 13,10
860
+ return "ENTER"
861
+ when 32
862
+ return "SPACE"
863
+ when 127
864
+ return "BACKSPACE"
865
+ when 27
866
+ return "ESCAPE"
867
+ end
868
+ keycode_tos c
869
+ # if c > 32 && c < 127
870
+ #return c.chr
871
+ #end
872
+ ## use keycode_tos from Utils.
873
+ end
874
+
875
+ def pbold text
876
+ #puts "#{BOLD}#{text}#{BOLD_OFF}"
877
+ get_single text, :color_pair => $reversecolor
878
+ end
879
+ def perror text
880
+ ##puts "#{RED}#{text}#{CLEAR}"
881
+ #get_char
882
+ #alert text
883
+ get_single text + " Press a key...", :color_pair => $errorcolor
884
+ end
885
+ def pause text=" Press a key ..."
886
+ get_single text
887
+ #get_char
888
+ end
889
+ ## return shortcut for an index (offset in file array)
890
+ # use 2 more arrays to make this faster
891
+ # if z or Z take another key if there are those many in view
892
+ # Also, display ROWS * COLS so now we are not limited to 60.
893
+ def get_shortcut ix
894
+ return "<" if ix < $stact
895
+ ix -= $stact
896
+ i = $IDX[ix]
897
+ return i if i
898
+ return "->"
899
+ end
900
+ ## returns the integer offset in view (file array based on a-y za-zz and Za - Zz
901
+ # Called when user types a key
902
+ # should we even ask for a second key if there are not enough rows
903
+ # What if we want to also trap z with numbers for other purposes
904
+ def get_index key, vsz=999
905
+ i = $IDX.index(key)
906
+ return i+$stact if i
907
+ #sz = $IDX.size
908
+ zch = nil
909
+ if vsz > 25
910
+ if key == "z" || key == "Z"
911
+ #print key
912
+ zch = get_char
913
+ #print zch
914
+ i = $IDX.index("#{key}#{zch}")
915
+ return i+$stact if i
916
+ end
917
+ end
918
+ return nil
919
+ end
920
+
921
+ def delete_file
922
+ file_actions :delete
923
+ end
924
+
925
+ ## generic external command program
926
+ # prompt is the user friendly text of command such as list for ls, or extract for dtrx, page for less
927
+ # pauseyn is whether to pause after command as in file or ls
928
+ #
929
+ def command_file prompt, *command
930
+ pauseyn = command.shift
931
+ command = command.join " "
932
+ #print "[#{prompt}] Choose a file [#{$view[$cursor]}]: "
933
+ t = "[#{prompt}] Choose a file [#{$view[$cursor]}]: "
934
+ file = ask_hint t, $view[$cursor]
935
+ #print "#{prompt} :: Enter file shortcut: "
936
+ #file = ask_hint
937
+ perror "Command Cancelled" unless file
938
+ return unless file
939
+ file = File.expand_path(file)
940
+ if File.exists? file
941
+ file = Shellwords.escape(file)
942
+ pbold "#{command} #{file} (#{pauseyn})"
943
+ system "#{command} #{file}"
944
+ pause if pauseyn == "y"
945
+ c_refresh
946
+ else
947
+ perror "File #{file} not found"
948
+ end
949
+ end
950
+
951
+ ## prompt user for file shortcut and return file or nil
952
+ #
953
+ def ask_hint text, deflt=nil
954
+ f = nil
955
+
956
+ #ch = get_char
957
+ ch = get_single text
958
+ if ch == "ENTER"
959
+ return deflt
960
+ end
961
+ ix = get_index(ch, $viewport.size)
962
+ f = $viewport[ix] if ix
963
+ return f
964
+ end
965
+
966
+ ## check screen size and accordingly adjust some variables
967
+ #
968
+ def screen_settings
969
+ # TODO these need to become part of our new full_indexer class, not hang about separately.
970
+ $glines=%x(tput lines).to_i
971
+ $gcols=%x(tput cols).to_i
972
+ # this depends now on textpad size not screen size TODO FIXME
973
+ $grows = $glines - 1
974
+ $pagesize = 60
975
+ #$gviscols = 3
976
+ $pagesize = $grows * $gviscols
977
+ end
978
+ ## moves column offset so we can reach unindexed columns or entries
979
+ # 0 forward and any other back/prev
980
+ def column_next dir=0
981
+ if dir == 0
982
+ $stact += $grows
983
+ $stact = 0 if $stact >= $viewport.size
984
+ else
985
+ $stact -= $grows
986
+ $stact = 0 if $stact < 0
987
+ end
988
+ end
989
+ # currently i am only passing the action in from the list there as a key
990
+ # I should be able to pass in new actions that are external commands
991
+ def file_actions action=nil
992
+ h = { :d => :delete, :m => :move, :r => :rename, :v => ENV["EDITOR"] || :vim,
993
+ :c => :copy, :C => :chdir,
994
+ :l => :less, :s => :most , :f => :file , :o => :open, :x => :dtrx, :z => :zip }
995
+ #acttext = h[action.to_sym] || action
996
+ acttext = action || ""
997
+ file = nil
998
+
999
+ sct = $selected_files.size
1000
+ if sct > 0
1001
+ text = "#{sct} files"
1002
+ file = $selected_files
1003
+ else
1004
+ #print "[#{acttext}] Choose a file [#{$view[$cursor]}]: "
1005
+ t = "[#{acttext}] Choose a file [#{$view[$cursor]}]: "
1006
+ file = ask_hint t, $view[$cursor]
1007
+ return unless file
1008
+ text = file
1009
+ end
1010
+
1011
+ case file
1012
+ when Array
1013
+ # escape the contents and create a string
1014
+ files = Shellwords.join(file)
1015
+ when String
1016
+ files = Shellwords.escape(file)
1017
+ end
1018
+
1019
+
1020
+ ch = nil
1021
+ if action
1022
+ menu_text = action
1023
+ else
1024
+ ch, menu_text = menu "File Menu for #{text}", h
1025
+ menu_text = :quit if ch == "q"
1026
+ end
1027
+ return unless menu_text
1028
+ case menu_text.to_sym
1029
+ when :quit
1030
+ when :delete
1031
+ ch = get_single "rmtrash #{files} ?[yn]: "
1032
+ #print "rmtrash #{files} ?[yn]: "
1033
+ #ch = get_char
1034
+ return if ch != "y"
1035
+ c_system "rmtrash #{files}"
1036
+ c_refresh
1037
+ when :move
1038
+ #print "move #{text} to : "
1039
+ #target = gets().chomp
1040
+ #target = Readline::readline('>', true)
1041
+ target = get_line "move #{text} to : "
1042
+ text=File.expand_path(text)
1043
+ return if target.nil? || target == ""
1044
+ if File.directory? target
1045
+ FileUtils.mv text, target
1046
+ c_refresh
1047
+ else
1048
+ perror "Target not a dir"
1049
+ end
1050
+ when :copy
1051
+ target = get_line "copy #{text} to : "
1052
+ #target = Readline::readline('>', true)
1053
+ return if target.nil? || target == ""
1054
+ text=File.expand_path(text)
1055
+ target = File.basename(text) if target == "."
1056
+ if File.exists? target
1057
+ perror "Target (#{target}) exists"
1058
+ else
1059
+ FileUtils.cp text, target
1060
+ c_refresh
1061
+ end
1062
+ when :chdir
1063
+ change_dir File.dirname(text)
1064
+ when :zip
1065
+ target = get_line "Archive name: "
1066
+ #target = gets().chomp
1067
+ #target = Readline::readline('>', true)
1068
+ return if target.nil? || target == ""
1069
+ # don't want a blank space or something screwing up
1070
+ if target && target.size > 3
1071
+ if File.exists? target
1072
+ perror "Target (#{target}) exists"
1073
+ else
1074
+ c_system "tar zcvf #{target} #{files}"
1075
+ c_refresh
1076
+ end
1077
+ end
1078
+ when :rename
1079
+ when :most, :less, :vim, ENV['EDITOR']
1080
+ c_system "#{menu_text} #{files}"
1081
+ else
1082
+ return unless menu_text
1083
+ $log.debug "XXX: menu_text #{menu_text.to_sym}"
1084
+ get_single "#{menu_text} #{files}"
1085
+ #pause
1086
+ #print
1087
+ c_system "#{menu_text} #{files}"
1088
+ pause
1089
+ c_refresh
1090
+ end
1091
+ # remove non-existent files from select list due to move or delete or rename or whatever
1092
+ if sct > 0
1093
+ $selected_files.reject! {|x| x = File.expand_path(x); !File.exists?(x) }
1094
+ end
1095
+ end
1096
+
1097
+ def columns_incdec howmany
1098
+ $gviscols += howmany.to_i
1099
+ $gviscols = 1 if $gviscols < 1
1100
+ $gviscols = 6 if $gviscols > 6
1101
+ $pagesize = $grows * $gviscols
1102
+ end
1103
+
1104
+ # bind a key to an external command wich can be then be used for files
1105
+ def bindkey_ext_command
1106
+ #print
1107
+ #pbold "Bind a capital letter to an external command"
1108
+ ch = get_single "Enter a capital letter to bind: "
1109
+ #ch = get_char
1110
+ return if ch == "Q"
1111
+ if ch =~ /^[A-Z]$/
1112
+ print "Enter an external command to bind to #{ch}: "
1113
+ com = gets().chomp
1114
+ if com != ""
1115
+ print "Enter prompt for command (blank if same as command): "
1116
+ pro = gets().chomp
1117
+ pro = com if pro == ""
1118
+ end
1119
+ print "Pause after output [y/n]: "
1120
+ yn = get_char
1121
+ $bindings[ch] = "command_file #{pro} #{yn} #{com}"
1122
+ end
1123
+ end
1124
+ def viminfo
1125
+ file = File.expand_path("~/.viminfo")
1126
+ if File.exists? file
1127
+ $title = "Files from ~/.viminfo"
1128
+ #$files = `grep '^>' ~/.viminfo | cut -d ' ' -f 2- | sed "s#~#$HOME#g"`.split("\n")
1129
+ $files = `grep '^>' ~/.viminfo | cut -d ' ' -f 2- `.split("\n")
1130
+ $files.reject! {|x| x = File.expand_path(x); !File.exists?(x) }
1131
+ show_list
1132
+ end
1133
+ end
1134
+ def z_interface
1135
+ file = File.expand_path("~/.z")
1136
+ if File.exists? file
1137
+ $title = "Directories from ~/.z"
1138
+ $files = `sort -rn -k2 -t '|' ~/.z | cut -f1 -d '|'`.split("\n")
1139
+ home = ENV['HOME']
1140
+ $files.collect! do |f|
1141
+ f.sub(/#{home}/,"~")
1142
+ end
1143
+ show_list
1144
+ end
1145
+ end
1146
+ def ack
1147
+ pattern = get_line "Enter a pattern to search (ack): "
1148
+ return if pattern.nil? || pattern == ""
1149
+ $title = "Files found using 'ack' #{pattern}"
1150
+ #system("ack #{pattern}")
1151
+ #pause
1152
+ files = `ack -l #{pattern}`.split("\n")
1153
+ if files.size == 0
1154
+ perror "No files found."
1155
+ else
1156
+ $files = files
1157
+ show_list
1158
+ end
1159
+ end
1160
+ def ffind
1161
+ pattern = get_line "Enter a file name pattern to find: "
1162
+ return if pattern.nil? || pattern == ""
1163
+ $title = "Files found using 'find' #{pattern}"
1164
+ files = `find . -name '#{pattern}'`.split("\n")
1165
+ if files.size == 0
1166
+ perror "No files found."
1167
+ else
1168
+ $files = files
1169
+ show_list
1170
+ end
1171
+ end
1172
+ def locate
1173
+ pattern = get_line "Enter a file name pattern to locate: "
1174
+ return if pattern.nil? || pattern == ""
1175
+ $title = "Files found using 'locate' #{pattern}"
1176
+ files = `locate #{pattern}`.split("\n")
1177
+ files.reject! {|x| x = File.expand_path(x); !File.exists?(x) }
1178
+ if files.size == 0
1179
+ perror "No files found."
1180
+ else
1181
+ $files = files
1182
+ show_list
1183
+ end
1184
+ end
1185
+
1186
+ ## Displays files from .viminfo file, if you use some other editor which tracks files opened
1187
+ # then you can modify this accordingly.
1188
+ #
1189
+
1190
+ ## takes directories from the z program, if you use autojump you can
1191
+ # modify this accordingly
1192
+ #
1193
+
1194
+ ## some cursor movement functions
1195
+ ##
1196
+ #
1197
+ def cursor_scroll_dn
1198
+ moveto(pos() + MSCROLL)
1199
+ end
1200
+ def cursor_scroll_up
1201
+ moveto(pos() - MSCROLL)
1202
+ end
1203
+ def cursor_dn
1204
+ moveto(pos() + 1)
1205
+ end
1206
+ def cursor_up
1207
+ moveto(pos() - 1)
1208
+ end
1209
+ def pos
1210
+ $cursor
1211
+ end
1212
+
1213
+ def moveto pos
1214
+ orig = $cursor
1215
+ $cursor = pos
1216
+ $cursor = [$cursor, $view.size - 1].min
1217
+ $cursor = [$cursor, 0].max
1218
+ star = [orig, $cursor].min
1219
+ fin = [orig, $cursor].max
1220
+ if $visual_mode
1221
+ # PWD has to be there in selction
1222
+ if $selected_files.index $view[$cursor]
1223
+ # this depends on the direction
1224
+ $selected_files = $selected_files - $view[star..fin]
1225
+ ## current row remains in selection always.
1226
+ $selected_files.push $view[$cursor]
1227
+ else
1228
+ $selected_files.concat $view[star..fin]
1229
+ end
1230
+ end
1231
+ end
1232
+ def visual_mode_toggle
1233
+ $visual_mode = !$visual_mode
1234
+ if $visual_mode
1235
+ $visual_block_start = $cursor
1236
+ $selected_files.push $view[$cursor]
1237
+ end
1238
+ end
1239
+ def visual_block_clear
1240
+ if $visual_block_start
1241
+ star = [$visual_block_start, $cursor].min
1242
+ fin = [$visual_block_start, $cursor].max
1243
+ $selected_files = $selected_files - $view[star..fin]
1244
+ end
1245
+ $visual_block_start = nil
1246
+ $visual_mode = nil
1247
+ end
1248
+ def file_matching? file, patt
1249
+ file =~ /#{patt}/
1250
+ end
1251
+
1252
+ ## generic method to take cursor to next position for a given condition
1253
+ def return_next_match binding, *args
1254
+ first = nil
1255
+ ix = 0
1256
+ $view.each_with_index do |elem,ii|
1257
+ if binding.call(elem, *args)
1258
+ first ||= ii
1259
+ if ii > $cursor
1260
+ ix = ii
1261
+ break
1262
+ end
1263
+ end
1264
+ end
1265
+ return first if ix == 0
1266
+ return ix
1267
+ end
1268
+ ##
1269
+ # position cursor on a specific line which could be on a nother page
1270
+ # therefore calculate the correct start offset of the display also.
1271
+ def goto_line pos
1272
+ pages = ((pos * 1.00)/$pagesize).ceil
1273
+ pages -= 1
1274
+ #$sta = pages * $pagesize + 1
1275
+ $sta = pages * $pagesize + 0
1276
+ $cursor = pos
1277
+ #$log.debug "XXX: GOTO_LINE #{$sta} :: #{$cursor}"
1278
+ end
1279
+ def filetype f
1280
+ return nil unless f
1281
+ f = Shellwords.escape(f)
1282
+ s = `file #{f}`
1283
+ if s.index "text"
1284
+ return :text
1285
+ elsif s.index(/[Zz]ip/)
1286
+ return :zip
1287
+ elsif s.index("archive")
1288
+ return :zip
1289
+ elsif s.index "image"
1290
+ return :image
1291
+ elsif s.index "data"
1292
+ return :text
1293
+ end
1294
+ nil
1295
+ end
1296
+
1297
+ def save_dir_pos
1298
+ return if $sta == 0 && $cursor == 0
1299
+ $dir_position[Dir.pwd] = [$sta, $cursor]
1300
+ end
1301
+ def revert_dir_pos
1302
+ $sta = 0
1303
+ $cursor = 0
1304
+ a = $dir_position[Dir.pwd]
1305
+ if a
1306
+ $sta = a.first
1307
+ $cursor = a[1]
1308
+ raise "sta is nil for #{Dir.pwd} : #{$dir_position[Dir.pwd]}" unless $sta
1309
+ raise "cursor is nil" unless $cursor
1310
+ end
1311
+ end
1312
+ def newdir
1313
+ #print
1314
+ #print "Enter directory name: "
1315
+ #str = Readline::readline('>', true)
1316
+ str = get_line "Enter directory name: "
1317
+ return if str.nil? || str == ""
1318
+ if File.exists? str
1319
+ perror "#{str} exists."
1320
+ return
1321
+ end
1322
+ begin
1323
+ FileUtils.mkdir str
1324
+ $used_dirs.insert(0, str) if File.exists?(str)
1325
+ c_refresh
1326
+ rescue Exception => ex
1327
+ perror "Error in newdir: #{ex}"
1328
+ end
1329
+ end
1330
+ def newfile
1331
+ #print
1332
+ str = get_line "Enter file name: "
1333
+ #str = Readline::readline('>', true)
1334
+ return if str.nil? || str == ""
1335
+ system "$EDITOR #{str}"
1336
+ $visited_files.insert(0, str) if File.exists?(str)
1337
+ c_refresh
1338
+ end
1339
+
1340
+ ##
1341
+ # Editing of the User Dir List.
1342
+ # remove current entry from used dirs list, since we may not want some entries being there
1343
+ #
1344
+ def remove_from_list
1345
+ if $selected_files.size > 0
1346
+ sz = $selected_files.size
1347
+ ch = get_single "Remove #{sz} files from used list (y)?: "
1348
+ #ch = get_char
1349
+ return if ch != "y"
1350
+ $used_dirs = $used_dirs - $selected_files
1351
+ $visited_files = $visited_files - $selected_files
1352
+ unselect_all
1353
+ $modified = true
1354
+ return
1355
+ end
1356
+ #print
1357
+ ## what if selected some rows
1358
+ file = $view[$cursor]
1359
+ ch = get_single "Remove #{file} from used list (y)?: "
1360
+ #ch = get_char
1361
+ return if ch != "y"
1362
+ file = File.expand_path(file)
1363
+ if File.directory? file
1364
+ $used_dirs.delete(file)
1365
+ else
1366
+ $visited_files.delete(file)
1367
+ end
1368
+ c_refresh
1369
+ $modified = true
1370
+ end
1371
+ #
1372
+ # If there's a short file list, take recently mod and accessed folders and put latest
1373
+ # files from there and insert it here. I take both since recent mod can be binaries / object
1374
+ # files and gems created by a process, and not actually edited files. Recent accessed gives
1375
+ # latest source, but in some cases even this can be misleading since running a program accesses
1376
+ # include files.
1377
+ def enhance_file_list
1378
+ return unless $enhanced_mode
1379
+ # if only one entry and its a dir
1380
+ # get its children and maybe the recent mod files a few
1381
+
1382
+ if $files.size == 1
1383
+ # its a dir, let give the next level at least
1384
+ if $files.first[-1] == "/"
1385
+ d = $files.first
1386
+ f = `zsh -c 'print -rl -- #{d}*(omM)'`.split("\n")
1387
+ if f && f.size > 0
1388
+ $files.concat f
1389
+ return
1390
+ end
1391
+ else
1392
+ # just a file, not dirs here
1393
+ return
1394
+ end
1395
+ end
1396
+ #
1397
+ # check if a ruby project dir, although it could be a backup file too,
1398
+ # if so , expand lib and maby bin, put a couple recent files
1399
+ #
1400
+ if $files.index("Gemfile") || $files.grep(/\.gemspec/).size > 0
1401
+ # usually the lib dir has only one file and one dir
1402
+ flg = false
1403
+ if $files.index("lib/")
1404
+ f = `zsh -c 'print -rl -- lib/*(om[1,5]M)'`.split("\n")
1405
+ if f && f.size() > 0
1406
+ insert_into_list("lib/", f)
1407
+ flg = true
1408
+ end
1409
+ dd = File.basename(Dir.pwd)
1410
+ if f.index("lib/#{dd}/")
1411
+ f = `zsh -c 'print -rl -- lib/#{dd}/*(om[1,5]M)'`.split("\n")
1412
+ if f && f.size() > 0
1413
+ insert_into_list("lib/#{dd}/", f)
1414
+ flg = true
1415
+ end
1416
+ end
1417
+ end
1418
+ if $files.index("bin/")
1419
+ f = `zsh -c 'print -rl -- bin/*(om[1,5]M)'`.split("\n")
1420
+ insert_into_list("bin/", f) if f && f.size() > 0
1421
+ flg = true
1422
+ end
1423
+ return if flg
1424
+
1425
+ # lib has a dir in it with the gem name
1426
+
1427
+ end
1428
+ return if $files.size > 15
1429
+
1430
+ ## first check accessed else modified will change accessed
1431
+ moda = `zsh -c 'print -rn -- *(/oa[1]M)'`
1432
+ if moda && moda != ""
1433
+ modf = `zsh -c 'print -rn -- #{moda}*(oa[1]M)'`
1434
+ if modf && modf != ""
1435
+ insert_into_list moda, modf
1436
+ end
1437
+ modm = `zsh -c 'print -rn -- #{moda}*(om[1]M)'`
1438
+ if modm && modm != "" && modm != modf
1439
+ insert_into_list moda, modm
1440
+ end
1441
+ end
1442
+ ## get last modified dir
1443
+ modm = `zsh -c 'print -rn -- *(/om[1]M)'`
1444
+ if modm != moda
1445
+ modmf = `zsh -c 'print -rn -- #{modm}*(oa[1]M)'`
1446
+ insert_into_list modm, modmf
1447
+ modmf1 = `zsh -c 'print -rn -- #{modm}*(om[1]M)'`
1448
+ insert_into_list(modm, modmf1) if modmf1 != modmf
1449
+ else
1450
+ # if both are same then our options get reduced so we need to get something more
1451
+ # If you access the latest mod dir, then come back you get only one, since mod and accessed
1452
+ # are the same dir, so we need to find the second modified dir
1453
+ end
1454
+ end
1455
+ def insert_into_list dir, file
1456
+ ix = $files.index(dir)
1457
+ raise "something wrong can find #{dir}." unless ix
1458
+ $files.insert ix, *file
1459
+ end
1460
+ #
1461
+ # prints a prompt at bottom of screen, takes a character and returns textual representation
1462
+ # of character (as per get_char) and not the int that window.getchar returns.
1463
+ # It uses a window, so underlying text is not touched.
1464
+ #
1465
+ def get_single text, config={}
1466
+ w = one_line_window
1467
+ x = y = 0
1468
+ color = config[:color_pair] || $datacolor
1469
+ color=Ncurses.COLOR_PAIR(color);
1470
+ w.attron(color);
1471
+ w.mvprintw(x, y, "%s" % text);
1472
+ w.attroff(color);
1473
+ w.wrefresh
1474
+ Ncurses::Panel.update_panels
1475
+ chr = get_char
1476
+ w.destroy
1477
+ w = nil
1478
+ return chr
1479
+ end
1480
+
1481
+ # identical to get_string but does not show as a popup with buttons, just ENTER
1482
+ # This is required if there are multiple inputs required and having several get_strings
1483
+ # one after the other seems really odd due to multiple popups
1484
+ # Unlike, get_string this does not return a nil if C-c pressed. Either returns a string if
1485
+ # ENTER pressed or a blank if C-c or Double Escape. So only blank to be checked
1486
+ def get_line text, config={}
1487
+ begin
1488
+ w = one_line_window
1489
+ form = RubyCurses::Form.new w
1490
+
1491
+ f = Field.new form, :label => text, :row => 0, :col => 1
1492
+ form.repaint
1493
+ w.wrefresh
1494
+ while((ch = w.getchar()) != FFI::NCurses::KEY_F10 )
1495
+ break if ch == 13
1496
+ if ch == 3 || ch == 27 || ch == 2727
1497
+ return ""
1498
+ end
1499
+ begin
1500
+ form.handle_key(ch)
1501
+ w.wrefresh
1502
+ rescue => err
1503
+ $log.debug( err) if err
1504
+ $log.debug(err.backtrace.join("\n")) if err
1505
+ textdialog ["Error in Messagebox: #{err} ", *err.backtrace], :title => "Exception"
1506
+ w.refresh # otherwise the window keeps showing (new FFI-ncurses issue)
1507
+ $error_message.value = ""
1508
+ ensure
1509
+ end
1510
+ end # while loop
1511
+
1512
+ ensure
1513
+ w.destroy
1514
+ w = nil
1515
+ end
1516
+ return f.text
1517
+ end
1518
+
1519
+ #run if __FILE__ == $PROGRAM_NAME
1520
+ end