dotfiles

My personal shell configs and stuff
git clone git://git.alex.balgavy.eu/dotfiles.git
Log | Files | Refs | Submodules | README | LICENSE

lfrc (10250B)


      1 # vim: foldmethod=marker foldlevel=0 filetype=lfrc
      2 # General settings {{{
      3 # interpreter for shell commands (needs to be POSIX compatible)
      4 set shell bash
      5 
      6 # set '-eu' options for shell commands
      7 # These options are used to have safer shell commands. Option '-e' is used to
      8 # exit on error and option '-u' is used to give error for unset variables.
      9 # Option '-f' disables pathname expansion which can be useful when $f, $fs, and
     10 # $fx variables contain names with '*' or '?' characters. However, this option
     11 # is used selectively within individual commands as it can be limiting at
     12 # times.
     13 set shellopts '-eu'
     14 
     15 # set internal field separator (IFS) to "\n" for shell commands
     16 # This is useful to automatically split file names in $fs and $fx properly
     17 # since default file separator used in these variables (i.e. 'filesep' option)
     18 # is newline. You need to consider the values of these options and create your
     19 # commands accordingly.
     20 set ifs "\n"
     21 
     22 # leave some space at the top and the bottom of the screen
     23 set scrolloff 10
     24 
     25 # Set a previewer
     26 set previewer ~/.config/lf/preview
     27 set preview
     28 
     29 # General options
     30 set ignorecase
     31 set icons
     32 set number
     33 set relativenumber
     34 
     35 # Show number of files in dirs instead of single size
     36 set info size
     37 set dircounts
     38 # }}}
     39 # Command definitions {{{
     40 # the prefix shows what kind of command it is
     41 #   $: runs as shell command
     42 #   %: runs as piped shell command (stdout => statusline)
     43 #   !: runs as interactive shell command (puts lf in the bg)
     44 #   :: runs as lf command
     45 #
     46 # commands run in the shell defined above, you can use $f, $fs, $fx vars.
     47 # you can also send remote commands.
     48 
     49 # define a custom 'open' command
     50 cmd open $opener $fx
     51 
     52 # define a custom 'rename' command without prompt for overwrite
     53 cmd rename %[ -e $1 ] && printf "file exists" || mv $f $1
     54 
     55 # rename multiple files the way nnn does it (open in editable buffer)
     56 cmd batch_rename !{{
     57   contentsfile=$(mktemp)
     58   trap 'rm $contentsfile' INT TERM EXIT
     59   dircontents="$(command ls -1p)"
     60   printf "%s" "$dircontents" | nl -n ln > "$contentsfile";
     61   $EDITOR "$contentsfile"
     62   printf "%s" "$dircontents" | paste -d '\t' "$contentsfile" - \
     63   | while read -r line; do
     64     orig="$(printf "%s" "$line" | cut -f3 | sed 's!/$!!')"
     65     new="$(printf "%s" "$line" | cut -f2 | sed 's!/$!!')"
     66     if [ "$new" != "$orig" ]; then
     67       if [ -e "$new" ]; then
     68         printf "%s already exists, not overwriting.\n" "$new"
     69       else
     70         printf "./%s -> ./%s\n" "$orig" "$new"
     71         command mv -i ./"$orig" ./"$new"
     72       fi
     73     fi
     74   done
     75   rm "$contentsfile"
     76   trap - INT TERM EXIT
     77 }}
     78 
     79 # change part of a filename using sed
     80 cmd change %{{
     81   printf "Substitute: "
     82   read -r sedcmd
     83   mv "$f" "$(dirname "$f")/$(printf '%s' "$(basename "$f")" | sed "s$sedcmd")"
     84 }}
     85 
     86 # load audio files/playlists in mpd
     87 cmd queue %{{
     88   for i in $fx; do
     89     mpcq "$i"
     90   done
     91 }}
     92 cmd queuenext %{{
     93   for i in $fx; do
     94     case "$i" in
     95       *.wav|*.flac|*.mp3|*.m4a|*.opus)
     96         mpc -h ~/.local/share/mpd/socket insert "file://$i"
     97         printf "Queued: %s" "$i"
     98         ;;
     99       *.m3u)
    100         mpc -h ~/.local/share/mpd/socket load "file://$i"
    101         printf "Loaded playlist: %s" "$i"
    102         ;;
    103     esac
    104   done
    105 }}
    106 
    107 # extract the current file with the right command
    108 # (xkcd link: https://xkcd.com/1168/)
    109 cmd extract ${{
    110     set -f
    111     case $f in
    112         *.tar.bz|*.tar.bz2|*.tbz|*.tbz2) tar xjvf $f;;
    113         *.tar.gz|*.tgz) tar xzvf $f;;
    114         *.tar.xz|*.txz) tar xJvf $f;;
    115         *.zip) unzip $f;;
    116         *.rar) unrar x $f;;
    117         *.7z) 7z x $f;;
    118         *.jar) jar -xvf $f;;
    119         *.tar) tar xvf $f;;
    120     esac
    121 }}
    122 
    123 # copy the path of the highlighted file
    124 # relies on my `clc` script
    125 cmd copy_path ${{
    126   echo "$f" | tr -d '\n' | clc
    127 }}
    128 
    129 # zip directories/files
    130 cmd zip ${{
    131   set -f
    132   mkdir $f-zip
    133   cp -r $fx $f-zip
    134   (cd $f-zip/ && zip -r $f.zip .)
    135   rm -rf $f-zip/
    136 }}
    137 
    138 # tar directories/files
    139 cmd tar ${{
    140   set -f
    141   mkdir $f-tgz
    142   cp -r $fx $f-tgz
    143   (cd $f-tgz && tar cvzf $f.tar.gz .)
    144   rm -rf $f-tgz
    145 }}
    146 
    147 # custom paste command using rsync/mv
    148 cmd paste &{{
    149     load=$(cat ~/.local/share/lf/files)
    150     mode=$(echo "$load" | sed -n '1p')
    151     list=$(echo "$load" | sed '1d')
    152     if [ $mode = 'copy' ]; then
    153         rsync -av --ignore-existing --progress $list . \
    154         | stdbuf -i0 -o0 -e0 tr '\r' '\n' \
    155         | while read line; do
    156             lf -remote "send $id echo $line"
    157         done
    158     elif [ $mode = 'move' ]; then
    159         mv -n $list .
    160     fi
    161     lf -remote 'send load'
    162     lf -remote 'send clear'
    163 }}
    164 
    165 # paste as a symbolic link to original
    166 cmd paste_link %{{
    167   if [ "$(wc -l < ~/.local/share/lf/files)" -gt 1 ]; then
    168     while read -r file; do
    169       case "$file" in
    170         copy|move) ;;
    171         *)
    172           ln -s "$file" .
    173           ;;
    174       esac
    175     done < ~/.local/share/lf/files
    176   else
    177     lf -remote "send $id echo no files to link."
    178   fi
    179 }}
    180 
    181 cmd paste_rename %{{
    182   mode="$(head -n 1 ~/.local/share/lf/files)"
    183   for fname in $(tail -n +2 ~/.local/share/lf/files); do
    184     while [ -z "${newfname:-}" ]; do
    185       printf "%s => " "$fname"
    186       read -r newfname
    187     done
    188     if [ "$mode" = 'copy' ]; then
    189       cp -n "$fname" ./"$newfname"
    190     else
    191       mv -n "$fname" ./"$newfname"
    192     fi
    193   done
    194   lf -remote 'send clear'
    195   lf -remote 'send load'
    196 }}
    197 
    198 # preview the highlighted file with quicklook (macOS)
    199 cmd ql_preview &{{
    200   qlmanage -p $fx &> /dev/null;
    201 }}
    202 
    203 # open the lfrc, and reload it after saving
    204 cmd edit_config :{{
    205   $$EDITOR ~/.config/lf/lfrc
    206   source ~/.config/lf/lfrc
    207 }}
    208 
    209 cmd z ${{
    210   set +u
    211   if [ $# -gt 0 ]; then
    212     dest=''
    213     if command -v zoxide >/dev/null 2>&1; then
    214         dest="$(zoxide query "$@")"
    215     fi
    216 
    217     if test -n "${dest}"; then
    218         lf -remote "send $id cd '${dest}'"
    219     fi
    220   fi
    221 }}
    222 
    223 # trash command with confirmation
    224 cmd trash %{{
    225   for f in $fx; do
    226     printf "Trash $f? [Y/n/a] "
    227     read -r ans
    228     case "$ans" in
    229       Y*|y*) trash "$f"; printf 'Deleted %s.\n' "$f";;
    230       A*|a*) trash $fx; printf 'Deleted all files.\n'; break;;
    231     esac
    232   done
    233 }}
    234 
    235 # empty the trash
    236 cmd trashempty %{{
    237   printf "Empty trash? [Y/n] "
    238   read -r ans
    239   case "$ans" in
    240     Y*|y*) trash -ey; printf "Emptied.";;
    241     *) printf "Not emptying.";;
    242   esac
    243 }}
    244 
    245 # PGP functions
    246 cmd pgp_encrypt_recipient %{{
    247   printf "Recipient: "
    248   read -r recipient
    249   case "$recipient" in
    250     "") printf "Cancelled.";;
    251     *) gpg --encrypt --sign --recipient "$recipient" $f;;
    252   esac
    253 }}
    254 
    255 cmd pgp_encrypt_pass ${{
    256   gpg --symmetric $f;
    257 }}
    258 
    259 cmd pgp_decrypt ${{
    260   case "$f" in
    261     *.gpg) outfile="${f%.gpg}";;
    262     *.pgp) outfile="${f%.pgp}";;
    263     *) outfile="decrypted-${f}";;
    264   esac
    265   gpg --output "$outfile" --decrypt "$f"
    266 }}
    267 
    268 # Encode input into binary or ASCII-armored output with an integrated signature
    269 cmd pgp_sign ${{
    270   gpg --sign $f
    271 }}
    272 
    273 # Wrap input in plaintext signature
    274 cmd pgp_sign_clear ${{
    275   gpg --clearsign $f
    276 }}
    277 
    278 # Create binary or ASCII-armored detached signature from input
    279 cmd pgp_sign_detach ${{
    280   gpg --detach-sign $f
    281 }}
    282 
    283 # Select files based on glob filter
    284 cmd glob-select-wrapper %{{
    285   printf "Filter: "
    286   read -r filter_expr
    287   lf -remote "send $id unselect"
    288   lf -remote "send $id glob-select $filter_expr"
    289 }}
    290 
    291 # Remove empty directories in current tree
    292 cmd rmempty %find .  -type d -empty -depth -delete
    293 
    294 # Use xld to transcode audio
    295 cmd xld %open -a XLD $f
    296 
    297 # fzf files
    298 cmd fuzzy_select $lf -remote "send $id select \"$(fzf --layout reverse)\""
    299 
    300 # fuzzy grep files
    301 cmd fuzzy_grep ${{
    302   fpath="$(rg --color=always --line-number --no-heading --smart-case "${*:-}" \
    303     | fzf --ansi --delimiter : --preview 'bat --color=always {1} --highlight-line {2}' --preview-window 'up,60%,border-bottom,+{2}+3/3,~3' --bind 'enter:become(printf "%s" {1})')"
    304   lf -remote "send $id select $fpath"
    305 }}
    306 
    307 # Show file info with exiftool
    308 cmd file_info $LESSOPEN='| exiftool %s' LESS='-cRiX' less $f
    309 
    310 # Preview a file
    311 cmd file_preview $LESSOPEN='| ~/.config/lf/preview %s' LESS='-cRiX' less $f
    312 
    313 # Scroll parent up/down
    314 cmd scroll_parent_up :{{
    315   updir; up
    316   %[ -d "$f" ] && lf -remote "send $id open"
    317 }}
    318 
    319 cmd scroll_parent_down :{{
    320   updir; down
    321   %[ -d "$f" ] && lf -remote "send $id open"
    322 }}
    323 
    324 cmd qrsend ${{
    325   clear
    326   qrcp send $fx || { clear; qrcp send --interface en0 $fx; }
    327 }}
    328 # }}}
    329 # Mappings {{{
    330 # Mouse
    331 map <m-down> down
    332 map <m-up> up
    333 map <m-1> open
    334 map <m-3> updir
    335 
    336 # Enter commands
    337 map ; push :
    338 map S shell-pipe
    339 
    340 # execute current file (must be executable)
    341 map x $$f
    342 map X !$f
    343 
    344 # Disk usage
    345 map ug %du -sh $f
    346 
    347 # Open (uses the above-defined command)
    348 map o open $fx
    349 map <enter> open $fx
    350 # Open containing dir
    351 map O $open $(dirname $f)
    352 
    353 # Listing
    354 map r reload
    355 map . set hidden!
    356 map U redraw
    357 
    358 # Selecting
    359 map f fuzzy_select
    360 map \\ fuzzy_grep
    361 map <esc> unselect; clear
    362 map <space> :toggle; down
    363 map F glob-select-wrapper
    364 map u
    365 map uu unselect
    366 map uy clear
    367 map ud clear
    368 map a :unselect; invert
    369 
    370 # Show help
    371 map g? $lf -doc | $PAGER; clear
    372 
    373 # Renaming
    374 map R push :rename<space>
    375 map <c-r> batch_rename
    376 map C change
    377 map cw push :rename<space>
    378 
    379 # <tab> == <c-i>
    380 map <tab> jump-next
    381 map <c-o> jump-prev
    382 # Delete
    383 map D trash
    384 
    385 # Copy (others are defined by default)
    386 map Y copy_path
    387 
    388 # Paste
    389 map p
    390 map pp paste
    391 map pl paste_link
    392 map pr paste_rename
    393 
    394 # Viewing
    395 map i file_info
    396 map P file_preview
    397 map v ql_preview
    398 map <c-v> :set preview!; reload
    399 
    400 # Navigation
    401 map z push :z
    402 map [ scroll_parent_up
    403 map ] scroll_parent_down
    404 map gd. cd ~/Documents
    405 map gds cd ~/Documents/School
    406 map gdw cd ~/Documents/Syncthing/alex.balgavy.eu/
    407 map gdb cd ~/Documents/Syncthing/blog.alex.balgavy.eu/
    408 map gdl cd ~/Documents/Syncthing/lectures.alex.balgavy.eu/
    409 map gb cd ~/Documents/Calibre\ Library/
    410 map gdp cd ~/Documents/Syncthing/Personal\ documents/
    411 map gD cd ~/Desktop
    412 map gl cd ~/Downloads
    413 map gw cd ~/Documents/Syncthing/vimwiki
    414 map gm cd ~/Music/Libraries/Mine
    415 map g/ cd /
    416 map g. cd $DOTFILES
    417 map gv cd /Volumes
    418 map gt cd ~/.Trash/
    419 
    420 # Creating
    421 map c
    422 map cf push %touch<space>
    423 map cd push %mkdir<space>
    424 
    425 # Editing
    426 map e
    427 map E $$EDITOR $fx
    428 map ee $$EDITOR $f
    429 map ec edit_config
    430 map ce edit_config
    431 
    432 map Q queue
    433 
    434 # Compressing
    435 map t
    436 map * tag-toggle
    437 map tz zip
    438 map tt tar
    439 map tu extract
    440 
    441 map td &ripdrag $fx
    442 
    443 # PGP
    444 map tp
    445 map tpe pgp_encrypt_recipient
    446 map tpp pgp_encrypt_pass
    447 map tpd pgp_decrypt
    448 map tps. pgp_sign
    449 map tpsc pgp_sign_clear
    450 map tpsd pgp_sign_detach
    451 
    452 map tq qrsend