cetus 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/cetus +467 -420
- data/cetus.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e62bb94776477721739341ff4f03ac93390501da588709f3115a2a4c4ee3ca0f
|
4
|
+
data.tar.gz: 496df10e6710d503de9d4aa2334ac932286b1cb7c23ccc4ef57af02bb310496d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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-
|
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.
|
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
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
93
|
+
'D' => 'delete_file',
|
90
94
|
# 'M' => 'file_actions most',
|
91
|
-
|
92
|
-
|
95
|
+
'M' => 'move_instant',
|
96
|
+
'q' => 'quit_command', # was Q now q 2019-03-04 -
|
93
97
|
# "RIGHT" => "column_next",
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
'M--' => 'columns_incdec -1: reduce column width',
|
100
|
-
'M-+' => 'columns_incdec 1: increase column width',
|
101
|
-
'
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
365
|
-
|
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
|
-
|
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
|
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
|
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 + @
|
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},#{@
|
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
|
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 "
|
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
|
882
|
-
|
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
|
-
|
980
|
-
color
|
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
|
-
|
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
|
-
|
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
|
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(
|
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.
|
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
|
-
|
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?
|
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
|
-
|
1192
|
-
|
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
|
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 =
|
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 =
|
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 =
|
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 =
|
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
|
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
|
1485
|
-
@title = 'Search Results: (Press
|
1514
|
+
def filter_files_by_pattern
|
1515
|
+
@title = 'Search Results: (Press Esc to cancel)'
|
1486
1516
|
@mode = 'SEARCH'
|
1487
|
-
if
|
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
|
1558
|
+
file.puts %(
|
1559
|
+
#{REVERSE} HELP #{CLEAR}
|
1527
1560
|
|
1528
|
-
|
1529
|
-
|
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
|
1536
|
-
file.
|
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
|
-
|
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 #{@
|
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
|
-
:
|
1633
|
-
|
1634
|
-
:
|
1635
|
-
:
|
1636
|
-
:
|
1637
|
-
:
|
1638
|
-
:
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
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
|
-
:
|
1658
|
-
:
|
1659
|
-
:
|
1660
|
-
|
1661
|
-
|
1662
|
-
|
1663
|
-
:
|
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
|
-
|
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
|
-
|
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 =
|
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}",
|
1809
|
-
@log.debug "instance_variable_set #{flag}, #{
|
1824
|
+
instance_variable_set "@#{flag}", x
|
1825
|
+
@log.debug "instance_variable_set #{flag}, #{x}"
|
1810
1826
|
end
|
1811
|
-
message "#{flag} is set to #{
|
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
|
-
|
1818
|
-
|
1832
|
+
hash = @options[symb]
|
1833
|
+
curr = hash[:current]
|
1834
|
+
index = hash[:values].index(curr) || 0
|
1819
1835
|
index += 1
|
1820
|
-
index = 0 if index >=
|
1821
|
-
x =
|
1822
|
-
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 #{
|
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
|
-
|
1843
|
-
return true
|
1844
|
-
end
|
1845
|
-
if @options.key? symb
|
1848
|
+
elsif @options.key? symb
|
1846
1849
|
rotate_value symb
|
1847
|
-
|
1848
|
-
|
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
|
-
|
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 = {
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
1963
|
-
|
1964
|
-
|
1965
|
-
|
1966
|
-
|
1967
|
-
|
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 = { :
|
1988
|
-
:
|
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
|
-
|
2030
|
-
pattern = gets.chomp
|
1996
|
+
pattern = readline 'Enter a pattern to reduce current list: '
|
2031
1997
|
end
|
2032
1998
|
@title = "Filter: pattern #{pattern}"
|
2033
|
-
@
|
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
|
-
|
2055
|
-
|
2056
|
-
#
|
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
|
-
|
2176
|
-
|
2143
|
+
# clear_last_line
|
2144
|
+
# pbold 'Subcommand:'
|
2177
2145
|
begin
|
2178
|
-
|
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
|
-
|
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
|
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 == '
|
2185
|
+
elsif command == 'P'
|
2186
|
+
# or should be put current file in clip ?
|
2216
2187
|
system 'echo $PWD | pbcopy'
|
2217
|
-
|
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 = @
|
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}. #{@
|
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(
|
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 =
|
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, @
|
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 >= @
|
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 >= @
|
2746
|
-
if @cursor - @sta >= @
|
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 = {
|
2768
|
-
|
2769
|
-
|
2770
|
-
|
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 =
|
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 =
|
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
|
-
|
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
|
2981
|
-
files =
|
2982
|
-
files.select! { |x| 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
|
-
|
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 >= @
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
3453
|
-
|
3454
|
-
|
3455
|
-
|
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
|
-
|
3469
|
-
|
3470
|
-
|
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
|
3499
|
+
@visited_files -= arr
|
3500
|
+
select_from_visited_files
|
3473
3501
|
end
|
3474
|
-
|
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|
|
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
|