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.c925
1 files changed, 604 insertions, 321 deletions
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index b071e10cf9..38d3942126 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -1,6 +1,3 @@
-// This is an open source non-commercial project. Dear PVS-Studio, please check
-// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
-
/// @file runtime.c
///
/// Management of runtime files (including packages)
@@ -9,44 +6,58 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <stdbool.h>
+#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <uv.h>
+#include "klib/kvec.h"
#include "nvim/api/private/defs.h"
#include "nvim/api/private/helpers.h"
-#include "nvim/ascii.h"
+#include "nvim/ascii_defs.h"
#include "nvim/autocmd.h"
-#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/cmdexpand.h"
#include "nvim/debugger.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
+#include "nvim/func_attr.h"
#include "nvim/garray.h"
#include "nvim/getchar.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
+#include "nvim/hashtab.h"
#include "nvim/lua/executor.h"
-#include "nvim/macros.h"
-#include "nvim/map.h"
+#include "nvim/macros_defs.h"
+#include "nvim/map_defs.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
+#include "nvim/option_defs.h"
+#include "nvim/option_vars.h"
+#include "nvim/os/fs.h"
#include "nvim/os/input.h"
#include "nvim/os/os.h"
#include "nvim/os/stdpaths_defs.h"
#include "nvim/path.h"
+#include "nvim/pos_defs.h"
#include "nvim/profile.h"
+#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/strings.h"
+#include "nvim/types_defs.h"
#include "nvim/usercmd.h"
-#include "nvim/vim.h"
+#include "nvim/vim_defs.h"
+#ifdef USE_CRNL
+# include "nvim/highlight.h"
+#endif
/// Structure used to store info for each sourced file.
/// It is shared between do_source() and getsourceline().
@@ -57,7 +68,7 @@ struct source_cookie {
char *nextline; ///< if not NULL: line that was read ahead
linenr_T sourcing_lnum; ///< line number of the source file
int finished; ///< ":finish" used
-#if defined(USE_CRNL)
+#ifdef USE_CRNL
int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS
bool error; ///< true if LF found after CR-LF
#endif
@@ -68,12 +79,26 @@ struct source_cookie {
vimconv_T conv; ///< type of conversion
};
+typedef struct {
+ char *path;
+ bool after;
+ TriState has_lua;
+} SearchPathItem;
+
+typedef kvec_t(SearchPathItem) RuntimeSearchPath;
+typedef kvec_t(char *) CharVec;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "runtime.c.generated.h"
#endif
garray_T exestack = { 0, 0, sizeof(estack_T), 50, NULL };
-garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL };
+garray_T script_items = { 0, 0, sizeof(scriptitem_T *), 20, NULL };
+
+/// The names of packages that once were loaded are remembered.
+static garray_T ga_loaded = { 0, 0, sizeof(char *), 4, NULL };
+
+static int last_current_SID_seq = 0;
/// Initialize the execution stack.
void estack_init(void)
@@ -144,8 +169,8 @@ char *estack_sfile(estack_arg_T which)
? &entry->es_info.ufunc->uf_script_ctx
: &entry->es_info.aucmd->script_ctx);
return def_ctx->sc_sid > 0
- ? xstrdup((SCRIPT_ITEM(def_ctx->sc_sid).sn_name))
- : NULL;
+ ? xstrdup((SCRIPT_ITEM(def_ctx->sc_sid)->sn_name))
+ : NULL;
} else if (entry->es_type == ETYPE_SCRIPT) {
return xstrdup(entry->es_name);
}
@@ -178,8 +203,8 @@ char *estack_sfile(estack_arg_T which)
len += strlen(type_name);
ga_grow(&ga, (int)len);
linenr_T lnum = idx == exestack.ga_len - 1
- ? which == ESTACK_STACK ? SOURCING_LNUM : 0
- : entry->es_lnum;
+ ? which == ESTACK_STACK ? SOURCING_LNUM : 0
+ : entry->es_lnum;
char *dots = idx == exestack.ga_len - 1 ? "" : "..";
if (lnum == 0) {
// For the bottom entry of <sfile>: do not add the line number,
@@ -246,6 +271,7 @@ void ex_runtime(exarg_T *eap)
int flags = eap->forceit ? DIP_ALL : 0;
char *p = skiptowhite(arg);
flags += get_runtime_cmd_flags(&arg, (size_t)(p - arg));
+ assert(arg != NULL); // suppress clang false positive
source_runtime(arg, flags);
}
@@ -257,29 +283,83 @@ void set_context_in_runtime_cmd(expand_T *xp, const char *arg)
char *p = skiptowhite(arg);
runtime_expand_flags
= *p != NUL ? get_runtime_cmd_flags((char **)&arg, (size_t)(p - arg)) : 0;
+ // Skip to the last argument.
+ while (*(p = skiptowhite_esc(arg)) != NUL) {
+ if (runtime_expand_flags == 0) {
+ // When there are multiple arguments and [where] is not specified,
+ // use an unrelated non-zero flag to avoid expanding [where].
+ runtime_expand_flags = DIP_ALL;
+ }
+ arg = skipwhite(p);
+ }
xp->xp_context = EXPAND_RUNTIME;
xp->xp_pattern = (char *)arg;
}
-static void source_callback(char *fname, void *cookie)
+/// Source all .vim and .lua files in "fnames" with .vim files being sourced first.
+static bool source_callback_vim_lua(int num_fnames, char **fnames, bool all, void *cookie)
{
- (void)do_source(fname, false, DOSO_NONE);
+ bool did_one = false;
+
+ for (int i = 0; i < num_fnames; i++) {
+ if (path_with_extension(fnames[i], "vim")) {
+ (void)do_source(fnames[i], false, DOSO_NONE, cookie);
+ did_one = true;
+ if (!all) {
+ return true;
+ }
+ }
+ }
+
+ for (int i = 0; i < num_fnames; i++) {
+ if (path_with_extension(fnames[i], "lua")) {
+ (void)do_source(fnames[i], false, DOSO_NONE, cookie);
+ did_one = true;
+ if (!all) {
+ return true;
+ }
+ }
+ }
+
+ return did_one;
}
-/// Find the file "name" in all directories in "path" and invoke
+/// Source all files in "fnames" with .vim files sourced first, .lua files
+/// sourced second, and any remaining files sourced last.
+static bool source_callback(int num_fnames, char **fnames, bool all, void *cookie)
+{
+ bool did_one = source_callback_vim_lua(num_fnames, fnames, all, cookie);
+
+ if (!all && did_one) {
+ return true;
+ }
+
+ for (int i = 0; i < num_fnames; i++) {
+ if (!path_with_extension(fnames[i], "vim")
+ && !path_with_extension(fnames[i], "lua")) {
+ (void)do_source(fnames[i], false, DOSO_NONE, cookie);
+ did_one = true;
+ if (!all) {
+ return true;
+ }
+ }
+ }
+
+ return did_one;
+}
+
+/// Find the patterns in "name" in all directories in "path" and invoke
/// "callback(fname, cookie)".
-/// "name" can contain wildcards.
+/// "prefix" is prepended to each pattern in "name".
/// 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_path(char *path, char *name, int flags, DoInRuntimepathCB callback, void *cookie)
+/// Return FAIL when no file could be sourced, OK otherwise.
+int do_in_path(const char *path, const char *prefix, char *name, int flags,
+ DoInRuntimepathCB callback, void *cookie)
+ FUNC_ATTR_NONNULL_ARG(1, 2)
{
- char *tail;
- int num_files;
- char **files;
- int i;
bool did_one = false;
// Make a copy of 'runtimepath'. Invoking the callback may change the
@@ -287,15 +367,22 @@ int do_in_path(char *path, char *name, int flags, DoInRuntimepathCB callback, vo
char *rtp_copy = xstrdup(path);
char *buf = xmallocz(MAXPATHL);
{
+ char *tail;
if (p_verbose > 10 && name != NULL) {
verbose_enter();
- smsg(_("Searching for \"%s\" in \"%s\""), name, path);
+ if (*prefix != NUL) {
+ smsg(0, _("Searching for \"%s\" under \"%s\" in \"%s\""), name, prefix, path);
+ } else {
+ smsg(0, _("Searching for \"%s\" in \"%s\""), name, path);
+ }
verbose_leave();
}
+ bool do_all = (flags & DIP_ALL) != 0;
+
// Loop over all entries in 'runtimepath'.
char *rtp = rtp_copy;
- while (*rtp != NUL && ((flags & DIP_ALL) || !did_one)) {
+ while (*rtp != NUL && (do_all || !did_one)) {
// Copy the path from 'runtimepath' to buf[].
copy_option_part(&rtp, buf, MAXPATHL, ",");
size_t buflen = strlen(buf);
@@ -311,39 +398,31 @@ int do_in_path(char *path, char *name, int flags, DoInRuntimepathCB callback, vo
}
if (name == NULL) {
- (*callback)(buf, cookie);
+ (*callback)(1, &buf, do_all, cookie);
did_one = true;
- } else if (buflen + strlen(name) + 2 < MAXPATHL) {
+ } else if (buflen + 2 + strlen(prefix) + strlen(name) < MAXPATHL) {
add_pathsep(buf);
+ STRCAT(buf, prefix);
tail = buf + strlen(buf);
// Loop over all patterns in "name"
char *np = name;
- while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
+ while (*np != NUL && (do_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);
+ smsg(0, _("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.
- if (gen_expand_wildcards(1, &buf, &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);
- }
+ | ((flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0);
+
+ did_one |= gen_expand_wildcards_and_cb(1, &buf, ew_flags, do_all, callback,
+ cookie) == OK;
}
}
}
@@ -357,7 +436,7 @@ int do_in_path(char *path, char *name, int flags, DoInRuntimepathCB callback, vo
semsg(_(e_dirnotf), basepath, name);
} else if (p_verbose > 1) {
verbose_enter();
- smsg(_("not found in '%s': \"%s\""), basepath, name);
+ smsg(0, _("not found in '%s': \"%s\""), basepath, name);
verbose_leave();
}
}
@@ -365,7 +444,7 @@ int do_in_path(char *path, char *name, int flags, DoInRuntimepathCB callback, vo
return did_one ? OK : FAIL;
}
-RuntimeSearchPath runtime_search_path_get_cached(int *ref)
+static RuntimeSearchPath runtime_search_path_get_cached(int *ref)
FUNC_ATTR_NONNULL_ALL
{
runtime_search_path_validate();
@@ -380,7 +459,7 @@ RuntimeSearchPath runtime_search_path_get_cached(int *ref)
return runtime_search_path;
}
-RuntimeSearchPath copy_runtime_search_path(const RuntimeSearchPath src)
+static RuntimeSearchPath copy_runtime_search_path(const RuntimeSearchPath src)
{
RuntimeSearchPath dst = KV_INITIAL_VALUE;
for (size_t j = 0; j < kv_size(src); j++) {
@@ -391,7 +470,7 @@ RuntimeSearchPath copy_runtime_search_path(const RuntimeSearchPath src)
return dst;
}
-void runtime_search_path_unref(RuntimeSearchPath path, const int *ref)
+static void runtime_search_path_unref(RuntimeSearchPath path, const int *ref)
FUNC_ATTR_NONNULL_ALL
{
if (*ref) {
@@ -411,25 +490,24 @@ void runtime_search_path_unref(RuntimeSearchPath path, const int *ref)
/// 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 *name, int flags, DoInRuntimepathCB callback, void *cookie)
+static int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *cookie)
{
char *tail;
- int num_files;
- char **files;
- int i;
bool did_one = false;
char buf[MAXPATHL];
if (p_verbose > 10 && name != NULL) {
verbose_enter();
- smsg(_("Searching for \"%s\" in runtime path"), name);
+ smsg(0, _("Searching for \"%s\" in runtime path"), name);
verbose_leave();
}
int ref;
RuntimeSearchPath path = runtime_search_path_get_cached(&ref);
+ bool do_all = (flags & DIP_ALL) != 0;
+
// Loop over all entries in cached path
for (size_t j = 0; j < kv_size(path); j++) {
SearchPathItem item = kv_A(path, j);
@@ -444,7 +522,7 @@ int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *c
}
if (name == NULL) {
- (*callback)(item.path, cookie);
+ (*callback)(1, &item.path, do_all, cookie);
} else if (buflen + strlen(name) + 2 < MAXPATHL) {
STRCPY(buf, item.path);
add_pathsep(buf);
@@ -452,32 +530,25 @@ int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *c
// Loop over all patterns in "name"
char *np = name;
- while (*np != NUL && ((flags & DIP_ALL) || !did_one)) {
+
+ while (*np != NUL && (do_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);
+ smsg(0, _("Searching for \"%s\""), buf);
verbose_leave();
}
int ew_flags = ((flags & DIP_DIR) ? EW_DIR : EW_FILE)
- | (flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0;
+ | ((flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0)
+ | EW_NOBREAK;
// Expand wildcards, invoke the callback for each match.
char *(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);
- }
+ did_one |= gen_expand_wildcards_and_cb(1, pat, ew_flags, do_all, callback, cookie) == OK;
}
}
}
@@ -487,7 +558,7 @@ int do_in_cached_path(char *name, int flags, DoInRuntimepathCB callback, void *c
semsg(_(e_dirnotf), "runtime path", name);
} else if (p_verbose > 1) {
verbose_enter();
- smsg(_("not found in runtime path: \"%s\""), name);
+ smsg(0, _("not found in runtime path: \"%s\""), name);
verbose_leave();
}
}
@@ -505,7 +576,7 @@ Array runtime_inspect(void)
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, CSTR_TO_OBJ(item->path));
ADD(entry, BOOLEAN_OBJ(item->after));
if (item->has_lua != kNone) {
ADD(entry, BOOLEAN_OBJ(item->has_lua == kTrue));
@@ -538,8 +609,8 @@ ArrayOf(String) runtime_get_named_thread(bool lua, Array pat, bool all)
return rv;
}
-ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
- RuntimeSearchPath path, char *buf, size_t buf_len)
+static ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
+ RuntimeSearchPath path, char *buf, size_t buf_len)
{
ArrayOf(String) rv = ARRAY_DICT_INIT;
for (size_t i = 0; i < kv_size(path); i++) {
@@ -561,7 +632,7 @@ ArrayOf(String) runtime_get_named_common(bool lua, Array pat, bool all,
item->path, pat_item.data.string.data);
if (size < buf_len) {
if (os_file_is_readable(buf)) {
- ADD(rv, STRING_OBJ(cstr_to_string(buf)));
+ ADD(rv, CSTR_TO_OBJ(buf));
if (!all) {
goto done;
}
@@ -587,73 +658,46 @@ int do_in_path_and_pp(char *path, char *name, int flags, DoInRuntimepathCB callb
int done = FAIL;
if ((flags & DIP_NORTP) == 0) {
- done |= do_in_path(path, (name && !*name) ? NULL : name, flags, callback,
+ done |= do_in_path(path, "", (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 *s = xmallocz(len); // TODO(bfredl): get rid of random allocations
- char *suffix = (flags & DIP_AFTER) ? "after/" : "";
-
- vim_snprintf(s, len, start_dir, suffix, name);
- done |= do_in_path(p_pp, s, flags & ~DIP_AFTER, callback, cookie);
-
- xfree(s);
+ const char *prefix
+ = (flags & DIP_AFTER) ? "pack/*/start/*/after/" : "pack/*/start/*/"; // NOLINT
+ done |= do_in_path(p_pp, prefix, name, flags & ~DIP_AFTER, callback, cookie);
if (done == FAIL || (flags & DIP_ALL)) {
- start_dir = "start/*/%s%s"; // NOLINT
- len = strlen(start_dir) + strlen(name) + 6;
- s = xmallocz(len);
-
- vim_snprintf(s, len, start_dir, suffix, name);
- done |= do_in_path(p_pp, s, flags & ~DIP_AFTER, callback, cookie);
-
- xfree(s);
+ prefix = (flags & DIP_AFTER) ? "start/*/after/" : "start/*/"; // NOLINT
+ done |= do_in_path(p_pp, prefix, name, flags & ~DIP_AFTER, callback, cookie);
}
}
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 *s = xmallocz(len);
-
- vim_snprintf(s, len, opt_dir, name);
- done |= do_in_path(p_pp, s, flags, callback, cookie);
-
- xfree(s);
+ done |= do_in_path(p_pp, "pack/*/opt/*/", name, flags, callback, cookie); // NOLINT
if (done == FAIL || (flags & DIP_ALL)) {
- opt_dir = "opt/*/%s"; // NOLINT
- len = strlen(opt_dir) + strlen(name);
- s = xmallocz(len);
-
- vim_snprintf(s, len, opt_dir, name);
- done |= do_in_path(p_pp, s, flags, callback, cookie);
-
- xfree(s);
+ done |= do_in_path(p_pp, "opt/*/", name, flags, callback, cookie); // NOLINT
}
}
return done;
}
-static void push_path(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, char *entry,
+static void push_path(RuntimeSearchPath *search_path, Set(String) *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 }));
+ String *key_alloc;
+ if (set_put_ref(String, rtp_used, cstr_as_string(entry), &key_alloc)) {
+ *key_alloc = cstr_to_string(entry);
+ kv_push(*search_path, ((SearchPathItem){ key_alloc->data, after, kNone }));
}
}
-static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used,
- char *entry, bool after)
+static void expand_rtp_entry(RuntimeSearchPath *search_path, Set(String) *rtp_used, char *entry,
+ bool after)
{
- if (map_get(String, handle_T)(rtp_used, cstr_as_string(entry))) {
+ if (set_has(String, rtp_used, cstr_as_string(entry))) {
return;
}
@@ -664,7 +708,7 @@ static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_
int num_files;
char **files;
char *(pat[]) = { entry };
- if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) {
+ if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR | EW_NOBREAK) == OK) {
for (int i = 0; i < num_files; i++) {
push_path(search_path, rtp_used, files[i], after);
}
@@ -672,7 +716,7 @@ static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_
}
}
-static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used,
+static void expand_pack_entry(RuntimeSearchPath *search_path, Set(String) *rtp_used,
CharVec *after_path, char *pack_entry, size_t pack_entry_len)
{
static char buf[MAXPATHL];
@@ -702,13 +746,11 @@ static bool path_is_after(char *buf, size_t buflen)
&& strcmp(buf + buflen - 5, "after") == 0;
}
-RuntimeSearchPath runtime_search_path_build(void)
+static 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;
+ Map(String, int) pack_used = MAP_INIT;
+ Set(String) rtp_used = SET_INIT;
RuntimeSearchPath search_path = KV_INITIAL_VALUE;
CharVec after_path = KV_INITIAL_VALUE;
@@ -720,7 +762,7 @@ RuntimeSearchPath runtime_search_path_build(void)
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);
+ map_put(String, int)(&pack_used, the_entry, 0);
}
char *rtp_entry;
@@ -737,7 +779,7 @@ RuntimeSearchPath runtime_search_path_build(void)
// fact: &rtp entries can contain wild chars
expand_rtp_entry(&search_path, &rtp_used, buf, false);
- handle_T *h = map_ref(String, handle_T)(&pack_used, cstr_as_string(buf), false);
+ handle_T *h = map_ref(String, int)(&pack_used, cstr_as_string(buf), NULL);
if (h) {
(*h)++;
expand_pack_entry(&search_path, &rtp_used, &after_path, buf, buflen);
@@ -746,7 +788,7 @@ RuntimeSearchPath runtime_search_path_build(void)
for (size_t i = 0; i < kv_size(pack_entries); i++) {
String item = kv_A(pack_entries, i);
- handle_T h = map_get(String, handle_T)(&pack_used, item);
+ handle_T h = map_get(String, int)(&pack_used, item);
if (h == 0) {
expand_pack_entry(&search_path, &rtp_used, &after_path, item.data, item.size);
}
@@ -767,18 +809,19 @@ RuntimeSearchPath runtime_search_path_build(void)
// 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);
+ map_destroy(String, &pack_used);
+ set_destroy(String, &rtp_used);
return search_path;
}
-void runtime_search_path_invalidate(void)
+const char *did_set_runtimepackpath(optset_T *args)
{
runtime_search_path_valid = false;
+ return NULL;
}
-void runtime_search_path_free(RuntimeSearchPath path)
+static void runtime_search_path_free(RuntimeSearchPath path)
{
for (size_t j = 0; j < kv_size(path); j++) {
SearchPathItem item = kv_A(path, j);
@@ -836,27 +879,46 @@ int source_runtime(char *name, int flags)
return do_in_runtimepath(name, flags, source_callback, NULL);
}
-/// Just like source_runtime(), but use "path" instead of 'runtimepath'.
-int source_in_path(char *path, char *name, int flags)
+/// Just like source_runtime(), but only source vim and lua files
+int source_runtime_vim_lua(char *name, int flags)
+{
+ return do_in_runtimepath(name, flags, source_callback_vim_lua, NULL);
+}
+
+/// Just like source_runtime(), but:
+/// - use "path" instead of 'runtimepath'.
+/// - only source .vim and .lua files
+int source_in_path_vim_lua(char *path, char *name, int flags)
{
- return do_in_path_and_pp(path, name, flags, source_callback, NULL);
+ return do_in_path_and_pp(path, name, flags, source_callback_vim_lua, NULL);
}
-// Expand wildcards in "pat" and invoke do_source()/nlua_exec_file()
-// for each match.
-static void source_all_matches(char *pat)
+/// Expand wildcards in "pats" and invoke callback matches.
+///
+/// @param num_pat is number of input patterns.
+/// @param patx is an array of pointers to input patterns.
+/// @param flags is a combination of EW_* flags used in
+/// expand_wildcards().
+/// @param all invoke callback on all matches or just one
+/// @param callback called for each match.
+/// @param cookie context for callback
+///
+/// @returns OK when some files were found, FAIL otherwise.
+static int gen_expand_wildcards_and_cb(int num_pat, char **pats, int flags, bool all,
+ DoInRuntimepathCB callback, void *cookie)
{
int num_files;
char **files;
- if (gen_expand_wildcards(1, &pat, &num_files, &files, EW_FILE) != OK) {
- return;
+ if (gen_expand_wildcards(num_pat, pats, &num_files, &files, flags) != OK) {
+ return FAIL;
}
- for (int i = 0; i < num_files; i++) {
- (void)do_source(files[i], false, DOSO_NONE);
- }
+ (*callback)(num_files, files, all, cookie);
+
FreeWild(num_files, files);
+
+ return OK;
}
/// Add the package directory to 'runtimepath'
@@ -866,7 +928,6 @@ static void source_all_matches(char *pat)
static int add_pack_dir_to_rtp(char *fname, bool is_pack)
{
char *p;
- char *buf = NULL;
char *afterdir = NULL;
int retval = FAIL;
@@ -901,29 +962,16 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
// Find "ffname" in "p_rtp", ignoring '/' vs '\' differences
// Also stop at the first "after" directory
size_t fname_len = strlen(ffname);
- buf = try_malloc(MAXPATHL);
+ char *buf = try_malloc(MAXPATHL);
if (buf == NULL) {
goto theend;
}
const char *insp = NULL;
const char *after_insp = NULL;
- for (const char *entry = (const char *)p_rtp; *entry != NUL;) {
+ for (const char *entry = p_rtp; *entry != NUL;) {
const char *cur_entry = entry;
copy_option_part((char **)&entry, buf, MAXPATHL, ",");
- if (insp == NULL) {
- add_pathsep(buf);
- char *const rtp_ffname = fix_fname(buf);
- if (rtp_ffname == NULL) {
- goto theend;
- }
- bool match = path_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
- xfree(rtp_ffname);
- if (match) {
- // Insert "ffname" after this entry (and comma).
- insp = entry;
- }
- }
if ((p = strstr(buf, "after")) != NULL
&& p > buf
@@ -937,11 +985,25 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
after_insp = cur_entry;
break;
}
+
+ if (insp == NULL) {
+ add_pathsep(buf);
+ char *const rtp_ffname = fix_fname(buf);
+ if (rtp_ffname == NULL) {
+ goto theend;
+ }
+ bool match = path_fnamencmp(rtp_ffname, ffname, fname_len) == 0;
+ xfree(rtp_ffname);
+ if (match) {
+ // Insert "ffname" after this entry (and comma).
+ insp = entry;
+ }
+ }
}
if (insp == NULL) {
// Both "fname" and "after" not found, append at the end.
- insp = (const char *)p_rtp + strlen(p_rtp);
+ insp = p_rtp + strlen(p_rtp);
}
// check if rtp/pack/name/start/name/after exists
@@ -962,7 +1024,7 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
// We now have 'rtp' parts: {keep}{keep_after}{rest}.
// Create new_rtp, first: {keep},{fname}
- size_t keep = (size_t)(insp - (const char *)p_rtp);
+ size_t keep = (size_t)(insp - p_rtp);
memmove(new_rtp, p_rtp, keep);
size_t new_rtp_len = keep;
if (*insp == NUL) {
@@ -975,7 +1037,7 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
}
if (afterlen > 0 && after_insp != NULL) {
- size_t keep_after = (size_t)(after_insp - (const char *)p_rtp);
+ size_t keep_after = (size_t)(after_insp - p_rtp);
// Add to new_rtp: {keep},{fname}{keep_after},{afterdir}
memmove(new_rtp + new_rtp_len, p_rtp + keep, keep_after - keep);
@@ -1000,7 +1062,7 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack)
xstrlcat(new_rtp, afterdir, new_rtp_capacity);
}
- set_option_value_give_err("rtp", 0L, new_rtp, 0);
+ set_option_value_give_err("rtp", CSTR_AS_OPTVAL(new_rtp), 0);
xfree(new_rtp);
retval = OK;
@@ -1013,30 +1075,27 @@ theend:
/// Load scripts in "plugin" directory of the package.
/// For opt packages, also load scripts in "ftdetect" (start packages already
-/// load these from filetype.vim)
+/// load these from filetype.lua)
static int load_pack_plugin(bool opt, char *fname)
{
- static const char *ftpat = "%s/ftdetect/*.vim"; // NOLINT
+ static const char plugpat[] = "%s/plugin/**/*"; // NOLINT
+ static const char ftpat[] = "%s/ftdetect/*"; // NOLINT
char *const ffname = fix_fname(fname);
- size_t len = strlen(ffname) + strlen(ftpat);
+ size_t len = strlen(ffname) + sizeof(plugpat);
char *pat = xmallocz(len);
- vim_snprintf(pat, len, "%s/plugin/**/*.vim", ffname); // NOLINT
- source_all_matches(pat);
- vim_snprintf(pat, len, "%s/plugin/**/*.lua", ffname); // NOLINT
- source_all_matches(pat);
+ vim_snprintf(pat, len, plugpat, ffname); // NOLINT
+ gen_expand_wildcards_and_cb(1, &pat, EW_FILE, true, source_callback_vim_lua, NULL);
char *cmd = xstrdup("g:did_load_filetypes");
- // If runtime/filetype.vim wasn't loaded yet, the scripts will be
+ // If runtime/filetype.lua wasn't loaded yet, the scripts will be
// found when it loads.
if (opt && eval_to_number(cmd) > 0) {
do_cmdline_cmd("augroup filetypedetect");
vim_snprintf(pat, len, ftpat, ffname);
- source_all_matches(pat);
- vim_snprintf((char *)pat, len, "%s/ftdetect/*.lua", ffname); // NOLINT
- source_all_matches(pat);
+ gen_expand_wildcards_and_cb(1, &pat, EW_FILE, true, source_callback_vim_lua, NULL);
do_cmdline_cmd("augroup END");
}
xfree(cmd);
@@ -1051,48 +1110,68 @@ static int APP_ADD_DIR;
static int APP_LOAD;
static int APP_BOTH;
-static void add_pack_plugin(bool opt, char *fname, void *cookie)
+static void add_pack_plugins(bool opt, int num_fnames, char **fnames, bool all, void *cookie)
{
+ bool did_one = false;
+
if (cookie != &APP_LOAD) {
char *buf = xmalloc(MAXPATHL);
- bool found = false;
-
- const char *p = (const char *)p_rtp;
- while (*p != NUL) {
- copy_option_part((char **)&p, buf, MAXPATHL, ",");
- if (path_fnamecmp(buf, fname) == 0) {
- found = true;
+ for (int i = 0; i < num_fnames; i++) {
+ bool found = false;
+
+ const char *p = p_rtp;
+ while (*p != NUL) {
+ copy_option_part((char **)&p, buf, MAXPATHL, ",");
+ if (path_fnamecmp(buf, fnames[i]) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // directory is not yet in 'runtimepath', add it
+ if (add_pack_dir_to_rtp(fnames[i], false) == FAIL) {
+ xfree(buf);
+ return;
+ }
+ }
+ did_one = true;
+ if (!all) {
break;
}
}
xfree(buf);
- if (!found) {
- // directory is not yet in 'runtimepath', add it
- if (add_pack_dir_to_rtp(fname, false) == FAIL) {
- return;
- }
- }
+ }
+
+ if (!all && did_one) {
+ return;
}
if (cookie != &APP_ADD_DIR) {
- load_pack_plugin(opt, fname);
+ for (int i = 0; i < num_fnames; i++) {
+ load_pack_plugin(opt, fnames[i]);
+ if (!all) {
+ break;
+ }
+ }
}
}
-static void add_start_pack_plugin(char *fname, void *cookie)
+static bool add_start_pack_plugins(int num_fnames, char **fnames, bool all, void *cookie)
{
- add_pack_plugin(false, fname, cookie);
+ add_pack_plugins(false, num_fnames, fnames, all, cookie);
+ return num_fnames > 0;
}
-static void add_opt_pack_plugin(char *fname, void *cookie)
+static bool add_opt_pack_plugins(int num_fnames, char **fnames, bool all, void *cookie)
{
- add_pack_plugin(true, fname, cookie);
+ add_pack_plugins(true, num_fnames, fnames, all, cookie);
+ return num_fnames > 0;
}
/// 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);
+ do_in_path(p_pp, "", NULL, DIP_ALL + DIP_DIR, add_pack_start_dir, NULL);
}
static bool pack_has_entries(char *buf)
@@ -1106,30 +1185,38 @@ static bool pack_has_entries(char *buf)
return num_files > 0;
}
-static void add_pack_start_dir(char *fname, void *cookie)
+static bool add_pack_start_dir(int num_fnames, char **fnames, bool all, void *cookie)
{
static char 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;
+ for (int i = 0; i < num_fnames; i++) {
+ char *(start_pat[]) = { "/start/*", "/pack/*/start/*" }; // NOLINT
+ for (int j = 0; j < 2; j++) {
+ if (strlen(fnames[i]) + strlen(start_pat[j]) + 1 > MAXPATHL) {
+ continue;
+ }
+ xstrlcpy(buf, fnames[i], MAXPATHL);
+ xstrlcat(buf, start_pat[j], sizeof buf);
+ if (pack_has_entries(buf)) {
+ add_pack_dir_to_rtp(buf, true);
+ }
}
- xstrlcpy(buf, fname, MAXPATHL);
- xstrlcat(buf, start_pat[i], sizeof buf);
- if (pack_has_entries(buf)) {
- add_pack_dir_to_rtp(buf, true);
+
+ if (!all) {
+ break;
}
}
+
+ return num_fnames > 1;
}
/// Load plugins from all packages in the "start" directory.
void load_start_packages(void)
{
did_source_packages = true;
- do_in_path(p_pp, "pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
- add_start_pack_plugin, &APP_LOAD);
- do_in_path(p_pp, "start/*", DIP_ALL + DIP_DIR, // NOLINT
- add_start_pack_plugin, &APP_LOAD);
+ do_in_path(p_pp, "", "pack/*/start/*", DIP_ALL + DIP_DIR, // NOLINT
+ add_start_pack_plugins, &APP_LOAD);
+ do_in_path(p_pp, "", "start/*", DIP_ALL + DIP_DIR, // NOLINT
+ add_start_pack_plugins, &APP_LOAD);
}
// ":packloadall"
@@ -1150,17 +1237,16 @@ void load_plugins(void)
{
if (p_lpl) {
char *rtp_copy = p_rtp;
- char *const plugin_pattern_vim = "plugin/**/*.vim"; // NOLINT
- char *const plugin_pattern_lua = "plugin/**/*.lua"; // NOLINT
+ char *const plugin_pattern = "plugin/**/*"; // NOLINT
if (!did_source_packages) {
rtp_copy = xstrdup(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);
+ // Don't use source_runtime_vim_lua() yet so we can check for :packloadall below.
+ // NB: after calling this "rtp_copy" may have been freed if it wasn't copied.
+ source_in_path_vim_lua(rtp_copy, plugin_pattern, DIP_ALL | DIP_NOAFTER);
TIME_MSG("loading rtp plugins");
// Only source "start" packages if not done already with a :packloadall
@@ -1171,8 +1257,7 @@ void load_plugins(void)
}
TIME_MSG("loading packages");
- source_runtime(plugin_pattern_vim, DIP_ALL | DIP_AFTER);
- source_runtime(plugin_pattern_lua, DIP_ALL | DIP_AFTER);
+ source_runtime_vim_lua(plugin_pattern, DIP_ALL | DIP_AFTER);
TIME_MSG("loading after plugins");
}
}
@@ -1180,7 +1265,7 @@ void load_plugins(void)
/// ":packadd[!] {name}"
void ex_packadd(exarg_T *eap)
{
- static const char *plugpat = "pack/*/%s/%s"; // NOLINT
+ static const char plugpat[] = "pack/*/%s/%s"; // NOLINT
int res = OK;
// Round 1: use "start", round 2: use "opt".
@@ -1190,14 +1275,15 @@ void ex_packadd(exarg_T *eap)
continue;
}
- const size_t len = strlen(plugpat) + strlen(eap->arg) + 5;
+ const size_t len = sizeof(plugpat) + strlen(eap->arg) + 5;
char *pat = xmallocz(len);
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, pat, DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
- round == 1 ? add_start_pack_plugin : add_opt_pack_plugin,
+ do_in_path(p_pp, "", pat,
+ DIP_ALL + DIP_DIR + (round == 2 && res == FAIL ? DIP_ERR : 0),
+ round == 1 ? add_start_pack_plugins : add_opt_pack_plugins,
eap->forceit ? &APP_ADD_DIR : &APP_BOTH);
xfree(pat);
}
@@ -1216,9 +1302,9 @@ static void ExpandRTDir_int(char *pat, size_t pat_len, int flags, bool keep_ext,
bool expand_dirs = false;
if (*dirnames[i] == NUL) { // empty dir used for :runtime
- snprintf(tail, tail_buflen, "%s*.\\(vim\\|lua\\)", pat);
+ snprintf(tail, tail_buflen, "%s*.{vim,lua}", pat);
} else {
- snprintf(tail, tail_buflen, "%s/%s*.\\(vim\\|lua\\)", dirnames[i], pat);
+ snprintf(tail, tail_buflen, "%s/%s*.{vim,lua}", dirnames[i], pat);
}
expand:
@@ -1292,11 +1378,11 @@ expand:
/// Expand color scheme, compiler or filetype names.
/// Search from 'runtimepath':
-/// 'runtimepath'/{dirnames}/{pat}.(vim|lua)
+/// 'runtimepath'/{dirnames}/{pat}.{vim,lua}
/// When "flags" has DIP_START: search also from "start" of 'packpath':
-/// 'packpath'/pack/*/start/*/{dirnames}/{pat}.(vim|lua)
+/// 'packpath'/pack/*/start/*/{dirnames}/{pat}.{vim,lua}
/// When "flags" has DIP_OPT: search also from "opt" of 'packpath':
-/// 'packpath'/pack/*/opt/*/{dirnames}/{pat}.(vim|lua)
+/// 'packpath'/pack/*/opt/*/{dirnames}/{pat}.{vim,lua}
/// "dirnames" is an array with one or more directory names.
int ExpandRTDir(char *pat, int flags, int *num_file, char ***file, char *dirnames[])
{
@@ -1437,7 +1523,6 @@ static inline size_t compute_double_env_sep_len(const char *const val, const siz
return ret;
}
-#define NVIM_SIZE (sizeof("nvim") - 1)
/// Add directories to a ENV_SEPCHAR-separated array from a colon-separated one
///
/// Commas are escaped in process. To each item PATHSEP "nvim" is appended in
@@ -1466,6 +1551,8 @@ static inline char *add_env_sep_dirs(char *dest, const char *const val, const ch
return dest;
}
const void *iter = NULL;
+ const char *appname = get_appname();
+ const size_t appname_len = strlen(appname);
do {
size_t dir_len;
const char *dir;
@@ -1476,8 +1563,8 @@ static inline char *add_env_sep_dirs(char *dest, const char *const val, const ch
if (!after_pathsep(dest - 1, dest)) {
*dest++ = PATHSEP;
}
- memmove(dest, "nvim", NVIM_SIZE);
- dest += NVIM_SIZE;
+ memmove(dest, appname, appname_len);
+ dest += appname_len;
if (suf1 != NULL) {
*dest++ = PATHSEP;
memmove(dest, suf1, len1);
@@ -1531,14 +1618,18 @@ static inline char *add_dir(char *dest, const char *const dir, const size_t dir_
if (!after_pathsep(dest - 1, dest)) {
*dest++ = PATHSEP;
}
+ const char *appname = get_appname();
+ size_t appname_len = strlen(appname);
+ assert(appname_len < (IOSIZE - sizeof("-data")));
+ xstrlcpy(IObuff, appname, appname_len + 1);
#if defined(MSWIN)
- size_t size = (type == kXDGDataHome ? sizeof("nvim-data") - 1 : NVIM_SIZE);
- memmove(dest, (type == kXDGDataHome ? "nvim-data" : "nvim"), size);
- dest += size;
-#else
- memmove(dest, "nvim", NVIM_SIZE);
- dest += NVIM_SIZE;
+ if (type == kXDGDataHome || type == kXDGStateHome) {
+ xstrlcat(IObuff, "-data", IOSIZE);
+ appname_len += 5;
+ }
#endif
+ xstrlcpy(dest, IObuff, appname_len + 1);
+ dest += appname_len;
if (suf1 != NULL) {
*dest++ = PATHSEP;
memmove(dest, suf1, len1);
@@ -1566,7 +1657,7 @@ char *get_lib_dir(void)
// Find library path relative to the nvim binary: ../lib/nvim/
char exe_name[MAXPATHL];
vim_get_prefix_from_exepath(exe_name);
- if (append_path(exe_name, "lib" _PATHSEPSTR "nvim", MAXPATHL) == OK) {
+ if (append_path(exe_name, "lib/nvim", MAXPATHL) == OK) {
return xstrdup(exe_name);
}
return NULL;
@@ -1583,11 +1674,11 @@ char *runtimepath_default(bool clean_arg)
{
size_t rtp_size = 0;
char *const data_home = clean_arg
- ? NULL
- : stdpaths_get_xdg_var(kXDGDataHome);
+ ? NULL
+ : stdpaths_get_xdg_var(kXDGDataHome);
char *const config_home = clean_arg
- ? NULL
- : stdpaths_get_xdg_var(kXDGConfigHome);
+ ? NULL
+ : stdpaths_get_xdg_var(kXDGConfigHome);
char *const vimruntime = vim_getenv("VIMRUNTIME");
char *const libdir = get_lib_dir();
char *const data_dirs = stdpaths_get_xdg_var(kXDGDataDirs);
@@ -1598,16 +1689,17 @@ char *runtimepath_default(bool clean_arg)
size_t config_len = 0;
size_t vimruntime_len = 0;
size_t libdir_len = 0;
+ const char *appname = get_appname();
+ size_t appname_len = strlen(appname);
if (data_home != NULL) {
data_len = strlen(data_home);
- if (data_len != 0) {
+ size_t nvim_data_size = appname_len;
#if defined(MSWIN)
- size_t nvim_size = (sizeof("nvim-data") - 1);
-#else
- size_t nvim_size = NVIM_SIZE;
+ nvim_data_size += sizeof("-data") - 1; // -1: NULL byte should be ignored
#endif
+ if (data_len != 0) {
rtp_size += ((data_len + memcnt(data_home, ',', data_len)
- + nvim_size + 1 + SITE_SIZE + 1
+ + nvim_data_size + 1 + SITE_SIZE + 1
+ !after_pathsep(data_home, data_home + data_len)) * 2
+ AFTER_SIZE + 1);
}
@@ -1616,7 +1708,7 @@ char *runtimepath_default(bool clean_arg)
config_len = strlen(config_home);
if (config_len != 0) {
rtp_size += ((config_len + memcnt(config_home, ',', config_len)
- + NVIM_SIZE + 1
+ + appname_len + 1
+ !after_pathsep(config_home, config_home + config_len)) * 2
+ AFTER_SIZE + 1);
}
@@ -1634,9 +1726,9 @@ char *runtimepath_default(bool clean_arg)
}
}
rtp_size += compute_double_env_sep_len(data_dirs,
- NVIM_SIZE + 1 + SITE_SIZE + 1,
+ appname_len + 1 + SITE_SIZE + 1,
AFTER_SIZE + 1);
- rtp_size += compute_double_env_sep_len(config_dirs, NVIM_SIZE + 1,
+ rtp_size += compute_double_env_sep_len(config_dirs, appname_len + 1,
AFTER_SIZE + 1);
char *rtp = NULL;
if (rtp_size == 0) {
@@ -1677,7 +1769,6 @@ freeall:
return rtp;
}
-#undef NVIM_SIZE
static void cmd_source(char *fname, exarg_T *eap)
{
@@ -1695,7 +1786,7 @@ static void cmd_source(char *fname, exarg_T *eap)
|| eap->cstack->cs_idx >= 0);
// ":source" read ex commands
- } else if (do_source(fname, false, DOSO_NONE) == FAIL) {
+ } else if (do_source(fname, false, DOSO_NONE, NULL) == FAIL) {
semsg(_(e_notopen), fname);
}
}
@@ -1759,7 +1850,7 @@ static FILE *fopen_noinh_readbin(char *filename)
return fdopen(fd_tmp, READBIN);
}
-/// Concatenate VimL line if it starts with a line continuation into a growarray
+/// Concatenate Vimscript line if it starts with a line continuation into a growarray
/// (excluding the continuation chars and leading whitespace)
///
/// @note Growsize of the growarray may be changed to speed up concatenations!
@@ -1775,7 +1866,7 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize, c
size_t len)
FUNC_ATTR_NONNULL_ALL
{
- const char *const line = skipwhite_len((char *)p, len);
+ const char *const line = skipwhite_len(p, len);
len -= (size_t)(line - p);
// Skip lines starting with '\" ', concat lines starting with '\'
if (len >= 3 && strncmp(line, "\"\\ ", 3) == 0) {
@@ -1823,7 +1914,7 @@ static char *get_str_line(int c, void *cookie, int indent, bool do_concat)
if (!concat_continued_line(&ga, 400, line, (size_t)(next_eol - line))) {
break;
}
- eol = (char *)next_eol;
+ eol = next_eol;
}
}
ga_append(&ga, NUL);
@@ -1846,13 +1937,18 @@ scriptitem_T *new_script_item(char *const name, scid_T *const sid_out)
}
ga_grow(&script_items, sid - script_items.ga_len);
while (script_items.ga_len < sid) {
+ scriptitem_T *si = xcalloc(1, sizeof(scriptitem_T));
script_items.ga_len++;
- SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
- SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
+ SCRIPT_ITEM(script_items.ga_len) = si;
+ si->sn_name = NULL;
+
+ // Allocate the local script variables to use for this script.
+ new_script_vars(script_items.ga_len);
+
+ si->sn_prof_on = false;
}
- SCRIPT_ITEM(sid).sn_name = name;
- new_script_vars(sid); // Allocate the local script variables to use for this script.
- return &SCRIPT_ITEM(sid);
+ SCRIPT_ITEM(sid)->sn_name = name;
+ return SCRIPT_ITEM(sid);
}
static int source_using_linegetter(void *cookie, LineGetter fgetline, const char *traceback_name)
@@ -1864,7 +1960,7 @@ static int source_using_linegetter(void *cookie, LineGetter fgetline, const char
if (save_sourcing_name == NULL) {
sname = (char *)traceback_name;
} else {
- snprintf((char *)sourcing_name_buf, sizeof(sourcing_name_buf),
+ snprintf(sourcing_name_buf, sizeof(sourcing_name_buf),
"%s called at %s:%" PRIdLINENR, traceback_name, save_sourcing_name,
save_sourcing_lnum);
sname = sourcing_name_buf;
@@ -1910,13 +2006,11 @@ static void cmd_source_buffer(const exarg_T *const eap)
.buf = ga.ga_data,
.offset = 0,
};
- if (curbuf->b_fname
- && path_with_extension((const char *)curbuf->b_fname, "lua")) {
- nlua_source_using_linegetter(get_str_line, (void *)&cookie,
- ":source (no file)");
+ if (strequal(curbuf->b_p_ft, "lua")
+ || (curbuf->b_fname && path_with_extension(curbuf->b_fname, "lua"))) {
+ nlua_source_using_linegetter(get_str_line, (void *)&cookie, ":source (no file)");
} else {
- source_using_linegetter((void *)&cookie, get_str_line,
- ":source (no file)");
+ source_using_linegetter((void *)&cookie, get_str_line, ":source (no file)");
}
ga_clear(&ga);
}
@@ -1943,13 +2037,14 @@ int do_source_str(const char *cmd, const char *traceback_name)
/// @param fname
/// @param check_other check for .vimrc and _vimrc
/// @param is_vimrc DOSO_ value
+/// @param ret_sid if not NULL and we loaded the script before, don't load it again
///
/// @return FAIL if file could not be opened, OK otherwise
-int do_source(char *fname, int check_other, int is_vimrc)
+///
+/// If a scriptitem_T was found or created "*ret_sid" is set to the SID.
+int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
{
struct source_cookie cookie;
- char *p;
- char *fname_exp;
uint8_t *firstline = NULL;
int retval = FAIL;
int save_debug_break_level = debug_break_level;
@@ -1957,17 +2052,26 @@ int do_source(char *fname, int check_other, int is_vimrc)
proftime_T wait_start;
bool trigger_source_post = false;
- p = expand_env_save(fname);
+ char *p = expand_env_save(fname);
if (p == NULL) {
return retval;
}
- fname_exp = fix_fname(p);
+ char *fname_exp = fix_fname(p);
xfree(p);
if (fname_exp == NULL) {
return retval;
}
if (os_isdir(fname_exp)) {
- smsg(_("Cannot source a directory: \"%s\""), fname);
+ smsg(0, _("Cannot source a directory: \"%s\""), fname);
+ goto theend;
+ }
+
+ // See if we loaded this script before.
+ int sid = find_script_by_name(fname_exp);
+ if (sid > 0 && ret_sid != NULL) {
+ // Already loaded and no need to load again, return here.
+ *ret_sid = sid;
+ retval = OK;
goto theend;
}
@@ -2002,9 +2106,9 @@ int do_source(char *fname, int check_other, int is_vimrc)
if (p_verbose > 1) {
verbose_enter();
if (SOURCING_NAME == NULL) {
- smsg(_("could not source \"%s\""), fname);
+ smsg(0, _("could not source \"%s\""), fname);
} else {
- smsg(_("line %" PRId64 ": could not source \"%s\""),
+ smsg(0, _("line %" PRId64 ": could not source \"%s\""),
(int64_t)SOURCING_LNUM, fname);
}
verbose_leave();
@@ -2018,9 +2122,9 @@ int do_source(char *fname, int check_other, int is_vimrc)
if (p_verbose > 1) {
verbose_enter();
if (SOURCING_NAME == NULL) {
- smsg(_("sourcing \"%s\""), fname);
+ smsg(0, _("sourcing \"%s\""), fname);
} else {
- smsg(_("line %" PRId64 ": sourcing \"%s\""), (int64_t)SOURCING_LNUM, fname);
+ smsg(0, _("line %" PRId64 ": sourcing \"%s\""), (int64_t)SOURCING_LNUM, fname);
}
verbose_leave();
}
@@ -2043,7 +2147,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
cookie.finished = false;
// Check if this script has a breakpoint.
- cookie.breakpoint = dbg_find_breakpoint(true, fname_exp, (linenr_T)0);
+ cookie.breakpoint = dbg_find_breakpoint(true, fname_exp, 0);
cookie.fname = fname_exp;
cookie.dbg_tick = debug_tick;
@@ -2069,7 +2173,24 @@ int do_source(char *fname, int check_other, int is_vimrc)
save_funccal(&funccalp_entry);
const sctx_T save_current_sctx = current_sctx;
- si = get_current_script_id(&fname_exp, &current_sctx);
+
+ current_sctx.sc_lnum = 0;
+
+ // Always use a new sequence number.
+ current_sctx.sc_seq = ++last_current_SID_seq;
+
+ if (sid > 0) {
+ // loading the same script again
+ si = SCRIPT_ITEM(sid);
+ } else {
+ // It's new, generate a new SID.
+ si = new_script_item(fname_exp, &sid);
+ fname_exp = xstrdup(si->sn_name); // used for autocmd
+ if (ret_sid != NULL) {
+ *ret_sid = sid;
+ }
+ }
+ current_sctx.sc_sid = sid;
// Keep the sourcing name/lnum, for recursive calls.
estack_push(ETYPE_SCRIPT, si->sn_name, 0);
@@ -2091,12 +2212,12 @@ int do_source(char *fname, int check_other, int is_vimrc)
cookie.conv.vc_type = CONV_NONE; // no conversion
- if (path_with_extension((const char *)fname_exp, "lua")) {
+ if (path_with_extension(fname_exp, "lua")) {
const sctx_T current_sctx_backup = current_sctx;
current_sctx.sc_sid = SID_LUA;
current_sctx.sc_lnum = 0;
// Source the file as lua
- nlua_exec_file((const char *)fname_exp);
+ nlua_exec_file(fname_exp);
current_sctx = current_sctx_backup;
} else {
// Read the first line so we can check for a UTF-8 BOM.
@@ -2120,7 +2241,7 @@ int do_source(char *fname, int check_other, int is_vimrc)
if (l_do_profiling == PROF_YES) {
// Get "si" again, "script_items" may have been reallocated.
- si = &SCRIPT_ITEM(current_sctx.sc_sid);
+ si = SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_prof_on) {
si->sn_pr_start = profile_end(si->sn_pr_start);
si->sn_pr_start = profile_sub_wait(wait_start, si->sn_pr_start);
@@ -2136,9 +2257,9 @@ int do_source(char *fname, int check_other, int is_vimrc)
estack_pop();
if (p_verbose > 1) {
verbose_enter();
- smsg(_("finished sourcing %s"), fname);
+ smsg(0, _("finished sourcing %s"), fname);
if (SOURCING_NAME != NULL) {
- smsg(_("continuing in %s"), SOURCING_NAME);
+ smsg(0, _("continuing in %s"), SOURCING_NAME);
}
verbose_leave();
}
@@ -2179,42 +2300,23 @@ theend:
return retval;
}
-/// Check if fname was sourced before to finds its SID.
-/// If it's new, generate a new SID.
-///
-/// @param[in,out] fnamep pointer to file path of script
-/// @param[out] ret_sctx sctx of this script
-scriptitem_T *get_current_script_id(char **fnamep, sctx_T *ret_sctx)
+/// Find an already loaded script "name".
+/// If found returns its script ID. If not found returns -1.
+int find_script_by_name(char *name)
{
- static int last_current_SID_seq = 0;
-
- sctx_T script_sctx = { .sc_seq = ++last_current_SID_seq,
- .sc_lnum = 0,
- .sc_sid = 0 };
- scriptitem_T *si = NULL;
-
assert(script_items.ga_len >= 0);
- for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; script_sctx.sc_sid--) {
+ for (int sid = script_items.ga_len; sid > 0; sid--) {
// We used to check inode here, but that doesn't work:
// - If a script is edited and written, it may get a different
// inode number, even though to the user it is the same script.
// - If a script is deleted and another script is written, with a
// different name, the inode may be re-used.
- si = &SCRIPT_ITEM(script_sctx.sc_sid);
- if (si->sn_name != NULL && path_fnamecmp(si->sn_name, *fnamep) == 0) {
- // Found it!
- break;
+ scriptitem_T *si = SCRIPT_ITEM(sid);
+ if (si->sn_name != NULL && path_fnamecmp(si->sn_name, name) == 0) {
+ return sid;
}
}
- if (script_sctx.sc_sid == 0) {
- si = new_script_item(*fnamep, &script_sctx.sc_sid);
- *fnamep = xstrdup(si->sn_name);
- }
- if (ret_sctx != NULL) {
- *ret_sctx = script_sctx;
- }
-
- return si;
+ return -1;
}
/// ":scriptnames"
@@ -2226,7 +2328,7 @@ void ex_scriptnames(exarg_T *eap)
emsg(_(e_invarg));
} else {
if (eap->addr_count > 0) {
- eap->arg = SCRIPT_ITEM(eap->line2).sn_name;
+ eap->arg = SCRIPT_ITEM(eap->line2)->sn_name;
} else {
expand_env(eap->arg, NameBuff, MAXPATHL);
eap->arg = NameBuff;
@@ -2237,12 +2339,12 @@ void ex_scriptnames(exarg_T *eap)
}
for (int i = 1; i <= script_items.ga_len && !got_int; i++) {
- if (SCRIPT_ITEM(i).sn_name != NULL) {
- home_replace(NULL, SCRIPT_ITEM(i).sn_name, NameBuff, MAXPATHL, true);
+ if (SCRIPT_ITEM(i)->sn_name != NULL) {
+ home_replace(NULL, SCRIPT_ITEM(i)->sn_name, NameBuff, MAXPATHL, true);
vim_snprintf(IObuff, IOSIZE, "%3d: %s", i, NameBuff);
if (!message_filtered(IObuff)) {
msg_putchar('\n');
- msg_outtrans(IObuff);
+ msg_outtrans(IObuff, 0);
line_breakcheck();
}
}
@@ -2254,8 +2356,8 @@ void ex_scriptnames(exarg_T *eap)
void scriptnames_slash_adjust(void)
{
for (int i = 1; i <= script_items.ga_len; i++) {
- if (SCRIPT_ITEM(i).sn_name != NULL) {
- slash_adjust(SCRIPT_ITEM(i).sn_name);
+ if (SCRIPT_ITEM(i)->sn_name != NULL) {
+ slash_adjust(SCRIPT_ITEM(i)->sn_name);
}
}
}
@@ -2289,7 +2391,7 @@ char *get_scriptname(LastSet last_set, bool *should_free)
case SID_STR:
return _("anonymous :source");
default: {
- char *const sname = SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name;
+ char *const sname = SCRIPT_ITEM(last_set.script_ctx.sc_sid)->sn_name;
if (sname == NULL) {
snprintf(IObuff, IOSIZE, _("anonymous :source (script id %d)"),
last_set.script_ctx.sc_sid);
@@ -2307,17 +2409,126 @@ void free_scriptnames(void)
{
profile_reset();
-# define FREE_SCRIPTNAME(item) xfree((item)->sn_name)
- GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME);
+# define FREE_SCRIPTNAME(item) \
+ do { \
+ scriptitem_T *_si = *(item); \
+ /* the variables themselves are cleared in evalvars_clear() */ \
+ xfree(_si->sn_vars); \
+ xfree(_si->sn_name); \
+ ga_clear(&_si->sn_prl_ga); \
+ xfree(_si); \
+ } while (0) \
+
+ GA_DEEP_CLEAR(&script_items, scriptitem_T *, FREE_SCRIPTNAME);
}
#endif
+void free_autoload_scriptnames(void)
+{
+ ga_clear_strings(&ga_loaded);
+}
+
linenr_T get_sourced_lnum(LineGetter fgetline, void *cookie)
FUNC_ATTR_PURE
{
return fgetline == getsourceline
- ? ((struct source_cookie *)cookie)->sourcing_lnum
- : SOURCING_LNUM;
+ ? ((struct source_cookie *)cookie)->sourcing_lnum
+ : SOURCING_LNUM;
+}
+
+/// Return a List of script-local functions defined in the script with id "sid".
+static list_T *get_script_local_funcs(scid_T sid)
+{
+ hashtab_T *const functbl = func_tbl_get();
+ list_T *l = tv_list_alloc((ptrdiff_t)functbl->ht_used);
+
+ // Iterate through all the functions in the global function hash table
+ // looking for functions with script ID "sid".
+ HASHTAB_ITER(functbl, hi, {
+ const ufunc_T *const fp = HI2UF(hi);
+ // Add functions with script id == "sid"
+ if (fp->uf_script_ctx.sc_sid == sid) {
+ const char *const name = fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name;
+ tv_list_append_string(l, name, -1);
+ }
+ });
+
+ return l;
+}
+
+/// "getscriptinfo()" function
+void f_getscriptinfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ tv_list_alloc_ret(rettv, script_items.ga_len);
+
+ if (tv_check_for_opt_dict_arg(argvars, 0) == FAIL) {
+ return;
+ }
+
+ list_T *l = rettv->vval.v_list;
+
+ regmatch_T regmatch = {
+ .regprog = NULL,
+ .rm_ic = p_ic,
+ };
+ bool filterpat = false;
+ varnumber_T sid = -1;
+
+ char *pat = NULL;
+ if (argvars[0].v_type == VAR_DICT) {
+ dictitem_T *sid_di = tv_dict_find(argvars[0].vval.v_dict, S_LEN("sid"));
+ if (sid_di != NULL) {
+ bool error = false;
+ sid = tv_get_number_chk(&sid_di->di_tv, &error);
+ if (error) {
+ return;
+ }
+ if (sid <= 0) {
+ semsg(_(e_invargNval), "sid", tv_get_string(&sid_di->di_tv));
+ return;
+ }
+ } else {
+ pat = tv_dict_get_string(argvars[0].vval.v_dict, "name", true);
+ if (pat != NULL) {
+ regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
+ }
+ if (regmatch.regprog != NULL) {
+ filterpat = true;
+ }
+ }
+ }
+
+ for (varnumber_T i = sid > 0 ? sid : 1;
+ (i == sid || sid <= 0) && i <= script_items.ga_len; i++) {
+ scriptitem_T *si = SCRIPT_ITEM(i);
+
+ if (si->sn_name == NULL) {
+ continue;
+ }
+
+ if (filterpat && !vim_regexec(&regmatch, si->sn_name, 0)) {
+ continue;
+ }
+
+ dict_T *d = tv_dict_alloc();
+ tv_list_append_dict(l, d);
+ tv_dict_add_str(d, S_LEN("name"), si->sn_name);
+ tv_dict_add_nr(d, S_LEN("sid"), i);
+ tv_dict_add_nr(d, S_LEN("version"), 1);
+ // Vim9 autoload script (:h vim9-autoload), not applicable to Nvim.
+ tv_dict_add_bool(d, S_LEN("autoload"), false);
+
+ // When a script ID is specified, return information about only the
+ // specified script, and add the script-local variables and functions.
+ if (sid > 0) {
+ dict_T *var_dict = tv_dict_copy(NULL, &si->sn_vars->sv_dict, true, get_copyID());
+ tv_dict_add_dict(d, S_LEN("variables"), var_dict);
+ tv_dict_add_list(d, S_LEN("functions"), get_script_local_funcs((scid_T)sid));
+ }
+ }
+
+ vim_regfree(regmatch.regprog);
+ xfree(pat);
}
/// Get one full line from a sourced file.
@@ -2329,7 +2540,6 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
{
struct source_cookie *sp = (struct source_cookie *)cookie;
char *line;
- char *p;
// If breakpoints have been added/deleted need to check for it.
if (sp->dbg_tick < debug_tick) {
@@ -2359,6 +2569,7 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
// Only concatenate lines starting with a \ when 'cpoptions' doesn't
// contain the 'C' flag.
if (line != NULL && do_concat && (vim_strchr(p_cpo, CPO_CONCAT) == NULL)) {
+ char *p;
// compensate for the one line read-ahead
sp->sourcing_lnum--;
@@ -2386,10 +2597,8 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
}
if (line != NULL && sp->conv.vc_type != CONV_NONE) {
- char *s;
-
// Convert the encoding of the script line.
- s = string_convert(&sp->conv, line, NULL);
+ char *s = string_convert(&sp->conv, line, NULL);
if (s != NULL) {
xfree(line);
line = s;
@@ -2423,7 +2632,7 @@ static char *get_one_sourceline(struct source_cookie *sp)
// Loop until there is a finished line (or end-of-file).
sp->sourcing_lnum++;
- for (;;) {
+ while (true) {
// make room to read at least 120 (more) characters
ga_grow(&ga, 120);
buf = ga.ga_data;
@@ -2551,8 +2760,6 @@ void ex_finish(exarg_T *eap)
/// an extra do_cmdline(). "reanimate" is used in the latter case.
void do_finish(exarg_T *eap, int reanimate)
{
- int idx;
-
if (reanimate) {
((struct source_cookie *)getline_cookie(eap->getline,
eap->cookie))->finished = false;
@@ -2562,7 +2769,7 @@ void do_finish(exarg_T *eap, int reanimate)
// not in its finally clause (which then is to be executed next) is found.
// In this case, make the ":finish" pending for execution at the ":endtry".
// Otherwise, finish normally.
- idx = cleanup_conditionals(eap->cstack, 0, true);
+ int idx = cleanup_conditionals(eap->cstack, 0, true);
if (idx >= 0) {
eap->cstack->cs_pending[idx] = CSTP_FINISH;
report_make_pending(CSTP_FINISH, NULL);
@@ -2580,3 +2787,79 @@ bool source_finished(LineGetter fgetline, void *cookie)
return getline_equal(fgetline, cookie, getsourceline)
&& ((struct source_cookie *)getline_cookie(fgetline, cookie))->finished;
}
+
+/// Return the autoload script name for a function or variable name
+/// Caller must make sure that "name" contains AUTOLOAD_CHAR.
+///
+/// @param[in] name Variable/function name.
+/// @param[in] name_len Name length.
+///
+/// @return [allocated] autoload script name.
+char *autoload_name(const char *const name, const size_t name_len)
+ FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // Get the script file name: replace '#' with '/', append ".vim".
+ char *const scriptname = xmalloc(name_len + sizeof("autoload/.vim"));
+ memcpy(scriptname, "autoload/", sizeof("autoload/") - 1);
+ memcpy(scriptname + sizeof("autoload/") - 1, name, name_len);
+ size_t auchar_idx = 0;
+ for (size_t i = sizeof("autoload/") - 1;
+ i - sizeof("autoload/") + 1 < name_len;
+ i++) {
+ if (scriptname[i] == AUTOLOAD_CHAR) {
+ scriptname[i] = '/';
+ auchar_idx = i;
+ }
+ }
+ memcpy(scriptname + auchar_idx, ".vim", sizeof(".vim"));
+
+ return scriptname;
+}
+
+/// If name has a package name try autoloading the script for it
+///
+/// @param[in] name Variable/function name.
+/// @param[in] name_len Name length.
+/// @param[in] reload If true, load script again when already loaded.
+///
+/// @return true if a package was loaded.
+bool script_autoload(const char *const name, const size_t name_len, const bool reload)
+{
+ // If there is no '#' after name[0] there is no package name.
+ const char *p = memchr(name, AUTOLOAD_CHAR, name_len);
+ if (p == NULL || p == name) {
+ return false;
+ }
+
+ bool ret = false;
+ char *tofree = autoload_name(name, name_len);
+ char *scriptname = tofree;
+
+ // Find the name in the list of previously loaded package names. Skip
+ // "autoload/", it's always the same.
+ int i = 0;
+ for (; i < ga_loaded.ga_len; i++) {
+ if (strcmp(((char **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) {
+ break;
+ }
+ }
+ if (!reload && i < ga_loaded.ga_len) {
+ ret = false; // Was loaded already.
+ } else {
+ // Remember the name if it wasn't loaded already.
+ if (i == ga_loaded.ga_len) {
+ GA_APPEND(char *, &ga_loaded, scriptname);
+ tofree = NULL;
+ }
+
+ // Try loading the package from $VIMRUNTIME/autoload/<name>.vim
+ // Use "ret_sid" to avoid loading the same script again.
+ int ret_sid;
+ if (do_in_runtimepath(scriptname, 0, source_callback, &ret_sid) == OK) {
+ ret = true;
+ }
+ }
+
+ xfree(tofree);
+ return ret;
+}