Merge remote-tracking branch 'krobelus/bracketed-paste'
This commit is contained in:
commit
f05ab99d4d
|
@ -60,6 +60,9 @@ Client::Client(std::unique_ptr<UserInterface>&& ui,
|
||||||
else
|
else
|
||||||
m_pending_keys.push_back(key);
|
m_pending_keys.push_back(key);
|
||||||
});
|
});
|
||||||
|
m_ui->set_on_paste([this](StringView content) {
|
||||||
|
context().input_handler().paste(content);
|
||||||
|
});
|
||||||
|
|
||||||
m_window->hooks().run_hook(Hook::WinDisplay, m_window->buffer().name(), context());
|
m_window->hooks().run_hook(Hook::WinDisplay, m_window->buffer().name(), context());
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ public:
|
||||||
InputMode& operator=(const InputMode&) = delete;
|
InputMode& operator=(const InputMode&) = delete;
|
||||||
|
|
||||||
void handle_key(Key key) { RefPtr<InputMode> keep_alive{this}; on_key(key); }
|
void handle_key(Key key) { RefPtr<InputMode> keep_alive{this}; on_key(key); }
|
||||||
|
virtual void paste(StringView content);
|
||||||
|
|
||||||
virtual void on_enabled() {}
|
virtual void on_enabled() {}
|
||||||
virtual void on_disabled(bool temporary) {}
|
virtual void on_disabled(bool temporary) {}
|
||||||
|
@ -71,6 +72,32 @@ private:
|
||||||
InputHandler& m_input_handler;
|
InputHandler& m_input_handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void InputMode::paste(StringView content)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Buffer& buffer = context().buffer();
|
||||||
|
const bool linewise = not content.empty() and content.back() == '\n';
|
||||||
|
ScopedEdition edition{context()};
|
||||||
|
ScopedSelectionEdition selection_edition{context()};
|
||||||
|
context().selections().for_each([&buffer, content=std::move(content), linewise]
|
||||||
|
(size_t index, Selection& sel) {
|
||||||
|
auto& min = sel.min();
|
||||||
|
auto& max = sel.max();
|
||||||
|
BufferRange range =
|
||||||
|
buffer.insert(paste_pos(buffer, min, max, PasteMode::Insert, linewise), content);
|
||||||
|
min = range.begin;
|
||||||
|
max = range.end > range.begin ? buffer.char_prev(range.end) : range.begin;
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
catch (Kakoune::runtime_error& error)
|
||||||
|
{
|
||||||
|
write_to_debug_buffer(format("Error: {}", error.what()));
|
||||||
|
context().print_status({error.what().str(), context().faces()["Error"] });
|
||||||
|
context().hooks().run_hook(Hook::RuntimeError, error.what(), context());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace InputModes
|
namespace InputModes
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -1015,6 +1042,17 @@ public:
|
||||||
m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context()));
|
m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void paste(StringView content) override
|
||||||
|
{
|
||||||
|
m_line_editor.insert(content);
|
||||||
|
clear_completions();
|
||||||
|
m_refresh_completion_pending = true;
|
||||||
|
display();
|
||||||
|
m_line_changed = true;
|
||||||
|
if (not (context().flags() & Context::Flags::Draft))
|
||||||
|
m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context()));
|
||||||
|
}
|
||||||
|
|
||||||
void set_prompt_face(Face face)
|
void set_prompt_face(Face face)
|
||||||
{
|
{
|
||||||
if (face != m_prompt_face)
|
if (face != m_prompt_face)
|
||||||
|
@ -1433,6 +1471,12 @@ public:
|
||||||
m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context()));
|
m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void paste(StringView content) override
|
||||||
|
{
|
||||||
|
insert(ConstArrayView{content});
|
||||||
|
m_idle_timer.set_next_date(Clock::now() + get_idle_timeout(context()));
|
||||||
|
}
|
||||||
|
|
||||||
DisplayLine mode_line() const override
|
DisplayLine mode_line() const override
|
||||||
{
|
{
|
||||||
auto num_sel = context().selections().size();
|
auto num_sel = context().selections().size();
|
||||||
|
@ -1462,7 +1506,8 @@ private:
|
||||||
selections.sort_and_merge_overlapping();
|
selections.sort_and_merge_overlapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
void insert(ConstArrayView<String> strings)
|
template<typename S>
|
||||||
|
void insert(ConstArrayView<S> strings)
|
||||||
{
|
{
|
||||||
m_completer.try_accept();
|
m_completer.try_accept();
|
||||||
context().selections().for_each([strings, &buffer=context().buffer()]
|
context().selections().for_each([strings, &buffer=context().buffer()]
|
||||||
|
@ -1474,7 +1519,7 @@ private:
|
||||||
void insert(Codepoint key)
|
void insert(Codepoint key)
|
||||||
{
|
{
|
||||||
String str{key};
|
String str{key};
|
||||||
insert(str);
|
insert(ConstArrayView{str});
|
||||||
context().hooks().run_hook(Hook::InsertChar, str, context());
|
context().hooks().run_hook(Hook::InsertChar, str, context());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1644,6 +1689,11 @@ void InputHandler::repeat_last_insert()
|
||||||
kak_assert(dynamic_cast<InputModes::Normal*>(¤t_mode()) != nullptr);
|
kak_assert(dynamic_cast<InputModes::Normal*>(¤t_mode()) != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputHandler::paste(StringView content)
|
||||||
|
{
|
||||||
|
current_mode().paste(content);
|
||||||
|
}
|
||||||
|
|
||||||
void InputHandler::prompt(StringView prompt, String initstr, String emptystr,
|
void InputHandler::prompt(StringView prompt, String initstr, String emptystr,
|
||||||
Face prompt_face, PromptFlags flags, char history_register,
|
Face prompt_face, PromptFlags flags, char history_register,
|
||||||
PromptCompleter completer, PromptCallback callback)
|
PromptCompleter completer, PromptCallback callback)
|
||||||
|
|
|
@ -72,6 +72,8 @@ public:
|
||||||
void insert(InsertMode mode, int count);
|
void insert(InsertMode mode, int count);
|
||||||
// repeat last insert mode key sequence
|
// repeat last insert mode key sequence
|
||||||
void repeat_last_insert();
|
void repeat_last_insert();
|
||||||
|
// insert a string without affecting the mode stack
|
||||||
|
void paste(StringView content);
|
||||||
|
|
||||||
// enter prompt mode, callback is called on each change,
|
// enter prompt mode, callback is called on each change,
|
||||||
// abort or validation with corresponding PromptEvent value
|
// abort or validation with corresponding PromptEvent value
|
||||||
|
|
|
@ -211,6 +211,11 @@ void JsonUI::set_on_key(OnKeyCallback callback)
|
||||||
m_on_key = std::move(callback);
|
m_on_key = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JsonUI::set_on_paste(OnPasteCallback callback)
|
||||||
|
{
|
||||||
|
m_on_paste = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
void JsonUI::eval_json(const Value& json)
|
void JsonUI::eval_json(const Value& json)
|
||||||
{
|
{
|
||||||
if (not json.is_a<JsonObject>())
|
if (not json.is_a<JsonObject>())
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
|
|
||||||
DisplayCoord dimensions() override;
|
DisplayCoord dimensions() override;
|
||||||
void set_on_key(OnKeyCallback callback) override;
|
void set_on_key(OnKeyCallback callback) override;
|
||||||
|
void set_on_paste(OnPasteCallback callback) override;
|
||||||
void set_ui_options(const Options& options) override;
|
void set_ui_options(const Options& options) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -54,6 +55,7 @@ private:
|
||||||
|
|
||||||
FDWatcher m_stdin_watcher;
|
FDWatcher m_stdin_watcher;
|
||||||
OnKeyCallback m_on_key;
|
OnKeyCallback m_on_key;
|
||||||
|
OnPasteCallback m_on_paste;
|
||||||
Vector<Key, MemoryDomain::Client> m_pending_keys;
|
Vector<Key, MemoryDomain::Client> m_pending_keys;
|
||||||
DisplayCoord m_dimensions;
|
DisplayCoord m_dimensions;
|
||||||
String m_requests;
|
String m_requests;
|
||||||
|
|
|
@ -626,6 +626,7 @@ std::unique_ptr<UserInterface> make_ui(UIType ui_type)
|
||||||
void set_cursor(CursorMode, DisplayCoord) override {}
|
void set_cursor(CursorMode, DisplayCoord) override {}
|
||||||
void refresh(bool) override {}
|
void refresh(bool) override {}
|
||||||
void set_on_key(OnKeyCallback) override {}
|
void set_on_key(OnKeyCallback) override {}
|
||||||
|
void set_on_paste(OnPasteCallback) override {}
|
||||||
void set_ui_options(const Options&) override {}
|
void set_ui_options(const Options&) override {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -657,13 +657,6 @@ void change(Context& context, NormalParams params)
|
||||||
enter_insert_mode<InsertMode::Replace>(context, params);
|
enter_insert_mode<InsertMode::Replace>(context, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class PasteMode
|
|
||||||
{
|
|
||||||
Append,
|
|
||||||
Insert,
|
|
||||||
Replace
|
|
||||||
};
|
|
||||||
|
|
||||||
BufferCoord paste_pos(Buffer& buffer, BufferCoord min, BufferCoord max, PasteMode mode, bool linewise)
|
BufferCoord paste_pos(Buffer& buffer, BufferCoord min, BufferCoord max, PasteMode mode, bool linewise)
|
||||||
{
|
{
|
||||||
switch (mode)
|
switch (mode)
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
namespace Kakoune
|
namespace Kakoune
|
||||||
{
|
{
|
||||||
|
|
||||||
|
class Buffer;
|
||||||
class Context;
|
class Context;
|
||||||
|
|
||||||
struct no_selections_remaining : runtime_error
|
struct no_selections_remaining : runtime_error
|
||||||
|
@ -40,6 +41,15 @@ struct KeyInfo
|
||||||
String build_autoinfo_for_mapping(const Context& context, KeymapMode mode,
|
String build_autoinfo_for_mapping(const Context& context, KeymapMode mode,
|
||||||
ConstArrayView<KeyInfo> built_ins);
|
ConstArrayView<KeyInfo> built_ins);
|
||||||
|
|
||||||
|
enum class PasteMode
|
||||||
|
{
|
||||||
|
Append,
|
||||||
|
Insert,
|
||||||
|
Replace
|
||||||
|
};
|
||||||
|
|
||||||
|
BufferCoord paste_pos(Buffer& buffer, BufferCoord min, BufferCoord max, PasteMode mode, bool linewise);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // normal_hh_INCLUDED
|
#endif // normal_hh_INCLUDED
|
||||||
|
|
|
@ -43,6 +43,7 @@ enum class MessageType : uint8_t
|
||||||
SetOptions,
|
SetOptions,
|
||||||
Exit,
|
Exit,
|
||||||
Key,
|
Key,
|
||||||
|
Paste,
|
||||||
};
|
};
|
||||||
|
|
||||||
class MsgWriter
|
class MsgWriter
|
||||||
|
@ -413,6 +414,9 @@ public:
|
||||||
void set_on_key(OnKeyCallback callback) override
|
void set_on_key(OnKeyCallback callback) override
|
||||||
{ m_on_key = std::move(callback); }
|
{ m_on_key = std::move(callback); }
|
||||||
|
|
||||||
|
void set_on_paste(OnPasteCallback callback) override
|
||||||
|
{ m_on_paste = std::move(callback); }
|
||||||
|
|
||||||
void set_ui_options(const Options& options) override;
|
void set_ui_options(const Options& options) override;
|
||||||
|
|
||||||
void exit(int status);
|
void exit(int status);
|
||||||
|
@ -430,6 +434,7 @@ private:
|
||||||
MsgReader m_reader;
|
MsgReader m_reader;
|
||||||
DisplayCoord m_dimensions;
|
DisplayCoord m_dimensions;
|
||||||
OnKeyCallback m_on_key;
|
OnKeyCallback m_on_key;
|
||||||
|
OnPasteCallback m_on_paste;
|
||||||
RemoteBuffer m_send_buffer;
|
RemoteBuffer m_send_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -479,17 +484,25 @@ RemoteUI::RemoteUI(int socket, DisplayCoord dimensions)
|
||||||
if (not m_reader.ready())
|
if (not m_reader.ready())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (m_reader.type() != MessageType::Key)
|
if (m_reader.type() == MessageType::Key)
|
||||||
|
{
|
||||||
|
auto key = m_reader.read<Key>();
|
||||||
|
m_reader.reset();
|
||||||
|
if (key.modifiers == Key::Modifiers::Resize)
|
||||||
|
m_dimensions = key.coord();
|
||||||
|
m_on_key(key);
|
||||||
|
}
|
||||||
|
else if (m_reader.type() == MessageType::Paste)
|
||||||
|
{
|
||||||
|
auto content = m_reader.read<String>();
|
||||||
|
m_reader.reset();
|
||||||
|
m_on_paste(content);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
m_socket_watcher.close_fd();
|
m_socket_watcher.close_fd();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto key = m_reader.read<Key>();
|
|
||||||
m_reader.reset();
|
|
||||||
if (key.modifiers == Key::Modifiers::Resize)
|
|
||||||
m_dimensions = key.coord();
|
|
||||||
m_on_key(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const disconnected& err)
|
catch (const disconnected& err)
|
||||||
|
@ -660,6 +673,11 @@ RemoteClient::RemoteClient(StringView session, StringView name, std::unique_ptr<
|
||||||
msg.write(key);
|
msg.write(key);
|
||||||
m_socket_watcher->events() |= FdEvents::Write;
|
m_socket_watcher->events() |= FdEvents::Write;
|
||||||
});
|
});
|
||||||
|
m_ui->set_on_paste([this](StringView content){
|
||||||
|
MsgWriter msg(m_send_buffer, MessageType::Paste);
|
||||||
|
msg.write(content);
|
||||||
|
m_socket_watcher->events() |= FdEvents::Write;
|
||||||
|
});
|
||||||
|
|
||||||
m_socket_watcher.reset(new FDWatcher{sock, FdEvents::Read | FdEvents::Write, EventMode::Urgent,
|
m_socket_watcher.reset(new FDWatcher{sock, FdEvents::Read | FdEvents::Write, EventMode::Urgent,
|
||||||
[this, reader = MsgReader{}](FDWatcher& watcher, FdEvents events, EventMode) mutable {
|
[this, reader = MsgReader{}](FDWatcher& watcher, FdEvents events, EventMode) mutable {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "terminal_ui.hh"
|
#include "terminal_ui.hh"
|
||||||
|
|
||||||
|
#include "buffer_utils.hh"
|
||||||
#include "display_buffer.hh"
|
#include "display_buffer.hh"
|
||||||
#include "event_manager.hh"
|
#include "event_manager.hh"
|
||||||
#include "exception.hh"
|
#include "exception.hh"
|
||||||
|
@ -683,12 +684,16 @@ Optional<Key> TerminalUI::get_next_key()
|
||||||
return resize(dimensions());
|
return resize(dimensions());
|
||||||
}
|
}
|
||||||
|
|
||||||
static auto get_char = []() -> Optional<unsigned char> {
|
static auto get_char = [this]() -> Optional<unsigned char> {
|
||||||
if (not fd_readable(STDIN_FILENO))
|
if (not fd_readable(STDIN_FILENO))
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (unsigned char c = 0; read(STDIN_FILENO, &c, 1) == 1)
|
if (unsigned char c = 0; read(STDIN_FILENO, &c, 1) == 1)
|
||||||
|
{
|
||||||
|
if (m_paste_buffer)
|
||||||
|
m_paste_buffer->push_back(c);
|
||||||
return c;
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
stdin_closed = 1;
|
stdin_closed = 1;
|
||||||
return {};
|
return {};
|
||||||
|
@ -700,7 +705,7 @@ Optional<Key> TerminalUI::get_next_key()
|
||||||
|
|
||||||
static constexpr auto control = [](char c) { return c & 037; };
|
static constexpr auto control = [](char c) { return c & 037; };
|
||||||
|
|
||||||
auto convert = [this](Codepoint c) -> Codepoint {
|
static auto convert = [this](Codepoint c) -> Codepoint {
|
||||||
if (c == control('m') or c == control('j'))
|
if (c == control('m') or c == control('j'))
|
||||||
return Key::Return;
|
return Key::Return;
|
||||||
if (c == control('i'))
|
if (c == control('i'))
|
||||||
|
@ -715,7 +720,7 @@ Optional<Key> TerminalUI::get_next_key()
|
||||||
return Key::Escape;
|
return Key::Escape;
|
||||||
return c;
|
return c;
|
||||||
};
|
};
|
||||||
auto parse_key = [&convert](unsigned char c) -> Key {
|
static auto parse_key = [](unsigned char c) -> Key {
|
||||||
if (Codepoint cp = convert(c); cp > 255)
|
if (Codepoint cp = convert(c); cp > 255)
|
||||||
return Key{cp};
|
return Key{cp};
|
||||||
// Special case: you can type NUL with Ctrl-2 or Ctrl-Shift-2 or
|
// Special case: you can type NUL with Ctrl-2 or Ctrl-Shift-2 or
|
||||||
|
@ -743,7 +748,7 @@ Optional<Key> TerminalUI::get_next_key()
|
||||||
return Key{utf8::codepoint(CharIterator{c}, Sentinel{})};
|
return Key{utf8::codepoint(CharIterator{c}, Sentinel{})};
|
||||||
};
|
};
|
||||||
|
|
||||||
auto parse_mask = [](int mask) {
|
static auto parse_mask = [](int mask) {
|
||||||
Key::Modifiers mod = Key::Modifiers::None;
|
Key::Modifiers mod = Key::Modifiers::None;
|
||||||
if (mask & 1)
|
if (mask & 1)
|
||||||
mod |= Key::Modifiers::Shift;
|
mod |= Key::Modifiers::Shift;
|
||||||
|
@ -754,7 +759,16 @@ Optional<Key> TerminalUI::get_next_key()
|
||||||
return mod;
|
return mod;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto parse_csi = [this, &convert, &parse_mask]() -> Optional<Key> {
|
enum class PasteEvent { Begin, End };
|
||||||
|
struct KeyOrPasteEvent {
|
||||||
|
KeyOrPasteEvent() = default;
|
||||||
|
KeyOrPasteEvent(Key key) : key(key) {}
|
||||||
|
KeyOrPasteEvent(Optional<Key> key) : key(key) {}
|
||||||
|
KeyOrPasteEvent(PasteEvent paste) : paste(paste) {}
|
||||||
|
const Optional<Key> key;
|
||||||
|
const Optional<PasteEvent> paste;
|
||||||
|
};
|
||||||
|
auto parse_csi = [this]() -> KeyOrPasteEvent {
|
||||||
auto next_char = [] { return get_char().value_or((unsigned char)0xff); };
|
auto next_char = [] { return get_char().value_or((unsigned char)0xff); };
|
||||||
int params[16][4] = {};
|
int params[16][4] = {};
|
||||||
auto c = next_char();
|
auto c = next_char();
|
||||||
|
@ -819,7 +833,7 @@ Optional<Key> TerminalUI::get_next_key()
|
||||||
{
|
{
|
||||||
if (params[0][0] == 2026)
|
if (params[0][0] == 2026)
|
||||||
m_synchronized.supported = (params[1][0] == 1 or params[1][0] == 2);
|
m_synchronized.supported = (params[1][0] == 1 or params[1][0] == 2);
|
||||||
return {Key::Invalid};
|
return Key{Key::Invalid};
|
||||||
}
|
}
|
||||||
switch (params[0][0])
|
switch (params[0][0])
|
||||||
{
|
{
|
||||||
|
@ -863,6 +877,10 @@ Optional<Key> TerminalUI::get_next_key()
|
||||||
return Key{Key::Modifiers::Shift, Key::F7 + params[0][0] - 31}; // rxvt style
|
return Key{Key::Modifiers::Shift, Key::F7 + params[0][0] - 31}; // rxvt style
|
||||||
case 33: case 34:
|
case 33: case 34:
|
||||||
return Key{Key::Modifiers::Shift, Key::F9 + params[0][0] - 33}; // rxvt style
|
return Key{Key::Modifiers::Shift, Key::F9 + params[0][0] - 33}; // rxvt style
|
||||||
|
case 200:
|
||||||
|
return PasteEvent::Begin;
|
||||||
|
case 201:
|
||||||
|
return PasteEvent::End;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
case 'u':
|
case 'u':
|
||||||
|
@ -899,7 +917,7 @@ Optional<Key> TerminalUI::get_next_key()
|
||||||
return {};
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
auto parse_ss3 = [&parse_mask]() -> Optional<Key> {
|
static auto parse_ss3 = []() -> Optional<Key> {
|
||||||
int raw_mask = 0;
|
int raw_mask = 0;
|
||||||
char code = '0';
|
char code = '0';
|
||||||
do {
|
do {
|
||||||
|
@ -944,17 +962,40 @@ Optional<Key> TerminalUI::get_next_key()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (m_paste_buffer)
|
||||||
|
{
|
||||||
|
if (*c == 27 and get_char() == '[' and parse_csi().paste == PasteEvent::End)
|
||||||
|
{
|
||||||
|
m_paste_buffer->resize(m_paste_buffer->length() - "\033[201~"_str.length(), '\0');
|
||||||
|
m_on_paste(*m_paste_buffer);
|
||||||
|
m_paste_buffer.reset();
|
||||||
|
}
|
||||||
|
return get_next_key();
|
||||||
|
}
|
||||||
|
|
||||||
if (*c != 27)
|
if (*c != 27)
|
||||||
return parse_key(*c);
|
return parse_key(*c);
|
||||||
|
|
||||||
if (auto next = get_char())
|
if (auto next = get_char())
|
||||||
{
|
{
|
||||||
if (*next == '[') // potential CSI
|
|
||||||
return parse_csi().value_or(alt('['));
|
|
||||||
if (*next == 'O') // potential SS3
|
if (*next == 'O') // potential SS3
|
||||||
return parse_ss3().value_or(alt('O'));
|
return parse_ss3().value_or(alt('O'));
|
||||||
return alt(parse_key(*next));
|
if (*next != '[')
|
||||||
|
return alt(parse_key(*next));
|
||||||
|
// potential CSI
|
||||||
|
KeyOrPasteEvent csi = parse_csi();
|
||||||
|
if (csi.paste == PasteEvent::Begin)
|
||||||
|
{
|
||||||
|
m_paste_buffer = String{};
|
||||||
|
return get_next_key();
|
||||||
|
}
|
||||||
|
if (csi.paste == PasteEvent::End) // Unmatched bracketed paste sequence.
|
||||||
|
return {};
|
||||||
|
if (csi.key)
|
||||||
|
return *csi.key;
|
||||||
|
return alt('[');
|
||||||
}
|
}
|
||||||
|
|
||||||
return Key{Key::Escape};
|
return Key{Key::Escape};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1395,6 +1436,11 @@ void TerminalUI::set_on_key(OnKeyCallback callback)
|
||||||
EventManager::instance().force_signal(0);
|
EventManager::instance().force_signal(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TerminalUI::set_on_paste(OnPasteCallback callback)
|
||||||
|
{
|
||||||
|
m_on_paste = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
DisplayCoord TerminalUI::dimensions()
|
DisplayCoord TerminalUI::dimensions()
|
||||||
{
|
{
|
||||||
return m_dimensions;
|
return m_dimensions;
|
||||||
|
@ -1422,6 +1468,7 @@ void TerminalUI::setup_terminal()
|
||||||
"\033[?25l" // hide cursor
|
"\033[?25l" // hide cursor
|
||||||
"\033=" // set application keypad mode, so the keypad keys send unique codes
|
"\033=" // set application keypad mode, so the keypad keys send unique codes
|
||||||
"\033[?2026$p" // query support for synchronize output
|
"\033[?2026$p" // query support for synchronize output
|
||||||
|
"\033[?2004h" // force enable bracketed-paste events
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1435,6 +1482,7 @@ void TerminalUI::restore_terminal()
|
||||||
"\033[>4;0m"
|
"\033[>4;0m"
|
||||||
"\033[?1004l"
|
"\033[?1004l"
|
||||||
"\033[?1049l"
|
"\033[?1049l"
|
||||||
|
"\033[?2004l"
|
||||||
"\033[m" // set the terminal output back to default colours and style
|
"\033[m" // set the terminal output back to default colours and style
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ public:
|
||||||
|
|
||||||
DisplayCoord dimensions() override;
|
DisplayCoord dimensions() override;
|
||||||
void set_on_key(OnKeyCallback callback) override;
|
void set_on_key(OnKeyCallback callback) override;
|
||||||
|
void set_on_paste(OnPasteCallback callback) override;
|
||||||
void set_ui_options(const Options& options) override;
|
void set_ui_options(const Options& options) override;
|
||||||
|
|
||||||
static void setup_terminal();
|
static void setup_terminal();
|
||||||
|
@ -137,6 +138,8 @@ private:
|
||||||
|
|
||||||
FDWatcher m_stdin_watcher;
|
FDWatcher m_stdin_watcher;
|
||||||
OnKeyCallback m_on_key;
|
OnKeyCallback m_on_key;
|
||||||
|
OnPasteCallback m_on_paste;
|
||||||
|
Optional<String> m_paste_buffer;
|
||||||
|
|
||||||
bool m_status_on_top = false;
|
bool m_status_on_top = false;
|
||||||
ConstArrayView<StringView> m_assistant;
|
ConstArrayView<StringView> m_assistant;
|
||||||
|
|
|
@ -43,6 +43,7 @@ enum class CursorMode
|
||||||
};
|
};
|
||||||
|
|
||||||
using OnKeyCallback = std::function<void(Key key)>;
|
using OnKeyCallback = std::function<void(Key key)>;
|
||||||
|
using OnPasteCallback = std::function<void(StringView content)>;
|
||||||
|
|
||||||
class UserInterface
|
class UserInterface
|
||||||
{
|
{
|
||||||
|
@ -78,6 +79,7 @@ public:
|
||||||
virtual void refresh(bool force) = 0;
|
virtual void refresh(bool force) = 0;
|
||||||
|
|
||||||
virtual void set_on_key(OnKeyCallback callback) = 0;
|
virtual void set_on_key(OnKeyCallback callback) = 0;
|
||||||
|
virtual void set_on_paste(OnPasteCallback callback) = 0;
|
||||||
|
|
||||||
using Options = HashMap<String, String, MemoryDomain::Options>;
|
using Options = HashMap<String, String, MemoryDomain::Options>;
|
||||||
virtual void set_ui_options(const Options& options) = 0;
|
virtual void set_ui_options(const Options& options) = 0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user