aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/runtime.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/runtime.c')
-rw-r--r--src/nvim/runtime.c521
1 files changed, 471 insertions, 50 deletions
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index d4191cff6b..5ade6244f9 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -5,21 +5,26 @@
///
/// Management of runtime files (including packages)
-#include "nvim/vim.h"
+#include "nvim/api/private/helpers.h"
#include "nvim/ascii.h"
#include "nvim/charset.h"
#include "nvim/eval.h"
-#include "nvim/option.h"
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
+#include "nvim/lua/executor.h"
#include "nvim/misc1.h"
+#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/runtime.h"
+#include "nvim/vim.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "runtime.c.generated.h"
#endif
+static bool runtime_search_path_valid = false;
+static int *runtime_search_path_ref = NULL;
+static RuntimeSearchPath runtime_search_path;
/// ":runtime [what] {name}"
void ex_runtime(exarg_T *eap)
@@ -43,13 +48,13 @@ void ex_runtime(exarg_T *eap)
arg = skipwhite(arg + len);
}
- source_runtime(arg, flags);
+ source_runtime((char *)arg, flags);
}
static void source_callback(char_u *fname, void *cookie)
{
- (void)do_source(fname, false, DOSO_NONE);
+ (void)do_source((char *)fname, false, DOSO_NONE);
}
/// Find the file "name" in all directories in "path" and invoke
@@ -60,12 +65,11 @@ static void source_callback(char_u *fname, void *cookie)
/// When "flags" has DIP_ERR: give an error message if there is no match.
///
/// return FAIL when no file could be sourced, OK otherwise.
-int do_in_path(char_u *path, char_u *name, int flags,
- DoInRuntimepathCB callback, void *cookie)
+int do_in_path(char_u *path, char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
- char_u *tail;
+ char_u *tail;
int num_files;
- char_u **files;
+ char_u **files;
int i;
bool did_one = false;
@@ -76,8 +80,7 @@ int do_in_path(char_u *path, char_u *name, int flags,
{
if (p_verbose > 10 && name != NULL) {
verbose_enter();
- smsg(_("Searching for \"%s\" in \"%s\""),
- (char *)name, (char *)path);
+ smsg(_("Searching for \"%s\" in \"%s\""), name, (char *)path);
verbose_leave();
}
@@ -90,8 +93,7 @@ int do_in_path(char_u *path, char_u *name, int flags,
// Skip after or non-after directories.
if (flags & (DIP_NOAFTER | DIP_AFTER)) {
- bool is_after = buflen >= 5
- && STRCMP(buf + buflen - 5, "after") == 0;
+ bool is_after = path_is_after(buf, buflen);
if ((is_after && (flags & DIP_NOAFTER))
|| (!is_after && (flags & DIP_AFTER))) {
@@ -107,7 +109,7 @@ int do_in_path(char_u *path, char_u *name, int flags,
tail = buf + STRLEN(buf);
// Loop over all patterns in "name"
- char_u *np = name;
+ char_u *np = (char_u *)name;
while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
// Append the pattern from "name" to buf[].
assert(MAXPATHL >= (tail - buf));
@@ -156,6 +158,188 @@ int do_in_path(char_u *path, char_u *name, int flags,
return did_one ? OK : FAIL;
}
+RuntimeSearchPath runtime_search_path_get_cached(int *ref)
+ FUNC_ATTR_NONNULL_ALL
+{
+ runtime_search_path_validate();
+
+ *ref = 0;
+ if (runtime_search_path_ref == NULL) {
+ // cached path was unreferenced. keep a ref to
+ // prevent runtime_search_path() to freeing it too early
+ (*ref)++;
+ runtime_search_path_ref = ref;
+ }
+ return runtime_search_path;
+}
+
+void runtime_search_path_unref(RuntimeSearchPath path, int *ref)
+ FUNC_ATTR_NONNULL_ALL
+{
+ if (*ref) {
+ if (runtime_search_path_ref == ref) {
+ runtime_search_path_ref = NULL;
+ } else {
+ runtime_search_path_free(path);
+ }
+ }
+}
+
+
+/// Find the file "name" in all directories in "path" and invoke
+/// "callback(fname, cookie)".
+/// "name" can contain wildcards.
+/// When "flags" has DIP_ALL: source all files, otherwise only the first one.
+/// When "flags" has DIP_DIR: find directories instead of files.
+/// When "flags" has DIP_ERR: give an error message if there is no match.
+///
+/// return FAIL when no file could be sourced, OK otherwise.
+int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void *cookie)
+{
+ char_u *tail;
+ int num_files;
+ char_u **files;
+ int i;
+ bool did_one = false;
+
+ char_u buf[MAXPATHL];
+
+ if (p_verbose > 10 && name != NULL) {
+ verbose_enter();
+ smsg(_("Searching for \"%s\" in runtime path"), (char *)name);
+ verbose_leave();
+ }
+
+ int ref;
+ RuntimeSearchPath path = runtime_search_path_get_cached(&ref);
+
+ // Loop over all entries in cached path
+ for (size_t j = 0; j < kv_size(path); j++) {
+ SearchPathItem item = kv_A(path, j);
+ size_t buflen = strlen(item.path);
+
+ // Skip after or non-after directories.
+ if (flags & (DIP_NOAFTER | DIP_AFTER)) {
+ if ((item.after && (flags & DIP_NOAFTER))
+ || (!item.after && (flags & DIP_AFTER))) {
+ continue;
+ }
+ }
+
+ if (name == NULL) {
+ (*callback)((char_u *)item.path, cookie);
+ } else if (buflen + STRLEN(name) + 2 < MAXPATHL) {
+ STRCPY(buf, item.path);
+ add_pathsep((char *)buf);
+ tail = buf + STRLEN(buf);
+
+ // Loop over all patterns in "name"
+ char_u *np = name;
+ while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
+ // Append the pattern from "name" to buf[].
+ assert(MAXPATHL >= (tail - buf));
+ copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)),
+ "\t ");
+
+ if (p_verbose > 10) {
+ verbose_enter();
+ smsg(_("Searching for \"%s\""), buf);
+ verbose_leave();
+ }
+
+ int ew_flags = ((flags & DIP_DIR) ? EW_DIR : EW_FILE)
+ | (flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0;
+
+ // Expand wildcards, invoke the callback for each match.
+ char_u *(pat[]) = { buf };
+ if (gen_expand_wildcards(1, pat, &num_files, &files, ew_flags) == OK) {
+ for (i = 0; i < num_files; i++) {
+ (*callback)(files[i], cookie);
+ did_one = true;
+ if (!(flags & DIP_ALL)) {
+ break;
+ }
+ }
+ FreeWild(num_files, files);
+ }
+ }
+ }
+ }
+
+ if (!did_one && name != NULL) {
+ if (flags & DIP_ERR) {
+ EMSG3(_(e_dirnotf), "runtime path", name);
+ } else if (p_verbose > 0) {
+ verbose_enter();
+ smsg(_("not found in runtime path: \"%s\""), name);
+ verbose_leave();
+ }
+ }
+
+ runtime_search_path_unref(path, &ref);
+
+ return did_one ? OK : FAIL;
+}
+
+Array runtime_inspect(void)
+{
+ RuntimeSearchPath path = runtime_search_path;
+ Array rv = ARRAY_DICT_INIT;
+
+ for (size_t i = 0; i < kv_size(path); i++) {
+ SearchPathItem *item = &kv_A(path, i);
+ Array entry = ARRAY_DICT_INIT;
+ ADD(entry, STRING_OBJ(cstr_to_string(item->path)));
+ ADD(entry, BOOLEAN_OBJ(item->after));
+ if (item->has_lua != kNone) {
+ ADD(entry, BOOLEAN_OBJ(item->has_lua == kTrue));
+ }
+ ADD(rv, ARRAY_OBJ(entry));
+ }
+ return rv;
+}
+
+ArrayOf(String) runtime_get_named(bool lua, Array pat, bool all)
+{
+ int ref;
+ RuntimeSearchPath path = runtime_search_path_get_cached(&ref);
+ ArrayOf(String) rv = ARRAY_DICT_INIT;
+ static char buf[MAXPATHL];
+
+ for (size_t i = 0; i < kv_size(path); i++) {
+ SearchPathItem *item = &kv_A(path, i);
+ if (lua) {
+ if (item->has_lua == kNone) {
+ size_t size = (size_t)snprintf(buf, sizeof buf, "%s/lua/", item->path);
+ item->has_lua = (size < sizeof buf && os_isdir((char_u *)buf)) ? kTrue : kFalse;
+ }
+ if (item->has_lua == kFalse) {
+ continue;
+ }
+ }
+
+ for (size_t j = 0; j < pat.size; j++) {
+ Object pat_item = pat.items[j];
+ if (pat_item.type == kObjectTypeString) {
+ size_t size = (size_t)snprintf(buf, sizeof buf, "%s/%s",
+ item->path, pat_item.data.string.data);
+ if (size < sizeof buf) {
+ if (os_file_is_readable(buf)) {
+ ADD(rv, STRING_OBJ(cstr_to_string(buf)));
+ if (!all) {
+ goto done;
+ }
+ }
+ }
+ }
+ }
+ }
+
+done:
+ runtime_search_path_unref(path, &ref);
+ return rv;
+}
+
/// Find "name" in "path". When found, invoke the callback function for
/// it: callback(fname, "cookie")
/// When "flags" has DIP_ALL repeat for all matches, otherwise only the first
@@ -164,29 +348,22 @@ int do_in_path(char_u *path, char_u *name, int flags,
/// If "name" is NULL calls callback for each entry in "path". Cookie is
/// passed by reference in this case, setting it to NULL indicates that callback
/// has done its job.
-int do_in_path_and_pp(char_u *path, char_u *name, int flags,
- DoInRuntimepathCB callback, void *cookie)
+int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB callback,
+ void *cookie)
{
int done = FAIL;
- if (!(flags & (DIP_NOAFTER | DIP_AFTER))) {
- done = do_in_path_and_pp(path, name, flags | DIP_NOAFTER, callback, cookie);
- if (done == OK && !(flags & DIP_ALL)) {
- return done;
- }
- flags |= DIP_AFTER;
- }
if ((flags & DIP_NORTP) == 0) {
- done |= do_in_path(path, (name && !*name) ? NULL : name, flags, callback, cookie);
+ done |= do_in_path(path, (char *)((name && !*name) ? NULL : name), flags, callback, cookie);
}
if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) {
char *start_dir = "pack/*/start/*/%s%s"; // NOLINT
size_t len = STRLEN(start_dir) + STRLEN(name) + 6;
- char_u *s = xmallocz(len); // TODO(bfredl): get rid of random allocations
+ char *s = xmallocz(len); // TODO(bfredl): get rid of random allocations
char *suffix = (flags & DIP_AFTER) ? "after/" : "";
- vim_snprintf((char *)s, len, start_dir, suffix, name);
+ vim_snprintf(s, len, start_dir, suffix, name);
done |= do_in_path(p_pp, s, flags & ~DIP_AFTER, callback, cookie);
xfree(s);
@@ -196,7 +373,7 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags,
len = STRLEN(start_dir) + STRLEN(name) + 6;
s = xmallocz(len);
- vim_snprintf((char *)s, len, start_dir, suffix, name);
+ vim_snprintf(s, len, start_dir, suffix, name);
done |= do_in_path(p_pp, s, flags & ~DIP_AFTER, callback, cookie);
xfree(s);
@@ -206,9 +383,9 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags,
if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_OPT)) {
char *opt_dir = "pack/*/opt/*/%s"; // NOLINT
size_t len = STRLEN(opt_dir) + STRLEN(name);
- char_u *s = xmallocz(len);
+ char *s = xmallocz(len);
- vim_snprintf((char *)s, len, opt_dir, name);
+ vim_snprintf(s, len, opt_dir, name);
done |= do_in_path(p_pp, s, flags, callback, cookie);
xfree(s);
@@ -218,7 +395,7 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags,
len = STRLEN(opt_dir) + STRLEN(name);
s = xmallocz(len);
- vim_snprintf((char *)s, len, opt_dir, name);
+ vim_snprintf(s, len, opt_dir, name);
done |= do_in_path(p_pp, s, flags, callback, cookie);
xfree(s);
@@ -228,10 +405,189 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags,
return done;
}
+static void push_path(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, char *entry,
+ bool after)
+{
+ handle_T h = map_get(String, handle_T)(rtp_used, cstr_as_string(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, kNone }));
+ }
+}
+
+static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used,
+ char *entry, bool after)
+{
+ 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;
+ }
+ STRLCPY(buf, 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);
+ }
+}
+
+static bool path_is_after(char_u *buf, size_t buflen)
+{
+ // NOTE: we only consider dirs exactly matching "after" to be an AFTER dir.
+ // vim8 considers all dirs like "foo/bar_after", "Xafter" etc, as an
+ // "after" dir in SOME codepaths not not in ALL codepaths.
+ return buflen >= 5
+ && (!(buflen >= 6) || vim_ispathsep(buf[buflen-6]))
+ && STRCMP(buf + buflen - 5, "after") == 0;
+}
+
+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;
+ Map(String, handle_T) rtp_used = MAP_INIT;
+ RuntimeSearchPath search_path = KV_INITIAL_VALUE;
+ CharVec after_path = KV_INITIAL_VALUE;
+
+ static char_u buf[MAXPATHL];
+ for (char *entry = (char *)p_pp; *entry != NUL; ) {
+ char *cur_entry = entry;
+ copy_option_part((char_u **)&entry, buf, MAXPATHL, ",");
+
+ String the_entry = { .data = cur_entry, .size = STRLEN(buf) };
+
+ kv_push(pack_entries, the_entry);
+ map_put(String, handle_T)(&pack_used, the_entry, 0);
+ }
+
+
+ char *rtp_entry;
+ for (rtp_entry = (char *)p_rtp; *rtp_entry != NUL; ) {
+ char *cur_entry = rtp_entry;
+ copy_option_part((char_u **)&rtp_entry, buf, MAXPATHL, ",");
+ size_t buflen = STRLEN(buf);
+
+ if (path_is_after(buf, buflen)) {
+ rtp_entry = cur_entry;
+ break;
+ }
+
+ // 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, &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, &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++) {
+ 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, ",");
+ 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;
+}
+
+void runtime_search_path_invalidate(void)
+{
+ runtime_search_path_valid = false;
+}
+
+void runtime_search_path_free(RuntimeSearchPath path)
+{
+ for (size_t j = 0; j < kv_size(path); j++) {
+ SearchPathItem item = kv_A(path, j);
+ xfree(item.path);
+ }
+ kv_destroy(path);
+}
+
+void runtime_search_path_validate(void)
+{
+ if (!nlua_is_deferred_safe()) {
+ // Cannot rebuild search path in an async context. As a plugin will invoke
+ // itself asynchronously from sync code in the same plugin, the sought
+ // after lua/autoload module will most likely already be in the cached path.
+ // Thus prefer using the stale cache over erroring out in this situation.
+ return;
+ }
+ if (!runtime_search_path_valid) {
+ if (!runtime_search_path_ref) {
+ runtime_search_path_free(runtime_search_path);
+ }
+ runtime_search_path = runtime_search_path_build();
+ runtime_search_path_valid = true;
+ runtime_search_path_ref = NULL; // initially unowned
+ }
+}
+
+
+
/// Just like do_in_path_and_pp(), using 'runtimepath' for "path".
int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
- return do_in_path_and_pp(p_rtp, name, flags | DIP_START, callback, cookie);
+ int success = FAIL;
+ if (!(flags & DIP_NORTP)) {
+ success |= do_in_cached_path((name && !*name) ? NULL : name, flags, callback, cookie);
+ flags = (flags & ~DIP_START) | DIP_NORTP;
+ }
+ // TODO(bfredl): we could integrate disabled OPT dirs into the cached path
+ // which would effectivize ":packadd myoptpack" as well
+ if ((flags & (DIP_START|DIP_OPT)) && (success == FAIL || (flags & DIP_ALL))) {
+ success |= do_in_path_and_pp(p_rtp, name, flags, callback, cookie);
+ }
+ return success;
}
/// Source the file "name" from all directories in 'runtimepath'.
@@ -239,10 +595,9 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, void
/// When "flags" has DIP_ALL: source all files, otherwise only the first one.
///
/// return FAIL when no file could be sourced, OK otherwise.
-int source_runtime(char_u *name, int flags)
+int source_runtime(char *name, int flags)
{
- flags |= (flags & DIP_NORTP) ? 0 : DIP_START;
- return source_in_path(p_rtp, name, flags);
+ return do_in_runtimepath((char_u *)name, flags, source_callback, NULL);
}
/// Just like source_runtime(), but use "path" instead of 'runtimepath'.
@@ -260,14 +615,17 @@ static void source_all_matches(char_u *pat)
if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) == OK) {
for (int i = 0; i < num_files; i++) {
- (void)do_source(files[i], false, DOSO_NONE);
+ (void)do_source((char *)files[i], false, DOSO_NONE);
}
FreeWild(num_files, files);
}
}
/// 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;
@@ -345,7 +703,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
}
@@ -466,7 +824,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;
}
}
@@ -487,13 +845,48 @@ 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[]) = { 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;
+ }
+ STRLCPY(buf, 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)
{
did_source_packages = true;
- do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
+ do_in_path(p_pp, "pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
add_start_pack_plugin, &APP_LOAD);
- do_in_path(p_pp, (char_u *)"start/*", DIP_ALL + DIP_DIR, // NOLINT
+ do_in_path(p_pp, "start/*", DIP_ALL + DIP_DIR, // NOLINT
add_start_pack_plugin, &APP_LOAD);
}
@@ -505,10 +898,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((char *)plugin_pattern_vim, DIP_ALL | DIP_AFTER);
+ source_runtime((char *)plugin_pattern_lua, DIP_ALL | DIP_AFTER);
+ TIME_MSG("loading after plugins");
+ }
+}
+
/// ":packadd[!] {name}"
void ex_packadd(exarg_T *eap)
{
@@ -527,9 +953,7 @@ void ex_packadd(exarg_T *eap)
vim_snprintf(pat, len, plugpat, round == 1 ? "start" : "opt", eap->arg);
// The first round don't give a "not found" error, in the second round
// only when nothing was found in the first round.
- res = do_in_path(p_pp, (char_u *)pat,
- DIP_ALL + DIP_DIR
- + (round == 2 && res == FAIL ? DIP_ERR : 0),
+ res = do_in_path(p_pp, pat, DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
round == 1 ? add_start_pack_plugin : add_opt_pack_plugin,
eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
xfree(pat);
@@ -564,8 +988,7 @@ static char *strcpy_comma_escaped(char *dest, const char *src, const size_t len)
/// (common_suf is present after each new item, single_suf is present
/// after half of the new items) and with commas after each item, commas
/// inside the values are escaped.
-static inline size_t compute_double_env_sep_len(const char *const val,
- const size_t common_suf_len,
+static inline size_t compute_double_env_sep_len(const char *const val, const size_t common_suf_len,
const size_t single_suf_len)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
{
@@ -608,9 +1031,8 @@ static inline size_t compute_double_env_sep_len(const char *const val,
/// Otherwise in reverse.
///
/// @return (dest + appended_characters_length)
-static inline char *add_env_sep_dirs(char *dest, const char *const val,
- const char *const suf1, const size_t len1,
- const char *const suf2, const size_t len2,
+static inline char *add_env_sep_dirs(char *dest, const char *const val, const char *const suf1,
+ const size_t len1, const char *const suf2, const size_t len2,
const bool forward)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1)
{
@@ -669,9 +1091,8 @@ static inline char *add_env_sep_dirs(char *dest, const char *const val,
/// Otherwise in reverse.
///
/// @return (dest + appended_characters_length)
-static inline char *add_dir(char *dest, const char *const dir,
- const size_t dir_len, const XDGVarType type,
- const char *const suf1, const size_t len1,
+static inline char *add_dir(char *dest, const char *const dir, const size_t dir_len,
+ const XDGVarType type, const char *const suf1, const size_t len1,
const char *const suf2, const size_t len2)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT
{