mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	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
			
			
This commit is contained in:
		| @@ -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. | ||||
|   | ||||
| @@ -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; | ||||
|   if (map_get(String, handle_T)(rtp_used, cstr_as_string(entry))) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|     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 (!*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) | ||||
| { | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
| @@ -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 ]]) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Björn Linse
					Björn Linse