Repository: gazorby/fifc Branch: main Commit: a01650cd432b Files: 50 Total size: 46.0 KB Directory structure: gitextract__9qpmax8/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows/ │ └── ci.yml ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── completions/ │ └── fifc.fish ├── conf.d/ │ └── fifc.fish ├── cz.yaml ├── functions/ │ ├── _fifc.fish │ ├── _fifc_action.fish │ ├── _fifc_completion_group.fish │ ├── _fifc_expand_tilde.fish │ ├── _fifc_file_type.fish │ ├── _fifc_help.fish │ ├── _fifc_open_cmd.fish │ ├── _fifc_open_dir.fish │ ├── _fifc_open_file.fish │ ├── _fifc_open_fn.fish │ ├── _fifc_open_opt.fish │ ├── _fifc_open_process.fish │ ├── _fifc_parse_complist.fish │ ├── _fifc_parse_pid.fish │ ├── _fifc_path_to_complete.fish │ ├── _fifc_preview_cmd.fish │ ├── _fifc_preview_dir.fish │ ├── _fifc_preview_file.fish │ ├── _fifc_preview_file_default.fish │ ├── _fifc_preview_fn.fish │ ├── _fifc_preview_opt.fish │ ├── _fifc_preview_process.fish │ ├── _fifc_source_directories.fish │ ├── _fifc_source_files.fish │ ├── _fifc_test_version.fish │ └── fifc.fish └── tests/ ├── _resources/ │ ├── dir with spaces/ │ │ ├── file 1.txt │ │ ├── file 2.txt │ │ └── file.json │ └── target.txt ├── test_exposed_vars.fish ├── test_fifc.fish ├── test_file_type.fish ├── test_group.fish ├── test_match_order.fish ├── test_open_cmd.fish ├── test_open_file.fish ├── test_preview_file.fish ├── test_source.fish └── version_test.fish ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: "" labels: bug assignees: Gazorby --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **System (please complete the following information):** - fish version - Distribution `cat /etc/os-release` **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: "" 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. ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: [main] pull_request: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: jobs: tests: runs-on: ${{ matrix.os }} strategy: matrix: os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v3 - uses: fish-actions/install-fish@v1.1.0 - name: Install abbreviation-tips with Fisher uses: fish-shop/install-plugin@v1 with: plugin-manager: fisher plugins: gazorby/fifc - name: Set up Homebrew uses: Homebrew/actions/setup-homebrew@master # Homebrew is only missing from ubuntu images per https://github.com/actions/runner-images/issues/6283 if: matrix.os == 'ubuntu-latest' - name: Install fzf and fd run: brew install fzf fd - name: Run Fishtape tests uses: fish-shop/run-fishtape-tests@v1 with: pattern: tests/**.fish # timeout in case tests get stuck on fzf timeout-minutes: 3 syntax-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: fish-actions/install-fish@v1 - uses: fish-actions/syntax-check@v1 # check Fish format format-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: fish-actions/install-fish@v1 - uses: fish-actions/format-check@v1 # check Markdown and Yaml format prettier: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actionsx/prettier@v2 with: args: --check . ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: check-toml - id: mixed-line-ending - id: check-case-conflict - id: check-merge-conflict - id: check-docstring-first - id: check-added-large-files - id: check-executables-have-shebangs - id: debug-statements - id: end-of-file-fixer - id: fix-byte-order-marker - id: trailing-whitespace - repo: https://github.com/hugoh/pre-commit-fish.git rev: v1.2 hooks: - id: fish_syntax - id: fish_indent ================================================ FILE: CHANGELOG.md ================================================ ## v0.1.1 (2023-03-05) ### Fix - **directories**: escape name with spaces instead of quoting them ### Perf - improve completion group determination ## v0.1.0 (2023-01-23) ### Feat - use `$EDITOR` as default editor ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2022 Matthieu MN 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. ================================================ FILE: README.md ================================================
# fifc 🐠 _fish fzf completions_ [![CI](https://github.com/gazorby/fifc/actions/workflows/ci.yml/badge.svg)](https://github.com/gazorby/fifc/actions/workflows/ci.yml) fifc brings fzf powers on top of fish completion engine and allows customizable completion rules
![gif usage](../assets/demo.gif) ## ✅ Requirements - [fish](https://github.com/fish-shell/fish-shell) 3.4.0+ ## ✨ Features - Preview/open any file: text, image, gif, pdf, archive, binary (using external tools) - Preview/open command's man page - Preview/open function definitions - Preview/open full option description when completing commands - Recursively search for files and folders when completing paths (using [fd](https://github.com/sharkdp/fd)) - Preview directory content - Preview process trees (using [procs](https://github.com/dalance/procs)) - Modular: easily add your own completion rules - Properly handle paths with spaces (needs fish 3.4+) ## 🚀 Install ```fish fisher install gazorby/fifc ``` ## 🔧 Usage You only need to set one setting after install: ```fish set -Ux fifc_editor ``` And enjoy built-in completions! By default fifc override `tab`, but you can assign another keybinding: ```fish # Bind fzf completions to ctrl-x set -U fifc_keybinding \cx ``` fifc can use modern tools if available: | Prefer | Fallback to | Used for | Custom options | | ------------------------------------------------ | ----------- | ----------------------------------------- | ------------------ | | [bat](https://github.com/sharkdp/bat) | cat | Preview files | `$fifc_bat_opts` | | [chafa](https://github.com/hpjansson/chafa) | file | Preview images, gif, pdf etc | `$fifc_chafa_opts` | | [hexyl](https://github.com/sharkdp/hexyl) | file | Preview binaries | `$fifc_hexyl_opts` | | [fd](https://github.com/sharkdp/fd) | find | Complete paths | `$fifc_fd_opts` | | [exa](https://github.com/ogham/exa) | ls | Preview directories | `$fifc_exa_opts` | | [ripgrep](https://github.com/BurntSushi/ripgrep) | pcregrep | Search options in man pages | - | | [procs](https://github.com/dalance/procs) | ps | Complete processes and preview their tree | `$fifc_procs_opts` | | [broot](https://github.com/Canop/broot) | - | Explore directory trees | `$fifc_broot_opts` | Custom options can be added for any of the commands used by fifc using the variable mentioned in the above table. Example: Show line number when previewing files: - `set -U fifc_bat_opts --style=numbers` Show hidden file by default: - `set -U fifc_fd_opts --hidden` ⚠️ Don't use quotes in variables, set them as a list: `set -U fifc_exa_opts --icons --tree` ## 🛠️ Write your own rules Custom rules can easily be added using the `fifc` command. Actually, all builtin rules are added this way: see [conf.d/fifc.fish](https://github.com/gazorby/fifc/blob/52ff966511ea97ed7be79db469fe178784e22fd8/conf.d/fifc.fish) See `fifc -h` for more details. Basically, a rule allows you to trigger some commands based on specific conditions. A condition can be either: - A regex that must match commandline before the cursor position - An arbitrary command that must exit with a non-zero status If conditions are met, you can bind custom commands: - **preview:** Command used for fzf preview - **source:** Command that feeds fzf input - **open:** Command binded to `fifc_open_keybinding` (defaults to ctrl-o) All commands have access to the following variable describing the completion context: | Variable | Description | Command availability | | ------------------ | -------------------------------------------------------------------------------------------------- | -------------------- | | `fifc_candidate` | Currently selected item in fzf | all except source | | `fifc_commandline` | Commandline part before the cursor position | all | | `fifc_token` | Last token from the commandline | all | | `fifc_group` | Group to which fish suggestions belong (possible values: directories, files, options or processes) | all | | `fifc_extracted` | Extracted string from the currently selected item using the `extracted` regex, if any | all except source | | `fifc_query` | fzf query. On source command, it is the initial fzf query (passed through `--query` option) | all | ### **fifc_group** values fifc test completion items to set `fifc_group` with the following conditions: | Group | Condition | | ----------- | ------------------------------------------------------------ | | directories | All completion items are directories | | files | Items can be either files _or_ directories | | options | All items match the following regex: `\h+\-+\h*$` | | processes | All items match the following regex `^[0-9]+$` (list of PID) | ### Matching order By default, fifc evaluate all rules in the order in which they have been defined and stops at the first where all conditions are met. It does this each time it has to resolve source, preview and open commands. Take the following scenario: ```fish # Rule 1 fifc -n 'test "$fifc_group" = files' -p 'bat $fifc_candidate' # Rule 2 fifc -n 'string match "*.json" "$fifc_candidate"' -p 'bat -l json $fifc_candidate' ``` When completing path, `$fifc_group` will be set to "files" so the first rule will always be valid in that case, and the second one will never be reached. Another example: ```fish # Rule 1 fifc --condition 'test "$fifc_group" = files' --preview 'bat $fifc_candidate' # Rule 2 fifc --condition 'test "$fifc_group" = files' --source 'fd . --color=always --hidden $HOME' ``` Here, even if both rules have the same conditions, they won't interfere because fifc has to resolve source commands _before_ the preview commands, so order doesn't matter in this case. ### Override builtin rules If you want to write your own rule based on the same conditions as one of the built-in ones, you can use fifc `--order` option. It tells fifc to evaluate the rule in a predefined order, so you can set it to 1 to make sure it will be evaluated first. When omitting the `--order`, the rule will be declared unordered and will be evaluated _after_ all other ordered rules, and all other unordered rules defined before. All built-in rules are unordered. ### Examples Here is how the built-in rule for file preview/open is implemented: ```fish fifc \ # If selected item is a file -n 'test -f "$fifc_candidate"' \ # bind `_fifc_preview_file` to preview command -p _fifc_preview_file \ # and `_fifc_preview_file` when pressing ctrl-o -o _fifc_open_file ``` Interactively search packages in archlinux: ```fish fifc \ -r '^(pacman|paru)(\\h*\\-S)?\\h+' \ -s 'pacman --color=always -Ss "$fifc_token" | string match -r \'^[^\\h+].*\'' \ -e '.*/(.*?)\\h.*' \ -f "--query ''" \ -p 'pacman -Si "$fifc_extracted"' ``` ![gif usage](../assets/pacman.gif) Search patterns in files and preview matches when commandline starts with `**` (using [ripgrep](https://github.com/burntsushi/ripgrep) and [batgrep](https://github.com/eth-p/bat-extras/blob/master/doc/batgrep.md#bat-extras-batgrep)): ```fish fifc \ -r '.*\*{2}.*' \ -s 'rg --hidden -l --no-messages (string match -r -g \'.*\*{2}(.*)\' "$fifc_commandline")' \ -p 'batgrep --color --paging=never (string match -r -g \'.*\*{2}(.*)\' "$fifc_commandline") "$fifc_candidate"' \ -f "--query ''" \ -o 'batgrep --color (string match -r -g \'.*\*{2}(.*)\' "$fifc_commandline") "$fifc_candidate" | less -R' \ -O 1 ``` ![gif usage](../assets/batgrep.gif) ## ❤️ Credits Thanks [PatrickF1](https://github.com/PatrickF1) (and collaborators!), for the great [fzf.fish](https://github.com/PatrickF1/fzf.fish) plugin which inspired me for the command-based configuration, and from which I copied the ci workflow. ## 📝 License [MIT](https://github.com/gazorby/fifc/blob/87c965fe42a5b2ddb6e0ea84871ca56651db1cb2/LICENSE) ================================================ FILE: completions/fifc.fish ================================================ complete fifc --long condition --short -n --description "Command or fish function to be evaluated" complete fifc --long regex --short r --description "Regex matching the part of the commandline before cursor" complete fifc --long extract --short e --description "Regex applied on the sele cted candidate(s) to extract string to bo appended to the commandline (using capture groups)" complete fifc --long order --short O --description "Order in which this rule must be evaluated" complete fifc --long preview --short p --description "Preview command to be evaluated on the current candidate" complete fifc --long open --short o --description "Open command to be evaluated on the current candidate" complete fifc --long fzf-options --short f --description "Custom fzf options" ================================================ FILE: conf.d/fifc.fish ================================================ # Private set -gx _fifc_comp_count 0 set -gx _fifc_unordered_comp set -gx _fifc_ordered_comp if status is-interactive # Keybindings set -qU fifc_keybinding or set -U fifc_keybinding \t set -qU fifc_open_keybinding or set -U fifc_open_keybinding ctrl-o for mode in default insert bind --mode $mode \t _fifc bind --mode $mode $fifc_keybinding _fifc end # Set sources rules fifc \ -n 'test "$fifc_group" = "directories"' \ -s _fifc_source_directories fifc \ -n 'test "$fifc_group" = "files"' \ -s _fifc_source_files fifc \ -n 'test "$fifc_group" = processes' \ -s 'ps -ax -o pid=,command=' end # Load fifc preview rules only when fish is launched fzf if set -q _fifc_launched_by_fzf # Builtin preview/open commands fifc \ -n 'test "$fifc_group" = "options"' \ -p _fifc_preview_opt \ -o _fifc_open_opt fifc \ -n 'test \( -n "$fifc_desc" -o -z "$fifc_commandline" \); and type -q -f -- "$fifc_candidate"' \ -r '^(?!\\w+\\h+)' \ -p _fifc_preview_cmd \ -o _fifc_open_cmd fifc \ -n 'test -n "$fifc_desc" -o -z "$fifc_commandline"' \ -r '^(functions)?\\h+' \ -p _fifc_preview_fn \ -o _fifc_open_fn fifc \ -n 'test -f "$fifc_candidate"' \ -p _fifc_preview_file \ -o _fifc_open_file fifc \ -n 'test -d "$fifc_candidate"' \ -p _fifc_preview_dir \ -o _fifc_open_dir fifc \ -n 'test "$fifc_group" = processes -a (ps -p (_fifc_parse_pid "$fifc_candidate") &>/dev/null)' \ -p _fifc_preview_process \ -o _fifc_open_process \ -e '^\\h*([0-9]+)' end # Fisher function _fifc_uninstall --on-event fifc_uninstall end ================================================ FILE: cz.yaml ================================================ --- commitizen: annotated_tag: true bump_message: v$new_version name: cz_conventional_commits style: - - qmark - fg:#ff9d00 bold - - question - bold - - answer - fg:#ff9d00 bold - - pointer - fg:#ff9d00 bold - - highlighted - fg:#ff9d00 bold - - selected - fg:#cc5454 - - separator - fg:#cc5454 - - instruction - "" - - text - "" - - disabled - fg:#858585 italic tag_format: v$version version: 0.1.1 ================================================ FILE: functions/_fifc.fish ================================================ function _fifc set -f --export SHELL (command --search fish) set -l result set -Ux _fifc_extract_regex set -gx _fifc_complist_path (string join '' (mktemp) "_fifc") set -gx _fifc_custom_fzf_opts set -gx fifc_extracted set -gx fifc_commandline set -gx fifc_token (commandline --current-token) set -gx fifc_query "$fifc_token" # Get commandline buffer if test "$argv" = "" set fifc_commandline (commandline --cut-at-cursor) else set fifc_commandline $argv end if _fifc_test_version "$FISH_VERSION" -ge "3.4" set complete_opts --escape end complete -C $complete_opts -- "$fifc_commandline" | string split '\n' >$_fifc_complist_path set -gx fifc_group (_fifc_completion_group) set source_cmd (_fifc_action source) set fifc_fzf_query (string trim --chars '\'' -- "$fifc_fzf_query") set -l fzf_cmd " _fifc_launched_by_fzf=1 SHELL=fish fzf \ -d \t \ --exact \ --tiebreak=length \ --select-1 \ --exit-0 \ --ansi \ --tabstop=4 \ --multi \ --reverse \ --header '$header' \ --preview '_fifc_action preview {} {q}' \ --bind='$fifc_open_keybinding:execute(_fifc_action open {} {q} &> /dev/tty)' \ --query '$fifc_query' \ $_fifc_custom_fzf_opts" set -l cmd (string join -- " | " $source_cmd $fzf_cmd) # We use eval hack because wrapping source command # inside a function cause some delay before fzf to show up eval $cmd | while read -l token # don't escape '~' for path, `$` for environ if string match --quiet '~*' -- $token set -a result (string join -- "" "~" (string sub --start 2 -- $token | string escape)) else if string match --quiet '$*' -- $token set -a result (string join -- "" "\$" (string sub --start 2 -- $token | string escape)) else set -a result (string escape --no-quoted -- $token) end # Perform extraction if needed if test -n "$_fifc_extract_regex" set result[-1] (string match --regex --groups-only -- "$_fifc_extract_regex" "$token") end end # Add space trailing space only if: # - there is no trailing space already present # - Result is not a directory # We need to unescape $result for directory test as we escaped it before if test (count $result) -eq 1; and not test -d (string unescape -- $result[1]) set -l buffer (string split -- "$fifc_commandline" (commandline -b)) if not string match -- ' *' "$buffer[2]" set -a result '' end end if test -n "$result" commandline --replace --current-token -- (string join -- ' ' $result) end commandline --function repaint rm $_fifc_complist_path # Clean state set -e _fifc_extract_regex set -e _fifc_custom_fzf_opts set -e _fifc_complist_path set -e fifc_token set -e fifc_group set -e fifc_extracted set -e fifc_candidate set -e fifc_commandline set -e fifc_query end ================================================ FILE: functions/_fifc_action.fish ================================================ function _fifc_action # Can be either "preview", "open" or "source" set -l action $argv[1] set -l comp $_fifc_ordered_comp $_fifc_unordered_comp set -l regex_val (string escape --style=regex -- "$argv[2]") # Escape '/' for sed processing set regex_val (string replace '/' '\/' --all "$regex_val") # Variables exposed to evaluated commands set -x fifc_desc (sed -nr (printf 's/^%s[[:blank:]]+(.*)/\\\1/p' "$regex_val") $_fifc_complist_path | string trim) set -x fifc_candidate "$argv[2]" set -x fifc_extracted (string match --regex --groups-only -- "$_fifc_extract_regex" "$argv[2]") if test "$action" = preview set default_preview 1 set fifc_query "$argv[3]" else if test "$action" = open set fifc_query "$argv[3]" else if test "$action" = source set default_source 1 end for i in (seq (count $comp)) set -l condition_cmd set -l regex_cmd set -l valid 1 if test -n "$$comp[$i][1]" set condition_cmd "$$comp[$i][1]" else set condition_cmd true end if test -n "$$comp[$i][2]" set -l val (string escape -- "$fifc_commandline") set regex_cmd "string match --regex --quiet -- '$$comp[$i][2]' $val" else set regex_cmd true end if not eval "$condition_cmd; and $regex_cmd" set valid 0 continue end set _fifc_extract_regex "$$comp[$i][7]" if test "$action" = preview; and test -n "$$comp[$i][3]" eval $$comp[$i][3] set default_preview 0 break else if test "$action" = open; and test -n "$$comp[$i][4]" eval $$comp[$i][4] break else if test "$action" = source; and test -n "$$comp[$i][5]" set _fifc_custom_fzf_opts "$$comp[$i][6]" if functions "$$comp[$i][5]" 1>/dev/null eval $$comp[$i][5] else echo $$comp[$i][5] end set default_source 0 break end end # We are in preview mode, but nothing matched # fallback to fish description if test "$default_preview" = 1 echo "$fifc_desc" else if test "$default_source" = 1 echo _fifc_parse_complist end end ================================================ FILE: functions/_fifc_completion_group.fish ================================================ function _fifc_completion_group -d "Determine completion group" set -l path_candidate (_fifc_path_to_complete) # Null means that either $path is empty or is not a directory set -l is_null (ls -A -- $path_candidate 2> /dev/null | string collect) set -l complist (string escape -- (_fifc_expand_tilde (_fifc_parse_complist))) # Directories set -l dir_test "test -d $complist[1]" set dir_test (string join -- " -a -d " $dir_test $complist[2..-1]) if test -n "$is_null"; and eval "$dir_test" echo directories # Files # When complist is big, avoid calling ls with all arguments if first is neither a file nor a directory else if test -n "$is_null"; and echo $complist | xargs ls -d -- &>/dev/null echo files # Options else if string match --regex --quiet -- '\h+\-+\h*$' $fifc_commandline set -e fifc_query echo options # PIDs else if string join -- '' $complist | string match --regex --quiet '^[0-9]+$' echo processes end end ================================================ FILE: functions/_fifc_expand_tilde.fish ================================================ function _fifc_expand_tilde string replace --regex -- '^~' "$HOME" $argv end ================================================ FILE: functions/_fifc_file_type.fish ================================================ function _fifc_file_type -d "Figure out file type (txt, json, image, pdf, archive, binary)" set -l mime (file --mime-type -b "$argv") set -l binary 0 if string match --quiet '*binary*' -- (file --mime -b -L "$argv") set binary 1 end switch $mime case application/{gzip,java-archive,x-{7z-compressed,bzip2,chrome-extension,rar,tar,xar},zip} echo archive return case "image/*" echo image return end if test $binary = 1 echo binary else switch $mime case application/pdf echo pdf case application/json echo json case "*" echo txt end end end ================================================ FILE: functions/_fifc_help.fish ================================================ function __fifc_help_print argparse -s "i/indentation=?" "l/level=?" "c/color=?" e/escape n -- $argv set -l spaces "$_flag_i" set -l level "$_flag_l" set -l color "$_flag_c" set -l echo_opt if test -z "$spaces" set spaces 4 end if test -z "$level" set level 1 end if test -n "$_flag_e" set -a echo_opt -e end if test -n "$_flag_n" set -a echo_opt -n end if test -z "$_flag_c" set color -o white end set indent (string repeat --count (math $spaces x $level) " ") set_color $color echo $echo_opt "$indent$argv[1]" for line in $argv[2..-1] set_color $color echo $echo_opt "\n$indent$line" end # end set_color normal end function __fifc_help_section __fifc_help_print -e -l0 --color=yellow (string join -- "" (string upper -- "$argv") "\n") end function __fifc_help_opt set -l opt (string split -- '=' $argv[1]) __fifc_help_print -n -e --color=green -l1 -- "$opt[1]" if test (count $opt) -eq 2 set_color yellow echo "=$opt[2]" else echo "" end set -l desc (string split -- '\n' $argv[2..-1] | string trim) __fifc_help_print -e -l2 -- $desc echo "" end function _fifc_help -d "Print fifc help message" __fifc_help_section NAME __fifc_help_print -e "fifc - Set custom completion rules for use with fifc fish plugin\n" __fifc_help_section SYNOPSIS __fifc_help_print -e "fifc [OPTIONS]\n" __fifc_help_section DESCRIPTION __fifc_help_print -e -n \ "The fifc command allows you to add custom completion rules that can enhance fish completions or override them.\n" \ "A rule is composed of condition(s) that, if valid, trigger commands that can:" \ " - Change fzf preview (-p)" \ " - Feed fzf input (-s)" \ " - Execute when fifc_open_keybinding is pressed (defaults to ctrl-o) (-o)" __fifc_help_print -e "\n\n" __fifc_help_print -e -n \ "A condition can be either:" \ " - A regex that must match commandline before cursor position (-r)" \ " - An arbitrary command that must exit with a non-zero status (-n)" __fifc_help_print -e "\n" __fifc_help_print -e -n \ "Rule are evaluated in the order in which they are defined," \ "and fifc will stop at the first rule where all conditions are met" __fifc_help_print -e "\n\n" __fifc_help_opt \ "-r, --regex=REGEX" \ "Regex that must match commandline preceding the cursor for the rule to be valid" __fifc_help_opt \ "-e, --extract=COMMAND" \ "Regex used to extract string from selected results before appending them to the commandline" __fifc_help_opt \ "-n, --condition=COMMAND" \ "Command or function that must exit with a non-zero status for the rule to be valid" __fifc_help_opt \ "-p, --preview=COMMAND" \ "Preview command passed to fzf if the rule is valid" __fifc_help_opt \ "-s, --source=COMMAND" \ "Command that will feed fzf input if the rule is valid" __fifc_help_opt \ "-o, --open=COMMAND" \ "Command binded to fifc_open_keybinding (defaults to ctrl-o) when using fzf" __fifc_help_opt \ "-O, --order=INT" \ "The order in which the rule is evaluated." \ "If missing, the rule will be evaluated after all ordered ones, and all unordered rules defined before." __fifc_help_opt \ "-f, --fzf-options" \ "Custom fzf options (can override previous ones)" __fifc_help_opt \ "-h, --help" \ "Show this help" __fifc_help_print -e "Examples:\n" __fifc_help_print -e -l2 -- "- Preview files using bat (already builtin):\n" __fifc_help_print -e -l2 --color=white -- \ ' fifc -n \'test -f "$fifc_candidate"\' -p "bat --color=always $fifc_candidate"' __fifc_help_print -e "\n" __fifc_help_print -e -l2 -- "- Use fd to search files recursively (already builtin):\n" __fifc_help_print -e -l2 --color=white -- \ ' fifc -n \'test "$fifc_group" = files\' -s \'fd . --color=always --strip-cwd-prefix\'' __fifc_help_print -e "\n" __fifc_help_print -e -l2 -- "- Interactively search packages on archlinux:\n" __fifc_help_print -e -n -l2 --color=white -- \ " fifc \\" \ " -r '^pacman(\h*\-S)?\h+\w+' \\" \ " -s 'pacman --color=always -Ss "\$fifc_token" | string match -r \"^[^\h+].*\"' \\" \ " -e '.*/(.*?)\h.*' \\" \ " -f '--query \"\"' \\" \ " -p 'pacman -Si "\$fifc_extracted"' \\ \n" end ================================================ FILE: functions/_fifc_open_cmd.fish ================================================ function _fifc_open_cmd _fifc_preview_cmd $fifc_candidate end ================================================ FILE: functions/_fifc_open_dir.fish ================================================ function _fifc_open_dir if type -q broot broot --color=yes $fifc_broot_opts $fifc_candidate end end ================================================ FILE: functions/_fifc_open_file.fish ================================================ function _fifc_open_file -d "Open a file with the right tool depending on its type" set -l filepath "$fifc_candidate" if test -n "$argv" set filepath "$argv" end set -q fifc_editor || set -l fifc_editor "$EDITOR" set -l file_type (_fifc_file_type "$filepath") switch $file_type case txt json archive $fifc_editor "$filepath" case image if type -q chafa chafa --watch $fifc_chafa_opts "$filepath" else $fifc_editor "$filepath" end case binary if type -q hexyl hexyl $fifc_hexyl_opts "$filepath" | less --RAW-CONTROL-CHARS else $fifc_editor "$filepath" end end end ================================================ FILE: functions/_fifc_open_fn.fish ================================================ function _fifc_open_fn -d "Open a function definition using open file wrapper" set -l pathname (functions --details $fifc_candidate 2>/dev/null) if test -f $pathname _fifc_open_file $pathname end end ================================================ FILE: functions/_fifc_open_opt.fish ================================================ function _fifc_open_opt -d "Open a man page starting at the selected option" set -l cmd (string match --regex --groups-only -- '(^|\h+)(\w+) ?-*$' "$fifc_commandline") set -l opt (string trim --chars '\n ' -- "$fifc_candidate") set -l regex "^\h*(-+[^\n]*)*$opt" if type -q bat man $cmd \ | bat --color=always --language man $fifc_bat_opts \ # --RAW-CONTROL-CHARS allow color output of bat to be displayed | less --RAW-CONTROL-CHARS --pattern "$regex" else man $cmd | less --pattern "$regex" end end ================================================ FILE: functions/_fifc_open_process.fish ================================================ function _fifc_open_process -d "Open the tree view of the selected process (procs only)" set -l pid (_fifc_parse_pid "$fifc_candidate") if type -q procs procs --color=always --tree --pager=always $fifc_procs_opts "$pid" end end ================================================ FILE: functions/_fifc_parse_complist.fish ================================================ function _fifc_parse_complist -d "Extract the first column of fish completion list" cat $_fifc_complist_path \ | string unescape \ | uniq \ | awk -F '\t' '{ print $1 }' end ================================================ FILE: functions/_fifc_parse_pid.fish ================================================ function _fifc_parse_pid -d "Extract pid at the beginning of ps output lines" string match --regex --groups-only -- "^\h*([0-9]+)" "$argv" end ================================================ FILE: functions/_fifc_path_to_complete.fish ================================================ function _fifc_path_to_complete set -l token (string unescape -- $fifc_token) if string match --regex --quiet -- '.*(\w|\.|/)+$' "$token" _fifc_expand_tilde "$token" else echo {$PWD}/ end end ================================================ FILE: functions/_fifc_preview_cmd.fish ================================================ function _fifc_preview_cmd -d "Open man page of the selected command" if type -q bat man $fifc_candidate 2>/dev/null | bat --color=always --language man $fifc_bat_opts else man $fifc_candidate 2>/dev/null end end ================================================ FILE: functions/_fifc_preview_dir.fish ================================================ function _fifc_preview_dir -d "List content of the selected directory" if type -q exa exa --color=always $fifc_exa_opts $fifc_candidate else ls --color=always $fifc_ls_opts $fifc_candidate end end ================================================ FILE: functions/_fifc_preview_file.fish ================================================ function _fifc_preview_file -d "Preview the selected file with the right tool depending on its type" set -l file_type (_fifc_file_type "$fifc_candidate") switch $file_type case txt if type -q bat bat --color=always $fifc_bat_opts "$fifc_candidate" else cat "$fifc_candidate" end case json if type -q bat bat --color=always -l json $fifc_bat_opts "$fifc_candidate" else cat "$fifc_candidate" end case image pdf if type -q chafa chafa $fifc_chafa_opts "$fifc_candidate" else _fifc_preview_file_default "$fifc_candidate" end case archive if type -q 7z 7z l ""$fifc_candidate"" | tail -n +17 | awk '{ print $6 }' else _fifc_preview_file_default "$fifc_candidate" end case binary if type -q hexyl hexyl $fifc_hexyl_opts "$fifc_candidate" else _fifc_preview_file_default "$fifc_candidate" end end end ================================================ FILE: functions/_fifc_preview_file_default.fish ================================================ function _fifc_preview_file_default -d "Display informations about the selected file" set -l mime (file --mime-type -b "$argv") set_color brgreen echo -e "$mime[1]\n" set_color --bold white file -b "$argv" set_color normal end ================================================ FILE: functions/_fifc_preview_fn.fish ================================================ function _fifc_preview_fn -d "Preview the function definition" if type -q bat type $fifc_candidate | bat --color=always --language fish $fifc_bat_opts else type $fifc_candidate end end ================================================ FILE: functions/_fifc_preview_opt.fish ================================================ function _fifc_preview_opt -d "Open man page of a command starting at the selected option" set -l regex "(?s)^(\-+[^\n]+)*$fifc_candidate([^\-\w\.]([^\.\n]|\.{2,}|\w+\.)*|)\n{1,2}.*?(^(\-+[^\n]+|\w+))" set -l regex_replace '^\h+(\-+[^\n]+.*)' set -l cmd (string match --regex --groups-only -- '(^|\h+)(\w+) ?-*$' $fifc_commandline) echo $group set out (man $cmd 2>/dev/null | string replace -r $regex_replace '$1' \ | begin if type -q rg rg --multiline $regex else if type -q pcre2grep pcre2grep --multiline $regex else pcregrep --multiline $regex end end \ # Remove last line as it should describes the next option | awk 'n>=1 { print a[n%1] } { a[n%1]=$0; n=n+1 }' \ | string trim \ ) # Fallback to fish description if there is no man page if test -z "$out" echo "$fifc_desc" end # Pretty printing set_color brgreen echo -e "$out[1]" set_color --bold white echo -e (string join -- "\n" "" $out[2..-1]) set_color normal end ================================================ FILE: functions/_fifc_preview_process.fish ================================================ function _fifc_preview_process -d "Preview process informations" set -l pid (_fifc_parse_pid "$fifc_candidate") set -l err_msg "\nThe process exited" if type -q procs procs --color=always --tree $fifc_procs_opts "$pid" else set -l ps_preview_fmt (string join ',' 'pid' 'ppid=PARENT' 'user' '%cpu' 'rss=RSS_IN_KB' 'start=START_TIME' 'command') ps -o "$ps_preview_fmt" -p "$pid" 2>/dev/null end if not ps -p "$pid" &>/dev/null set_color yellow echo -e "$err_msg" end end ================================================ FILE: functions/_fifc_source_directories.fish ================================================ function _fifc_source_directories -d "Return a command to recursively find directories" if type -q fd set --local --export fifc_fd_opts -t d _fifc_source_files else set --local --export fifc_find_opts -type d _fifc_source_files end end ================================================ FILE: functions/_fifc_source_files.fish ================================================ function _fifc_source_files -d "Return a command to recursively find files" set -l path (_fifc_path_to_complete | string escape) set -l hidden (string match "*." "$path") if string match --quiet -- '~*' "$fifc_query" set -e fifc_query end if type -q fd if _fifc_test_version (fd --version) -ge "8.3.0" set fd_custom_opts --strip-cwd-prefix end if test "$path" = {$PWD}/ echo "fd . $fifc_fd_opts --color=always $fd_custom_opts" else if test "$path" = "." echo "fd . $fifc_fd_opts --color=always --hidden $fd_custom_opts" else if test -n "$hidden" echo "fd . $fifc_fd_opts --color=always --hidden -- $path" else echo "fd . $fifc_fd_opts --color=always -- $path" end else if test -n "$hidden" # Use sed to strip cwd prefix echo "find . $path $fifc_find_opts ! -path . -print 2>/dev/null | sed 's|^\./||'" else # Exclude hidden directories echo "find . $path $fifc_find_opts ! -path . ! -path '*/.*' -print 2>/dev/null | sed 's|^\./||'" end end ================================================ FILE: functions/_fifc_test_version.fish ================================================ function _fifc_test_version -d "Compare version numbers" set -l arg_1 (string replace --regex --all '[^\d]' '' -- "$argv[1]") set -l arg_2 (string replace --regex --all '[^\d]' '' -- "$argv[3]") set -l op "$argv[2]" set -l v_diff (math (string length -- $arg_1) - (string length -- $arg_2)) # Ensure both versions are the same length if test $v_diff -gt 0 set arg_2 (string join '' -- "$arg_2" (string repeat -N -n $v_diff '0')) else if test $v_diff -lt 0 set v_diff (math abs $v_diff) set arg_1 (string join '' -- "$arg_1" (string repeat -N -n $v_diff '0')) end set -l cmd (string collect -- "test " "$arg_1" " $op " "$arg_2") eval $cmd end ================================================ FILE: functions/fifc.fish ================================================ function __fifc_check_flag -d "Check flag value for fifc" switch $_flag_name case O order if not test 0 -lt $_flag_value echo "$_argparse_cmd: Order must be a positive integer" return 1 end # Ensure regex is valid case r regex set -l out (string match --regex --quiet $_flag_value 2>&1 | string join '\n') if test -n "$out" echo -e "$_argparse_cmd:\n$out" end end end function fifc -d "Add your own fish fzf completions" set -l option_spec 'n/condition=' 'p/preview=' 'o/open=' 's/source=' 'e/extract=' 'f/fzf-options=' h/help set -a option_spec 'r/regex=!__fifc_check_flag' 'O/order=!__fifc_check_flag' argparse --name fifc $option_spec -- $argv if test "$status" != 0 return 1 end if test -n "$_flag_h" _fifc_help return end if test \( -n "$_flag_n" -o -n "$_flag_r" \) \ -a \( -z "$_flag_p" -a -z "$_flag_o" -a -z "$_flag_s" -a -z "$_flag_e" -a -z "$_flag_f" \) echo "fifc: You have not specified any binding (preview, open, source or extract)" return 1 end set _fifc_comp_count (math $_fifc_comp_count + 1) set -l count $_fifc_comp_count # Ensure completion vars are empty before setting them set -e "_fifc_comp_$count" set -gx "_fifc_comp_$count" set -a "_fifc_comp_$count" "$_flag_n" set -a "_fifc_comp_$count" "$_flag_r" set -a "_fifc_comp_$count" "$_flag_p" set -a "_fifc_comp_$count" "$_flag_o" set -a "_fifc_comp_$count" "$_flag_s" set -a "_fifc_comp_$count" "$_flag_f" set -a "_fifc_comp_$count" "$_flag_e" if test -z "$_flag_O" set -a _fifc_unordered_comp "_fifc_comp_$count" else set _fifc_ordered_comp[$_flag_O] "_fifc_comp_$count" end end ================================================ FILE: tests/_resources/dir with spaces/file 1.txt ================================================ foo 1 ================================================ FILE: tests/_resources/dir with spaces/file 2.txt ================================================ foo 2 ================================================ FILE: tests/_resources/dir with spaces/file.json ================================================ {} ================================================ FILE: tests/_resources/target.txt ================================================ foo ================================================ FILE: tests/test_exposed_vars.fish ================================================ set curr_fifc_unordered_comp $_fifc_unordered_comp set dir "tests/_resources/dir with spaces" set _fifc_complist_path (mktemp) function _fifc_test_exposed_vars switch $var case candidate echo -n "$fifc_candidate" case extracted echo -n "$fifc_extracted" case query echo -n "$fifc_query" end end set comp_1 \ 'test -f $fifc_candidate' \ '.*' \ _fifc_test_exposed_vars \ open_cmd \ source_cmd \ --fzf_option \ '.*/(.*\.txt)$' set _fifc_unordered_comp comp_1 set var candidate set actual (_fifc_action "preview" "$dir/file 1.txt") @test "exposed vars fifc_candidate" "$actual" = "$dir/file 1.txt" set var extracted set -x fifc_extracted set _fifc_extract_regex '.*/(.*\.txt)$' set actual (_fifc_action "preview" "$dir/file 1.txt") @test "exposed vars fifc_extracted" "$actual" = "file 1.txt" set var query set -x fifc_query set actual (_fifc_action "preview" "$dir/file 1.txt" "1") @test "exposed vars fifc_query" "$actual" = 1 rm $_fifc_complist_path ================================================ FILE: tests/test_fifc.fish ================================================ set curr_fifc_unordered_comp $_fifc_unordered_comp set _fifc_unordered_comp fifc \ -n 'test -n "$fifc_desc"' \ -r '^functions\h+|^\h+' \ -p _fifc_preview_fn \ -o _fifc_open_fn @test "fifc command completion added" (count $_fifc_unordered_comp) = 1 @test "fifc command completion condition" "$$_fifc_unordered_comp[1][1]" = 'test -n "$fifc_desc"' @test "fifc command completion regex" "$$_fifc_unordered_comp[1][2]" = '^functions\h+|^\h+' @test "fifc command completion preview" "$$_fifc_unordered_comp[1][3]" = _fifc_preview_fn @test "fifc command completion open" "$$_fifc_unordered_comp[1][4]" = _fifc_open_fn # set _fifc_unordered_comp fifc \ -n 'test -n "$fifc_desc"' \ -r '^functions\h+|^\h+' \ -s source_cmd @test "fifc source added" (count $_fifc_unordered_comp) = 2 @test "fifc source condition" "$$_fifc_unordered_comp[2][1]" = 'test -n "$fifc_desc"' @test "fifc source regex" "$$_fifc_unordered_comp[2][2]" = '^functions\h+|^\h+' @test "fifc source command" "$$_fifc_unordered_comp[2][5]" = source_cmd set -gx _fifc_unordered_comp $curr_fifc_unordered_comp ================================================ FILE: tests/test_file_type.fish ================================================ set actual (_fifc_file_type "tests/_resources/dir with spaces/file 71.txt") @test "file type test txt" "$actual" = txt set actual (_fifc_file_type "tests/_resources/dir with spaces/file.json") @test "file type test json" "$actual" = json -o "$actual" = txt set actual (_fifc_file_type "tests/_resources/dir with spaces/fish.png") @test "file type test image" "$actual" = image set actual (_fifc_file_type "tests/_resources/dir with spaces/file.bin") @test "file type test binary" "$actual" = binary set actual (_fifc_file_type "tests/_resources/dir with spaces/file.7z") @test "file type test archive" "$actual" = archive set actual (_fifc_file_type "tests/_resources/dir with spaces/file.pdf") @test "file type test pdf" "$actual" = pdf set actual_target (_fifc_file_type "tests/_resources/target.txt") @test "file type test link target" "$actual_target" = txt set actual_link (_fifc_file_type "tests/_resources/dir with spaces/symlink.txt") @test "file type test link source" "$actual_link" = "$actual_target" ================================================ FILE: tests/test_group.fish ================================================ set _fifc_complist_path (mktemp) set _commandline "kill " complete -C --escape -- "$_commandline" >$_fifc_complist_path set fifc_commandline "$_commandline" set actual (_fifc_completion_group) @test "group test pid" "$actual" = processes set _commandline "ls tests/_resources/dir\ with\ spaces/" complete -C --escape -- "$_commandline" >$_fifc_complist_path set fifc_commandline "$_commandline" set actual (_fifc_completion_group) @test "group test files" "$actual" = files set _commandline "ls -" complete -C --escape -- "$_commandline" >$_fifc_complist_path set fifc_commandline "$_commandline" set actual (_fifc_completion_group) @test "group test options" "$actual" = options set -e _fifc_complist set -e fifc_commandline rm $_fifc_complist_path ================================================ FILE: tests/test_match_order.fish ================================================ set curr_fifc_unordered_comp $_fifc_unordered_comp set curr_fifc_ordered_comp $_fifc_ordered_comp set dir "tests/_resources/dir with spaces" set _fifc_complist_path (mktemp) # Add unordered completions set comp_1 \ 'test -f $fifc_candidate' \ '^cat $' \ 'echo comp_1' \ open_cmd \ source_cmd \ --fzf_option \ extract_regex set comp_2 \ 'test -d $fifc_candidate' \ '^ls $' \ 'echo comp_2' \ open_cmd \ source_cmd \ --fzf_option \ extract_regex set _fifc_unordered_comp comp_1 comp_2 set fifc_commandline "cat " set actual (_fifc_action "preview" "$dir/file 1.txt") @test "preview match condition and regex first completion" "$actual" = comp_1 set fifc_commandline "ls " set actual (_fifc_action "preview" "$dir") @test "preview match condition and regex second completion" "$actual" = comp_2 echo "fallback description" >$_fifc_complist_path set fifc_commandline "fallback " set actual (_fifc_action "preview" 'fallback') @test "preview fallback fish description" "$actual" = description # Add ordered completion, should be evaluated before unordered ones set o_comp_1 \ 'test -f $fifc_candidate' \ '^cat $' \ 'echo o_comp_1' \ open_cmd \ source_cmd_1 \ --fzf_option_1 \ extract_regex_1 set _fifc_ordered_comp o_comp_1 set fifc_commandline "cat " set actual (_fifc_action "preview" "$dir/file 1.txt") @test "preview match condition and regex ordered completion" "$actual" = o_comp_1 set -e fifc_commandline set -gx _fifc_unordered_comp $curr_fifc_unordered_comp set -gx _fifc_ordered_comp $curr_fifc_ordered_comp rm $_fifc_complist_path ================================================ FILE: tests/test_open_cmd.fish ================================================ set fifc_candidate mkdir set fifc_bat_opts '--color=never' set actual (_fifc_open_cmd) set expected (man mkdir) @test "builtin cmd open" "$actual" = "$expected" ================================================ FILE: tests/test_open_file.fish ================================================ set dir "tests/_resources/dir with spaces" set curr_fifc_editor $fifc_editor set fifc_editor cat set fifc_candidate "$dir/file 1.txt" set actual (_fifc_open_file) @test "builtin file open" "$actual" = 'foo 1' set fifc_editor $curr_fifc_editor ================================================ FILE: tests/test_preview_file.fish ================================================ set dir "tests/_resources/dir with spaces" set fifc_candidate "$dir/file 1.txt" set fifc_bat_opts '--color=never' set actual (_fifc_preview_file) @test "builtin file preview" "$actual" = 'foo 1' ================================================ FILE: tests/test_source.fish ================================================ set curr_fifc_unordered_comp $_fifc_unordered_comp set curr_fifc_ordered_comp $_fifc_ordered_comp set _fifc_complist_path (mktemp) # Add unordered sources set comp_1 'test "$any" = "1"' '^kill $' '' '' 'echo comp_1' set comp_2 'test "$any" = "2"' '^ps -p $' '' '' 'echo comp_2' set _fifc_unordered_comp comp_1 comp_2 set fifc_commandline "kill " set any 1 set actual (_fifc_action "source") @test "source match first" "$actual" = "echo comp_1" set fifc_commandline "ps -p " set any 2 set actual (_fifc_action "source") @test "source match second" "$actual" = "echo comp_2" set fifc_commandline "foo " set actual (_fifc_action "source") @test "source fallback fish suggestions" "$actual" = _fifc_parse_complist set -e fifc_commandline set -gx _fifc_unordered_comp $curr_fifc_unordered_comp set -gx _fifc_unordered_comp $curr_fifc_ordered_comp rm $_fifc_complist_path ================================================ FILE: tests/version_test.fish ================================================ set -l actual (_fifc_test_version "3.4" -gt "3.0") @test "version test basic gt" $status = 0 set -l actual (_fifc_test_version "3.4" -ge "3.4") @test "version test basic ge equal" $status = 0 set -l actual (_fifc_test_version "3.5" -ge "3.4") @test "version test basic ge greater" $status = 0 set -l actual (_fifc_test_version "3.4" -lt "3.5") @test "version test basic lt" $status = 0 set -l actual (_fifc_test_version "3.4" -le "3.4") @test "version test basic le equal" $status = 0 set -l actual (_fifc_test_version "3.4" -le "3.5") @test "version test basic le lower" $status = 0 set -l actual (_fifc_test_version "3.4" -gt "3") @test "version test length not equal" $status = 0 set -l actual (_fifc_test_version "3.4" -gt "3") @test "version test length not equal 1" $status = 0 set -l actual (_fifc_test_version "3" -gt "3.4") @test "version test length not equal 2" $status = 1 set -l actual (_fifc_test_version "fish 3.5.0" -gt "3.4.2") @test "version test extract version left" $status = 0 set -l actual (_fifc_test_version "3.5.0" -gt "fish 3.4.2") @test "version test extract version right" $status = 0