mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 02:12:09 -05:00
LibLine: Handle Ctrl-C and Ctrl-D in a way similar to other line editors
Makes C-c print "^C" and continue prompting on a new line. Also fixes a problem where an interrupted get_line() would need more read()'s than required to update the display.
This commit is contained in:
parent
c8cf465174
commit
238e87de4e
4 changed files with 81 additions and 38 deletions
|
@ -407,6 +407,55 @@ void Editor::initialize()
|
|||
m_initialized = true;
|
||||
}
|
||||
|
||||
void Editor::interrupted()
|
||||
{
|
||||
if (!m_is_editing)
|
||||
return;
|
||||
|
||||
m_was_interrupted = true;
|
||||
handle_interrupt_event();
|
||||
if (!m_finish)
|
||||
return;
|
||||
|
||||
m_finish = false;
|
||||
reposition_cursor(true);
|
||||
if (m_suggestion_display->cleanup())
|
||||
reposition_cursor();
|
||||
cleanup();
|
||||
fprintf(stderr, "\n");
|
||||
fflush(stderr);
|
||||
m_buffer.clear();
|
||||
m_is_editing = false;
|
||||
restore();
|
||||
m_notifier->set_event_mask(Core::Notifier::None);
|
||||
deferred_invoke([this](auto&) {
|
||||
remove_child(*m_notifier);
|
||||
m_notifier = nullptr;
|
||||
Core::EventLoop::current().quit(Retry);
|
||||
});
|
||||
}
|
||||
|
||||
void Editor::really_quit_event_loop()
|
||||
{
|
||||
m_finish = false;
|
||||
reposition_cursor(true);
|
||||
fprintf(stderr, "\n");
|
||||
fflush(stderr);
|
||||
auto string = line();
|
||||
m_buffer.clear();
|
||||
m_is_editing = false;
|
||||
restore();
|
||||
|
||||
m_returned_line = string;
|
||||
|
||||
m_notifier->set_event_mask(Core::Notifier::None);
|
||||
deferred_invoke([this](auto&) {
|
||||
remove_child(*m_notifier);
|
||||
m_notifier = nullptr;
|
||||
Core::EventLoop::current().quit(Exit);
|
||||
});
|
||||
}
|
||||
|
||||
auto Editor::get_line(const String& prompt) -> Result<String, Editor::Error>
|
||||
{
|
||||
initialize();
|
||||
|
@ -454,8 +503,9 @@ auto Editor::get_line(const String& prompt) -> Result<String, Editor::Error>
|
|||
add_child(*m_notifier);
|
||||
|
||||
m_notifier->on_ready_to_read = [&] {
|
||||
if (m_was_interrupted)
|
||||
if (m_was_interrupted) {
|
||||
handle_interrupt_event();
|
||||
}
|
||||
|
||||
handle_read_event();
|
||||
|
||||
|
@ -464,28 +514,12 @@ auto Editor::get_line(const String& prompt) -> Result<String, Editor::Error>
|
|||
|
||||
refresh_display();
|
||||
|
||||
if (m_finish) {
|
||||
m_finish = false;
|
||||
reposition_cursor(true);
|
||||
fprintf(stderr, "\n");
|
||||
fflush(stderr);
|
||||
auto string = line();
|
||||
m_buffer.clear();
|
||||
m_is_editing = false;
|
||||
restore();
|
||||
|
||||
m_returned_line = string;
|
||||
|
||||
m_notifier->set_event_mask(Core::Notifier::None);
|
||||
deferred_invoke([this](auto&) {
|
||||
remove_child(*m_notifier);
|
||||
m_notifier = nullptr;
|
||||
Core::EventLoop::current().quit(0);
|
||||
});
|
||||
}
|
||||
if (m_finish)
|
||||
really_quit_event_loop();
|
||||
};
|
||||
|
||||
loop.exec();
|
||||
if (loop.exec() == Retry)
|
||||
return get_line(prompt);
|
||||
|
||||
return m_input_error.has_value() ? Result<String, Editor::Error> { m_input_error.value() } : Result<String, Editor::Error> { m_returned_line };
|
||||
}
|
||||
|
@ -512,17 +546,29 @@ void Editor::handle_interrupt_event()
|
|||
{
|
||||
m_was_interrupted = false;
|
||||
|
||||
if (!m_buffer.is_empty())
|
||||
fprintf(stderr, "^C");
|
||||
auto cb = m_key_callbacks.get(ctrl('C'));
|
||||
if (cb.has_value()) {
|
||||
if (!cb.value()->callback(*this)) {
|
||||
// Oh well.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer.clear();
|
||||
m_cursor = 0;
|
||||
if (!m_buffer.is_empty()) {
|
||||
fprintf(stderr, "^C");
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
if (on_interrupt_handled)
|
||||
on_interrupt_handled();
|
||||
|
||||
m_refresh_needed = true;
|
||||
refresh_display();
|
||||
if (m_buffer.is_empty())
|
||||
return;
|
||||
|
||||
m_buffer.clear();
|
||||
m_cursor = 0;
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
void Editor::handle_read_event()
|
||||
|
|
|
@ -196,13 +196,7 @@ public:
|
|||
|
||||
// FIXME: we will have to kindly ask our instantiators to set our signal handlers,
|
||||
// since we can not do this cleanly ourselves. (signal() limitation: cannot give member functions)
|
||||
void interrupted()
|
||||
{
|
||||
if (m_is_editing) {
|
||||
m_was_interrupted = true;
|
||||
handle_interrupt_event();
|
||||
}
|
||||
}
|
||||
void interrupted();
|
||||
void resized()
|
||||
{
|
||||
m_was_resized = true;
|
||||
|
@ -277,6 +271,11 @@ private:
|
|||
|
||||
static VTState actual_rendered_string_length_step(StringMetrics&, size_t& length, u32, u32, VTState);
|
||||
|
||||
enum LoopExitCode {
|
||||
Exit = 0,
|
||||
Retry
|
||||
};
|
||||
|
||||
// ^Core::Object
|
||||
virtual void save_to(JsonObject&) override;
|
||||
|
||||
|
@ -336,6 +335,7 @@ private:
|
|||
|
||||
void refresh_display();
|
||||
void cleanup();
|
||||
void really_quit_event_loop();
|
||||
|
||||
void restore()
|
||||
{
|
||||
|
|
|
@ -168,6 +168,7 @@ void Editor::finish_edit()
|
|||
if (!m_always_refresh) {
|
||||
m_input_error = Error::Eof;
|
||||
finish();
|
||||
really_quit_event_loop();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -238,10 +238,6 @@ int main(int argc, char** argv)
|
|||
return 1;
|
||||
}
|
||||
|
||||
editor->on_interrupt_handled = [&] {
|
||||
editor->finish();
|
||||
};
|
||||
|
||||
shell->add_child(*editor);
|
||||
|
||||
Core::EventLoop::current().post_event(*shell, make<Core::CustomEvent>(Shell::ShellEventType::ReadLine));
|
||||
|
|
Loading…
Add table
Reference in a new issue