summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxime Coste <mawww@kakoune.org>2023-03-13 20:37:45 +1100
committerMaxime Coste <mawww@kakoune.org>2023-03-13 20:37:45 +1100
commitf05ab99d4d7322055f7eb9b9d86b529062885208 (patch)
treed7feff3cf4bef65b5979dbfeec267a22271848a2
parent38077ca8265358894fe831dd274f6f6eadbd7455 (diff)
parent1990a764e3d2ffa77931068f876cd49f76bd43f9 (diff)
Merge remote-tracking branch 'krobelus/bracketed-paste'
-rw-r--r--src/client.cc3
-rw-r--r--src/input_handler.cc54
-rw-r--r--src/input_handler.hh2
-rw-r--r--src/json_ui.cc5
-rw-r--r--src/json_ui.hh2
-rw-r--r--src/main.cc1
-rw-r--r--src/normal.cc7
-rw-r--r--src/normal.hh10
-rw-r--r--src/remote.cc32
-rw-r--r--src/terminal_ui.cc68
-rw-r--r--src/terminal_ui.hh3
-rw-r--r--src/user_interface.hh2
12 files changed, 163 insertions, 26 deletions
diff --git a/src/client.cc b/src/client.cc
index 3ddddd87..ec90982c 100644
--- a/src/client.cc
+++ b/src/client.cc
@@ -60,6 +60,9 @@ Client::Client(std::unique_ptr<UserInterface>&& ui,
else
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());
diff --git a/src/input_handler.cc b/src/input_handler.cc
index 69ef3385..877517e2 100644
--- a/src/input_handler.cc
+++ b/src/input_handler.cc
@@ -32,6 +32,7 @@ public:
InputMode& operator=(const InputMode&) = delete;
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_disabled(bool temporary) {}
@@ -71,6 +72,32 @@ private:
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
{
@@ -1015,6 +1042,17 @@ public:
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)
{
if (face != m_prompt_face)
@@ -1433,6 +1471,12 @@ public:
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
{
auto num_sel = context().selections().size();
@@ -1462,7 +1506,8 @@ private:
selections.sort_and_merge_overlapping();
}
- void insert(ConstArrayView<String> strings)
+ template<typename S>
+ void insert(ConstArrayView<S> strings)
{
m_completer.try_accept();
context().selections().for_each([strings, &buffer=context().buffer()]
@@ -1474,7 +1519,7 @@ private:
void insert(Codepoint key)
{
String str{key};
- insert(str);
+ insert(ConstArrayView{str});
context().hooks().run_hook(Hook::InsertChar, str, context());
}
@@ -1644,6 +1689,11 @@ void InputHandler::repeat_last_insert()
kak_assert(dynamic_cast<InputModes::Normal*>(&current_mode()) != nullptr);
}
+void InputHandler::paste(StringView content)
+{
+ current_mode().paste(content);
+}
+
void InputHandler::prompt(StringView prompt, String initstr, String emptystr,
Face prompt_face, PromptFlags flags, char history_register,
PromptCompleter completer, PromptCallback callback)
diff --git a/src/input_handler.hh b/src/input_handler.hh
index 5cb150b5..b7e53aaf 100644
--- a/src/input_handler.hh
+++ b/src/input_handler.hh
@@ -72,6 +72,8 @@ public:
void insert(InsertMode mode, int count);
// repeat last insert mode key sequence
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,
// abort or validation with corresponding PromptEvent value
diff --git a/src/json_ui.cc b/src/json_ui.cc
index 8f0af46d..1200ed60 100644
--- a/src/json_ui.cc
+++ b/src/json_ui.cc
@@ -211,6 +211,11 @@ void JsonUI::set_on_key(OnKeyCallback 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)
{
if (not json.is_a<JsonObject>())
diff --git a/src/json_ui.hh b/src/json_ui.hh
index 7b1abf44..545e4f35 100644
--- a/src/json_ui.hh
+++ b/src/json_ui.hh
@@ -46,6 +46,7 @@ public:
DisplayCoord dimensions() override;
void set_on_key(OnKeyCallback callback) override;
+ void set_on_paste(OnPasteCallback callback) override;
void set_ui_options(const Options& options) override;
private:
@@ -54,6 +55,7 @@ private:
FDWatcher m_stdin_watcher;
OnKeyCallback m_on_key;
+ OnPasteCallback m_on_paste;
Vector<Key, MemoryDomain::Client> m_pending_keys;
DisplayCoord m_dimensions;
String m_requests;
diff --git a/src/main.cc b/src/main.cc
index 8ce47d8e..fef83090 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -626,6 +626,7 @@ std::unique_ptr<UserInterface> make_ui(UIType ui_type)
void set_cursor(CursorMode, DisplayCoord) override {}
void refresh(bool) override {}
void set_on_key(OnKeyCallback) override {}
+ void set_on_paste(OnPasteCallback) override {}
void set_ui_options(const Options&) override {}
};
diff --git a/src/normal.cc b/src/normal.cc
index 7dc14382..e3fdee16 100644
--- a/src/normal.cc
+++ b/src/normal.cc
@@ -657,13 +657,6 @@ void change(Context& context, NormalParams 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)
{
switch (mode)
diff --git a/src/normal.hh b/src/normal.hh
index ad0f81be..080c3d01 100644
--- a/src/normal.hh
+++ b/src/normal.hh
@@ -10,6 +10,7 @@
namespace Kakoune
{
+class Buffer;
class Context;
struct no_selections_remaining : runtime_error
@@ -40,6 +41,15 @@ struct KeyInfo
String build_autoinfo_for_mapping(const Context& context, KeymapMode mode,
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
diff --git a/src/remote.cc b/src/remote.cc
index 50dda1b0..5b97f985 100644
--- a/src/remote.cc
+++ b/src/remote.cc
@@ -43,6 +43,7 @@ enum class MessageType : uint8_t
SetOptions,
Exit,
Key,
+ Paste,
};
class MsgWriter
@@ -413,6 +414,9 @@ public:
void set_on_key(OnKeyCallback callback) override
{ 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 exit(int status);
@@ -430,6 +434,7 @@ private:
MsgReader m_reader;
DisplayCoord m_dimensions;
OnKeyCallback m_on_key;
+ OnPasteCallback m_on_paste;
RemoteBuffer m_send_buffer;
};
@@ -479,17 +484,25 @@ RemoteUI::RemoteUI(int socket, DisplayCoord dimensions)
if (not m_reader.ready())
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();
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)
@@ -660,6 +673,11 @@ RemoteClient::RemoteClient(StringView session, StringView name, std::unique_ptr<
msg.write(key);
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,
[this, reader = MsgReader{}](FDWatcher& watcher, FdEvents events, EventMode) mutable {
diff --git a/src/terminal_ui.cc b/src/terminal_ui.cc
index 311c2527..612029e8 100644
--- a/src/terminal_ui.cc
+++ b/src/terminal_ui.cc
@@ -1,5 +1,6 @@
#include "terminal_ui.hh"
+#include "buffer_utils.hh"
#include "display_buffer.hh"
#include "event_manager.hh"
#include "exception.hh"
@@ -683,12 +684,16 @@ Optional<Key> TerminalUI::get_next_key()
return resize(dimensions());
}
- static auto get_char = []() -> Optional<unsigned char> {
+ static auto get_char = [this]() -> Optional<unsigned char> {
if (not fd_readable(STDIN_FILENO))
return {};
if (unsigned char c = 0; read(STDIN_FILENO, &c, 1) == 1)
+ {
+ if (m_paste_buffer)
+ m_paste_buffer->push_back(c);
return c;
+ }
stdin_closed = 1;
return {};
@@ -700,7 +705,7 @@ Optional<Key> TerminalUI::get_next_key()
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'))
return Key::Return;
if (c == control('i'))
@@ -715,7 +720,7 @@ Optional<Key> TerminalUI::get_next_key()
return Key::Escape;
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)
return Key{cp};
// 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{})};
};
- auto parse_mask = [](int mask) {
+ static auto parse_mask = [](int mask) {
Key::Modifiers mod = Key::Modifiers::None;
if (mask & 1)
mod |= Key::Modifiers::Shift;
@@ -754,7 +759,16 @@ Optional<Key> TerminalUI::get_next_key()
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); };
int params[16][4] = {};
auto c = next_char();
@@ -819,7 +833,7 @@ Optional<Key> TerminalUI::get_next_key()
{
if (params[0][0] == 2026)
m_synchronized.supported = (params[1][0] == 1 or params[1][0] == 2);
- return {Key::Invalid};
+ return Key{Key::Invalid};
}
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
case 33: case 34:
return Key{Key::Modifiers::Shift, Key::F9 + params[0][0] - 33}; // rxvt style
+ case 200:
+ return PasteEvent::Begin;
+ case 201:
+ return PasteEvent::End;
}
return {};
case 'u':
@@ -899,7 +917,7 @@ Optional<Key> TerminalUI::get_next_key()
return {};
};
- auto parse_ss3 = [&parse_mask]() -> Optional<Key> {
+ static auto parse_ss3 = []() -> Optional<Key> {
int raw_mask = 0;
char code = '0';
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)
return parse_key(*c);
if (auto next = get_char())
{
- if (*next == '[') // potential CSI
- return parse_csi().value_or(alt('['));
if (*next == 'O') // potential SS3
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};
}
@@ -1395,6 +1436,11 @@ void TerminalUI::set_on_key(OnKeyCallback callback)
EventManager::instance().force_signal(0);
}
+void TerminalUI::set_on_paste(OnPasteCallback callback)
+{
+ m_on_paste = std::move(callback);
+}
+
DisplayCoord TerminalUI::dimensions()
{
return m_dimensions;
@@ -1422,6 +1468,7 @@ void TerminalUI::setup_terminal()
"\033[?25l" // hide cursor
"\033=" // set application keypad mode, so the keypad keys send unique codes
"\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[?1004l"
"\033[?1049l"
+ "\033[?2004l"
"\033[m" // set the terminal output back to default colours and style
);
}
diff --git a/src/terminal_ui.hh b/src/terminal_ui.hh
index fe3f1d6b..dd4ef870 100644
--- a/src/terminal_ui.hh
+++ b/src/terminal_ui.hh
@@ -55,6 +55,7 @@ public:
DisplayCoord dimensions() override;
void set_on_key(OnKeyCallback callback) override;
+ void set_on_paste(OnPasteCallback callback) override;
void set_ui_options(const Options& options) override;
static void setup_terminal();
@@ -137,6 +138,8 @@ private:
FDWatcher m_stdin_watcher;
OnKeyCallback m_on_key;
+ OnPasteCallback m_on_paste;
+ Optional<String> m_paste_buffer;
bool m_status_on_top = false;
ConstArrayView<StringView> m_assistant;
diff --git a/src/user_interface.hh b/src/user_interface.hh
index 5bab3e89..643f0d1d 100644
--- a/src/user_interface.hh
+++ b/src/user_interface.hh
@@ -43,6 +43,7 @@ enum class CursorMode
};
using OnKeyCallback = std::function<void(Key key)>;
+using OnPasteCallback = std::function<void(StringView content)>;
class UserInterface
{
@@ -78,6 +79,7 @@ public:
virtual void refresh(bool force) = 0;
virtual void set_on_key(OnKeyCallback callback) = 0;
+ virtual void set_on_paste(OnPasteCallback callback) = 0;
using Options = HashMap<String, String, MemoryDomain::Options>;
virtual void set_ui_options(const Options& options) = 0;