diff options
author | Björn Linse <bjorn.linse@gmail.com> | 2021-10-02 17:47:55 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-02 17:47:55 +0200 |
commit | 79fb9ed080bb32a442d1c788da6f0b71d72bcedd (patch) | |
tree | 9696af62e973d2b739d30db91007b3923dffdc6d | |
parent | 9c268263b1792d00b3ffdfd7495af2575862656e (diff) | |
parent | 9df7e022b498cb74ffbf5b8fd2ddc4dd5c04d127 (diff) | |
download | rneovim-79fb9ed080bb32a442d1c788da6f0b71d72bcedd.tar.gz rneovim-79fb9ed080bb32a442d1c788da6f0b71d72bcedd.tar.bz2 rneovim-79fb9ed080bb32a442d1c788da6f0b71d72bcedd.zip |
Merge pull request #15867 from bfredl/starpack
fix(runtime): add compressed {&packpath}/start/*/pack/*[/after] representation to &rtp
by suggestion by at-tpope
Summary:
We can add XDG_DATA_DIR/nvim/site/pack/*/start/* (et al) as an unexpanded wildchar to &rtp which keeps it both short and explicit and still supporting globpath(&rtp, ...).
ref #15101
-rw-r--r-- | src/nvim/main.c | 29 | ||||
-rw-r--r-- | src/nvim/runtime.c | 168 | ||||
-rw-r--r-- | test/functional/api/vim_spec.lua | 17 | ||||
-rw-r--r-- | test/functional/core/startup_spec.lua | 40 |
4 files changed, 185 insertions, 69 deletions
diff --git a/src/nvim/main.c b/src/nvim/main.c index d977589ad7..f801351d2d 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1306,35 +1306,6 @@ static void set_window_layout(mparm_T *paramp) } } -/* - * Read all the plugin files. - * Only when compiled with +eval, since most plugins need it. - */ -static void load_plugins(void) -{ - if (p_lpl) { - char_u *rtp_copy = NULL; - char_u *const plugin_pattern_vim = (char_u *)"plugin/**/*.vim"; // NOLINT - char_u *const plugin_pattern_lua = (char_u *)"plugin/**/*.lua"; // NOLINT - - // don't use source_runtime() yet so we can check for :packloadall below - source_in_path(p_rtp, plugin_pattern_vim, DIP_ALL | DIP_NOAFTER); - source_in_path(p_rtp, plugin_pattern_lua, DIP_ALL | DIP_NOAFTER); - TIME_MSG("loading rtp plugins"); - xfree(rtp_copy); - - // Only source "start" packages if not done already with a :packloadall - // command. - if (!did_source_packages) { - load_start_packages(); - } - TIME_MSG("loading packages"); - - source_runtime(plugin_pattern_vim, DIP_ALL | DIP_AFTER); - source_runtime(plugin_pattern_lua, DIP_ALL | DIP_AFTER); - TIME_MSG("loading after plugins"); - } -} /* * "-q errorfile": Load the error file now. diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index f0b54a254f..e1669a8c19 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -332,40 +332,56 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB c return done; } -static void push_path(RuntimeSearchPath *search_path, char *entry, bool after) +static void push_path(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, + char *entry, bool after) { - kv_push(*search_path, ((SearchPathItem){ entry, after })); + handle_T h = map_get(String, handle_T)(rtp_used, cstr_as_string((char *)entry)); + if (h == 0) { + char *allocated = xstrdup(entry); + map_put(String, handle_T)(rtp_used, cstr_as_string(allocated), 1); + kv_push(*search_path, ((SearchPathItem){ allocated, after })); + } } -static void expand_pack_entry(RuntimeSearchPath *search_path, CharVec *after_path, - char_u *pack_entry) +static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, + char *entry, bool after) { - static char_u buf[MAXPATHL], buf2[MAXPATHL]; - char *start_dir = "/pack/*/start/*"; // NOLINT - if (STRLEN(pack_entry) + STRLEN(start_dir) + 1 < MAXPATHL) { - xstrlcpy((char *)buf, (char *)pack_entry, MAXPATHL); - xstrlcpy((char *)buf2, (char *)pack_entry, MAXPATHL); - xstrlcat((char *)buf, start_dir, sizeof buf); - xstrlcat((char *)buf2, "/start/*", sizeof buf); // NOLINT - int num_files; - char_u **files; - - char_u *(pat[]) = { buf, buf2 }; - if (gen_expand_wildcards(2, pat, &num_files, &files, EW_DIR) == OK) { - for (int i = 0; i < num_files; i++) { - push_path(search_path, xstrdup((char *)files[i]), false); - size_t after_size = STRLEN(files[i])+7; - char *after = xmallocz(after_size); - xstrlcpy(after, (char *)files[i], after_size); - xstrlcat(after, "/after", after_size); - if (os_isdir((char_u *)after)) { - kv_push(*after_path, after); - } else { - xfree(after); - } - } - FreeWild(num_files, files); + if (map_get(String, handle_T)(rtp_used, cstr_as_string(entry))) { + return; + } + + if (!*entry) { + push_path(search_path, rtp_used, entry, after); + } + + int num_files; + char_u **files; + char_u *(pat[]) = { (char_u *)entry }; + if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { + for (int i = 0; i < num_files; i++) { + push_path(search_path, rtp_used, (char *)files[i], after); + } + FreeWild(num_files, files); + } +} + +static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, + CharVec *after_path, char_u *pack_entry) +{ + static char buf[MAXPATHL]; + char *(start_pat[]) = { "/pack/*/start/*", "/start/*" }; // NOLINT + for (int i = 0; i < 2; i++) { + if (STRLEN(pack_entry) + STRLEN(start_pat[i]) + 1 > MAXPATHL) { + continue; } + xstrlcpy(buf, (char *)pack_entry, MAXPATHL); + xstrlcat(buf, start_pat[i], sizeof buf); + expand_rtp_entry(search_path, rtp_used, buf, false); + size_t after_size = STRLEN(buf)+7; + char *after = xmallocz(after_size); + xstrlcpy(after, buf, after_size); + xstrlcat(after, "/after", after_size); + kv_push(*after_path, after); } } @@ -382,8 +398,10 @@ static bool path_is_after(char_u *buf, size_t buflen) RuntimeSearchPath runtime_search_path_build(void) { kvec_t(String) pack_entries = KV_INITIAL_VALUE; + // TODO(bfredl): these should just be sets, when Set(String) is do merge to + // master. Map(String, handle_T) pack_used = MAP_INIT; - // TODO(bfredl): add a set of existing rtp entries to not duplicate those + Map(String, handle_T) rtp_used = MAP_INIT; RuntimeSearchPath search_path = KV_INITIAL_VALUE; CharVec after_path = KV_INITIAL_VALUE; @@ -410,37 +428,40 @@ RuntimeSearchPath runtime_search_path_build(void) break; } - push_path(&search_path, xstrdup((char *)buf), false); + // fact: &rtp entries can contain wild chars + expand_rtp_entry(&search_path, &rtp_used, (char *)buf, false); handle_T *h = map_ref(String, handle_T)(&pack_used, cstr_as_string((char *)buf), false); if (h) { (*h)++; - expand_pack_entry(&search_path, &after_path, buf); + expand_pack_entry(&search_path, &rtp_used, &after_path, buf); } } for (size_t i = 0; i < kv_size(pack_entries); i++) { handle_T h = map_get(String, handle_T)(&pack_used, kv_A(pack_entries, i)); if (h == 0) { - expand_pack_entry(&search_path, &after_path, (char_u *)kv_A(pack_entries, i).data); + expand_pack_entry(&search_path, &rtp_used, &after_path, (char_u *)kv_A(pack_entries, i).data); } } // "after" packages for (size_t i = 0; i < kv_size(after_path); i++) { - push_path(&search_path, kv_A(after_path, i), true); + expand_rtp_entry(&search_path, &rtp_used, kv_A(after_path, i), true); + xfree(kv_A(after_path, i)); } // "after" dirs in rtp for (; *rtp_entry != NUL;) { copy_option_part((char_u **)&rtp_entry, buf, MAXPATHL, ","); - push_path(&search_path, xstrdup((char *)buf), path_is_after(buf, STRLEN(buf))); + expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after(buf, STRLEN(buf))); } // strings are not owned kv_destroy(pack_entries); kv_destroy(after_path); map_destroy(String, handle_T)(&pack_used); + map_destroy(String, handle_T)(&rtp_used); return search_path; } @@ -521,7 +542,10 @@ static void source_all_matches(char_u *pat) } /// Add the package directory to 'runtimepath' -static int add_pack_dir_to_rtp(char_u *fname) +/// +/// @param fname the package path +/// @param is_pack whether the added dir is a "pack/*/start/*/" style package +static int add_pack_dir_to_rtp(char_u *fname, bool is_pack) { char_u *p4, *p3, *p2, *p1, *p; char_u *buf = NULL; @@ -599,7 +623,7 @@ static int add_pack_dir_to_rtp(char_u *fname) // check if rtp/pack/name/start/name/after exists afterdir = concat_fnames((char *)fname, "after", true); size_t afterlen = 0; - if (os_isdir((char_u *)afterdir)) { + if (is_pack ? pack_has_entries((char_u *)afterdir) : os_isdir((char_u *)afterdir)) { afterlen = strlen(afterdir) + 1; // add one for comma } @@ -720,7 +744,7 @@ static void add_pack_plugin(bool opt, char_u *fname, void *cookie) xfree(buf); if (!found) { // directory is not yet in 'runtimepath', add it - if (add_pack_dir_to_rtp(fname) == FAIL) { + if (add_pack_dir_to_rtp(fname, false) == FAIL) { return; } } @@ -741,6 +765,41 @@ static void add_opt_pack_plugin(char_u *fname, void *cookie) add_pack_plugin(true, fname, cookie); } + +/// Add all packages in the "start" directory to 'runtimepath'. +void add_pack_start_dirs(void) +{ + do_in_path(p_pp, NULL, DIP_ALL + DIP_DIR, add_pack_start_dir, NULL); +} + +static bool pack_has_entries(char_u *buf) +{ + int num_files; + char_u **files; + char_u *(pat[]) = { (char_u *)buf }; + if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { + FreeWild(num_files, files); + } + return num_files > 0; +} + +static void add_pack_start_dir(char_u *fname, void *cookie) +{ + static char_u buf[MAXPATHL]; + char *(start_pat[]) = { "/start/*", "/pack/*/start/*" }; // NOLINT + for (int i = 0; i < 2; i++) { + if (STRLEN(fname) + STRLEN(start_pat[i]) + 1 > MAXPATHL) { + continue; + } + xstrlcpy((char *)buf, (char *)fname, MAXPATHL); + xstrlcat((char *)buf, start_pat[i], sizeof buf); + if (pack_has_entries(buf)) { + add_pack_dir_to_rtp(buf, true); + } + } +} + + /// Load plugins from all packages in the "start" directory. void load_start_packages(void) { @@ -759,10 +818,43 @@ void ex_packloadall(exarg_T *eap) // First do a round to add all directories to 'runtimepath', then load // the plugins. This allows for plugins to use an autoload directory // of another plugin. + add_pack_start_dirs(); load_start_packages(); } } +/// Read all the plugin files at startup +void load_plugins(void) +{ + if (p_lpl) { + char_u *rtp_copy = p_rtp; + char_u *const plugin_pattern_vim = (char_u *)"plugin/**/*.vim"; // NOLINT + char_u *const plugin_pattern_lua = (char_u *)"plugin/**/*.lua"; // NOLINT + + if (!did_source_packages) { + rtp_copy = vim_strsave(p_rtp); + add_pack_start_dirs(); + } + + // don't use source_runtime() yet so we can check for :packloadall below + source_in_path(rtp_copy, plugin_pattern_vim, DIP_ALL | DIP_NOAFTER); + source_in_path(rtp_copy, plugin_pattern_lua, DIP_ALL | DIP_NOAFTER); + TIME_MSG("loading rtp plugins"); + + // Only source "start" packages if not done already with a :packloadall + // command. + if (!did_source_packages) { + xfree(rtp_copy); + load_start_packages(); + } + TIME_MSG("loading packages"); + + source_runtime(plugin_pattern_vim, DIP_ALL | DIP_AFTER); + source_runtime(plugin_pattern_lua, DIP_ALL | DIP_AFTER); + TIME_MSG("loading after plugins"); + } +} + /// ":packadd[!] {name}" void ex_packadd(exarg_T *eap) { diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index ffef6a6066..df602ed48c 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -12,10 +12,12 @@ local funcs = helpers.funcs local iswin = helpers.iswin local meths = helpers.meths local matches = helpers.matches +local mkdir_p = helpers.mkdir_p local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed local is_os = helpers.is_os local parse_context = helpers.parse_context local request = helpers.request +local rmdir = helpers.rmdir local source = helpers.source local next_msg = helpers.next_msg local tmpname = helpers.tmpname @@ -1574,6 +1576,18 @@ describe('API', function() end) describe('nvim_list_runtime_paths', function() + setup(function() + local pathsep = helpers.get_pathsep() + mkdir_p('Xtest'..pathsep..'a') + mkdir_p('Xtest'..pathsep..'b') + end) + teardown(function() + rmdir 'Xtest' + end) + before_each(function() + meths.set_current_dir 'Xtest' + end) + it('returns nothing with empty &runtimepath', function() meths.set_option('runtimepath', '') eq({}, meths.list_runtime_paths()) @@ -1601,8 +1615,7 @@ describe('API', function() local long_path = ('/a'):rep(8192) meths.set_option('runtimepath', long_path) local paths_list = meths.list_runtime_paths() - neq({long_path}, paths_list) - eq({long_path:sub(1, #(paths_list[1]))}, paths_list) + eq({}, paths_list) end) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 1d83eb799f..bf2559f8d7 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -20,6 +20,7 @@ local retry = helpers.retry local rmdir = helpers.rmdir local sleep = helpers.sleep local iswin = helpers.iswin +local startswith = helpers.startswith local write_file = helpers.write_file local meths = helpers.meths @@ -355,11 +356,50 @@ describe('startup', function() eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) end) + it("handles the correct order with start packages and after/ after startup", function() + pack_clear [[ lua _G.test_loadorder = {} ]] + command [[ runtime! filen.lua ]] + eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with globpath(&rtp, ...)", function() + pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] + command [[ + for x in globpath(&rtp, "filen.lua",1,1) + call v:lua.dofile(x) + endfor + ]] + eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + + local rtp = meths.get_option'rtp' + ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'), 'rtp='..rtp) + end) + it("handles the correct order with opt packages and after/", function() pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! superspecial\nruntime! filen.lua" ]] eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) end) + it("handles the correct order with opt packages and after/ after startup", function() + pack_clear [[ lua _G.test_loadorder = {} ]] + command [[ + packadd! superspecial + runtime! filen.lua + ]] + eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with opt packages and globpath(&rtp, ...)", function() + pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] + command [[ + packadd! superspecial + for x in globpath(&rtp, "filen.lua",1,1) + call v:lua.dofile(x) + endfor + ]] + eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'SuperSpecial after', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + it("handles the correct order with a package that changes packpath", function() pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! funky\nruntime! filen.lua" ]] eq({'ordinary', 'funky!', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) |