cetus 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/cetus +467 -420
  3. data/cetus.gemspec +1 -1
  4. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 354100c7054b31bc96f5bcc0a34056b68dcb061181467f0555443e340eac7922
4
- data.tar.gz: 66f325607b440d2ebc476246b136f8c8e7d7e70a76cd27822c4abb56ff26f44f
3
+ metadata.gz: e62bb94776477721739341ff4f03ac93390501da588709f3115a2a4c4ee3ca0f
4
+ data.tar.gz: 496df10e6710d503de9d4aa2334ac932286b1cb7c23ccc4ef57af02bb310496d
5
5
  SHA512:
6
- metadata.gz: c497713cbae448bdac329afa7f030046ad889f1a7053ce5e0c6debd55d60649107fca9b7d697193f6e7bbcbc2f028902c29c512cfc04008c75ed77450e73a6f9
7
- data.tar.gz: d811607e119d64186c1c49b38e6d23c0e643c20a0fe9cafe95c83ab8144157a2e6f152e3039fbc441c3e4bca956d44fc445d5ea59eedd1cfdc38ddf6fd4d43cf
6
+ metadata.gz: a3fc6945a92dde16f2c4dbb6dac0b2e1ffd93968caca6cfaadba473ab183b612d88c6916792fbdeffd1d5c5cbcd561c794c4588f6a76c35a9fe6dc1221a2d747
7
+ data.tar.gz: 62f47e97a020a759d14f31c6eb3c6cdefaccea57fb1a14997f535e53b6e6fd914b67b72ce0027cb4d8bf82fb652650654aa09c1742d44a70d60dc92981990b08
data/bin/cetus CHANGED
@@ -8,7 +8,7 @@
8
8
  # Author: rkumar http://github.com/rkumar/cetus/
9
9
  # Date: 2013-02-17 - 17:48
10
10
  # License: GPL
11
- # Last update: 2019-04-24 13:56
11
+ # Last update: 2019-05-14 10:21
12
12
  # --------------------------------------------------------------------------- #
13
13
  # cetus.rb Copyright (C) 2012-2019 rahul kumar
14
14
  # == CHANGELOG
@@ -21,6 +21,7 @@
21
21
  # 2019-03-22 - refactoring the code, esp run()
22
22
  # 2019-04-21 - search_as_you_type
23
23
  # 2019-04-22 - new search_as_you_type
24
+ # 2019-05-13 - add "\r" so WINCH can work
24
25
  # == TODO
25
26
 
26
27
  require 'readline'
@@ -40,82 +41,82 @@ now = Time.now.strftime("%Y-%m-%d %H:%M:%S")
40
41
  # alias c=~/bin/cetus.rb
41
42
  # c
42
43
 
43
- VERSION = '0.2.0.0'
44
+ VERSION = '0.3.0.0'
44
45
  CONFIG_PATH = ENV['XDG_CONFIG_HOME'] || File.join(ENV['HOME'], '.config')
45
46
  CONFIG_FILE = "#{CONFIG_PATH}/cetus/conf.yml"
46
47
 
48
+ # NOTE: if changing binding to symbol, there are many places where searched as
49
+ # string.
47
50
  @bindings = {
48
- '`' => 'main_menu',
49
- '=' => 'toggle_menu',
50
- 'M-s' => 'selection_menu',
51
- 'M-o' => 'order_menu',
52
- 'ENTER' => 'select_current',
53
- 'C-p' => 'page_current',
54
- 'C-e' => 'edit_current',
55
- 'C-o' => 'open_current',
56
- 'C-s' => 'toggle_select',
57
- 'C-r' => 'reduce',
58
- 'C-g' => 'debug_vars',
59
- '*' => 'toggle_multiple_selection',
60
- 'M-a' => 'select_all',
61
- 'M-A' => 'unselect_all',
62
- '!' => 'execute',
63
- ',' => 'goto_parent_dir',
64
- '~' => 'goto_home_dir',
65
- '-' => 'goto_previous_dir',
66
- '+' => 'goto_dir', # 2019-03-07 - TODO: change binding
67
- '.' => 'pop_dir',
68
- ':' => 'subcommand',
69
- "'" => 'goto_bookmark',
70
- '"' => 'file_starting_with',
71
- '/' => 'ask_regex',
72
- 'M-p' => 'prev_page',
73
- 'M-n' => 'next_page',
74
- 'PgUp' => 'prev_page',
75
- 'PgDn' => 'next_page',
76
- 'Home' => 'goto_top',
77
- 'End' => 'goto_end',
78
- 'SPACE' => 'next_page:Page Down',
79
- 'M-f' => 'select_from_visited_files',
80
- 'M-d' => 'select_from_used_dirs',
81
- 'M-b' => 'bookmark_menu',
82
- 'M-m' => 'create_bookmark',
83
- 'M-M' => 'view_bookmarks',
84
- 'C-c' => 'escape',
85
- 'ESCAPE' => 'escape',
86
- 'TAB' => 'views',
87
- 'C-i' => 'views',
51
+ '`' => 'main_menu',
52
+ '=' => 'toggle_menu',
53
+ '%' => 'filter_menu',
54
+ 'M-s' => 'selection_menu',
55
+ 'M-o' => 'order_menu',
56
+ 'ENTER' => 'select_current',
57
+ 'C-p' => 'page_current',
58
+ 'C-e' => 'edit_current',
59
+ 'C-o' => 'open_current',
60
+ 'C-s' => 'toggle_select',
61
+ 'C-r' => 'reduce',
62
+ 'C-g' => 'debug_vars',
63
+ '*' => 'toggle_multiple_selection',
64
+ 'M-a' => 'select_all',
65
+ 'M-A' => 'unselect_all',
66
+ '!' => 'execute',
67
+ ',' => 'goto_parent_dir',
68
+ '~' => 'goto_home_dir',
69
+ '-' => 'goto_previous_dir',
70
+ '+' => 'goto_dir', # 2019-03-07 - TODO: change binding
71
+ '.' => 'pop_dir',
72
+ ':' => 'subcommand',
73
+ "'" => 'goto_bookmark',
74
+ '"' => 'file_starting_with',
75
+ '/' => 'filter_files_by_pattern',
76
+ 'M-p' => 'prev_page',
77
+ 'M-n' => 'next_page',
78
+ 'PgUp' => 'prev_page',
79
+ 'PgDn' => 'next_page',
80
+ 'Home' => 'goto_top',
81
+ 'End' => 'goto_end',
82
+ 'SPACE' => 'next_page:Page Down',
83
+ 'M-f' => 'select_from_visited_files',
84
+ 'M-d' => 'select_from_used_dirs',
85
+ 'M-b' => 'bookmark_menu',
86
+ 'M-m' => 'create_bookmark',
87
+ 'M-M' => 'view_bookmarks',
88
+ 'C-c' => 'escape',
89
+ 'ESCAPE' => 'escape',
90
+ 'TAB' => 'views',
91
+ 'C-i' => 'views',
88
92
  # '?' => 'dirtree',
89
- 'D' => 'delete_file',
93
+ 'D' => 'delete_file',
90
94
  # 'M' => 'file_actions most',
91
- 'M' => 'move_instant',
92
- 'q' => 'quit_command', # was Q now q 2019-03-04 -
95
+ 'M' => 'move_instant',
96
+ 'q' => 'quit_command', # was Q now q 2019-03-04 -
93
97
  # "RIGHT" => "column_next",
94
- 'RIGHT' => 'select_current', # changed 2018-03-12 - for faster navigation
95
- 'LEFT' => 'goto_parent_dir', # changed on 2018-03-12 - earlier column_next 1
96
- ']' => 'column_next: goto next column',
97
- '[' => 'column_next 1: goto previous column',
98
- 'C-x' => 'file_actions',
99
- 'M--' => 'columns_incdec -1: reduce column width',
100
- 'M-+' => 'columns_incdec 1: increase column width',
101
- 'S' => 'command_file list y ls -lh',
102
- 'L' => 'command_file Page n less',
103
- 'C-d' => 'cursor_scroll_dn',
104
- 'C-b' => 'cursor_scroll_up',
105
- 'UP' => 'cursor_up',
106
- 'DOWN' => 'cursor_dn',
98
+ 'RIGHT' => 'select_current', # changed 2018-03-12 - for faster navigation
99
+ 'LEFT' => 'goto_parent_dir', # changed on 2018-03-12 - earlier column_next 1
100
+ ']' => 'column_next: goto next column',
101
+ '[' => 'column_next 1: goto previous column',
102
+ 'C-x' => 'file_actions',
103
+ # 'M--' => 'columns_incdec -1: reduce column width',
104
+ # 'M-+' => 'columns_incdec 1: increase column width',
105
+ # 'L' => 'command_file Page n less: Page current file',
106
+ 'C-d' => 'cursor_scroll_dn',
107
+ 'C-b' => 'cursor_scroll_up',
108
+ 'UP' => 'cursor_up',
109
+ 'DOWN' => 'cursor_dn',
107
110
  'C-SPACE' => 'toggle_visual_mode',
108
- '@' => 'scripts',
109
- '#' => 'generators',
110
-
111
- '?' => 'print_help',
112
- 'F1' => 'print_help',
113
- 'F2' => 'child_dirs',
114
- 'F3' => 'dirtree',
115
- 'F4' => 'tree',
116
- 'S-F1' => 'dirtree',
117
- 'S-F2' => 'tree'
118
-
111
+ '@' => 'scripts',
112
+ '#' => 'generators',
113
+ '?' => 'print_help',
114
+ 'F1' => 'print_help',
115
+ 'F2' => 'child_dirs',
116
+ 'F3' => 'dirtree',
117
+ 'F4' => 'tree',
118
+ 'S-F1' => 'dirtree',
119
+ 'S-F2' => 'tree'
119
120
  }
120
121
 
121
122
  ## clean this up a bit, copied from shell program and macro'd
@@ -327,7 +328,6 @@ IDX.freeze
327
328
  @stact = 0 # used when panning a folder to next column
328
329
  # @editor_mode = true
329
330
  @editor_mode = false # changed 2018-03-12 - so we start in pager mode
330
- @enhanced_mode = true
331
331
  @visual_block_start = nil
332
332
  PAGER_COMMAND = {
333
333
  text: 'most',
@@ -338,7 +338,11 @@ PAGER_COMMAND = {
338
338
  }.freeze
339
339
  @dir_position = {}
340
340
  @cursor_movement = @old_cursor = nil # cursor movement has happened only, don't repaint
341
+ @keys_to_clear = nil # in how many strokes should message be cleared, set later.
342
+ # ---------------------------------------------
341
343
  ## FLAGS
344
+ @visual_mode = false
345
+ @enhanced_mode = true
342
346
  @multiple_selection = true # single select
343
347
  @group_directories = :first
344
348
  # truncate long filenames from :right, :left or :center.
@@ -347,39 +351,41 @@ PAGER_COMMAND = {
347
351
  @display_file_stats = true
348
352
  @selected_files_fullpath_flag = false
349
353
  @selected_files_escaped_flag = false
350
- @keys_to_clear = nil
351
354
  @ignore_case = true
352
355
  @highlight_row_flag = true # false
353
356
  @debug_flag = false
357
+ @date_func = :mtime # which date to display in long listing.
354
358
 
355
359
  # See toggle_value
356
360
  # we need to set these on startup
357
361
  @toggles = {
358
362
  ignore_case: true,
359
- # group_directories: true,
360
363
  long_listing: false,
361
364
  enhanced_mode: true,
362
365
  visual_mode: false,
363
366
  display_file_stats: true,
364
- toggle_selected_files_fullpath_flag: false,
365
- toggle_selected_files_escaped_flag: false,
367
+ selected_files_fullpath_flag: false,
368
+ selected_files_escaped_flag: false,
366
369
  multiple_selection: true,
367
370
  editor_mode: false,
368
371
  selection_mode: false, # typing hint adds to selection, does not open
369
372
  debug_flag: false,
370
- toggle_filename_status_line: true,
373
+ filename_status_line: true,
371
374
  instant_search: true,
372
375
  highlight_row_flag: @highlight_row_flag
373
376
  }
377
+ # These are flags that have multiple values.
374
378
  # var is name of variable to be set
375
379
  @options = {
376
380
  truncate_from: { current: :center, values: %i[left right center], var: :truncate_from },
377
381
  group_directories: { current: :first, values: %i[first none last], var: :group_directories },
378
- show_hidden: { current: nil, values: [nil, 'D'], var: :hidden }
382
+ show_hidden: { current: nil, values: [nil, 'D'], var: :hidden },
383
+ date_func: { current: :mtime, values: [:mtime, :atime, :ctime], var: :date_func }
379
384
  }
380
385
 
381
386
  ## ----------------- CONSTANTS ----------------- ##
382
387
  GMARK = '*'
388
+ VMARK = '+'
383
389
  CURMARK = '>'
384
390
  MSCROLL = 10
385
391
  SPACE = ' '
@@ -436,7 +442,6 @@ CURSOR_COLOR = REVERSE
436
442
  ## --------------------------------------------- ##
437
443
 
438
444
  @patt = nil
439
- @ignorecase = true
440
445
  @quitting = false
441
446
  @modified = @writing = false
442
447
  @visited_files = []
@@ -450,22 +455,20 @@ CURSOR_COLOR = REVERSE
450
455
  @viewctr = 0
451
456
  ## sta is where view (viewport) begins, cursor is current row/file
452
457
  @sta = @cursor = 0
453
- @visual_mode = false
454
458
  @status_color = 4 # status line, can be 2 3 4 5 6
455
459
  @status_color_right = 8 # status line right part
456
460
 
457
461
  # Menubar on top of screen
458
462
  @help = "#{BOLD}?#{BOLD_OFF} Help #{BOLD}`#{BOLD_OFF} Menu #{BOLD}!#{BOLD_OFF} Execute #{BOLD}=#{BOLD_OFF} Toggle #{BOLD}C-x#{BOLD_OFF} File Actions #{BOLD}q#{BOLD_OFF} Quit "
459
463
 
460
- # ------------------- read_directory ------------------ #
461
464
  def read_directory
462
465
  rescan_required false
463
466
 
464
467
  @filterstr ||= 'M' # XXX can we remove from here
468
+ @current_dir ||= Dir.pwd
465
469
  list_files
466
470
 
467
471
  group_directories_first
468
- # return unless cget(:enhanced_mode)
469
472
  return unless @enhanced_mode
470
473
 
471
474
  enhance_file_list
@@ -505,8 +508,7 @@ def list_files dir='*', sorto=@sorto, hidden=@hidden, _filter=@filterstr
505
508
  Dir.glob(dir)
506
509
  end
507
510
 
508
- # WARN: crashes on a dead link since no mtime
509
- # HACK: use first files return value for bad link
511
+ # WARN: crashes on a deadlink since no mtime
510
512
  if func
511
513
  sorted_files = sorted_files.sort_by do |f|
512
514
  if File.exist? f
@@ -520,16 +522,16 @@ def list_files dir='*', sorto=@sorto, hidden=@hidden, _filter=@filterstr
520
522
  sorted_files.sort! { |w1, w2| w1.casecmp(w2) } if func == :path && @ignore_case
521
523
 
522
524
  # zsh gives mtime sort with latest first, ruby gives latest last
523
- sorted_files.reverse! if sorto[0] == 'O'
525
+ sorted_files.reverse! if sorto && sorto[0] == 'O'
524
526
 
525
527
  # add slash to directories
526
528
  sorted_files = add_slash sorted_files
527
529
  # return sorted_files
528
530
  @files = sorted_files
531
+ calculate_bookmarks_for_dir # we don't want to override those set by others
529
532
  end
530
- # ------------- end of read_directory --------------------------------#
531
533
 
532
- # Deal with dead links.
534
+ # Deal with deadlinks.
533
535
  def sys_stat func, file
534
536
  return unless File.symlink? file
535
537
 
@@ -542,7 +544,6 @@ end
542
544
  # ------------------- create_viewport ------------------ #
543
545
  def create_viewport
544
546
  @view = if @patt
545
- # if cget(:ignore_case)
546
547
  if @ignore_case
547
548
  @files.grep(/#{@patt}/i)
548
549
  else
@@ -562,13 +563,21 @@ def create_viewport
562
563
 
563
564
  # viewport are the files that are visible, subset of view
564
565
  @viewport = @view[@sta, @pagesize]
566
+ @vps = @viewport.size
565
567
  end
566
568
  # ------------- end of create_viewport --------------------------------#
567
569
 
570
+ # FIXME: need to update when bookmark created or deleted
571
+ def calculate_bookmarks_for_dir
572
+
573
+ bm = @bookmarks.select { |k, v| v == @current_dir }.keys.join(',')
574
+ bm = " ('#{bm})" if bm && bm != ''
575
+ @bm = bm
576
+ end
568
577
  # ------------------- print_title ------------------ #
569
578
  def print_title
570
579
  # print help line and version
571
- print "#{GREEN}#{@help} #{BLUE}cetus #{VERSION}#{CLEAR}\n"
580
+ print "\r#{GREEN}#{@help} #{BLUE}cetus #{VERSION}#{CLEAR}\n"
572
581
  @current_dir ||= Dir.pwd
573
582
 
574
583
  # print 1 of n files, sort order, filter etc details
@@ -576,16 +585,11 @@ def print_title
576
585
 
577
586
  # Add bookmark next to name of dir, if exists
578
587
  # FIXME This should not happen in other listings like find selected files etc
579
- # bm = @bookmarks.key(Dir.pwd)
580
- # Display multiple bookmarks. Can be calculated on changing directory ?
581
- bm = @bookmarks.select { |k, v| v == @current_dir }.keys.join(',')
582
- bm = " ('#{bm})" if bm && bm != ''
583
588
 
584
- fin = @sta + @viewport.size
589
+ fin = @sta + @vps
585
590
  fl = @view.size
586
591
 
587
592
  # fix count of entries so separator and enhanced entries don't show up
588
- # if cget(:enhanced_mode)
589
593
  if @enhanced_mode
590
594
  ix = @viewport.index SEPARATOR
591
595
  fin = @sta + ix if ix
@@ -594,15 +598,16 @@ def print_title
594
598
  fl = ix if ix
595
599
  end
596
600
 
597
- t = fl.zero? ? "#{@title}#{bm} No files." : "#{@title}#{bm} #{@sta + 1} to #{fin} of #{fl} #{@sorto} F:#{@filterstr}"
601
+ t = fl.zero? ? "#{@title}#{@bm} No files." : "#{@title}#{@bm} #{@sta + 1} to #{fin} of #{fl} #{@sorto} F:#{@filterstr}"
598
602
 
599
603
  # don't exceed columns while printing
600
604
  t = t[t.size - @gcols..-1] if t.size >= @gcols
601
605
 
602
- print "#{BOLD}#{t}#{CLEAR}"
606
+ print "\r#{BOLD}#{t}#{CLEAR}"
603
607
 
604
608
  tput_cup(-1, @gcols - @hk.length - 2)
605
609
  puts '[' + @hk + ']'
610
+ print "\r"
606
611
 
607
612
  print "#{CURSOR_COLOR}EMPTY#{CLEAR}" if fl == 0
608
613
  end
@@ -623,7 +628,6 @@ def status_line
623
628
  # Print the filename at the right side of the status line
624
629
  # sometimes due to search, there is no file
625
630
  if cf
626
- # if cget(:debug_flag)
627
631
  if @debug_flag
628
632
  print_debug_info cf
629
633
  else
@@ -635,7 +639,6 @@ def status_line
635
639
  # patt and message are together, no gap, why not ? 2019-04-08 -
636
640
  if @patt && @patt != ''
637
641
  patt = "[/#{@patt}" + ']' # to get unfrozen string
638
- # patt[-1] = '/i]' if cget(:ignore_case)
639
642
  patt[-1] = '/i]' if @ignore_case
640
643
  end
641
644
  # bring cursor to start of line
@@ -648,12 +651,12 @@ def status_line
648
651
  end
649
652
 
650
653
  def print_debug_info cf=current_file
651
- print_on_right "len:#{cf.length}/#{@temp_wid} = #{@sta},#{@cursor},#{@stact},#{@viewport.size},#{@grows} | #{cf}"
654
+ print_on_right "len:#{cf.length}/#{@temp_wid} = #{@sta},#{@cursor},#{@stact},#{@vps},#{@grows} | #{cf}"
652
655
  end
653
656
 
654
657
  def print_filename_status_line cf=current_file
655
658
  if @display_file_stats
656
- ff = if cf[0] == '~'
659
+ ff = if cf.start_with?("~/")
657
660
  File.expand_path(cf)
658
661
  else
659
662
  cf
@@ -666,8 +669,9 @@ def print_filename_status_line cf=current_file
666
669
  date_format(File.stat(ff).mtime)
667
670
  end
668
671
  end
672
+ mtime = "| #{mtime} |" if mtime
669
673
  # print size and mtime only if more data requested.
670
- print_on_right "| #{mtime} | #{cf}".rjust(40)
674
+ print_on_right "#{mtime} #{cf}".rjust(40)
671
675
  end
672
676
 
673
677
  # should we do a read of the dir
@@ -698,7 +702,7 @@ def draw_directory
698
702
  buff = columnate @viewport, @grows
699
703
 
700
704
  # starts printing array on line 3
701
- buff.each { |line| print line, "\n" }
705
+ buff.each { |line| print "\r", line, "\n" }
702
706
  print
703
707
 
704
708
  status_line
@@ -717,16 +721,14 @@ end
717
721
  # if CLEAR then we use original colors from get_formatted_filename.
718
722
  # Otherwise we need to use CURSOR_COLOR in place of current color
719
723
  def place_cursor color=CURSOR_COLOR
720
- vps = @viewport.size
721
-
722
724
  # empty directory
723
- if vps == 0
725
+ if @vps == 0
724
726
  tput_cup 0, 0
725
727
  return
726
728
  end
727
729
 
728
730
  c = @cursor - @sta
729
- wid = get_width vps, @grows
731
+ wid = get_width @vps, @grows
730
732
  if c < @grows
731
733
  rows = c
732
734
  cols = 0
@@ -790,6 +792,7 @@ def resolve_binding key
790
792
  # split into binding and args
791
793
  x = x.split if x
792
794
  if x
795
+ redraw_required # trying here so default 2019-04-26 -
793
796
  binding = x.shift
794
797
  args = x
795
798
  send(binding, *args) if binding
@@ -849,6 +852,10 @@ end
849
852
 
850
853
  def set_bookmark key, dir=Dir.pwd
851
854
  @bookmarks[key] = dir
855
+ calculate_numeric_hotkeys
856
+ end
857
+
858
+ def calculate_numeric_hotkeys
852
859
  @hk = @bookmarks.select { |k, _| k.match?(/[0-9]/) }.keys.sort.join('')
853
860
  end
854
861
 
@@ -878,8 +885,9 @@ def readable_file_size size, precision
878
885
  end
879
886
 
880
887
  ## format date for file given stat
881
- def date_format t
882
- t.strftime '%Y/%m/%d %H:%M:%S'
888
+ def date_format tim
889
+ # tim.strftime '%Y/%m/%d %H:%M:%S'
890
+ tim.strftime '%Y/%m/%d %H:%M'
883
891
  end
884
892
 
885
893
  ##
@@ -936,7 +944,7 @@ def truncate_formatted_filename f, unformatted_len, wid
936
944
 
937
945
  when :right
938
946
  # FIXME: 2019-04-23 - do we need the control code at end ??
939
- f[0..wid - 3] + "$ "
947
+ f[0..wid - 3] + '$ '
940
948
 
941
949
  when :center
942
950
 
@@ -957,8 +965,6 @@ def truncate_formatted_filename f, unformatted_len, wid
957
965
  sindex = f.index ' '
958
966
  # 4 = 2 for literals, 2 to get ahead of sindex+1
959
967
  f[0..sindex + 1] + '<' + f[sindex + 4 + excess..-1] + ' '
960
- # in some cases 29/32 we are actually overlapping 2 parts above
961
- # same for 34/32
962
968
  end
963
969
  return f
964
970
  end
@@ -971,13 +977,15 @@ def get_formatted_filename ix, wid
971
977
 
972
978
  ind = get_shortcut(ix)
973
979
  mark = get_mark(f)
980
+ # make visited files bold
981
+ bcolor = BOLD if mark == VMARK
974
982
  # Handle separator before enhanced file list.
975
983
  # We do lose a shortcut
976
984
  ind = cur = mark = '-' if f == SEPARATOR
977
985
  prefix = "#{ind}#{mark}#{cur}"
978
986
 
979
- # fullname = f[0] == '~' ? File.expand_path(f) : f
980
- color ||= color_for(f)
987
+ color = color_for(f)
988
+ color = "#{bcolor}#{color}"
981
989
 
982
990
  f = format_long_listing(f) if @long_listing
983
991
 
@@ -989,9 +997,9 @@ def get_formatted_filename ix, wid
989
997
  unformatted_len = f.length
990
998
  if unformatted_len > wid
991
999
  # take the excess out from the center on both sides
992
- # TODO clear this up since no color info here
1000
+
993
1001
  f = truncate_formatted_filename(f, unformatted_len, wid)
994
- f = "#{color}#{f}#{CLEAR}" if color
1002
+ f = "#{color}#{f}#{CLEAR}" if color # NOTE clear not added if not color
995
1003
 
996
1004
  elsif unformatted_len < wid
997
1005
 
@@ -1016,14 +1024,22 @@ def get_mark file
1016
1024
  return SPACE if @selected_files.empty? && @visited_files.empty?
1017
1025
 
1018
1026
  @current_dir ||= Dir.pwd
1019
- fullname = File.join(@current_dir, file)
1027
+ # this crashes on filename that starts with a ~. 2019-05-04 -
1028
+ fullname = if file.start_with?("~/")
1029
+ File.expand_path(file)
1030
+ else
1031
+ File.join(@current_dir, file)
1032
+ end
1020
1033
 
1021
1034
  return GMARK if selected?(fullname)
1022
- return '+' if visited? fullname
1035
+ return VMARK if visited? fullname
1036
+
1037
+ # 2019-04-27 - this boldfaces visited files, but should only be done if color
1038
+ # otherwise it will overflow.
1039
+ # This was nice but it messes with width in multiple columns truncate
1040
+ # return "#{BOLD}+" if visited? fullname
1023
1041
 
1024
1042
  SPACE
1025
- # cur = CURMARK if ix + @sta == @cursor # 2019-03-29 - removed reduced calls
1026
- # NOTE seems like f and ary[ix] are the same
1027
1043
  end
1028
1044
 
1029
1045
  def format_long_listing f
@@ -1034,7 +1050,7 @@ def format_long_listing f
1034
1050
  if File.exist? f
1035
1051
  stat = File.stat(f)
1036
1052
  elsif f[0] == '~'
1037
- stat = File.stat(File.expand_path(f))
1053
+ stat = File.stat(expand_path(f))
1038
1054
  elsif File.symlink?(f)
1039
1055
  # dead link
1040
1056
  stat = File.lstat(f)
@@ -1043,10 +1059,11 @@ def format_long_listing f
1043
1059
  last = f[-1]
1044
1060
  stat = File.stat(f.chop) if last == ' ' || last == '@' || last == '*'
1045
1061
  end
1062
+ # TODO select date_func from toggles
1046
1063
 
1047
1064
  f = format('%10s %s %s',
1048
1065
  readable_file_size(stat.size, 1),
1049
- date_format(stat.mtime),
1066
+ date_format(stat.send(@date_func)),
1050
1067
  f)
1051
1068
 
1052
1069
  rescue StandardError => e
@@ -1060,7 +1077,8 @@ end
1060
1077
  # determine color for a filename based on extension, then pattern, then filetype
1061
1078
  def color_for f
1062
1079
  return nil if f == SEPARATOR
1063
- fname = f[0] == '~' ? File.expand_path(f) : f
1080
+
1081
+ fname = f.start_with?("~/") ? File.expand_path(f) : f
1064
1082
 
1065
1083
  extension = File.extname(fname)
1066
1084
  color = @ls_color[extension]
@@ -1155,6 +1173,7 @@ def parse_ls_colors
1155
1173
  # @log.debug "COLOR: ftype #{patt}"
1156
1174
  end
1157
1175
  end
1176
+ # @log.debug "LSP: #{@ls_pattern.values}"
1158
1177
  end
1159
1178
 
1160
1179
  ## select file based on key pressed
@@ -1181,20 +1200,27 @@ end
1181
1200
 
1182
1201
  ## toggle selection state of file
1183
1202
  def toggle_select f=current_file
1184
- if selected? f
1203
+ # if selected? File.join(@current_dir, current_file)
1204
+ if selected? expand_path(current_file)
1185
1205
  remove_from_selection f
1186
1206
  else
1187
1207
  @selected_files.clear unless @multiple_selection
1188
1208
  add_to_selection f
1189
1209
  end
1190
1210
  message "#{@selected_files.count} files selected. "
1191
- # XXX is it possible to just redraw this line
1192
- redraw_required
1211
+
1212
+ # 2019-04-24 - trying to avoid redrawing entire screen.
1213
+ # If multiple_selection then current selection is either added or removed,
1214
+ # nothing else changes, so we redraw only if not multi. Also place cursor
1215
+ # i.e. redraw current row if mutliple selection
1216
+ if @multiple_selection
1217
+ redraw_required false
1218
+ place_cursor
1219
+ end
1193
1220
  end
1194
1221
 
1195
1222
  # allow single or multiple selection with C-s key
1196
1223
  def toggle_multiple_selection
1197
- # @multiple_selection = !@multiple_selection
1198
1224
  toggle_value :multiple_selection
1199
1225
  end
1200
1226
 
@@ -1202,7 +1228,7 @@ end
1202
1228
  def open_file f
1203
1229
  return unless f
1204
1230
 
1205
- f = File.expand_path(f) if f[0] == '~'
1231
+ f = File.expand_path(f) if f.start_with?("~/")
1206
1232
  unless File.exist? f
1207
1233
  # this happens if we use (T) in place of (M)
1208
1234
  # it places a space after normal files and @ and * which borks commands
@@ -1228,7 +1254,7 @@ def open_file f
1228
1254
  system(comm.to_s)
1229
1255
  setup_terminal
1230
1256
  # XXX maybe use absolute_path instead of hardcoding
1231
- f = @current_dir + '/' + f if f[0] != '/'
1257
+ f = expand_path(f)
1232
1258
  @visited_files.insert(0, f)
1233
1259
  push_used_dirs @current_dir
1234
1260
  else
@@ -1248,13 +1274,13 @@ end
1248
1274
  def edit_current
1249
1275
  command = ENV['EDITOR'] || ENV['VISUAL'] || 'vim'
1250
1276
  run_on_current command
1251
- @visited_files.insert(0, current_file)
1277
+ @visited_files.insert(0, expand_path(current_file))
1252
1278
  end
1253
1279
 
1254
1280
  def open_current
1255
1281
  opener = /darwin/.match?(RUBY_PLATFORM) ? 'open' : 'xdg-open'
1256
1282
  run_on_current opener
1257
- @visited_files.insert(0, current_file)
1283
+ @visited_files.insert(0, expand_path(current_file))
1258
1284
  end
1259
1285
 
1260
1286
  # run given command on current file
@@ -1262,7 +1288,7 @@ def run_on_current command
1262
1288
  f = current_file
1263
1289
  return unless f
1264
1290
 
1265
- f = File.expand_path(f)
1291
+ f = expand_path(f)
1266
1292
  return unless File.readable?(f)
1267
1293
 
1268
1294
  f = Shellwords.escape(f)
@@ -1272,6 +1298,7 @@ def run_on_current command
1272
1298
  system(comm.to_s)
1273
1299
  push_used_dirs
1274
1300
  setup_terminal
1301
+ redraw_required
1275
1302
  end
1276
1303
 
1277
1304
  ## run system command on given file/s
@@ -1321,7 +1348,7 @@ def change_dir f
1321
1348
  @visited_dirs.insert(0, Dir.pwd)
1322
1349
  save_dir_pos
1323
1350
 
1324
- f = File.expand_path(f)
1351
+ f = expand_path(f)
1325
1352
  Dir.chdir f
1326
1353
  @current_dir = Dir.pwd # 2019-04-24 - earlier was in post_cd but too late
1327
1354
  read_directory
@@ -1344,7 +1371,6 @@ end
1344
1371
  ## clear sort order and refresh listing, used typically if you are in some view
1345
1372
  # such as visited dirs or files
1346
1373
  def escape
1347
- @sorto = nil
1348
1374
  @sorto = @default_sort_order
1349
1375
  @viewctr = 0
1350
1376
  @title = nil
@@ -1365,7 +1391,6 @@ end
1365
1391
 
1366
1392
  # put directories first, then files
1367
1393
  def group_directories_first
1368
- # return if cget(:group_directories) == :none
1369
1394
  return if @group_directories == :none
1370
1395
 
1371
1396
  files = @files
@@ -1388,6 +1413,7 @@ end
1388
1413
  ## select all entries (files and directories)
1389
1414
  def select_all
1390
1415
  dir = Dir.pwd
1416
+ # check this out with visited_files TODO FIXME
1391
1417
  @selected_files = @view.map { |file| File.join(dir, file) }
1392
1418
  message "#{@selected_files.count} files selected."
1393
1419
  end
@@ -1411,7 +1437,7 @@ def goto_dir
1411
1437
  ensure
1412
1438
  # print "\e[?25l"
1413
1439
  end
1414
- f = File.expand_path(path)
1440
+ f = expand_path(path)
1415
1441
  unless File.directory? f
1416
1442
  ## check for env variable
1417
1443
  tmp = ENV[path]
@@ -1464,14 +1490,18 @@ def goto_home_dir
1464
1490
  change_dir ENV['HOME']
1465
1491
  end
1466
1492
 
1467
- # Goes to directory bookmarked with number or upper case char.
1468
- # If lower case character given, then go to first file starting with char.
1493
+ # Goes to directory bookmarked with number or char.
1469
1494
  def goto_bookmark key=nil
1470
1495
  unless key
1471
1496
  clear_last_line
1472
- print 'Enter bookmark char: '
1497
+ print 'Enter bookmark char (? to view): '
1473
1498
  key = get_char
1474
1499
  end
1500
+ if key == '?'
1501
+ view_bookmarks
1502
+ return
1503
+ end
1504
+
1475
1505
  d = @bookmarks[key]
1476
1506
  if d
1477
1507
  change_dir d
@@ -1481,10 +1511,10 @@ def goto_bookmark key=nil
1481
1511
  end
1482
1512
 
1483
1513
  ## take regex from user, to run on files on screen, user can filter file names
1484
- def ask_regex
1485
- @title = 'Search Results: (Press Escape to cancel)'
1514
+ def filter_files_by_pattern
1515
+ @title = 'Search Results: (Press Esc to cancel)'
1486
1516
  @mode = 'SEARCH'
1487
- if cget(:instant_search)
1517
+ if @toggles[:instant_search]
1488
1518
  search_as_you_type
1489
1519
  else
1490
1520
  @patt = readline '/'
@@ -1512,6 +1542,7 @@ end
1512
1542
  def goto_top
1513
1543
  @sta = @cursor = 0
1514
1544
  @old_cursor = nil
1545
+ redraw_required
1515
1546
  end
1516
1547
 
1517
1548
  # goto end / bottom
@@ -1519,32 +1550,40 @@ def goto_end
1519
1550
  @cursor = @view.size - 1
1520
1551
  @sta = @view.size - @pagesize
1521
1552
  @old_cursor = nil
1553
+ redraw_required
1522
1554
  end
1523
1555
 
1524
1556
  def print_help
1525
1557
  page_with_tempfile do |file|
1526
- file.puts ' HELP'
1558
+ file.puts %(
1559
+ #{REVERSE} HELP #{CLEAR}
1527
1560
 
1528
- file.puts
1529
- file.puts 'To open a file or dir, press a-z A-Z (shortcut on left of file)'
1530
- file.puts 'Ctrl-s to select file under cursor. * for multiple select.'
1531
- file.puts 'Ctrl-Space: Enter and exit Visual Selection mode'
1532
- file.puts 'Ctrl-x: file actions for selected files, or file under cursor'
1533
- file.puts '1-9: bookmark a dir, and go to it.'
1561
+ Tilde (`) is the main menu key. Many important operations are
1562
+ available through it, or through its sub-menus.
1534
1563
 
1535
- file.puts 'Use left and right arrows to move through directories'
1536
- file.puts
1564
+ To open a file or dir, press a-z A-Z (shortcut on left of file)
1565
+ Ctrl-s to select file under cursor. * for multiple select.
1566
+ Ctrl-Space: Enter and exit Visual Selection mode
1567
+ Ctrl-x: file actions for selected files, or file under cursor
1568
+ 1-9: bookmark a dir, and go to it.
1569
+
1570
+ Use left and right arrows to move through directories
1571
+
1572
+ )
1537
1573
  ary = []
1538
1574
  # 2019-03-19 - if : then show text after colon
1539
1575
  @bindings.each_pair do |k, v|
1540
1576
  vv = v.tr('_', ' ')
1541
1577
  vv = vv.split(':')[1].strip if vv.include?(':')
1542
- ary.push "#{k.ljust(7)} => #{vv}"
1578
+ ary.push " #{k.ljust(7)} => #{vv}"
1543
1579
  end
1544
1580
  # FIXME: this works but not properly when long_listing is true.
1545
1581
  # We should avoid using columnate as there are several file related things.
1546
- ary = columnate ary, (ary.size / 2) + 1
1582
+ # next line no longer working.
1583
+ # ary = columnate ary, (ary.size / 2) + 1
1547
1584
  ary.each { |line| file.puts line }
1585
+ # TODO: 2019-04-26 - add other hashes for other menus here ?
1586
+ # but those hashes are not available
1548
1587
  end
1549
1588
  end # print_help
1550
1589
 
@@ -1601,7 +1640,7 @@ def debug_vars
1601
1640
  file.puts "sta #{@sta}"
1602
1641
  file.puts "cursor #{@cursor}"
1603
1642
  file.puts "stact #{@stact}"
1604
- file.puts "viewport.size #{@viewport.size}"
1643
+ file.puts "viewport.size #{@vps}"
1605
1644
  file.puts "pagesize #{@pagesize}"
1606
1645
  file.puts "view.size #{@view.size}"
1607
1646
  file.puts "grows #{@grows}"
@@ -1627,40 +1666,51 @@ def view_bookmarks
1627
1666
  end
1628
1667
 
1629
1668
  # MENU MAIN
1669
+ # maybe a list menu - options for date format, size format, age, truncate_from, inode etc
1630
1670
  def main_menu
1631
1671
  h = {
1632
- :a => :ag,
1633
- '/' => :ffind,
1634
- :l => :locate,
1635
- :v => :vidir,
1636
- :z => :z_interface,
1637
- :d => :child_dirs,
1638
- :r => :recent_files,
1639
- '1' => :select_from_visited_files,
1640
- '2' => :select_from_used_dirs,
1641
- '3' => :list_selected_files,
1642
- :t => :dirtree,
1643
- '4' => :tree,
1644
- :o => :order_menu,
1645
- :F => :filter_menu,
1646
- :c => :create_menu,
1647
- :b => :bookmark_menu,
1648
- :s => :selection_menu,
1649
- :x => :extras
1650
- }
1672
+ a: :ag,
1673
+ z: :z_interface,
1674
+ # f: :file_actions,
1675
+ b: :bookmark_menu,
1676
+ c: :create_menu,
1677
+ f: :filter_menu,
1678
+ o: :order_menu,
1679
+ s: :selection_menu,
1680
+ t: :toggle_menu,
1681
+ v: :view_menu,
1682
+ '`' => :goto_parent_dir,
1683
+ x: :extras
1684
+ }
1651
1685
  menu 'Main Menu', h
1652
1686
  end
1653
1687
 
1688
+ # if menu options returns a hash, then treat as another level menu and process
1689
+ # rather than having to create more of these. but these can be called directly too.
1690
+ def view_menu
1691
+ h = {
1692
+ f: :select_from_visited_files,
1693
+ d: :select_from_used_dirs,
1694
+ b: :view_bookmarks,
1695
+ s: :list_selected_files,
1696
+ c: :child_dirs,
1697
+ r: :recent_files,
1698
+ t: :tree,
1699
+ e: :dirtree
1700
+ }
1701
+ menu 'View Menu', h
1702
+ end
1703
+
1654
1704
  # copy and move here ?
1655
1705
  def selection_menu
1656
1706
  h = {
1657
- :a => :select_all,
1658
- :u => :unselect_all,
1659
- :s => :toggle_select,
1660
- '*' => 'toggle_multiple_selection',
1661
- 'x' => 'toggle_visual_mode',
1662
- 'm' => 'toggle_selection_mode',
1663
- :v => :view_selected_files
1707
+ a: :select_all,
1708
+ u: :unselect_all,
1709
+ s: :toggle_select,
1710
+ '*' => 'toggle_multiple_selection',
1711
+ 'x' => 'toggle_visual_mode',
1712
+ 'm' => 'toggle_selection_mode',
1713
+ v: :view_selected_files
1664
1714
  }
1665
1715
  menu 'Selection Menu', h
1666
1716
  end
@@ -1689,13 +1739,22 @@ def menu title, h
1689
1739
  # binding in brackets
1690
1740
  h.each_pair do |k, v|
1691
1741
  # get global binding
1692
- scut = @bindings.key(v.to_s)
1742
+ vs = v.to_s
1743
+ scut = @bindings.key(vs)
1693
1744
  scut = " (#{scut})" if scut
1694
1745
 
1695
- ary << " #{k}: #{v} #{scut}"
1746
+ # ary << " #{k}: #{v} #{scut}"
1747
+ vs = vs.sub('_menu', '...') if vs.end_with?('_menu')
1748
+ vs = vs.tr('_', ' ')
1749
+ ary << " [#{k}] #{vs} #{scut}"
1696
1750
  end
1697
1751
  x = ary.join("\n")
1698
- puts `echo "#{x}" | column`
1752
+ # echo column line bombs when x contains a single quote.
1753
+ # If i double quote it then it bombs with double quote or backtick
1754
+ # and prints the entire main menu two times.
1755
+ x = x.gsub("'", 'single quote')
1756
+ # x = x.gsub("`", "back-tick")
1757
+ puts `echo '#{x}' | column`
1699
1758
 
1700
1759
  key = get_char
1701
1760
  binding = h[key]
@@ -1712,46 +1771,6 @@ def menu title, h
1712
1771
  [key, binding]
1713
1772
  end
1714
1773
 
1715
- # some toggles can be invoked directly on a key, or through another menu.
1716
- # Some go to methods like multiple_selection from a key
1717
- # WARNING: method and toggle should not have same name, else flag will toggle twice.
1718
- def toggle_menu
1719
- h = { h: :show_hidden,
1720
- c: :ignore_case,
1721
- l: :long_listing,
1722
- '1' => :toggle_columns,
1723
- d: :group_directories,
1724
- :e => :editor_mode,
1725
- :D => :debug_flag,
1726
- '8' => :multiple_selection,
1727
- '*' => :multiple_selection,
1728
- 'S' => :selection_mode,
1729
- v: :visual_mode,
1730
- t: :truncate_from,
1731
- n: :enhanced_mode,
1732
- i: :instant_search,
1733
- H: :highlight_row_flag }
1734
- # TODO: add other values at end, what key ?
1735
-
1736
- _, menu_text = menu 'Toggle Menu', h
1737
- return unless menu_text
1738
-
1739
- # menu would have called if symbol only responds so return.
1740
- return if respond_to?(menu_text, true) # already handled
1741
-
1742
- # for visual and selection mode
1743
- # check if respond_to? symbol or toggle + symbol then call and return.
1744
- symb = "toggle_#{menu_text}".to_sym
1745
- @log.debug "trying #{symb}."
1746
- if respond_to?(symb, true)
1747
- @log.debug "calling #{symb}."
1748
- send(symb)
1749
- return
1750
- end
1751
-
1752
- cset menu_text
1753
- end
1754
-
1755
1774
  def toggle_columns
1756
1775
  @gviscols = if @gviscols == 1
1757
1776
  3
@@ -1778,7 +1797,7 @@ end
1778
1797
 
1779
1798
  def toggle_long_listing
1780
1799
  toggle_value :long_listing
1781
- @long_listing = cget(:long_listing)
1800
+ @long_listing = @toggles[:long_listing]
1782
1801
  if @long_listing
1783
1802
  @saved_gviscols = @gviscols
1784
1803
  @gviscols = 1
@@ -1792,64 +1811,48 @@ def toggle_long_listing
1792
1811
  @sta = @stact
1793
1812
  @stact = 0 # in case user was panned 2019-03-20 -
1794
1813
  end
1795
- message "Long listing is #{@long_listing}, visible columns is #{@gviscols}."
1796
- rescan_required
1814
+ message "Long listing is #{@long_listing}, date_func is #{@date_func}. visible columns is #{@gviscols}."
1815
+ # rescan_required
1797
1816
  end
1798
1817
 
1799
1818
  # ----------------- flag related functions -----------------------------------#
1800
1819
  # toggles the value of a toggle flag, also setting the variable if defined
1801
1820
  # WARN: be careful of variable being set directly. Replace such vars one by one.
1802
- # NOTE: please use cset
1803
1821
  def toggle_value flag
1804
- @toggles[flag] = !@toggles[flag]
1805
- # TODO: 2019-04-16 - can we set a variable so we don't have to check
1806
- # against a hash everywhere. variable name can be in hash too
1822
+ x = @toggles[flag] = !@toggles[flag]
1807
1823
  if instance_variable_defined? "@#{flag}"
1808
- instance_variable_set "@#{flag}", @toggles[flag]
1809
- @log.debug "instance_variable_set #{flag}, #{@toggles[flag]}"
1824
+ instance_variable_set "@#{flag}", x
1825
+ @log.debug "instance_variable_set #{flag}, #{x}"
1810
1826
  end
1811
- message "#{flag} is set to #{@toggles[flag]}"
1827
+ message "#{flag} is set to #{x}"
1812
1828
  end
1813
1829
 
1814
1830
  # rotates the value of an option that has multiple values
1815
- # NOTE: please use cset
1816
1831
  def rotate_value symb
1817
- curr = cget(symb) # get current value
1818
- index = @options[symb][:values].index(curr) || 0
1832
+ hash = @options[symb]
1833
+ curr = hash[:current]
1834
+ index = hash[:values].index(curr) || 0
1819
1835
  index += 1
1820
- index = 0 if index >= @options[symb][:values].count
1821
- x = @options[symb][:current] = @options[symb][:values][index]
1822
- var = @options[symb][:var]
1836
+ index = 0 if index >= hash[:values].count
1837
+ x = hash[:current] = hash[:values][index]
1838
+ var = hash[:var]
1823
1839
  instance_variable_set "@#{var}", x if var
1824
- message "#{symb} is set to #{@options[symb][:current]}. "
1840
+ message "#{symb} is set to #{x}. "
1825
1841
  end
1826
1842
 
1827
- # get the value of a flag given the symbol.
1828
- # NOTE: use this rather than access the hashes directly, since changes in structure
1829
- # may happen
1830
- def cget symb
1831
- return @toggles[symb] if @toggles.key? symb
1832
- return @options[symb][:current] if @options.key? symb
1833
-
1834
- @log.warn "CGET: #{symb} does not exist. Please check code."
1835
- raise ArgumentError, "CGET: #{symb} does not exist. Please check code."
1836
- end
1837
-
1838
- # toggles or rotates the value of a flag
1839
1843
  def cset symb
1844
+ return if symb.nil? || symb == :''
1845
+
1840
1846
  if @toggles.key? symb
1841
1847
  toggle_value symb
1842
- rescan_required
1843
- return true
1844
- end
1845
- if @options.key? symb
1848
+ elsif @options.key? symb
1846
1849
  rotate_value symb
1847
- rescan_required
1848
- return true
1850
+ else
1851
+ @log.warn "CSET: #{symb} does not exist. Please check code."
1852
+ raise ArgumentError, "CSET: (#{symb}) does not exist. Please check code."
1849
1853
  end
1850
-
1851
- @log.warn "CSET: #{symb} does not exist. Please check code."
1852
- raise ArgumentError, "CSET: #{symb} does not exist. Please check code."
1854
+ rescan_required
1855
+ return true
1853
1856
  end
1854
1857
  # ----------------- end of flag related functions ----------------------------#
1855
1858
 
@@ -1887,6 +1890,8 @@ def order_menu
1887
1890
  lo = '/'
1888
1891
  when :clear
1889
1892
  lo = ''
1893
+ else
1894
+ return
1890
1895
  end
1891
1896
  ## This needs to persist and be a part of all listings, put in change_dir.
1892
1897
  @sorto = lo
@@ -1903,74 +1908,26 @@ def create_menu
1903
1908
  _, menu_text = menu 'Create Menu', h
1904
1909
  end
1905
1910
 
1906
- # thse need to be placed in correct position, some do nothing
1907
- # and some like ffind have no menu item
1908
- # TODO FIXME uncalled now. look into this
1909
- def command_menu
1910
- # since these involve full paths, we need more space, like only one column
1911
- ## in these cases, getting back to the earlier dir, back to earlier listing
1912
- # since we've basically overlaid the old listing
1913
- # should be able to sort THIS listing and not rerun command. But for that I'd need to use
1914
- # xargs ls -t etc rather than the zsh sort order. But we can run a filter using |.
1915
- #
1916
- h = { t: :today, D: :default_command, R: :remove_from_list,
1917
- v: :view_selected_files }
1918
- h[:e] = if @editor_mode
1919
- :pager_mode
1920
- else
1921
- :editor_mode
1922
- end
1923
- _, menu_text = menu 'Command Menu', h
1924
- case menu_text
1925
- when :pager_mode
1926
- @editor_mode = false
1927
- @default_command = ENV['MANPAGER'] || ENV['PAGER']
1928
- when :editor_mode
1929
- @editor_mode = true
1930
- @default_command = nil
1931
- when :ffind
1932
- ffind
1933
- when :locate
1934
- locate
1935
- when :today
1936
- # zshglob: M = MARK_DIRS with slash
1937
- # zshglob: 'm0' 'm' = modified time, '0' = 0 days ago
1938
- @files = `zsh -c 'print -rl -- *(#{@hidden}Mm0)'`.split("\n")
1939
- @title = "Today's files"
1940
- when :default_command
1941
- puts ' This no longer works'
1942
- puts 'Selecting a file usually invokes $EDITOR'
1943
- puts 'What command do you want to use repeatedly on selected files: '
1944
- @default_command = gets.chomp
1945
- if @default_command != ''
1946
- print 'Second part of command (maybe blank): '
1947
- @default_command2 = gets.chomp
1948
- else
1949
- print 'Cleared default command, will default to $EDITOR'
1950
- @default_command2 = nil
1951
- @default_command = nil
1952
- end
1953
- end
1954
- # redraw
1955
- end
1956
-
1957
1911
  # This is quite badly placed and named. Maybe these should go elsewhere
1958
1912
  def extras
1959
- h = { '1' => :one_column,
1960
- '2' => :multi_column,
1961
- c: :columns,
1962
- s: :scripts,
1963
- g: :generators,
1964
- B: :bindkey_ext_command,
1965
- f: :page_flags,
1966
- r: :config_read,
1967
- w: :config_write }
1913
+ h = {
1914
+ '1' => :one_column,
1915
+ '2' => :multi_column,
1916
+ c: :columns,
1917
+ s: :scripts,
1918
+ g: :generators,
1919
+ B: :bindkey_ext_command,
1920
+ f: :page_flags,
1921
+ R: :remove_from_list,
1922
+ v: :vidir,
1923
+ r: :config_read,
1924
+ w: :config_write
1925
+ }
1968
1926
  key, menu_text = menu 'Extras Menu', h
1969
1927
  case menu_text
1970
1928
  when :one_column
1971
1929
  @pagesize = @grows
1972
1930
  when :multi_column
1973
- # @pagesize = 60
1974
1931
  @pagesize = @grows * @gviscols
1975
1932
  when :columns
1976
1933
  print "How many columns to show: 1-6 [current #{@gviscols}]? "
@@ -1984,8 +1941,11 @@ def extras
1984
1941
  end
1985
1942
 
1986
1943
  def filter_menu
1987
- h = { :d => :dirs, :f => :files, :e => :emptydirs, '0' => :emptyfiles,
1988
- :r => :reduce_list, :x => :extension }
1944
+ h = { d: :dirs, f: :files,
1945
+ e: :emptydirs, '0' => :emptyfiles,
1946
+ r: :recent_files,
1947
+ a: :reduce_list, # key ??? XXX
1948
+ x: :extension }
1989
1949
  _, menu_text = menu 'Filter Menu', h
1990
1950
  files = nil
1991
1951
  case menu_text
@@ -2013,11 +1973,18 @@ def filter_menu
2013
1973
  files = reduce
2014
1974
  when :extension
2015
1975
  files = filter_for_current_extension
1976
+ when :recent_files
1977
+ # files = recent_files
1978
+ @title = 'Filter: files by mtime'
1979
+ files = get_files_by_mtime.first(10)
1980
+ else
1981
+ return
2016
1982
  end
2017
1983
  if files && files.count > 0
2018
1984
  @files = files
2019
1985
  @stact = 0
2020
1986
  @message = "Filtered on #{menu_text}. Press ESC to return."
1987
+ @bm = ' (' + @bindings.key('filter_menu') + ') '
2021
1988
  else
2022
1989
  perror 'Sorry, No files. '
2023
1990
  @title = nil
@@ -2026,11 +1993,11 @@ end
2026
1993
 
2027
1994
  def reduce pattern=nil
2028
1995
  unless pattern
2029
- last_line 'Enter a pattern to reduce current list: '
2030
- pattern = gets.chomp
1996
+ pattern = readline 'Enter a pattern to reduce current list: '
2031
1997
  end
2032
1998
  @title = "Filter: pattern #{pattern}"
2033
- @files = @files.select { |f| f.match(pattern) }
1999
+ @bm = "(%r)"
2000
+ @files = @files.select { |f| f.match?(pattern) }
2034
2001
  end
2035
2002
 
2036
2003
  def filter_for_current_extension
@@ -2044,19 +2011,18 @@ def select_from_used_dirs
2044
2011
  @title = 'Used Directories'
2045
2012
  home = File.expand_path '~'
2046
2013
  @files = @used_dirs.uniq.map { |path| path.sub(home.to_s, '~') }
2014
+ @bm = @bindings.key('select_from_used_dirs')
2015
+ @bm = ' (' + @bm + ')' if @bm
2016
+ # redraw_required
2047
2017
  end
2048
2018
 
2049
2019
  def select_from_visited_files
2050
- # not yet a unique list, needs to be unique and have latest pushed to top
2051
2020
  @title = 'Visited Files'
2052
2021
  home = File.expand_path '~'
2053
2022
  @files = @visited_files.uniq.map { |path| path.sub(home.to_s, '~') }
2054
- end
2055
-
2056
- # maybe unused ??? XXX
2057
- def select_bookmarks
2058
- @title = 'Bookmarks'
2059
- @files = @bookmarks.values
2023
+ @bm = @bindings.key('select_from_visited_files')
2024
+ @bm = ' (' + @bm + ')' if @bm
2025
+ # redraw_required
2060
2026
  end
2061
2027
 
2062
2028
  ## part copied and changed from change_dir since we don't dir going back on top
@@ -2069,6 +2035,7 @@ def pop_dir
2069
2035
  ## XXX make sure the dir exists, cuold have been deleted. can be an error or crash otherwise
2070
2036
  @visited_dirs.push d
2071
2037
  Dir.chdir d
2038
+ @current_dir = Dir.pwd # 2019-04-24 - earlier was in post_cd but too late
2072
2039
  post_cd
2073
2040
  rescan_required
2074
2041
  end
@@ -2079,6 +2046,7 @@ def post_cd
2079
2046
  @sta = @cursor = @stact = 0
2080
2047
  @visual_block_start = nil
2081
2048
  screen_settings
2049
+ calculate_bookmarks_for_dir
2082
2050
 
2083
2051
  # goto last position cursor was in this dir
2084
2052
  revert_dir_pos
@@ -2172,37 +2140,31 @@ end
2172
2140
  # Was this supposed to be augmented, or just remain limited like this
2173
2141
  # We should be able to do everything in the menus from here. TODO
2174
2142
  def subcommand
2175
- last_line
2176
- print 'Enter command: q x wq p w e r h '
2143
+ # clear_last_line
2144
+ # pbold 'Subcommand:'
2177
2145
  begin
2178
- command = readline
2146
+ prompt = %(
2147
+ [q] quit [w] config write [d] delete
2148
+ [x] update config + quit [r] config read [r] rename
2149
+ [wq] write config + quit [e] edit file under cursor [m] move
2150
+ [P] copy PWD to clipboard [o] open file under cursor [c] copy
2151
+ [p] copy filename to clipboard [h] help [t] toggle flags
2152
+ )
2153
+ # command = readline 'Enter command: q x wq P p w e r h :'
2154
+ command = readline prompt
2179
2155
  return if command == ''
2180
2156
  rescue StandardError
2181
2157
  return
2182
2158
  end
2183
2159
  if command == 'q'
2184
- # FIXME: 2019-03-22 - should this not call quit_command ?
2185
- if @modified
2186
- last_line
2187
- print 'Do you want to save bookmarks? (y/n): '
2188
- key = get_char
2189
- if key == 'y'
2190
- @writing = true
2191
- @quitting = true
2192
- elsif key == 'n'
2193
- @quitting = true
2194
- print 'Quitting without saving bookmarks'
2195
- else
2196
- perror 'No action taken.'
2197
- end
2198
- else
2199
- @quitting = true
2200
- end
2160
+ quit_command
2201
2161
  elsif command == 'wq'
2202
2162
  @quitting = true
2203
2163
  @writing = true
2204
2164
  elsif command == 'w'
2205
2165
  config_write
2166
+ elsif command == 'r'
2167
+ config_read
2206
2168
  elsif command == 'x'
2207
2169
  @quitting = true
2208
2170
  @writing = true if @modified
@@ -2210,16 +2172,66 @@ def subcommand
2210
2172
  edit_current
2211
2173
  elsif command == 'o'
2212
2174
  open_current
2213
- elsif (command == 'h') || (command == 'help') || (command == '?')
2175
+ elsif command == 'd'
2176
+ delete_file
2177
+ elsif command == 'c'
2178
+ copy_file
2179
+ elsif command == 'r'
2180
+ rename_file
2181
+ elsif command == 'm'
2182
+ move_file
2183
+ elsif command == 'h' || command == 'help' || command == '?'
2214
2184
  print_help
2215
- elsif command == 'p'
2185
+ elsif command == 'P'
2186
+ # or should be put current file in clip ?
2216
2187
  system 'echo $PWD | pbcopy'
2217
- puts 'Stored PWD in clipboard (using pbcopy)'
2188
+ message 'Stored PWD in clipboard (using pbcopy)'
2189
+ elsif command == 'p'
2190
+ system "echo #{current_file} | pbcopy"
2191
+ message "Stored #{current_file} in clipboard (using pbcopy)"
2192
+ elsif command == 't' || command == 'toggle'
2193
+ toggle_menu
2218
2194
  else
2219
2195
  perror "Don't know about command #{command}. Try :h or :help"
2220
2196
  end
2221
2197
  end
2222
2198
 
2199
+ # 2019-03-08 - 23:46
2200
+ # FIXME returns a String not symbol, so some callers can fail
2201
+ def fzfmenu(title, h)
2202
+ return unless h
2203
+
2204
+ pbold title.to_s
2205
+ # XXX using keys not values since toggle hash being used
2206
+ values = h.keys.join("\n")
2207
+ binding = `echo "#{values}" | fzf --prompt="#{title.to_s} :"`
2208
+ if binding
2209
+ binding = binding.chomp
2210
+ send(binding) if respond_to?(binding, true)
2211
+ end
2212
+ binding
2213
+ end
2214
+
2215
+ def toggle_menu
2216
+ binding = fzfmenu 'Toggles', @toggles.merge(@options)
2217
+ return if binding.nil? || binding == ''
2218
+
2219
+ menu_text = binding.to_sym
2220
+ return if respond_to?(menu_text, true) # already handled
2221
+
2222
+ # for visual and selection mode
2223
+ # check if respond_to? symbol or toggle + symbol then call and return.
2224
+ symb = "toggle_#{menu_text}".to_sym
2225
+ @log.debug "trying #{symb}."
2226
+ if respond_to?(symb, true)
2227
+ @log.debug "calling #{symb}."
2228
+ send(symb)
2229
+ return
2230
+ end
2231
+
2232
+ cset menu_text
2233
+ end
2234
+
2223
2235
  def quit_command
2224
2236
  # if we are in some mode, like search results then 'q' should come out.
2225
2237
  if @mode
@@ -2248,6 +2260,7 @@ def views
2248
2260
  @viewctr = 0 if @viewctr > views.size
2249
2261
 
2250
2262
  @files = `zsh -c 'print -rl -- *(#{@sorto}#{@hidden}M)'`.split("\n")
2263
+ redraw_required
2251
2264
  end
2252
2265
 
2253
2266
  def child_dirs
@@ -2340,7 +2353,7 @@ def get_shortcut index
2340
2353
  # Now we show unused shortcuts after exhausting them.
2341
2354
  # return '<' if index < @stact
2342
2355
  if index < @stact
2343
- index = @viewport.size - @stact + index
2356
+ index = @vps - @stact + index
2344
2357
  i = IDX[index]
2345
2358
  return i if i
2346
2359
 
@@ -2387,22 +2400,21 @@ def convert_key_to_index key
2387
2400
  i = IDX.index(key)
2388
2401
  return nil unless i
2389
2402
 
2390
- # @log.debug "get_index with #{key}: #{i}. #{@stact}. #{@viewport.size}"
2391
- vps = @viewport.size
2403
+ # @log.debug "get_index with #{key}: #{i}. #{@stact}. #{@vps}"
2392
2404
  # TODO: if very high key given, consider going to last file ?
2393
2405
  # that way one can press zz or ZZ to go to last file.
2394
2406
  # 2019-04-11 - XXX actually this doesnt place the cursor on last file
2395
2407
  # it opens it, which may not be what we want
2396
2408
  retnil = nil # vps - 1 # nil
2397
2409
  # user has entered a key that is outside of range, return nil
2398
- return retnil if @stact == 0 && i + @stact >= vps
2410
+ return retnil if @stact == 0 && i + @stact >= @vps
2399
2411
 
2400
2412
  # again, key out of range
2401
- # return nil if @stact > 0 && i + @stact >= vps && i + @stact - vps >= @stact
2402
- return retnil if @stact > 0 && i + @stact >= vps && i - vps >= 0
2413
+ # return nil if @stact > 0 && i + @stact >= @vps && i + @stact - @vps >= @stact
2414
+ return retnil if @stact > 0 && i + @stact >= @vps && i - @vps >= 0
2403
2415
 
2404
2416
  # panning case, hints are recycled
2405
- return (i + @stact) - vps if i + @stact >= vps
2417
+ return (i + @stact) - @vps if i + @stact >= @vps
2406
2418
 
2407
2419
  # regular hint
2408
2420
  return i + @stact # if i
@@ -2544,7 +2556,7 @@ def get_unique_file_name fname
2544
2556
  return f unless File.exist?(f)
2545
2557
  end
2546
2558
 
2547
- timestamp = Time.now.strftime("%Y%m%d-%H%M%S")
2559
+ timestamp = Time.now.strftime('%Y%m%d-%H%M%S')
2548
2560
  return fname + '.' + timestamp
2549
2561
  end
2550
2562
 
@@ -2630,6 +2642,7 @@ end
2630
2642
 
2631
2643
  # instantly move to move target. Use during bulk operations so you don't have to
2632
2644
  # enter or goto target dir and come back.
2645
+ # XXX try removing this, making things complex.
2633
2646
  def move_instant
2634
2647
  # FIXME: cannot be in target directory and do auto update
2635
2648
  # should we have a key for specifying move_target ?
@@ -2691,7 +2704,7 @@ def command_file prompt, *command
2691
2704
  perror 'Command Cancelled' unless file
2692
2705
  return unless file
2693
2706
 
2694
- file = File.expand_path(file)
2707
+ file = expand_path(file)
2695
2708
  if File.exist? file
2696
2709
  file = Shellwords.escape(file)
2697
2710
  pbold "#{command} #{file} (#{pauseyn})"
@@ -2711,7 +2724,7 @@ def ask_hint deflt=nil
2711
2724
  key = get_char
2712
2725
  return deflt if key == 'ENTER'
2713
2726
 
2714
- ix = get_index(key, @viewport.size)
2727
+ ix = get_index(key, @vps)
2715
2728
  f = @viewport[ix] if ix
2716
2729
  f
2717
2730
  end
@@ -2739,11 +2752,11 @@ def column_next direction=0
2739
2752
  # leftward movement stops at first column.
2740
2753
  if direction == 0
2741
2754
  @stact += @grows
2742
- @stact = 0 if @stact >= @viewport.size
2755
+ @stact = 0 if @stact >= @vps
2743
2756
  @cursor += @grows
2744
2757
  # 2019-03-18 - zero loses offset. we need to maintain it
2745
- # @cursor = 0 if @cursor >= @viewport.size
2746
- if @cursor - @sta >= @viewport.size
2758
+ # @cursor = 0 if @cursor >= @vps
2759
+ if @cursor - @sta >= @vps
2747
2760
  @cursor -= @grows while @cursor > @sta
2748
2761
  @stact -= @grows while @stact > 0
2749
2762
  @cursor += @grows if @cursor < @sta
@@ -2764,18 +2777,27 @@ end
2764
2777
  # 2019-03-08 - TODO when a file name changes or moves it must be removed
2765
2778
  # from selection
2766
2779
  def file_actions action=nil
2767
- h = { d: :delete, D: '/bin/rm', m: :move, v: ENV['EDITOR'] || :vim,
2768
- c: :copy, e: :execute,
2769
- l: :less, p: :most,
2770
- o: :open_current }
2780
+ h = {
2781
+ d: :delete,
2782
+ D: '/bin/rm',
2783
+ m: :move,
2784
+ c: :copy,
2785
+ r: :rename,
2786
+ e: :execute,
2787
+ v: ENV['EDITOR'] || :vim,
2788
+ o: :open_current,
2789
+ p: :most
2790
+ }
2771
2791
 
2772
2792
  rbfiles = current_or_selected_files # use with ruby FileUtils
2773
2793
  return if rbfiles.nil? || rbfiles.empty?
2774
2794
 
2775
2795
  count = rbfiles.count
2776
2796
  first = rbfiles.first
2797
+
2798
+ h.delete(:r) if count > 1
2799
+
2777
2800
  if count == 1
2778
- h[:r] = :rename
2779
2801
 
2780
2802
  # add chdir if dir of file under cursor is not same as current dir
2781
2803
  h[:C] = :chdir if File.dirname(File.expand_path(first)) != Dir.pwd
@@ -2795,6 +2817,8 @@ def file_actions action=nil
2795
2817
  end
2796
2818
  h[:M] = :set_move_target if File.directory?(current_file)
2797
2819
  h[:z] = :zip unless filetype(first) == :zip
2820
+ h['/'] = :ffind
2821
+ h[:l] = :locate
2798
2822
 
2799
2823
  # if first file has spaces then add remspace method
2800
2824
  # TODO: put this in scripts
@@ -2840,10 +2864,6 @@ def file_actions action=nil
2840
2864
  rename_file
2841
2865
 
2842
2866
  when :chdir
2843
- # will only work if one selected. Check this out
2844
- # This works if you have searched for files and got a list
2845
- # with different paths.
2846
- # This should not be shown in regular listings XXX
2847
2867
  change_dir File.dirname(File.expand_path(rbfiles.first)) if count == 1
2848
2868
 
2849
2869
  when :set_move_target
@@ -2865,6 +2885,8 @@ def file_actions action=nil
2865
2885
  1
2866
2886
  # already been executed by menu
2867
2887
  # We could have just put 'stat' in the menu but that doesn't look so nice
2888
+ when :locate
2889
+ 1
2868
2890
  else
2869
2891
  return unless menu_text
2870
2892
 
@@ -2887,13 +2909,13 @@ end
2887
2909
  # remove non-existent files from select list due to move or delete
2888
2910
  # or rename or whatever
2889
2911
  def clean_selected_files
2890
- @selected_files.select! { |x| x = File.expand_path(x); File.exist?(x) }
2912
+ @selected_files.select! { |x| x = expand_path(x); File.exist?(x) }
2891
2913
  end
2892
2914
 
2893
2915
  # set the default target for further moves
2894
2916
  # We need to also be able to set it to current dir in which user is. TODO
2895
2917
  def set_move_target cf=current_file
2896
- ff = File.expand_path(cf)
2918
+ ff = expand_path(cf)
2897
2919
  return unless File.directory? ff
2898
2920
 
2899
2921
  @move_target = ff
@@ -2973,18 +2995,19 @@ def ffind
2973
2995
  end
2974
2996
 
2975
2997
  def locate
2976
- print 'Enter a file name pattern to locate: '
2977
- pattern = readline
2998
+ @locate_command ||= /darwin/.match?(RUBY_PLATFORM) ? 'mdfind -name' : 'locate'
2999
+ pattern = readline "Enter a file name pattern to #{@locate_command}: "
2978
3000
  return if pattern == ''
2979
3001
 
2980
- @title = "Files found using 'locate' #{pattern}"
2981
- files = `locate #{pattern}`.split("\n")
2982
- files.select! { |x| x = File.expand_path(x); File.exist?(x) }
3002
+ @title = "Files found using: (#{@locate_command} #{pattern})"
3003
+ files = `#{@locate_command} #{pattern}`.split("\n")
3004
+ files.select! { |x| x = expand_path(x); File.exist?(x) }
2983
3005
  if files.empty?
2984
3006
  perror 'No files found.'
2985
- else
2986
- @files = files
3007
+ return
2987
3008
  end
3009
+ @files = files
3010
+ @bm = nil
2988
3011
  end
2989
3012
 
2990
3013
  ## takes directories from the z program, if you use autojump you can
@@ -3065,7 +3088,7 @@ def move_to position
3065
3088
  oldsta = @sta
3066
3089
  if @cursor - @sta >= @pagesize
3067
3090
  @sta += @pagesize
3068
- # elsif @sta - @cursor >= @viewport.size
3091
+ # elsif @sta - @cursor >= @vps
3069
3092
  end
3070
3093
  if @sta > @cursor
3071
3094
  @sta -= @pagesize
@@ -3082,7 +3105,8 @@ def move_to position
3082
3105
  @cursor_movement = nil # visual mode needs to redraw page
3083
3106
 
3084
3107
  # PWD has to be there in selction
3085
- if selected? current_file
3108
+ # FIXME with visited_files
3109
+ if selected? File.join(@current_dir, current_file)
3086
3110
  # this depends on the direction
3087
3111
  # @selected_files = @selected_files - @view[star..fin]
3088
3112
  remove_from_selection @view[star..fin]
@@ -3104,6 +3128,8 @@ end
3104
3128
  # ------------- selection related methods --------------------------------#
3105
3129
 
3106
3130
  # is given file in selected array
3131
+ # 2019-04-24 - now takes fullname so path addition does not keep happening in
3132
+ # a loop in draw directory.
3107
3133
  def selected? fullname
3108
3134
  return @selected_files.index fullname
3109
3135
  end
@@ -3117,7 +3143,9 @@ def add_to_selection file
3117
3143
  end
3118
3144
  @current_dir ||= Dir.pwd
3119
3145
  ff.each do |f|
3120
- full = File.join(@current_dir, f)
3146
+ # this is wrong if the file is a dir listing or visited files listing.
3147
+ # full = File.join(@current_dir, f)
3148
+ full = expand_path(f)
3121
3149
  @selected_files.push(full) unless @selected_files.include?(full)
3122
3150
  end
3123
3151
  end
@@ -3130,7 +3158,8 @@ def remove_from_selection file
3130
3158
  end
3131
3159
  @current_dir ||= Dir.pwd
3132
3160
  ff.each do |f|
3133
- full = File.join(@current_dir, f)
3161
+ # full = File.join(@current_dir, f)
3162
+ full = expand_path(f)
3134
3163
  @selected_files.delete full
3135
3164
  end
3136
3165
  end
@@ -3290,7 +3319,7 @@ def create_a_file
3290
3319
 
3291
3320
  system %($EDITOR "#{str}")
3292
3321
  setup_terminal
3293
- @visited_files.insert(0, str) if File.exist?(str)
3322
+ @visited_files.insert(0, expand_path(str)) if File.exist?(str)
3294
3323
  refresh
3295
3324
  end
3296
3325
 
@@ -3338,6 +3367,7 @@ def scripts binding=nil
3338
3367
 
3339
3368
  # reset clears the screen, we don't want that. just unhide cursor and echo keys TODO
3340
3369
  partial_reset_terminal
3370
+ @log.info "Calling #{binding}."
3341
3371
  system %( #{binding} "#{current_file}" )
3342
3372
 
3343
3373
  # system %(echo "#{cf}" | xargs #{binding})
@@ -3400,6 +3430,9 @@ end
3400
3430
  def list_selected_files
3401
3431
  @title = 'Selected Files'
3402
3432
  @files = @selected_files
3433
+
3434
+ @bm = @bindings.key('list_selected_files')
3435
+ @bm = ' (' + @bm + ')' if @bm
3403
3436
  end
3404
3437
 
3405
3438
  # write selected files to a file and return path
@@ -3410,7 +3443,7 @@ def write_selected_files
3410
3443
  # fname = File.join(File.dirname(CONFIG_FILE), 'selected_files')
3411
3444
  # 2019-04-10 - changed to ~/tmp otherwise confusion about location
3412
3445
  fname = File.join('~/tmp/', 'selected_files')
3413
- fname = File.expand_path(fname)
3446
+ fname = expand_path(fname)
3414
3447
 
3415
3448
  # remove file if no selection
3416
3449
  if @selected_files.empty?
@@ -3439,40 +3472,36 @@ end
3439
3472
  ##
3440
3473
  # Editing of the User Dir List.
3441
3474
  # remove current entry from used dirs list, since we may not want some entries being there
3442
- #
3475
+ # Need to call this from somewhere. Test it out again.
3476
+ # Usage. Invoke `1` or `2` and select some files and then call remove
3443
3477
  def remove_from_list
3444
- unless @selected_files.empty?
3445
- sz = @selected_files.size
3446
- print "Remove #{sz} files from used list (y)?: "
3447
- key = get_char
3448
- return if key != 'y'
3449
-
3450
- arr = @selected_files.map { |path| File.expand_path(path) }
3451
3478
 
3452
- @used_dirs = @used_dirs - arr
3453
- @visited_files = @visited_files - arr
3454
- unselect_all
3455
- @modified = true
3456
- refresh
3457
- return
3458
- end
3459
-
3460
- # no file selected, use file under cursor
3461
- print
3462
- ## what if selected some rows
3463
- file = @view[@cursor]
3464
- print "Remove #{file} from used list (y)?: "
3479
+ # XXX key to invoke this is difficult. make it easier
3480
+ selfiles = current_or_selected_files
3481
+ sz = selfiles.size
3482
+ print "Remove #{sz} files from used list (y)?: "
3465
3483
  key = get_char
3466
3484
  return if key != 'y'
3467
3485
 
3468
- file = File.expand_path(file)
3469
- if File.directory? file
3470
- @used_dirs.delete(file)
3486
+ # arr = @selected_files.map { |path| File.expand_path(path) }
3487
+ # @log.debug "BEFORE: Selected files are: #{@selected_files}"
3488
+ arr = selfiles.map { |path|
3489
+ if path[0] != '/'
3490
+ expand_path(path)
3491
+ else
3492
+ path
3493
+ end
3494
+ }
3495
+ if File.directory? arr.first
3496
+ @used_dirs -= arr
3497
+ select_from_used_dirs
3471
3498
  else
3472
- @visited_files.delete(file)
3499
+ @visited_files -= arr
3500
+ select_from_visited_files
3473
3501
  end
3474
- refresh
3502
+ unselect_all
3475
3503
  @modified = true
3504
+ # redraw_required
3476
3505
  end
3477
3506
 
3478
3507
  #
@@ -3482,7 +3511,6 @@ end
3482
3511
  # latest source, but in some cases even this can be misleading since running a program accesses
3483
3512
  # include files.
3484
3513
  def enhance_file_list
3485
- # return unless cget(:enhanced_mode)
3486
3514
  return unless @enhanced_mode
3487
3515
  @current_dir ||= Dir.pwd
3488
3516
 
@@ -3703,7 +3731,13 @@ def gfb dir, func
3703
3731
  dir = dir.gsub('//', '/')
3704
3732
 
3705
3733
  # sort by time and then reverse so latest first.
3706
- sorted_files = Dir[dir].sort_by { |f| File.send(func, f) }.reverse
3734
+ sorted_files = Dir[dir].sort_by { |f|
3735
+ if File.exist? f
3736
+ File.send(func, f)
3737
+ else
3738
+ sys_stat(func, f)
3739
+ end
3740
+ }.reverse
3707
3741
 
3708
3742
  # add slash to directories
3709
3743
  sorted_files = add_slash sorted_files
@@ -3789,12 +3823,25 @@ def only_cursor_moved flag=true
3789
3823
  @cursor_movement = flag
3790
3824
  end
3791
3825
 
3826
+ # Had to create this since ruby crashes on files start with '~'
3827
+ def expand_path file
3828
+ return File.expand_path(file) if file.start_with?("~/")
3829
+ return file if file[0] == '/'
3830
+
3831
+ return File.join(@current_dir, file)
3832
+ end
3833
+
3792
3834
  # main loop which calls all other programs
3793
3835
  def run
3794
3836
  Signal.trap('EXIT') do
3795
3837
  reset_terminal
3796
3838
  exit
3797
3839
  end
3840
+ Signal.trap('WINCH') do
3841
+ screen_settings
3842
+ redraw
3843
+ place_cursor
3844
+ end
3798
3845
 
3799
3846
  setup_terminal
3800
3847
  config_read