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