diff --git a/CMakeLists.txt b/CMakeLists.txt
index 577ac12..dfcec19 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -117,8 +117,8 @@ configure_file(
set(XEUS_OCTAVE_SRC
src/xoctave_interpreter.cpp
src/xoctave_interpreter.hpp
- src/input.cpp
- src/input.hpp
+ src/io.cpp
+ src/io.hpp
src/toolkits/plotly.cpp
src/toolkits/plotly.hpp
src/toolkits/notebook.cpp
diff --git a/src/input.cpp b/src/io.cpp
similarity index 55%
rename from src/input.cpp
rename to src/io.cpp
index 059c0ad..bd8ff83 100644
--- a/src/input.cpp
+++ b/src/io.cpp
@@ -17,7 +17,7 @@
* along with xeus-octave. If not, see .
*/
-#include "input.hpp"
+#include "io.hpp"
#include
@@ -55,31 +55,59 @@ void _reset_command_editor();
} // namespace
-void input::set_command_editor(octave::command_editor& n) {
+void input::override(input& n) {
_set_command_editor(n);
}
-void input::reset_command_editor() {
+
+void input::restore() {
_reset_command_editor();
}
+input::input(std::function callback) : m_callback(std::move(callback)) {}
+
std::string input::do_readline(const std::string& prompt, bool&) {
- auto& interpreter = dynamic_cast(xeus::get_interpreter());
+ return m_callback(prompt);
+}
- if (interpreter.m_allow_stdin) {
- // Print any output if needed
- interpreter.do_print_output();
+void output::override(std::ostream& stream, output& buf) {
+ // Backup previous buffer
+ buf.p_oldbuf = stream.rdbuf();
- // Perform the read request
- auto input = xeus::blocking_input_request(prompt, false);
+ stream.rdbuf(&buf);
+}
- // Print newline
- std::cout << std::endl;
- interpreter.do_print_output();
+void output::restore(std::ostream& stream, output& buf) {
+ stream.rdbuf(buf.p_oldbuf);
+}
- return input;
+output::output(std::function callback)
+ : m_callback(std::move(callback)) {
+}
+
+output::int_type output::overflow(output::int_type c) {
+ std::lock_guard lock(m_mutex);
+ // Called for each output character.
+ if (!traits_type::eq_int_type(c, traits_type::eof())) {
+ m_output.push_back(traits_type::to_char_type(c));
}
+ return c;
+}
- throw std::runtime_error("This frontend does not support input requests");
+std::streamsize output::xsputn(const char* s, std::streamsize count) {
+ std::lock_guard lock(m_mutex);
+ // Called for a string of characters.
+ m_output.append(s, count);
+ return count;
+}
+
+output::int_type output::sync() {
+ std::lock_guard lock(m_mutex);
+ // Called in case of flush.
+ if (!m_output.empty()) {
+ m_callback(m_output);
+ m_output.clear();
+ }
+ return 0;
}
} // namespace xoctave
\ No newline at end of file
diff --git a/src/input.hpp b/src/io.hpp
similarity index 68%
rename from src/input.hpp
rename to src/io.hpp
index 703ad6d..f729cbf 100644
--- a/src/input.hpp
+++ b/src/io.hpp
@@ -17,8 +17,12 @@
* along with xeus-octave. If not, see .
*/
-#ifndef INPUT_H
-#define INPUT_H
+#ifndef IO_H
+#define IO_H
+
+#include
+#include
+#include
#include "octave/cmd-edit.h"
@@ -26,8 +30,10 @@ namespace xoctave {
class input : public octave::command_editor {
public:
- static void set_command_editor(octave::command_editor &n);
- static void reset_command_editor();
+ input(std::function callback);
+
+ static void override(input &n);
+ static void restore();
std::string do_readline(const std::string &prompt, bool &) override;
void do_set_input_stream(FILE *) override {}
@@ -43,6 +49,28 @@ class input : public octave::command_editor {
void do_insert_text(const std::string &) override {}
void do_newline(void) override {}
void do_accept_line(void) override {}
+
+private:
+ std::function m_callback;
+};
+
+class output : public std::streambuf {
+public:
+ output(std::function callback);
+
+ static void override(std::ostream &, output &);
+ static void restore(std::ostream &, output &);
+
+protected:
+ int_type overflow(int_type c) override;
+ std::streamsize xsputn(const char *s, std::streamsize count) override;
+ int_type sync() override;
+
+ std::function m_callback;
+ std::string m_output;
+ std::mutex m_mutex;
+
+ std::streambuf *p_oldbuf;
};
} // namespace xoctave
diff --git a/src/xoctave_interpreter.cpp b/src/xoctave_interpreter.cpp
index 4a71918..465d20b 100644
--- a/src/xoctave_interpreter.cpp
+++ b/src/xoctave_interpreter.cpp
@@ -48,7 +48,7 @@
#include
#include "config.h"
-#include "input.hpp"
+#include "io.hpp"
#include "toolkits/notebook.hpp"
#include "toolkits/plotly.hpp"
#include "xeus/xinterpreter.hpp"
@@ -60,31 +60,6 @@ using namespace octave;
namespace xoctave {
-void xoctave_interpreter::do_print_output(bool drawnow) {
- bool draw = drawnow && (buf_stderr.str().length() ||
- buf_stdout.str().length());
-
- // Print output if necessary
- if (!buf_stderr.str().empty()) {
- publish_stream("stderr", buf_stderr.str());
-
- // Clear stream
- buf_stderr.str("");
- buf_stderr.clear();
- }
-
- if (!buf_stdout.str().empty()) {
- publish_stream("stdout", buf_stdout.str());
-
- // Clear stream
- buf_stdout.str("");
- buf_stdout.clear();
- }
-
- if (draw)
- octave::feval("drawnow");
-}
-
void xoctave_interpreter::publish_stream(const std::string& name, const std::string& text) {
if (!m_silent)
xinterpreter::publish_stream(name, text);
@@ -112,6 +87,24 @@ void xoctave_interpreter::publish_execution_error(const std::string& ename,
xinterpreter::publish_execution_error(ename, evalue, trace_back);
}
+std::string xoctave_interpreter::blocking_input_request(const std::string& prompt, bool password) {
+ if (m_allow_stdin) {
+ // Register the input handler
+ std::string value;
+ register_input_handler([&value](const std::string& v) { value = v; });
+
+ // Send the input request
+ input_request(prompt, password);
+
+ // Remove input handler
+ register_input_handler(nullptr);
+
+ return value;
+ }
+
+ throw std::runtime_error("This frontend does not support input requests");
+}
+
nl::json xoctave_interpreter::execute_request_impl(int execution_counter,
const std::string& code,
bool silent,
@@ -160,7 +153,6 @@ nl::json xoctave_interpreter::execute_request_impl(int execution_counter,
if (stmt_list) {
interpreter.get_evaluator().eval(stmt_list, false);
- do_print_output();
} else if (str_parser.at_end_of_input()) {
exit_status = EOF;
break;
@@ -168,14 +160,12 @@ nl::json xoctave_interpreter::execute_request_impl(int execution_counter,
}
} catch (const interrupt_exception&) {
interpreter.recover_from_exception();
- do_print_output();
publish_execution_error("Interrupt exception", "Kernel was interrupted", std::vector());
result["status"] = "error";
} catch (const index_exception& e) {
error = e.message();
error += "\n" + e.stack_trace();
interpreter.recover_from_exception();
- do_print_output(false);
publish_execution_error("Index exception", error, std::vector());
result["status"] = "error";
} catch (const execution_exception& ee) {
@@ -183,12 +173,10 @@ nl::json xoctave_interpreter::execute_request_impl(int execution_counter,
error += "\n" + ee.stack_trace();
interpreter.get_error_system().save_exception(ee);
interpreter.recover_from_exception();
- do_print_output(false);
publish_execution_error("Execution exception", error, std::vector());
result["status"] = "error";
} catch (const std::bad_alloc&) {
interpreter.recover_from_exception();
- do_print_output(false);
publish_execution_error("Out of memory", "Trying to return to prompt", std::vector());
result["status"] = "error";
}
@@ -229,8 +217,10 @@ void xoctave_interpreter::configure_impl() {
octave::feval("graphics_toolkit", ovl("plotly"));
#endif
- // Override the default input system
- input::set_command_editor(input_handler);
+ // Override the default io system
+ input::override(m_stdin);
+ output::override(std::cout, m_stdout);
+ output::override(std::cerr, m_stderr);
// Create the xoctave package
auto xoctave_package = interpreter.get_cdef_manager().make_package("xoctave");
@@ -240,10 +230,6 @@ void xoctave_interpreter::configure_impl() {
// Register the xoctave package
interpreter.get_cdef_manager().register_package(xoctave_package);
-
- // Redirect output to string
- std::cout.rdbuf(buf_stdout.rdbuf());
- std::cerr.rdbuf(buf_stderr.rdbuf());
}
nl::json xoctave_interpreter::complete_request_impl(const std::string& code,
@@ -333,8 +319,11 @@ nl::json xoctave_interpreter::kernel_info_request_impl() {
}
void xoctave_interpreter::shutdown_request_impl() {
- // Recover the old input system before shutting down the interpreter
- input::reset_command_editor();
+ // Recover the old io system before shutting down the interpreter
+ input::restore();
+ output::restore(std::cout, m_stdout);
+ output::restore(std::cerr, m_stderr);
+
interpreter.shutdown();
#ifndef NDEBUG
diff --git a/src/xoctave_interpreter.hpp b/src/xoctave_interpreter.hpp
index 3c634e0..ad28528 100644
--- a/src/xoctave_interpreter.hpp
+++ b/src/xoctave_interpreter.hpp
@@ -23,11 +23,12 @@
#include
#include
+#include
#include
#include
#include
-#include "input.hpp"
+#include "io.hpp"
using nlohmann::json;
using xeus::xinterpreter;
@@ -35,8 +36,6 @@ using xeus::xinterpreter;
namespace xoctave {
class xoctave_interpreter : public xinterpreter {
- friend input;
-
private:
octave::interpreter interpreter;
@@ -64,8 +63,6 @@ class xoctave_interpreter : public xinterpreter {
void shutdown_request_impl() override;
public:
- void do_print_output(bool drawnow = true);
-
void publish_stream(const std::string& name, const std::string& text);
void display_data(json data, json metadata = json::object(), json transient = json::object());
void update_display_data(json data, json metadata = json::object(), json transient = json::object());
@@ -73,13 +70,15 @@ class xoctave_interpreter : public xinterpreter {
void publish_execution_error(const std::string& ename,
const std::string& evalue,
const std::vector& trace_back);
+ std::string blocking_input_request(const std::string& prompt, bool password);
private:
std::string get_symbol(const std::string& code, int cursor_pos) const;
json get_help_for_symbol(const std::string& symbol);
- std::stringstream buf_stdout, buf_stderr;
- input input_handler;
+ output m_stdout{std::bind(&xoctave_interpreter::publish_stream, this, "stdout", std::placeholders::_1)};
+ output m_stderr{std::bind(&xoctave_interpreter::publish_stream, this, "stderr", std::placeholders::_1)};
+ input m_stdin{std::bind(&xoctave_interpreter::blocking_input_request, this, std::placeholders::_1, false)};
bool m_silent, m_allow_stdin;
};