Compare commits
No commits in common. "a55657f4d0d99c3a0371d875341ed33b3d74eee3" and "ffaf21b484c80412af6baba3b8efd6d6e7e0a8dd" have entirely different histories.
a55657f4d0
...
ffaf21b484
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/.deployed_cache
|
||||||
|
**/cache
|
||||||
|
**/*.log
|
||||||
|
**/logs
|
||||||
|
**/tmp/
|
||||||
|
/.git
|
||||||
|
/.yolk_git
|
||||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
|||||||
[submodule "eggs/vim/pack/vim-wayland-clipboard"]
|
[submodule "eggs/vim/pack/vim-wayland-clipboard"]
|
||||||
path = eggs/vim/pack/vim-wayland-clipboard
|
path = eggs/vim/pack/vim-wayland-clipboard
|
||||||
url = https://github.com/jasonccox/vim-wayland-clipboard.git
|
url = https://github.com/jasonccox/vim-wayland-clipboard.git
|
||||||
[submodule "eggs/zsh/config/fzf-tab"]
|
|
||||||
path = eggs/zsh/config/fzf-tab
|
|
||||||
url = https://github.com/Aloxaf/fzf-tab.git
|
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<conf:configuration-backend-db xmlns:conf="http://openoffice.org/extensionmanager/configuration-registry/2010"/>
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<help:help-backend-db xmlns:help="http://openoffice.org/extensionmanager/help-registry/2010"/>
|
||||||
1
eggs/vim/pack/vim-wayland-clipboard
Submodule
1
eggs/vim/pack/vim-wayland-clipboard
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 2fa6178d39925eab6a33dd13583d1bd9b67d3f65
|
||||||
@ -1,21 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
## [0.2.1] - 2022-02-18
|
|
||||||
### Fixed
|
|
||||||
- Don't print control characters on screen from system calls to `wl-copy`/`wl-paste`.
|
|
||||||
|
|
||||||
## [0.2.0] - 2021-04-30
|
|
||||||
### Added
|
|
||||||
- Make `ctrl-r+` and friends work in insert mode.
|
|
||||||
### Fixed
|
|
||||||
- Corrected check for `clipboard` feature.
|
|
||||||
|
|
||||||
## [0.1.0] - 2021-04-27
|
|
||||||
### Added
|
|
||||||
- Start maintaining versions and a changelog.
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
# vim-wayland-clipboard
|
|
||||||
|
|
||||||
**[jasoncarloscox.com/creations/vim-wayland-clipboard](https://jasoncarloscox.com/creations/vim-wayland-clipboard/)**
|
|
||||||
|
|
||||||
This plugin allows Vim to integrate with the Wayland clipboard when using the `+` register. This means you can yank text into the `+` register and paste it in other Wayland programs, or copy text in other Wayland programs and paste it in Vim from the `+` register. Operators and counts work, too!
|
|
||||||
|
|
||||||
When running Vim outside of Wayland, the `+` register continues to work as normal.
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
|
|
||||||
For this plugin to work, you need [wl-clipboard](https://github.com/bugaevc/wl-clipboard) installed, and Vim must be compiled with the `+eval` feature.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Use Vim8's built-in packages:
|
|
||||||
|
|
||||||
1. `mkdir -p ~/.vim/pack/vim-wayland-clipboard/start/`
|
|
||||||
2. `git clone https://github.com/jasonccox/vim-wayland-clipboard.git ~/.vim/pack/vim-wayland-clipboard/start/vim-wayland-clipboard`
|
|
||||||
|
|
||||||
## Usage and Features
|
|
||||||
|
|
||||||
Just use `"+y`, `"+p`, `<C-R>+`, and friends as you always do. Specifically, here's what's supported:
|
|
||||||
|
|
||||||
- Any yank command that starts with `"+` (e.g. `"+yy` or `"+yiw`) in insert and visual modes.
|
|
||||||
- Pasting in normal and visual modes with `"+p` or `"+P`.
|
|
||||||
- Pasting in insert mode with `<C-R>+`, `<C-R><C-R>+`, `<C-R><C-O>+`, or `<C-R><C-P>+`.
|
|
||||||
- Yanking and pasting (`p` and `P` in normal and visual modes) with `clipboard=unnamedplus` or `g:wayland_clipboard_unnamedplus = 1`.
|
|
||||||
|
|
||||||
If you need more functionality, consider checking out [vim-fakeclip](https://github.com/kana/vim-fakeclip).
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
### Passing extra arguments to `wl-copy` or `wl-paste`
|
|
||||||
|
|
||||||
If you want to pass extra arguments to `wl-copy`, set `g:wayland_clipboard_copy_args` to a list of strings, one per argument. For example, to copy to the primary clipboard and only allow the contents to be pasted once, you could do the following:
|
|
||||||
|
|
||||||
```vimscript
|
|
||||||
let g:wayland_clipboard_copy_args = ['--primary', '--paste-once']
|
|
||||||
```
|
|
||||||
|
|
||||||
To pass extra arguments to `wl-paste`, use `g:wayland_clipboard_paste_args` in the same way.
|
|
||||||
|
|
||||||
### Clobbering the `w` Register
|
|
||||||
|
|
||||||
On Vim builds without `clipboard`, or if Xwayland isn't running, the `+` register doesn't work for yanking. My solution is to map `"+` to `"w` and send the `w` register to the Wayland clipboard as well. (This only occurs when the `clipboard` feature is missing or the X `$DISPLAY` environment vairable is empty.) If you use the `w` register for other things and don't want it to clobber your system clipboard, put `let g:wayland_clipboard_no_plus_to_w = 1` in your `vimrc` to disable this feature.
|
|
||||||
|
|
||||||
### Non-recursive Mappings
|
|
||||||
|
|
||||||
This plugin uses mappings of `"+p`, `<C-R>+`, `"+`, etc. to do its job. As a result, it won't work with existing *non-recursive* mappings that run these commands, e.g. `nnoremap <Leader>p "+p`. If you have mappings like these, you'll need to use their recursive counterparts instead for the plugin to work.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Contributions are welcome! You can send questions, bug reports, patches, etc. by email to [~jcc/public-inbox@lists.sr.ht](https://lists.sr.ht/~jcc/public-inbox). (Don't know how to contribute via email? Check out the interactive tutorial at [git-send-email.io](https://git-send-email.io), or [email me](mailto:me@jasoncarloscox.com) for help.)
|
|
||||||
|
|
||||||
GitHub issues and pull requests are fine, too.
|
|
||||||
@ -1,116 +0,0 @@
|
|||||||
" wayland-clipboard.vim - Integrate with Wayland's clipboard when using the '+'
|
|
||||||
" register. Requires wl-clipboard and the +eval and +clipboard Vim features.
|
|
||||||
"
|
|
||||||
" This script was inspired by
|
|
||||||
" https://www.reddit.com/r/Fedora/comments/ax9p9t/vim_and_system_clipboard_under_wayland/
|
|
||||||
" but uses an autocmd to allow yanking with operators to work.
|
|
||||||
|
|
||||||
" Early exit checks {{{
|
|
||||||
|
|
||||||
" only load this script once
|
|
||||||
if exists('g:loaded_wayland_clipboard')
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
let g:loaded_wayland_clipboard = 1
|
|
||||||
|
|
||||||
" only run this in Vim on Wayland -- Vim on X has native clipboard support,
|
|
||||||
" and Neovim already works with wl-copy by default
|
|
||||||
if has('nvim') || empty($WAYLAND_DISPLAY)
|
|
||||||
finish
|
|
||||||
endif
|
|
||||||
|
|
||||||
" }}}
|
|
||||||
|
|
||||||
" Yanking {{{
|
|
||||||
|
|
||||||
" The '+' register doesn't work for yanking if:
|
|
||||||
" - vim was built without 'clipboard'.
|
|
||||||
" - x11 / xwayland is unavailable.
|
|
||||||
" (https://github.com/vim/vim/blob/93197fde0f1db09b1e495cf3eb14a8f42c318b80/src/register.c#L247)
|
|
||||||
"
|
|
||||||
" My solution is to map '"+' to '"w' and send the 'w' register to the
|
|
||||||
" Wayland clipboard as well.
|
|
||||||
"
|
|
||||||
" This variable controls whether '"+' gets mapped to '"w'. It's on by default
|
|
||||||
" if the 'clipboard' feature isn't available, or if $DISPLAY isn't set,
|
|
||||||
" but the user can turn it off always if desired.
|
|
||||||
let s:plus_to_w = (!has('clipboard') || empty($DISPLAY)) && !exists('g:wayland_clipboard_no_plus_to_w')
|
|
||||||
|
|
||||||
" remap '"+' to '"w' -- this will only apply to yanking since '"+p' and '"+P'
|
|
||||||
" are also remapped below
|
|
||||||
if s:plus_to_w
|
|
||||||
nnoremap "+ "w
|
|
||||||
vnoremap "+ "w
|
|
||||||
endif
|
|
||||||
|
|
||||||
let s:copy_args = exists('g:wayland_clipboard_copy_args') ? g:wayland_clipboard_copy_args : []
|
|
||||||
|
|
||||||
function! s:unnamedplus()
|
|
||||||
return &clipboard =~ 'unnamedplus'
|
|
||||||
\ || (exists('g:wayland_clipboard_unnamedplus') && g:wayland_clipboard_unnamedplus)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" pass register contents to wl-copy if the '+' (or 'w') register was used
|
|
||||||
function! s:WaylandYank()
|
|
||||||
if v:event['regname'] == '+' ||
|
|
||||||
\ (v:event['regname'] == 'w' && s:plus_to_w) ||
|
|
||||||
\ (v:event['regname'] == '' && s:unnamedplus())
|
|
||||||
let job = job_start(['wl-copy'] + s:copy_args, {
|
|
||||||
\ "in_io": "pipe", "out_io": "null", "err_io": "null",
|
|
||||||
\ "stoponexit": "",
|
|
||||||
\ })
|
|
||||||
call ch_sendraw(job, getreg(v:event['regname']))
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" run s:WaylandYank() after every time text is yanked
|
|
||||||
augroup waylandyank
|
|
||||||
autocmd!
|
|
||||||
autocmd TextYankPost * call s:WaylandYank()
|
|
||||||
augroup END
|
|
||||||
|
|
||||||
" }}}
|
|
||||||
|
|
||||||
" Pasting {{{
|
|
||||||
|
|
||||||
" remap paste commands to first pull in clipboard contents with wl-paste
|
|
||||||
|
|
||||||
let s:paste_args = exists('g:wayland_clipboard_paste_args') ? g:wayland_clipboard_paste_args : []
|
|
||||||
let s:paste_args_str = empty(s:paste_args) ? '' : ' ' . join(s:paste_args)
|
|
||||||
|
|
||||||
function! s:clipboard_to_unnamed()
|
|
||||||
silent let @"=substitute(system('wl-paste --no-newline' . s:paste_args_str), "\r", '', 'g')
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:put(p, fallback)
|
|
||||||
if a:fallback
|
|
||||||
return a:p
|
|
||||||
endif
|
|
||||||
|
|
||||||
call s:clipboard_to_unnamed()
|
|
||||||
return '""' . a:p
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:ctrl_r(cr)
|
|
||||||
call s:clipboard_to_unnamed()
|
|
||||||
return a:cr . '"'
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
nnoremap <expr> <silent> "+p <SID>put('p', v:false)
|
|
||||||
nnoremap <expr> <silent> "+P <SID>put('P', v:false)
|
|
||||||
nnoremap <expr> <silent> p <SID>put('p', !<SID>unnamedplus())
|
|
||||||
nnoremap <expr> <silent> P <SID>put('P', !<SID>unnamedplus())
|
|
||||||
|
|
||||||
vnoremap <expr> <silent> "+p <SID>put('p', v:false)
|
|
||||||
vnoremap <expr> <silent> "+P <SID>put('P', v:false)
|
|
||||||
vnoremap <expr> <silent> p <SID>put('p', !<SID>unnamedplus())
|
|
||||||
vnoremap <expr> <silent> P <SID>put('P', !<SID>unnamedplus())
|
|
||||||
|
|
||||||
inoremap <expr> <silent> <C-R>+ <SID>ctrl_r("\<C-R>")
|
|
||||||
inoremap <expr> <silent> <C-R><C-R>+ <SID>ctrl_r("\<C-R>\<C-R>")
|
|
||||||
inoremap <expr> <silent> <C-R><C-O>+ <SID>ctrl_r("\<C-R>\<C-O>")
|
|
||||||
inoremap <expr> <silent> <C-R><C-P>+ <SID>ctrl_r("\<C-R>\<C-P>")
|
|
||||||
|
|
||||||
" }}}
|
|
||||||
|
|
||||||
" vim:foldmethod=marker:foldlevel=0
|
|
||||||
@ -1 +0,0 @@
|
|||||||
Subproject commit 2abe1f2f1cbcb3d3c6b879d849d683de5688111f
|
|
||||||
42
eggs/zsh/config/fzf-tab/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
42
eggs/zsh/config/fzf-tab/.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
name: Bug report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: "[BUG]"
|
||||||
|
labels: bug
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Describe the bug
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
I can make sure:
|
||||||
|
- [ ] I am using the latest version of fzf-tab
|
||||||
|
- [ ] this is the minimal zshrc which can reproduce this bug
|
||||||
|
- [ ] fzf-tab is loaded after `compinit`
|
||||||
|
- [ ] fzf-tab is loaded after plugins which will wrap <kbd>Tab</kbd>, like [junegunn/fzf/completion.zsh](https://github.com/junegunn/fzf/blob/master/shell/completion.zsh)
|
||||||
|
- [ ] fzf-tab is loaded before zsh-autosuggestions, zsh-syntax-highlighting and fast-syntax-highlighting.
|
||||||
|
|
||||||
|
#### To Reproduce
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Type '...'
|
||||||
|
2. Press <kbd>Tab</kbd>
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
#### Expected behavior
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
#### Screenshots
|
||||||
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
|
#### Environment:
|
||||||
|
- OS: [e.g. Arch Linux]
|
||||||
|
- zsh version: [e.g. 5.8.1]
|
||||||
|
|
||||||
|
#### Minimal zshrc
|
||||||
|
If applicable, add a minimal zshrc to help us analyze.
|
||||||
|
|
||||||
|
#### Log
|
||||||
|
If applicable, use `C-x .` to trigger completion and provide the log.
|
||||||
|
|
||||||
|
If there are only three lines in your log, please make sure your fzf-tab is loaded with the correct order (see the checklist above).
|
||||||
20
eggs/zsh/config/fzf-tab/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
eggs/zsh/config/fzf-tab/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: "[FR]"
|
||||||
|
labels: enhancement
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
11
eggs/zsh/config/fzf-tab/.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
11
eggs/zsh/config/fzf-tab/.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: Question
|
||||||
|
about: Ask a question about fzf-tab
|
||||||
|
title: "[Q]"
|
||||||
|
labels: question
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Describe your question**
|
||||||
|
A clear and concise description of your question.
|
||||||
44
eggs/zsh/config/fzf-tab/.github/workflows/linux.yaml
vendored
Normal file
44
eggs/zsh/config/fzf-tab/.github/workflows/linux.yaml
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
name: Linux compability
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: ["ubuntu:latest", "archlinux:latest"]
|
||||||
|
container:
|
||||||
|
image: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: install dependencies
|
||||||
|
run: |
|
||||||
|
if [ "${{ matrix.os }}" = "ubuntu:latest" ]; then
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y zsh git curl build-essential autoconf libncurses-dev
|
||||||
|
elif [ "${{ matrix.os }}" = "archlinux:latest" ]; then
|
||||||
|
pacman -Syu --noconfirm
|
||||||
|
pacman -S --noconfirm zsh base-devel git
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: test completion
|
||||||
|
run: cd test && zsh -f runtests.zsh fzftab.ztst
|
||||||
|
|
||||||
|
- name: build binary module
|
||||||
|
run: zsh -fc 'source ./fzf-tab.zsh && build-fzf-tab-module'
|
||||||
|
|
||||||
|
- name: test binary module
|
||||||
|
run: cd test && zsh -f runtests.zsh fzftab.ztst
|
||||||
|
|
||||||
30
eggs/zsh/config/fzf-tab/.github/workflows/macos.yaml
vendored
Normal file
30
eggs/zsh/config/fzf-tab/.github/workflows/macos.yaml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: macOS compability
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- run: brew install autoconf automake libtool
|
||||||
|
|
||||||
|
# FIXME: test on macOS
|
||||||
|
#- name: test completion
|
||||||
|
# run: cd test && zsh -f runtests.zsh fzftab.ztst
|
||||||
|
|
||||||
|
- name: build binary module
|
||||||
|
run: zsh -fc 'source ./fzf-tab.zsh && build-fzf-tab-module'
|
||||||
|
|
||||||
|
#- name: test binary module
|
||||||
|
# run: cd test && zsh -f runtests.zsh fzftab.ztst
|
||||||
45
eggs/zsh/config/fzf-tab/.github/workflows/zsh.yaml
vendored
Normal file
45
eggs/zsh/config/fzf-tab/.github/workflows/zsh.yaml
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
name: Zsh compability
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# TODO: test fzf version?
|
||||||
|
zsh_version:
|
||||||
|
- 5.3.1
|
||||||
|
- 5.4.2
|
||||||
|
- 5.5.1
|
||||||
|
- 5.6.2
|
||||||
|
- 5.7.1
|
||||||
|
- 5.8
|
||||||
|
- 5.9
|
||||||
|
container:
|
||||||
|
image: zshusers/zsh:${{ matrix.zsh_version }}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: install dependencies
|
||||||
|
run: apt update && apt-get install -y git curl build-essential autoconf libncurses-dev
|
||||||
|
|
||||||
|
- name: test completion
|
||||||
|
run: cd test && zsh -f runtests.zsh fzftab.ztst
|
||||||
|
|
||||||
|
- name: build binary module
|
||||||
|
run: zsh -fc 'source ./fzf-tab.zsh && build-fzf-tab-module'
|
||||||
|
|
||||||
|
- name: test binary module
|
||||||
|
run: cd test && zsh -f runtests.zsh fzftab.ztst
|
||||||
|
|
||||||
1
eggs/zsh/config/fzf-tab/.gitignore
vendored
Normal file
1
eggs/zsh/config/fzf-tab/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.zwc
|
||||||
@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2021 Jason Cox
|
Copyright (c) 2019-2024 Aloxaf
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
169
eggs/zsh/config/fzf-tab/README.md
Normal file
169
eggs/zsh/config/fzf-tab/README.md
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
# fzf-tab
|
||||||
|
|
||||||
|
[](https://github.com/Aloxaf/fzf-tab/actions?query=workflow%3Aci)
|
||||||
|
[](https://github.com/Aloxaf/fzf-tab/blob/master/LICENSE)
|
||||||
|
|
||||||
|
Replace zsh's default completion selection menu with fzf!
|
||||||
|
|
||||||
|
[](https://asciinema.org/a/293849)
|
||||||
|
|
||||||
|
<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc -->
|
||||||
|
**Table of Contents**
|
||||||
|
|
||||||
|
- [fzf-tab](#fzf-tab)
|
||||||
|
- [Install](#install)
|
||||||
|
- [Manual](#manual)
|
||||||
|
- [Antigen](#antigen)
|
||||||
|
- [Zinit](#zinit)
|
||||||
|
- [Oh-My-Zsh](#oh-my-zsh)
|
||||||
|
- [Prezto](#prezto)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [Configure](#configure)
|
||||||
|
- [Tmux](#tmux)
|
||||||
|
- [Binary module](#binary-module)
|
||||||
|
- [Difference from other plugins](#difference-from-other-plugins)
|
||||||
|
- [Compatibility with other plugins](#compatibility-with-other-plugins)
|
||||||
|
- [Related projects](#related-projects)
|
||||||
|
|
||||||
|
<!-- markdown-toc end -->
|
||||||
|
|
||||||
|
# Install
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
>
|
||||||
|
> 1. make sure [fzf](https://github.com/junegunn/fzf) is installed
|
||||||
|
> 2. fzf-tab needs to be loaded after `compinit`, but before plugins which will wrap widgets, such as [zsh-autosuggestions](https://github.com/zsh-users/zsh-autosuggestions) or [fast-syntax-highlighting](https://github.com/zdharma-continuum/fast-syntax-highlighting)
|
||||||
|
> 3. Completions should be configured before `compinit`, as stated in the [zsh-completions manual installation guide](https://github.com/zsh-users/zsh-completions#manual-installation).
|
||||||
|
|
||||||
|
### Manual
|
||||||
|
|
||||||
|
First, clone this repository.
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
git clone https://github.com/Aloxaf/fzf-tab ~/somewhere
|
||||||
|
```
|
||||||
|
|
||||||
|
Then add the following line to your `~/.zshrc`.
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
autoload -U compinit; compinit
|
||||||
|
source ~/somewhere/fzf-tab.plugin.zsh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Antigen
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
antigen bundle Aloxaf/fzf-tab
|
||||||
|
```
|
||||||
|
|
||||||
|
### Zinit
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
zinit light Aloxaf/fzf-tab
|
||||||
|
```
|
||||||
|
|
||||||
|
### Oh-My-Zsh
|
||||||
|
|
||||||
|
Clone this repository to your custom directory and then add `fzf-tab` to your plugin list.
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
git clone https://github.com/Aloxaf/fzf-tab ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/fzf-tab
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prezto
|
||||||
|
|
||||||
|
Clone this repository to your contrib directory and then add `fzf-tab` to your module list in `.zpreztorc`.
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
git clone https://github.com/Aloxaf/fzf-tab $ZPREZTODIR/contrib/fzf-tab
|
||||||
|
```
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
Just press <kbd>Tab</kbd> as usual~
|
||||||
|
|
||||||
|
Available keybindings:
|
||||||
|
|
||||||
|
- <kbd>Ctrl</kdb>+<kdb>Space</kbd>: select multiple results, can be configured by `fzf-bindings` tag
|
||||||
|
|
||||||
|
- <kbd>F1</kbd>/<kbd>F2</kbd>: switch between groups, can be configured by `switch-group` tag
|
||||||
|
|
||||||
|
- <kbd>/</kbd>: trigger continuous completion (useful when completing a deep path), can be configured by `continuous-trigger` tag
|
||||||
|
|
||||||
|
Available commands:
|
||||||
|
|
||||||
|
- `disable-fzf-tab`: disable fzf-tab and fallback to compsys
|
||||||
|
|
||||||
|
- `enable-fzf-tab`: enable fzf-tab
|
||||||
|
|
||||||
|
- `toggle-fzf-tab`: toggle the state of fzf-tab. This is also a zle widget.
|
||||||
|
|
||||||
|
## Configure
|
||||||
|
|
||||||
|
A common configuration is:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
# disable sort when completing `git checkout`
|
||||||
|
zstyle ':completion:*:git-checkout:*' sort false
|
||||||
|
# set descriptions format to enable group support
|
||||||
|
# NOTE: don't use escape sequences (like '%F{red}%d%f') here, fzf-tab will ignore them
|
||||||
|
zstyle ':completion:*:descriptions' format '[%d]'
|
||||||
|
# set list-colors to enable filename colorizing
|
||||||
|
zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
|
||||||
|
# force zsh not to show completion menu, which allows fzf-tab to capture the unambiguous prefix
|
||||||
|
zstyle ':completion:*' menu no
|
||||||
|
# preview directory's content with eza when completing cd
|
||||||
|
zstyle ':fzf-tab:complete:cd:*' fzf-preview 'eza -1 --color=always $realpath'
|
||||||
|
# custom fzf flags
|
||||||
|
# NOTE: fzf-tab does not follow FZF_DEFAULT_OPTS by default
|
||||||
|
zstyle ':fzf-tab:*' fzf-flags --color=fg:1,fg+:2 --bind=tab:accept
|
||||||
|
# To make fzf-tab follow FZF_DEFAULT_OPTS.
|
||||||
|
# NOTE: This may lead to unexpected behavior since some flags break this plugin. See Aloxaf/fzf-tab#455.
|
||||||
|
zstyle ':fzf-tab:*' use-fzf-default-opts yes
|
||||||
|
# switch group using `<` and `>`
|
||||||
|
zstyle ':fzf-tab:*' switch-group '<' '>'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tmux
|
||||||
|
|
||||||
|
If you're using tmux >= 3.2, we provide a script `ftb-tmux-popup` to make full use of it's "popup" feature.
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
zstyle ':fzf-tab:*' fzf-command ftb-tmux-popup
|
||||||
|
```
|
||||||
|
|
||||||
|
BTW, you can also use this script outside the fzf-tab.
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
ls | ftb-tmux-popup
|
||||||
|
```
|
||||||
|
|
||||||
|
[](https://asciinema.org/a/367471)
|
||||||
|
|
||||||
|
For more information, please see [Wiki#Configuration](https://github.com/Aloxaf/fzf-tab/wiki/Configuration).
|
||||||
|
|
||||||
|
## Binary module
|
||||||
|
|
||||||
|
By default, fzf-tab uses [zsh-ls-colors](https://github.com/xPMo/zsh-ls-colors) to parse and apply ZLS_COLORS if you have set the `list-colors` tag.
|
||||||
|
|
||||||
|
However, it is a pure zsh script and is slow if you have too many files to colorize.
|
||||||
|
fzf-tab is shipped with a binary module to speed up this process. You can build it with `build-fzf-tab-module`, then it will be enabled automatically.
|
||||||
|
|
||||||
|
# Difference from other plugins
|
||||||
|
|
||||||
|
fzf-tab doesn't do "complete", it just shows you the results of the default completion system.
|
||||||
|
|
||||||
|
So it works EVERYWHERE (variables, function names, directory stack, in-word completion, etc.).
|
||||||
|
And most of your configuration for default completion system is still valid.
|
||||||
|
|
||||||
|
# Compatibility with other plugins
|
||||||
|
|
||||||
|
Some plugins may also bind "^I" to their custom widget, like [fzf/shell/completion.zsh](https://github.com/junegunn/fzf/blob/master/shell/completion.zsh) or [ohmyzsh/lib/completion.zsh](https://github.com/ohmyzsh/ohmyzsh/blob/master/lib/completion.zsh#L61-L73).
|
||||||
|
|
||||||
|
By default, fzf-tab will call the widget previously bound to "^I" to get the completion list. So there is no problem in most cases, unless fzf-tab is initialized before a plugin which doesn't handle the previous binding properly.
|
||||||
|
|
||||||
|
So if you find your fzf-tab doesn't work properly, **please make sure it is the last plugin to bind "^I"** (If you don't know what I mean, just put it to the end of your plugin list).
|
||||||
|
|
||||||
|
# Related projects
|
||||||
|
|
||||||
|
- https://github.com/lincheney/fzf-tab-completion (fzf tab completion for zsh, bash and GNU readline apps)
|
||||||
3
eggs/zsh/config/fzf-tab/fzf-tab.plugin.zsh
Normal file
3
eggs/zsh/config/fzf-tab/fzf-tab.plugin.zsh
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}"
|
||||||
|
0="${${(M)0:#/*}:-$PWD/$0}"
|
||||||
|
source "${0:A:h}/fzf-tab.zsh"
|
||||||
440
eggs/zsh/config/fzf-tab/fzf-tab.zsh
Normal file
440
eggs/zsh/config/fzf-tab/fzf-tab.zsh
Normal file
@ -0,0 +1,440 @@
|
|||||||
|
# temporarily change options
|
||||||
|
'builtin' 'local' '-a' '_ftb_opts'
|
||||||
|
[[ ! -o 'aliases' ]] || _ftb_opts+=('aliases')
|
||||||
|
[[ ! -o 'sh_glob' ]] || _ftb_opts+=('sh_glob')
|
||||||
|
[[ ! -o 'no_brace_expand' ]] || _ftb_opts+=('no_brace_expand')
|
||||||
|
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
|
||||||
|
|
||||||
|
# disable aliases
|
||||||
|
typeset _ftb_aliases="$(builtin alias -Lm '[^+]*')"
|
||||||
|
builtin unalias -m '[^+]*'
|
||||||
|
|
||||||
|
# thanks Valodim/zsh-capture-completion
|
||||||
|
-ftb-compadd() {
|
||||||
|
# parse all options
|
||||||
|
local -A apre hpre dscrs _oad _mesg
|
||||||
|
local -a isfile _opts __ expl
|
||||||
|
zparseopts -a _opts P:=apre p:=hpre d:=dscrs X+:=expl O:=_oad A:=_oad D:=_oad f=isfile \
|
||||||
|
i: S: s: I: x:=_mesg r: R: W: F: M+: E: q e Q n U C \
|
||||||
|
J:=__ V:=__ a=__ l=__ k=__ o::=__ 1=__ 2=__
|
||||||
|
|
||||||
|
# store $curcontext for further usage
|
||||||
|
_ftb_curcontext=${curcontext#:}
|
||||||
|
|
||||||
|
# just delegate and leave if any of -O, -A or -D are given or fzf-tab is not enabled
|
||||||
|
# or fzf-tab is disabled in the current context
|
||||||
|
if (( $#_oad != 0 || ! IN_FZF_TAB )) \
|
||||||
|
|| { -ftb-zstyle -m disabled-on "any" } \
|
||||||
|
|| { -ftb-zstyle -m disabled-on "files" && [[ -n $isfile ]] }; then
|
||||||
|
builtin compadd "$@"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# store matches in $__hits and descriptions in $__dscr
|
||||||
|
local -a __hits __dscr
|
||||||
|
if (( $#dscrs == 1 )); then
|
||||||
|
__dscr=( "${(@P)${(v)dscrs}}" )
|
||||||
|
fi
|
||||||
|
builtin compadd -A __hits -D __dscr "$@"
|
||||||
|
local ret=$?
|
||||||
|
if (( $#__hits == 0 )); then
|
||||||
|
if is-at-least 5.9 && (( $#_mesg != 0 )); then
|
||||||
|
builtin compadd -x $_mesg
|
||||||
|
fi
|
||||||
|
return $ret
|
||||||
|
fi
|
||||||
|
|
||||||
|
# only store the fist `-X`
|
||||||
|
expl=$expl[2]
|
||||||
|
|
||||||
|
# keep order of group description
|
||||||
|
[[ -n $expl ]] && _ftb_groups+=$expl
|
||||||
|
|
||||||
|
# store these values in _ftb_compcap
|
||||||
|
local -a keys=(apre hpre PREFIX SUFFIX IPREFIX ISUFFIX)
|
||||||
|
local key expanded __tmp_value=$'<\0>' # placeholder
|
||||||
|
for key in $keys; do
|
||||||
|
expanded=${(P)key}
|
||||||
|
if [[ -n $expanded ]]; then
|
||||||
|
__tmp_value+=$'\0'$key$'\0'$expanded
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ -n $expl ]]; then
|
||||||
|
# store group index
|
||||||
|
__tmp_value+=$'\0group\0'$_ftb_groups[(ie)$expl]
|
||||||
|
fi
|
||||||
|
if [[ -n $isfile ]]; then
|
||||||
|
# NOTE: need a extra ${} here or ~ expansion won't work
|
||||||
|
__tmp_value+=$'\0realdir\0'${${(Qe)~${:-$IPREFIX$hpre}}}
|
||||||
|
fi
|
||||||
|
_opts+=("${(@kv)apre}" "${(@kv)hpre}" $isfile)
|
||||||
|
__tmp_value+=$'\0args\0'${(pj:\1:)_opts}
|
||||||
|
|
||||||
|
if (( $+builtins[fzf-tab-compcap-generate] )); then
|
||||||
|
fzf-tab-compcap-generate __hits __dscr __tmp_value
|
||||||
|
else
|
||||||
|
# dscr - the string to show to users
|
||||||
|
# word - the string to be inserted
|
||||||
|
local dscr word i
|
||||||
|
for i in {1..$#__hits}; do
|
||||||
|
word=$__hits[i] dscr=$__dscr[i]
|
||||||
|
if [[ -n $dscr ]]; then
|
||||||
|
dscr=${dscr//$'\n'}
|
||||||
|
elif [[ -n $word ]]; then
|
||||||
|
dscr=$word
|
||||||
|
fi
|
||||||
|
_ftb_compcap+=$dscr$'\2'$__tmp_value$'\0word\0'$word
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# tell zsh that the match is successful
|
||||||
|
builtin compadd "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
-ftb-zstyle() {
|
||||||
|
zstyle $1 ":fzf-tab:$_ftb_curcontext" ${@:2}
|
||||||
|
}
|
||||||
|
|
||||||
|
-ftb-complete() {
|
||||||
|
local -Ua _ftb_groups
|
||||||
|
local choice choices _ftb_curcontext continuous_trigger print_query accept_line bs=$'\2' nul=$'\0'
|
||||||
|
local ret=0
|
||||||
|
|
||||||
|
# must run with user options; don't move `emulate -L zsh` above this line
|
||||||
|
(( $+builtins[fzf-tab-compcap-generate] )) && fzf-tab-compcap-generate -i
|
||||||
|
COLUMNS=500 _ftb__main_complete "$@" || ret=$?
|
||||||
|
(( $+builtins[fzf-tab-compcap-generate] )) && fzf-tab-compcap-generate -o
|
||||||
|
|
||||||
|
emulate -L zsh -o extended_glob
|
||||||
|
|
||||||
|
local _ftb_query _ftb_complist=() _ftb_headers=() command opts
|
||||||
|
-ftb-generate-complist # sets `_ftb_complist`
|
||||||
|
|
||||||
|
-ftb-zstyle -s continuous-trigger continuous_trigger || {
|
||||||
|
[[ $OSTYPE == msys ]] && continuous_trigger=// || continuous_trigger=/
|
||||||
|
}
|
||||||
|
|
||||||
|
case $#_ftb_complist in
|
||||||
|
0) return 1;;
|
||||||
|
1)
|
||||||
|
choices=("EXPECT_KEY" "${_ftb_compcap[1]%$bs*}")
|
||||||
|
if (( _ftb_continue_last )); then
|
||||||
|
choices[1]=$continuous_trigger
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if (( ! _ftb_continue_last )) \
|
||||||
|
&& [[ $compstate[insert] == *"unambiguous" ]] \
|
||||||
|
&& [[ -n $compstate[unambiguous] ]] \
|
||||||
|
&& [[ "$compstate[unambiguous]" != "$compstate[quote]$IPREFIX$PREFIX$compstate[quote]" ]]; then
|
||||||
|
compstate[list]=
|
||||||
|
compstate[insert]=unambiguous
|
||||||
|
_ftb_finish=1
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
-ftb-generate-query # sets `_ftb_query`
|
||||||
|
-ftb-generate-header # sets `_ftb_headers`
|
||||||
|
-ftb-zstyle -s print-query print_query || print_query=alt-enter
|
||||||
|
-ftb-zstyle -s accept-line accept_line
|
||||||
|
|
||||||
|
choices=("${(@f)"$(builtin print -rl -- $_ftb_headers $_ftb_complist | -ftb-fzf)"}")
|
||||||
|
ret=$?
|
||||||
|
# choices=(query_string expect_key returned_word)
|
||||||
|
|
||||||
|
# insert query string directly
|
||||||
|
if [[ $choices[2] == $print_query ]] || [[ -n $choices[1] && $#choices == 1 ]] ; then
|
||||||
|
local -A v=("${(@0)${_ftb_compcap[1]}}")
|
||||||
|
local -a args=("${(@ps:\1:)v[args]}")
|
||||||
|
[[ -z $args[1] ]] && args=() # don't pass an empty string
|
||||||
|
IPREFIX=$v[IPREFIX] PREFIX=$v[PREFIX] SUFFIX=$v[SUFFIX] ISUFFIX=$v[ISUFFIX]
|
||||||
|
# NOTE: should I use `-U` here?, ../f\tabcd -> ../abcd
|
||||||
|
builtin compadd "${args[@]:--Q}" -Q -- $choices[1]
|
||||||
|
|
||||||
|
compstate[list]=
|
||||||
|
compstate[insert]=
|
||||||
|
if (( $#choices[1] > 0 )); then
|
||||||
|
compstate[insert]='1'
|
||||||
|
[[ $RBUFFER == ' '* ]] || compstate[insert]+=' '
|
||||||
|
fi
|
||||||
|
_ftb_finish=1
|
||||||
|
return $ret
|
||||||
|
fi
|
||||||
|
choices[1]=()
|
||||||
|
|
||||||
|
choices=("${(@)${(@)choices%$nul*}#*$nul}")
|
||||||
|
|
||||||
|
unset CTXT
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [[ -n $choices[1] && $choices[1] == $continuous_trigger ]]; then
|
||||||
|
typeset -gi _ftb_continue=1
|
||||||
|
typeset -gi _ftb_continue_last=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $choices[1] && $choices[1] == $accept_line ]]; then
|
||||||
|
typeset -gi _ftb_accept=1
|
||||||
|
fi
|
||||||
|
choices[1]=()
|
||||||
|
|
||||||
|
_ftb_choices=("${(@)choices}")
|
||||||
|
|
||||||
|
compstate[list]=
|
||||||
|
compstate[insert]=
|
||||||
|
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
_fzf-tab-apply() {
|
||||||
|
local choice bs=$'\2'
|
||||||
|
for choice in "$_ftb_choices[@]"; do
|
||||||
|
local -A v=("${(@0)${_ftb_compcap[(r)${(b)choice}$bs*]#*$bs}}")
|
||||||
|
local -a args=("${(@ps:\1:)v[args]}")
|
||||||
|
[[ -z $args[1] ]] && args=() # don't pass an empty string
|
||||||
|
IPREFIX=${v[IPREFIX]-} PREFIX=${v[PREFIX]-} SUFFIX=${v[SUFFIX]-} ISUFFIX=${v[ISUFFIX]-}
|
||||||
|
builtin compadd "${args[@]:--Q}" -Q -- "$v[word]"
|
||||||
|
done
|
||||||
|
|
||||||
|
compstate[list]=
|
||||||
|
if (( $#_ftb_choices == 1 )); then
|
||||||
|
compstate[insert]='1'
|
||||||
|
[[ $RBUFFER == ' '* ]] || compstate[insert]+=' '
|
||||||
|
elif (( $#_ftb_choices > 1 )); then
|
||||||
|
compstate[insert]='all'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
fzf-tab-debug() {
|
||||||
|
(( $+_ftb_debug_cnt )) || typeset -gi _ftb_debug_cnt
|
||||||
|
local tmp=${TMPPREFIX:-/tmp/zsh}-$$-fzf-tab-$(( ++_ftb_debug_cnt )).log
|
||||||
|
local -i debug_fd=-1 IN_FZF_TAB=1
|
||||||
|
{
|
||||||
|
exec {debug_fd}>&2 2>| $tmp
|
||||||
|
local -a debug_indent; debug_indent=( '%'{3..20}'(e. .)' )
|
||||||
|
local PROMPT4 PS4="${(j::)debug_indent}+%N:%i> "
|
||||||
|
functions -t -- -ftb-complete _fzf-tab-apply fzf-tab-complete
|
||||||
|
{
|
||||||
|
echo $ZSH_NAME $ZSH_VERSION
|
||||||
|
echo fzf-tab: $(-ftb-version)
|
||||||
|
typeset -p FZF_DEFAULT_OPTS
|
||||||
|
echo $commands[fzf] $(fzf --version)
|
||||||
|
} >&2
|
||||||
|
zle fzf-tab-complete
|
||||||
|
if (( debug_fd != -1 )); then
|
||||||
|
zle -M "fzf-tab-debug: Trace output left in $tmp"
|
||||||
|
fi
|
||||||
|
} always {
|
||||||
|
functions +t -- -ftb-complete _fzf-tab-apply fzf-tab-complete
|
||||||
|
(( debug_fd != -1 )) && exec 2>&$debug_fd {debug_fd}>&-
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fzf-tab-complete() {
|
||||||
|
# this name must be ugly to avoid clashes
|
||||||
|
local -i _ftb_continue=1 _ftb_continue_last=0 _ftb_accept=0 ret=0
|
||||||
|
# hide the cursor until finishing completion, so that users won't see cursor up and down
|
||||||
|
# NOTE: MacOS Terminal doesn't support civis & cnorm
|
||||||
|
echoti civis >/dev/tty 2>/dev/null
|
||||||
|
while (( _ftb_continue )); do
|
||||||
|
local _ftb_choices=() _ftb_compcap=() _ftb_finish=0
|
||||||
|
_ftb_continue=0
|
||||||
|
local IN_FZF_TAB=1
|
||||||
|
{
|
||||||
|
zle .fzf-tab-orig-$_ftb_orig_widget || ret=$?
|
||||||
|
if (( ! ret && ! _ftb_finish )); then
|
||||||
|
zle _fzf-tab-apply || ret=$?
|
||||||
|
fi
|
||||||
|
} always {
|
||||||
|
IN_FZF_TAB=0
|
||||||
|
}
|
||||||
|
if (( _ftb_continue )); then
|
||||||
|
zle .split-undo
|
||||||
|
zle .reset-prompt
|
||||||
|
zle -R
|
||||||
|
zle fzf-tab-dummy
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echoti cnorm >/dev/tty 2>/dev/null
|
||||||
|
zle .redisplay
|
||||||
|
(( _ftb_accept )) && zle .accept-line
|
||||||
|
return $ret
|
||||||
|
}
|
||||||
|
|
||||||
|
# this function does nothing, it is used to be wrapped by other plugins like f-sy-h.
|
||||||
|
# this make it possible to call the wrapper function without causing any other side effects.
|
||||||
|
fzf-tab-dummy() { }
|
||||||
|
|
||||||
|
zle -N fzf-tab-debug
|
||||||
|
zle -N fzf-tab-complete
|
||||||
|
zle -N fzf-tab-dummy
|
||||||
|
# this is registered as a completion widget
|
||||||
|
# so that we can have a clean completion list to only insert the results user selected
|
||||||
|
zle -C _fzf-tab-apply complete-word _fzf-tab-apply
|
||||||
|
|
||||||
|
disable-fzf-tab() {
|
||||||
|
emulate -L zsh -o extended_glob
|
||||||
|
(( $+_ftb_orig_widget )) || return 0
|
||||||
|
|
||||||
|
bindkey '^I' $_ftb_orig_widget
|
||||||
|
case $_ftb_orig_list_grouped in
|
||||||
|
0) zstyle ':completion:*' list-grouped false ;;
|
||||||
|
1) zstyle ':completion:*' list-grouped true ;;
|
||||||
|
2) zstyle -d ':completion:*' list-grouped ;;
|
||||||
|
esac
|
||||||
|
unset _ftb_orig_widget _ftb_orig_list_groupded
|
||||||
|
|
||||||
|
# unhook compadd so that _approximate can work properply
|
||||||
|
unfunction compadd 2>/dev/null
|
||||||
|
|
||||||
|
functions[_main_complete]=$functions[_ftb__main_complete]
|
||||||
|
functions[_approximate]=$functions[_ftb__approximate]
|
||||||
|
|
||||||
|
# Don't remove .fzf-tab-orig-$_ftb_orig_widget as we won't be able to reliably
|
||||||
|
# create it if enable-fzf-tab is called again.
|
||||||
|
}
|
||||||
|
|
||||||
|
enable-fzf-tab() {
|
||||||
|
emulate -L zsh -o extended_glob
|
||||||
|
(( ! $+_ftb_orig_widget )) || disable-fzf-tab
|
||||||
|
|
||||||
|
typeset -g _ftb_orig_widget="${${$(builtin bindkey '^I')##* }:-expand-or-complete}"
|
||||||
|
if (( ! $+widgets[.fzf-tab-orig-$_ftb_orig_widget] )); then
|
||||||
|
# Widgets that get replaced by compinit.
|
||||||
|
local compinit_widgets=(
|
||||||
|
complete-word
|
||||||
|
delete-char-or-list
|
||||||
|
expand-or-complete
|
||||||
|
expand-or-complete-prefix
|
||||||
|
list-choices
|
||||||
|
menu-complete
|
||||||
|
menu-expand-or-complete
|
||||||
|
reverse-menu-complete
|
||||||
|
)
|
||||||
|
# Note: We prefix the name of the widget with '.' so that it doesn't get wrapped.
|
||||||
|
if [[ $widgets[$_ftb_orig_widget] == builtin &&
|
||||||
|
$compinit_widgets[(Ie)$_ftb_orig_widget] != 0 ]]; then
|
||||||
|
# We are initializing before compinit and being asked to fall back to a completion
|
||||||
|
# widget that isn't defined yet. Create our own copy of the widget ahead of time.
|
||||||
|
zle -C .fzf-tab-orig-$_ftb_orig_widget .$_ftb_orig_widget _main_complete
|
||||||
|
else
|
||||||
|
# Copy the widget before it's wrapped by zsh-autosuggestions and zsh-syntax-highlighting.
|
||||||
|
zle -A $_ftb_orig_widget .fzf-tab-orig-$_ftb_orig_widget
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
zstyle -t ':completion:*' list-grouped false
|
||||||
|
typeset -g _ftb_orig_list_grouped=$?
|
||||||
|
|
||||||
|
zstyle ':completion:*' list-grouped false
|
||||||
|
bindkey -M emacs '^I' fzf-tab-complete
|
||||||
|
bindkey -M viins '^I' fzf-tab-complete
|
||||||
|
bindkey -M emacs '^X.' fzf-tab-debug
|
||||||
|
bindkey -M viins '^X.' fzf-tab-debug
|
||||||
|
|
||||||
|
# make sure we can copy them
|
||||||
|
autoload +X -Uz _main_complete _approximate
|
||||||
|
|
||||||
|
# hook compadd
|
||||||
|
functions[compadd]=$functions[-ftb-compadd]
|
||||||
|
|
||||||
|
# hook _main_complete to trigger fzf-tab
|
||||||
|
functions[_ftb__main_complete]=$functions[_main_complete]
|
||||||
|
function _main_complete() { -ftb-complete "$@" }
|
||||||
|
|
||||||
|
# TODO: This is not a full support, see #47
|
||||||
|
# _approximate will also hook compadd
|
||||||
|
# let it call -ftb-compadd instead of builtin compadd so that fzf-tab can capture result
|
||||||
|
# make sure _approximate has been loaded.
|
||||||
|
functions[_ftb__approximate]=$functions[_approximate]
|
||||||
|
function _approximate() {
|
||||||
|
# if not called by fzf-tab, don't do anything with compadd
|
||||||
|
(( ! IN_FZF_TAB )) || unfunction compadd
|
||||||
|
_ftb__approximate
|
||||||
|
(( ! IN_FZF_TAB )) || functions[compadd]=$functions[-ftb-compadd]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle-fzf-tab() {
|
||||||
|
emulate -L zsh -o extended_glob
|
||||||
|
if (( $+_ftb_orig_widget )); then
|
||||||
|
disable-fzf-tab
|
||||||
|
else
|
||||||
|
enable-fzf-tab
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
build-fzf-tab-module() {
|
||||||
|
{
|
||||||
|
pushd -q $FZF_TAB_HOME/modules
|
||||||
|
if -ftb-build-module $@; then
|
||||||
|
print -P "%F{green}%BThe module has been built successfully. Please restart zsh to apply it.%f%b"
|
||||||
|
else
|
||||||
|
print -P -u2 "%F{red}%BThe module building has failed. See the output above for details.%f%b"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
} always {
|
||||||
|
popd -q
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zmodload zsh/zutil
|
||||||
|
zmodload zsh/mapfile
|
||||||
|
zmodload -F zsh/stat b:zstat
|
||||||
|
|
||||||
|
0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}"
|
||||||
|
0="${${(M)0:#/*}:-$PWD/$0}"
|
||||||
|
FZF_TAB_HOME="${0:A:h}"
|
||||||
|
|
||||||
|
source "$FZF_TAB_HOME"/lib/zsh-ls-colors/ls-colors.zsh fzf-tab-lscolors
|
||||||
|
|
||||||
|
typeset -ga _ftb_group_colors=(
|
||||||
|
$'\x1b[94m' $'\x1b[32m' $'\x1b[33m' $'\x1b[35m' $'\x1b[31m' $'\x1b[38;5;27m' $'\x1b[36m'
|
||||||
|
$'\x1b[38;5;100m' $'\x1b[38;5;98m' $'\x1b[91m' $'\x1b[38;5;80m' $'\x1b[92m'
|
||||||
|
$'\x1b[38;5;214m' $'\x1b[38;5;165m' $'\x1b[38;5;124m' $'\x1b[38;5;120m'
|
||||||
|
)
|
||||||
|
|
||||||
|
# init
|
||||||
|
() {
|
||||||
|
emulate -L zsh -o extended_glob
|
||||||
|
|
||||||
|
if (( ! $fpath[(I)$FZF_TAB_HOME/lib] )); then
|
||||||
|
fpath+=($FZF_TAB_HOME/lib)
|
||||||
|
fi
|
||||||
|
|
||||||
|
autoload -Uz is-at-least -- $FZF_TAB_HOME/lib/-#ftb*(:t)
|
||||||
|
|
||||||
|
if (( $+FZF_TAB_COMMAND || $+FZF_TAB_OPTS || $+FZF_TAB_QUERY || $+FZF_TAB_SINGLE_GROUP || $+fzf_tab_preview_init )) \
|
||||||
|
|| zstyle -m ":fzf-tab:*" command '*' \
|
||||||
|
|| zstyle -m ":fzf-tab:*" extra-opts '*'; then
|
||||||
|
print -P "%F{red}%B[fzf-tab] Sorry, your configuration is not supported anymore\n" \
|
||||||
|
"See https://github.com/Aloxaf/fzf-tab/pull/132 for more information%f%b"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n $FZF_TAB_HOME/modules/Src/aloxaf/fzftab.(so|bundle)(#qN) ]]; then
|
||||||
|
module_path+=("$FZF_TAB_HOME/modules/Src")
|
||||||
|
zmodload aloxaf/fzftab
|
||||||
|
|
||||||
|
if [[ $FZF_TAB_MODULE_VERSION != "0.2.2" ]]; then
|
||||||
|
zmodload -u aloxaf/fzftab
|
||||||
|
local rebuild
|
||||||
|
print -Pn "%F{yellow}fzftab module needs to be rebuild, rebuild now?[Y/n]:%f"
|
||||||
|
read -q rebuild
|
||||||
|
if [[ $rebuild == y ]]; then
|
||||||
|
build-fzf-tab-module
|
||||||
|
zmodload aloxaf/fzftab
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
enable-fzf-tab
|
||||||
|
zle -N toggle-fzf-tab
|
||||||
|
|
||||||
|
# restore aliases
|
||||||
|
eval "$_ftb_aliases"
|
||||||
|
builtin unset _ftb_aliases
|
||||||
|
|
||||||
|
# restore options
|
||||||
|
(( ${#_ftb_opts} )) && setopt ${_ftb_opts[@]}
|
||||||
|
'builtin' 'unset' '_ftb_opts'
|
||||||
38
eggs/zsh/config/fzf-tab/lib/-ftb-build-module
Normal file
38
eggs/zsh/config/fzf-tab/lib/-ftb-build-module
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/hint/zsh
|
||||||
|
emulate -LR zsh -o extended_glob -o err_return
|
||||||
|
|
||||||
|
local zsh_version=${1:-${FZF_TAB_ZSH_SRC_VERSION:-$ZSH_VERSION}}
|
||||||
|
|
||||||
|
# macos check
|
||||||
|
local ret bundle nproc
|
||||||
|
[[ $OSTYPE == darwin* ]] && {
|
||||||
|
[[ -n ${module_path[1]}/**/*.bundle(#qN) ]] && bundle=true
|
||||||
|
nproc=$(sysctl -n hw.logicalcpu)
|
||||||
|
} || {
|
||||||
|
nproc=$(nproc)
|
||||||
|
}
|
||||||
|
|
||||||
|
# clone zsh source code if not exists
|
||||||
|
[[ -d ./zsh/$zsh_version ]] || {
|
||||||
|
git clone --depth=1 --branch zsh-$zsh_version https://github.com/zsh-users/zsh ./zsh/$zsh_version
|
||||||
|
}
|
||||||
|
|
||||||
|
ln -sf $PWD/Src/fzftab.c ./zsh/$zsh_version/Src/Modules/
|
||||||
|
ln -sf $PWD/Src/fzftab.mdd ./zsh/$zsh_version/Src/Modules/
|
||||||
|
|
||||||
|
# build zsh
|
||||||
|
cd -q ./zsh/$zsh_version
|
||||||
|
|
||||||
|
git checkout -- .
|
||||||
|
|
||||||
|
[[ $zsh_version != "5.9" ]] || {
|
||||||
|
curl -s https://github.com/zsh-users/zsh/commit/4c89849c98172c951a9def3690e8647dae76308f.patch | git apply --exclude=ChangeLog -
|
||||||
|
curl -s https://github.com/zsh-users/zsh/commit/ab4d62eb975a4c4c51dd35822665050e2ddc6918.patch | git apply --exclude=ChangeLog -
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ -f ./configure ]] || ./Util/preconfig
|
||||||
|
[[ -f ./Makefile ]] || ./configure --disable-gdbm --disable-pcre --without-tcsetpgrp --prefix=/tmp/zsh-fzf-tab-module ${bundle:+DL_EXT=bundle}
|
||||||
|
make -j$nproc
|
||||||
|
|
||||||
|
# we only need aloxaf/fzftab.so
|
||||||
|
mv ./Src/Modules/fzftab.(so|bundle) $FZF_TAB_HOME/modules/Src/aloxaf/
|
||||||
34
eggs/zsh/config/fzf-tab/lib/-ftb-colorize
Normal file
34
eggs/zsh/config/fzf-tab/lib/-ftb-colorize
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#!/hint/zsh
|
||||||
|
emulate -L zsh -o cbases -o octalzeroes
|
||||||
|
|
||||||
|
local REPLY
|
||||||
|
local -a reply stat lstat
|
||||||
|
|
||||||
|
# fzf-tab-lscolors::match-by $1 lstat follow
|
||||||
|
zstat -A lstat -L -- $1
|
||||||
|
# follow symlink
|
||||||
|
(( lstat[3] & 0170000 )) && zstat -A stat -- $1 2>/dev/null
|
||||||
|
|
||||||
|
fzf-tab-lscolors::from-mode "$1" "$lstat[3]" $stat[3]
|
||||||
|
# fall back to name
|
||||||
|
[[ -z $REPLY ]] && fzf-tab-lscolors::from-name $1
|
||||||
|
|
||||||
|
# If this is a symlink
|
||||||
|
if [[ -n $lstat[14] ]]; then
|
||||||
|
local sym_color=$REPLY
|
||||||
|
local rsv_color=$REPLY
|
||||||
|
local rsv=$lstat[14]
|
||||||
|
# If this is not a broken symlink
|
||||||
|
if [[ -e $rsv ]]; then
|
||||||
|
# fzf-tab-lscolors::match-by $rsv stat
|
||||||
|
zstat -A stat -- $rsv
|
||||||
|
fzf-tab-lscolors::from-mode $rsv $stat[3]
|
||||||
|
# fall back to name
|
||||||
|
[[ -z $REPLY ]] && fzf-tab-lscolors::from-name $rsv
|
||||||
|
rsv_color=$REPLY
|
||||||
|
fi
|
||||||
|
dpre=$'\033[0m\033['$sym_color'm'
|
||||||
|
dsuf+=$'\033[0m -> \033['$rsv_color'm'$rsv
|
||||||
|
else
|
||||||
|
dpre=$'\033[0m\033['$REPLY'm'
|
||||||
|
fi
|
||||||
114
eggs/zsh/config/fzf-tab/lib/-ftb-fzf
Executable file
114
eggs/zsh/config/fzf-tab/lib/-ftb-fzf
Executable file
@ -0,0 +1,114 @@
|
|||||||
|
#!/hint/zsh
|
||||||
|
|
||||||
|
# import math functions
|
||||||
|
autoload -Uz zmathfunc
|
||||||
|
zmathfunc
|
||||||
|
|
||||||
|
local tmp_dir=${TMPPREFIX:-/tmp/zsh}-fzf-tab-$USER
|
||||||
|
[[ -d $tmp_dir ]] || command mkdir $tmp_dir
|
||||||
|
|
||||||
|
local ftb_preview_init="
|
||||||
|
zmodload zsh/mapfile
|
||||||
|
local -a _ftb_compcap=(\"\${(@f)mapfile[$tmp_dir/compcap.$$]}\")
|
||||||
|
local -a _ftb_groups=(\"\${(@f)mapfile[$tmp_dir/groups.$$]}\")
|
||||||
|
local bs=\$'\2'
|
||||||
|
# get description
|
||||||
|
export desc=\${\${\"\$(<{f})\"%\$'\0'*}#*\$'\0'}
|
||||||
|
# get ctxt for current completion
|
||||||
|
local -A ctxt=(\"\${(@0)\${_ftb_compcap[(r)\${(b)desc}\$bs*]#*\$bs}}\")
|
||||||
|
# get group
|
||||||
|
if (( \$+ctxt[group] )); then
|
||||||
|
export group=\$_ftb_groups[\$ctxt[group]]
|
||||||
|
fi
|
||||||
|
# get original word
|
||||||
|
export word=\${(Q)ctxt[word]}
|
||||||
|
# get real path if it is file
|
||||||
|
if (( \$+ctxt[realdir] )); then
|
||||||
|
export realpath=\${ctxt[realdir]}\$word
|
||||||
|
fi
|
||||||
|
$(typeset -p words)
|
||||||
|
"
|
||||||
|
local default_binds=tab:down,btab:up,change:top,ctrl-space:toggle,bspace:backward-delete-char/eof,ctrl-h:backward-delete-char/eof
|
||||||
|
local fzf_command fzf_flags fzf_preview debug_command tmp switch_group fzf_pad fzf_min_height binds
|
||||||
|
local ret=0
|
||||||
|
|
||||||
|
-ftb-zstyle -s fzf-command fzf_command || fzf_command=fzf
|
||||||
|
-ftb-zstyle -a fzf-bindings-default tmp && binds=${(j:,:)tmp} || binds=$default_binds
|
||||||
|
-ftb-zstyle -a fzf-bindings tmp && binds+=,${(j:,:)tmp}
|
||||||
|
-ftb-zstyle -a fzf-flags fzf_flags
|
||||||
|
-ftb-zstyle -s fzf-preview fzf_preview
|
||||||
|
-ftb-zstyle -a switch-group switch_group || switch_group=(F1 F2)
|
||||||
|
-ftb-zstyle -s fzf-pad fzf_pad || fzf_pad=2
|
||||||
|
-ftb-zstyle -s fzf-min-height fzf_min_height || fzf_min_height=0
|
||||||
|
-ftb-zstyle -b use-fzf-default-opts use_fzf_default_opts || use_fzf_default_opts="no"
|
||||||
|
|
||||||
|
-ftb-zstyle -a debug-command debug_command && {
|
||||||
|
${(eX)debug_command} $fzf_flags
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
print -rl -- $_ftb_compcap > $tmp_dir/compcap.$$
|
||||||
|
print -rl -- $_ftb_groups > $tmp_dir/groups.$$
|
||||||
|
print -r -- ${ftb_preview_init/{f}/\$1} > $tmp_dir/ftb_preview_init.$$
|
||||||
|
|
||||||
|
binds=${binds//{_FTB_INIT_}/. $tmp_dir/ftb_preview_init.$$ {f} $'\n'}
|
||||||
|
|
||||||
|
local -i header_lines=$#_ftb_headers
|
||||||
|
local -i lines=$(( $#_ftb_compcap + fzf_pad + header_lines ))
|
||||||
|
local reload_command="$commands[zsh] -f $FZF_TAB_HOME/lib/ftb-switch-group $$ $header_lines $tmp_dir"
|
||||||
|
|
||||||
|
# detect if we will use tmux popup
|
||||||
|
local use_tmux_popup=0
|
||||||
|
if [[ $fzf_command == "ftb-tmux-popup" ]]; then
|
||||||
|
use_tmux_popup=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( ! use_tmux_popup )); then
|
||||||
|
# fzf will cause the current line to refresh, so move the cursor down.
|
||||||
|
echoti cud1 >/dev/tty
|
||||||
|
# reset cursor before call fzf
|
||||||
|
echoti cnorm >/dev/tty 2>/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat > $tmp_dir/completions.$$
|
||||||
|
|
||||||
|
local dd='gdd'
|
||||||
|
if (( ${+commands[$dd]} == 0 )) ; then
|
||||||
|
dd='dd'
|
||||||
|
fi
|
||||||
|
if (( ${+commands[$dd]} == 0 )) ; then
|
||||||
|
dd='true' # nop if dd is not installed
|
||||||
|
fi
|
||||||
|
|
||||||
|
_ftb_query="${_ftb_query}$(command "$dd" bs=1G count=1 status=none iflag=nonblock < /dev/tty 2>/dev/null)" || true
|
||||||
|
|
||||||
|
local fzf_default_opts=''
|
||||||
|
if [[ "$use_fzf_default_opts" == "yes" ]]; then
|
||||||
|
fzf_default_opts=$FZF_DEFAULT_OPTS
|
||||||
|
fi
|
||||||
|
|
||||||
|
FZF_DEFAULT_OPTS=$fzf_default_opts SHELL=$ZSH_NAME $fzf_command \
|
||||||
|
--ansi \
|
||||||
|
--bind=$binds \
|
||||||
|
--bind="${switch_group[1]}:reload($reload_command -1),${switch_group[2]}:reload($reload_command 1)" \
|
||||||
|
--cycle \
|
||||||
|
--delimiter='\x00' \
|
||||||
|
--expect=$continuous_trigger,$print_query,$accept_line \
|
||||||
|
--header-lines=$header_lines \
|
||||||
|
--height=${FZF_TMUX_HEIGHT:=$(( min(max(lines, fzf_min_height), LINES / 3 * 2) ))} \
|
||||||
|
--layout=reverse \
|
||||||
|
--multi \
|
||||||
|
--nth=2,3 \
|
||||||
|
--print-query \
|
||||||
|
--query=$_ftb_query \
|
||||||
|
--tiebreak=begin \
|
||||||
|
${fzf_preview:+--preview=${ftb_preview_init/{f}/'{f}'}$fzf_preview} \
|
||||||
|
$fzf_flags < $tmp_dir/completions.$$ || ret=$?
|
||||||
|
|
||||||
|
if (( ! use_tmux_popup )); then
|
||||||
|
echoti civis >/dev/tty 2>/dev/null
|
||||||
|
echoti cuu1 >/dev/tty
|
||||||
|
fi
|
||||||
|
|
||||||
|
command rm $tmp_dir/*.$$ 2>/dev/null
|
||||||
|
return $ret
|
||||||
113
eggs/zsh/config/fzf-tab/lib/-ftb-generate-complist
Normal file
113
eggs/zsh/config/fzf-tab/lib/-ftb-generate-complist
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#!/hint/zsh
|
||||||
|
|
||||||
|
local dsuf dpre k _v filepath first_word show_group default_color prefix bs=$'\b'
|
||||||
|
local -a list_colors group_colors tcandidates reply match mbegin mend
|
||||||
|
local -i same_word=1 colorful=0
|
||||||
|
local -Ua duplicate_groups=()
|
||||||
|
local -A word_map=()
|
||||||
|
|
||||||
|
(( $#_ftb_compcap == 0 )) && return
|
||||||
|
|
||||||
|
-ftb-zstyle -s show-group show_group || show_group=full
|
||||||
|
-ftb-zstyle -s default-color default_color || default_color=''
|
||||||
|
-ftb-zstyle -s prefix prefix || {
|
||||||
|
zstyle -m ':completion:*:descriptions' format '*' && prefix='·'
|
||||||
|
}
|
||||||
|
-ftb-zstyle -a group-colors group_colors || group_colors=($_ftb_group_colors)
|
||||||
|
zstyle -a ":completion:$_ftb_curcontext" list-colors list_colors
|
||||||
|
|
||||||
|
# init colorize
|
||||||
|
if (( $+builtins[fzf-tab-candidates-generate] )); then
|
||||||
|
fzf-tab-candidates-generate -i list_colors
|
||||||
|
else
|
||||||
|
local -A namecolors=(${(@s:=:)${(@s.:.)list_colors}:#[[:alpha:]][[:alpha:]]=*})
|
||||||
|
local -A modecolors=(${(@Ms:=:)${(@s.:.)list_colors}:#[[:alpha:]][[:alpha:]]=*})
|
||||||
|
(( $#namecolors == 0 && $#modecolors == 0 )) && list_colors=()
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( $#_ftb_groups == 1 )); then
|
||||||
|
-ftb-zstyle -m single-group prefix || prefix=''
|
||||||
|
-ftb-zstyle -m single-group color || group_colors=("$default_color")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( $+builtins[fzf-tab-candidates-generate] )); then
|
||||||
|
fzf-tab-candidates-generate
|
||||||
|
else
|
||||||
|
for k _v in "${(@ps:\2:)_ftb_compcap}"; do
|
||||||
|
local -A v=("${(@0)_v}")
|
||||||
|
[[ $v[word] == ${first_word:=$v[word]} ]] || same_word=0
|
||||||
|
|
||||||
|
# add character and color to describe the type of the files
|
||||||
|
dsuf='' dpre=''
|
||||||
|
if (( $+v[realdir] )); then
|
||||||
|
filepath=$v[realdir]${(Q)v[word]}
|
||||||
|
if [[ -d $filepath ]]; then
|
||||||
|
dsuf=/
|
||||||
|
fi
|
||||||
|
# add color and resolve symlink if have list-colors
|
||||||
|
# detail: http://zsh.sourceforge.net/Doc/Release/Zsh-Modules.html#The-zsh_002fcomplist-Module
|
||||||
|
if (( $#list_colors )) && [[ -a $filepath || -L $filepath ]]; then
|
||||||
|
-ftb-colorize $filepath
|
||||||
|
colorful=1
|
||||||
|
elif [[ -L $filepath ]]; then
|
||||||
|
dsuf=@
|
||||||
|
fi
|
||||||
|
if [[ $options[list_types] == off ]]; then
|
||||||
|
dsuf=''
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# add color to description if they have group index
|
||||||
|
if (( $+v[group] )); then
|
||||||
|
local color=$group_colors[$v[group]]
|
||||||
|
# add a hidden group index at start of string to keep group order when sorting
|
||||||
|
# first group index is for builtin sort, sencond is for GNU sort
|
||||||
|
tcandidates+=$v[group]$'\b'$color$prefix$dpre$'\0'$v[group]$'\b'$k$'\0'$dsuf
|
||||||
|
else
|
||||||
|
tcandidates+=$default_color$dpre$'\0'$k$'\0'$dsuf
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check group with duplicate member
|
||||||
|
if [[ $show_group == brief ]]; then
|
||||||
|
if (( $+word_map[$v[word]] && $+v[group] )); then
|
||||||
|
duplicate_groups+=$v[group] # add this group
|
||||||
|
duplicate_groups+=$word_map[$v[word]] # add previous group
|
||||||
|
fi
|
||||||
|
word_map[$v[word]]=$v[group]
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
(( same_word )) && tcandidates[2,-1]=()
|
||||||
|
|
||||||
|
# sort and remove sort group or other index
|
||||||
|
zstyle -T ":completion:$_ftb_curcontext" sort
|
||||||
|
if (( $? != 1 )); then
|
||||||
|
if (( colorful )); then
|
||||||
|
# if enable list_colors, we should skip the first field
|
||||||
|
if [[ ${commands[sort]:A:t} != (|busybox*) ]]; then
|
||||||
|
# this is faster but doesn't work if `find` is from busybox
|
||||||
|
tcandidates=(${(f)"$(command sort -u -t '\0' -k 2 <<< ${(pj:\n:)tcandidates})"})
|
||||||
|
else
|
||||||
|
# slower but portable
|
||||||
|
tcandidates=(${(@o)${(@)tcandidates:/(#b)([^$'\0']#)$'\0'(*)/$match[2]$'\0'$match[1]}})
|
||||||
|
tcandidates=(${(@)tcandidates/(#b)(*)$'\0'([^$'\0']#)/$match[2]$'\0'$match[1]})
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
tcandidates=("${(@o)tcandidates}")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
typeset -gUa _ftb_complist=("${(@)tcandidates//[0-9]#$bs}")
|
||||||
|
|
||||||
|
# hide needless group
|
||||||
|
if (( $#_ftb_groups )); then
|
||||||
|
local i to_hide indexs=({1..$#_ftb_groups})
|
||||||
|
case $show_group in
|
||||||
|
brief) to_hide=(${indexs:|duplicate_groups}) ;;
|
||||||
|
none) to_hide=($indexs) ;;
|
||||||
|
esac
|
||||||
|
for i in $to_hide; do
|
||||||
|
# NOTE: _ftb_groups is unique array
|
||||||
|
_ftb_groups[i]="__hide__$i"
|
||||||
|
done
|
||||||
|
fi
|
||||||
40
eggs/zsh/config/fzf-tab/lib/-ftb-generate-header
Normal file
40
eggs/zsh/config/fzf-tab/lib/-ftb-generate-header
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#!/hint/zsh
|
||||||
|
|
||||||
|
typeset -ga _ftb_headers=()
|
||||||
|
local i tmp group_colors
|
||||||
|
local -i mlen=0 len=0
|
||||||
|
|
||||||
|
# Keep ${group} value for preview, binds, etc without displaying group headers
|
||||||
|
if { -ftb-zstyle -m show-group "quiet" }; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( $#_ftb_groups == 1 )) && { ! -ftb-zstyle -m single-group "header" }; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# calculate the max column width
|
||||||
|
for i in $_ftb_groups; do
|
||||||
|
(( $#i > mlen )) && mlen=$#i
|
||||||
|
done
|
||||||
|
mlen+=1
|
||||||
|
|
||||||
|
-ftb-zstyle -a group-colors group_colors || group_colors=($_ftb_group_colors)
|
||||||
|
|
||||||
|
for (( i=1; i<=$#_ftb_groups; i++ )); do
|
||||||
|
[[ $_ftb_groups[i] == "__hide__"* ]] && continue
|
||||||
|
|
||||||
|
if (( len + $#_ftb_groups[i] > COLUMNS - 5 )); then
|
||||||
|
_ftb_headers+=$tmp
|
||||||
|
tmp='' && len=0
|
||||||
|
fi
|
||||||
|
if (( len + mlen > COLUMNS - 5 )); then
|
||||||
|
# the last column doesn't need padding
|
||||||
|
_ftb_headers+=$tmp$group_colors[i]$_ftb_groups[i]$'\033[00m'
|
||||||
|
tmp='' && len=0
|
||||||
|
else
|
||||||
|
tmp+=$group_colors[i]${(r:$mlen:)_ftb_groups[i]}$'\033[00m'
|
||||||
|
len+=$mlen
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
(( $#tmp )) && _ftb_headers+=$tmp
|
||||||
40
eggs/zsh/config/fzf-tab/lib/-ftb-generate-query
Normal file
40
eggs/zsh/config/fzf-tab/lib/-ftb-generate-query
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#!/hint/zsh
|
||||||
|
|
||||||
|
if zmodload -s zsh/pcre; then
|
||||||
|
setopt localoptions rematch_pcre
|
||||||
|
fi
|
||||||
|
|
||||||
|
local key qtype tmp query_string
|
||||||
|
typeset -g _ftb_query=
|
||||||
|
-ftb-zstyle -a query-string query_string || query_string=(prefix input first)
|
||||||
|
for qtype in $query_string; do
|
||||||
|
if [[ $qtype == prefix ]]; then
|
||||||
|
# find the longest common prefix among descriptions
|
||||||
|
local -a keys=(${_ftb_compcap%$'\2'*})
|
||||||
|
tmp=$keys[1]
|
||||||
|
local MATCH match mbegin mend prefix=(${(s::)tmp})
|
||||||
|
for key in ${keys:1}; do
|
||||||
|
(( $#tmp )) || break
|
||||||
|
[[ $key == $tmp* ]] && continue
|
||||||
|
# interpose characters from the current common prefix and $key and see how
|
||||||
|
# many pairs of equal characters we get at the start of the resulting string
|
||||||
|
[[ ${(j::)${${(s::)key[1,$#tmp]}:^prefix}} =~ '^(((.)\3)*)' ]]
|
||||||
|
# truncate common prefix and maintain loop invariant: ${(s::)tmp} == $prefix
|
||||||
|
tmp[$#MATCH/2+1,-1]=""
|
||||||
|
prefix[$#MATCH/2+1,-1]=()
|
||||||
|
done
|
||||||
|
elif [[ $qtype == input ]]; then
|
||||||
|
local fv=${_ftb_compcap[1]#*$'\2'}
|
||||||
|
local -A v=("${(@0)fv}")
|
||||||
|
tmp=$v[PREFIX]
|
||||||
|
if (( $RBUFFER[(i)$v[SUFFIX]] != 1 )); then
|
||||||
|
tmp=${tmp/%$v[SUFFIX]}
|
||||||
|
fi
|
||||||
|
tmp=${${tmp#$v[hpre]}#$v[apre]}
|
||||||
|
fi
|
||||||
|
if (( $query_string[(I)longest] )); then
|
||||||
|
(( $#tmp > $#_ftb_query )) && _ftb_query=$tmp
|
||||||
|
elif [[ -n $tmp ]]; then
|
||||||
|
_ftb_query=$tmp && break
|
||||||
|
fi
|
||||||
|
done
|
||||||
10
eggs/zsh/config/fzf-tab/lib/-ftb-version
Normal file
10
eggs/zsh/config/fzf-tab/lib/-ftb-version
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/hint/zsh
|
||||||
|
emulate -L zsh -o pipe_fail
|
||||||
|
pushd -q $FZF_TAB_HOME
|
||||||
|
if [[ -d .git ]]; then
|
||||||
|
git describe --long --tags --abbrev=7 2>/dev/null | sed 's/\([^-]*-g\)/r\1/;s/-/./g' \
|
||||||
|
|| printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short=7 HEAD)"
|
||||||
|
else
|
||||||
|
echo unknown
|
||||||
|
fi
|
||||||
|
popd -q
|
||||||
38
eggs/zsh/config/fzf-tab/lib/ftb-switch-group
Normal file
38
eggs/zsh/config/fzf-tab/lib/ftb-switch-group
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/hint/zsh
|
||||||
|
emulate -L zsh -o extended_glob
|
||||||
|
|
||||||
|
zmodload zsh/mapfile
|
||||||
|
|
||||||
|
# receive arguments
|
||||||
|
local pid=$1 header_lines=$2 tmp_dir=$3 offset=$@[-1]
|
||||||
|
|
||||||
|
# read completion list
|
||||||
|
local -a list=(${(f)mapfile[$tmp_dir/completions.$pid]})
|
||||||
|
|
||||||
|
# get total group count
|
||||||
|
if (( $#list > 10000 )); then
|
||||||
|
local -Ua total=(${(f)"$(print -l ${list:$header_lines} | grep -a -oP '^\x1b\[[0-9;]*m')"})
|
||||||
|
else
|
||||||
|
local -Ua total=(${(M)${list:$header_lines}#$'\x1b['[0-9;]#*m})
|
||||||
|
fi
|
||||||
|
|
||||||
|
# get current group index, start from 2
|
||||||
|
local current=2
|
||||||
|
if [[ -f $tmp_dir/current-group.$pid ]]; then
|
||||||
|
current=$(( $(<$tmp_dir/current-group.$pid) + offset ))
|
||||||
|
fi
|
||||||
|
(( current > $#total )) && current=1
|
||||||
|
(( current == 0 )) && current=$#total
|
||||||
|
echo $current > $tmp_dir/current-group.$pid
|
||||||
|
|
||||||
|
# print headers
|
||||||
|
if (( header_lines != 0 )); then
|
||||||
|
print -l ${list[1,header_lines]/${total[current]}/$'\x1b[1m'}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# print current group
|
||||||
|
if (( $#list > 10000 )); then
|
||||||
|
print -l ${list:$header_lines} | grep -a -F "${total[current]}"
|
||||||
|
else
|
||||||
|
print -l ${(M)${list:$header_lines}:#${total[current]}*}
|
||||||
|
fi
|
||||||
100
eggs/zsh/config/fzf-tab/lib/ftb-tmux-popup
Executable file
100
eggs/zsh/config/fzf-tab/lib/ftb-tmux-popup
Executable file
@ -0,0 +1,100 @@
|
|||||||
|
#!/hint/zsh
|
||||||
|
# Show results with tmux popup
|
||||||
|
# Example usage:
|
||||||
|
# zstyle ':fzf-tab:*' fzf-command ftb-tmux-popup
|
||||||
|
# zstyle ':fzf-tab:*' popup-pad 0 0
|
||||||
|
# It can also be used as a standalone tool, like:
|
||||||
|
# ls | ftb-tmux-popup
|
||||||
|
emulate -L zsh -o extended_glob
|
||||||
|
|
||||||
|
# import math functions (only if they're not already defined)
|
||||||
|
if (( ! $+functions[zsh_math_func_min] )); then
|
||||||
|
autoload -Uz zmathfunc
|
||||||
|
zmathfunc
|
||||||
|
fi
|
||||||
|
|
||||||
|
: ${tmp_dir:=${TMPPREFIX:-/tmp/zsh}-fzf-tab-$USER}
|
||||||
|
|
||||||
|
# fallback to fzf if it is not running in tmux
|
||||||
|
if (( ! $+TMUX_PANE )); then
|
||||||
|
fzf $@
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local ret=0
|
||||||
|
|
||||||
|
local -a fzf_opts=($@)
|
||||||
|
fzf_opts=(${${fzf_opts/--height*}/--layout*})
|
||||||
|
|
||||||
|
# get position of cursor and size of window
|
||||||
|
local -a tmp=($(command tmux display-message -p "#{pane_top} #{cursor_y} #{pane_left} #{cursor_x} #{window_height} #{window_width} #{status} #{status-position}"))
|
||||||
|
local cursor_y=$((tmp[1] + tmp[2])) cursor_x=$((tmp[3] + tmp[4])) window_height=$tmp[5] window_width=$tmp[6] window_top=0
|
||||||
|
|
||||||
|
if [[ $tmp[8] == 'top' ]]; then
|
||||||
|
window_top=$tmp[7]
|
||||||
|
cursor_y=$((cursor_y + window_top))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if not called by fzf-tab
|
||||||
|
if (( ! $+IN_FZF_TAB )); then
|
||||||
|
[[ -d $tmp_dir ]] || mkdir -p $tmp_dir
|
||||||
|
cat > $tmp_dir/completions.$$
|
||||||
|
fi
|
||||||
|
|
||||||
|
local text REPLY comp_lines comp_length length popup_pad popup_min_size
|
||||||
|
|
||||||
|
zstyle -a ":fzf-tab:$_ftb_curcontext" popup-pad popup_pad || popup_pad=(0 0)
|
||||||
|
zstyle -a ":fzf-tab:$_ftb_curcontext" popup-min-size popup_min_size || popup_min_size=(0 0)
|
||||||
|
|
||||||
|
# get the size of content, note we should remove all ANSI color code
|
||||||
|
comp_lines=$(( ${#${(f)mapfile[$tmp_dir/completions.$$]}} + $popup_pad[2] ))
|
||||||
|
if (( comp_lines <= 500 )); then
|
||||||
|
comp_length=0
|
||||||
|
for line in ${(f)mapfile[$tmp_dir/completions.$$]}; do
|
||||||
|
length=${(m)#${(S)line//$'\x1b['[0-9]#*m}}
|
||||||
|
(( length >= comp_length )) && comp_length=$length
|
||||||
|
done
|
||||||
|
else
|
||||||
|
# FIXME: can't get the correct width of CJK characters.
|
||||||
|
comp_length=$( command perl -ne 's/\x1b\[[0-9;]*m//g;s/\x00//g; $m= length() if $m < length(); END { print $m }' < $tmp_dir/completions.$$ )
|
||||||
|
fi
|
||||||
|
comp_length=$(( comp_length + $popup_pad[1] ))
|
||||||
|
|
||||||
|
local popup_height popup_y popup_width popup_x adjust_height
|
||||||
|
|
||||||
|
# adjust the popup height if the fzf finder info style is not default
|
||||||
|
if (( $fzf_opts[(I)--info=*(hidden|inline)*] > 0 )); then
|
||||||
|
adjust_height=-1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# calculate the popup height and y position
|
||||||
|
if (( cursor_y * 2 > window_height )); then
|
||||||
|
# show above the cursor
|
||||||
|
popup_height=$(( min(max(comp_lines + 4, popup_min_size[2]), cursor_y - window_top) + adjust_height ))
|
||||||
|
popup_y=$cursor_y
|
||||||
|
if zstyle -T ":fzf-tab:$_ftb_curcontext" popup-smart-tab; then
|
||||||
|
fzf_opts+=(--bind=tab:up,btab:down)
|
||||||
|
fi
|
||||||
|
fzf_opts+=(--layout=default)
|
||||||
|
else
|
||||||
|
# show below the cursor
|
||||||
|
popup_height=$(( min(max(comp_lines + 4, popup_min_size[2]), window_height - cursor_y + window_top - 1) + adjust_height ))
|
||||||
|
popup_y=$(( cursor_y + popup_height + 1 ))
|
||||||
|
fzf_opts+=(--layout=reverse)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# calculate the popup width and x position
|
||||||
|
popup_width=$(( min(max(comp_length + 5, popup_min_size[1]), window_width) ))
|
||||||
|
popup_x=$(( cursor_x + popup_width > window_width ? window_width - popup_width : cursor_x ))
|
||||||
|
|
||||||
|
echo -E "env FZF_DEFAULT_OPTS=${(qq)FZF_DEFAULT_OPTS} SHELL=$ZSH_NAME $commands[fzf] ${(qq)fzf_opts[@]} < $tmp_dir/completions.$$ > $tmp_dir/result-$$" > $tmp_dir/fzf-$$
|
||||||
|
{
|
||||||
|
tmux popup -x $popup_x -y $popup_y \
|
||||||
|
-w $popup_width -h $popup_height \
|
||||||
|
-d $PWD -E ". $tmp_dir/fzf-$$" || ret=$?
|
||||||
|
echo -E "$(<$tmp_dir/result-$$)"
|
||||||
|
} always {
|
||||||
|
command rm $tmp_dir/*-$$
|
||||||
|
(( $+IN_FZF_TAB )) || command rm $tmp_dir/completions.$$
|
||||||
|
}
|
||||||
|
return $ret
|
||||||
21
eggs/zsh/config/fzf-tab/lib/zsh-ls-colors/LICENSE
Normal file
21
eggs/zsh/config/fzf-tab/lib/zsh-ls-colors/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Gamma
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
114
eggs/zsh/config/fzf-tab/lib/zsh-ls-colors/README.md
Normal file
114
eggs/zsh/config/fzf-tab/lib/zsh-ls-colors/README.md
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
# zsh-ls-colors
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
A zsh library to use `LS_COLORS` in scripts or other plugins.
|
||||||
|
|
||||||
|
For a simple demo, see the `demo` script in this repo.
|
||||||
|
|
||||||
|
For more advanced usage,
|
||||||
|
instructions are located at top of the source files for `from-mode` and `from-name`.
|
||||||
|
If a use case isn't adequately covered,
|
||||||
|
please open an issue!
|
||||||
|
|
||||||
|
## Using zsh-ls-colors in a plugin
|
||||||
|
|
||||||
|
You can use this as a submodule or a subtree.
|
||||||
|
|
||||||
|
### submodule:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Add (only once)
|
||||||
|
git submodule add git://github.com/xPMo/zsh-ls-colors.git ls-colors
|
||||||
|
git commit -m 'Add ls-colors as submodule'
|
||||||
|
|
||||||
|
# Update
|
||||||
|
cd ls-colors
|
||||||
|
git fetch
|
||||||
|
git checkout origin/master
|
||||||
|
cd ..
|
||||||
|
git commit ls-colors -m 'Update ls-colors to latest'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Subtree:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Initial add
|
||||||
|
git subtree add --prefix=ls-colors/ --squash -m 'Add ls-colors as a subtree' \
|
||||||
|
git://github.com/xPMo/zsh-ls-colors.git master
|
||||||
|
|
||||||
|
# Update
|
||||||
|
git subtree pull --prefix=ls-colors/ --squash -m 'Update ls-colors to latest' \
|
||||||
|
git://github.com/xPMo/zsh-ls-colors.git master
|
||||||
|
|
||||||
|
|
||||||
|
# Or, after adding a remote:
|
||||||
|
git remote add ls-colors git://github.com/xPMo/zsh-ls-colors.git
|
||||||
|
|
||||||
|
# Initial add
|
||||||
|
git subtree add --prefix=ls-colors/ --squash -m 'Add ls-colors as a subtree' ls-colors master
|
||||||
|
|
||||||
|
# Update
|
||||||
|
git subtree pull --prefix=ls-colors/ --squash -m 'Update ls-colors to latest' ls-colors master
|
||||||
|
```
|
||||||
|
|
||||||
|
### Function namespacing
|
||||||
|
|
||||||
|
Since functions are a public namespace,
|
||||||
|
this plugin allows you to customize the preifix for your plugin:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
# load functions as my-lscolors::{init,match-by,from-name,from-mode}
|
||||||
|
source ${0:h}/ls-colors/ls-colors.zsh my-lscolors
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameter namespacing
|
||||||
|
|
||||||
|
While indirect parameter expansion exists with `${(P)var}`,
|
||||||
|
it doesn't play nicely with array parameters.
|
||||||
|
|
||||||
|
There are multiple strategies to prevent unnecessary re-parsing:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
# Call once when loading.
|
||||||
|
# Pollutes global namespace but prevents re-parsing
|
||||||
|
ls-color::init
|
||||||
|
```
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
# Don't call init at all and only use ::match-by.
|
||||||
|
# Doesn't pollute global namespace but reparses LS_COLORS on every call
|
||||||
|
ls-color::match-by $file lstat
|
||||||
|
```
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
# Initialize within a scope with local parameters.
|
||||||
|
# Best for not polluting global namespace when multiple filenames need to be parsed.
|
||||||
|
(){
|
||||||
|
local -A namecolors modecolors
|
||||||
|
ls-color::init
|
||||||
|
|
||||||
|
for arg; do
|
||||||
|
...
|
||||||
|
done
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
# Serialize:
|
||||||
|
typeset -g LS_COLORS_CACHE_FILE=$(mktemp)
|
||||||
|
(){
|
||||||
|
local -A namecolors modecolors
|
||||||
|
ls-color::init
|
||||||
|
typeset -p modecolors namecolors >| $LS_COLORS_CACHE_FILE
|
||||||
|
zcompile $LS_COLORS_CACHE_FILE
|
||||||
|
}
|
||||||
|
|
||||||
|
my-function(){
|
||||||
|
local -A namecolors modecolors
|
||||||
|
source $LS_COLORS_CACHE_FILE
|
||||||
|
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
65
eggs/zsh/config/fzf-tab/lib/zsh-ls-colors/demo
Executable file
65
eggs/zsh/config/fzf-tab/lib/zsh-ls-colors/demo
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
# set $0 (ref: zdharma.org/Zsh-100-Commits-Club/Zsh-Plugin-Standard.html#zero-handling)
|
||||||
|
0="${${ZERO:-${0:#$ZSH_ARGZERO}}:-${(%):-%N}}"
|
||||||
|
0="${${(M)0:#/*}:-$PWD/$0}"
|
||||||
|
|
||||||
|
# load library functions
|
||||||
|
source ls-colors.zsh ''
|
||||||
|
|
||||||
|
# to name the functions with a different namespace
|
||||||
|
# call source with a different argument
|
||||||
|
#source my-plugin::ls
|
||||||
|
|
||||||
|
# init (sets modecolors and namecolors)
|
||||||
|
# You have options. Either you can pollute global namespace:
|
||||||
|
ls-color::init
|
||||||
|
# Or you can have ::match-by re-parse colors on every call
|
||||||
|
: # (do nothing)
|
||||||
|
# Or if you have multiple calls, you can parse colors once for a scope:
|
||||||
|
(){
|
||||||
|
local -A modecolors namecolors
|
||||||
|
ls-color::init
|
||||||
|
|
||||||
|
for arg; do
|
||||||
|
ls-color::match-by $arg lstat
|
||||||
|
: do something else
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# colors can also be added for other globs after init as well:
|
||||||
|
namecolors[*.md]='01' # bold markdown files
|
||||||
|
|
||||||
|
# EXTENDED_GLOB is enabled when matching, so things like this are possible:
|
||||||
|
namecolors[(#i)(*/|)license(|.*)]='04' # underline LICENSE, or license.txt, or similar
|
||||||
|
|
||||||
|
local file reply
|
||||||
|
# color each file in the argument list
|
||||||
|
for file; do
|
||||||
|
ls-color::match-by $file all
|
||||||
|
# point to symlink resolution if it exists
|
||||||
|
print '\e['$reply[1]'m'$file'\e[0m'${reply[2]:+' → \e['$reply[3]'m'$reply[2]'\e[0m'}
|
||||||
|
done
|
||||||
|
|
||||||
|
# =======================
|
||||||
|
# Alternate manual method:
|
||||||
|
for file; do
|
||||||
|
ls-color::match-by $file lstat follow
|
||||||
|
if [[ $reply[2] ]]; then
|
||||||
|
# This is a symlink
|
||||||
|
symlink_color=$reply[1]
|
||||||
|
# If broken, use link color for destination
|
||||||
|
resolved_color=$reply[1]
|
||||||
|
resolved=$reply[2]
|
||||||
|
if [[ -e $file ]]; then
|
||||||
|
# Not broken, update destination color
|
||||||
|
ls-color::match-by $file stat
|
||||||
|
resolved_color=$reply[1]
|
||||||
|
fi
|
||||||
|
print '\e['$symlink_color'm'$file'\e[0m → \e['$resolved_color'm'$resolved'\e[0m'
|
||||||
|
else
|
||||||
|
# This is not a symlink
|
||||||
|
print '\e['$reply[1]'m'$file'\e[0m'
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
186
eggs/zsh/config/fzf-tab/lib/zsh-ls-colors/ls-colors.zsh
Normal file
186
eggs/zsh/config/fzf-tab/lib/zsh-ls-colors/ls-colors.zsh
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
|
||||||
|
# set the prefix for all functions
|
||||||
|
local pfx=${1:-'ls-color'}
|
||||||
|
|
||||||
|
# {{{ From mode
|
||||||
|
# Usage:
|
||||||
|
# $1: filename
|
||||||
|
# $2: The value of struct stat st_mode
|
||||||
|
# If empty, modecolors lookup will be skipped
|
||||||
|
# $3: (If symlink) The value of struct stat st_mode
|
||||||
|
# for the target of $1's symlink. If unset,
|
||||||
|
# interpret as a broken link.
|
||||||
|
# Sets REPLY to the console code
|
||||||
|
${pfx}::from-mode () {
|
||||||
|
|
||||||
|
emulate -L zsh
|
||||||
|
setopt cbases octalzeroes extendedglob
|
||||||
|
|
||||||
|
[[ -z $2 ]] && return 1
|
||||||
|
|
||||||
|
local -i reg=0
|
||||||
|
local -a codes
|
||||||
|
|
||||||
|
local -i st_mode=$(($2))
|
||||||
|
# See man 7 inode for more info
|
||||||
|
# file type
|
||||||
|
case $(( st_mode & 0170000 )) in
|
||||||
|
$(( 0140000 )) ) codes=( $modecolors[so] ) ;;
|
||||||
|
$(( 0120000 )) ) # symlink, special handling
|
||||||
|
if ! (($+3)); then
|
||||||
|
REPLY=$modecolors[or]
|
||||||
|
elif [[ $modecolors[ln] = target ]]; then
|
||||||
|
"$0" "$1" "${@:3}"
|
||||||
|
else
|
||||||
|
REPLY=$modecolors[ln]
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
$(( 0100000 )) ) codes=( ); reg=1 ;; # regular file
|
||||||
|
$(( 0060000 )) ) codes=( $modecolors[bd] ) ;;
|
||||||
|
$(( 0040000 )) ) codes=( $modecolors[di] ) ;;
|
||||||
|
$(( 0020000 )) ) codes=( $modecolors[cd] ) ;;
|
||||||
|
$(( 0010000 )) ) codes=( $modecolors[pi] ) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# setuid/setgid/sticky/other-writable
|
||||||
|
(( st_mode & 04000 )) && codes+=( $modecolors[su] )
|
||||||
|
(( st_mode & 02000 )) && codes+=( $modecolors[sg] )
|
||||||
|
(( ! reg )) && case $(( st_mode & 01002 )) in
|
||||||
|
# sticky
|
||||||
|
$(( 01000 )) ) codes+=( $modecolors[st] ) ;;
|
||||||
|
# other-writable
|
||||||
|
$(( 00002 )) ) codes+=( $modecolors[ow] ) ;;
|
||||||
|
# other-writable and sticky
|
||||||
|
$(( 01002 )) ) codes+=( $modecolors[tw] ) ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# executable
|
||||||
|
if (( ! $#codes )); then
|
||||||
|
(( st_mode & 0111 )) && codes+=( $modecolors[ex] )
|
||||||
|
fi
|
||||||
|
|
||||||
|
# return nonzero if no matching code
|
||||||
|
[[ ${REPLY::=${(j:;:)codes}} ]]
|
||||||
|
} # }}}
|
||||||
|
# {{{ From name
|
||||||
|
# Usage:
|
||||||
|
# $1: filename
|
||||||
|
#
|
||||||
|
# Sets REPLY to the console code
|
||||||
|
${pfx}::from-name () {
|
||||||
|
|
||||||
|
emulate -L zsh
|
||||||
|
setopt extendedglob
|
||||||
|
|
||||||
|
# Return non-zero if no keys match
|
||||||
|
[[ ${REPLY::=$namecolors[(k)$1]} ]]
|
||||||
|
} # }}}
|
||||||
|
# {{{ Init
|
||||||
|
# WARNING: initializes namecolors and modecolors in global scope
|
||||||
|
${pfx}::init () {
|
||||||
|
emulate -L zsh
|
||||||
|
|
||||||
|
# Use $1 if provided, otherwise use LS_COLORS
|
||||||
|
# Use LSCOLORS on BSD
|
||||||
|
local LS_COLORS=${1:-${LS_COLORS:-$LSCOLORS}}
|
||||||
|
|
||||||
|
# read in LS_COLORS
|
||||||
|
typeset -gA namecolors=(${(@s:=:)${(@s.:.)LS_COLORS}:#[[:alpha:]][[:alpha:]]=*})
|
||||||
|
typeset -gA modecolors=(${(@Ms:=:)${(@s.:.)LS_COLORS}:#[[:alpha:]][[:alpha:]]=*})
|
||||||
|
}
|
||||||
|
# }}}
|
||||||
|
# {{{ Match by
|
||||||
|
# Usage:
|
||||||
|
# $1: filename
|
||||||
|
# Optional (must be $2): g[lobal]: Use existing stat | lstat in parent scope
|
||||||
|
# ${@:2}: Append to reply:
|
||||||
|
# - l[stat] : Look up using lstat (don't follow symlink), if empty match name
|
||||||
|
# - s[tat] : Look up using stat (do follow symlink), if empty match name
|
||||||
|
# - n[ame] : Only match name
|
||||||
|
# - f[ollow]: Get resolution path of symlink
|
||||||
|
# - L[stat] : Same as above but don't match name
|
||||||
|
# - S[tat] : Same as above but don't match name
|
||||||
|
# - a[ll] : If a broken symlink: lstat follow lstat
|
||||||
|
# : If a symlink : lstat follow stat
|
||||||
|
# : Otherwise : lstat
|
||||||
|
# - A[ll] : If a broken symlink: Lstat follow Lstat
|
||||||
|
# : If a symlink : Lstat follow Stat
|
||||||
|
# : Otherwise : Lstat
|
||||||
|
#
|
||||||
|
# or returns non-zero
|
||||||
|
${pfx}::match-by () {
|
||||||
|
emulate -L zsh
|
||||||
|
setopt extendedglob cbases octalzeroes
|
||||||
|
|
||||||
|
local arg REPLY name=$1 pfx=${0%::match-by}
|
||||||
|
shift
|
||||||
|
|
||||||
|
# init in local scope if not using global params
|
||||||
|
if ! [[ -v namecolors && -v modecolors ]]; then
|
||||||
|
local -A namecolors modecolors
|
||||||
|
${pfx}::init
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${1:l} = (g|global) ]]; then
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
local -a stat lstat
|
||||||
|
declare -ga reply=()
|
||||||
|
fi
|
||||||
|
|
||||||
|
zmodload -F zsh/stat b:zstat
|
||||||
|
for arg; do
|
||||||
|
case ${arg[1]:l} in
|
||||||
|
n|name)
|
||||||
|
${pfx}::from-name $name
|
||||||
|
reply+=("$REPLY")
|
||||||
|
;;
|
||||||
|
l|lstat)
|
||||||
|
(($#lstat)) || zstat -A lstat -L $name || return 1
|
||||||
|
if ((lstat[3] & 0170000 )); then
|
||||||
|
# follow symlink
|
||||||
|
(($#stat)) || zstat -A stat $name 2>/dev/null
|
||||||
|
fi
|
||||||
|
${pfx}::from-mode "$name" "$lstat[3]" $stat[3]
|
||||||
|
if [[ $REPLY || ${2[1]} = L ]]; then
|
||||||
|
reply+=("$REPLY")
|
||||||
|
else # fall back to name
|
||||||
|
"$0" "$name" g n
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
s|stat)
|
||||||
|
(($#stat)) || zstat -A stat $name || return 1
|
||||||
|
${pfx}::from-mode $name $stat[3]
|
||||||
|
reply+=("$REPLY")
|
||||||
|
if [[ $REPLY || ${arg[1]} = S ]]; then
|
||||||
|
reply+=("$REPLY")
|
||||||
|
else # fall back to name
|
||||||
|
"$0" "$name" g n
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
f|follow)
|
||||||
|
(($#lstat)) || zstat -A lstat -L $name || return 1
|
||||||
|
reply+=("$lstat[14]")
|
||||||
|
;;
|
||||||
|
a|all)
|
||||||
|
# Match case
|
||||||
|
"$0" "$name" g ${${${arg[1]%a}:+L}:-l}
|
||||||
|
# won't append if empty
|
||||||
|
reply+=($lstat[14])
|
||||||
|
# $stat[14] will be empty if not a symlink
|
||||||
|
if [[ $lstat[14] ]]; then
|
||||||
|
if [[ -e $name ]]; then
|
||||||
|
"$0" "$name" g ${${${arg[1]%a}:+S}:-s}
|
||||||
|
else
|
||||||
|
reply+=($reply[-2])
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*) return 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
# }}}
|
||||||
|
# vim: set foldmethod=marker:
|
||||||
16
eggs/zsh/config/fzf-tab/modules/.cvsignore
Normal file
16
eggs/zsh/config/fzf-tab/modules/.cvsignore
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
Makefile
|
||||||
|
META-FAQ
|
||||||
|
config.cache
|
||||||
|
config.h
|
||||||
|
config.h.in
|
||||||
|
config.log
|
||||||
|
config.modules
|
||||||
|
config.modules.sh
|
||||||
|
config.status
|
||||||
|
configure
|
||||||
|
cscope.out
|
||||||
|
stamp-h
|
||||||
|
stamp-h.in
|
||||||
|
autom4te.cache
|
||||||
|
*.swp
|
||||||
|
.git
|
||||||
4
eggs/zsh/config/fzf-tab/modules/.distfiles
Normal file
4
eggs/zsh/config/fzf-tab/modules/.distfiles
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
DISTFILES_SRC='
|
||||||
|
META-FAQ
|
||||||
|
configure config.h.in stamp-h.in
|
||||||
|
'
|
||||||
15
eggs/zsh/config/fzf-tab/modules/.editorconfig
Normal file
15
eggs/zsh/config/fzf-tab/modules/.editorconfig
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Top-most editorconfig file
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
tab_width = 8
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[ChangeLog]
|
||||||
|
indent_size = 8
|
||||||
|
|
||||||
|
[*.[ch]]
|
||||||
|
indent_size = 4
|
||||||
1
eggs/zsh/config/fzf-tab/modules/.gitignore
vendored
Normal file
1
eggs/zsh/config/fzf-tab/modules/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
zsh/
|
||||||
7
eggs/zsh/config/fzf-tab/modules/.preconfig
Executable file
7
eggs/zsh/config/fzf-tab/modules/.preconfig
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
autoconf
|
||||||
|
autoheader
|
||||||
|
echo > stamp-h.in
|
||||||
539
eggs/zsh/config/fzf-tab/modules/Src/fzftab.c
Normal file
539
eggs/zsh/config/fzf-tab/modules/Src/fzftab.c
Normal file
@ -0,0 +1,539 @@
|
|||||||
|
#include "fzftab.mdh"
|
||||||
|
#include "fzftab.pro"
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
const char* get_color(char* file, const struct stat* sb);
|
||||||
|
const char* colorize_from_mode(char* file, const struct stat* sb);
|
||||||
|
const char* colorize_from_name(char* file);
|
||||||
|
int compile_patterns(char* nam, char** list_colors);
|
||||||
|
char* ftb_strcat(char* dst, int n, ...);
|
||||||
|
|
||||||
|
/* Indixes into the terminal string arrays. */
|
||||||
|
#define COL_NO 0
|
||||||
|
#define COL_FI 1
|
||||||
|
#define COL_DI 2
|
||||||
|
#define COL_LN 3
|
||||||
|
#define COL_PI 4
|
||||||
|
#define COL_SO 5
|
||||||
|
#define COL_BD 6
|
||||||
|
#define COL_CD 7
|
||||||
|
#define COL_OR 8
|
||||||
|
#define COL_MI 9
|
||||||
|
#define COL_SU 10
|
||||||
|
#define COL_SG 11
|
||||||
|
#define COL_TW 12
|
||||||
|
#define COL_OW 13
|
||||||
|
#define COL_ST 14
|
||||||
|
#define COL_EX 15
|
||||||
|
#define COL_LC 16
|
||||||
|
#define COL_RC 17
|
||||||
|
#define COL_EC 18
|
||||||
|
#define COL_TC 19
|
||||||
|
#define COL_SP 20
|
||||||
|
#define COL_MA 21
|
||||||
|
#define COL_HI 22
|
||||||
|
#define COL_DU 23
|
||||||
|
#define COL_SA 24
|
||||||
|
|
||||||
|
#define NUM_COLS 25
|
||||||
|
|
||||||
|
/* Names of the terminal strings. */
|
||||||
|
static char* colnames[] = { "no", "fi", "di", "ln", "pi", "so", "bd", "cd", "or", "mi", "su", "sg",
|
||||||
|
"tw", "ow", "st", "ex", "lc", "rc", "ec", "tc", "sp", "ma", "hi", "du", "sa", NULL };
|
||||||
|
|
||||||
|
/* Default values. */
|
||||||
|
static char* defcols[]
|
||||||
|
= { "0", "0", "1;31", "1;36", "33", "1;35", "1;33", "1;33", NULL, NULL, "37;41", "30;43",
|
||||||
|
"30;42", "34;42", "37;44", "1;32", "\033[", "m", NULL, "0", "0", "7", NULL, NULL, "0" };
|
||||||
|
|
||||||
|
static char* fzf_tab_module_version;
|
||||||
|
|
||||||
|
struct pattern {
|
||||||
|
Patprog pat;
|
||||||
|
char color[50];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int opt_list_type = OPT_INVALID;
|
||||||
|
static int pat_cnt = 0;
|
||||||
|
static struct pattern* name_color = NULL;
|
||||||
|
static char mode_color[NUM_COLS][20];
|
||||||
|
|
||||||
|
// TODO: use ZLS_COLORS ?
|
||||||
|
int compile_patterns(char* nam, char** list_colors)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
// clean old name_color and set pat_cnt = 0
|
||||||
|
if (pat_cnt != 0) {
|
||||||
|
while (--pat_cnt) {
|
||||||
|
freepatprog(name_color[pat_cnt].pat);
|
||||||
|
}
|
||||||
|
free(name_color);
|
||||||
|
}
|
||||||
|
// initialize mode_color with default value
|
||||||
|
for (i = 0; i < NUM_COLS; i++) {
|
||||||
|
if (defcols[i]) {
|
||||||
|
strcpy(mode_color[i], defcols[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// empty array, just return
|
||||||
|
if (list_colors == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// how many pattens?
|
||||||
|
while (list_colors[pat_cnt] != NULL) {
|
||||||
|
pat_cnt++;
|
||||||
|
}
|
||||||
|
name_color = zshcalloc(pat_cnt * sizeof(struct pattern));
|
||||||
|
|
||||||
|
for (i = 0; i < pat_cnt; i++) {
|
||||||
|
char* pat = ztrdup(list_colors[i]);
|
||||||
|
char* color = strrchr(pat, '=');
|
||||||
|
if (color == NULL)
|
||||||
|
continue;
|
||||||
|
*color = '\0';
|
||||||
|
tokenize(pat);
|
||||||
|
remnulargs(pat);
|
||||||
|
|
||||||
|
// mode=color
|
||||||
|
bool skip = false;
|
||||||
|
for (j = 0; j < NUM_COLS; j++) {
|
||||||
|
if (strpfx(colnames[j], list_colors[i])) {
|
||||||
|
strcpy(mode_color[j], color + 1);
|
||||||
|
name_color[i].pat = NULL;
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skip) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// name=color
|
||||||
|
if ((name_color[i].pat = patcompile(pat, PAT_ZDUP, NULL))) {
|
||||||
|
strcpy(name_color[i].color, color + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(pat);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char get_suffix(char* file, const struct stat* sb)
|
||||||
|
{
|
||||||
|
struct stat sb2;
|
||||||
|
mode_t filemode = sb->st_mode;
|
||||||
|
|
||||||
|
if (S_ISLNK(filemode))
|
||||||
|
if (strpfx(mode_color[COL_LN], "target")) {
|
||||||
|
if (stat(file, &sb2) == -1) {
|
||||||
|
return '@';
|
||||||
|
}
|
||||||
|
return get_suffix(file, &sb2);
|
||||||
|
} else {
|
||||||
|
return '@';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return file_type(filemode);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* get_color(char* file, const struct stat* sb)
|
||||||
|
{
|
||||||
|
// no list-colors, return NULL
|
||||||
|
if (pat_cnt == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
const char* ret;
|
||||||
|
if ((ret = colorize_from_mode(file, sb)) || (ret = colorize_from_name(file))) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return mode_color[COL_FI];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* colorize_from_name(char* file)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < pat_cnt; i++) {
|
||||||
|
if (name_color && name_color[i].pat && pattry(name_color[i].pat, file)) {
|
||||||
|
return name_color[i].color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* colorize_from_mode(char* file, const struct stat* sb)
|
||||||
|
{
|
||||||
|
struct stat sb2;
|
||||||
|
|
||||||
|
switch (sb->st_mode & S_IFMT) {
|
||||||
|
case S_IFDIR:
|
||||||
|
return mode_color[COL_DI];
|
||||||
|
case S_IFLNK: {
|
||||||
|
if (strpfx(mode_color[COL_LN], "target")) {
|
||||||
|
if (stat(file, &sb2) == -1) {
|
||||||
|
return mode_color[COL_OR];
|
||||||
|
}
|
||||||
|
return get_color(file, &sb2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case S_IFIFO:
|
||||||
|
return mode_color[COL_PI];
|
||||||
|
case S_IFSOCK:
|
||||||
|
return mode_color[COL_SO];
|
||||||
|
case S_IFBLK:
|
||||||
|
return mode_color[COL_BD];
|
||||||
|
case S_IFCHR:
|
||||||
|
return mode_color[COL_CD];
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sb->st_mode & S_ISUID) {
|
||||||
|
return mode_color[COL_SU];
|
||||||
|
} else if (sb->st_mode & S_ISGID) {
|
||||||
|
return mode_color[COL_SG];
|
||||||
|
// tw ow st ??
|
||||||
|
} else if (sb->st_mode & S_IXUSR) {
|
||||||
|
return mode_color[COL_EX];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for suffix alias */
|
||||||
|
size_t len = strlen(file);
|
||||||
|
/* shortest valid suffix format is a.b */
|
||||||
|
if (len > 2) {
|
||||||
|
const char* suf = file + len - 1;
|
||||||
|
while (suf > file + 1) {
|
||||||
|
if (suf[-1] == '.') {
|
||||||
|
if (sufaliastab->getnode(sufaliastab, suf)) {
|
||||||
|
return mode_color[COL_SA];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
suf--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct {
|
||||||
|
char** array;
|
||||||
|
size_t len;
|
||||||
|
size_t size;
|
||||||
|
} ftb_compcap;
|
||||||
|
|
||||||
|
// Usage:
|
||||||
|
// initialize fzf-tab-generate-compcap -i
|
||||||
|
// output to _ftb_compcap fzf-tab-generate-compcap -o
|
||||||
|
// add a entry fzf-tab-generate-compcap word desc opts
|
||||||
|
static int bin_fzf_tab_compcap_generate(char* nam, char** args, Options ops, UNUSED(int func))
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (OPT_ISSET(ops, 'o')) {
|
||||||
|
// write final result to _ftb_compcap
|
||||||
|
setaparam("_ftb_compcap", ftb_compcap.array);
|
||||||
|
ftb_compcap.array = NULL;
|
||||||
|
return 0;
|
||||||
|
} else if (OPT_ISSET(ops, 'i')) {
|
||||||
|
// init
|
||||||
|
if (ftb_compcap.array)
|
||||||
|
freearray(ftb_compcap.array);
|
||||||
|
ftb_compcap.array = zshcalloc(1024 * sizeof(char*));
|
||||||
|
ftb_compcap.len = 0, ftb_compcap.size = 1024;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ftb_compcap.array == NULL) {
|
||||||
|
zwarnnam(nam, "please initialize it first");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get paramaters
|
||||||
|
char **words = getaparam(args[0]), **dscrs = getaparam(args[1]), *opts = getsparam(args[2]);
|
||||||
|
if (!words || !dscrs || !opts) {
|
||||||
|
zwarnnam(nam, "wrong argument");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *bs = metafy("\2", 1, META_DUP), *nul = metafy("\0word\0", 6, META_DUP);
|
||||||
|
size_t dscrs_cnt = arrlen(dscrs);
|
||||||
|
|
||||||
|
for (i = 0; words[i]; i++) {
|
||||||
|
// TODO: replace '\n'
|
||||||
|
char* dscr = i < dscrs_cnt ? dscrs[i] : words[i];
|
||||||
|
char *buffer = ftb_strcat(NULL, 5, dscr, bs, opts, nul, words[i]);
|
||||||
|
ftb_compcap.array[ftb_compcap.len++] = buffer;
|
||||||
|
|
||||||
|
if (ftb_compcap.len == ftb_compcap.size) {
|
||||||
|
ftb_compcap.array
|
||||||
|
= zrealloc(ftb_compcap.array, (1024 + ftb_compcap.size) * sizeof(char*));
|
||||||
|
ftb_compcap.size += 1024;
|
||||||
|
memset(ftb_compcap.array + ftb_compcap.size - 1024, 0, 1024 * sizeof(char*));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zsfree(bs);
|
||||||
|
zsfree(nul);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cat n string, return the pointer to the new string
|
||||||
|
// dst will be reallocated if is not big enough
|
||||||
|
// if dst is NULL, it will be allocated
|
||||||
|
char* ftb_strcat(char* dst, int n, ...)
|
||||||
|
{
|
||||||
|
int i, idx;
|
||||||
|
|
||||||
|
va_list valist;
|
||||||
|
va_start(valist, n);
|
||||||
|
|
||||||
|
char* final = dst ? zrealloc(dst, 128) : zalloc(128);
|
||||||
|
size_t size = 128, max_len = 128 - 1;
|
||||||
|
dst = final;
|
||||||
|
|
||||||
|
for (i = 0, idx = 0; i < n; i++) {
|
||||||
|
char* src = va_arg(valist, char*);
|
||||||
|
if (src == NULL)
|
||||||
|
continue;
|
||||||
|
for (; *src != '\0'; dst++, src++, idx++) {
|
||||||
|
if (idx == max_len) {
|
||||||
|
size += size / 2;
|
||||||
|
final = zrealloc(final, size);
|
||||||
|
max_len = size - 1;
|
||||||
|
dst = &final[idx];
|
||||||
|
}
|
||||||
|
*dst = *src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*dst = '\0';
|
||||||
|
va_end(valist);
|
||||||
|
|
||||||
|
return final;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct file_color {
|
||||||
|
char *fc_begin;
|
||||||
|
char *fc_end;
|
||||||
|
char *sc;
|
||||||
|
char *suffix;
|
||||||
|
};
|
||||||
|
|
||||||
|
// accept an
|
||||||
|
static struct file_color* fzf_tab_colorize(char* file)
|
||||||
|
{
|
||||||
|
struct file_color *fc = zalloc(sizeof(struct file_color));
|
||||||
|
|
||||||
|
file = unmeta(file);
|
||||||
|
|
||||||
|
struct stat sb;
|
||||||
|
if (lstat(file, &sb) == -1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char suffix[2] = {0};
|
||||||
|
if (isset(opt_list_type)) {
|
||||||
|
suffix[0] = get_suffix(file, &sb);
|
||||||
|
}
|
||||||
|
const char* color = get_color(file, &sb);
|
||||||
|
|
||||||
|
char* symlink = NULL;
|
||||||
|
const char* symcolor = "";
|
||||||
|
if ((sb.st_mode & S_IFMT) == S_IFLNK) {
|
||||||
|
symlink = zshcalloc(PATH_MAX);
|
||||||
|
size_t end = readlink(file, symlink, PATH_MAX);
|
||||||
|
symlink[end] = '\0';
|
||||||
|
if (stat(file, &sb) == -1) {
|
||||||
|
symcolor = mode_color[COL_OR];
|
||||||
|
} else {
|
||||||
|
char tmp[PATH_MAX];
|
||||||
|
realpath(file, tmp);
|
||||||
|
symcolor = get_color(file, &sb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (color != NULL) {
|
||||||
|
fc->fc_begin = ftb_strcat(NULL, 3, mode_color[COL_LC], color, mode_color[COL_RC]);
|
||||||
|
fc->fc_end = ftb_strcat(NULL, 3, mode_color[COL_LC], "0", mode_color[COL_RC]);
|
||||||
|
} else {
|
||||||
|
fc->fc_begin = ztrdup("");
|
||||||
|
fc->fc_end = ztrdup("");
|
||||||
|
}
|
||||||
|
|
||||||
|
fc->suffix = ztrdup(suffix);
|
||||||
|
|
||||||
|
if (symlink != NULL) {
|
||||||
|
fc->sc = ftb_strcat(NULL, 7, mode_color[COL_LC], symcolor, mode_color[COL_RC],
|
||||||
|
symlink, mode_color[COL_LC], "0", mode_color[COL_RC]);
|
||||||
|
free(symlink);
|
||||||
|
} else {
|
||||||
|
fc->sc = ztrdup("");
|
||||||
|
}
|
||||||
|
|
||||||
|
fc->fc_begin = metafy(fc->fc_begin, -1, META_REALLOC);
|
||||||
|
fc->fc_end = metafy(fc->fc_end, -1, META_REALLOC);
|
||||||
|
fc->sc = metafy(fc->sc, -1, META_REALLOC);
|
||||||
|
fc->suffix = metafy(fc->suffix, -1, META_REALLOC);
|
||||||
|
|
||||||
|
return fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bin_fzf_tab_candidates_generate(char* nam, char** args, Options ops, UNUSED(int func))
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
if (OPT_ISSET(ops, 'i')) {
|
||||||
|
// compile list_colors pattern
|
||||||
|
if (*args == NULL) {
|
||||||
|
zwarnnam(nam, "please specify an array");
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
char** array = getaparam(*args);
|
||||||
|
return compile_patterns(nam, array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char **ftb_compcap = getaparam("_ftb_compcap"), **group_colors = getaparam("group_colors"),
|
||||||
|
*default_color = getsparam("default_color"), *prefix = getsparam("prefix");
|
||||||
|
|
||||||
|
size_t group_colors_len = arrlen(group_colors);
|
||||||
|
size_t ftb_compcap_len = arrlen(ftb_compcap);
|
||||||
|
|
||||||
|
char *bs = metafy("\b", 1, META_DUP), *nul = metafy("\0", 1, META_DUP),
|
||||||
|
*soh = metafy("\2", 1, META_DUP);
|
||||||
|
|
||||||
|
char **candidates = zshcalloc(sizeof(char*) * (ftb_compcap_len + 1)),
|
||||||
|
*filepath = zshcalloc(PATH_MAX * sizeof(char)), *dpre = zshcalloc(128),
|
||||||
|
*dsuf = zshcalloc(128);
|
||||||
|
|
||||||
|
char* first_word = NULL;
|
||||||
|
int same_word = 1;
|
||||||
|
|
||||||
|
for (i = 0; i < ftb_compcap_len; i++) {
|
||||||
|
char *word = "", *group = NULL, *realdir = NULL;
|
||||||
|
strcpy(dpre, "");
|
||||||
|
strcpy(dsuf, "");
|
||||||
|
|
||||||
|
// extract all the variables what we need
|
||||||
|
char** compcap = sepsplit(ftb_compcap[i], soh, 1, 0);
|
||||||
|
char* desc = compcap[0];
|
||||||
|
char** info = sepsplit(compcap[1], nul, 1, 0);
|
||||||
|
|
||||||
|
for (j = 0; info[j]; j += 2) {
|
||||||
|
if (!strcmp(info[j], "word")) {
|
||||||
|
word = info[j + 1];
|
||||||
|
// unquote word
|
||||||
|
parse_subst_string(word);
|
||||||
|
remnulargs(word);
|
||||||
|
untokenize(word);
|
||||||
|
} else if (!strcmp(info[j], "group")) {
|
||||||
|
group = info[j + 1];
|
||||||
|
} else if (!strcmp(info[j], "realdir")) {
|
||||||
|
realdir = info[j + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if all the words are the same
|
||||||
|
if (first_word == NULL) {
|
||||||
|
first_word = ztrdup(word);
|
||||||
|
} else if (same_word && strcmp(word, first_word) != 0) {
|
||||||
|
same_word = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add character and color to describe the type of the files
|
||||||
|
if (realdir) {
|
||||||
|
filepath = ftb_strcat(filepath, 2, realdir, word);
|
||||||
|
struct file_color *fc = fzf_tab_colorize(filepath);
|
||||||
|
if (fc != NULL) {
|
||||||
|
dpre = ftb_strcat(dpre, 2, fc->fc_end, fc->fc_begin);
|
||||||
|
if (fc->sc[0] != '\0') {
|
||||||
|
dsuf = ftb_strcat(dsuf, 4, fc->fc_end, fc->suffix, " -> ", fc->sc);
|
||||||
|
} else {
|
||||||
|
dsuf = ftb_strcat(dsuf, 2, fc->fc_end, fc->suffix);
|
||||||
|
}
|
||||||
|
if (dpre[0] != '\0') {
|
||||||
|
setiparam("colorful", 1);
|
||||||
|
}
|
||||||
|
free(fc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char* result = zshcalloc(256 * sizeof(char));
|
||||||
|
|
||||||
|
// add color to description if they have group index
|
||||||
|
if (group) {
|
||||||
|
// use strtol ?
|
||||||
|
int group_index = atoi(group);
|
||||||
|
char* color = group_index >= group_colors_len ? "" : group_colors[group_index - 1];
|
||||||
|
// add prefix
|
||||||
|
result = ftb_strcat(
|
||||||
|
result, 11, group, bs, color, prefix, dpre, nul, group, bs, desc, nul, dsuf);
|
||||||
|
} else {
|
||||||
|
result = ftb_strcat(result, 6, default_color, dpre, nul, desc, nul, dsuf);
|
||||||
|
}
|
||||||
|
// quotedzputs(result, stdout);
|
||||||
|
// putchar('\n');
|
||||||
|
candidates[i] = result;
|
||||||
|
|
||||||
|
freearray(info);
|
||||||
|
freearray(compcap);
|
||||||
|
}
|
||||||
|
|
||||||
|
setaparam("tcandidates", candidates);
|
||||||
|
setiparam("same_word", same_word);
|
||||||
|
zsfree(dpre);
|
||||||
|
zsfree(dsuf);
|
||||||
|
zsfree(filepath);
|
||||||
|
zsfree(first_word);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct builtin bintab[] = {
|
||||||
|
BUILTIN("fzf-tab-compcap-generate", 0, bin_fzf_tab_compcap_generate, 0, -1, 0, "io", NULL),
|
||||||
|
BUILTIN("fzf-tab-candidates-generate", 0, bin_fzf_tab_candidates_generate, 0, -1, 0, "i", NULL),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct paramdef patab[] = {
|
||||||
|
STRPARAMDEF("FZF_TAB_MODULE_VERSION", &fzf_tab_module_version),
|
||||||
|
};
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
static struct features module_features = {
|
||||||
|
bintab, sizeof(bintab) / sizeof(*bintab),
|
||||||
|
NULL, 0,
|
||||||
|
NULL, 0,
|
||||||
|
patab, sizeof(patab) / sizeof(*patab),
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
int setup_(UNUSED(Module m)) { return 0; }
|
||||||
|
|
||||||
|
int features_(Module m, char*** features)
|
||||||
|
{
|
||||||
|
*features = featuresarray(m, &module_features);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int enables_(Module m, int** enables) { return handlefeatures(m, &module_features, enables); }
|
||||||
|
|
||||||
|
int boot_(UNUSED(Module m))
|
||||||
|
{
|
||||||
|
fzf_tab_module_version = ztrdup("0.2.2");
|
||||||
|
// different zsh version may have different value of list_type's index
|
||||||
|
// so query it dynamically
|
||||||
|
opt_list_type = optlookup("list_types");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cleanup_(UNUSED(Module m)) { return setfeatureenables(m, &module_features, NULL); }
|
||||||
|
|
||||||
|
int finish_(UNUSED(Module m))
|
||||||
|
{
|
||||||
|
printf("fzf-tab module unloaded.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
7
eggs/zsh/config/fzf-tab/modules/Src/fzftab.mdd
Normal file
7
eggs/zsh/config/fzf-tab/modules/Src/fzftab.mdd
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
name=aloxaf/fzftab
|
||||||
|
link=dynamic
|
||||||
|
load=no
|
||||||
|
|
||||||
|
autofeatures="b:fzf-tab-colorize p:FZF_TAB_MODULE_VERSION"
|
||||||
|
|
||||||
|
objects="fzftab.o"
|
||||||
1
eggs/zsh/config/fzf-tab/test/.gitignore
vendored
Normal file
1
eggs/zsh/config/fzf-tab/test/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.zcompdump
|
||||||
174
eggs/zsh/config/fzf-tab/test/comptest
Normal file
174
eggs/zsh/config/fzf-tab/test/comptest
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
comptestinit () {
|
||||||
|
setopt extendedglob
|
||||||
|
|
||||||
|
zmodload zsh/zpty || return $?
|
||||||
|
|
||||||
|
comptest_zsh=${ZSH:-zsh}
|
||||||
|
comptest_keymap=e
|
||||||
|
|
||||||
|
while getopts vz: opt; do
|
||||||
|
case $opt in
|
||||||
|
z) comptest_zsh="$OPTARG";;
|
||||||
|
v) comptest_keymap="v";;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
(( OPTIND > 1 )) && shift $(( OPTIND - 1 ))
|
||||||
|
|
||||||
|
export PS1="<PROMPT>"
|
||||||
|
zpty zsh "$comptest_zsh -f +Z"
|
||||||
|
|
||||||
|
zpty -r zsh log1 "*<PROMPT>*" || {
|
||||||
|
print "first prompt hasn't appeared."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
comptesteval \
|
||||||
|
"export LC_ALL=${ZSH_TEST_LANG:-C}" \
|
||||||
|
"emulate -R zsh" \
|
||||||
|
"export ZDOTDIR=$ZTST_testdir" \
|
||||||
|
"bindkey -$comptest_keymap" \
|
||||||
|
'LISTMAX=10000000
|
||||||
|
stty 38400 columns 80 rows 24 tabs -icanon -iexten
|
||||||
|
TERM=vt100
|
||||||
|
KEYTIMEOUT=1
|
||||||
|
setopt zle
|
||||||
|
autoload -U compinit
|
||||||
|
compinit -u
|
||||||
|
zstyle ":completion:*:default" list-colors "no=<NO>" "fi=<FI>" "di=<DI>" "ln=<LN>" "pi=<PI>" "so=<SO>" "bd=<BD>" "cd=<CD>" "ex=<EX>" "mi=<MI>" "tc=<TC>" "sp=<SP>" "lc=<LC>" "ec=<EC>\n" "rc=<RC>"
|
||||||
|
zstyle ":completion:*" group-name ""
|
||||||
|
zstyle ":completion:*:messages" format "<MESSAGE>%d</MESSAGE>
|
||||||
|
"
|
||||||
|
zstyle ":completion:*:descriptions" format "<DESCRIPTION>%d</DESCRIPTION>"
|
||||||
|
zstyle ":completion:*:options" verbose yes
|
||||||
|
zstyle ":completion:*:values" verbose yes
|
||||||
|
setopt noalwayslastprompt listrowsfirst completeinword
|
||||||
|
zmodload zsh/complist
|
||||||
|
expand-or-complete-with-report () {
|
||||||
|
print -lr "<WIDGET><expand-or-complete>"
|
||||||
|
zle expand-or-complete
|
||||||
|
print -lr - "<LBUFFER>$LBUFFER</LBUFFER>" "<RBUFFER>$RBUFFER</RBUFFER>"
|
||||||
|
zle clear-screen
|
||||||
|
zle -R
|
||||||
|
}
|
||||||
|
list-choices-with-report () {
|
||||||
|
print -lr "<WIDGET><list-choices>"
|
||||||
|
zle list-choices
|
||||||
|
zle clear-screen
|
||||||
|
zle -R
|
||||||
|
}
|
||||||
|
comp-finish () {
|
||||||
|
print "<WIDGET><finish>"
|
||||||
|
zle kill-whole-line
|
||||||
|
zle clear-screen
|
||||||
|
zle -R
|
||||||
|
}
|
||||||
|
zle-finish () {
|
||||||
|
local buffer="$BUFFER" cursor="$CURSOR" mark="$MARK"
|
||||||
|
(( region_active)) || unset mark
|
||||||
|
BUFFER=""
|
||||||
|
zle -I
|
||||||
|
zle clear-screen
|
||||||
|
zle redisplay
|
||||||
|
print -lr "<WIDGET><finish>" "BUFFER: $buffer" "CURSOR: $cursor"
|
||||||
|
(( $+mark )) && print -lr "MARK: $mark"
|
||||||
|
zle accept-line
|
||||||
|
}
|
||||||
|
zle -N expand-or-complete-with-report
|
||||||
|
zle -N list-choices-with-report
|
||||||
|
zle -N comp-finish
|
||||||
|
zle -N zle-finish
|
||||||
|
bindkey "^I" expand-or-complete-with-report
|
||||||
|
bindkey "^D" list-choices-with-report
|
||||||
|
bindkey "^Z" comp-finish
|
||||||
|
bindkey "^X" zle-finish
|
||||||
|
bindkey -a "^X" zle-finish
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
zpty_flush() {
|
||||||
|
local junk
|
||||||
|
if zpty -r -t zsh junk \*; then
|
||||||
|
(( ZTST_verbose > 2 )) && print -n -u $ZTST_fd "$*: ${(V)junk}"
|
||||||
|
while zpty -r -t zsh junk \* ; do
|
||||||
|
(( ZTST_verbose > 2 )) && print -n -u $ZTST_fd "${(V)junk}"
|
||||||
|
done
|
||||||
|
(( ZTST_verbose > 2 )) && print -u $ZTST_fd ''
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
zpty_run() {
|
||||||
|
zpty -w zsh "$*"
|
||||||
|
zpty -r -m zsh log "*<PROMPT>*" || {
|
||||||
|
print "prompt hasn't appeared."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comptesteval () {
|
||||||
|
local tmp=/tmp/comptest.$$
|
||||||
|
|
||||||
|
print -lr - "$@" > $tmp
|
||||||
|
# zpty_flush Before comptesteval
|
||||||
|
zpty -w zsh ". $tmp"
|
||||||
|
zpty -r -m zsh log_eval "*<PROMPT>*" || {
|
||||||
|
print "prompt hasn't appeared."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
zpty_flush After comptesteval
|
||||||
|
rm $tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
comptest () {
|
||||||
|
input="$*"
|
||||||
|
zpty -n -w zsh "$input"$'\C-Z'
|
||||||
|
zpty -r -m zsh log "*<WIDGET><finish>*<PROMPT>*" || {
|
||||||
|
print "failed to invoke finish widget."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
logs=(${(s:<WIDGET>:)log})
|
||||||
|
shift logs
|
||||||
|
|
||||||
|
for log in "$logs[@]"; do
|
||||||
|
if [[ "$log" = (#b)*$'<LBUFFER>'(*)$'</LBUFFER>\r\n<RBUFFER>'(*)$'</RBUFFER>'* ]]; then
|
||||||
|
print -lr "line: {$match[1]}{$match[2]}"
|
||||||
|
fi
|
||||||
|
while (( ${(N)log#*(#b)(<LC><(??)><RC>(*)<EC>|<DESCRIPTION>(*)</DESCRIPTION>|<MESSAGE>(*)</MESSAGE>|<COMPADD>(*)</COMPADD>|<INSERT_POSITIONS>(*)</INSERT_POSITIONS>|<QUERY>(*)</QUERY>|<WARN>(*)</WARN>)} )); do
|
||||||
|
log="${log[$mend[1]+1,-1]}"
|
||||||
|
if (( 0 <= $mbegin[2] )); then
|
||||||
|
if [[ $match[2] != TC && $match[3] != \ # ]]; then
|
||||||
|
print -lr "$match[2]:{${match[3]%${(%):-%E}}}"
|
||||||
|
fi
|
||||||
|
elif (( 0 <= $mbegin[4] )); then
|
||||||
|
print -lr "DESCRIPTION:{$match[4]}"
|
||||||
|
elif (( 0 <= $mbegin[5] )); then
|
||||||
|
print -lr "MESSAGE:{$match[5]}"
|
||||||
|
elif (( 0 <= $mbegin[6] )); then
|
||||||
|
print -lr "COMPADD:{${${match[6]}//[$'\r\n']/}}"
|
||||||
|
elif (( 0 <= $mbegin[7] )); then
|
||||||
|
print -lr "INSERT_POSITIONS:{${${match[7]}//[$'\r\n']/}}"
|
||||||
|
elif (( 0 <= $mbegin[8] )); then
|
||||||
|
print -lr "QUERY:{${match[8]}}"
|
||||||
|
elif (( 0 <= $mbegin[9] )); then
|
||||||
|
print -lr "WARN:{${match[9]}}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
zletest () {
|
||||||
|
local first=0
|
||||||
|
for input; do
|
||||||
|
# zpty_flush Before zletest
|
||||||
|
# sleep for $KEYTIMEOUT
|
||||||
|
(( first++ )) && { sleep 2 & } | read -t 0.011 -u 0 -k 1
|
||||||
|
zpty -n -w zsh "$input"
|
||||||
|
done
|
||||||
|
zpty -n -w zsh $'\C-X'
|
||||||
|
zpty -r -m zsh log "*<WIDGET><finish>*<PROMPT>*" || {
|
||||||
|
print "failed to invoke finish widget."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
# zpty_flush After zletest
|
||||||
|
print -lr "${(@)${(@ps:\r\n:)log##*<WIDGET><finish>}[2,-2]}"
|
||||||
|
}
|
||||||
253
eggs/zsh/config/fzf-tab/test/fzftab.ztst
Normal file
253
eggs/zsh/config/fzf-tab/test/fzftab.ztst
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
# Tests for fzf tab.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
unset -m LC_\*
|
||||||
|
ZSH_TEST_LANG=
|
||||||
|
langs=(en_{US,GB}.{UTF-,utf}8 en.UTF-8
|
||||||
|
$(locale -a 2>/dev/null | egrep 'utf8|UTF-8'))
|
||||||
|
for LANG in $langs; do
|
||||||
|
if [[ é = ? ]]; then
|
||||||
|
ZSH_TEST_LANG=$LANG
|
||||||
|
break;
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ $OSTYPE = cygwin ]]; then
|
||||||
|
ZTST_unimplemented="the zsh/zpty module does not work on Cygwin"
|
||||||
|
elif ( zmodload zsh/zpty 2>/dev/null ); then
|
||||||
|
. $ZTST_srcdir/comptest
|
||||||
|
mkdir comp.tmp
|
||||||
|
cd comp.tmp
|
||||||
|
comptestinit -z zsh &&
|
||||||
|
{
|
||||||
|
comptesteval 'compdef _tst tst'
|
||||||
|
mkdir dir1 &&
|
||||||
|
mkdir dir2 &&
|
||||||
|
touch file1 &&
|
||||||
|
touch file2
|
||||||
|
touch dir1/file1
|
||||||
|
git init
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ZTST_unimplemented="the zsh/zpty module is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
|
comptesteval ". $ZTST_srcdir/../fzf-tab.zsh"
|
||||||
|
comptesteval "zstyle ':fzf-tab:*' debug-command $ZTST_srcdir/select -n 1 -h '\$#_ftb_headers' -q '\"\$_ftb_query\"'"
|
||||||
|
comptesteval '
|
||||||
|
zstyle ":fzf-tab:*" default-color "<LC><C0><RC>"
|
||||||
|
zstyle ":fzf-tab:*" single-group color header
|
||||||
|
zstyle ":fzf-tab:*" group-colors "<LC><C1><RC>" "<LC><C2><RC>" "<LC><C3><RC>" "<LC><C4><RC>"
|
||||||
|
fzf-tab-complete-with-report() {
|
||||||
|
print -lr "<WIDGET><fzf-tab-complete>"
|
||||||
|
zle fzf-tab-complete 2>&1
|
||||||
|
print -lr - "<LBUFFER>$LBUFFER</LBUFFER>" "<RBUFFER>$RBUFFER</RBUFFER>"
|
||||||
|
zle clear-screen
|
||||||
|
zle -R
|
||||||
|
}
|
||||||
|
zle -N fzf-tab-complete-with-report
|
||||||
|
bindkey "^I" fzf-tab-complete-with-report
|
||||||
|
'
|
||||||
|
|
||||||
|
%test
|
||||||
|
|
||||||
|
comptest $': \t'
|
||||||
|
0:directories and files
|
||||||
|
>line: {: dir1/}{}
|
||||||
|
>QUERY:{}
|
||||||
|
>DESCRIPTION:{file}
|
||||||
|
>C1:{dir1/}
|
||||||
|
>C1:{dir2/}
|
||||||
|
>C1:{file1}
|
||||||
|
>C1:{file2}
|
||||||
|
|
||||||
|
comptest $': d\t'
|
||||||
|
0:unambiguous prefix
|
||||||
|
>line: {: dir}{}
|
||||||
|
|
||||||
|
comptesteval '_tst() { compadd ".#abc" ".#def" ".#hij" }'
|
||||||
|
comptest $'tst ".#"\t'
|
||||||
|
0:unambiguous prefix in quote string
|
||||||
|
>line: {tst ".#abc }{"}
|
||||||
|
>QUERY:{.#}
|
||||||
|
>C0:{.#abc}
|
||||||
|
>C0:{.#def}
|
||||||
|
>C0:{.#hij}
|
||||||
|
|
||||||
|
comptesteval '_tst() { compadd /home /usr /lib; compstate[insert]=menu }'
|
||||||
|
comptest $'tst \t'
|
||||||
|
0:force list
|
||||||
|
>line: {tst /home }{}
|
||||||
|
>QUERY:{/}
|
||||||
|
>C0:{/home}
|
||||||
|
>C0:{/lib}
|
||||||
|
>C0:{/usr}
|
||||||
|
|
||||||
|
comptesteval 'zstyle ":completion:*" menu true'
|
||||||
|
comptest $': d\t'
|
||||||
|
0:prefix
|
||||||
|
>line: {: dir1/}{}
|
||||||
|
>QUERY:{dir}
|
||||||
|
>DESCRIPTION:{file}
|
||||||
|
>C1:{dir1/}
|
||||||
|
>C1:{dir2/}
|
||||||
|
|
||||||
|
comptesteval '_tst () { compadd d c b a }'
|
||||||
|
comptest $'tst \t'
|
||||||
|
0:normal
|
||||||
|
>line: {tst a }{}
|
||||||
|
>QUERY:{}
|
||||||
|
>C0:{a}
|
||||||
|
>C0:{b}
|
||||||
|
>C0:{c}
|
||||||
|
>C0:{d}
|
||||||
|
|
||||||
|
comptesteval 'zstyle ":completion:*:tst:*" sort false'
|
||||||
|
comptest $'tst \t'
|
||||||
|
0:no sort
|
||||||
|
>line: {tst d }{}
|
||||||
|
>QUERY:{}
|
||||||
|
>C0:{d}
|
||||||
|
>C0:{c}
|
||||||
|
>C0:{b}
|
||||||
|
>C0:{a}
|
||||||
|
|
||||||
|
comptesteval 'zstyle ":fzf-tab:*:tst:*" fzf-flags -n 1,2'
|
||||||
|
comptest $'tst \t'
|
||||||
|
comptesteval 'zstyle -d ":fzf-tab:*:tst:*" fzf-flags'
|
||||||
|
0:multi select
|
||||||
|
>line: {tst c d }{}
|
||||||
|
>QUERY:{}
|
||||||
|
>C0:{d}
|
||||||
|
>C0:{c}
|
||||||
|
>C0:{b}
|
||||||
|
>C0:{a}
|
||||||
|
|
||||||
|
comptest $': *\t'
|
||||||
|
0:expand
|
||||||
|
>line: {: dir1 dir2 file1 file2 }{}
|
||||||
|
|
||||||
|
comptesteval 'zstyle ":completion:*:warnings" format "<WARN>%d</WARN>"'
|
||||||
|
comptest $': asd\t'
|
||||||
|
0:warnings
|
||||||
|
>line: {: asd}{}
|
||||||
|
>WARN:{`file'}
|
||||||
|
|
||||||
|
# enclose ' for syntax highlight
|
||||||
|
|
||||||
|
comptesteval "touch 'abc def'"
|
||||||
|
comptest $': ./a\t'
|
||||||
|
0:filename with space
|
||||||
|
>line: {: ./abc\ def }{}
|
||||||
|
|
||||||
|
comptest $': ./abdef\C-b\C-b\C-b\t'
|
||||||
|
0:complete in word
|
||||||
|
>line: {: ./abc\ def }{}
|
||||||
|
|
||||||
|
comptest $': ./abc def\C-b\C-b\C-b\C-b\t'
|
||||||
|
comptesteval "rm 'abc def'"
|
||||||
|
0:complete in word(with known bug)
|
||||||
|
>line: {: ./abc\ def}{ def}
|
||||||
|
|
||||||
|
comptesteval 'mkdir -p abc/def/hij abc/dfe/hij'
|
||||||
|
comptest $': ./a/d/h\t'
|
||||||
|
comptesteval 'rm -rd abc'
|
||||||
|
0:nested directory
|
||||||
|
>line: {: ./abc/def/h}{}
|
||||||
|
>QUERY:{d}
|
||||||
|
>DESCRIPTION:{file}
|
||||||
|
>C1:{def/}
|
||||||
|
>C1:{dfe/}
|
||||||
|
|
||||||
|
comptesteval '_tst() { a=(a); _describe "group1" a; a=(b); _describe "group2" a }'
|
||||||
|
comptest $'tst \t'
|
||||||
|
0:multi headers
|
||||||
|
>line: {tst a }{}
|
||||||
|
>QUERY:{}
|
||||||
|
>DESCRIPTION:{group1}
|
||||||
|
>DESCRIPTION:{group2}
|
||||||
|
>C1:{·a}
|
||||||
|
>C2:{·b}
|
||||||
|
|
||||||
|
comptest $'git add dir1\t'
|
||||||
|
0:add empty word
|
||||||
|
>line: {git add dir1/}{}
|
||||||
|
|
||||||
|
comptesteval "zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' 'r:|=*' 'l:|=* r:|=*'"
|
||||||
|
comptesteval "touch vim.coc"
|
||||||
|
comptest $': coc\t'
|
||||||
|
comptesteval "rm vim.coc; zstyle -d ':completion:*' matcher-list"
|
||||||
|
0:matcher-list
|
||||||
|
>line: {: vim.coc }{}
|
||||||
|
|
||||||
|
comptesteval $'cd dir1'
|
||||||
|
comptest $': ../d\t'
|
||||||
|
comptesteval $'cd ..'
|
||||||
|
0:IPREFIX
|
||||||
|
>line: {: ../dir1/}{}
|
||||||
|
>QUERY:{dir}
|
||||||
|
>DESCRIPTION:{file}
|
||||||
|
>C1:{dir1/}
|
||||||
|
>C1:{dir2/}
|
||||||
|
|
||||||
|
comptest $': $PWD/d\t'
|
||||||
|
0:expansion
|
||||||
|
>line: {: $PWD/dir1/}{}
|
||||||
|
>QUERY:{dir}
|
||||||
|
>DESCRIPTION:{file}
|
||||||
|
>C1:{dir1/}
|
||||||
|
>C1:{dir2/}
|
||||||
|
|
||||||
|
comptesteval 'echo no > called'
|
||||||
|
comptesteval "touch 'dir\`echo yes > called\`'"
|
||||||
|
comptest $': d\t'
|
||||||
|
echo called:$(<called)
|
||||||
|
comptesteval "rm 'dir\`echo yes > called\`' called"
|
||||||
|
0:don''t expand file name
|
||||||
|
>line: {: dir1/}{}
|
||||||
|
>QUERY:{dir}
|
||||||
|
>DESCRIPTION:{file}
|
||||||
|
>C1:{dir1/}
|
||||||
|
>C1:{dir2/}
|
||||||
|
>C1:{dir`echo yes > called`}
|
||||||
|
>called:no
|
||||||
|
|
||||||
|
comptesteval "zstyle ':fzf-tab:*' debug-command true"
|
||||||
|
comptest $': d\t'
|
||||||
|
comptesteval "zstyle ':fzf-tab:*' debug-command $ZTST_srcdir/select -n 1 -h '\$#headers' -q '\"\$query\"'"
|
||||||
|
0:cancel completion
|
||||||
|
>line: {: d}{}
|
||||||
|
|
||||||
|
comptesteval "zstyle ':fzf-tab:*' debug-command $ZTST_srcdir/select -n QUERY -h '\$#headers' -q '\"dragon\"'"
|
||||||
|
comptest $': ./d\t'
|
||||||
|
comptesteval "zstyle ':fzf-tab:*' debug-command $ZTST_srcdir/select -n 1 -h '\$#headers' -q '\"\$query\"'"
|
||||||
|
0:use query directly
|
||||||
|
>line: {: ./dragon}{}
|
||||||
|
>QUERY:{dragon}
|
||||||
|
>DESCRIPTION:{file}
|
||||||
|
>C1:{dir1/}
|
||||||
|
>C1:{dir2/}
|
||||||
|
|
||||||
|
comptesteval 'zstyle ":completion:*" menu false'
|
||||||
|
comptesteval "local prefix_1=1 prefix_2=1 prenofix_3=1"
|
||||||
|
comptest $'echo $pre\t'
|
||||||
|
comptesteval 'zstyle ":completion:*" menu true'
|
||||||
|
0:parameter completion
|
||||||
|
>line: {echo $pre}{}
|
||||||
|
>QUERY:{}
|
||||||
|
>DESCRIPTION:{parameter}
|
||||||
|
>C1:{prefix_1}
|
||||||
|
>C1:{prefix_2}
|
||||||
|
>C1:{prenofix_3}
|
||||||
|
|
||||||
|
comptesteval '_tst() { compadd -J packages -X package openpgp-keys-gentoo-release -MERGING-pnpm-bin }'
|
||||||
|
comptest $'tst \t'
|
||||||
|
0:completions starts with dash
|
||||||
|
>line: {tst }{}
|
||||||
|
>QUERY:{}
|
||||||
|
>C1:{package }
|
||||||
|
>C1:{openpgp-keys-gentoo-release}
|
||||||
|
>C1:{-MERGING-pnpm-bin}
|
||||||
|
%clean
|
||||||
|
|
||||||
|
zmodload -ui zsh/zpty
|
||||||
|
|
||||||
27
eggs/zsh/config/fzf-tab/test/runtests.zsh
Normal file
27
eggs/zsh/config/fzf-tab/test/runtests.zsh
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/zsh -f
|
||||||
|
|
||||||
|
emulate zsh
|
||||||
|
|
||||||
|
# Run all specified tests, keeping count of which succeeded.
|
||||||
|
# The reason for this extra layer above the test script is to
|
||||||
|
# protect from catastrophic failure of an individual test.
|
||||||
|
# We could probably do that with subshells instead.
|
||||||
|
|
||||||
|
integer success failure skipped retval
|
||||||
|
for file in ${@:1}; do
|
||||||
|
zsh +Z -f ./ztst.zsh $file
|
||||||
|
retval=$?
|
||||||
|
if (( $retval == 2 )); then
|
||||||
|
(( skipped++ ))
|
||||||
|
elif (( $retval )); then
|
||||||
|
(( failure++ ))
|
||||||
|
else
|
||||||
|
(( success++ ))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
print "**************************************
|
||||||
|
$success successful test script${${success:#1}:+s}, \
|
||||||
|
$failure failure${${failure:#1}:+s}, \
|
||||||
|
$skipped skipped
|
||||||
|
**************************************"
|
||||||
|
return $(( failure ? 1 : 0 ))
|
||||||
36
eggs/zsh/config/fzf-tab/test/select
Executable file
36
eggs/zsh/config/fzf-tab/test/select
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env zsh
|
||||||
|
|
||||||
|
zmodload zsh/zutil
|
||||||
|
|
||||||
|
local -A headers range query expect
|
||||||
|
|
||||||
|
# -h: lines of headers
|
||||||
|
# -n: selection index, if it is QUERY, query string will be used
|
||||||
|
# -q: query string
|
||||||
|
# -e: expect key
|
||||||
|
zparseopts -E h:=headers n:=range q:=query e:=expect
|
||||||
|
|
||||||
|
print -r -- "${query//\"/}"
|
||||||
|
print -r -- "$expect"
|
||||||
|
print -r -- "<QUERY>${query//\"/}</QUERY>" >&2
|
||||||
|
|
||||||
|
local -a lines=()
|
||||||
|
while read input; do
|
||||||
|
lines+=(${input%$'\033[00m'})
|
||||||
|
done
|
||||||
|
|
||||||
|
for ((i = 1; i <= headers; i++)); do
|
||||||
|
print -r -- $lines[i] >&2
|
||||||
|
done
|
||||||
|
|
||||||
|
lines[1,headers]=()
|
||||||
|
|
||||||
|
for i in {1..$#lines}; do
|
||||||
|
print -r -- ${lines[i]//$'\0'}"<EC>" >&2
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ $range != QUERY ]]; then
|
||||||
|
for i in ${(s:,:)range}; do
|
||||||
|
print -r -- $lines[i]
|
||||||
|
done
|
||||||
|
fi
|
||||||
581
eggs/zsh/config/fzf-tab/test/ztst.zsh
Executable file
581
eggs/zsh/config/fzf-tab/test/ztst.zsh
Executable file
@ -0,0 +1,581 @@
|
|||||||
|
#!/bin/zsh -f
|
||||||
|
# The line above is just for convenience. Normally tests will be run using
|
||||||
|
# a specified version of zsh. With dynamic loading, any required libraries
|
||||||
|
# must already have been installed in that case.
|
||||||
|
#
|
||||||
|
# Takes one argument: the name of the test file. Currently only one such
|
||||||
|
# file will be processed each time ztst.zsh is run. This is slower, but
|
||||||
|
# much safer in terms of preserving the correct status.
|
||||||
|
# To avoid namespace pollution, all functions and parameters used
|
||||||
|
# only by the script begin with ZTST_.
|
||||||
|
#
|
||||||
|
# Options (without arguments) may precede the test file argument; these
|
||||||
|
# are interpreted as shell options to set. -x is probably the most useful.
|
||||||
|
|
||||||
|
# Produce verbose messages if non-zero.
|
||||||
|
# If 1, produce reports of tests executed; if 2, also report on progress.
|
||||||
|
# Defined in such a way that any value from the environment is used.
|
||||||
|
: ${ZTST_verbose:=0}
|
||||||
|
|
||||||
|
# We require all options to be reset, not just emulation options.
|
||||||
|
# Unfortunately, due to the crud which may be in /etc/zshenv this might
|
||||||
|
# still not be good enough. Maybe we should trick it somehow.
|
||||||
|
emulate -R zsh
|
||||||
|
|
||||||
|
# Ensure the locale does not screw up sorting. Don't supply a locale
|
||||||
|
# unless there's one set, to minimise problems.
|
||||||
|
[[ -n $LC_ALL ]] && LC_ALL=C
|
||||||
|
[[ -n $LC_COLLATE ]] && LC_COLLATE=C
|
||||||
|
[[ -n $LC_NUMERIC ]] && LC_NUMERIC=C
|
||||||
|
[[ -n $LC_MESSAGES ]] && LC_MESSAGES=C
|
||||||
|
[[ -n $LANG ]] && LANG=C
|
||||||
|
|
||||||
|
# Don't propagate variables that are set by default in the shell.
|
||||||
|
typeset +x WORDCHARS
|
||||||
|
|
||||||
|
# We need to be able to save and restore the options used in the test.
|
||||||
|
# We use the $options variable of the parameter module for this.
|
||||||
|
zmodload zsh/parameter
|
||||||
|
|
||||||
|
# Note that both the following are regular arrays, since we only use them
|
||||||
|
# in whole array assignments to/from $options.
|
||||||
|
# Options set in test code (i.e. by default all standard options)
|
||||||
|
ZTST_testopts=(${(kv)options})
|
||||||
|
|
||||||
|
setopt extendedglob nonomatch
|
||||||
|
while [[ $1 = [-+]* ]]; do
|
||||||
|
set $1
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
# Options set in main script
|
||||||
|
ZTST_mainopts=(${(kv)options})
|
||||||
|
|
||||||
|
# We run in the current directory, so remember it.
|
||||||
|
ZTST_testdir=$PWD
|
||||||
|
ZTST_testname=$1
|
||||||
|
|
||||||
|
integer ZTST_testfailed
|
||||||
|
|
||||||
|
# This is POSIX nonsense. Because of the vague feeling someone, somewhere
|
||||||
|
# may one day need to examine the arguments of "tail" using a standard
|
||||||
|
# option parser, every Unix user in the world is expected to switch
|
||||||
|
# to using "tail -n NUM" instead of "tail -NUM". Older versions of
|
||||||
|
# tail don't support this.
|
||||||
|
tail() {
|
||||||
|
emulate -L zsh
|
||||||
|
|
||||||
|
if [[ -z $TAIL_SUPPORTS_MINUS_N ]]; then
|
||||||
|
local test
|
||||||
|
test=$(echo "foo\nbar" | command tail -n 1 2>/dev/null)
|
||||||
|
if [[ $test = bar ]]; then
|
||||||
|
TAIL_SUPPORTS_MINUS_N=1
|
||||||
|
else
|
||||||
|
TAIL_SUPPORTS_MINUS_N=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
integer argi=${argv[(i)-<->]}
|
||||||
|
|
||||||
|
if [[ $argi -le $# && $TAIL_SUPPORTS_MINUS_N = 1 ]]; then
|
||||||
|
argv[$argi]=(-n ${argv[$argi][2,-1]})
|
||||||
|
fi
|
||||||
|
|
||||||
|
command tail "$argv[@]"
|
||||||
|
}
|
||||||
|
|
||||||
|
# The source directory is not necessarily the current directory,
|
||||||
|
# but if $0 doesn't contain a `/' assume it is.
|
||||||
|
if [[ $0 = */* ]]; then
|
||||||
|
ZTST_srcdir=${0%/*}
|
||||||
|
else
|
||||||
|
ZTST_srcdir=$PWD
|
||||||
|
fi
|
||||||
|
[[ $ZTST_srcdir = /* ]] || ZTST_srcdir="$ZTST_testdir/$ZTST_srcdir"
|
||||||
|
|
||||||
|
|
||||||
|
: ${TMPPREFIX:=/tmp/zsh}
|
||||||
|
ZTST_tmp=${TMPPREFIX}.ztst.$$
|
||||||
|
if ! rm -f $ZTST_tmp || ! mkdir -p $ZTST_tmp || ! chmod go-w $ZTST_tmp; then
|
||||||
|
print "Can't create $ZTST_tmp for exclusive use." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Temporary files for redirection inside tests.
|
||||||
|
ZTST_in=${ZTST_tmp}/ztst.in
|
||||||
|
# hold the expected output
|
||||||
|
ZTST_out=${ZTST_tmp}/ztst.out
|
||||||
|
ZTST_err=${ZTST_tmp}/ztst.err
|
||||||
|
# hold the actual output from the test
|
||||||
|
ZTST_tout=${ZTST_tmp}/ztst.tout
|
||||||
|
ZTST_terr=${ZTST_tmp}/ztst.terr
|
||||||
|
|
||||||
|
ZTST_cleanup() {
|
||||||
|
cd $ZTST_testdir
|
||||||
|
rm -rf $ZTST_testdir/dummy.tmp $ZTST_testdir/*.tmp(N) ${ZTST_tmp}
|
||||||
|
}
|
||||||
|
|
||||||
|
# This cleanup always gets performed, even if we abort. Later,
|
||||||
|
# we should try and arrange that any test-specific cleanup
|
||||||
|
# always gets called as well.
|
||||||
|
##trap 'print cleaning up...
|
||||||
|
##ZTST_cleanup' INT QUIT TERM
|
||||||
|
# Make sure it's clean now.
|
||||||
|
rm -rf dummy.tmp *.tmp
|
||||||
|
|
||||||
|
# Report failure. Note that all output regarding the tests goes to stdout.
|
||||||
|
# That saves an unpleasant mixture of stdout and stderr to sort out.
|
||||||
|
ZTST_testfailed() {
|
||||||
|
print -r "Test $ZTST_testname failed: $1"
|
||||||
|
if [[ -n $ZTST_message ]]; then
|
||||||
|
print -r "Was testing: $ZTST_message"
|
||||||
|
fi
|
||||||
|
print -r "$ZTST_testname: test failed."
|
||||||
|
if [[ -n $ZTST_failmsg ]]; then
|
||||||
|
print -r "The following may (or may not) help identifying the cause:
|
||||||
|
$ZTST_failmsg"
|
||||||
|
fi
|
||||||
|
ZTST_testfailed=1
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print messages if $ZTST_verbose is non-empty
|
||||||
|
ZTST_verbose() {
|
||||||
|
local lev=$1
|
||||||
|
shift
|
||||||
|
if [[ -n $ZTST_verbose && $ZTST_verbose -ge $lev ]]; then
|
||||||
|
print -r -u $ZTST_fd -- $*
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
ZTST_hashmark() {
|
||||||
|
if [[ ZTST_verbose -le 0 && -t $ZTST_fd ]]; then
|
||||||
|
print -n -u$ZTST_fd -- ${(pl:SECONDS::\#::\#\r:)}
|
||||||
|
fi
|
||||||
|
(( SECONDS > COLUMNS+1 && (SECONDS -= COLUMNS) ))
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ ! -r $ZTST_testname ]]; then
|
||||||
|
ZTST_testfailed "can't read test file."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec {ZTST_fd}>&1
|
||||||
|
exec {ZTST_input}<$ZTST_testname
|
||||||
|
|
||||||
|
# The current line read from the test file.
|
||||||
|
ZTST_curline=''
|
||||||
|
# The current section being run
|
||||||
|
ZTST_cursect=''
|
||||||
|
|
||||||
|
# Get a new input line. Don't mangle spaces; set IFS locally to empty.
|
||||||
|
# We shall skip comments at this level.
|
||||||
|
ZTST_getline() {
|
||||||
|
local IFS=
|
||||||
|
while true; do
|
||||||
|
read -u $ZTST_input -r ZTST_curline || return 1
|
||||||
|
[[ $ZTST_curline == \#* ]] || return 0
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the name of the section. It may already have been read into
|
||||||
|
# $curline, or we may have to skip some initial comments to find it.
|
||||||
|
# If argument present, it's OK to skip the reset of the current section,
|
||||||
|
# so no error if we find garbage.
|
||||||
|
ZTST_getsect() {
|
||||||
|
local match mbegin mend
|
||||||
|
|
||||||
|
while [[ $ZTST_curline != '%'(#b)([[:alnum:]]##)* ]]; do
|
||||||
|
ZTST_getline || return 1
|
||||||
|
[[ $ZTST_curline = [[:blank:]]# ]] && continue
|
||||||
|
if [[ $# -eq 0 && $ZTST_curline != '%'[[:alnum:]]##* ]]; then
|
||||||
|
ZTST_testfailed "bad line found before or after section:
|
||||||
|
$ZTST_curline"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# have the next line ready waiting
|
||||||
|
ZTST_getline
|
||||||
|
ZTST_cursect=${match[1]}
|
||||||
|
ZTST_verbose 2 "ZTST_getsect: read section name: $ZTST_cursect"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read in an indented code chunk for execution
|
||||||
|
ZTST_getchunk() {
|
||||||
|
# Code chunks are always separated by blank lines or the
|
||||||
|
# end of a section, so if we already have a piece of code,
|
||||||
|
# we keep it. Currently that shouldn't actually happen.
|
||||||
|
ZTST_code=''
|
||||||
|
# First find the chunk.
|
||||||
|
while [[ $ZTST_curline = [[:blank:]]# ]]; do
|
||||||
|
ZTST_getline || break
|
||||||
|
done
|
||||||
|
while [[ $ZTST_curline = [[:blank:]]##[^[:blank:]]* ]]; do
|
||||||
|
ZTST_code="${ZTST_code:+${ZTST_code}
|
||||||
|
}${ZTST_curline}"
|
||||||
|
ZTST_getline || break
|
||||||
|
done
|
||||||
|
ZTST_verbose 2 "ZTST_getchunk: read code chunk:
|
||||||
|
$ZTST_code"
|
||||||
|
[[ -n $ZTST_code ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read in a piece for redirection.
|
||||||
|
ZTST_getredir() {
|
||||||
|
local char=${ZTST_curline[1]} fn
|
||||||
|
ZTST_redir=${ZTST_curline[2,-1]}
|
||||||
|
while ZTST_getline; do
|
||||||
|
[[ $ZTST_curline[1] = $char ]] || break
|
||||||
|
ZTST_redir="${ZTST_redir}
|
||||||
|
${ZTST_curline[2,-1]}"
|
||||||
|
done
|
||||||
|
ZTST_verbose 2 "ZTST_getredir: read redir for '$char':
|
||||||
|
$ZTST_redir"
|
||||||
|
|
||||||
|
case $char in
|
||||||
|
('<') fn=$ZTST_in
|
||||||
|
;;
|
||||||
|
('>') fn=$ZTST_out
|
||||||
|
;;
|
||||||
|
('?') fn=$ZTST_err
|
||||||
|
;;
|
||||||
|
(*) ZTST_testfailed "bad redir operator: $char"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
if [[ $ZTST_flags = *q* && $char = '<' ]]; then
|
||||||
|
# delay substituting output until variables are set
|
||||||
|
print -r -- "${(e)ZTST_redir}" >>$fn
|
||||||
|
else
|
||||||
|
print -r -- "$ZTST_redir" >>$fn
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute an indented chunk. Redirections will already have
|
||||||
|
# been set up, but we need to handle the options.
|
||||||
|
ZTST_execchunk() {
|
||||||
|
setopt localloops # don't let continue & break propagate out
|
||||||
|
options=($ZTST_testopts)
|
||||||
|
() {
|
||||||
|
unsetopt localloops
|
||||||
|
eval "$ZTST_code"
|
||||||
|
}
|
||||||
|
ZTST_status=$?
|
||||||
|
# careful... ksh_arrays may be in effect.
|
||||||
|
ZTST_testopts=(${(kv)options[*]})
|
||||||
|
options=(${ZTST_mainopts[*]})
|
||||||
|
ZTST_verbose 2 "ZTST_execchunk: status $ZTST_status"
|
||||||
|
return $ZTST_status
|
||||||
|
}
|
||||||
|
|
||||||
|
# Functions for preparation and cleaning.
|
||||||
|
# When cleaning up (non-zero string argument), we ignore status.
|
||||||
|
ZTST_prepclean() {
|
||||||
|
# Execute indented code chunks.
|
||||||
|
while ZTST_getchunk; do
|
||||||
|
ZTST_execchunk >/dev/null || [[ -n $1 ]] || {
|
||||||
|
[[ -n "$ZTST_unimplemented" ]] ||
|
||||||
|
ZTST_testfailed "non-zero status from preparation code:
|
||||||
|
$ZTST_code" && return 0
|
||||||
|
}
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# diff wrapper
|
||||||
|
ZTST_diff() {
|
||||||
|
emulate -L zsh
|
||||||
|
setopt extendedglob
|
||||||
|
|
||||||
|
local diff_out
|
||||||
|
integer diff_pat diff_ret
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
(p)
|
||||||
|
diff_pat=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
(d)
|
||||||
|
;;
|
||||||
|
|
||||||
|
(*)
|
||||||
|
print "Bad ZTST_diff code: d for diff, p for pattern match"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
|
||||||
|
if (( diff_pat )); then
|
||||||
|
local -a diff_lines1 diff_lines2
|
||||||
|
integer failed i l
|
||||||
|
local p
|
||||||
|
|
||||||
|
diff_lines1=("${(f@)$(<$argv[-2])}")
|
||||||
|
diff_lines2=("${(f@)$(<$argv[-1])}")
|
||||||
|
if (( ${#diff_lines1} != ${#diff_lines2} )); then
|
||||||
|
failed=1
|
||||||
|
print -r "Pattern match failed, line mismatch (${#diff_lines1}/${#diff_lines2}):"
|
||||||
|
else
|
||||||
|
for (( i = 1; i <= ${#diff_lines1}; i++ )); do
|
||||||
|
if [[ ${diff_lines2[i]} != ${~diff_lines1[i]} ]]; then
|
||||||
|
failed=1
|
||||||
|
print -r "Pattern match failed, line $i:"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if (( failed )); then
|
||||||
|
for (( l = 1; l <= ${#diff_lines1}; ++l )); do
|
||||||
|
if (( l == i )); then
|
||||||
|
p="-"
|
||||||
|
else
|
||||||
|
p=" "
|
||||||
|
fi
|
||||||
|
print -r -- "$p<${diff_lines1[l]}"
|
||||||
|
done
|
||||||
|
for (( l = 1; l <= ${#diff_lines2}; ++l )); do
|
||||||
|
if (( l == i )); then
|
||||||
|
p="+"
|
||||||
|
else
|
||||||
|
p=" "
|
||||||
|
fi
|
||||||
|
print -r -- "$p>${diff_lines2[l]}"
|
||||||
|
done
|
||||||
|
diff_ret=1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
diff_out=$(diff -a "$@")
|
||||||
|
diff_ret="$?"
|
||||||
|
if [[ "$diff_ret" != "0" ]]; then
|
||||||
|
print -r -- "$diff_out"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return "$diff_ret"
|
||||||
|
}
|
||||||
|
|
||||||
|
ZTST_test() {
|
||||||
|
local last match mbegin mend found substlines
|
||||||
|
local diff_out diff_err
|
||||||
|
local ZTST_skip
|
||||||
|
integer expected_to_fail
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
rm -f $ZTST_in $ZTST_out $ZTST_err
|
||||||
|
touch $ZTST_in $ZTST_out $ZTST_err
|
||||||
|
ZTST_message=''
|
||||||
|
ZTST_failmsg=''
|
||||||
|
found=0
|
||||||
|
diff_out=d
|
||||||
|
diff_err=d
|
||||||
|
|
||||||
|
ZTST_verbose 2 "ZTST_test: looking for new test"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
ZTST_verbose 2 "ZTST_test: examining line:
|
||||||
|
$ZTST_curline"
|
||||||
|
case $ZTST_curline in
|
||||||
|
(%*) if [[ $found = 0 ]]; then
|
||||||
|
break 2
|
||||||
|
else
|
||||||
|
last=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
([[:space:]]#)
|
||||||
|
if [[ $found = 0 ]]; then
|
||||||
|
ZTST_getline || break 2
|
||||||
|
continue
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
([[:space:]]##[^[:space:]]*) ZTST_getchunk
|
||||||
|
if [[ $ZTST_curline == (#b)([-0-9]##)([[:alpha:]]#)(:*)# ]]; then
|
||||||
|
ZTST_xstatus=$match[1]
|
||||||
|
ZTST_flags=$match[2]
|
||||||
|
ZTST_message=${match[3]:+${match[3][2,-1]}}
|
||||||
|
else
|
||||||
|
ZTST_testfailed "expecting test status at:
|
||||||
|
$ZTST_curline"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
ZTST_getline
|
||||||
|
found=1
|
||||||
|
;;
|
||||||
|
('<'*) ZTST_getredir || return 1
|
||||||
|
found=1
|
||||||
|
;;
|
||||||
|
('*>'*)
|
||||||
|
ZTST_curline=${ZTST_curline[2,-1]}
|
||||||
|
diff_out=p
|
||||||
|
;&
|
||||||
|
('>'*)
|
||||||
|
ZTST_getredir || return 1
|
||||||
|
found=1
|
||||||
|
;;
|
||||||
|
('*?'*)
|
||||||
|
ZTST_curline=${ZTST_curline[2,-1]}
|
||||||
|
diff_err=p
|
||||||
|
;&
|
||||||
|
('?'*)
|
||||||
|
ZTST_getredir || return 1
|
||||||
|
found=1
|
||||||
|
;;
|
||||||
|
('F:'*) ZTST_failmsg="${ZTST_failmsg:+${ZTST_failmsg}
|
||||||
|
} ${ZTST_curline[3,-1]}"
|
||||||
|
ZTST_getline
|
||||||
|
found=1
|
||||||
|
;;
|
||||||
|
(*) ZTST_testfailed "bad line in test block:
|
||||||
|
$ZTST_curline"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# If we found some code to execute...
|
||||||
|
if [[ -n $ZTST_code ]]; then
|
||||||
|
ZTST_hashmark
|
||||||
|
ZTST_verbose 1 "Running test: $ZTST_message"
|
||||||
|
ZTST_verbose 2 "ZTST_test: expecting status: $ZTST_xstatus"
|
||||||
|
ZTST_verbose 2 "Input: $ZTST_in, output: $ZTST_out, error: $ZTST_terr"
|
||||||
|
|
||||||
|
ZTST_execchunk <$ZTST_in >$ZTST_tout 2>$ZTST_terr
|
||||||
|
|
||||||
|
if [[ -n $ZTST_skip ]]; then
|
||||||
|
ZTST_verbose 0 "Test case skipped: $ZTST_skip"
|
||||||
|
ZTST_skip=
|
||||||
|
if [[ -n $last ]]; then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $ZTST_flags = *f* ]]; then
|
||||||
|
expected_to_fail=1
|
||||||
|
ZTST_xfail_diff() { ZTST_diff "$@" > /dev/null }
|
||||||
|
ZTST_diff=ZTST_xfail_diff
|
||||||
|
else
|
||||||
|
expected_to_fail=0
|
||||||
|
ZTST_diff=ZTST_diff
|
||||||
|
fi
|
||||||
|
|
||||||
|
# First check we got the right status, if specified.
|
||||||
|
if [[ $ZTST_xstatus != - && $ZTST_xstatus != $ZTST_status ]]; then
|
||||||
|
if (( expected_to_fail )); then
|
||||||
|
ZTST_verbose 1 "Test failed, as expected."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
ZTST_testfailed "bad status $ZTST_status, expected $ZTST_xstatus from:
|
||||||
|
$ZTST_code${$(<$ZTST_terr):+
|
||||||
|
Error output:
|
||||||
|
$(<$ZTST_terr)}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ZTST_verbose 2 "ZTST_test: test produced standard output:
|
||||||
|
$(<$ZTST_tout)
|
||||||
|
ZTST_test: and standard error:
|
||||||
|
$(<$ZTST_terr)"
|
||||||
|
|
||||||
|
# Now check output and error.
|
||||||
|
if [[ $ZTST_flags = *q* && -s $ZTST_out ]]; then
|
||||||
|
substlines="$(<$ZTST_out)"
|
||||||
|
rm -rf $ZTST_out
|
||||||
|
print -r -- "${(e)substlines}" >$ZTST_out
|
||||||
|
fi
|
||||||
|
if [[ $ZTST_flags != *d* ]] && ! $ZTST_diff $diff_out -u $ZTST_out $ZTST_tout; then
|
||||||
|
if (( expected_to_fail )); then
|
||||||
|
ZTST_verbose 1 "Test failed, as expected."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
ZTST_testfailed "output differs from expected as shown above for:
|
||||||
|
$ZTST_code${$(<$ZTST_terr):+
|
||||||
|
Error output:
|
||||||
|
$(<$ZTST_terr)}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if [[ $ZTST_flags = *q* && -s $ZTST_err ]]; then
|
||||||
|
substlines="$(<$ZTST_err)"
|
||||||
|
rm -rf $ZTST_err
|
||||||
|
print -r -- "${(e)substlines}" >$ZTST_err
|
||||||
|
fi
|
||||||
|
if [[ $ZTST_flags != *D* ]] && ! $ZTST_diff $diff_err -u $ZTST_err $ZTST_terr; then
|
||||||
|
if (( expected_to_fail )); then
|
||||||
|
ZTST_verbose 1 "Test failed, as expected."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
ZTST_testfailed "error output differs from expected as shown above for:
|
||||||
|
$ZTST_code"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
if (( expected_to_fail )); then
|
||||||
|
ZTST_testfailed "test was expected to fail, but passed."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
ZTST_verbose 1 "Test successful."
|
||||||
|
[[ -n $last ]] && break
|
||||||
|
done
|
||||||
|
|
||||||
|
ZTST_verbose 2 "ZTST_test: all tests successful"
|
||||||
|
|
||||||
|
# reset message to keep ZTST_testfailed output correct
|
||||||
|
ZTST_message=''
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Remember which sections we've done.
|
||||||
|
typeset -A ZTST_sects
|
||||||
|
ZTST_sects=(prep 0 test 0 clean 0)
|
||||||
|
|
||||||
|
print "$ZTST_testname: starting."
|
||||||
|
|
||||||
|
# Now go through all the different sections until the end.
|
||||||
|
# prep section may set ZTST_unimplemented, in this case the actual
|
||||||
|
# tests will be skipped
|
||||||
|
ZTST_skipok=
|
||||||
|
ZTST_unimplemented=
|
||||||
|
while [[ -z "$ZTST_unimplemented" ]] && ZTST_getsect $ZTST_skipok; do
|
||||||
|
case $ZTST_cursect in
|
||||||
|
(prep) if (( ${ZTST_sects[prep]} + ${ZTST_sects[test]} + \
|
||||||
|
${ZTST_sects[clean]} )); then
|
||||||
|
ZTST_testfailed "\`prep' section must come first"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
ZTST_prepclean
|
||||||
|
ZTST_sects[prep]=1
|
||||||
|
;;
|
||||||
|
(test)
|
||||||
|
if (( ${ZTST_sects[test]} + ${ZTST_sects[clean]} )); then
|
||||||
|
ZTST_testfailed "bad placement of \`test' section"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# careful here: we can't execute ZTST_test before || or &&
|
||||||
|
# because that affects the behaviour of traps in the tests.
|
||||||
|
ZTST_test
|
||||||
|
(( $? )) && ZTST_skipok=1
|
||||||
|
ZTST_sects[test]=1
|
||||||
|
;;
|
||||||
|
(clean)
|
||||||
|
if (( ${ZTST_sects[test]} == 0 || ${ZTST_sects[clean]} )); then
|
||||||
|
ZTST_testfailed "bad use of \`clean' section"
|
||||||
|
else
|
||||||
|
ZTST_prepclean 1
|
||||||
|
ZTST_sects[clean]=1
|
||||||
|
fi
|
||||||
|
ZTST_skipok=
|
||||||
|
;;
|
||||||
|
*) ZTST_testfailed "bad section name: $ZTST_cursect"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "$ZTST_unimplemented" ]]; then
|
||||||
|
print "$ZTST_testname: skipped ($ZTST_unimplemented)"
|
||||||
|
ZTST_testfailed=2
|
||||||
|
elif (( ! $ZTST_testfailed )); then
|
||||||
|
print "$ZTST_testname: all tests successful."
|
||||||
|
fi
|
||||||
|
ZTST_cleanup
|
||||||
|
exit $(( ZTST_testfailed ))
|
||||||
Loading…
x
Reference in New Issue
Block a user