1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-30 15:48:11 +00:00

Shell: Be more smart with pasted stuff

Shell can now use LibLine's `on_paste` hook to more intelligently escape
pasted data, with the following heuristics:
- If the current command is invalid, just pile the pasted string on top
- If the cursor is *after* a command node, escape the pasted data,
  whichever way yields a smaller encoding
- If the cursor is at the start of or in the middle of a command name,
  paste the data as-is, assuming that the user wants to paste code
- If the cursor is otherwise in some argument, escape the pasted data
  according to which kind of string the cursor is in the middle of
  (double-quoted, single-quoted or a simple bareword)
This commit is contained in:
Ali Mohammad Pur 2022-03-06 13:04:31 +03:30 committed by Andreas Kling
parent c4d9377477
commit a76730823a
3 changed files with 148 additions and 66 deletions

View file

@ -1168,6 +1168,8 @@ Shell::SpecialCharacterEscapeMode Shell::special_character_escape_mode(u32 code_
case '}':
case '&':
case ';':
case '?':
case '*':
case ' ':
if (mode == EscapeMode::SingleQuotedString || mode == EscapeMode::DoubleQuotedString)
return SpecialCharacterEscapeMode::Untouched;
@ -1184,76 +1186,82 @@ Shell::SpecialCharacterEscapeMode Shell::special_character_escape_mode(u32 code_
}
}
static String do_escape(Shell::EscapeMode escape_mode, auto& token)
{
StringBuilder builder;
for (auto c : token) {
static_assert(sizeof(c) == sizeof(u32) || sizeof(c) == sizeof(u8));
switch (Shell::special_character_escape_mode(c, escape_mode)) {
case Shell::SpecialCharacterEscapeMode::Untouched:
if constexpr (sizeof(c) == sizeof(u8))
builder.append(c);
else
builder.append(Utf32View { &c, 1 });
break;
case Shell::SpecialCharacterEscapeMode::Escaped:
if (escape_mode == Shell::EscapeMode::SingleQuotedString)
builder.append("'");
builder.append('\\');
builder.append(c);
if (escape_mode == Shell::EscapeMode::SingleQuotedString)
builder.append("'");
break;
case Shell::SpecialCharacterEscapeMode::QuotedAsEscape:
if (escape_mode == Shell::EscapeMode::SingleQuotedString)
builder.append("'");
if (escape_mode != Shell::EscapeMode::DoubleQuotedString)
builder.append("\"");
switch (c) {
case '\n':
builder.append(R"(\n)");
break;
case '\t':
builder.append(R"(\t)");
break;
case '\r':
builder.append(R"(\r)");
break;
default:
VERIFY_NOT_REACHED();
}
if (escape_mode != Shell::EscapeMode::DoubleQuotedString)
builder.append("\"");
if (escape_mode == Shell::EscapeMode::SingleQuotedString)
builder.append("'");
break;
case Shell::SpecialCharacterEscapeMode::QuotedAsHex:
if (escape_mode == Shell::EscapeMode::SingleQuotedString)
builder.append("'");
if (escape_mode != Shell::EscapeMode::DoubleQuotedString)
builder.append("\"");
if (c <= NumericLimits<u8>::max())
builder.appendff(R"(\x{:0>2x})", static_cast<u8>(c));
else
builder.appendff(R"(\u{:0>8x})", static_cast<u32>(c));
if (escape_mode != Shell::EscapeMode::DoubleQuotedString)
builder.append("\"");
if (escape_mode == Shell::EscapeMode::SingleQuotedString)
builder.append("'");
break;
}
}
return builder.build();
}
String Shell::escape_token(Utf32View token, EscapeMode escape_mode)
{
return do_escape(escape_mode, token);
}
String Shell::escape_token(StringView token, EscapeMode escape_mode)
{
auto do_escape = [escape_mode](auto& token) {
StringBuilder builder;
for (auto c : token) {
static_assert(sizeof(c) == sizeof(u32) || sizeof(c) == sizeof(u8));
switch (special_character_escape_mode(c, escape_mode)) {
case SpecialCharacterEscapeMode::Untouched:
if constexpr (sizeof(c) == sizeof(u8))
builder.append(c);
else
builder.append(Utf32View { &c, 1 });
break;
case SpecialCharacterEscapeMode::Escaped:
if (escape_mode == EscapeMode::SingleQuotedString)
builder.append("'");
builder.append('\\');
builder.append(c);
if (escape_mode == EscapeMode::SingleQuotedString)
builder.append("'");
break;
case SpecialCharacterEscapeMode::QuotedAsEscape:
if (escape_mode == EscapeMode::SingleQuotedString)
builder.append("'");
if (escape_mode != EscapeMode::DoubleQuotedString)
builder.append("\"");
switch (c) {
case '\n':
builder.append(R"(\n)");
break;
case '\t':
builder.append(R"(\t)");
break;
case '\r':
builder.append(R"(\r)");
break;
default:
VERIFY_NOT_REACHED();
}
if (escape_mode != EscapeMode::DoubleQuotedString)
builder.append("\"");
if (escape_mode == EscapeMode::SingleQuotedString)
builder.append("'");
break;
case SpecialCharacterEscapeMode::QuotedAsHex:
if (escape_mode == EscapeMode::SingleQuotedString)
builder.append("'");
if (escape_mode != EscapeMode::DoubleQuotedString)
builder.append("\"");
if (c <= NumericLimits<u8>::max())
builder.appendff(R"(\x{:0>2x})", static_cast<u8>(c));
else
builder.appendff(R"(\u{:0>8x})", static_cast<u32>(c));
if (escape_mode != EscapeMode::DoubleQuotedString)
builder.append("\"");
if (escape_mode == EscapeMode::SingleQuotedString)
builder.append("'");
break;
}
}
return builder.build();
};
Utf8View view { token };
if (view.validate())
return do_escape(view);
return do_escape(token);
return do_escape(escape_mode, view);
return do_escape(escape_mode, token);
}
String Shell::unescape_token(StringView token)