├── LICENSE ├── Makefile ├── README.md ├── denops ├── @ddu-filters │ └── matcher_dein_update.ts ├── @ddu-kinds │ └── dein_update.ts ├── @ddu-sources │ └── dein_update.ts └── @ddu_dein_update │ ├── deps.ts │ ├── deps_test.ts │ ├── git.ts │ ├── git_test.ts │ ├── process.ts │ ├── text.ts │ └── text_test.ts └── doc └── ddu-source-dein_update.txt /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Haruki Matsui 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGETS := $$(find . \( -name '*.ts' -or -name '*.md' \) -not -path './.deno/*') 2 | 3 | .DEFAULT_GOAL := help 4 | 5 | help: 6 | @cat $(MAKEFILE_LIST) | \ 7 | perl -ne 'print if /^\w+.*##/;' | \ 8 | perl -pe 's/(.*):.*##\s*/sprintf("%-20s",$$1)/eg;' 9 | 10 | fmt: FORCE ## Format code 11 | @deno fmt 12 | 13 | fmt-check: FORCE ## Format check 14 | @deno fmt --check 15 | 16 | lint: FORCE ## Lint code 17 | @deno lint 18 | 19 | type-check: FORCE ## Type check 20 | @deno test --unstable --no-run ${TARGETS} 21 | 22 | test: FORCE ## Test 23 | @deno test --unstable -A --no-check --jobs 24 | 25 | deps: FORCE ## Update dependencies 26 | @deno run -A https://deno.land/x/udd@0.7.2/main.ts ${TARGETS} 27 | @make fmt 28 | 29 | FORCE: 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ddu-source-dein_update 2 | 3 | This source updates the plugins installed by dein.vim and output the result to 4 | ddu buffer. You can use this plugin instead of `dein#update()`. 5 | 6 | ![ddu-dein_update](https://user-images.githubusercontent.com/63794197/162573810-9cb9cfbc-052d-4863-8ec7-4deecb3356aa.gif) 7 | 8 | ## Actions 9 | 10 | You can see the output of the result of update and see the diff in a new buffer. 11 | 12 | ![ddu-dein_update-actions](https://user-images.githubusercontent.com/63794197/162574041-f847ea9c-8a07-44e7-85e9-09f8f00dd604.gif) 13 | 14 | ## Install 15 | 16 | In addition to this plugin, please install 17 | [ddu.vim](https://github.com/Shougo/ddu.vim), 18 | [denops.vim](https://github.com/vim-denops/denops.vim) and 19 | [dein.vim](https://github.com/Shougo/dein.vim). 20 | 21 | ## Setup 22 | 23 | You need to specify matcher_dein_update as ddu matcher. 24 | 25 | ```vim 26 | call ddu#custom#patch_global({ 27 | \ 'sourceOptions' : { 28 | \ 'dein_update': { 29 | \ 'matchers': ['matcher_dein_update'], 30 | \ }, 31 | \ }, 32 | \ 'kindOptions': { 33 | \ 'dein_update': { 34 | \ 'defaultAction': 'viewDiff', 35 | \ }, 36 | \ }, 37 | \ 'actionOptions': { 38 | \ 'echo': { 39 | \ 'quit': v:false, 40 | \ }, 41 | \ 'echoDiff': { 42 | \ 'quit': v:false, 43 | \ }, 44 | \ }, 45 | \ }) 46 | ``` 47 | -------------------------------------------------------------------------------- /denops/@ddu-filters/matcher_dein_update.ts: -------------------------------------------------------------------------------- 1 | import { BaseFilter, DduItem, Denops } from "../@ddu_dein_update/deps.ts"; 2 | import { 3 | ActionData, 4 | PluginData, 5 | ProgressData, 6 | } from "../@ddu-sources/dein_update.ts"; 7 | 8 | type Params = { 9 | hlGroup: string; 10 | }; 11 | 12 | export class Filter extends BaseFilter { 13 | filter(args: { 14 | denops: Denops; 15 | input: string; 16 | items: DduItem[]; 17 | filterParams: Params; 18 | }): Promise { 19 | const pluginMap: Record = {}; 20 | let progress: DduItem | undefined = undefined; 21 | let numDone = 0; 22 | for (const item of args.items) { 23 | const action = item.action as ActionData; 24 | if (action.kind == "progress") { 25 | progress = item; 26 | continue; 27 | } 28 | if (action.done) { 29 | numDone++; 30 | } 31 | const path = action.path; 32 | const prev = pluginMap[path]?.action as PluginData; 33 | if (!prev || !prev.done) { 34 | pluginMap[path] = item; 35 | } 36 | } 37 | const items = Object.values(pluginMap).sort(( 38 | a, 39 | b, 40 | // @ts-ignore: 41 | ) => (b.action.score - a.action.score)); 42 | if (progress) { 43 | const numAll = (progress.action as ProgressData).numPlugin; 44 | const lenBar = (numDone / numAll) * 50; 45 | progress.word = `${numDone}/${numAll} [${"=".repeat(lenBar)}${ 46 | "-".repeat(50 - lenBar) 47 | }]`; 48 | items.unshift(progress); 49 | } 50 | return Promise.resolve(items); 51 | } 52 | 53 | params(): Params { 54 | return { 55 | hlGroup: "Title", 56 | }; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /denops/@ddu-kinds/dein_update.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ActionArguments, 3 | ActionFlags, 4 | BaseKind, 5 | batch, 6 | buffer, 7 | DduItem, 8 | fn, 9 | helper, 10 | Previewer, 11 | } from "../@ddu_dein_update/deps.ts"; 12 | import { ActionData } from "../@ddu-sources/dein_update.ts"; 13 | import { getOutput } from "../@ddu_dein_update/process.ts"; 14 | 15 | type Params = Record; 16 | 17 | type ViewParams = { 18 | paths: string[]; 19 | fold: boolean; 20 | }; 21 | 22 | type PluginDiff = { 23 | name: string; 24 | contents: string[]; 25 | }; 26 | 27 | export class Kind extends BaseKind { 28 | actions = { 29 | "echo": async (args: ActionArguments) => { 30 | const action = args.items[0].action as ActionData; 31 | if (action.kind == "progress") { 32 | return ActionFlags.Persist; 33 | } 34 | const res = action.result; 35 | if (res) { 36 | await helper.echo(args.denops, res.out + res.stderrOutput); 37 | } 38 | return ActionFlags.Persist; 39 | }, 40 | "echoDiff": async (args: ActionArguments) => { 41 | const action = args.items[0].action as ActionData; 42 | if (action.kind == "progress") { 43 | return ActionFlags.Persist; 44 | } 45 | if (action.revOld && action.revNew && action.revOld != action.revNew) { 46 | const res = await getOutput( 47 | action.path, 48 | "git", 49 | "diff", 50 | `${action.revOld}..${action.revNew}`, 51 | ); 52 | await helper.echo(args.denops, res); 53 | } 54 | return ActionFlags.Persist; 55 | }, 56 | "viewDiff": async (args: ActionArguments) => { 57 | const params = args.actionParams as ViewParams; 58 | const openPaths = params.paths ? params.paths : [ 59 | "doc", 60 | "README", 61 | "README.md", 62 | ]; 63 | const doFold = params.fold ? params.fold : true; 64 | 65 | const diffs: PluginDiff[] = []; 66 | for (const item of args.items) { 67 | const action = item.action as ActionData; 68 | if (action.kind == "progress") { 69 | continue; 70 | } 71 | if (action.revOld && action.revNew && action.revOld != action.revNew) { 72 | const res = await getOutput( 73 | action.path, 74 | "git", 75 | "diff", 76 | `${action.revOld}..${action.revNew}`, 77 | "--", 78 | ...openPaths, 79 | ); 80 | if (res.trim().length != 0) { 81 | diffs.push({ 82 | name: action.name, 83 | contents: [action.name].concat(res.split("\n")), 84 | }); 85 | } 86 | } 87 | } 88 | if (diffs.length !== 0) { 89 | await buffer.open( 90 | args.denops, 91 | `ddu_dein_update:diff`, 92 | ); 93 | const bufnr = await fn.bufnr(args.denops) as number; 94 | await batch(args.denops, async (denops) => { 95 | await fn.setbufvar(denops, bufnr, "&buftype", "nofile"); 96 | await fn.setbufvar(denops, bufnr, "&filetype", "diff"); 97 | await buffer.replace( 98 | denops, 99 | bufnr, 100 | diffs.flatMap((item) => item.contents), 101 | ); 102 | if (doFold && diffs.length > 0) { 103 | let total = 1; 104 | for (const diff of diffs) { 105 | denops.cmd( 106 | `${total},${total + diff.contents.length - 1}fold`, 107 | ); 108 | total += diff.contents.length; 109 | } 110 | } 111 | await buffer.concrete(denops, bufnr); 112 | }); 113 | return ActionFlags.None; 114 | } 115 | return ActionFlags.Persist; 116 | }, 117 | }; 118 | params(): Params { 119 | return {}; 120 | } 121 | 122 | getPreviewer(args: { 123 | item: DduItem; 124 | }): Promise { 125 | const action = args.item.action as ActionData; 126 | if (!action) { 127 | return Promise.resolve(undefined); 128 | } 129 | if (action.kind == "plugin" && action.result) { 130 | return Promise.resolve({ 131 | kind: "nofile", 132 | contents: action.result.out.split("\n"), 133 | }); 134 | } 135 | return Promise.resolve(undefined); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /denops/@ddu-sources/dein_update.ts: -------------------------------------------------------------------------------- 1 | import { 2 | abortable, 3 | BaseSource, 4 | Denops, 5 | helper, 6 | Item, 7 | ItemHighlight, 8 | pooledMap, 9 | } from "../@ddu_dein_update/deps.ts"; 10 | import { runInDir } from "../@ddu_dein_update/process.ts"; 11 | import { checkChanged, getDiff, getRev } from "../@ddu_dein_update/git.ts"; 12 | import { decode } from "../@ddu_dein_update/text.ts"; 13 | 14 | export type ActionData = ProgressData | PluginData; 15 | 16 | export type ProgressData = { 17 | kind: "progress"; 18 | numPlugin: number; 19 | }; 20 | 21 | export type PluginData = { 22 | kind: "plugin"; 23 | done: boolean; 24 | path: string; 25 | score: number; 26 | name: string; 27 | result?: RunResult; 28 | revOld?: string; 29 | revNew?: string; 30 | }; 31 | 32 | type Params = { 33 | maxProcess: number; 34 | useGraphQL: boolean; 35 | }; 36 | 37 | export type RunResult = { 38 | status: Deno.ProcessStatus; 39 | out: string; 40 | stderrOutput: string; 41 | }; 42 | 43 | type Dein = { 44 | name: string; 45 | path: string; 46 | repo: string; 47 | }; 48 | 49 | export function wordWithHighlights( 50 | items: [word: string, hlGroup?: string][], 51 | ): [string, ItemHighlight[]] { 52 | let len = 1; 53 | let wordAll = ""; 54 | const hls: ItemHighlight[] = []; 55 | for (const item of items) { 56 | const [word, hlGroup] = item; 57 | wordAll = wordAll.concat(word); 58 | if (hlGroup) { 59 | hls.push({ 60 | name: "ddu-dein_update-hl", 61 | hl_group: hlGroup, 62 | col: len, 63 | width: word.length, 64 | }); 65 | } 66 | len += word.length; 67 | } 68 | return [wordAll, hls]; 69 | } 70 | 71 | async function getDduItem( 72 | action: PluginData, 73 | dein: Dein, 74 | ): Promise> { 75 | if (!action.result?.status.success) { 76 | const [word, hls] = wordWithHighlights([ 77 | ["failure", "Error"], 78 | [" " + dein.repo], 79 | ]); 80 | action.score += 1e7; 81 | return { 82 | word: word, 83 | action, 84 | highlights: hls, 85 | }; 86 | } else { 87 | if (action.revOld && action.revNew && action.revOld != action.revNew) { 88 | const diff = await getDiff(dein.path, action.revOld, action.revNew); 89 | const changed = await checkChanged( 90 | dein.path, 91 | action.revOld, 92 | action.revNew, 93 | "doc", 94 | "README", 95 | "README.md", 96 | ); 97 | const [word, hls] = wordWithHighlights([ 98 | ["upgraded ", "Keyword"], 99 | [dein.repo + " "], 100 | diff?.insertions ? [diff.insertions + "+ ", "SpecialKey"] : [""], 101 | diff?.deletions ? [diff.deletions + "- ", "WarningMsg"] : [""], 102 | changed ? ["doc is changed"] : [""], 103 | ]); 104 | action.score += 2e7; 105 | return { 106 | word: word, 107 | action, 108 | highlights: hls, 109 | }; 110 | } else { 111 | return { 112 | word: `already up to date: ${dein.repo}`, 113 | action, 114 | }; 115 | } 116 | } 117 | } 118 | 119 | async function getPlugins( 120 | denops: Denops, 121 | useGraphQL: boolean, 122 | ): Promise { 123 | if (useGraphQL) { 124 | return (await denops.call("dein#get_updated_plugins")) as Dein[]; 125 | } else { 126 | return Object.values( 127 | await denops.call("dein#get") as Record, 128 | ); 129 | } 130 | } 131 | 132 | export class Source extends BaseSource { 133 | kind = "dein_update"; 134 | 135 | gather(args: { 136 | denops: Denops; 137 | sourceParams: Params; 138 | }): ReadableStream[]> { 139 | const abortController = new AbortController(); 140 | return new ReadableStream({ 141 | async start(controller) { 142 | const deins = await getPlugins( 143 | args.denops, 144 | args.sourceParams.useGraphQL, 145 | ); 146 | 147 | if (deins.length == 0) { 148 | await helper.echo(args.denops, "Nothing to update"); 149 | controller.close(); 150 | return; 151 | } 152 | 153 | controller.enqueue( 154 | [{ 155 | word: "[]", 156 | action: { kind: "progress", numPlugin: deins.length }, 157 | }], 158 | ); 159 | const synced: string[] = []; 160 | const results = pooledMap( 161 | args.sourceParams.maxProcess, 162 | deins, 163 | async (d) => { 164 | controller.enqueue( 165 | [{ 166 | word: `...upgrading ${d.repo}`, 167 | action: { 168 | kind: "plugin", 169 | done: false, 170 | path: d.path, 171 | score: 0, 172 | name: d.name, 173 | }, 174 | }], 175 | ); 176 | const revOld = await getRev(d.path); 177 | return new Promise>((resolve, reject) => { 178 | const proc = runInDir(d.path, "git", "pull", "--ff", "--ff-only"); 179 | const running = abortable( 180 | Promise.all([ 181 | proc.status(), 182 | proc.output(), 183 | proc.stderrOutput(), 184 | ]), 185 | abortController.signal, 186 | ); 187 | running.then( 188 | async ([status, out, stderrOutput]) => { 189 | const action: ActionData = { 190 | kind: "plugin", 191 | done: true, 192 | result: { 193 | status, 194 | out: decode(out), 195 | stderrOutput: decode(stderrOutput), 196 | }, 197 | path: d.path, 198 | score: Date.now(), 199 | revOld: revOld, 200 | revNew: await getRev(d.path), 201 | name: d.name, 202 | }; 203 | if (action.revNew && action.revOld != action.revNew) { 204 | synced.push(d.name); 205 | } 206 | resolve(await getDduItem(action, d)); 207 | }, 208 | ).catch((e) => { 209 | if (e instanceof DOMException) { 210 | proc.kill("SIGTERM"); 211 | } 212 | reject(e); 213 | }); 214 | }); 215 | }, 216 | ); 217 | try { 218 | for await (const result of results) { 219 | controller.enqueue([result]); 220 | } 221 | } catch (e: unknown) { 222 | if (e instanceof AggregateError) { 223 | for (const error of e.errors) { 224 | if (!(error instanceof DOMException)) { 225 | console.error(error); 226 | } else { 227 | // console.log("cancel"); 228 | } 229 | } 230 | } 231 | } 232 | if (synced.length != 0) { 233 | await helper.echo(args.denops, "Executing post sync process"); 234 | await args.denops.call("dein#post_sync", synced); 235 | } 236 | await helper.echo(args.denops, "Done"); 237 | controller.close(); 238 | }, 239 | 240 | cancel(reason): void { 241 | abortController.abort(reason); 242 | }, 243 | }); 244 | } 245 | 246 | params(): Params { 247 | return { 248 | maxProcess: 32, 249 | useGraphQL: false, 250 | }; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /denops/@ddu_dein_update/deps.ts: -------------------------------------------------------------------------------- 1 | export type { Denops } from "https://deno.land/x/denops_std@v4.3.3/mod.ts"; 2 | export { 3 | batch, 4 | gather, 5 | } from "https://deno.land/x/denops_std@v4.3.3/batch/mod.ts"; 6 | export * as op from "https://deno.land/x/denops_std@v4.3.3/option/mod.ts"; 7 | export * as path from "https://deno.land/std@0.187.0/path/mod.ts"; 8 | export * as fn from "https://deno.land/x/denops_std@v4.3.3/function/mod.ts"; 9 | export * as nvimFn from "https://deno.land/x/denops_std@v4.3.3/function/nvim/mod.ts"; 10 | export * as helper from "https://deno.land/x/denops_std@v4.3.3/helper/mod.ts"; 11 | export * as vars from "https://deno.land/x/denops_std@v4.3.3/variable/mod.ts"; 12 | export * as autocmd from "https://deno.land/x/denops_std@v4.3.3/autocmd/mod.ts"; 13 | export { isLike } from "https://deno.land/x/unknownutil@v2.1.1/mod.ts"; 14 | export * from "https://deno.land/x/ddu_vim@v2.8.4/types.ts"; 15 | export * as buffer from "https://deno.land/x/denops_std@v4.3.3/buffer/mod.ts"; 16 | export { 17 | abortable, 18 | pooledMap, 19 | } from "https://deno.land/std@0.187.0/async/mod.ts"; 20 | -------------------------------------------------------------------------------- /denops/@ddu_dein_update/deps_test.ts: -------------------------------------------------------------------------------- 1 | export * from "https://deno.land/std@0.187.0/testing/asserts.ts"; 2 | export * from "https://deno.land/std@0.187.0/testing/asserts.ts"; 3 | -------------------------------------------------------------------------------- /denops/@ddu_dein_update/git.ts: -------------------------------------------------------------------------------- 1 | import { getOutput } from "./process.ts"; 2 | 3 | export async function getRev(dir: string): Promise { 4 | try { 5 | return (await getOutput(dir, "git", "rev-parse", "HEAD")).trim(); 6 | } catch { 7 | return undefined; 8 | } 9 | } 10 | 11 | export async function checkChanged( 12 | dir: string, 13 | revOld: string, 14 | revNew: string, 15 | ...paths: string[] 16 | ): Promise { 17 | const out = (await getOutput( 18 | dir, 19 | "git", 20 | "diff", 21 | "--name-only", 22 | `${revOld}..${revNew}`, 23 | "--", 24 | ...paths, 25 | ))?.trim(); 26 | if (out.length != 0) return true; 27 | return false; 28 | } 29 | 30 | export type DiffItem = { 31 | changed: string; 32 | insertions?: string; 33 | deletions?: string; 34 | }; 35 | 36 | /** 37 | * Retrieve diff items like and parse it 38 | * "1 file changed, 3 insertions(+), 3 deletions(-)" 39 | */ 40 | export async function getDiff( 41 | dir: string, 42 | revOld: string, 43 | revNew: string, 44 | ): Promise { 45 | const out = (await getOutput( 46 | dir, 47 | "git", 48 | "diff", 49 | `${revOld}..${revNew}`, 50 | "--shortstat", 51 | ))?.trim(); 52 | if (!out) return undefined; 53 | return parseDiff(out); 54 | } 55 | 56 | /** 57 | * Parse retrieved diff 58 | */ 59 | export function parseDiff(diff: string): DiffItem | undefined { 60 | const m = diff.match( 61 | /^(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(\-\))?/, 62 | ); 63 | if (m) { 64 | return { 65 | changed: m[1], 66 | insertions: m[2], 67 | deletions: m[3], 68 | }; 69 | } 70 | return undefined; 71 | } 72 | -------------------------------------------------------------------------------- /denops/@ddu_dein_update/git_test.ts: -------------------------------------------------------------------------------- 1 | import { DiffItem, parseDiff } from "./git.ts"; 2 | import { assertEquals } from "./deps_test.ts"; 3 | 4 | Deno.test("parseDiff parse git diff result", () => { 5 | const dst: DiffItem = { 6 | changed: "1", 7 | insertions: "3", 8 | deletions: "3", 9 | }; 10 | const exp = parseDiff("1 file changed, 3 insertions(+), 3 deletions(-)"); 11 | assertEquals(dst, exp); 12 | }); 13 | 14 | Deno.test("parseDiff with large number and multiple files change", () => { 15 | const dst: DiffItem = { 16 | changed: "5", 17 | insertions: "102", 18 | deletions: "18", 19 | }; 20 | const exp = parseDiff("5 files changed, 102 insertions(+), 18 deletions(-)"); 21 | assertEquals(dst, exp); 22 | }); 23 | 24 | Deno.test("parseDiff with no deletions", () => { 25 | const exp: DiffItem = { 26 | changed: "1", 27 | insertions: "3", 28 | deletions: undefined, 29 | }; 30 | const dst = parseDiff("1 file changed, 3 insertions(+)"); 31 | assertEquals(dst, exp); 32 | }); 33 | 34 | Deno.test("parseDiff with single deletions", () => { 35 | const exp: DiffItem = { 36 | changed: "1", 37 | deletions: "1", 38 | insertions: undefined, 39 | }; 40 | const dst = parseDiff("1 file changed, 1 deletion(-)"); 41 | assertEquals(dst, exp); 42 | }); 43 | 44 | Deno.test("parseDiff with no insertions", () => { 45 | const exp: DiffItem = { 46 | changed: "1", 47 | deletions: "3", 48 | insertions: undefined, 49 | }; 50 | const dst = parseDiff("1 file changed, 3 deletions(-)"); 51 | assertEquals(dst, exp); 52 | }); 53 | -------------------------------------------------------------------------------- /denops/@ddu_dein_update/process.ts: -------------------------------------------------------------------------------- 1 | import { decode } from "./text.ts"; 2 | export class ExecuteError extends Error { 3 | constructor( 4 | public args: string[], 5 | public code: number, 6 | public stdout: Uint8Array, 7 | public stderr: Uint8Array, 8 | ) { 9 | super(`[${code}]: ${decode(stderr)}`); 10 | this.name = "ExecuteError"; 11 | } 12 | } 13 | 14 | export async function getOutput( 15 | dir: string, 16 | ...cmds: string[] 17 | ): Promise { 18 | const proc = Deno.run({ 19 | cmd: cmds, 20 | stdout: "piped", 21 | stderr: "piped", 22 | cwd: dir, 23 | }); 24 | const [status, stdout, stderr] = await Promise.all([ 25 | proc.status(), 26 | proc.output(), 27 | proc.stderrOutput(), 28 | ]); 29 | proc.close(); 30 | 31 | if (!status.success) { 32 | throw new ExecuteError(cmds, status.code, stdout, stderr); 33 | } 34 | return decode(stdout); 35 | } 36 | 37 | export function runInDir(dir: string, ...cmds: string[]): Deno.Process { 38 | return Deno.run({ 39 | cmd: cmds, 40 | stdout: "piped", 41 | stderr: "piped", 42 | cwd: dir, 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /denops/@ddu_dein_update/text.ts: -------------------------------------------------------------------------------- 1 | const textDecoder = new TextDecoder(); 2 | 3 | export function decode(arr: BufferSource): string { 4 | return textDecoder.decode(arr); 5 | } 6 | -------------------------------------------------------------------------------- /denops/@ddu_dein_update/text_test.ts: -------------------------------------------------------------------------------- 1 | import { decode } from "./text.ts"; 2 | import { assertEquals } from "./deps_test.ts"; 3 | 4 | Deno.test("decode()", () => { 5 | const exp = "Hello world!"; 6 | const input = new Uint8Array([ 7 | 72, 8 | 101, 9 | 108, 10 | 108, 11 | 111, 12 | 32, 13 | 119, 14 | 111, 15 | 114, 16 | 108, 17 | 100, 18 | 33, 19 | ]); 20 | assertEquals(decode(input), exp); 21 | }); 22 | 23 | Deno.test("decode() empty", () => { 24 | const exp = ""; 25 | const input = new Uint8Array([]); 26 | assertEquals(decode(input), exp); 27 | }); 28 | -------------------------------------------------------------------------------- /doc/ddu-source-dein_update.txt: -------------------------------------------------------------------------------- 1 | *ddu-source-dein_update.txt* Dein upgrader source for ddu.vim 2 | 3 | Author: matsui54 4 | License: MIT license 5 | 6 | CONTENTS *ddu-dein_update-contents* 7 | 8 | Introduction |ddu-dein_update-introduction| 9 | Install |ddu-dein_update-install| 10 | Usage |ddu-dein_update-usage| 11 | Params |ddu-dein_update-source-params| 12 | Actions |ddu-dein_update-actions| 13 | 14 | 15 | ============================================================================== 16 | INTRODUCTION *ddu-dein_update-introduction* 17 | 18 | This source updates the plugins installed by dein.vim and output the result 19 | to ddu buffer. 20 | 21 | ============================================================================== 22 | INSTALL *ddu-dein_update-install* 23 | 24 | Please install both "ddu.vim", "denops.vim" and "dein.vim". 25 | 26 | https://github.com/Shougo/ddu.vim 27 | https://github.com/vim-denops/denops.vim 28 | https://github.com/Shougo/dein.vim 29 | 30 | 31 | ============================================================================== 32 | USAGE *ddu-dein_update-usage* 33 | 34 | You need to specify matcher_dein_update as ddu matcher. 35 | > 36 | call ddu#custom#patch_global({ 37 | \ 'sourceOptions' : { 38 | \ 'dein_update': { 39 | \ 'matchers': ['matcher_dein_update'], 40 | \ }, 41 | \ }, 42 | \ 'kindOptions': { 43 | \ 'dein_update': { 44 | \ 'defaultAction': 'viewDiff', 45 | \ }, 46 | \ }, 47 | \ 'actionOptions': { 48 | \ 'echo': { 49 | \ 'quit': v:false, 50 | \ }, 51 | \ 'echoDiff': { 52 | \ 'quit': v:false, 53 | \ }, 54 | \ }, 55 | \ }) 56 | < 57 | 58 | ============================================================================== 59 | PARAMS *ddu-dein_update-source-params* 60 | 61 | *ddu-dein_update-source-param-maxProcess* 62 | maxProcess 63 | number (default 32) 64 | The max number of processes to update the plugins. 65 | 66 | *ddu-dein_update-source-param-useGraphQL* 67 | useGraphQL 68 | boolean (default false) 69 | If it is true, plugins are updated using github GraphQL API, which is 70 | the same behavior as |dein#check_update()|. 71 | Note: You need to set |g:dein#install_github_api_token| to use 72 | the feature. 73 | 74 | ============================================================================== 75 | ACTIONS *ddu-dein_update-actions* 76 | 77 | *ddu-dein_update-action-echo* 78 | echo 79 | Echo the output of `git pull` command. 80 | 81 | *ddu-dein_update-action-echoDiff* 82 | echoDiff 83 | Echo the diff of update. 84 | 85 | *ddu-dein_update-action-viewDiff* 86 | viewDiff 87 | Create a new buffer with the diffs of selected plugins. 88 | The following params are available. 89 | 90 | paths: string[] 91 | (default ["doc", "README", "README.md"]) 92 | Paths to show the diffs. 93 | 94 | fold: boolean 95 | (default true) 96 | If it is true and multiple plugins are selected, each diff of 97 | plugins is folded. 98 | 99 | *ddu-dein_update-action-preview* 100 | preview 101 | Preview the output of `git pull` command. 102 | 103 | ============================================================================== 104 | vim:tw=78:ts=8:ft=help:norl:noet:fen:noet: 105 | --------------------------------------------------------------------------------