feat: initial release
This commit is contained in:
commit
a2fe68b248
26 changed files with 960 additions and 0 deletions
9
.busted
Normal file
9
.busted
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
return {
|
||||||
|
_all = {
|
||||||
|
lua = 'nvim -l',
|
||||||
|
ROOT = { './spec/' },
|
||||||
|
},
|
||||||
|
default = {
|
||||||
|
verbose = true,
|
||||||
|
},
|
||||||
|
}
|
||||||
9
.editorconfig
Normal file
9
.editorconfig
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
insert_final_newline = true
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[*.lua]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
17
.github/DISCUSSION_TEMPLATE/q-a.yaml
vendored
Normal file
17
.github/DISCUSSION_TEMPLATE/q-a.yaml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
title: 'Q&A'
|
||||||
|
labels: []
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Use this space for questions, ideas, and general discussion about blink-cmp-tmux.
|
||||||
|
For bug reports, please [open an issue](https://github.com/barrettruth/blink-cmp-tmux/issues/new/choose) instead.
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Question or topic
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Context
|
||||||
|
description: Any relevant details (Neovim version, config, screenshots)
|
||||||
85
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
85
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
name: Bug Report
|
||||||
|
description: Report a bug
|
||||||
|
title: 'bug: '
|
||||||
|
labels: [bug]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Prerequisites
|
||||||
|
options:
|
||||||
|
- label:
|
||||||
|
I have searched [existing
|
||||||
|
issues](https://github.com/barrettruth/blink-cmp-tmux/issues)
|
||||||
|
required: true
|
||||||
|
- label: I have updated to the latest version
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: 'Neovim version'
|
||||||
|
description: 'Output of `nvim --version`'
|
||||||
|
render: text
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
attributes:
|
||||||
|
label: 'Operating system'
|
||||||
|
placeholder: 'e.g. Arch Linux, macOS 15, Ubuntu 24.04'
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: What happened? What did you expect?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Minimal steps to trigger the bug
|
||||||
|
value: |
|
||||||
|
1.
|
||||||
|
2.
|
||||||
|
3.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Minimal reproduction
|
||||||
|
description: |
|
||||||
|
Save the script below as `repro.lua`, edit if needed, and run:
|
||||||
|
```
|
||||||
|
nvim -u repro.lua
|
||||||
|
```
|
||||||
|
Confirm the bug reproduces with this config before submitting.
|
||||||
|
render: lua
|
||||||
|
value: |
|
||||||
|
vim.env.LAZY_STDPATH = '.repro'
|
||||||
|
load(vim.fn.system('curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua'))()
|
||||||
|
require('lazy.nvim').setup({
|
||||||
|
spec = {
|
||||||
|
{
|
||||||
|
'saghen/blink.cmp',
|
||||||
|
dependencies = {
|
||||||
|
'barrettruth/blink-cmp-tmux',
|
||||||
|
},
|
||||||
|
opts = {
|
||||||
|
sources = {
|
||||||
|
default = { 'tmux' },
|
||||||
|
providers = {
|
||||||
|
tmux = {
|
||||||
|
name = 'Tmux',
|
||||||
|
module = 'blink-cmp-tmux',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
5
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yaml
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Questions
|
||||||
|
url: https://github.com/barrettruth/blink-cmp-tmux/discussions
|
||||||
|
about: Ask questions and discuss ideas
|
||||||
30
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
30
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
name: Feature Request
|
||||||
|
description: Suggest a feature
|
||||||
|
title: 'feat: '
|
||||||
|
labels: [enhancement]
|
||||||
|
body:
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Prerequisites
|
||||||
|
options:
|
||||||
|
- label:
|
||||||
|
I have searched [existing
|
||||||
|
issues](https://github.com/barrettruth/blink-cmp-tmux/issues)
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Problem
|
||||||
|
description: What problem does this solve?
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Proposed solution
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
attributes:
|
||||||
|
label: Alternatives considered
|
||||||
21
.github/workflows/luarocks.yaml
vendored
Normal file
21
.github/workflows/luarocks.yaml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
name: luarocks
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
quality:
|
||||||
|
uses: ./.github/workflows/quality.yaml
|
||||||
|
|
||||||
|
publish:
|
||||||
|
needs: quality
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: nvim-neorocks/luarocks-tag-release@v7
|
||||||
|
env:
|
||||||
|
LUAROCKS_API_KEY: ${{ secrets.LUAROCKS_API_KEY }}
|
||||||
89
.github/workflows/quality.yaml
vendored
Normal file
89
.github/workflows/quality.yaml
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
name: quality
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
changes:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
lua: ${{ steps.changes.outputs.lua }}
|
||||||
|
markdown: ${{ steps.changes.outputs.markdown }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dorny/paths-filter@v3
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
filters: |
|
||||||
|
lua:
|
||||||
|
- 'lua/**'
|
||||||
|
- 'plugin/**'
|
||||||
|
- '*.lua'
|
||||||
|
- '.luarc.json'
|
||||||
|
- '*.toml'
|
||||||
|
markdown:
|
||||||
|
- '*.md'
|
||||||
|
|
||||||
|
lua-format:
|
||||||
|
name: Lua Format Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: changes
|
||||||
|
if: ${{ needs.changes.outputs.lua == 'true' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: JohnnyMorganz/stylua-action@v4
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
version: 2.1.0
|
||||||
|
args: --check .
|
||||||
|
|
||||||
|
lua-lint:
|
||||||
|
name: Lua Lint Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: changes
|
||||||
|
if: ${{ needs.changes.outputs.lua == 'true' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Lint with Selene
|
||||||
|
uses: NTBBloodbath/selene-action@v1.0.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --display-style quiet .
|
||||||
|
|
||||||
|
lua-typecheck:
|
||||||
|
name: Lua Type Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: changes
|
||||||
|
if: ${{ needs.changes.outputs.lua == 'true' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Run Lua LS Type Check
|
||||||
|
uses: mrcjkb/lua-typecheck-action@v0
|
||||||
|
with:
|
||||||
|
checklevel: Warning
|
||||||
|
directories: lua
|
||||||
|
configpath: .luarc.json
|
||||||
|
|
||||||
|
markdown-format:
|
||||||
|
name: Markdown Format Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: changes
|
||||||
|
if: ${{ needs.changes.outputs.markdown == 'true' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup pnpm
|
||||||
|
uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 8
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
- name: Install prettier
|
||||||
|
run: pnpm add -g prettier@3.1.0
|
||||||
|
- name: Check markdown formatting with prettier
|
||||||
|
run: prettier --check .
|
||||||
22
.github/workflows/test.yaml
vendored
Normal file
22
.github/workflows/test.yaml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
nvim: [stable, nightly]
|
||||||
|
name: Test (Neovim ${{ matrix.nvim }})
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: nvim-neorocks/nvim-busted-action@v1
|
||||||
|
with:
|
||||||
|
nvim_version: ${{ matrix.nvim }}
|
||||||
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
*.log
|
||||||
|
|
||||||
|
.*cache*
|
||||||
|
CLAUDE.md
|
||||||
|
.claude/
|
||||||
|
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
result
|
||||||
|
result-*
|
||||||
|
.direnv/
|
||||||
8
.luarc.json
Normal file
8
.luarc.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"runtime.version": "Lua 5.1",
|
||||||
|
"runtime.path": ["lua/?.lua", "lua/?/init.lua"],
|
||||||
|
"diagnostics.globals": ["vim", "jit"],
|
||||||
|
"workspace.library": ["$VIMRUNTIME/lua", "${3rd}/luv/library"],
|
||||||
|
"workspace.checkThirdParty": false,
|
||||||
|
"completion.callSnippet": "Replace"
|
||||||
|
}
|
||||||
17
.pre-commit-config.yaml
Normal file
17
.pre-commit-config.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
minimum_pre_commit_version: '3.5.0'
|
||||||
|
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/JohnnyMorganz/StyLua
|
||||||
|
rev: v2.3.1
|
||||||
|
hooks:
|
||||||
|
- id: stylua-github
|
||||||
|
name: stylua (Lua formatter)
|
||||||
|
files: \.lua$
|
||||||
|
pass_filenames: true
|
||||||
|
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||||
|
rev: v4.0.0-alpha.8
|
||||||
|
hooks:
|
||||||
|
- id: prettier
|
||||||
|
name: prettier
|
||||||
|
files: \.(md|toml|yaml|yml|sh)$
|
||||||
1
.prettierignore
Normal file
1
.prettierignore
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
||||||
9
.prettierrc
Normal file
9
.prettierrc
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"proseWrap": "always",
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true
|
||||||
|
}
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Raphael
|
||||||
|
|
||||||
|
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.
|
||||||
47
README.md
Normal file
47
README.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# blink-cmp-tmux
|
||||||
|
|
||||||
|
Tmux command completion source for
|
||||||
|
[blink.cmp](https://github.com/saghen/blink.cmp).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Completes tmux commands with full usage signatures
|
||||||
|
- Includes alias information for commands
|
||||||
|
- Shows man page descriptions in documentation
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Neovim 0.10.0+
|
||||||
|
- [blink.cmp](https://github.com/saghen/blink.cmp)
|
||||||
|
- tmux
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Install via
|
||||||
|
[luarocks](https://luarocks.org/modules/barrettruth/blink-cmp-tmux):
|
||||||
|
|
||||||
|
```
|
||||||
|
luarocks install blink-cmp-tmux
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with lazy.nvim:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
'saghen/blink.cmp',
|
||||||
|
dependencies = {
|
||||||
|
'barrettruth/blink-cmp-tmux',
|
||||||
|
},
|
||||||
|
opts = {
|
||||||
|
sources = {
|
||||||
|
default = { 'tmux' },
|
||||||
|
providers = {
|
||||||
|
tmux = {
|
||||||
|
name = 'Tmux',
|
||||||
|
module = 'blink-cmp-tmux',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
30
blink-cmp-tmux-scm-1.rockspec
Normal file
30
blink-cmp-tmux-scm-1.rockspec
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
rockspec_format = '3.0'
|
||||||
|
package = 'blink-cmp-tmux'
|
||||||
|
version = 'scm-1'
|
||||||
|
|
||||||
|
source = {
|
||||||
|
url = 'git+https://github.com/barrettruth/blink-cmp-tmux.git',
|
||||||
|
}
|
||||||
|
|
||||||
|
description = {
|
||||||
|
summary = 'Tmux command completion source for blink.cmp',
|
||||||
|
homepage = 'https://github.com/barrettruth/blink-cmp-tmux',
|
||||||
|
license = 'MIT',
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies = {
|
||||||
|
'lua >= 5.1',
|
||||||
|
}
|
||||||
|
|
||||||
|
test_dependencies = {
|
||||||
|
'nlua',
|
||||||
|
'busted >= 2.1.1',
|
||||||
|
}
|
||||||
|
|
||||||
|
test = {
|
||||||
|
type = 'busted',
|
||||||
|
}
|
||||||
|
|
||||||
|
build = {
|
||||||
|
type = 'builtin',
|
||||||
|
}
|
||||||
43
flake.lock
generated
Normal file
43
flake.lock
generated
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1771207753,
|
||||||
|
"narHash": "sha256-b9uG8yN50DRQ6A7JdZBfzq718ryYrlmGgqkRm9OOwCE=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "d1c15b7d5806069da59e819999d70e1cec0760bf",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"systems": "systems"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
35
flake.nix
Normal file
35
flake.nix
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
{
|
||||||
|
description = "blink-cmp-tmux — tmux command completion source for blink.cmp";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
systems.url = "github:nix-systems/default";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
nixpkgs,
|
||||||
|
systems,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
forEachSystem = f: nixpkgs.lib.genAttrs (import systems) (system: f nixpkgs.legacyPackages.${system});
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells = forEachSystem (pkgs: {
|
||||||
|
default = pkgs.mkShell {
|
||||||
|
packages = [
|
||||||
|
(pkgs.luajit.withPackages (
|
||||||
|
ps: with ps; [
|
||||||
|
busted
|
||||||
|
nlua
|
||||||
|
]
|
||||||
|
))
|
||||||
|
pkgs.prettier
|
||||||
|
pkgs.stylua
|
||||||
|
pkgs.selene
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
155
lua/blink-cmp-tmux.lua
Normal file
155
lua/blink-cmp-tmux.lua
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
---@class blink-cmp-tmux : blink.cmp.Source
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
---@type blink.cmp.CompletionItem[]?
|
||||||
|
local cache = nil
|
||||||
|
|
||||||
|
function M.new()
|
||||||
|
return setmetatable({}, { __index = M })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return boolean
|
||||||
|
function M.enabled()
|
||||||
|
return vim.bo.filetype == 'tmux'
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return table<string, string>
|
||||||
|
local function parse_descriptions()
|
||||||
|
local result = vim.system({ 'bash', '-c', 'MANWIDTH=80 man -P cat tmux 2>/dev/null' }):wait()
|
||||||
|
local stdout = result.stdout or ''
|
||||||
|
local lines = {}
|
||||||
|
for line in (stdout .. '\n'):gmatch('(.-)\n') do
|
||||||
|
lines[#lines + 1] = line
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd_result = vim.system({ 'tmux', 'list-commands', '-F', '#{command_list_name}' }):wait()
|
||||||
|
local cmds = {}
|
||||||
|
for name in (cmd_result.stdout or ''):gmatch('[^\n]+') do
|
||||||
|
cmds[name] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local defs = {}
|
||||||
|
for i, line in ipairs(lines) do
|
||||||
|
local cmd = line:match('^ ([a-z][a-z-]+)')
|
||||||
|
if cmd and cmds[cmd] then
|
||||||
|
local rest = line:sub(8 + #cmd)
|
||||||
|
if rest == '' or rest:match('^%s+%[') or rest:match('^%s%s+') then
|
||||||
|
defs[#defs + 1] = { line = i, cmd = cmd }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local descs = {}
|
||||||
|
for idx, def in ipairs(defs) do
|
||||||
|
local block_end = (defs[idx + 1] and defs[idx + 1].line or #lines) - 1
|
||||||
|
local j = def.line + 1
|
||||||
|
while j <= block_end do
|
||||||
|
local l = lines[j]
|
||||||
|
if l:match('^%s+%(alias:') then
|
||||||
|
j = j + 1
|
||||||
|
elseif l:match('^ ') then
|
||||||
|
local stripped = vim.trim(l)
|
||||||
|
if stripped == '' or stripped:match('[%[%]]') then
|
||||||
|
j = j + 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
elseif vim.trim(l) == '' then
|
||||||
|
j = j + 1
|
||||||
|
else
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local desc_lines = {}
|
||||||
|
for k = j, block_end do
|
||||||
|
desc_lines[#desc_lines + 1] = lines[k]
|
||||||
|
end
|
||||||
|
local paragraphs = { {} }
|
||||||
|
for _, dl in ipairs(desc_lines) do
|
||||||
|
local stripped = vim.trim(dl)
|
||||||
|
if stripped == '' then
|
||||||
|
if #paragraphs[#paragraphs] > 0 then
|
||||||
|
paragraphs[#paragraphs + 1] = {}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local para = paragraphs[#paragraphs]
|
||||||
|
para[#para + 1] = stripped
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local parts = {}
|
||||||
|
for _, para in ipairs(paragraphs) do
|
||||||
|
if #para > 0 then
|
||||||
|
parts[#parts + 1] = table.concat(para, ' ')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local desc = table.concat(parts, '\n\n')
|
||||||
|
desc = desc:gsub('\xe2\x80\x90 ', '')
|
||||||
|
desc = desc:gsub(' +', ' ')
|
||||||
|
if desc ~= '' then
|
||||||
|
descs[def.cmd] = desc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return descs
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param output string
|
||||||
|
---@param descs table<string, string>
|
||||||
|
---@return blink.cmp.CompletionItem[]
|
||||||
|
local function parse(output, descs)
|
||||||
|
local Kind = require('blink.cmp.types').CompletionItemKind
|
||||||
|
local items = {}
|
||||||
|
for line in output:gmatch('[^\n]+') do
|
||||||
|
local name, alias = line:match('^([a-z-]+)%s+%(([a-z-]+)%)')
|
||||||
|
if not name then
|
||||||
|
name = line:match('^([a-z-]+)')
|
||||||
|
end
|
||||||
|
if name then
|
||||||
|
local doc_parts = {}
|
||||||
|
if alias then
|
||||||
|
doc_parts[#doc_parts + 1] = ('**alias**: `%s`\n'):format(alias)
|
||||||
|
end
|
||||||
|
doc_parts[#doc_parts + 1] = '```\n' .. line .. '\n```'
|
||||||
|
if descs[name] then
|
||||||
|
doc_parts[#doc_parts + 1] = '\n---\n\n' .. descs[name]
|
||||||
|
end
|
||||||
|
items[#items + 1] = {
|
||||||
|
label = name,
|
||||||
|
kind = Kind.Keyword,
|
||||||
|
documentation = {
|
||||||
|
kind = 'markdown',
|
||||||
|
value = table.concat(doc_parts),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return items
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param ctx blink.cmp.Context
|
||||||
|
---@param callback fun(response: blink.cmp.CompletionResponse)
|
||||||
|
---@return fun()
|
||||||
|
function M:get_completions(ctx, callback)
|
||||||
|
if not cache then
|
||||||
|
local ok, descs = pcall(parse_descriptions)
|
||||||
|
if not ok then
|
||||||
|
descs = {}
|
||||||
|
end
|
||||||
|
local result = vim.system({ 'tmux', 'list-commands' }):wait()
|
||||||
|
cache = parse(result.stdout or '', descs)
|
||||||
|
end
|
||||||
|
|
||||||
|
local before = ctx.line:sub(1, ctx.cursor[2])
|
||||||
|
if before:match('^%s*[a-z-]*$') then
|
||||||
|
callback({
|
||||||
|
is_incomplete_forward = false,
|
||||||
|
is_incomplete_backward = false,
|
||||||
|
items = vim.deepcopy(cache),
|
||||||
|
})
|
||||||
|
else
|
||||||
|
callback({ items = {} })
|
||||||
|
end
|
||||||
|
return function() end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
1
selene.toml
Normal file
1
selene.toml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
std = 'vim'
|
||||||
54
spec/helpers.lua
Normal file
54
spec/helpers.lua
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
local plugin_dir = vim.fn.getcwd()
|
||||||
|
vim.opt.runtimepath:prepend(plugin_dir)
|
||||||
|
|
||||||
|
if not package.loaded['blink.cmp.types'] then
|
||||||
|
package.loaded['blink.cmp.types'] = {
|
||||||
|
CompletionItemKind = {
|
||||||
|
Text = 1,
|
||||||
|
Method = 2,
|
||||||
|
Function = 3,
|
||||||
|
Constructor = 4,
|
||||||
|
Field = 5,
|
||||||
|
Variable = 6,
|
||||||
|
Class = 7,
|
||||||
|
Interface = 8,
|
||||||
|
Module = 9,
|
||||||
|
Property = 10,
|
||||||
|
Unit = 11,
|
||||||
|
Value = 12,
|
||||||
|
Enum = 13,
|
||||||
|
Keyword = 14,
|
||||||
|
Snippet = 15,
|
||||||
|
Color = 16,
|
||||||
|
File = 17,
|
||||||
|
Reference = 18,
|
||||||
|
Folder = 19,
|
||||||
|
EnumMember = 20,
|
||||||
|
Constant = 21,
|
||||||
|
Struct = 22,
|
||||||
|
Event = 23,
|
||||||
|
Operator = 24,
|
||||||
|
TypeParameter = 25,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.create_buffer(lines, filetype)
|
||||||
|
local bufnr = vim.api.nvim_create_buf(false, true)
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines or {})
|
||||||
|
if filetype then
|
||||||
|
vim.api.nvim_set_option_value('filetype', filetype, { buf = bufnr })
|
||||||
|
end
|
||||||
|
vim.api.nvim_set_current_buf(bufnr)
|
||||||
|
return bufnr
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.delete_buffer(bufnr)
|
||||||
|
if bufnr and vim.api.nvim_buf_is_valid(bufnr) then
|
||||||
|
vim.api.nvim_buf_delete(bufnr, { force = true })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
34
spec/minimal_init.lua
Normal file
34
spec/minimal_init.lua
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
vim.cmd([[set runtimepath=$VIMRUNTIME]])
|
||||||
|
vim.opt.runtimepath:append('.')
|
||||||
|
vim.opt.packpath = {}
|
||||||
|
vim.opt.loadplugins = false
|
||||||
|
|
||||||
|
package.loaded['blink.cmp.types'] = {
|
||||||
|
CompletionItemKind = {
|
||||||
|
Text = 1,
|
||||||
|
Method = 2,
|
||||||
|
Function = 3,
|
||||||
|
Constructor = 4,
|
||||||
|
Field = 5,
|
||||||
|
Variable = 6,
|
||||||
|
Class = 7,
|
||||||
|
Interface = 8,
|
||||||
|
Module = 9,
|
||||||
|
Property = 10,
|
||||||
|
Unit = 11,
|
||||||
|
Value = 12,
|
||||||
|
Enum = 13,
|
||||||
|
Keyword = 14,
|
||||||
|
Snippet = 15,
|
||||||
|
Color = 16,
|
||||||
|
File = 17,
|
||||||
|
Reference = 18,
|
||||||
|
Folder = 19,
|
||||||
|
EnumMember = 20,
|
||||||
|
Constant = 21,
|
||||||
|
Struct = 22,
|
||||||
|
Event = 23,
|
||||||
|
Operator = 24,
|
||||||
|
TypeParameter = 25,
|
||||||
|
},
|
||||||
|
}
|
||||||
166
spec/tmux_spec.lua
Normal file
166
spec/tmux_spec.lua
Normal file
|
|
@ -0,0 +1,166 @@
|
||||||
|
local helpers = require('spec.helpers')
|
||||||
|
|
||||||
|
local TMUX_COMMANDS = table.concat({
|
||||||
|
'bind-key (bind) [-lnrN:T:] key command [arguments]',
|
||||||
|
'display-message (display) [-aINpv] [-c target-client] [-d delay] [-t target-pane] [message]',
|
||||||
|
'set-option (set) [-aFgopqsuUw] [-t target-pane] option [value]',
|
||||||
|
}, '\n')
|
||||||
|
|
||||||
|
local TMUX_NAMES = 'bind-key\ndisplay-message\nset-option\n'
|
||||||
|
|
||||||
|
local MAN_PAGE = table.concat({
|
||||||
|
' bind-key [-lnrN:T:] key command [arguments]',
|
||||||
|
' (alias: bind)',
|
||||||
|
'',
|
||||||
|
' Bind a key to a command.',
|
||||||
|
'',
|
||||||
|
' display-message [-aINpv] [-c target-client] [-d delay] [-t target-pane] [message]',
|
||||||
|
' (alias: display)',
|
||||||
|
'',
|
||||||
|
' Display a message.',
|
||||||
|
'',
|
||||||
|
' set-option [-aFgopqsuUw] [-t target-pane] option [value]',
|
||||||
|
' (alias: set)',
|
||||||
|
'',
|
||||||
|
' Set a window option.',
|
||||||
|
}, '\n')
|
||||||
|
|
||||||
|
local function mock_system()
|
||||||
|
local original = vim.system
|
||||||
|
---@diagnostic disable-next-line: duplicate-set-field
|
||||||
|
vim.system = function(cmd)
|
||||||
|
if cmd[1] == 'bash' then
|
||||||
|
return {
|
||||||
|
wait = function()
|
||||||
|
return { stdout = MAN_PAGE, code = 0 }
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
elseif cmd[1] == 'tmux' and cmd[2] == 'list-commands' then
|
||||||
|
if cmd[3] == '-F' then
|
||||||
|
return {
|
||||||
|
wait = function()
|
||||||
|
return { stdout = TMUX_NAMES, code = 0 }
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
wait = function()
|
||||||
|
return { stdout = TMUX_COMMANDS, code = 0 }
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
wait = function()
|
||||||
|
return { stdout = '', code = 1 }
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return function()
|
||||||
|
vim.system = original
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe('blink-cmp-tmux', function()
|
||||||
|
local restore
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
package.loaded['blink-cmp-tmux'] = nil
|
||||||
|
restore = mock_system()
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
if restore then
|
||||||
|
restore()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('enabled', function()
|
||||||
|
it('returns true for tmux filetype', function()
|
||||||
|
local bufnr = helpers.create_buffer({}, 'tmux')
|
||||||
|
local source = require('blink-cmp-tmux')
|
||||||
|
assert.is_true(source.enabled())
|
||||||
|
helpers.delete_buffer(bufnr)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('returns false for other filetypes', function()
|
||||||
|
local bufnr = helpers.create_buffer({}, 'lua')
|
||||||
|
local source = require('blink-cmp-tmux')
|
||||||
|
assert.is_false(source.enabled())
|
||||||
|
helpers.delete_buffer(bufnr)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('get_completions', function()
|
||||||
|
it('returns items with Keyword kind', function()
|
||||||
|
local source = require('blink-cmp-tmux').new()
|
||||||
|
local items
|
||||||
|
source:get_completions({ line = '', cursor = { 1, 0 } }, function(response)
|
||||||
|
items = response.items
|
||||||
|
end)
|
||||||
|
assert.is_not_nil(items)
|
||||||
|
assert.equals(3, #items)
|
||||||
|
for _, item in ipairs(items) do
|
||||||
|
assert.equals(14, item.kind)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('returns items on empty line', function()
|
||||||
|
local source = require('blink-cmp-tmux').new()
|
||||||
|
local items
|
||||||
|
source:get_completions({ line = '', cursor = { 1, 0 } }, function(response)
|
||||||
|
items = response.items
|
||||||
|
end)
|
||||||
|
assert.equals(3, #items)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('returns items when typing command prefix', function()
|
||||||
|
local source = require('blink-cmp-tmux').new()
|
||||||
|
local items
|
||||||
|
source:get_completions({ line = 'bind', cursor = { 1, 4 } }, function(response)
|
||||||
|
items = response.items
|
||||||
|
end)
|
||||||
|
assert.is_true(#items > 0)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('returns empty after command arguments', function()
|
||||||
|
local source = require('blink-cmp-tmux').new()
|
||||||
|
local items
|
||||||
|
source:get_completions({ line = 'bind-key -n ', cursor = { 1, 12 } }, function(response)
|
||||||
|
items = response.items
|
||||||
|
end)
|
||||||
|
assert.equals(0, #items)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('includes documentation with man page description', function()
|
||||||
|
local source = require('blink-cmp-tmux').new()
|
||||||
|
local items
|
||||||
|
source:get_completions({ line = '', cursor = { 1, 0 } }, function(response)
|
||||||
|
items = response.items
|
||||||
|
end)
|
||||||
|
local bind = vim.iter(items):find(function(item)
|
||||||
|
return item.label == 'bind-key'
|
||||||
|
end)
|
||||||
|
assert.is_not_nil(bind)
|
||||||
|
assert.is_not_nil(bind.documentation)
|
||||||
|
assert.is_truthy(bind.documentation.value:find('Bind a key'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('includes alias in documentation', function()
|
||||||
|
local source = require('blink-cmp-tmux').new()
|
||||||
|
local items
|
||||||
|
source:get_completions({ line = '', cursor = { 1, 0 } }, function(response)
|
||||||
|
items = response.items
|
||||||
|
end)
|
||||||
|
local bind = vim.iter(items):find(function(item)
|
||||||
|
return item.label == 'bind-key'
|
||||||
|
end)
|
||||||
|
assert.is_truthy(bind.documentation.value:find('alias'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('returns a cancel function', function()
|
||||||
|
local source = require('blink-cmp-tmux').new()
|
||||||
|
local cancel = source:get_completions({ line = '', cursor = { 1, 0 } }, function() end)
|
||||||
|
assert.is_function(cancel)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
8
stylua.toml
Normal file
8
stylua.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
column_width = 100
|
||||||
|
line_endings = "Unix"
|
||||||
|
indent_type = "Spaces"
|
||||||
|
indent_width = 2
|
||||||
|
quote_style = "AutoPreferSingle"
|
||||||
|
call_parentheses = "Always"
|
||||||
|
[sort_requires]
|
||||||
|
enabled = true
|
||||||
33
vim.toml
Normal file
33
vim.toml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
[selene]
|
||||||
|
base = "lua51"
|
||||||
|
name = "vim"
|
||||||
|
|
||||||
|
[vim]
|
||||||
|
any = true
|
||||||
|
|
||||||
|
[jit]
|
||||||
|
any = true
|
||||||
|
|
||||||
|
[bit]
|
||||||
|
any = true
|
||||||
|
|
||||||
|
[assert]
|
||||||
|
any = true
|
||||||
|
|
||||||
|
[describe]
|
||||||
|
any = true
|
||||||
|
|
||||||
|
[it]
|
||||||
|
any = true
|
||||||
|
|
||||||
|
[before_each]
|
||||||
|
any = true
|
||||||
|
|
||||||
|
[after_each]
|
||||||
|
any = true
|
||||||
|
|
||||||
|
[spy]
|
||||||
|
any = true
|
||||||
|
|
||||||
|
[stub]
|
||||||
|
any = true
|
||||||
Loading…
Add table
Add a link
Reference in a new issue