cygnus 0.0.2

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.
@@ -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