Repository: yuki-ycino/fzf-preview.vim Branch: main Commit: 933836744ad5 Files: 295 Total size: 466.7 KB Directory structure: gitextract_mlnhz3k1/ ├── .eslintignore ├── .eslintrc.js ├── .github/ │ ├── ISSUE_TEMPLATE.md │ ├── issue_example/ │ │ └── Dockerfile │ └── workflows/ │ ├── build.yml │ ├── can-release.yml │ ├── release-coc.yml │ ├── release-remote.yml │ └── release-rpc.yml ├── .gitignore ├── .luarc.json ├── .markdownlint.json ├── .prettierrc.json ├── LICENSE ├── README.md ├── autoload/ │ ├── fzf_preview/ │ │ ├── remote/ │ │ │ ├── consumer/ │ │ │ │ ├── git.vim │ │ │ │ └── register.vim │ │ │ ├── exec_fzf.vim │ │ │ ├── handler_to_process.vim │ │ │ ├── mr.vim │ │ │ ├── process.vim │ │ │ ├── resource/ │ │ │ │ ├── all_buffers.vim │ │ │ │ ├── bookmarks.vim │ │ │ │ ├── buffers.vim │ │ │ │ ├── changes.vim │ │ │ │ ├── directory_files.vim │ │ │ │ ├── git_files.vim │ │ │ │ ├── git_status.vim │ │ │ │ ├── grep.vim │ │ │ │ ├── jumps.vim │ │ │ │ ├── lines.vim │ │ │ │ ├── marks.vim │ │ │ │ ├── memolist.vim │ │ │ │ ├── project_files.vim │ │ │ │ ├── quickfix_and_locationlist.vim │ │ │ │ ├── tags.vim │ │ │ │ ├── todo_comments.vim │ │ │ │ ├── util.vim │ │ │ │ ├── vim_command.vim │ │ │ │ ├── vim_lsp.vim │ │ │ │ ├── vista.vim │ │ │ │ └── yankround.vim │ │ │ ├── runner.vim │ │ │ ├── tagstack.vim │ │ │ ├── util.vim │ │ │ ├── variable.vim │ │ │ └── window.vim │ │ ├── rpc/ │ │ │ └── server.vim │ │ └── rpc.vim │ ├── fzf_preview.vim │ └── vital/ │ ├── _fzf_preview/ │ │ ├── Async/ │ │ │ ├── Later.vim │ │ │ └── Promise.vim │ │ └── VS/ │ │ ├── Event/ │ │ │ └── Emitter.vim │ │ ├── RPC/ │ │ │ └── JSON.vim │ │ └── System/ │ │ └── Job.vim │ ├── _fzf_preview.vim │ ├── fzf_preview.vim │ └── fzf_preview.vital ├── bin/ │ ├── git_actions_preview │ ├── git_blame_pr │ └── preview_yankround_register ├── doc/ │ └── fzf_preview_vim.txt ├── jest.config.ts ├── lua/ │ └── fzf-preview/ │ └── init.lua ├── package.json ├── plugin/ │ └── fzf_preview.vim ├── rplugin/ │ └── node/ │ └── fzf-preview.vim/ │ └── .keep ├── scripts/ │ └── preview.js ├── src/ │ ├── @types/ │ │ └── index.d.ts │ ├── args/ │ │ ├── add-fzf-arg-parser.test.ts │ │ ├── add-fzf-arg-parser.ts │ │ ├── directory-files-args-parser.ts │ │ ├── empty-source-func-args-parser.ts │ │ ├── experimental-parser.ts │ │ ├── files-from-resources-parser.ts │ │ ├── grep-args-parser.ts │ │ ├── index.ts │ │ ├── parser.ts │ │ ├── processes-parser.test.ts │ │ ├── processes-parser.ts │ │ ├── resume-parser.ts │ │ └── session-parser.ts │ ├── association/ │ │ ├── coc-command.ts │ │ ├── command.ts │ │ └── vim-variable.ts │ ├── coc.ts │ ├── connector/ │ │ ├── bookmarks.ts │ │ ├── buffers.ts │ │ ├── changes.ts │ │ ├── coc.ts │ │ ├── convert-for-fzf.ts │ │ ├── directory-files.ts │ │ ├── fzf.ts │ │ ├── git.ts │ │ ├── grep.ts │ │ ├── jumps.ts │ │ ├── lines.ts │ │ ├── lsp.ts │ │ ├── marks.ts │ │ ├── memolist.ts │ │ ├── nvim-lsp.ts │ │ ├── old-files.ts │ │ ├── open-bufnr.ts │ │ ├── open-file.ts │ │ ├── project-files.ts │ │ ├── quickfix-and-locationlist.ts │ │ ├── register.ts │ │ ├── resume.ts │ │ ├── tags.ts │ │ ├── todo-comments.ts │ │ ├── util.ts │ │ ├── vim-command.ts │ │ ├── vim-help.ts │ │ ├── vim-lsp.ts │ │ ├── vista.ts │ │ └── yankround.ts │ ├── const/ │ │ ├── fzf-handler.ts │ │ ├── fzf-option.ts │ │ ├── fzf-processes.ts │ │ ├── fzf-resource.ts │ │ ├── fzf-runner.ts │ │ ├── git.ts │ │ ├── module.ts │ │ └── system.ts │ ├── fzf/ │ │ ├── command/ │ │ │ ├── execute-fast.ts │ │ │ ├── execute-normal.ts │ │ │ ├── index.ts │ │ │ └── util.ts │ │ ├── function/ │ │ │ └── index.ts │ │ ├── handler/ │ │ │ └── index.ts │ │ ├── option/ │ │ │ ├── convert.test.ts │ │ │ ├── convert.ts │ │ │ ├── generator.test.ts │ │ │ └── generator.ts │ │ ├── process/ │ │ │ ├── command-palette.ts │ │ │ ├── consumer/ │ │ │ │ ├── command-palette.ts │ │ │ │ ├── git-action.ts │ │ │ │ ├── git-branch-action.ts │ │ │ │ ├── git-branch.ts │ │ │ │ ├── git-log-action.ts │ │ │ │ ├── git-log.ts │ │ │ │ ├── git-reflog-action.ts │ │ │ │ ├── git-reflog.ts │ │ │ │ ├── git-stash-action.ts │ │ │ │ ├── git-stash.ts │ │ │ │ ├── git-status-action.ts │ │ │ │ ├── git-status.ts │ │ │ │ ├── git.ts │ │ │ │ ├── index.ts │ │ │ │ ├── open-buffer.ts │ │ │ │ ├── open-bufnr.ts │ │ │ │ ├── open-file.test.ts │ │ │ │ ├── open-file.ts │ │ │ │ ├── open-pr.ts │ │ │ │ └── register.ts │ │ │ ├── git-action.ts │ │ │ ├── git-branch-action.ts │ │ │ ├── git-branch.ts │ │ │ ├── git-log-action.ts │ │ │ ├── git-log.ts │ │ │ ├── git-reflog-action.ts │ │ │ ├── git-reflog.ts │ │ │ ├── git-stash-action.ts │ │ │ ├── git-stash.ts │ │ │ ├── git-status-action.ts │ │ │ ├── git-status.ts │ │ │ ├── index.ts │ │ │ ├── open-buffer.ts │ │ │ ├── open-bufnr.ts │ │ │ ├── open-file.ts │ │ │ ├── open-pr.ts │ │ │ ├── process.ts │ │ │ └── register.ts │ │ ├── resource/ │ │ │ ├── all-buffers.ts │ │ │ ├── blame-pr.ts │ │ │ ├── bookmarks.ts │ │ │ ├── buffer-lines.ts │ │ │ ├── buffer-tags.ts │ │ │ ├── buffer-vista.ts │ │ │ ├── buffers.ts │ │ │ ├── changes.ts │ │ │ ├── coc/ │ │ │ │ ├── coc-current-diagnostics.ts │ │ │ │ ├── coc-definitions.ts │ │ │ │ ├── coc-diagnostics.ts │ │ │ │ ├── coc-implementations.ts │ │ │ │ ├── coc-outline.ts │ │ │ │ ├── coc-references.ts │ │ │ │ ├── coc-tsserver-source-definition.ts │ │ │ │ ├── coc-type-definitions.ts │ │ │ │ └── index.ts │ │ │ ├── command-palette.ts │ │ │ ├── ctags.ts │ │ │ ├── directory-files.ts │ │ │ ├── files-from-resources.ts │ │ │ ├── git-actions.ts │ │ │ ├── git-branch-actions.ts │ │ │ ├── git-branches.ts │ │ │ ├── git-files.ts │ │ │ ├── git-log-actions.ts │ │ │ ├── git-logs.ts │ │ │ ├── git-reflog-actions.ts │ │ │ ├── git-reflogs.ts │ │ │ ├── git-stash-actions.ts │ │ │ ├── git-stashes.ts │ │ │ ├── git-status-actions.ts │ │ │ ├── git-status.ts │ │ │ ├── grep.ts │ │ │ ├── index.ts │ │ │ ├── jumps.ts │ │ │ ├── lines.ts │ │ │ ├── locationlist.ts │ │ │ ├── marks.ts │ │ │ ├── memolist-grep.ts │ │ │ ├── memolist.ts │ │ │ ├── mru.ts │ │ │ ├── mrw.ts │ │ │ ├── nvim-lsp-current-diagnostics.ts │ │ │ ├── nvim-lsp-definition.ts │ │ │ ├── nvim-lsp-diagnostics.ts │ │ │ ├── nvim-lsp-implementation.ts │ │ │ ├── nvim-lsp-references.ts │ │ │ ├── nvim-lsp-type-definition.ts │ │ │ ├── oldfiles.ts │ │ │ ├── project-files.ts │ │ │ ├── project-mru.ts │ │ │ ├── project-mrw.ts │ │ │ ├── project-oldfiles.ts │ │ │ ├── quickfix.ts │ │ │ ├── todo-comments.ts │ │ │ ├── vim-help.ts │ │ │ ├── vim-lsp-current-diagnostics.ts │ │ │ ├── vim-lsp-definition.ts │ │ │ ├── vim-lsp-diagnostics.ts │ │ │ ├── vim-lsp-implementation.ts │ │ │ ├── vim-lsp-references.ts │ │ │ ├── vim-lsp-type-definition.ts │ │ │ ├── vista.ts │ │ │ └── yankround.ts │ │ ├── syntax/ │ │ │ └── colorize.ts │ │ └── util.ts │ ├── module/ │ │ ├── execute-command.ts │ │ ├── file-path.ts │ │ ├── git-config.ts │ │ ├── recall.ts │ │ ├── resume.ts │ │ ├── selector/ │ │ │ ├── execute-command.ts │ │ │ ├── file-path.ts │ │ │ ├── git-config.ts │ │ │ ├── recall.ts │ │ │ ├── resume.ts │ │ │ ├── session.ts │ │ │ └── vim-variable.ts │ │ ├── session.ts │ │ └── vim-variable.ts │ ├── plugin/ │ │ ├── fzf-runner.ts │ │ ├── index.ts │ │ ├── process-runner.ts │ │ └── sync-vim-variable.ts │ ├── register/ │ │ ├── coc/ │ │ │ └── index.ts │ │ └── remote/ │ │ └── index.ts │ ├── remote.ts │ ├── rpc.ts │ ├── store/ │ │ └── index.ts │ ├── system/ │ │ ├── command.ts │ │ ├── file.ts │ │ ├── mr.ts │ │ ├── project.ts │ │ └── tags.ts │ ├── type/ │ │ ├── args.ts │ │ ├── command.ts │ │ ├── connector.ts │ │ ├── fzf.ts │ │ ├── git.ts │ │ ├── index.ts │ │ ├── lsp.ts │ │ ├── process.ts │ │ ├── resource.ts │ │ ├── rpc.ts │ │ ├── syntax.ts │ │ ├── system.ts │ │ ├── vim-variable.ts │ │ └── vim.ts │ └── util/ │ ├── align.test.ts │ ├── align.ts │ ├── array.ts │ ├── type.ts │ └── uniq-with.ts ├── stylua.toml ├── tsconfig.json ├── tsconfig.webpack-jest.json ├── webpack.coc.ts ├── webpack.common.ts ├── webpack.preview.ts ├── webpack.remote.ts └── webpack.rpc.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintignore ================================================ .eslintrc.js ================================================ FILE: .eslintrc.js ================================================ module.exports = { parser: "@typescript-eslint/parser", extends: [ "airbnb-base", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "plugin:@typescript-eslint/recommended-requiring-type-checking", "plugin:eslint-comments/recommended", "plugin:n/recommended", "plugin:import/errors", "plugin:import/warnings", "plugin:import/typescript", "prettier", ], plugins: ["@typescript-eslint", "import", "simple-import-sort"], parserOptions: { sourceType: "module", project: "./tsconfig.json", ecmaVersion: 2020, }, settings: { "import/resolver": { typescript: {}, }, "import/parsers": { "@typescript-eslint/parser": [".ts"], }, }, rules: { complexity: ["error", 7], "no-console": "off", "no-else-return": "off", "object-shorthand": "error", "arrow-body-style": "off", "no-restricted-syntax": "off", "no-param-reassign": [ "error", { props: true, ignorePropertyModificationsFor: ["draft", "state"], }, ], "padding-line-between-statements": [ "error", { blankLine: "always", prev: "*", next: "return", }, ], "no-return-await": "off", "simple-import-sort/imports": "warn", "simple-import-sort/exports": "warn", "no-restricted-imports": ["error", { patterns: ["./", "../"] }], "import/prefer-default-export": "off", "import/extensions": ["error", "ignorePackages", { ts: "never" }], "@typescript-eslint/consistent-type-imports": "error", "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], "@typescript-eslint/strict-boolean-expressions": "error", "@typescript-eslint/array-type": ["error", { default: "generic" }], "@typescript-eslint/prefer-optional-chain": "error", "@typescript-eslint/prefer-nullish-coalescing": "error", "@typescript-eslint/no-namespace": "error", "@typescript-eslint/no-unnecessary-type-assertion": "error", "@typescript-eslint/restrict-plus-operands": "error", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/no-misused-promises": ["error", { checksVoidReturn: false }], "eslint-comments/no-unused-disable": "error", "n/no-unsupported-features/es-syntax": "off", "n/no-missing-import": "off", }, overrides: [ { files: ["**/*.test.ts"], extends: ["plugin:jest/recommended", "plugin:jest/style"], plugins: ["jest"], env: { jest: true, }, rules: { "@typescript-eslint/no-empty-function": "off", }, }, { parserOptions: { sourceType: "module", project: "./tsconfig.webpack-jest.json", }, files: ["./webpack.*.ts", "./jest.config.ts"], rules: { "import/no-extraneous-dependencies": ["error", { devDependencies: true }], "no-restricted-imports": "off", }, }, ], } ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ ## Problems summary ### Expected ### Environment Information - fzf-preview version (package.json): - OS: - Vim/Neovim version: ### Provide a minimal init.vim ```vim " call plug#begin('~/.vim/plugged') " Plug 'junegunn/fzf' " Plug 'neoclide/coc.nvim', {'branch': 'release'} " Plug 'ryanoasis/vim-devicons' " call plug#end() " " let g:coc_global_extensions = ['coc-fzf-preview'] ``` ### Screenshot ### Dockerfile that reproduces the problem (if possible) Refer to https://github.com/yuki-yano/fzf-preview.vim/blob/main/.github/issue_example/Dockerfile ```Dockerfile ``` ================================================ FILE: .github/issue_example/Dockerfile ================================================ FROM ubuntu:focal ENV DEBIAN_FRONTEND=noninteractive # Neovim RUN apt update && apt install -y ninja-build gettext libtool libtool-bin autoconf automake cmake g++ pkg-config unzip git WORKDIR /usr/local/src RUN git clone https://github.com/neovim/neovim.git WORKDIR /usr/local/src/neovim RUN make && make install # zsh RUN apt install -y zsh # Node RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - RUN apt install -y nodejs # Python RUN apt install -y python python3 python3-pip # RUN pip3 install neovim # bat & ripgrep # RUN apt install -y bat # RUN apt install -y ripgrep # Workaround for https://github.com/sharkdp/bat/issues/938 RUN apt install -y -o Dpkg::Options::="--force-overwrite" bat ripgrep # fd RUN apt install -y fd-find # vim plugin RUN sh -c 'curl -fLo "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/autoload/plug.vim --create-dirs https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim' RUN mkdir -p /root/.config/nvim RUN echo "call plug#begin('~/.vim/plugged') \n\ Plug 'junegunn/fzf', {'dir': '~/.fzf', 'do': './install --all'} \n\ Plug 'neoclide/coc.nvim', {'branch': 'release'} \n\ call plug#end() \n\ \n\ let g:coc_global_extensions = ['coc-fzf-preview'] \n" >> /root/.config/nvim/init.vim RUN nvim -c "PlugInstall" -c "qa!" RUN nvim -c "CocInstall -sync coc-fzf-preview" -c "qa!" ENTRYPOINT ["nvim"] ================================================ FILE: .github/workflows/build.yml ================================================ name: Build on: push: branches: - main pull_request: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 16 - run: yarn install --ignore-script --frozen-lockfile - run: yarn run typecheck - run: yarn run lint - run: yarn run prettier - run: yarn run test - run: yarn run release-build:remote - run: yarn run release-build:coc - run: yarn run release-build:rpc - id: check-release run: | if [[ $(git show --no-patch --format=%s) =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo ::set-output name=IS_RELEASE::true fi - if: steps.check-release.outputs.IS_RELEASE == 'true' uses: peter-evans/repository-dispatch@v1 with: token: ${{ secrets.REPO_ACCESS_TOKEN }} event-type: can-release ================================================ FILE: .github/workflows/can-release.yml ================================================ name: Can release on: repository_dispatch: types: - can-release jobs: can_release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 16 - run: yarn install --ignore-script --frozen-lockfile - run: yarn run can-npm-publish ================================================ FILE: .github/workflows/release-coc.yml ================================================ name: Release coc extensions on: workflow_run: workflows: - Can release branches: - main types: - completed jobs: release: name: Release coc extensions if: github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: fetch-depth: 0 ref: main - uses: actions/setup-node@v1 with: node-version: 16 registry-url: https://registry.npmjs.org/ - run: yarn install --ignore-script --frozen-lockfile - run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" - run: yarn run release-build:coc - run: yarn run build:preview-script - run: | TAG="v$(yarn run --silent print-version)" git tag $TAG git push origin $TAG - run: yarn publish env: NODE_AUTH_TOKEN: ${{ secrets.NODE_AUTH_TOKEN }} ================================================ FILE: .github/workflows/release-remote.yml ================================================ name: Release remote plugin on: workflow_run: workflows: - Can release branches: - main types: - completed jobs: release: name: Release remote plugin if: github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - uses: actions/setup-node@v1 with: node-version: 16 - run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" - run: git checkout release/remote - run: git merge main - run: yarn install --ignore-script --frozen-lockfile - run: yarn run release-build:remote - run: yarn run build:preview-script - run: git add --all - run: git commit -m "Release $(yarn run --silent print-version)" --allow-empty - run: git push origin release/remote ================================================ FILE: .github/workflows/release-rpc.yml ================================================ name: Release Vim script RPC on: workflow_run: workflows: - Can release branches: - main types: - completed jobs: release: name: Release Vim script RPC if: github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - uses: actions/setup-node@v1 with: node-version: 16 - run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" - run: git checkout release/rpc - run: git merge main - run: yarn install --ignore-script --frozen-lockfile - run: yarn run release-build:rpc - run: yarn run build:preview-script - run: git add --all - run: git commit -m "Release $(yarn run --silent print-version)" --allow-empty - run: git push origin release/rpc ================================================ FILE: .gitignore ================================================ /node_modules /lib /rplugin/node/fzf-preview.vim/index.js /bin/preview_fzf_grep /doc/tags ================================================ FILE: .luarc.json ================================================ { "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", "Lua.diagnostics.disable": [ "undefined-global" ], "Lua.diagnostics.globals": [ "vim" ] } ================================================ FILE: .markdownlint.json ================================================ { "line-length": false, "no-inline-html": false, "no-duplicate-header": false, "commands-show-output": false } ================================================ FILE: .prettierrc.json ================================================ { "semi": false, "printWidth": 120 } ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Yuki Yano 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 ================================================ # ![logo](https://user-images.githubusercontent.com/5423775/104124691-7f8b4c00-5395-11eb-85d6-93b1c55cf0c8.png) [![Build](https://github.com/yuki-yano/fzf-preview.vim/workflows/Build/badge.svg)](https://github.com/yuki-yano/fzf-preview.vim/actions?query=workflow:Build) [![Release RPC](https://github.com/yuki-yano/fzf-preview.vim/workflows/Release%20Vim%20script%20RPC/badge.svg)](https://github.com/yuki-yano/fzf-preview.vim/actions?query=workflow:"Release+Vim+script+RPC") [![Release remote plugin](https://github.com/yuki-yano/fzf-preview.vim/workflows/Release%20remote%20plugin/badge.svg)](https://github.com/yuki-yano/fzf-preview.vim/actions?query=workflow:%22Release+remote+plugin%22) [![Release coc extensions](https://github.com/yuki-yano/fzf-preview.vim/workflows/Release%20coc%20extensions/badge.svg)](https://github.com/yuki-yano/fzf-preview.vim/actions?query=workflow:%22Release+coc+extensions%22) [![Support](https://img.shields.io/badge/Support-Vim%208.1%20or%20above-yellowgreen)](https://www.vim.org) [![Support](https://img.shields.io/badge/Support-Neovim%200.4%20or%20above-yellowgreen)](https://neovim.io) [![Language](https://img.shields.io/badge/Language-TypeScript-blue)](https://www.typescriptlang.org) [![Language](https://img.shields.io/badge/Language-Vim%20script-green)](https://www.vim.org) [![Lint with](https://img.shields.io/badge/Lint%20with-ESLint-blueviolet)](https://eslint.org) [![Styled with](https://img.shields.io/badge/Styled%20with-Prettier-ff69b4)](https://prettier.io) [![Tested with](https://img.shields.io/badge/Tested%20with-Jest-green)](https://jestjs.io/) [![Powered by](https://img.shields.io/badge/Powered%20by-fzf-7b3948)](https://github.com/junegunn/fzf) [![Powered by](https://img.shields.io/badge/Powered%20by-coc.nvim-7b3948)](https://github.com/neoclide/coc.nvim) [![Powered by](https://img.shields.io/badge/Powered%20by-vital--vs-7b3948)](https://github.com/hrsh7th/vim-vital-vs) [![License](https://img.shields.io/badge/License-MIT-green)](https://github.com/yuki-yano/fzf-preview.vim/blob/main/LICENSE) [![Doc](https://img.shields.io/badge/Doc-:h%20fzf--preview-orange)](https://github.com/yuki-yano/fzf-preview.vim/blob/main/doc/fzf_preview_vim.txt) [![All contributors](https://img.shields.io/badge/All%20contributors-17-orange)](https://github.com/yuki-yano/fzf-preview.vim/graphs/contributors) fzf-preview is a (Neo)vim plugin and coc extension written in TypeScript that provide powerful integration with fzf. It provides multiple presets for fzf and correspondingly powerful preview functionality. It also provides advanced interactive git integration. Since fzf-preview.vim implements RPC in the Vim script, it will work in both Vim and Neovim if you use the RPC release. It can also be installed as Remote Plugin and coc extensions. If you want to use the integration with coc, install coc extensions. [Introductory Article](https://zenn.dev/yano/articles/vim_with_fzf_preview_is_best_experience) (Japanese) This plugin can be easily extended in comparison to [fzf.vim](https://github.com/junegunn/fzf.vim). e.g. [Fugitive](https://github.com/tpope/vim-fugitive)(launch git commands), bdelete(delete a selected buffer from the buffer list) ## TOC - [1. Features](#features) - [2. Demo](#demo) - [3. Requirements](#requirements) - [4. Installation](#installation) - [5. Usage](#usage) - [6. Customization](#customization) - [7. Release note](#release-note) - [8. Others](#others) - [9. License](#license) ## Features 1. Provides an excellent UI with floating windows by default 2. Supports devicons and output highlighting by default 3. Preview the selected item (with an arbitrary command) 4. Fast file and buffer search by fuzzy matching 5. Search all project files and history 6. Search from file history files using oldfiles and mru 7. Interactive git integration (with [Fugitive](https://github.com/tpope/vim-fugitive) or [Gina](https://github.com/lambdalisue/gina.vim)) 8. Jump lines from jumplist or changelist 9. Interactive grep and preview from the current project 10. Export the selected items to QuickFix. ## Demo ### Open file and :bdelete ![fzf-preview](https://user-images.githubusercontent.com/5423775/88540152-6e4fbc80-d04d-11ea-8d19-314ee5e4d294.gif "fzf-preview") ### Interactive git integration (Integrate with [Fugitive](https://github.com/tpope/vim-fugitive) or [Gina](https://github.com/lambdalisue/gina.vim)) ![fzf-preview](https://user-images.githubusercontent.com/5423775/88540232-86bfd700-d04d-11ea-8604-8ad8aed09cbb.gif "fzf-preview") ### Grep ![fzf-preview](https://user-images.githubusercontent.com/5423775/88540281-9ccd9780-d04d-11ea-9672-b9af8a6d6307.gif "fzf-preview") ### Export quickfix and refactor (with [vim-qfreplace](https://github.com/thinca/vim-qfreplace)) ![fzf-preview](https://user-images.githubusercontent.com/5423775/88540327-af47d100-d04d-11ea-99b5-f453862ae892.gif "fzf-preview") ## Requirements - **Node** - git - fzf ### Remote Plugin - Neovim ### coc extensions - coc.nvim ### Optional #### Functional - **ripgrep (Require FzfPreviewProjectGrep and FzfPreviewDirectoryFiles)** (Recommended) - **Fugitive (Require git integration)** (Recommended) - Gina (Require git integration) - universal-ctags (Require FzfPreviewCtags and FzfPreviewBufferTags) - vista.vim (Require FzfPreviewVistaCtags and FzfPreviewVistaBufferCtags) - vim-bookmarks (Require FzfPreviewBookmarks) - yankround.vim (Require FzfPreviewYankround) - memolist.vim (Require FzfPreviewMemoList and FzfPreviewMemoListGrep) - todo-comments.nvim (Require FzfPreviewTodoComments) - GitHub cli (Require FzfPreviewBlamePR) - Yarn (Require build latest version) #### Appearance When bat is installed you can highlight the preview and see it. Otherwise, head will be used - **bat (Add color to the preview)** (Recommended) - vim-devicons (Use devicons) ## Installation ### Vim script RPC Use [Dein](https://github.com/Shougo/dein.vim), [vim-plug](https://github.com/junegunn/vim-plug) or any Vim plugin manager of your choice. Install `release/rpc` branch. ```vim Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } Plug 'yuki-yano/fzf-preview.vim', { 'branch': 'release/rpc' } ``` or ```vim call dein#add('junegunn/fzf', { 'build': './install --all', 'merged': 0 }) call dein#add('yuki-yano/fzf-preview.vim', { 'rev': 'release/rpc' }) ``` ### Remote Plugin Install the npm package [neovim](https://www.npmjs.com/package/neovim) to get the remote plugin working. ```shell $ npm install -g neovim ``` Use [Dein](https://github.com/Shougo/dein.vim), [vim-plug](https://github.com/junegunn/vim-plug) or any Vim plugin manager of your choice. Install `release/remote` branch and execute `:UpdateRemotePlugins` when after installed plugin. ```vim Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } Plug 'yuki-yano/fzf-preview.vim', { 'branch': 'release/remote', 'do': ':UpdateRemotePlugins' } ``` or ```vim call dein#add('junegunn/fzf', { 'build': './install --all', 'merged': 0 }) call dein#add('yuki-yano/fzf-preview.vim', { 'rev': 'release/remote' }) ``` ### coc extensions Install the [fzf](https://github.com/junegunn/fzf), [coc.nvim](https://github.com/neoclide/coc.nvim) and install coc-fzf-preview ```vim Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } Plug 'neoclide/coc.nvim', { 'branch': 'release' } ``` ```vim call dein#add('junegunn/fzf', { 'build': './install --all', 'merged': 0 }) call dein#add('neoclide/coc.nvim', { 'rev': 'release', 'merged': 0 }) ``` and ```vim :CocInstall coc-fzf-preview ``` ## Usage ### Command Vim script RPC, Remote Plugin, and coc extensions, in that order. ```vim " Select project files :FzfPreviewProjectFilesRpc :FzfPreviewProjectFiles :CocCommand fzf-preview.ProjectFiles " Select file from git ls-files :FzfPreviewGitFilesRpc :FzfPreviewGitFiles :CocCommand fzf-preview.GitFiles " Select file from directory files (default to current working directory) (Required [ripgrep](https://github.com/BurntSushi/ripgrep)) :FzfPreviewDirectoryFilesRpc {path or none} :FzfPreviewDirectoryFiles {path or none} :CocCommand fzf-preview.DirectoryFiles " Select file buffers. Used open-buffer processes. :FzfPreviewBuffersRpc :FzfPreviewBuffers :CocCommand fzf-preview.Buffers " Select all buffers. Used open-bufnr processes :FzfPreviewAllBuffersRpc :FzfPreviewAllBuffers :CocCommand fzf-preview.AllBuffers " Select project files from oldfiles :FzfPreviewProjectOldFilesRpc :CocCommand fzf-preview.ProjectOldFiles " Select project mru (Most Recently Used) files :FzfPreviewProjectMruFilesRpc :FzfPreviewProjectMruFiles :CocCommand fzf-preview.ProjectMruFiles " Select project mrw (Most Recently Written) files :FzfPreviewProjectMrwFilesRpc :FzfPreviewProjectMrwFiles :CocCommand fzf-preview.ProjectMrwFiles " Grep project files from args word :FzfPreviewProjectGrepRpc {args} :FzfPreviewProjectGrep {args} :CocCommand fzf-preview.ProjectGrep {args} " Run FzfPreviewProjectGrep with the same arguments as before. :FzfPreviewProjectGrepRecallRpc :FzfPreviewProjectGrepRecall :CocCommand fzf-preview.ProjectGrepRecall " Select tags from tags file (Required [universal-ctags](https://github.com/universal-ctags/ctags)) :FzfPreviewCtagsRpc :FzfPreviewCtags :CocCommand fzf-preview.Ctags " Select tags from current files (Required [universal-ctags](https://github.com/universal-ctags/ctags)) :FzfPreviewBufferTagsRpc :FzfPreviewBufferTags :CocCommand fzf-preview.BufferTags " Select files from oldfiles :FzfPreviewOldFilesRpc :FzfPreviewOldFiles :CocCommand fzf-preview.OldFiles " Select mru (Most Recently Used) files :FzfPreviewMruFilesRpc :FzfPreviewMruFiles :CocCommand fzf-preview.MruFiles " Select mrw (Most Recently Written) files :FzfPreviewMrwFilesRpc :FzfPreviewMrwFiles :CocCommand fzf-preview.MrwFiles " Select line from QuickFix :FzfPreviewQuickFixRpc :FzfPreviewQuickFix :CocCommand fzf-preview.QuickFix " Select line from LocationList :FzfPreviewLocationListRpc :FzfPreviewLocationList :CocCommand fzf-preview.LocationList " Select line from current buffer (Required [bat](https://github.com/sharkdp/bat)) :FzfPreviewLinesRpc :FzfPreviewLines :CocCommand fzf-preview.Lines " Select line from loaded buffer :FzfPreviewBufferLinesRpc :FzfPreviewBufferLines :CocCommand fzf-preview.BufferLines " Select jumplist item :FzfPreviewJumpsRpc :FzfPreviewJumps :CocCommand fzf-preview.Jumps " Select changelist item :FzfPreviewChangesRpc :FzfPreviewChanges :CocCommand fzf-preview.Changes " Select mark :FzfPreviewMarksRpc :CocCommand fzf-preview.Marks " Select files from selected resources (project, git, directory, buffer, project_old, project_mru, project_mrw, old, mru, mrw) :FzfPreviewFromResourcesRpc :FzfPreviewFromResources :CocCommand fzf-preview.FromResources " Execute and edit command history :FzfPreviewCommandPaletteRpc :FzfPreviewCommandPalette :CocCommand fzf-preview.CommandPalette # Grep vim help :FzfPreviewGrepHelpRpc :FzfPreviewGrepHelp :CocCommand fzf-preview.GrepHelp " Interactive git integration. (Required [Fugitive](https://github.com/tpope/vim-fugitive) or [Gina](https://github.com/lambdalisue/gina.vim)) :FzfPreviewGitActionsRpc :FzfPreviewGitActions :CocCommand fzf-preview.GitActions " Select git status listed file. (Required [Fugitive](https://github.com/tpope/vim-fugitive) or [Gina](https://github.com/lambdalisue/gina.vim)) :FzfPreviewGitStatusRpc :FzfPreviewGitStatus :CocCommand fzf-preview.GitStatus " Select references from nvim-lsp :FzfPreviewNvimLspReferencesRpc :FzfPreviewNvimLspReferences " Select diagnostics from nvim-lsp :FzfPreviewNvimLspDiagnosticsRpc :FzfPreviewNvimLspDiagnostics " Select current file diagnostics from nvim-lsp :FzfPreviewNvimLspCurrentDiagnosticsRpc :FzfPreviewNvimLspCurrentDiagnostics " Select definitions from nvim-lsp :FzfPreviewNvimLspDefinitionRpc :FzfPreviewNvimLspDefinition " Select type definitions from nvim-lsp :FzfPreviewNvimLspTypeDefinitionRpc :FzfPreviewNvimLspTypeDefinition " Select implementations from nvim-lsp :FzfPreviewNvimLspImplementationsRpc :FzfPreviewNvimLspImplementations " Select references from vim-lsp :FzfPreviewVimLspReferencesRpc :FzfPreviewVimLspReferences " Select diagnostics from vim-lsp :FzfPreviewVimLspDiagnosticsRpc :FzfPreviewVimLspDiagnostics " Select current file diagnostics from vim-lsp :FzfPreviewVimLspCurrentDiagnosticsRpc :FzfPreviewVimLspCurrentDiagnostics " Select definitions from vim-lsp :FzfPreviewVimLspDefinitionRpc :FzfPreviewVimLspDefinition " Select type definitions from vim-lsp :FzfPreviewVimLspTypeDefinitionRpc :FzfPreviewVimLspTypeDefinition " Select implementations from vim-lsp :FzfPreviewVimLspImplementationsRpc :FzfPreviewVimLspImplementations " Select tags from vista.vim (Required [vista.vim](https://github.com/liuchengxu/vista.vim)) :FzfPreviewVistaCtagsRpc :FzfPreviewVistaCtags :CocCommand fzf-preview.VistaCtags " Select current buffer tags from vista.vim (Required [vista.vim](https://github.com/liuchengxu/vista.vim)) :FzfPreviewVistaBufferCtagsRpc :FzfPreviewVistaBufferCtags :CocCommand fzf-preview.VistaBufferCtags " Select bookmarks (Required [vim-bookmarks](https://github.com/MattesGroeger/vim-bookmarks)) :FzfPreviewBookmarksRpc :FzfPreviewBookmarks :CocCommand fzf-preview.Bookmarks " Select register history (Required [yankround.vim](https://github.com/LeafCage/yankround.vim)) :FzfPreviewYankroundRpc :FzfPreviewYankround :CocCommand fzf-preview.Yankround " Select memolist (Required [glidenote/memolist.vim](https://github.com/glidenote/memolist.vim)) :FzfPreviewMemoListRpc :FzfPreviewMemoList :CocCommand fzf-preview.MemoList " Grep memolist (Required [glidenote/memolist.vim](https://github.com/glidenote/memolist.vim)) :FzfPreviewMemoListGrepRpc :FzfPreviewMemoListGrep :CocCommand fzf-preview.MemoListGrep " Search TodoComments (Required [folke/todo-comments.nvim](https://github.com/folke/todo-comments.nvim)) :FzfPreviewTodoCommentsRpc :FzfPreviewTodoComments :CocCommand fzf-preview.TodoComments " Open the PR corresponding to the selected line (Required [GitHub cli](https://github.com/cli/cli)) :FzfPreviewBlamePRRpc :FzfPreviewBlamePR :CocCommand fzf-preview.BlamePR " Select references from coc.nvim (only coc extensions) :CocCommand fzf-preview.CocReferences " Select diagnostics from coc.nvim (only coc extensions) :CocCommand fzf-preview.CocDiagnostics " Select current file diagnostics from coc.nvim (only coc extensions) :CocCommand fzf-preview.CocCurrentDiagnostics " Select definitions from coc.nvim (only coc extensions) :CocCommand fzf-preview.CocDefinition " Select type definitions from coc.nvim (only coc extensions) :CocCommand fzf-preview.CocTypeDefinition " Select implementations from coc.nvim (only coc extensions) :CocCommand fzf-preview.CocImplementations " Select outline from coc.nvim (only coc extensions) :CocCommand fzf-preview.CocOutline ``` ### Recommended mappings #### Vim script RPC ```vim nmap f [fzf-p] xmap f [fzf-p] nnoremap [fzf-p]p :FzfPreviewFromResourcesRpc project_mru git nnoremap [fzf-p]gs :FzfPreviewGitStatusRpc nnoremap [fzf-p]ga :FzfPreviewGitActionsRpc nnoremap [fzf-p]b :FzfPreviewBuffersRpc nnoremap [fzf-p]B :FzfPreviewAllBuffersRpc nnoremap [fzf-p]o :FzfPreviewFromResourcesRpc buffer project_mru nnoremap [fzf-p] :FzfPreviewJumpsRpc nnoremap [fzf-p]g; :FzfPreviewChangesRpc nnoremap [fzf-p]/ :FzfPreviewLinesRpc --add-fzf-arg=--no-sort --add-fzf-arg=--query="'" nnoremap [fzf-p]* :FzfPreviewLinesRpc --add-fzf-arg=--no-sort --add-fzf-arg=--query="'=expand('')" nnoremap [fzf-p]gr :FzfPreviewProjectGrepRpc xnoremap [fzf-p]gr "sy:FzfPreviewProjectGrepRpc-F"=substitute(substitute(@s, '\n', '', 'g'), '/', '\\/', 'g')" nnoremap [fzf-p]t :FzfPreviewBufferTagsRpc nnoremap [fzf-p]q :FzfPreviewQuickFixRpc nnoremap [fzf-p]l :FzfPreviewLocationListRpc ``` #### Remote Plugin ```vim nmap f [fzf-p] xmap f [fzf-p] nnoremap [fzf-p]p :FzfPreviewFromResources project_mru git nnoremap [fzf-p]gs :FzfPreviewGitStatus nnoremap [fzf-p]ga :FzfPreviewGitActions nnoremap [fzf-p]b :FzfPreviewBuffers nnoremap [fzf-p]B :FzfPreviewAllBuffers nnoremap [fzf-p]o :FzfPreviewFromResources buffer project_mru nnoremap [fzf-p] :FzfPreviewJumps nnoremap [fzf-p]g; :FzfPreviewChanges nnoremap [fzf-p]/ :FzfPreviewLines --add-fzf-arg=--no-sort --add-fzf-arg=--query="'" nnoremap [fzf-p]* :FzfPreviewLines --add-fzf-arg=--no-sort --add-fzf-arg=--query="'=expand('')" nnoremap [fzf-p]gr :FzfPreviewProjectGrep xnoremap [fzf-p]gr "sy:FzfPreviewProjectGrep-F"=substitute(substitute(@s, '\n', '', 'g'), '/', '\\/', 'g')" nnoremap [fzf-p]t :FzfPreviewBufferTags nnoremap [fzf-p]q :FzfPreviewQuickFix nnoremap [fzf-p]l :FzfPreviewLocationList ``` #### coc extensions ```vim nmap f [fzf-p] xmap f [fzf-p] nnoremap [fzf-p]p :CocCommand fzf-preview.FromResources project_mru git nnoremap [fzf-p]gs :CocCommand fzf-preview.GitStatus nnoremap [fzf-p]ga :CocCommand fzf-preview.GitActions nnoremap [fzf-p]b :CocCommand fzf-preview.Buffers nnoremap [fzf-p]B :CocCommand fzf-preview.AllBuffers nnoremap [fzf-p]o :CocCommand fzf-preview.FromResources buffer project_mru nnoremap [fzf-p] :CocCommand fzf-preview.Jumps nnoremap [fzf-p]g; :CocCommand fzf-preview.Changes nnoremap [fzf-p]/ :CocCommand fzf-preview.Lines --add-fzf-arg=--no-sort --add-fzf-arg=--query="'" nnoremap [fzf-p]* :CocCommand fzf-preview.Lines --add-fzf-arg=--no-sort --add-fzf-arg=--query="'=expand('')" nnoremap [fzf-p]gr :CocCommand fzf-preview.ProjectGrep xnoremap [fzf-p]gr "sy:CocCommand fzf-preview.ProjectGrep-F"=substitute(substitute(@s, '\n', '', 'g'), '/', '\\/', 'g')" nnoremap [fzf-p]t :CocCommand fzf-preview.BufferTags nnoremap [fzf-p]q :CocCommand fzf-preview.QuickFix nnoremap [fzf-p]l :CocCommand fzf-preview.LocationList ``` ### Base Fzf window Keymaps ```text , Cancel fzf Open split Open vsplit Open tabedit Jump to buffer if already open. See :drop. If g:fzf_preview_buffers_jump is set to 1 then it will open the buffer in current window instead. Build QuickFix in open-file processes. Execute :bdelete! command from open-buffer and open-bufnr processes. Preview page down Preview page up ? Toggle display of preview screen ``` ## Customization ### Optional Configuration Tips - Increase the size of file history: ```vim " oldfiles uses viminfo, but the default setting is 100 " Change the number by setting it in viminfo with a single quote. " Ref: viminfo-' set viminfo='1000 ``` - Set values for each variable. The default settings are as follows. ```vim " floating window size ratio let g:fzf_preview_floating_window_rate = 0.9 " fzf window position settings let g:fzf_preview_direct_window_option = '' " fzf command default options let g:fzf_preview_default_fzf_options = { '--reverse': v:true, '--preview-window': 'wrap' } " Add fzf quit mapping let g:fzf_preview_quit_map = 1 " jump to the buffers by default, when possible let g:fzf_preview_buffers_jump = 0 " Commands used for fzf preview. " The file name selected by fzf becomes {} let g:fzf_preview_command = 'cat' " Not installed bat " let g:fzf_preview_command = 'bat --color=always --plain {-1}' " Installed bat " g:fzf_binary_preview_command is executed if this command succeeds, and g:fzf_preview_command is executed if it fails let g:fzf_preview_if_binary_command = '[[ "$(file --mime {})" =~ binary ]]' " Commands used for binary file let g:fzf_binary_preview_command = 'echo "{} is a binary file"' " Commands used to get the file list from project let g:fzf_preview_filelist_command = 'git ls-files --exclude-standard' " Not Installed ripgrep " let g:fzf_preview_filelist_command = 'rg --files --hidden --follow --no-messages -g \!"* *"' " Installed ripgrep " Commands used to get the file list from git repository let g:fzf_preview_git_files_command = 'git ls-files --exclude-standard' " Commands used to get the file list from current directory let g:fzf_preview_directory_files_command = 'rg --files --hidden --follow --no-messages -g \!"* *"' " Commands used to get the git status file list let g:fzf_preview_git_status_command = 'git -c color.status=always status --short --untracked-files=all' " Commands used for git status preview. let g:fzf_preview_git_status_preview_command = "[[ $(git diff --cached -- {-1}) != \"\" ]] && git diff --cached --color=always -- {-1} || " . \ "[[ $(git diff -- {-1}) != \"\" ]] && git diff --color=always -- {-1} || " . \ g:fzf_preview_command " Commands used for project grep let g:fzf_preview_grep_cmd = 'rg --line-number --no-heading --color=never --hidden' " MRU and MRW cache directory let g:fzf_preview_cache_directory = expand('~/.cache/vim/fzf_preview') " If this value is not 0, disable mru and mrw let g:fzf_preview_disable_mru = 0 " Limit of the number of files to be saved by mru let g:fzf_preview_mru_limit = 1000 " Commands used for current file lines let g:fzf_preview_lines_command = 'cat -n' " Not Installed bat " let g:fzf_preview_lines_command = 'bat --color=always --plain --number' " Installed bat " Commands used for preview of the grep result let g:fzf_preview_grep_preview_cmd = expand(':h:h') . '/bin/preview_fzf_grep' " Cache directory for mru and mrw let g:fzf_preview_cache_directory = expand('~/.cache/vim/fzf_preview') " Keyboard shortcuts while fzf preview is active let g:fzf_preview_preview_key_bindings = '' " let g:fzf_preview_preview_key_bindings = 'ctrl-d:preview-page-down,ctrl-u:preview-page-up,?:toggle-preview' " Specify the color of fzf let g:fzf_preview_fzf_color_option = '' " Set the processes when selecting an element with fzf let g:fzf_preview_custom_processes = {} " For example, set split to ctrl-s " let g:fzf_preview_custom_processes['open-file'] = fzf_preview#remote#process#get_default_processes('open-file') " on coc extensions " let g:fzf_preview_custom_processes['open-file'] = fzf_preview#remote#process#get_default_processes('open-file', 'coc') " let g:fzf_preview_custom_processes['open-file']['ctrl-s'] = g:fzf_preview_custom_processes['open-file']['ctrl-x'] " call remove(g:fzf_preview_custom_processes['open-file'], 'ctrl-x') " Use as fzf preview-window option let g:fzf_preview_fzf_preview_window_option = '' " let g:fzf_preview_fzf_preview_window_option = 'up:30%' " Use vim-devicons let g:fzf_preview_use_dev_icons = 0 " Use fzf history option let g:fzf_preview_history_dir = false " let g:fzf_preview_history_dir = '~/.fzf' " devicons character width let g:fzf_preview_dev_icon_prefix_string_length = 3 " Devicons can make fzf-preview slow when the number of results is high " By default icons are disable when number of results is higher that 5000 let g:fzf_preview_dev_icons_limit = 5000 " The theme used in the bat preview $FZF_PREVIEW_PREVIEW_BAT_THEME = 'OneHalfDark' ``` ### Command Options Commented-out lines are settings for the coc extension. ```vim --processes " Set process when selecting element with fzf started by this command. " Value must be a global variable name. " Variable is dictionary and format is same as g:fzf_preview_custom_processes['open-file']. " " Most commands are passed a file path to the process function. " FzfPreviewAllBuffers will be passed “buffer {bufnr}” " " Value example: let g:foo_processes = { " \ '': 'FzfPreviewOpenFileEnter', " \ 'ctrl-x': get(function('s:foo_function'), 'name'), " \ } " --add-fzf-arg " Set the arguments to be passed when executing fzf. " This value is added to the default options. " Value must be a string without spaces. " Example: Exclude filename with FzfPreviewProjectGrep nnoremap g :FzfPreviewProjectGrep --add-fzf-arg=--nth=3 " nnoremap g :CocCommand fzf-preview.ProjectGrep --add-fzf-arg=--nth=3 --resume " Reuse the input that was last used to select the element with fzf. " Do not need to pass a value for this option. " Example: Reuse last query for project grep. nnoremap G :FzfPreviewProjectGrep . --resume " nnoremap G :CocCommand fzf-preview.ProjectGrep . --resume ``` ### Function ```vim " Get the initial value of the open file processes " processes_name is 'open-file', 'open-buffer' and 'open-bufnr'. " plugin_type is 'remote', 'coc' or 'rpc'. Default value is 'remote' call fzf_preview#remote#process#get_default_processes({processes_name}, {plugin_type}) ``` ## Release note
Changes history - 2023/01/02 version 2.16.0 - Implement nvim-lsp resources. - 2021/12/05 version 2.12.0 - Implement vim-lsp resources. - 2022/08/23 version 2.13.0 - Implement coc TypeScript go to source definition resource. - 2021/12/05 version 2.12.0 - Implement vim-lsp resources. - 2021/10/15 version 2.9.0 - Implement coc outline resource. - 2021/09/10 version 2.7.0 - Implement vim help resource. - 2021/09/08 version 2.6.0 - Improve project files performance - 2021/06/06 version 2.5.0 - Implement --experimental-fast option. - 2021/06/04 version 2.4.0 - Implement [todo-comments.nvim](https://github.com/folke/todo-comments.nvim) resource. - 2021/05/19 version 2.3.0 - Implement fzf history option. - 2021/05/18 version 2.2.0 - Implement grep recall. - 2021/01/16 version 2.0.7 - Implement coc implementations resource. - 2021/01/16 version 2.0.6 - Implement [memolist.vim](https://github.com/glidenote/memolist.vim) resource. - 2021/01/10 version 2.0.0 - Release of stable version. - Change release from github actions. - Update latest npm packages. - 2021/01/06 version 0.6.0 - Update coc.nvim package version to 0.0.80. - 2020/12/31 version 0.5.0 - Implement Vim script RPC - Only need Vim and Node - **Breaking change**: The release branch of the Remote Plugin has been changed to release/remote. - 2020/11/08 version 0.4.27 - Add g:fzf_preview_direct_window_option option. - 2020/11/07 version 0.4.26 - Change buffer sort with mru order. - Add mru and mrw limit settings. - Improve grep preview highlight. - 2020/10/30 version 0.4.24 - Improved grep etc previews to scroll to the top of the file. - 2020/10/4 version 0.4.20 - Implement CommandPalette resource. - 2020/10/4 version 0.4.17 - Implement CocTypeDefinitions resource. - 2020/07/30 version 0.4.7 - Implement git reflog integration. - 2020/07/30 version 0.4.6 - Implement git stash integration. - Implement rename git branch. - 2020/07/27 version 0.4.1 - Implement create git branch. - 2020/07/27 version 0.4.0 - Implement interactive git integration. (`:FzfPreviewGitActions` and `:CocCommand fzf-preview.GitActions`) - 2020/07/24 version 0.3.2 - Remove g:fzf_preview_filelist_postprocess_command and to colorize the fzf window by default - 2020/07/24 version 0.2.1 - Change g:fzf_preview_default_fzf_options and g:fzf_preview_fzf_preview_window_option default value. - Fix export quickfix bug in grep. - 2020/07/23 version 0.2.0 - Improving the internal data structure. - Remove `FzfPreviewProjectCommandGrep` command. (Changes to the data structure have made implementation difficult) - Some bug fixes.
## Others - How to use fish user? - Set the `set shell` and `$SHELL`. ```vim set shell=/bin/zsh let $SHELL = "/bin/zsh" ``` - Use true color preview in Neovim - Set the preview command to `COLORTERM=truecolor` ```vim augroup fzf_preview autocmd! autocmd User fzf_preview#rpc#initialized call s:fzf_preview_settings() " fzf_preview#remote#initialized or fzf_preview#coc#initialized augroup END function! s:fzf_preview_settings() abort let g:fzf_preview_command = 'COLORTERM=truecolor ' . g:fzf_preview_command let g:fzf_preview_grep_preview_cmd = 'COLORTERM=truecolor ' . g:fzf_preview_grep_preview_cmd endfunction ``` - `FzfPreviewVistaBufferCtags` does not work - Vista must be initialized. Run the Vista command once or write the following settings. ```vim autocmd VimEnter * call vista#RunForNearestMethodOrFunction() ``` ## License The MIT License (MIT) ================================================ FILE: autoload/fzf_preview/remote/consumer/git.vim ================================================ " using pseudo synchronous call in Vim8 because Vim8 asynchronous call is unstable function! s:execute(command) abort if !has('nvim') call feedkeys(':' . a:command . "\n", 'n') else execute a:command endif endfunction function! fzf_preview#remote#consumer#git#add(file) abort call system('git add ' . shellescape(a:file)) if v:shell_error echomsg 'Failed: git add ' . a:file endif endfunction function! fzf_preview#remote#consumer#git#add_intent_to_add(file) abort call system('git add --intent-to-add ' . shellescape(a:file)) if v:shell_error echomsg 'Failed: git add --intent-to-add ' . a:file endif endfunction function! fzf_preview#remote#consumer#git#reset(file, option) abort if a:option !=# '' let command = 'git reset ' . a:option . ' ' . shellescape(a:file) else let command = 'git reset ' . shellescape(a:file) endif call system(command) if v:shell_error echomsg 'Failed: ' . command endif endfunction function! fzf_preview#remote#consumer#git#reset_intent_to_add(file) abort call system('git reset --intent-to-add ' . shellescape(a:file)) if v:shell_error echomsg 'Failed: git reset --intent-to-add ' . a:file endif endfunction function! fzf_preview#remote#consumer#git#patch(file) abort if exists(':Gin') == 2 tabedit call s:execute('GinPatch ' . fnamemodify(a:file, ':p')) return elseif exists(':Gina') == 2 call s:execute('Gina patch ' . fnamemodify(a:file, ':p')) return elseif exists(':Git') != 0 execute 'tabedit ' . a:file . ' | Git diff' return endif echoerr 'Gin, Gina and Fugitive not installed' endfunction function! fzf_preview#remote#consumer#git#chaperon(file) abort if exists(':Gin') == 2 tabedit call s:execute('GinChaperon ' . fnamemodify(a:file, ':p')) return elseif exists(':Gina') == 2 call s:execute('Gina chaperon ' . fnamemodify(a:file, ':p')) return endif echoerr 'Gin and Gina not installed' endfunction function! fzf_preview#remote#consumer#git#commit(option) abort if match(a:option, '--fixup') != -1 echomsg system('git commit ' . a:option) return elseif exists(':Gin') == 2 call s:execute('Gin commit --verbose ' . a:option) return elseif exists(':Gina') == 2 call s:execute('Gina commit --verbose ' . a:option) return elseif exists(':Git') == 2 execute 'Git commit --verbose ' . a:option return endif echoerr 'Gin, Gina and Fugitive not installed' endfunction function! fzf_preview#remote#consumer#git#restore(file) abort if exists(':Gin') == 2 call s:execute('Gin checkout -- ' . fnamemodify(a:file, ':p')) return elseif exists(':Gina') == 2 call s:execute('Gina checkout -- ' . fnamemodify(a:file, ':p')) return elseif exists(':Git') == 2 execute 'Git checkout -- ' . a:file return else call system('git checkout -- ' . shellescape(a:file)) if v:shell_error echomsg 'Failed: git checkout -- ' . a:file endif endif endfunction function! fzf_preview#remote#consumer#git#switch(branch) abort if exists(':Gin') == 2 call s:execute('Gin checkout ' . a:branch) return elseif exists(':Gina') == 2 call s:execute('Gina checkout ' . a:branch) return elseif exists(':Git') == 2 execute 'Git checkout ' . a:branch return else call system('git checkout ' . shellescape(a:branch)) if v:shell_error echomsg 'Failed: git checkout ' . a:branch endif endif endfunction function! fzf_preview#remote#consumer#git#create_branch() abort let branch_name = input('Branch name: ') if branch_name !=# '' echomsg system('git checkout -b ' . shellescape(branch_name)) endif endfunction function! fzf_preview#remote#consumer#git#diff(branch, ...) abort let branch2 = get(a:, 1, '') if exists(':Gin') == 2 execute 'silent GinDiff ' . a:branch . '..' . branch2 echomsg 'git diff ' . a:branch . '..' . branch2 return elseif exists(':Gina') == 2 execute 'silent Gina diff ' . a:branch . '..' . branch2 echomsg 'git diff ' . a:branch . '..' . branch2 return elseif exists(':Git') == 2 execute 'silent Git diff ' . a:branch . '..' . branch2 echomsg 'git diff ' . a:branch . '..' . branch2 return endif echoerr 'Gin, Gina and Fugitive not installed' endfunction function! fzf_preview#remote#consumer#git#show(name_or_hash) abort " TODO: Until GinShow is implemented in gin, gina is the priority if exists(':Gina') == 2 call s:execute('Gina show ' . a:name_or_hash) return elseif exists(':Gin') == 2 call s:execute('GinBuffer show ' . a:name_or_hash) return elseif exists(':Git') == 2 execute 'Git show ' . a:name_or_hash return endif echoerr 'Gin, Gina and Fugitive not installed' endfunction function! fzf_preview#remote#consumer#git#merge(branch, option) abort if exists(':Gin') == 2 call s:execute('Gin merge ' . a:option . ' ' . a:branch) return elseif exists(':Gina') == 2 call s:execute('Gina merge ' . a:option . ' ' . a:branch) return elseif exists(':Git') == 2 execute 'Git merge ' . a:option . ' ' . a:branch return endif echoerr 'Gin, Gina and Fugitive not installed' endfunction function! fzf_preview#remote#consumer#git#rebase(branch) abort if exists(':Gin') == 2 call s:execute('Gin rebase ' . a:branch) return elseif exists(':Gina') == 2 call s:execute('Gina rebase ' . a:branch) return elseif exists(':Git') == 2 execute 'Git rebase ' . a:branch return endif echoerr 'Fugitive and Gina not installed' endfunction function! fzf_preview#remote#consumer#git#rebase_interactive(branch_or_hash) abort if exists(':Gin') == 2 execute 'Gin rebase --interactive ' . a:branch_or_hash return elseif exists(':Git') == 2 execute 'Git rebase --interactive ' . a:branch_or_hash return endif echoerr 'Gin and Fugitive not installed' endfunction function! fzf_preview#remote#consumer#git#push(option) abort echomsg system('git push ' . a:option) if v:shell_error echomsg 'Failed: git push ' . a:option endif endfunction function! fzf_preview#remote#consumer#git#fetch() abort echomsg system('git fetch') if v:shell_error echomsg 'Failed: git fetch' endif endfunction function! fzf_preview#remote#consumer#git#delete_branch(branch, option) abort echomsg system('git branch --delete ' . a:option . ' ' . shellescape(a:branch)) if v:shell_error echomsg 'Failed: git branch --delete ' . a:option . ' ' . a:branch endif endfunction function! fzf_preview#remote#consumer#git#rename_branch(src) abort let dest = input('Branch name: ') if dest !=# '' let command = 'git branch -m ' . shellescape(a:src) . ' ' . dest echo system(command) if v:shell_error echomsg 'Failed: ' . command endif endif endfunction function! fzf_preview#remote#consumer#git#stash_apply(stash) abort let command = 'git stash apply ' . shellescape(a:stash) echo system(command) if v:shell_error echomsg 'Failed: ' . command endif endfunction function! fzf_preview#remote#consumer#git#stash_pop(stash) abort let command = 'git stash pop ' . shellescape(a:stash) echo system(command) if v:shell_error echomsg 'Failed: ' . command endif endfunction function! fzf_preview#remote#consumer#git#stash_drop(stash) abort let command = 'git stash drop ' . shellescape(a:stash) echo system(command) if v:shell_error echomsg 'Failed: ' . command endif endfunction function! fzf_preview#remote#consumer#git#stash_create() abort let message = input('Message: ') if message !=# '' let command = 'git stash save "' . message . '"' else let command = 'git stash save' endif echo system(command) if v:shell_error echomsg 'Failed: ' . command endif endfunction function! fzf_preview#remote#consumer#git#pull() abort echomsg system('git pull') if v:shell_error echomsg 'Failed: git pull' endif endfunction function! fzf_preview#remote#consumer#git#yank(branch) abort let hash = system('git rev-parse ' . shellescape(a:branch)) call fzf_preview#remote#consumer#register#set(hash, 'v') echomsg 'yanked ' a:branch . ' branch hash: ' . hash endfunction ================================================ FILE: autoload/fzf_preview/remote/consumer/register.vim ================================================ function! fzf_preview#remote#consumer#register#set(str, options) abort call setreg('"', a:str, a:options) endfunction function! fzf_preview#remote#consumer#register#paste(str, options) abort call setreg('"', a:str, a:options) normal! ""p endfunction ================================================ FILE: autoload/fzf_preview/remote/exec_fzf.vim ================================================ function! fzf_preview#remote#exec_fzf#exec(command, env, ...) abort let session_token = get(a:, 1, '') if a:env ==# 'remote' if session_token !=# '' execute a:command . ' --session=' . session_token else execute a:command endif elseif a:env ==# 'coc' let command_name = substitute(a:command, '^FzfPreview', 'fzf-preview.', '') if session_token !=# '' execute 'CocCommand ' . command_name . ' --session=' . session_token else execute 'CocCommand ' . command_name endif elseif a:env ==# 'rpc' if session_token !=# '' execute a:command . 'Rpc --session=' . session_token else execute a:command . 'Rpc' endif endif endfunction ================================================ FILE: autoload/fzf_preview/remote/handler_to_process.vim ================================================ function! fzf_preview#remote#handler_to_process#call_funcref_or_fallback_default_process(env, default_process_function_name, expect_key, lines, process_name) abort if a:env ==# 'remote' call s:call_remote_plugin(a:default_process_function_name, a:expect_key, a:lines, a:process_name) elseif a:env ==# 'coc' call s:call_coc(a:default_process_function_name, a:expect_key, a:lines, a:process_name) elseif a:env ==# 'rpc' call s:call_rpc(a:default_process_function_name, a:expect_key, a:lines, a:process_name) endif endfunction function! s:call_remote_plugin(default_process_function_name, expect_key, lines, process_name) abort if (a:process_name == v:null) call call(a:default_process_function_name, [a:lines]) else let processes = eval('g:' . a:process_name) let Process = processes[a:expect_key] if type(Process) == v:t_string if exists('*' . Process) call call(Process, [a:lines]) else call fzf_preview#rpc#exec_process_callback(Process, a:lines) endif elseif type(Process) == v:t_func call Process(a:lines) endif endif endfunction function! s:call_coc(default_process_function_name, expect_key, lines, process_name) abort if (a:process_name == v:null) let process_name = substitute(a:default_process_function_name, '^FzfPreview', '', '') call CocAction('runCommand', 'fzf-preview-callback.' . process_name, [a:lines]) else let processes = eval('g:' . a:process_name) let Process = processes[a:expect_key] if type(Process) == v:t_string let Process = substitute(Process, '^FzfPreview', '', '') if exists('*' . Process) call call(Process, [a:lines]) else call CocAction('runCommand', 'fzf-preview-callback.' . Process, [a:lines]) endif elseif type(Process) == v:t_func call Process(a:lines) endif endif endfunction function! s:call_rpc(default_process_function_name, expect_key, lines, process_name) abort if (a:process_name == v:null) call fzf_preview#rpc#exec_process_callback(a:default_process_function_name, a:lines) else let processes = eval('g:' . a:process_name) let Process = processes[a:expect_key] if type(Process) == v:t_string if exists('*' . Process) call call(Process, [a:lines]) else call fzf_preview#rpc#exec_process_callback(Process, a:lines) endif elseif type(Process) == v:t_func call Process(a:lines) endif endif endfunction ================================================ FILE: autoload/fzf_preview/remote/mr.vim ================================================ function! fzf_preview#remote#mr#append(path, cache_path) abort let files = s:get_files_with_create_directory(a:cache_path) call insert(files, a:path) call writefile(fzf_preview#remote#util#uniq(files)[:g:fzf_preview_mru_limit - 1], a:cache_path) endfunction function! fzf_preview#remote#mr#mru_file_path() abort return g:fzf_preview_cache_directory . '/mru' endfunction function! fzf_preview#remote#mr#mrw_file_path() abort return g:fzf_preview_cache_directory . '/mrw' endfunction function! s:get_files_with_create_directory(cache_path) abort if !isdirectory(g:fzf_preview_cache_directory) call mkdir(g:fzf_preview_cache_directory, 'p') endif try let files = readfile(a:cache_path) catch let files = [] endtry return files endfunction ================================================ FILE: autoload/fzf_preview/remote/process.vim ================================================ function! fzf_preview#remote#process#get_default_processes(name, ...) abort let env = get(a:, 1, 'remote') if env ==# 'remote' return FzfPreviewGetDefaultProcesses(a:name) elseif env ==# 'coc' return CocAction('runCommand', 'fzf-preview.GetDefaultProcesses', [a:name]) elseif env ==# 'rpc' return fzf_preview#rpc#get_default_processes(a:name) endif endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/all_buffers.vim ================================================ function! fzf_preview#remote#resource#all_buffers#get() abort let buffers = [] for bufinfo in copy(getbufinfo()) let buffer = { \ 'name': fnamemodify(bufinfo['name'], ':.'), \ 'bufnr': bufinfo['bufnr'], \ } call add(buffers, buffer) endfor let buffers = map(copy(getbufinfo({ 'buflisted': 1 })), \ { _, buffer -> { \ 'fileName': fnamemodify(buffer['name'], ':.'), \ 'bufnr': buffer['bufnr'], \ 'isCurrent': v:false, \ 'isAlternate': v:false, \ 'isModified': v:false, \ } \ } \ ) return buffers endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/bookmarks.vim ================================================ function! fzf_preview#remote#resource#bookmarks#get() abort return filter(map(bm#location_list(), { \ _, b -> s:bookmarks_format_line(b) \ }), { \ _, b -> b.file !=# '' \ }) endfunction function! s:bookmarks_format_line(line) abort let line = split(a:line, ':') let filename = fnamemodify(line[0], ':.') if !filereadable(filename) return { 'file': '' } endif let line_number = line[1] let text = line[2] if text ==# 'Annotation' let comment = line[3] else let text = join(line[2:], ':') endif if text !=# 'Annotation' return { 'file': filename, 'line': line_number, 'text': text, 'comment': '' } else return { 'file': filename, 'line': line_number, 'text': text, 'comment': comment } endif endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/buffers.vim ================================================ function! fzf_preview#remote#resource#buffers#get_other_buffers() abort let list = filter(range(1, bufnr('$')), \ { _, bufnr -> bufexists(bufnr) && buflisted(bufnr) && filereadable(expand('#' . bufnr . ':p')) && bufnr != bufnr('%') && bufnr != bufnr('#')} \ ) return map(list, { _, bufnr -> s:bufnr_to_bufinfo(bufnr) }) endfunction function! fzf_preview#remote#resource#buffers#get_current_buffer() abort return s:bufnr_to_bufinfo(bufnr('%')) endfunction function! fzf_preview#remote#resource#buffers#get_alternate_buffer() abort return s:bufnr_to_bufinfo(bufnr('#')) endfunction function! fzf_preview#remote#resource#buffers#get() abort let list = filter(range(1, bufnr('$')), \ { _, bufnr -> bufexists(bufnr) && buflisted(bufnr) && filereadable(expand('#' . bufnr . ':p'))} \ ) return map(list, { _, bufnr -> s:bufnr_to_bufinfo(bufnr) }) endfunction function! s:bufnr_to_bufinfo(bufnr) abort let name = bufname(a:bufnr) let is_current = a:bufnr == bufnr('%') let is_alternate = a:bufnr == bufnr('#') let is_modified = getbufvar(a:bufnr, '&modified') return { \ 'fileName': name, \ 'bufnr': a:bufnr, \ 'isCurrent': is_current == 1 ? v:true : v:false, \ 'isAlternate': is_alternate == 1 ? v:true : v:false, \ 'isModified': is_modified == 1 ? v:true : v:false, \ } endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/changes.vim ================================================ function! fzf_preview#remote#resource#changes#get() abort if !filereadable(expand('%')) return [] endif let list = [] let lnums = map(copy(getchangelist('%')[0]), { _, change -> change['lnum'] }) for lnum in lnums let lines = getbufline(bufnr('%'), lnum) if len(lines) > 0 call add(list, lnum . ' ' . lines[0]) endif endfor call reverse(list) let list = fzf_preview#remote#util#uniq(list) return list endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/directory_files.vim ================================================ function! fzf_preview#remote#resource#directory_files#get(command) abort return systemlist(a:command) endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/git_files.vim ================================================ function! fzf_preview#remote#resource#git_files#get(command) abort return systemlist(a:command) endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/git_status.vim ================================================ function! fzf_preview#remote#resource#git_status#get(command) abort return systemlist(a:command) endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/grep.vim ================================================ function! fzf_preview#remote#resource#grep#get(command) abort return systemlist(a:command) endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/jumps.vim ================================================ function! fzf_preview#remote#resource#jumps#get() abort let splitted_project_path = split(fzf_preview#remote#util#project_root(), '/') let bufnr_and_lnum_list = map(copy(getjumplist()[0]), { \ _, jump -> { 'bufnr': jump['bufnr'], 'lnum': jump['lnum'] } \ }) let result = fzf_preview#remote#util#bufnr_and_lnum_to_resource(bufnr_and_lnum_list, splitted_project_path) call reverse(result) return result endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/lines.vim ================================================ function! fzf_preview#remote#resource#lines#get(command) abort return systemlist(a:command) endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/marks.vim ================================================ function! fzf_preview#remote#resource#marks#get() abort let splitted_project_path = split(fzf_preview#remote#util#project_root(), '/') let chars = [ \ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', \ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', \ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', \ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', \ ] let bufnr_and_lnum_list = map(map(copy(chars), { \ _, char -> getpos("'" . char) \ }), { \ _, pos -> { 'bufnr': pos[0] == 0 ? bufnr('%') : pos[0], 'lnum': pos[1] } \ }) call filter(bufnr_and_lnum_list, { _, bufnr_and_lnum -> bufnr_and_lnum['lnum'] != 0 }) let result = fzf_preview#remote#util#bufnr_and_lnum_to_resource(bufnr_and_lnum_list, splitted_project_path) return result endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/memolist.vim ================================================ function! fzf_preview#remote#resource#memolist#files() abort return memolist#files() endfunction function! fzf_preview#remote#resource#memolist#grep(command) abort return systemlist(a:command. ' ' . g:memolist_path) endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/project_files.vim ================================================ function! fzf_preview#remote#resource#project_files#get(command) abort return systemlist(a:command) endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/quickfix_and_locationlist.vim ================================================ function! fzf_preview#remote#resource#quickfix_and_locationlist#get(type) abort let lines = s:get_quickfix_or_locationlist_lines(a:type) if !empty(filter(lines, { _, line -> line !=# '' })) return lines else return [] endif endfunction function! s:get_quickfix_or_locationlist_lines(type) abort let qf_or_loc_lists = s:get_quickfix_or_loclist(a:type) if len(qf_or_loc_lists) != 0 return len(qf_or_loc_lists) > 0 ? getbufline(qf_or_loc_lists[0]['bufnr'], 1, '$') : [] endif return s:open_process_with_qf_and_close(a:type, { type -> s:get_quickfix_or_locationlist_lines(type) }) endfunction function! s:get_quickfix_or_loclist(type) abort return filter(getwininfo(), { _, w -> w['tabnr'] == tabpagenr() && getwinvar(w['winnr'], '&filetype') ==# 'qf' && w[a:type]}) endfunction function! s:open_process_with_qf_and_close(type, F) abort let winid = win_getid() if a:type ==# 'quickfix' copen elseif a:type ==# 'loclist' try lopen catch return [] endtry else return [] endif call win_gotoid(winid) let result = a:F(a:type) if a:type ==# 'quickfix' cclose elseif a:type ==# 'loclist' lclose endif return result endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/tags.vim ================================================ function! fzf_preview#remote#resource#tags#ctags() abort return s:read_tag_file() endfunction function! s:read_tag_file() abort let lines = [] let files = filter(s:get_tag_files(), { _, file -> filereadable(file) }) for file in files let lines = lines + filter(readfile(file), { _, line -> match(line, '^!') == -1 }) endfor call map(lines, { _, line -> s:parse_tagline(line) }) return lines endfunction function! s:get_tag_files() abort return split(&tags, ',') endfunction function! s:parse_tagline(line) abort let elem = split(a:line, '\t') let file_path = fnamemodify(elem[1], ':.') let match = matchlist(elem[2], '^\(\d\+\);"') try let info = { \ 'name': elem[0], \ 'file': file_path, \ 'line': match[1], \ 'type': elem[3], \ } catch throw 'Set excmd=number or excmd=combine in universal-ctags options' endtry return info endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/todo_comments.vim ================================================ function! fzf_preview#remote#resource#todo_comments#get() abort lua vim.g.fzf_preview_todo_keywords = vim.fn.keys((require('todo-comments.config').keywords)) return g:fzf_preview_todo_keywords endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/util.vim ================================================ function! fzf_preview#remote#resource#util#exec_command(command) abort return systemlist(a:command) endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/vim_command.vim ================================================ function! fzf_preview#remote#resource#vim_command#commands() abort return map(getcompletion('', 'command'), {_, command -> { \ 'name': command, \ 'number': v:null, \ }}) endfunction function! fzf_preview#remote#resource#vim_command#history() abort let commands = filter(map(range(1, max([0, histnr(':')])), {_, index -> histget(':', index)}), {_, command -> command !=# ''}) return map(reverse(commands), {index, command -> { \ 'name': command, \ 'number': index + 1, \ }}) endfunction function! fzf_preview#remote#resource#vim_command#exec(command) abort echo a:command execute a:command endfunction function! fzf_preview#remote#resource#vim_command#edit(command) abort call histadd(':', a:command) redraw call feedkeys(":\", 'n') endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/vim_lsp.vim ================================================ let s:references = {} let s:definition = {} let s:type_definition = {} let s:implementation = {} function! fzf_preview#remote#resource#vim_lsp#servers(method) abort return filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_' . a:method . '_provider(v:val)') endfunction function! fzf_preview#remote#resource#vim_lsp#request_references(servers) abort let s:references = {} let command_id = lsp#_new_command() let ctx = { \ 'counter': len(a:servers), \ 'list':[], \ 'last_command_id': command_id, \ 'mods': '', \ 'in_preview': 0, \ 'jump_if_one': 0, \ } let params = { \ 'textDocument': lsp#get_text_document_identifier(), \ 'position': lsp#get_position(), \ 'context': {'includeDeclaration': v:false}, \ } for server in a:servers call lsp#send_request(server, { \ 'method': 'textDocument/references', \ 'params': params, \ 'on_notification': function('s:handle_references', [ctx, server, 'references']), \ }) endfor endfunction function! fzf_preview#remote#resource#vim_lsp#request_definition(servers) abort let s:definition = {} let command_id = lsp#_new_command() let ctx = { \ 'counter': len(a:servers), \ 'list':[], \ 'last_command_id': command_id, \ 'mods': '', \ 'in_preview': 0, \ 'jump_if_one': 0, \ } let params = { \ 'textDocument': lsp#get_text_document_identifier(), \ 'position': lsp#get_position(), \ } for server in a:servers call lsp#send_request(server, { \ 'method': 'textDocument/definition', \ 'params': params, \ 'on_notification': function('s:handle_definition', [ctx, server, 'definition']), \ }) endfor endfunction function! fzf_preview#remote#resource#vim_lsp#request_type_definition(servers) abort let s:type_definition = {} let command_id = lsp#_new_command() let ctx = { \ 'counter': len(a:servers), \ 'list':[], \ 'last_command_id': command_id, \ 'mods': '', \ 'in_preview': 0, \ 'jump_if_one': 0, \ } let params = { \ 'textDocument': lsp#get_text_document_identifier(), \ 'position': lsp#get_position(), \ } for server in a:servers call lsp#send_request(server, { \ 'method': 'textDocument/typeDefinition', \ 'params': params, \ 'on_notification': function('s:handle_type_definition', [ctx, server, 'typeDefinition']), \ }) endfor endfunction function! fzf_preview#remote#resource#vim_lsp#request_implementation(servers) abort let s:implementation = {} let command_id = lsp#_new_command() let ctx = { \ 'counter': len(a:servers), \ 'list':[], \ 'last_command_id': command_id, \ 'mods': '', \ 'in_preview': 0, \ 'jump_if_one': 0, \ } let params = { \ 'textDocument': lsp#get_text_document_identifier(), \ 'position': lsp#get_position(), \ } for server in a:servers call lsp#send_request(server, { \ 'method': 'textDocument/implementation', \ 'params': params, \ 'on_notification': function('s:handle_implementation', [ctx, server, 'implementation']), \ }) endfor endfunction function! fzf_preview#remote#resource#vim_lsp#fetch_references() abort return s:references endfunction function! fzf_preview#remote#resource#vim_lsp#fetch_definition() abort return s:definition endfunction function! fzf_preview#remote#resource#vim_lsp#fetch_type_definition() abort return s:type_definition endfunction function! fzf_preview#remote#resource#vim_lsp#fetch_implementation() abort return s:implementation endfunction function! s:handle_references(ctx, server, type, data) abort let s:references[a:data['server_name']] = a:data['response']['result'] endfunction function! s:handle_definition(ctx, server, type, data) abort let s:definition[a:data['server_name']] = a:data['response']['result'] endfunction function! s:handle_type_definition(ctx, server, type, data) abort let s:type_definition[a:data['server_name']] = a:data['response']['result'] endfunction function! s:handle_implementation(ctx, server, type, data) abort let s:implementation[a:data['server_name']] = a:data['response']['result'] endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/vista.vim ================================================ function! fzf_preview#remote#resource#vista#ctags() abort let result = vista#executive#ctags#ProjectRun() let keys = keys(result) let source = [] let index = 0 while index < len(keys) let kind = keys[index] let values = result[kind] for value in values call add(source, { 'lineNumber': value['lnum'], 'kind': kind, 'text': value['text'], 'tagFile': value['tagfile'] }) endfor let index = index + 1 endwhile return source endfunction function! fzf_preview#remote#resource#vista#buffer_ctags() abort let result = vista#executive#ctags#Run(expand('%:p')) let keys = keys(result) let source = [] let index = 0 while index < len(keys) let kind = keys[index] let values = result[kind] for value in values call add(source, { 'lineNumber': value['lnum'], 'kind': kind, 'text': value['text'], 'line': getline(value['lnum']) }) endfor let index = index + 1 endwhile return source endfunction ================================================ FILE: autoload/fzf_preview/remote/resource/yankround.vim ================================================ function! fzf_preview#remote#resource#yankround#get() abort let histories = map(copy(g:_yankround_cache), 'split(v:val, "\t", 1)') return map(histories, { key, val -> { 'line': key + 1, 'option': val[0], 'text': get(val, 1) } }) endfunction ================================================ FILE: autoload/fzf_preview/remote/runner.vim ================================================ function! fzf_preview#remote#runner#fzf_run(params) abort let source = a:params['source'] let options = a:params['options'] let handler = a:params['handler'] let env = a:params['environment'] if env ==# 'remote' let Sink = function('s:handler_wrapper', [handler]) elseif env ==# 'coc' let Sink = function('s:coc_handler') elseif env ==# 'rpc' let Sink = function('fzf_preview#rpc#rpc_handler') endif let window = !empty(g:fzf_preview_direct_window_option) ? \ g:fzf_preview_direct_window_option : \ { 'width': g:fzf_preview_floating_window_rate, 'height': g:fzf_preview_floating_window_rate } call feedkeys("\nohlsearch\", 'nit') call fzf#run({ \ 'source': source, \ 'sink*': Sink, \ 'options': options, \ 'window': window, \ }) endfunction function! s:handler_wrapper(handler, lines) abort call feedkeys('', 'x') call call(a:handler, [a:lines]) endfunction function! s:coc_handler(lines) abort call feedkeys('', 'x') call CocAction('runCommand', 'fzf-preview.HandleResource', [a:lines]) endfunction ================================================ FILE: autoload/fzf_preview/remote/tagstack.vim ================================================ function! fzf_preview#remote#tagstack#push_tag_stack() abort let from = [bufnr('%'), line('.'), col('.'), 0] let tagname = expand('') let winid = win_getid() call settagstack(winid, {'items': [{'from': from, 'tagname': tagname}]}, 'a') call settagstack(winid, {'curidx': len(gettagstack(winid)['items']) + 1}) endfunction ================================================ FILE: autoload/fzf_preview/remote/util.vim ================================================ function! fzf_preview#remote#util#is_git_directory() abort let git_root = system('git rev-parse --show-toplevel 2>/dev/null') if git_root ==# '' return v:false else return v:true endif endfunction function! fzf_preview#remote#util#project_root() abort let git_root = system('git rev-parse --show-toplevel 2>/dev/null') if git_root ==# '' echomsg 'The current directory is not a git project' return '' endif return strpart(git_root, 0, strlen(git_root) - 1) endfunction function! fzf_preview#remote#util#is_project_file(file, splitted_project_path) abort let splitted_file_path = split(a:file, '/') if len(splitted_file_path) == 0 return 0 endif let is_project_file = 1 let index = 0 for dir_name in a:splitted_project_path[:len(splitted_file_path) - 1] if dir_name !=# splitted_file_path[index] let is_project_file = 0 endif let index = index + 1 endfor return is_project_file endfunction function! fzf_preview#remote#util#bufnr_and_lnum_to_resource(bufnr_and_lnum_list, splitted_project_path) abort let result = [] for bufnr_and_lnum in a:bufnr_and_lnum_list let bufnr = bufnr_and_lnum['bufnr'] let lnum = bufnr_and_lnum['lnum'] let bufinfos = getbufinfo(bufnr) if len(bufinfos) > 0 let bufinfo = bufinfos[0] let file = bufinfo['name'] if fzf_preview#remote#util#is_project_file(file, a:splitted_project_path) && filereadable(file) let file = fnamemodify(file, ':.') let line_number = lnum let lines = getbufline(bufname(bufnr), lnum) if len(lines) > 0 let text = lines[0] else let text = '' endif call add(result, { 'file': file, 'line': line_number, 'text': text }) endif endif endfor return result endfunction function! fzf_preview#remote#util#get_columns() abort return &columns endfunction function! fzf_preview#remote#util#uniq(list) abort let result = [] for item in a:list if index(result, item) == -1 call add(result, item) endif endfor return result endfunction ================================================ FILE: autoload/fzf_preview/remote/variable.vim ================================================ function! fzf_preview#remote#variable#get_global_variables() abort return { \ 'fzfPreviewDefaultFzfOptions': g:fzf_preview_default_fzf_options, \ 'fzfPreviewUseDevIcons': g:fzf_preview_use_dev_icons, \ 'fzfPreviewDevIconPrefixStringLength': g:fzf_preview_dev_icon_prefix_string_length, \ 'fzfPreviewDevIconsLimit': g:fzf_preview_dev_icons_limit, \ 'webDevIconsUnicodeDecorateFileNodesDefaultSymbol': get(g:, 'WebDevIconsUnicodeDecorateFileNodesDefaultSymbol', ''), \ 'webDevIconsUnicodeDecorateFileNodesExtensionSymbols': get(g:, 'WebDevIconsUnicodeDecorateFileNodesExtensionSymbols', {}), \ 'webDevIconsUnicodeDecorateFileNodesExactSymbols': get(g:, 'WebDevIconsUnicodeDecorateFileNodesExactSymbols', {}), \ 'webDevIconsUnicodeDecorateFileNodesPatternSymbols': get(g:, 'WebDevIconsUnicodeDecorateFileNodesPatternSymbols', {}), \ 'fzfPreviewCommand': g:fzf_preview_command, \ 'fzfBinaryPreviewCommand': g:fzf_binary_preview_command, \ 'fzfPreviewIfBinaryCommand': g:fzf_preview_if_binary_command, \ 'fzfPreviewFilelistCommand': g:fzf_preview_filelist_command, \ 'fzfPreviewGitFilesCommand': g:fzf_preview_git_files_command, \ 'fzfPreviewDirectoryFilesCommand': g:fzf_preview_directory_files_command, \ 'fzfPreviewGitStatusCommand': g:fzf_preview_git_status_command, \ 'fzfPreviewGitStatusPreviewCommand': g:fzf_preview_git_status_preview_command, \ 'fzfPreviewGrepCmd': g:fzf_preview_grep_cmd, \ 'fzfPreviewScriptDir': g:fzf_preview_script_dir, \ 'fzfPreviewCacheDirectory': g:fzf_preview_cache_directory, \ 'fzfPreviewLinesCommand': g:fzf_preview_lines_command, \ 'fzfPreviewGrepPreviewCmd': g:fzf_preview_grep_preview_cmd, \ 'fzfPreviewCustomProcesses': g:fzf_preview_custom_processes, \ 'fzfPreviewFzfPreviewWindowOption': g:fzf_preview_fzf_preview_window_option, \ 'fzfPreviewPreviewKeyBindings': g:fzf_preview_preview_key_bindings, \ 'fzfPreviewHistoryDir': g:fzf_preview_history_dir, \ 'fzfPreviewFzfColorOption': g:fzf_preview_fzf_color_option, \ 'fzfPreviewBuffersJump': g:fzf_preview_buffers_jump, \ 'yankroundDir': get(g:, 'yankround_dir', ''), \ 'fzfPreviewYankroundPreviewCommand': g:fzf_preview_yankround_preview_command, \ 'fzfPreviewBlamePrCommand': g:fzf_preview_blame_pr_command, \ } endfunction ================================================ FILE: autoload/fzf_preview/remote/window.vim ================================================ " TODO: Command other than fzf-preview saves wrong resumes let s:resource_command_name = '' function! fzf_preview#remote#window#set_resource_command_name(command_name) abort if s:resource_command_name !=# a:command_name let s:resource_command_name = a:command_name endif call timer_start(10, { -> fzf_preview#remote#window#set_status_line(v:true) }) endfunction function! fzf_preview#remote#window#get_resource_command_name() abort return substitute(s:resource_command_name, '^FzfPreview', '', '') endfunction function! fzf_preview#remote#window#set_status_line(updated_command_name) abort if !g:fzf_preview_update_statusline return endif if &laststatus != 3 return endif if a:updated_command_name setlocal statusline=%#Identifier#\ >\ fzf-preview\ %{fzf_preview#remote#window#get_resource_command_name()} else setlocal statusline=\ endif endfunction function! fzf_preview#remote#window#set_fzf_last_query(...) abort if &filetype ==# 'fzf' && s:resource_command_name !=# '' let matches = matchlist(getline('.'), '\w\+\>.\(\(\w\|\s\|''\)\+\)') if len(matches) > 0 let query = substitute(substitute(matches[1], '\s\+$', '', ''), '^\s\+', '', '') if get(g:, 'fzf_preview_has_remote', v:false) call FzfPreviewDispatchResumeQuery(s:resource_command_name, query) endif if get(g:, 'fzf_preview_has_coc', v:false) call CocAction('runCommand', 'fzf-preview-function.DispatchResumeQuery', [s:resource_command_name, query]) endif if get(g:, 'fzf_preview_has_rpc', v:false) call fzf_preview#rpc#dispatch_resume_query(s:resource_command_name, query) endif endif call timer_start(50, function('fzf_preview#remote#window#set_fzf_last_query')) endif endfunction ================================================ FILE: autoload/fzf_preview/rpc/server.vim ================================================ let s:JSON = vital#fzf_preview#import('VS.RPC.JSON') let s:Server = {} function! fzf_preview#rpc#server#import() abort return s:Server endfunction function! s:Server.new(args) abort let l:server = extend(deepcopy(s:Server), { 'cmd': a:args.command, 'rpc': s:JSON.new(), 'request_id': 0 }) let l:server.events = l:server.rpc.events return l:server endfunction function! s:Server.start() abort return self.rpc.start({ 'cmd': self.cmd }) endfunction function! s:Server.request(method, params) abort return self.rpc.request(self.id(), a:method, a:params) endfunction function! s:Server.response(id, params) abort return self.rpc.response(a:id, a:params) endfunction function! s:Server.id() abort let self.request_id += 1 return self.request_id endfunction ================================================ FILE: autoload/fzf_preview/rpc.vim ================================================ let s:Promise = vital#fzf_preview#import('Async.Promise') let s:Server = fzf_preview#rpc#server#import() let s:root_dir = expand(':h:h:h') let s:state = { \ 'id': -1, \ 'sources': {}, \ 'server': v:null \ } let default_processes = {} call s:Promise.on_unhandled_rejection({ err -> fzf_preview#rpc#log('[ERROR]', err) }) function! fzf_preview#rpc#initialize() abort if !filereadable(printf('%s/lib/rpc.js', s:root_dir)) return endif call s:start() function! s:initialize_default_processes(response) abort let s:default_processes = a:response let g:fzf_preview_has_rpc = v:true silent doautocmd User fzf_preview#initialized silent doautocmd User fzf_preview#rpc#initialized endfunction call s:state.server.request('getDefaultProcesses', {}).then({ response -> s:initialize_default_processes(response) }) endfunction function! fzf_preview#rpc#restart() abort if !empty(s:state.server) call s:state.server.stop() endif let s:state.server = v:null call s:start() endfunction function! fzf_preview#rpc#get_default_processes(name) abort return s:default_processes[a:name] endfunction function! fzf_preview#rpc#command(command, ...) abort call s:start() if a:0 == 0 call s:state.server.request('execCommand', { 'commandName': a:command }) else call s:state.server.request('execCommand', { 'commandName': a:command, 'args': a:1 }) endif endfunction function! fzf_preview#rpc#rpc_handler(lines) abort call feedkeys('', 'x') call s:state.server.request('callProcess', { 'lines': a:lines }) endfunction function! fzf_preview#rpc#exec_process_callback(process_name, lines) abort call s:state.server.request('execProcessCallback', { 'processName': a:process_name, 'lines': a:lines }) endfunction function! fzf_preview#rpc#dispatch_resume_query(command_name, query) abort call s:state.server.request('dispatchResumeQuery', { 'commandName': a:command_name, 'query': a:query }) endfunction function! fzf_preview#rpc#log(...) abort if exists('g:fzf_preview_debug') call writefile([join([strftime('%H:%M:%S')] + a:000, "\t")], '/tmp/fzf_preview_rpc.log', 'a') endif endfunction function! s:start() abort try if !empty(s:state.server) return s:state.server endif let s:state.server = s:Server.new({ 'command': s:command() }) " call s:state.server.rpc.job.events.on('stdout', { out -> fzf_preview#rpc#log('[STDOUT]', out) }) call s:state.server.events.on('stderr', { err -> fzf_preview#rpc#log('[ERROR]', err) }) call s:state.server.events.on('request', { request -> s:on_request(request) }) call s:state.server.start() return s:state.server catch /.*/ echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint }) endtry endfunction function! s:on_request(request) abort if a:request['method'] ==# 'execCommand' execute a:request['params']['command'] call s:state.server.response(a:request['id'], { 'result': v:null }) elseif a:request['method'] ==# 'execCall' let result = call(a:request['params']['fname'], a:request['params']['args']) call s:state.server.response(a:request['id'], { 'result': result }) elseif a:request['method'] ==# 'getVar' let result = get(g:, a:request['params']['name']) call s:state.server.response(a:request['id'], { 'result': result }) elseif a:request['method'] ==# 'getVvar' let result = get(v:, a:request['params']['name']) call s:state.server.response(a:request['id'], { 'result': result }) endif endfunction function! s:command() abort return ['node', printf('%s/lib/rpc.js', s:root_dir)] endfunction ================================================ FILE: autoload/fzf_preview.vim ================================================ function! fzf_preview#install() abort !yarn install UpdateRemotePlugins endfunction ================================================ FILE: autoload/vital/_fzf_preview/Async/Later.vim ================================================ " ___vital___ " NOTE: lines between '" ___vital___' is generated by :Vitalize. " Do not modify the code nor insert new lines before '" ___vital___' function! s:_SID() abort return matchstr(expand(''), '\zs\d\+\ze__SID$') endfunction execute join(['function! vital#_fzf_preview#Async#Later#import() abort', printf("return map({'set_max_workers': '', 'call': '', 'get_max_workers': '', 'set_error_handler': '', 'get_error_handler': ''}, \"vital#_fzf_preview#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") delfunction s:_SID " ___vital___ let s:tasks = [] let s:workers = [] let s:max_workers = 50 let s:error_handler = v:null function! s:call(fn, ...) abort call add(s:tasks, [a:fn, a:000]) if empty(s:workers) call add(s:workers, timer_start(0, s:Worker, { 'repeat': -1 })) endif endfunction function! s:get_max_workers() abort return s:max_workers endfunction function! s:set_max_workers(n) abort if a:n <= 0 throw 'vital: Async.Later: the n must be a positive integer' endif let s:max_workers = a:n endfunction function! s:get_error_handler() abort return s:error_handler endfunction function! s:set_error_handler(handler) abort let s:error_handler = a:handler endfunction function! s:_default_error_handler() abort let ms = split(v:exception . "\n" . v:throwpoint, '\n') echohl ErrorMsg for m in ms echomsg m endfor echohl None endfunction function! s:_worker(...) abort if v:dying return endif let n_workers = len(s:workers) if empty(s:tasks) if n_workers call timer_stop(remove(s:workers, 0)) endif return endif try call call('call', remove(s:tasks, 0)) catch if s:error_handler is# v:null call s:_default_error_handler() else call s:error_handler() endif endtry if n_workers < s:max_workers call add(s:workers, timer_start(0, s:Worker, { 'repeat': -1 })) endif endfunction let s:Worker = funcref('s:_worker') ================================================ FILE: autoload/vital/_fzf_preview/Async/Promise.vim ================================================ " ___vital___ " NOTE: lines between '" ___vital___' is generated by :Vitalize. " Do not modify the code nor insert new lines before '" ___vital___' function! s:_SID() abort return matchstr(expand(''), '\zs\d\+\ze__SID$') endfunction execute join(['function! vital#_fzf_preview#Async#Promise#import() abort', printf("return map({'resolve': '', '_vital_depends': '', 'race': '', 'wait': '', '_vital_created': '', 'all': '', 'noop': '', 'on_unhandled_rejection': '', 'is_promise': '', 'chain': '', 'is_available': '', 'reject': '', 'new': '', '_vital_loaded': ''}, \"vital#_fzf_preview#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") delfunction s:_SID " ___vital___ " ECMAScript like Promise library for asynchronous operations. " Spec: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise " This implementation is based upon es6-promise npm package. " Repo: https://github.com/stefanpenner/es6-promise " States of promise let s:PENDING = 0 let s:FULFILLED = 1 let s:REJECTED = 2 let s:DICT_T = type({}) let s:TIMEOUT_ERROR = 'vital: Async.Promise: Timeout' let s:DEFAULT_WAIT_INTERVAL = 30 function! s:_vital_created(module) abort let a:module.TimeoutError = s:TIMEOUT_ERROR lockvar a:module.TimeoutError endfunction function! s:_vital_loaded(V) abort let s:Later = a:V.import('Async.Later') endfunction function! s:_vital_depends() abort return ['Async.Later'] endfunction function! s:noop(...) abort endfunction let s:NOOP = funcref('s:noop') " Internal APIs let s:PROMISE = { \ '_state': s:PENDING, \ '_has_floating_child': v:false, \ '_children': [], \ '_fulfillments': [], \ '_rejections': [], \ '_result': v:null, \ } let s:id = -1 function! s:_next_id() abort let s:id += 1 return s:id endfunction " ... is added to use this function as a callback of s:Later.call() function! s:_invoke_callback(settled, promise, callback, result, ...) abort let has_callback = a:callback isnot v:null let success = 1 let err = v:null if has_callback try let l:Result = a:callback(a:result) catch let err = { \ 'exception' : v:exception, \ 'throwpoint' : v:throwpoint, \ } let success = 0 endtry else let l:Result = a:result endif if a:promise._state != s:PENDING " Do nothing elseif has_callback && success call s:_resolve(a:promise, Result) elseif !success call s:_reject(a:promise, err) elseif a:settled == s:FULFILLED call s:_fulfill(a:promise, Result) elseif a:settled == s:REJECTED call s:_reject(a:promise, Result) endif endfunction " ... is added to use this function as a callback of s:Later.call() function! s:_publish(promise, ...) abort let settled = a:promise._state if settled == s:PENDING throw 'vital: Async.Promise: Cannot publish a pending promise' endif if empty(a:promise._children) if settled == s:REJECTED && !a:promise._has_floating_child call s:_on_unhandled_rejection(a:promise._result) endif return endif for i in range(len(a:promise._children)) if settled == s:FULFILLED let l:CB = a:promise._fulfillments[i] else " When rejected let l:CB = a:promise._rejections[i] endif let child = a:promise._children[i] if child isnot v:null call s:_invoke_callback(settled, child, l:CB, a:promise._result) else call l:CB(a:promise._result) endif endfor let a:promise._children = [] let a:promise._fulfillments = [] let a:promise._rejections = [] endfunction function! s:_subscribe(parent, child, on_fulfilled, on_rejected) abort let a:parent._children += [ a:child ] let a:parent._fulfillments += [ a:on_fulfilled ] let a:parent._rejections += [ a:on_rejected ] endfunction function! s:_handle_thenable(promise, thenable) abort if a:thenable._state == s:FULFILLED call s:_fulfill(a:promise, a:thenable._result) elseif a:thenable._state == s:REJECTED call s:_reject(a:promise, a:thenable._result) else call s:_subscribe( \ a:thenable, \ v:null, \ funcref('s:_resolve', [a:promise]), \ funcref('s:_reject', [a:promise]), \ ) endif endfunction function! s:_resolve(promise, ...) abort let l:Result = a:0 > 0 ? a:1 : v:null if s:is_promise(Result) call s:_handle_thenable(a:promise, Result) else call s:_fulfill(a:promise, Result) endif endfunction function! s:_fulfill(promise, value) abort if a:promise._state != s:PENDING return endif let a:promise._result = a:value let a:promise._state = s:FULFILLED if !empty(a:promise._children) call s:Later.call(funcref('s:_publish', [a:promise])) endif endfunction function! s:_reject(promise, ...) abort if a:promise._state != s:PENDING return endif let a:promise._result = a:0 > 0 ? a:1 : v:null let a:promise._state = s:REJECTED call s:Later.call(funcref('s:_publish', [a:promise])) endfunction function! s:_notify_done(wg, index, value) abort let a:wg.results[a:index] = a:value let a:wg.remaining -= 1 if a:wg.remaining == 0 call a:wg.resolve(a:wg.results) endif endfunction function! s:_all(promises, resolve, reject) abort let total = len(a:promises) if total == 0 call a:resolve([]) return endif let wait_group = { \ 'results': repeat([v:null], total), \ 'resolve': a:resolve, \ 'remaining': total, \ } " 'for' statement is not available here because iteration variable is captured into lambda " expression by **reference**. call map( \ copy(a:promises), \ {i, p -> p.then({v -> s:_notify_done(wait_group, i, v)}, a:reject)}, \ ) endfunction function! s:_race(promises, resolve, reject) abort for p in a:promises call p.then(a:resolve, a:reject) endfor endfunction " Public APIs function! s:new(resolver) abort let promise = deepcopy(s:PROMISE) let promise._vital_promise = s:_next_id() try if a:resolver != s:NOOP call a:resolver( \ funcref('s:_resolve', [promise]), \ funcref('s:_reject', [promise]), \ ) endif catch call s:_reject(promise, { \ 'exception' : v:exception, \ 'throwpoint' : v:throwpoint, \ }) endtry return promise endfunction function! s:all(promises) abort return s:new(funcref('s:_all', [a:promises])) endfunction function! s:race(promises) abort return s:new(funcref('s:_race', [a:promises])) endfunction function! s:resolve(...) abort let promise = s:new(s:NOOP) call s:_resolve(promise, a:0 > 0 ? a:1 : v:null) return promise endfunction function! s:reject(...) abort let promise = s:new(s:NOOP) call s:_reject(promise, a:0 > 0 ? a:1 : v:null) return promise endfunction function! s:is_available() abort return has('lambda') && has('timers') endfunction function! s:is_promise(maybe_promise) abort return type(a:maybe_promise) == s:DICT_T && has_key(a:maybe_promise, '_vital_promise') endfunction function! s:wait(promise, ...) abort if a:0 && type(a:1) is# v:t_number let t = a:1 let i = s:DEFAULT_WAIT_INTERVAL . 'm' else let o = a:0 ? a:1 : {} let t = get(o, 'timeout', v:null) let i = get(o, 'interval', s:DEFAULT_WAIT_INTERVAL) . 'm' endif let s = reltime() while a:promise._state is# s:PENDING if (t isnot# v:null && reltimefloat(reltime(s)) * 1000 > t) return [v:null, s:TIMEOUT_ERROR] endif execute 'sleep' i endwhile if a:promise._state is# s:FULFILLED return [a:promise._result, v:null] else return [v:null, a:promise._result] endif endfunction function! s:chain(promise_factories) abort return s:_chain(copy(a:promise_factories), []) endfunction function! s:_chain(promise_factories, results) abort if len(a:promise_factories) is# 0 return s:resolve(a:results) endif let l:Factory = remove(a:promise_factories, 0) try return Factory() \.then({ v -> add(a:results, v) }) \.then({ -> s:_chain(a:promise_factories, a:results) }) catch return s:reject({ \ 'exception': v:exception, \ 'throwpoint': v:throwpoint, \}) endtry endfunction let s:_on_unhandled_rejection = s:NOOP function! s:on_unhandled_rejection(on_unhandled_rejection) abort let s:_on_unhandled_rejection = a:on_unhandled_rejection endfunction function! s:_promise_then(...) dict abort let parent = self let state = parent._state let child = s:new(s:NOOP) let l:Res = a:0 > 0 ? a:1 : v:null let l:Rej = a:0 > 1 ? a:2 : v:null if state == s:FULFILLED let parent._has_floating_child = v:true call s:Later.call(funcref('s:_invoke_callback', [state, child, Res, parent._result])) elseif state == s:REJECTED let parent._has_floating_child = v:true call s:Later.call(funcref('s:_invoke_callback', [state, child, Rej, parent._result])) else call s:_subscribe(parent, child, Res, Rej) endif return child endfunction let s:PROMISE.then = funcref('s:_promise_then') " .catch() is just a syntax sugar of .then() function! s:_promise_catch(...) dict abort return self.then(v:null, a:0 > 0 ? a:1 : v:null) endfunction let s:PROMISE.catch = funcref('s:_promise_catch') function! s:_on_finally(CB, parent, Result) abort call a:CB() if a:parent._state == s:FULFILLED return a:Result else " REJECTED return s:reject(a:Result) endif endfunction function! s:_promise_finally(...) dict abort let parent = self let state = parent._state let child = s:new(s:NOOP) if a:0 == 0 let l:CB = v:null else let l:CB = funcref('s:_on_finally', [a:1, parent]) endif if state != s:PENDING call s:Later.call(funcref('s:_invoke_callback', [state, child, CB, parent._result])) else call s:_subscribe(parent, child, CB, CB) endif return child endfunction let s:PROMISE.finally = funcref('s:_promise_finally') " vim:set et ts=2 sts=2 sw=2 tw=0: ================================================ FILE: autoload/vital/_fzf_preview/VS/Event/Emitter.vim ================================================ " ___vital___ " NOTE: lines between '" ___vital___' is generated by :Vitalize. " Do not modify the code nor insert new lines before '" ___vital___' function! s:_SID() abort return matchstr(expand(''), '\zs\d\+\ze__SID$') endfunction execute join(['function! vital#_fzf_preview#VS#Event#Emitter#import() abort', printf("return map({'new': ''}, \"vital#_fzf_preview#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") delfunction s:_SID " ___vital___ " " new " function! s:new() abort return s:Emitter.new() endfunction " " Emitter " let s:Emitter = {} " " new " function! s:Emitter.new() abort return extend(deepcopy(s:Emitter), { \ 'events': {} \ }) endfunction " " emit " function! s:Emitter.emit(event_name, ...) abort for l:Listener in get(self.events, a:event_name, []) call call(l:Listener, a:000) endfor endfunction " " on " function! s:Emitter.on(event_name, Listener) abort let self.events[a:event_name] = get(self.events, a:event_name, []) call add(self.events[a:event_name], a:Listener) endfunction " " listener_count " function! s:Emitter.listener_count(event_name) abort return len(get(self.events, a:event_name, [])) endfunction " " off " function! s:Emitter.off(event_name, ...) abort let self.events[a:event_name] = get(self.events, a:event_name, []) let l:Listener = get(a:000, 0, v:null) let l:i = len(self.events[a:event_name]) - 1 while l:i >= 0 if self.events[a:event_name][l:i] ==# l:Listener || l:Listener is# v:null call remove(self.events[a:event_name], l:i) endif let l:i -= 1 endwhile endfunction ================================================ FILE: autoload/vital/_fzf_preview/VS/RPC/JSON.vim ================================================ " ___vital___ " NOTE: lines between '" ___vital___' is generated by :Vitalize. " Do not modify the code nor insert new lines before '" ___vital___' function! s:_SID() abort return matchstr(expand(''), '\zs\d\+\ze__SID$') endfunction execute join(['function! vital#_fzf_preview#VS#RPC#JSON#import() abort', printf("return map({'_vital_depends': '', 'new': '', '_vital_loaded': ''}, \"vital#_fzf_preview#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") delfunction s:_SID " ___vital___ " " _vital_loaded " function! s:_vital_loaded(V) abort let s:Promise = a:V.import('Async.Promise') let s:Job = a:V.import('VS.System.Job') let s:Emitter = a:V.import('VS.Event.Emitter') endfunction " " _vital_depends " function! s:_vital_depends() abort return ['Async.Promise', 'VS.Event.Emitter', 'VS.System.Job'] endfunction " " new " function! s:new() abort return s:Connection.new() endfunction " " s:Connection " let s:Connection = {} " " new " function! s:Connection.new() abort return extend(deepcopy(s:Connection), { \ 'job': s:Job.new(), \ 'events': s:Emitter.new(), \ 'buffer': '', \ 'header_length': -1, \ 'message_length': -1, \ 'request_map': {}, \ }) endfunction " " start " function! s:Connection.start(args) abort if !self.job.is_running() call self.job.events.on('stdout', self.on_stdout) call self.job.events.on('stderr', self.on_stderr) call self.job.events.on('exit', self.on_exit) call self.job.start(a:args) endif endfunction " " stop " function! s:Connection.stop() abort if self.job.is_running() call self.job.events.off('stdout', self.on_stdout) call self.job.events.off('stderr', self.on_stderr) call self.job.events.off('exit', self.on_exit) call self.job.stop() endif endfunction " " is_running " function! s:Connection.is_running() abort return self.job.is_running() endfunction " " request " function! s:Connection.request(id, method, params) abort let l:ctx = {} function! l:ctx.callback(id, method, params, resolve, reject) abort let self.request_map[a:id] = { 'resolve': a:resolve, 'reject': a:reject } let l:message = { 'id': a:id, 'method': a:method } if a:params isnot# v:null let l:message.params = a:params endif call self.job.send(self.to_message(l:message)) endfunction return s:Promise.new(function(l:ctx.callback, [a:id, a:method, a:params], self)) endfunction " " response " function! s:Connection.response(id, ...) abort let l:message = { 'id': a:id } let l:message = extend(l:message, len(a:000) > 0 ? a:000[0] : {}) call self.job.send(self.to_message(l:message)) endfunction " " notify " function! s:Connection.notify(method, params) abort let l:message = { 'method': a:method } if a:params isnot# v:null let l:message.params = a:params endif call self.job.send(self.to_message(l:message)) endfunction " " cancel " function! s:Connection.cancel(id) abort if has_key(self.request_map, a:id) call remove(self.request_map, a:id) endif endfunction " " to_message " function! s:Connection.to_message(message) abort let a:message.jsonrpc = '2.0' let l:message = json_encode(a:message) return 'Content-Length: ' . strlen(l:message) . "\r\n\r\n" . l:message endfunction " " on_message " function! s:Connection.on_message(message) abort if has_key(a:message, 'id') " Request from server. if has_key(a:message, 'method') call self.events.emit('request', a:message) " Response from server. else if has_key(self.request_map, a:message.id) let l:request = remove(self.request_map, a:message.id) if has_key(a:message, 'error') call l:request.reject(a:message.error) else call l:request.resolve(get(a:message, 'result', v:null)) endif endif endif " Notify from server. elseif has_key(a:message, 'method') call self.events.emit('notify', a:message) endif endfunction " " flush " function! s:Connection.flush(data) abort let self.buffer .= a:data while self.buffer !=# '' " header check. if self.header_length == -1 let l:header_length = stridx(self.buffer, "\r\n\r\n") + 4 if l:header_length < 4 return endif let self.header_length = l:header_length let self.message_length = self.header_length + str2nr(get(matchlist(self.buffer, '\ccontent-length:\s*\(\d\+\)'), 1, '-1')) endif " content check. let l:buffer_len = strlen(self.buffer) if l:buffer_len < self.message_length return endif let l:content = strpart(self.buffer, self.header_length, self.message_length - self.header_length) try call self.on_message(json_decode(l:content)) catch /.*/ endtry let self.buffer = strpart(self.buffer, self.message_length) let self.header_length = -1 endwhile endfunction " " on_stdout " function! s:Connection.on_stdout(data) abort call self.flush(a:data) endfunction " " on_stderr " function! s:Connection.on_stderr(data) abort call self.events.emit('stderr', a:data) endfunction " " on_exit " function! s:Connection.on_exit(code) abort call self.events.emit('exit', a:code) endfunction ================================================ FILE: autoload/vital/_fzf_preview/VS/System/Job.vim ================================================ " ___vital___ " NOTE: lines between '" ___vital___' is generated by :Vitalize. " Do not modify the code nor insert new lines before '" ___vital___' function! s:_SID() abort return matchstr(expand(''), '\zs\d\+\ze__SID$') endfunction execute join(['function! vital#_fzf_preview#VS#System#Job#import() abort', printf("return map({'_vital_depends': '', 'new': '', '_vital_loaded': ''}, \"vital#_fzf_preview#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") delfunction s:_SID " ___vital___ " " _vital_loaded " function! s:_vital_loaded(V) abort let s:Emitter = a:V.import('VS.Event.Emitter') endfunction " " _vital_depends " function! s:_vital_depends() abort return ['VS.Event.Emitter'] endfunction " " new " function! s:new() abort return s:Job.new() endfunction let s:chunk_size = 2048 let s:Job = {} " " new " function! s:Job.new() abort let l:job = extend(deepcopy(s:Job), { \ 'events': s:Emitter.new(), \ 'write_buffer': '', \ 'write_timer': -1, \ 'job': v:null, \ }) let l:job.write = function(l:job.write, [], l:job) return l:job endfunction " " start " function! s:Job.start(args) abort if self.is_running() return endif let l:option = {} for l:key in ['cwd', 'env'] if has_key(a:args, l:key) let l:option[l:key] = a:args[l:key] endif endfor let self.job = s:_create( \ a:args.cmd, \ l:option, \ function(self.on_stdout, [], self), \ function(self.on_stderr, [], self), \ function(self.on_exit, [], self) \ ) endfunction " " stop " function! s:Job.stop() abort if !self.is_running() return endif call self.job.stop() let self.job = v:null endfunction " " is_running " function! s:Job.is_running() abort return !empty(self.job) endfunction " " send " function! s:Job.send(data) abort if !self.is_running() return endif let self.write_buffer .= a:data if self.write_timer != -1 return endif call self.write() endfunction " " write " function! s:Job.write(...) abort let self.write_timer = -1 if self.write_buffer ==# '' return endif call self.job.send(strpart(self.write_buffer, 0, s:chunk_size)) let self.write_buffer = strpart(self.write_buffer, s:chunk_size) if self.write_buffer !=# '' let self.write_timer = timer_start(0, self.write) endif endfunction " " on_stdout " function! s:Job.on_stdout(data) abort call self.events.emit('stdout', a:data) endfunction " " on_stderr " function! s:Job.on_stderr(data) abort call self.events.emit('stderr', a:data) endfunction " " on_exit " function! s:Job.on_exit(code) abort call self.events.emit('exit', a:code) endfunction " " create job instance " if has('nvim') function! s:_create(cmd, option, out, err, exit) abort let a:option.on_stdout = { id, data, event -> a:out(join(data, "\n")) } let a:option.on_stderr = { id, data, event -> a:err(join(data, "\n")) } let a:option.on_exit = { id, data, code -> a:exit(code) } let l:job = jobstart(a:cmd, a:option) return { \ 'stop': { -> jobstop(l:job) }, \ 'send': { data -> jobsend(l:job, data) } \ } endfunction else function! s:_create(cmd, option, out, err, exit) abort let a:option.noblock = v:true let a:option.in_io = 'pipe' let a:option.in_mode = 'raw' let a:option.out_io = 'pipe' let a:option.out_mode = 'raw' let a:option.err_io = 'pipe' let a:option.err_mode = 'raw' let a:option.out_cb = { job, data -> a:out(data) } let a:option.err_cb = { job, data -> a:err(data) } let a:option.exit_cb = { job, code -> a:exit(code) } let l:job = job_start(a:cmd, a:option) return { \ 'stop': { -> ch_close(l:job) }, \ 'send': { data -> ch_sendraw(l:job, data) } \ } endfunction endif ================================================ FILE: autoload/vital/_fzf_preview.vim ================================================ let s:_plugin_name = expand(':t:r') function! vital#{s:_plugin_name}#new() abort return vital#{s:_plugin_name[1:]}#new() endfunction function! vital#{s:_plugin_name}#function(funcname) abort silent! return function(a:funcname) endfunction ================================================ FILE: autoload/vital/fzf_preview.vim ================================================ let s:plugin_name = expand(':t:r') let s:vital_base_dir = expand(':h') let s:project_root = expand(':h:h:h') let s:is_vital_vim = s:plugin_name is# 'vital' let s:loaded = {} let s:cache_sid = {} function! vital#{s:plugin_name}#new() abort return s:new(s:plugin_name) endfunction function! vital#{s:plugin_name}#import(...) abort if !exists('s:V') let s:V = s:new(s:plugin_name) endif return call(s:V.import, a:000, s:V) endfunction let s:Vital = {} function! s:new(plugin_name) abort let base = deepcopy(s:Vital) let base._plugin_name = a:plugin_name return base endfunction function! s:vital_files() abort if !exists('s:vital_files') let s:vital_files = map( \ s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(), \ 'fnamemodify(v:val, ":p:gs?[\\\\/]?/?")') endif return copy(s:vital_files) endfunction let s:Vital.vital_files = function('s:vital_files') function! s:import(name, ...) abort dict let target = {} let functions = [] for a in a:000 if type(a) == type({}) let target = a elseif type(a) == type([]) let functions = a endif unlet a endfor let module = self._import(a:name) if empty(functions) call extend(target, module, 'keep') else for f in functions if has_key(module, f) && !has_key(target, f) let target[f] = module[f] endif endfor endif return target endfunction let s:Vital.import = function('s:import') function! s:load(...) abort dict for arg in a:000 let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg] let target = split(join(as, ''), '\W\+') let dict = self let dict_type = type({}) while !empty(target) let ns = remove(target, 0) if !has_key(dict, ns) let dict[ns] = {} endif if type(dict[ns]) == dict_type let dict = dict[ns] else unlet dict break endif endwhile if exists('dict') call extend(dict, self._import(name)) endif unlet arg endfor return self endfunction let s:Vital.load = function('s:load') function! s:unload() abort dict let s:loaded = {} let s:cache_sid = {} unlet! s:vital_files endfunction let s:Vital.unload = function('s:unload') function! s:exists(name) abort dict if a:name !~# '\v^\u\w*%(\.\u\w*)*$' throw 'vital: Invalid module name: ' . a:name endif return s:_module_path(a:name) isnot# '' endfunction let s:Vital.exists = function('s:exists') function! s:search(pattern) abort dict let paths = s:_extract_files(a:pattern, self.vital_files()) let modules = sort(map(paths, 's:_file2module(v:val)')) return uniq(modules) endfunction let s:Vital.search = function('s:search') function! s:plugin_name() abort dict return self._plugin_name endfunction let s:Vital.plugin_name = function('s:plugin_name') function! s:_self_vital_files() abort let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name) let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name) let base = builtin . ',' . installed return split(globpath(base, '**/*.vim', 1), "\n") endfunction function! s:_global_vital_files() abort let pattern = 'autoload/vital/__*__/**/*.vim' return split(globpath(&runtimepath, pattern, 1), "\n") endfunction function! s:_extract_files(pattern, files) abort let tr = {'.': '/', '*': '[^/]*', '**': '.*'} let target = substitute(a:pattern, '\.\|\*\*\?', '\=tr[submatch(0)]', 'g') let regexp = printf('autoload/vital/[^/]\+/%s.vim$', target) return filter(a:files, 'v:val =~# regexp') endfunction function! s:_file2module(file) abort let filename = fnamemodify(a:file, ':p:gs?[\\/]?/?') let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$') return join(split(tail, '[\\/]\+'), '.') endfunction " @param {string} name e.g. Data.List function! s:_import(name) abort dict if has_key(s:loaded, a:name) return copy(s:loaded[a:name]) endif let module = self._get_module(a:name) if has_key(module, '_vital_created') call module._vital_created(module) endif let export_module = filter(copy(module), 'v:key =~# "^\\a"') " Cache module before calling module._vital_loaded() to avoid cyclic " dependences but remove the cache if module._vital_loaded() fails. " let s:loaded[a:name] = export_module let s:loaded[a:name] = export_module if has_key(module, '_vital_loaded') try call module._vital_loaded(vital#{s:plugin_name}#new()) catch unlet s:loaded[a:name] throw 'vital: fail to call ._vital_loaded(): ' . v:exception . " from:\n" . s:_format_throwpoint(v:throwpoint) endtry endif return copy(s:loaded[a:name]) endfunction let s:Vital._import = function('s:_import') function! s:_format_throwpoint(throwpoint) abort let funcs = [] let stack = matchstr(a:throwpoint, '^function \zs.*, .\{-} \d\+$') for line in split(stack, '\.\.') let m = matchlist(line, '^\(.\+\)\%(\[\(\d\+\)\]\|, .\{-} \(\d\+\)\)$') if !empty(m) let [name, lnum, lnum2] = m[1:3] if empty(lnum) let lnum = lnum2 endif let info = s:_get_func_info(name) if !empty(info) let attrs = empty(info.attrs) ? '' : join([''] + info.attrs) let flnum = info.lnum == 0 ? '' : printf(' Line:%d', info.lnum + lnum) call add(funcs, printf('function %s(...)%s Line:%d (%s%s)', \ info.funcname, attrs, lnum, info.filename, flnum)) continue endif endif " fallback when function information cannot be detected call add(funcs, line) endfor return join(funcs, "\n") endfunction function! s:_get_func_info(name) abort let name = a:name if a:name =~# '^\d\+$' " is anonymous-function let name = printf('{%s}', a:name) elseif a:name =~# '^\d\+$' " is lambda-function let name = printf("{'%s'}", a:name) endif if !exists('*' . name) return {} endif let body = execute(printf('verbose function %s', name)) let lines = split(body, "\n") let signature = matchstr(lines[0], '^\s*\zs.*') let [_, file, lnum; __] = matchlist(lines[1], \ '^\t\%(Last set from\|.\{-}:\)\s*\zs\(.\{-}\)\%( \S\+ \(\d\+\)\)\?$') return { \ 'filename': substitute(file, '[/\\]\+', '/', 'g'), \ 'lnum': 0 + lnum, \ 'funcname': a:name, \ 'arguments': split(matchstr(signature, '(\zs.*\ze)'), '\s*,\s*'), \ 'attrs': filter(['dict', 'abort', 'range', 'closure'], 'signature =~# (").*" . v:val)'), \ } endfunction " s:_get_module() returns module object wihch has all script local functions. function! s:_get_module(name) abort dict let funcname = s:_import_func_name(self.plugin_name(), a:name) try return call(funcname, []) catch /^Vim\%((\a\+)\)\?:E117:/ return s:_get_builtin_module(a:name) endtry endfunction function! s:_get_builtin_module(name) abort return s:sid2sfuncs(s:_module_sid(a:name)) endfunction if s:is_vital_vim " For vital.vim, we can use s:_get_builtin_module directly let s:Vital._get_module = function('s:_get_builtin_module') else let s:Vital._get_module = function('s:_get_module') endif function! s:_import_func_name(plugin_name, module_name) abort return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name)) endfunction function! s:_module_sid(name) abort let path = s:_module_path(a:name) if !filereadable(path) throw 'vital: module not found: ' . a:name endif let vital_dir = s:is_vital_vim ? '__\w\+__' : printf('_\{1,2}%s\%%(__\)\?', s:plugin_name) let base = join([vital_dir, ''], '[/\\]\+') let p = base . substitute('' . a:name, '\.', '[/\\\\]\\+', 'g') let sid = s:_sid(path, p) if !sid call s:_source(path) let sid = s:_sid(path, p) if !sid throw printf('vital: cannot get from path: %s', path) endif endif return sid endfunction function! s:_module_path(name) abort return get(s:_extract_files(a:name, s:vital_files()), 0, '') endfunction function! s:_module_sid_base_dir() abort return s:is_vital_vim ? &rtp : s:project_root endfunction function! s:_dot_to_sharp(name) abort return substitute(a:name, '\.', '#', 'g') endfunction function! s:_source(path) abort execute 'source' fnameescape(a:path) endfunction " @vimlint(EVL102, 1, l:_) " @vimlint(EVL102, 1, l:__) function! s:_sid(path, filter_pattern) abort let unified_path = s:_unify_path(a:path) if has_key(s:cache_sid, unified_path) return s:cache_sid[unified_path] endif for line in filter(split(execute(':scriptnames'), "\n"), 'v:val =~# a:filter_pattern') let [_, sid, path; __] = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$') if s:_unify_path(path) is# unified_path let s:cache_sid[unified_path] = sid return s:cache_sid[unified_path] endif endfor return 0 endfunction if filereadable(expand(':r') . '.VIM') " is case-insensitive or not let s:_unify_path_cache = {} " resolve() is slow, so we cache results. " Note: On windows, vim can't expand path names from 8.3 formats. " So if getting full path via and $HOME was set as 8.3 format, " vital load duplicated scripts. Below's :~ avoid this issue. function! s:_unify_path(path) abort if has_key(s:_unify_path_cache, a:path) return s:_unify_path_cache[a:path] endif let value = tolower(fnamemodify(resolve(fnamemodify( \ a:path, ':p')), ':~:gs?[\\/]?/?')) let s:_unify_path_cache[a:path] = value return value endfunction else function! s:_unify_path(path) abort return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?')) endfunction endif " copied and modified from Vim.ScriptLocal let s:SNR = join(map(range(len("\")), '"[\\x" . printf("%0x", char2nr("\"[v:val])) . "]"'), '') function! s:sid2sfuncs(sid) abort let fs = split(execute(printf(':function /^%s%s_', s:SNR, a:sid)), "\n") let r = {} let pattern = printf('\m^function\s%d_\zs\w\{-}\ze(', a:sid) for fname in map(fs, 'matchstr(v:val, pattern)') let r[fname] = function(s:_sfuncname(a:sid, fname)) endfor return r endfunction "" Return funcname of script local functions with SID function! s:_sfuncname(sid, funcname) abort return printf('%s_%s', a:sid, a:funcname) endfunction ================================================ FILE: autoload/vital/fzf_preview.vital ================================================ fzf_preview 8e269699b0b993fc68599ed94571db5d3e933bcf VS.RPC.JSON Async.Promise VS.System.Job VS.Event.Emitter ================================================ FILE: bin/git_actions_preview ================================================ #!/usr/bin/env bash if [[ $1 == 'status' ]]; then git -c color.status=always status elif [[ $1 == 'branch' ]]; then git for-each-ref --sort=-committerdate --count=10 --color=always --format="Date: %(color:red)%(authordate:iso)%(color:reset) %(color:green)[%(authorname)]%(color:reset) Ref: %(color:yellow)%(refname:short)%(color:reset) Subject: %(subject) " elif [[ $1 =~ ^(log|push|fetch|pull) ]]; then git log --decorate --pretty='format:%C(yellow)%h %C(green)%cd %C(reset)%s %C(red)%d %C(cyan)[%an]' --date=iso --graph --color=always elif [[ $1 =~ ^current-log ]]; then file=$(echo "$1" | awk -F':' '{print $2}') if [[ $file != '' ]]; then git log --decorate --pretty='format:%C(yellow)%h %C(green)%cd %C(reset)%s %C(red)%d %C(cyan)[%an]' --date=iso --graph --color=always -- "$file" fi elif [[ $1 =~ ^commit ]]; then git diff --cached --color=always fi ================================================ FILE: bin/git_blame_pr ================================================ #! /usr/bin/perl # # Written in 2017 by Kazuho Oku # # To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. # You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . use strict; use warnings; my $git_blame_pid = open(my $fh, "-|", "git", "blame", "--first-parent", @ARGV) or die "failed to invoke git-blame:$!"; my %cached; # commit-id -> substitution string while (my $line = <$fh>) { my ($commit, $src) = split / .*?\) /, $line, 2; $cached{$commit} = lookup($commit) unless $cached{$commit}; print $cached{$commit}, ' ', $src; } while (waitpid($git_blame_pid, 0) != $git_blame_pid) {} exit $?; sub lookup { my $commit = shift; my $message = `git show --oneline $commit`; if ($message =~ /Merge\s+(?:pull\s+request|pr)\s+\#?(\d+)\s/i) { return sprintf '%-9s', "PR #$1"; } return $commit; } ================================================ FILE: bin/preview_yankround_register ================================================ #!/usr/bin/env bash if which gsed > /dev/null && which gcut > /dev/null; then gsed -n "$2,$2p" "$1" | gcut -f 2 | gsed -e "s/\x0/\n/g" else sed -n "$2,$2p" "$1" | cut -f 2 | sed -e "s/\x0/\n/g" fi ================================================ FILE: doc/fzf_preview_vim.txt ================================================ *fzf-preview-vim.txt* Author: yuki-yano License: MIT Version: 0.1 ============================================================================== CONTENTS *fzf-preview-contents* Introduction |fzf-preview-introduction| Installation |fzf-preview-installation| Mappings |fzf-preview-mappings| Commands |fzf-preview-commands| Keymap |fzf-preview-keymap| ============================================================================== INTRODUCTION *fzf-preview-introduction* *fzf-preview* is Vim script RPC, coc extensions or Neovim remote plugin to provide some fzf sources with preview. Requirements: - Neovim (Remote Plugin) https://neovim.io/ - coc.nvim (coc extensions) https://github.com/neoclide/coc.nvim - Node https://nodejs.org/ - git https://git-scm.com/ - fzf https://github.com/junegunn/fzf - ripgrep (Require some preview) https://github.com/BurntSushi/ripgrep Optional: - bat (Add color to the preview) https://github.com/sharkdp/bat - vim-devicons (Use devicons) https://github.com/ryanoasis/vim-devicons - Fugitive (Require git integration) https://github.com/tpope/vim-fugitive - Gina (Require git integration) https://github.com/lambdalisue/gina.vim - universal-ctags (Requre FzfPreviewCtags and FzfPreviewBufferTags) https://github.com/universal-ctags/ctags - vista.vim (Require FzfPreviewVistaCtags and FzfPreviewVistaBufferCtags) https://github.com/liuchengxu/vista.vim - vim-bookmarks (Require FzfPreviewBookmarks) https://github.com/MattesGroeger/vim-bookmarks - yankround.vim (Require FzfPreviewYankround) https://github.com/LeafCage/yankround.vim - memolist.vim (Require FzfPreviewMemoList and FzfPreviewMemoListGrep) https://github.com/glidenote/memolist.vim - todo-comments.nvim (Require FzfPreviewTodoComments) https://github.com/folke/todo-comments.nvim - GitHub cli (Require integrate with GitHub) https://github.com/cli/cli - Yarn (Build latest remote plugin) https://classic.yarnpkg.com/ Latest version: https://github.com/yuki-yano/fzf-preview.vim ============================================================================== VIM SCRIPT RPC PLUGIN INSTALLATION *fzf-preview-vim-rpc-installation* Use Dein, vim-plug or any Vim plugin manager of your choice. Install release/rpc branch. > Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } Plug 'yuki-yano/fzf-preview.vim', { 'branch': 'release/rpc' } < or > call dein#add('junegunn/fzf', { 'build': './install --all', 'merged': 0 }) call dein#add('yuki-yano/fzf-preview.vim', { 'rev': 'release/rpc' }) < ============================================================================== REMOTE PLUGIN INSTALLATION *fzf-preview-remote-installation* Install the npm package [neovim](https://www.npmjs.com/package/neovim) to get the remote plugin working. > $ npm install -g neovim < Use Dein, vim-plug or any Vim plugin manager of your choice. Install release/remote branch and `:UpdateRemotePlugins` when after installed plugin. > Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } Plug 'yuki-yano/fzf-preview.vim', { 'branch': 'release/remote', 'do': ':UpdateRemotePlugins' } < or > call dein#add('junegunn/fzf', { 'build': './install --all', 'merged': 0 }) call dein#add('yuki-yano/fzf-preview.vim', { 'rev': 'release/remote' }) < ============================================================================== COC EXTENSION INSTALLATION *fzf-preview-coc-installation* Install the [coc.nvim](https://github.com/neoclide/coc.nvim) Install coc extensions fzf-preview. use vim-plug: > Plug 'junegunn/fzf', { 'do': { -> fzf#install() } } Plug 'neoclide/coc.nvim', { 'branch': 'release' } < use dein: > call dein#add('junegunn/fzf', { 'build': './install --all', 'merged': 0 }) call dein#add('neoclide/coc.nvim', { 'rev': 'release', 'merged': 0 }) < execute after :CocInstall coc-fzf-preview ============================================================================== COMMANDS *fzf-preview-commands* Vim script RPC, Remote Plugin, and coc extensions, in that order. *:FzfPreviewProjectFiles* :FzfPreviewProjectFilesRpc :FzfPreviewProjectFiles :CocCommand fzf-preview.ProjectFiles Select and open git repository files from project. *:FzfPreviewGitFiles* :FzfPreviewGitFilesRpc :FzfPreviewGitFiles :CocCommand fzf-preview.GitFiles Select and open git repository files from git ls-files. *:FzfPreviewDirectoryFiles* :FzfPreviewDirectoryFilesRpc {path or none} :FzfPreviewDirectoryFiles {path or none} :CocCommand fzf-preview.DirectoryFiles {path or none} Select and open directory files from ripgrep with fzf interface. Default to current working directory. *:FzfPreviewBuffers* :FzfPreviewBuffersRpc :FzfPreviewBuffers :CocCommand fzf-preview.Buffers Select and open file buffers. Use open-buffer processes. *:FzfPreviewAllBuffers* :FzfPreviewAllBuffersRpc :FzfPreviewAllBuffers :CocCommand fzf-preview.AllBuffers Select and open all buffers(include not file). Use open-bufnr processes. *:FzfPreviewProjectOldFiles* :FzfPreviewProjectOldFilesRpc :FzfPreviewProjectOldFiles :CocCommand fzf-preview.ProjectOldFiles Select and open the past open files in the project using fzf. The target file is selected from |v:oldfiles|. *:FzfPreviewProjectMruFiles* :FzfPreviewProjectMruFilesRpc :FzfPreviewProjectMruFiles :CocCommand fzf-preview.ProjectMruFiles Select and open the past open files in the project using fzf. The target file is selected from most recently used. *:FzfPreviewProjectMrwFiles* :FzfPreviewMrwFilesRpc :FzfPreviewMrwFiles :CocCommand fzf-preview.MrwFiles Select and open the past open files in the project using fzf. The target file is selected from most recently written. *:FzfPreviewProjectGrep* :FzfPreviewProjectGrepRpc :FzfPreviewProjectGrep :CocCommand fzf-preview.ProjectGrep Search text from the project. The retrieved result is displayed in file name, number of lines, preview and can be opened with fzf interface. *:FzfPreviewProjectGrepRecall* :FzfPreviewProjectGrepRecallRpc :FzfPreviewProjectGrepRecall :CocCommand fzf-preview.ProjectGrepRecall Run FzfPreviewProjectGrep with the same arguments as before. *:FzfPreviewCTags* :FzfPreviewCtagsRpc :FzfPreviewCtags :CocCommand fzf-preview.Ctags Select tags from tags file The same script as FzfPreviewProjectGrep is used for preview. Required: |universal-ctags| Cautions: Set --excmd=number or --excmd=combine to universal-ctags option *:FzfPreviewBufferTags* :FzfPreviewBufferTagsRpc :FzfPreviewBufferTags :CocCommand fzf-preview.BufferTags Select from tags of currently open buffer The same script as FzfPreviewProjectGrep is used for preview. Required: |universal-ctags| *:FzfPreviewOldFiles* :FzfPreviewOldFilesRpc :FzfPreviewOldFiles :CocCommand fzf-preview.OldFiles Select and open the opened file. For the target file, see |v:oldfiles|. *:FzfPreviewMruFiles* :FzfPreviewMruFilesRpc :FzfPreviewMruFiles :CocCommand fzf-preview.MruFiles Select and open the opened file. For the target file, most recently used. *:FzfPreviewMrwFiles* :FzfPreviewMrwFilesRpc :FzfPreviewMrwFiles :CocCommand fzf-preview.MrwFiles Select and open the opened file. For the target file, most recently written. *:FzfPreviewQuickFix* :FzfPreviewQuickFixRpc :FzfPreviewQuickFix :CocCommand fzf-preview.QuickFix Select and open the QuickFix item. *:FzfPreviewLocationList* :FzfPreviewLocationListRpc :FzfPreviewLocationList :CocCommand fzf-preview.LocationList Select and open the LocationList item. *:FzfPreviewLines* :FzfPreviewLinesRpc :FzfPreviewLines :CocCommand fzf-preview.Lines Select line from current buffer. *:FzfPreviewBufferLines* :FzfPreviewBufferLinesRpc :FzfPreviewBufferLines :CocCommand fzf-preview.BufferLines Select line from all buffer *:FzfPreviewJumps* :FzfPreviewJumpsRpc :FzfPreviewJumps :CocCommand fzf-preview.Jumps Select and open the jumplist *:FzfPreviewChanges* :FzfPreviewChangesRpc :FzfPreviewChanges :CocCommand fzf-preview.Changes Select and open the changelist *:FzfPreviewMarks* :FzfPreviewMarksRpc :FzfPreviewMarks :CocCommand fzf-preview.Marks List marks from the project. The retrieved result is displayed in file name, number of lines. The same script as FzfPreviewProjectGrep is used for preview. *:FzfPreviewFromResources* :FzfPreviewFromResourcesRpc :FzfPreviewFromResources :CocCommand fzf-preview.FromResources Select a resource and generate a file list. From there, select the file using the fzf interface. Resources: [project, git, directory, buffer, project_old, project_mru, project_mrw, old, mru, mrw] *:FzfPreviewCommandPalette* :FzfPreviewCommandPaletteRpc :FzfPreviewCommandPalette :CocCommand fzf-preview.CommandPalette Execute and edit command history *:FzfPreviewGrepHelp* :FzfPreviewGrepHelpRpc :FzfPreviewGrepHelp :CocCommand fzf-preview.GrepHelp Grep vim help. *:FzfPreviewGitStatus* :FzfPreviewGitStatusRpc :FzfPreviewGitStatus :CocCommand fzf-preview.GitStatus Git status integration. *:FzfPreviewGitActions* :FzfPreviewGitActionsRpc :FzfPreviewGitActions :CocCommand fzf-preview.GitActions Interactive git integration. Select resource and action. *:FzfPreviewVistaCtags* :FzfPreviewVistaCtagsRpc :FzfPreviewVistaCtags :CocCommand fzf-preview.VistaCtags Select tags from vista.vim. Required: vista.vim *:FzfPreviewVistaBufferCtags* :FzfPreviewVistaBufferCtagsRpc :FzfPreviewVistaBufferCtags :CocCommand fzf-preview.VistaBufferCtags Select current buffer tags from vista.vim. Required: vista.vim *:FzfPreviewNvimLspReferences* :FzfPreviewNvimLspReferencesRpc :FzfPreviewNvimLspReferences Select and open the references from nvim-lsp. *:FzfPreviewNvimLspDiagnostics* :FzfPreviewNvimLspDiagnosticsRpc :FzfPreviewNvimLspDiagnostics Select and open the diagnostics from nvim-lsp. *:FzfPreviewNvimLspCurrentDiagnostics* :FzfPreviewNvimLspCurrentDiagnosticsRpc :FzfPreviewNvimLspCurrentDiagnostics Select and open the current file diagnostics from nvim-lsp. *:FzfPreviewNvimLspDefinitions* :FzfPreviewNvimLspDefinitionRpc :FzfPreviewNvimLspDefinition Select and open the definitions from nvim-lsp. *:FzfPreviewNvimLspTypeDefinitions* :FzfPreviewNvimLspTypeDefinitionsRpc :FzfPreviewNvimLspTypeDefinition Select and open the type definitions from nvim-lsp. *:FzfPreviewNvimLspImplementations* :FzfPreviewNvimLspImplementationsRpc :FzfPreviewNvimLspImplementations Select and open the implementations from nvim-lsp. *:FzfPreviewVimLspReferences* :FzfPreviewVimLspReferencesRpc :FzfPreviewVimLspReferences Select and open the references from vim-lsp. *:FzfPreviewVimLspDiagnostics* :FzfPreviewVimLspDiagnosticsRpc :FzfPreviewVimLspDiagnostics Select and open the diagnostics from vim-lsp. *:FzfPreviewVimLspCurrentDiagnostics* :FzfPreviewVimLspCurrentDiagnosticsRpc :FzfPreviewVimLspCurrentDiagnostics Select and open the current file diagnostics from vim-lsp. *:FzfPreviewVimLspDefinitions* :FzfPreviewVimLspDefinitionRpc :FzfPreviewVimLspDefinition Select and open the definitions from vim-lsp. *:FzfPreviewVimLspTypeDefinitions* :FzfPreviewVimLspTypeDefinitionsRpc :FzfPreviewVimLspTypeDefinition Select and open the type definitions from vim-lsp. *:FzfPreviewVimLspImplementations* :FzfPreviewVimLspImplementationsRpc :FzfPreviewVimLspImplementations Select and open the implementations from vim-lsp. *:FzfPreviewBookmarks* :FzfPreviewBookmarksRpc :FzfPreviewBookmarks :CocCommand fzf-preview.Bookmarks Select and open the bookmarks from vim-bookmarks *:FzfPreviewYankround* :FzfPreviewYankroundRpc :FzfPreviewYankround :CocCommand fzf-preview.Yankround Select register history and insert " register from yankround.vim Use register processes. *:FzfPreviewMemoList* :FzfPreviewMemoListRpc :FzfPreviewMemoList :CocCommand fzf-preview.MemoList Select memolist and open from memolist.vim. *:FzfPreviewMemoListGrep* :FzfPreviewMemoListGrepRpc :FzfPreviewMemoListGrep :CocCommand fzf-preview.MemoListGrep Grep memolist and open from memolist.vim. *:FzfPreviewTodoComments* :FzfPreviewTodoCommentsRpc :FzfPreviewTodoComments :CocCommand fzf-preview.TodoComments Search TodoComments from todo-comments.nvim *:FzfPreviewBlamePR* :FzfPreviewBlamePRRpc :FzfPreviewBlamePR :CocCommand fzf-preview.BlamePR The PR corresponding to each line is displayed and the selected PR is opened in the browser. Use open-pr processes. *:FzfPreviewCocReferences* :CocCommand fzf-preview.CocReferences Select and open the references from coc.nvim. Only coc extensions. *:FzfPreviewCocDiagnostics* :CocCommand fzf-preview.CocDiagnostics Select and open the diagnostics from coc.nvim. Only coc extensions. *:FzfPreviewCocCurrentDiagnostics* :CocCommand fzf-preview.CocCurrentDiagnostics Select and open the current file diagnostics from coc.nvim. Only coc extensions. *:FzfPreviewCocDefinition* :CocCommand fzf-preview.CocDefinition Select and open the definitions from coc.nvim. Only coc extensions. *:FzfPreviewCocTypeDefinition* :CocCommand fzf-preview.CocTypeDefinition Select and open the type definitions from coc.nvim. Only coc extensions. *:FzfPreviewCocImplementations* :CocCommand fzf-preview.CocImplementations Select and open the implementations from coc.nvim. Only coc extensions. *:FzfPreviewCocOutline* :CocCommand fzf-preview.CocOutline Select and open the outline from coc.nvim. Only coc extensions. ============================================================================== COMMAND OPTIONS *fzf-preview-command-options* --processes Set process when selecting element with fzf started by this command. Value must be a global variable name. Variable is dictionary and format is same as get from |fzf_preview#remote#process#get_default_processes|. Most command pass a file paths to the process. FzfPreviewAllBuffers will be passed “buffer {bufnr}” Value example: let g:foo_processes = { \ 'enter': 'FzfPreviewOpenFileEnter', \ 'ctrl-x': get(function('s:foo_function'), 'name'), \ } --add-fzf-arg Set the arguments to be passed when executing fzf. This value is added to the default options. Value must be a string without spaces. Usage example: Exclude filename with FzfPreviewProjectGrep > nnoremap g :FzfPreviewProjectGrep --add-fzf-arg=--nth=3 " nnoremap g :CocCommand fzf-preview.ProjectGrep --add-fzf-arg=--nth=3 < --resume Reuse the input that was last used to select the element with fzf. Do not need to pass a value for this option. Usage example: Reuse last query for project grep. > nnoremap G :FzfPreviewProjectGrep . --resume " nnoremap G :CocCommand fzf-preview.ProjectGrep . --resume < ============================================================================== OPTIONS *fzf-preview-options* *g:fzf_preview_floating_window_rate* This value is used for the size of the floating window. The size of the floating window is the size of the vim multiplied by this value. Set between 0 and 1. Default value is 0.9 *g:fzf_preview_direct_window_option* This value is used to position the window. It is passed to the window option of |fzf#run|, and |g:fzf_preview_floating_window_rate| is disabled. Default value is '' *g:fzf_preview_default_fzf_options* It is used as the default option when starting fzf. Set a dictionary variable whose option name is key. The string you pass to value is automatically enclosed in double quotes. If the value to be passed to the option is empty, set v:true. Default value is { '--reverse': v:true, '--preview-window': 'wrap' } *g:fzf_preview_quit_map* If this value is 1, mapping that will safely quit in fzf's normal mode and visual mode is added Default value is 1 *g:fzf_preview_command* Shell command used for fzf preview. The head command is used by default. If the bat command is installed, the bat is used by default and the preview is syntax highlighted. Not installed bat: Default value is 'cat' Installed bat: Default value is 'bat --color=always --plain {-1}' *g:fzf_preview_if_binary_command* g:fzf_binary_preview_command is executed if this command succeeds, and g:fzf_preview_command is executed if it fails Default value is '[[ "$(file --mime {})" =~ binary ]]' *g:fzf_binary_preview_command* Shell command used for binary file preview. Since it is heavy to display in text, if there is no particular reason, echo etc. substitute. Default value is 'echo "{} is a binary file"' *g:fzf_preview_filelist_command* This is the command used to search for files in the project. If ripgrep is not included, git ls-files will be used. Installed ripgrep: Default value is 'rg --files --hidden --follow --no-messages --glob "!.git/*" --glob \!"* *"' Not installed ripgrep: Default value is 'git ls-files --exclude-standard' *g:fzf_preview_git_files_command* This is the command used to search for files in the git repository. Default value is 'git ls-files --exclude-standard' *g:fzf_preview_directory_files_command* This is the command used to search for files in the current directory. Default value is 'rg --files --hidden --follow --no-messages -g \!"* *"' Keep in mind a path can be append at the end of the command. *g:fzf_preview_git_status_command* This is the command used to git status files Default value is 'git -c color.status=always status --short --untracked-files=all' *g:fzf_preview_git_status_preview_command* This is the command used to git status preview Default value is "[[ $(git diff --cached -- {-1}) != \"\" ]] && git diff --cached --color=always -- {-1} || " . \ "[[ $(git diff -- {-1}) != \"\" ]] && git diff --color=always -- {-1} || " . \ g:fzf_preview_command *g:fzf_preview_grep_cmd* This command is used for project grep. Recommend using a fast grep command such as ripgrep or ag. Interactively grep the specified directory. Installed ripgrep: Default value is 'rg --line-number --no-heading --color=never --hidden' Not installed ripgrep: :ProjectGrepPreview is disabled. *g:fzf_preview_cache_directory* Specify the directory to store the MRU and MRW cache files. Default value is 'expand('~/.cache/vim/fzf_preview')' *g:fzf_preview_disable_mru* If this value is not 0, disable mru and mrw. Default value is 0 *g:fzf_preview_mru_limit* Limit of the number of files to be saved by mru. Default value is 1000 *g:fzf_preview_lines_command* This command is used for get current file lines. Installed bat: Default value is 'bat --color=always --plain --number' Not installed bat: Default value is 'cat -n' *g:fzf_preview_grep_preview_cmd* This command is used for :ProjectGrepPreview. bin/preview.rb is used by default. Default value is 'expand(":h:h") . "/bin/preview.rb"' *g:fzf_preview_preview_key_bindings* This command determines keyboard shortcuts during an interactive FZF session. Options are a string passed directly to fzf's "--bind" option. Default value is '' *g:fzf_preview_fzf_color_option* Specify the color option to be passed to the fzf command. Options are a string passed directly to fzf's "--color" option. Default value is '' *g:fzf_preview_custom_processes* Set the custom processes. Dictionary format is { '{processes_name}': { '{input_key}': |String|(Global function name) } } Default child value can be get with |fzf_preview#remote#process#get_default_processes| Default value is {} Example: { 'open-file': { 'ctrl-o': 'FzfPreviewOpenFileCtrlO', 'ctrl-q': 'FzfPreviewOpenFileCtrlQ', 'ctrl-t': 'FzfPreviewOpenFileCtrlT', 'ctrl-v': 'FzfPreviewOpenFileCtrlV', 'ctrl-x': 'FzfPreviewOpenFileCtrlX', 'enter': 'FzfPreviewOpenFileEnter' }, 'register': { 'enter': 'FzfPreviewRegisterEnter' } } *g:fzf_preview_fzf_preview_window_option* Used as fzf preview-window option For example, 'up:30%' Default value is '' *g:fzf_preview_use_dev_icons* If this value is 1, devicons is used as fzf candidate. Default value is 0 *g:fzf_preview_history_dir* If this value is directory path, use the files in the target directory and the fzf history option will be enabled. For example, '~/.fzf' Default value is false *g:fzf_preview_dev_icon_prefix_string_length* Set how many string length devicon is represented. Default value is 3 *g:fzf_preview_dev_icons_limit* Devicons can make fzf-preview slow when the number of results is high. By default icons are disable when number of results is higher this value. Default value is 5000 *g:fzf_preview_yankround_preview_command* Command to preview yankround history. Defaul value is expand(':h:h') . '/bin/preview_yankround_register' *g:fzf_preview_blame_pr_command* Command to FzfPreviewBlamePR source. Defaul value is expand(':h:h') . '/bin/git_blame_pr' *g:fzf_preview_update_statusline* If true, statusline is updated during fzf-preview startup. Defaul value is true *$FZF_PREVIEW_PREVIEW_BAT_THEME* The theme used in the bat preview. Default value is 'OneHalfDark' *$FZF_PREVIEW_PLUGIN_HELP_ROOT_DIR* The root directory to be searched by GrepHelp. Default value is '' ============================================================================== FUNCTIONS *fzf-preview-functions* *fzf_preview#remote#process#get_default_processes* Get the initial value of the processes. Args is {process_name} and {plugin_type (optional)}. processes_name is 'open-file', 'open-buffer' and 'open-bufnr'. plugin_type is 'remote', 'coc' or 'rpc'. Default value is 'remote'. value from 'open-file' (and 'remote') is { 'ctrl-o': 'FzfPreviewOpenFileCtrlO', 'ctrl-q': 'FzfPreviewOpenFileCtrlQ', 'ctrl-t': 'FzfPreviewOpenFileCtrlT', 'ctrl-v': 'FzfPreviewOpenFileCtrlV', 'ctrl-x': 'FzfPreviewOpenFileCtrlX', 'enter': 'FzfPreviewOpenFileEnter' } value from 'open-file' and 'coc' is { 'ctrl-o': 'OpenFileCtrlO', 'ctrl-q': 'OpenFileCtrlQ', 'ctrl-t': 'OpenFileCtrlT', 'ctrl-v': 'OpenFileCtrlV', 'ctrl-x': 'OpenFileCtrlX', 'enter': 'OpenFileEnter' } value from 'open-file' and 'rpc' is { 'ctrl-o': 'FzfPreviewOpenFileCtrlO', 'ctrl-q': 'FzfPreviewOpenFileCtrlQ', 'ctrl-t': 'FzfPreviewOpenFileCtrlT', 'ctrl-v': 'FzfPreviewOpenFileCtrlV', 'ctrl-x': 'FzfPreviewOpenFileCtrlX', 'enter': 'FzfPreviewOpenFileEnter' } ============================================================================== USER AUTOCMD EVENTS *fzf-preview-user-autocmd-events* *fzf_preview#remote#initialized* Runs when initialization of the Remote Plugin is complete. *fzf_preview#rpc#initialized* Runs when initialization of the Vim script RPC is complete. *fzf_preview#coc#initialized* Runs when initialization of the coc extensions is complete. Deprecated *fzf_preview#initialized* Runs when initialization of the Remote Plugin, coc extensions and Vim script RPC is complete. ============================================================================== BASE DEFAULT KEYMAP *fzf-preview-keymap* , - Cancel fzf - Open split - Open vsplit - Open tabedit - Open drop - Build QuickFix from open-file processes. - Execute :bdelete! command from open-buffer and open-bufnr processes. - Preview page down - Preview page up ? - Toggle Preview ============================================================================== EXAMPLE *fzf-preview-mappings* Here is mapping example. > nnoremap p :FzfPreviewProjectFiles nnoremap b :FzfPreviewBuffers nnoremap m :FzfPreviewProjectOldFiles nnoremap M :FzfPreviewOldFiles < vim:tw=78:sw=2:ts=2:ft=help:norl:nowrap: ================================================ FILE: jest.config.ts ================================================ import type { Config } from "@jest/types" const config: Config.InitialOptions = { verbose: true, preset: "ts-jest/presets/js-with-ts", transform: { "^.+\\.ts$": "ts-jest", }, moduleNameMapper: { "^@/(.*)$": "/src/$1", }, testMatch: ["**/*.test.ts"], restoreMocks: true, clearMocks: true, resetMocks: true, } export default config ================================================ FILE: lua/fzf-preview/init.lua ================================================ local M = {} local lsp = vim.lsp function M.nvim_lsp_references() vim.g.fzf_preview_nvim_lsp_references = nil local method = 'textDocument/references' ---@diagnostic disable-next-line: missing-parameter local params = lsp.util.make_position_params() params.context = { includeDeclaration = false } local response = lsp.buf_request_sync(0, method, params, 1000) if response == nil or vim.tbl_isempty(response) then print('No location found: ' .. method) return end local result = {} for _, v in pairs(response) do vim.list_extend(result, v.result) end vim.g.fzf_preview_nvim_lsp_references = result end function M.nvim_lsp_diagnostics() vim.g.fzf_preview_nvim_lsp_diagnostics = vim.diagnostic.get() end function M.nvim_lsp_current_diagnostics() vim.g.fzf_preview_nvim_lsp_current_diagnostics = vim.diagnostic.get(0) end function M.nvim_lsp_definition() vim.g.fzf_preview_nvim_lsp_definition = nil local method = 'textDocument/definition' ---@diagnostic disable-next-line: missing-parameter local params = lsp.util.make_position_params() local response = lsp.buf_request_sync(0, method, params, 1000) if response == nil or vim.tbl_isempty(response) then print('No location found: ' .. method) return end local result = {} for _, v in pairs(response) do vim.list_extend(result, v.result) end vim.g.fzf_preview_nvim_lsp_definition = result end function M.nvim_lsp_type_definition() vim.g.fzf_preview_nvim_lsp_type_definition = nil local method = 'textDocument/typeDefinition' ---@diagnostic disable-next-line: missing-parameter local params = lsp.util.make_position_params() local response = lsp.buf_request_sync(0, method, params, 1000) if response == nil or vim.tbl_isempty(response) then print('No location found: ' .. method) return end local result = {} for _, v in pairs(response) do vim.list_extend(result, v.result) end vim.g.fzf_preview_nvim_lsp_type_definition = result end function M.nvim_lsp_implementations() vim.g.fzf_preview_nvim_lsp_implementations = nil local method = 'textDocument/implementation' ---@diagnostic disable-next-line: missing-parameter local params = lsp.util.make_position_params() local response = lsp.buf_request_sync(0, method, params, 1000) if response == nil or vim.tbl_isempty(response) then print('No location found: ' .. method) return end local result = {} for _, v in pairs(response) do vim.list_extend(result, v.result) end vim.g.fzf_preview_nvim_lsp_implementations = result end return M ================================================ FILE: package.json ================================================ { "name": "coc-fzf-preview", "version": "2.16.6", "description": "The plugin that powerfully integrates fzf and (Neo)vim. It is also possible to integrate with coc.nvim.", "author": "yuki-yano", "license": "MIT", "repository": { "url": "git@github.com:yuki-yano/fzf-preview.vim" }, "main": "./lib/coc.js", "files": [ "lib/coc.js", "autoload", "plugin", "bin" ], "keywords": [ "coc.nvim", "fzf" ], "engines": { "coc": "^0.0.80", "node": ">=12.0.0" }, "activationEvents": [ "*" ], "scripts": { "build": "npm-run-all --parallel build:*", "build:remote": "yarn run webpack --config webpack.remote.ts", "build:coc": "yarn run webpack --config webpack.coc.ts", "build:rpc": "yarn run webpack --config webpack.rpc.ts", "build:preview-script": "yarn run webpack --config webpack.preview.ts", "dev": "npm-run-all --parallel dev:*", "dev:remote": "yarn run webpack --watch --config webpack.remote.ts", "dev:coc": "yarn run webpack --watch --config webpack.coc.ts", "dev:rpc": "yarn run webpack --watch --config webpack.rpc.ts", "release-build:remote": "yarn run clean && yarn run build:remote --mode=production", "release-build:coc": "yarn run clean && yarn run build:coc --mode=production", "release-build:rpc": "yarn run clean && yarn run build:rpc --mode=production", "typecheck": "tsc --noEmit", "typecheck:watch": "tsc --noEmit --watch", "lint": "eslint --ext .ts src", "lint:fix": "eslint --ext .ts --fix src", "lint:watch": "esw --watch --color --clear --ext .ts src", "prettier": "prettier --check src/**/*.ts", "prettier:fix": "prettier --write src/**/*.ts", "test": "jest", "test:watch": "jest --watch", "cli": "ts-node --files -r tsconfig-paths/register", "clean": "rimraf lib rplugin/node/fzf-preview.vim/index.js", "print-version": "cat package.json | yarn run --silent json version" }, "dependencies": { "@reduxjs/toolkit": "^1.8.5", "camelcase": "^6.3.0", "chalk": "^5.0.1", "coc.nvim": "^0.0.82", "expand-tilde": "^2.0.2", "immer": "^9.0.15", "lodash": "^4.17.20", "neovim": "^4.10.1", "printf": "^0.6.0", "redux": "^4.2.0", "strip-ansi": "^7.0.1", "type-fest": "^2.19.0", "uuid": "^9.0.0", "vscode-jsonrpc": "^8.0.2", "vscode-languageserver-types": "^3.17.2", "yargs": "^17.5.1" }, "devDependencies": { "@jest/types": "^29.0.3", "@types/eslint": "^8.4.6", "@types/expand-tilde": "^2.0.0", "@types/jest": "^29.0.1", "@types/lodash": "^4.14.185", "@types/node": "^18.7.16", "@types/prettier": "^2.7.0", "@types/rimraf": "^3.0.2", "@types/uuid": "^8.3.4", "@types/webpack": "^5.28.0", "@types/webpack-merge": "^5.0.0", "@types/yargs": "^17.0.12", "@typescript-eslint/eslint-plugin": "^5.36.2", "@typescript-eslint/parser": "^5.36.2", "can-npm-publish": "^1.3.6", "eslint": "^8.23.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-typescript": "^3.5.1", "eslint-plugin-eslint-comments": "^3.2.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jest": "^27.0.4", "eslint-plugin-n": "^15.2.5", "eslint-plugin-simple-import-sort": "^8.0.0", "eslint-watch": "^8.0.0", "jest": "^29.0.3", "json": "^11.0.0", "npm-run-all": "^4.1.5", "prettier": "^2.7.1", "rimraf": "^3.0.2", "ts-jest": "^29.0.0", "ts-loader": "^9.3.1", "ts-node": "^10.9.1", "tsconfig-paths": "^4.1.0", "typescript": "^4.8.3", "webpack": "^5.74.0", "webpack-cli": "^4.10.0", "webpack-merge": "^5.8.0" } } ================================================ FILE: plugin/fzf_preview.vim ================================================ scriptencoding utf-8 if exists('s:loaded') finish endif let s:loaded = 1 if !exists('$FZF_PREVIEW_PREVIEW_BAT_THEME') let $FZF_PREVIEW_PREVIEW_BAT_THEME = 'OneHalfDark' endif let s:bat_theme_option = '--theme=' . $FZF_PREVIEW_PREVIEW_BAT_THEME if !exists('g:fzf_preview_floating_window_rate') let g:fzf_preview_floating_window_rate = 0.9 endif if !exists('g:fzf_preview_direct_window_option') let g:fzf_preview_direct_window_option = '' endif if !exists('g:fzf_preview_default_fzf_options') let g:fzf_preview_default_fzf_options = { \ '--reverse': v:true, \ '--preview-window': 'wrap', \ } endif if !exists('g:fzf_preview_quit_map') let g:fzf_preview_quit_map = 1 endif if !exists('g:fzf_preview_command') if executable('bat') let g:fzf_preview_command = 'bat ' . s:bat_theme_option . ' --color=always --plain --number {-1}' else let g:fzf_preview_command = 'head -100 {-1}' endif endif if !exists('g:fzf_preview_if_binary_command') let g:fzf_preview_if_binary_command = '[[ "$(file --mime {})" =~ binary ]]' endif if !exists('g:fzf_binary_preview_command') let g:fzf_binary_preview_command = 'echo "{} is a binary file"' endif if !exists('g:fzf_preview_filelist_command') if executable('rg') let g:fzf_preview_filelist_command = "rg --files --hidden --no-messages --glob '!.git/*' --glob \!'* *'" else let g:fzf_preview_filelist_command = 'git ls-files --exclude-standard' endif endif if !exists('g:fzf_preview_git_files_command') let g:fzf_preview_git_files_command = 'git ls-files --exclude-standard' endif if !exists('g:fzf_preview_directory_files_command') let g:fzf_preview_directory_files_command = 'rg --files --hidden --no-messages -g \!"* *"' endif if !exists('g:fzf_preview_git_status_command') let g:fzf_preview_git_status_command = 'git -c color.status=always status --short --untracked-files=all' endif if !exists('g:fzf_preview_git_status_preview_command') let g:fzf_preview_git_status_preview_command = '[[ $(git diff --cached -- {-1}) != "" ]] && git diff --cached --color=always -- {-1} || ' . \ '[[ $(git diff -- {-1}) != "" ]] && git diff --color=always -- {-1} || ' . \ g:fzf_preview_command endif if !exists('g:fzf_preview_grep_cmd') let g:fzf_preview_grep_cmd = 'rg --line-number --no-heading --color=never --hidden' endif if !exists('g:fzf_preview_lines_command') if executable('bat') let g:fzf_preview_lines_command = 'bat ' . s:bat_theme_option . ' --color=always --plain --number' else let g:fzf_preview_lines_command = 'cat -n' endif endif if !exists('g:fzf_preview_grep_preview_cmd') let g:fzf_preview_grep_preview_cmd = expand(':h:h') . '/bin/preview_fzf_grep' endif if !exists('g:fzf_preview_disable_mru') let g:fzf_preview_disable_mru = 0 endif if !exists('g:fzf_preview_mru_limit') let g:fzf_preview_mru_limit = 1000 endif if !exists('g:fzf_preview_cache_directory') let g:fzf_preview_cache_directory = expand('~/.cache/vim/fzf_preview') endif if !exists('g:fzf_preview_fzf_color_option') let g:fzf_preview_fzf_color_option = '' endif if !exists('g:fzf_preview_custom_processes') let g:fzf_preview_custom_processes = {} endif if !exists('g:fzf_preview_fzf_preview_window_option') let g:fzf_preview_fzf_preview_window_option = '' endif if !exists('g:fzf_preview_preview_key_bindings') let g:fzf_preview_preview_key_bindings = '' endif if !exists('g:fzf_preview_history_dir') let g:fzf_preview_history_dir = v:false endif if !exists('g:fzf_preview_buffers_jump') let g:fzf_preview_buffers_jump = 0 endif if !exists('g:fzf_preview_use_dev_icons') let g:fzf_preview_use_dev_icons = 0 endif if !exists('g:fzf_preview_dev_icon_prefix_string_length') let g:fzf_preview_dev_icon_prefix_string_length = 3 endif if !exists('g:fzf_preview_dev_icons_limit') let g:fzf_preview_dev_icons_limit = 5000 endif if !exists('g:fzf_preview_yankround_preview_command') let g:fzf_preview_yankround_preview_command = expand(':h:h') . '/bin/preview_yankround_register' endif if !exists('g:fzf_preview_blame_pr_command') let g:fzf_preview_blame_pr_command = expand(':h:h') . '/bin/git_blame_pr' endif if !exists('g:fzf_preview_update_statusline') let g:fzf_preview_update_statusline = v:true endif let g:fzf_preview_script_dir = expand(':h:h') . '/bin' let s:save_cpo = &cpoptions set cpoptions&vim command! FzfPreviewInstall :call fzf_preview#install() augroup fzf_preview_buffers autocmd! if g:fzf_preview_quit_map autocmd FileType fzf tnoremap autocmd FileType fzf nnoremap i autocmd FileType fzf vnoremap i endif augroup END augroup fzf_preview_mr autocmd! if g:fzf_preview_disable_mru == 0 autocmd BufEnter,VimEnter,BufWinEnter,BufWritePost * call s:mru_append(expand('')) autocmd BufWritePost * call s:mrw_append(expand('')) endif augroup END function! s:mru_append(path) abort if s:enable_file(a:path) call fzf_preview#remote#mr#append(a:path, fzf_preview#remote#mr#mru_file_path()) endif endfunction function! s:mrw_append(path) abort if s:enable_file(a:path) call fzf_preview#remote#mr#append(a:path, fzf_preview#remote#mr#mrw_file_path()) endif endfunction function! s:enable_file(path) abort if bufnr('%') != expand('') || a:path == '' return v:false else return v:true endif endfunction augroup fzf_preview_initialized autocmd! autocmd VimEnter * call s:doautocmd_from_remote() autocmd User CocNvimInit call s:doautocmd_from_coc() autocmd VimEnter * call s:doautocmd_from_rpc() autocmd FileType fzf call fzf_preview#remote#window#set_fzf_last_query() autocmd FileType fzf call fzf_preview#remote#window#set_status_line(v:false) augroup END function! s:doautocmd_from_remote() abort if exists(':FzfPreviewRemoteEnvironment') let g:fzf_preview_has_remote = v:true silent doautocmd User fzf_preview#initialized silent doautocmd User fzf_preview#remote#initialized endif endfunction function! s:doautocmd_from_coc() abort silent doautocmd User fzf_preview#initialized silent doautocmd User fzf_preview#coc#initialized endfunction function! s:doautocmd_from_rpc() abort call fzf_preview#rpc#initialize() endfunction command! -nargs=? FzfPreviewProjectFilesRpc call fzf_preview#rpc#command('FzfPreviewProjectFiles', ) command! -nargs=? FzfPreviewGitFilesRpc call fzf_preview#rpc#command('FzfPreviewGitFiles', ) command! -nargs=? FzfPreviewDirectoryFilesRpc call fzf_preview#rpc#command('FzfPreviewDirectoryFiles', ) command! -nargs=? FzfPreviewBuffersRpc call fzf_preview#rpc#command('FzfPreviewBuffers', ) command! -nargs=? FzfPreviewAllBuffersRpc call fzf_preview#rpc#command('FzfPreviewAllBuffers', ) command! -nargs=? FzfPreviewProjectOldFilesRpc call fzf_preview#rpc#command('FzfPreviewProjectOldFiles', ) command! -nargs=? FzfPreviewProjectMruFilesRpc call fzf_preview#rpc#command('FzfPreviewProjectMruFiles', ) command! -nargs=? FzfPreviewProjectMrwFilesRpc call fzf_preview#rpc#command('FzfPreviewProjectMrwFiles', ) command! -nargs=? FzfPreviewLinesRpc call fzf_preview#rpc#command('FzfPreviewLines', ) command! -nargs=? FzfPreviewBufferLinesRpc call fzf_preview#rpc#command('FzfPreviewBufferLines', ) command! -nargs=? FzfPreviewCtagsRpc call fzf_preview#rpc#command('FzfPreviewCtags', ) command! -nargs=? FzfPreviewBufferTagsRpc call fzf_preview#rpc#command('FzfPreviewBufferTags', ) command! -nargs=? FzfPreviewOldFilesRpc call fzf_preview#rpc#command('FzfPreviewOldFiles', ) command! -nargs=? FzfPreviewMruFilesRpc call fzf_preview#rpc#command('FzfPreviewMruFiles', ) command! -nargs=? FzfPreviewMrwFilesRpc call fzf_preview#rpc#command('FzfPreviewMrwFiles', ) command! -nargs=? FzfPreviewQuickFixRpc call fzf_preview#rpc#command('FzfPreviewQuickFix', ) command! -nargs=? FzfPreviewLocationListRpc call fzf_preview#rpc#command('FzfPreviewLocationList', ) command! -nargs=? FzfPreviewJumpsRpc call fzf_preview#rpc#command('FzfPreviewJumps', ) command! -nargs=? FzfPreviewChangesRpc call fzf_preview#rpc#command('FzfPreviewChanges', ) command! -nargs=? FzfPreviewMarksRpc call fzf_preview#rpc#command('FzfPreviewMarks', ) command! -nargs=? FzfPreviewProjectGrepRpc call fzf_preview#rpc#command('FzfPreviewProjectGrep', ) command! -nargs=? FzfPreviewProjectGrepRecallRpc call fzf_preview#rpc#command('FzfPreviewProjectGrepRecall', ) command! -nargs=? FzfPreviewFromResourcesRpc call fzf_preview#rpc#command('FzfPreviewFromResources', ) command! -nargs=? FzfPreviewCommandPaletteRpc call fzf_preview#rpc#command('FzfPreviewCommandPalette', ) command! -nargs=1 FzfPreviewGrepHelpRpc call fzf_preview#rpc#command('FzfPreviewGrepHelp', ) command! -nargs=? FzfPreviewGitActionsRpc call fzf_preview#rpc#command('FzfPreviewGitActions', ) command! -nargs=? FzfPreviewGitStatusRpc call fzf_preview#rpc#command('FzfPreviewGitStatus', ) command! -nargs=? FzfPreviewGitStatusActionsRpc call fzf_preview#rpc#command('FzfPreviewGitStatusActions', ) command! -nargs=? FzfPreviewGitBranchesRpc call fzf_preview#rpc#command('FzfPreviewGitBranches', ) command! -nargs=? FzfPreviewGitBranchActionsRpc call fzf_preview#rpc#command('FzfPreviewGitBranchActions', ) command! -nargs=? FzfPreviewGitLogsRpc call fzf_preview#rpc#command('FzfPreviewGitLogs', ) command! -nargs=? FzfPreviewGitCurrentLogsRpc call fzf_preview#rpc#command('FzfPreviewGitCurrentLogs', ) command! -nargs=? FzfPreviewGitLogActionsRpc call fzf_preview#rpc#command('FzfPreviewGitLogActions', ) command! -nargs=? FzfPreviewGitStashesRpc call fzf_preview#rpc#command('FzfPreviewGitStashes', ) command! -nargs=? FzfPreviewGitStashActionsRpc call fzf_preview#rpc#command('FzfPreviewGitStashActions', ) command! -nargs=? FzfPreviewGitReflogsRpc call fzf_preview#rpc#command('FzfPreviewGitReflogs', ) command! -nargs=? FzfPreviewGitReflogActionsRpc call fzf_preview#rpc#command('FzfPreviewGitReflogActions', ) command! -nargs=? FzfPreviewVimLspReferencesRpc call fzf_preview#rpc#command('FzfPreviewVimLspReferences', ) command! -nargs=? FzfPreviewVimLspDiagnosticsRpc call fzf_preview#rpc#command('FzfPreviewVimLspDiagnostics', ) command! -nargs=? FzfPreviewVimLspCurrentDiagnosticsRpc call fzf_preview#rpc#command('FzfPreviewVimLspCurrentDiagnostics', ) command! -nargs=? FzfPreviewVimLspDefinitionRpc call fzf_preview#rpc#command('FzfPreviewVimLspDefinition', ) command! -nargs=? FzfPreviewVimLspTypeDefinitionRpc call fzf_preview#rpc#command('FzfPreviewVimLspTypeDefinition', ) command! -nargs=? FzfPreviewVimLspImplementationRpc call fzf_preview#rpc#command('FzfPreviewVimLspImplementation', ) command! -nargs=? FzfPreviewNvimLspReferencesRpc call fzf_preview#rpc#command('FzfPreviewNvimLspReferences', ) command! -nargs=? FzfPreviewNvimLspDiagnosticsRpc call fzf_preview#rpc#command('FzfPreviewNvimLspDiagnostics', ) command! -nargs=? FzfPreviewNvimLspCurrentDiagnosticsRpc call fzf_preview#rpc#command('FzfPreviewNvimLspCurrentDiagnostics', ) command! -nargs=? FzfPreviewNvimLspDefinitionRpc call fzf_preview#rpc#command('FzfPreviewNvimLspDefinition', ) command! -nargs=? FzfPreviewNvimLspTypeDefinitionRpc call fzf_preview#rpc#command('FzfPreviewNvimLspTypeDefinition', ) command! -nargs=? FzfPreviewNvimLspImplementationRpc call fzf_preview#rpc#command('FzfPreviewNvimLspImplementation', ) command! -nargs=? FzfPreviewBookmarksRpc call fzf_preview#rpc#command('FzfPreviewBookmarks', ) command! -nargs=? FzfPreviewYankroundRpc call fzf_preview#rpc#command('FzfPreviewYankround', ) command! -nargs=? FzfPreviewMemoListRpc call fzf_preview#rpc#command('FzfPreviewMemoList', ) command! -nargs=? FzfPreviewMemoListGrepRpc call fzf_preview#rpc#command('FzfPreviewMemoListGrep', ) command! -nargs=? FzfPreviewTodoCommentsRpc call fzf_preview#rpc#command('FzfPreviewTodoComments', ) command! -nargs=? FzfPreviewVistaCtagsRpc call fzf_preview#rpc#command('FzfPreviewVistaCtags', ) command! -nargs=? FzfPreviewVistaBufferCtagsRpc call fzf_preview#rpc#command('FzfPreviewVistaBufferCtags', ) command! -nargs=? FzfPreviewBlamePRRpc call fzf_preview#rpc#command('FzfPreviewBlamePR', ) let &cpoptions = s:save_cpo unlet s:save_cpo " vim:set expandtab shiftwidth=2 softtabstop=2 tabstop=2 foldenable foldmethod=marker: ================================================ FILE: rplugin/node/fzf-preview.vim/.keep ================================================ ================================================ FILE: scripts/preview.js ================================================ const { execSync } = require("child_process") const path = require("path") const { existsSync } = require("fs") const util = require("util") const glob = util.promisify(require("glob")) const MAX_BUFFER_SIZE = 1024 * 1024 * 1000 const BAT_THEME = process.env.FZF_PREVIEW_PREVIEW_BAT_THEME || "OneHalfDark" const VIM_RUNTIME_DIR = process.env.VIMRUNTIME || "" const HELP_ROOT_DIR = process.env.FZF_PREVIEW_PLUGIN_HELP_ROOT_DIR || "" const GREP_OUTPUT_REGEX = /^(?.\s\s)?(?[^:]+):(?\d+)(:(?\d+))?(:(?.*))?/ const expandHome = (filePath) => { if (filePath.startsWith("~")) { return path.join(process.env.HOME, filePath.slice(1)) } return filePath } const isInstalled = (command) => { try { execSync(`which ${command.split(" ")[0]}`, { encoding: "utf-8" }) return true } catch (_) { return false } } const main = async () => { const match = GREP_OUTPUT_REGEX.exec(process.argv[2]) if (match == null) { console.log( "Cannot process the entry :(\n", "Please open an issue and describe what happened\n", "including information such as file name and g:fzf_preview_grep_cmd" ) process.exit(1) } let fileName = match.groups["fileName"] const lineNum = Number(match.groups["lineNum"]) if (!existsSync(fileName)) { let helpFileList = [] if (VIM_RUNTIME_DIR != "") { helpFileList = await glob(`${VIM_RUNTIME_DIR}/doc/*`) } if (HELP_ROOT_DIR != "") { helpFileList = [...helpFileList, ...(await glob(`${expandHome(HELP_ROOT_DIR)}/**/doc/*`))] } const matchHelpFile = helpFileList.find((file) => file.split("/").slice(-1)[0] === fileName) if (matchHelpFile == null) { process.exit(1) } else { fileName = matchHelpFile } } const cats = [`bat --highlight-line="${lineNum}" --color=always --theme="${BAT_THEME}" --plain --number`, "cat"] const cat = cats.find((cat) => isInstalled(cat)) const result = execSync(`${cat} ${fileName}`, { encoding: "utf-8", maxBuffer: MAX_BUFFER_SIZE }) if (cat === "cat") { for (const [index, line] of result.split("\n").entries()) { if (lineNum - 1 === index) { console.log(`\x1b[1m\x1b[4m\x1b[31m${line}\x1b[0m`) } else { console.log(line) } } } else { console.log(result) } } main() ================================================ FILE: src/@types/index.d.ts ================================================ declare namespace NodeJS { interface ProcessEnv { readonly VIMRUNTIME: string readonly FZF_PREVIEW_PLUGIN_HELP_ROOT_DIR?: string } } declare const PLUGIN: { ENV: "remote" | "coc" | "rpc" } ================================================ FILE: src/args/add-fzf-arg-parser.test.ts ================================================ import { parseAddFzfArg } from "@/args/add-fzf-arg-parser" describe("parseAddFzfArgs", () => { it('parseAddFzfArgs("--add-fzf-arg=-a")', () => { expect(parseAddFzfArg("--add-fzf-arg=-a")).toEqual([ { optionName: "-a", }, ]) }) it('parseAddFzfArgs("--add-fzf-arg=--foo")', () => { expect(parseAddFzfArg("--add-fzf-arg=--foo")).toEqual([ { optionName: "--foo", }, ]) }) it('parseAddFzfArgs("--add-fzf-arg=--foo --add-fzf-arg=--bar")', () => { expect(parseAddFzfArg("--add-fzf-arg=--foo --add-fzf-arg=--bar")).toEqual([ { optionName: "--foo", }, { optionName: "--bar", }, ]) }) it('parseAddFzfArgs("--add-fzf-arg=--foo=bar")', () => { expect(parseAddFzfArg("--add-fzf-arg=--foo=bar")).toEqual([ { optionName: "--foo", value: "bar", }, ]) }) it('parseAddFzfArgs("--add-fzf-arg=--foo --add-fzf-arg=--foobar="hoge fuga"")', () => { expect(parseAddFzfArg('--add-fzf-arg=--foo --add-fzf-arg=--foobar="hoge fuga"')).toEqual([ { optionName: "--foo", }, { optionName: "--foobar", value: '"hoge fuga"', }, ]) }) }) ================================================ FILE: src/args/add-fzf-arg-parser.ts ================================================ import { argsParser } from "@/args/parser" import type { AddFzfArg, ArgsOptions } from "@/type" const optionsToAddFzfArg = (options: ArgsOptions): ReadonlyArray => { if (options["add-fzf-arg"] != null && Array.isArray(options["add-fzf-arg"])) { return options["add-fzf-arg"] as ReadonlyArray } if (options["add-fzf-arg"] != null && typeof options["add-fzf-arg"] === "string") { return [options["add-fzf-arg"]] } return [] } const parseOptions = (options: ArgsOptions) => { const addFzfArg = optionsToAddFzfArg(options) const notExistsValueOptions: ReadonlyArray = addFzfArg .map((arg) => /(.+)$/.exec(arg)) .filter((match): match is RegExpExecArray => match != null && !match[0].includes("=")) .map((match) => { return { optionName: match[1] } }) const existsValueOptions: ReadonlyArray = addFzfArg .map((arg) => /(.+)=(.+)$/.exec(arg)) .filter((match): match is RegExpExecArray => match != null) .map((match) => ({ optionName: match[1], value: match[2] })) return [...notExistsValueOptions, ...existsValueOptions] } export const parseAddFzfArg = (args: string): ReadonlyArray => { const parser = argsParser() const options = parser.parseSync(args) return parseOptions(options) } ================================================ FILE: src/args/directory-files-args-parser.ts ================================================ import { argsParser } from "@/args/parser" import type { SourceFuncArgs } from "@/type" export const parseDictionaryFilesArgs = (args: string): SourceFuncArgs => { const parser = argsParser() const options = parser.parseSync(args) const directories = options._.map((resource) => (typeof resource === "number" ? resource.toString() : resource)) return { args: directories.length > 0 ? [directories[0]] : [], } } ================================================ FILE: src/args/empty-source-func-args-parser.ts ================================================ import type { SourceFuncArgs } from "@/type" export const parseEmptySourceFuncArgs = (_args: string): SourceFuncArgs => { return { args: [], } } ================================================ FILE: src/args/experimental-parser.ts ================================================ import { argsParser } from "@/args/parser" type Experimental = { fast: boolean } export const parseExperimental = (args: string): Experimental => { const parser = argsParser() const options = parser.parseSync(args) return { fast: options["experimental-fast"] != null, } } ================================================ FILE: src/args/files-from-resources-parser.ts ================================================ import { argsParser } from "@/args/parser" import { FILE_RESOURCES } from "@/const/fzf-option" import type { SourceFuncArgs } from "@/type" export const parseResources = (args: string): SourceFuncArgs => { const parser = argsParser() const options = parser.parseSync(args) const resources = options._.map((resource) => (typeof resource === "number" ? resource.toString() : resource)) if (resources.length === 0) { throw new Error("Select one or more resources") } if (!resources.every((resource) => (FILE_RESOURCES as ReadonlyArray).includes(resource))) { throw new Error(`Invalid resource: "${resources.join(", ")}"`) } return { args: resources, } } ================================================ FILE: src/args/grep-args-parser.ts ================================================ import { argsParser } from "@/args/parser" import type { SourceFuncArgs } from "@/type" export const parseGrepArgs = (args: string): SourceFuncArgs => { const parser = argsParser() const options = parser.parseSync(args) const grepArgs = options._.map((resource) => (typeof resource === "number" ? resource.toString() : resource)) return { args: grepArgs, } } ================================================ FILE: src/args/index.ts ================================================ export * from "@/args/add-fzf-arg-parser" export * from "@/args/directory-files-args-parser" export * from "@/args/empty-source-func-args-parser" export * from "@/args/grep-args-parser" export * from "@/args/processes-parser" export * from "@/args/resume-parser" ================================================ FILE: src/args/parser.ts ================================================ import yargs from "yargs" export const argsParser = () => { return yargs .options("add-fzf-arg", { type: "string" }) .options("processes", { type: "string" }) .options("resume", { type: "boolean" }) .options("session", { type: "string" }) .options("experimental-fast", { type: "boolean" }) .parserConfiguration({ "camel-case-expansion": false, "unknown-options-as-args": true, }) } ================================================ FILE: src/args/processes-parser.test.ts ================================================ import { parseProcesses } from "@/args/processes-parser" describe("parseProcesses", () => { it('parseProcesses("foo")', () => { expect(parseProcesses("open-file", "foo")).toBeUndefined() }) it('parseProcesses("--foo --processes=foo_processes")', () => { expect(parseProcesses("open-file", "--processes=foo_processes")).toEqual({ type: "global_variable", value: "foo_processes", }) }) it('parseProcesses("--processes=foo_processes --processes=bar_processes")', () => { expect(() => { parseProcesses("open-file", "--processes=foo_processes --processes=bar_processes") }).toThrow() }) }) ================================================ FILE: src/args/processes-parser.ts ================================================ import { isObject } from "lodash" import { argsParser } from "@/args/parser" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { ArgsOptions, CustomProcessesVimVariable, ProcessesName, UserProcesses } from "@/type" const parseOptions = (options: ArgsOptions): UserProcesses | null => { const processesArgs = options.processes if (processesArgs == null) { return null } if (typeof processesArgs === "string") { return { type: "global_variable", value: processesArgs } } throw new Error("--processes option can only be used once") } export const parseProcesses = (defaultProcessesName: ProcessesName, args: string): UserProcesses | undefined => { const parser = argsParser() const options = parser.parseSync(args) const parsedOptions = parseOptions(options) if (parsedOptions != null) { return parsedOptions } const customProcessesDictionary = globalVariableSelector("fzfPreviewCustomProcesses") if ( isObject(customProcessesDictionary) && isObject((customProcessesDictionary as CustomProcessesVimVariable)[defaultProcessesName]) ) { return { type: "custom_processes_variable", value: defaultProcessesName } } return undefined } ================================================ FILE: src/args/resume-parser.ts ================================================ import { argsParser } from "@/args/parser" import { resumeSelector } from "@/module/selector/resume" import type { FzfCommandName, ResumeQuery } from "@/type" export const parseResume = (commandName: FzfCommandName, args: string): ResumeQuery => { const parser = argsParser() const options = parser.parseSync(args) if (options.resume == null) { return null } const resumeQuery = resumeSelector(commandName) return resumeQuery != null ? resumeQuery : null } ================================================ FILE: src/args/session-parser.ts ================================================ import { argsParser } from "@/args/parser" import { sessionSelector } from "@/module/selector/session" import type { Session } from "@/type" export const parseSession = (args: string): Session | null => { const parser = argsParser() const options = parser.parseSync(args) if (options.session == null) { return null } const sessionToken = options.session const currentSession = sessionSelector(sessionToken) if (currentSession == null) { throw new Error(`Invalid session token: ${sessionToken}`) } return currentSession } ================================================ FILE: src/association/coc-command.ts ================================================ import { parseEmptySourceFuncArgs } from "@/args" import { commandDefinition, vimCommandOptions } from "@/association/command" import { cocCurrentDiagnostics, cocCurrentDiagnosticsDefaultOptions, cocDiagnostics, cocDiagnosticsDefaultOptions, cocImplementations, cocImplementationsDefaultOptions, cocReferences, cocReferencesDefaultOptions, cocTypeDefinitions, cocTypeDefinitionsDefaultOptions, } from "@/fzf/resource/coc" import { cocDefinitions, cocDefinitionsDefaultOptions } from "@/fzf/resource/coc/coc-definitions" import { cocOutline, cocOutlineDefaultOptions } from "@/fzf/resource/coc/coc-outline" import { cocTsServerSourceDefinition, cocTsServerSourceDefinitionsDefaultOptions, } from "@/fzf/resource/coc/coc-tsserver-source-definition" import type { FzfCommand } from "@/type" export const cocCommandDefinition: ReadonlyArray = [ ...commandDefinition, { commandName: "FzfPreviewCocReferences", sourceFunc: cocReferences, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: cocReferencesDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewCocDiagnostics", sourceFunc: cocDiagnostics, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: cocDiagnosticsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewCocCurrentDiagnostics", sourceFunc: cocCurrentDiagnostics, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: cocCurrentDiagnosticsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewCocDefinition", sourceFunc: cocDefinitions, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: cocDefinitionsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewCocTypeDefinition", sourceFunc: cocTypeDefinitions, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: cocTypeDefinitionsDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewCocImplementations", sourceFunc: cocImplementations, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: cocImplementationsDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewCocOutline", sourceFunc: cocOutline, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: cocOutlineDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewCocTsServerSourceDefinition", sourceFunc: cocTsServerSourceDefinition, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: cocTsServerSourceDefinitionsDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, ] ================================================ FILE: src/association/command.ts ================================================ import { parseDictionaryFilesArgs, parseEmptySourceFuncArgs, parseGrepArgs } from "@/args" import { parseResources } from "@/args/files-from-resources-parser" import { allBuffers, allBuffersDefaultOptions, blamePr, blamePrDefaultOptions, bookmarks, bookmarksDefaultOptions, bufferLines, bufferLinesDefaultOptions, buffers, buffersDefaultOptions, bufferTags, bufferTagsDefaultOptions, changes, changesDefaultOptions, commandPalette, commandPaletteDefaultOptions, ctags, ctagsDefaultOptions, directoryFiles, directoryFilesDefaultOptions, filesFromResources, filesFromResourcesDefaultOptions, gitActions, gitActionsDefaultOptions, gitBranchActions, gitBranchActionsDefaultOptions, gitBranches, gitBranchesDefaultOptions, gitCurrentLogs, gitCurrentLogsDefaultOptions, gitFiles, gitFilesDefaultOptions, gitLogActions, gitLogActionsDefaultOptions, gitLogs, gitLogsDefaultOptions, gitReflogActions, gitReflogActionsDefaultOptions, gitReflogs, gitReflogsDefaultOptions, gitStashActions, gitStashActionsDefaultOptions, gitStashes, gitStashesDefaultOptions, gitStatus, gitStatusActions, gitStatusActionsDefaultOptions, gitStatusDefaultOptions, jumps, jumpsDefaultOptions, lines, linesDefaultOptions, locationList, locationListDefaultOptions, marks, marksDefaultOptions, memoList, memoListDefaultOptions, memoListGrep, memoListGrepDefaultOptions, mruFiles, mruFilesDefaultOptions, mrwFiles, mrwFilesDefaultOptions, oldFiles, oldFilesDefaultOptions, projectFiles, projectFilesDefaultOptions, projectGrep, projectGrepDefaultOptions, projectMruFiles, projectMruFilesDefaultOptions, projectMrwFiles, projectMrwFilesDefaultOptions, projectOldFiles, projectOldFilesDefaultOptions, quickFix, quickFixDefaultOptions, vistaBufferCtags, vistaBufferCtagsDefaultOptions, vistaCtags, vistaCtagsDefaultOptions, yankround, yankroundDefaultOptions, } from "@/fzf/resource" import { nvimLspCurrentDiagnostics, nvimLspCurrentDiagnosticsDefaultOptions, } from "@/fzf/resource/nvim-lsp-current-diagnostics" import { nvimLspDefinition, nvimLspDefinitionDefaultOptions } from "@/fzf/resource/nvim-lsp-definition" import { nvimLspDiagnostics, nvimLspDiagnosticsDefaultOptions } from "@/fzf/resource/nvim-lsp-diagnostics" import { nvimLspImplementation, nvimLspImplementationDefaultOptions } from "@/fzf/resource/nvim-lsp-implementation" import { nvimLspReferences, nvimLspReferencesDefaultOptions } from "@/fzf/resource/nvim-lsp-references" import { nvimLspTypeDefinition, nvimLspTypeDefinitionDefaultOptions } from "@/fzf/resource/nvim-lsp-type-definition" import { todoComments, todoCommentsDefaultOptions } from "@/fzf/resource/todo-comments" import { grepHelp, grepHelpDefaultOptions } from "@/fzf/resource/vim-help" import { vimLspCurrentDiagnostics, vimLspCurrentDiagnosticsDefaultOptions, } from "@/fzf/resource/vim-lsp-current-diagnostics" import { vimLspDefinition, vimLspDefinitionDefaultOptions } from "@/fzf/resource/vim-lsp-definition" import { vimLspDiagnostics, vimLspDiagnosticsDefaultOptions } from "@/fzf/resource/vim-lsp-diagnostics" import { vimLspImplementation, vimLspImplementationDefaultOptions } from "@/fzf/resource/vim-lsp-implementation" import { vimLspReferences, vimLspReferencesDefaultOptions } from "@/fzf/resource/vim-lsp-references" import { vimLspTypeDefinition, vimLspTypeDefinitionDefaultOptions } from "@/fzf/resource/vim-lsp-type-definition" import type { BaseFzfCommand } from "@/type" export const vimCommandOptions = { nargs: "?", sync: true, } as const export const commandDefinition: ReadonlyArray = [ { commandName: "FzfPreviewProjectFiles", sourceFunc: projectFiles, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: projectFilesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, addGitStatus: true, }, { commandName: "FzfPreviewGitFiles", sourceFunc: gitFiles, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitFilesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewDirectoryFiles", sourceFunc: directoryFiles, sourceFuncArgsParser: parseDictionaryFilesArgs, vimCommandOptions, defaultFzfOptionFunc: directoryFilesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewBuffers", sourceFunc: buffers, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: buffersDefaultOptions, defaultProcessesName: "open-buffer", enableConvertForFzf: true, enableDevIcons: false, }, { commandName: "FzfPreviewAllBuffers", sourceFunc: allBuffers, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: allBuffersDefaultOptions, defaultProcessesName: "open-bufnr", enableConvertForFzf: true, enableDevIcons: false, }, { commandName: "FzfPreviewProjectOldFiles", sourceFunc: projectOldFiles, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: projectOldFilesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, addGitStatus: true, }, { commandName: "FzfPreviewProjectMruFiles", sourceFunc: projectMruFiles, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: projectMruFilesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, addGitStatus: true, }, { commandName: "FzfPreviewProjectMrwFiles", sourceFunc: projectMrwFiles, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: projectMrwFilesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, addGitStatus: true, }, { commandName: "FzfPreviewLines", sourceFunc: lines, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: linesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewBufferLines", sourceFunc: bufferLines, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: bufferLinesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewCtags", sourceFunc: ctags, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: ctagsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewBufferTags", sourceFunc: bufferTags, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: bufferTagsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewOldFiles", sourceFunc: oldFiles, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: oldFilesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewMruFiles", sourceFunc: mruFiles, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: mruFilesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewMrwFiles", sourceFunc: mrwFiles, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: mrwFilesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewQuickFix", sourceFunc: quickFix, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: quickFixDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewLocationList", sourceFunc: locationList, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: locationListDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewJumps", sourceFunc: jumps, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: jumpsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewChanges", sourceFunc: changes, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: changesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewMarks", sourceFunc: marks, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: marksDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewProjectGrep", sourceFunc: projectGrep, sourceFuncArgsParser: parseGrepArgs, vimCommandOptions, defaultFzfOptionFunc: projectGrepDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewProjectGrepRecall", sourceFunc: projectGrep, sourceFuncArgsParser: parseGrepArgs, vimCommandOptions, defaultFzfOptionFunc: projectGrepDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewFromResources", sourceFunc: filesFromResources, sourceFuncArgsParser: parseResources, vimCommandOptions, defaultFzfOptionFunc: filesFromResourcesDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, addGitStatus: true, }, { commandName: "FzfPreviewCommandPalette", sourceFunc: commandPalette, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: commandPaletteDefaultOptions, defaultProcessesName: "command-palette", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGrepHelp", sourceFunc: grepHelp, sourceFuncArgsParser: parseGrepArgs, vimCommandOptions, defaultFzfOptionFunc: grepHelpDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: false, }, { commandName: "FzfPreviewGitActions", sourceFunc: gitActions, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitActionsDefaultOptions, defaultProcessesName: "git-action", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitStatus", sourceFunc: gitStatus, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitStatusDefaultOptions, defaultProcessesName: "git-status", enableConvertForFzf: true, enableDevIcons: false, }, { commandName: "FzfPreviewGitStatusActions", sourceFunc: gitStatusActions, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitStatusActionsDefaultOptions, defaultProcessesName: "git-status-actions", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitBranches", sourceFunc: gitBranches, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitBranchesDefaultOptions, defaultProcessesName: "git-branch", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitBranchActions", sourceFunc: gitBranchActions, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitBranchActionsDefaultOptions, defaultProcessesName: "git-branch-actions", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitLogs", sourceFunc: gitLogs, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitLogsDefaultOptions, defaultProcessesName: "git-log", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitCurrentLogs", sourceFunc: gitCurrentLogs, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitCurrentLogsDefaultOptions, defaultProcessesName: "git-log", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitLogActions", sourceFunc: gitLogActions, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitLogActionsDefaultOptions, defaultProcessesName: "git-log-actions", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitStashes", sourceFunc: gitStashes, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitStashesDefaultOptions, defaultProcessesName: "git-stash", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitStashActions", sourceFunc: gitStashActions, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitStashActionsDefaultOptions, defaultProcessesName: "git-stash-actions", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitReflogs", sourceFunc: gitReflogs, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitReflogsDefaultOptions, defaultProcessesName: "git-reflog", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewGitReflogActions", sourceFunc: gitReflogActions, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: gitReflogActionsDefaultOptions, defaultProcessesName: "git-reflog-actions", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewVimLspReferences", sourceFunc: vimLspReferences, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: vimLspReferencesDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewVimLspDiagnostics", sourceFunc: vimLspDiagnostics, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: vimLspDiagnosticsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewVimLspCurrentDiagnostics", sourceFunc: vimLspCurrentDiagnostics, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: vimLspCurrentDiagnosticsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewVimLspDefinition", sourceFunc: vimLspDefinition, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: vimLspDefinitionDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewVimLspTypeDefinition", sourceFunc: vimLspTypeDefinition, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: vimLspTypeDefinitionDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewVimLspImplementation", sourceFunc: vimLspImplementation, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: vimLspImplementationDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewNvimLspReferences", sourceFunc: nvimLspReferences, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: nvimLspReferencesDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewNvimLspDiagnostics", sourceFunc: nvimLspDiagnostics, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: nvimLspDiagnosticsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewNvimLspCurrentDiagnostics", sourceFunc: nvimLspCurrentDiagnostics, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: nvimLspCurrentDiagnosticsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewNvimLspDefinition", sourceFunc: nvimLspDefinition, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: nvimLspDefinitionDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewNvimLspTypeDefinition", sourceFunc: nvimLspTypeDefinition, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: nvimLspTypeDefinitionDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewNvimLspImplementation", sourceFunc: nvimLspImplementation, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: nvimLspImplementationDefaultOptions, defaultProcessesName: "open-file-with-tag-stack", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewBookmarks", sourceFunc: bookmarks, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: bookmarksDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewYankround", sourceFunc: yankround, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: yankroundDefaultOptions, defaultProcessesName: "register", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewMemoList", sourceFunc: memoList, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: memoListDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewMemoListGrep", sourceFunc: memoListGrep, sourceFuncArgsParser: parseGrepArgs, vimCommandOptions, defaultFzfOptionFunc: memoListGrepDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewTodoComments", sourceFunc: todoComments, sourceFuncArgsParser: parseGrepArgs, vimCommandOptions, defaultFzfOptionFunc: todoCommentsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: true, enableDevIcons: true, }, { commandName: "FzfPreviewVistaCtags", sourceFunc: vistaCtags, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: vistaCtagsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewVistaBufferCtags", sourceFunc: vistaBufferCtags, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: vistaBufferCtagsDefaultOptions, defaultProcessesName: "open-file", enableConvertForFzf: false, enableDevIcons: false, }, { commandName: "FzfPreviewBlamePR", sourceFunc: blamePr, sourceFuncArgsParser: parseEmptySourceFuncArgs, vimCommandOptions, defaultFzfOptionFunc: blamePrDefaultOptions, defaultProcessesName: "open-pr", enableConvertForFzf: false, enableDevIcons: false, }, ] as const ================================================ FILE: src/association/vim-variable.ts ================================================ export const vimVariableAssociation = { fzfPreviewDefaultFzfOptions: "fzf_preview_default_fzf_options", fzfPreviewUseDevIcons: "fzf_preview_use_dev_icons", fzfPreviewDevIconPrefixStringLength: "fzf_preview_dev_icon_prefix_string_length", fzfPreviewDevIconsLimit: "fzf_preview_dev_icons_limit", webDevIconsUnicodeDecorateFileNodesDefaultSymbol: "WebDevIconsUnicodeDecorateFileNodesDefaultSymbol", webDevIconsUnicodeDecorateFileNodesExtensionSymbols: "WebDevIconsUnicodeDecorateFileNodesExtensionSymbols", webDevIconsUnicodeDecorateFileNodesExactSymbols: "WebDevIconsUnicodeDecorateFileNodesExactSymbols", webDevIconsUnicodeDecorateFileNodesPatternSymbols: "WebDevIconsUnicodeDecorateFileNodesPatternSymbols", fzfPreviewCommand: "fzf_preview_command", fzfBinaryPreviewCommand: "fzf_binary_preview_command", fzfPreviewIfBinaryCommand: "fzf_preview_if_binary_command", fzfPreviewFilelistCommand: "fzf_preview_filelist_command", fzfPreviewGitFilesCommand: "fzf_preview_git_files_command", fzfPreviewDirectoryFilesCommand: "fzf_preview_directory_files_command", fzfPreviewGitStatusCommand: "fzf_preview_git_status_command", fzfPreviewGitStatusPreviewCommand: "fzf_preview_git_status_preview_command", fzfPreviewGrepCmd: "fzf_preview_grep_cmd", fzfPreviewScriptDir: "fzf_preview_script_dir", fzfPreviewCacheDirectory: "fzf_preview_cache_directory", fzfPreviewLinesCommand: "fzf_preview_lines_command", fzfPreviewGrepPreviewCmd: "fzf_preview_grep_preview_cmd", fzfPreviewCustomProcesses: "fzf_preview_custom_processes", fzfPreviewFzfPreviewWindowOption: "fzf_preview_fzf_preview_window_option", fzfPreviewPreviewKeyBindings: "fzf_preview_preview_key_bindings", fzfPreviewFzfColorOption: "fzf_preview_fzf_color_option", fzfPreviewHistoryDir: "fzf_preview_history_dir", fzfPreviewBuffersJump: "fzf_preview_buffers_jump", yankroundDir: "yankround_dir", fzfPreviewYankroundPreviewCommand: "fzf_preview_yankround_preview_command", fzfPreviewBlamePrCommand: "fzf_preview_blame_pr_command", } as const export const vimOptions = ["columns"] as const ================================================ FILE: src/coc.ts ================================================ import type { ExtensionContext } from "coc.nvim" import { commands } from "coc.nvim" import { initializeExtension, registerCommands, registerFunctions, registerProcesses, setRuntimePath, } from "@/register/coc" export async function activate(context: ExtensionContext): Promise { await initializeExtension() await setRuntimePath(context) context.subscriptions.push( ...registerCommands(commands), ...registerProcesses(commands), ...registerFunctions(commands) ) } ================================================ FILE: src/connector/bookmarks.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { pluginCall } from "@/plugin" type Bookmark = ReadonlyDeep<{ file: string line: string text: string comment: string }> export const getBookmarks = async (): Promise> => (await pluginCall("fzf_preview#remote#resource#bookmarks#get")) as ReadonlyArray ================================================ FILE: src/connector/buffers.ts ================================================ import { pluginCall, pluginCommand } from "@/plugin" import type { VimBuffer } from "@/type" export const getBuffers = async (): Promise> => { const buffers = (await pluginCall("fzf_preview#remote#resource#buffers#get")) as ReadonlyArray return buffers } export const getCurrentBuffer = async (): Promise => { const buffer = (await pluginCall("fzf_preview#remote#resource#buffers#get_current_buffer")) as VimBuffer return buffer } export const getAlternateBuffer = async (): Promise => { const buffer = (await pluginCall("fzf_preview#remote#resource#buffers#get_alternate_buffer")) as VimBuffer return buffer } export const getOtherBuffers = async (): Promise> => { const buffers = (await pluginCall( "fzf_preview#remote#resource#buffers#get_other_buffers" )) as ReadonlyArray return buffers } export const getAllBuffers = async (): Promise> => { const buffers = (await pluginCall("fzf_preview#remote#resource#all_buffers#get")) as ReadonlyArray return buffers } export const deleteBuffer = async (bufnr: string): Promise => { await pluginCommand(`bdelete! ${bufnr}`) } ================================================ FILE: src/connector/changes.ts ================================================ import { pluginCall } from "@/plugin" export const getChanges = async (): Promise> => (await pluginCall("fzf_preview#remote#resource#changes#get")) as ReadonlyArray ================================================ FILE: src/connector/coc.ts ================================================ import type { CancellationToken, DefinitionProvider, DiagnosticItem, ImplementationProvider, Position, Range, ReferenceProvider, TypeDefinitionProvider, } from "coc.nvim" import { CancellationTokenSource, languages, services, workspace } from "coc.nvim" import type { DefinitionLink as CocDefinitionLink, Location as CocLocation, LocationLink as CocLocationLink, } from "vscode-languageserver-types" import { diagnosticItemToData, lspLocationToLocation } from "@/connector/lsp" import { pluginCall } from "@/plugin" import { getCurrentFilePath, getCurrentPath, readFileLine } from "@/system/file" import { dropFileProtocol, filePathToRelativeFilePath } from "@/system/project" import type { Diagnostic, Location } from "@/type" import { uniqWith } from "@/util/uniq-with" type ReferenceProviders = ReadonlyArray<{ provider: ReferenceProvider }> type DefinitionProviders = ReadonlyArray<{ provider: DefinitionProvider }> type TypeDefinitionProviders = ReadonlyArray<{ provider: TypeDefinitionProvider }> type ImplementationProviders = ReadonlyArray<{ provider: ImplementationProvider }> export const getDiagnostics = async (): Promise> => { const diagnosticItems = (await pluginCall("CocAction", ["diagnosticList"])) as ReadonlyArray const diagnostics = await Promise.all(diagnosticItems.map(async (item) => await diagnosticItemToData(item))) return diagnostics.filter((diagnostic): diagnostic is Diagnostic => diagnostic != null) } export const getCurrentDiagnostics = async (): Promise> => { const currentFile = await getCurrentFilePath() const diagnosticItems = (await pluginCall("CocAction", ["diagnosticList"])) as ReadonlyArray const diagnostics = await Promise.all( diagnosticItems.map(async (item) => await diagnosticItemToData(item, { currentFile })) ) return diagnostics.filter((diagnostic): diagnostic is Diagnostic => diagnostic != null) } const getCurrentState = async () => { const { document, position } = await workspace.getCurrentState() const range = (await workspace.document).getWordRangeAtPosition(position) const symbol = range != null ? document.getText(range) : "" return { document, position, symbol } as const } export const getReferences = async (): Promise<{ references: ReadonlyArray symbol: string }> => { let locations: ReadonlyArray = [] const { document, position, symbol } = await getCurrentState() const tokenSource = new CancellationTokenSource() // @ts-expect-error // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access const providers: ReferenceProviders = Array.from(languages.referenceManager.providers) for (const { provider } of providers) { // eslint-disable-next-line no-await-in-loop const references = await provider.provideReferences( document, position, { includeDeclaration: false }, tokenSource.token ) if (references != null) { locations = [...locations, ...references] } } const references = uniqWith( (await lspLocationToLocation(locations.map((v) => ({ ...v, kind: "location" })))) as Array, (a, b) => a.file === b.file && a.lineNumber === b.lineNumber && a.text === b.text ) return { references, symbol, } as const } export const getDefinition = async (): Promise<{ definitions: ReadonlyArray; symbol: string }> => { let locations: ReadonlyArray = [] const { document, position, symbol } = await getCurrentState() const tokenSource = new CancellationTokenSource() // @ts-expect-error // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument const providers: DefinitionProviders = Array.from(languages.definitionManager.providers) for (const { provider } of providers) { /* eslint-disable no-await-in-loop */ const definitions = ( (await provider.provideDefinition(document, position, tokenSource.token)) as unknown as | ReadonlyArray | undefined )?.map((definitionLink) => ({ range: definitionLink.targetRange, uri: definitionLink.targetUri, })) /* eslint-enable */ if (definitions != null) { locations = [...locations, ...definitions] } } const definitions = uniqWith( (await lspLocationToLocation(locations.map((v) => ({ ...v, kind: "location" })))) as Array, (a, b) => a.file === b.file && a.lineNumber === b.lineNumber && a.text === b.text ) return { definitions, symbol, } as const } export const getTypeDefinition = async (): Promise<{ typeDefinitions: ReadonlyArray; symbol: string }> => { let locations: ReadonlyArray = [] const { document, position, symbol } = await getCurrentState() const tokenSource = new CancellationTokenSource() // @ts-expect-error // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument const providers: TypeDefinitionProviders = Array.from(languages.typeDefinitionManager.providers) for (const { provider } of providers) { // eslint-disable-next-line no-await-in-loop const typeDefinitions = (await provider.provideTypeDefinition( document, position, tokenSource.token )) as unknown as ReadonlyArray if (typeDefinitions != null) { locations = [...locations, ...typeDefinitions] } } const typeDefinitions = uniqWith( (await lspLocationToLocation(locations.map((v) => ({ ...v, kind: "locationLink" })))) as Array, (a, b) => a.file === b.file && a.lineNumber === b.lineNumber && a.text === b.text ) return { typeDefinitions, symbol, } as const } export const getImplementation = async (): Promise<{ implementations: ReadonlyArray symbol: string }> => { let locations: ReadonlyArray = [] const { document, position, symbol } = await getCurrentState() const tokenSource = new CancellationTokenSource() // @ts-expect-error // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument const providers: ImplementationProviders = Array.from(languages.implementationManager.providers) for (const { provider } of providers) { // eslint-disable-next-line no-await-in-loop const implementations = (await provider.provideImplementation( document, position, tokenSource.token )) as unknown as ReadonlyArray if (implementations != null) { locations = [...locations, ...implementations] } } const implementations = uniqWith( (await lspLocationToLocation(locations.map((v) => ({ ...v, kind: "locationLink" })))) as Array, (a, b) => a.file === b.file && a.lineNumber === b.lineNumber && a.text === b.text ) return { implementations, symbol, } as const } type CocOutlineItem = { data?: { kind: string } filterText: string label: string location: { range: Range uri: string } } type OutlineItem = { kind: string | undefined text: string label: string lineNumber: number file: string } const outlineItemToData = async ({ data, filterText, label, location: { uri, range: { start: { line }, }, }, }: CocOutlineItem): Promise => { const currentPath = await getCurrentPath() const file = filePathToRelativeFilePath(decodeURIComponent(dropFileProtocol(uri)), currentPath) if (file == null) { return null } return { kind: data?.kind, file, text: filterText, label, lineNumber: line + 1, } } export const getOutline = async (): Promise> => { const cocOutlineItems = (await pluginCall("CocAction", ["listLoadItems", "outline"])) as ReadonlyArray const data = Promise.all( cocOutlineItems .map(async (cocItem) => await outlineItemToData(cocItem)) .filter(async (item) => (await item) != null) ) as Promise> return data } type TsServerSourceDefinitionServiceClient = { toOpenedFilePath: (uri: string) => string execute: ( command: "findSourceDefinition", args: { file: string; line: number; offset: number }, token: CancellationToken ) => Promise<{ type: "response" success: true body: Array<{ file: string start: Position end: Position }> }> } export const getTsServerSourceDefinition = async (): Promise<{ sourceDefinitions: ReadonlyArray symbol: string }> => { const { document, position, symbol } = await getCurrentState() const currentPath = await getCurrentPath() // @ts-expect-error // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const serviceClient = services.getService("tsserver")?.clientHost ?.serviceClient as TsServerSourceDefinitionServiceClient if (serviceClient == null) { return { sourceDefinitions: [], symbol } } const file = serviceClient.toOpenedFilePath(document.uri) const tokenSource = new CancellationTokenSource() const args = { file, line: position.line + 1, offset: position.character + 1, } const response = await serviceClient.execute("findSourceDefinition", args, tokenSource.token) return { symbol, sourceDefinitions: response.body.map((v) => { const relativeFilePath = filePathToRelativeFilePath(v.file, currentPath) if (relativeFilePath == null) { console.error(v) throw new Error("Source definition response error.") } return { file: relativeFilePath, lineNumber: v.start.line, text: readFileLine(v.file, v.start.line), } }), } } ================================================ FILE: src/connector/convert-for-fzf.ts ================================================ import stripAnsi from "strip-ansi" import type { ReadonlyDeep } from "type-fest" import { execGitStatus } from "@/connector/git" import { USE_DEV_ICONS_PATTERN_LIMIT } from "@/const/fzf-resource" import { colorize, colorizeDevIcon } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FileData, ResourceLines } from "@/type" type Options = ReadonlyDeep<{ enableConvertForFzf: boolean enableDevIcons: boolean addGitStatus: boolean | undefined }> const createDevIconsList = (files: ReadonlyArray): ReadonlyArray => { const defaultIcon = globalVariableSelector("webDevIconsUnicodeDecorateFileNodesDefaultSymbol") as string const extensionIcons = globalVariableSelector("webDevIconsUnicodeDecorateFileNodesExtensionSymbols") as { [key: string]: string | undefined } const exactIcons = globalVariableSelector("webDevIconsUnicodeDecorateFileNodesExactSymbols") as { [key: string]: string | undefined } const patternIcons = globalVariableSelector("webDevIconsUnicodeDecorateFileNodesPatternSymbols") as { [key: string]: string | undefined } return files.map((file) => { if (USE_DEV_ICONS_PATTERN_LIMIT > files.length) { for (const [regexp, icon] of Object.entries(patternIcons)) { if (icon == null) { throw new Error("Unexpected pattern icon") } if (new RegExp(regexp).exec(file)) { return icon } } } const exactFile = exactIcons[file.toLowerCase()] if (exactFile != null) { return exactFile } const extension = file.split(".").slice(-1)[0] const extensionIcon = extensionIcons[extension] return extensionIcon != null ? extensionIcon : defaultIcon }) } export const convertForFzf = async (lines: ResourceLines, options: Options): Promise => { const { enableConvertForFzf, enableDevIcons, addGitStatus } = options if (!enableConvertForFzf) { return lines } let convertedLines: ResourceLines = [] if (enableDevIcons) { const convertedTexts = lines.map((line) => stripAnsi(line.displayText).split(":")[0]) const icons = createDevIconsList(convertedTexts).map((icon) => colorizeDevIcon(icon)) convertedLines = lines.map((line, i) => { const lineNumber = line.data.lineNumber != null ? `${line.data.lineNumber} ` : "" return { data: line.data, displayText: `${lineNumber}${icons[i]} ${line.displayText}`, } }) } else { convertedLines = lines.map((line) => { const lineNumber = line.data.lineNumber != null ? `${line.data.lineNumber} ` : "" return { data: line.data, displayText: `${lineNumber}${line.displayText}`, } }) } if (addGitStatus === true) { const status = await execGitStatus() if (status.length > 0) { convertedLines = convertedLines.map((line) => { const data = line.data as FileData const statusLine = status.find((s) => s.file === data.file) if (statusLine == null) { return { data: line.data, displayText: `\xA0\xA0${line.displayText}`, } } const statusText = stripAnsi(statusLine.status.trim()).slice(0, 1) return { data: line.data, displayText: `${colorize(statusText, "yellow")} ${line.displayText}`, } }) } } return convertedLines } ================================================ FILE: src/connector/directory-files.ts ================================================ import { globalVariableSelector } from "@/module/selector/vim-variable" import { pluginCall } from "@/plugin" export const execDirectoryFiles = async (args: string): Promise> => { const filelistCommand = globalVariableSelector("fzfPreviewDirectoryFilesCommand") if (typeof filelistCommand !== "string") { return [] } const lines = (await pluginCall("fzf_preview#remote#resource#directory_files#get", [ `${filelistCommand} ${args}`, ])) as ReadonlyArray return lines } ================================================ FILE: src/connector/fzf.ts ================================================ import { sessionModule } from "@/module/session" import { pluginCall } from "@/plugin" import { dispatch } from "@/store" import type { FzfCommandName } from "@/type" type Options = { sessionToken?: string clearSession?: boolean } export const execFzfCommand = async (command: FzfCommandName, options?: Options): Promise => { if (options != null && options.clearSession === true) { dispatch(sessionModule.actions.clearCurrentSession()) } if (options?.sessionToken != null) { await pluginCall("fzf_preview#remote#exec_fzf#exec", [command, PLUGIN.ENV, options.sessionToken]) } else { await pluginCall("fzf_preview#remote#exec_fzf#exec", [command, PLUGIN.ENV]) } } ================================================ FILE: src/connector/git.ts ================================================ import stripAnsi from "strip-ansi" import { GIT_BRANCH_COMMAND } from "@/const/git" import { createGitLogCommand, gitReflogDecorateCommand, gitReflogNameCommand, gitStashDecorateCommand, gitStashNameCommand, } from "@/fzf/util" import { gitConfigSelector } from "@/module/selector/git-config" import { globalVariableSelector } from "@/module/selector/vim-variable" import { pluginCall } from "@/plugin" import { getCurrentFilePath } from "@/system/file" import type { GitBranch, GitLog, GitReflog, GitStash, ParsedGitStatus } from "@/type" import { unreachable } from "@/util/type" export const execGitFiles = async (): Promise> => { const gitFilesCommand = globalVariableSelector("fzfPreviewGitFilesCommand") if (typeof gitFilesCommand !== "string") { return [] } const lines = (await pluginCall("fzf_preview#remote#resource#git_files#get", [ gitFilesCommand, ])) as ReadonlyArray return lines } export const execGitStatus = async (): Promise> => { const gitStatusCommand = globalVariableSelector("fzfPreviewGitStatusCommand") if (typeof gitStatusCommand !== "string") { return [] } const lines = (await pluginCall("fzf_preview#remote#resource#git_status#get", [ gitStatusCommand, ])) as ReadonlyArray return lines.map((line) => { const result = /(?.+)\s(?.+)/.exec(line) if (result?.groups == null) { throw new Error(`Unexpected line: ${line}`) } const file = stripAnsi(result.groups.file) const { status } = result.groups return { file, status, } }) } export const execGitBranch = async (): Promise> => { const lines = (await pluginCall("fzf_preview#remote#resource#util#exec_command", [ GIT_BRANCH_COMMAND, ])) as ReadonlyArray return lines.map((line) => { const [prefix, name, date, author] = line.split(" ") return { prefix, name, date, author, } }) } export const execGitLog = async (options?: { currentFile: boolean }): Promise> => { const command = options?.currentFile === true ? createGitLogCommand(await getCurrentFilePath()) : createGitLogCommand() const lines = (await pluginCall("fzf_preview#remote#resource#util#exec_command", [command])) as ReadonlyArray return lines.map((line) => { const [prefix, hash, date, author, comment] = line.split(/\s{4,}/) return { prefix, hash, date, author, comment, } }) } export const execGitReflog = async (): Promise> => { const lines1 = (await pluginCall("fzf_preview#remote#resource#util#exec_command", [ gitReflogDecorateCommand, ])) as ReadonlyArray const lines2 = (await pluginCall("fzf_preview#remote#resource#util#exec_command", [ gitReflogNameCommand, ])) as ReadonlyArray return lines1.map((line, i) => { const [prefix, hash, date, author, comment] = line.split(/\s{4,}/) const name = lines2[i] return { prefix, name, hash, date, author, comment, } }) } export const execGitStash = async (): Promise> => { const lines1 = (await pluginCall("fzf_preview#remote#resource#util#exec_command", [ gitStashDecorateCommand, ])) as ReadonlyArray const lines2 = (await pluginCall("fzf_preview#remote#resource#util#exec_command", [ gitStashNameCommand, ])) as ReadonlyArray return lines1.map((line, i) => { const [prefix, hash, date, author, comment] = line.split(/\s{4,}/) const name = lines2[i] return { prefix, name, hash, date, author, comment, } }) } export const gitAdd = async (file: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#add", [file]) } export const gitReset = async (file: string, option?: "--soft" | "--hard"): Promise => { await pluginCall("fzf_preview#remote#consumer#git#reset", [file, option == null ? "" : option]) } export const gitPatch = async (file: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#patch", [file]) } export const gitChaperon = async (file: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#chaperon", [file]) } export const gitAddIntentToAdd = async (file: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#add_intent_to_add", [file]) } export const gitResetIntentToAdd = async (file: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#reset_intent_to_add", [file]) } type CommitOption = | { name: "--amend" } | { name: "--amend --no-edit" } | { name: "--squash"; hash: string } | { name: "--fixup"; hash: string } export const gitCommit = async (option?: CommitOption): Promise => { const noVerify = gitConfigSelector("noVerify") const addNoVerifyOption = (optionString: string) => (noVerify ? `${optionString} --no-verify` : optionString) if (option == null) { await pluginCall("fzf_preview#remote#consumer#git#commit", [addNoVerifyOption("")]) return } switch (option.name) { case "--amend": case "--amend --no-edit": { await pluginCall("fzf_preview#remote#consumer#git#commit", [addNoVerifyOption(option.name)]) break } case "--squash": case "--fixup": { await pluginCall("fzf_preview#remote#consumer#git#commit", [addNoVerifyOption(`${option.name} ${option.hash}`)]) break } default: { unreachable(option) } } } export const gitRestore = async (file: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#restore", [file]) } export const gitSwitch = async (branch: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#switch", [branch]) } export const gitCreateBranch = async (): Promise => { await pluginCall("fzf_preview#remote#consumer#git#create_branch") } export const gitDiff = async (branch: string, branch2?: string): Promise => { if (branch2 == null) { await pluginCall("fzf_preview#remote#consumer#git#diff", [branch]) } else { await pluginCall("fzf_preview#remote#consumer#git#diff", [branch, branch2]) } } export const gitShow = async (nameOrHash: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#show", [nameOrHash]) } export const gitPush = async (option?: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#push", [option != null ? option : ""]) } export const gitFetch = async (): Promise => { await pluginCall("fzf_preview#remote#consumer#git#fetch") } export const gitPull = async (): Promise => { await pluginCall("fzf_preview#remote#consumer#git#pull") } export const gitMerge = async (branch: string, option?: "--no-ff"): Promise => { await pluginCall("fzf_preview#remote#consumer#git#merge", [branch, option != null ? option : ""]) } export const gitRebase = async (branch: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#rebase", [branch]) } export const gitRebaseInteractive = async (branchOrHash: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#rebase_interactive", [branchOrHash]) } export const gitDeleteBranch = async (branch: string, option?: { name: "--force" }): Promise => { await pluginCall("fzf_preview#remote#consumer#git#delete_branch", [branch, option != null ? option.name : ""]) } export const gitRenameBranch = async (branch: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#rename_branch", [branch]) } export const gitStashApply = async (name: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#stash_apply", [name]) } export const gitStashPop = async (name: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#stash_pop", [name]) } export const gitStashDrop = async (name: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#stash_drop", [name]) } export const gitStashCreate = async (): Promise => { await pluginCall("fzf_preview#remote#consumer#git#stash_create") } export const gitYank = async (branchOrHash: string): Promise => { await pluginCall("fzf_preview#remote#consumer#git#yank", [branchOrHash]) } ================================================ FILE: src/connector/grep.ts ================================================ import { globalVariableSelector } from "@/module/selector/vim-variable" import { pluginCall } from "@/plugin" export const execGrep = async (args: string): Promise> => { const grepCommand = globalVariableSelector("fzfPreviewGrepCmd") as string const lines = (await pluginCall("fzf_preview#remote#resource#grep#get", [ `${grepCommand} ${args}`, ])) as ReadonlyArray return lines } ================================================ FILE: src/connector/jumps.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { pluginCall } from "@/plugin" type Jump = ReadonlyDeep<{ file: string line: string text: string }> export const getJumps = async (): Promise> => (await pluginCall("fzf_preview#remote#resource#jumps#get")) as ReadonlyArray ================================================ FILE: src/connector/lines.ts ================================================ import { globalVariableSelector } from "@/module/selector/vim-variable" import { pluginCall } from "@/plugin" export const execLines = async (filePath: string): Promise> => { const linesCommand = globalVariableSelector("fzfPreviewLinesCommand") as string if (typeof linesCommand !== "string") { return [] } const lines = (await pluginCall("fzf_preview#remote#resource#lines#get", [ `${linesCommand} ${filePath}`, ])) as ReadonlyArray return lines } ================================================ FILE: src/connector/lsp.ts ================================================ import type { ReadonlyDeep } from "type-fest" import type { Location as LspLocation, LocationLink } from "vscode-languageserver-types" import { getLineFromFile } from "@/connector/util" import { collapseHome, existsFileAsync, getCurrentPath } from "@/system/file" import { dropFileProtocol, filePathToRelativeFilePath } from "@/system/project" import type { Diagnostic, DiagnosticItem, DiagnosticLevel, Location } from "@/type" export const diagnosticItemToData = async ( item: ReadonlyDeep, option?: { currentFile: string } ): Promise> => { if (!(await existsFileAsync(item.file))) { return null } const currentPath = await getCurrentPath() const file = filePathToRelativeFilePath(item.file, currentPath) if (file == null) { return null } if (option?.currentFile != null && option.currentFile !== file) { return null } return { file, lineNumber: item.lnum, severity: item.severity as DiagnosticLevel, message: item.message, } as const } type LocationOrLocationLink = | (LspLocation & { kind?: undefined }) | (LspLocation & { kind: "location" }) | (LocationLink & { kind: "locationLink" }) export const lspLocationToLocation = async ( locations: ReadonlyArray ): Promise> => { const currentPath = await getCurrentPath() return ( await Promise.all( locations.map(async (location) => { let lineNumber: number let uri: string if (location.kind == null || location.kind === "location") { lineNumber = location.range.start.line + 1 uri = location.uri } else if (location.targetRange != null) { lineNumber = location.targetRange.start.line + 1 uri = location.targetUri } else { throw new Error("Unexpected location") } const absoluteFilePath = decodeURIComponent(dropFileProtocol(uri)) const filePath = new RegExp(`^${currentPath}`).exec(absoluteFilePath) != null ? filePathToRelativeFilePath(absoluteFilePath, currentPath) : collapseHome(absoluteFilePath) if (filePath == null) { return null } const text = await getLineFromFile(absoluteFilePath, lineNumber) return { file: filePath, lineNumber, text } }) ) ).filter((location): location is Location => location != null) } ================================================ FILE: src/connector/marks.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { pluginCall } from "@/plugin" type Mark = ReadonlyDeep<{ file: string line: string text: string }> export const getMarks = async (): Promise> => (await pluginCall("fzf_preview#remote#resource#marks#get")) as ReadonlyArray ================================================ FILE: src/connector/memolist.ts ================================================ import { globalVariableSelector } from "@/module/selector/vim-variable" import { pluginCall } from "@/plugin" export const execMemoListFiles = async (): Promise> => (await pluginCall("fzf_preview#remote#resource#memolist#files")) as ReadonlyArray export const execMemoListGrep = async (args: string): Promise> => { const grepCommand = globalVariableSelector("fzfPreviewGrepCmd") as string const lines = (await pluginCall("fzf_preview#remote#resource#memolist#grep", [ `${grepCommand} ${args}`, ])) as ReadonlyArray return lines } ================================================ FILE: src/connector/nvim-lsp.ts ================================================ import type { LocationLink } from "coc.nvim" import type { Location as LspLocation } from "vscode-languageserver-types" import { lspLocationToLocation } from "@/connector/lsp" import { pluginCall, pluginCommand, pluginGetVar } from "@/plugin" import type { Diagnostic, Location } from "@/type" export const getReferences = async (): Promise<{ references: ReadonlyArray }> => { await pluginCommand(`lua require("fzf-preview").nvim_lsp_references()`) const references = (await pluginGetVar("fzf_preview_nvim_lsp_references")) as ReadonlyArray return { references: await lspLocationToLocation(references), } } type NvimLspDiagnostic = { bufnr: number lnum: number message: string severity: number } const severity = ["", "Error", "Warning", "Information", "Hint"] as const const nvimLspDiagnosticToDiagnosticItem = async (nvimDiagnostic: NvimLspDiagnostic): Promise => { const file = (await pluginCall("bufname", [nvimDiagnostic.bufnr])) as string return { file, lineNumber: nvimDiagnostic.lnum + 1, message: nvimDiagnostic.message, severity: severity[nvimDiagnostic.severity], } } export const getDiagnostics = async (): Promise<{ diagnostics: ReadonlyArray }> => { await pluginCommand(`lua require("fzf-preview").nvim_lsp_diagnostics()`) const diagnostics = (await pluginGetVar("fzf_preview_nvim_lsp_diagnostics")) as ReadonlyArray return { diagnostics: await Promise.all(diagnostics.map(nvimLspDiagnosticToDiagnosticItem)), } } export const getCurrentDiagnostics = async (): Promise<{ diagnostics: ReadonlyArray }> => { await pluginCommand(`lua require("fzf-preview").nvim_lsp_current_diagnostics()`) const diagnostics = (await pluginGetVar( "fzf_preview_nvim_lsp_current_diagnostics" )) as ReadonlyArray return { diagnostics: await Promise.all(diagnostics.map(nvimLspDiagnosticToDiagnosticItem)), } } export const getDefinition = async (): Promise<{ definition: ReadonlyArray }> => { await pluginCommand(`lua require("fzf-preview").nvim_lsp_definition()`) const definition = (await pluginGetVar("fzf_preview_nvim_lsp_definition")) as ReadonlyArray return { definition: await lspLocationToLocation(definition.map((v) => ({ ...v, kind: "locationLink" }))), } } export const getTypeDefinition = async (): Promise<{ typeDefinition: ReadonlyArray }> => { await pluginCommand(`lua require("fzf-preview").nvim_lsp_type_definition()`) const typeDefinition = (await pluginGetVar("fzf_preview_nvim_lsp_type_definition")) as ReadonlyArray return { typeDefinition: await lspLocationToLocation(typeDefinition), } } export const getImplementation = async (): Promise<{ implementations: ReadonlyArray }> => { await pluginCommand(`lua require("fzf-preview").nvim_lsp_implementations()`) const implementations = (await pluginGetVar("fzf_preview_nvim_lsp_implementations")) as ReadonlyArray return { implementations: await lspLocationToLocation(implementations), } } ================================================ FILE: src/connector/old-files.ts ================================================ import { pluginGetVvar } from "@/plugin" export const getOldFiles = async (): Promise> => (await pluginGetVvar("oldfiles")) as ReadonlyArray ================================================ FILE: src/connector/open-bufnr.ts ================================================ import { pluginCommand } from "@/plugin" import type { OpenCommand } from "@/type" export const openBufnr = async (openCommand: OpenCommand, bufnr: string): Promise => { if (openCommand !== "edit") { await pluginCommand(`execute 'silent ${openCommand} | buffer ${bufnr}'`) } await pluginCommand(`execute 'silent buffer ${bufnr}'`) } ================================================ FILE: src/connector/open-file.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { pluginCall, pluginCommand } from "@/plugin" import type { ExportQuickFix, OpenFile } from "@/type" export const openFile = async ({ openCommand, file, lineNumber, setTagStack, }: ReadonlyDeep): Promise => { if (setTagStack === true) { await pluginCall("fzf_preview#remote#tagstack#push_tag_stack") } await pluginCommand(`execute 'silent ${openCommand} ${file}'`) if (lineNumber != null) { await pluginCall("cursor", [lineNumber, 0]) } } type Option = { readonly title?: string } export const exportQuickFix = async ( quickFixList: ReadonlyDeep>, option?: Option ): Promise => { await pluginCall("setqflist", [[], "r", { items: quickFixList, ...option }]) await pluginCommand("copen") } ================================================ FILE: src/connector/project-files.ts ================================================ import { globalVariableSelector } from "@/module/selector/vim-variable" import { pluginCall } from "@/plugin" export const execProjectFiles = async (): Promise> => { const filelistCommand = globalVariableSelector("fzfPreviewFilelistCommand") if (typeof filelistCommand !== "string") { return [] } const lines = (await pluginCall("fzf_preview#remote#resource#project_files#get", [ filelistCommand, ])) as ReadonlyArray return lines } ================================================ FILE: src/connector/quickfix-and-locationlist.ts ================================================ import { pluginCall } from "@/plugin" export const getQuickFix = async (): Promise> => (await pluginCall("fzf_preview#remote#resource#quickfix_and_locationlist#get", ["quickfix"])) as ReadonlyArray export const getLocationList = async (): Promise> => (await pluginCall("fzf_preview#remote#resource#quickfix_and_locationlist#get", ["loclist"])) as ReadonlyArray ================================================ FILE: src/connector/register.ts ================================================ import { pluginCall } from "@/plugin" export const setRegister = async (str: string, options: string): Promise => { await pluginCall("fzf_preview#remote#consumer#register#set", [str, options]) } export const pasteRegister = async (str: string, options: string): Promise => { await pluginCall("fzf_preview#remote#consumer#register#paste", [str, options]) } ================================================ FILE: src/connector/resume.ts ================================================ import { resumeModule } from "@/module/resume" import { pluginCall } from "@/plugin" import { dispatch } from "@/store" import type { FzfPreviewCommandList } from "@/type" export const setResourceCommandName = async (commandName: string): Promise => { await pluginCall("fzf_preview#remote#window#set_resource_command_name", [commandName]) } export const dispatchResumeQuery = ([commandName, query]: [FzfPreviewCommandList, string]): void => { dispatch(resumeModule.actions.setQuery({ commandName, query })) } ================================================ FILE: src/connector/tags.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { pluginCall } from "@/plugin" type Tag = ReadonlyDeep<{ name: string file: string line: string type: string }> export const getCtags = async (): Promise> => (await pluginCall("fzf_preview#remote#resource#tags#ctags")) as ReadonlyArray ================================================ FILE: src/connector/todo-comments.ts ================================================ import { globalVariableSelector } from "@/module/selector/vim-variable" import { pluginCall } from "@/plugin" export const execSearchTodoComments = async (): Promise> => { const grepCommand = globalVariableSelector("fzfPreviewGrepCmd") as string const todoKeywords = (await pluginCall("fzf_preview#remote#resource#todo_comments#get", [])) as ReadonlyArray const keywordsRegexp = todoKeywords.map((keyword) => `${keyword}: `).join("|") const grepKeywords = `${grepCommand} "${keywordsRegexp}"` const lines = (await pluginCall("fzf_preview#remote#resource#grep#get", [grepKeywords])) as ReadonlyArray return lines } ================================================ FILE: src/connector/util.ts ================================================ import { pluginCall, pluginCommand } from "@/plugin" import { execAsyncCommand } from "@/system/command" export const vimEchoMessage = async (message: string): Promise => { await pluginCommand(`echomsg '${message}'`) } export const isGitDirectory = async (): Promise => { const result = (await pluginCall("fzf_preview#remote#util#is_git_directory")) as boolean return result } export const getLineFromFile = async (file: string, line: number): Promise => (await execAsyncCommand(`sed -n ${line}p ${file}`)).stdout.trim() ================================================ FILE: src/connector/vim-command.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { pluginCall } from "@/plugin" type VimCommand = ReadonlyDeep<{ name: string number: number | null }> export const getVimCommands = async (): Promise> => { const commands = (await pluginCall("fzf_preview#remote#resource#vim_command#commands")) as ReadonlyArray return commands } export const getVimCommandHistory = async (): Promise> => { const commands = (await pluginCall("fzf_preview#remote#resource#vim_command#history")) as ReadonlyArray return commands } export const execVimCommand = async (command: string): Promise => { await pluginCall("fzf_preview#remote#resource#vim_command#exec", [command]) } export const editVimCommand = async (command: string): Promise => { await pluginCall("fzf_preview#remote#resource#vim_command#edit", [command]) } ================================================ FILE: src/connector/vim-help.ts ================================================ import { pluginCall } from "@/plugin" export const execHelpTags = async (args: string): Promise> => { const runtimeHelpDir = `${process.env.VIMRUNTIME}/doc/*` const pluginHelpDir = process.env.FZF_PREVIEW_PLUGIN_HELP_ROOT_DIR == null ? "" : `${process.env.FZF_PREVIEW_PLUGIN_HELP_ROOT_DIR}/**/doc/*` const grepCommand = `rg --line-number --no-heading --color=never --sort=path ${args} ${runtimeHelpDir} ${pluginHelpDir}` const lines = (await pluginCall("fzf_preview#remote#resource#grep#get", [`${grepCommand}`])) as ReadonlyArray return lines } ================================================ FILE: src/connector/vim-lsp.ts ================================================ import { isEqual } from "lodash" import type { Diagnostic as LspDiagnostic, Location as VimLspLocation, LocationLink } from "vscode-languageserver-types" import { diagnosticItemToData, lspLocationToLocation } from "@/connector/lsp" import { pluginCall } from "@/plugin" import { getCurrentFilePath } from "@/system/file" import { dropFileProtocol } from "@/system/project" import type { Diagnostic, DiagnosticItem, Location } from "@/type" type VimLspDiagnostic = { [file: string]: { [server: string]: { params: { diagnostics: ReadonlyArray } } } } const severity = ["", "Error", "Warning", "Information", "Hint"] const getDiagnosticsItems = async () => { const lspDiagnostic = (await pluginCall( "lsp#internal#diagnostics#state#_get_all_diagnostics_grouped_by_uri_and_server" )) as VimLspDiagnostic const diagnosticItems: ReadonlyArray = Object.entries(lspDiagnostic).flatMap( ([path, result]) => { const diagnostics = Object.entries(result).flatMap(([_, v]) => v.params.diagnostics) const file = decodeURIComponent(dropFileProtocol(path)) if (file == null) { return null } return diagnostics.map((diagnostic) => ({ file, lnum: diagnostic.range.start.line, message: diagnostic.message, severity: severity[diagnostic?.severity as number] ?? "", })) } ) return diagnosticItems } export const getDiagnostics = async (): Promise> => { const diagnosticItems = await getDiagnosticsItems() const diagnostics = await Promise.all( diagnosticItems.filter((v): v is DiagnosticItem => v != null).map(async (item) => await diagnosticItemToData(item)) ) return diagnostics.filter((diagnostic): diagnostic is Diagnostic => diagnostic != null) } export const getCurrentDiagnostics = async (): Promise> => { const currentFile = await getCurrentFilePath() const diagnosticItems = await getDiagnosticsItems() const diagnostics = await Promise.all( diagnosticItems .filter((v): v is DiagnosticItem => v != null) .map(async (item) => await diagnosticItemToData(item, { currentFile })) ) return diagnostics.filter((diagnostic): diagnostic is Diagnostic => diagnostic != null) } export const getReferences = async (): Promise<{ references: ReadonlyArray }> => { const servers = (await pluginCall("fzf_preview#remote#resource#vim_lsp#servers", [ "references", ])) as ReadonlyArray await pluginCall("fzf_preview#remote#resource#vim_lsp#request_references", [servers]) let referencesWithServer: { [server: string]: ReadonlyArray } = {} for (let i = 0; i < 100; i += 1) { // eslint-disable-next-line no-await-in-loop referencesWithServer = (await pluginCall("fzf_preview#remote#resource#vim_lsp#fetch_references")) as { [server: string]: ReadonlyArray } if (isEqual([...servers].sort(), Object.keys(referencesWithServer).sort())) { break } // eslint-disable-next-line no-await-in-loop, no-promise-executor-return await new Promise((resolve) => setTimeout(() => resolve(), 50)) } return { references: await lspLocationToLocation( Object.entries(referencesWithServer).flatMap(([_, references]) => references.map((v) => ({ ...v, kind: "location" })) ) ), } } export const getDefinition = async (): Promise<{ definitions: ReadonlyArray }> => { const servers = (await pluginCall("fzf_preview#remote#resource#vim_lsp#servers", [ "definition", ])) as ReadonlyArray await pluginCall("fzf_preview#remote#resource#vim_lsp#request_definition", [servers]) let definitionWithServer: { [server: string]: ReadonlyArray } = {} for (let i = 0; i < 100; i += 1) { // eslint-disable-next-line no-await-in-loop definitionWithServer = (await pluginCall("fzf_preview#remote#resource#vim_lsp#fetch_definition")) as { [server: string]: ReadonlyArray } if (isEqual([...servers].sort(), Object.keys(definitionWithServer).sort())) { break } // eslint-disable-next-line no-await-in-loop, no-promise-executor-return await new Promise((resolve) => setTimeout(resolve, 50)) } return { definitions: await lspLocationToLocation( Object.entries(definitionWithServer).flatMap(([_, definitions]) => definitions.map((v) => ({ ...v, kind: "location" })) ) ), } } export const getTypeDefinition = async (): Promise<{ typeDefinitions: ReadonlyArray }> => { const servers = (await pluginCall("fzf_preview#remote#resource#vim_lsp#servers", [ "type_definition", ])) as ReadonlyArray await pluginCall("fzf_preview#remote#resource#vim_lsp#request_type_definition", [servers]) let typeDefinitionWithServer: { [server: string]: ReadonlyArray } = {} for (let i = 0; i < 100; i += 1) { // eslint-disable-next-line no-await-in-loop typeDefinitionWithServer = (await pluginCall("fzf_preview#remote#resource#vim_lsp#fetch_type_definition")) as { [server: string]: ReadonlyArray } if (isEqual([...servers].sort(), Object.keys(typeDefinitionWithServer).sort())) { break } // eslint-disable-next-line no-await-in-loop, no-promise-executor-return await new Promise((resolve) => setTimeout(resolve, 50)) } return { typeDefinitions: await lspLocationToLocation( Object.entries(typeDefinitionWithServer).flatMap(([_, typeDefinitions]) => typeDefinitions.map((v) => ({ ...v, kind: "locationLink" })) ) ), } } export const getImplementation = async (): Promise<{ implementations: ReadonlyArray }> => { const servers = (await pluginCall("fzf_preview#remote#resource#vim_lsp#servers", [ "implementation", ])) as ReadonlyArray await pluginCall("fzf_preview#remote#resource#vim_lsp#request_implementation", [servers]) let implementationWithServer: { [server: string]: ReadonlyArray } = {} for (let i = 0; i < 100; i += 1) { // eslint-disable-next-line no-await-in-loop implementationWithServer = (await pluginCall("fzf_preview#remote#resource#vim_lsp#fetch_implementation")) as { [server: string]: ReadonlyArray } if (isEqual([...servers].sort(), Object.keys(implementationWithServer).sort())) { break } // eslint-disable-next-line no-await-in-loop, no-promise-executor-return await new Promise((resolve) => setTimeout(resolve, 50)) } return { implementations: await lspLocationToLocation( Object.entries(implementationWithServer).flatMap(([_, implementations]) => implementations.map((v) => ({ ...v, kind: "locationLink" })) ) ), } } ================================================ FILE: src/connector/vista.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { pluginCall } from "@/plugin" export type VistaTag = ReadonlyDeep<{ lineNumber: number kind: string text: string tagFile: string }> export type VistaBufferTag = ReadonlyDeep<{ lineNumber: number kind: string text: string line: string }> export const getVistaCtags = async (): Promise> => { const tags = (await pluginCall("fzf_preview#remote#resource#vista#ctags")) as ReadonlyArray return tags } export const getVistaBufferCtags = async (): Promise> => { const tags = (await pluginCall("fzf_preview#remote#resource#vista#buffer_ctags")) as ReadonlyArray return tags } ================================================ FILE: src/connector/yankround.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { pluginCall } from "@/plugin" type YankHistory = ReadonlyDeep<{ line: number text: string option: string }> export const getYankround = async (): Promise> => (await pluginCall("fzf_preview#remote#resource#yankround#get")) as ReadonlyArray ================================================ FILE: src/const/fzf-handler.ts ================================================ export const HANDLER_NAME = "FzfPreviewHandleResource" ================================================ FILE: src/const/fzf-option.ts ================================================ export const DEFINED_FZF_OPTION_TYPES_IN_PLUGIN = ["--ansi", "--bind", "--expect"] as const export const FILE_RESOURCES = [ "project", "git", "directory", "buffer", "project_old", "project_mru", "project_mrw", "old", "mru", "mrw", ] as const export const PREVIEW_WINDOW_LAYOUT_CHANGE_SIZE = 150 ================================================ FILE: src/const/fzf-processes.ts ================================================ export const PROCESSES_NAME = [ "open-file", "open-file-with-tag-stack", "open-buffer", "open-bufnr", "command-palette", "git-action", "git-status", "git-status-actions", "git-branch", "git-branch-actions", "git-log", "git-log-actions", "git-stash", "git-stash-actions", "git-reflog", "git-reflog-actions", "register", "open-pr", ] as const ================================================ FILE: src/const/fzf-resource.ts ================================================ export const USE_DEV_ICONS_PATTERN_LIMIT = 3000 ================================================ FILE: src/const/fzf-runner.ts ================================================ import { TEMPORALLY_DATA_FILE_PATH } from "@/const/system" export const TAIL_RESOURCE_FILE_COMMAND = `tail -n +1 -f ${TEMPORALLY_DATA_FILE_PATH}` ================================================ FILE: src/const/git.ts ================================================ export const GIT_ACTIONS = [ "status", "branch", "log", "current-log", "stash", "reflog", "commit", "commit --amend", "commit --amend --no-edit", "push", "push --force", "fetch", "pull", "toggle --no-verify", ] as const export const GIT_STATUS_ACTIONS = [ "add", "reset", "patch", "restore", "chaperon", "add --intent-to-add", "reset --intent-to-add", ] as const export const GIT_BRANCH_ACTIONS = [ "diff", "switch", "reset", "reset --hard", "reset --soft", "merge", "merge --no-ff", "rebase", "rebase --interactive", "delete", "delete --force", "rename", "yank", ] as const export const GIT_LOG_ACTIONS = [ "show", "diff", "reset", "reset-hard", "reset-soft", "switch", "commit --squash", "commit --fixup", "rebase --interactive", "yank", ] as const export const GIT_STASH_ACTIONS = ["show", "diff", "apply", "pop", "drop", "yank"] as const export const GIT_REFLOG_ACTIONS = ["show", "diff", "reset", "reset-hard", "reset-soft", "switch", "yank"] as const export const GIT_BRANCH_COMMAND = "git for-each-ref refs/heads refs/remotes --color=always --format='%(color:green)[branch]%(color:reset) %(color:reset)%(HEAD) %(color:magenta)%(refname:short)%(color:reset) %(color:yellow)%(authordate:short)%(color:reset) %(color:blue)[%(authorname)]%(color:reset)%09' 2> /dev/null" const GIT_BRANCH_PREVIEW_COMMAND_OPTION = "--decorate --pretty='format:%C(yellow)%h %C(green)%cd %C(reset)%s %C(red)%d %C(cyan)[%an]' --date=iso --graph --color=always" export const GIT_BRANCH_PREVIEW_COMMAND = `[[ '{2}' != '*' ]] && git log {2} ${GIT_BRANCH_PREVIEW_COMMAND_OPTION} 2> /dev/null || git log {3} ${GIT_BRANCH_PREVIEW_COMMAND_OPTION} 2> /dev/null` export const GIT_LOG_PREVIEW_COMMAND = "git show {2} --color=always" export const GIT_STASH_PREVIEW_COMMAND = "git show {2} --color=always 2> /dev/null" export const GIT_REFLOG_PREVIEW_COMMAND = "git show {2} --color=always" ================================================ FILE: src/const/module.ts ================================================ export const VIM_VARIABLE = "vim_variable" export const EXECUTE_COMMAND = "execute_command" export const RESUME = "resume" export const SESSION = "session" export const GIT_CONFIG = "git_config" export const RECALL = "recall" export const FILE_PATH = "file_path" ================================================ FILE: src/const/system.ts ================================================ export const BUFFER_TAGS_COMMAND = "ctags -f - --sort=yes --excmd=number" // Default: 1024 * 1024 export const MAX_BUFFER_SIZE = 1024 * 1024 * 1000 export const TEMPORALLY_DATA_FILE_PATH = "/tmp/fzf-preview" ================================================ FILE: src/fzf/command/execute-fast.ts ================================================ import fs from "fs" import { convertForFzf } from "@/connector/convert-for-fzf" import { setResourceCommandName } from "@/connector/resume" import { HANDLER_NAME } from "@/const/fzf-handler" import { TAIL_RESOURCE_FILE_COMMAND } from "@/const/fzf-runner" import { TEMPORALLY_DATA_FILE_PATH } from "@/const/system" import { getEnableDevIcons } from "@/fzf/command/util" import { fzfOptionsToString } from "@/fzf/option/convert" import { generateOptions } from "@/fzf/option/generator" import { executeCommandModule } from "@/module/execute-command" import { pluginCall } from "@/plugin" import { resourceLineToFzfLine } from "@/plugin/fzf-runner" import { dispatch } from "@/store" import { getCurrentFilePath } from "@/system/file" import type { ExecuteArgs } from "@/type" export const executeExperimentalFast = async ({ sourceFunc, sourceFuncArgs, enableDevIconsCommandSetting, commandName, userProcesses, fzfCommandDefaultOptions, defaultProcesses, addFzfOptions, historyOption, resumeQuery, enableConvertForFzf, addGitStatus, }: ExecuteArgs): Promise => { const fzfOptions = await generateOptions({ fzfCommandDefaultOptions, dynamicOptions: undefined, defaultProcesses, userProcesses, userOptions: addFzfOptions, historyOption, resumeQuery, }) // eslint-disable-next-line @typescript-eslint/no-floating-promises pluginCall("fzf_preview#remote#runner#fzf_run", { source: TAIL_RESOURCE_FILE_COMMAND, handler: HANDLER_NAME, options: fzfOptionsToString(fzfOptions), environment: PLUGIN.ENV, }) const resource = await sourceFunc(sourceFuncArgs) const enableDevIcons = getEnableDevIcons(resource.lines, enableDevIconsCommandSetting) // For execute and display dispatch( executeCommandModule.actions.setExecuteCommand({ commandName, options: { userProcesses, enableDevIcons, currentFilePath: await getCurrentFilePath(), }, }) ) // For resume option await setResourceCommandName(commandName) const fd = fs.openSync(TEMPORALLY_DATA_FILE_PATH, "a") const lines = await convertForFzf(resource.lines, { enableConvertForFzf, enableDevIcons, addGitStatus, }) for (const line of lines) { fs.appendFileSync(fd, `${resourceLineToFzfLine(line)}\n`) } fs.closeSync(fd) } ================================================ FILE: src/fzf/command/execute-normal.ts ================================================ import { convertForFzf } from "@/connector/convert-for-fzf" import { setResourceCommandName } from "@/connector/resume" import { HANDLER_NAME } from "@/const/fzf-handler" import { getEnableDevIcons } from "@/fzf/command/util" import { generateOptions } from "@/fzf/option/generator" import { executeCommandModule } from "@/module/execute-command" import { fzfRunner } from "@/plugin/fzf-runner" import { dispatch } from "@/store" import { getCurrentFilePath } from "@/system/file" import type { ExecuteArgs } from "@/type" export const executeNormal = async ({ sourceFunc, sourceFuncArgs, enableDevIconsCommandSetting, commandName, userProcesses, fzfCommandDefaultOptions, defaultProcesses, addFzfOptions, historyOption, resumeQuery, enableConvertForFzf, addGitStatus, }: ExecuteArgs): Promise => { const resource = await sourceFunc(sourceFuncArgs) const enableDevIcons = getEnableDevIcons(resource.lines, enableDevIconsCommandSetting) // For execute and display dispatch( executeCommandModule.actions.setExecuteCommand({ commandName, options: { userProcesses, enableDevIcons, currentFilePath: await getCurrentFilePath(), }, }) ) // For resume option await setResourceCommandName(commandName) const fzfOptions = await generateOptions({ fzfCommandDefaultOptions, dynamicOptions: resource.options, defaultProcesses, userProcesses, userOptions: addFzfOptions, historyOption, resumeQuery, }) const resourceForFzf = await convertForFzf(resource.lines, { enableConvertForFzf, enableDevIcons, addGitStatus, }) await fzfRunner({ resourceLines: resourceForFzf, handler: HANDLER_NAME, options: fzfOptions, }) } ================================================ FILE: src/fzf/command/index.ts ================================================ import { parseAddFzfArg, parseGrepArgs, parseProcesses, parseResume } from "@/args" import { parseExperimental } from "@/args/experimental-parser" import { parseSession } from "@/args/session-parser" import { TEMPORALLY_DATA_FILE_PATH } from "@/const/system" import { executeExperimentalFast } from "@/fzf/command/execute-fast" import { executeNormal } from "@/fzf/command/execute-normal" import { processesDefinition } from "@/fzf/process" import { recallModule } from "@/module/recall" import { recallSelector } from "@/module/selector/recall" import { globalVariableSelector } from "@/module/selector/vim-variable" import { sessionModule } from "@/module/session" import { syncVimOptions, syncVimVariable } from "@/plugin/sync-vim-variable" import { dispatch } from "@/store" import { initializeDataTransferFile } from "@/system/file" import type { FzfCommand } from "@/type" const getDefaultProcesses = (defaultProcessesName: string) => { const targetProcessesDefinition = processesDefinition.find((define) => define.name === defaultProcessesName) if (targetProcessesDefinition == null) { throw new Error(`Processes not found: "${defaultProcessesName}"`) } return targetProcessesDefinition.processes } const getDefaultOptions = async (defaultFzfOptionFunc: FzfCommand["defaultFzfOptionFunc"]) => { const defaultOptions = defaultFzfOptionFunc() return defaultOptions instanceof Promise ? await defaultOptions : defaultOptions } export const executeCommand = async ( args: string, { commandName, sourceFunc, sourceFuncArgsParser, defaultFzfOptionFunc, defaultProcessesName, enableConvertForFzf, enableDevIcons: enableDevIconsCommandSetting, beforeCommandHook, addGitStatus, }: FzfCommand ): Promise => { await Promise.all([syncVimVariable(), syncVimOptions()]) initializeDataTransferFile(TEMPORALLY_DATA_FILE_PATH) if (beforeCommandHook != null) { beforeCommandHook(args) } // For ProjectGrepRecall if (commandName === "FzfPreviewProjectGrep") { dispatch( recallModule.actions.setGrepArgs({ grepArgs: parseGrepArgs(args).args.join(" "), }) ) } if (commandName === "FzfPreviewProjectGrepRecall") { // eslint-disable-next-line no-param-reassign args = `${args} ${recallSelector("grepArgs")}` // eslint-disable-next-line no-param-reassign commandName = "FzfPreviewProjectGrep" } const addFzfOptions = parseAddFzfArg(args) const userProcesses = parseProcesses(defaultProcessesName, args) const fzfCommandDefaultOptions = await getDefaultOptions(defaultFzfOptionFunc) const defaultProcesses = getDefaultProcesses(defaultProcessesName) const resumeQuery = parseResume(commandName, args) const currentSession = parseSession(args) const experimental = parseExperimental(args) if (currentSession != null) { dispatch(sessionModule.actions.setCurrentSession({ session: currentSession })) } const historyDir = globalVariableSelector("fzfPreviewHistoryDir") as string | false const historyOption = { commandName, historyDir, } const sourceFuncArgs = sourceFuncArgsParser(args) const executeArgs = { sourceFunc, sourceFuncArgs, enableDevIconsCommandSetting, commandName, userProcesses, fzfCommandDefaultOptions, defaultProcesses, addFzfOptions, historyOption, resumeQuery, enableConvertForFzf, addGitStatus, } if (experimental.fast) { await executeExperimentalFast(executeArgs) } else { await executeNormal(executeArgs) } } ================================================ FILE: src/fzf/command/util.ts ================================================ import { globalVariableSelector } from "@/module/selector/vim-variable" import type { ResourceLines } from "@/type" export const getEnableDevIcons = (resourceLines: ResourceLines, enableDevIconsCommandSetting: boolean): boolean => { return ( enableDevIconsCommandSetting && globalVariableSelector("fzfPreviewUseDevIcons") !== 0 && globalVariableSelector("fzfPreviewDevIconsLimit") > resourceLines.length ) } ================================================ FILE: src/fzf/function/index.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { processesDefinition } from "@/fzf/process" import { createProcessFunctionName } from "@/fzf/util" import type { ProcessesName } from "@/type" export const getDefaultProcesses = (processesName: string): ReadonlyDeep<{ [key: string]: string }> => { const targetProcessesDefinition = processesDefinition.find((define) => define.name === processesName) if (targetProcessesDefinition == null) { throw new Error(`Processes not found: "${processesName}"`) } return targetProcessesDefinition.processes.reduce( (acc: { [key: string]: string }, cur) => ({ ...acc, [cur.key]: createProcessFunctionName(processesName as ProcessesName, cur.key), }), {} ) } ================================================ FILE: src/fzf/handler/index.ts ================================================ import { createProcessFunctionName } from "@/fzf/util" import type { State as ExecuteCommandState } from "@/module/execute-command" import { executeCommandSelector } from "@/module/selector/execute-command" import { processesRunner } from "@/plugin/process-runner" import { syncVimVariable } from "@/plugin/sync-vim-variable" import type { CallbackLines, ExpectKeyAndSelectedLines, FzfCommand, SelectedLines } from "@/type" /* eslint-disable @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-var-requires, global-require, n/no-missing-require, no-nested-ternary */ const commands: ReadonlyArray = PLUGIN.ENV === "remote" || PLUGIN.ENV === "rpc" ? (require("@/association/command").commandDefinition as ReadonlyArray) : PLUGIN.ENV === "coc" ? (require("@/association/coc-command").cocCommandDefinition as ReadonlyArray) : [] /* eslint-enable */ const runProcess = async ( lines: ExpectKeyAndSelectedLines, { commandName, options: { userProcesses } }: ExecuteCommandState ) => { const expectKey = lines[0] === "" ? "enter" : lines[0] const selectedLines = lines.slice(1) as SelectedLines const { defaultProcessesName } = commands.find((command) => command.commandName === commandName) as FzfCommand if (commandName != null) { await processesRunner({ processesFunctionName: createProcessFunctionName(defaultProcessesName, expectKey), expectKey, lines: selectedLines, userProcesses, }) } } export const callProcess = async ([lines]: [CallbackLines, ...ReadonlyArray]): Promise => { await syncVimVariable() const executeCommand = executeCommandSelector() await runProcess(lines, executeCommand) } ================================================ FILE: src/fzf/option/convert.test.ts ================================================ import { fzfOptionsToString, joinBind } from "@/fzf/option/convert" import type { FzfOptions } from "@/type" const defaultBind = [ { key: "ctrl-d", action: "preview-page-down", }, { key: "ctrl-u", action: "preview-page-up", }, { key: "?", action: "toggle-preview", }, ] describe("joinBind", () => { it("bind option is array", () => { expect(joinBind(defaultBind)).toBe("ctrl-d:preview-page-down,ctrl-u:preview-page-up,?:toggle-preview") }) }) describe("fzfOptionsToString", () => { let options: FzfOptions = {} describe("setting defined options", () => { beforeEach(() => { options = { "--ansi": true, "--bind": defaultBind, "--expect": ["ctrl-x", "ctrl-v", "ctrl-t"], } }) it("default value", () => { expect(fzfOptionsToString(options)).toBe( '--ansi --bind=ctrl-d:preview-page-down,ctrl-u:preview-page-up,?:toggle-preview --expect="ctrl-x,ctrl-v,ctrl-t"' ) }) it("delete ansi", () => { options = { ...options, "--ansi": undefined } expect(fzfOptionsToString(options)).toBe( '--bind=ctrl-d:preview-page-down,ctrl-u:preview-page-up,?:toggle-preview --expect="ctrl-x,ctrl-v,ctrl-t"' ) }) it("string bind", () => { options = { ...options, "--bind": '"foo"' } expect(fzfOptionsToString(options)).toBe('--ansi --bind="foo" --expect="ctrl-x,ctrl-v,ctrl-t"') }) it("string expect", () => { options = { ...options, "--expect": '"foo"' } expect(fzfOptionsToString(options)).toBe( '--ansi --bind=ctrl-d:preview-page-down,ctrl-u:preview-page-up,?:toggle-preview --expect="foo"' ) }) }) describe("setting undefined options", () => { it('--foo: undefined, --bar: "bar"', () => { options = { "--foo": undefined, "--bar": "bar", } expect(fzfOptionsToString(options)).toBe("--foo --bar=bar") }) it('use double quote in value: --foobar: ""foo bar""', () => { options = { "--foobar": '"foo bar"', } expect(fzfOptionsToString(options)).toBe('--foobar="foo bar"') }) // If not enclosed in double quotes, another args is used it('not use double quote in value: --foobar: "foo bar"', () => { options = { "--foobar": "foo bar", } expect(fzfOptionsToString(options)).toBe("--foobar=foo bar") }) }) }) ================================================ FILE: src/fzf/option/convert.ts ================================================ import { DEFINED_FZF_OPTION_TYPES_IN_PLUGIN } from "@/const/fzf-option" import type { FzfOptions } from "@/type" export const joinBind = ( bind: ReadonlyArray<{ key: string action: string }> ): string => { return bind.map(({ key, action }) => `${key}:${action}`).join(",") } // eslint-disable-next-line complexity const definedOptionsToArray = (options: FzfOptions) => { const arrayOptions: Array = [] if (options["--ansi"] != null) { arrayOptions.push("--ansi") } if (options["--bind"] != null && Array.isArray(options["--bind"])) { arrayOptions.push(`--bind=${joinBind(options["--bind"])}`) } else if (options["--bind"] != null && typeof options["--bind"] === "string") { arrayOptions.push(`--bind=${options["--bind"]}`) } if (options["--expect"] != null && Array.isArray(options["--expect"])) { if (options["--expect"].length > 0) { arrayOptions.push(`--expect="${options["--expect"].join(",")}"`) } else { arrayOptions.push(`--expect="alt-enter"`) } } else if (options["--expect"] != null && typeof options["--expect"] === "string") { arrayOptions.push(`--expect=${options["--expect"]}`) } return arrayOptions } const optionsToArray = (options: FzfOptions) => { const arrayOptions = definedOptionsToArray(options) Object.entries(options) .filter(([key]) => !(DEFINED_FZF_OPTION_TYPES_IN_PLUGIN as ReadonlyArray).includes(key)) .forEach(([key, value]) => { if (typeof value !== "string") { arrayOptions.push(`${key}`) } else { arrayOptions.push(`${key}=${value}`) } }) return arrayOptions } export const fzfOptionsToString = (options: FzfOptions): string => { return optionsToArray(options).join(" ") } ================================================ FILE: src/fzf/option/generator.test.ts ================================================ import { generateOptions } from "@/fzf/option/generator" import { openFileProcesses as defaultProcesses } from "@/fzf/process/open-file" import { globalVariableSelector, vimOptionsSelector } from "@/module/selector/vim-variable" import { pluginGetVar } from "@/plugin" import type { FzfOptions, Processes, ResourceData } from "@/type" jest.mock("@/plugin") jest.mock("@/module/selector/vim-variable") describe("generateOptions", () => { let fzfCommandDefaultOptions: FzfOptions = {} beforeEach(() => { ;(globalVariableSelector as jest.Mock).mockImplementation((variableName) => variableName === "fzfPreviewDefaultFzfOptions" ? { "--reverse": true } : undefined ) fzfCommandDefaultOptions = { "--ansi": true, "--bind": [ { key: "ctrl-d", action: "preview-page-down", }, { key: "ctrl-u", action: "preview-page-up", }, { key: "?", action: "toggle-preview", }, ], "--expect": ["ctrl-x", "ctrl-v", "ctrl-t", "ctrl-o", "ctrl-q"], "--reverse": true, "--with-nth": '"2.."', "--no-separator": true, } }) const otherDefaultProcesses: Processes = [ { name: "", key: "enter", execute: (_: ReadonlyArray) => {}, }, { name: "", key: "ctrl-a", execute: (_: ReadonlyArray) => {}, }, { name: "", key: "ctrl-b", execute: (_: ReadonlyArray) => {}, }, { name: "", key: "ctrl-c", execute: (_: ReadonlyArray) => {}, }, ] const emptyHistoryOption = { commandName: "Foo", historyDir: false, } as const describe("dynamic options", () => { it("undefined", async () => { expect( await generateOptions({ fzfCommandDefaultOptions, dynamicOptions: undefined, defaultProcesses, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual(fzfCommandDefaultOptions) }) it("for grep", async () => { const grepArgs = "foo" expect( await generateOptions({ fzfCommandDefaultOptions, dynamicOptions: { "--header": `"[Grep from] ${grepArgs}"` }, defaultProcesses, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual({ ...fzfCommandDefaultOptions, "--header": `"[Grep from] ${grepArgs}"`, }) }) }) describe("empty user processes", () => { it("open file process", async () => { expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual(fzfCommandDefaultOptions) }) it("other default processes", async () => { fzfCommandDefaultOptions = { ...fzfCommandDefaultOptions, "--expect": ["ctrl-a", "ctrl-b", "ctrl-c"] } expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses: otherDefaultProcesses, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual(fzfCommandDefaultOptions) }) }) describe("set user processes from global variable", () => { it("open file processes", async () => { ;(pluginGetVar as jest.Mock).mockReturnValue( new Promise>((resolve) => { resolve({ enter: null, "ctrl-d": null, "ctrl-e": null, "ctrl-f": null, }) }) ) fzfCommandDefaultOptions = { ...fzfCommandDefaultOptions, "--expect": ["ctrl-d", "ctrl-e", "ctrl-f"] } expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userProcesses: { type: "global_variable", value: "foo" }, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual(fzfCommandDefaultOptions) }) it("other processes (not open file processes)", async () => { ;(pluginGetVar as jest.Mock).mockReturnValue( new Promise>((resolve) => { resolve({ enter: null, "ctrl-d": null, "ctrl-e": null, "ctrl-f": null, }) }) ) fzfCommandDefaultOptions = { ...fzfCommandDefaultOptions, "--expect": ["ctrl-d", "ctrl-e", "ctrl-f"] } expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses: otherDefaultProcesses, userProcesses: { type: "global_variable", value: "foo" }, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual(fzfCommandDefaultOptions) }) }) describe("set user processes from custom processes variable", () => { beforeEach(() => { ;(globalVariableSelector as jest.Mock).mockImplementation((variableName) => { if (variableName === "fzfPreviewDefaultFzfOptions") { return { "--reverse": true } } else if (variableName === "fzfPreviewCustomProcesses") { return { "open-file": { "ctrl-h": "", "ctrl-i": "", "ctrl-j": "", }, register: { "ctrl-k": "", "ctrl-l": "", "ctrl-m": "", }, } } else { return undefined } }) }) it("open file processes", async () => { const customOpenProcessesExpectOptions = { "--expect": ["ctrl-h", "ctrl-i", "ctrl-j"] } expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userProcesses: { type: "custom_processes_variable", value: "open-file" }, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual({ ...fzfCommandDefaultOptions, ...customOpenProcessesExpectOptions }) }) it("other processes (not open file processes)", async () => { const customOtherProcessesExpectOptions = { "--expect": ["ctrl-k", "ctrl-l", "ctrl-m"] } expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userProcesses: { type: "custom_processes_variable", value: "register" }, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual({ ...fzfCommandDefaultOptions, ...customOtherProcessesExpectOptions }) }) }) it("set user not dictionary processes", async () => { ;(pluginGetVar as jest.Mock).mockImplementation(() => { throw new Error("foo") }) await expect( generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userProcesses: { type: "global_variable", value: "foo" }, userOptions: [], historyOption: emptyHistoryOption, }) ).rejects.toThrow("foo") }) it("set --preview-window options", async () => { ;(globalVariableSelector as jest.Mock).mockImplementation((variableName) => { if (variableName === "fzfPreviewDefaultFzfOptions") { return { "--reverse": true } } else if (variableName === "fzfPreviewFzfPreviewWindowOption") { return "foo" } else { return undefined } }) expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual({ ...fzfCommandDefaultOptions, "--preview-window": '"foo"' }) }) it("--preview-window options when columns less than layout change size", async () => { ;(vimOptionsSelector as jest.Mock).mockImplementation((optionName) => { if (optionName === "columns") { return 1 } else { return undefined } }) expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual({ ...fzfCommandDefaultOptions, "--preview-window": '"down:50%"' }) }) it("set --color options", async () => { ;(globalVariableSelector as jest.Mock).mockImplementation((variableName) => { if (variableName === "fzfPreviewDefaultFzfOptions") { return { "--reverse": true } } else if (variableName === "fzfPreviewFzfColorOption") { return "foo" } else { return undefined } }) expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual({ ...fzfCommandDefaultOptions, "--color": '"foo"' }) }) it("empty user options", async () => { expect( await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions: [], historyOption: emptyHistoryOption, }) ).toEqual(fzfCommandDefaultOptions) }) it("added user options", async () => { const userOptions = [{ optionName: "foo" }, { optionName: "bar", value: "bar" }] const convertedUserOptions = { bar: "bar", } const generatedOptions = await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions, historyOption: emptyHistoryOption, }) expect(generatedOptions).toEqual(expect.objectContaining(fzfCommandDefaultOptions)) expect(generatedOptions).toEqual({ ...fzfCommandDefaultOptions, ...convertedUserOptions }) }) describe("resume option", () => { it("resume query is undefined", async () => { const generatedOptions = await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions: [], resumeQuery: undefined, historyOption: emptyHistoryOption, }) expect(generatedOptions).toEqual(expect.objectContaining(fzfCommandDefaultOptions)) expect(generatedOptions).toEqual(fzfCommandDefaultOptions) }) it("resume query is exists", async () => { const generatedOptions = await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions: [], resumeQuery: "foo", historyOption: emptyHistoryOption, }) const queryOption = { "--query": '"foo"' } expect(generatedOptions).toEqual(expect.objectContaining(fzfCommandDefaultOptions)) expect(generatedOptions).toEqual({ ...fzfCommandDefaultOptions, ...queryOption }) }) }) describe("history option", () => { it("history is unused", async () => { const generatedOptions = await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions: [], historyOption: emptyHistoryOption, }) expect(generatedOptions).toEqual(expect.objectContaining(fzfCommandDefaultOptions)) expect(generatedOptions).toEqual(fzfCommandDefaultOptions) }) it("resume query is exists", async () => { const historyOption = { commandName: "Foo", historyDir: "bar", } as const const generatedOptions = await generateOptions({ fzfCommandDefaultOptions, defaultProcesses, userOptions: [], historyOption, }) const expandedHistoryOption = { "--history": '"bar/Foo"' } expect(generatedOptions).toEqual(expect.objectContaining(fzfCommandDefaultOptions)) expect(generatedOptions).toEqual({ ...fzfCommandDefaultOptions, ...expandedHistoryOption }) }) }) }) ================================================ FILE: src/fzf/option/generator.ts ================================================ import expandTilde from "expand-tilde" import { isObject } from "lodash" import { PREVIEW_WINDOW_LAYOUT_CHANGE_SIZE } from "@/const/fzf-option" import { globalVariableSelector, vimOptionsSelector } from "@/module/selector/vim-variable" import { pluginGetVar } from "@/plugin" import type { AddFzfArg, CustomProcessesVimVariable, FzfOptions, Processes, ResumeQuery, UserProcesses } from "@/type" const defaultBind = [ { key: "ctrl-d", action: "preview-half-page-down", }, { key: "ctrl-u", action: "preview-half-page-up", }, { key: "?", action: "toggle-preview", }, ] as const const defaultOptions: FzfOptions = { "--ansi": true, // alt-enter is workaround "--expect": ["alt-enter"], "--bind": defaultBind, "--with-nth": '"2.."', "--no-separator": true, } as const const getUserDefaultOptions = (): FzfOptions => { const userDefaultOptions = globalVariableSelector("fzfPreviewDefaultFzfOptions") if (!isObject(userDefaultOptions) || Array.isArray(userDefaultOptions)) { throw new Error("g:fzf_preview_default_fzf_options must be dictionary variable.") } return Object.fromEntries( Object.entries(userDefaultOptions).map(([k, v]) => { if (typeof v === "string") { return [k, `"${v}"`] } return [k, v] }) ) } const isCustomProcessesVimVariable = ( variable: unknown, userProcesses: UserProcesses ): variable is CustomProcessesVimVariable => { if (userProcesses.type !== "custom_processes_variable") { return false } return isObject(variable) && isObject((variable as CustomProcessesVimVariable)[userProcesses.value]) } const getExpectFromDefaultProcesses = (defaultProcesses: Processes): FzfOptions => { return { "--expect": defaultProcesses.map(({ key }) => key).filter((key) => key !== "enter") } } // eslint-disable-next-line complexity const getPreviewWindowOption = (fzfCommandDefaultOptions: FzfOptions): FzfOptions => { const defaultPreviewWindowOption = fzfCommandDefaultOptions["--preview-window"] != null && typeof fzfCommandDefaultOptions["--preview-window"] === "string" ? `${fzfCommandDefaultOptions["--preview-window"]}` : null const previewWindowOptionVimValue = globalVariableSelector("fzfPreviewFzfPreviewWindowOption") if (previewWindowOptionVimValue != null && previewWindowOptionVimValue !== "") { if (defaultPreviewWindowOption != null) { return { "--preview-window": `"${defaultPreviewWindowOption}:${previewWindowOptionVimValue as string}"`, } } else { return { "--preview-window": `"${previewWindowOptionVimValue as string}"`, } } } const columns = vimOptionsSelector("columns") if (columns < PREVIEW_WINDOW_LAYOUT_CHANGE_SIZE) { if (defaultPreviewWindowOption != null) { return { "--preview-window": `"${defaultPreviewWindowOption}:down:50%"` } } else { return { "--preview-window": '"down:50%"' } } } else if (defaultPreviewWindowOption != null) { return { "--preview-window": defaultPreviewWindowOption } } else { return {} } } const getPreviewKeyBindings = (): FzfOptions => { const previewKeyBindings = globalVariableSelector("fzfPreviewPreviewKeyBindings") return previewKeyBindings == null || previewKeyBindings === "" ? {} : { "--bind": `"${previewKeyBindings as string}"` } } const getColorOption = (): FzfOptions => { const colorOptionVimValue = globalVariableSelector("fzfPreviewFzfColorOption") return colorOptionVimValue == null || colorOptionVimValue === "" ? {} : { "--color": `"${colorOptionVimValue as string}"` } } const getExpectFromUserProcesses = async (userProcesses: UserProcesses | undefined): Promise => { if (userProcesses == null) { return {} } if (userProcesses.type === "global_variable") { const userProcessesGlobalVariable = await pluginGetVar(userProcesses.value) if (isObject(userProcessesGlobalVariable)) { return { "--expect": Object.entries(userProcessesGlobalVariable) .map(([key]) => key) .filter((key) => key !== "enter"), } } } if (userProcesses.type === "custom_processes_variable") { const userProcessesCustomVariable = globalVariableSelector("fzfPreviewCustomProcesses") if (isCustomProcessesVimVariable(userProcessesCustomVariable, userProcesses)) { return { "--expect": Object.entries(userProcessesCustomVariable[userProcesses.value]) .map(([key]) => key) .filter((key) => key !== "enter"), } } } throw new Error("--processes must be dictionary variable") } type OptionsArgs = { fzfCommandDefaultOptions: FzfOptions dynamicOptions?: FzfOptions defaultProcesses: Processes userProcesses?: UserProcesses userOptions: ReadonlyArray historyOption: { commandName: string historyDir: string | false } resumeQuery?: ResumeQuery } export const generateOptions = async ({ fzfCommandDefaultOptions, dynamicOptions, defaultProcesses, userProcesses, userOptions, historyOption: { commandName, historyDir }, resumeQuery, }: OptionsArgs): Promise => { const historyOption: FzfOptions = typeof historyDir === "string" ? { "--history": `"${expandTilde(historyDir)}/${commandName}"` } : {} const resumeQueryOption: FzfOptions = resumeQuery == null ? {} : { "--query": `"${resumeQuery}"` } const fzfCommandOptions = { ...defaultOptions, ...getUserDefaultOptions(), ...fzfCommandDefaultOptions, ...dynamicOptions, ...getExpectFromDefaultProcesses(defaultProcesses), ...getPreviewWindowOption(fzfCommandDefaultOptions), ...getPreviewKeyBindings(), ...getColorOption(), ...(await getExpectFromUserProcesses(userProcesses)), ...historyOption, ...resumeQueryOption, } userOptions.forEach((userOption) => { fzfCommandOptions[userOption.optionName] = userOption.value }) return fzfCommandOptions } ================================================ FILE: src/fzf/process/command-palette.ts ================================================ import { editCommandPaletteConsumer, execCommandPaletteConsumer } from "@/fzf/process/consumer/command-palette" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createCommandPaletteProcess = createProcessCreator("command-palette") export const commandPaletteProcesses: Processes = [ createCommandPaletteProcess("enter", execCommandPaletteConsumer), createCommandPaletteProcess("ctrl-e", editCommandPaletteConsumer), ] ================================================ FILE: src/fzf/process/consumer/command-palette.ts ================================================ import { editVimCommand, execVimCommand } from "@/connector/vim-command" import { createSingleLineConsumer } from "@/fzf/process/consumer" export const execCommandPaletteConsumer = createSingleLineConsumer(async (data) => { if (data.type !== "command-palette") { throw new Error(`Unexpected data type: ${data.type}`) } await execVimCommand(data.name) }) export const editCommandPaletteConsumer = createSingleLineConsumer(async (data) => { if (data.type !== "command-palette") { throw new Error(`Unexpected data type: ${data.type}`) } await editVimCommand(data.name) }) ================================================ FILE: src/fzf/process/consumer/git-action.ts ================================================ import { gitCommit, gitFetch, gitPull, gitPush } from "@/connector/git" import { chainFzfCommand, createSingleLineConsumer } from "@/fzf/process/consumer" import { gitConfigModule } from "@/module/git-config" import { dispatch } from "@/store" import { unreachable } from "@/util/type" // eslint-disable-next-line complexity export const execGitActionConsumer = createSingleLineConsumer(async (data) => { if (data.type !== "git-actions") { throw new Error(`Unexpected data type: ${data.type}`) } switch (data.action) { case "status": { await chainFzfCommand("FzfPreviewGitStatus") break } case "branch": { await chainFzfCommand("FzfPreviewGitBranches") break } case "log": { await chainFzfCommand("FzfPreviewGitLogs") break } case "current-log": { await chainFzfCommand("FzfPreviewGitCurrentLogs") break } case "stash": { await chainFzfCommand("FzfPreviewGitStashes") break } case "reflog": { await chainFzfCommand("FzfPreviewGitReflogs") break } case "commit": { await gitCommit() break } case "commit --amend": { await gitCommit({ name: "--amend" }) break } case "commit --amend --no-edit": { await gitCommit({ name: "--amend --no-edit" }) break } case "push": { await gitPush() break } case "push --force": { await gitPush("--force") break } case "fetch": { await gitFetch() break } case "pull": { await gitPull() break } case "toggle --no-verify": { dispatch(gitConfigModule.actions.toggleNoVerify()) await chainFzfCommand("FzfPreviewGitActions") break } case "header": { break } default: { unreachable(data) } } }) ================================================ FILE: src/fzf/process/consumer/git-branch-action.ts ================================================ import { gitDeleteBranch, gitDiff, gitMerge, gitRebase, gitRebaseInteractive, gitRenameBranch, gitReset, gitSwitch, gitYank, } from "@/connector/git" import { chainFzfCommand, createSingleLineConsumer } from "@/fzf/process/consumer" import { unreachable } from "@/util/type" // eslint-disable-next-line complexity export const execGitBranchActionConsumer = createSingleLineConsumer(async (data) => { if (data.type !== "git-branch-actions") { throw new Error(`Unexpected data type: ${data.type}`) } const branches = data.branches.filter((branch) => branch !== "") if (branches.length === 0) { throw new Error("Branches must be more then one") } switch (data.action) { case "switch": { if (data.branches.length > 1) { throw new Error("Branch must be one") } await gitSwitch(data.branches[0]) await chainFzfCommand("FzfPreviewGitBranches") break } case "reset": { if (data.branches.length > 1) { throw new Error("branches must be one") } await gitReset(data.branches[0]) await chainFzfCommand("FzfPreviewGitBranches") break } case "reset --soft": { if (data.branches.length > 1) { throw new Error("branches must be one") } await gitReset(data.branches[0], "--soft") await chainFzfCommand("FzfPreviewGitBranches") break } case "reset --hard": { if (data.branches.length > 1) { throw new Error("branches must be one") } await gitReset(data.branches[0], "--hard") await chainFzfCommand("FzfPreviewGitBranches") break } case "diff": { if (data.branches.length > 2) { throw new Error("Branch must be one or two") } await gitDiff(data.branches[0], data.branches[1]) break } case "merge": { if (data.branches.length > 1) { throw new Error("Branch must be one") } await gitMerge(data.branches[0]) await chainFzfCommand("FzfPreviewGitBranches") break } case "merge --no-ff": { if (data.branches.length > 1) { throw new Error("Branch must be one") } await gitMerge(data.branches[0], "--no-ff") await chainFzfCommand("FzfPreviewGitBranches") break } case "rebase": { if (data.branches.length > 1) { throw new Error("Branch must be one") } await gitRebase(data.branches[0]) await chainFzfCommand("FzfPreviewGitBranches") break } case "rebase --interactive": { if (data.branches.length > 1) { throw new Error("Branch must be one") } await gitRebaseInteractive(data.branches[0]) break } case "delete": { for (const branch of data.branches) { // eslint-disable-next-line no-await-in-loop await gitDeleteBranch(branch) } await chainFzfCommand("FzfPreviewGitBranches") break } case "delete --force": { for (const branch of data.branches) { // eslint-disable-next-line no-await-in-loop await gitDeleteBranch(branch, { name: "--force" }) } await chainFzfCommand("FzfPreviewGitBranches") break } case "rename": { if (data.branches.length > 1) { throw new Error("Branch must be one") } await gitRenameBranch(data.branches[0]) await chainFzfCommand("FzfPreviewGitBranches") break } case "yank": { if (data.branches.length > 1) { throw new Error("Branch must be one") } await gitYank(data.branches[0]) await chainFzfCommand("FzfPreviewGitBranches") break } case "header": { break } default: { unreachable(data) } } }) ================================================ FILE: src/fzf/process/consumer/git-branch.ts ================================================ import { chainFzfCommand, createBulkLineConsumer } from "@/fzf/process/consumer" import type { GitBranchData } from "@/type" export const chainGitBranchActionsConsumer = createBulkLineConsumer(async (dataList) => { const gitBranchData = dataList.filter((data): data is GitBranchData => data.type === "git-branch") await chainFzfCommand("FzfPreviewGitBranchActions", { gitBranches: gitBranchData }) }) ================================================ FILE: src/fzf/process/consumer/git-log-action.ts ================================================ import { gitCommit, gitDiff, gitRebaseInteractive, gitReset, gitShow, gitSwitch, gitYank } from "@/connector/git" import { chainFzfCommand, createSingleLineConsumer } from "@/fzf/process/consumer" import type { FzfCommandName } from "@/type" import { unreachable } from "@/util/type" // eslint-disable-next-line complexity export const execGitLogActionConsumer = createSingleLineConsumer(async (data) => { if (data.type !== "git-log-actions") { throw new Error(`Unexpected data type: ${data.type}`) } if (data.hashes.length === 0) { throw new Error("Hashes must be more then one") } const nextCommand: FzfCommandName = data.isCurrentFile ? "FzfPreviewGitCurrentLogs" : "FzfPreviewGitLogs" switch (data.action) { case "show": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitShow(data.hashes[0]) break } case "diff": { if (data.hashes.length > 2) { throw new Error("Hashes must be one or two") } await gitDiff(data.hashes[0], data.hashes[1]) break } case "reset": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitReset(data.hashes[0]) await chainFzfCommand(nextCommand) break } case "reset-soft": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitReset(data.hashes[0], "--soft") await chainFzfCommand(nextCommand) break } case "reset-hard": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitReset(data.hashes[0], "--hard") await chainFzfCommand(nextCommand) break } case "switch": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitSwitch(data.hashes[0]) await chainFzfCommand(nextCommand) break } case "commit --squash": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitCommit({ name: "--squash", hash: data.hashes[0] }) break } case "commit --fixup": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitCommit({ name: "--fixup", hash: data.hashes[0] }) await chainFzfCommand(nextCommand) break } case "rebase --interactive": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitRebaseInteractive(data.hashes[0]) break } case "yank": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitYank(data.hashes[0]) await chainFzfCommand(nextCommand) break } case "header": { break } default: { unreachable(data) } } }) ================================================ FILE: src/fzf/process/consumer/git-log.ts ================================================ import { gitShow } from "@/connector/git" import { vimEchoMessage } from "@/connector/util" import { chainFzfCommand, createBulkLineConsumer, createSingleLineConsumer } from "@/fzf/process/consumer" import type { GitLogData } from "@/type" export const gitShowConsumer = createSingleLineConsumer(async (data) => { if (data.type !== "git-log" && data.type !== "git-reflog") { throw new Error(`Unexpected data type: ${data.type}`) } await gitShow(data.hash) await vimEchoMessage(`git show ${data.hash}`) }) export const chainGitLogActionsConsumer = createBulkLineConsumer(async (dataList) => { const gitLogData = dataList.filter((data): data is GitLogData => data.type === "git-log") await chainFzfCommand("FzfPreviewGitLogActions", { gitLogs: gitLogData }) }) ================================================ FILE: src/fzf/process/consumer/git-reflog-action.ts ================================================ import { gitDiff, gitReset, gitShow, gitSwitch, gitYank } from "@/connector/git" import { chainFzfCommand, createSingleLineConsumer } from "@/fzf/process/consumer" import { unreachable } from "@/util/type" // eslint-disable-next-line complexity export const execGitReflogActionConsumer = createSingleLineConsumer(async (data) => { if (data.type !== "git-reflog-actions") { throw new Error(`Unexpected data type: ${data.type}`) } if (data.hashes.length === 0) { throw new Error("Reflogs must be more then one") } switch (data.action) { case "show": { if (data.hashes.length !== 1) { throw new Error("Reflogs must be one") } await gitShow(data.hashes[0]) break } case "diff": { if (data.hashes.length !== 1 && data.hashes.length !== 2) { throw new Error("Reflogs must be one or two") } await gitDiff(data.hashes[0], data.hashes[1]) break } case "reset": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitReset(data.hashes[0]) await chainFzfCommand("FzfPreviewGitReflogs") break } case "reset-soft": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitReset(data.hashes[0], "--soft") await chainFzfCommand("FzfPreviewGitReflogs") break } case "reset-hard": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitReset(data.hashes[0], "--hard") await chainFzfCommand("FzfPreviewGitReflogs") break } case "switch": { if (data.hashes.length > 1) { throw new Error("Hashes must be one") } await gitSwitch(data.hashes[0]) await chainFzfCommand("FzfPreviewGitReflogs") break } case "yank": { if (data.hashes.length !== 1) { throw new Error("Reflogs must be one") } await gitYank(data.hashes[0]) await chainFzfCommand("FzfPreviewGitReflogs") break } case "header": { break } default: { unreachable(data) } } }) ================================================ FILE: src/fzf/process/consumer/git-reflog.ts ================================================ import { chainFzfCommand, createBulkLineConsumer } from "@/fzf/process/consumer" import type { GitReflogData } from "@/type" export const chainGitReflogActionsConsumer = createBulkLineConsumer(async (dataList) => { const gitReflogData = dataList.filter((data): data is GitReflogData => data.type === "git-reflog") await chainFzfCommand("FzfPreviewGitReflogActions", { gitReflogs: gitReflogData }) }) ================================================ FILE: src/fzf/process/consumer/git-stash-action.ts ================================================ import { gitDiff, gitShow, gitStashApply, gitStashDrop, gitStashPop, gitYank } from "@/connector/git" import { chainFzfCommand, createSingleLineConsumer } from "@/fzf/process/consumer" import { unreachable } from "@/util/type" // eslint-disable-next-line complexity export const execGitStashActionConsumer = createSingleLineConsumer(async (data) => { if (data.type !== "git-stash-actions") { throw new Error(`Unexpected data type: ${data.type}`) } const stashes = data.names.filter((stash) => stash !== "") if (stashes.length === 0) { throw new Error("Stashes must be more then one") } switch (data.action) { case "show": { if (data.hashes.length > 1) { throw new Error("Stashes must be one") } await gitShow(data.hashes[0]) break } case "diff": { if (data.hashes.length > 2) { throw new Error("Stashes must be one or two") } await gitDiff(data.hashes[0], data.hashes[1]) break } case "apply": { if (data.hashes.length > 1) { throw new Error("Stashes must be one") } await gitStashApply(data.names[0]) await chainFzfCommand("FzfPreviewGitStashes") break } case "pop": { if (data.hashes.length > 1) { throw new Error("Stashes must be one") } await gitStashPop(data.names[0]) await chainFzfCommand("FzfPreviewGitStashes") break } case "drop": { for (const name of data.names) { // eslint-disable-next-line no-await-in-loop await gitStashDrop(name) } await chainFzfCommand("FzfPreviewGitStashes") break } case "yank": { if (data.hashes.length > 1) { throw new Error("Stashes must be one") } await gitYank(data.hashes[0]) await chainFzfCommand("FzfPreviewGitStashes") break } case "header": { break } default: { unreachable(data) } } }) ================================================ FILE: src/fzf/process/consumer/git-stash.ts ================================================ import { gitShow, gitStashCreate } from "@/connector/git" import { vimEchoMessage } from "@/connector/util" import { chainFzfCommand, createBulkLineConsumer } from "@/fzf/process/consumer" import type { GitStashData } from "@/type" export const chainGitStashActionsConsumer = createBulkLineConsumer(async (dataList) => { const gitStashData = dataList.filter((data): data is GitStashData => data.type === "git-stash") await chainFzfCommand("FzfPreviewGitStashActions", { gitStashes: gitStashData }) }) export const gitStashDefaultConsumer = createBulkLineConsumer(async (dataList) => { if (dataList.length > 1) { throw new Error("The git stash show should not be multiple lines.") } const data = dataList[0] if (data.type !== "git-stash") { throw new Error(`Unexpected data type: ${data.type}`) } if (data.isCreate === false) { await gitShow(data.hash) await vimEchoMessage(`git show ${data.hash}`) } else { await gitStashCreate() await chainFzfCommand("FzfPreviewGitStashes") } }) ================================================ FILE: src/fzf/process/consumer/git-status-action.ts ================================================ import { gitAdd, gitAddIntentToAdd, gitChaperon, gitPatch, gitReset, gitResetIntentToAdd, gitRestore, } from "@/connector/git" import { chainFzfCommand, createSingleLineConsumer } from "@/fzf/process/consumer" import { unreachable } from "@/util/type" // eslint-disable-next-line complexity export const execGitStatusActionConsumer = createSingleLineConsumer(async (data) => { if (data.type !== "git-status-actions") { throw new Error(`Unexpected data type: ${data.type}`) } if (data.files.length === 0) { throw new Error("Files must be more then one") } switch (data.action) { case "add": { for (const file of data.files) { // eslint-disable-next-line no-await-in-loop await gitAdd(file) } await chainFzfCommand("FzfPreviewGitStatus") break } case "reset": { for (const file of data.files) { // eslint-disable-next-line no-await-in-loop await gitReset(file) } await chainFzfCommand("FzfPreviewGitStatus") break } case "patch": { for (const file of data.files) { // eslint-disable-next-line no-await-in-loop await gitPatch(file) } break } case "restore": { for (const file of data.files) { // eslint-disable-next-line no-await-in-loop await gitRestore(file) } await chainFzfCommand("FzfPreviewGitStatus") break } case "chaperon": { for (const file of data.files) { // eslint-disable-next-line no-await-in-loop await gitChaperon(file) } break } case "add --intent-to-add": { for (const file of data.files) { // eslint-disable-next-line no-await-in-loop await gitAddIntentToAdd(file) } await chainFzfCommand("FzfPreviewGitStatus") break } case "reset --intent-to-add": { for (const file of data.files) { // eslint-disable-next-line no-await-in-loop await gitResetIntentToAdd(file) } await chainFzfCommand("FzfPreviewGitStatus") break } case "header": { break } default: { unreachable(data) } } }) ================================================ FILE: src/fzf/process/consumer/git-status.ts ================================================ import { gitAdd, gitCommit, gitReset } from "@/connector/git" import { vimEchoMessage } from "@/connector/util" import { chainFzfCommand, createBulkLineConsumer } from "@/fzf/process/consumer" import type { GitStatusData } from "@/type" export const chainGitStatusActionsConsumer = createBulkLineConsumer(async (dataList) => { const gitStatusDataList = dataList.filter((data): data is GitStatusData => data.type === "git-status") await chainFzfCommand("FzfPreviewGitStatusActions", { gitStatusDataList }) }) export const gitAddConsumer = createBulkLineConsumer(async (dataList) => { const gitDataList = dataList.filter((data): data is GitStatusData => data.type === "git-status") for (const data of gitDataList) { // eslint-disable-next-line no-await-in-loop await gitAdd(data.file) } await vimEchoMessage(`git add ${gitDataList.map((data) => data.file).join(" ")}`) await chainFzfCommand("FzfPreviewGitStatus") }) export const gitResetConsumer = createBulkLineConsumer(async (dataList) => { const gitDataList = dataList.filter((data): data is GitStatusData => data.type === "git-status") for (const data of gitDataList) { // eslint-disable-next-line no-await-in-loop await gitReset(data.file) } await vimEchoMessage(`git reset ${gitDataList.map((data) => data.file).join(" ")}`) await chainFzfCommand("FzfPreviewGitStatus") }) export const gitCommitConsumer = createBulkLineConsumer(async (_) => { await gitCommit() }) ================================================ FILE: src/fzf/process/consumer/git.ts ================================================ import { gitCreateBranch, gitSwitch } from "@/connector/git" import { vimEchoMessage } from "@/connector/util" import { chainFzfCommand, createBulkLineConsumer } from "@/fzf/process/consumer" export const chainGitActionsConsumer = createBulkLineConsumer(async (_) => { await chainFzfCommand("FzfPreviewGitActions") }) export const chainGitStatusConsumer = createBulkLineConsumer(async (_) => { await chainFzfCommand("FzfPreviewGitStatus") }) export const chainGitBranchesConsumer = createBulkLineConsumer(async (_) => { await chainFzfCommand("FzfPreviewGitBranches") }) export const chainGitStashesConsumer = createBulkLineConsumer(async (_) => { await chainFzfCommand("FzfPreviewGitStashes") }) export const chainGitReflogsConsumer = createBulkLineConsumer(async (_) => { await chainFzfCommand("FzfPreviewGitReflogs") }) export const chainGitLogsConsumer = createBulkLineConsumer(async (dataList) => { if (dataList[0].type === "git-log-actions" && dataList[0].isCurrentFile === true) { await chainFzfCommand("FzfPreviewGitCurrentLogs") } else if (dataList[0].type === "git-log-actions" && dataList[0].isCurrentFile === false) { await chainFzfCommand("FzfPreviewGitLogs") } }) export const gitSwitchConsumer = createBulkLineConsumer(async (dataList) => { if (dataList.length > 1) { throw new Error("The git switch should not be multiple lines.") } const data = dataList[0] switch (data.type) { case "git-branch": { if (data.isCreate === false) { await gitSwitch(data.name) await vimEchoMessage(`git switch ${data.name}`) } else { await gitCreateBranch() } break } case "git-log": { await gitSwitch(data.hash) await vimEchoMessage(`git switch ${data.hash}`) break } default: { throw new Error(`Unexpected data type: ${data.type}`) } } await chainFzfCommand("FzfPreviewGitBranches") }) ================================================ FILE: src/fzf/process/consumer/index.ts ================================================ import { v4 as uuidv4 } from "uuid" import { execFzfCommand } from "@/connector/fzf" import { sessionModule } from "@/module/session" import { dispatch } from "@/store" import type { CallbackLine, FzfCommandName, ResourceData, Session } from "@/type" export const decodeLine = (callbackLine: CallbackLine): ResourceData => { return JSON.parse(decodeURIComponent(callbackLine.trim().split(" ")[0])) as ResourceData } export const createSingleLineConsumer = (consume: (data: ResourceData) => Promise) => ({ consume, kind: "single", } as const) export const createBulkLineConsumer = (consume: (dataList: ReadonlyArray) => Promise) => ({ consume, kind: "bulk", } as const) export const chainFzfCommand = async (fzfCommandName: FzfCommandName, session?: Session): Promise => { if (session == null) { await execFzfCommand(fzfCommandName, { clearSession: true }) } else { const sessionToken = uuidv4() dispatch(sessionModule.actions.setSession({ session, sessionToken })) await execFzfCommand(fzfCommandName, { sessionToken }) } } ================================================ FILE: src/fzf/process/consumer/open-buffer.ts ================================================ import { deleteBuffer } from "@/connector/buffers" import { openFile } from "@/connector/open-file" import { createSingleLineConsumer } from "@/fzf/process/consumer" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { OpenCommand, OpenFile, SingleLineConsumer } from "@/type" const convertOpenCommand = (openCommand: OpenCommand): OpenCommand => { if (openCommand === "edit" && globalVariableSelector("fzfPreviewBuffersJump") !== 0) { return "drop" } return openCommand } const createOpenBufferConsumer = (openCommand: OpenCommand) => createSingleLineConsumer(async (data) => { if (data.type !== "buffer") { throw new Error(`Unexpected data type: ${data.type}`) } const openFileFormat: OpenFile = { openCommand: convertOpenCommand(openCommand), file: data.file, } await openFile(openFileFormat) }) export const createDeleteBufferConsumer = (): SingleLineConsumer => createSingleLineConsumer(async (data) => { if (data.type !== "buffer") { throw new Error(`Unexpected data type: ${data.type}`) } await deleteBuffer(data.bufnr.toString()) }) export const editConsumer = createOpenBufferConsumer("edit") export const splitConsumer = createOpenBufferConsumer("split") export const vsplitConsumer = createOpenBufferConsumer("vsplit") export const tabeditConsumer = createOpenBufferConsumer("tabedit") export const dropConsumer = createOpenBufferConsumer("drop") export const deleteBufferConsumer = createDeleteBufferConsumer() ================================================ FILE: src/fzf/process/consumer/open-bufnr.ts ================================================ import { openBufnr } from "@/connector/open-bufnr" import { createSingleLineConsumer } from "@/fzf/process/consumer" import { createDeleteBufferConsumer } from "@/fzf/process/consumer/open-buffer" import type { OpenCommand } from "@/type" const createOpenBufnrConsumer = (openCommand: OpenCommand) => createSingleLineConsumer(async (data) => { if (data.type !== "buffer") { throw new Error(`Unexpected data type: ${data.type}`) } await openBufnr(openCommand, data.bufnr.toString()) }) export const editBufnrConsumer = createOpenBufnrConsumer("edit") export const splitBufnrConsumer = createOpenBufnrConsumer("split") export const vsplitBufnrConsumer = createOpenBufnrConsumer("vsplit") export const tabeditBufnrConsumer = createOpenBufnrConsumer("tabedit") export const deleteBufnrConsumer = createDeleteBufferConsumer() ================================================ FILE: src/fzf/process/consumer/open-file.test.ts ================================================ import { exportQuickFix, openFile } from "@/connector/open-file" import { editConsumer, exportQuickfixConsumer, splitConsumer } from "@/fzf/process/consumer/open-file" import { executeCommandSelector } from "@/module/selector/execute-command" import type { FileData, LineData } from "@/type" jest.mock("@/connector/open-file") jest.mock("@/module/selector/execute-command") describe("open file consumer", () => { describe("open command", () => { describe("with only filename", () => { it("edit open file consumer", async () => { const data: FileData = { command: "FzfPreviewProjectFiles", type: "file", file: "foo.txt", } await editConsumer.consume(data) expect(openFile).toHaveBeenCalledWith({ openCommand: "edit", file: "foo.txt" }) }) it("split open file consumer", async () => { const data: FileData = { command: "FzfPreviewProjectFiles", type: "file", file: "foo.txt", } await splitConsumer.consume(data) expect(openFile).toHaveBeenCalledWith({ openCommand: "split", file: "foo.txt" }) }) }) it("with filename and line number", async () => { const data: LineData = { command: "FzfPreviewProjectGrep", type: "line", file: "foo.txt", lineNumber: 10, text: "", } await editConsumer.consume(data) expect(openFile).toHaveBeenCalledWith({ openCommand: "edit", file: "foo.txt", lineNumber: 10 }) }) it("with filename, line number and text", async () => { const data: LineData = { command: "FzfPreviewProjectGrep", type: "line", file: "foo.txt", lineNumber: 10, text: "bar", } await editConsumer.consume(data) expect(openFile).toHaveBeenCalledWith({ openCommand: "edit", file: "foo.txt", lineNumber: 10 }) }) }) describe("export quickfix", () => { it("with only filename", async () => { ;(executeCommandSelector as jest.Mock).mockImplementation(() => ({ commandName: "fooCommand" })) const dataList: ReadonlyArray = [ { command: "FzfPreviewProjectFiles", type: "file", file: "foo.txt", }, { command: "FzfPreviewProjectFiles", type: "file", file: "bar.txt", }, ] await exportQuickfixConsumer.consume(dataList) expect(exportQuickFix).toHaveBeenCalledWith([{ filename: "foo.txt" }, { filename: "bar.txt" }], { title: "fooCommand", }) }) it("with filename, line number and text", async () => { ;(executeCommandSelector as jest.Mock).mockImplementation(() => ({ commandName: "fooCommand" })) const dataList: ReadonlyArray = [ { command: "FzfPreviewProjectGrep", type: "line", file: "foo.txt", text: "", lineNumber: 10, }, { command: "FzfPreviewProjectGrep", type: "line", file: "bar.txt", text: "foobar", lineNumber: 20, }, ] await exportQuickfixConsumer.consume(dataList) expect(exportQuickFix).toHaveBeenCalledWith( [ { filename: "foo.txt", lnum: 10, text: "" }, { filename: "bar.txt", lnum: 20, text: "foobar" }, ], { title: "fooCommand" } ) }) }) }) ================================================ FILE: src/fzf/process/consumer/open-file.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { exportQuickFix, openFile } from "@/connector/open-file" import { createBulkLineConsumer, createSingleLineConsumer } from "@/fzf/process/consumer" import { executeCommandSelector } from "@/module/selector/execute-command" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { ExportQuickFix, OpenCommand } from "@/type" const convertOpenCommand = (openCommand: OpenCommand): OpenCommand => { if (openCommand === "edit" && globalVariableSelector("fzfPreviewBuffersJump") !== 0) { return "drop" } return openCommand } const createOpenFileConsumer = (openCommand: OpenCommand, opt?: { setTagStack?: boolean }) => createSingleLineConsumer(async (data) => { const command = convertOpenCommand(openCommand) switch (data.type) { case "file": { await openFile({ openCommand: command, file: data.file, }) break } case "buffer": { await openFile({ openCommand: command, file: data.file, }) break } case "line": { await openFile({ openCommand: command, file: data.file, lineNumber: data.lineNumber, setTagStack: opt?.setTagStack, }) break } case "git-status": { await openFile({ openCommand: command, file: data.file, }) break } default: { throw new Error(`Unexpected data type: ${data.type}`) } } }) export const editConsumer = createOpenFileConsumer("edit") export const editWithTagStackConsumer = createOpenFileConsumer("edit", { setTagStack: true }) export const splitConsumer = createOpenFileConsumer("split") export const vsplitConsumer = createOpenFileConsumer("vsplit") export const tabeditConsumer = createOpenFileConsumer("tabedit") export const dropConsumer = createOpenFileConsumer("drop") export const exportQuickfixConsumer = createBulkLineConsumer(async (dataList) => { const quickFixList: ReadonlyDeep> = dataList.map((data) => { switch (data.type) { case "file": { return { filename: data.file, } } case "buffer": { return { filename: data.file, } } case "line": { return { filename: data.file, lnum: data.lineNumber, text: data.text, } } case "git-status": { return { filename: data.file, } } default: { throw new Error(`Unexpected data type: ${data.type}`) } } }) const title = executeCommandSelector().commandName as string await exportQuickFix(quickFixList, { title }) }) ================================================ FILE: src/fzf/process/consumer/open-pr.ts ================================================ import { createSingleLineConsumer } from "@/fzf/process/consumer" import { execSyncCommand } from "@/system/command" // eslint-disable-next-line @typescript-eslint/require-await export const openPr = createSingleLineConsumer(async (data) => { if (data.type === "git-pr" && data.prNumber != null) { execSyncCommand(`gh pr view --web ${data.prNumber}`) } else { throw new Error("Data has no PR") } }) ================================================ FILE: src/fzf/process/consumer/register.ts ================================================ import { pasteRegister as pasteVimRegister,setRegister as setVimRegister } from "@/connector/register" import { createSingleLineConsumer } from "@/fzf/process/consumer" export const setRegister = createSingleLineConsumer(async (data) => { if (data.type !== "register") { throw new Error(`Unexpected data type: ${data.type}`) } await setVimRegister(data.text, data.option) }) export const pasteRegister = createSingleLineConsumer(async (data) => { if (data.type !== "register") { throw new Error(`Unexpected data type: ${data.type}`) } await pasteVimRegister(data.text, data.option) }) ================================================ FILE: src/fzf/process/git-action.ts ================================================ import { execGitActionConsumer } from "@/fzf/process/consumer/git-action" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitActionProcess = createProcessCreator("git-action") export const gitActionProcesses: Processes = [createGitActionProcess("enter", execGitActionConsumer)] ================================================ FILE: src/fzf/process/git-branch-action.ts ================================================ import { chainGitBranchesConsumer } from "@/fzf/process/consumer/git" import { execGitBranchActionConsumer } from "@/fzf/process/consumer/git-branch-action" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitBranchActionProcess = createProcessCreator("git-branch-actions") export const gitBranchActionProcesses: Processes = [ createGitBranchActionProcess("enter", execGitBranchActionConsumer), createGitBranchActionProcess("<", chainGitBranchesConsumer), ] ================================================ FILE: src/fzf/process/git-branch.ts ================================================ import { chainGitActionsConsumer, chainGitStatusConsumer, gitSwitchConsumer } from "@/fzf/process/consumer/git" import { chainGitBranchActionsConsumer } from "@/fzf/process/consumer/git-branch" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitBranchProcess = createProcessCreator("git-branch") export const gitBranchProcesses: Processes = [ createGitBranchProcess("enter", gitSwitchConsumer), createGitBranchProcess("ctrl-s", chainGitStatusConsumer), createGitBranchProcess("<", chainGitActionsConsumer), createGitBranchProcess(">", chainGitBranchActionsConsumer), ] ================================================ FILE: src/fzf/process/git-log-action.ts ================================================ import { chainGitLogsConsumer } from "@/fzf/process/consumer/git" import { execGitLogActionConsumer } from "@/fzf/process/consumer/git-log-action" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitLogActionProcess = createProcessCreator("git-log-actions") export const gitLogActionProcesses: Processes = [ createGitLogActionProcess("enter", execGitLogActionConsumer), createGitLogActionProcess("<", chainGitLogsConsumer), ] ================================================ FILE: src/fzf/process/git-log.ts ================================================ import { chainGitActionsConsumer, chainGitStatusConsumer } from "@/fzf/process/consumer/git" import { chainGitLogActionsConsumer, gitShowConsumer } from "@/fzf/process/consumer/git-log" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitLogProcess = createProcessCreator("git-log") export const gitLogProcesses: Processes = [ createGitLogProcess("enter", gitShowConsumer), createGitLogProcess("ctrl-s", chainGitStatusConsumer), createGitLogProcess("<", chainGitActionsConsumer), createGitLogProcess(">", chainGitLogActionsConsumer), ] ================================================ FILE: src/fzf/process/git-reflog-action.ts ================================================ import { chainGitReflogsConsumer } from "@/fzf/process/consumer/git" import { execGitReflogActionConsumer } from "@/fzf/process/consumer/git-reflog-action" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitReflogActionProcess = createProcessCreator("git-reflog-actions") export const gitReflogActionProcesses: Processes = [ createGitReflogActionProcess("enter", execGitReflogActionConsumer), createGitReflogActionProcess("<", chainGitReflogsConsumer), ] ================================================ FILE: src/fzf/process/git-reflog.ts ================================================ import { chainGitActionsConsumer, chainGitStatusConsumer } from "@/fzf/process/consumer/git" import { gitShowConsumer } from "@/fzf/process/consumer/git-log" import { chainGitReflogActionsConsumer } from "@/fzf/process/consumer/git-reflog" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitReflogProcess = createProcessCreator("git-reflog") export const gitReflogProcesses: Processes = [ createGitReflogProcess("enter", gitShowConsumer), createGitReflogProcess("ctrl-s", chainGitStatusConsumer), createGitReflogProcess("<", chainGitActionsConsumer), createGitReflogProcess(">", chainGitReflogActionsConsumer), ] ================================================ FILE: src/fzf/process/git-stash-action.ts ================================================ import { chainGitStashesConsumer } from "@/fzf/process/consumer/git" import { execGitStashActionConsumer } from "@/fzf/process/consumer/git-stash-action" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitStashActionProcess = createProcessCreator("git-stash-actions") export const gitStashActionProcesses: Processes = [ createGitStashActionProcess("enter", execGitStashActionConsumer), createGitStashActionProcess("<", chainGitStashesConsumer), ] ================================================ FILE: src/fzf/process/git-stash.ts ================================================ import { chainGitActionsConsumer, chainGitStatusConsumer } from "@/fzf/process/consumer/git" import { chainGitStashActionsConsumer, gitStashDefaultConsumer } from "@/fzf/process/consumer/git-stash" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitStashProcess = createProcessCreator("git-stash") export const gitStashProcesses: Processes = [ createGitStashProcess("enter", gitStashDefaultConsumer), createGitStashProcess("ctrl-s", chainGitStatusConsumer), createGitStashProcess("<", chainGitActionsConsumer), createGitStashProcess(">", chainGitStashActionsConsumer), ] ================================================ FILE: src/fzf/process/git-status-action.ts ================================================ import { chainGitStatusConsumer } from "@/fzf/process/consumer/git" import { execGitStatusActionConsumer } from "@/fzf/process/consumer/git-status-action" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitStatusActionProcess = createProcessCreator("git-status-actions") export const gitStatusActionProcesses: Processes = [ createGitStatusActionProcess("enter", execGitStatusActionConsumer), createGitStatusActionProcess("<", chainGitStatusConsumer), ] ================================================ FILE: src/fzf/process/git-status.ts ================================================ import { chainGitActionsConsumer } from "@/fzf/process/consumer/git" import { chainGitStatusActionsConsumer, gitAddConsumer, gitCommitConsumer, gitResetConsumer, } from "@/fzf/process/consumer/git-status" import { dropConsumer, editConsumer, splitConsumer, tabeditConsumer, vsplitConsumer, } from "@/fzf/process/consumer/open-file" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createGitStatusProcess = createProcessCreator("git-status") export const gitStatusProcesses: Processes = [ createGitStatusProcess("enter", editConsumer), createGitStatusProcess("ctrl-x", splitConsumer), createGitStatusProcess("ctrl-v", vsplitConsumer), createGitStatusProcess("ctrl-t", tabeditConsumer), createGitStatusProcess("ctrl-o", dropConsumer), createGitStatusProcess("ctrl-a", gitAddConsumer), createGitStatusProcess("ctrl-r", gitResetConsumer), createGitStatusProcess("ctrl-c", gitCommitConsumer), createGitStatusProcess("<", chainGitActionsConsumer), createGitStatusProcess(">", chainGitStatusActionsConsumer), ] ================================================ FILE: src/fzf/process/index.ts ================================================ import { commandPaletteProcesses } from "@/fzf/process/command-palette" import { decodeLine } from "@/fzf/process/consumer" import { gitActionProcesses } from "@/fzf/process/git-action" import { gitBranchProcesses } from "@/fzf/process/git-branch" import { gitBranchActionProcesses } from "@/fzf/process/git-branch-action" import { gitLogProcesses } from "@/fzf/process/git-log" import { gitLogActionProcesses } from "@/fzf/process/git-log-action" import { gitReflogProcesses } from "@/fzf/process/git-reflog" import { gitReflogActionProcesses } from "@/fzf/process/git-reflog-action" import { gitStashProcesses } from "@/fzf/process/git-stash" import { gitStashActionProcesses } from "@/fzf/process/git-stash-action" import { gitStatusProcesses } from "@/fzf/process/git-status" import { gitStatusActionProcesses } from "@/fzf/process/git-status-action" import { openBufferProcesses } from "@/fzf/process/open-buffer" import { openBufnrProcesses } from "@/fzf/process/open-bufnr" import { openFileProcesses, openFileWithTagStackProcesses } from "@/fzf/process/open-file" import { openPrProcesses } from "@/fzf/process/open-pr" import { registerProcesses } from "@/fzf/process/register" import { syncVimVariable } from "@/plugin/sync-vim-variable" import type { CallbackLines, Process, ProcessesDefinition } from "@/type" export const processesDefinition: ProcessesDefinition = [ { name: "open-file", processes: openFileProcesses, }, { name: "open-file-with-tag-stack", processes: openFileWithTagStackProcesses, }, { name: "open-buffer", processes: openBufferProcesses, }, { name: "open-bufnr", processes: openBufnrProcesses, }, { name: "command-palette", processes: commandPaletteProcesses, }, { name: "git-action", processes: gitActionProcesses, }, { name: "git-status", processes: gitStatusProcesses, }, { name: "git-status-actions", processes: gitStatusActionProcesses, }, { name: "git-branch", processes: gitBranchProcesses, }, { name: "git-branch-actions", processes: gitBranchActionProcesses, }, { name: "git-log", processes: gitLogProcesses, }, { name: "git-log-actions", processes: gitLogActionProcesses, }, { name: "git-stash", processes: gitStashProcesses, }, { name: "git-stash-actions", processes: gitStashActionProcesses, }, { name: "git-reflog", processes: gitReflogProcesses, }, { name: "git-reflog-actions", processes: gitReflogActionProcesses, }, { name: "register", processes: registerProcesses, }, { name: "open-pr", processes: openPrProcesses, }, ] export const executeProcess = async (lines: CallbackLines, process: Process): Promise => { await syncVimVariable() await process.execute(lines.map((line) => decodeLine(line))) } ================================================ FILE: src/fzf/process/open-buffer.ts ================================================ import { deleteBufferConsumer, dropConsumer, editConsumer, splitConsumer, tabeditConsumer, vsplitConsumer, } from "@/fzf/process/consumer/open-buffer" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createOpenBufferProcess = createProcessCreator("open-buffer") export const openBufferProcesses: Processes = [ createOpenBufferProcess("enter", editConsumer), createOpenBufferProcess("ctrl-x", splitConsumer), createOpenBufferProcess("ctrl-v", vsplitConsumer), createOpenBufferProcess("ctrl-t", tabeditConsumer), createOpenBufferProcess("ctrl-o", dropConsumer), createOpenBufferProcess("ctrl-q", deleteBufferConsumer), ] ================================================ FILE: src/fzf/process/open-bufnr.ts ================================================ import { deleteBufnrConsumer, editBufnrConsumer, splitBufnrConsumer, tabeditBufnrConsumer, vsplitBufnrConsumer, } from "@/fzf/process/consumer/open-bufnr" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createOpenBufnrProcess = createProcessCreator("open-bufnr") export const openBufnrProcesses: Processes = [ createOpenBufnrProcess("enter", editBufnrConsumer), createOpenBufnrProcess("ctrl-x", splitBufnrConsumer), createOpenBufnrProcess("ctrl-v", vsplitBufnrConsumer), createOpenBufnrProcess("ctrl-t", tabeditBufnrConsumer), createOpenBufnrProcess("ctrl-q", deleteBufnrConsumer), ] ================================================ FILE: src/fzf/process/open-file.ts ================================================ import type { ReadonlyDeep } from "type-fest" import { dropConsumer, editConsumer, editWithTagStackConsumer, exportQuickfixConsumer, splitConsumer, tabeditConsumer, vsplitConsumer, } from "@/fzf/process/consumer/open-file" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createOpenFileProcess = createProcessCreator("open-file") export const openFileProcesses: ReadonlyDeep = [ createOpenFileProcess("enter", editConsumer), createOpenFileProcess("ctrl-x", splitConsumer), createOpenFileProcess("ctrl-v", vsplitConsumer), createOpenFileProcess("ctrl-t", tabeditConsumer), createOpenFileProcess("ctrl-o", dropConsumer), createOpenFileProcess("ctrl-q", exportQuickfixConsumer), ] const createOpenFileWithTagStackProcess = createProcessCreator("open-file-with-tag-stack") export const openFileWithTagStackProcesses: ReadonlyDeep = [ createOpenFileWithTagStackProcess("enter", editWithTagStackConsumer), createOpenFileWithTagStackProcess("ctrl-x", splitConsumer), createOpenFileWithTagStackProcess("ctrl-v", vsplitConsumer), createOpenFileWithTagStackProcess("ctrl-t", tabeditConsumer), createOpenFileWithTagStackProcess("ctrl-o", dropConsumer), createOpenFileWithTagStackProcess("ctrl-q", exportQuickfixConsumer), ] ================================================ FILE: src/fzf/process/open-pr.ts ================================================ import { openPr } from "@/fzf/process/consumer/open-pr" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createOpenPrProcess = createProcessCreator("open-pr") export const openPrProcesses: Processes = [createOpenPrProcess("enter", openPr)] ================================================ FILE: src/fzf/process/process.ts ================================================ import { createProcessFunctionName } from "@/fzf/util" import type { CreateProcessCreator, ResourceData } from "@/type" import { unreachable } from "@/util/type" export const createProcessCreator: CreateProcessCreator = (processesName) => (expectKey, lineConsumer) => ({ name: createProcessFunctionName(processesName, expectKey), key: expectKey, execute: async (dataList: ReadonlyArray) => { switch (lineConsumer.kind) { case "single": { for (const data of dataList) { // eslint-disable-next-line no-await-in-loop await lineConsumer.consume(data) } break } case "bulk": { await lineConsumer.consume(dataList) break } default: { unreachable(lineConsumer) } } }, }) ================================================ FILE: src/fzf/process/register.ts ================================================ import { pasteRegister, setRegister } from "@/fzf/process/consumer/register" import { createProcessCreator } from "@/fzf/process/process" import type { Processes } from "@/type" const createRegisterProcess = createProcessCreator("register") export const registerProcesses: Processes = [ createRegisterProcess("enter", setRegister), createRegisterProcess("ctrl-y", pasteRegister), ] ================================================ FILE: src/fzf/resource/all-buffers.ts ================================================ import { getAllBuffers } from "@/connector/buffers" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const SPACER = " " // Colorize after align // If it contains ansi escape, it will not align well const colorizeArrayedBuffer = (list: ReadonlyArray): string => { const [bufnr, fileName] = list return [colorize(bufnr, "blue"), colorizeFile(fileName)].join(SPACER).trim() } export const allBuffers = async (_args: SourceFuncArgs): Promise => { const buffers = await getAllBuffers() const displayLines = alignLists(buffers.map((buffer) => [`[${buffer.bufnr}]`, buffer.fileName])).map((list) => colorizeArrayedBuffer(list) ) return { type: "json", lines: buffers.map((buffer, i) => ({ data: { command: "FzfPreviewAllBuffers", type: "buffer", file: buffer.fileName, bufnr: buffer.bufnr, }, displayText: displayLines[i], })), } } export const allBuffersDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"AllBuffers> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/blame-pr.ts ================================================ import { globalVariableSelector } from "@/module/selector/vim-variable" import { execSyncCommand } from "@/system/command" import { existsFileAsync, getCurrentFilePath } from "@/system/file" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const blamePr = async (_args: SourceFuncArgs): Promise => { if (!(await existsFileAsync(await getCurrentFilePath()))) { return { type: "json", lines: [], } } const file = await getCurrentFilePath() const openPrCommand = globalVariableSelector("fzfPreviewBlamePrCommand") as string const { stdout, stderr, status } = execSyncCommand(`${openPrCommand} ${file}`) if (stderr !== "" || status !== 0) { throw new Error(`Failed open pr command: "${openPrCommand}"`) } const lines = stdout.split("\n").filter((line) => line !== "") const resourceLines: ResourceLines = lines.map((line) => { const result = /^PR\s#(?\d+)/.exec(line) if (result?.groups != null) { return { data: { command: "FzfPreviewBlamePR", type: "git-pr", prNumber: Number(result.groups.prNumber), }, displayText: line, } } return { data: { command: "FzfPreviewBlamePR", type: "git-pr", }, displayText: line, } }) return { type: "json", lines: resourceLines, } } export const blamePrDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Blame PR> "', "--multi": true, "--preview": '"gh pr view {3}"', }) ================================================ FILE: src/fzf/resource/bookmarks.ts ================================================ import { getBookmarks } from "@/connector/bookmarks" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const bookmarks = async (_args: SourceFuncArgs): Promise => { const resourceLines: ResourceLines = (await getBookmarks()).map(({ file, line, text, comment }) => { return { data: { command: "FzfPreviewBookmarks", type: "line", file, text, lineNumber: Number(line), }, displayText: `${colorizeFile(file)}:${colorize(line, "green")}: ${text}:${comment}`, } }) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const bookmarksDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Bookmarks> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/buffer-lines.ts ================================================ import { getBuffers } from "@/connector/buffers" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import { readFile } from "@/system/file" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const bufferLines = async (_args: SourceFuncArgs): Promise => { const buffers = await getBuffers() const lines = buffers.reduce((acc: ResourceLines, cur) => { const fileLines = readFile(cur.fileName) .split("\n") .map((line, lineIndex) => ({ lineNumber: lineIndex + 1, text: line, })) .slice(0, -1) const resourceLines: ResourceLines = fileLines.map((line) => ({ data: { command: "FzfPreviewBufferLines", type: "line", file: cur.fileName, text: line.text, lineNumber: line.lineNumber, }, displayText: `${colorizeFile(cur.fileName)}:${colorize(line.lineNumber.toString(), "green")}:${line.text}`, })) return [...acc, ...resourceLines] }, []) return { type: "json", lines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const bufferLinesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"BufferLines> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/buffer-tags.ts ================================================ import { globalVariableSelector } from "@/module/selector/vim-variable" import { existsFileAsync, getCurrentFilePath } from "@/system/file" import { getBufferTags } from "@/system/tags" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const SPACER = " " export const bufferTags = async (_args: SourceFuncArgs): Promise => { if (!(await existsFileAsync(await getCurrentFilePath()))) { return { type: "json", lines: [], } } const file = await getCurrentFilePath() const parsedTags = getBufferTags(file) .map((line) => /^(?[^\t]+)\t(?\S+)\t(?\d+);"\t(?.+)/.exec(line)) .filter((match): match is RegExpExecArray => match != null && "groups" in match) .map((match) => { return match.groups as { tagName: string tagFile: string lineNumber: string tagField: string } }) .sort((a, b) => Number(a.lineNumber) - Number(b.lineNumber)) const currentFile = await getCurrentFilePath() const textList = alignLists( parsedTags.map(({ lineNumber, tagName, tagField }) => [lineNumber, tagName, tagField]) ).map((tagArray) => tagArray.join(SPACER).trim()) return { type: "json", lines: parsedTags.map((tag, i) => ({ data: { command: "FzfPreviewBufferTags", type: "line", file: currentFile, text: textList[i], lineNumber: Number(tag.lineNumber), }, displayText: textList[i], })), } } const previewCommand = async () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} ${await getCurrentFilePath()}:{2..}"` } export const bufferTagsDefaultOptions = async (): Promise => ({ "--prompt": '"BufferTags> "', "--multi": true, "--preview": await previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/buffer-vista.ts ================================================ import type { VistaBufferTag } from "@/connector/vista" import { getVistaBufferCtags } from "@/connector/vista" import { globalVariableSelector } from "@/module/selector/vim-variable" import { getCurrentFilePath } from "@/system/file" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const SPACER = " " const vistaBufferTagToArray = ({ lineNumber, kind, text, line }: VistaBufferTag) => [ lineNumber.toString(), `[${kind}]`, text, line, ] export const vistaBufferCtags = async (_args: SourceFuncArgs): Promise => { const tags = await getVistaBufferCtags() const displayTextList = alignLists( [...tags].sort((a, b) => a.lineNumber - b.lineNumber).map((tag) => vistaBufferTagToArray(tag)) ).map((tag) => tag.join(SPACER).trim()) const currentFile = await getCurrentFilePath() const resourceLines: ResourceLines = tags.map((tag, i) => ({ data: { command: "FzfPreviewVistaBufferCtags", type: "line", file: currentFile, lineNumber: tag.lineNumber, text: displayTextList[i], }, displayText: `${displayTextList[i]}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = async () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} '${await getCurrentFilePath()}:{2}'"` } export const vistaBufferCtagsDefaultOptions = async (): Promise => ({ "--prompt": '"VistaBufferCtags> "', "--multi": true, "--preview": await previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/buffers.ts ================================================ import { getAlternateBuffer, getCurrentBuffer, getOtherBuffers } from "@/connector/buffers" import { isGitDirectory } from "@/connector/util" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import { existsFileAsync, getCurrentPath } from "@/system/file" import { readMruFile } from "@/system/mr" import { filterProjectEnabledFile } from "@/system/project" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs, VimBuffer } from "@/type" import { alignLists } from "@/util/align" import { asyncFilter } from "@/util/array" const bufferToArray = (buffer: VimBuffer) => { if (buffer.isCurrent === true) { return [`[${buffer.bufnr}] `, "%", `${buffer.isModified ? " [+] " : ""}`, ` ${buffer.fileName}`] } return [ `[${buffer.bufnr}] `, `${buffer.isAlternate ? "#" : ""}`, `${buffer.isModified ? " [+] " : ""}`, ` ${buffer.fileName}`, ] } // Colorize after align // If it contains ansi escape, it will not align well const colorizeArrayedBuffer = (list: ReadonlyArray): string => { const [bufnr, symbol, modified, fileName] = list if (symbol.includes("%")) { return list.join("").trim() } else { return [colorize(bufnr, "blue"), symbol, colorize(modified, "red"), colorizeFile(fileName)].join("").trim() } } const existsBuffer = async (buffer: VimBuffer): Promise => { return await existsFileAsync(buffer.fileName) } const getSimpleBuffers = async (options?: { ignoreCurrentBuffer: boolean }) => { const currentBuffer = await getCurrentBuffer() const alternateBuffer = await getAlternateBuffer() const otherBuffers = await getOtherBuffers() if (options?.ignoreCurrentBuffer != null) { return [alternateBuffer, ...otherBuffers] } return [currentBuffer, alternateBuffer, ...otherBuffers] } const getGitProjectBuffers = async (options?: { ignoreCurrentBuffer: boolean }) => { const currentBuffer = await getCurrentBuffer() const alternateBuffer = await getAlternateBuffer() const otherBuffers = await getOtherBuffers() const currentPath = await getCurrentPath() const mruFiles = await filterProjectEnabledFile(readMruFile(), currentPath) const sortedBuffers = mruFiles .map((file) => otherBuffers.find((buffer) => buffer.fileName === file)) .filter((buffer): buffer is VimBuffer => buffer != null) if (options?.ignoreCurrentBuffer != null) { return await asyncFilter(Array.from(new Set([alternateBuffer, ...sortedBuffers, ...otherBuffers])), (buffer) => existsBuffer(buffer) ) } return await asyncFilter( Array.from(new Set([currentBuffer, alternateBuffer, ...sortedBuffers, ...otherBuffers])), (buffer) => existsBuffer(buffer) ) } const createBuffers = (bufferList: ReadonlyArray, displayLines: ReadonlyArray): Resource => ({ type: "json", lines: bufferList.map((buffer, i) => ({ data: { command: "FzfPreviewBuffers", type: "buffer", file: buffer.fileName, bufnr: buffer.bufnr, }, displayText: displayLines[i], })), }) export const buffers = async (_args: SourceFuncArgs): Promise => { const bufferList = await getGitProjectBuffers() const displayLines = alignLists(bufferList.map((buffer) => bufferToArray(buffer))).map((list) => colorizeArrayedBuffer(list) ) return { ...createBuffers(bufferList, displayLines), ...{ options: { "--header-lines": (await existsBuffer(await getCurrentBuffer())) ? "1" : "0" } }, } } export const fileFormatBuffers = async (_args: SourceFuncArgs): Promise => { // TODO: sort with mru if (!(await isGitDirectory())) { const bufferList = await getSimpleBuffers({ ignoreCurrentBuffer: true }) const displayLines = bufferList.map((buffer) => buffer.fileName) return createBuffers(bufferList, displayLines) } const bufferList = await getGitProjectBuffers({ ignoreCurrentBuffer: true }) const displayLines = bufferList.map((buffer) => buffer.fileName) return createBuffers(bufferList, displayLines) } export const buffersDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Buffers> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/changes.ts ================================================ import { getChanges } from "@/connector/changes" import { globalVariableSelector } from "@/module/selector/vim-variable" import { getCurrentFilePath } from "@/system/file" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const SPACER = " " export const changes = async (_args: SourceFuncArgs): Promise => { const changeList = (await getChanges()).map((change) => { const result = /^(?\d+)\s(?.*)/.exec(change) if (result?.groups == null) { throw new Error(`Changes line is invalid: "${change}"`) } const { lineNumber, text } = result.groups return { lineNumber: Number(lineNumber), text } }) const currentFile = await getCurrentFilePath() const displayTextList = alignLists(changeList.map(({ lineNumber, text }) => [lineNumber.toString(), text])).map( (change) => change.join(SPACER).trim() ) const resourceLines: ResourceLines = changeList.map(({ lineNumber, text }, i) => { return { data: { command: "FzfPreviewChanges", type: "line", file: currentFile, text, lineNumber, }, displayText: displayTextList[i], } }) return { type: "json", lines: resourceLines, } } const previewCommand = async () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} ${await getCurrentFilePath()}:{2..}"` } export const changesDefaultOptions = async (): Promise => ({ "--prompt": '"Changes> "', "--multi": true, "--preview": await previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/coc/coc-current-diagnostics.ts ================================================ import { getCurrentDiagnostics } from "@/connector/coc" import { diagnosticToResourceLine } from "@/fzf/resource/coc/coc-diagnostics" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const cocCurrentDiagnostics = async (_args: SourceFuncArgs): Promise => { const diagnostics = await getCurrentDiagnostics() const resourceLines: ResourceLines = diagnostics.map((diagnostic) => diagnosticToResourceLine(diagnostic)) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const cocCurrentDiagnosticsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"CurrentDiagnostics> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/coc/coc-definitions.ts ================================================ import { getDefinition } from "@/connector/coc" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const cocDefinitions = async (_args: SourceFuncArgs): Promise => { const { definitions, symbol } = await getDefinition() const resourceLines: ResourceLines = definitions.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewCocDefinition", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, options: { "--header": `"[Symbol] ${symbol}"` }, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const cocDefinitionsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Definitions> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/coc/coc-diagnostics.ts ================================================ import { getDiagnostics } from "@/connector/coc" import { diagnosticToDisplayText } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { Diagnostic, FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs, } from "@/type" export const diagnosticToResourceLine = (diagnostic: Diagnostic): ResourceLine => { const { file, lineNumber, message } = diagnostic return { data: { command: "FzfPreviewCocDiagnostics", type: "line", file, lineNumber, text: message, }, displayText: diagnosticToDisplayText(diagnostic), } } export const cocDiagnostics = async (_args: SourceFuncArgs): Promise => { const diagnostics = await getDiagnostics() const resourceLines: ResourceLines = diagnostics.map((diagnostic) => diagnosticToResourceLine(diagnostic)) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const cocDiagnosticsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Diagnostics> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/coc/coc-implementations.ts ================================================ import { getImplementation } from "@/connector/coc" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const cocImplementations = async (_args: SourceFuncArgs): Promise => { const { implementations, symbol } = await getImplementation() const resourceLines: ResourceLines = implementations.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewCocImplementations", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, options: { "--header": `"[Symbol] ${symbol}"` }, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const cocImplementationsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Implementations> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/coc/coc-outline.ts ================================================ import { getOutline } from "@/connector/coc" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const colorizeOutline = (list: ReadonlyArray): string => { const [file, lineNumber, kind, text] = list return `${colorizeFile(file)}:${colorize(lineNumber, "green")} ${colorize(kind, "red")}${text}` } export const cocOutline = async (_args: SourceFuncArgs): Promise => { const outline = await getOutline() const displayLines = alignLists( outline.map(({ kind, label, file, lineNumber }) => [ `${file}`, `${lineNumber.toString()}:`, kind != null ? `[${kind}]` : "", ` ${label.split("\t")[0]}`, ]) ).map((line) => colorizeOutline(line)) const resourceLines: ResourceLines = outline.map(({ file, lineNumber, text }, i) => ({ data: { command: "FzfPreviewCocOutline", type: "line", file, text, lineNumber, }, displayText: displayLines[i], })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const cocOutlineDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Outline> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', "--nth": '"4.."', }) ================================================ FILE: src/fzf/resource/coc/coc-references.ts ================================================ import { getReferences } from "@/connector/coc" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const cocReferences = async (_args: SourceFuncArgs): Promise => { const { references, symbol } = await getReferences() const resourceLines: ResourceLines = references.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewCocReferences", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, options: { "--header": `"[Symbol] ${symbol}"` }, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const cocReferencesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"References> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/coc/coc-tsserver-source-definition.ts ================================================ import { getTsServerSourceDefinition } from "@/connector/coc" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const cocTsServerSourceDefinition = async (_args: SourceFuncArgs): Promise => { const { sourceDefinitions, symbol } = await getTsServerSourceDefinition() const resourceLines: ResourceLines = sourceDefinitions.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewCocTsServerSourceDefinition", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, options: { "--header": `"[Symbol] ${symbol}"` }, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const cocTsServerSourceDefinitionsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"SourceDefinitions> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/coc/coc-type-definitions.ts ================================================ import { getTypeDefinition } from "@/connector/coc" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const cocTypeDefinitions = async (_args: SourceFuncArgs): Promise => { const { typeDefinitions, symbol } = await getTypeDefinition() const resourceLines: ResourceLines = typeDefinitions.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewCocTypeDefinition", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, options: { "--header": `"[Symbol] ${symbol}"` }, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const cocTypeDefinitionsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"TypeDefinitions> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/coc/index.ts ================================================ export * from "@/fzf/resource/coc/coc-current-diagnostics" export * from "@/fzf/resource/coc/coc-diagnostics" export * from "@/fzf/resource/coc/coc-implementations" export * from "@/fzf/resource/coc/coc-references" export * from "@/fzf/resource/coc/coc-type-definitions" ================================================ FILE: src/fzf/resource/command-palette.ts ================================================ import { getVimCommandHistory, getVimCommands } from "@/connector/vim-command" import { colorize } from "@/fzf/syntax/colorize" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" import { uniqWith } from "@/util/uniq-with" export const commandPalette = async (_args: SourceFuncArgs): Promise => { const commands = await getVimCommands() const history = await getVimCommandHistory() return { type: "json", lines: uniqWith([...history, ...commands], (a, b) => a.name === b.name).map((command) => ({ data: { command: "FzfPreviewCommandPalette", type: "command-palette", name: command.name, }, displayText: command.number == null ? `${colorize("Command", "cyan")}: ${command.name}` : `${colorize("History", "magenta")} ${command.number}: ${command.name}`, })), } } export const commandPaletteDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"CommandPalette> "', "--header": '"C-e: Edit"', }) ================================================ FILE: src/fzf/resource/ctags.ts ================================================ import { getCtags } from "@/connector/tags" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" export const ctags = async (_args: SourceFuncArgs): Promise => { const tagList = await getCtags() return { type: "json", lines: tagList.map((tag) => ({ data: { command: "FzfPreviewCtags", type: "line", file: tag.file, text: `${tag.line} ${tag.name} ${tag.type} ${tag.file}`, lineNumber: Number(tag.line), }, displayText: `${tag.line} ${tag.name} ${tag.type} ${tag.file}`, })), } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} '{-1}:{2}'"` } export const ctagsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Ctags> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"2.."', }) ================================================ FILE: src/fzf/resource/directory-files.ts ================================================ import { execDirectoryFiles } from "@/connector/directory-files" import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import type { FzfCommandDefinitionDefaultOption, FzfCommandDynamicOption, Resource, SourceFuncArgs } from "@/type" export const directoryFiles = async ({ args }: SourceFuncArgs): Promise => { const arg = args[0] != null ? args[0] : "" const lines = (await execDirectoryFiles(arg)).filter((file) => file !== "" && !file.includes(" ")) const options: FzfCommandDynamicOption | undefined = arg ? { "--header": `"[Directory] ${arg}"` } : undefined return { type: "json", lines: lines.map((line) => ({ data: { command: "FzfPreviewDirectoryFiles", type: "file", file: line, }, displayText: colorizeFile(line), })), options, } } export const directoryFilesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"DirectoryFiles> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/files-from-resources.ts ================================================ import type { FILE_RESOURCES } from "@/const/fzf-option" import { fileFormatBuffers } from "@/fzf/resource/buffers" import { directoryFiles } from "@/fzf/resource/directory-files" import { gitFiles } from "@/fzf/resource/git-files" import { mruFiles } from "@/fzf/resource/mru" import { mrwFiles } from "@/fzf/resource/mrw" import { oldFiles } from "@/fzf/resource/oldfiles" import { projectFiles } from "@/fzf/resource/project-files" import { projectMruFiles } from "@/fzf/resource/project-mru" import { projectMrwFiles } from "@/fzf/resource/project-mrw" import { projectOldFiles } from "@/fzf/resource/project-oldfiles" import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import type { FileData, FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" import { uniqWith } from "@/util/uniq-with" type ResourceFunctions = { [key in typeof FILE_RESOURCES[number]]: (args: SourceFuncArgs) => Promise } const resourceFunctions: ResourceFunctions = { project: projectFiles, git: gitFiles, directory: directoryFiles, buffer: fileFormatBuffers, project_old: projectOldFiles, project_mru: projectMruFiles, project_mrw: projectMrwFiles, old: oldFiles, mru: mruFiles, mrw: mrwFiles, } export const filesFromResources = async (args: SourceFuncArgs): Promise => { const emptySourceFuncArgs = { args: [] } let lines: ResourceLines = [] for (const resource of args.args) { // eslint-disable-next-line no-await-in-loop const filesFromResource = await resourceFunctions[resource as typeof FILE_RESOURCES[number]](emptySourceFuncArgs) lines = [...lines, ...filesFromResource.lines] } const uniqLines = uniqWith(lines as Array, (line1, line2) => { if ( (line1.data.type === "file" || line1.data.type === "buffer") && (line2.data.type === "file" || line2.data.type === "buffer") ) { return line1.data.file === line2.data.file } return true }) return { type: "json", lines: uniqLines.map((line) => ({ data: { command: "FzfPreviewFromResources", type: "file", file: (line.data as FileData).file, }, displayText: colorizeFile((line.data as FileData).file), })), options: { "--header": `"[Resources] ${args.args.join(" ")}"` }, } } export const filesFromResourcesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"ResourceFrom> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/git-actions.ts ================================================ import { isGitDirectory } from "@/connector/util" import { GIT_ACTIONS } from "@/const/git" import { gitConfigSelector } from "@/module/selector/git-config" import { globalVariableSelector } from "@/module/selector/vim-variable" import { getCurrentFilePath } from "@/system/file" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" const createDisplayText = async (action: typeof GIT_ACTIONS[number]) => { const noVerify = gitConfigSelector("noVerify") if (action === "current-log") { return `${action}:${await getCurrentFilePath()}` } else if (/^commit|^push/.exec(action) && noVerify) { return `${action} --no-verify` } else { return action } } export const gitActions = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const displayTextList = await Promise.all( GIT_ACTIONS.map(async (action) => { return await createDisplayText(action) }) ) return { type: "json", lines: GIT_ACTIONS.map((action, i) => ({ data: { command: "FzfPreviewGitActions", type: "git-actions", action, }, displayText: displayTextList[i], })), } } export const gitActionsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"GitActions> "', "--preview": `"${globalVariableSelector("fzfPreviewScriptDir") as string}/git_actions_preview {2}"`, "--preview-window": '"down:50%"', }) ================================================ FILE: src/fzf/resource/git-branch-actions.ts ================================================ import { isGitDirectory } from "@/connector/util" import { GIT_BRANCH_ACTIONS } from "@/const/git" import { currentSessionSelector } from "@/module/selector/session" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs } from "@/type" export const gitBranchActions = async (_args: SourceFuncArgs): Promise => { const currentSession = currentSessionSelector() if (currentSession == null) { throw new Error("Not exists current session") } else if (currentSession.gitBranches == null) { throw new Error("Branch is not exists in current session") } if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const branches = currentSession.gitBranches const headers: ResourceLines = [ { data: { command: "FzfPreviewGitBranchActions", type: "git-branch-actions", action: "header", branches: [], }, displayText: "<: Back to git branch", }, { data: { command: "FzfPreviewGitBranchActions", type: "git-branch-actions", action: "header", branches: [], }, displayText: `Selected branch: ${branches.map((branch) => branch.name).join(" ")}`, }, ] const lines = [ ...headers, ...GIT_BRANCH_ACTIONS.map((action) => ({ data: { command: "FzfPreviewGitBranchActions", type: "git-branch-actions", action, branches: branches.map((branch) => branch.name), }, displayText: action, })), ] return { type: "json", lines, } } export const gitBranchActionsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"GitBranchActions> "', "--header-lines": "2", }) ================================================ FILE: src/fzf/resource/git-branches.ts ================================================ import stripAnsi from "strip-ansi" import { execGitBranch } from "@/connector/git" import { isGitDirectory } from "@/connector/util" import { GIT_BRANCH_PREVIEW_COMMAND } from "@/const/git" import { colorize } from "@/fzf/syntax/colorize" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const SPACER = " " export const gitBranches = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const branches = await execGitBranch() const displayLines = alignLists(branches.map(({ name, date, author }) => [name, date, author])).map((list) => list.join(SPACER).trim() ) const extraActions: ResourceLines = [ { data: { command: "FzfPreviewGitBranches", type: "git-branch", name: "", date: "", author: "", isCreate: true, }, displayText: colorize("\xA0\xA0Create branch", "green"), }, ] const lines = [ ...branches.map(({ name, date, author }, i) => ({ data: { command: "FzfPreviewGitBranches", type: "git-branch", name: stripAnsi(name).replace("* ", "").trim(), date: stripAnsi(date).trim(), author: stripAnsi(author).trim(), isCreate: false, }, displayText: displayLines[i], })), ...extraActions, ] return { type: "json", lines, } } export const gitBranchesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--multi": true, "--header": '"Enter: switch, C-s: git status, <: Back actions, >: Select action"', "--prompt": '"GitBranch> "', "--preview": `"${GIT_BRANCH_PREVIEW_COMMAND}"`, "--preview-window": '"down:50%"', }) ================================================ FILE: src/fzf/resource/git-files.ts ================================================ import { execGitFiles } from "@/connector/git" import { isGitDirectory } from "@/connector/util" import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" export const gitFiles = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const lines = (await execGitFiles()).filter((file) => file !== "" && !file.includes(" ")) return { type: "json", lines: lines.map((line) => ({ data: { command: "FzfPreviewGitFiles", type: "file", file: line, }, displayText: colorizeFile(line), })), } } export const gitFilesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"GitFiles> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/git-log-actions.ts ================================================ import { isGitDirectory } from "@/connector/util" import { GIT_LOG_ACTIONS } from "@/const/git" import { currentSessionSelector } from "@/module/selector/session" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs } from "@/type" export const gitLogActions = async (_args: SourceFuncArgs): Promise => { const currentSession = currentSessionSelector() if (currentSession == null) { throw new Error("Not exists current session") } else if (currentSession.gitLogs == null) { throw new Error("Logs is not exists in current session") } if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const logs = currentSession.gitLogs const headers: ResourceLines = [ { data: { command: "FzfPreviewGitLogActions", type: "git-log-actions", action: "header", hashes: [], isCurrentFile: false, }, displayText: "<: Back to git log", }, { data: { command: "FzfPreviewGitLogActions", type: "git-log-actions", action: "header", hashes: [], isCurrentFile: false, }, displayText: `Selected log: ${logs.map((log) => log.hash).join(" ")}`, }, ] const lines = [ ...headers, ...GIT_LOG_ACTIONS.map((action) => ({ data: { command: "FzfPreviewGitLogActions", type: "git-log-actions", action, hashes: logs.map((log) => log.hash), isCurrentFile: logs[0].isCurrentFile, }, displayText: action, })), ] return { type: "json", lines, } } export const gitLogActionsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"GitLogActions> "', "--header-lines": "2", }) ================================================ FILE: src/fzf/resource/git-logs.ts ================================================ import stripAnsi from "strip-ansi" import { execGitLog } from "@/connector/git" import { isGitDirectory } from "@/connector/util" import { GIT_LOG_PREVIEW_COMMAND } from "@/const/git" import type { FzfCommandDefinitionDefaultOption, GitLog, Resource, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const SPACER = " " const createResource = (logs: ReadonlyArray, isCurrentFile: boolean): Resource => { const displayLines = alignLists(logs.map(({ hash, date, author, comment }) => [hash, date, author, comment])).map( (list) => list.join(SPACER).trim() ) return { type: "json", lines: logs.map(({ hash, date, author, comment }, i) => ({ data: { command: "FzfPreviewGitLogs", type: "git-log", hash: stripAnsi(hash).trim(), date: stripAnsi(date).trim(), author: stripAnsi(author).trim(), comment: stripAnsi(comment).trim(), isCurrentFile, }, displayText: displayLines[i], })), } } export const gitLogs = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const logs = await execGitLog() return createResource(logs, false) } export const gitCurrentLogs = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const logs = await execGitLog({ currentFile: true }) return createResource(logs, true) } export const gitLogsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--multi": true, "--header": '"Enter: git show, C-s: git status, <: Back actions, >: Select action"', "--prompt": '"GitLog> "', "--preview": `"${GIT_LOG_PREVIEW_COMMAND}"`, "--preview-window": '"down:50%"', }) export const gitCurrentLogsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--multi": true, "--header": '"Enter: git show, C-s: git status, <: Back actions, >: Select action"', "--prompt": '"GitCurrentLog> "', "--preview": `"${GIT_LOG_PREVIEW_COMMAND}"`, "--preview-window": '"down:50%"', }) ================================================ FILE: src/fzf/resource/git-reflog-actions.ts ================================================ import { isGitDirectory } from "@/connector/util" import { GIT_REFLOG_ACTIONS } from "@/const/git" import { currentSessionSelector } from "@/module/selector/session" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs } from "@/type" export const gitReflogActions = async (_args: SourceFuncArgs): Promise => { const currentSession = currentSessionSelector() if (currentSession == null) { throw new Error("Not exists current session") } else if (currentSession.gitReflogs == null) { throw new Error("Reflogs is not exists in current session") } if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const reflogs = currentSession.gitReflogs const headers: ResourceLines = [ { data: { command: "FzfPreviewGitReflogActions", type: "git-reflog-actions", action: "header", names: [], hashes: [], }, displayText: "<: Back to git reflog", }, { data: { command: "FzfPreviewGitReflogActions", type: "git-reflog-actions", action: "header", names: [], hashes: [], }, displayText: `Selected reflog: ${reflogs.map((reflog) => reflog.name).join(" ")}`, }, ] const lines = [ ...headers, ...GIT_REFLOG_ACTIONS.map((action) => ({ data: { command: "FzfPreviewGitReflogActions", type: "git-reflog-actions", action, names: reflogs.map((reflog) => reflog.name), hashes: reflogs.map((reflog) => reflog.hash), }, displayText: action, })), ] return { type: "json", lines, } } export const gitReflogActionsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"GitReflogActions> "', "--header-lines": "2", }) ================================================ FILE: src/fzf/resource/git-reflogs.ts ================================================ import stripAnsi from "strip-ansi" import { execGitReflog } from "@/connector/git" import { isGitDirectory } from "@/connector/util" import { GIT_REFLOG_PREVIEW_COMMAND } from "@/const/git" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const SPACER = " " export const gitReflogs = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const reflogs = await execGitReflog() const displayLines = alignLists( reflogs.map(({ name, hash, date, author, comment }) => [name, hash, date, author, comment]) ).map((list) => list.join(SPACER).trim()) return { type: "json", lines: reflogs.map(({ name, hash, date, author, comment }, i) => ({ data: { command: "FzfPreviewGitReflogs", type: "git-reflog", name: stripAnsi(name).trim(), hash: stripAnsi(hash).trim(), date: stripAnsi(date).trim(), author: stripAnsi(author).trim(), comment: stripAnsi(comment).trim(), }, displayText: displayLines[i], })), } } export const gitReflogsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--multi": true, "--header": '"Enter: git show, C-s: git status, <: Back actions, >: Select action"', "--prompt": '"GitReflog> "', "--preview": `"${GIT_REFLOG_PREVIEW_COMMAND}"`, "--preview-window": '"down:50%"', }) ================================================ FILE: src/fzf/resource/git-stash-actions.ts ================================================ import { isGitDirectory } from "@/connector/util" import { GIT_STASH_ACTIONS } from "@/const/git" import { currentSessionSelector } from "@/module/selector/session" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs } from "@/type" export const gitStashActions = async (_args: SourceFuncArgs): Promise => { const currentSession = currentSessionSelector() if (currentSession == null) { throw new Error("Not exists current session") } else if (currentSession.gitStashes == null) { throw new Error("Stashes is not exists in current session") } if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const stashes = currentSession.gitStashes const headers: ResourceLines = [ { data: { command: "FzfPreviewGitStashActions", type: "git-stash-actions", action: "header", names: [], hashes: [], }, displayText: "<: Back to git stash", }, { data: { command: "FzfPreviewGitStashActions", type: "git-stash-actions", action: "header", names: [], hashes: [], }, displayText: `Selected stash: ${stashes.map((stash) => stash.name).join(" ")}`, }, ] const lines = [ ...headers, ...GIT_STASH_ACTIONS.map((action) => ({ data: { command: "FzfPreviewGitStashActions", type: "git-stash-actions", action, names: stashes.map((stash) => stash.name), hashes: stashes.map((stash) => stash.hash), }, displayText: action, })), ] return { type: "json", lines, } } export const gitStashActionsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"GitStashActions> "', "--header-lines": "2", }) ================================================ FILE: src/fzf/resource/git-stashes.ts ================================================ import stripAnsi from "strip-ansi" import { execGitStash } from "@/connector/git" import { isGitDirectory } from "@/connector/util" import { GIT_STASH_PREVIEW_COMMAND } from "@/const/git" import { colorize } from "@/fzf/syntax/colorize" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const SPACER = " " export const gitStashes = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const stashes = await execGitStash() const displayLines = alignLists( stashes.map(({ name, hash, date, author, comment }) => [name, hash, date, author, comment]) ).map((list) => list.join(SPACER).trim()) const extraActions: ResourceLines = [ { data: { command: "FzfPreviewGitStashes", type: "git-stash", name: "", hash: "", date: "", author: "", comment: "", isCreate: true, }, displayText: colorize("Create stash", "green"), }, ] const lines = [ ...stashes.map(({ name, hash, date, author, comment }, i) => ({ data: { command: "FzfPreviewGitStashes", type: "git-stash", name: stripAnsi(name).trim(), hash: stripAnsi(hash).trim(), date: stripAnsi(date).trim(), author: stripAnsi(author).trim(), comment: stripAnsi(comment).trim(), isCreate: false, }, displayText: displayLines[i], })), ...extraActions, ] return { type: "json", lines, } } export const gitStashesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--multi": true, "--header": '"Enter: git show, C-s: git status, <: Back actions, >: Select action"', "--prompt": '"GitStash> "', "--preview": `"${GIT_STASH_PREVIEW_COMMAND}"`, "--preview-window": '"down:50%"', }) ================================================ FILE: src/fzf/resource/git-status-actions.ts ================================================ import { isGitDirectory } from "@/connector/util" import { GIT_STATUS_ACTIONS } from "@/const/git" import { currentSessionSelector } from "@/module/selector/session" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs } from "@/type" export const gitStatusActions = async (_args: SourceFuncArgs): Promise => { const currentSession = currentSessionSelector() if (currentSession == null) { throw new Error("Not exists current session") } else if (currentSession.gitStatusDataList == null) { throw new Error("Selected git status file is not exists in current session") } if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const statusDataList = currentSession.gitStatusDataList const headers: ResourceLines = [ { data: { command: "FzfPreviewGitStatusActions", type: "git-status-actions", action: "header", files: [], }, displayText: "<: Back to git status", }, { data: { command: "FzfPreviewGitStatusActions", type: "git-status-actions", action: "header", files: [], }, displayText: `Selected file: ${statusDataList.map((data) => data.file).join(" ")}`, }, ] const lines = [ ...headers, ...GIT_STATUS_ACTIONS.map((action) => ({ data: { command: "FzfPreviewGitStatusActions", type: "git-status-actions", action, files: statusDataList.map((data) => data.file), }, displayText: action, })), ] return { type: "json", lines, } } export const gitStatusActionsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"GitStatusActions> "', "--header-lines": "2", }) ================================================ FILE: src/fzf/resource/git-status.ts ================================================ import stripAnsi from "strip-ansi" import { execGitStatus } from "@/connector/git" import { isGitDirectory } from "@/connector/util" import { colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs } from "@/type" export const gitStatus = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const statuses = await execGitStatus() const headers: ResourceLines = [ { data: { command: "FzfPreviewGitStatus", type: "git-status", action: "header", file: "", status: "", }, displayText: "C-a: git add, C-r: git reset, C-c: git commit", }, { data: { command: "FzfPreviewGitStatus", type: "git-status", action: "header", file: "", status: "", }, displayText: "<: Back actions, >: Select action", }, ] const lines = [ ...headers, ...statuses.map(({ file, status }) => { return { data: { command: "FzfPreviewGitStatus", type: "git-status", file, status: stripAnsi(status), }, displayText: `${status.replace(/^\s/, "\xA0")} ${colorizeFile(file)}`, } }), ] return { type: "json", lines, } } export const gitStatusDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"GitStatus> "', "--multi": true, "--preview": `'${globalVariableSelector("fzfPreviewGitStatusPreviewCommand") as string}'`, "--keep-right": true, "--header-lines": "2", }) ================================================ FILE: src/fzf/resource/grep.ts ================================================ import stripAnsi from "strip-ansi" import { execGrep } from "@/connector/grep" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" export const projectGrep = async (args: SourceFuncArgs): Promise => { const grepArgs = args.args.join(" ") const lines = await execGrep(grepArgs) return { type: "json", lines: lines.map((line) => { const [file, lineNumber, ...rest] = line.split(":") return { data: { command: "FzfPreviewProjectGrep", type: "line", file: stripAnsi(file), lineNumber: Number(stripAnsi(lineNumber)), text: stripAnsi(rest.join(":")), }, displayText: `${colorizeFile(file)}:${colorize(lineNumber, "green")}: ${rest.join(":")}`, } }), options: { "--header": `'[Grep from] ${grepArgs}'` }, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const projectGrepDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"ProjectGrep> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/index.ts ================================================ export * from "@/fzf/resource/all-buffers" export * from "@/fzf/resource/blame-pr" export * from "@/fzf/resource/bookmarks" export * from "@/fzf/resource/buffer-lines" export * from "@/fzf/resource/buffer-tags" export * from "@/fzf/resource/buffer-vista" export * from "@/fzf/resource/buffers" export * from "@/fzf/resource/changes" export * from "@/fzf/resource/command-palette" export * from "@/fzf/resource/ctags" export * from "@/fzf/resource/directory-files" export * from "@/fzf/resource/files-from-resources" export * from "@/fzf/resource/git-actions" export * from "@/fzf/resource/git-branch-actions" export * from "@/fzf/resource/git-branches" export * from "@/fzf/resource/git-files" export * from "@/fzf/resource/git-log-actions" export * from "@/fzf/resource/git-logs" export * from "@/fzf/resource/git-reflog-actions" export * from "@/fzf/resource/git-reflogs" export * from "@/fzf/resource/git-stash-actions" export * from "@/fzf/resource/git-stashes" export * from "@/fzf/resource/git-status" export * from "@/fzf/resource/git-status-actions" export * from "@/fzf/resource/grep" export * from "@/fzf/resource/jumps" export * from "@/fzf/resource/lines" export * from "@/fzf/resource/locationlist" export * from "@/fzf/resource/marks" export * from "@/fzf/resource/memolist" export * from "@/fzf/resource/memolist-grep" export * from "@/fzf/resource/mru" export * from "@/fzf/resource/mrw" export * from "@/fzf/resource/oldfiles" export * from "@/fzf/resource/project-files" export * from "@/fzf/resource/project-mru" export * from "@/fzf/resource/project-mrw" export * from "@/fzf/resource/project-oldfiles" export * from "@/fzf/resource/quickfix" export * from "@/fzf/resource/vista" export * from "@/fzf/resource/yankround" ================================================ FILE: src/fzf/resource/jumps.ts ================================================ import { getJumps } from "@/connector/jumps" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const jumps = async (_args: SourceFuncArgs): Promise => { const resourceLines: ResourceLines = (await getJumps()).map(({ file, line, text }) => { return { data: { command: "FzfPreviewJumps", type: "line", file, text, lineNumber: Number(line), }, displayText: `${colorizeFile(file)}:${colorize(line, "green")}:${text}`, } }) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const jumpsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Jumps> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/lines.ts ================================================ import stripAnsi from "strip-ansi" import { execLines } from "@/connector/lines" import { globalVariableSelector } from "@/module/selector/vim-variable" import { existsFileAsync, getCurrentFilePath } from "@/system/file" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" export const lines = async (_args: SourceFuncArgs): Promise => { const currentFile = await getCurrentFilePath() if (!(await existsFileAsync(currentFile))) { return { type: "json", lines: [], } } const lineList = await execLines(currentFile) return { type: "json", lines: lineList.map((line) => { const result = /^\s*(?\d+)\s(?.*)/.exec(stripAnsi(line)) if (result?.groups == null) { throw new Error(`Unexpected line format: ${line}`) } return { data: { command: "FzfPreviewLines", type: "line", file: currentFile, text: result.groups.text, lineNumber: Number(result.groups.lineNumber), }, displayText: line, } }), } } const previewCommand = async () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} ${await getCurrentFilePath()}:{2}"` } export const linesDefaultOptions = async (): Promise => ({ "--prompt": '"Lines> "', "--multi": true, "--preview": await previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/locationlist.ts ================================================ import { getLocationList } from "@/connector/quickfix-and-locationlist" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { parseQuickFixAndLocationListLine } from "@/fzf/util" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const locationList = async (_args: SourceFuncArgs): Promise => { const resourceLines: ResourceLines = (await getLocationList()).map((line) => { const { fileName, lineNumber, text } = parseQuickFixAndLocationListLine(line) return { data: { command: "FzfPreviewLocationList", type: "line", file: fileName, text, lineNumber, }, displayText: `${colorizeFile(fileName)}:${colorize(lineNumber.toString(), "green")}:${text}`, } }) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const locationListDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"LocationList> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/marks.ts ================================================ import { getMarks } from "@/connector/marks" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const marks = async (_args: SourceFuncArgs): Promise => { const resourceLines: ResourceLines = (await getMarks()).map(({ file, line, text }) => ({ data: { command: "FzfPreviewMarks", type: "line", file, text, lineNumber: Number(line), }, displayText: `${colorizeFile(file)}:${colorize(line, "green")}:${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const marksDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Marks> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/memolist-grep.ts ================================================ import stripAnsi from "strip-ansi" import { execMemoListGrep } from "@/connector/memolist" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" export const memoListGrep = async (args: SourceFuncArgs): Promise => { const grepArgs = args.args.join(" ") const lines = await execMemoListGrep(grepArgs) return { type: "json", lines: lines.map((line) => { const [file, lineNumber, ...rest] = line.split(":") return { data: { command: "FzfPreviewProjectGrep", type: "line", file: stripAnsi(file), lineNumber: Number(stripAnsi(lineNumber)), text: stripAnsi(rest.join(":")), }, displayText: `${colorizeFile(file)}:${colorize(lineNumber, "green")}: ${rest.join(":")}`, } }), options: { "--header": `'[Grep from] ${grepArgs}'` }, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const memoListGrepDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"MemoListGrep> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/memolist.ts ================================================ import { execMemoListFiles } from "@/connector/memolist" import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import type { FzfCommandDefinitionDefaultOption, Resource } from "@/type" export const memoList = async (): Promise => { const lines = await execMemoListFiles() return { type: "json", lines: lines.map((line) => ({ data: { command: "FzfPreviewMemoList", type: "file", file: line, }, displayText: colorizeFile(line), })), } } export const memoListDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"MemoList> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/mru.ts ================================================ import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import { existsFileAsync } from "@/system/file" import { readMruFile } from "@/system/mr" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" import { asyncFilter } from "@/util/array" export const mruFiles = async (_args: SourceFuncArgs): Promise => { const files = readMruFile() const existsFiles = await asyncFilter(files, (file) => existsFileAsync(file)) const resourceLines: ResourceLines = existsFiles.map((file) => ({ data: { command: "FzfPreviewMruFiles", type: "file", file, }, displayText: colorizeFile(file), })) return { type: "json", lines: resourceLines, } } export const mruFilesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"MruFiles> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/mrw.ts ================================================ import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import { existsFileAsync } from "@/system/file" import { readMrwFile } from "@/system/mr" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" import { asyncFilter } from "@/util/array" export const mrwFiles = async (_args: SourceFuncArgs): Promise => { const files = readMrwFile() const existsFiles = await asyncFilter(files, (file) => existsFileAsync(file)) const resourceLines: ResourceLines = existsFiles.map((file) => ({ data: { command: "FzfPreviewMrwFiles", type: "file", file, }, displayText: colorizeFile(file), })) return { type: "json", lines: resourceLines, } } export const mrwFilesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"MrwFiles> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/nvim-lsp-current-diagnostics.ts ================================================ import { getCurrentDiagnostics } from "@/connector/nvim-lsp" import { diagnosticToDisplayText } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { Diagnostic, FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs, } from "@/type" export const diagnosticToResourceLine = (diagnostic: Diagnostic): ResourceLine => { const { file, lineNumber, message } = diagnostic return { data: { command: "FzfPreviewNvimLspCurrentDiagnostics", type: "line", file, lineNumber, text: message, }, displayText: diagnosticToDisplayText(diagnostic), } } export const nvimLspCurrentDiagnostics = async (_args: SourceFuncArgs): Promise => { const { diagnostics } = await getCurrentDiagnostics() const resourceLines: ResourceLines = diagnostics.map((diagnostic) => diagnosticToResourceLine(diagnostic)) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const nvimLspCurrentDiagnosticsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"CurrentDiagnostics> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/nvim-lsp-definition.ts ================================================ import { getDefinition } from "@/connector/nvim-lsp" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const nvimLspDefinition = async (_args: SourceFuncArgs): Promise => { const { definition } = await getDefinition() const resourceLines: ResourceLines = definition.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewNvimLspDefinition", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const nvimLspDefinitionDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Definition> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/nvim-lsp-diagnostics.ts ================================================ import { getDiagnostics } from "@/connector/nvim-lsp" import { diagnosticToDisplayText } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { Diagnostic, FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs, } from "@/type" export const diagnosticToResourceLine = (diagnostic: Diagnostic): ResourceLine => { const { file, lineNumber, message } = diagnostic return { data: { command: "FzfPreviewNvimLspDiagnostics", type: "line", file, lineNumber, text: message, }, displayText: diagnosticToDisplayText(diagnostic), } } export const nvimLspDiagnostics = async (_args: SourceFuncArgs): Promise => { const { diagnostics } = await getDiagnostics() const resourceLines: ResourceLines = diagnostics.map((diagnostic) => diagnosticToResourceLine(diagnostic)) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const nvimLspDiagnosticsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Diagnostics> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/nvim-lsp-implementation.ts ================================================ import { getImplementation } from "@/connector/nvim-lsp" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const nvimLspImplementation = async (_args: SourceFuncArgs): Promise => { const { implementations } = await getImplementation() const resourceLines: ResourceLines = implementations.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewNvimLspImplementation", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const nvimLspImplementationDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Implementation> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/nvim-lsp-references.ts ================================================ import { getReferences } from "@/connector/nvim-lsp" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const nvimLspReferences = async (_args: SourceFuncArgs): Promise => { const { references } = await getReferences() const resourceLines: ResourceLines = references.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewNvimLspReferences", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const nvimLspReferencesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"References> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/nvim-lsp-type-definition.ts ================================================ import { getTypeDefinition } from "@/connector/nvim-lsp" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const nvimLspTypeDefinition = async (_args: SourceFuncArgs): Promise => { const { typeDefinition } = await getTypeDefinition() const resourceLines: ResourceLines = typeDefinition.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewNvimLspTypeDefinition", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const nvimLspTypeDefinitionDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"TypeDefinition> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/oldfiles.ts ================================================ import { getOldFiles } from "@/connector/old-files" import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import { existsFileAsync } from "@/system/file" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" import { asyncFilter } from "@/util/array" export const oldFiles = async (_args: SourceFuncArgs): Promise => { const files = await getOldFiles() const existsFiles = await asyncFilter(files, (file) => existsFileAsync(file)) const resourceLines: ResourceLines = existsFiles.map((file) => ({ data: { command: "FzfPreviewOldFiles", type: "file", file, }, displayText: colorizeFile(file), })) return { type: "json", lines: resourceLines, } } export const oldFilesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"OldFiles> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/project-files.ts ================================================ import { execProjectFiles } from "@/connector/project-files" import { isGitDirectory } from "@/connector/util" import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" export const projectFiles = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const lines = (await execProjectFiles()).filter((file) => file !== "" && !file.includes(" ")) return { type: "json", lines: lines.map((line) => ({ data: { command: "FzfPreviewProjectFiles", type: "file", file: line, }, displayText: colorizeFile(line), })), } } export const projectFilesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"ProjectFiles> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/project-mru.ts ================================================ import { isGitDirectory } from "@/connector/util" import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import { getCurrentFilePath, getCurrentPath } from "@/system/file" import { readMruFile } from "@/system/mr" import { filterProjectEnabledFile } from "@/system/project" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const projectMruFiles = async (_args: SourceFuncArgs): Promise => { const currentFile = await getCurrentFilePath() const currentPath = await getCurrentPath() if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const files = await filterProjectEnabledFile( readMruFile().filter((file) => file !== currentFile), currentPath ) const resourceLines: ResourceLines = files.map((file) => ({ data: { command: "FzfPreviewProjectMruFiles", type: "file", file, }, displayText: colorizeFile(file), })) return { type: "json", lines: resourceLines, } } export const projectMruFilesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"ProjectMruFiles> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/project-mrw.ts ================================================ import { isGitDirectory } from "@/connector/util" import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import { getCurrentFilePath, getCurrentPath } from "@/system/file" import { readMrwFile } from "@/system/mr" import { filterProjectEnabledFile } from "@/system/project" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const projectMrwFiles = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const currentFile = await getCurrentFilePath() const currentPath = await getCurrentPath() const files = await filterProjectEnabledFile( readMrwFile().filter((file) => file !== currentFile), currentPath ) const resourceLines: ResourceLines = files.map((file) => ({ data: { command: "FzfPreviewProjectMrwFiles", type: "file", file, }, displayText: colorizeFile(file), })) return { type: "json", lines: resourceLines, } } export const projectMrwFilesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"ProjectMrwFiles> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/project-oldfiles.ts ================================================ import { getOldFiles } from "@/connector/old-files" import { isGitDirectory } from "@/connector/util" import { colorizeFile } from "@/fzf/syntax/colorize" import { filePreviewCommand } from "@/fzf/util" import { getCurrentPath } from "@/system/file" import { filterProjectEnabledFile } from "@/system/project" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const projectOldFiles = async (_args: SourceFuncArgs): Promise => { if (!(await isGitDirectory())) { throw new Error("The current directory is not a git project") } const currentPath = await getCurrentPath() const files = await filterProjectEnabledFile(await getOldFiles(), currentPath) const resourceLines: ResourceLines = files.map((file) => ({ data: { command: "FzfPreviewProjectOldFiles", type: "file", file, }, displayText: colorizeFile(file), })) return { type: "json", lines: resourceLines, } } export const projectOldFilesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"ProjectOldFiles> "', "--multi": true, "--preview": filePreviewCommand(), "--keep-right": true, }) ================================================ FILE: src/fzf/resource/quickfix.ts ================================================ import { getQuickFix } from "@/connector/quickfix-and-locationlist" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { parseQuickFixAndLocationListLine } from "@/fzf/util" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const quickFix = async (_args: SourceFuncArgs): Promise => { const resourceLines: ResourceLines = (await getQuickFix()).map((line) => { const { fileName, lineNumber, text } = parseQuickFixAndLocationListLine(line) return { data: { command: "FzfPreviewQuickFix", type: "line", file: fileName, text, lineNumber, }, displayText: `${colorizeFile(fileName)}:${colorize(lineNumber.toString(), "green")}:${text}`, } }) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const quickFixDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"QuickFix> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/todo-comments.ts ================================================ import stripAnsi from "strip-ansi" import { execSearchTodoComments } from "@/connector/todo-comments" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource } from "@/type" export const todoComments = async (): Promise => { const lines = await execSearchTodoComments() return { type: "json", lines: lines.map((line) => { const [file, lineNumber, ...rest] = line.split(":") return { data: { command: "FzfPreviewTodoComments", type: "line", file: stripAnsi(file), lineNumber: Number(stripAnsi(lineNumber)), text: stripAnsi(rest.join(":")), }, displayText: `${colorizeFile(file)}:${colorize(lineNumber, "green")}: ${rest.join(":")}`, } }), } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const todoCommentsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"TodoComments> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/vim-help.ts ================================================ import stripAnsi from "strip-ansi" import { execHelpTags } from "@/connector/vim-help" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import { collapseHome } from "@/system/file" import type { FzfCommandDefinitionDefaultOption, Resource, SourceFuncArgs } from "@/type" export const grepHelp = async (args: SourceFuncArgs): Promise => { const helpTagsArgs = args.args.length > 0 ? args.args.join(" ") : "." const lines = await execHelpTags(helpTagsArgs) return { type: "json", lines: lines.map((line) => { const [file, lineNumber, ...rest] = line.split(":") return { data: { command: "FzfPreviewGrepHelp", type: "line", file: stripAnsi(file), lineNumber: Number(stripAnsi(lineNumber)), text: stripAnsi(rest.join(":")), }, displayText: `${colorizeFile(collapseHome(file.split("/").slice(-1).join("/")))}:${colorize( lineNumber, "green" )}: ${rest.join(":")}`, } }), options: { "--header": `'[Search from] ${helpTagsArgs}'` }, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const grepHelpDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"GrepHelp> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/vim-lsp-current-diagnostics.ts ================================================ import { getCurrentDiagnostics } from "@/connector/vim-lsp" import { diagnosticToResourceLine } from "@/fzf/resource/vim-lsp-diagnostics" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const vimLspCurrentDiagnostics = async (_args: SourceFuncArgs): Promise => { const diagnostics = await getCurrentDiagnostics() const resourceLines: ResourceLines = diagnostics.map((diagnostic) => diagnosticToResourceLine(diagnostic)) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const vimLspCurrentDiagnosticsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"CurrentDiagnostics> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/vim-lsp-definition.ts ================================================ import { getDefinition } from "@/connector/vim-lsp" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const vimLspDefinition = async (_args: SourceFuncArgs): Promise => { const { definitions } = await getDefinition() const resourceLines: ResourceLines = definitions.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewVimLspDefinition", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const vimLspDefinitionDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Definition> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/vim-lsp-diagnostics.ts ================================================ import { getDiagnostics } from "@/connector/vim-lsp" import { diagnosticToDisplayText } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { Diagnostic, FzfCommandDefinitionDefaultOption, Resource, ResourceLine, ResourceLines, SourceFuncArgs, } from "@/type" export const diagnosticToResourceLine = (diagnostic: Diagnostic): ResourceLine => { const { file, lineNumber, message } = diagnostic return { data: { command: "FzfPreviewVimLspDiagnostics", type: "line", file, lineNumber, text: message, }, displayText: diagnosticToDisplayText(diagnostic), } } export const vimLspDiagnostics = async (_args: SourceFuncArgs): Promise => { const diagnostics = await getDiagnostics() const resourceLines: ResourceLines = diagnostics.map((diagnostic) => diagnosticToResourceLine(diagnostic)) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const vimLspDiagnosticsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Diagnostics> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/vim-lsp-implementation.ts ================================================ import { getImplementation } from "@/connector/vim-lsp" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const vimLspImplementation = async (_args: SourceFuncArgs): Promise => { const { implementations } = await getImplementation() const resourceLines: ResourceLines = implementations.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewVimLspImplementation", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const vimLspImplementationDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Implementation> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/vim-lsp-references.ts ================================================ import { getReferences } from "@/connector/vim-lsp" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const vimLspReferences = async (_args: SourceFuncArgs): Promise => { const { references } = await getReferences() const resourceLines: ResourceLines = references.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewVimLspReferences", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const vimLspReferencesDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"References> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/vim-lsp-type-definition.ts ================================================ import { getTypeDefinition } from "@/connector/vim-lsp" import { colorize, colorizeFile } from "@/fzf/syntax/colorize" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const vimLspTypeDefinition = async (_args: SourceFuncArgs): Promise => { const { typeDefinitions } = await getTypeDefinition() const resourceLines: ResourceLines = typeDefinitions.map(({ file, lineNumber, text }) => ({ data: { command: "FzfPreviewVimLspTypeDefinition", type: "line", file, text, lineNumber, }, displayText: `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} {3..}"` } export const vimLspTypeDefinitionDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"TypeDefinition> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"3.."', }) ================================================ FILE: src/fzf/resource/vista.ts ================================================ import type { VistaTag } from "@/connector/vista" import { getVistaCtags } from "@/connector/vista" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" import { alignLists } from "@/util/align" const SPACER = " " const vistaTagToArray = ({ lineNumber, kind, text }: VistaTag) => [lineNumber.toString(), `[${kind}]`, text] export const vistaCtags = async (_args: SourceFuncArgs): Promise => { const tags = await getVistaCtags() const displayTextList = alignLists(tags.map((tag) => vistaTagToArray(tag))).map((tag) => tag.join(SPACER).trim()) const resourceLines: ResourceLines = tags.map((tag, i) => ({ data: { command: "FzfPreviewVistaCtags", type: "line", file: tag.tagFile, lineNumber: tag.lineNumber, text: displayTextList[i], }, displayText: `${displayTextList[i]}${SPACER}${tag.tagFile}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const grepPreviewCommand = globalVariableSelector("fzfPreviewGrepPreviewCmd") as string return `"${grepPreviewCommand} '{-1}:{2}'"` } export const vistaCtagsDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"VistaCtags> "', "--multi": true, "--preview": previewCommand(), "--preview-window": '"+{2}-10"', "--with-nth": '"2.."', }) ================================================ FILE: src/fzf/resource/yankround.ts ================================================ import { getYankround } from "@/connector/yankround" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { FzfCommandDefinitionDefaultOption, Resource, ResourceLines, SourceFuncArgs } from "@/type" export const yankround = async (_args: SourceFuncArgs): Promise => { const yankHistories = await getYankround() const resourceLines: ResourceLines = yankHistories.map(({ line, option, text }) => ({ data: { command: "FzfPreviewYankround", type: "register", lineNumber: line, option, text, }, displayText: `${line} ${option} ${text}`, })) return { type: "json", lines: resourceLines, } } const previewCommand = () => { const yankroundPreviewCommand = globalVariableSelector("fzfPreviewYankroundPreviewCommand") as string const historyFile = `${globalVariableSelector("yankroundDir") as string}/history` return `"${yankroundPreviewCommand} ${historyFile} {2}"` } export const yankroundDefaultOptions = (): FzfCommandDefinitionDefaultOption => ({ "--prompt": '"Yankround> "', "--preview": previewCommand(), "--no-sort": true, "--with-nth": "4..", "--header": '"C-y: Paste"', }) ================================================ FILE: src/fzf/syntax/colorize.ts ================================================ import chalk from "chalk" import type { Color, Diagnostic } from "@/type" chalk.level = 3 type Options = { bold?: boolean } export const colorize = (str: string, color: Color, options?: Options): string => { const line = chalk[color](str) if (options == null) { return line } else if (options.bold === true) { return chalk[color](chalk.bold(line)) } else { return line } } export const colorizeFile = (filePath: string): string => { const splittedFilePath = filePath.split("/") if (splittedFilePath.length === 1) { return filePath } else { const file = splittedFilePath.slice(-1).toString() const directory = splittedFilePath.slice(0, -1).join("/") return `${colorize(`${directory}/`, "cyan")}${file}` } } // The colorize based on coc-fzf // REF: https://github.com/antoinemadec/coc-fzf // MIT: Copyright (c) 2020 Antoine export const diagnosticToDisplayText = ({ file, lineNumber, severity, message }: Diagnostic): string => { const severityColor = { Error: "red", Warning: "yellow", Information: "blue", Hint: "cyan", "": "white", } as const return `${colorizeFile(file)}:${colorize(lineNumber.toString(), "green")}: ${colorize( severity, severityColor[severity] as Color )} ${message}` } const iconToColor: { [icon: string]: Color } = { "": "green", "": "magenta", "": "yellow", "": "blue", "": "yellow", "": "yellow", "": "yellow", "": "blue", "": "red", "": "magenta", "": "yellow", "": "white", "": "cyan", "": "green", "": "blue", "": "blue", "": "white", "": "yellow", "": "magenta", "": "magenta", "": "magenta", λ: "yellow", "": "white", "": "blue", "": "green", "": "green", "": "red", "": "yellow", "": "white", "": "yellow", "": "magenta", "": "blue", "": "yellow", "": "blue", "": "yellow", "": "red", "": "magenta", "": "magenta", "": "green", "": "yellow", "": "blue", "": "blue", "": "magenta", "": "white", } export const colorizeDevIcon = (icon: string): string => { const color = iconToColor[icon] ?? null return color != null ? chalk[color](icon) : icon } ================================================ FILE: src/fzf/util.ts ================================================ import camelCase from "camelcase" import type { ReadonlyDeep } from "type-fest" import { globalVariableSelector } from "@/module/selector/vim-variable" import type { ProcessesName } from "@/type" export const createProcessFunctionName = (processesName: ProcessesName, expectKey: string): string => camelCase(`fzf-preview-${processesName}-${expectKey}`, { pascalCase: true }) export const filePreviewCommand = (): string => { const previewCommand = globalVariableSelector("fzfPreviewCommand") as string const binaryPreviewCommand = globalVariableSelector("fzfBinaryPreviewCommand") as string const ifBinaryCommand = globalVariableSelector("fzfPreviewIfBinaryCommand") as string return `'${ifBinaryCommand} && ${binaryPreviewCommand} || ${previewCommand}'` } const createGitLogAndStashOption = (prefix: string) => { return `--decorate --color=always --date=iso --format='%C(green)[${prefix}]%Creset %C(magenta)%h%Creset %C(yellow)%ad %x09%Creset [%C(blue)%an%Creset] %x09%C(auto)%s'` } export const gitStashDecorateCommand = `git stash list ${createGitLogAndStashOption("stash")}` export const gitStashNameCommand = `git stash list --decorate --color=always --format='%C(cyan)%gd%Creset'` export const gitReflogDecorateCommand = `git reflog ${createGitLogAndStashOption("reflog")}` export const gitReflogNameCommand = `git reflog --decorate --color=always --format='%C(cyan)%gd%Creset'` export const createGitLogCommand = (file?: string): string => { const targetFile = file != null ? `-- ${file}` : "" return `git log ${createGitLogAndStashOption("commit")} ${targetFile}` } type ParsedQuickFix = ReadonlyDeep<{ fileName: string lineNumber: number text: string }> export const parseQuickFixAndLocationListLine = (line: string): ParsedQuickFix => { const result = /^(?[^|]*)\|((?\d+)( col (\d+))?[^|]*)?\|(?.*)/.exec(line) if (result?.groups == null) { throw new Error(`line is not quickfix format: "${line}"`) } const { fileName, lineNumber, text } = result.groups return { fileName, lineNumber: Number(lineNumber), text } } ================================================ FILE: src/module/execute-command.ts ================================================ import type { PayloadAction } from "@reduxjs/toolkit" import { createSlice } from "@reduxjs/toolkit" import type { VimValue } from "neovim/lib/types/VimValue" import { EXECUTE_COMMAND } from "@/const/module" import type { FzfPreviewCommandList, UserProcesses } from "@/type" export type State = { commandName?: FzfPreviewCommandList options: { userProcesses?: UserProcesses enableDevIcons: VimValue currentFilePath: string } } const initialState: State = { commandName: undefined, options: { enableDevIcons: false, currentFilePath: "", }, } export const executeCommandModule = createSlice({ name: EXECUTE_COMMAND, initialState, reducers: { restore: (state, { payload }: PayloadAction) => { if (payload) { return { ...state, ...payload } } return state }, setExecuteCommand: ( state, { payload }: PayloadAction<{ commandName: FzfPreviewCommandList; options: State["options"] }> ) => { const { commandName, options } = payload state.commandName = commandName state.options = options }, }, }) ================================================ FILE: src/module/file-path.ts ================================================ import type { PayloadAction } from "@reduxjs/toolkit" import { createSlice } from "@reduxjs/toolkit" import { FILE_PATH } from "@/const/module" export type State = { [filePath in string]: { relativePath?: string } } const initialState: State = {} export const filePathModule = createSlice({ name: FILE_PATH, initialState, reducers: { registerRelativePath: (state, { payload }: PayloadAction<{ absolutePath: string; relativePath: string }>) => { state[payload.absolutePath] = { relativePath: payload.relativePath } }, }, }) ================================================ FILE: src/module/git-config.ts ================================================ import type { PayloadAction } from "@reduxjs/toolkit" import { createSlice } from "@reduxjs/toolkit" import { GIT_CONFIG } from "@/const/module" export type State = { noVerify: boolean } const initialState: State = { noVerify: false, } export const gitConfigModule = createSlice({ name: GIT_CONFIG, initialState, reducers: { restore: (state, { payload }: PayloadAction) => { if (payload != null) { state.noVerify = payload.noVerify } }, toggleNoVerify: (state) => { state.noVerify = !state.noVerify }, }, }) ================================================ FILE: src/module/recall.ts ================================================ import type { PayloadAction } from "@reduxjs/toolkit" import { createSlice } from "@reduxjs/toolkit" import { RECALL } from "@/const/module" export type State = { grepArgs: string } const initialState: State = { grepArgs: ".", } export const recallModule = createSlice({ name: RECALL, initialState, reducers: { restore: (state, { payload }: PayloadAction) => { if (payload) { return { ...state, ...payload } } return state }, setGrepArgs: (state, { payload }: PayloadAction<{ grepArgs: string }>) => { const { grepArgs } = payload state.grepArgs = grepArgs }, }, }) ================================================ FILE: src/module/resume.ts ================================================ import type { PayloadAction } from "@reduxjs/toolkit" import { createSlice } from "@reduxjs/toolkit" import { RESUME } from "@/const/module" import type { FzfCommandName } from "@/type" type State = { [commandName in FzfCommandName]?: string } const initialState: State = {} export const resumeModule = createSlice({ name: RESUME, initialState, reducers: { restore: (state, { payload }: PayloadAction) => { if (payload) { return { ...state, ...payload } } return state }, setQuery: (state, { payload }: PayloadAction<{ commandName: FzfCommandName; query: string }>) => { const { commandName, query } = payload state[commandName] = query }, }, }) ================================================ FILE: src/module/selector/execute-command.ts ================================================ import type { State } from "@/module/execute-command" import { store } from "@/store" export const executeCommandSelector = (): State => store.getState().executeCommand ================================================ FILE: src/module/selector/file-path.ts ================================================ import type { State } from "@/module/file-path" import { store } from "@/store" export const filePathSelector = (filePath: string): State[string] | undefined => store.getState().filePath[filePath] ================================================ FILE: src/module/selector/git-config.ts ================================================ import type { State } from "@/module/git-config" import { store } from "@/store" export const gitConfigSelector = (name: keyof State): State[typeof name] => store.getState().gitConfig[name] ================================================ FILE: src/module/selector/recall.ts ================================================ import type { State } from "@/module/recall" import { store } from "@/store" export const recallSelector = (key: keyof State): State[keyof State] => store.getState().recall[key] ================================================ FILE: src/module/selector/resume.ts ================================================ import { store } from "@/store" import type { FzfCommandName } from "@/type" export const resumeSelector = (commandName: FzfCommandName): string | undefined => store.getState().resume[commandName] ================================================ FILE: src/module/selector/session.ts ================================================ import { store } from "@/store" import type { Session, SessionToken } from "@/type" export const sessionSelector = (sessionToken: SessionToken): Session | null => store.getState().session.sessions[sessionToken] ?? null export const currentSessionSelector = (): Session | null => store.getState().session.currentSession ?? null ================================================ FILE: src/module/selector/vim-variable.ts ================================================ import type { VimValue } from "neovim/lib/types/VimValue" import { store } from "@/store" import type { GlobalVariableName, VimOptionName } from "@/type" export const globalVariableSelector = (name: GlobalVariableName): VimValue => store.getState().vimVariable.global[name] export const vimOptionsSelector = (name: VimOptionName): VimValue => store.getState().vimVariable.options[name] ================================================ FILE: src/module/session.ts ================================================ import type { PayloadAction } from "@reduxjs/toolkit" import { createSlice } from "@reduxjs/toolkit" import { castDraft, createDraft } from "immer" import { SESSION } from "@/const/module" import type { Session, SessionToken } from "@/type" type State = { currentSession?: Session sessions: { [key in SessionToken]: Session } } const initialState: State = { sessions: {}, } export const sessionModule = createSlice({ name: SESSION, initialState, reducers: { restore: (state, { payload }: PayloadAction) => { if (payload != null) { // NOTE: Not run createDraft on the payload, the state will not work properly on other actions. // or not use createDraft and `state = payload` (not want use it) const { sessions, currentSession } = createDraft(payload) state.sessions = sessions state.currentSession = currentSession } }, setSession: (state, { payload }: PayloadAction<{ sessionToken: SessionToken; session: Session }>) => { state.sessions[payload.sessionToken] = castDraft(payload.session) state.currentSession = undefined }, setCurrentSession: (state, { payload }: PayloadAction<{ session: Session }>) => { state.currentSession = castDraft(payload.session) }, clearCurrentSession: (state, _: PayloadAction) => { state.currentSession = undefined }, }, }) ================================================ FILE: src/module/vim-variable.ts ================================================ import type { PayloadAction } from "@reduxjs/toolkit" import { createSlice } from "@reduxjs/toolkit" import { mapKeys, mapValues } from "lodash" import type { VimValue } from "neovim/lib/types/VimValue" import type { vimOptions } from "@/association/vim-variable" import { VIM_VARIABLE } from "@/const/module" import type { GlobalVariableName } from "@/type" type State = { global: { [key in GlobalVariableName]: VimValue } options: { [key in typeof vimOptions[number]]: VimValue } } type GlobalVariable = { name: keyof State["global"] value: VimValue } type VimOption = { name: keyof State["options"] value: VimValue } const initialState: State = { global: { fzfPreviewDefaultFzfOptions: {}, fzfPreviewUseDevIcons: false, fzfPreviewDevIconPrefixStringLength: 0, fzfPreviewDevIconsLimit: 5000, webDevIconsUnicodeDecorateFileNodesDefaultSymbol: " ", webDevIconsUnicodeDecorateFileNodesExtensionSymbols: {}, webDevIconsUnicodeDecorateFileNodesExactSymbols: {}, webDevIconsUnicodeDecorateFileNodesPatternSymbols: {}, fzfPreviewCommand: "", fzfBinaryPreviewCommand: "", fzfPreviewIfBinaryCommand: "", fzfPreviewFilelistCommand: "", fzfPreviewGitFilesCommand: "", fzfPreviewDirectoryFilesCommand: "", fzfPreviewGitStatusCommand: "", fzfPreviewGitStatusPreviewCommand: "", fzfPreviewGrepCmd: "", fzfPreviewScriptDir: "", fzfPreviewCacheDirectory: "", fzfPreviewLinesCommand: "", fzfPreviewGrepPreviewCmd: "", fzfPreviewCustomProcesses: {}, fzfPreviewFzfPreviewWindowOption: "", fzfPreviewPreviewKeyBindings: "", fzfPreviewFzfColorOption: "", fzfPreviewHistoryDir: false, fzfPreviewBuffersJump: 0, yankroundDir: "", fzfPreviewYankroundPreviewCommand: "", fzfPreviewBlamePrCommand: "", }, options: { columns: 0, }, } type CustomProcesses = { [key: string]: string } export const vimVariableModule = createSlice({ name: VIM_VARIABLE, initialState, reducers: { restore: (state, { payload }: PayloadAction) => { if (payload) { return { ...state, ...payload } } return state }, setGlobalVariable: (state, { payload }: PayloadAction) => { const { name, value } = payload switch (name) { case "fzfPreviewCustomProcesses": { if (typeof value === "object" && !Array.isArray(value)) { const customProcessesDictionary = mapValues( value as { [key: string]: CustomProcesses }, (processes: CustomProcesses) => mapKeys(processes, (_, expectKey) => (expectKey === "" ? "enter" : expectKey)) ) state.global.fzfPreviewCustomProcesses = customProcessesDictionary } else { throw new Error("fzf_preview_custom_processes must be Dictionary variable") } break } default: { state.global[name] = value } } }, setOption: (state, { payload }: PayloadAction) => { const { name, value } = payload state.options[name] = value }, }, }) ================================================ FILE: src/plugin/fzf-runner.ts ================================================ import { fzfOptionsToString } from "@/fzf/option/convert" import { pluginCall } from "@/plugin" import type { FzfOptions, ResourceLine, ResourceLines } from "@/type" const PREFIX_SPACE = " " type Parameter = { resourceLines: ResourceLines handler: string options: FzfOptions } export const resourceLineToFzfLine = (resourceLine: ResourceLine): string => { return `${PREFIX_SPACE}${encodeURIComponent(JSON.stringify(resourceLine.data))} ${resourceLine.displayText}` } export const fzfRunner = async ({ resourceLines, handler, options }: Parameter): Promise => { await pluginCall("fzf_preview#remote#runner#fzf_run", { source: resourceLines.map((line) => resourceLineToFzfLine(line)), handler, options: fzfOptionsToString(options), environment: PLUGIN.ENV, }) } ================================================ FILE: src/plugin/index.ts ================================================ import type { Neovim } from "coc.nvim" import type { NvimPlugin } from "neovim" import type { CommandOptions, NvimFunctionOptions } from "neovim/lib/host/NvimPlugin" import type { VimValue } from "neovim/lib/types/VimValue" import type { MessageConnection } from "vscode-jsonrpc" // @ts-ignore let remotePlugin: NvimPlugin = null // @ts-ignore let cocClient: Neovim = null // @ts-ignore let rpcClient: MessageConnection = null export const setRemotePlugin = (initialPlugin: NvimPlugin): void => { remotePlugin = initialPlugin } export const setCocClient = (initialCocClient: Neovim): void => { cocClient = initialCocClient } export const setRpcClient = (initialRpcClient: MessageConnection): void => { rpcClient = initialRpcClient } // eslint-disable-next-line @typescript-eslint/ban-types export const remotePluginRegisterCommand = (name: string, fn: Function, options?: CommandOptions): void => remotePlugin.registerCommand(name, fn, options) // eslint-disable-next-line @typescript-eslint/ban-types export const remotePluginRegisterFunction = (name: string, fn: Function, options?: NvimFunctionOptions): void => remotePlugin.registerFunction(name, fn, options) // eslint-disable-next-line @typescript-eslint/no-explicit-any export const pluginCommand = (command: string): Promise => { if (remotePlugin != null) { return remotePlugin.nvim.command(command) } else if (cocClient != null) { return cocClient.command(command) } else if (rpcClient != null) { return rpcClient.sendRequest("execCommand", { command }) } throw new Error("Unexpected remote plugin, coc client and rpc client is not exists") } const convertRpcArgs = (args?: VimValue | ReadonlyArray) => { if (args == null) { return [] } else if (Array.isArray(args)) { return args } else { return [args] } } // eslint-disable-next-line @typescript-eslint/no-explicit-any export const pluginCall = (fname: string, args?: VimValue | ReadonlyArray): Promise | null => { if (remotePlugin != null) { return remotePlugin.nvim.call(fname, args) } else if (cocClient != null) { return cocClient.call(fname, args) } else if (rpcClient != null) { return rpcClient.sendRequest("execCall", { fname, args: convertRpcArgs(args) }) } throw new Error("Unexpected remote plugin, coc client and rpc client is not exists") } export const pluginGetVar = (name: string): Promise => { if (remotePlugin != null) { return remotePlugin.nvim.getVar(name) } else if (cocClient != null) { return cocClient.getVar(name) } else if (rpcClient != null) { return rpcClient.sendRequest("getVar", { name }) } throw new Error("Unexpected remote plugin, coc client and rpc client is not exists") } export const pluginGetVvar = (name: string): Promise => { if (remotePlugin != null) { return remotePlugin.nvim.getVvar(name) } else if (cocClient != null) { return cocClient.getVvar(name) } else if (rpcClient != null) { return rpcClient.sendRequest("getVvar", name) } throw new Error("Unexpected remote plugin, coc client and rpc client is not exists") } ================================================ FILE: src/plugin/process-runner.ts ================================================ import { pluginCall } from "@/plugin" import type { CallbackLines, UserProcesses } from "@/type" type Args = { processesFunctionName: string expectKey: string lines: CallbackLines userProcesses?: UserProcesses } const getProcessesName = (userProcesses?: UserProcesses) => { if (userProcesses?.type === "global_variable") { return userProcesses.value } else if (userProcesses?.type === "custom_processes_variable") { return `fzf_preview_custom_processes["${userProcesses.value}"]` } return undefined } export const processesRunner = async ({ processesFunctionName, expectKey, lines, userProcesses, }: Args): Promise => { await pluginCall("fzf_preview#remote#handler_to_process#call_funcref_or_fallback_default_process", [ PLUGIN.ENV, processesFunctionName, expectKey, lines, getProcessesName(userProcesses), ]) } ================================================ FILE: src/plugin/sync-vim-variable.ts ================================================ import { vimVariableModule } from "@/module/vim-variable" import { pluginCall } from "@/plugin" import { dispatch } from "@/store" import type { GlobalVariableName, GlobalVariables } from "@/type" export const syncVimVariable = async (): Promise => { const { actions } = vimVariableModule const variables = await (pluginCall("fzf_preview#remote#variable#get_global_variables") as Promise) Object.entries(variables).forEach(([name, value]) => { dispatch(actions.setGlobalVariable({ name: name as GlobalVariableName, value })) }) } export const syncVimOptions = async (): Promise => { const columns = (await pluginCall("fzf_preview#remote#util#get_columns")) as number dispatch(vimVariableModule.actions.setOption({ name: "columns", value: columns })) } ================================================ FILE: src/register/coc/index.ts ================================================ import type { commands, Disposable, ExtensionContext } from "coc.nvim" import { workspace } from "coc.nvim" import { flatMap, mapValues } from "lodash" import { cocCommandDefinition } from "@/association/coc-command" import { dispatchResumeQuery } from "@/connector/resume" import { HANDLER_NAME } from "@/const/fzf-handler" import { executeCommand } from "@/fzf/command" import { getDefaultProcesses } from "@/fzf/function" import { callProcess } from "@/fzf/handler" import { executeProcess, processesDefinition } from "@/fzf/process" import { pluginCommand, setCocClient } from "@/plugin" import type { CallbackLines } from "@/type" const removeFzfPreviewPrefix = (name: string) => { const result = /^FzfPreview(?\S+)/.exec(name) if (result?.groups != null) { return result.groups.name } return name } export const setRuntimePath = async (context: ExtensionContext): Promise => { const rtp = (await workspace.nvim.getOption("runtimepath")) as string const paths = rtp.split(",") if (!paths.includes(context.extensionPath)) { await workspace.nvim.command(`execute 'noautocmd set runtimepath^='.fnameescape('${context.extensionPath}')`) } await workspace.nvim.command("runtime plugin/fzf_preview.vim") } export const initializeExtension = async (): Promise => { setCocClient(workspace.nvim) await pluginCommand("let g:fzf_preview_has_coc = v:true") } export const registerCommands = (commandManager: typeof commands): ReadonlyArray => cocCommandDefinition.map((fzfCommand) => commandManager.registerCommand( `fzf-preview.${removeFzfPreviewPrefix(fzfCommand.commandName)}`, async (...params: ReadonlyArray) => { const args = params.join(" ") await executeCommand(args, fzfCommand) } ) ) export const registerProcesses = (commandManager: typeof commands): ReadonlyArray => flatMap(processesDefinition, ({ processes }) => processes.map((process) => commandManager.registerCommand( `fzf-preview-callback.${removeFzfPreviewPrefix(process.name)}`, async ([lines]: [CallbackLines, ...ReadonlyArray]) => { await executeProcess(lines, process) } ) ) ) export const registerFunctions = (commandManager: typeof commands): ReadonlyArray => [ commandManager.registerCommand(`fzf-preview.${removeFzfPreviewPrefix(HANDLER_NAME)}`, callProcess), commandManager.registerCommand("fzf-preview.GetDefaultProcesses", ([processesName]: ReadonlyArray) => mapValues(getDefaultProcesses(processesName), (name) => name) ), commandManager.registerCommand("fzf-preview-function.DispatchResumeQuery", dispatchResumeQuery), ] ================================================ FILE: src/register/remote/index.ts ================================================ import { commandDefinition } from "@/association/command" import { dispatchResumeQuery } from "@/connector/resume" import { HANDLER_NAME } from "@/const/fzf-handler" import { executeCommand } from "@/fzf/command" import { getDefaultProcesses } from "@/fzf/function" import { callProcess } from "@/fzf/handler" import { executeProcess, processesDefinition } from "@/fzf/process" import { remotePluginRegisterCommand, remotePluginRegisterFunction } from "@/plugin" import type { CallbackLines } from "@/type" export const registerRemoteCommands = (): void => { commandDefinition.forEach((fzfCommand) => { remotePluginRegisterCommand( fzfCommand.commandName, async (params: ReadonlyArray) => { const args = params[0] != null ? params[0] : "" await executeCommand(args, fzfCommand) }, fzfCommand.vimCommandOptions ) }) } export const registerProcesses = (): void => { processesDefinition.forEach(({ processes }) => { processes.forEach((process) => { remotePluginRegisterFunction( process.name, async ([lines]: [CallbackLines, ...ReadonlyArray]) => { await executeProcess(lines, process) }, { sync: false } ) }) }) } export const registerFunction = (): void => { remotePluginRegisterFunction(HANDLER_NAME, callProcess, { sync: true }) remotePluginRegisterFunction( "FzfPreviewGetDefaultProcesses", ([processesName]: ReadonlyArray) => getDefaultProcesses(processesName), { sync: true } ) remotePluginRegisterFunction("FzfPreviewDispatchResumeQuery", dispatchResumeQuery, { sync: false }) } ================================================ FILE: src/remote.ts ================================================ /* eslint-disable import/no-import-module-exports */ import type { NvimPlugin } from "neovim" import { setRemotePlugin } from "@/plugin" import { registerFunction, registerProcesses, registerRemoteCommands } from "@/register/remote" /* eslint-enable */ module.exports = (plugin: NvimPlugin) => { setRemotePlugin(plugin) registerRemoteCommands() registerProcesses() registerFunction() plugin.registerCommand( "FzfPreviewRemoteEnvironment", async (_args: ReadonlyArray) => { await plugin.nvim.command("echo 'fzf-preview is remote plugin'") }, { nargs: "*" } ) } ================================================ FILE: src/rpc.ts ================================================ import * as rpc from "vscode-jsonrpc" import { commandDefinition } from "@/association/command" import { dispatchResumeQuery } from "@/connector/resume" import { executeCommand } from "@/fzf/command" import { getDefaultProcesses } from "@/fzf/function" import { callProcess } from "@/fzf/handler" import { executeProcess, processesDefinition } from "@/fzf/process" import { setRpcClient } from "@/plugin" import type { DispatchResumeQueryParams, RpcCallProcessParams, RpcExecCommandParams, RpcExecProcessCallbackParams, } from "@/type" const connection = rpc.createMessageConnection( // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call new rpc.StreamMessageReader(process.stdin), // @ts-ignore // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call new rpc.StreamMessageWriter(process.stdout) ) const getDefaultProcessesRequest = new rpc.RequestType( "getDefaultProcesses" ) connection.onRequest(getDefaultProcessesRequest, () => { const openFile = getDefaultProcesses("open-file") const openFileWithTagStack = getDefaultProcesses("open-file-with-tag-stack") const openBuffer = getDefaultProcesses("open-buffer") const openBufnr = getDefaultProcesses("open-bufnr") const gitStatus = getDefaultProcesses("git-status") return { "open-file": openFile, "open-file-with-tag-stack": openFileWithTagStack, "open-buffer": openBuffer, "open-bufnr": openBufnr, "git-status": gitStatus, } }) const execCommandRequest = new rpc.RequestType("execCommand") connection.onRequest(execCommandRequest, async ({ commandName, args }) => { for (const fzfCommand of commandDefinition) { if (commandName === fzfCommand.commandName) { // eslint-disable-next-line no-await-in-loop await executeCommand(args != null ? args : "", fzfCommand) return } } }) const callProcessRequest = new rpc.RequestType("callProcess") connection.onRequest(callProcessRequest, async ({ lines }) => { await callProcess([lines]) }) const execProcessCallbackRequest = new rpc.RequestType("execProcessCallback") connection.onRequest(execProcessCallbackRequest, async ({ processName, lines }) => { for (const { processes } of processesDefinition) { for (const process of processes) { if (processName === process.name) { // eslint-disable-next-line no-await-in-loop await executeProcess(lines, process) return } } } }) const dispatchResumeQueryRequest = new rpc.RequestType("dispatchResumeQuery") connection.onRequest(dispatchResumeQueryRequest, ({ commandName, query }) => { dispatchResumeQuery([commandName, query]) }) setRpcClient(connection) connection.listen() ================================================ FILE: src/store/index.ts ================================================ import { configureStore, getDefaultMiddleware } from "@reduxjs/toolkit" import { executeCommandModule } from "@/module/execute-command" import { filePathModule } from "@/module/file-path" import { gitConfigModule } from "@/module/git-config" import { recallModule } from "@/module/recall" import { resumeModule } from "@/module/resume" import { sessionModule } from "@/module/session" import { vimVariableModule } from "@/module/vim-variable" const setupStore = () => { const store = configureStore({ reducer: { vimVariable: vimVariableModule.reducer, executeCommand: executeCommandModule.reducer, resume: resumeModule.reducer, session: sessionModule.reducer, gitConfig: gitConfigModule.reducer, recall: recallModule.reducer, filePath: filePathModule.reducer, }, middleware: getDefaultMiddleware(), }) return store } export const store = setupStore() export const { dispatch } = store ================================================ FILE: src/system/command.ts ================================================ import type { SpawnSyncReturns } from "child_process" import { exec, execSync } from "child_process" import { promisify } from "util" import { MAX_BUFFER_SIZE } from "@/const/system" import type { CommandResult } from "@/type" export const execAsyncCommand = async (command: string): Promise => { const promiseExec = promisify(exec) const { stdout, stderr } = await promiseExec(command, { encoding: "utf-8", maxBuffer: MAX_BUFFER_SIZE }) if (stderr === "") { return { stdout, stderr, status: 0, } } return { stdout, stderr, status: null, } } export const execSyncCommand = (command: string): CommandResult => { try { const stdout = execSync(command, { encoding: "utf-8", maxBuffer: MAX_BUFFER_SIZE }) return { stdout, stderr: "", status: 0, } } catch (error) { const { stdout, stderr, status } = error as SpawnSyncReturns return { stdout, stderr, status, } } } ================================================ FILE: src/system/file.ts ================================================ import fs from "fs" import path from "path" import { TEMPORALLY_DATA_FILE_PATH } from "@/const/system" import { pluginCall } from "@/plugin" export const expandHome = (filePath: string): string => { if (filePath.startsWith("~")) { return path.join(process.env.HOME as string, filePath.slice(1)) } return filePath } export const collapseHome = (filePath: string): string => { return filePath.replace(process.env.HOME as string, "~") } export const existsFileAsync = async (filePath: string): Promise => { const result = (await pluginCall("filereadable", [filePath])) as number if (result === 0) { return false } else { return true } } // TODO: Use Vim script export const existsDirectory = (dirPath: string): boolean => { try { const stats = fs.statSync(dirPath) return stats.isDirectory() } catch (_error) { return false } } export const readFile = (filePath: string): string => { return fs.readFileSync(filePath, { encoding: "utf-8" }) } export const readFileLine = (filePath: string, lineNumber: number): string => { return readFile(filePath).split("\n")[lineNumber - 1] } export const getCurrentFilePath = async (): Promise => { const file = (await pluginCall("expand", "%")) as string return file } export const getCurrentPath = async (): Promise => { const pwd = (await pluginCall("getcwd")) as string return pwd } export const initializeDataTransferFile = (filePath: string): void => { if (fs.existsSync(filePath)) { fs.unlinkSync(filePath) } fs.closeSync(fs.openSync(TEMPORALLY_DATA_FILE_PATH, "w")) } ================================================ FILE: src/system/mr.ts ================================================ import fs from "fs" import { globalVariableSelector } from "@/module/selector/vim-variable" import { existsDirectory, expandHome } from "@/system/file" const cacheDirectory = () => { const cacheDir = globalVariableSelector("fzfPreviewCacheDirectory") if (typeof cacheDir !== "string" || cacheDir === "") { throw new Error("g:fzf_preview_cache_directory must be string") } return cacheDir } const readFileOrCreateDirectory = (cacheFile: string) => { const cacheDirectoryPath = expandHome(cacheDirectory()) if (!existsDirectory(cacheDirectoryPath)) { fs.mkdirSync(cacheDirectoryPath, { recursive: true }) } try { return fs.readFileSync(cacheFile, { encoding: "utf-8" }).split("\n") } catch (_error) { return [] } } const mruFilePath = () => `${cacheDirectory()}/mru` const mrwFilePath = () => `${cacheDirectory()}/mrw` const readFile = (filePath: string) => { const files = readFileOrCreateDirectory(filePath) return files } export const readMruFile = (): ReadonlyArray => { const files = readFile(mruFilePath()) return files } export const readMrwFile = (): ReadonlyArray => { const files = readFile(mrwFilePath()) return files } ================================================ FILE: src/system/project.ts ================================================ import { execProjectFiles } from "@/connector/project-files" import { filePathModule } from "@/module/file-path" import { filePathSelector } from "@/module/selector/file-path" import { dispatch } from "@/store" export const dropFileProtocol = (uri: string): string => { const result = /file:\/\/(?\S+)/.exec(uri) if (result?.groups != null) { return result.groups.path } return uri } export const filePathToRelativeFilePath = (file: string, currentPath: string): string | null => { const cachedPath: ReturnType | undefined = filePathSelector(file) if (cachedPath?.relativePath != null) { return cachedPath.relativePath } const regex = new RegExp(`^${currentPath}/(?.+)`) const regExpExecArray = regex.exec(file) if (regExpExecArray?.groups == null) { return null } const relativePath = regExpExecArray.groups.fileName dispatch(filePathModule.actions.registerRelativePath({ absolutePath: file, relativePath })) return relativePath } export const filterProjectEnabledFile = async ( files: ReadonlyArray, currentPath: string ): Promise> => { const projectFiles = await execProjectFiles() const existsFiles = files .map((file) => filePathToRelativeFilePath(file, currentPath)) .filter((file): file is string => projectFiles.some((projectFile) => file != null && projectFile === file)) return existsFiles } ================================================ FILE: src/system/tags.ts ================================================ import { BUFFER_TAGS_COMMAND } from "@/const/system" import { execSyncCommand } from "@/system/command" export const getBufferTags = (filePath: string): ReadonlyArray => { const { stdout, stderr, status } = execSyncCommand(`${BUFFER_TAGS_COMMAND} ${filePath}`) if (stderr !== "" || status !== 0) { throw new Error(`Failed buffer tags command: "${BUFFER_TAGS_COMMAND} ${filePath}"`) } return stdout.split("\n") } ================================================ FILE: src/type/args.ts ================================================ import type { ReadonlyDeep } from "type-fest" import type { argsParser } from "@/args/parser" export type ArgsOptions = ReadonlyDeep["parseSync"]>> ================================================ FILE: src/type/command.ts ================================================ import type { cocCommandDefinition } from "@/association/coc-command" import type { AddFzfArg, FzfCommandName, FzfOptions, Processes, Resource, ResumeQuery, SourceFuncArgs, UserProcesses, } from "@/type" export type FzfPreviewCommandList = typeof cocCommandDefinition[number]["commandName"] export type ExecuteArgs = { sourceFunc: (args: SourceFuncArgs) => Promise sourceFuncArgs: SourceFuncArgs enableDevIconsCommandSetting: boolean commandName: FzfCommandName userProcesses?: UserProcesses fzfCommandDefaultOptions: FzfOptions defaultProcesses: Processes addFzfOptions: ReadonlyArray historyOption: { commandName: string historyDir: string | false } resumeQuery?: ResumeQuery enableConvertForFzf: boolean addGitStatus: boolean | undefined } ================================================ FILE: src/type/connector.ts ================================================ import type { OpenCommand } from "@/type/process" export type OpenFile = { openCommand: OpenCommand file: string lineNumber?: number setTagStack?: boolean } export type ExportQuickFix = | { filename: string } | { filename: string lnum: number text: string } ================================================ FILE: src/type/fzf.ts ================================================ import type { CommandOptions } from "neovim/lib/host/NvimPlugin" import type { Merge, ReadonlyDeep } from "type-fest" import type { GitBranchData, GitLogData, GitReflogData, GitStashData, GitStatusData, ProcessesName, Resource, } from "@/type" export type FzfCommandDynamicOption = ReadonlyDeep<{ "--header"?: string "--header-lines"?: string }> type SelectedLine = string export type SelectedLines = ReadonlyArray export type ExpectKeyAndSelectedLines = ReadonlyArray export type BaseFzfCommandName = | "FzfPreviewProjectFiles" | "FzfPreviewGitFiles" | "FzfPreviewDirectoryFiles" | "FzfPreviewBuffers" | "FzfPreviewAllBuffers" | "FzfPreviewProjectOldFiles" | "FzfPreviewProjectMruFiles" | "FzfPreviewProjectMrwFiles" | "FzfPreviewLines" | "FzfPreviewBufferLines" | "FzfPreviewCtags" | "FzfPreviewBufferTags" | "FzfPreviewOldFiles" | "FzfPreviewMruFiles" | "FzfPreviewMrwFiles" | "FzfPreviewQuickFix" | "FzfPreviewLocationList" | "FzfPreviewJumps" | "FzfPreviewChanges" | "FzfPreviewMarks" | "FzfPreviewProjectGrep" | "FzfPreviewProjectGrepRecall" | "FzfPreviewFromResources" | "FzfPreviewCommandPalette" | "FzfPreviewGrepHelp" | "FzfPreviewGitActions" | "FzfPreviewGitStatus" | "FzfPreviewGitStatusActions" | "FzfPreviewGitBranches" | "FzfPreviewGitBranchActions" | "FzfPreviewGitLogs" | "FzfPreviewGitCurrentLogs" | "FzfPreviewGitLogActions" | "FzfPreviewGitStashes" | "FzfPreviewGitStashActions" | "FzfPreviewGitReflogs" | "FzfPreviewGitReflogActions" | "FzfPreviewVimLspReferences" | "FzfPreviewVimLspDiagnostics" | "FzfPreviewVimLspCurrentDiagnostics" | "FzfPreviewVimLspDefinition" | "FzfPreviewVimLspTypeDefinition" | "FzfPreviewVimLspImplementation" | "FzfPreviewNvimLspReferences" | "FzfPreviewNvimLspDiagnostics" | "FzfPreviewNvimLspCurrentDiagnostics" | "FzfPreviewNvimLspDefinition" | "FzfPreviewNvimLspTypeDefinition" | "FzfPreviewNvimLspImplementation" | "FzfPreviewBookmarks" | "FzfPreviewYankround" | "FzfPreviewVistaCtags" | "FzfPreviewVistaBufferCtags" | "FzfPreviewMemoList" | "FzfPreviewMemoListGrep" | "FzfPreviewTodoComments" | "FzfPreviewBlamePR" type CocFzfCommandName = | "FzfPreviewCocReferences" | "FzfPreviewCocDiagnostics" | "FzfPreviewCocCurrentDiagnostics" | "FzfPreviewCocDefinition" | "FzfPreviewCocTypeDefinition" | "FzfPreviewCocImplementations" | "FzfPreviewCocOutline" | "FzfPreviewCocTsServerSourceDefinition" export type FzfCommandName = BaseFzfCommandName | CocFzfCommandName export type SourceFuncArgs = ReadonlyDeep<{ args: ReadonlyArray }> type FzfCommandBase = ReadonlyDeep<{ sourceFunc: (sourceFuncArgs: SourceFuncArgs) => Promise sourceFuncArgsParser: (args: string) => SourceFuncArgs vimCommandOptions: CommandOptions defaultFzfOptionFunc: () => | { [optionName: string]: string | boolean | undefined } | Promise<{ [optionName: string]: string | boolean | undefined }> defaultProcessesName: ProcessesName enableConvertForFzf: boolean enableDevIcons: boolean addGitStatus?: boolean beforeCommandHook?: (args: string) => void }> export type BaseFzfCommand = ReadonlyDeep< Merge< FzfCommandBase, { commandName: BaseFzfCommandName } > > type CocFzfCommand = ReadonlyDeep< Merge< FzfCommandBase, { commandName: CocFzfCommandName } > > export type FzfCommand = BaseFzfCommand | CocFzfCommand export type FzfOptions = ReadonlyDeep<{ "--ansi"?: true "--bind"?: | ReadonlyArray<{ key: string action: string }> | string "--expect"?: ReadonlyArray | string "--history"?: string "--no-separator"?: true [otherProperty: string]: any // eslint-disable-line @typescript-eslint/no-explicit-any }> export type FzfCommandDefinitionDefaultOption = ReadonlyDeep<{ "--header"?: string "--header-lines"?: string "--prompt": string "--multi"?: true "--preview"?: string "--preview-window"?: string "--no-sort"?: true "--delimiter"?: string "--phony"?: true "--bind"?: string "--query"?: string "--with-nth"?: string "--nth"?: string "--keep-right"?: true }> export type AddFzfArg = ReadonlyDeep<{ optionName: string value?: string }> export type ResumeQuery = string | null export type Session = { gitStatusDataList?: ReadonlyArray gitBranches?: ReadonlyArray gitLogs?: ReadonlyArray gitStashes?: ReadonlyArray gitReflogs?: ReadonlyArray } export type SessionToken = string ================================================ FILE: src/type/git.ts ================================================ import type { ReadonlyDeep } from "type-fest" import type { GIT_ACTIONS, GIT_BRANCH_ACTIONS, GIT_LOG_ACTIONS, GIT_REFLOG_ACTIONS, GIT_STASH_ACTIONS, GIT_STATUS_ACTIONS, } from "@/const/git" export type GitAction = typeof GIT_ACTIONS[number] | "header" export type GitStatusAction = typeof GIT_STATUS_ACTIONS[number] | "header" export type GitBranchAction = typeof GIT_BRANCH_ACTIONS[number] | "header" export type GitLogAction = typeof GIT_LOG_ACTIONS[number] | "header" export type GitStashAction = typeof GIT_STASH_ACTIONS[number] | "header" export type GitReflogAction = typeof GIT_REFLOG_ACTIONS[number] | "header" export type GitBranch = ReadonlyDeep<{ prefix: string name: string date: string author: string }> export type GitLog = ReadonlyDeep<{ prefix: string hash: string date: string author: string comment: string }> export type GitStash = ReadonlyDeep<{ prefix: string name: string hash: string date: string author: string comment: string }> export type GitReflog = ReadonlyDeep<{ prefix: string name: string hash: string date: string author: string comment: string }> export type ParsedGitStatus = ReadonlyDeep<{ file: string status: string }> ================================================ FILE: src/type/index.ts ================================================ export * from "@/type/args" export * from "@/type/command" export * from "@/type/connector" export * from "@/type/fzf" export * from "@/type/git" export * from "@/type/lsp" export * from "@/type/process" export * from "@/type/resource" export * from "@/type/rpc" export * from "@/type/syntax" export * from "@/type/system" export * from "@/type/vim" export * from "@/type/vim-variable" ================================================ FILE: src/type/lsp.ts ================================================ import type { ReadonlyDeep } from "type-fest" export type Location = ReadonlyDeep<{ file: string lineNumber: number text: string }> export type DiagnosticLevel = "Error" | "Warning" | "Information" | "Hint" | "" export type Diagnostic = ReadonlyDeep<{ file: string lineNumber: number severity: DiagnosticLevel message: string }> export type DiagnosticItem = ReadonlyDeep<{ file: string lnum: number message: string severity: string }> ================================================ FILE: src/type/process.ts ================================================ import type { ReadonlyDeep } from "type-fest" import type { PROCESSES_NAME } from "@/const/fzf-processes" import type { ResourceData } from "@/type/resource" export type CallbackLine = string export type CallbackLines = ReadonlyArray export type Process = ReadonlyDeep<{ name: string key: string execute: (lines: ReadonlyArray) => void | Promise }> export type Processes = ReadonlyDeep> export type ProcessesName = typeof PROCESSES_NAME[number] export type ProcessesDefinition = ReadonlyDeep< ReadonlyArray<{ name: ProcessesName processes: Processes }> > export type UserProcesses = | { type: "custom_processes_variable" value: ProcessesName } | { type: "global_variable" value: string } export type CustomProcessesVimVariable = ReadonlyDeep<{ [key in ProcessesName]: { [key: string]: string } }> export type SingleLineConsumer = ReadonlyDeep<{ consume: (line: ReadonlyDeep) => Promise kind: "single" }> type BulkLineConsumer = ReadonlyDeep<{ consume: (lines: ReadonlyArray) => Promise kind: "bulk" }> type LineConsumer = SingleLineConsumer | BulkLineConsumer export type CreateProcessCreator = ( processesName: ProcessesName ) => (expectKey: string, lineConsumer: LineConsumer) => Process export type OpenCommand = "edit" | "split" | "vsplit" | "tabedit" | "drop" ================================================ FILE: src/type/resource.ts ================================================ import type { ReadonlyDeep } from "type-fest" import type { FzfCommandDynamicOption, FzfCommandName, GitAction, GitBranchAction, GitLogAction, GitReflogAction, GitStashAction, GitStatusAction, } from "@/type" export type FileData = ReadonlyDeep<{ command: FzfCommandName type: "file" file: string lineNumber?: undefined }> export type LineData = ReadonlyDeep<{ command: FzfCommandName type: "line" file: string lineNumber: number text: string }> type BufferData = ReadonlyDeep<{ command: FzfCommandName type: "buffer" file: string bufnr: number lineNumber?: undefined }> type CommandPaletteData = ReadonlyDeep<{ command: FzfCommandName type: "command-palette" name: string lineNumber?: undefined }> type GitActionData = ReadonlyDeep<{ command: FzfCommandName type: "git-actions" action: GitAction lineNumber?: undefined }> export type GitStatusData = ReadonlyDeep<{ command: FzfCommandName type: "git-status" action?: "header" file: string status: string lineNumber?: undefined }> type GitStatusActionData = ReadonlyDeep<{ command: FzfCommandName type: "git-status-actions" action: GitStatusAction files: ReadonlyArray lineNumber?: undefined }> export type GitBranchData = ReadonlyDeep<{ command: FzfCommandName type: "git-branch" name: string date: string author: string isCreate: boolean lineNumber?: undefined }> type GitBranchActionData = ReadonlyDeep<{ command: FzfCommandName type: "git-branch-actions" action: GitBranchAction branches: ReadonlyArray lineNumber?: undefined }> export type GitLogData = ReadonlyDeep<{ command: FzfCommandName type: "git-log" hash: string date: string author: string comment: string isCurrentFile: boolean lineNumber?: undefined }> type GitLogActionData = ReadonlyDeep<{ command: FzfCommandName type: "git-log-actions" action: GitLogAction hashes: ReadonlyArray isCurrentFile: boolean lineNumber?: undefined }> export type GitStashData = ReadonlyDeep<{ command: FzfCommandName type: "git-stash" name: string hash: string date: string author: string comment: string isCreate: boolean lineNumber?: undefined }> type GitStashActionData = ReadonlyDeep<{ command: FzfCommandName type: "git-stash-actions" action: GitStashAction names: ReadonlyArray hashes: ReadonlyArray lineNumber?: undefined }> export type GitReflogData = ReadonlyDeep<{ command: FzfCommandName type: "git-reflog" name: string hash: string date: string author: string comment: string lineNumber?: undefined }> type GitReflogActionData = ReadonlyDeep<{ command: FzfCommandName type: "git-reflog-actions" action: GitReflogAction names: ReadonlyArray hashes: ReadonlyArray lineNumber?: undefined }> type RegisterData = ReadonlyDeep<{ command: FzfCommandName type: "register" lineNumber: number text: string option: string }> type GitPrData = ReadonlyDeep<{ command: FzfCommandName type: "git-pr" prNumber?: number lineNumber?: undefined }> export type ResourceData = | FileData | LineData | BufferData | CommandPaletteData | GitActionData | GitStatusData | GitStatusActionData | GitBranchData | GitBranchActionData | GitLogData | GitLogActionData | GitStashData | GitStashActionData | GitReflogData | GitReflogActionData | RegisterData | GitPrData export type ResourceLine = ReadonlyDeep<{ data: ResourceData displayText: string }> export type ResourceLines = ReadonlyArray type JsonResource = ReadonlyDeep<{ type: "json" lines: ResourceLines options?: FzfCommandDynamicOption }> type TextResource = ReadonlyDeep<{ type: "text" lines: ResourceLines options?: FzfCommandDynamicOption }> export type Resource = JsonResource | TextResource ================================================ FILE: src/type/rpc.ts ================================================ import type { BaseFzfCommandName } from "@/type" export type RpcExecCommandParams = { commandName: BaseFzfCommandName args?: string } export type RpcCallProcessParams = { lines: ReadonlyArray } export type RpcExecProcessCallbackParams = { processName: string lines: ReadonlyArray } export type DispatchResumeQueryParams = { commandName: BaseFzfCommandName query: string } ================================================ FILE: src/type/syntax.ts ================================================ export type Color = "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" ================================================ FILE: src/type/system.ts ================================================ import type { ReadonlyDeep } from "type-fest" export type CommandResult = ReadonlyDeep<{ stdout: string stderr: string status: number | null }> ================================================ FILE: src/type/vim-variable.ts ================================================ import type { VimValue } from "neovim/lib/types/VimValue" import type { ReadonlyDeep } from "type-fest" import type { vimOptions, vimVariableAssociation } from "@/association/vim-variable" export type GlobalVariableName = keyof typeof vimVariableAssociation export type GlobalVariables = ReadonlyDeep<{ [key in GlobalVariableName]: VimValue }> export type VimOptionName = typeof vimOptions[number] ================================================ FILE: src/type/vim.ts ================================================ import type { ReadonlyDeep } from "type-fest" export type VimBuffer = ReadonlyDeep<{ fileName: string bufnr: number isCurrent: boolean isAlternate: boolean isModified: boolean }> ================================================ FILE: src/util/align.test.ts ================================================ import { alignLists } from "@/util/align" describe("align lists", () => { it("align 1 digit", () => { const lists = [ ["1", "a"], ["2", "b"], ["3", "c"], ] expect(alignLists(lists)).toEqual([ ["1", "a"], ["2", "b"], ["3", "c"], ]) }) it("align 3 digit", () => { const lists = [ ["1", "foo"], ["10", "bar"], ["100", "foobar"], ] expect(alignLists(lists)).toEqual([ ["1 ", "foo "], ["10 ", "bar "], ["100", "foobar"], ]) }) it("align 5 digit", () => { const lists = [ ["1", "a"], ["10000", "bbbbb"], ] expect(alignLists(lists)).toEqual([ ["1 ", "a "], ["10000", "bbbbb"], ]) }) }) ================================================ FILE: src/util/align.ts ================================================ import printf from "printf" import { transpose } from "@/util/array" export const alignLists = (lists: ReadonlyArray>): ReadonlyArray> => { if (lists.length === 0) { return lists } const maxes = transpose(lists).map((list) => list.reduce((acc, cur) => Math.max(acc, cur.length), 0)) return lists.map((list) => list.map((value, i) => printf(`%-${maxes[i]}s`, value))) } ================================================ FILE: src/util/array.ts ================================================ export const transpose = (table: ReadonlyArray>): ReadonlyArray> => table[0].map((_, index) => table.map((row) => row[index])) export const asyncFilter = async ( array: ReadonlyArray, asyncCallback: (args: T) => Promise ): Promise> => { const bits = await Promise.all(array.map(asyncCallback)) return array.filter((_, i) => bits[i]) } ================================================ FILE: src/util/type.ts ================================================ export const unreachable = (_: never): never => _ ================================================ FILE: src/util/uniq-with.ts ================================================ export const uniqWith = (array: Array, comparator: (a: T, b: T) => boolean): Array => { return array.reduce((acc: Array, v1: T) => { if (!acc.some((v2) => comparator(v1, v2))) { acc.push(v1) } return acc }, []) } ================================================ FILE: stylua.toml ================================================ indent_type = "Spaces" indent_width = 2 column_width = 120 quote_style = "AutoPreferSingle" ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "target": "es2017", "module": "commonjs", "lib": ["es6"], "esModuleInterop": true, "moduleResolution": "node", "skipLibCheck": true, "strict": true, "baseUrl": "./", "rootDir": "./src", "paths": { "@/*": ["src/*"] }, "outDir": "./build", "noErrorTruncation": true }, "exclude": ["node_modules"], "include": ["./src/**/*.ts"] } ================================================ FILE: tsconfig.webpack-jest.json ================================================ { "compilerOptions": { "target": "es5", "module": "commonjs", "strict": true, "esModuleInterop": true, "baseUrl": "./", "rootDir": "./" } } ================================================ FILE: webpack.coc.ts ================================================ import path from "path" import { DefinePlugin } from "webpack" import { merge } from "webpack-merge" import common from "./webpack.common" export default merge(common, { entry: "./src/coc.ts", externals: { "coc.nvim": "commonjs coc.nvim", }, output: { path: path.join(__dirname, "lib"), filename: "coc.js", libraryTarget: "commonjs", }, plugins: [ new DefinePlugin({ PLUGIN: JSON.stringify({ ENV: "coc", }), }), ], }) ================================================ FILE: webpack.common.ts ================================================ import path from "path" import type { Configuration } from "webpack" const common: Configuration = { target: "node", mode: "none", resolve: { mainFields: ["module", "main"], extensions: [".js", ".ts"], alias: { "@": path.resolve(__dirname, "src"), }, }, module: { rules: [ { test: /\.ts$/, include: [path.resolve(__dirname, "src")], loader: "ts-loader", options: { transpileOnly: true, }, }, ], }, externals: { // NOTE: Disable `node:{module}` import // SEE: https://github.com/webpack/webpack/issues/14166 "node:process": "commonjs2 process", "node:os": "commonjs2 os", "node:tty": "commonjs2 tty", }, ignoreWarnings: [{ module: /yargs/ }], node: { __dirname: false, __filename: false, }, } export default common ================================================ FILE: webpack.preview.ts ================================================ import fs from "fs" import path from "path" import type { Configuration } from "webpack" import { BannerPlugin } from "webpack" const preview: Configuration = { entry: "./scripts/preview.js", output: { path: path.join(__dirname, "bin"), filename: "preview_fzf_grep", libraryTarget: "commonjs", }, target: "node", mode: "production", resolve: { mainFields: ["module", "main"], extensions: [".js"], }, node: { __dirname: false, __filename: false, }, plugins: [ new BannerPlugin({ banner: "#!/usr/bin/env node\n", raw: true }), function addExec(): void { this.hooks.done.tap("chmod", () => { fs.chmodSync(path.resolve(__dirname, "bin", "preview_fzf_grep"), "755") }) }, ], } export default preview ================================================ FILE: webpack.remote.ts ================================================ import path from "path" import { DefinePlugin } from "webpack" import { merge } from "webpack-merge" import common from "./webpack.common" export default merge(common, { entry: "./src/remote.ts", externals: { "coc.nvim": "commonjs2 coc.nvim", }, output: { path: path.join(__dirname, "rplugin/node/fzf-preview.vim"), filename: "index.js", libraryTarget: "commonjs2", }, plugins: [ new DefinePlugin({ PLUGIN: JSON.stringify({ ENV: "remote", }), }), ], }) ================================================ FILE: webpack.rpc.ts ================================================ import path from "path" import { DefinePlugin } from "webpack" import { merge } from "webpack-merge" import common from "./webpack.common" export default merge(common, { entry: "./src/rpc.ts", externals: { "coc.nvim": "commonjs coc.nvim", }, output: { path: path.join(__dirname, "lib"), filename: "rpc.js", libraryTarget: "commonjs", }, plugins: [ new DefinePlugin({ PLUGIN: JSON.stringify({ ENV: "rpc", }), }), ], })