cetus 0.1.34 → 0.1.36
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/cetus +516 -250
- data/cetus.gemspec +1 -1
- data/scripts/create_dir_with_selection +21 -0
- data/scripts/encrypt.sh +17 -0
- data/scripts/zip +20 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 273d25acae029a8c12d1e07bd80396c7f6eeb05f6136b112be702a7cebd7a01d
|
4
|
+
data.tar.gz: 25891b9fc8105132be8cb4325f23767fc2abf79096b98bcee9c11a41f0ffb612
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 905206ed55286799c28ef2e3c9fee6fc1eedc34730ac4632138c117ce747be62da7ed377c44414ba8499f3cfc2a2b539c4fb2b868522ef9ffc775aa0528b1d43
|
7
|
+
data.tar.gz: 53e8235558e298e9f08dbaefd5f65fcde07a821e8052ee630db51bc74e340a56ddae0abc430028dfda702700c659686ad449c342250d4cc9a26ef5387eadb98c
|
data/bin/cetus
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# Author: rkumar http://github.com/rkumar/cetus/
|
7
7
|
# Date: 2013-02-17 - 17:48
|
8
8
|
# License: GPL
|
9
|
-
# Last update: 2019-04-
|
9
|
+
# Last update: 2019-04-14 19:11
|
10
10
|
# --------------------------------------------------------------------------- #
|
11
11
|
# cetus.rb Copyright (C) 2012-2019 rahul kumar
|
12
12
|
# == CHANGELOG
|
@@ -16,13 +16,8 @@
|
|
16
16
|
# 2019-03-04 - change clear to go to 0,0 and clear down to reduce pollution
|
17
17
|
# 2019-03-04 - changed quit to q (earlier Q)
|
18
18
|
# 2019-03-04 - first dirs then files
|
19
|
-
# 2019-03-10 - changing selected_files to have fullpath
|
20
19
|
# 2019-03-22 - refactoring the code, esp run()
|
21
20
|
# == TODO
|
22
|
-
# a method is called from. a menu or direct from key. e.g help
|
23
|
-
# To dirs add GEMHOME RUBYLIB PYTHONILB or PYTHONPATH,
|
24
|
-
# Make rubygem aware : gemspec or Gemfile the expand lib and bin
|
25
|
-
# fpath if existing
|
26
21
|
|
27
22
|
require 'readline'
|
28
23
|
require 'io/wait'
|
@@ -39,11 +34,10 @@ require 'logger'
|
|
39
34
|
# alias c=~/bin/cetus.rb
|
40
35
|
# c
|
41
36
|
|
42
|
-
VERSION = '0.1.
|
37
|
+
VERSION = '0.1.36.0'.freeze
|
43
38
|
CONFIG_PATH = ENV['XDG_CONFIG_HOME'] || File.join(ENV['HOME'], '.config')
|
44
39
|
CONFIG_FILE = "#{CONFIG_PATH}/cetus/conf.yml".freeze
|
45
40
|
|
46
|
-
# $bindings = {}
|
47
41
|
$bindings = {
|
48
42
|
'`' => 'main_menu',
|
49
43
|
'=' => 'toggle_menu',
|
@@ -52,7 +46,7 @@ $bindings = {
|
|
52
46
|
'ENTER' => 'select_current',
|
53
47
|
'C-p' => 'page_current',
|
54
48
|
'C-e' => 'edit_current',
|
55
|
-
'C-o' => '
|
49
|
+
'C-o' => 'open_current',
|
56
50
|
'C-s' => 'toggle_select',
|
57
51
|
'C-r' => 'reduce',
|
58
52
|
'C-g' => 'debug_vars',
|
@@ -83,7 +77,8 @@ $bindings = {
|
|
83
77
|
'C-i' => 'views',
|
84
78
|
# '?' => 'dirtree',
|
85
79
|
'D' => 'delete_file',
|
86
|
-
'M' => 'file_actions most',
|
80
|
+
# 'M' => 'file_actions most',
|
81
|
+
'M' => 'move_instant',
|
87
82
|
'q' => 'quit_command', # was Q now q 2019-03-04 -
|
88
83
|
# "RIGHT" => "column_next",
|
89
84
|
'RIGHT' => 'select_current', # changed 2018-03-12 - for faster navigation
|
@@ -305,24 +300,31 @@ $stact = 0 # used when panning a folder to next column
|
|
305
300
|
$editor_mode = false # changed 2018-03-12 - so we start in pager mode
|
306
301
|
$enhanced_mode = true
|
307
302
|
$visual_block_start = nil
|
308
|
-
|
303
|
+
PAGER_COMMAND = {
|
309
304
|
text: 'most',
|
310
305
|
image: 'open',
|
311
306
|
zip: 'tar ztvf %% | most',
|
312
307
|
unknown: 'open'
|
313
308
|
}
|
314
309
|
$dir_position = {}
|
315
|
-
|
310
|
+
@movement = @old_cursor = nil # cursor movement has happened only, don't repaint
|
316
311
|
$selection_mode = 1 # single select
|
312
|
+
## FLAGS
|
317
313
|
@group_dirs = true
|
318
314
|
# truncate long filenames from :right, :left or :center.
|
319
315
|
@truncate_from = :center
|
316
|
+
@filename_status_line = true
|
317
|
+
@display_file_stats = true
|
318
|
+
@selected_files_fullpath_flag = false
|
319
|
+
@selected_files_escaped_flag = false
|
320
|
+
@keys_to_clear = nil
|
320
321
|
|
321
322
|
## ----------------- CONSTANTS ----------------- ##
|
322
323
|
GMARK = '*'.freeze
|
323
324
|
CURMARK = '>'.freeze
|
324
325
|
MSCROLL = 10
|
325
326
|
SPACE = ' '.freeze
|
327
|
+
SEPARATOR = '-------'.freeze
|
326
328
|
CLEAR = "\e[0m".freeze
|
327
329
|
BOLD = "\e[1m".freeze
|
328
330
|
BOLD_OFF = "\e[22m".freeze
|
@@ -355,6 +357,7 @@ $ls_color = {
|
|
355
357
|
'.zip' => MAGENTA,
|
356
358
|
'.torrent' => GREEN,
|
357
359
|
'.srt' => GREEN,
|
360
|
+
'.part' => "\e[40;31;01m",
|
358
361
|
'.sh' => CYAN
|
359
362
|
}
|
360
363
|
# This hash contains colors for file patterns, updated from LS_COLORS
|
@@ -387,7 +390,8 @@ $history = []
|
|
387
390
|
## sta is where view (viewport) begins, cursor is current row/file
|
388
391
|
$sta = $cursor = 0
|
389
392
|
$visual_mode = false
|
390
|
-
|
393
|
+
@status_color = 4 # status line, can be 2 3 4 5 6
|
394
|
+
@status_color_right = 8 # status line right part
|
391
395
|
|
392
396
|
# Menubar on top of screen
|
393
397
|
@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 "
|
@@ -511,14 +515,25 @@ def print_title
|
|
511
515
|
|
512
516
|
# print 1 of n files, sort order, filter etc details
|
513
517
|
$title ||= Dir.pwd.sub(ENV['HOME'], '~')
|
518
|
+
|
519
|
+
# Add bookmark next to name of dir, if exists
|
520
|
+
bm = $bookmarks.key(Dir.pwd)
|
521
|
+
bm = " ('#{bm})" if bm
|
522
|
+
|
514
523
|
fin = $sta + $viewport.size
|
515
524
|
fl = $view.size
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
525
|
+
|
526
|
+
# fix count of entries so separator and enhanced entries don't show up
|
527
|
+
if $enhanced_mode
|
528
|
+
ix = $viewport.index SEPARATOR
|
529
|
+
fin = $sta + ix if ix
|
530
|
+
|
531
|
+
ix = $view.index SEPARATOR
|
532
|
+
fl = ix if ix
|
520
533
|
end
|
521
534
|
|
535
|
+
t = fl.zero? ? "#{$title}#{bm} No files." : "#{$title}#{bm} #{$sta + 1} to #{fin} of #{fl} #{$sorto} F:#{$filterstr}"
|
536
|
+
|
522
537
|
# don't exceed columns while printing
|
523
538
|
t = t[t.size - $gcols..-1] if t.size >= $gcols
|
524
539
|
|
@@ -530,6 +545,7 @@ end
|
|
530
545
|
# TODO: clean this up and simplify it
|
531
546
|
# NOTE: earlier this was called on every key (up-arow down arrow, now only
|
532
547
|
# called when page changes, so we only put directory name)
|
548
|
+
# NOTE: called only from draw_directory.
|
533
549
|
def status_line
|
534
550
|
# prompt
|
535
551
|
v_mm = $mode ? "[#{$mode}] " : ''
|
@@ -538,14 +554,15 @@ def status_line
|
|
538
554
|
|
539
555
|
clear_last_line
|
540
556
|
|
541
|
-
# Print the filename at the right side of the
|
557
|
+
# Print the filename at the right side of the status line
|
542
558
|
# sometimes due to search, there is no file
|
543
559
|
if cf
|
544
560
|
if @debug_flag
|
545
561
|
# XXX this will not work on file basis FIXME
|
546
562
|
print_debug_info cf
|
547
563
|
else
|
548
|
-
print_on_right "#{Dir.pwd}"
|
564
|
+
# print_on_right "#{Dir.pwd}"
|
565
|
+
print_filename_status_line if @filename_status_line
|
549
566
|
end
|
550
567
|
end
|
551
568
|
# move to beginning of line, reset text mode after printing
|
@@ -560,7 +577,7 @@ def status_line
|
|
560
577
|
# print search pattern if any
|
561
578
|
# print message if any
|
562
579
|
# print "\r#{v_mm}#{patt}#{$message}\e[m"
|
563
|
-
print "\r\e[33;4#{
|
580
|
+
print "\r\e[33;4#{@status_color}m#{v_mm}#{patt}#{$message}\e[m"
|
564
581
|
|
565
582
|
end
|
566
583
|
|
@@ -568,6 +585,25 @@ def print_debug_info cf=current_file()
|
|
568
585
|
print_on_right "len:#{cf.length}/#{$temp_wid} = #{$sta},#{$cursor},#{$stact},#{$viewport.size},#{$grows} | #{cf}"
|
569
586
|
end
|
570
587
|
|
588
|
+
def print_filename_status_line cf=current_file
|
589
|
+
if @display_file_stats
|
590
|
+
ff = if cf[0] == '~'
|
591
|
+
File.expand_path(cf)
|
592
|
+
else
|
593
|
+
cf
|
594
|
+
end
|
595
|
+
|
596
|
+
mtime = if !File.exist? ff
|
597
|
+
# take care of dead links lstat TODO
|
598
|
+
date_format(File.lstat(ff).mtime) if File.symlink?(ff)
|
599
|
+
else
|
600
|
+
date_format(File.stat(ff).mtime)
|
601
|
+
end
|
602
|
+
end
|
603
|
+
# print size and mtime only if more data requested.
|
604
|
+
print_on_right "| #{mtime} | #{cf}".rjust(40)
|
605
|
+
end
|
606
|
+
|
571
607
|
# should we do a read of the dir
|
572
608
|
def rescan?
|
573
609
|
$rescan_required
|
@@ -640,26 +676,27 @@ end
|
|
640
676
|
# clear the earlier position when we move.
|
641
677
|
# Currently not using until I can implement chgat here.
|
642
678
|
def clear_cursor
|
643
|
-
return unless
|
679
|
+
return unless @old_cursor
|
644
680
|
|
645
|
-
hint = get_shortcut(
|
646
|
-
if
|
681
|
+
hint = get_shortcut(@old_cursor)
|
682
|
+
if @old_cursor < $grows
|
647
683
|
# FIXME: faster way of getting here ? see fff
|
648
|
-
# system "tput cup #{
|
649
|
-
tput_cup
|
684
|
+
# system "tput cup #{@old_cursor + 2} 0"
|
685
|
+
tput_cup @old_cursor, 0
|
650
686
|
# print "#{CURSOR_COLOR}#{hint} >#{CLEAR}"
|
651
687
|
print "#{hint} "
|
652
688
|
return
|
653
689
|
end
|
654
690
|
wid = get_width $viewport.size, $grows
|
655
|
-
rows =
|
656
|
-
cols = (
|
691
|
+
rows = @old_cursor % $grows
|
692
|
+
cols = (@old_cursor / $grows) * wid
|
657
693
|
# system "tput cup #{rows + 2} #{cols}"
|
658
694
|
tput_cup rows, cols
|
659
695
|
# print "#{CURSOR_COLOR}#{hint} >#{CLEAR}"
|
660
696
|
print "#{hint} "
|
661
697
|
end
|
662
698
|
|
699
|
+
# place cursor on row and col taking first two rows into account
|
663
700
|
def tput_cup row, col
|
664
701
|
# we add 3: 2 is for the help and directory line. 1 is since tput is 1 based
|
665
702
|
print "\e[#{row + 3};#{col + 1}H"
|
@@ -669,6 +706,8 @@ def redraw_required(flag=true) $redraw_required = flag; end
|
|
669
706
|
|
670
707
|
def resolve_key key
|
671
708
|
ret = true
|
709
|
+
clear_message
|
710
|
+
|
672
711
|
if key.match?(/^[a-pr-zZ]$/)
|
673
712
|
# hint mode
|
674
713
|
ret = select_hint $viewport, key
|
@@ -852,7 +891,7 @@ def truncate_formatted_filename f, unformatted_len, wid
|
|
852
891
|
# hintsize = sindex - 1
|
853
892
|
# end
|
854
893
|
# f[0..sindex + 1] + '<' + f[-wid + hintsize..-1] + ' '
|
855
|
-
@log.debug "XXX #{excess}: #{f} / #{wid}"
|
894
|
+
# @log.debug "XXX #{excess}: #{f} / #{wid}"
|
856
895
|
# 4 = 2 for literals, 2 to get ahead of sindex+1
|
857
896
|
f[0..sindex + 1] + '<' + f[sindex + 4 + excess..-1] + ' '
|
858
897
|
# in some cases 29/32 we are actually overlapping 2 parts above
|
@@ -898,10 +937,18 @@ def format_array(ary)
|
|
898
937
|
ctr = 0
|
899
938
|
ary.each do |f|
|
900
939
|
## ctr refers to the index in the column
|
901
|
-
ind = get_shortcut(ix)
|
902
940
|
mark = SPACE
|
903
|
-
mark = '+' if visited? f
|
904
941
|
cur = SPACE
|
942
|
+
ind = get_shortcut(ix)
|
943
|
+
|
944
|
+
|
945
|
+
# Handle separator before enhanced file list.
|
946
|
+
# We do lost a shortcut
|
947
|
+
if f == SEPARATOR
|
948
|
+
ind = cur = mark = '-'
|
949
|
+
end
|
950
|
+
|
951
|
+
mark = '+' if visited? f
|
905
952
|
# cur = CURMARK if ix + $sta == $cursor # 2019-03-29 - removed reduced calls
|
906
953
|
# NOTE seems like f and ary[ix] are the same
|
907
954
|
mark = GMARK if selected?(ary[ix])
|
@@ -952,6 +999,8 @@ end
|
|
952
999
|
|
953
1000
|
# determine color for a filename based on extension, then pattern, then filetype
|
954
1001
|
def color_for fname
|
1002
|
+
return nil if fname == SEPARATOR
|
1003
|
+
|
955
1004
|
extension = File.extname(fname)
|
956
1005
|
color = $ls_color[extension]
|
957
1006
|
return color if color
|
@@ -1050,20 +1099,20 @@ end
|
|
1050
1099
|
|
1051
1100
|
## select file based on key pressed
|
1052
1101
|
def select_hint view, key
|
1053
|
-
|
1054
|
-
# if x or z take a key IF there are those many
|
1055
|
-
#
|
1102
|
+
|
1056
1103
|
ix = get_index(key, view.size)
|
1057
1104
|
return nil unless ix
|
1058
1105
|
|
1059
1106
|
f = view[ix]
|
1060
1107
|
return nil unless f
|
1108
|
+
return nil if f == SEPARATOR
|
1061
1109
|
|
1062
1110
|
$cursor = $sta + ix
|
1063
1111
|
|
1064
1112
|
if $mode == 'SEL'
|
1065
1113
|
toggle_select f
|
1066
1114
|
elsif $mode == 'COM'
|
1115
|
+
# not being called any longer I think
|
1067
1116
|
run_command f
|
1068
1117
|
else
|
1069
1118
|
open_file f
|
@@ -1103,19 +1152,8 @@ def open_file(f)
|
|
1103
1152
|
change_dir f #, nextpos
|
1104
1153
|
elsif File.readable? f
|
1105
1154
|
# TODO: looks complex pls simplify !! XXX
|
1106
|
-
|
1107
|
-
|
1108
|
-
ft = filetype f
|
1109
|
-
if ft
|
1110
|
-
comm = $pager_command[ft]
|
1111
|
-
else
|
1112
|
-
comm = $pager_command[File.extname(f)]
|
1113
|
-
comm ||= $pager_command['unknown']
|
1114
|
-
end
|
1115
|
-
else
|
1116
|
-
comm = $default_command
|
1117
|
-
end
|
1118
|
-
comm ||= $default_command
|
1155
|
+
comm = opener_for f
|
1156
|
+
# '%%' will be substituted with the filename. See zip
|
1119
1157
|
comm = if comm.index('%%')
|
1120
1158
|
comm.gsub('%%', Shellwords.escape(f))
|
1121
1159
|
else
|
@@ -1125,6 +1163,7 @@ def open_file(f)
|
|
1125
1163
|
reset_terminal
|
1126
1164
|
system(comm.to_s)
|
1127
1165
|
setup_terminal
|
1166
|
+
# XXX maybe use absolute_path instead of hardcoding
|
1128
1167
|
f = Dir.pwd + '/' + f if f[0] != '/'
|
1129
1168
|
$visited_files.insert(0, f)
|
1130
1169
|
push_used_dirs Dir.pwd
|
@@ -1148,9 +1187,15 @@ def edit_current
|
|
1148
1187
|
$visited_files.insert(0, current_file)
|
1149
1188
|
end
|
1150
1189
|
|
1190
|
+
def open_current
|
1191
|
+
opener = /darwin/ =~ RUBY_PLATFORM ? 'open' : 'xdg-open'
|
1192
|
+
run_on_current opener
|
1193
|
+
$visited_files.insert(0, current_file)
|
1194
|
+
end
|
1195
|
+
|
1151
1196
|
# run given command on current file
|
1152
1197
|
def run_on_current(command)
|
1153
|
-
f =
|
1198
|
+
f = current_file
|
1154
1199
|
return unless f
|
1155
1200
|
f = File.expand_path(f)
|
1156
1201
|
return unless File.readable?(f)
|
@@ -1163,40 +1208,39 @@ def run_on_current(command)
|
|
1163
1208
|
setup_terminal
|
1164
1209
|
end
|
1165
1210
|
|
1166
|
-
## run command on given file/s
|
1167
|
-
# Accepts command from user
|
1211
|
+
## run system command on given file/s
|
1212
|
+
# Accepts external command from user
|
1168
1213
|
# After putting readline in place of gets, pressing a C-c has a delayed effect.
|
1169
1214
|
# It goes into exception block after executing other commands and still
|
1170
1215
|
# does not do the return !
|
1171
1216
|
def run_command(f)
|
1172
|
-
files =
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
end
|
1217
|
+
files = Shellwords.join(f)
|
1218
|
+
count = f.count
|
1219
|
+
text = if count > 1
|
1220
|
+
"#{count} files"
|
1221
|
+
else
|
1222
|
+
files[0..40]
|
1223
|
+
end
|
1180
1224
|
begin
|
1181
|
-
#
|
1182
|
-
command = readline "Run a command on #{files}: "
|
1183
|
-
# command = gets().chomp
|
1225
|
+
command = readline "Run a command on #{text}: "
|
1184
1226
|
return if command.empty?
|
1185
1227
|
|
1186
1228
|
# command2 = gets().chomp
|
1187
1229
|
command2 = readline 'Second part of command: '
|
1188
|
-
|
1230
|
+
pause "#{command} #{files} #{command2}"
|
1231
|
+
|
1232
|
+
reset_terminal
|
1189
1233
|
system "#{command} #{files} #{command2}"
|
1190
1234
|
setup_terminal
|
1191
1235
|
rescue StandardError => ex
|
1192
|
-
perror "Canceled command, (#{ex}) press a key"
|
1236
|
+
perror "Canceled or failed command, (#{ex}) press a key."
|
1237
|
+
@log.warn "RUNCOMMAND: #{ex.to_s}"
|
1193
1238
|
return
|
1194
1239
|
end
|
1195
1240
|
|
1196
1241
|
refresh
|
1197
|
-
puts 'Press a key ...'
|
1198
1242
|
push_used_dirs Dir.pwd
|
1199
|
-
|
1243
|
+
# should we clear selection also ?
|
1200
1244
|
end
|
1201
1245
|
|
1202
1246
|
## cd to a dir.
|
@@ -1243,6 +1287,7 @@ def escape
|
|
1243
1287
|
end
|
1244
1288
|
|
1245
1289
|
## refresh listing after some change like option change, or toggle
|
1290
|
+
# Should we check selected_files array also for deleted/renamed files
|
1246
1291
|
def refresh
|
1247
1292
|
$patt = nil
|
1248
1293
|
$title = nil
|
@@ -1263,7 +1308,7 @@ def unselect_all
|
|
1263
1308
|
$visual_mode = nil
|
1264
1309
|
end
|
1265
1310
|
|
1266
|
-
## select all files
|
1311
|
+
## select all entries (files and directories)
|
1267
1312
|
def select_all
|
1268
1313
|
dir = Dir.pwd
|
1269
1314
|
$selected_files = $view.map { |file| File.join(dir, file) }
|
@@ -1340,7 +1385,7 @@ def goto_parent_dir
|
|
1340
1385
|
end
|
1341
1386
|
|
1342
1387
|
def goto_home_dir
|
1343
|
-
change_dir '
|
1388
|
+
change_dir ENV['HOME']
|
1344
1389
|
end
|
1345
1390
|
|
1346
1391
|
|
@@ -1374,14 +1419,14 @@ def next_page
|
|
1374
1419
|
$cursor += $pagesize
|
1375
1420
|
$sta = $cursor if $sta > $cursor
|
1376
1421
|
$stact = 0
|
1377
|
-
|
1422
|
+
@old_cursor = nil
|
1378
1423
|
redraw_required
|
1379
1424
|
end
|
1380
1425
|
|
1381
1426
|
def prev_page
|
1382
1427
|
$sta -= $pagesize
|
1383
1428
|
$cursor -= $pagesize
|
1384
|
-
|
1429
|
+
@old_cursor = nil
|
1385
1430
|
# FIXME: check cursor sanity and if not changed then no redraw
|
1386
1431
|
redraw_required
|
1387
1432
|
end
|
@@ -1447,7 +1492,9 @@ def debug_vars
|
|
1447
1492
|
file.puts "pagesize #{$pagesize}"
|
1448
1493
|
file.puts "view.size #{$view.size}"
|
1449
1494
|
file.puts "grows #{$grows}"
|
1450
|
-
file.puts "
|
1495
|
+
file.puts "File: #{current_file}"
|
1496
|
+
file.puts
|
1497
|
+
file.puts "Opener: #{opener_for(current_file)}"
|
1451
1498
|
file.puts
|
1452
1499
|
file.puts `file "#{current_file}"`
|
1453
1500
|
file.puts
|
@@ -1457,7 +1504,7 @@ def debug_vars
|
|
1457
1504
|
end
|
1458
1505
|
|
1459
1506
|
def view_bookmarks
|
1460
|
-
|
1507
|
+
clear_last_line
|
1461
1508
|
puts 'Bookmarks: '
|
1462
1509
|
$bookmarks.each_pair { |k, v| puts "#{k.ljust(7)} => #{v}" }
|
1463
1510
|
puts
|
@@ -1515,7 +1562,7 @@ end
|
|
1515
1562
|
def menu title, h
|
1516
1563
|
return unless h
|
1517
1564
|
|
1518
|
-
|
1565
|
+
clear_last_line # 2019-03-30 - required since cursor is not longer at bottom
|
1519
1566
|
pbold title.to_s
|
1520
1567
|
# h.each_pair { |k, v| puts " #{k}: #{v}" }
|
1521
1568
|
# 2019-03-09 - trying out using `column` to print in cols
|
@@ -1993,6 +2040,8 @@ def subcommand
|
|
1993
2040
|
$writing = true if $modified
|
1994
2041
|
elsif command == 'e'
|
1995
2042
|
edit_current
|
2043
|
+
elsif command == 'o'
|
2044
|
+
open_current
|
1996
2045
|
elsif command == 'h' or command == 'help' or command == '?'
|
1997
2046
|
print_help
|
1998
2047
|
elsif command == 'p'
|
@@ -2105,7 +2154,7 @@ end
|
|
2105
2154
|
# but prints on next line.FIXME 2019-03-24 - 00:08
|
2106
2155
|
def perror text
|
2107
2156
|
clear_last_line
|
2108
|
-
|
2157
|
+
puts "\r#{RED}#{text}. Press a key.#{CLEAR}"
|
2109
2158
|
get_char
|
2110
2159
|
end
|
2111
2160
|
|
@@ -2155,7 +2204,7 @@ def get_index(key, vsz = 999)
|
|
2155
2204
|
zch = get_char
|
2156
2205
|
print zch
|
2157
2206
|
i = convert_key_to_index("#{key}#{zch}")
|
2158
|
-
@log.debug "convert returned #{i} for #{key}#{zch}"
|
2207
|
+
# @log.debug "convert returned #{i} for #{key}#{zch}"
|
2159
2208
|
return i if i
|
2160
2209
|
# i = $IDX.index
|
2161
2210
|
# return i + $stact if i
|
@@ -2172,9 +2221,14 @@ def convert_key_to_index key
|
|
2172
2221
|
if i
|
2173
2222
|
# @log.debug "get_index with #{key}: #{i}. #{$stact}. #{$viewport.size}"
|
2174
2223
|
vps = $viewport.size
|
2175
|
-
|
2224
|
+
# TODO: if very high key given, consider going to last file ?
|
2225
|
+
# that way one can press zz or ZZ to go to last file.
|
2226
|
+
# 2019-04-11 - XXX actually this doesnt place the cursor on last file
|
2227
|
+
# it opens it, which may not be what we want
|
2228
|
+
retnil = nil # vps - 1 # nil
|
2229
|
+
return retnil if $stact == 0 && i + $stact >= vps
|
2176
2230
|
# return nil if $stact > 0 && i + $stact >= vps && i + $stact - vps >= $stact
|
2177
|
-
return
|
2231
|
+
return retnil if $stact > 0 && i + $stact >= vps && i - vps >= 0
|
2178
2232
|
|
2179
2233
|
if i + $stact >= vps
|
2180
2234
|
# panning case, hints are recycled
|
@@ -2191,6 +2245,71 @@ def delete_file
|
|
2191
2245
|
file_actions :delete
|
2192
2246
|
end
|
2193
2247
|
|
2248
|
+
def move_instant
|
2249
|
+
# FIXME: cannot be in target directory and do auto update
|
2250
|
+
|
2251
|
+
if $selected_files.empty?
|
2252
|
+
target = readline "Enter target directory for moving files (#{@move_target}):"
|
2253
|
+
return unless target
|
2254
|
+
return if target == '' && @move_target.nil?
|
2255
|
+
|
2256
|
+
# take default value if user presses Enter
|
2257
|
+
target = @move_target if target == ''
|
2258
|
+
return unless target
|
2259
|
+
|
2260
|
+
# current dir if dot pressed FIXME you cannot move to current dir !!!
|
2261
|
+
target = Dir.pwd if target == '.'
|
2262
|
+
target = File.expand_path(target)
|
2263
|
+
unless File.directory? target
|
2264
|
+
perror "#{target} not a directory."
|
2265
|
+
return
|
2266
|
+
end
|
2267
|
+
begin
|
2268
|
+
f = current_file
|
2269
|
+
FileUtils.mv f, target
|
2270
|
+
@move_target = target
|
2271
|
+
message "#{f} moved to #{File.basename(target)}. Target set."
|
2272
|
+
@log.info "1.#{f} moved to #{target}."
|
2273
|
+
rescue StandardError => exc
|
2274
|
+
@log.warn "Case 1:"
|
2275
|
+
@log.warn "Target is #{target}, file was #{f}"
|
2276
|
+
@log.warn exc.to_s
|
2277
|
+
end
|
2278
|
+
refresh
|
2279
|
+
return
|
2280
|
+
end
|
2281
|
+
|
2282
|
+
# files selected. Use earlier target if there, else ask
|
2283
|
+
target = @move_target
|
2284
|
+
if target.nil? || target == ''
|
2285
|
+
target = readline "Enter directory for moving files #{@move_target}:"
|
2286
|
+
return unless target
|
2287
|
+
end
|
2288
|
+
target = File.expand_path(target)
|
2289
|
+
unless File.directory? target
|
2290
|
+
perror "#{target} not a directory."
|
2291
|
+
return
|
2292
|
+
end
|
2293
|
+
|
2294
|
+
files = $selected_files
|
2295
|
+
# todo: shellwords
|
2296
|
+
ccount = 0
|
2297
|
+
files.each do |f|
|
2298
|
+
FileUtils.mv f, target
|
2299
|
+
@log.info "2.#{f} moved to #{target}."
|
2300
|
+
ccount += 1
|
2301
|
+
rescue StandardError => exc
|
2302
|
+
@log.warn "Case 2:"
|
2303
|
+
@log.warn "Target is #{target}, file was #{f}"
|
2304
|
+
@log.warn exc.to_s
|
2305
|
+
perror exc.to_s
|
2306
|
+
end
|
2307
|
+
@move_target = target
|
2308
|
+
clean_selected_files
|
2309
|
+
message "#{ccount} files moved to #{target}."
|
2310
|
+
refresh
|
2311
|
+
end
|
2312
|
+
|
2194
2313
|
## generic external command program
|
2195
2314
|
# prompt is the user friendly text of command such as list for ls, or extract for dtrx, page for less
|
2196
2315
|
# pauseyn is whether to pause after command as in file or ls
|
@@ -2220,7 +2339,7 @@ end
|
|
2220
2339
|
|
2221
2340
|
## prompt user for file shortcut and return file or nil
|
2222
2341
|
#
|
2223
|
-
def ask_hint
|
2342
|
+
def ask_hint deflt=nil
|
2224
2343
|
f = nil
|
2225
2344
|
key = get_char
|
2226
2345
|
return deflt if key == 'ENTER'
|
@@ -2279,43 +2398,53 @@ end
|
|
2279
2398
|
# 2019-03-08 - TODO when a file name changes or moves it must be removed
|
2280
2399
|
# from selection
|
2281
2400
|
def file_actions(action = nil)
|
2282
|
-
|
2283
|
-
|
2284
|
-
|
2285
|
-
|
2286
|
-
#
|
2287
|
-
|
2288
|
-
|
2289
|
-
|
2290
|
-
|
2291
|
-
if
|
2292
|
-
|
2293
|
-
|
2294
|
-
|
2295
|
-
|
2296
|
-
|
2297
|
-
|
2298
|
-
|
2299
|
-
|
2300
|
-
|
2301
|
-
|
2401
|
+
h = { d: :delete, D: '/bin/rm', m: :move, v: ENV['EDITOR'] || :vim,
|
2402
|
+
c: :copy, e: :execute,
|
2403
|
+
l: :less, p: :most, o: :open }
|
2404
|
+
|
2405
|
+
rbfiles = current_or_selected_files # use with ruby FileUtils
|
2406
|
+
return if rbfiles.nil? || rbfiles.empty?
|
2407
|
+
|
2408
|
+
count = rbfiles.count
|
2409
|
+
first = rbfiles.first
|
2410
|
+
if count == 1
|
2411
|
+
h[:r] = :rename
|
2412
|
+
|
2413
|
+
# add chdir if dir of file under cursor is not same as current dir
|
2414
|
+
h[:C] = :chdir if File.dirname(File.expand_path(first)) != Dir.pwd
|
2415
|
+
|
2416
|
+
h[:s] = :page_stat_for_file
|
2417
|
+
h[:f] = :file
|
2418
|
+
if filetype(first) == :zip
|
2419
|
+
h[:x] = :dtrx
|
2420
|
+
h[:u] = :unzip
|
2302
2421
|
end
|
2303
|
-
|
2422
|
+
h[:g] = if File.extname(first) == '.gz'
|
2423
|
+
:gunzip
|
2424
|
+
else
|
2425
|
+
:gzip
|
2426
|
+
end
|
2304
2427
|
|
2305
|
-
text = file
|
2306
2428
|
end
|
2307
|
-
|
2308
|
-
|
2429
|
+
h[:M] = :set_move_target if File.directory?(current_file)
|
2430
|
+
h[:z] = :zip unless filetype(first) == :zip
|
2309
2431
|
|
2310
|
-
|
2311
|
-
|
2312
|
-
|
2313
|
-
|
2314
|
-
|
2315
|
-
|
2316
|
-
|
2432
|
+
# if first file has spaces then add remspace method
|
2433
|
+
# TODO: put this in scripts
|
2434
|
+
# take care that directories can have spaces
|
2435
|
+
h[:W] = :remspace if File.basename(first).index ' '
|
2436
|
+
|
2437
|
+
text = count == 1 ? File.basename(first) : "#{count} files"
|
2438
|
+
shfiles = Shellwords.join(rbfiles)
|
2439
|
+
|
2440
|
+
# --------------------------------------------------------------
|
2441
|
+
# Use 'text' for display
|
2442
|
+
# Use 'shfiles' for system actions, these are escaped
|
2443
|
+
# Use 'rbfiles' for looping and ruby FileUtils, system commands can bork on unescaped names
|
2444
|
+
# Use 'count' for how many files, in case of single file operation.
|
2445
|
+
# --------------------------------------------------------------
|
2317
2446
|
|
2318
|
-
|
2447
|
+
# if no action passed, then ask for action
|
2319
2448
|
if action
|
2320
2449
|
menu_text = action
|
2321
2450
|
else
|
@@ -2330,7 +2459,7 @@ def file_actions(action = nil)
|
|
2330
2459
|
|
2331
2460
|
when :delete
|
2332
2461
|
delcommand = 'rmtrash'
|
2333
|
-
|
2462
|
+
clear_last_line
|
2334
2463
|
print "#{delcommand} #{text} ? [yn?]: "
|
2335
2464
|
key = get_char
|
2336
2465
|
view_selected_files if key == '?'
|
@@ -2338,119 +2467,176 @@ def file_actions(action = nil)
|
|
2338
2467
|
|
2339
2468
|
clear_last_line
|
2340
2469
|
print "\r deleting ..."
|
2341
|
-
system "#{delcommand} #{
|
2470
|
+
system "#{delcommand} #{shfiles}"
|
2471
|
+
message "Deleted #{text}."
|
2342
2472
|
refresh
|
2343
2473
|
|
2344
2474
|
when :move
|
2345
|
-
#
|
2346
|
-
|
2347
|
-
|
2348
|
-
target = readline "Move #{text} to : "
|
2475
|
+
# multiple files can only be moved to a directory
|
2476
|
+
default = @move_target.nil? ? '.' : @move_target
|
2477
|
+
target = readline "Move #{text} to (#{default}): "
|
2349
2478
|
return unless target
|
2350
2479
|
|
2351
|
-
target =
|
2352
|
-
# 2019-03-07 - NOTE cannot use text if multiple files
|
2353
|
-
# text = File.expand_path(text)
|
2480
|
+
target = default if target == ''
|
2354
2481
|
target = File.expand_path(target)
|
2355
2482
|
return if target == ''
|
2356
2483
|
|
2357
|
-
if File.directory?
|
2358
|
-
|
2359
|
-
|
2360
|
-
message "Moved #{text} to #{target}"
|
2361
|
-
rescue StandardError => exc
|
2362
|
-
perror exc.to_s
|
2363
|
-
end
|
2364
|
-
# 2019-03-08 - TODO if success remove from selection
|
2365
|
-
refresh
|
2366
|
-
else
|
2367
|
-
perror 'Target not a dir'
|
2484
|
+
if count > 1 && !File.directory?(target)
|
2485
|
+
perror 'Move target must be a directory for multiple files.'
|
2486
|
+
return
|
2368
2487
|
end
|
2369
2488
|
|
2489
|
+
if count == 1 && !File.directory?(target) && File.exist?(target)
|
2490
|
+
perror "Target #{target} exists."
|
2491
|
+
return
|
2492
|
+
end
|
2493
|
+
|
2494
|
+
begin
|
2495
|
+
FileUtils.mv rbfiles, target
|
2496
|
+
message "Moved #{text} to #{target}."
|
2497
|
+
rescue StandardError => exc
|
2498
|
+
@log.warn "C-x move: #{exc.to_s}."
|
2499
|
+
@log.warn "MOVE: files: #{rbfiles}, target:#{target}"
|
2500
|
+
perror exc.to_s
|
2501
|
+
end
|
2502
|
+
# 2019-03-08 - TODO if success remove from selection
|
2503
|
+
refresh
|
2504
|
+
|
2370
2505
|
when :copy
|
2371
|
-
# Target must be directory
|
2372
|
-
target
|
2506
|
+
# Target must be directory for multiple files.
|
2507
|
+
# NOTE: target should not be same as source dir but there can be files
|
2508
|
+
# from multiple directories
|
2509
|
+
default = @move_target.nil? ? '.' : @move_target
|
2510
|
+
target = readline "Copy #{text} to (#{default}): "
|
2373
2511
|
return unless target # C-c
|
2374
2512
|
|
2375
|
-
target =
|
2513
|
+
target = default if target == ''
|
2376
2514
|
target = File.expand_path(target)
|
2377
2515
|
return if target == ''
|
2378
2516
|
|
2379
|
-
if File.directory?
|
2380
|
-
|
2381
|
-
|
2382
|
-
message "Copied #{text} to #{target}"
|
2383
|
-
rescue StandardError => exc
|
2384
|
-
perror exc.to_s
|
2385
|
-
end
|
2386
|
-
refresh
|
2387
|
-
else
|
2388
|
-
perror 'Copy target must be a dir'
|
2517
|
+
if count > 1 && !File.directory?(target)
|
2518
|
+
perror 'Copy target must be a directory for multiple files.'
|
2519
|
+
return
|
2389
2520
|
end
|
2390
2521
|
|
2522
|
+
if count == 1 && !File.directory?(target) && File.exist?(target)
|
2523
|
+
perror "Target #{target} exists."
|
2524
|
+
return
|
2525
|
+
end
|
2526
|
+
|
2527
|
+
begin
|
2528
|
+
FileUtils.cp rbfiles, target
|
2529
|
+
message "Copied #{text} to #{target}."
|
2530
|
+
rescue StandardError => exc
|
2531
|
+
perror exc.to_s
|
2532
|
+
end
|
2533
|
+
refresh
|
2534
|
+
|
2391
2535
|
when :chdir
|
2392
|
-
|
2536
|
+
# will only work if one selected. Check this out
|
2537
|
+
# This works if you have searched for files and got a list
|
2538
|
+
# with different paths.
|
2539
|
+
# This should not be shown in regular listings XXX
|
2540
|
+
if count == 1
|
2541
|
+
change_dir File.dirname(File.expand_path(rbfiles.first))
|
2542
|
+
end
|
2543
|
+
|
2544
|
+
when :set_move_target
|
2545
|
+
set_move_target current_file
|
2393
2546
|
|
2394
2547
|
when :zip
|
2395
|
-
|
2548
|
+
|
2396
2549
|
target = readline 'Archive name: '
|
2397
2550
|
return unless target
|
2398
2551
|
return if target == ''
|
2399
2552
|
|
2400
|
-
|
2401
|
-
|
2402
|
-
|
2403
|
-
|
2404
|
-
|
2405
|
-
|
2406
|
-
|
2407
|
-
refresh
|
2408
|
-
end
|
2553
|
+
if File.exist? target
|
2554
|
+
perror "Target (#{target}) exists"
|
2555
|
+
return
|
2556
|
+
end
|
2557
|
+
if target && target.size < 4
|
2558
|
+
perror 'Use target of more than 4 characters.'
|
2559
|
+
return
|
2409
2560
|
end
|
2410
2561
|
|
2562
|
+
# convert absolute paths to relative ones in this zip
|
2563
|
+
require 'pathname'
|
2564
|
+
base = Pathname.new Dir.pwd
|
2565
|
+
relfiles = rbfiles.map {|f| p = Pathname.new(f); p.relative_path_from(base) }
|
2566
|
+
zfiles = Shellwords.join relfiles
|
2567
|
+
|
2568
|
+
# the problem with zip is that we have full paths
|
2569
|
+
# so the zip file has full paths and extraction sucks
|
2570
|
+
|
2571
|
+
# pause "(#{target}).zipping #{relfiles.count}: #{zfiles}"
|
2572
|
+
system "tar zcvf #{target} #{zfiles}"
|
2573
|
+
message "Created #{target} with #{relfiles.count} files."
|
2574
|
+
setup_terminal
|
2575
|
+
refresh
|
2576
|
+
|
2411
2577
|
when :rename
|
2412
|
-
|
2413
|
-
|
2578
|
+
if count > 1
|
2579
|
+
perror 'Select only one file for rename.'
|
2580
|
+
return
|
2581
|
+
end
|
2582
|
+
|
2414
2583
|
target = readline "Rename #{text} to : "
|
2415
|
-
return if target == ''
|
2584
|
+
return if target == '' || target == '.' || target == '..'
|
2416
2585
|
|
2417
|
-
|
2418
|
-
target = File.basename(
|
2586
|
+
# file = File.expand_path(files)
|
2587
|
+
# target = File.basename(file) if target == '.'
|
2419
2588
|
if File.exist? target
|
2420
|
-
perror "Target (#{target}) exists"
|
2421
|
-
|
2422
|
-
|
2423
|
-
|
2589
|
+
perror "Target (#{target}) exists."
|
2590
|
+
return
|
2591
|
+
end
|
2592
|
+
|
2593
|
+
begin
|
2594
|
+
FileUtils.mv first, target
|
2595
|
+
message "Renamed #{first} to #{target}."
|
2596
|
+
rescue StandardError => exc
|
2597
|
+
@log.warn exc.to_s
|
2598
|
+
@log.warn "RENAME: files: #{first}, target:#{target}"
|
2599
|
+
pause exc.to_s
|
2424
2600
|
end
|
2601
|
+
refresh
|
2602
|
+
|
2425
2603
|
when :most, :less, :vim
|
2426
|
-
|
2604
|
+
|
2605
|
+
system "#{menu_text} #{shfiles}"
|
2427
2606
|
setup_terminal
|
2428
2607
|
# should we remove from selection ?
|
2429
2608
|
|
2430
2609
|
when :remspace
|
2431
|
-
#
|
2432
|
-
|
2433
|
-
|
2434
|
-
|
2435
|
-
|
2436
|
-
|
2437
|
-
case file
|
2438
|
-
when String
|
2439
|
-
farray = [file]
|
2440
|
-
when Array
|
2441
|
-
farray = file
|
2442
|
-
end
|
2610
|
+
# replace space with underscore in filename
|
2611
|
+
# Issue. in many cases directory may have spaces,
|
2612
|
+
# so we are using basename only. So use in one directory.
|
2613
|
+
# TODO: move to script
|
2614
|
+
clear_last_line
|
2615
|
+
pause "Remove spaces from #{text}."
|
2443
2616
|
|
2444
|
-
|
2445
|
-
|
2446
|
-
|
2447
|
-
|
2448
|
-
|
2617
|
+
ccount = 0
|
2618
|
+
rbfiles.each do |name|
|
2619
|
+
f = File.basename(name)
|
2620
|
+
next unless File.exist?(f)
|
2621
|
+
next unless f.index ' '
|
2622
|
+
|
2623
|
+
target = f.tr(' ', '_')
|
2624
|
+
next if File.exist? target
|
2625
|
+
|
2626
|
+
begin
|
2627
|
+
FileUtils.mv f, target
|
2628
|
+
ccount += 1
|
2629
|
+
rescue StandardError => exc
|
2630
|
+
@log.warn "REMSPACE: #{exc.to_s}"
|
2631
|
+
perror exc.to_s
|
2449
2632
|
end
|
2450
2633
|
end
|
2634
|
+
message "Renamed #{ccount} files."
|
2451
2635
|
refresh
|
2636
|
+
|
2452
2637
|
when :execute
|
2453
2638
|
execute
|
2639
|
+
|
2454
2640
|
when :page_stat_for_file
|
2455
2641
|
1
|
2456
2642
|
# already been executed by menu
|
@@ -2458,21 +2644,38 @@ def file_actions(action = nil)
|
|
2458
2644
|
else
|
2459
2645
|
return unless menu_text
|
2460
2646
|
|
2461
|
-
|
2462
|
-
|
2463
|
-
|
2464
|
-
|
2647
|
+
clear_last_line
|
2648
|
+
pause "#{menu_text} #{shfiles} "
|
2649
|
+
system "#{menu_text} #{shfiles}"
|
2650
|
+
pause # putting this back 2019-04-13 - file doesn't show anything
|
2651
|
+
message "Ran #{menu_text}."
|
2465
2652
|
setup_terminal
|
2466
2653
|
refresh
|
2467
2654
|
end
|
2468
2655
|
|
2469
|
-
|
2470
|
-
|
2471
|
-
|
2472
|
-
|
2473
|
-
|
2656
|
+
return if count == 0
|
2657
|
+
|
2658
|
+
clean_selected_files
|
2659
|
+
|
2660
|
+
end
|
2661
|
+
|
2662
|
+
# remove non-existent files from select list due to move or delete
|
2663
|
+
# or rename or whatever
|
2664
|
+
def clean_selected_files
|
2665
|
+
$selected_files.select! { |x| x = File.expand_path(x); File.exist?(x) }
|
2666
|
+
end
|
2667
|
+
|
2668
|
+
# set the default target for further moves
|
2669
|
+
# We need to also be able to set it to current dir in which user is. TODO
|
2670
|
+
def set_move_target cf=current_file
|
2671
|
+
ff = File.expand_path(cf)
|
2672
|
+
return unless File.directory? ff
|
2673
|
+
|
2674
|
+
@move_target = ff
|
2675
|
+
message "Move target set to #{cf}."
|
2474
2676
|
end
|
2475
2677
|
|
2678
|
+
|
2476
2679
|
# increase or decrease column
|
2477
2680
|
def columns_incdec(howmany)
|
2478
2681
|
$gviscols += howmany.to_i
|
@@ -2505,11 +2708,7 @@ end
|
|
2505
2708
|
|
2506
2709
|
# execute a command on selected or current file
|
2507
2710
|
def execute
|
2508
|
-
|
2509
|
-
run_command current_file
|
2510
|
-
return
|
2511
|
-
end
|
2512
|
-
run_command $selected_files
|
2711
|
+
run_command current_or_selected_files
|
2513
2712
|
end
|
2514
2713
|
|
2515
2714
|
def ag
|
@@ -2593,23 +2792,25 @@ end
|
|
2593
2792
|
|
2594
2793
|
## scroll cursor down
|
2595
2794
|
def cursor_scroll_dn
|
2795
|
+
@movement = :down
|
2596
2796
|
moveto(pos + MSCROLL)
|
2597
2797
|
end
|
2598
2798
|
|
2599
2799
|
def cursor_scroll_up
|
2800
|
+
@movement = :up
|
2600
2801
|
moveto(pos - MSCROLL)
|
2601
2802
|
end
|
2602
2803
|
|
2603
2804
|
# move cursor down a line
|
2604
2805
|
def cursor_dn
|
2605
|
-
|
2606
|
-
|
2806
|
+
@movement = :down
|
2807
|
+
@old_cursor = $cursor
|
2607
2808
|
moveto(pos + 1)
|
2608
2809
|
end
|
2609
2810
|
|
2610
2811
|
def cursor_up
|
2611
|
-
|
2612
|
-
|
2812
|
+
@old_cursor = $cursor
|
2813
|
+
@movement = :up
|
2613
2814
|
moveto(pos - 1)
|
2614
2815
|
end
|
2615
2816
|
|
@@ -2619,11 +2820,19 @@ def pos
|
|
2619
2820
|
end
|
2620
2821
|
|
2621
2822
|
# move cursor to given position/line
|
2622
|
-
def moveto
|
2823
|
+
def moveto position
|
2623
2824
|
orig = $cursor
|
2624
2825
|
$cursor = position
|
2625
2826
|
$cursor = [$cursor, $view.size - 1].min
|
2626
2827
|
$cursor = [$cursor, 0].max
|
2828
|
+
|
2829
|
+
# try to stop it from landing on separator
|
2830
|
+
if current_file == SEPARATOR
|
2831
|
+
$cursor += 1 if @movement == :down
|
2832
|
+
$cursor -= 1 if @movement == :up
|
2833
|
+
return
|
2834
|
+
end
|
2835
|
+
|
2627
2836
|
# 2019-03-18 - adding sta
|
2628
2837
|
# $sta = position - only when page flips and file not visible
|
2629
2838
|
# FIXME not correct, it must stop at end or correctly cycle
|
@@ -2639,13 +2848,13 @@ def moveto(position)
|
|
2639
2848
|
# $sta = $cursor
|
2640
2849
|
end
|
2641
2850
|
|
2642
|
-
|
2851
|
+
@movement = nil if oldsta != $sta # we need to redraw
|
2643
2852
|
|
2644
2853
|
star = [orig, $cursor].min
|
2645
2854
|
fin = [orig, $cursor].max
|
2646
2855
|
return unless $visual_mode
|
2647
2856
|
|
2648
|
-
|
2857
|
+
@movement = nil # visual mode needs to redraw page
|
2649
2858
|
|
2650
2859
|
# PWD has to be there in selction
|
2651
2860
|
if selected? current_file
|
@@ -2658,6 +2867,7 @@ def moveto(position)
|
|
2658
2867
|
# $selected_files.concat $view[star..fin]
|
2659
2868
|
add_to_selection $view[star..fin]
|
2660
2869
|
end
|
2870
|
+
message "#{$selected_files.count} files selected. "
|
2661
2871
|
# ensure
|
2662
2872
|
# redraw
|
2663
2873
|
end
|
@@ -2715,9 +2925,10 @@ def visual_mode_toggle
|
|
2715
2925
|
$visual_block_start = $cursor
|
2716
2926
|
add_to_selection current_file
|
2717
2927
|
end
|
2718
|
-
message "Visual mode is #{$visual_mode}"
|
2928
|
+
message "Visual mode is #{$visual_mode}."
|
2719
2929
|
end
|
2720
2930
|
|
2931
|
+
# Called from Escape key only. Clears selection.
|
2721
2932
|
def visual_block_clear
|
2722
2933
|
if $visual_block_start
|
2723
2934
|
star = [$visual_block_start, $cursor].min
|
@@ -2726,6 +2937,7 @@ def visual_block_clear
|
|
2726
2937
|
end
|
2727
2938
|
$visual_block_start = nil
|
2728
2939
|
$visual_mode = nil
|
2940
|
+
$mode = nil if $mode == 'VIS'
|
2729
2941
|
end
|
2730
2942
|
|
2731
2943
|
# ------------- file matching methods --------------------------------#
|
@@ -2771,27 +2983,44 @@ def goto_line pos
|
|
2771
2983
|
$cursor = pos
|
2772
2984
|
end
|
2773
2985
|
|
2774
|
-
#
|
2775
|
-
|
2986
|
+
# return filetype of file using `file` external command.
|
2987
|
+
# NOTE: Should we send back executable as separate type or allow
|
2988
|
+
# it to be nil, so it will be paged.
|
2989
|
+
def filetype f
|
2776
2990
|
return nil unless f
|
2777
2991
|
|
2778
2992
|
f = Shellwords.escape(f)
|
2779
2993
|
s = `file #{f}`
|
2780
|
-
if s.index 'text'
|
2781
|
-
|
2782
|
-
|
2783
|
-
|
2784
|
-
|
2785
|
-
return :zip
|
2786
|
-
elsif s.index 'image'
|
2787
|
-
return :image
|
2788
|
-
elsif s.index 'data'
|
2789
|
-
return :text
|
2790
|
-
end
|
2994
|
+
return :text if s.index 'text'
|
2995
|
+
return :zip if s.index(/[Zz]ip/)
|
2996
|
+
return :zip if s.index('archive')
|
2997
|
+
return :image if s.index 'image'
|
2998
|
+
return :text if s.index 'data'
|
2791
2999
|
|
2792
3000
|
nil
|
2793
3001
|
end
|
2794
3002
|
|
3003
|
+
def opener_for f
|
3004
|
+
# by default, default command is nil. Changed in toggle_pager_mode
|
3005
|
+
$default_command ||= '$PAGER'
|
3006
|
+
# by default mode, is false, changed in toggle_pager_mode
|
3007
|
+
# Get filetype, and check for command for type, else extn else unknown
|
3008
|
+
if !$editor_mode
|
3009
|
+
ft = filetype f
|
3010
|
+
comm = PAGER_COMMAND[ft] if ft
|
3011
|
+
comm ||= PAGER_COMMAND[File.extname(f)]
|
3012
|
+
comm ||= PAGER_COMMAND[:unknown]
|
3013
|
+
else
|
3014
|
+
# 2019-04-10 - what does this mean, that in editor_mode, editor
|
3015
|
+
# opens everything? what of images etc
|
3016
|
+
# TODO use editor only for text, otherwise use filetype or another hash
|
3017
|
+
# like editor_command
|
3018
|
+
comm = $default_command
|
3019
|
+
end
|
3020
|
+
comm ||= $default_command
|
3021
|
+
comm
|
3022
|
+
end
|
3023
|
+
|
2795
3024
|
# save offset in directory so we can revert to it when we return
|
2796
3025
|
def save_dir_pos
|
2797
3026
|
# the next line meant that it would not save first directory.
|
@@ -2846,7 +3075,7 @@ def current_file
|
|
2846
3075
|
end
|
2847
3076
|
|
2848
3077
|
def current_or_selected_files
|
2849
|
-
return $selected_files if
|
3078
|
+
return $selected_files if !$selected_files.empty?
|
2850
3079
|
|
2851
3080
|
return [current_file]
|
2852
3081
|
end
|
@@ -2874,11 +3103,13 @@ def scripts
|
|
2874
3103
|
|
2875
3104
|
# 2019-04-08 - to avoid confusion, we pass name of file under cursor
|
2876
3105
|
# script may ignore this and use selected_files
|
3106
|
+
reset_terminal
|
2877
3107
|
system %( #{binding} "#{current_file}" )
|
2878
3108
|
|
2879
3109
|
# system %(echo "#{cf}" | xargs #{binding})
|
2880
3110
|
pause
|
2881
|
-
|
3111
|
+
setup_terminal
|
3112
|
+
refresh
|
2882
3113
|
end
|
2883
3114
|
|
2884
3115
|
# allow user to select a script that generates filenames which
|
@@ -2917,17 +3148,32 @@ end
|
|
2917
3148
|
# script could use old selection again.
|
2918
3149
|
def write_selected_files
|
2919
3150
|
|
2920
|
-
|
3151
|
+
require 'pathname'
|
3152
|
+
# fname = File.join(File.dirname(CONFIG_FILE), 'selected_files')
|
3153
|
+
# 2019-04-10 - changed to ~/tmp otherwise confusion about location
|
3154
|
+
fname = File.join('~/tmp/', 'selected_files')
|
2921
3155
|
fname = File.expand_path(fname)
|
2922
3156
|
|
2923
3157
|
# remove file if no selection
|
2924
|
-
|
2925
|
-
File.unlink(fname)
|
3158
|
+
if $selected_files.empty?
|
3159
|
+
File.unlink(fname) if File.exist?(fname)
|
2926
3160
|
return nil
|
2927
3161
|
end
|
2928
3162
|
|
3163
|
+
# TODO : what if user does not want full path e,g zip
|
3164
|
+
# TODO: what if unix commands need escaped files ?
|
3165
|
+
base = Pathname.new Dir.pwd
|
2929
3166
|
File.open(fname, 'w') do |file|
|
2930
|
-
$selected_files.each
|
3167
|
+
$selected_files.each do |row|
|
3168
|
+
|
3169
|
+
# use relative filename. Otherwise things like zip and tar run into issues
|
3170
|
+
unless @selected_files_fullpath_flag
|
3171
|
+
p = Pathname.new(row)
|
3172
|
+
row = p.relative_path_from(base)
|
3173
|
+
end
|
3174
|
+
row = Shellwords.escape(row) if @selected_files_escaped_flag
|
3175
|
+
file.puts row
|
3176
|
+
end
|
2931
3177
|
end
|
2932
3178
|
|
2933
3179
|
return fname
|
@@ -2979,36 +3225,40 @@ end
|
|
2979
3225
|
# include files.
|
2980
3226
|
def enhance_file_list
|
2981
3227
|
return unless $enhanced_mode
|
3228
|
+
begin
|
3229
|
+
actr = $files.size
|
2982
3230
|
|
2983
3231
|
# zshglob: M = MARK_DIRS with slash
|
2984
3232
|
# zshglob: N = NULL_GLOB no error if no result, this is causing space to split
|
2985
3233
|
# file sometimes for single file.
|
2986
3234
|
|
3235
|
+
# FIXME: append only if we are adding.
|
3236
|
+
# FIXME: this makes it possible to select this row,
|
3237
|
+
# FIXME: and count it as a file !!
|
3238
|
+
# $files.append SEPARATOR
|
3239
|
+
|
2987
3240
|
# if only one entry and its a dir
|
2988
3241
|
# get its children and maybe the recent mod files a few
|
2989
3242
|
# FIXME: simplify condition into one
|
2990
3243
|
if $files.size == 1
|
2991
3244
|
# its a dir, let give the next level at least
|
2992
|
-
|
2993
|
-
d = $files.first
|
2994
|
-
# zshglob: 'om' = ordered on modification time
|
2995
|
-
# f1 = `zsh -c 'print -rl -- #{d}*(omM)'`.split("\n")
|
2996
|
-
f = get_files_by_mtime(d)
|
3245
|
+
return unless $files.first[-1] == '/'
|
2997
3246
|
|
2998
|
-
|
2999
|
-
|
3247
|
+
d = $files.first
|
3248
|
+
# zshglob: 'om' = ordered on modification time
|
3249
|
+
# f1 = `zsh -c 'print -rl -- #{d}*(omM)'`.split("\n")
|
3250
|
+
f = get_files_by_mtime(d)
|
3000
3251
|
|
3001
|
-
|
3002
|
-
|
3003
|
-
|
3004
|
-
|
3005
|
-
|
3006
|
-
|
3007
|
-
|
3008
|
-
|
3009
|
-
# just a file, not dirs here
|
3010
|
-
return
|
3252
|
+
# @log.warn "f1:#{f1} != f:#{f} in #{d}" if f1 != f
|
3253
|
+
# order returned by zsh and ruby are different since the time is the same
|
3254
|
+
|
3255
|
+
# TODO: use ruby this throws errors if not files
|
3256
|
+
if f && !f.empty?
|
3257
|
+
# @log.debug "CONCAT: #{f}" if @debug_flag
|
3258
|
+
$files.concat f
|
3259
|
+
$files.concat get_important_files(d)
|
3011
3260
|
end
|
3261
|
+
return
|
3012
3262
|
end
|
3013
3263
|
#
|
3014
3264
|
# check if a ruby project dir, although it could be a backup file too,
|
@@ -3114,18 +3364,22 @@ def enhance_file_list
|
|
3114
3364
|
# If you access the latest mod dir, then come back you get only one, since mod and accessed
|
3115
3365
|
# are the same dir, so we need to find the second modified dir
|
3116
3366
|
end
|
3367
|
+
ensure
|
3368
|
+
# if any files were added, then add a separator
|
3369
|
+
bctr = $files.size
|
3370
|
+
if actr < bctr
|
3371
|
+
$files.insert actr, SEPARATOR
|
3372
|
+
end
|
3373
|
+
end
|
3117
3374
|
end
|
3118
3375
|
|
3119
3376
|
# insert important files to end of $files
|
3120
3377
|
def insert_into_list _dir, file
|
3121
|
-
# ix = $files.index(dir)
|
3122
|
-
# raise "something wrong can find #{dir}." unless ix
|
3123
|
-
# $files.insert ix, *file
|
3124
|
-
# 2013-03-19 - 19:42 adding at end to avoid confusion
|
3125
|
-
# $files.concat file
|
3126
3378
|
$files.push(*file)
|
3127
3379
|
end
|
3128
3380
|
|
3381
|
+
# Get visited files and bookmarks that are inside this directory
|
3382
|
+
# at a lower level.
|
3129
3383
|
# 2019-03-23 - not exactly clear what is happening XXX
|
3130
3384
|
# this gets a directory (containing '/' at end)
|
3131
3385
|
def get_important_files dir
|
@@ -3206,15 +3460,13 @@ def gfb dir, func
|
|
3206
3460
|
# add slash to directories
|
3207
3461
|
sorted_files = add_slash sorted_files
|
3208
3462
|
return sorted_files
|
3209
|
-
# sorted_files.map do |f|
|
3210
|
-
# File.directory?(f) ? f + '/' : f
|
3211
|
-
# end
|
3212
3463
|
end
|
3213
3464
|
|
3214
3465
|
# set message which will be displayed in status line
|
3215
3466
|
# TODO: maybe we should pad it 2019-04-08 -
|
3216
3467
|
def message mess
|
3217
3468
|
$message = mess
|
3469
|
+
@keys_to_clear = 2 if mess
|
3218
3470
|
end
|
3219
3471
|
|
3220
3472
|
def last_line
|
@@ -3230,11 +3482,13 @@ def clear_last_line
|
|
3230
3482
|
# %*s - set blank spaces for entire line
|
3231
3483
|
# \e[m - reset text mode
|
3232
3484
|
# \r - bring to start of line since callers will print.
|
3233
|
-
print "\e[33;4%sm%*s\e[m\r" % [
|
3485
|
+
print "\e[33;4%sm%*s\e[m\r" % [@status_color || '1', $gcols, ' ']
|
3234
3486
|
end
|
3235
3487
|
|
3236
3488
|
# print right aligned
|
3237
|
-
#
|
3489
|
+
# XXX does not clear are, if earlier text was longer then that remains.
|
3490
|
+
# TODO: 2019-04-10 - this should update a variable, and status_line
|
3491
|
+
# should clear and reprint mode, message, patt and right text
|
3238
3492
|
def print_on_right text
|
3239
3493
|
sz = text.size
|
3240
3494
|
col = $gcols - sz - 1
|
@@ -3246,7 +3500,7 @@ def print_on_right text
|
|
3246
3500
|
system "tput cup #{$glines} #{col}"
|
3247
3501
|
# tput_cup $glines, $gcols - sz - 1
|
3248
3502
|
# print text
|
3249
|
-
print "\e[33;4#{
|
3503
|
+
print "\e[33;4#{@status_color_right}m#{text}\e[m"
|
3250
3504
|
end
|
3251
3505
|
|
3252
3506
|
# unused, should set bgcolor before printing
|
@@ -3256,6 +3510,15 @@ def print_last_line text
|
|
3256
3510
|
print text
|
3257
3511
|
end
|
3258
3512
|
|
3513
|
+
def clear_message
|
3514
|
+
if @keys_to_clear
|
3515
|
+
@keys_to_clear -= 1
|
3516
|
+
if @keys_to_clear == 0
|
3517
|
+
message nil
|
3518
|
+
@keys_to_clear = nil
|
3519
|
+
end
|
3520
|
+
end
|
3521
|
+
end
|
3259
3522
|
# main loop which calls all other programs
|
3260
3523
|
def run
|
3261
3524
|
|
@@ -3281,22 +3544,25 @@ def run
|
|
3281
3544
|
|
3282
3545
|
key = get_char
|
3283
3546
|
|
3547
|
+
|
3284
3548
|
unless resolve_key key # key did not map to file name, so don't redraw
|
3285
3549
|
place_cursor
|
3286
3550
|
next
|
3287
3551
|
end
|
3288
3552
|
|
3289
3553
|
# only movement has happened within this page, don't redraw
|
3290
|
-
if
|
3554
|
+
if @movement && @old_cursor
|
3291
3555
|
|
3292
3556
|
# we may want to print debug info if flag is on
|
3293
3557
|
if @debug_flag
|
3294
3558
|
clear_last_line
|
3295
3559
|
print_debug_info
|
3560
|
+
else
|
3561
|
+
status_line
|
3296
3562
|
end
|
3297
3563
|
|
3298
3564
|
place_cursor
|
3299
|
-
|
3565
|
+
@movement = false
|
3300
3566
|
next
|
3301
3567
|
end
|
3302
3568
|
|