import * as sourceList from "../../../denops/@ddu-sources/list.ts"; // import { KindGitStatusActionData } from "../../../denops/@deps/ddu-kinds.ts"; import { cmd, map } from "../../../denops/@vimrc/lib/lambda/map.ts"; import { dduHelper } from "./lib/helper.ts"; import { is, maybe } from "jsr:@core/unknownutil"; import { Denops } from "jsr:@denops/std"; import * as autocmd from "jsr:@denops/std/autocmd"; import { ActionData as KindFileActionData, FileActions, } from "jsr:@shougo/ddu-kind-file"; import { BaseConfig, ConfigArguments } from "jsr:@shougo/ddu-vim/config"; import { ActionFlags, DduOptions, SourceOptions, } from "jsr:@shougo/ddu-vim/types"; import * as stdpath from "jsr:@std/path"; const augroup = "vimrc#ddu"; /* main section */ type Filter = { matchers: SourceOptions["matchers"]; sorters: SourceOptions["sorters"]; converters: SourceOptions["converters"]; }; type Filters = Record; // satisfiesしておくとRecordにはならないので便利 const Filters = { fzf: { matchers: ["matcher_fzf"], sorters: ["sorter_fzf"], converters: [], }, fzf_shuffle: { matchers: ["matcher_fzf"], sorters: ["sorter_shuffle", "sorter_fzf"], converters: [], }, sorter_alpha_path: { matchers: [], sorters: ["sorter_alpha_path"], converters: [], }, mtime_substring: { matchers: ["matcher_substring"], sorters: ["sorter_mtime"], converters: [], }, } satisfies Filters; const FiltersLocal = { file_hl_dir: { ...Filters.fzf, converters: ["converter_hl_dir"], }, git_status: { ...Filters.fzf, converters: [ "converter_hl_dir", "converter_git_status", ], }, } satisfies Filters; function setupGitStatus(args: ConfigArguments) { const ddu = dduHelper(args.denops); args.contextBuilder.patchGlobal({ kindOptions: { git_status: { actions: { commit: async () => { await args.denops.call("p#gin#komitto"); // await args.denops.cmd("Gin commit"); return ActionFlags.None; }, diff: (args) => { const action = args.items[0].action as KindGitStatusActionData; const path = stdpath.join(action.worktree, action.path); ddu.start({ name: "git_diff", sources: [{ name: "git_diff", options: { path, }, params: { unifiedContext: 0, ...maybe(args.actionParams, is.Record) ?? {}, onlyFile: true, }, }], }); return Promise.resolve(ActionFlags.None); }, patch: async (args) => { for (const item of args.items) { const action = item.action as KindGitStatusActionData; await args.denops.cmd("tabnew"); await args.denops.cmd("tcd " + action.worktree); await args.denops.cmd("GinPatch ++no-head " + action.path); } return ActionFlags.None; }, }, defaultAction: "open", }, }, sourceOptions: { // X git_status: { ...FiltersLocal.git_status, }, }, }); args.contextBuilder.patchLocal("git_status", { actionOptions: { add: { quit: false }, reset: { quit: false }, restore: { quit: false }, }, sources: ["git_status"], }); } // X // L も直す function setupLocals(args: ConfigArguments) { // X for (const type of ["mru", "mrw", "mrr", "mrd"]) { args.contextBuilder.patchLocal(type, { sources: [{ name: "mr", options: { converters: ["converter_hl_dir"], }, params: { kind: type, }, }], }); } // X args.contextBuilder.patchLocal("dpp", { sources: [{ name: "dpp", options: { ...Filters.fzf_shuffle, }, }], }); // X args.contextBuilder.patchLocal("file_ext_mrw", { sources: [{ name: "file_external", options: { ...Filters.mtime_substring, }, }], }); args.contextBuilder.patchLocal("file_rec_mrw", { sources: [{ name: "file_rec", options: { ...Filters.mtime_substring, }, }], }); // X args.contextBuilder.patchLocal("git_diff", { actionOptions: { applyPatch: { quit: false }, }, sources: ["git_diff"], uiParams: { ff: { maxDisplayItems: 100000, maxHighlightItems: 100000, }, }, }); // X args.contextBuilder.patchLocal("help", { sources: ["help"], }); // X args.contextBuilder.patchLocal("line", { sources: ["line"], }); // X args.contextBuilder.patchLocal("lsp", { // b:ddu_source_lsp_clientNameをセットしているため // sync付けてないと参照できない sync: true, }); // X args.contextBuilder.patchLocal("tags", { sources: ["tags"], sync: true, }); // X args.contextBuilder.patchLocal("file", { // sources: ["file"], sourceOptions: { "file": { ...Filters.sorter_alpha_path, }, }, sync: true, uiParams: { ff: { maxDisplayItems: 10000, }, }, }); } async function mainConfig(args: ConfigArguments) { // X args.contextBuilder.patchGlobal({ actionOptions: { narrow: { quit: false, }, }, filterParams: { // X converter_hl_dir: { hlGroup: "String", }, // X matcher_fzf: { highlightMatched: "DduMatch", }, // X matcher_kensaku: { highlightMatched: "DduMatch", }, }, kindOptions: { callback: { defaultAction: "call" }, // X file: { actions: { fern: async ({ items }) => { for (const item of items) { const action = item.action as KindFileActionData; await args.denops.cmd(`Fern ${action.path}`); } return ActionFlags.None; }, smartopen: async (args) => { for (const item of args.items) { const isDirectory = (_item: typeof item): boolean => { const data = _item.action as KindFileActionData; try { const stat = Deno.statSync(data.path!); return stat.isDirectory; } catch { return false; } }; if (isDirectory(item)) { // deno-lint-ignore no-explicit-any await (FileActions as any).cd.callback({ ...args, items: [item], }); } else { // deno-lint-ignore no-explicit-any await (FileActions as any).open.callback({ ...args, items: [item], }); } } return ActionFlags.None; }, }, defaultAction: "smartopen", }, help: { defaultAction: "tabopen" }, lsp: { defaultAction: "open" }, lsp_codeAction: { defaultAction: "apply" }, nixpkgs: { defaultAction: "open" }, tag: { defaultAction: "jump" }, }, postFilters: [{ name: "converter_color", params: { group: "DduNormal", }, }, "converter_normalize_hl"], sourceOptions: { _: { ignoreCase: true, ...Filters.fzf, }, help: { ...Filters.fzf_shuffle, }, }, sourceParams: { file_external: { cmd: ["rg", "--files", "--no-ignore"], }, }, }); setupGitStatus(args); setupLocals(args); } /* selector section */ type Promisify = T | Promise; // 環境から情報を収集してオプションに変える感じで type POptions = Promisify>; type Collector = (denops: Denops) => POptions; async function ripgrep( denops: Denops, findPath: (denops: Denops) => Promise, live: boolean, ): Promise> { // ddu-source-rg is set to lazy, load it. await denops.call("dpp#source", ["ddu-source-rg"]); return { name: "file:rg", sources: [{ name: "rg", options: { ...Filters.sorter_alpha_path, path: await findPath(denops), volatile: live, }, params: live ? {} : { input: String(await denops.call("input", "Pattern: ")) }, }], uiParams: { ff: { ignoreEmpty: false, autoResize: false, }, }, }; } // X const definition: Record = { file: async (denops) => ({ name: "file", sourceOptions: { file: { path: String(await denops.call("expand", "%:p:h")), }, }, searchPath: String(await denops.call("expand", "%:p")), sync: true, }), filer: async (denops) => { await denops.dispatcher.loadConfig( await denops.call("expand", "$VIMCONF/conf/plug/ddu/filer.ts"), ); return { name: "filer", sources: [{ name: "file", options: { columns: ["filename"], path: String(await denops.call("expand", "%:p:h")), }, }], // syncとsearchPathによりrevealできるらしい searchPath: String(await denops.call("expand", "%:p")), sync: true, ui: "filer", }; }, git_branch: async (denops) => ({ name: "git_branch", sources: [{ name: "git_branch", options: { path: String(await denops.call("expand", "%:p:h")), }, params: { remote: true, }, }], }), git_diff: async (denops) => ({ name: "git_diff", sourceOptions: { git_diff: { path: String(await denops.call("expand", "%:p")), }, }, }), github_repo_pull: async (denops) => { // L await denops.call("dpp#source", "ddu-source-github"); return { name: "github_repo_pull", sources: [{ name: "github_repo_pull", params: { path: await denops.call("expand", "%:p:h"), }, }], }; }, grep: (denops) => ripgrep( denops, async (denops) => String(await denops.call("expand", "%:p:h")), false, ), grep_git: (denops) => ripgrep( denops, async (denops) => String(await denops.call("vimrc#git#find_root")), false, ), live_grep: (denops) => ripgrep( denops, async (denops) => String(await denops.call("expand", "%:p:h")), true, ), live_grep_git: (denops) => ripgrep( denops, async (denops) => String(await denops.call("vimrc#git#find_root")), true, ), // X lsp_codeAction: () => ({ name: "lsp", sources: [{ name: "lsp_codeAction", }], }), lsp_definition: () => ({ name: "lsp", sources: ["lsp_definition"], }), lsp_diagnostic: () => ({ name: "lsp", sources: [{ name: "lsp_diagnostic", params: { clientName: "lspoints", buffer: 0, }, }], }), lsp_references: () => ({ name: "lsp", sources: ["lsp_references"], }), quickfix: () => ({ name: "file", sources: ["qf"], }), }; async function selectorConfig(args: ConfigArguments) { const { denops } = args; const ddu = dduHelper(denops); await map(denops, ";s", () => { // コレクターを選んで実行しdduに渡す ddu.start( sourceList.buildOptions( Object.keys(definition).sort(), async (items) => { if (items[0] != null) { await denops.call( "histadd", ":", "DduSelectorCall " + items[0].word, ); ddu.start(await definition[items[0].word](denops)); } return ActionFlags.None; }, ), ); }, { noremap: true, }); await cmd(denops, "DduSelectorCall", async (args) => { const def = await definition[args.arg]?.(denops); if (def == null) { throw Error(`DduSelectorCall failed: ${args.arg}`); } ddu.start(def); }); } export class Config extends BaseConfig { override async config(args: ConfigArguments) { await autocmd.group(args.denops, augroup, (helper) => { helper.remove("*"); helper.define( "User", "vimrc#ddu#ready", "let g:vimrc#ddu#ready = v:true", { once: true }, ); }); mainConfig(args); await selectorConfig(args); await new (await import("./ff.ts")).Config().config(args); // このタイミングでddu呼び出しするとロックかかるのでawaitしない autocmd.emit(args.denops, "User", "vimrc#ddu#ready"); } }