├── LICENSE ├── README.md ├── denops └── @ddc-sources │ └── nextword.ts └── doc └── ddc-source-nextword.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 | # ddc-source-nextword: nextword completion for ddc.vim 2 | 3 | A ddc.vim source for `nextword` for completing words in English. 4 | 5 | **Note: "nextword" is deprecated. You should use 6 | [ddc-mocword](https://github.com/Shougo/ddc-mocword) instead.** 7 | 8 | ## Dependencies 9 | 10 | **Note: "nextword" binary must be installed in your `$PATH`!!** 11 | 12 | - https://github.com/high-moctane/nextword 13 | 14 | - https://github.com/high-moctane/nextword-data 15 | 16 | - Set `$NEXTWORD_DATA_PATH` environment variable 17 | 18 | Please test `nextword -n 100 -g` works from command line. 19 | 20 | ## Configuration 21 | 22 | ```vim 23 | call ddc#custom#patch_global('sources', ['nextword']) 24 | call ddc#custom#patch_global('sourceOptions', #{ 25 | \ nextword: #{ 26 | \ mark: 'nextword', 27 | \ minAutoCompleteLength: 3, 28 | \ isVolatile: v:true, 29 | \ }}) 30 | ``` 31 | 32 | ## License 33 | 34 | MIT 35 | -------------------------------------------------------------------------------- /denops/@ddc-sources/nextword.ts: -------------------------------------------------------------------------------- 1 | import { type Item } from "jsr:@shougo/ddc-vim@~7.0.0/types"; 2 | import { 3 | BaseSource, 4 | type GatherArguments, 5 | type OnInitArguments, 6 | } from "jsr:@shougo/ddc-vim@~7.0.0/source"; 7 | import { printError } from "jsr:@shougo/ddc-vim@~7.0.0/utils"; 8 | 9 | import { assertEquals } from "jsr:@std/assert@~1.0.3/equals"; 10 | import { TextLineStream } from "jsr:@std/streams@~1.0.3/text-line-stream"; 11 | 12 | type Params = Record; 13 | 14 | const encoder = new TextEncoder(); 15 | 16 | export class Source extends BaseSource { 17 | #proc: Deno.ChildProcess | undefined; 18 | #readCallback: (result: string) => void = () => {}; 19 | #writer: WritableStreamDefaultWriter | undefined; 20 | 21 | override async onInit(args: OnInitArguments): Promise { 22 | try { 23 | this.#proc = new Deno.Command( 24 | "nextword", 25 | { 26 | args: ["-n", "100", "-g"], 27 | stdout: "piped", 28 | stderr: "piped", 29 | stdin: "piped", 30 | }, 31 | ).spawn(); 32 | } catch (error: unknown) { 33 | if (error instanceof Deno.errors.NotFound) { 34 | await printError( 35 | args.denops, 36 | 'Spawning "nextword" is failed. "nextword" binary seems not installed', 37 | ); 38 | return; 39 | } 40 | 41 | throw error; 42 | } 43 | this.#proc.stdout 44 | .pipeThrough(new TextDecoderStream()) 45 | .pipeThrough(new TextLineStream()) 46 | .pipeTo( 47 | new WritableStream({ 48 | write: (chunk: string) => this.#readCallback(chunk), 49 | }), 50 | ).finally(() => { 51 | this.#proc = undefined; 52 | this.#readCallback = () => {}; 53 | this.#writer = undefined; 54 | }); 55 | this.#proc.status.then(async (status) => { 56 | if (!status.success) { 57 | await printError( 58 | args.denops, 59 | '"nextword" exited with non-zero status code. $NEXTWORD_DATA_PATH seems not set correctly.', 60 | ); 61 | } 62 | }); 63 | this.#writer = this.#proc.stdin.getWriter(); 64 | } 65 | 66 | override async gather(args: GatherArguments): Promise { 67 | if (!this.#proc || !this.#writer) { 68 | return []; 69 | } 70 | 71 | const [sentence, offset] = extractWords(args.completeStr); 72 | const query = offset > 0 ? sentence : args.context.input; 73 | const precedingLetters = args.completeStr.slice(0, offset); 74 | 75 | const { promise, resolve } = Promise.withResolvers(); 76 | this.#readCallback = resolve; 77 | 78 | await this.#writer.write(encoder.encode(query + "\n")); 79 | return (await promise).split(/\s/) 80 | .map((word: string) => ({ word: precedingLetters.concat(word) })); 81 | } 82 | 83 | override params(): Params { 84 | return {}; 85 | } 86 | } 87 | 88 | function extractWords( 89 | completeStr: string, 90 | ): [string, number] { 91 | const upperCaseRegexp = /[A-Z][A-Z]+/g; 92 | 93 | // Also matched to PascalCase 94 | const camelCaseRegexp = /([A-Z]?[a-z]+|[A-Z][a-z]*)/g; 95 | 96 | // Also matched to kebab-case, etc. 97 | const snakeCaseRegexp = /[a-z][a-z]*/g; 98 | 99 | let matches: string[] | null = completeStr.match(upperCaseRegexp); 100 | if (matches === null) matches = completeStr.match(camelCaseRegexp); 101 | if (matches === null) matches = completeStr.match(snakeCaseRegexp); 102 | if (matches === null) return [completeStr, 0]; 103 | 104 | const sentence = matches.join(" "); 105 | if (completeStr.match(/[^a-zA-Z]+$/)) { 106 | return [sentence.concat(" "), completeStr.length]; 107 | } 108 | 109 | const lastWord = matches.at(-1) || completeStr; 110 | const offset = completeStr.lastIndexOf(lastWord); 111 | return [sentence, offset]; 112 | } 113 | 114 | Deno.test("extractWords", () => { 115 | assertEquals( 116 | extractWords("input"), 117 | ["input", 0], 118 | ); 119 | assertEquals( 120 | extractWords("UPPER_CASE_INPUT"), 121 | ["UPPER CASE INPUT", 11], 122 | ); 123 | assertEquals( 124 | extractWords("camelCaseInput"), 125 | ["camel Case Input", 9], 126 | ); 127 | assertEquals( 128 | extractWords("_snake_case_input"), 129 | ["snake case input", 12], 130 | ); 131 | assertEquals( 132 | extractWords("_unfinished_input_"), 133 | ["unfinished input ", 18], 134 | ); 135 | assertEquals( 136 | extractWords("unfinishedI"), 137 | ["unfinished I", 10], 138 | ); 139 | assertEquals( 140 | extractWords("_i"), 141 | ["i", 1], 142 | ); 143 | }); 144 | -------------------------------------------------------------------------------- /doc/ddc-source-nextword.txt: -------------------------------------------------------------------------------- 1 | *ddc-source-nextword.txt* nextword completion for ddc.vim 2 | 3 | Author: Shougo 4 | License: MIT license 5 | 6 | CONTENTS *ddc-source-nextword-contents* 7 | 8 | Introduction |ddc-source-nextword-introduction| 9 | Install |ddc-source-nextword-install| 10 | Examples |ddc-source-nextword-examples| 11 | 12 | 13 | ============================================================================== 14 | INTRODUCTION *ddc-source-nextword-introduction* 15 | 16 | A ddc.vim source for `nextword` for completing words in English. 17 | 18 | Note: "nextword" is deprecated. You should use ddc-mocword instead. 19 | https://github.com/Shougo/ddc-mocword 20 | 21 | 22 | ============================================================================== 23 | INSTALL *ddc-source-nextword-install* 24 | 25 | Note: "nextword" binary must be installed in your `$PATH`!! 26 | 27 | Please install both "ddc.vim" and "denops.vim". 28 | 29 | https://github.com/Shougo/ddc.vim 30 | https://github.com/vim-denops/denops.vim 31 | 32 | And you must both install "nextword" binary and "nextword-data". 33 | 34 | https://github.com/high-moctane/nextword 35 | https://github.com/high-moctane/nextword-data 36 | 37 | You must set `$NEXTWORD_DATA_PATH` environment variable 38 | 39 | Please test `nextword -n 100 -g` works from command line. 40 | 41 | 42 | ============================================================================== 43 | EXAMPLES *ddc-source-nextword-examples* 44 | > 45 | call ddc#custom#patch_global('sources', ['nextword']) 46 | call ddc#custom#patch_global('sourceOptions', #{ 47 | \ nextword: { 48 | \ mark: 'nextword', 49 | \ minAutoCompleteLength: 3, 50 | \ isVolatile: v:true, 51 | \ }}) 52 | < 53 | 54 | ============================================================================== 55 | vim:tw=78:ts=8:ft=help:norl:noet:fen:noet: 56 | --------------------------------------------------------------------------------