2016-04-30 20:31:02 -07:00
|
|
|
// Functions for storing and retrieving function information. These functions also take care of
|
|
|
|
// autoloading functions in the $fish_function_path. Actual function evaluation is taken care of by
|
|
|
|
// the parser and to some degree the builtin handling library.
|
|
|
|
//
|
2016-05-18 22:30:21 +00:00
|
|
|
#include "config.h" // IWYU pragma: keep
|
|
|
|
|
2022-08-21 14:51:33 -07:00
|
|
|
#include "function.h"
|
2022-08-20 23:14:48 -07:00
|
|
|
|
2022-08-21 14:51:33 -07:00
|
|
|
#include <algorithm>
|
2022-08-20 23:14:48 -07:00
|
|
|
#include <cstdint>
|
2019-10-13 15:50:48 -07:00
|
|
|
#include <cwchar>
|
2012-01-13 23:44:18 -08:00
|
|
|
#include <map>
|
2016-04-30 20:31:02 -07:00
|
|
|
#include <memory>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <string>
|
2017-08-19 11:55:06 -05:00
|
|
|
#include <unordered_map>
|
2018-02-09 21:53:06 -08:00
|
|
|
#include <unordered_set>
|
2015-07-25 23:14:25 +08:00
|
|
|
#include <utility>
|
2022-08-20 23:14:48 -07:00
|
|
|
#include <vector>
|
2005-09-20 23:26:39 +10:00
|
|
|
|
2022-08-20 23:14:48 -07:00
|
|
|
#include "ast.h"
|
2015-07-25 23:14:25 +08:00
|
|
|
#include "autoload.h"
|
2005-09-20 23:26:39 +10:00
|
|
|
#include "common.h"
|
2022-08-20 23:14:48 -07:00
|
|
|
#include "complete.h"
|
2016-04-30 20:31:02 -07:00
|
|
|
#include "env.h"
|
2005-10-06 08:37:08 +10:00
|
|
|
#include "event.h"
|
2016-04-30 20:31:02 -07:00
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
2022-08-20 23:14:48 -07:00
|
|
|
#include "maybe.h"
|
|
|
|
#include "parse_constants.h"
|
2018-09-24 22:26:46 -04:00
|
|
|
#include "parser.h"
|
2007-04-22 19:50:26 +10:00
|
|
|
#include "parser_keywords.h"
|
2023-01-14 14:56:24 -08:00
|
|
|
#include "signals.h"
|
2020-09-21 17:44:58 +02:00
|
|
|
#include "wcstringutil.h"
|
2016-04-30 20:31:02 -07:00
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2005-09-20 23:26:39 +10:00
|
|
|
|
2021-09-28 12:50:27 -07:00
|
|
|
namespace {
|
2018-02-10 17:38:57 -08:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
/// Type wrapping up the set of all functions.
|
|
|
|
/// There's only one of these; it's managed by a lock.
|
|
|
|
struct function_set_t {
|
|
|
|
/// The map of all functions by name.
|
2021-10-21 14:17:16 -07:00
|
|
|
std::unordered_map<wcstring, function_properties_ref_t> funcs;
|
2011-12-26 19:18:46 -08:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
/// Tombstones for functions that should no longer be autoloaded.
|
|
|
|
std::unordered_set<wcstring> autoload_tombstones;
|
2015-03-11 14:14:56 +01:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
/// The autoloader for our functions.
|
2019-04-27 15:42:34 -07:00
|
|
|
autoload_t autoloader{L"fish_function_path"};
|
2011-12-26 19:18:46 -08:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
/// Remove a function.
|
|
|
|
/// \return true if successful, false if it doesn't exist.
|
|
|
|
bool remove(const wcstring &name);
|
2017-01-29 18:56:55 -08:00
|
|
|
|
2021-10-21 13:23:49 -07:00
|
|
|
/// Get the properties for a function, or nullptr if none.
|
|
|
|
function_properties_ref_t get_props(const wcstring &name) const {
|
|
|
|
auto iter = funcs.find(name);
|
2021-10-21 14:17:16 -07:00
|
|
|
return iter == funcs.end() ? nullptr : iter->second;
|
2021-10-21 13:23:49 -07:00
|
|
|
}
|
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
/// \return true if we should allow autoloading a given function.
|
|
|
|
bool allow_autoload(const wcstring &name) const;
|
|
|
|
|
|
|
|
function_set_t() = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// The big set of all functions.
|
|
|
|
static owning_lock<function_set_t> function_set;
|
|
|
|
|
|
|
|
bool function_set_t::allow_autoload(const wcstring &name) const {
|
|
|
|
// Prohibit autoloading if we have a non-autoload (explicit) function, or if the function is
|
|
|
|
// tombstoned.
|
2021-10-21 13:23:49 -07:00
|
|
|
auto props = get_props(name);
|
|
|
|
bool has_explicit_func = props && !props->is_autoload;
|
2019-04-26 15:03:41 -07:00
|
|
|
bool is_tombstoned = autoload_tombstones.count(name) > 0;
|
|
|
|
return !has_explicit_func && !is_tombstoned;
|
|
|
|
}
|
2021-09-28 12:50:27 -07:00
|
|
|
} // namespace
|
2006-02-08 19:20:05 +10:00
|
|
|
|
2021-10-21 14:17:16 -07:00
|
|
|
/// \return a copy of some function props, in a new shared_ptr.
|
2021-10-31 03:51:16 -07:00
|
|
|
static std::shared_ptr<function_properties_t> copy_props(const function_properties_ref_t &props) {
|
2021-10-21 14:17:16 -07:00
|
|
|
assert(props && "Null props");
|
|
|
|
return std::make_shared<function_properties_t>(*props);
|
|
|
|
}
|
|
|
|
|
2016-04-30 20:31:02 -07:00
|
|
|
/// Make sure that if the specified function is a dynamically loaded function, it has been fully
|
|
|
|
/// loaded.
|
2019-04-26 15:03:41 -07:00
|
|
|
/// Note this executes fish script code.
|
2022-06-11 11:34:52 -07:00
|
|
|
bool function_load(const wcstring &name, parser_t &parser) {
|
2022-06-19 16:27:06 -07:00
|
|
|
parser.assert_can_execute();
|
2019-04-26 15:03:41 -07:00
|
|
|
maybe_t<wcstring> path_to_autoload;
|
|
|
|
// Note we can't autoload while holding the funcset lock.
|
|
|
|
// Lock around a local region.
|
|
|
|
{
|
|
|
|
auto funcset = function_set.acquire();
|
|
|
|
if (funcset->allow_autoload(name)) {
|
2019-07-12 12:19:00 -07:00
|
|
|
path_to_autoload = funcset->autoloader.resolve_command(name, env_stack_t::globals());
|
2019-04-26 15:03:41 -07:00
|
|
|
}
|
2011-12-26 19:18:46 -08:00
|
|
|
}
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
// Release the lock and perform any autoload, then reacquire the lock and clean up.
|
|
|
|
if (path_to_autoload) {
|
2022-06-11 11:34:52 -07:00
|
|
|
// Crucially, the lock is acquired after perform_autoload().
|
2019-05-04 20:20:52 -07:00
|
|
|
autoload_t::perform_autoload(*path_to_autoload, parser);
|
2019-04-26 15:03:41 -07:00
|
|
|
function_set.acquire()->autoloader.mark_autoload_finished(name);
|
|
|
|
}
|
2022-06-11 11:34:52 -07:00
|
|
|
return path_to_autoload.has_value();
|
2006-02-08 19:20:05 +10:00
|
|
|
}
|
|
|
|
|
2016-04-30 20:31:02 -07:00
|
|
|
/// Insert a list of all dynamically loaded functions into the specified list.
|
2022-06-16 15:48:46 +02:00
|
|
|
static void autoload_names(std::unordered_set<wcstring> &names, bool get_hidden) {
|
2012-11-18 16:30:30 -08:00
|
|
|
size_t i;
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2022-10-28 20:07:36 -07:00
|
|
|
// TODO: justify this.
|
2018-09-24 22:26:46 -04:00
|
|
|
auto &vars = env_stack_t::principal();
|
|
|
|
const auto path_var = vars.get(L"fish_function_path");
|
2017-08-05 21:40:27 -07:00
|
|
|
if (path_var.missing_or_empty()) return;
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2020-12-15 15:26:38 +01:00
|
|
|
const wcstring_list_t &path_list = path_var->as_list();
|
2006-02-09 03:37:18 +10:00
|
|
|
|
2016-04-30 20:31:02 -07:00
|
|
|
for (i = 0; i < path_list.size(); i++) {
|
2012-11-18 16:30:30 -08:00
|
|
|
const wcstring &ndir_str = path_list.at(i);
|
2022-09-25 12:52:44 -07:00
|
|
|
dir_iter_t dir(ndir_str);
|
2018-09-22 06:28:19 -05:00
|
|
|
if (!dir.valid()) continue;
|
2012-11-18 16:30:30 -08:00
|
|
|
|
2022-09-25 12:52:44 -07:00
|
|
|
while (const auto *entry = dir.next()) {
|
|
|
|
const wchar_t *fn = entry->name.c_str();
|
2012-11-18 16:30:30 -08:00
|
|
|
const wchar_t *suffix;
|
2016-04-30 20:31:02 -07:00
|
|
|
if (!get_hidden && fn[0] == L'_') continue;
|
2012-11-18 16:30:30 -08:00
|
|
|
|
2019-03-12 14:06:01 -07:00
|
|
|
suffix = std::wcsrchr(fn, L'.');
|
2022-10-29 10:24:03 +02:00
|
|
|
// We need a ".fish" *suffix*, it can't be the entire name.
|
|
|
|
if (suffix && suffix != fn && (std::wcscmp(suffix, L".fish") == 0)) {
|
|
|
|
// Also ignore directories.
|
|
|
|
if (!entry->is_dir()) {
|
|
|
|
wcstring name(fn, suffix - fn);
|
|
|
|
names.insert(name);
|
|
|
|
}
|
2012-11-18 16:30:30 -08:00
|
|
|
}
|
|
|
|
}
|
2012-11-18 11:23:22 +01:00
|
|
|
}
|
2006-02-08 19:20:05 +10:00
|
|
|
}
|
|
|
|
|
2021-10-21 13:56:32 -07:00
|
|
|
void function_add(wcstring name, std::shared_ptr<function_properties_t> props) {
|
2021-10-21 13:34:46 -07:00
|
|
|
assert(props && "Null props");
|
2019-04-26 15:03:41 -07:00
|
|
|
auto funcset = function_set.acquire();
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
// Historical check. TODO: rationalize this.
|
2019-11-12 11:25:41 -08:00
|
|
|
if (name.empty()) {
|
2019-04-26 15:03:41 -07:00
|
|
|
return;
|
|
|
|
}
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2016-04-30 20:31:02 -07:00
|
|
|
// Remove the old function.
|
2019-11-12 11:25:41 -08:00
|
|
|
funcset->remove(name);
|
2019-04-26 15:03:41 -07:00
|
|
|
|
|
|
|
// Check if this is a function that we are autoloading.
|
2021-10-21 13:23:49 -07:00
|
|
|
props->is_autoload = funcset->autoloader.autoload_in_progress(name);
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2016-04-30 20:31:02 -07:00
|
|
|
// Create and store a new function.
|
2021-10-21 14:17:16 -07:00
|
|
|
auto ins = funcset->funcs.emplace(std::move(name), std::move(props));
|
2019-04-26 15:03:41 -07:00
|
|
|
assert(ins.second && "Function should not already be present in the table");
|
|
|
|
(void)ins;
|
2005-09-20 23:26:39 +10:00
|
|
|
}
|
|
|
|
|
2021-10-21 14:32:35 -07:00
|
|
|
function_properties_ref_t function_get_props(const wcstring &name) {
|
2018-02-11 17:21:24 -08:00
|
|
|
if (parser_keywords_is_reserved(name)) return nullptr;
|
2021-10-21 14:17:16 -07:00
|
|
|
return function_set.acquire()->get_props(name);
|
2018-02-11 17:21:24 -08:00
|
|
|
}
|
|
|
|
|
2021-10-21 14:28:39 -07:00
|
|
|
function_properties_ref_t function_get_props_autoload(const wcstring &name, parser_t &parser) {
|
2022-06-19 16:27:06 -07:00
|
|
|
parser.assert_can_execute();
|
2021-10-21 14:28:39 -07:00
|
|
|
if (parser_keywords_is_reserved(name)) return nullptr;
|
2022-06-11 11:34:52 -07:00
|
|
|
function_load(name, parser);
|
2021-10-21 14:28:39 -07:00
|
|
|
return function_get_props(name);
|
2011-12-26 19:18:46 -08:00
|
|
|
}
|
2010-09-18 09:51:16 +08:00
|
|
|
|
2021-10-21 14:28:39 -07:00
|
|
|
bool function_exists(const wcstring &cmd, parser_t &parser) {
|
2022-06-19 16:27:06 -07:00
|
|
|
parser.assert_can_execute();
|
2021-10-21 14:28:39 -07:00
|
|
|
if (!valid_func_name(cmd)) return false;
|
|
|
|
return function_get_props_autoload(cmd, parser) != nullptr;
|
2015-10-07 18:59:41 -07:00
|
|
|
}
|
|
|
|
|
2021-06-22 12:37:45 -07:00
|
|
|
bool function_exists_no_autoload(const wcstring &cmd) {
|
|
|
|
if (!valid_func_name(cmd)) return false;
|
|
|
|
if (parser_keywords_is_reserved(cmd)) return false;
|
2019-04-26 15:03:41 -07:00
|
|
|
auto funcset = function_set.acquire();
|
2015-03-11 14:14:56 +01:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
// Check if we either have the function, or it could be autoloaded.
|
2021-10-21 14:17:16 -07:00
|
|
|
return funcset->get_props(cmd) || funcset->autoloader.can_autoload(cmd);
|
2012-01-25 18:40:08 -08:00
|
|
|
}
|
2006-02-05 23:10:35 +10:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
bool function_set_t::remove(const wcstring &name) {
|
|
|
|
size_t amt = funcs.erase(name);
|
|
|
|
if (amt > 0) {
|
|
|
|
event_remove_function_handlers(name);
|
|
|
|
}
|
|
|
|
return amt > 0;
|
2012-01-23 20:32:36 -08:00
|
|
|
}
|
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
void function_remove(const wcstring &name) {
|
|
|
|
auto funcset = function_set.acquire();
|
2019-06-22 11:08:36 +02:00
|
|
|
funcset->remove(name);
|
|
|
|
// Prevent (re-)autoloading this function.
|
|
|
|
funcset->autoload_tombstones.insert(name);
|
2005-09-20 23:26:39 +10:00
|
|
|
}
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2021-10-21 14:28:39 -07:00
|
|
|
// \return the body of a function (everything after the header, up to but not including the 'end').
|
|
|
|
static wcstring get_function_body_source(const function_properties_t &props) {
|
2020-01-03 14:40:28 -08:00
|
|
|
// We want to preserve comments that the AST attaches to the header (#5285).
|
2020-07-03 11:16:51 -07:00
|
|
|
// Take everything from the end of the header to the 'end' keyword.
|
2021-10-21 14:28:39 -07:00
|
|
|
auto header_src = props.func_node->header->try_source_range();
|
|
|
|
auto end_kw_src = props.func_node->end.try_source_range();
|
2020-07-03 11:16:51 -07:00
|
|
|
if (header_src && end_kw_src) {
|
2020-01-03 14:40:28 -08:00
|
|
|
uint32_t body_start = header_src->start + header_src->length;
|
2020-07-03 11:16:51 -07:00
|
|
|
uint32_t body_end = end_kw_src->start;
|
|
|
|
assert(body_start <= body_end && "end keyword should come after header");
|
2021-10-21 14:28:39 -07:00
|
|
|
return wcstring(props.parsed_source->src, body_start, body_end - body_start);
|
2012-05-17 19:37:46 -07:00
|
|
|
}
|
2021-10-21 14:28:39 -07:00
|
|
|
return wcstring{};
|
2005-09-20 23:26:39 +10:00
|
|
|
}
|
|
|
|
|
2019-05-04 20:20:52 -07:00
|
|
|
void function_set_desc(const wcstring &name, const wcstring &desc, parser_t &parser) {
|
2022-06-19 16:27:06 -07:00
|
|
|
parser.assert_can_execute();
|
2022-06-11 11:34:52 -07:00
|
|
|
function_load(name, parser);
|
2019-04-26 15:03:41 -07:00
|
|
|
auto funcset = function_set.acquire();
|
|
|
|
auto iter = funcset->funcs.find(name);
|
|
|
|
if (iter != funcset->funcs.end()) {
|
2021-10-21 13:56:32 -07:00
|
|
|
// Note the description is immutable, as it may be accessed on another thread, so we copy
|
|
|
|
// the properties to modify it.
|
2021-10-21 14:17:16 -07:00
|
|
|
auto new_props = copy_props(iter->second);
|
2021-10-21 13:56:32 -07:00
|
|
|
new_props->description = desc;
|
2021-10-21 14:17:16 -07:00
|
|
|
iter->second = new_props;
|
2012-05-18 14:00:36 -07:00
|
|
|
}
|
2005-09-20 23:26:39 +10:00
|
|
|
}
|
|
|
|
|
2023-02-13 19:29:28 +03:30
|
|
|
bool function_copy(const wcstring &name, const wcstring &new_name, parser_t &parser) {
|
|
|
|
auto filename = parser.current_filename();
|
|
|
|
auto lineno = parser.get_lineno();
|
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
auto funcset = function_set.acquire();
|
2021-10-21 14:17:16 -07:00
|
|
|
auto props = funcset->get_props(name);
|
|
|
|
if (!props) {
|
2019-04-26 15:03:41 -07:00
|
|
|
// No such function.
|
|
|
|
return false;
|
2012-01-13 23:44:18 -08:00
|
|
|
}
|
2021-10-21 13:23:49 -07:00
|
|
|
// Copy the function's props.
|
2021-10-21 14:17:16 -07:00
|
|
|
auto new_props = copy_props(props);
|
2021-10-21 13:23:49 -07:00
|
|
|
new_props->is_autoload = false;
|
2023-02-13 19:29:28 +03:30
|
|
|
new_props->is_copy = true;
|
|
|
|
new_props->copy_definition_file = filename;
|
|
|
|
new_props->copy_definition_lineno = lineno;
|
2021-10-21 13:23:49 -07:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
// Note this will NOT overwrite an existing function with the new name.
|
|
|
|
// TODO: rationalize if this behavior is desired.
|
2021-10-21 14:17:16 -07:00
|
|
|
funcset->funcs.emplace(new_name, std::move(new_props));
|
2019-04-26 15:03:41 -07:00
|
|
|
return true;
|
2005-09-20 23:26:39 +10:00
|
|
|
}
|
|
|
|
|
2022-06-16 15:48:46 +02:00
|
|
|
wcstring_list_t function_get_names(bool get_hidden) {
|
2017-08-19 15:29:52 -05:00
|
|
|
std::unordered_set<wcstring> names;
|
2019-04-26 15:03:41 -07:00
|
|
|
auto funcset = function_set.acquire();
|
2012-11-18 16:30:30 -08:00
|
|
|
autoload_names(names, get_hidden);
|
2019-04-26 15:03:41 -07:00
|
|
|
for (const auto &func : funcset->funcs) {
|
2017-08-19 15:29:52 -05:00
|
|
|
const wcstring &name = func.first;
|
2012-11-18 11:23:22 +01:00
|
|
|
|
2016-04-30 20:31:02 -07:00
|
|
|
// Maybe skip hidden.
|
2016-10-22 11:21:13 -07:00
|
|
|
if (!get_hidden && (name.empty() || name.at(0) == L'_')) {
|
|
|
|
continue;
|
2012-01-13 23:44:18 -08:00
|
|
|
}
|
|
|
|
names.insert(name);
|
|
|
|
}
|
|
|
|
return wcstring_list_t(names.begin(), names.end());
|
2005-09-20 23:26:39 +10:00
|
|
|
}
|
2005-10-06 08:37:08 +10:00
|
|
|
|
2019-04-26 15:03:41 -07:00
|
|
|
void function_invalidate_path() {
|
|
|
|
// Remove all autoloaded functions and update the autoload path.
|
|
|
|
// Note we don't want to risk removal during iteration; we expect this to be called
|
|
|
|
// infrequently.
|
|
|
|
auto funcset = function_set.acquire();
|
|
|
|
wcstring_list_t autoloadees;
|
|
|
|
for (const auto &kv : funcset->funcs) {
|
2021-10-21 14:17:16 -07:00
|
|
|
if (kv.second->is_autoload) {
|
2019-04-26 15:03:41 -07:00
|
|
|
autoloadees.push_back(kv.first);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const wcstring &name : autoloadees) {
|
|
|
|
funcset->remove(name);
|
|
|
|
}
|
|
|
|
funcset->autoloader.clear();
|
|
|
|
}
|
2020-09-21 17:44:58 +02:00
|
|
|
|
2021-10-21 14:28:39 -07:00
|
|
|
wcstring function_properties_t::annotated_definition(const wcstring &name) const {
|
2020-09-21 17:44:58 +02:00
|
|
|
wcstring out;
|
2021-10-21 14:28:39 -07:00
|
|
|
wcstring desc = this->localized_description();
|
|
|
|
wcstring def = get_function_body_source(*this);
|
2020-09-21 17:44:58 +02:00
|
|
|
std::vector<std::shared_ptr<event_handler_t>> ev = event_get_function_handlers(name);
|
|
|
|
|
|
|
|
out.append(L"function ");
|
|
|
|
|
|
|
|
// Typically we prefer to specify the function name first, e.g. "function foo --description bar"
|
|
|
|
// But if the function name starts with a -, we'll need to output it after all the options.
|
|
|
|
bool defer_function_name = (name.at(0) == L'-');
|
|
|
|
if (!defer_function_name) {
|
2022-07-25 16:25:04 +02:00
|
|
|
out.append(escape_string(name));
|
2020-09-21 17:44:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Output wrap targets.
|
|
|
|
for (const wcstring &wrap : complete_get_wrap_targets(name)) {
|
|
|
|
out.append(L" --wraps=");
|
2022-07-25 16:25:04 +02:00
|
|
|
out.append(escape_string(wrap));
|
2020-09-21 17:44:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!desc.empty()) {
|
|
|
|
out.append(L" --description ");
|
2022-07-25 16:25:04 +02:00
|
|
|
out.append(escape_string(desc));
|
2020-09-21 17:44:58 +02:00
|
|
|
}
|
|
|
|
|
2021-10-21 14:28:39 -07:00
|
|
|
if (!this->shadow_scope) {
|
2020-09-21 17:44:58 +02:00
|
|
|
out.append(L" --no-scope-shadowing");
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const auto &next : ev) {
|
|
|
|
const event_description_t &d = next->desc;
|
|
|
|
switch (d.type) {
|
|
|
|
case event_type_t::signal: {
|
|
|
|
append_format(out, L" --on-signal %ls", sig2wcs(d.param1.signal));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case event_type_t::variable: {
|
|
|
|
append_format(out, L" --on-variable %ls", d.str_param1.c_str());
|
|
|
|
break;
|
|
|
|
}
|
2021-05-19 11:29:03 -07:00
|
|
|
case event_type_t::process_exit: {
|
|
|
|
append_format(out, L" --on-process-exit %d", d.param1.pid);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case event_type_t::job_exit: {
|
2021-05-20 11:25:32 -07:00
|
|
|
append_format(out, L" --on-job-exit %d", d.param1.jobspec.pid);
|
2020-09-21 17:44:58 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case event_type_t::caller_exit: {
|
|
|
|
append_format(out, L" --on-job-exit caller");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case event_type_t::generic: {
|
|
|
|
append_format(out, L" --on-event %ls", d.str_param1.c_str());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case event_type_t::any:
|
|
|
|
default: {
|
|
|
|
DIE("unexpected next->type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-21 14:28:39 -07:00
|
|
|
const wcstring_list_t &named = this->named_arguments;
|
2020-09-21 17:44:58 +02:00
|
|
|
if (!named.empty()) {
|
|
|
|
append_format(out, L" --argument");
|
|
|
|
for (const auto &name : named) {
|
|
|
|
append_format(out, L" %ls", name.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Output the function name if we deferred it.
|
|
|
|
if (defer_function_name) {
|
|
|
|
out.append(L" -- ");
|
2022-07-25 16:25:04 +02:00
|
|
|
out.append(escape_string(name));
|
2020-09-21 17:44:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Output any inherited variables as `set -l` lines.
|
2021-10-21 14:28:39 -07:00
|
|
|
for (const auto &kv : this->inherit_vars) {
|
2020-09-21 17:44:58 +02:00
|
|
|
// We don't know what indentation style the function uses,
|
|
|
|
// so we do what fish_indent would.
|
|
|
|
append_format(out, L"\n set -l %ls", kv.first.c_str());
|
|
|
|
for (const auto &arg : kv.second) {
|
|
|
|
out.push_back(L' ');
|
2022-07-25 16:25:04 +02:00
|
|
|
out.append(escape_string(arg));
|
2020-09-21 17:44:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
out.push_back('\n');
|
|
|
|
out.append(def);
|
|
|
|
|
|
|
|
// Append a newline before the 'end', unless there already is one there.
|
|
|
|
if (!string_suffixes_string(L"\n", def)) {
|
|
|
|
out.push_back(L'\n');
|
|
|
|
}
|
|
|
|
out.append(L"end\n");
|
|
|
|
return out;
|
|
|
|
}
|
2021-10-21 14:28:39 -07:00
|
|
|
|
|
|
|
const wchar_t *function_properties_t::localized_description() const {
|
|
|
|
if (description.empty()) return L"";
|
|
|
|
return _(description.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
int function_properties_t::definition_lineno() const {
|
|
|
|
// return one plus the number of newlines at offsets less than the start of our function's
|
|
|
|
// statement (which includes the header).
|
|
|
|
// TODO: merge with line_offset_of_character_at_offset?
|
|
|
|
auto source_range = func_node->try_source_range();
|
|
|
|
assert(source_range && "Function has no source range");
|
|
|
|
uint32_t func_start = source_range->start;
|
|
|
|
const wcstring &source = parsed_source->src;
|
|
|
|
assert(func_start <= source.size() && "function start out of bounds");
|
|
|
|
return 1 + std::count(source.begin(), source.begin() + func_start, L'\n');
|
|
|
|
}
|