mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Add autoloader_t
autoloader_t will be the reimplementation of autoloading. Crucically it no longer manages any locking or loading itself; instead all locking and loading is performed by clients. This makes it easier to test and helps limit its responsibilities.
This commit is contained in:
parent
b7ad6b5bdc
commit
3950dab9ff
2 changed files with 108 additions and 2 deletions
|
@ -70,6 +70,9 @@ class autoload_file_cache_t {
|
|||
/// Initialize with a set of directories.
|
||||
explicit autoload_file_cache_t(wcstring_list_t dirs) : dirs_(std::move(dirs)) {}
|
||||
|
||||
/// Initialize with empty directories.
|
||||
autoload_file_cache_t() = default;
|
||||
|
||||
/// \return the directories.
|
||||
const wcstring_list_t &dirs() const { return dirs_; }
|
||||
|
||||
|
@ -145,6 +148,46 @@ maybe_t<autoloadable_file_t> autoload_file_cache_t::check(const wcstring &cmd, b
|
|||
return file;
|
||||
}
|
||||
|
||||
autoloader_t::autoloader_t(wcstring env_var_name)
|
||||
: env_var_name_(std::move(env_var_name)), cache_(make_unique<autoload_file_cache_t>()) {}
|
||||
|
||||
autoloader_t::~autoloader_t() = default;
|
||||
|
||||
bool autoloader_t::can_autoload(const wcstring &cmd) {
|
||||
return cache_->check(cmd, true /* allow stale */).has_value();
|
||||
}
|
||||
|
||||
maybe_t<wcstring> autoloader_t::resolve_command(const wcstring &cmd, const environment_t &env) {
|
||||
// Are we currently in the process of autoloading this?
|
||||
if (current_autoloading_.count(cmd) > 0) return none();
|
||||
|
||||
// Check to see if our paths have changed. If so, replace our cache.
|
||||
// Note we don't have to modify autoloadable_files_. We'll naturally detect if those have
|
||||
// changed when we query the cache.
|
||||
maybe_t<env_var_t> mvar = env.get(env_var_name_);
|
||||
const wcstring_list_t empty;
|
||||
const wcstring_list_t &paths = mvar ? mvar->as_list() : empty;
|
||||
if (paths != cache_->dirs()) {
|
||||
cache_ = make_unique<autoload_file_cache_t>(paths);
|
||||
}
|
||||
|
||||
// Do we have an entry to load?
|
||||
auto mfile = cache_->check(cmd);
|
||||
if (!mfile) return none();
|
||||
|
||||
// Is this file the same as what we previously autoloaded?
|
||||
auto iter = autoloaded_files_.find(cmd);
|
||||
if (iter != autoloaded_files_.end() && iter->second == mfile->file_id) {
|
||||
// The file has been autoloaded and is unchanged.
|
||||
return none();
|
||||
}
|
||||
|
||||
// We're going to (tell our caller to) autoload this command.
|
||||
current_autoloading_.insert(cmd);
|
||||
autoloaded_files_[cmd] = mfile->file_id;
|
||||
return std::move(mfile->path);
|
||||
}
|
||||
|
||||
file_access_attempt_t access_file(const wcstring &path, int mode) {
|
||||
file_access_attempt_t result = {};
|
||||
file_id_t file_id = file_id_for_path(path);
|
||||
|
|
|
@ -13,6 +13,71 @@
|
|||
#include "lru.h"
|
||||
#include "wutil.h"
|
||||
|
||||
class autoload_file_cache_t;
|
||||
class environment_t;
|
||||
|
||||
/// autoloader_t is a class that knows how to autoload .fish files from a list of directories. This
|
||||
/// is used by autoloading functions and completions. It maintains a file cache, which is
|
||||
/// responsible for potentially cached accesses of files, and then a list of files that have
|
||||
/// actually been autoloaded. A client may request a file to autoload given a command name, and may
|
||||
/// be returned a path which it is expected to source.
|
||||
/// autoloader_t does not have any internal locks; it is the responsibility of the caller to lock
|
||||
/// it.
|
||||
class autoloader_t {
|
||||
/// The environment variable whose paths we observe.
|
||||
const wcstring env_var_name_;
|
||||
|
||||
/// A map from command to the files we have autoloaded.
|
||||
std::unordered_map<wcstring, file_id_t> autoloaded_files_;
|
||||
|
||||
/// The list of commands that we are currently autoloading.
|
||||
std::unordered_set<wcstring> current_autoloading_;
|
||||
|
||||
/// The autoload cache.
|
||||
/// This is a unique_ptr because want to change it if the value of our environment variable
|
||||
/// changes. This is never null (but it may be a cache with no paths).
|
||||
std::unique_ptr<autoload_file_cache_t> cache_;
|
||||
|
||||
public:
|
||||
/// Construct an autoloader that loads from the paths given by \p env_var_name.
|
||||
explicit autoloader_t(wcstring env_var_name);
|
||||
|
||||
~autoloader_t();
|
||||
|
||||
/// Given a command, get a path to autoload.
|
||||
/// For example, if the environment variable is 'fish_function_path' and the command is 'foo',
|
||||
/// this will look for a file 'foo.fish' in one of the directories given by fish_function_path.
|
||||
/// If there is no such file, OR if the file has been previously resolved and is now unchanged,
|
||||
/// this will return none. But if the file is either new or changed, this will return the path.
|
||||
/// After returning a path, the command is marked in-progress until the caller calls
|
||||
/// mark_autoload_finished() with the same command. Note this does not actually execute any
|
||||
/// code; it is the caller's responsibility to load the file.
|
||||
maybe_t<wcstring> resolve_command(const wcstring &cmd, const environment_t &env);
|
||||
|
||||
/// Mark that a command previously returned from path_to_autoload is finished autoloading.
|
||||
void mark_autoload_finished(const wcstring &cmd) {
|
||||
size_t amt = current_autoloading_.erase(cmd);
|
||||
assert(amt > 0 && "cmd was not being autoloaded");
|
||||
(void)amt;
|
||||
}
|
||||
|
||||
/// \return whether a command is currently being autoloaded.
|
||||
bool autoload_in_progress(const wcstring &cmd) const {
|
||||
return current_autoloading_.count(cmd) > 0;
|
||||
}
|
||||
|
||||
/// \return whether a command could potentially be autoloaded.
|
||||
/// This does not actually mark the command as being autoloaded.
|
||||
bool can_autoload(const wcstring &cmd);
|
||||
|
||||
/// Mark that all autoloaded files have been forgotten.
|
||||
/// Future calls to path_to_autoload() will return previously-returned paths.
|
||||
void clear() {
|
||||
// Note there is no reason to invalidate the cache here.
|
||||
autoloaded_files_.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/// Record of an attempt to access a file.
|
||||
struct file_access_attempt_t {
|
||||
/// If filled, the file ID of the checked file.
|
||||
|
@ -41,8 +106,6 @@ struct autoload_function_t {
|
|||
bool is_placeholder;
|
||||
};
|
||||
|
||||
class environment_t;
|
||||
|
||||
/// Class representing a path from which we can autoload and the autoloaded contents.
|
||||
class autoload_t : public lru_cache_t<autoload_t, autoload_function_t> {
|
||||
private:
|
||||
|
|
Loading…
Reference in a new issue