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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 443d16e21b148f67cc9f5e946cd30c208849851d3f00d61654f9397608d9efc2
4
- data.tar.gz: e313ebd0039f87472eff95115a56484bb2fa298e2a94783ea52bdeb804fcaeed
3
+ metadata.gz: 273d25acae029a8c12d1e07bd80396c7f6eeb05f6136b112be702a7cebd7a01d
4
+ data.tar.gz: 25891b9fc8105132be8cb4325f23767fc2abf79096b98bcee9c11a41f0ffb612
5
5
  SHA512:
6
- metadata.gz: fc89827c004339856387fa481fd88ae15bf5a0d0eb0ae75fc4a7837ea055adc9f19e96b6ac128897c2655beeaedc0bdbbc72b023155b0dbd1ee9322f0ae818c1
7
- data.tar.gz: b72d9d557dba6c150720362053ba247df1745c018a53c387ef857f63d2b6937931f59668b2fa2b518456a6972353f228cc975e8c760ebecf00f4a07faf1ecd8e
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-10 11:18
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.34.0'.freeze
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' => 'edit_current',
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
- $pager_command = {
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
- $movement = $old_cursor = nil # cursor movement has happened only, don't repaint
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
- $status_color = 4 # status line, can be 2 3 4 5 6
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
- if fl.zero?
517
- t = "#{$title} No files."
518
- else
519
- t = "#{$title} #{$sta + 1} to #{fin} of #{fl} #{$sorto} F:#{$filterstr}"
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 status_line
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#{$status_color}m#{v_mm}#{patt}#{$message}\e[m"
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 $old_cursor
679
+ return unless @old_cursor
644
680
 
645
- hint = get_shortcut($old_cursor)
646
- if $old_cursor < $grows
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 #{$old_cursor + 2} 0"
649
- tput_cup $old_cursor, 0
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 = $old_cursor % $grows
656
- cols = ($old_cursor / $grows) * wid
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
- # a to y is direct
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
- $default_command ||= '$PAGER'
1107
- if !$editor_mode
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 = $view[$cursor]
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 = nil
1173
- case f
1174
- when Array
1175
- # escape the contents and create a string
1176
- files = Shellwords.join(f)
1177
- when String
1178
- files = Shellwords.escape(f)
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
- # Readline::HISTORY.push(*values)
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
- puts "#{command} #{files} #{command2}"
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
- get_char
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
- $old_cursor = nil
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
- $old_cursor = nil
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 "file #{current_file}"
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
- puts
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
- last_line # 2019-03-30 - required since cursor is not longer at bottom
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
- print "\r#{RED}#{text}. Press a key.#{CLEAR}"
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
- return nil if $stact == 0 && i + $stact >= vps
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 nil if $stact > 0 && i + $stact >= vps && i - vps >= 0
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(deflt = nil)
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
- # only add dtrx for gz
2283
- h = { d: :delete, D: '/bin/rm', m: :move, r: :rename, v: ENV['EDITOR'] || :vim,
2284
- c: :copy, C: :chdir, W: :remspace, e: :execute, s: :page_stat_for_file,
2285
- l: :less, p: :most, f: :file, o: :open, x: :dtrx, z: :zip }
2286
- # acttext = h[action.to_sym] || action
2287
- acttext = action || ''
2288
- file = nil
2289
-
2290
- sct = $selected_files.size
2291
- if sct > 0
2292
- text = "#{sct} files"
2293
- file = $selected_files
2294
- else
2295
- # 2019-03-07 - trying out direct deletes
2296
- # why were we aksing to select a file when user is on a file
2297
- # print "[#{acttext}] Choose a file [#{$view[$cursor]}]: "
2298
- # file = ask_hint $view[$cursor]
2299
- file = $view[$cursor]
2300
- unless file
2301
- file = ask_hint $view[$cursor]
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
- return unless file
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
- # 2019-03-07 - NOTE at this point file can be one or more files.
2308
- # 2019-03-07 - text can be a file or count of files, so unreliable !!! FIXME
2429
+ h[:M] = :set_move_target if File.directory?(current_file)
2430
+ h[:z] = :zip unless filetype(first) == :zip
2309
2431
 
2310
- case file
2311
- when Array
2312
- # escape the contents and create a string
2313
- files = Shellwords.join(file)
2314
- when String
2315
- files = Shellwords.escape(file)
2316
- end
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
- key = nil
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
- last_line
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} #{files}"
2470
+ system "#{delcommand} #{shfiles}"
2471
+ message "Deleted #{text}."
2342
2472
  refresh
2343
2473
 
2344
2474
  when :move
2345
- # 2019-03-07 - NOTE this will only work with single file selection
2346
- # target = gets().chomp
2347
- # target = Readline.readline('>', true)
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 = '.' if 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? target
2358
- begin
2359
- FileUtils.mv file, target
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 = readline "Copy #{text} to : "
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 = '.' if target == ''
2513
+ target = default if target == ''
2376
2514
  target = File.expand_path(target)
2377
2515
  return if target == ''
2378
2516
 
2379
- if File.directory? target
2380
- begin
2381
- FileUtils.cp file, target
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
- change_dir File.dirname(text)
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
- # target = gets().chomp
2548
+
2396
2549
  target = readline 'Archive name: '
2397
2550
  return unless target
2398
2551
  return if target == ''
2399
2552
 
2400
- # don't want a blank space or something screwing up
2401
- if target && target.size > 3
2402
- if File.exist? target
2403
- perror "Target (#{target}) exists"
2404
- else
2405
- system "tar zcvf #{target} #{files}"
2406
- setup_terminal
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
- # 2019-03-07 NOTE works for single file FIXME
2413
- # 2019-03-07 - TODO for n files replace pattern with string
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
- text = File.expand_path(text)
2418
- target = File.basename(text) if target == '.'
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
- else
2422
- FileUtils.mv text, target
2423
- refresh
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
- system "#{menu_text} #{files}"
2604
+
2605
+ system "#{menu_text} #{shfiles}"
2427
2606
  setup_terminal
2428
2607
  # should we remove from selection ?
2429
2608
 
2430
2609
  when :remspace
2431
- # 2019-03-08 - 00:07 added replace space with underscore in filename
2432
- print "Remove spaces from #{file}"
2433
- pause
2434
- farray = nil
2435
-
2436
- # stupidly using one variable for scalar and array DUH !
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
- farray.each do |f|
2445
- if f.index ' '
2446
- nname = f.tr(' ', '_')
2447
- FileUtils.mv f, nname unless File.exist? nname
2448
- # if success then remove from selection
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
- @log.debug "#{menu_text} #{files}"
2462
- last_line
2463
- pause "#{menu_text} #{files}"
2464
- system "#{menu_text} #{files}"
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
- # remove non-existent files from select list due to move or delete
2470
- # or rename or whatever
2471
- if sct > 0
2472
- $selected_files.select! { |x| x = File.expand_path(x); File.exist?(x) }
2473
- end
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
- if $selected_files.empty?
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
- $movement = true
2606
- $old_cursor = $cursor
2806
+ @movement = :down
2807
+ @old_cursor = $cursor
2607
2808
  moveto(pos + 1)
2608
2809
  end
2609
2810
 
2610
2811
  def cursor_up
2611
- $old_cursor = $cursor
2612
- $movement = true
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(position)
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
- $movement = nil if oldsta != $sta # we need to redraw
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
- $movement = nil # visual mode needs to redraw page
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
- def filetype(f)
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
- return :text
2782
- elsif s.index(/[Zz]ip/)
2783
- return :zip
2784
- elsif s.index('archive')
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 $selected_files.size > 0
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
- redraw_required
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
- fname = File.join(File.dirname(CONFIG_FILE), 'selected_files')
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
- unless $selected_files
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 { |row| file.puts row }
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
- if $files.first[-1] == '/'
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
- # @log.warn "f1:#{f1} != f:#{f} in #{d}" if f1 != f
2999
- # order returned by zsh and ruby are different since the time is the same
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
- # TODO: use ruby this throws errors if not files
3002
- if f && !f.empty?
3003
- # @log.debug "CONCAT: #{f}" if @debug_flag
3004
- $files.concat f
3005
- $files.concat get_important_files(d)
3006
- return
3007
- end
3008
- else
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" % [$status_color || '1', $gcols, " "]
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
- # FIXME: if text longer then width then does not print
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#{$status_color}m#{text}\e[m"
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 $movement && $old_cursor
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
- $movement = false
3565
+ @movement = false
3300
3566
  next
3301
3567
  end
3302
3568