cetus 0.1.32 → 0.1.33

Sign up to get free protection for your applications and to get access to all the features.
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