Add support for recursive expansions with %exp{...}

%exp{...} just expands its content the same way double quoted strings
do, but using a named expansion type makes it possible to use the
more quoting mechanism to avoid quoting hell.
This commit is contained in:
Maxime Coste 2023-05-04 12:49:50 +10:00
parent a4918f934c
commit 04780b235b
4 changed files with 21 additions and 9 deletions

View File

@ -427,3 +427,8 @@ everywhere.
A value described as a "quoted list" will follow the rules of Kakoune string A value described as a "quoted list" will follow the rules of Kakoune string
quoting (See <<command-parsing#,`:doc command-parsing`>>). An "unquoted list" quoting (See <<command-parsing#,`:doc command-parsing`>>). An "unquoted list"
cannot contain any special characters that would require quoting. cannot contain any special characters that would require quoting.
== Recursive Expansions
Expansions with the type `exp` expand their content, the same way doubly
quoted strings do.

View File

@ -220,6 +220,8 @@ Token::Type token_type(StringView type_name, bool throw_on_invalid)
return Token::Type::ArgExpand; return Token::Type::ArgExpand;
else if (type_name == "file") else if (type_name == "file")
return Token::Type::FileExpand; return Token::Type::FileExpand;
else if (type_name == "exp")
return Token::Type::Expand;
else if (throw_on_invalid) else if (throw_on_invalid)
throw parse_error{format("unknown expand '{}'", type_name)}; throw parse_error{format("unknown expand '{}'", type_name)};
else else
@ -396,7 +398,7 @@ void expand_token(Token&& token, const Context& context, const ShellContext& she
} }
case Token::Type::FileExpand: case Token::Type::FileExpand:
return set_target(read_file(content)); return set_target(read_file(content));
case Token::Type::RawEval: case Token::Type::Expand:
return set_target(expand(content, context, shell_context)); return set_target(expand(content, context, shell_context));
case Token::Type::Raw: case Token::Type::Raw:
case Token::Type::RawQuoted: case Token::Type::RawQuoted:
@ -425,7 +427,7 @@ Optional<Token> CommandParser::read_token(bool throw_on_unterminated)
ParseResult quoted = parse_quoted(m_state, c); ParseResult quoted = parse_quoted(m_state, c);
if (throw_on_unterminated and not quoted.terminated) if (throw_on_unterminated and not quoted.terminated)
throw parse_error{format("unterminated string {0}...{0}", c)}; throw parse_error{format("unterminated string {0}...{0}", c)};
return Token{c == '"' ? Token::Type::RawEval return Token{c == '"' ? Token::Type::Expand
: Token::Type::RawQuoted, : Token::Type::RawQuoted,
start - line.begin(), std::move(quoted.content), start - line.begin(), std::move(quoted.content),
quoted.terminated}; quoted.terminated};
@ -613,7 +615,7 @@ Optional<CommandInfo> CommandManager::command_info(const Context& context, Strin
{ {
if (it->type == Token::Type::Raw or if (it->type == Token::Type::Raw or
it->type == Token::Type::RawQuoted or it->type == Token::Type::RawQuoted or
it->type == Token::Type::RawEval) it->type == Token::Type::Expand)
params.push_back(it->content); params.push_back(it->content);
} }
String helpstr = cmd->value.helper(context, params); String helpstr = cmd->value.helper(context, params);
@ -692,9 +694,9 @@ static Completions complete_expansion(const Context& context, CompletionFlags fl
} }
} }
static Completions complete_raw_eval(const Context& context, CompletionFlags flags, static Completions complete_expand(const Context& context, CompletionFlags flags,
StringView prefix, ByteCount start, StringView prefix, ByteCount start,
ByteCount cursor_pos, ByteCount pos_in_token) ByteCount cursor_pos, ByteCount pos_in_token)
{ {
ParseState state{prefix, prefix.begin()}; ParseState state{prefix, prefix.begin()};
while (state) while (state)
@ -849,8 +851,8 @@ Completions CommandManager::complete(const Context& context,
return offset_pos(requote(command.completer(context, flags, params, index, pos_in_token), token.type), start); return offset_pos(requote(command.completer(context, flags, params, index, pos_in_token), token.type), start);
} }
case Token::Type::RawEval: case Token::Type::Expand:
return complete_raw_eval(context, flags, token.content, start, cursor_pos, pos_in_token); return complete_expand(context, flags, token.content, start, cursor_pos, pos_in_token);
default: default:
break; break;
} }

View File

@ -45,7 +45,7 @@ struct Token
{ {
Raw, Raw,
RawQuoted, RawQuoted,
RawEval, Expand,
ShellExpand, ShellExpand,
RegisterExpand, RegisterExpand,
OptionExpand, OptionExpand,

View File

@ -44,6 +44,11 @@ struct {
unsigned int version; unsigned int version;
StringView notes; StringView notes;
} constexpr version_notes[] = { { } constexpr version_notes[] = { {
0,
"» History is now stored linearly instead of in a tree\n"
"» {+u}%exp\\{...}{} expansions provide flexible quoting for expanded "
"strings (as double quoted strings)\n"
}, {
20221031, 20221031,
"» {+b}<esc>{} does not end macro recording anymore, use {+b}Q{}\n" "» {+b}<esc>{} does not end macro recording anymore, use {+b}Q{}\n"
"» pipe commands do not append final end-of-lines anymore\n" "» pipe commands do not append final end-of-lines anymore\n"