├── README.md ├── denops └── @ddc-sources │ ├── path.ts │ └── path_test.ts └── doc └── ddc-path.txt /README.md: -------------------------------------------------------------------------------- 1 | # ddc-path 2 | 3 | Path completion for ddc.vim. 4 | This source collects path names with GNU find or sharkdp/fd. 5 | 6 | 'sharkdp/fd' is a simple, fast and user-friendly alternative to 'find'. 7 | 8 | To install this source, 9 | 10 | ```vim 11 | Plug 'tani/ddc-path', 12 | 13 | call ddc#custom#patch_global('sources', ['path']) 14 | call ddc#custom#patch_global('sourceOptions', { 15 | \ 'path': {'mark': 'P'}, 16 | \ }) 17 | call ddc#custom#patch_global('sourceParams', { 18 | \ 'path': { 19 | \ 'cmd': ['fd', '--max-depth', '5'], 20 | \ } 21 | \ }) 22 | " or 'cmd': ['find', '-maxdepth', '5'], 23 | ``` 24 | 25 | Copyright (c) 2021 TANIGUCHI Masaya. All rights reserved. 26 | 27 | This work is licensed under the MIT license. 28 | git.io/mit-license 29 | -------------------------------------------------------------------------------- /denops/@ddc-sources/path.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 TANIGUCHI Masaya. All rights reserved. 3 | * This work is licensed under the MIT license. git.io/mit-license 4 | */ 5 | import { 6 | BaseSource, 7 | DdcGatherItems, 8 | } from "https://lib.deno.dev/x/ddc_vim@v4/types.ts"; 9 | import { 10 | GatherArguments, 11 | GetCompletePositionArguments, 12 | } from "https://lib.deno.dev/x/ddc_vim@v4/base/source.ts"; 13 | import * as fn from "https://lib.deno.dev/x/denops_std@v5/function/mod.ts"; 14 | import * as op from "https://lib.deno.dev/x/denops_std@v5/option/mod.ts"; 15 | import type { Denops } from "https://lib.deno.dev/x/denops_std@v5/mod.ts"; 16 | 17 | type UserData = Record; 18 | type DirSeparator = "slash" | "backslash"; 19 | type Params = { 20 | cmd: string[]; 21 | absolute: boolean; 22 | dirSeparator: DirSeparator | ""; 23 | escapeChars: string; 24 | }; 25 | type PathChecker = (path: string) => boolean; 26 | type WordFilter = (word: string) => string; 27 | 28 | function isValidUnixPath(path: string, escapeChars: string): boolean { 29 | for (let i = 0; i < path.length; ++i) { 30 | if ( 31 | path[i] === "\\" && (i + 1) < path.length && 32 | escapeChars.includes(path[i + 1]) 33 | ) { 34 | ++i; 35 | } else if (escapeChars.includes(path[i])) { 36 | return false; 37 | } 38 | } 39 | return true; 40 | } 41 | 42 | function isValidWindowsPath(path: string, escapeChars: string): boolean { 43 | // allow both '/' and '\' 44 | const invalidChars = ':*?"<>|' + escapeChars; 45 | let i = 0; 46 | if (/^[a-zA-Z]:/.test(path)) { 47 | i = 2; 48 | } 49 | for (; i < path.length; ++i) { 50 | if ( 51 | path[i] === "\\" && (i + 1) < path.length && 52 | escapeChars.includes(path[i + 1]) 53 | ) { 54 | ++i; 55 | } else if (invalidChars.includes(path[i])) { 56 | return false; 57 | } 58 | } 59 | return true; 60 | } 61 | 62 | function getCompletePosition(input: string, isPath: PathChecker): number { 63 | // skip leading whitespace 64 | const startIndex = input.search(/\S/); 65 | const trimedInput = startIndex < 0 ? "" : input.slice(startIndex); 66 | const index = Array.from(trimedInput).findIndex((_, i) => 67 | isPath(trimedInput.slice(i)) 68 | ); 69 | return index < 0 ? input.length : (startIndex + index); 70 | } 71 | 72 | function getWordFilter(escapeChars: string): WordFilter { 73 | const regexpsrc = `[${escapeChars.replaceAll(/[-\\\]]/g, "\\$&")}]` 74 | const escapeRegex = new RegExp(regexpsrc, "g"); 75 | return (word) => word.replaceAll(escapeRegex, "\\$&"); 76 | } 77 | 78 | export class Source extends BaseSource { 79 | override getCompletePosition( 80 | { context, sourceParams }: GetCompletePositionArguments, 81 | ): Promise { 82 | const isValidPath = Deno.build.os === "windows" 83 | ? isValidWindowsPath 84 | : isValidUnixPath; 85 | const { escapeChars } = sourceParams; 86 | const isPath = (path: string) => isValidPath(path, escapeChars); 87 | const index = getCompletePosition(context.input, isPath); 88 | return Promise.resolve(index); 89 | } 90 | 91 | override async gather( 92 | { denops, sourceParams }: GatherArguments, 93 | ): Promise> { 94 | const decoder = new TextDecoder(); 95 | let cwd = await fn.getcwd(denops) as string; 96 | const proc = Deno.run({ 97 | cwd, 98 | cmd: sourceParams.cmd, 99 | stdout: "piped", 100 | stderr: "null", 101 | }); 102 | const output = await proc.output(); 103 | let text = decoder.decode(output); 104 | 105 | // fix Windows directory separator to Unix 106 | if (Deno.build.os === "windows") { 107 | cwd = cwd.replaceAll("\\", "/"); 108 | text = text.replaceAll("\\", "/"); 109 | } 110 | 111 | let words = text.split("\n").map((word) => word.replace(/^\.\//, "")); 112 | 113 | // change relative path to absolute 114 | if (sourceParams.absolute) { 115 | words = words.map((word) => `${cwd}/${word}`); 116 | } 117 | 118 | // change directory separator to backslash 119 | if (await this.getDirSeparator(denops, sourceParams) === "backslash") { 120 | words = words.map((word) => word.replaceAll("/", "\\")); 121 | } 122 | 123 | const wordFilter = getWordFilter(sourceParams.escapeChars); 124 | return words.map((word) => ({ 125 | word: wordFilter(word), 126 | abbr: word, 127 | })); 128 | } 129 | 130 | params(): Params { 131 | return { 132 | // cmd: ["fd", "--max-depth", "3"] 133 | cmd: ["find", "-maxdepth", "3"], 134 | absolute: true, 135 | dirSeparator: "", 136 | escapeChars: " ", 137 | }; 138 | } 139 | 140 | private async getDirSeparator( 141 | denops: Denops, 142 | sourceParams: Params, 143 | ): Promise { 144 | const { dirSeparator } = sourceParams; 145 | if (dirSeparator === "slash" || dirSeparator === "backslash") { 146 | return dirSeparator; 147 | } 148 | if (!await fn.exists(denops, "+completeslash")) { 149 | return "slash"; 150 | } 151 | const completeslash = await op.completeslash.get(denops); 152 | if (completeslash !== "") { 153 | return completeslash as DirSeparator; 154 | } 155 | return await op.shellslash.get(denops) ? "slash" : "backslash"; 156 | } 157 | } 158 | 159 | export const _internals = { 160 | getCompletePosition, 161 | getWordFilter, 162 | isValidUnixPath, 163 | isValidWindowsPath, 164 | }; 165 | -------------------------------------------------------------------------------- /denops/@ddc-sources/path_test.ts: -------------------------------------------------------------------------------- 1 | import { assertEquals } from "https://deno.land/std@0.136.0/testing/asserts.ts"; 2 | import { describe, it } from "https://deno.land/x/test_suite@0.16.1/mod.ts"; 3 | import { _internals } from "./path.ts"; 4 | 5 | describe("getWordFilter()", () => { 6 | const parametrize = [ 7 | { 8 | escapeChars: "", 9 | patterns: [ 10 | { input: "abc", expected: "abc" }, 11 | { input: "a b c", expected: "a b c" }, 12 | { input: "a\\b c", expected: "a\\b c" }, 13 | ], 14 | }, 15 | { 16 | escapeChars: " ", 17 | patterns: [ 18 | { input: "abc", expected: "abc" }, 19 | { input: "a b c", expected: "a\\ b\\ c" }, 20 | { input: "a\\b c", expected: "a\\b\\ c" }, 21 | ], 22 | }, 23 | { 24 | escapeChars: " \\", 25 | patterns: [ 26 | { input: "abc", expected: "abc" }, 27 | { input: "a b c", expected: "a\\ b\\ c" }, 28 | { input: "a\\b c", expected: "a\\\\b\\ c" }, 29 | ], 30 | }, 31 | { 32 | escapeChars: "[a-z]", 33 | patterns: [ 34 | { input: "abc", expected: "\\abc" }, 35 | { input: "a b c", expected: "\\a b c" }, 36 | { input: "a-b-c", expected: "\\a\\-b\\-c" }, 37 | ], 38 | }, 39 | { 40 | escapeChars: ` !"#$%&'()*+,-./<=>?@[\\]_\`{|}~`, 41 | patterns: [ 42 | { input: "abc", expected: "abc" }, 43 | { input: "a b c", expected: "a\\ b\\ c" }, 44 | { input: "a][b}{c", expected: "a\\]\\[b\\}\\{c" }, 45 | ], 46 | }, 47 | ]; 48 | 49 | for (const { escapeChars, patterns } of parametrize) { 50 | for (const { input, expected } of patterns) { 51 | it(`escapeChars=${escapeChars} input=${input}`, () => { 52 | const wordFilter = _internals.getWordFilter(escapeChars); 53 | const actual = wordFilter(input); 54 | assertEquals(actual, expected); 55 | }); 56 | } 57 | } 58 | }); 59 | 60 | describe("isValidUnixPath()", () => { 61 | const parametrize = [ 62 | { 63 | path: "foo/bar baz", 64 | escapeChars: "", 65 | expected: true, 66 | }, 67 | { 68 | path: "foo/bar baz", 69 | escapeChars: " ", 70 | expected: false, 71 | }, 72 | { 73 | path: "foo/bar\\ baz", 74 | escapeChars: " ", 75 | expected: true, 76 | }, 77 | { 78 | path: "foo\\bar\\baz", 79 | escapeChars: "", 80 | expected: true, 81 | }, 82 | { 83 | path: "foo\\bar\\baz", 84 | escapeChars: " ", 85 | expected: true, 86 | }, 87 | { 88 | path: "foo\\bar\\baz", 89 | escapeChars: "\\", 90 | expected: false, 91 | }, 92 | { 93 | path: "foo\\\\bar\\\\baz", 94 | escapeChars: "\\", 95 | expected: true, 96 | }, 97 | { 98 | path: "f%oo/b ar/ba#z", 99 | escapeChars: " %#", 100 | expected: false, 101 | }, 102 | { 103 | path: "f\\%oo/b\\ ar/ba\\#z", 104 | escapeChars: " %#", 105 | expected: true, 106 | }, 107 | { 108 | path: "f%oo\\b ar\\ba#z", 109 | escapeChars: " %#", 110 | expected: false, 111 | }, 112 | { 113 | path: "f\\%oo\\b\\ ar\\ba\\#z", 114 | escapeChars: " %#", 115 | expected: true, 116 | }, 117 | { 118 | path: "C:/foo/bar/baz", 119 | escapeChars: " %#", 120 | expected: true, 121 | }, 122 | { 123 | path: "C:foo/bar/baz", 124 | escapeChars: " %#", 125 | expected: true, 126 | }, 127 | { 128 | path: "foo/C:/bar/baz", 129 | escapeChars: " %#", 130 | expected: true, 131 | }, 132 | { 133 | path: "foo*bar", 134 | escapeChars: " %#", 135 | expected: true, 136 | }, 137 | { 138 | path: "foo?bar", 139 | escapeChars: " %#", 140 | expected: true, 141 | }, 142 | { 143 | path: 'foo"bar', 144 | escapeChars: " %#", 145 | expected: true, 146 | }, 147 | { 148 | path: "foobar", 154 | escapeChars: " %#", 155 | expected: true, 156 | }, 157 | { 158 | path: "foo|bar", 159 | escapeChars: " %#", 160 | expected: true, 161 | }, 162 | ]; 163 | 164 | for (const { path, escapeChars, expected } of parametrize) { 165 | it(`path=${path} escapeChars=${escapeChars}`, () => { 166 | const actual = _internals.isValidUnixPath(path, escapeChars); 167 | assertEquals(actual, expected); 168 | }); 169 | } 170 | }); 171 | 172 | describe("isValidWindowsPath()", () => { 173 | const parametrize = [ 174 | { 175 | path: "foo/bar baz", 176 | escapeChars: "", 177 | expected: true, 178 | }, 179 | { 180 | path: "foo/bar baz", 181 | escapeChars: " ", 182 | expected: false, 183 | }, 184 | { 185 | path: "foo/bar\\ baz", 186 | escapeChars: " ", 187 | expected: true, 188 | }, 189 | { 190 | path: "foo\\bar\\baz", 191 | escapeChars: "", 192 | expected: true, 193 | }, 194 | { 195 | path: "foo\\bar\\baz", 196 | escapeChars: " ", 197 | expected: true, 198 | }, 199 | { 200 | path: "foo\\bar\\baz", 201 | escapeChars: "\\", 202 | expected: false, 203 | }, 204 | { 205 | path: "foo\\\\bar\\\\baz", 206 | escapeChars: "\\", 207 | expected: true, 208 | }, 209 | { 210 | path: "f%oo/b ar/ba#z", 211 | escapeChars: " %#", 212 | expected: false, 213 | }, 214 | { 215 | path: "f\\%oo/b\\ ar/ba\\#z", 216 | escapeChars: " %#", 217 | expected: true, 218 | }, 219 | { 220 | path: "f%oo\\b ar\\ba#z", 221 | escapeChars: " %#", 222 | expected: false, 223 | }, 224 | { 225 | path: "f\\%oo\\b\\ ar\\ba\\#z", 226 | escapeChars: " %#", 227 | expected: true, 228 | }, 229 | { 230 | path: "C:\\foo\\bar\\baz", 231 | escapeChars: " %#", 232 | expected: true, 233 | }, 234 | { 235 | path: "C:foo\\bar\\baz", 236 | escapeChars: " %#", 237 | expected: true, 238 | }, 239 | { 240 | path: "foo\\C:\\bar\\baz", 241 | escapeChars: " %#", 242 | expected: false, 243 | }, 244 | { 245 | path: "foo*bar", 246 | escapeChars: " %#", 247 | expected: false, 248 | }, 249 | { 250 | path: "foo?bar", 251 | escapeChars: " %#", 252 | expected: false, 253 | }, 254 | { 255 | path: 'foo"bar', 256 | escapeChars: " %#", 257 | expected: false, 258 | }, 259 | { 260 | path: "foobar", 266 | escapeChars: " %#", 267 | expected: false, 268 | }, 269 | { 270 | path: "foo|bar", 271 | escapeChars: " %#", 272 | expected: false, 273 | }, 274 | ]; 275 | 276 | for (const { path, escapeChars, expected } of parametrize) { 277 | it(`path=${path} escapeChars=${escapeChars}`, () => { 278 | const actual = _internals.isValidWindowsPath(path, escapeChars); 279 | assertEquals(actual, expected); 280 | }); 281 | } 282 | }); 283 | 284 | describe("getCompletePosition()", () => { 285 | const parametrize = [ 286 | { 287 | os: "windows", 288 | escapeChars: " ", 289 | patterns: [ 290 | { input: "", expected: 0 }, 291 | { input: "foo", expected: 0 }, 292 | { input: "foo ", expected: 5 }, 293 | { input: "foo bar", expected: 5 }, 294 | { input: "foo \\ bar", expected: 5 }, 295 | { input: "foo \\ bar\\", expected: 5 }, 296 | { input: "foo \\ bar\\ ", expected: 5 }, 297 | { input: " ", expected: 3 }, 298 | { input: " foo", expected: 3 }, 299 | { input: " foo ", expected: 8 }, 300 | { input: " foo bar", expected: 8 }, 301 | { input: " foo \\ bar", expected: 8 }, 302 | { input: " foo \\ bar\\", expected: 8 }, 303 | { input: " foo \\ bar\\ ", expected: 8 }, 304 | { input: "C:/foo/bar", expected: 0 }, 305 | { input: "C:/foo/b ar", expected: 9 }, 306 | { input: "C:/foo/ bar", expected: 8 }, 307 | { input: "C:/foo// bar", expected: 9 }, 308 | { input: "foo/C:/bar", expected: 4 }, 309 | { input: "/foo/C:/bar", expected: 5 }, 310 | { input: " C:/foo/bar", expected: 2 }, 311 | { input: " C:/foo/b ar", expected: 11 }, 312 | { input: " C:/foo/ bar", expected: 10 }, 313 | { input: " C:/foo// bar", expected: 11 }, 314 | { input: " foo/C:/bar", expected: 6 }, 315 | { input: " /foo/C:/bar", expected: 7 }, 316 | { input: "C:\\foo\\bar", expected: 0 }, 317 | { input: "C:\\foo\\b ar", expected: 9 }, 318 | { input: "C:\\foo\\ bar", expected: 0 }, 319 | { input: "C:\\foo\\\\ bar", expected: 0 }, 320 | { input: "foo\\C:\\bar", expected: 4 }, 321 | { input: "\\foo\\C:\\bar", expected: 5 }, 322 | { input: " C:\\foo\\bar", expected: 2 }, 323 | { input: " C:\\foo\\b ar", expected: 11 }, 324 | { input: " C:\\foo\\ bar", expected: 2 }, 325 | { input: " C:\\foo\\\\ bar", expected: 2 }, 326 | { input: " foo\\C:\\bar", expected: 6 }, 327 | { input: " \\foo\\C:\\bar", expected: 7 }, 328 | ], 329 | }, 330 | { 331 | os: "windows", 332 | escapeChars: "", 333 | patterns: [ 334 | { input: "", expected: 0 }, 335 | { input: "foo", expected: 0 }, 336 | { input: "foo ", expected: 0 }, 337 | { input: "foo bar", expected: 0 }, 338 | { input: "foo \\ bar", expected: 0 }, 339 | { input: "foo \\ bar\\", expected: 0 }, 340 | { input: "foo \\ bar\\ ", expected: 0 }, 341 | { input: " ", expected: 3 }, 342 | { input: " foo", expected: 3 }, 343 | { input: " foo ", expected: 3 }, 344 | { input: " foo bar", expected: 3 }, 345 | { input: " foo \\ bar", expected: 3 }, 346 | { input: " foo \\ bar\\", expected: 3 }, 347 | { input: " foo \\ bar\\ ", expected: 3 }, 348 | { input: "C:/foo/bar", expected: 0 }, 349 | { input: "C:/foo/b ar", expected: 0 }, 350 | { input: "C:/foo/ bar", expected: 0 }, 351 | { input: "C:/foo// bar", expected: 0 }, 352 | { input: "foo/C:/bar", expected: 4 }, 353 | { input: "/foo/C:/bar", expected: 5 }, 354 | { input: " C:/foo/bar", expected: 2 }, 355 | { input: " C:/foo/b ar", expected: 2 }, 356 | { input: " C:/foo/ bar", expected: 2 }, 357 | { input: " C:/foo// bar", expected: 2 }, 358 | { input: " foo/C:/bar", expected: 6 }, 359 | { input: " /foo/C:/bar", expected: 7 }, 360 | { input: "C:\\foo\\bar", expected: 0 }, 361 | { input: "C:\\foo\\b ar", expected: 0 }, 362 | { input: "C:\\foo\\ bar", expected: 0 }, 363 | { input: "C:\\foo\\\\ bar", expected: 0 }, 364 | { input: "foo\\C:\\bar", expected: 4 }, 365 | { input: "\\foo\\C:\\bar", expected: 5 }, 366 | { input: " C:\\foo\\bar", expected: 2 }, 367 | { input: " C:\\foo\\b ar", expected: 2 }, 368 | { input: " C:\\foo\\ bar", expected: 2 }, 369 | { input: " C:\\foo\\\\ bar", expected: 2 }, 370 | { input: " foo\\C:\\bar", expected: 6 }, 371 | { input: " \\foo\\C:\\bar", expected: 7 }, 372 | ], 373 | }, 374 | { 375 | os: "linux", 376 | escapeChars: " ", 377 | patterns: [ 378 | { input: "", expected: 0 }, 379 | { input: "foo", expected: 0 }, 380 | { input: "foo ", expected: 5 }, 381 | { input: "foo bar", expected: 5 }, 382 | { input: "foo \\ bar", expected: 5 }, 383 | { input: "foo \\ bar\\", expected: 5 }, 384 | { input: "foo \\ bar\\ ", expected: 5 }, 385 | { input: " ", expected: 3 }, 386 | { input: " foo", expected: 3 }, 387 | { input: " foo ", expected: 8 }, 388 | { input: " foo bar", expected: 8 }, 389 | { input: " foo \\ bar", expected: 8 }, 390 | { input: " foo \\ bar\\", expected: 8 }, 391 | { input: " foo \\ bar\\ ", expected: 8 }, 392 | { input: "C:/foo/bar", expected: 0 }, 393 | { input: "C:/foo/b ar", expected: 9 }, 394 | { input: "C:/foo/ bar", expected: 8 }, 395 | { input: "C:/foo// bar", expected: 9 }, 396 | { input: "foo/C:/bar", expected: 0 }, 397 | { input: "/foo/C:/bar", expected: 0 }, 398 | { input: " C:/foo/bar", expected: 2 }, 399 | { input: " C:/foo/b ar", expected: 11 }, 400 | { input: " C:/foo/ bar", expected: 10 }, 401 | { input: " C:/foo// bar", expected: 11 }, 402 | { input: " foo/C:/bar", expected: 2 }, 403 | { input: " /foo/C:/bar", expected: 2 }, 404 | { input: "C:\\foo\\bar", expected: 0 }, 405 | { input: "C:\\foo\\b ar", expected: 9 }, 406 | { input: "C:\\foo\\ bar", expected: 0 }, 407 | { input: "C:\\foo\\\\ bar", expected: 0 }, 408 | { input: "foo\\C:\\bar", expected: 0 }, 409 | { input: "\\foo\\C:\\bar", expected: 0 }, 410 | { input: " C:\\foo\\bar", expected: 2 }, 411 | { input: " C:\\foo\\b ar", expected: 11 }, 412 | { input: " C:\\foo\\ bar", expected: 2 }, 413 | { input: " C:\\foo\\\\ bar", expected: 2 }, 414 | { input: " foo\\C:\\bar", expected: 2 }, 415 | { input: " \\foo\\C:\\bar", expected: 2 }, 416 | ], 417 | }, 418 | { 419 | os: "linux", 420 | escapeChars: "", 421 | patterns: [ 422 | { input: "", expected: 0 }, 423 | { input: "foo", expected: 0 }, 424 | { input: "foo ", expected: 0 }, 425 | { input: "foo bar", expected: 0 }, 426 | { input: "foo \\ bar", expected: 0 }, 427 | { input: "foo \\ bar\\", expected: 0 }, 428 | { input: "foo \\ bar\\ ", expected: 0 }, 429 | { input: " ", expected: 3 }, 430 | { input: " foo", expected: 3 }, 431 | { input: " foo ", expected: 3 }, 432 | { input: " foo bar", expected: 3 }, 433 | { input: " foo \\ bar", expected: 3 }, 434 | { input: " foo \\ bar\\", expected: 3 }, 435 | { input: " foo \\ bar\\ ", expected: 3 }, 436 | { input: "C:/foo/bar", expected: 0 }, 437 | { input: "C:/foo/b ar", expected: 0 }, 438 | { input: "C:/foo/ bar", expected: 0 }, 439 | { input: "C:/foo// bar", expected: 0 }, 440 | { input: "foo/C:/bar", expected: 0 }, 441 | { input: "/foo/C:/bar", expected: 0 }, 442 | { input: " C:/foo/bar", expected: 2 }, 443 | { input: " C:/foo/b ar", expected: 2 }, 444 | { input: " C:/foo/ bar", expected: 2 }, 445 | { input: " C:/foo// bar", expected: 2 }, 446 | { input: " foo/C:/bar", expected: 2 }, 447 | { input: " /foo/C:/bar", expected: 2 }, 448 | { input: "C:\\foo\\bar", expected: 0 }, 449 | { input: "C:\\foo\\b ar", expected: 0 }, 450 | { input: "C:\\foo\\ bar", expected: 0 }, 451 | { input: "C:\\foo\\\\ bar", expected: 0 }, 452 | { input: "foo\\C:\\bar", expected: 0 }, 453 | { input: "\\foo\\C:\\bar", expected: 0 }, 454 | { input: " C:\\foo\\bar", expected: 2 }, 455 | { input: " C:\\foo\\b ar", expected: 2 }, 456 | { input: " C:\\foo\\ bar", expected: 2 }, 457 | { input: " C:\\foo\\\\ bar", expected: 2 }, 458 | { input: " foo\\C:\\bar", expected: 2 }, 459 | { input: " \\foo\\C:\\bar", expected: 2 }, 460 | ], 461 | }, 462 | ]; 463 | 464 | for (const { os, escapeChars, patterns } of parametrize) { 465 | for (const { input, expected } of patterns) { 466 | it(`os=${os} escapeChars=${escapeChars} input=${input}`, () => { 467 | const isValidPath = os === "windows" 468 | ? _internals.isValidWindowsPath 469 | : _internals.isValidUnixPath; 470 | const isPath = (path: string) => isValidPath(path, escapeChars); 471 | const actual = _internals.getCompletePosition(input, isPath); 472 | assertEquals(actual, expected); 473 | }); 474 | } 475 | } 476 | }); 477 | -------------------------------------------------------------------------------- /doc/ddc-path.txt: -------------------------------------------------------------------------------- 1 | *ddc-path.txt* Path completion for ddc.vim 2 | 3 | Copyright (c) 2021 TANIGUCHI Masaya. All rights reserved. 4 | 5 | This work is licensed under the MIT license. 6 | git.io/mit-license 7 | 8 | CONTENTS *ddc-path-contents* 9 | 10 | Introduction |ddc-path-introduction| 11 | Install |ddc-path-install| 12 | Examples |ddc-path-examples| 13 | Params |ddc-path-params| 14 | 15 | 16 | ============================================================================== 17 | INTRODUCTION *ddc-path-introduction* 18 | 19 | Path completion for ddc.vim. 20 | This source collects path names with GNU find or sharkdp/fd. 21 | 22 | "sharkdp/fd" is a simple, fast and user-friendly alternative to "find". 23 | 24 | 25 | ============================================================================== 26 | INSTALL *ddc-path-install* 27 | 28 | Please install both "ddc.vim" and "denops.vim". 29 | 30 | https://github.com/Shougo/ddc.vim 31 | https://github.com/vim-denops/denops.vim 32 | 33 | Use your preferred plugin manager. 34 | > 35 | Plug 'tani/ddc-path', 36 | < 37 | 38 | ============================================================================== 39 | EXAMPLES *ddc-path-examples* 40 | 41 | > 42 | call ddc#custom#patch_global('sources', ['path']) 43 | call ddc#custom#patch_global('sourceOptions', { 44 | \ 'path': {'mark': 'P'}, 45 | \ }) 46 | call ddc#custom#patch_global('sourceParams', { 47 | \ 'path': { 48 | \ 'cmd': ['fd', '--max-depth', '5'], 49 | \ } 50 | \ }) 51 | " or 'cmd': ['find', '-maxdepth', '5'], 52 | < 53 | 54 | ============================================================================== 55 | PARAMS *ddc-path-params* 56 | 57 | *ddc-path-param-absolute* 58 | absolute (boolean) 59 | If it is true, the corrected paths are converted to absolute. 60 | Otherwise, paths are relative from the current directory. 61 | 62 | Default: v:true 63 | 64 | *ddc-path-param-cmd* 65 | cmd (string[]) 66 | An array of "find" program arguments. 67 | 68 | Default: ["find", "-maxdepth", "5"] 69 | 70 | *ddc-path-param-dirSeparator* 71 | dirSeparator ("slash" | "backslash" | "") 72 | {for MS-Windows} 73 | Change the directory separator to a forward or backward slash. 74 | 75 | slash A forward slash is used. 76 | backslash A backward slash is used. 77 | (empty) Auto detect by: 78 | - When 'completeslash' is not empty, the value is 79 | used for this option. 80 | - When 'shellslash' is enabled, "slash" is used. 81 | - When 'noshellslash', "backslash" is used. 82 | 83 | default: "" 84 | 85 | *ddc-path-param-escapeChars* 86 | escapeChars (string) 87 | Escape the characters in this value that occur in result with a 88 | backslash. 89 | If you want to use the result in |command-mode|, you should set " #%". 90 | 91 | Default: " " 92 | 93 | 94 | ============================================================================== 95 | vim:tw=78:ts=8:ft=help:norl:noet:fen:noet: 96 | --------------------------------------------------------------------------------