├── .gitignore ├── LICENSE ├── README.md ├── denops └── @ddc-sources │ └── tmux.ts └── doc └── ddc-tmux.txt /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/b0012e4930d0a8c350254a3caeedf7441ea286a3/Global/Vim.gitignore 2 | 3 | # Swap 4 | [._]*.s[a-v][a-z] 5 | !*.svg # comment out if you don't need vector files 6 | [._]*.sw[a-p] 7 | [._]s[a-rt-v][a-z] 8 | [._]ss[a-gi-z] 9 | [._]sw[a-p] 10 | 11 | # Session 12 | Session.vim 13 | Sessionx.vim 14 | 15 | # Temporary 16 | .netrwhist 17 | *~ 18 | # Auto-generated tag files 19 | tags 20 | # Persistent undo 21 | [._]*.un~ 22 | 23 | 24 | ### https://raw.github.com/github/gitignore/b0012e4930d0a8c350254a3caeedf7441ea286a3/Node.gitignore 25 | 26 | # Logs 27 | logs 28 | *.log 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | lerna-debug.log* 33 | .pnpm-debug.log* 34 | 35 | # Diagnostic reports (https://nodejs.org/api/report.html) 36 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 37 | 38 | # Runtime data 39 | pids 40 | *.pid 41 | *.seed 42 | *.pid.lock 43 | 44 | # Directory for instrumented libs generated by jscoverage/JSCover 45 | lib-cov 46 | 47 | # Coverage directory used by tools like istanbul 48 | coverage 49 | *.lcov 50 | 51 | # nyc test coverage 52 | .nyc_output 53 | 54 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 55 | .grunt 56 | 57 | # Bower dependency directory (https://bower.io/) 58 | bower_components 59 | 60 | # node-waf configuration 61 | .lock-wscript 62 | 63 | # Compiled binary addons (https://nodejs.org/api/addons.html) 64 | build/Release 65 | 66 | # Dependency directories 67 | node_modules/ 68 | jspm_packages/ 69 | 70 | # Snowpack dependency directory (https://snowpack.dev/) 71 | web_modules/ 72 | 73 | # TypeScript cache 74 | *.tsbuildinfo 75 | 76 | # Optional npm cache directory 77 | .npm 78 | 79 | # Optional eslint cache 80 | .eslintcache 81 | 82 | # Microbundle cache 83 | .rpt2_cache/ 84 | .rts2_cache_cjs/ 85 | .rts2_cache_es/ 86 | .rts2_cache_umd/ 87 | 88 | # Optional REPL history 89 | .node_repl_history 90 | 91 | # Output of 'npm pack' 92 | *.tgz 93 | 94 | # Yarn Integrity file 95 | .yarn-integrity 96 | 97 | # dotenv environment variables file 98 | .env 99 | .env.test 100 | .env.production 101 | 102 | # parcel-bundler cache (https://parceljs.org/) 103 | .cache 104 | .parcel-cache 105 | 106 | # Next.js build output 107 | .next 108 | out 109 | 110 | # Nuxt.js build / generate output 111 | .nuxt 112 | dist 113 | 114 | # Gatsby files 115 | .cache/ 116 | # Comment in the public line in if your project uses Gatsby and not Next.js 117 | # https://nextjs.org/blog/next-9-1#public-directory-support 118 | # public 119 | 120 | # vuepress build output 121 | .vuepress/dist 122 | 123 | # Serverless directories 124 | .serverless/ 125 | 126 | # FuseBox cache 127 | .fusebox/ 128 | 129 | # DynamoDB Local files 130 | .dynamodb/ 131 | 132 | # TernJS port file 133 | .tern-port 134 | 135 | # Stores VSCode versions used for testing VSCode extensions 136 | .vscode-test 137 | 138 | # yarn v2 139 | .yarn/cache 140 | .yarn/unplugged 141 | .yarn/build-state.yml 142 | .yarn/install-state.gz 143 | .pnp.* 144 | 145 | 146 | ### https://raw.github.com/github/gitignore/b0012e4930d0a8c350254a3caeedf7441ea286a3/Global/macOS.gitignore 147 | 148 | # General 149 | .DS_Store 150 | .AppleDouble 151 | .LSOverride 152 | 153 | # Icon must end with two \r 154 | Icon 155 | 156 | # Thumbnails 157 | ._* 158 | 159 | # Files that might appear in the root of a volume 160 | .DocumentRevisions-V100 161 | .fseventsd 162 | .Spotlight-V100 163 | .TemporaryItems 164 | .Trashes 165 | .VolumeIcon.icns 166 | .com.apple.timemachine.donotpresent 167 | 168 | # Directories potentially created on remote AFP share 169 | .AppleDB 170 | .AppleDesktop 171 | Network Trash Folder 172 | Temporary Items 173 | .apdisk 174 | 175 | 176 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | License: MIT license 2 | AUTHOR: Shougo Matsushita 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ddc-tmux 2 | 3 | Around completion for ddc.vim 4 | 5 | This source collects candidates from all panes of [tmux](https://github.com/tmux/tmux). 6 | 7 | ## Required 8 | 9 | ### denops.vim 10 | 11 | https://github.com/vim-denops/denops.vim 12 | 13 | ### ddc.vim 14 | 15 | https://github.com/Shougo/ddc.vim 16 | 17 | ### tmux 18 | 19 | https://github.com/tmux/tmux 20 | 21 | ## Configuration 22 | 23 | ```vim 24 | " Use around source. 25 | call ddc#custom#patch_global('sources', ['tmux']) 26 | 27 | " Change source options 28 | call ddc#custom#patch_global('sourceOptions', { 29 | \ 'tmux': {'mark': 'T'}, 30 | \ }) 31 | ``` 32 | -------------------------------------------------------------------------------- /denops/@ddc-sources/tmux.ts: -------------------------------------------------------------------------------- 1 | import type { Denops } from "jsr:@denops/std@^7.1.1"; 2 | import * as fn from "jsr:@denops/std@^7.1.1/function"; 3 | import { 4 | BaseSource, 5 | type GatherArguments, 6 | type GetCompletePositionArguments, 7 | type OnInitArguments, 8 | } from "jsr:@shougo/ddc-vim@^7.0.0/source"; 9 | import type { Item } from "jsr:@shougo/ddc-vim@^7.0.0/types"; 10 | 11 | type Params = { 12 | currentWinOnly: boolean; 13 | excludeCurrentPane: boolean; 14 | executable: string; 15 | kindFormat: string; 16 | }; 17 | 18 | interface PaneInfo { 19 | kind: string; 20 | id: string; 21 | } 22 | 23 | const DECODER = new TextDecoder(); 24 | const SEP = "\x1f"; // U+001F UNIT SEPARATOR 25 | 26 | export class Source extends BaseSource { 27 | #available = false; 28 | #executable = ""; 29 | 30 | override async onInit( 31 | { denops, sourceParams }: OnInitArguments, 32 | ): Promise { 33 | const { executable } = sourceParams; 34 | if (typeof executable !== "string") { 35 | await this.#printError(denops, "executable should be a string"); 36 | return; 37 | } 38 | if ((await fn.executable(denops, executable)) !== 1) { 39 | await this.#printError(denops, "executable not found"); 40 | return; 41 | } 42 | this.#available = true; 43 | this.#executable = executable; 44 | } 45 | 46 | override getCompletePosition( 47 | { context }: GetCompletePositionArguments, 48 | ): number { 49 | return context.input.search(/[-_\p{L}\d]+$/u); 50 | } 51 | 52 | override async gather( 53 | { sourceParams }: GatherArguments, 54 | ): Promise { 55 | if (!this.#available) { 56 | return []; 57 | } 58 | const paneInfos = await this.#panes(sourceParams); 59 | const results = await Promise.all( 60 | paneInfos.map(({ kind, id }) => 61 | this.#capturePane(id).then((result) => ({ kind, result })) 62 | ), 63 | ); 64 | return results.reduce( 65 | (a, { kind, result }) => 66 | a.concat(this.#allWords(result).map((word) => ({ word, kind }))), 67 | [], 68 | ); 69 | } 70 | 71 | override params(): Params { 72 | return { 73 | currentWinOnly: false, 74 | excludeCurrentPane: false, 75 | executable: "tmux", 76 | kindFormat: "#{session_name}:#{window_index}.#{pane_index}", 77 | }; 78 | } 79 | 80 | async #runCmd(args: string[]): Promise { 81 | const { success, stdout, stderr } = await new Deno.Command( 82 | this.#executable, 83 | { args, stdout: "piped", stderr: "piped" }, 84 | ).output(); 85 | if (success) { 86 | return DECODER.decode(stdout).split(/\n/); 87 | } 88 | throw new Error(DECODER.decode(stderr)); 89 | } 90 | 91 | async #panes( 92 | { currentWinOnly, excludeCurrentPane, kindFormat }: Params, 93 | ): Promise { 94 | const lines = await this.#runCmd([ 95 | "list-panes", 96 | "-F", 97 | [kindFormat, "#D", "#{pane_active}"].join(SEP), 98 | ...(currentWinOnly ? [] : ["-a"]), 99 | ]).catch((e: unknown) => { 100 | if (e instanceof Error && /no server running/.test(e.message)) { 101 | return []; 102 | } 103 | throw e; 104 | }); 105 | return lines.reduce((a, b) => { 106 | const cells = b.split(SEP); 107 | if (cells.length === 3) { 108 | const [kind, id, paneActive] = cells; 109 | if (!excludeCurrentPane || paneActive !== "1") { 110 | a.push({ kind, id }); 111 | } 112 | } 113 | return a; 114 | }, []); 115 | } 116 | 117 | async #capturePane(id: string): Promise { 118 | return await this.#runCmd(["capture-pane", "-p", "-J", "-t", id]); 119 | } 120 | 121 | #allWords(lines: string[]): string[] { 122 | const words = lines 123 | .flatMap((line) => [...line.matchAll(/[-_\p{L}\d]+/gu)]) 124 | .map((match) => match[0]); 125 | return Array.from(new Set(words)); // remove duplication 126 | } 127 | 128 | async #printError(denops: Denops, message: string): Promise { 129 | await denops.call("ddc#util#print_error", message, "ddc-tmux"); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /doc/ddc-tmux.txt: -------------------------------------------------------------------------------- 1 | *ddc-tmux.txt* Tmux completion for ddc.vim 2 | 3 | Author: delphinus 4 | License: MIT license 5 | 6 | CONTENTS *ddc-tmux-contents* 7 | 8 | Introduction |ddc-tmux-introduction| 9 | Install |ddc-tmux-install| 10 | Examples |ddc-tmux-examples| 11 | Params |ddc-tmux-params| 12 | 13 | 14 | ============================================================================== 15 | INTRODUCTION *ddc-tmux-introduction* 16 | 17 | This source collects candidates from words in Tmux panes. 18 | 19 | 20 | ============================================================================== 21 | INSTALL *ddc-tmux-install* 22 | 23 | Please install both "ddc.vim" and "denops.vim". 24 | 25 | https://github.com/Shougo/ddc.vim 26 | https://github.com/vim-denops/denops.vim 27 | 28 | And install Tmux. 29 | 30 | https://github.com/tmux/tmux 31 | 32 | 33 | ============================================================================== 34 | EXAMPLES *ddc-tmux-examples* 35 | > 36 | " Use tmux source. 37 | call ddc#custom#patch_global('sources', ['tmux']) 38 | 39 | " Change source options 40 | call ddc#custom#patch_global('sourceOptions', { 41 | \ 'tmux': {'mark': 'T'}, 42 | \ }) 43 | 44 | " Set a valid path for the executable 45 | call ddc#custom#patch_global('sourceParams', { 46 | \ 'tmux': {'executable': '/usr/local/bin/tmux'}, 47 | \ }) 48 | < 49 | This shows such completions below. 50 | > 51 | v Cursor position 52 | ins| 53 | instance 0:2.1 [T] 54 | instructions 0:3.1 [T] 55 | in_progress 0:2.1 [T] 56 | initialSteps 0:2.1 [T] 57 | invalid_params 0:2.1 [T] 58 | 59 | Digits in the kind means `${session_name}:${window_index}.${pane_index}`. This 60 | is the same as ones `tmux list-panes` shows in default. The format can be 61 | changed by |ddc-tmux-param-kindFormat|. 62 | 63 | 64 | ============================================================================== 65 | PARAMS *ddc-tmux-params* 66 | 67 | *ddc-tmux-param-currentWinOnly* 68 | currentWinOnly (boolean) 69 | If true, it gathers candidates only from panes on the 70 | current window. 71 | 72 | Default: `v:false` 73 | 74 | *ddc-tmux-param-excludeCurrentPane* 75 | excludeCurrentPane (boolean) 76 | If true, it gathers candidates from panes other than 77 | the current one. This is the default behavior by 78 | https://github.com/wellle/tmux-complete.vim 79 | 80 | Default: `v:false` 81 | 82 | *ddc-tmux-param-executable* 83 | executable (string) 84 | Path for the executable of Tmux. 85 | 86 | Default: `"tmux"` 87 | 88 | *ddc-tmux-param-kindFormat* 89 | kindFormat (string) 90 | Format string to show in the kind of candidates. This 91 | is passed to `tmux list-panes -F` as it is. You can know 92 | available attributes from FORMATS section in Tmux man 93 | pages. 94 | 95 | Default: 96 | `"#{session_name}:#{window_index}.#{pane_index}"` 97 | 98 | 99 | ============================================================================== 100 | vim:tw=78:ts=8:ft=help:norl:noet:fen:noet: 101 | --------------------------------------------------------------------------------