mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 18:02:05 -05:00
LibLine: Add bracketed paste mode support
This mode makes the editor insert all the "pasted" text into the buffer without interpreting it in any way.
This commit is contained in:
parent
e54d96d53e
commit
e318f12263
2 changed files with 64 additions and 7 deletions
|
@ -42,8 +42,15 @@ Configuration Configuration::from_config(const StringView& libname)
|
|||
// Read behaviour options.
|
||||
auto refresh = config_file->read_entry("behaviour", "refresh", "lazy");
|
||||
auto operation = config_file->read_entry("behaviour", "operation_mode");
|
||||
auto bracketed_paste = config_file->read_bool_entry("behaviour", "bracketed_paste", true);
|
||||
auto default_text_editor = config_file->read_entry("behaviour", "default_text_editor");
|
||||
|
||||
Configuration::Flags flags { Configuration::Flags::None };
|
||||
if (bracketed_paste)
|
||||
flags = static_cast<Flags>(flags | Configuration::Flags::BracketedPaste);
|
||||
|
||||
configuration.set(flags);
|
||||
|
||||
if (refresh.equals_ignoring_case("lazy"))
|
||||
configuration.set(Configuration::Lazy);
|
||||
else if (refresh.equals_ignoring_case("eager"))
|
||||
|
@ -650,6 +657,9 @@ auto Editor::get_line(const String& prompt) -> Result<String, Editor::Error>
|
|||
auto old_lines = m_num_lines;
|
||||
get_terminal_size();
|
||||
|
||||
if (m_configuration.enable_bracketed_paste)
|
||||
fprintf(stderr, "\x1b[?2004h");
|
||||
|
||||
if (m_num_columns != old_cols || m_num_lines != old_lines)
|
||||
m_refresh_needed = true;
|
||||
|
||||
|
@ -844,13 +854,8 @@ void Editor::handle_read_event()
|
|||
m_state = InputState::CSIExpectFinal;
|
||||
[[fallthrough]];
|
||||
case InputState::CSIExpectFinal: {
|
||||
m_state = InputState::Free;
|
||||
if (!(code_point >= 0x40 && code_point <= 0x7f)) {
|
||||
dbgln("LibLine: Invalid CSI: {:02x} ({:c})", code_point, code_point);
|
||||
continue;
|
||||
}
|
||||
csi_final = code_point;
|
||||
|
||||
m_state = m_previous_free_state;
|
||||
auto is_in_paste = m_state == InputState::Paste;
|
||||
for (auto& parameter : String::copy(csi_parameter_bytes).split(';')) {
|
||||
if (auto value = parameter.to_uint(); value.has_value())
|
||||
csi_parameters.append(value.value());
|
||||
|
@ -864,6 +869,25 @@ void Editor::handle_read_event()
|
|||
param2 = csi_parameters[1];
|
||||
unsigned modifiers = param2 ? param2 - 1 : 0;
|
||||
|
||||
if (is_in_paste && code_point != '~' && param1 != 201) {
|
||||
// The only valid escape to process in paste mode is the stop-paste sequence.
|
||||
// so treat everything else as part of the pasted data.
|
||||
insert('\x1b');
|
||||
insert('[');
|
||||
insert(StringView { csi_parameter_bytes.data(), csi_parameter_bytes.size() });
|
||||
insert(StringView { csi_intermediate_bytes.data(), csi_intermediate_bytes.size() });
|
||||
insert(code_point);
|
||||
continue;
|
||||
}
|
||||
if (!(code_point >= 0x40 && code_point <= 0x7f)) {
|
||||
dbgln("LibLine: Invalid CSI: {:02x} ({:c})", code_point, code_point);
|
||||
continue;
|
||||
}
|
||||
csi_final = code_point;
|
||||
csi_parameters.clear();
|
||||
csi_parameter_bytes.clear();
|
||||
csi_intermediate_bytes.clear();
|
||||
|
||||
if (csi_final == 'Z') {
|
||||
// 'reverse tab'
|
||||
reverse_tab = true;
|
||||
|
@ -905,6 +929,18 @@ void Editor::handle_read_event()
|
|||
m_search_offset = 0;
|
||||
continue;
|
||||
}
|
||||
if (m_configuration.enable_bracketed_paste) {
|
||||
// ^[[200~: start bracketed paste
|
||||
// ^[[201~: end bracketed paste
|
||||
if (!is_in_paste && param1 == 200) {
|
||||
m_state = InputState::Paste;
|
||||
continue;
|
||||
}
|
||||
if (is_in_paste && param1 == 201) {
|
||||
m_state = InputState::Free;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// ^[[5~: page up
|
||||
// ^[[6~: page down
|
||||
dbgln("LibLine: Unhandled '~': {}", param1);
|
||||
|
@ -920,7 +956,16 @@ void Editor::handle_read_event()
|
|||
// Verbatim mode will bypass all mechanisms and just insert the code point.
|
||||
insert(code_point);
|
||||
continue;
|
||||
case InputState::Paste:
|
||||
if (code_point == 27) {
|
||||
m_previous_free_state = InputState::Paste;
|
||||
m_state = InputState::GotEscape;
|
||||
continue;
|
||||
}
|
||||
insert(code_point);
|
||||
continue;
|
||||
case InputState::Free:
|
||||
m_previous_free_state = InputState::Free;
|
||||
if (code_point == 27) {
|
||||
m_callback_machine.key_pressed(*this, code_point);
|
||||
// Note that this should also deal with explicitly registered keys
|
||||
|
|
|
@ -61,6 +61,11 @@ struct Configuration {
|
|||
NoSignalHandlers,
|
||||
};
|
||||
|
||||
enum Flags : u32 {
|
||||
None = 0,
|
||||
BracketedPaste = 1,
|
||||
};
|
||||
|
||||
struct DefaultTextEditor {
|
||||
String command;
|
||||
};
|
||||
|
@ -81,6 +86,10 @@ struct Configuration {
|
|||
void set(SignalHandler mode) { m_signal_mode = mode; }
|
||||
void set(const KeyBinding& binding) { keybindings.append(binding); }
|
||||
void set(DefaultTextEditor editor) { m_default_text_editor = move(editor.command); }
|
||||
void set(Flags flags)
|
||||
{
|
||||
enable_bracketed_paste = flags & Flags::BracketedPaste;
|
||||
}
|
||||
|
||||
static Configuration from_config(const StringView& libname = "line");
|
||||
|
||||
|
@ -89,6 +98,7 @@ struct Configuration {
|
|||
OperationMode operation_mode { OperationMode::Unset };
|
||||
Vector<KeyBinding> keybindings;
|
||||
String m_default_text_editor {};
|
||||
bool enable_bracketed_paste { false };
|
||||
};
|
||||
|
||||
#define ENUMERATE_EDITOR_INTERNAL_FUNCTIONS(M) \
|
||||
|
@ -450,12 +460,14 @@ private:
|
|||
enum class InputState {
|
||||
Free,
|
||||
Verbatim,
|
||||
Paste,
|
||||
GotEscape,
|
||||
CSIExpectParameter,
|
||||
CSIExpectIntermediate,
|
||||
CSIExpectFinal,
|
||||
};
|
||||
InputState m_state { InputState::Free };
|
||||
InputState m_previous_free_state { InputState::Free };
|
||||
|
||||
struct Spans {
|
||||
HashMap<u32, HashMap<u32, Style>> m_spans_starting;
|
||||
|
|
Loading…
Add table
Reference in a new issue