cetus 0.1.29 → 0.1.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +16 -0
- data/README.md +2 -0
- data/bin/cetus +714 -269
- data/cetus.gemspec +4 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97704d648638971cc77d3f7f9b22e631893f7f176013da0acd95babf9dd69db1
|
4
|
+
data.tar.gz: fc1759b09b662da0f31a93593d1b9eee8859503e122a84927873830bec342603
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9975620b45a6b2a426af50e3c6d6afe48738d69a04d090aaf71090c8e647f1fe15fd17e3780eecd3dd7b4f749904432397f71aaaf66c140d67b2035432c75a6
|
7
|
+
data.tar.gz: af1b12d3aa62b1f8e5a828d62bbedee51b222c71b6aab9840f44799322499c838a2ff168ca3f14ba5e09255a49246d6daebc0eba1234fe0562e74c0f7e5629cf
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -6,6 +6,8 @@ lightning-fast file navigator
|
|
6
6
|
Tested with ruby 2.5
|
7
7
|
|
8
8
|
Latest changes:
|
9
|
+
2019-03-26 - Reading up 'LS_COLORS' and coloring filenames. v0.1.30
|
10
|
+
2019-03-24 - Major refactoring and cleanup. v 0.1.29
|
9
11
|
2019-03-04 - q is quit key, not Q
|
10
12
|
- show directories first, then files.
|
11
13
|
- trying not to pollute main screen/terminal with listings (i.e. use alt screen)
|
data/bin/cetus
CHANGED
@@ -6,10 +6,12 @@
|
|
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-
|
9
|
+
# Last update: 2019-04-06 10:15
|
10
10
|
# --------------------------------------------------------------------------- #
|
11
11
|
# cetus.rb Copyright (C) 2012-2019 rahul kumar
|
12
12
|
# == CHANGELOG
|
13
|
+
# 2019-03-24 - adding colors per line, but columnate will have to change
|
14
|
+
# since size calc will include color codes. Same for truncate
|
13
15
|
# 2019-02-20 - added smcup and rmcup so alt-screen is used. works a bit
|
14
16
|
# 2019-03-04 - change clear to go to 0,0 and clear down to reduce pollution
|
15
17
|
# 2019-03-04 - changed quit to q (earlier Q)
|
@@ -17,7 +19,6 @@
|
|
17
19
|
# 2019-03-10 - changing selected_files to have fullpath
|
18
20
|
# 2019-03-22 - refactoring the code, esp run()
|
19
21
|
# == TODO
|
20
|
-
# FIXME how to check that redraw not called twice depending on where
|
21
22
|
# a method is called from. a menu or direct from key. e.g help
|
22
23
|
# To dirs add GEMHOME RUBYLIB PYTHONILB or PYTHONPATH,
|
23
24
|
# Make rubygem aware : gemspec or Gemfile the expand lib and bin
|
@@ -29,21 +30,25 @@ require 'io/wait'
|
|
29
30
|
require 'shellwords'
|
30
31
|
# https://docs.ruby-lang.org/en/2.6.0/FileUtils.html
|
31
32
|
require 'fileutils'
|
33
|
+
require 'logger'
|
34
|
+
# @log = Logger.new(File.expand_path '~/tmp/log.txt')
|
35
|
+
@log = Logger.new('log.txt')
|
32
36
|
|
33
37
|
## INSTALLATION
|
34
38
|
# copy into PATH
|
35
39
|
# alias c=~/bin/cetus.rb
|
36
40
|
# c
|
37
41
|
|
38
|
-
VERSION = '0.1.
|
39
|
-
|
42
|
+
VERSION = '0.1.32.0'.freeze
|
43
|
+
CONFIG_PATH = ENV['XDG_CONFIG_HOME'] || File.join(ENV['HOME'], '.config')
|
44
|
+
CONFIG_FILE = "#{CONFIG_PATH}/cetus/conf.yml".freeze
|
40
45
|
|
41
|
-
$bindings = {}
|
46
|
+
# $bindings = {}
|
42
47
|
$bindings = {
|
43
48
|
'`' => 'main_menu',
|
44
49
|
'=' => 'toggle_menu',
|
45
|
-
'M-
|
46
|
-
'M-
|
50
|
+
'M-s' => 'selection_menu',
|
51
|
+
'M-o' => 'sort_menu',
|
47
52
|
'ENTER' => 'select_current',
|
48
53
|
'C-p' => 'page_current',
|
49
54
|
'C-e' => 'edit_current',
|
@@ -51,7 +56,7 @@ $bindings = {
|
|
51
56
|
'C-s' => 'toggle_select',
|
52
57
|
'C-r' => 'reduce',
|
53
58
|
'C-g' => 'debug_vars',
|
54
|
-
'@' => '
|
59
|
+
'@' => 'toggle_selection_mode',
|
55
60
|
'M-a' => 'select_all',
|
56
61
|
'M-A' => 'unselect_all',
|
57
62
|
'!' => 'execute',
|
@@ -193,7 +198,9 @@ def setup_terminal
|
|
193
198
|
# '\e[2J': Clear the screen.
|
194
199
|
# '\e[1;Nr': Limit scrolling to scrolling area.
|
195
200
|
# Also sets cursor to (0,0).
|
196
|
-
printf("\e[?1049h\e[?7l\e[?25l\e[2J\e[1;%sr", $glines)
|
201
|
+
# printf("\e[?1049h\e[?7l\e[?25l\e[2J\e[1;%sr", $glines)
|
202
|
+
# 2019-03-29 - XXX temporarily not hiding cursor to see if we can place it.
|
203
|
+
printf("\e[?1049h\e[?7l\e[?25h\e[2J\e[1;%sr", $glines)
|
197
204
|
# earlier glines was grows
|
198
205
|
|
199
206
|
# Hide echoing of user input
|
@@ -202,15 +209,20 @@ end
|
|
202
209
|
|
203
210
|
# wrap readline so C-c can be ignored, but blank is taken as default
|
204
211
|
def readline prompt='>'
|
212
|
+
last_line
|
213
|
+
# do we need to clear till end of line, see enter_regex commented
|
205
214
|
# unhide cursor
|
206
215
|
print "\e[?25h"
|
216
|
+
system 'stty echo'
|
207
217
|
begin
|
208
218
|
target = Readline.readline(prompt, true)
|
209
219
|
rescue Interrupt
|
210
220
|
return nil
|
211
221
|
ensure
|
212
222
|
# hide cursor
|
213
|
-
|
223
|
+
# NO LONGER HIDING cursor 2019-03-29 -
|
224
|
+
# print "\e[?25l"
|
225
|
+
system 'stty -echo'
|
214
226
|
end
|
215
227
|
target.chomp
|
216
228
|
end
|
@@ -261,7 +273,10 @@ def get_char
|
|
261
273
|
# end
|
262
274
|
return c.chr if c
|
263
275
|
ensure
|
264
|
-
system('stty -raw echo 2>/dev/null') # turn raw input on
|
276
|
+
# system('stty -raw echo 2>/dev/null') # turn raw input on
|
277
|
+
# 2019-03-29 - echo was causing printing of arrow key code on screen
|
278
|
+
# if moving fast
|
279
|
+
system('stty -raw 2>/dev/null') # turn raw input on
|
265
280
|
end
|
266
281
|
|
267
282
|
## GLOBALS
|
@@ -292,6 +307,8 @@ $pager_command = {
|
|
292
307
|
unknown: 'open'
|
293
308
|
}
|
294
309
|
$dir_position = {}
|
310
|
+
$movement = $old_cursor = nil # cursor movement has happened only, don't repaint
|
311
|
+
$selection_mode = 1 # single select
|
295
312
|
|
296
313
|
## ----------------- CONSTANTS ----------------- ##
|
297
314
|
GMARK = '*'.freeze
|
@@ -306,11 +323,43 @@ ON_RED = "\e[41m".freeze
|
|
306
323
|
GREEN = "\e[32m".freeze
|
307
324
|
YELLOW = "\e[33m".freeze
|
308
325
|
BLUE = "\e[1;34m".freeze
|
326
|
+
MAGENTA = "\e[35m".freeze
|
327
|
+
CYAN = "\e[36m".freeze
|
309
328
|
|
310
329
|
ON_BLUE = "\e[44m".freeze
|
311
330
|
REVERSE = "\e[7m".freeze
|
312
331
|
UNDERLINE = "\e[4m".freeze
|
313
|
-
CURSOR_COLOR =
|
332
|
+
CURSOR_COLOR = REVERSE
|
333
|
+
|
334
|
+
# NOTE: that osx uses LSCOLORS which only has colors for filetypes not
|
335
|
+
# extensions and patterns which LS_COLORS has.
|
336
|
+
# LS_COLORS contains 2 character filetype colors. ex executable mi broken link
|
337
|
+
# extension based colros starting with '*.'
|
338
|
+
# file pattern starting with '*' and a character that is not .
|
339
|
+
# File.ftype(path) returns
|
340
|
+
# file, directory di, characterSpecial cd, blockSpecial bd, fifo pi, link ln, socket so, or unknown
|
341
|
+
|
342
|
+
# This hash contains color codes for extensions. It is updated from
|
343
|
+
# LS_COLORS.
|
344
|
+
$ls_color = {
|
345
|
+
'.rb' => RED,
|
346
|
+
'.tgz' => MAGENTA,
|
347
|
+
'.zip' => MAGENTA,
|
348
|
+
'.torrent' => GREEN,
|
349
|
+
'.srt' => GREEN,
|
350
|
+
'.sh' => CYAN
|
351
|
+
}
|
352
|
+
# This hash contains colors for file patterns, updated from LS_COLORS
|
353
|
+
$ls_pattern = {}
|
354
|
+
# This hash contains colors for file types, updated from LS_COLORS
|
355
|
+
# Default values in absence of LS_COLORS
|
356
|
+
$ls_ftype = {
|
357
|
+
'directory' => BLUE,
|
358
|
+
'link' => "\e[01;36m",
|
359
|
+
'mi' => "\e[01;31;7m",
|
360
|
+
'or' => "\e[40;31;01m",
|
361
|
+
'ex' => "\e[01;32m"
|
362
|
+
}
|
314
363
|
## --------------------------------------------- ##
|
315
364
|
|
316
365
|
$patt = nil
|
@@ -322,7 +371,8 @@ $visited_files = []
|
|
322
371
|
$visited_dirs = []
|
323
372
|
## dirs where some work has been done, for saving and restoring
|
324
373
|
$used_dirs = []
|
325
|
-
|
374
|
+
# zsh o = order m = modified time
|
375
|
+
$default_sort_order = 'Om'
|
326
376
|
$sorto = $default_sort_order
|
327
377
|
$viewctr = 0
|
328
378
|
$history = []
|
@@ -337,16 +387,85 @@ $status_color = 4 # status line, can be 2 3 4 5 6
|
|
337
387
|
# ------------------- read_directory ------------------ #
|
338
388
|
def read_directory
|
339
389
|
rescan_required false
|
340
|
-
|
390
|
+
|
391
|
+
# http://zsh.sourceforge.net/Doc/Release/Shell-Builtin-Commands.html
|
392
|
+
# zsh print -rl
|
393
|
+
# -l Print the arguments separated by newlines instead of spaces.
|
394
|
+
# -r Ignore the escape conventions of echo.
|
395
|
+
# zshglob M = MARK_DIRS
|
396
|
+
|
341
397
|
$filterstr ||= 'M'
|
342
|
-
$files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}#{$filterstr})'`.split("\n")
|
398
|
+
# $files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}#{$filterstr})'`.split("\n")
|
399
|
+
$files = list_files
|
343
400
|
$files = sort_file_list $files
|
401
|
+
return unless $enhanced_mode
|
402
|
+
|
344
403
|
enhance_file_list
|
345
|
-
|
346
|
-
|
404
|
+
$files = $files.uniq
|
405
|
+
end
|
406
|
+
|
407
|
+
# return a list of directory contents sorted as per sort order
|
408
|
+
# NOTE: FNM_CASEFOLD does not work with Dir.glob
|
409
|
+
def list_files dir='*', sorto=$sorto, hidden=$hidden, filter=$filterstr
|
410
|
+
|
411
|
+
dir += '/*' if File.directory?(dir)
|
412
|
+
dir = dir.gsub('//', '/')
|
413
|
+
|
414
|
+
# decide sort method based on second character
|
415
|
+
# first char is o or O (reverse)
|
416
|
+
# second char is macLn etc (as in zsh glob)
|
417
|
+
so = sorto ? sorto[1] : nil
|
418
|
+
func = case so
|
419
|
+
when 'm'
|
420
|
+
:mtime
|
421
|
+
when 'a'
|
422
|
+
:atime
|
423
|
+
when 'c'
|
424
|
+
:ctime
|
425
|
+
when 'L'
|
426
|
+
:size
|
427
|
+
when 'n'
|
428
|
+
:path
|
429
|
+
end
|
430
|
+
|
431
|
+
# sort by time and then reverse so latest first.
|
432
|
+
sorted_files = if hidden == 'D'
|
433
|
+
Dir.glob(dir, File::FNM_DOTMATCH)
|
434
|
+
else
|
435
|
+
Dir.glob(dir)
|
436
|
+
end
|
437
|
+
|
438
|
+
# WARN: crashes on a dead link since no mtime
|
439
|
+
# HACK: use first files return value for bad link
|
440
|
+
if func
|
441
|
+
sorted_files = sorted_files.sort_by do |f|
|
442
|
+
if File.exist? f
|
443
|
+
File.send(func, f)
|
444
|
+
else
|
445
|
+
sys_stat(f, func)
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
sorted_files.sort! { |w1, w2| w1.casecmp(w2) } if func == :path && $ignorecase
|
451
|
+
|
452
|
+
# zsh gives mtime sort with latest first, ruby gives latest last
|
453
|
+
sorted_files.reverse! if sorto[0] == 'O'
|
454
|
+
|
455
|
+
# add slash to directories
|
456
|
+
sorted_files = add_slash sorted_files
|
457
|
+
return sorted_files
|
347
458
|
end
|
348
459
|
# ------------- end of read_directory --------------------------------#
|
349
460
|
|
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
|
464
|
+
return unless File.symlink? file
|
465
|
+
|
466
|
+
return File.lstat(file).send(func)
|
467
|
+
end
|
468
|
+
|
350
469
|
# ------------------- create_viewport ------------------ #
|
351
470
|
def create_viewport
|
352
471
|
$view = if $patt
|
@@ -382,21 +501,27 @@ def print_title
|
|
382
501
|
$title ||= Dir.pwd.sub(ENV['HOME'], '~')
|
383
502
|
fin = $sta + $viewport.size
|
384
503
|
fl = $view.size
|
385
|
-
|
504
|
+
if fl.zero?
|
505
|
+
t = "#{$title} No files."
|
506
|
+
else
|
507
|
+
t = "#{$title} #{$sta + 1} to #{fin} of #{fl} #{$sorto} F:#{$filterstr}"
|
508
|
+
end
|
386
509
|
|
387
510
|
# don't exceed columns while printing
|
388
511
|
t = t[t.size - $gcols..-1] if t.size >= $gcols
|
389
512
|
|
390
513
|
print "#{BOLD}#{t}#{CLEAR}\n"
|
514
|
+
print "EMPTY" if fl.zero?
|
391
515
|
end
|
392
516
|
# ------------- end of print_title --------------------------------#
|
393
517
|
|
394
|
-
# TODO clean this up and simplify it
|
518
|
+
# TODO: clean this up and simplify it
|
519
|
+
# NOTE: earlier this was called on every key (up-arow down arrow, now only
|
520
|
+
# called when page changes, so we only put directory name)
|
395
521
|
def status_line
|
396
522
|
# prompt
|
397
523
|
v_mm = $mode ? "[#{$mode}] " : ''
|
398
524
|
cf = current_file
|
399
|
-
$message = "No file highlighted (#{cf})" if cf and !$highlighted_a_column
|
400
525
|
$message = ' | No matches. Press ESCAPE' if $patt && !cf
|
401
526
|
|
402
527
|
clear_status_line
|
@@ -404,23 +529,22 @@ def status_line
|
|
404
529
|
# Print the filename at the right side of the status_line
|
405
530
|
# sometimes due to search, there is no file
|
406
531
|
if cf
|
407
|
-
# cfl = cf.size
|
408
|
-
# print right aligned TODO REFACTOR THIS
|
409
|
-
# padding = @debug_flag ? 15 : 3
|
410
|
-
# system "tput cup #{$glines} #{$gcols - cfl - padding}"
|
411
|
-
# print "#{REVERSE}#{cf}#{CLEAR}"
|
412
532
|
if @debug_flag
|
413
|
-
|
533
|
+
# XXX this will not work on file basis FIXME
|
534
|
+
print_debug_info cf
|
414
535
|
else
|
415
|
-
print_on_right "#{
|
536
|
+
print_on_right "#{Dir.pwd}"
|
416
537
|
end
|
417
538
|
end
|
418
|
-
# print "\r#{v_mm}#{$patt} #{GREEN}#{$message}#{CLEAR} >"
|
419
539
|
# move to beginning of line, reset text mode after printing
|
420
540
|
print "\r#{v_mm}#{$patt}#{$message}\e[m"
|
421
541
|
|
422
542
|
end
|
423
543
|
|
544
|
+
def print_debug_info cf=current_file()
|
545
|
+
print_on_right "= #{$sta},#{$cursor},#{$stact},#{$viewport.size},#{$grows} | #{cf}"
|
546
|
+
end
|
547
|
+
|
424
548
|
# should we do a read of the dir
|
425
549
|
def rescan?
|
426
550
|
$rescan_required
|
@@ -449,16 +573,73 @@ def draw_directory
|
|
449
573
|
print_title
|
450
574
|
|
451
575
|
# add hint and format the line
|
452
|
-
buff =
|
576
|
+
buff = format_array $viewport
|
453
577
|
|
454
|
-
# break into as many columns as required
|
578
|
+
# break viewport into as many columns as required
|
455
579
|
# This is where directories get their blue color
|
456
580
|
buff = columnate buff, $grows
|
457
581
|
|
582
|
+
# starts printing array on line 3
|
458
583
|
buff.each { |line| print line, "\n" }
|
459
584
|
print
|
460
585
|
|
461
586
|
status_line
|
587
|
+
|
588
|
+
# place cursor correctly, we will use this to highlight current row
|
589
|
+
end
|
590
|
+
|
591
|
+
# place cursor correctly, we will use this to highlight current row
|
592
|
+
# XXX i am not sure how to highlight the bg without rewriting the text.
|
593
|
+
# I can get the filename but it has been truncated.
|
594
|
+
# I can get the earlier filename but the color has to be determined again.
|
595
|
+
# FIXME flinks after paging
|
596
|
+
# FIXME color of original hint lost
|
597
|
+
def place_cursor
|
598
|
+
# clear_cursor
|
599
|
+
# hint = get_shortcut($cursor)
|
600
|
+
c = $cursor - $sta
|
601
|
+
if c < $grows
|
602
|
+
# system "tput cup #{c + 2} 0"
|
603
|
+
# print "\e[#{c + 3};0H"
|
604
|
+
tput_cup c, 0
|
605
|
+
# print "#{CURSOR_COLOR}#{hint} >#{CLEAR}"
|
606
|
+
return
|
607
|
+
end
|
608
|
+
wid = get_width $viewport.size, $grows
|
609
|
+
rows = c % $grows
|
610
|
+
cols = (c / $grows) * wid
|
611
|
+
# system "tput cup #{rows + 2} #{cols}"
|
612
|
+
# print "\e[#{rows + 3};#{cols + 1}H"
|
613
|
+
tput_cup rows, cols
|
614
|
+
# print "#{CURSOR_COLOR}#{hint} >#{CLEAR}"
|
615
|
+
end
|
616
|
+
|
617
|
+
# clear the earlier position when we move.
|
618
|
+
# Currently not using until I can implement chgat here.
|
619
|
+
def clear_cursor
|
620
|
+
return unless $old_cursor
|
621
|
+
|
622
|
+
hint = get_shortcut($old_cursor)
|
623
|
+
if $old_cursor < $grows
|
624
|
+
# FIXME: faster way of getting here ? see fff
|
625
|
+
# system "tput cup #{$old_cursor + 2} 0"
|
626
|
+
tput_cup $old_cursor, 0
|
627
|
+
# print "#{CURSOR_COLOR}#{hint} >#{CLEAR}"
|
628
|
+
print "#{hint} "
|
629
|
+
return
|
630
|
+
end
|
631
|
+
wid = get_width $viewport.size, $grows
|
632
|
+
rows = $old_cursor % $grows
|
633
|
+
cols = ($old_cursor / $grows) * wid
|
634
|
+
# system "tput cup #{rows + 2} #{cols}"
|
635
|
+
tput_cup rows, cols
|
636
|
+
# print "#{CURSOR_COLOR}#{hint} >#{CLEAR}"
|
637
|
+
print "#{hint} "
|
638
|
+
end
|
639
|
+
|
640
|
+
def tput_cup row, col
|
641
|
+
# we add 3: 2 is for the help and directory line. 1 is since tput is 1 based
|
642
|
+
print "\e[#{row + 3};#{col + 1}H"
|
462
643
|
end
|
463
644
|
|
464
645
|
def redraw_required(flag=true) $redraw_required = flag; end
|
@@ -490,12 +671,10 @@ def resolve_binding key
|
|
490
671
|
args = x
|
491
672
|
send(binding, *args) if binding
|
492
673
|
else
|
493
|
-
perror "No binding for #{key}"
|
674
|
+
# perror "No binding for #{key}"
|
494
675
|
end
|
495
676
|
end
|
496
677
|
|
497
|
-
|
498
|
-
|
499
678
|
## write current dir to a file so we can ccd to it when exiting
|
500
679
|
def write_curdir
|
501
680
|
f = File.expand_path('~/.fff_d')
|
@@ -529,79 +708,43 @@ end
|
|
529
708
|
##
|
530
709
|
#
|
531
710
|
# print in columns
|
532
|
-
# ary - array of data
|
533
|
-
#
|
711
|
+
# ary - array of data (viewport, not view)
|
712
|
+
# siz - how many lines should there be in one column
|
534
713
|
#
|
535
|
-
def columnate
|
714
|
+
def columnate ary, siz
|
536
715
|
buff = []
|
537
716
|
return buff if ary.nil? || ary.empty?
|
538
717
|
|
539
|
-
# 2019-03-19 - just to make sure highlighted column is visible
|
540
|
-
$highlighted_a_column = false
|
541
|
-
|
542
718
|
# determine width based on number of files to show
|
543
719
|
# if less than sz then 1 col and full width
|
544
720
|
#
|
545
|
-
wid =
|
546
|
-
ars = ary.size
|
547
|
-
ars = [$pagesize, ary.size].min
|
548
|
-
d = 0
|
549
|
-
if ars <= sz
|
550
|
-
wid = $gcols - d
|
551
|
-
else
|
552
|
-
tmp = (ars * 1.000 / sz).ceil
|
553
|
-
wid = $gcols / tmp - d
|
554
|
-
end
|
555
|
-
# elsif ars < sz * 2
|
556
|
-
# wid = $gcols/2 - d
|
557
|
-
# elsif ars < sz * 3
|
558
|
-
# wid = $gcols/3 - d
|
559
|
-
# else
|
560
|
-
# wid = $gcols/$gviscols - d
|
561
|
-
# end
|
721
|
+
wid = get_width ary.size, siz
|
562
722
|
|
563
723
|
# ix refers to the index in the complete file list, wherease we only show 60 at a time
|
564
724
|
ix = 0
|
565
725
|
loop do
|
566
726
|
## ctr refers to the index in the column
|
727
|
+
# siz is how many items in one column
|
567
728
|
ctr = 0
|
568
|
-
while ctr <
|
729
|
+
while ctr < siz
|
569
730
|
|
570
731
|
f = ary[ix]
|
571
732
|
# f is not just filename but marker and hint
|
572
|
-
# Check last char to see if directory
|
573
|
-
linecolor = nil
|
574
|
-
linecolor = BLUE if f[-1] == '/'
|
575
733
|
|
576
734
|
# check to see if we need to truncate
|
577
|
-
#
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
#
|
583
|
-
f = f
|
584
|
-
|
585
|
-
if ix + $sta == $cursor
|
586
|
-
f = "#{CURSOR_COLOR}#{f}#{CLEAR}"
|
587
|
-
$highlighted_a_column = true
|
588
|
-
elsif linecolor
|
589
|
-
f = "#{linecolor}#{f}#{CLEAR}"
|
590
|
-
end
|
735
|
+
# 2019-03-18 - truncate from middle of string.
|
736
|
+
# 2019-03-24 - truncate and size to take care of color codes
|
737
|
+
# fsz = f.size
|
738
|
+
unformatted_len, _ = filename_len(f)
|
739
|
+
if unformatted_len > wid
|
740
|
+
# take the excess out from the center on both sides
|
741
|
+
f = truncate_formatted_filename(f, unformatted_len, wid)
|
742
|
+
|
591
743
|
|
592
744
|
else
|
593
745
|
|
594
|
-
## we do the coloring before padding so the entire line does not get
|
595
|
-
# padded, only file name
|
596
|
-
# f = "#{CURSOR_COLOR}#{f}#{CLEAR}" if ix + $sta == $cursor
|
597
|
-
if ix + $sta == $cursor
|
598
|
-
f = "#{CURSOR_COLOR}#{f}#{CLEAR}"
|
599
|
-
$highlighted_a_column = true
|
600
|
-
else
|
601
|
-
f = "#{linecolor}#{f}#{CLEAR}" if linecolor
|
602
|
-
end
|
603
746
|
# f = f.ljust(wid)
|
604
|
-
f << ' ' * (wid -
|
747
|
+
f << ' ' * (wid - unformatted_len)
|
605
748
|
|
606
749
|
end
|
607
750
|
|
@@ -621,15 +764,43 @@ def columnate(ary, sz)
|
|
621
764
|
buff
|
622
765
|
end
|
623
766
|
|
767
|
+
# shorten the filename to wid
|
768
|
+
# unformatted_len is the length without ANSI formatting
|
769
|
+
def truncate_formatted_filename f, unformatted_len, wid
|
770
|
+
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
|
778
|
+
end
|
779
|
+
|
780
|
+
def get_width arysz, siz
|
781
|
+
ars = [$pagesize, arysz].min
|
782
|
+
d = 0
|
783
|
+
return $gcols - d if ars <= siz
|
784
|
+
|
785
|
+
tmp = (ars * 1.000 / siz).ceil
|
786
|
+
wid = $gcols / tmp - d
|
787
|
+
wid
|
788
|
+
end
|
789
|
+
|
790
|
+
# calculate length of filename without ANSI color codes
|
791
|
+
def filename_len name
|
792
|
+
l = name.length
|
793
|
+
return l if name[0] != "\e"
|
794
|
+
|
795
|
+
# we have an escape code at the start of the name (and end, too)
|
796
|
+
index = name.index('m')
|
797
|
+
eindex = name.rindex("\e")
|
798
|
+
reall = eindex - index - 1
|
799
|
+
return reall, l
|
800
|
+
end
|
801
|
+
|
624
802
|
## formats the data with number, mark and details
|
625
|
-
|
626
|
-
# method and this overshadowed that method.
|
627
|
-
# NOTE: this does not do any coloring since the codes may get chopped by
|
628
|
-
# columnate method
|
629
|
-
# TODO 2019-03-23 - 23:30 we can do coloring here. columnate must check for color
|
630
|
-
# code at start of line, and check size accordingly. If it needs to truncate, then
|
631
|
-
# first take off the ANSI codes, truncate and put back.
|
632
|
-
def _format(ary)
|
803
|
+
def format_array(ary)
|
633
804
|
# buff = Array.new
|
634
805
|
buff = Array.new(ary.size)
|
635
806
|
return buff if ary.nil? || ary.empty?
|
@@ -648,37 +819,45 @@ def _format(ary)
|
|
648
819
|
mark = SPACE
|
649
820
|
mark = '+' if visited? f
|
650
821
|
cur = SPACE
|
651
|
-
cur = CURMARK if ix + $sta == $cursor
|
822
|
+
# cur = CURMARK if ix + $sta == $cursor # 2019-03-29 - removed reduced calls
|
652
823
|
# NOTE seems like f and ary[ix] are the same
|
653
824
|
mark = GMARK if selected?(ary[ix])
|
654
825
|
|
826
|
+
# take care of shortcuts in visited files and dirs
|
827
|
+
fullname = f[0] == '~' ? File.expand_path(f) : f
|
828
|
+
color = color_for fullname
|
829
|
+
|
655
830
|
if $long_listing
|
656
831
|
begin
|
657
832
|
if File.exist? f
|
658
833
|
stat = File.stat(f)
|
659
834
|
else
|
660
|
-
#
|
661
|
-
|
662
|
-
|
835
|
+
# dead link
|
836
|
+
if File.symlink?(f)
|
837
|
+
stat = File.lstat(f)
|
838
|
+
else
|
839
|
+
# remove last character and get stat
|
840
|
+
last = f[-1]
|
841
|
+
stat = File.stat(f.chop) if last == ' ' || last == '@' || last == '*'
|
842
|
+
end
|
663
843
|
end
|
664
844
|
# this is for saved directories etc which are shortened
|
665
845
|
stat ||= File.stat(File.expand_path(f))
|
666
846
|
f = format('%10s %s %s', readable_file_size(stat.size, 1),
|
667
847
|
date_format(stat.mtime), f)
|
668
848
|
rescue StandardError => e
|
849
|
+
@log.warn "WARN::#{e}: FILE:: #{f}"
|
669
850
|
f = format('%10s %s %s', '?', '??????????', f)
|
670
851
|
end
|
671
852
|
end
|
672
853
|
|
673
|
-
|
854
|
+
# 2019-03-31 - replace unprintable chars with ?
|
855
|
+
f = f.gsub(/[^[:print:]]/, '?')
|
674
856
|
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
# if ix + $sta == $cursor
|
680
|
-
# s = "#{RED}#{s}#{CLEAR}"
|
681
|
-
# end
|
857
|
+
s = "#{ind}#{mark}#{cur}#{f}"
|
858
|
+
if color
|
859
|
+
s = "#{color}#{s}#{CLEAR}"
|
860
|
+
end
|
682
861
|
|
683
862
|
buff[ctr] = s
|
684
863
|
|
@@ -688,6 +867,104 @@ def _format(ary)
|
|
688
867
|
buff
|
689
868
|
end
|
690
869
|
|
870
|
+
# determine color for a filename based on extension, then pattern, then filetype
|
871
|
+
def color_for fname
|
872
|
+
extension = File.extname(fname)
|
873
|
+
color = $ls_color[extension]
|
874
|
+
return color if color
|
875
|
+
|
876
|
+
# check file against patterns
|
877
|
+
if File.file?(fname)
|
878
|
+
$ls_pattern.each_pair do |k, v|
|
879
|
+
# if fname.match?(/k/)
|
880
|
+
if fname =~ /#{k}/
|
881
|
+
# @log.debug "#{fname} matched #{k}. color is #{v[1..-2]}"
|
882
|
+
return v
|
883
|
+
# else
|
884
|
+
# @log.debug "#{fname} di not match #{k}. color is #{v[1..-2]}"
|
885
|
+
end
|
886
|
+
end
|
887
|
+
end
|
888
|
+
|
889
|
+
# check filetypes
|
890
|
+
if File.exist? fname
|
891
|
+
# @log.debug "Filetype:#{File.ftype(fname)}"
|
892
|
+
|
893
|
+
return $ls_ftype[File.ftype(fname)] if $ls_ftype.key? File.ftype(fname)
|
894
|
+
return $ls_ftype['ex'] if File.executable?(fname)
|
895
|
+
else
|
896
|
+
# orphan file, but fff uses mi
|
897
|
+
return $ls_ftype['mi'] if File.symlink?(fname)
|
898
|
+
|
899
|
+
@log.warn "FILE WRONG: #{fname}"
|
900
|
+
return $ls_ftype['or']
|
901
|
+
end
|
902
|
+
|
903
|
+
nil
|
904
|
+
end
|
905
|
+
|
906
|
+
def parse_ls_colors
|
907
|
+
|
908
|
+
colorvar = ENV['LS_COLORS']
|
909
|
+
if colorvar.nil?
|
910
|
+
$ls_colors_found = nil
|
911
|
+
return
|
912
|
+
end
|
913
|
+
$ls_colors_found = true
|
914
|
+
ls = colorvar.split(':')
|
915
|
+
ls.each do |e|
|
916
|
+
patt, colr = e.split '='
|
917
|
+
colr = "\e[" + colr + 'm'
|
918
|
+
if e.start_with? '*.'
|
919
|
+
# extension, avoid '*' and use the rest as key
|
920
|
+
$ls_color[patt[1..-1]] = colr
|
921
|
+
# @log.debug "COLOR: Writing extension (#{patt})."
|
922
|
+
elsif e[0] == '*'
|
923
|
+
# file pattern, this would be a glob pattern not regex
|
924
|
+
# only for files not directories
|
925
|
+
patt = patt.gsub('.', '\.')
|
926
|
+
patt = patt.sub('+', '\\\+') # if i put a plus it does not go at all
|
927
|
+
patt = patt.gsub('-', '\-')
|
928
|
+
patt = patt.gsub('?', '.')
|
929
|
+
patt = patt.gsub('*', '.*')
|
930
|
+
patt = "^#{patt}" if patt[0] != '.'
|
931
|
+
patt = "#{patt}$" if patt[-1] != '*'
|
932
|
+
$ls_pattern[patt] = colr
|
933
|
+
# @log.debug "COLOR: Writing file (#{patt})."
|
934
|
+
elsif patt.length == 2
|
935
|
+
# file type, needs to be mapped to what ruby will return
|
936
|
+
# file, directory di, characterSpecial cd, blockSpecial bd, fifo pi, link ln, socket so, or unknown
|
937
|
+
# di = directory
|
938
|
+
# fi = file
|
939
|
+
# ln = symbolic link
|
940
|
+
# pi = fifo file
|
941
|
+
# so = socket file
|
942
|
+
# bd = block (buffered) special file
|
943
|
+
# cd = character (unbuffered) special file
|
944
|
+
# or = symbolic link pointing to a non-existent file (orphan)
|
945
|
+
# mi = non-existent file pointed to by a symbolic link (visible when you type ls -l)
|
946
|
+
# ex = file which is executable (ie. has 'x' set in permissions).
|
947
|
+
case patt
|
948
|
+
when 'di'
|
949
|
+
$ls_ftype['directory'] = colr
|
950
|
+
when 'cd'
|
951
|
+
$ls_ftype['characterSpecial'] = colr
|
952
|
+
when 'bd'
|
953
|
+
$ls_ftype['blockSpecial'] = colr
|
954
|
+
when 'pi'
|
955
|
+
$ls_ftype['fifo'] = colr
|
956
|
+
when 'ln'
|
957
|
+
$ls_ftype['link'] = colr
|
958
|
+
when 'so'
|
959
|
+
$ls_ftype['socket'] = colr
|
960
|
+
else
|
961
|
+
$ls_ftype[patt] = colr
|
962
|
+
end
|
963
|
+
# @log.debug "COLOR: ftype #{patt}"
|
964
|
+
end
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
691
968
|
## select file based on key pressed
|
692
969
|
def select_hint view, key
|
693
970
|
# a to y is direct
|
@@ -711,14 +988,14 @@ def select_hint view, key
|
|
711
988
|
end
|
712
989
|
|
713
990
|
## toggle selection state of file
|
714
|
-
def toggle_select
|
715
|
-
# dir = Dir.pwd
|
716
|
-
# file = File.join(dir, f)
|
991
|
+
def toggle_select f=current_file
|
717
992
|
if selected? f
|
718
993
|
remove_from_selection f
|
719
994
|
else
|
995
|
+
$selected_files.clear if $selection_mode == 1
|
720
996
|
add_to_selection f
|
721
997
|
end
|
998
|
+
message "#{$selected_files.count} files selected. "
|
722
999
|
# XXX is it possible to just redraw this line
|
723
1000
|
redraw_required
|
724
1001
|
end
|
@@ -734,13 +1011,12 @@ def open_file(f)
|
|
734
1011
|
last = f[-1]
|
735
1012
|
f = f.chop if last == ' ' || last == '@' || last == '*'
|
736
1013
|
end
|
737
|
-
nextpos = nil
|
738
1014
|
|
739
1015
|
# could be a bookmark with position attached to it
|
740
|
-
f,
|
1016
|
+
f, _nextpos = f.split(':') if f.index(':')
|
741
1017
|
if File.directory? f
|
742
1018
|
save_dir_pos
|
743
|
-
change_dir f
|
1019
|
+
change_dir f #, nextpos
|
744
1020
|
elsif File.readable? f
|
745
1021
|
# TODO: looks complex pls simplify !! XXX
|
746
1022
|
$default_command ||= '$PAGER'
|
@@ -842,9 +1118,7 @@ def run_command(f)
|
|
842
1118
|
end
|
843
1119
|
|
844
1120
|
## cd to a dir.
|
845
|
-
|
846
|
-
# is true.
|
847
|
-
def change_dir f, position_cursor=false
|
1121
|
+
def change_dir f
|
848
1122
|
unless File.directory? f
|
849
1123
|
perror "#{f} is not a directory, or does not exist."
|
850
1124
|
return
|
@@ -852,8 +1126,7 @@ def change_dir f, position_cursor=false
|
|
852
1126
|
|
853
1127
|
# before leaving a dir we save it in the list, as well as the cursor
|
854
1128
|
# position, so we can restore that position when we return
|
855
|
-
|
856
|
-
$visited_dirs.insert(0, dir_from)
|
1129
|
+
$visited_dirs.insert(0, Dir.pwd)
|
857
1130
|
save_dir_pos
|
858
1131
|
|
859
1132
|
f = File.expand_path(f)
|
@@ -861,14 +1134,6 @@ def change_dir f, position_cursor=false
|
|
861
1134
|
read_directory
|
862
1135
|
post_cd
|
863
1136
|
|
864
|
-
# position the cursor on the previous directory
|
865
|
-
# This happens when we go to parent directory
|
866
|
-
if position_cursor
|
867
|
-
# get the position of the previous directory
|
868
|
-
pos = index_of(File.basename(dir_from) + '/')
|
869
|
-
$cursor = pos if pos
|
870
|
-
end
|
871
|
-
|
872
1137
|
redraw_required
|
873
1138
|
end
|
874
1139
|
|
@@ -903,10 +1168,11 @@ def refresh
|
|
903
1168
|
end
|
904
1169
|
|
905
1170
|
# put directories first, then files
|
906
|
-
def sort_file_list
|
907
|
-
|
908
|
-
|
909
|
-
|
1171
|
+
def sort_file_list files
|
1172
|
+
dirs = files.select { |f| File.directory?(f) }
|
1173
|
+
# earlier I had File? which removed links, esp dead ones
|
1174
|
+
fi = files.select { |f| !File.directory?(f) }
|
1175
|
+
dirs + fi
|
910
1176
|
end
|
911
1177
|
|
912
1178
|
## unselect all files
|
@@ -924,10 +1190,10 @@ end
|
|
924
1190
|
## accept dir to goto and change to that ( can be a file too)
|
925
1191
|
def goto_dir
|
926
1192
|
# print "\e[?25h"
|
927
|
-
print_last_line 'Enter path: '
|
1193
|
+
# print_last_line 'Enter path: '
|
928
1194
|
begin
|
929
1195
|
# path = gets.chomp
|
930
|
-
path = readline
|
1196
|
+
path = readline 'Enter path to go to: '
|
931
1197
|
if path.nil? || path == ''
|
932
1198
|
clear_status_line
|
933
1199
|
return
|
@@ -960,44 +1226,35 @@ end
|
|
960
1226
|
# In selection, pressed hotkey selects a file without opening, one can keep selecting
|
961
1227
|
# (or deselecting).
|
962
1228
|
#
|
963
|
-
def
|
1229
|
+
def toggle_selection_mode
|
964
1230
|
if $mode == 'SEL'
|
1231
|
+
$selection_mode = 1
|
965
1232
|
unselect_all
|
966
1233
|
$mode = nil
|
1234
|
+
message 'Selection mode is single. '
|
967
1235
|
else
|
968
|
-
|
1236
|
+
$selection_mode = 2
|
969
1237
|
$mode = 'SEL'
|
1238
|
+
message 'Typing a hint selects the file. Toggling again will clear selection. '
|
970
1239
|
end
|
971
1240
|
end
|
972
1241
|
|
973
|
-
## toggle command mode
|
974
|
-
# 2019-03-15 - TODO FIXME preferable when we go into command mode
|
975
|
-
# run a command on current or selected files. But then is it a mode, do we remain
|
976
|
-
# there, or come out immediately ?
|
977
|
-
# 2019-03-17 - have removed this altogether. not required
|
978
|
-
def command_mode
|
979
|
-
if $mode == 'COM'
|
980
|
-
$mode = nil
|
981
|
-
return
|
982
|
-
end
|
983
|
-
$mode = 'COM'
|
984
|
-
end
|
985
|
-
|
986
1242
|
# go to parent dir, and maintain cursor on the dir we came out of
|
987
1243
|
def goto_parent_dir
|
988
|
-
#
|
1244
|
+
# When changing to parent, we need to keep cursor on
|
989
1245
|
# parent dir, not first
|
990
|
-
|
1246
|
+
curr = File.basename(Dir.pwd)
|
1247
|
+
|
1248
|
+
return if curr == '/'
|
1249
|
+
|
1250
|
+
change_dir '..'
|
991
1251
|
|
992
|
-
|
1252
|
+
return if curr == Dir.pwd
|
993
1253
|
|
994
|
-
# TODO 2019-03-24 - we can revert to old thing and remove second arg from cd
|
995
|
-
# FIXME 2019-03-23 - too late to change the cursor here since change_dir
|
996
|
-
# now does a redraw !!! XXX
|
997
1254
|
# get index of child dir in this dir, and set cursor to it.
|
998
|
-
|
999
|
-
|
1000
|
-
|
1255
|
+
index = $files.index(curr + '/')
|
1256
|
+
pause "WARNING: Could not find #{curr} in this directory." unless index
|
1257
|
+
$cursor = index if index
|
1001
1258
|
end
|
1002
1259
|
|
1003
1260
|
def goto_home_dir
|
@@ -1022,6 +1279,7 @@ end
|
|
1022
1279
|
# If lower case character given, then go to first file starting with char.
|
1023
1280
|
def goto_bookmark(key = nil)
|
1024
1281
|
unless key
|
1282
|
+
last_line
|
1025
1283
|
print 'Enter bookmark char: '
|
1026
1284
|
key = get_char
|
1027
1285
|
end
|
@@ -1036,7 +1294,7 @@ def goto_bookmark(key = nil)
|
|
1036
1294
|
nextpos = d[ix + 1..-1]
|
1037
1295
|
d = d[0, ix]
|
1038
1296
|
end
|
1039
|
-
change_dir d
|
1297
|
+
change_dir d #, nextpos
|
1040
1298
|
else
|
1041
1299
|
perror "#{key} not a bookmark"
|
1042
1300
|
end
|
@@ -1050,7 +1308,7 @@ end
|
|
1050
1308
|
def enter_regex
|
1051
1309
|
# print 'Enter (regex) pattern: '
|
1052
1310
|
# move to beginning of line, and clear till EOL
|
1053
|
-
print "\r\e[K"
|
1311
|
+
# print "\r\e[K"
|
1054
1312
|
$patt = readline '/'
|
1055
1313
|
end
|
1056
1314
|
|
@@ -1059,23 +1317,16 @@ def next_page
|
|
1059
1317
|
$sta += $pagesize
|
1060
1318
|
$cursor += $pagesize
|
1061
1319
|
$sta = $cursor if $sta > $cursor
|
1062
|
-
# FIXME this is sometimes correct, but in short cases, cursor no longer refers
|
1063
|
-
# to a file. also after cycling, it no longer has a file.
|
1064
|
-
# FIXME: 2019-03-20 - if cursor is panned to 3rd column and then we page,
|
1065
|
-
# then after a while no hints show up since stact is high.
|
1066
1320
|
$stact = 0
|
1321
|
+
$old_cursor = nil
|
1067
1322
|
redraw_required
|
1068
|
-
# next just does not work on the last page when it should
|
1069
|
-
# perhaps viewport has not yet been adjusted, that's why
|
1070
|
-
# $stact = 0 if $stact >= $viewport.size
|
1071
|
-
# setting to zero is not the best solution since hints are reset but this is better
|
1072
|
-
# than trying to maintain the same stact since the columns reduce in the end.
|
1073
|
-
# I tried checking that stact < viewport.size but nothing happened
|
1074
1323
|
end
|
1075
1324
|
|
1076
1325
|
def prev_page
|
1077
1326
|
$sta -= $pagesize
|
1078
1327
|
$cursor -= $pagesize
|
1328
|
+
$old_cursor = nil
|
1329
|
+
# FIXME: check cursor sanity and if not changed then no redraw
|
1079
1330
|
redraw_required
|
1080
1331
|
end
|
1081
1332
|
|
@@ -1130,9 +1381,8 @@ def page_with_tempfile
|
|
1130
1381
|
end
|
1131
1382
|
|
1132
1383
|
def debug_vars
|
1133
|
-
@debug_flag = true
|
1134
1384
|
page_with_tempfile do |file|
|
1135
|
-
file.puts
|
1385
|
+
file.puts "DEBUG VARIABLES for #{current_file}:"
|
1136
1386
|
file.puts
|
1137
1387
|
file.puts "sta #{$sta}"
|
1138
1388
|
file.puts "cursor #{$cursor}"
|
@@ -1142,6 +1392,10 @@ def debug_vars
|
|
1142
1392
|
file.puts "view.size #{$view.size}"
|
1143
1393
|
file.puts "grows #{$grows}"
|
1144
1394
|
file.puts "file #{current_file}"
|
1395
|
+
file.puts
|
1396
|
+
file.puts `file "#{current_file}"`
|
1397
|
+
file.puts
|
1398
|
+
file.puts %x[stat "#{current_file}"]
|
1145
1399
|
end
|
1146
1400
|
redraw_required
|
1147
1401
|
end
|
@@ -1156,7 +1410,7 @@ def view_bookmarks
|
|
1156
1410
|
goto_bookmark(key) if key =~ /^[0-9A-Z]$/
|
1157
1411
|
end
|
1158
1412
|
|
1159
|
-
# MENU MAIN
|
1413
|
+
# MENU MAIN
|
1160
1414
|
def main_menu
|
1161
1415
|
h = {
|
1162
1416
|
:a => :ag,
|
@@ -1171,11 +1425,11 @@ def main_menu
|
|
1171
1425
|
'2' => :select_from_used_dirs,
|
1172
1426
|
:t => :dirtree,
|
1173
1427
|
'4' => :tree,
|
1174
|
-
:
|
1428
|
+
:o => :sort_menu,
|
1175
1429
|
:F => :filter_menu,
|
1176
1430
|
:c => :command_menu,
|
1177
1431
|
:b => :bookmark_menu,
|
1178
|
-
:
|
1432
|
+
:s => :selection_menu,
|
1179
1433
|
:B => :bindkey_ext_command,
|
1180
1434
|
:M => :create_a_dir,
|
1181
1435
|
'%' => :create_a_file,
|
@@ -1190,7 +1444,7 @@ def selection_menu
|
|
1190
1444
|
:a => :select_all,
|
1191
1445
|
:u => :unselect_all,
|
1192
1446
|
:s => :toggle_select,
|
1193
|
-
'@' => '
|
1447
|
+
'@' => 'toggle_selection_mode',
|
1194
1448
|
'x' => 'visual_mode_toggle',
|
1195
1449
|
:v => :view_selected_files
|
1196
1450
|
}
|
@@ -1210,6 +1464,7 @@ end
|
|
1210
1464
|
def menu title, h
|
1211
1465
|
return unless h
|
1212
1466
|
|
1467
|
+
last_line # 2019-03-30 - required since cursor is not longer at bottom
|
1213
1468
|
pbold title.to_s
|
1214
1469
|
# h.each_pair { |k, v| puts " #{k}: #{v}" }
|
1215
1470
|
# 2019-03-09 - trying out using `column` to print in cols
|
@@ -1231,15 +1486,25 @@ def menu title, h
|
|
1231
1486
|
end
|
1232
1487
|
|
1233
1488
|
def toggle_menu
|
1234
|
-
h = { :h => :toggle_hidden,
|
1235
|
-
:
|
1489
|
+
h = { :h => :toggle_hidden,
|
1490
|
+
:c => :toggle_case,
|
1491
|
+
:l => :toggle_long_list,
|
1492
|
+
'1' => :toggle_columns,
|
1493
|
+
:p => :toggle_pager_mode,
|
1494
|
+
:d => :toggle_debug_flag,
|
1495
|
+
:m => :toggle_selection_mode,
|
1496
|
+
:e => :toggle_enhanced_list }
|
1497
|
+
|
1236
1498
|
_, menu_text = menu 'Toggle Menu', h
|
1237
1499
|
return unless menu_text
|
1238
1500
|
|
1239
1501
|
case menu_text
|
1240
1502
|
when :toggle_hidden
|
1503
|
+
# NOTE: now that I am shifting queries to ruby not sure this will continue
|
1504
|
+
# FNM_DOTMATCH
|
1505
|
+
# working everywhere
|
1506
|
+
# zsh D - dot files should show
|
1241
1507
|
$hidden = $hidden ? nil : 'D'
|
1242
|
-
# pause "Show hidden files is now #{!$hidden.nil?}. Press a key."
|
1243
1508
|
message "Show hidden is now #{!$hidden.nil?}"
|
1244
1509
|
rescan_required
|
1245
1510
|
when :toggle_case
|
@@ -1249,8 +1514,6 @@ def toggle_menu
|
|
1249
1514
|
message "Ignore Case is now #{$ignorecase}"
|
1250
1515
|
rescan_required
|
1251
1516
|
when :toggle_columns
|
1252
|
-
# FIXME: 2019-03-20 - if 3 then 1
|
1253
|
-
# adjust stact and sta, if moving from panned position
|
1254
1517
|
if $gviscols == 1
|
1255
1518
|
$gviscols = 3
|
1256
1519
|
else
|
@@ -1287,27 +1550,34 @@ def toggle_menu
|
|
1287
1550
|
$pagesize = $pagesize == x ? $grows : x
|
1288
1551
|
end
|
1289
1552
|
if $stact > 0
|
1290
|
-
# FIXME what if we had paged down as well and sta was > 0
|
1291
1553
|
$sta = $stact
|
1292
1554
|
$stact = 0 # in case user was panned 2019-03-20 -
|
1293
1555
|
end
|
1294
1556
|
message "Long listing is #{$long_listing}, vis cols is #{$gviscols}"
|
1295
1557
|
rescan_required
|
1558
|
+
when :toggle_debug_flag
|
1559
|
+
@debug_flag = !@debug_flag
|
1560
|
+
message "Debug flag is #{@debug_flag}"
|
1296
1561
|
end
|
1297
1562
|
end
|
1298
1563
|
|
1299
1564
|
def sort_menu
|
1565
|
+
# zsh o = order, O = reverse order
|
1566
|
+
# ruby mtime/atime/ctime come reversed so we have to change o to O
|
1300
1567
|
lo = nil
|
1301
1568
|
h = { m: :modified, a: :accessed, M: :oldest,
|
1302
|
-
l: :largest, s: :smallest, n: :name, r: :rev_name, d: :dirs, c: :
|
1569
|
+
l: :largest, s: :smallest, n: :name, r: :rev_name, d: :dirs, c: :inode,
|
1570
|
+
z: :clear }
|
1303
1571
|
_, menu_text = menu 'Sort Menu', h
|
1304
1572
|
case menu_text
|
1305
1573
|
when :modified
|
1306
|
-
lo = '
|
1574
|
+
lo = 'Om'
|
1307
1575
|
when :accessed
|
1308
|
-
lo = '
|
1576
|
+
lo = 'Oa'
|
1577
|
+
when :inode
|
1578
|
+
lo = 'Oc'
|
1309
1579
|
when :oldest
|
1310
|
-
lo = '
|
1580
|
+
lo = 'om'
|
1311
1581
|
when :largest
|
1312
1582
|
lo = 'OL'
|
1313
1583
|
when :smallest
|
@@ -1325,9 +1595,6 @@ def sort_menu
|
|
1325
1595
|
$sorto = lo
|
1326
1596
|
message "Sorted on #{menu_text}"
|
1327
1597
|
rescan_required
|
1328
|
-
# $files = `zsh -c 'print -rl -- *(#{lo}#{$hidden}M)'`.split("\n") if lo
|
1329
|
-
# $title = nil
|
1330
|
-
# $files =$(eval "print -rl -- ${pattern}(${MFM_LISTORDER}$filterstr)")
|
1331
1598
|
end
|
1332
1599
|
|
1333
1600
|
# thse need to be placed in correct position, some do nothing
|
@@ -1359,11 +1626,12 @@ def command_menu
|
|
1359
1626
|
when :locate
|
1360
1627
|
locate
|
1361
1628
|
when :today
|
1362
|
-
#
|
1629
|
+
# zshglob: M = MARK_DIRS with slash
|
1630
|
+
# zshglob: 'm0' 'm' = modified time, '0' = 0 days ago
|
1363
1631
|
$files = `zsh -c 'print -rl -- *(#{$hidden}Mm0)'`.split("\n")
|
1364
1632
|
$title = "Today's files"
|
1365
1633
|
when :default_command
|
1366
|
-
puts
|
1634
|
+
puts ' This no longer works'
|
1367
1635
|
puts 'Selecting a file usually invokes $EDITOR'
|
1368
1636
|
puts 'What command do you want to use repeatedly on selected files: '
|
1369
1637
|
$default_command = gets.chomp
|
@@ -1408,18 +1676,22 @@ def filter_menu
|
|
1408
1676
|
case menu_text
|
1409
1677
|
when :dirs
|
1410
1678
|
$filterstr = '/M'
|
1679
|
+
# zsh /M MARK_DIRS appends trailing '/' to directories
|
1411
1680
|
files = `zsh -c 'print -rl -- *(#{$sorto}/M)'`.split("\n")
|
1412
1681
|
$title = 'Filter: directories only'
|
1413
1682
|
when :files
|
1414
1683
|
$filterstr = '.'
|
1684
|
+
# zsh '.' for files, '/' for dirs
|
1415
1685
|
files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}.)'`.split("\n")
|
1416
1686
|
$title = 'Filter: files only'
|
1417
1687
|
when :emptydirs
|
1418
1688
|
$filterstr = '/D^F'
|
1689
|
+
# zsh F = full dirs, ^F empty dirs
|
1419
1690
|
files = `zsh -c 'print -rl -- *(#{$sorto}/D^F)'`.split("\n")
|
1420
1691
|
$title = 'Filter: empty directories'
|
1421
1692
|
when :emptyfiles
|
1422
1693
|
$filterstr = '.L0'
|
1694
|
+
# zsh .L size in bytes
|
1423
1695
|
files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}.L0)'`.split("\n")
|
1424
1696
|
$title = 'Filter: empty files'
|
1425
1697
|
when :reduce_list
|
@@ -1427,17 +1699,20 @@ def filter_menu
|
|
1427
1699
|
when :extension
|
1428
1700
|
files = filter_for_current_extension
|
1429
1701
|
end
|
1430
|
-
if files
|
1702
|
+
if files && files.count > 0
|
1431
1703
|
$files = files
|
1432
1704
|
$stact = 0
|
1433
|
-
$message = "Filtered on #{menu_text}"
|
1705
|
+
$message = "Filtered on #{menu_text}. Press ESC to return."
|
1434
1706
|
# redraw
|
1707
|
+
else
|
1708
|
+
perror 'Sorry, No files. '
|
1709
|
+
$title = nil
|
1435
1710
|
end
|
1436
1711
|
end
|
1437
1712
|
|
1438
1713
|
def reduce pattern=nil
|
1439
1714
|
unless pattern
|
1440
|
-
|
1715
|
+
last_line 'Enter a pattern to reduce current list: '
|
1441
1716
|
pattern = gets.chomp
|
1442
1717
|
end
|
1443
1718
|
$title = "Filter: pattern #{pattern}"
|
@@ -1481,9 +1756,6 @@ def pop_dir
|
|
1481
1756
|
## XXX make sure the dir exists, cuold have been deleted. can be an error or crash otherwise
|
1482
1757
|
$visited_dirs.push d
|
1483
1758
|
Dir.chdir d
|
1484
|
-
# $filterstr ||= 'M'
|
1485
|
-
# $files = `zsh -c 'print -rl -- *(#{$sorto}#{$hidden}#{$filterstr})'`.split("\n")
|
1486
|
-
# $files = sort_file_list $files
|
1487
1759
|
post_cd
|
1488
1760
|
rescan_required
|
1489
1761
|
end
|
@@ -1493,11 +1765,10 @@ def post_cd
|
|
1493
1765
|
$title = $patt = $message = nil
|
1494
1766
|
$sta = $cursor = $stact = 0
|
1495
1767
|
$visual_block_start = nil
|
1496
|
-
$current_dir = Dir.pwd
|
1768
|
+
$current_dir = Dir.pwd
|
1497
1769
|
screen_settings
|
1498
1770
|
|
1499
1771
|
# goto last position cursor was in this dir
|
1500
|
-
# THis only makes sense if called after read_directory but before redraw
|
1501
1772
|
revert_dir_pos
|
1502
1773
|
end
|
1503
1774
|
|
@@ -1562,9 +1833,9 @@ end
|
|
1562
1833
|
def create_bookmark
|
1563
1834
|
|
1564
1835
|
print 'Enter A to Z or 0-9 to create a bookmark: '
|
1565
|
-
print "\e[?25h" # unhide cursor
|
1836
|
+
# print "\e[?25h" # unhide cursor
|
1566
1837
|
key = get_char
|
1567
|
-
print "\e[?25l" # hide cursor
|
1838
|
+
# print "\e[?25l" # hide cursor
|
1568
1839
|
if key =~ /^[0-9A-Z]$/
|
1569
1840
|
# $bookmarks[key] = "#{Dir.pwd}:#{$cursor}"
|
1570
1841
|
$bookmarks[key] = Dir.pwd
|
@@ -1580,6 +1851,7 @@ end
|
|
1580
1851
|
# Was this supposed to be augmented, or just remain limited like this
|
1581
1852
|
# We should be able to do everything in the menus from here. TODO
|
1582
1853
|
def subcommand
|
1854
|
+
last_line
|
1583
1855
|
print 'Enter command: q x wq p w e r h '
|
1584
1856
|
begin
|
1585
1857
|
command = readline
|
@@ -1590,6 +1862,7 @@ def subcommand
|
|
1590
1862
|
if command == 'q'
|
1591
1863
|
# FIXME: 2019-03-22 - should this not call quit_command ?
|
1592
1864
|
if $modified
|
1865
|
+
last_line
|
1593
1866
|
print 'Do you want to save bookmarks? (y/n): '
|
1594
1867
|
key = get_char
|
1595
1868
|
if key == 'y'
|
@@ -1627,14 +1900,15 @@ end
|
|
1627
1900
|
|
1628
1901
|
def quit_command
|
1629
1902
|
if $modified
|
1630
|
-
|
1631
|
-
|
1903
|
+
last_line
|
1904
|
+
puts 'Press y to save bookmarks before quitting ' if $modified
|
1905
|
+
print 'Press n to quit without saving'
|
1632
1906
|
key = get_char
|
1633
1907
|
else
|
1634
1908
|
$quitting = true
|
1635
1909
|
end
|
1636
|
-
$quitting = true if key == '
|
1637
|
-
$quitting = $writing = true if key == '
|
1910
|
+
$quitting = true if key == 'n'
|
1911
|
+
$quitting = $writing = true if key == 'y'
|
1638
1912
|
end
|
1639
1913
|
|
1640
1914
|
def views
|
@@ -1651,14 +1925,32 @@ end
|
|
1651
1925
|
|
1652
1926
|
def child_dirs
|
1653
1927
|
$title = 'Directories in current directory'
|
1654
|
-
|
1655
|
-
|
1928
|
+
# M is MARK_DIRS option for putting trailing slash after dir
|
1929
|
+
# $files = `zsh -c 'print -rl -- *(/#{$sorto}#{$hidden}M)'`.split("\n")
|
1930
|
+
# $files = Dir.glob("*", File::FNM_DOTMATCH).select {|f| File.directory?(f)} - %w[ . ..]
|
1931
|
+
$files = dirs
|
1932
|
+
message "#{$files.size} directories."
|
1656
1933
|
# redraw
|
1657
1934
|
end
|
1658
1935
|
|
1936
|
+
def dirs dir='*'
|
1937
|
+
files = Dir.glob(dir, File::FNM_DOTMATCH).select {|f| File.directory?(f)} - %w[ . ..]
|
1938
|
+
files = add_slash files
|
1939
|
+
files
|
1940
|
+
end
|
1941
|
+
|
1942
|
+
def add_slash files
|
1943
|
+
return files.map do |f|
|
1944
|
+
File.directory?(f) ? f + '/' : f
|
1945
|
+
end
|
1946
|
+
end
|
1947
|
+
|
1659
1948
|
def dirtree
|
1660
|
-
$title = 'Child directories
|
1661
|
-
|
1949
|
+
$title = 'Child directories recursive'
|
1950
|
+
# zsh **/ is recursive
|
1951
|
+
files1 = `zsh -c 'print -rl -- **/*(/#{$sorto}#{$hidden}M)'`.split("\n")
|
1952
|
+
$files = Dir['**/']
|
1953
|
+
@log.debug "zsh #{files1} != rb #{$files}" if files1 != $files
|
1662
1954
|
message "#{$files.size} files."
|
1663
1955
|
# redraw
|
1664
1956
|
end
|
@@ -1669,7 +1961,8 @@ end
|
|
1669
1961
|
def tree
|
1670
1962
|
# Caution: use only for small projects, don't use in root.
|
1671
1963
|
$title = 'Full Tree'
|
1672
|
-
$files = `zsh -c 'print -rl -- **/*(#{$sorto}#{$hidden}M)'`.split("\n")
|
1964
|
+
# $files = `zsh -c 'print -rl -- **/*(#{$sorto}#{$hidden}M)'`.split("\n")
|
1965
|
+
$files = Dir['**/*']
|
1673
1966
|
message "#{$files.size} files."
|
1674
1967
|
# redraw
|
1675
1968
|
end
|
@@ -1679,6 +1972,8 @@ end
|
|
1679
1972
|
def recent_files
|
1680
1973
|
# print -rl -- **/*(Dom[1,10])
|
1681
1974
|
$title = 'Recent files'
|
1975
|
+
# zsh D DOT_GLOB, show dot files
|
1976
|
+
# zsh om order on modification time
|
1682
1977
|
$files = `zsh -c 'print -rl -- **/*(Dom[1,15])'`.split("\n").reject {|f| f[0] == '.'}
|
1683
1978
|
# redraw
|
1684
1979
|
end
|
@@ -1711,6 +2006,7 @@ def perror text
|
|
1711
2006
|
end
|
1712
2007
|
|
1713
2008
|
def pause text=' Press a key.'
|
2009
|
+
last_line
|
1714
2010
|
print text
|
1715
2011
|
get_char
|
1716
2012
|
end
|
@@ -1795,10 +2091,12 @@ def ask_hint(deflt = nil)
|
|
1795
2091
|
end
|
1796
2092
|
|
1797
2093
|
## check screen size and accordingly adjust some variables
|
2094
|
+
# NOTE: tput is ncurses dependent, so use stty
|
1798
2095
|
#
|
1799
2096
|
def screen_settings
|
1800
|
-
$glines = `
|
1801
|
-
$
|
2097
|
+
$glines, $gcols = `stty size`.split.map{|e| e.to_i}
|
2098
|
+
# $glines = `tput lines`.to_i
|
2099
|
+
# $gcols = `tput cols`.to_i
|
1802
2100
|
$grows = $glines - 3
|
1803
2101
|
# $pagesize = 60
|
1804
2102
|
# $gviscols = 3
|
@@ -1842,7 +2140,7 @@ end
|
|
1842
2140
|
# from selection
|
1843
2141
|
def file_actions(action = nil)
|
1844
2142
|
# only add dtrx for gz
|
1845
|
-
h = { d: :delete, m: :move, r: :rename, v: ENV['EDITOR'] || :vim,
|
2143
|
+
h = { d: :delete, D: '/bin/rm', m: :move, r: :rename, v: ENV['EDITOR'] || :vim,
|
1846
2144
|
c: :copy, C: :chdir, W: :remspace, e: :execute, s: :page_stat_for_file,
|
1847
2145
|
l: :less, p: :most, f: :file, o: :open, x: :dtrx, z: :zip }
|
1848
2146
|
# acttext = h[action.to_sym] || action
|
@@ -1892,20 +2190,22 @@ def file_actions(action = nil)
|
|
1892
2190
|
|
1893
2191
|
when :delete
|
1894
2192
|
delcommand = 'rmtrash'
|
1895
|
-
|
2193
|
+
last_line
|
2194
|
+
print "#{delcommand} #{text} ? [yn?]: "
|
1896
2195
|
key = get_char
|
1897
2196
|
view_selected_files if key == '?'
|
1898
2197
|
return if key != 'y'
|
1899
2198
|
|
2199
|
+
clear_status_line
|
2200
|
+
print "\r deleting ..."
|
1900
2201
|
system "#{delcommand} #{files}"
|
1901
2202
|
refresh
|
1902
2203
|
|
1903
2204
|
when :move
|
1904
2205
|
# 2019-03-07 - NOTE this will only work with single file selection
|
1905
|
-
print "move #{text} to : "
|
1906
2206
|
# target = gets().chomp
|
1907
2207
|
# target = Readline.readline('>', true)
|
1908
|
-
target = readline
|
2208
|
+
target = readline "Move #{text} to : "
|
1909
2209
|
return unless target
|
1910
2210
|
|
1911
2211
|
target = '.' if target == ''
|
@@ -1929,8 +2229,7 @@ def file_actions(action = nil)
|
|
1929
2229
|
|
1930
2230
|
when :copy
|
1931
2231
|
# Target must be directory
|
1932
|
-
|
1933
|
-
target = readline
|
2232
|
+
target = readline "Copy #{text} to : "
|
1934
2233
|
return unless target # C-c
|
1935
2234
|
|
1936
2235
|
target = '.' if target == ''
|
@@ -1953,9 +2252,8 @@ def file_actions(action = nil)
|
|
1953
2252
|
change_dir File.dirname(text)
|
1954
2253
|
|
1955
2254
|
when :zip
|
1956
|
-
print 'Archive name: '
|
1957
2255
|
# target = gets().chomp
|
1958
|
-
target = readline
|
2256
|
+
target = readline 'Archive name: '
|
1959
2257
|
return unless target
|
1960
2258
|
return if target == ''
|
1961
2259
|
|
@@ -1973,8 +2271,7 @@ def file_actions(action = nil)
|
|
1973
2271
|
when :rename
|
1974
2272
|
# 2019-03-07 NOTE works for single file FIXME
|
1975
2273
|
# 2019-03-07 - TODO for n files replace pattern with string
|
1976
|
-
|
1977
|
-
target = readline
|
2274
|
+
target = readline "Rename #{text} to : "
|
1978
2275
|
return if target == ''
|
1979
2276
|
|
1980
2277
|
text = File.expand_path(text)
|
@@ -2021,13 +2318,12 @@ def file_actions(action = nil)
|
|
2021
2318
|
else
|
2022
2319
|
return unless menu_text
|
2023
2320
|
|
2024
|
-
|
2025
|
-
|
2026
|
-
|
2321
|
+
@log.debug "#{menu_text} #{files}"
|
2322
|
+
last_line
|
2323
|
+
pause "#{menu_text} #{files}"
|
2027
2324
|
system "#{menu_text} #{files}"
|
2028
2325
|
setup_terminal
|
2029
2326
|
refresh
|
2030
|
-
pause
|
2031
2327
|
end
|
2032
2328
|
|
2033
2329
|
# remove non-existent files from select list due to move or delete
|
@@ -2077,16 +2373,22 @@ def execute
|
|
2077
2373
|
end
|
2078
2374
|
|
2079
2375
|
def ag
|
2080
|
-
|
2081
|
-
pattern = readline
|
2376
|
+
pattern = readline 'Enter a pattern to search (ag): '
|
2082
2377
|
return if pattern == ''
|
2083
2378
|
|
2084
|
-
$title = "Files found using 'ag' #{pattern}"
|
2085
|
-
|
2379
|
+
$title = "Files found using 'ag -t: ' #{pattern}"
|
2380
|
+
|
2381
|
+
## ag options :
|
2382
|
+
# -t : all text files
|
2383
|
+
# -l : print only file names
|
2384
|
+
# -a : print all files, even ignored
|
2385
|
+
system %[ag -t "#{pattern}"]
|
2386
|
+
|
2086
2387
|
pause
|
2087
|
-
files = `ag -
|
2388
|
+
files = `ag -lt "#{pattern}"`.split("\n")
|
2088
2389
|
if files.empty?
|
2089
|
-
perror
|
2390
|
+
perror "No files found for #{pattern}."
|
2391
|
+
$title = nil
|
2090
2392
|
return
|
2091
2393
|
end
|
2092
2394
|
$files = files
|
@@ -2094,12 +2396,13 @@ def ag
|
|
2094
2396
|
end
|
2095
2397
|
|
2096
2398
|
def ffind
|
2097
|
-
|
2098
|
-
pattern
|
2399
|
+
last_line
|
2400
|
+
# print 'Enter a file name pattern to find: '
|
2401
|
+
pattern = readline '! find . -iname :'
|
2099
2402
|
return if pattern == ''
|
2100
2403
|
|
2101
2404
|
$title = "Files found using 'find' #{pattern}"
|
2102
|
-
files = `find . -
|
2405
|
+
files = `find . -iname "#{pattern}"`.split("\n")
|
2103
2406
|
if files.empty?
|
2104
2407
|
perror 'No files found. Try adding *'
|
2105
2408
|
else
|
@@ -2169,10 +2472,14 @@ def cursor_scroll_up
|
|
2169
2472
|
end
|
2170
2473
|
|
2171
2474
|
def cursor_dn
|
2475
|
+
$movement = true
|
2476
|
+
$old_cursor = $cursor
|
2172
2477
|
moveto(pos + 1)
|
2173
2478
|
end
|
2174
2479
|
|
2175
2480
|
def cursor_up
|
2481
|
+
$old_cursor = $cursor
|
2482
|
+
$movement = true
|
2176
2483
|
moveto(pos - 1)
|
2177
2484
|
end
|
2178
2485
|
|
@@ -2191,6 +2498,7 @@ def moveto(position)
|
|
2191
2498
|
# FIXME not correct, it must stop at end or correctly cycle
|
2192
2499
|
# sta goes to 0 but cursor remains at 70
|
2193
2500
|
# viewport.size may be wrong here, maybe should be pagesize only
|
2501
|
+
oldsta = $sta
|
2194
2502
|
if $cursor - $sta >= $pagesize
|
2195
2503
|
$sta += $pagesize
|
2196
2504
|
# elsif $sta - $cursor >= $viewport.size
|
@@ -2200,6 +2508,8 @@ def moveto(position)
|
|
2200
2508
|
# $sta = $cursor
|
2201
2509
|
end
|
2202
2510
|
|
2511
|
+
$movement = nil if oldsta != $sta # we need to redraw
|
2512
|
+
|
2203
2513
|
star = [orig, $cursor].min
|
2204
2514
|
fin = [orig, $cursor].max
|
2205
2515
|
return unless $visual_mode
|
@@ -2215,7 +2525,7 @@ def moveto(position)
|
|
2215
2525
|
# $selected_files.concat $view[star..fin]
|
2216
2526
|
add_to_selection $view[star..fin]
|
2217
2527
|
end
|
2218
|
-
ensure
|
2528
|
+
# ensure
|
2219
2529
|
# redraw
|
2220
2530
|
end
|
2221
2531
|
|
@@ -2233,7 +2543,6 @@ def visited?(file)
|
|
2233
2543
|
return $visited_files.index file
|
2234
2544
|
end
|
2235
2545
|
|
2236
|
-
# TODO: can be array
|
2237
2546
|
def add_to_selection(file)
|
2238
2547
|
ff = file
|
2239
2548
|
case file
|
@@ -2247,7 +2556,6 @@ def add_to_selection(file)
|
|
2247
2556
|
end
|
2248
2557
|
end
|
2249
2558
|
|
2250
|
-
# TODO: can be array
|
2251
2559
|
def remove_from_selection(file)
|
2252
2560
|
ff = file
|
2253
2561
|
case file
|
@@ -2382,10 +2690,7 @@ def create_a_file
|
|
2382
2690
|
print
|
2383
2691
|
print 'Enter file name: '
|
2384
2692
|
str = readline
|
2385
|
-
return if str == ''
|
2386
|
-
|
2387
|
-
# FIXME a file with space will not create single file
|
2388
|
-
# 2019-03-10 - maybe touch a file
|
2693
|
+
return if str.nil? || str == ''
|
2389
2694
|
|
2390
2695
|
system %($EDITOR "#{str}")
|
2391
2696
|
setup_terminal
|
@@ -2408,6 +2713,9 @@ end
|
|
2408
2713
|
# prompt for scripts to execute, giving selected file names
|
2409
2714
|
# NOTE: TODO 2019-03-21 - some scripts can output a filelist to display
|
2410
2715
|
def scripts
|
2716
|
+
write_selected_files
|
2717
|
+
# some scripts may work with the selected_files and not want to be called
|
2718
|
+
# with filenames.
|
2411
2719
|
title = 'Select a script'
|
2412
2720
|
script_path = '~/.config/cetus/scripts'
|
2413
2721
|
# TODO write selected files to a known file before calling
|
@@ -2429,20 +2737,22 @@ end
|
|
2429
2737
|
|
2430
2738
|
# ------------------- view_selected_files ------------------ #
|
2431
2739
|
def view_selected_files
|
2432
|
-
|
2433
|
-
|
2434
|
-
|
2435
|
-
$selected_files.each { |row| file.puts row }
|
2436
|
-
file.flush
|
2437
|
-
system "$PAGER #{file.path}"
|
2438
|
-
setup_terminal
|
2439
|
-
rescue
|
2440
|
-
file.close
|
2441
|
-
file.unlink
|
2442
|
-
end
|
2740
|
+
fname = write_selected_files
|
2741
|
+
system "$PAGER #{fname}"
|
2742
|
+
setup_terminal
|
2443
2743
|
end
|
2444
2744
|
# ------------- end of view_selected_files --------------------------------#
|
2445
2745
|
|
2746
|
+
# write selected files to a file and return path
|
2747
|
+
def write_selected_files
|
2748
|
+
fname = File.join(File.dirname(CONFIG_FILE), 'selected_files')
|
2749
|
+
fname = File.expand_path(fname)
|
2750
|
+
|
2751
|
+
File.open(fname, 'w') do |file|
|
2752
|
+
$selected_files.each { |row| file.puts row }
|
2753
|
+
end
|
2754
|
+
return fname
|
2755
|
+
end
|
2446
2756
|
##
|
2447
2757
|
# Editing of the User Dir List.
|
2448
2758
|
# remove current entry from used dirs list, since we may not want some entries being there
|
@@ -2491,15 +2801,27 @@ end
|
|
2491
2801
|
def enhance_file_list
|
2492
2802
|
return unless $enhanced_mode
|
2493
2803
|
|
2804
|
+
# zshglob: M = MARK_DIRS with slash
|
2805
|
+
# zshglob: N = NULL_GLOB no error if no result, this is causing space to split
|
2806
|
+
# file sometimes for single file.
|
2807
|
+
|
2494
2808
|
# if only one entry and its a dir
|
2495
2809
|
# get its children and maybe the recent mod files a few
|
2496
|
-
|
2810
|
+
# FIXME: simplify condition into one
|
2497
2811
|
if $files.size == 1
|
2498
2812
|
# its a dir, let give the next level at least
|
2499
2813
|
if $files.first[-1] == '/'
|
2500
2814
|
d = $files.first
|
2501
|
-
|
2815
|
+
# zshglob: 'om' = ordered on modification time
|
2816
|
+
# f1 = `zsh -c 'print -rl -- #{d}*(omM)'`.split("\n")
|
2817
|
+
f = get_files_by_mtime(d)
|
2818
|
+
|
2819
|
+
# @log.warn "f1:#{f1} != f:#{f} in #{d}" if f1 != f
|
2820
|
+
# order returned by zsh and ruby are different since the time is the same
|
2821
|
+
|
2822
|
+
# TODO: use ruby this throws errors if not files
|
2502
2823
|
if f && !f.empty?
|
2824
|
+
# @log.debug "CONCAT: #{f}" if @debug_flag
|
2503
2825
|
$files.concat f
|
2504
2826
|
$files.concat get_important_files(d)
|
2505
2827
|
return
|
@@ -2511,29 +2833,40 @@ def enhance_file_list
|
|
2511
2833
|
end
|
2512
2834
|
#
|
2513
2835
|
# check if a ruby project dir, although it could be a backup file too,
|
2514
|
-
# if so , expand lib and
|
2515
|
-
#
|
2836
|
+
# if so , expand lib and maybe bin, put a couple recent files
|
2837
|
+
# FIXME: gemspec file will be same as current folder
|
2516
2838
|
if $files.index('Gemfile') || !$files.grep(/\.gemspec/).empty?
|
2517
2839
|
# usually the lib dir has only one file and one dir
|
2518
2840
|
flg = false
|
2519
2841
|
$files.concat get_important_files(Dir.pwd)
|
2520
2842
|
if $files.index('lib/')
|
2521
|
-
|
2843
|
+
# get first five entries by modification time
|
2844
|
+
# f1 = `zsh -c 'print -rl -- lib/*(om[1,5]MN)'`.split("\n")
|
2845
|
+
f = get_files_by_mtime('lib')&.first(5)
|
2846
|
+
# @log.warn "f1 #{f1} != #{f} in lib" if f1 != f
|
2522
2847
|
if f && !f.empty?
|
2523
2848
|
insert_into_list('lib/', f)
|
2524
2849
|
flg = true
|
2525
2850
|
end
|
2851
|
+
|
2852
|
+
# look into lib file for that project
|
2526
2853
|
dd = File.basename(Dir.pwd)
|
2527
2854
|
if f.index("lib/#{dd}/")
|
2528
|
-
|
2855
|
+
# f1 = `zsh -c 'print -rl -- lib/#{dd}/*(om[1,5]MN)'`.split("\n")
|
2856
|
+
f = get_files_by_mtime("lib/#{dd}")&.first(5)
|
2857
|
+
# @log.warn "2756 f1 #{f1} != #{f} in lib/#{dd}" if f1 != f
|
2529
2858
|
if f && !f.empty?
|
2530
2859
|
insert_into_list("lib/#{dd}/", f)
|
2531
2860
|
flg = true
|
2532
2861
|
end
|
2533
2862
|
end
|
2534
2863
|
end
|
2864
|
+
|
2865
|
+
# look into bin directory and get first five modified files
|
2535
2866
|
if $files.index('bin/')
|
2536
|
-
|
2867
|
+
# f1 = `zsh -c 'print -rl -- bin/*(om[1,5]MN)'`.split("\n")
|
2868
|
+
f = get_files_by_mtime("bin")&.first(5)
|
2869
|
+
# @log.warn "2768 f1 #{f1} != #{f} in bin/" if f1 != f
|
2537
2870
|
insert_into_list('bin/', f) if f && !f.empty?
|
2538
2871
|
flg = true
|
2539
2872
|
end
|
@@ -2544,20 +2877,58 @@ def enhance_file_list
|
|
2544
2877
|
end
|
2545
2878
|
return if $files.size > 15
|
2546
2879
|
|
2547
|
-
|
2548
|
-
|
2880
|
+
# Get most recently accessed directory
|
2881
|
+
## NOTE: first check accessed else modified will change accessed
|
2882
|
+
# 2019-03-28 - adding NULL_GLOB breaks file name on spaces
|
2883
|
+
# print -n : don't add newline
|
2884
|
+
# zzmoda = `zsh -c 'print -rn -- *(/oa[1]MN)'`
|
2885
|
+
# zzmoda = nil if zzmoda == ''
|
2886
|
+
moda = get_most_recently_accessed_dir
|
2887
|
+
# @log.warn "Error 2663 #{zzmoda} != #{moda}" if zzmoda != moda
|
2549
2888
|
if moda && moda != ''
|
2550
|
-
|
2889
|
+
|
2890
|
+
# get most recently accessed file in that directory
|
2891
|
+
# NOTE: adding NULL_GLOB splits files on spaces
|
2892
|
+
# FIXME: this zsh one gave a dir instead of file.
|
2893
|
+
# zzmodf = `zsh -c 'print -rl -- #{moda}*(oa[1]M)'`.chomp
|
2894
|
+
# zzmodf = nil if zzmodf == ''
|
2895
|
+
modf = get_most_recently_accessed_file moda
|
2896
|
+
# @log.warn "Error 2670 (#{zzmodf}) != (#{modf}) gmra in #{moda} #{zzmodf.class}, #{modf.class} : Loc: #{Dir.pwd}" if zzmodf != modf
|
2897
|
+
|
2898
|
+
raise "2784: #{modf}" if modf && !File.exist?(modf)
|
2899
|
+
|
2551
2900
|
insert_into_list moda, modf if modf && modf != ''
|
2552
|
-
|
2901
|
+
|
2902
|
+
# get most recently modified file in that directory
|
2903
|
+
# zzmodm = `zsh -c 'print -rn -- #{moda}*(om[1]M)'`.chomp
|
2904
|
+
modm = get_most_recently_modified_file moda
|
2905
|
+
# zzmodm = nil if zzmodm == ''
|
2906
|
+
# @log.debug "Error 2678 (gmrmf) #{zzmodm} != #{modm} in #{moda}" if zzmodm != modm
|
2907
|
+
raise "2792: #{modm}" if modm && !File.exist?(modm)
|
2908
|
+
|
2553
2909
|
insert_into_list moda, modm if modm && modm != '' && modm != modf
|
2554
2910
|
end
|
2555
|
-
|
2556
|
-
|
2911
|
+
|
2912
|
+
## get most recently modified dir
|
2913
|
+
# zzmodm = `zsh -c 'print -rn -- *(/om[1]M)'`
|
2914
|
+
# zzmodm = nil if zzmodm == ''
|
2915
|
+
modm = get_most_recently_modified_dir
|
2916
|
+
# @log.debug "Error 2686 rmd #{zzmodm} != #{modm}" if zzmodm != modm
|
2917
|
+
|
2557
2918
|
if modm != moda
|
2558
|
-
|
2919
|
+
|
2920
|
+
# get most recently accessed file in that directory
|
2921
|
+
# modmf = `zsh -c 'print -rn -- #{modm}*(oa[1]M)'`
|
2922
|
+
modmf = get_most_recently_accessed_file modm
|
2923
|
+
raise "2806: #{modmf}" if modmf && !File.exist?(modmf)
|
2924
|
+
|
2559
2925
|
insert_into_list modm, modmf
|
2560
|
-
|
2926
|
+
|
2927
|
+
# get most recently modified file in that directory
|
2928
|
+
# modmf11 = `zsh -c 'print -rn -- #{modm}*(om[1]M)'`
|
2929
|
+
modmf1 = get_most_recently_modified_file modm
|
2930
|
+
raise "2812: #{modmf1}" if modmf1 && !File.exist?(modmf1)
|
2931
|
+
|
2561
2932
|
insert_into_list(modm, modmf1) if modmf1 != modmf
|
2562
2933
|
else
|
2563
2934
|
# if both are same then our options get reduced so we need to get something more
|
@@ -2567,7 +2938,7 @@ def enhance_file_list
|
|
2567
2938
|
end
|
2568
2939
|
|
2569
2940
|
# insert important files to end of $files
|
2570
|
-
def insert_into_list
|
2941
|
+
def insert_into_list _dir, file
|
2571
2942
|
# ix = $files.index(dir)
|
2572
2943
|
# raise "something wrong can find #{dir}." unless ix
|
2573
2944
|
# $files.insert ix, *file
|
@@ -2579,33 +2950,96 @@ end
|
|
2579
2950
|
# 2019-03-23 - not exactly clear what is happening XXX
|
2580
2951
|
# this gets a directory (containing '/' at end)
|
2581
2952
|
def get_important_files dir
|
2953
|
+
|
2582
2954
|
# checks various lists like visited_files and bookmarks
|
2583
2955
|
# to see if files from this dir or below are in it.
|
2584
2956
|
# More to be used in a dir with few files.
|
2585
2957
|
list = []
|
2586
2958
|
l = dir.size + 1
|
2959
|
+
|
2587
2960
|
# 2019-03-23 - i think we are getting the basename of the file
|
2588
2961
|
# if it is present in the given directory XXX
|
2589
2962
|
$visited_files.each do |e|
|
2590
2963
|
list << e[l..-1] if e.index(dir) == 0
|
2591
2964
|
end
|
2965
|
+
|
2592
2966
|
# bookmarks if it starts with this directory then add it
|
2593
2967
|
# FIXME it puts same directory cetus into the list with full path
|
2594
2968
|
# We need to remove the base until this dir. get relative part
|
2595
2969
|
list1 = $bookmarks.values.select do |e|
|
2596
2970
|
e.index(dir) == 0 && e != dir
|
2597
2971
|
end
|
2972
|
+
|
2598
2973
|
list.concat list1
|
2599
2974
|
list
|
2600
2975
|
end
|
2601
2976
|
|
2977
|
+
def get_most_recently_accessed_dir dir='.'
|
2978
|
+
gmr dir, :directory?, :atime
|
2979
|
+
end
|
2980
|
+
|
2981
|
+
def get_most_recently_accessed_file dir='.'
|
2982
|
+
gmr dir, :file?, :atime
|
2983
|
+
end
|
2984
|
+
|
2985
|
+
def get_most_recently_modified_file dir='.'
|
2986
|
+
gmr dir, :file?, :mtime
|
2987
|
+
end
|
2988
|
+
|
2989
|
+
def get_most_recently_modified_dir dir='.'
|
2990
|
+
file = gmr dir, :directory?, :mtime
|
2991
|
+
end
|
2992
|
+
|
2993
|
+
# get most recent file or directory, based on atime or mtime
|
2994
|
+
# dir is name of directory in which to get files, default is '.'
|
2995
|
+
# type is :file? or :directory?
|
2996
|
+
# func can be :mtime or :atime or :ctime or :birthtime
|
2997
|
+
def gmr dir, type, func
|
2998
|
+
file = Dir.glob(dir + '/*')
|
2999
|
+
.select { |f| File.send(type, f) }
|
3000
|
+
.max_by { |f| File.send(func, f) }
|
3001
|
+
file = File.basename(file) + '/' if file && type == :directory?
|
3002
|
+
return file.gsub('//', '/') if file
|
3003
|
+
|
3004
|
+
nil
|
3005
|
+
end
|
3006
|
+
|
3007
|
+
# return a list of entries sorted by mtime.
|
3008
|
+
# A / is added after to directories
|
3009
|
+
def get_files_by_mtime dir='*'
|
3010
|
+
gfb dir, :mtime
|
3011
|
+
end
|
3012
|
+
|
3013
|
+
def get_files_by_atime dir='.'
|
3014
|
+
gfb dir, :atime
|
3015
|
+
end
|
3016
|
+
|
3017
|
+
# get files ordered by mtime or atime, returning latest first
|
3018
|
+
# dir is dir to get files in, default '.'
|
3019
|
+
# func can be :atime or :mtime or even :size or :ftype
|
3020
|
+
def gfb dir, func
|
3021
|
+
dir += '/*' if File.directory?(dir)
|
3022
|
+
dir = dir.gsub('//', '/')
|
3023
|
+
|
3024
|
+
# sort by time and then reverse so latest first.
|
3025
|
+
sorted_files = Dir[dir].sort_by { |f| File.send(func, f) }.reverse
|
3026
|
+
|
3027
|
+
# add slash to directories
|
3028
|
+
sorted_files = add_slash sorted_files
|
3029
|
+
return sorted_files
|
3030
|
+
# sorted_files.map do |f|
|
3031
|
+
# File.directory?(f) ? f + '/' : f
|
3032
|
+
# end
|
3033
|
+
end
|
2602
3034
|
# set message which will be displayed in status line
|
2603
3035
|
def message mess
|
2604
3036
|
$message = mess
|
2605
3037
|
end
|
2606
3038
|
|
2607
3039
|
def last_line
|
2608
|
-
system "tput cup #{$glines} 0"
|
3040
|
+
# system "tput cup #{$glines} 0"
|
3041
|
+
# print "\e[#{$glines};0H"
|
3042
|
+
tput_cup $glines, 0
|
2609
3043
|
end
|
2610
3044
|
|
2611
3045
|
def clear_status_line
|
@@ -2615,9 +3049,11 @@ def clear_status_line
|
|
2615
3049
|
# %*s - set blank spaces for entire line
|
2616
3050
|
print "\e[33;4%sm%*s" % [$status_color || '1', $gcols, " "]
|
2617
3051
|
end
|
3052
|
+
|
2618
3053
|
def print_on_right text
|
2619
3054
|
sz = text.size
|
2620
|
-
system "tput cup #{$glines} #{$gcols - sz -1}"
|
3055
|
+
system "tput cup #{$glines} #{$gcols - sz - 1}"
|
3056
|
+
# tput_cup $glines, $gcols - sz - 1
|
2621
3057
|
print text
|
2622
3058
|
end
|
2623
3059
|
|
@@ -2636,8 +3072,10 @@ def run
|
|
2636
3072
|
|
2637
3073
|
setup_terminal
|
2638
3074
|
config_read
|
3075
|
+
parse_ls_colors
|
2639
3076
|
|
2640
3077
|
redraw true
|
3078
|
+
place_cursor
|
2641
3079
|
|
2642
3080
|
# do we need this, have they changed after redraw
|
2643
3081
|
$patt = nil
|
@@ -2646,18 +3084,25 @@ def run
|
|
2646
3084
|
# forever loop that prints dir and takes a key
|
2647
3085
|
loop do
|
2648
3086
|
|
2649
|
-
# moved the printing out of here
|
2650
|
-
# TODO we need to call it from where required
|
2651
|
-
|
2652
3087
|
key = get_char
|
2653
3088
|
resolve_key key
|
3089
|
+
if $movement && $old_cursor
|
3090
|
+
clear_status_line
|
3091
|
+
print_debug_info if @debug_flag
|
3092
|
+
# we may want to print debug info if flag is on
|
3093
|
+
place_cursor
|
3094
|
+
$movement = false
|
3095
|
+
next
|
3096
|
+
end
|
2654
3097
|
redraw rescan?
|
3098
|
+
place_cursor
|
2655
3099
|
|
2656
3100
|
break if $quitting
|
2657
3101
|
end
|
2658
3102
|
write_curdir
|
2659
3103
|
puts 'bye'
|
2660
3104
|
config_write if $writing
|
3105
|
+
@log&.close
|
2661
3106
|
end
|
2662
3107
|
|
2663
3108
|
run
|