├── LICENSE ├── README.md ├── denops └── @ddu-sources │ └── file_rec.ts └── doc └── ddu-source-file_rec.txt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT license 2 | 3 | Copyright (c) Shougo Matsushita 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ddu-source-file_rec 2 | 3 | File recursive source for ddu.vim 4 | 5 | This source collects files in the path recursively. 6 | 7 | ## Required 8 | 9 | ### denops.vim 10 | 11 | https://github.com/vim-denops/denops.vim 12 | 13 | ### ddu.vim 14 | 15 | https://github.com/Shougo/ddu.vim 16 | 17 | ### ddu-kind-file 18 | 19 | https://github.com/Shougo/ddu-kind-file 20 | 21 | ## Configuration 22 | 23 | ```vim 24 | call ddu#start(#{ sources: [#{ name: 'file_rec' }] }) 25 | 26 | " Change base path. 27 | " NOTE: "path" must be full path. 28 | call ddu#custom#patch_global('sourceOptions', #{ 29 | \ file_rec: #{ path: expand("~") }, 30 | \ }) 31 | ``` 32 | -------------------------------------------------------------------------------- /denops/@ddu-sources/file_rec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type Context, 3 | type Item, 4 | type SourceOptions, 5 | } from "jsr:@shougo/ddu-vim@~9.4.0/types"; 6 | import { BaseSource } from "jsr:@shougo/ddu-vim@~9.4.0/source"; 7 | import { treePath2Filename } from "jsr:@shougo/ddu-vim@~9.4.0/utils"; 8 | 9 | import { type ActionData } from "jsr:@shougo/ddu-kind-file@~0.9.0"; 10 | 11 | import type { Denops } from "jsr:@denops/core@~7.0.0"; 12 | 13 | import { join } from "jsr:@std/path@~1.0.3/join"; 14 | import { resolve } from "jsr:@std/path@~1.0.3/resolve"; 15 | import { relative } from "jsr:@std/path@~1.0.3/relative"; 16 | import { abortable } from "jsr:@std/async@~1.0.4/abortable"; 17 | 18 | type Params = { 19 | chunkSize: 1000; 20 | ignoredDirectories: string[]; 21 | expandSymbolicLink: boolean; 22 | }; 23 | 24 | type Args = { 25 | denops: Denops; 26 | context: Context; 27 | sourceOptions: SourceOptions; 28 | sourceParams: Params; 29 | }; 30 | 31 | export class Source extends BaseSource { 32 | override kind = "file"; 33 | 34 | override gather( 35 | { sourceOptions, sourceParams, context }: Args, 36 | ): ReadableStream[]> { 37 | const abortController = new AbortController(); 38 | 39 | return new ReadableStream({ 40 | async start(controller) { 41 | const root = treePath2Filename( 42 | sourceOptions.path.length != 0 ? sourceOptions.path : context.path, 43 | ); 44 | const it = walk( 45 | resolve(root, root), 46 | sourceParams.ignoredDirectories, 47 | abortController.signal, 48 | sourceParams.chunkSize, 49 | sourceParams.expandSymbolicLink, 50 | ); 51 | let enqueueSize: number = sourceParams.chunkSize; 52 | let items: Item[] = []; 53 | try { 54 | for await (const chunk of it) { 55 | items = items.concat(chunk); 56 | if (items.length >= enqueueSize) { 57 | enqueueSize = 10 * sourceParams.chunkSize; 58 | controller.enqueue(items); 59 | items = []; 60 | } 61 | } 62 | if (items.length) { 63 | controller.enqueue(items); 64 | } 65 | } catch (e: unknown) { 66 | if (e instanceof DOMException) { 67 | return; 68 | } 69 | console.error(e); 70 | } finally { 71 | controller.close(); 72 | } 73 | }, 74 | 75 | cancel(reason): void { 76 | abortController.abort(reason); 77 | }, 78 | }); 79 | } 80 | 81 | override params(): Params { 82 | return { 83 | chunkSize: 1000, 84 | ignoredDirectories: [".git"], 85 | expandSymbolicLink: false, 86 | }; 87 | } 88 | } 89 | 90 | async function* walk( 91 | root: string, 92 | ignoredDirectories: string[], 93 | signal: AbortSignal, 94 | chunkSize: number, 95 | expandSymbolicLink: boolean, 96 | ): AsyncGenerator[]> { 97 | const walk = async function* ( 98 | dir: string, 99 | ): AsyncGenerator[]> { 100 | let chunk: Item[] = []; 101 | try { 102 | for await (const entry of abortable(Deno.readDir(dir), signal)) { 103 | const abspath = join(dir, entry.name); 104 | const stat = await readStat(abspath, expandSymbolicLink); 105 | 106 | if (stat === null) { 107 | // Skip invalid files 108 | continue; 109 | } 110 | 111 | if (!stat.isDirectory) { 112 | const n = chunk.push({ 113 | word: relative(root, abspath), 114 | action: { 115 | path: abspath, 116 | isDirectory: false, 117 | }, 118 | }); 119 | if (n >= chunkSize) { 120 | yield chunk; 121 | chunk = []; 122 | } 123 | } else if (ignoredDirectories.includes(entry.name)) { 124 | continue; 125 | } else if ( 126 | stat.isSymlink && stat.isDirectory && 127 | abspath.includes(await Deno.realPath(abspath)) 128 | ) { 129 | // Looped link 130 | continue; 131 | } else { 132 | yield* walk(abspath); 133 | } 134 | } 135 | if (chunk.length) { 136 | yield chunk; 137 | } 138 | } catch (e: unknown) { 139 | if (e instanceof Deno.errors.PermissionDenied) { 140 | // Ignore this error 141 | // See https://github.com/Shougo/ddu-source-file_rec/issues/2 142 | return; 143 | } 144 | throw e; 145 | } 146 | }; 147 | yield* walk(root); 148 | } 149 | 150 | async function readStat( 151 | path: string, 152 | expandSymbolicLink: boolean, 153 | ): Promise { 154 | try { 155 | const stat = await Deno.lstat(path); 156 | if (stat.isSymlink && expandSymbolicLink) { 157 | return { 158 | ...(await Deno.stat(path)), 159 | isSymlink: true, 160 | }; 161 | } 162 | return stat; 163 | } catch (_: unknown) { 164 | // Ignore stat exception 165 | return null; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /doc/ddu-source-file_rec.txt: -------------------------------------------------------------------------------- 1 | *ddu-source-file_rec.txt* File recursive source for ddu.vim 2 | 3 | Author: Shougo 4 | License: MIT license 5 | 6 | CONTENTS *ddu-source-file_rec-contents* 7 | 8 | Introduction |ddu-source-file_rec-introduction| 9 | Install |ddu-source-file_rec-install| 10 | Examples |ddu-source-file_rec-examples| 11 | Params |ddu-source-file_rec-params| 12 | FAQ |ddu-source-file_rec-faq| 13 | 14 | 15 | ============================================================================== 16 | INTRODUCTION *ddu-source-file_rec-introduction* 17 | 18 | This source collects files in the path recursively. 19 | 20 | 21 | ============================================================================== 22 | INSTALL *ddu-source-file_rec-install* 23 | 24 | Please install both "ddu.vim" and "denops.vim" and "ddu-kind-file". 25 | 26 | https://github.com/Shougo/ddu.vim 27 | https://github.com/vim-denops/denops.vim 28 | https://github.com/Shougo/ddu-kind-file 29 | 30 | 31 | ============================================================================== 32 | EXAMPLES *ddu-source-file_rec-examples* 33 | > 34 | call ddu#start(#{ sources: [#{ name: 'file_rec' }] }) 35 | 36 | " Change base path. 37 | " NOTE: "path" must be full path. 38 | call ddu#custom#patch_global('sourceOptions', #{ 39 | \ file_rec: #{ path: expand("~") }, 40 | \ }) 41 | < 42 | 43 | ============================================================================== 44 | PARAMS *ddu-source-file_rec-params* 45 | 46 | *ddu-source-file_rec-param-chunkSize* 47 | chunkSize (number) 48 | The gather files chunkSize. 49 | 50 | Default: 1000 51 | 52 | *ddu-source-file_rec-param-ignoredDirectories* 53 | ignoredDirectories (string[]) 54 | Ignored directories list. 55 | Note: It must be directory name. 56 | 57 | Default: ".git" 58 | 59 | *ddu-source-file_rec-param-expandSymbolicLink* 60 | expandSymbolicLink (bool) 61 | When true, It searches within a directory pointed to by 62 | searched symbolic link. 63 | 64 | Default: v:false 65 | 66 | 67 | ============================================================================== 68 | FREQUENTLY ASKED QUESTIONS (FAQ) *ddu-source-file_rec-faq* 69 | 70 | Q: ".gitignore" is not respected. 71 | 72 | A: It is feature. If you want to use ".gitignore", please use 73 | "ddu-source-file_external" instead. 74 | 75 | https://github.com/matsui54/ddu-source-file_external 76 | 77 | 78 | ============================================================================== 79 | vim:tw=78:ts=8:ft=help:norl:noet:fen:noet: 80 | --------------------------------------------------------------------------------