From d9f10f0710a3816e9aa132c610f82137df7a464a Mon Sep 17 00:00:00 2001 From: Shougo Matsushita Date: Mon, 23 Oct 2023 18:35:43 +0900 Subject: [PATCH] Add hooks_file feature --- autoload/dpp/util.vim | 2 +- denops/dpp/dpp.ts | 109 +++++++++++++++++++++++++++++----------- denops/dpp/types.ts | 5 +- denops/dpp/utils.ts | 112 +++++++++++++++++++++++++++++++++++++++++- doc/dpp.txt | 36 ++------------ 5 files changed, 198 insertions(+), 66 deletions(-) diff --git a/autoload/dpp/util.vim b/autoload/dpp/util.vim index 256ea3c..c4315a7 100644 --- a/autoload/dpp/util.vim +++ b/autoload/dpp/util.vim @@ -24,7 +24,7 @@ endfunction function! dpp#util#_check_files() abort const time = dpp#util#_get_runtime_path()->getftime() const ret = !(g:dpp#_check_files->copy() - \ ->map({ _, val -> val->expand()->getftime() }) + \ ->map({ _, val -> dpp#util#_expand(val)->getftime() }) \ ->filter({ _, val -> time < val })->empty()) if !ret return 0 diff --git a/denops/dpp/dpp.ts b/denops/dpp/dpp.ts index e108884..3b6fed4 100644 --- a/denops/dpp/dpp.ts +++ b/denops/dpp/dpp.ts @@ -35,7 +35,7 @@ import { Loader } from "./loader.ts"; import { defaultExtOptions } from "./base/ext.ts"; import { defaultProtocolOptions } from "./base/protocol.ts"; import { ConfigReturn } from "./base/config.ts"; -import { errorException, isDirectory } from "./utils.ts"; +import { errorException, isDirectory, parseHooksFile } from "./utils.ts"; export class Dpp { private loader: Loader; @@ -113,14 +113,32 @@ export class Dpp { // Initialize plugins const protocols = await this.getProtocols(denops, options); const recordPlugins: Record = {}; - for (const plugin of configReturn.plugins) { - recordPlugins[plugin.name] = initPlugin( - await detectPlugin( - denops, - options, - protocols, + for (let plugin of configReturn.plugins) { + plugin = await detectPlugin( + denops, + options, + protocols, + plugin, + ); + + if (plugin.hooks_file) { + const hooksFile = await denops.call( + "dpp#util#_expand", + plugin.hooks_file, + ) as string; + const hooksFileLines = (await Deno.readTextFile(hooksFile)).split("\n"); + + plugin = Object.assign( plugin, - ), + parseHooksFile( + options.hooksFileMarker, + hooksFileLines, + ), + ); + } + + recordPlugins[plugin.name] = initPlugin( + plugin, basePath, ); } @@ -228,12 +246,45 @@ export class Dpp { `let &runtimepath = '${newRuntimepath}'`, ]; - for (const plugin of Object.values(recordPlugins)) { - if (!plugin.path || !await isDirectory(plugin.path) || !plugin.hook_add) { - continue; + // hooksFiles + if (configReturn.hooksFiles) { + for ( + const hooksFile of await Promise.all(configReturn.hooksFiles.map( + async (hooksFile) => + await denops.call("dpp#util#_expand", hooksFile) as string, + )) + ) { + const hooksFileLines = (await Deno.readTextFile(hooksFile)).split("\n"); + + const parsedHooksFile = parseHooksFile( + options.hooksFileMarker, + hooksFileLines, + ); + + // Use ftplugin only + if (parsedHooksFile.ftplugin && is.Record(parsedHooksFile.ftplugin)) { + if (!configReturn.ftplugins) { + configReturn.ftplugins = {}; + } + + // Merge ftplugins + for (const filetype of Object.keys(parsedHooksFile.ftplugin)) { + if (configReturn.ftplugins[filetype]) { + configReturn.ftplugins[filetype] += `\n${ + parsedHooksFile.ftplugin[filetype] + }`; + } else { + configReturn.ftplugins[filetype] = + parsedHooksFile.ftplugin[filetype]; + } + } + } } + } - stateLines.push(plugin.hook_add); + let checkFiles = configReturn.checkFiles ?? []; + if (configReturn.hooksFiles) { + checkFiles = checkFiles.concat(configReturn.hooksFiles); } if (await vars.g.get(denops, "did_load_filetypes", false)) { @@ -249,6 +300,20 @@ export class Dpp { stateLines = stateLines.concat(configReturn.stateLines); } + for (const plugin of Object.values(recordPlugins)) { + if (!plugin.path || !await isDirectory(plugin.path)) { + continue; + } + + if (plugin.hooks_file) { + checkFiles.push(plugin.hooks_file); + } + + if (plugin.hook_add) { + stateLines.push(plugin.hook_add); + } + } + const inlineVimrcs = await Promise.all(options.inlineVimrcs.map( async (vimrc) => await denops.call("dpp#util#_expand", vimrc) as string, )); @@ -279,7 +344,7 @@ export class Dpp { recordPlugins, {}, options, - configReturn.checkFiles ?? [], + checkFiles, ]), ]; //console.log(cacheFile); @@ -292,7 +357,7 @@ export class Dpp { await this.mergePlugins(denops, dppRuntimepath, recordPlugins); // Generate ftplugin files - console.log(configReturn.ftplugins); + //console.log(configReturn.ftplugins); if (configReturn.ftplugins) { const generatedFtplugins = await denops.call( "dpp#util#_generate_ftplugin", @@ -666,22 +731,6 @@ async function detectPlugin( return plugin; } -async function parseHooksFile( - denops: Denops, - marker: string, - hooksFile: string[], -): Promise> { - const startMarker = marker.split(',')[0] - const endMarker = marker.split(',')[1] - const options: Partial = {}; - let hookName = ""; - - for (const line of hooksFile) { - } - - return options; -} - Deno.test("initPlugin", () => { assertEquals( initPlugin({ diff --git a/denops/dpp/types.ts b/denops/dpp/types.ts index 9048b3b..49d6bda 100644 --- a/denops/dpp/types.ts +++ b/denops/dpp/types.ts @@ -81,15 +81,18 @@ export type Plugin = { augroup?: string; build?: string; depends?: string | string[]; + dummy_commands?: string[]; + dummy_mappings?: string[]; frozen?: boolean; ftplugin?: Record; - if?: boolean | string; hook_add?: string; hook_depends_update?: string; hook_done_update?: string; hook_post_source?: string; hook_post_update?: string; hook_source?: string; + hooks_file?: string; + if?: boolean | string; lazy?: boolean; local?: boolean; lua_add?: string; diff --git a/denops/dpp/utils.ts b/denops/dpp/utils.ts index 32e4990..09017bf 100644 --- a/denops/dpp/utils.ts +++ b/denops/dpp/utils.ts @@ -1,4 +1,4 @@ -import { Denops } from "./deps.ts"; +import { assertEquals, Denops } from "./deps.ts"; export async function errorException( denops: Denops, @@ -61,3 +61,113 @@ export async function safeStat(path: string): Promise { } return null; } + +export function parseHooksFile( + marker: string, + hooksFile: string[], +): Record> { + const startMarker = marker.split(",")[0]; + const endMarker = marker.split(",")[1]; + const options: Record> = {}; + const ftplugin: Record = {}; + let dest: + | Record> + | Record + | null = null; + let hookName = ""; + + for (const line of hooksFile) { + if (hookName.length === 0) { + const markerPos = line.lastIndexOf(startMarker); + if (markerPos < 0) { + continue; + } + + dest = null; + + // Get hookName + const match = line.slice(0, markerPos).match( + /\s+(?[0-9a-zA-Z_-]+)\s*/, + ); + if (!match || !match.groups) { + // Invalid marker + continue; + } + + hookName = match.groups.hookName; + if (hookName.startsWith("hook_") || hookName.startsWith("lua_")) { + dest = options; + } else { + // Use ftplugin + dest = ftplugin; + } + } else { + if (line.endsWith(endMarker)) { + hookName = ""; + continue; + } + + if (!dest) { + continue; + } + + if (dest[hookName]) { + dest[hookName] += "\n" + line; + } else { + dest[hookName] = line; + } + } + } + + options["ftplugin"] = ftplugin; + + return options; +} + +Deno.test("parseHooksFile", () => { + assertEquals( + parseHooksFile("{{{,}}}", [ + '" c {{{', + "hogera", + "}}}", + '" hook_source {{{', + "piyo", + "}}}", + ]), + { + hook_source: "piyo", + ftplugin: { + c: "hogera", + }, + }, + ); + + // Invalid line + assertEquals( + parseHooksFile("{{{,}}}", [ + '" {{{', + "hogera", + "}}}", + '" hook_source {{{', + "piyo", + "}}}", + ]), + { + hook_source: "piyo", + ftplugin: {}, + }, + ); + + // Lua + assertEquals( + parseHooksFile("{{{,}}}", [ + "-- lua_source {{{", + "piyo", + "-- }}}", + ]), + { + lua_source: "piyo", + ftplugin: {}, + }, + ); +}); diff --git a/doc/dpp.txt b/doc/dpp.txt index d184150..84e750d 100644 --- a/doc/dpp.txt +++ b/doc/dpp.txt @@ -235,37 +235,6 @@ ftplugin (Dictionary) NOTE: You need to call |dpp#clear_state()| after vimrc is changed. - *dpp-plugin-option-hooks_file* -hooks_file (String) or (List) - Hooks file path. It must be valid path. - It is useful to define long hooks. - - The file format is: - - {hook_name} {start_marker} - ... - {end_marker} - - {start_marker} and {end_marker} are from - |dpp-option-hooksFileMarker|. - {hook_name} is "hook_xxx" or "lua_xxx" or - |dpp-plugin-options-ftplugin| filetype. - - Example: -> - " hook_source {{{ - let g:foo = 'bar' - " }}} - " cpp {{{ - let g:bar = 'baz' - " }}} - " Use vim9script - " hook_update {{{ - vim9script - g:bar = 'baz' - " }}} -< - *dpp-plugin-option-if* if (Bool) or (String) If set to |v:false|, dpp doesn't load the plugin. If it is |String|, dpp will eval it. @@ -440,9 +409,10 @@ hooks_file (String) or (List) ... {end_marker} - {start_marker} and {end_marker} are from hooks_file_marker. + {start_marker} and {end_marker} are from + |dpp-option-hooksFileMarker|. {hook_name} is "hook_xxx" or "lua_xxx" or - |dpp-plugin-option-ftplugin| filetype. + |dpp-plugin-options-ftplugin| filetype. Example: >