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