-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathzfm.zsh
executable file
·1947 lines (1855 loc) · 66.1 KB
/
zfm.zsh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#!/usr/bin/env zsh
# header {
# vim: set foldmarker={,} foldlevel=0 foldmethod=marker :
# ----------------------------------------------------------------------------- #
# File: zfm.zsh
# Description: file/dir browser/navigator using hotkeys
# Author: rkumar http://github.com/rkumar/rbcurse/
# Date: 2012-12-17 - 19:21
# License: GPL
# Last update: 2013-03-03 15:13
# This is the new kind of file browser that allows selection based on keys
# either chose 1-9 or drill down based on starting letters
#
# In memory of my dear child Gabbar missing since Nov 13th, 2012.
# ----------------------------------------------------------------------------- #
# Copyright (C) 2012-2013 rahul kumar
# header }
ZFM_DIR=${ZFM_DIR:-~/bin}
export ZFM_DIR
export EDITOR=${EDITOR:-vi}
## This is the startup mode, also whenever escaping from another mode such as HINTs
# app will come back to this mode
export ZFM_DEFAULT_MODE=${ZFM_DEFAULT_MODE:-HINT}
source ${ZFM_DIR}/zfm_menu.zsh
source $ZFM_DIR/zfm_viewoptions.zsh
setopt MARK_DIRS
## color of current line: use from autocolors red blue white black cyan magenta yellow
CURSOR_COLOR="red"
export M_FULL_INDEXING=
export TAB=$'\t'
set_auto_view
# This did not work when called from a function
#stty_settings
ttysave=$(stty -g)
#stty raw echo
# We need C-c for mappings, so we disable it
stty intr '^-'
# this is strangely eating up C-o
stty flush '^-'
# so we can trap C-\ to abort
stty quit '^-'
# so we can trap C-q to quit
stty start '^-'
#
# for printing details
zmodload zsh/stat
zmodload -F zsh/stat b:zstat
M_SCROLL=${M_SCROLL:-10}
export M_SCROLL
# list_printer {
# Used to take title and data, now takes no parameters
# This was called in a loop from the caller, everytime a file was selected we would return to caller
# and come back here, now we only return when caller wants to exit.
#
function list_printer() {
selection=
local width=30
#local title=$1
title="$PWD"
#local viewport vpa fin
param=$(print -rl -- *(M))
myopts=("${(@f)$(print -rl -- $param)}")
# using cols to calculate cursor movement right
LIST_COLS=3
local tot=$#myopts
#local sta=1
# 2012-12-26 - 00:49 trygin this out so after a selection i don't lose what's filtered
# but changing dirs must clear this, so it's dicey
PATT=${PATT:-""}
local mark ic approx
globflags=
ic=
approx=
while (true)
do
if [[ -z $M_NO_REPRINT ]]; then
clear
print -l -- ${M_MESSAGE:-" $M_HELP"}
(( fin = sta + $PAGESZ )) # 60
# THIS WORKS FINE but trying to avoid external commands
#viewport=$(print -rl -- $myopts | grep "$PATT" | sed "$sta,${fin}"'!d')
# this line replace grep and searches from start. if we plae a * after
# the '#' then the match works throughout filename
ic=${ZFM_IGNORE_CASE:+"-i"}
approx=${ZFM_APPROX_MATCH+a1}
# in case other programs need to display or account for, put in round bracks
globflags="$ic$approx"
# we keep filtering, not refreshing so deleted moved files still show up
# the caller queries, and that sucks
# I am fed up of this crazy crap. Things were great when i used grep and sed
# I am giong back even though it will cause various changes
if [[ -z $M_MATCH_ANYWHERE ]]; then
#viewport=(${(M)myopts:#(#${ic}${approx})$PATT*})
mark="^"
prefix="^"
else
#viewport=(${(M)myopts:#(${ic}${approx})*$PATT*})
mark=" "
prefix=""
fi
viewport=("${(@f)$(print -rl -- $myopts | grep $ic "${prefix}$PATT" )}")
## testing out
#if [[ $#viewport -eq 0 ]]; then
## Ifwe don't get any results lets take the last entered pattern
## and place a .* before it so its a little more helpful. This is since
## sometimes there are too many common characters in file names.
#
## This was nice but can be confusing esp when you backspace
#
if [[ -n "$M_SMART_FUZZY" ]]; then
if [[ -z $viewport ]]; then
if [[ $#PATT -ge 2 ]]; then
local testpatt=$PATT
testpatt[-2]+='.*'
## we repeat the above command, so make sure it is identical
viewport=("${(@f)$(print -rl -- $myopts | grep "${prefix}$testpatt" )}")
[[ ! -z $viewport ]] && PATT=$testpatt
fi
fi
fi
## Run a filter entered by the user on the existing data
#
if [[ -n "$M_CFILTER" ]]; then
viewport=("${(@f)$(print -rl -- $viewport | eval "$M_CFILTER" )}")
fi
## these lines must come after any filtering, othewise totals displayed
## are wrong
let tot=$#viewport # store the size of matching rows prior to paging it. 2013-01-09 - 01:37
END=$tot
[[ $fin -gt $tot ]] && fin=$tot
## this line replaces the sed filter
# 2013-02-09 - 00:38 I am trying to separate viewport and vpa
# viewport will have data prior to grep so "yG" etc can work. I am losing the entire
# list which is paged. I only have the slice shown, or the entire myopts.
# ------------- operation code commaneted out START ----
#viewport=(${viewport[$sta, $fin]})
#vpa=("${(@f)$(print -rl -- $viewport)}")
# ------------- operation code commaneted out END ----
vpa=(${viewport[$sta, $fin]})
#vpa=("${(@f)$(print -rl -- $viewport[$sta, $fin])}")
# -------- end of new code -----
VPACOUNT=$#vpa
#PAGE_END=$VPACOUNT
#PAGE_TOP=1
(( PAGE_END= VPACOUNT + sta - 1 ))
(( PAGE_TOP = sta ))
ZFM_LS_L=
if (( $VPACOUNT < (ZFM_LINES -2 ) )); then
# need to account for title and read lines at least and message line
LIST_COLS=1
# this could have the entire listing which contains TABS !!!
(( width= ZFM_COLS - 2 ))
ZFM_LS_L=1
elif [[ $VPACOUNT -lt 40 ]]; then
LIST_COLS=2
(( width = (ZFM_COLS / LIST_COLS) - 2 ))
else
LIST_COLS=3
# i can use 1 instead of 2, it touches the end, 2 to be safe for other widths
(( width = (ZFM_COLS / LIST_COLS) - 2 ))
fi
# NO, vpa is not entire thing, its grepped and filtered, so it can't be more than page size=
#let tot=$#vpa
[[ $fin -gt $tot ]] && fin=$tot
local sortorder=""
[[ -n $ZFM_SORT_ORDER ]] && sortorder="o=$ZFM_SORT_ORDER"
## This relates to the new cursor functionality. Arrow keys allow us to
## move around the file list and press ENTER
#
(( CURSOR == -1 || CURSOR > tot )) && CURSOR=$tot
#
# If user presses down at last file, and there are more we should
# page down, but that's not working at present, some glitches, so we just
# bring cursor back to 1
#(( CURSOR > $VPACOUNT && CURSOR < $tot )) && { sta=$CURSOR ; CURSOR=1 }
# this is fine but does not redraw the page until cursor moves
(( CURSOR > VPACOUNT && CURSOR < tot )) && { zfm_next_page }
## if there are no rows then CURSOR gets set to 0 and remains there forever, check
(( CURSOR == 0 )) && CURSOR=1
print_title "$title $sta to $fin of $tot ${COLOR_GREEN}$sortorder $ZFM_STRING ${globflags}${COLOR_DEFAULT} "
## This is the original line, which had a pipeline. I had to break this up
## since it updates a cache of file details and this cache is lost each
## time a call is made, since it is in another process
#
#print -rC$LIST_COLS "${(@f)$(print -rl -- $viewport | numberlines -p "$PATT" -w $width)}"
# 2013-02-09 - 00:49 replaced viewport with vpa
#numberlines -p "$PATT" -w $width $viewport
numberlines -p "$PATT" -w $width $vpa
print -rC$LIST_COLS "${(@f)$(print -l -- $OUTPUT)}"
[[ -n $M_SELECTION_MODE ]] && mode="[SEL $#selectedfiles] "
fi # M_NO_REPRINT
## ---
# If we want a file preview we will have to put the code here, or
# a hook here for stuff that needs to be printed in addition to the listing
# since this list will erase anything printed on the side.
# ---
M_NO_REPRINT=
#print -n "$mode${mark}$PATT > "
print -n "\r$mode${mark}$PATT > "
# prompt for key PROMPT
#read -k -r ans
# see zfm_menu.zsh for _read moved there
## check for pending key - should be integrate this with read_k so it works everywhre
reply=
pop_key_stack
ret=0
[[ -z $reply ]] && { _read_keys; ret=$? }
#M_MESSAGE=
if [[ $ret != 0 ]]; then
# maybe ^C
perror "$ret: Got error from _read_keys: r=$reply , k=$key"
key=''
ans=''
#break
else
#[[ -n $ckey ]] && reply=$ckey
ans="${reply}"
#pdebug "Got ($reply)"
fi
if [[ $ans == "C-q" ]]; then
if [[ $ZFM_MODE == $ZFM_DEFAULT_MODE ]]; then
QUITTING=true
break
else
zfm_set_mode $ZFM_DEFAULT_MODE
fi
elif [[ $ans == '' ]]; then
QUITTING=true
break
elif [[ -n $ZFM_MODE ]]; then
if [[ -z $MODE_KEY_HANDLER ]]; then
MODE_KEY_HANDLER=${ZFM_MODE:l}_key_handler
fi
ZFM_KEY=$ans
NO_BREAKING=
$MODE_KEY_HANDLER $ZFM_KEY
ans=
## 2013-02-22 - 00:36 LP added next line so break from mode can happen
[[ -n $QUITTING ]] && break
# should be not break only if selection has been set XXX
# 2013-02-22 - 00:14 LP commented off next line
#[[ -n $NO_BREAKING ]] || break
# above is for modes
else
[[ $ZFM_QUIT_KEY == $ans ]] && { QUITTING=true; ans= ; break; }
zfm_exec_key_binding $ans
[[ -n $binding ]] && { ans= ;
# 2013-02-22 - 00:14 LP commented off next line
#[[ -n $NO_BREAKING ]] || break
## added 2013-02-22 - 00:31 LP
[[ -n $QUITTING ]] && break
}
#[[ -n $binding ]] && { $binding ; ans= ; break }
fi
## added 2013-02-22 - 00:31 LP 3 lines
[[ -n $QUITTING ]] && break
[[ -n $selection ]] && zfm_open_file $selection
selection=
## end added LP
## 2013-01-24 - 20:24 thre break in the next line without clearing ans
## was causing the unused error to keep popping up when no rows were returned
## 2013-02-22 - 01:05 LP break removed from next line
#[[ $sta -ge $tot ]] && { sta=1; CURSOR=1; ans= ; pinfo "wrapping"; }
# break takes control back to MARK1 section below
done
}
# }
function zfm_next_page () {
local pos
curpos pos
(( pos += PAGESZ1 ))
zfm_goto_line $pos
return
}
function zfm_prev_page () {
local pos
curpos pos
(( pos -= PAGESZ1 ))
zfm_goto_line $pos
}
## currently called by C-d
# scrolls using value of M_SCROLL
#
function zfm_scroll_down () {
local pos
curpos pos
(( pos += M_SCROLL ))
zfm_goto_line $pos
}
## currently called by C-b
# scrolls using value of M_SCROLL
function zfm_scroll_up () {
local pos
curpos pos
(( pos -= M_SCROLL ))
zfm_goto_line $pos
}
function zfm_go_top () {
CURSOR=1
sta=1
}
function zfm_go_bottom () {
CURSOR=$VPACOUNT
(( sta = tot - VPACOUNT + 1 ))
}
function patt_toggle() {
local gpatt=$1
gpatt=${gpatt:gs/*//}
gpatt="${gpatt}"
if [[ -z "$ZFM_FUZZY_MATCH_DIR" ]]; then
else
gpatt=$(print $gpatt | sed 's/\(.\)/\1\*/g')
fi
print "$gpatt"
}
function toggle_match_from_start() {
# default is unset, it matches what you type from start
if [[ -z "$M_MATCH_ANYWHERE" ]]; then
M_MATCH_ANYWHERE=1
else
M_MATCH_ANYWHERE=
fi
export M_MATCH_ANYWHERE
}
# utility functions {
# check if there is only one file for this pattern, then straight go for it
# with some rare cases the next char is a number, so then don't jump.
function check_patt() {
#local p=${1:s/^//} # obsolete, refers to earlier grep version
local p=$1
local approx
local ic=
ic=${ZFM_IGNORE_CASE+i}
approx=${ZFM_APPROX_MATCH+a1}
## XXX TODO needs to be checked sicne we have moved back to grep
if [[ -z $M_MATCH_ANYWHERE ]]; then
# match from start - default
lines=$(print -rl -- (#$ic${approx})${p}*)
else
lines=$(print -rl -- (#$ic${approx})*${p}*)
fi
# need to account for match from start
print $lines
}
## triggered upon pressing :.
# Allows user to type in command such as help, marks etc
# Someday we will allow history and completion
#
function subcommand() {
#dcommand=${dcommand:-""}
#[[ $dcommand == "?" ]] && dcommand=""
local dcommand
vared -h -p "Enter command (? - help): " dcommand
[[ "$dcommand" = "q" || $dcommand = "quit" ]] && { QUITTING=1 ; break }
[[ "$dcommand" = "wq" ]] && { config_write; QUITTING=1 ; break }
[[ "$dcommand" = "x" ]] && { [[ -n "$M_MODIFIED" ]] && config_write; QUITTING=1 ; break }
# write command to history file
[[ -n $dcommand ]] && print -s -- "$dcommand"
if [[ $dcommand[1] == '!' ]]; then
dcommand=${dcommand[2,-1]}
# subst selected files for %% NOT TESTED
if [[ $dcommand = *%%* ]]; then
dcommand=${(S)dcommand//\%\%/${selectedfiles:q}}
fi
eval "$dcommand"
pause
return
else
binding=$M_SUBCOMMAND[$dcommand]
if [[ -n $binding ]]; then
zfm_exec_binding $binding
pause
return
fi
fi
case "$dcommand" in
# these two save and pop ae quite stupid, are we ever gonna use it
"S"|"save")
print "Saving $PWD to directory stack"
push_pwd
print "Dir Stack: $ZFM_DIR_STACK"
pause
;;
"P"|"pop")
pop_pwd
;;
"a"|"ack")
zfm_ack
;;
"l"|"locate")
zfm_locate
;;
"f"|"file")
if [[ -n $selectedfiles ]]; then
pdebug "selected files: $#selectedfiles"
M_NO_AUTO=1
call_fileoptions $selectedfiles
else
selection=${selection:-$vpa[$CURSOR]}
if [[ -n "$selection" ]]; then
M_NO_AUTO=1
fileopt $selection
selection=
else
perror "Please select a file first. Use $ZFM_SELECTION_MODE_KEY key to toggle selection mode"
fi
fi
;;
"?"|"h"|"help")
print "Commands are save (S), pop (P), help (h)"
print ""
print "'S' 'save' - save this dir in stack for later returning"
print "'P' 'pop' - revert to saved dir"
print "'f' 'file' - file operations on selected file"
print " helpful if you have auto-actions on but want to execute"
print " another action on selected file"
print "'a' ack (search string) in files"
print "'p' 'pwd' copy PWD ( $PWD ) to clipboard"
print "'q' 'quit' - quit application"
print "You may enter any other command too such as 'git status'"
print
pbold "Subcommands: "
for kk in ${(k)M_SUBCOMMAND} ; do
val=$M_SUBCOMMAND[$kk]
print "$fg_bold[white]$kk$reset_color - $val "
done
;;
"pipe")
# accept a command and pass the result to selectrows
command_select
;;
"l"|"locate")
zfm_locate
;;
"p"|"pwd") print -r -- $PWD | pbcopy
pinfo "Copied $PWD to clipboard"
;;
*)
# actually it should have had a ! before it. We should not allow this.
eval "$dcommand"
;;
esac
M_SELECTION_MODE=
[[ "$dcommand" = "q" || $dcommand = "quit" ]] && QUITTING=1
pause
}
# add current dir to stack so we can pop back
# We add it backwards so i can shift
# Currently aclled only from GOTO_DIR and :S
function push_pwd() {
local dir
dir=${1:-$PWD}
ZFM_DIR_STACK+=( $dir:q )
#print $ZFM_DIR_STACK
}
## this is only called from :P not from pop, see popd
# This does not remove dirs when popping so we always have all visited dirs with us
function pop_pwd() {
# remove from end
newd=$ZFM_DIR_STACK[-1]
ZFM_DIR_STACK[-1]=()
# put it back on top (first)
ZFM_DIR_STACK[1]+=( $newd:q )
# XXX maybe should cd to new top dir, not removed one.
cd $newd
pwd
post_cd
}
# executed when dir changed
function post_cd() {
PATT=""
filterstr=${filterstr:-M}
param=$(eval "print -rl -- ${pattern}${M_EXCLUDE_PATTERN}(${MFM_LISTORDER}$filterstr)")
## added 2013-02-22 - 00:50 LP myopts
title=$PWD
myopts=("${(@f)$(print -rl -- $param)}")
param=
[[ $#myopts -eq 0 ]] && {
M_MESSAGE="$#param files, use UP or ZFM_GOTO_PARENT_KEY to go to parent folder, LEFT to popd"
}
# clear hash of file details to avoid recomp
FILES_HASH=()
execute_hooks "chdir"
CURSOR=1
sta=1
revert_dir_pos
}
function zfm_refresh() {
title=$PWD
filterstr=${filterstr:-M}
param=$(eval "print -rl -- ${pattern}${M_EXCLUDE_PATTERN}(${MFM_LISTORDER}$filterstr)")
restore_exoanded_state
myopts=("${(@f)$(print -rl -- $param)}")
param=
sms "Rescanned..."
}
## This will ensure that when you return to the directory where
# some dirs were exploded, they will be exploded again
function restore_exoanded_state() {
local td
for d in $ZFM_EXPANDED_DIRS ; do
if [[ -d "$d" ]]; then
td=$d:t
_files=("${(@f)$(print -rl -- $td/*)}")
for f in $_files ; do
param+=( $f )
done
else
# This happens when we move to another dir, so don't worry
perror "$d not a directory: [$ZFM_EXPANDED_DIRS[1]]"
fi
done
}
function print_help_keys() {
local str
str=""
# first print mode related help
local f
f=${ZFM_MODE:l}_help
str=$(eval "$f")
print
str+=" \n"
str+="$fg_bold[white]$ZFM_APP_NAME some keys$reset_color"
str+=" \n"
str+=$(cat <<EndHelp
= General application keys =
* Note: These may have been overriden by individual modes
$ZFM_MENU_KEY - Invoke menu (default: backtick)
^ - toggle match from start of filename
$ZFM_GOTO_DIR_KEY - Enter directory name to jump to
$ZFM_SELECTION_MODE_KEY - Toggle selection mode
$ZFM_EDIT_REGEX_KEY - Edit pattern (should be valid grep regex)
$ZFM_GOTO_PARENT_KEY - Goto parent of existing dir (cd ..)
$ZFM_POPD_KEY - popd (go back to previously visited dirs)
: - Command key
* S - Save current dir in list
* P - Pop dirs from list
$ZFM_RESET_PATTERN_KEY - Clear existing search pattern **
$ZFM_REFRESH_KEY - refresh/rescan dir listing **
$ZFM_SORT_KEY - change sort order (pref. use menu) **
$ZFM_FILTER_KEY - change filter criteria (pref. use menu) **
$ZFM_SIBLING_DIR_KEY - view/select sibling directories **
$ZFM_CD_OLD_NEW_KEY - cd OLD NEW functionality (visit second cousins) **
$ZFM_OPEN_FILES_KEY - open file/s (selected) or under cursor
Most keys are likely to change after getting feedback, the ** ones definitely will
EndHelp
)
str+=" \n"
local keys
keys=(${(k)zfm_keymap})
keys=(${(o)keys})
for key in ${keys} ; do
#print $key : $zfm_keymap[$key]
str+=$(print " $key : $zfm_keymap[$key]")"\n"
done
print -l -- "$str" | $PAGER
#pbold "Key mappings"
}
# utility }
# main {
# alias this to some single letter after sourcing this file in .zshrc
function myzfm() {
## global section
ZFM_APP_NAME="zfm"
ZFM_VERSION="0.1.15-alnitak4"
M_TITLE="$ZFM_APP_NAME $ZFM_VERSION 2013/02/25"
# Array to place selected files
typeset -U selectedfiles
# hash of file details to avoid recomp each time while inside a dir
typeset -Ag FILES_HASH
# hash to store position in dir when we went somewhere else such as into a child dir
# We need to position cursor back when we come up.
typeset -Ag DIR_POSITION
## M_SUBCOMMAND keeps a map of command shortname and function
# These are commands typed in on : prompt
typeset -Ag ZFM_MODE_MAP M_SUBCOMMAND
selectedfiles=()
# directory stack for jumping back, opened fies, and expanded dirs
typeset -U ZFM_DIR_STACK ZFM_FILE_STACK ZFM_EXPANDED_DIRS ZFM_USED_DIRS
ZFM_DIR_STACK=()
ZFM_FILE_STACK=()
ZFM_EXPANDED_DIRS=()
ZFM_USED_DIRS=()
DIR_POSITION=()
M_SUBCOMMAND=()
ZFM_CD_COMMAND="pushd" # earlier cd lets see if dirs affected
export ZFM_CD_COMMAND
ZFM_START_DIR="$PWD"
## If user has passed in any keystrokes read them from here
# passed in keys will only work in our own read here, not in other
# reads or vareds. They will be passed as a string, but we weill put into our array
[[ -n $Z_KEY_STACK ]] && {
Z_KEY_STACK=("${(s/ /)Z_KEY_STACK}")
}
ZFM_FILE_SELECT_FUNCTION=fuzzyselectrow
export ZFM_FILE_SELECT_FUNCTION
export last_viewed_files
# defaults KEYS
ZFM_OPEN_FILES_KEY=${ZFM_OPEN_FILES_KEY:-'C-o'} # pressing selects whatever cursor is on
ZFM_MENU_KEY=${ZFM_MENU_KEY:-$'\`'} # pops up menu
ZFM_GOTO_PARENT_KEY=${ZFM_GOTO_PARENT_KEY:-','} # goto parent of this dir
ZFM_GOTO_DIR_KEY=${ZFM_GOTO_DIR_KEY:-'+'} # goto dir
ZFM_POPD_KEY=${ZFM_POPD_KEY:-"."} # goto previously visited dir
ZFM_SELECTION_MODE_KEY=${ZFM_SELECTION_MODE_KEY:-"@"} # toggle selection mode
ZFM_SORT_KEY=${ZFM_SORT_KEY:-"%"} # change sort options
ZFM_FILTER_KEY=${ZFM_FILTER_KEY:-"#"} # change filter options
ZFM_TOGGLE_MENU_KEY=${ZFM_TOGGLE_MENU_KEY:-"="} # change toggle options
ZFM_TOGGLE_FILE_KEY=${ZFM_TOGGLE_FILE_KEY:-"C-SPACE"} # change toggle options
ZFM_SIBLING_DIR_KEY=${ZFM_SIBLING_DIR_KEY:-"["} # change to sibling dirs
ZFM_CD_OLD_NEW_KEY=${ZFM_CD_OLD_NEW_KEY:-"]"} # change to second cousins
ZFM_QUIT_KEY=${ZFM_QUIT_KEY:-'Q'} # quit application
ZFM_SELECT_ALL_KEY=${ZFM_SELECT_ALL_KEY:-"M-a"} # select all files on screen
ZFM_EDIT_REGEX_KEY=${ZFM_EDIT_REGEX_KEY:-"/"} # edit PATT used to filter
export ZFM_REFRESH_KEY=${ZFM_REFRESH_KEY:-'"'} # refresh the listing
ZFM_MAP_LEADER=${ZFM_MAP_LEADER:-'\'}
ZFM_HINT_KEY=${ZFM_HINT_KEY:-';'}
#export ZFM_NO_COLOR # use to swtich off color in selection
M_SWITCH_OFF_DUPL_CHECK=
MFM_LISTORDER=${MFM_LISTORDER:-""}
M_EXCLUDE_PATTERN=
pattern='*' # this is separate from patt which is a temp filter based on hotkeys
filterstr="M"
M_PRINT_COMMAND_DESC=1
MFM_NLIDX="123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPRSTUVWXYZ"
ZFM_STRING="${pattern}(${MFM_LISTORDER}$filterstr)"
integer ZFM_COLS=$(tput cols)
integer ZFM_LINES=$(tput lines)
## we want 59 if we want no long list info, else lines -4 or 5
PAGESZ=59 # used for incrementing while paging
#(( PAGESZ = ZFM_LINES - 4 ))
(( PAGESZ1 = PAGESZ + 1 ))
integer CURSOR=1
export ZFM_COLS ZFM_LINES CURSOR
export ZFM_STRING
init_key_function_map
init_menu_options
init_file_menus
if [[ -f $ZFM_DIR/bindings.zsh ]]; then
source ${ZFM_DIR}/bindings.zsh
else
perror "Can't find bindings.zsh"
exit 1
fi
source $ZFM_DIR/cursor.zsh
for ff in ${ZFM_DIR}/modes/*
do
source $ff
done
source $ZFM_DIR/bookmark.zsh
source_addons
config_read
zfm_set_mode $ZFM_DEFAULT_MODE
# at this point read up users bindings
#print "$ZFM_TOGGLE_MENU_KEY Toggle | $ZFM_MENU_KEY menu | ? help"
aa=( "?" Help "$ZFM_MENU_KEY" Menu "$ZFM_TOGGLE_MENU_KEY" Toggle)
M_HELP_GEN=$( print_hash $aa )
ab="M_HELP_$ZFM_MODE"
M_HELP="$M_HELP_GEN | ${(P)ab}"
#print $M_HELP
M_MESSAGE="$M_HELP $M_TITLE"
# sta was local in list_printer, tring out belove
# 2013-02-22 - we had a loop here, files were only opened on coming back, I've deleted the loop.
sta=1
list_printer
print "bye"
#stty intr ''
stty $ttysave
} # myzfm
function zfm_open_file() {
local selection=$1
[[ -z $selection ]] && selection=$vpa[$CURSOR]
if [[ -d "$selection" ]]; then
[[ -n $ZFM_VERBOSE ]] && print "got a directory $selection"
save_dir_pos
$ZFM_CD_COMMAND $selection
post_cd
elif [[ -f "$selection" ]]; then
# although nice to immediately open, but what if its not a text file
# and what if i want to do something else
#vim $selection
if [[ -n "$M_SELECTION_MODE" ]]; then
selection=$PWD/$selection
zfm_toggle_file $selection
else
fileopt $selection
fi
else
[[ -n "$selection" ]] && {
# sometimes comes here on a link (esp broken) and fileopt will check for -f and reject
pbold "$0: Don't know how to handle $selection"
file $selection
fileopt $selection
pause
}
fi
}
## temporary func name for C-o key open selected files or what's under cursor
#
function zfm_selected_file_options() {
if [[ -n $selectedfiles ]];then
call_fileoptions $selectedfiles
else
selection=$vpa[$CURSOR]
fi
}
## line numbering function, also takes care of widths and coloring since these are interdependent
# and can clobber one another.
## Earlier this acted as a filter and read lines and printed back output, But now we cache
# file details to avoid screen flicker, so the hash must be in the same shell/process, thus
# it stored details in OUTPUT string. And reads from viewport.
#
# NOTE: Avoid app specific code in here, i can see PWD already and perhaps get_file_details
# Keep it general and simple so we can reuse for other apps.
function numberlines() {
let c=1
local patt='.'
if [[ -n "$ZFM_NO_COLOR" ]]; then
BOLD='*'
BOLD_OFF=
COLOR_STANDOUT=
COLOR_STANDOUTOFF=
else
BOLD=$COLOR_BOLD
COLOR_STANDOUT="\\033[7m"
COLOR_STANDOUTOFF="\\033[27m"
BOLD_OFF=$COLOR_DEFAULT
fi
OUTPUT=""
##local defpatt='.'
local defpatt=""
local selct=$#selectedfiles
[[ $1 = "-p" ]] && { shift; patt="$1"; shift }
[[ $1 = "-w" ]] && { shift; width="$1"; shift }
# since string searching in zsh isn;t on regular expressions and ^ is not respected
# i am taking width of match after removing ^ and using next char as next shortcut
# # no longer required as i don't use grep, but i wish i still were since it allows better
# matching
#patt=${patt:s/^//}
local w=$#patt
#let w++
#while IFS= read -r line; do
for line in $*; do
cc=' '
(( c == CURSOR )) && cc=$CURSOR_MARK
#if [[ -n "$M_FULL_INDEXING" ]]; then
if [[ $ZFM_MODE == "HINT" ]]; then
sub=$MFM_NLIDX[$c]
elif [[ $ZFM_NUMBERING == "ABSOLUTE" ]]; then
## This is triggered by vim when we do a "g"
#sub=$c
# moved to absolute numbering not just cursor which was page relative
(( sub = c + sta - 1 ))
elif [[ $ZFM_NUMBERING == "RELATIVE" ]]; then
## This is triggered by vim when we do a j or k
#sub=$c
if [[ $c -lt $CURSOR ]]; then
(( sub = CURSOR - c ))
elif [[ $c -gt $CURSOR ]]; then
(( sub = c - CURSOR ))
else
## instead of zero show actual pos so some calculations can be done
sub=$c
fi
elif [[ $ZFM_MODE == "VIM" ]]; then
# print absolute number by default, see note below why i do it differently
(( _c = c + sta - 1 ))
sub=$_c
## for some strange reason if i put the next line here
#then HINT starts printing a 0 for all indices after 9 and it prints a 12 for 12
#(( sub = c + sta - 1 ))
elif [[ $ZFM_MODE == "INS" ]]; then
sub=$c
[[ $c -gt 9 ]] && {
#sub=$line[$w,$w] ;
# in the beginning since the patter is . we show first char
# otherwise this will match the dot
if [[ $patt = "$defpatt" ]]; then
sub=$line[1,1]
else
# after removing the ^ we find match and get the character after the pattern
# NOTE: that if the match is at end of filename there is no next character i can show.
ix=$line[(i)$patt]
(( ix += w ))
sub=$line[$ix,$ix] ;
fi
}
else
sub=$c
fi
## pad it to prevent the wiggle/dance
## CRASHES IN INS MODE if you type t* in work folder saying bad math expression
if [[ $ZFM_MODE == "VIM" && $sub -le 9 ]]; then
sub=" $sub"
fi
link=
_detail=
if [[ -n "$ZFM_LS_L" ]]; then
if [[ -n "$line" ]]; then
if [[ -e "$line" ]]; then
# check cache for file details
get_file_details "$line"
# above call updates _detail and the hash, so has to be in current process
else
_detail="(deleted? $PWD)"
# file does not exist so it could be deleted ?
fi
fi
fi
# only if there are selections we check against the array and color
# otherwise no check, remember that the cut that comes later can cut the
# escape chars
_line=
boldflag=0
# 2013-01-09 - 19:33 I am trying out only highlighting the number or else
# its becoming too confusing, and even now the trunc is taking size of
# ANSI codes which are not displayed, so a little less is shown that cold be
if [[ $selct -gt 0 ]]; then
# quoted spaces causing failure in matching,
# however if i don't quote then other programs fail such as ls and tar
if [[ $selectedfiles[(ie)$PWD/${line}] -gt $selct ]]; then
#_line="$sub) $_detail $line $link"
else
#_line="$sub) $_detail ${BOLD}$line${BOLD_OFF}"
#sub="${BOLD}$sub${BOLD_OFF}"
boldflag=1
fi
else
#_line="$sub) $_detail $line $link"
fi
_line="$sub)$cc $_detail $line $link"
(( $#_line > width )) && _line=$_line[1,$width] # cut here itself so ANSI not truncated
(( boldflag == 1 )) && _line="${BOLD}$_line${BOLD_OFF}"
#(( c == CURSOR )) && _line="${COLOR_STANDOUT}$_line${COLOR_STANDOUTOFF}"
(( c == CURSOR )) && _line="${bg_bold[$CURSOR_COLOR]}$_line${reset_color}"
### 2013-01-21 - 21:09 trying to do this in same process so hash be updated
#print -l -- $_line
OUTPUT+="$_line\n"
let c++
done
#print -l -- $OUTPUT
} # numberlines
##
# updates file details in _detail and also updates hash/cache
# this cannot be called in new process, must be called and then _detail used
function get_file_details() {
local line=$1
# TAB required because some methods will parse this when selection ??
# changed tab to spaces on 2013-02-12 - 14:29 BE CAREFUL if things stop working, revert to tab
local tt=" "
local sz link
_detail=$FILES_HASH[$line]
if [[ -z $_detail ]]; then
mtime=$(zstat -L -F "%Y-%m-%d %H:%M" +mtime $line)
zstat -L -H hash $line
sz=$hash[size]
if [[ $sz -gt 1048576 ]]; then
(( sz = sz / 1048576 )) ; sz="${sz}M"
# statements
elif [[ $sz -gt 9999 ]]; then
(( sz = sz / 1024 )) ; sz="${sz}k"
fi
sz=$( print ${(l:6:)sz} )
#[[ $sz -gt 9999 ]] && { (( sz = sz / 1024 )) ; sz="${sz}k" }
link=$hash[link]
[[ -n $link ]] && link=" -> $link"
#_detail="${TAB}$sz${TAB}$mtime${TAB}"
_detail="${tt}$sz${tt}$mtime${tt}"
# cache details of file
FILES_HASH[$line]=$_detail
else
#_detail="$_detail +"
fi
}
function selection_menu() {
local mode="remove_mode"
local mmode="Selection"
[[ $#selectedfiles -eq 0 ]] && ZFM_REMOVE_MODE=
if [[ -n $ZFM_REMOVE_MODE ]]; then
mode="add_mode"
mmode="Unselection "
fi
menu_loop "$mmode Options ($#selectedfiles)" "today extn ack invert $mode" "txaim"
files=
case $menu_text in
"today")
# finding common rows between what's visible and today's files
files=("${(@f)$(print -rl -- *(.m0))}")
pdebug "files $#files : $files"
;;
"extn")
# finding common rows between what's visible and today's files
print -n "Enter extensions to select (space delim *.c *.h): "
read extns
files=("${(@f)$(eval print -rl -- $extns)}")
;;
"ack")
# files containing some text
print -n "Enter pattern to search : "
read cpattern
files=("${(@f)$(eval ack -l $M_ACK_REC_FLAG $cpattern)}")
pdebug "file $#files : $files"
;;
"remove_mode")
if [[ $#selectedfiles -eq 0 ]]; then
perror "There are no files to unselect"
else
ZFM_REMOVE_MODE=1
pinfo "Files selected will be removed from selection"
fi
;;
#(( ZFM_REMOVE_FLAG = ZFM_REMOVE_MODE * -1 ))
"add_mode")
ZFM_REMOVE_MODE=
pinfo "Files selected will be added to selection (normal mode)"
;;
#(( ZFM_REMOVE_FLAG = ZFM_REMOVE_MODE * -1 ))
"invert")
## This is resulting in directories getting selected, avoid that
local vp
vp=($PWD/${^viewport}) # prepend PWD to each element 2013-01-10 - 00:17
selectedfiles=( ${vp:|selectedfiles} )
;;
esac
if [[ -n $files ]]; then
files=($PWD/${^files}) # prepend PWD to each element
# don't quote files again in common loop or spaced files will not get added
if [[ -n $ZFM_REMOVE_MODE ]]; then
#files=( $files:q )
selectedfiles=(${selectedfiles:|files})
else
# i think viewport has only file names, no details
# so we can just do a one line operation
vp=($PWD/${^viewport})
common=( ${vp:*files} )
for line in $common
do