1
1
mirror of https://github.com/mawww/kakoune.git synced 2024-07-14 16:10:24 +03:00

Also check shell parameters for kak_* references

This makes it easier to pass shell fragments as arguments so that
%sh{ eval "$@" } just works even if arguments refer to Kakoune's
vars.
This commit is contained in:
Maxime Coste 2024-06-07 18:59:30 +10:00
parent 688e87d668
commit 1bdae3f9c4
4 changed files with 48 additions and 25 deletions

View File

@ -3,6 +3,10 @@
This changelog contains major and/or breaking changes to Kakoune between
released versions.
== Development version
* Expose env vars that are mentionned in the arguments passed to shell expansions
== Kakoune 2024.05.18
* Fixed tests on Alpine Linux and *BSD

View File

@ -146,14 +146,16 @@ and unquote them, then execute the resulting `set` command, which sets
the shell's argument variables to the items from `$kak_selections`. The
`while` loop with `shift` iterates through the arguments one by one.
Only variables actually mentioned in the body of the shell expansion will
be exported into the shell's environment. For example:
Only variables actually mentioned in the body of the shell expansion or
in passed arguments will be exported into the shell's environment.
For example:
----
echo %sh{ env | grep ^kak_ }
----
... will find none of Kakoune's special environment variables, but:
... will not find any of Kakoune's special environment variables, but:
----
echo %sh{ env | grep ^kak_ # kak_session }
@ -162,6 +164,15 @@ echo %sh{ env | grep ^kak_ # kak_session }
... will find the `$kak_session` variable because it was mentioned by name
in a comment, even though it wasn't directly used.
----
define-command -params .. eval-shell %{ echo %sh{ eval "$@" } }
eval-shell 'echo $kak_session'
----
... will also find the `$kak_session` variable because it was mentioned by name
in the command arguments, which are automatically made available to shell blocks
as "$@"
TIP: These environment variables are also available in other contexts where
Kakoune uses a shell command, such as the `|`, `!` or `$` normal mode commands
(See <<keys#,`:doc keys`>>).

View File

@ -45,6 +45,9 @@ struct {
unsigned int version;
StringView notes;
} constexpr version_notes[] = { {
0,
"» kak_* appearing in shell arguments will be added to the environment\n"
}, {
20240518,
"» Fix tests failing on some platforms\n"
}, {

View File

@ -141,30 +141,35 @@ Shell spawn_shell(const char* shell, StringView cmdline,
}
template<typename GetValue>
Vector<String> generate_env(StringView cmdline, const Context& context, GetValue&& get_value)
Vector<String> generate_env(StringView cmdline, ConstArrayView<String> params, const Context& context, GetValue&& get_value)
{
static const Regex re(R"(\bkak_(quoted_)?(\w+)\b)");
Vector<String> env;
for (auto&& match : RegexIterator{cmdline.begin(), cmdline.end(), re})
{
StringView name{match[2].first, match[2].second};
StringView shell_name{match[0].first, match[0].second};
auto match_name = [&](const String& s) {
return s.substr(0_byte, shell_name.length()) == shell_name and
s.substr(shell_name.length(), 1_byte) == "=";
};
if (any_of(env, match_name))
continue;
try
auto add_matches = [&](StringView s) {
for (auto&& match : RegexIterator{s.begin(), s.end(), re})
{
StringView quoted{match[1].first, match[1].second};
Quoting quoting = match[1].matched ? Quoting::Shell : Quoting::Raw;
env.push_back(format("kak_{}{}={}", quoted, name, get_value(name, quoting)));
} catch (runtime_error&) {}
}
StringView name{match[2].first, match[2].second};
StringView shell_name{match[0].first, match[0].second};
auto match_name = [&](const String& s) {
return s.substr(0_byte, shell_name.length()) == shell_name and
s.substr(shell_name.length(), 1_byte) == "=";
};
if (any_of(env, match_name))
continue;
try
{
StringView quoted{match[1].first, match[1].second};
Quoting quoting = match[1].matched ? Quoting::Shell : Quoting::Raw;
env.push_back(format("kak_{}{}={}", quoted, name, get_value(name, quoting)));
} catch (runtime_error&) {}
}
};
add_matches(cmdline);
for (auto&& param : params)
add_matches(param);
return env;
}
@ -265,13 +270,13 @@ std::pair<String, int> ShellManager::eval(
const DebugFlags debug_flags = context.options()["debug"].get<DebugFlags>();
const bool profile = debug_flags & DebugFlags::Profile;
if (debug_flags & DebugFlags::Shell)
write_to_debug_buffer(format("shell:\n{}\n----\n", cmdline));
write_to_debug_buffer(format("shell:\n{}\n----\nargs: {}\n----\n", cmdline, join(shell_context.params | transform(shell_quote), ' ')));
auto start_time = profile ? Clock::now() : Clock::time_point{};
Optional<CommandFifos> command_fifos;
auto kak_env = generate_env(cmdline, context, [&](StringView name, Quoting quoting) {
auto kak_env = generate_env(cmdline, shell_context.params, context, [&](StringView name, Quoting quoting) {
if (name == "command_fifo" or name == "response_fifo")
{
if (not command_fifos)
@ -377,7 +382,7 @@ std::pair<String, int> ShellManager::eval(
Shell ShellManager::spawn(StringView cmdline, const Context& context,
bool open_stdin, const ShellContext& shell_context)
{
auto kak_env = generate_env(cmdline, context, [&](StringView name, Quoting quoting) {
auto kak_env = generate_env(cmdline, shell_context.params, context, [&](StringView name, Quoting quoting) {
if (auto it = shell_context.env_vars.find(name); it != shell_context.env_vars.end())
return it->value;
return join(get_val(name, context) | transform(quoter(quoting)), ' ', false);