cetus 0.1.32 → 0.1.33

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: 97704d648638971cc77d3f7f9b22e631893f7f176013da0acd95babf9dd69db1
4
- data.tar.gz: fc1759b09b662da0f31a93593d1b9eee8859503e122a84927873830bec342603
3
+ metadata.gz: 6ff3303709b421f3fed64413d85fc6c78e7693e3fd57f4223da67a6044a302eb
4
+ data.tar.gz: 414a490eb02e8aa98701b86674cf67597fca22cc144a2aedb1c5bbd0c7c37789
5
5
  SHA512:
6
- metadata.gz: d9975620b45a6b2a426af50e3c6d6afe48738d69a04d090aaf71090c8e647f1fe15fd17e3780eecd3dd7b4f749904432397f71aaaf66c140d67b2035432c75a6
7
- data.tar.gz: af1b12d3aa62b1f8e5a828d62bbedee51b222c71b6aab9840f44799322499c838a2ff168ca3f14ba5e09255a49246d6daebc0eba1234fe0562e74c0f7e5629cf
6
+ metadata.gz: ab77cb6b97de0d32f4ec89540ea0c296164d54805f9d4a83c452d9b513712e2a6a2942a4a16071e3f5fb3c2add77077e179d7c1028cd36f8a0003d357b797377
7
+ data.tar.gz: def9fe901b105277bedcfd6e6c6cd9fd1a41bb25771bbd6306f1c457f8f6b380a51d248081d456f864be37618f32398c41e69e1e8e1267e968473a66b2810302
data/.gitignore CHANGED
@@ -30,5 +30,6 @@ tada.txt
30
30
  todo
31
31
  todo.notes
32
32
  fix.txt
33
+ old*
33
34
  deadlink
34
35
  livelink
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-06 10:15
9
+ # Last update: 2019-04-10 10:16
10
10
  # --------------------------------------------------------------------------- #
11
11
  # cetus.rb Copyright (C) 2012-2019 rahul kumar
12
12
  # == CHANGELOG
@@ -39,7 +39,7 @@ require 'logger'
39
39
  # alias c=~/bin/cetus.rb
40
40
  # c
41
41
 
42
- VERSION = '0.1.32.0'.freeze
42
+ VERSION = '0.1.33.0'.freeze
43
43
  CONFIG_PATH = ENV['XDG_CONFIG_HOME'] || File.join(ENV['HOME'], '.config')
44
44
  CONFIG_FILE = "#{CONFIG_PATH}/cetus/conf.yml".freeze
45
45
 
@@ -48,7 +48,7 @@ $bindings = {
48
48
  '`' => 'main_menu',
49
49
  '=' => 'toggle_menu',
50
50
  'M-s' => 'selection_menu',
51
- 'M-o' => 'sort_menu',
51
+ 'M-o' => 'order_menu',
52
52
  'ENTER' => 'select_current',
53
53
  'C-p' => 'page_current',
54
54
  'C-e' => 'edit_current',
@@ -56,7 +56,7 @@ $bindings = {
56
56
  'C-s' => 'toggle_select',
57
57
  'C-r' => 'reduce',
58
58
  'C-g' => 'debug_vars',
59
- '@' => 'toggle_selection_mode',
59
+ '*' => 'toggle_selection_mode',
60
60
  'M-a' => 'select_all',
61
61
  'M-A' => 'unselect_all',
62
62
  '!' => 'execute',
@@ -99,6 +99,8 @@ $bindings = {
99
99
  'UP' => 'cursor_up',
100
100
  'DOWN' => 'cursor_dn',
101
101
  'C-SPACE' => 'visual_mode_toggle',
102
+ '@' => 'scripts',
103
+ '#' => 'generators',
102
104
 
103
105
  '?' => 'print_help',
104
106
  'F1' => 'print_help',
@@ -209,7 +211,8 @@ end
209
211
 
210
212
  # wrap readline so C-c can be ignored, but blank is taken as default
211
213
  def readline prompt='>'
212
- last_line
214
+ clear_last_line
215
+ print "\r"
213
216
  # do we need to clear till end of line, see enter_regex commented
214
217
  # unhide cursor
215
218
  print "\e[?25h"
@@ -280,6 +283,7 @@ ensure
280
283
  end
281
284
 
282
285
  ## GLOBALS
286
+ # hints or shortcuts to get to files without moving
283
287
  $IDX = ('a'..'y').to_a
284
288
  $IDX.delete 'q'
285
289
  $IDX.concat ('za'..'zz').to_a
@@ -309,6 +313,9 @@ $pager_command = {
309
313
  $dir_position = {}
310
314
  $movement = $old_cursor = nil # cursor movement has happened only, don't repaint
311
315
  $selection_mode = 1 # single select
316
+ @group_dirs = true
317
+ # truncate long filenames from :right, :left or :center.
318
+ @truncate_from = :center
312
319
 
313
320
  ## ----------------- CONSTANTS ----------------- ##
314
321
  GMARK = '*'.freeze
@@ -397,7 +404,7 @@ def read_directory
397
404
  $filterstr ||= 'M'
398
405
  # $files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}#{$filterstr})'`.split("\n")
399
406
  $files = list_files
400
- $files = sort_file_list $files
407
+ $files = group_directories_first($files) if @group_dirs
401
408
  return unless $enhanced_mode
402
409
 
403
410
  enhance_file_list
@@ -426,6 +433,8 @@ def list_files dir='*', sorto=$sorto, hidden=$hidden, filter=$filterstr
426
433
  :size
427
434
  when 'n'
428
435
  :path
436
+ when 'x'
437
+ :extname
429
438
  end
430
439
 
431
440
  # sort by time and then reverse so latest first.
@@ -442,7 +451,7 @@ def list_files dir='*', sorto=$sorto, hidden=$hidden, filter=$filterstr
442
451
  if File.exist? f
443
452
  File.send(func, f)
444
453
  else
445
- sys_stat(f, func)
454
+ sys_stat(func, f)
446
455
  end
447
456
  end
448
457
  end
@@ -458,11 +467,13 @@ def list_files dir='*', sorto=$sorto, hidden=$hidden, filter=$filterstr
458
467
  end
459
468
  # ------------- end of read_directory --------------------------------#
460
469
 
461
- # this is just a tempo method to deal with dead links.
462
- # I should be doing a system stat and parsing that
463
- def sys_stat file, func
470
+ # Deal with dead links.
471
+ def sys_stat func, file
464
472
  return unless File.symlink? file
465
473
 
474
+ # lstat does not respond to path and extname
475
+ return File.send(func, file) unless File.lstat(file).respond_to? func
476
+
466
477
  return File.lstat(file).send(func)
467
478
  end
468
479
 
@@ -524,7 +535,7 @@ def status_line
524
535
  cf = current_file
525
536
  $message = ' | No matches. Press ESCAPE' if $patt && !cf
526
537
 
527
- clear_status_line
538
+ clear_last_line
528
539
 
529
540
  # Print the filename at the right side of the status_line
530
541
  # sometimes due to search, there is no file
@@ -537,12 +548,23 @@ def status_line
537
548
  end
538
549
  end
539
550
  # move to beginning of line, reset text mode after printing
540
- print "\r#{v_mm}#{$patt}#{$message}\e[m"
551
+ # patt and message are together, no gap, why not ? 2019-04-08 -
552
+ if $patt && $patt != ''
553
+ patt = "[/#{$patt}]"
554
+ patt[-1] = '/i]' if $ignorecase
555
+ end
556
+ # bring cursor to start of line
557
+ # add background color
558
+ # print mode
559
+ # print search pattern if any
560
+ # print message if any
561
+ # print "\r#{v_mm}#{patt}#{$message}\e[m"
562
+ print "\r\e[33;4#{$status_color}m#{v_mm}#{patt}#{$message}\e[m"
541
563
 
542
564
  end
543
565
 
544
566
  def print_debug_info cf=current_file()
545
- print_on_right "= #{$sta},#{$cursor},#{$stact},#{$viewport.size},#{$grows} | #{cf}"
567
+ print_on_right "len:#{cf.length}/#{$temp_wid} = #{$sta},#{$cursor},#{$stact},#{$viewport.size},#{$grows} | #{cf}"
546
568
  end
547
569
 
548
570
  # should we do a read of the dir
@@ -645,16 +667,20 @@ end
645
667
  def redraw_required(flag=true) $redraw_required = flag; end
646
668
 
647
669
  def resolve_key key
670
+ ret = true
648
671
  if key.match?(/^[a-pr-zZ]$/)
649
672
  # hint mode
650
- select_hint $viewport, key
673
+ ret = select_hint $viewport, key
651
674
  elsif key == 'BACKSPACE'
652
675
  # do we really need this TODO
653
676
  $patt = $patt[0..-2] if $patt && !$patt.empty?
654
677
  $message = $patt = nil if $patt == ''
678
+ elsif '0123456789'.include? key
679
+ resolve_numeric_key key
655
680
  else
656
681
  resolve_binding key
657
682
  end
683
+ ret
658
684
  end
659
685
 
660
686
  def resolve_binding key
@@ -675,6 +701,23 @@ def resolve_binding key
675
701
  end
676
702
  end
677
703
 
704
+ # numbers represent quick bookmarks
705
+ # if bookmark exists, go to else create it.
706
+ def resolve_numeric_key key
707
+ d = $bookmarks[key]
708
+ if d
709
+ change_dir d
710
+ return
711
+ end
712
+
713
+ set_bookmark key
714
+ message "Created bookmark #{key}."
715
+ end
716
+
717
+ def set_bookmark key, dir=Dir.pwd
718
+ $bookmarks[key] = dir
719
+ end
720
+
678
721
  ## write current dir to a file so we can ccd to it when exiting
679
722
  def write_curdir
680
723
  f = File.expand_path('~/.fff_d')
@@ -719,6 +762,7 @@ def columnate ary, siz
719
762
  # if less than sz then 1 col and full width
720
763
  #
721
764
  wid = get_width ary.size, siz
765
+ $temp_wid = wid
722
766
 
723
767
  # ix refers to the index in the complete file list, wherease we only show 60 at a time
724
768
  ix = 0
@@ -741,7 +785,7 @@ def columnate ary, siz
741
785
  f = truncate_formatted_filename(f, unformatted_len, wid)
742
786
 
743
787
 
744
- else
788
+ elsif unformatted_len < wid
745
789
 
746
790
  # f = f.ljust(wid)
747
791
  f << ' ' * (wid - unformatted_len)
@@ -766,15 +810,54 @@ end
766
810
 
767
811
  # shorten the filename to wid
768
812
  # unformatted_len is the length without ANSI formatting
813
+ # wid is the exact width every line should restrict itself to.
814
+ # f is filename with hint and space and possible ANSI codes.
815
+ # WARN: check for hint getting swallowed in case of 5 columns
769
816
  def truncate_formatted_filename f, unformatted_len, wid
770
817
  excess = unformatted_len - wid
771
- center = unformatted_len / 2
772
- excess_half = excess / 2
773
- point = center + excess_half
774
- point1 = point - excess
775
- # the space comes after the ANSI formatting
776
- f = f[0..(point1 - 1)] + '$' + f[point + 2 .. -1] + ' '
777
- f
818
+
819
+ f = case @truncate_from
820
+
821
+ when :right
822
+ # text starts at 0, or 'm' if escape code
823
+ sindex = f[0] == "\e" ? f.index('m') : 0
824
+
825
+ # We show exactly `wid` characters, but escapes are not displayed
826
+ # so add that may characters
827
+ # Also, remove 2 for $ and space
828
+ f[0..wid + sindex - 2] + "$ \e[m"
829
+
830
+ when :center
831
+
832
+ # from central point calculate how much to remove in both directions
833
+ center = unformatted_len / 2
834
+ excess_half = excess / 2
835
+ point = center + excess_half
836
+ point1 = point - excess
837
+
838
+ # remove text between point1 and point
839
+ f[0..(point1 - 1)] + '$' + f[point + 2..-1] + ' '
840
+
841
+ when :left
842
+
843
+ # NOTE: we cannot remove the hint
844
+ # for single hints we need to add extra space
845
+ # there could be escape codes of varying length
846
+ sindex = f.index ' '
847
+ # if f[0] == "\e"
848
+ # mindex = f.index('m')
849
+ # hintsize = sindex - mindex - 1
850
+ # else
851
+ # hintsize = sindex - 1
852
+ # end
853
+ # f[0..sindex + 1] + '<' + f[-wid + hintsize..-1] + ' '
854
+ @log.debug "XXX #{excess}: #{f} / #{wid}"
855
+ # 4 = 2 for literals, 2 to get ahead of sindex+1
856
+ f[0..sindex + 1] + '<' + f[sindex + 4 + excess..-1] + ' '
857
+ # in some cases 29/32 we are actually overlapping 2 parts above
858
+ # same for 34/32
859
+ end
860
+ return f
778
861
  end
779
862
 
780
863
  def get_width arysz, siz
@@ -813,7 +896,6 @@ def format_array(ary)
813
896
  ix = 0
814
897
  ctr = 0
815
898
  ary.each do |f|
816
- # raise "#{f} != #{ary[ix]}" if f != ary[ix]
817
899
  ## ctr refers to the index in the column
818
900
  ind = get_shortcut(ix)
819
901
  mark = SPACE
@@ -971,10 +1053,10 @@ def select_hint view, key
971
1053
  # if x or z take a key IF there are those many
972
1054
  #
973
1055
  ix = get_index(key, view.size)
974
- return unless ix
1056
+ return nil unless ix
975
1057
 
976
1058
  f = view[ix]
977
- return unless f
1059
+ return nil unless f
978
1060
 
979
1061
  $cursor = $sta + ix
980
1062
 
@@ -985,6 +1067,7 @@ def select_hint view, key
985
1067
  else
986
1068
  open_file f
987
1069
  end
1070
+ true
988
1071
  end
989
1072
 
990
1073
  ## toggle selection state of file
@@ -1093,16 +1176,14 @@ def run_command(f)
1093
1176
  when String
1094
1177
  files = Shellwords.escape(f)
1095
1178
  end
1096
- print "Run a command on #{files}: "
1097
1179
  begin
1098
1180
  # Readline::HISTORY.push(*values)
1099
- command = readline
1181
+ command = readline "Run a command on #{files}: "
1100
1182
  # command = gets().chomp
1101
1183
  return if command.empty?
1102
1184
 
1103
- print 'Second part of command: '
1104
1185
  # command2 = gets().chomp
1105
- command2 = readline
1186
+ command2 = readline 'Second part of command: '
1106
1187
  puts "#{command} #{files} #{command2}"
1107
1188
  system "#{command} #{files} #{command2}"
1108
1189
  setup_terminal
@@ -1168,7 +1249,7 @@ def refresh
1168
1249
  end
1169
1250
 
1170
1251
  # put directories first, then files
1171
- def sort_file_list files
1252
+ def group_directories_first files
1172
1253
  dirs = files.select { |f| File.directory?(f) }
1173
1254
  # earlier I had File? which removed links, esp dead ones
1174
1255
  fi = files.select { |f| !File.directory?(f) }
@@ -1195,7 +1276,7 @@ def goto_dir
1195
1276
  # path = gets.chomp
1196
1277
  path = readline 'Enter path to go to: '
1197
1278
  if path.nil? || path == ''
1198
- clear_status_line
1279
+ clear_last_line
1199
1280
  return
1200
1281
  end
1201
1282
  # rescue => ex
@@ -1235,7 +1316,7 @@ def toggle_selection_mode
1235
1316
  else
1236
1317
  $selection_mode = 2
1237
1318
  $mode = 'SEL'
1238
- message 'Typing a hint selects the file. Toggling again will clear selection. '
1319
+ message 'Typing a hint selects the file. Typing again will clear . '
1239
1320
  end
1240
1321
  end
1241
1322
 
@@ -1285,16 +1366,8 @@ def goto_bookmark(key = nil)
1285
1366
  end
1286
1367
  if key =~ /^[0-9A-Z]$/
1287
1368
  d = $bookmarks[key]
1288
- # this is if we use zfm's bookmarks which have a position
1289
- # this way we leave the position as is, so it gets written back
1290
- nextpos = nil
1291
1369
  if d
1292
- if d.index(':')
1293
- ix = d.index(':')
1294
- nextpos = d[ix + 1..-1]
1295
- d = d[0, ix]
1296
- end
1297
- change_dir d #, nextpos
1370
+ change_dir d
1298
1371
  else
1299
1372
  perror "#{key} not a bookmark"
1300
1373
  end
@@ -1416,7 +1489,6 @@ def main_menu
1416
1489
  :a => :ag,
1417
1490
  '/' => :ffind,
1418
1491
  :l => :locate,
1419
- :V => :viminfo,
1420
1492
  :v => :vidir,
1421
1493
  :z => :z_interface,
1422
1494
  :d => :child_dirs,
@@ -1425,15 +1497,11 @@ def main_menu
1425
1497
  '2' => :select_from_used_dirs,
1426
1498
  :t => :dirtree,
1427
1499
  '4' => :tree,
1428
- :o => :sort_menu,
1500
+ :o => :order_menu,
1429
1501
  :F => :filter_menu,
1430
- :c => :command_menu,
1502
+ :c => :create_menu,
1431
1503
  :b => :bookmark_menu,
1432
1504
  :s => :selection_menu,
1433
- :B => :bindkey_ext_command,
1434
- :M => :create_a_dir,
1435
- '%' => :create_a_file,
1436
- 'S' => :scripts,
1437
1505
  :x => :extras
1438
1506
  }
1439
1507
  menu 'Main Menu', h
@@ -1444,7 +1512,7 @@ def selection_menu
1444
1512
  :a => :select_all,
1445
1513
  :u => :unselect_all,
1446
1514
  :s => :toggle_select,
1447
- '@' => 'toggle_selection_mode',
1515
+ '*' => 'toggle_selection_mode',
1448
1516
  'x' => 'visual_mode_toggle',
1449
1517
  :v => :view_selected_files
1450
1518
  }
@@ -1469,7 +1537,17 @@ def menu title, h
1469
1537
  # h.each_pair { |k, v| puts " #{k}: #{v}" }
1470
1538
  # 2019-03-09 - trying out using `column` to print in cols
1471
1539
  ary = []
1472
- h.each_pair { |k, v| ary << " #{k}: #{v}" }
1540
+
1541
+ # 2019-04-07 - check $bindings for shortcut and get key, add global
1542
+ # binding in brackets
1543
+ h.each_pair do |k, v|
1544
+
1545
+ # get global binding
1546
+ scut = $bindings.key(v.to_s)
1547
+ scut = " (#{scut})" if scut
1548
+
1549
+ ary << " #{k}: #{v} #{scut}"
1550
+ end
1473
1551
  x = ary.join("\n")
1474
1552
  puts %x{echo "#{x}" | column}
1475
1553
 
@@ -1486,14 +1564,19 @@ def menu title, h
1486
1564
  end
1487
1565
 
1488
1566
  def toggle_menu
1489
- h = { :h => :toggle_hidden,
1490
- :c => :toggle_case,
1491
- :l => :toggle_long_list,
1567
+ h = { h: :toggle_hidden,
1568
+ c: :toggle_case,
1569
+ l: :toggle_long_list,
1492
1570
  '1' => :toggle_columns,
1571
+ d: :toggle_group_dirs,
1493
1572
  :p => :toggle_pager_mode,
1494
- :d => :toggle_debug_flag,
1495
- :m => :toggle_selection_mode,
1496
- :e => :toggle_enhanced_list }
1573
+ :D => :toggle_debug_flag,
1574
+ '8' => :toggle_selection_mode,
1575
+ '*' => :toggle_selection_mode,
1576
+ v: :visual_mode_toggle,
1577
+ t: :toggle_truncate_from,
1578
+ :e => :toggle_enhanced_list
1579
+ }
1497
1580
 
1498
1581
  _, menu_text = menu 'Toggle Menu', h
1499
1582
  return unless menu_text
@@ -1508,11 +1591,13 @@ def toggle_menu
1508
1591
  message "Show hidden is now #{!$hidden.nil?}"
1509
1592
  rescan_required
1510
1593
  when :toggle_case
1511
- # $ignorecase = $ignorecase ? "" : "i"
1512
1594
  $ignorecase = !$ignorecase
1513
- # pause "Ignore Case is now #{$ignorecase}. Press a key."
1514
1595
  message "Ignore Case is now #{$ignorecase}"
1515
1596
  rescan_required
1597
+ when :toggle_group_dirs
1598
+ @group_dirs = !@group_dirs
1599
+ message "Group Dirs First is now #{@group_dirs}"
1600
+ rescan_required
1516
1601
  when :toggle_columns
1517
1602
  if $gviscols == 1
1518
1603
  $gviscols = 3
@@ -1535,7 +1620,7 @@ def toggle_menu
1535
1620
  message "Default command is #{$default_command}"
1536
1621
  when :toggle_enhanced_list
1537
1622
  $enhanced_mode = !$enhanced_mode
1538
- message "Enhanced mode is #{$long_listing}"
1623
+ message "Enhanced mode is #{$enhanced_mode}"
1539
1624
  rescan_required
1540
1625
 
1541
1626
  when :toggle_long_list
@@ -1558,15 +1643,32 @@ def toggle_menu
1558
1643
  when :toggle_debug_flag
1559
1644
  @debug_flag = !@debug_flag
1560
1645
  message "Debug flag is #{@debug_flag}"
1646
+
1647
+ when :toggle_truncate_from
1648
+ # if filename exceeds width cut from which direction
1649
+ @truncate_from = case @truncate_from
1650
+ when :center
1651
+ :left
1652
+ when :left
1653
+ :right
1654
+ when :right
1655
+ :center
1656
+ else
1657
+ :center
1658
+ end
1561
1659
  end
1660
+ message "Truncate long filenames from: #{@truncate_from}"
1562
1661
  end
1563
1662
 
1564
- def sort_menu
1663
+ def order_menu
1565
1664
  # zsh o = order, O = reverse order
1566
1665
  # ruby mtime/atime/ctime come reversed so we have to change o to O
1567
1666
  lo = nil
1568
1667
  h = { m: :modified, a: :accessed, M: :oldest,
1569
- l: :largest, s: :smallest, n: :name, r: :rev_name, d: :dirs, c: :inode,
1668
+ s: :largest, S: :smallest, n: :name, N: :rev_name,
1669
+ # d: :dirs,
1670
+ c: :inode,
1671
+ x: :extension,
1570
1672
  z: :clear }
1571
1673
  _, menu_text = menu 'Sort Menu', h
1572
1674
  case menu_text
@@ -1584,6 +1686,8 @@ def sort_menu
1584
1686
  lo = 'oL'
1585
1687
  when :name
1586
1688
  lo = 'on'
1689
+ when :extension
1690
+ lo = 'ox'
1587
1691
  when :rev_name
1588
1692
  lo = 'On'
1589
1693
  when :dirs
@@ -1597,8 +1701,17 @@ def sort_menu
1597
1701
  rescan_required
1598
1702
  end
1599
1703
 
1704
+ def create_menu
1705
+ h = { f: :create_a_file,
1706
+ d: :create_a_dir,
1707
+ b: :create_bookmark
1708
+ }
1709
+ _, menu_text = menu 'Create Menu', h
1710
+ end
1711
+
1600
1712
  # thse need to be placed in correct position, some do nothing
1601
1713
  # and some like ffind have no menu item
1714
+ # TODO FIXME uncalled now. look into this
1602
1715
  def command_menu
1603
1716
  # since these involve full paths, we need more space, like only one column
1604
1717
  ## in these cases, getting back to the earlier dir, back to earlier listing
@@ -1649,7 +1762,15 @@ end
1649
1762
 
1650
1763
  # This is quite badly placed and named. Maybe these should go elsewhere
1651
1764
  def extras
1652
- h = { '1' => :one_column, '2' => :multi_column, :c => :columns, :r => :config_read, :w => :config_write }
1765
+ h = { '1' => :one_column,
1766
+ '2' => :multi_column,
1767
+ :c => :columns,
1768
+ s: :scripts,
1769
+ g: :generators,
1770
+ :B => :bindkey_ext_command,
1771
+ :r => :config_read,
1772
+ :w => :config_write
1773
+ }
1653
1774
  key, menu_text = menu 'Extras Menu', h
1654
1775
  case menu_text
1655
1776
  when :one_column
@@ -1716,13 +1837,14 @@ def reduce pattern=nil
1716
1837
  pattern = gets.chomp
1717
1838
  end
1718
1839
  $title = "Filter: pattern #{pattern}"
1719
- $files = $files.select {|f| f.match(pattern)}
1840
+ $files = $files.select { |f| f.match(pattern) }
1720
1841
  end
1721
1842
 
1722
1843
  def filter_for_current_extension
1723
1844
  extn = File.extname(current_file)
1724
1845
  return unless extn
1725
- reduce extn
1846
+
1847
+ $files = $files.select { |f| !File.directory?(f) && extn == File.extname(f) }
1726
1848
  end
1727
1849
 
1728
1850
  def select_from_used_dirs
@@ -1838,7 +1960,7 @@ def create_bookmark
1838
1960
  # print "\e[?25l" # hide cursor
1839
1961
  if key =~ /^[0-9A-Z]$/
1840
1962
  # $bookmarks[key] = "#{Dir.pwd}:#{$cursor}"
1841
- $bookmarks[key] = Dir.pwd
1963
+ set_bookmark key
1842
1964
  $modified = true
1843
1965
  message "Created bookmark #{key}"
1844
1966
  else
@@ -1927,7 +2049,6 @@ def child_dirs
1927
2049
  $title = 'Directories in current directory'
1928
2050
  # M is MARK_DIRS option for putting trailing slash after dir
1929
2051
  # $files = `zsh -c 'print -rl -- *(/#{$sorto}#{$hidden}M)'`.split("\n")
1930
- # $files = Dir.glob("*", File::FNM_DOTMATCH).select {|f| File.directory?(f)} - %w[ . ..]
1931
2052
  $files = dirs
1932
2053
  message "#{$files.size} directories."
1933
2054
  # redraw
@@ -1999,7 +2120,7 @@ end
1999
2120
  # This is supposed to print on the status line
2000
2121
  # but prints on next line.FIXME 2019-03-24 - 00:08
2001
2122
  def perror text
2002
- clear_status_line
2123
+ clear_last_line
2003
2124
  last_line
2004
2125
  print "#{RED}#{text}. Press a key.#{CLEAR}"
2005
2126
  get_char
@@ -2011,15 +2132,25 @@ def pause text=' Press a key.'
2011
2132
  get_char
2012
2133
  end
2013
2134
 
2014
- ## return shortcut for an index (offset in file array)
2015
- # use 2 more arrays to make this faster
2016
- # if z or Z take another key if there are those many in view
2017
- # Also, display ROWS * COLS so now we are not limited to 60.
2018
- def get_shortcut(ix)
2019
- return '<' if ix < $stact
2135
+ ## return shortcut/hint for an index (offset in file array)
2136
+ # ix is the index of a file in the complete array (view)
2137
+ def get_shortcut index
2020
2138
 
2021
- ix -= $stact
2022
- i = $IDX[ix]
2139
+ # Case where user has panned to the right columns:
2140
+ # Earlier, we showed '<' in left columns, if user has panned right.
2141
+ # Now we show unused shortcuts after exhausting them.
2142
+ # return '<' if index < $stact
2143
+ if index < $stact
2144
+ index = $viewport.size - $stact + index
2145
+ i = $IDX[index]
2146
+ return i if i
2147
+
2148
+ return '['
2149
+ end
2150
+
2151
+ # Normal case (user has not panned columns)
2152
+ index -= $stact
2153
+ i = $IDX[index]
2023
2154
  return i if i
2024
2155
 
2025
2156
  '->'
@@ -2030,18 +2161,44 @@ end
2030
2161
  # should we even ask for a second key if there are not enough rows
2031
2162
  # What if we want to also trap z with numbers for other purposes
2032
2163
  def get_index(key, vsz = 999)
2033
- i = $IDX.index(key)
2034
- return i + $stact if i
2164
+ # @log.debug "Etners get_index with #{key}"
2165
+ i = convert_key_to_index key
2166
+ return i if i
2035
2167
 
2036
- # sz = $IDX.size
2037
- zch = nil
2038
2168
  if vsz > 25
2039
2169
  if key == 'z' || key == 'Z'
2170
+ last_line
2040
2171
  print key
2041
2172
  zch = get_char
2042
2173
  print zch
2043
- i = $IDX.index("#{key}#{zch}")
2044
- return i + $stact if i
2174
+ i = convert_key_to_index("#{key}#{zch}")
2175
+ @log.debug "convert returned #{i} for #{key}#{zch}"
2176
+ return i if i
2177
+ # i = $IDX.index
2178
+ # return i + $stact if i
2179
+ end
2180
+ end
2181
+ nil
2182
+ end
2183
+
2184
+ # convert pressed key to an index in viewport.
2185
+ # Earlier this was simple, but now that we put hints/shortcuts
2186
+ # in rows on the left after panning, we need to account for cycled hints.
2187
+ def convert_key_to_index key
2188
+ i = $IDX.index(key)
2189
+ if i
2190
+ # @log.debug "get_index with #{key}: #{i}. #{$stact}. #{$viewport.size}"
2191
+ vps = $viewport.size
2192
+ return nil if $stact == 0 && i + $stact >= vps
2193
+ # return nil if $stact > 0 && i + $stact >= vps && i + $stact - vps >= $stact
2194
+ return nil if $stact > 0 && i + $stact >= vps && i - vps >= 0
2195
+
2196
+ if i + $stact >= vps
2197
+ # panning case, hints are recycled
2198
+ return (i + $stact) - vps
2199
+ else
2200
+ # regular hint
2201
+ return i + $stact #if i
2045
2202
  end
2046
2203
  end
2047
2204
  nil
@@ -2196,7 +2353,7 @@ def file_actions(action = nil)
2196
2353
  view_selected_files if key == '?'
2197
2354
  return if key != 'y'
2198
2355
 
2199
- clear_status_line
2356
+ clear_last_line
2200
2357
  print "\r deleting ..."
2201
2358
  system "#{delcommand} #{files}"
2202
2359
  refresh
@@ -2427,26 +2584,13 @@ def locate
2427
2584
  # redraw
2428
2585
  end
2429
2586
 
2430
- ## Displays files from .viminfo file, if you use some other editor which
2431
- # tracks files opened then you can modify this accordingly.
2432
- # Neovim does not use viminfo
2433
- #
2434
- def viminfo
2435
- file = File.expand_path('~/.viminfo')
2436
- return unless File.exist? file
2437
- $title = 'Files from ~/.viminfo'
2438
- # $files = `grep '^>' ~/.viminfo | cut -d ' ' -f 2- | sed "s#~#$HOME#g"`.split("\n")
2439
- $files = `grep '^>' ~/.viminfo | cut -d ' ' -f 2- `.split("\n")
2440
- $files.select! { |x| x = File.expand_path(x); File.exist?(x) }
2441
- # redraw
2442
- end
2443
-
2444
2587
  ## takes directories from the z program, if you use autojump you can
2445
2588
  # modify this accordingly
2446
2589
  #
2447
2590
  def z_interface
2448
2591
  file = File.expand_path('~/.z')
2449
2592
  return unless File.exist? file
2593
+
2450
2594
  $title = 'Directories from ~/.z'
2451
2595
  $files = `sort -rn -k2 -t '|' ~/.z | cut -f1 -d '|'`.split("\n")
2452
2596
  home = ENV['HOME']
@@ -2462,7 +2606,9 @@ def vidir
2462
2606
  setup_terminal
2463
2607
  end
2464
2608
 
2465
- ## some cursor movement functions
2609
+ # ------------- movement related methods --------------------------------#
2610
+
2611
+ ## scroll cursor down
2466
2612
  def cursor_scroll_dn
2467
2613
  moveto(pos + MSCROLL)
2468
2614
  end
@@ -2471,6 +2617,7 @@ def cursor_scroll_up
2471
2617
  moveto(pos - MSCROLL)
2472
2618
  end
2473
2619
 
2620
+ # move cursor down a line
2474
2621
  def cursor_dn
2475
2622
  $movement = true
2476
2623
  $old_cursor = $cursor
@@ -2483,6 +2630,7 @@ def cursor_up
2483
2630
  moveto(pos - 1)
2484
2631
  end
2485
2632
 
2633
+ # return cursor position
2486
2634
  def pos
2487
2635
  $cursor
2488
2636
  end
@@ -2514,6 +2662,8 @@ def moveto(position)
2514
2662
  fin = [orig, $cursor].max
2515
2663
  return unless $visual_mode
2516
2664
 
2665
+ $movement = nil # visual mode needs to redraw page
2666
+
2517
2667
  # PWD has to be there in selction
2518
2668
  if selected? current_file
2519
2669
  # this depends on the direction
@@ -2528,21 +2678,25 @@ def moveto(position)
2528
2678
  # ensure
2529
2679
  # redraw
2530
2680
  end
2681
+ # --
2531
2682
 
2532
2683
  # is given file in selected array
2533
- def selected?(file)
2684
+ def visited?(file)
2534
2685
  $current_dir ||= Dir.pwd
2535
2686
  file = File.join($current_dir, file)
2536
- return $selected_files.index file
2687
+ return $visited_files.index file
2537
2688
  end
2538
2689
 
2690
+ # ------------- selection related methods --------------------------------#
2691
+
2539
2692
  # is given file in selected array
2540
- def visited?(file)
2693
+ def selected?(file)
2541
2694
  $current_dir ||= Dir.pwd
2542
2695
  file = File.join($current_dir, file)
2543
- return $visited_files.index file
2696
+ return $selected_files.index file
2544
2697
  end
2545
2698
 
2699
+ # add given file/s to selected file list
2546
2700
  def add_to_selection(file)
2547
2701
  ff = file
2548
2702
  case file
@@ -2569,6 +2723,7 @@ def remove_from_selection(file)
2569
2723
  end
2570
2724
  end
2571
2725
 
2726
+ # ------------- visual mode methods --------------------------------#
2572
2727
  def visual_mode_toggle
2573
2728
  $mode = nil
2574
2729
  $visual_mode = !$visual_mode
@@ -2577,6 +2732,7 @@ def visual_mode_toggle
2577
2732
  $visual_block_start = $cursor
2578
2733
  add_to_selection current_file
2579
2734
  end
2735
+ message "Visual mode is #{$visual_mode}"
2580
2736
  end
2581
2737
 
2582
2738
  def visual_block_clear
@@ -2589,7 +2745,11 @@ def visual_block_clear
2589
2745
  $visual_mode = nil
2590
2746
  end
2591
2747
 
2592
- def file_starting_with(first_char)
2748
+ # ------------- file matching methods --------------------------------#
2749
+ # this is a bit silly because it only works with lower case chars, we
2750
+ # cannot get to files starting with numbers or uppercase since it is not
2751
+ # called in those cases !!
2752
+ def file_starting_with first_char
2593
2753
  ix = return_next_match(method(:file_matching?), "^#{first_char}")
2594
2754
  goto_line ix if ix
2595
2755
  end
@@ -2619,13 +2779,14 @@ end
2619
2779
  ##
2620
2780
  # position cursor on a specific line which could be on a nother page
2621
2781
  # therefore calculate the correct start offset of the display also.
2622
- def goto_line(pos)
2782
+ def goto_line pos
2623
2783
  pages = ((pos * 1.00) / $pagesize).ceil
2624
2784
  pages -= 1
2625
2785
  $sta = pages * $pagesize + 1
2626
2786
  $cursor = pos
2627
2787
  end
2628
2788
 
2789
+ # ---
2629
2790
  def filetype(f)
2630
2791
  return nil unless f
2631
2792
 
@@ -2668,9 +2829,7 @@ def revert_dir_pos
2668
2829
  end
2669
2830
 
2670
2831
  def create_a_dir
2671
- print
2672
- print 'Enter directory name: '
2673
- str = readline
2832
+ str = readline 'Enter directory name: '
2674
2833
  return if str == ''
2675
2834
 
2676
2835
  if File.exist? str
@@ -2687,9 +2846,7 @@ def create_a_dir
2687
2846
  end
2688
2847
 
2689
2848
  def create_a_file
2690
- print
2691
- print 'Enter file name: '
2692
- str = readline
2849
+ str = readline 'Enter file name: '
2693
2850
  return if str.nil? || str == ''
2694
2851
 
2695
2852
  system %($EDITOR "#{str}")
@@ -2710,47 +2867,84 @@ def current_or_selected_files
2710
2867
  end
2711
2868
 
2712
2869
  # ------------------- scripts ------------------ #
2713
- # prompt for scripts to execute, giving selected file names
2714
- # NOTE: TODO 2019-03-21 - some scripts can output a filelist to display
2870
+ # prompt for scripts to execute, giving file name under cursor
2715
2871
  def scripts
2716
- write_selected_files
2717
2872
  # some scripts may work with the selected_files and not want to be called
2718
2873
  # with filenames.
2874
+ write_selected_files
2875
+
2719
2876
  title = 'Select a script'
2720
2877
  script_path = '~/.config/cetus/scripts'
2721
- # TODO write selected files to a known file before calling
2722
- binding = `find #{script_path} -type f | fzf --prompt="#{title.to_s} :"`
2723
- return unless binding
2878
+ binding = `find #{script_path} -type f | fzf --prompt="#{title} :"`.chomp
2879
+ return if binding.nil? || binding == ''
2724
2880
 
2725
- binding = binding.chomp if binding
2726
2881
  # TODO: check if binding is a file and executable
2727
2882
  # xargs only seems to take the first file
2728
2883
  # cf = current_or_selected_files.join('\0')
2729
2884
  # cf = Shellwords.join(current_or_selected_files)
2730
- current_or_selected_files.each do |file|
2731
- system %( #{binding} "#{file}" )
2732
- end
2885
+ # This was getting called repeatedly even if script used selected_files
2886
+ # current_or_selected_files.each do |file|
2887
+ # system %( #{binding} "#{file}" )
2888
+ # end
2889
+
2890
+ # 2019-04-08 - to avoid confusion, we pass name of file under cursor
2891
+ # script may ignore this and use selected_files
2892
+ system %( #{binding} "#{current_file}" )
2893
+
2733
2894
  # system %(echo "#{cf}" | xargs #{binding})
2734
2895
  pause
2896
+ redraw_required
2897
+ end
2898
+
2899
+ # allow user to select a script that generates filenames which
2900
+ # will be displayed for selection or action.
2901
+ def generators
2902
+ write_selected_files
2903
+
2904
+ title = 'Select a generator'
2905
+ script_path = '~/.config/cetus/generators'
2906
+ binding = `find #{script_path} -type f | fzf --prompt="#{title} :"`.chomp
2907
+ return if binding.nil? || binding == ''
2908
+
2909
+ # call generator and accept list of files
2910
+ $title = "Files from #{File.basename(binding)}"
2911
+ $files = `#{binding} "#{current_file}"`.split("\n")
2912
+
2735
2913
  end
2736
2914
  # ------------- end of scripts --------------------------------#
2737
2915
 
2738
2916
  # ------------------- view_selected_files ------------------ #
2739
2917
  def view_selected_files
2740
2918
  fname = write_selected_files
2919
+
2920
+ unless fname
2921
+ message "No file selected. "
2922
+ return
2923
+ end
2924
+
2741
2925
  system "$PAGER #{fname}"
2742
2926
  setup_terminal
2743
2927
  end
2744
2928
  # ------------- end of view_selected_files --------------------------------#
2745
2929
 
2746
2930
  # write selected files to a file and return path
2931
+ # if no selected files then blank out the file, or else
2932
+ # script could use old selection again.
2747
2933
  def write_selected_files
2934
+
2748
2935
  fname = File.join(File.dirname(CONFIG_FILE), 'selected_files')
2749
2936
  fname = File.expand_path(fname)
2750
2937
 
2938
+ # remove file if no selection
2939
+ unless $selected_files
2940
+ File.unlink(fname)
2941
+ return nil
2942
+ end
2943
+
2751
2944
  File.open(fname, 'w') do |file|
2752
2945
  $selected_files.each { |row| file.puts row }
2753
2946
  end
2947
+
2754
2948
  return fname
2755
2949
  end
2756
2950
  ##
@@ -3031,7 +3225,9 @@ def gfb dir, func
3031
3225
  # File.directory?(f) ? f + '/' : f
3032
3226
  # end
3033
3227
  end
3228
+
3034
3229
  # set message which will be displayed in status line
3230
+ # TODO: maybe we should pad it 2019-04-08 -
3035
3231
  def message mess
3036
3232
  $message = mess
3037
3233
  end
@@ -3042,22 +3238,34 @@ def last_line
3042
3238
  tput_cup $glines, 0
3043
3239
  end
3044
3240
 
3045
- def clear_status_line
3241
+ def clear_last_line
3046
3242
  last_line
3047
3243
  # print a colored line at bottom of screen
3048
3244
  # \e[33;41m - set color of status_line
3049
3245
  # %*s - set blank spaces for entire line
3050
- print "\e[33;4%sm%*s" % [$status_color || '1', $gcols, " "]
3246
+ # \e[m - reset text mode
3247
+ print "\e[33;4%sm%*s\e[m" % [$status_color || '1', $gcols, " "]
3051
3248
  end
3052
3249
 
3250
+ # print right aligned
3251
+ # FIXME: if text longer then width then does not print
3053
3252
  def print_on_right text
3054
3253
  sz = text.size
3055
- system "tput cup #{$glines} #{$gcols - sz - 1}"
3254
+ col = $gcols - sz - 1
3255
+ col = 2 if col < 2
3256
+ if sz > $gcols - 2
3257
+ text = text[0..$gcols-3]
3258
+ end
3259
+ # system "tput cup #{$glines} #{$gcols - sz - 1}"
3260
+ system "tput cup #{$glines} #{col}"
3056
3261
  # tput_cup $glines, $gcols - sz - 1
3057
- print text
3262
+ # print text
3263
+ print "\e[33;4#{$status_color}m#{text}\e[m"
3058
3264
  end
3059
3265
 
3266
+ # unused, should set bgcolor before printing
3060
3267
  def print_last_line text
3268
+ # unused ??
3061
3269
  last_line
3062
3270
  print text
3063
3271
  end
@@ -3073,6 +3281,7 @@ def run
3073
3281
  setup_terminal
3074
3282
  config_read
3075
3283
  parse_ls_colors
3284
+ set_bookmark '0'
3076
3285
 
3077
3286
  redraw true
3078
3287
  place_cursor
@@ -3085,15 +3294,26 @@ def run
3085
3294
  loop do
3086
3295
 
3087
3296
  key = get_char
3088
- resolve_key key
3297
+
3298
+ unless resolve_key key # key did not map to file name, so don't redraw
3299
+ place_cursor
3300
+ next
3301
+ end
3302
+
3303
+ # only movement has happened within this page, don't redraw
3089
3304
  if $movement && $old_cursor
3090
- clear_status_line
3091
- print_debug_info if @debug_flag
3305
+
3092
3306
  # we may want to print debug info if flag is on
3307
+ if @debug_flag
3308
+ clear_last_line
3309
+ print_debug_info
3310
+ end
3311
+
3093
3312
  place_cursor
3094
3313
  $movement = false
3095
3314
  next
3096
3315
  end
3316
+
3097
3317
  redraw rescan?
3098
3318
  place_cursor
3099
3319
 
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
6
 
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = 'cetus'
9
- spec.version = '0.1.32'
9
+ spec.version = '0.1.33'
10
10
  spec.authors = ['Rahul Kumar']
11
11
  spec.email = ['oneness.univ@gmail.com']
12
12
  spec.description = %q{lightning fast file navigator}
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ ## Displays files from .viminfo file, if you use some other editor which
3
+ # tracks files opened then you can modify this accordingly.
4
+ # Neovim does not use viminfo
5
+ file = File.expand_path('~/.viminfo')
6
+ return unless File.exist? file
7
+ files = `grep '^>' ~/.viminfo | cut -d ' ' -f 2- `.split("\n")
8
+ files.select! { |x| x = File.expand_path(x); File.exist?(x) }
9
+ files.each { |f| puts f }
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # testing z interface
4
+
5
+ file = File.expand_path('~/.z')
6
+ return unless File.exist? file
7
+
8
+ # $title = 'Directories from ~/.z'
9
+ files = `sort -rn -k2 -t '|' ~/.z | cut -f1 -d '|'`.split("\n")
10
+ home = ENV['HOME']
11
+ # shorten file names
12
+ files.collect! do |f|
13
+ f.sub(/#{home}/, '~')
14
+ end
15
+ files.each { |f| puts f }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cetus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.32
4
+ version: 0.1.33
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rahul Kumar
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-06 00:00:00.000000000 Z
11
+ date: 2019-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,6 +53,8 @@ files:
53
53
  - Rakefile
54
54
  - bin/cetus
55
55
  - cetus.gemspec
56
+ - generators/viminfo
57
+ - generators/z
56
58
  homepage: http://github.com/rkumar/cetus
57
59
  licenses:
58
60
  - MIT