cetus 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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