diff --git a/apps/code/Makefile b/apps/code/Makefile index 2f7448978..f2d18db06 100644 --- a/apps/code/Makefile +++ b/apps/code/Makefile @@ -8,7 +8,6 @@ app_objs += $(addprefix apps/code/,\ console_line_cell.o\ console_store.o\ editor_controller.o\ - executor_controller.o\ menu_controller.o\ program.o\ ) diff --git a/apps/code/base.de.i18n b/apps/code/base.de.i18n index 04c1c8871..49ef71391 100644 --- a/apps/code/base.de.i18n +++ b/apps/code/base.de.i18n @@ -1,3 +1,4 @@ Console = "Console" +ConsoleError = "Error" EditProgram = "Programm bearbeiten" ExecuteProgram = "Programm ausfuhren" diff --git a/apps/code/base.en.i18n b/apps/code/base.en.i18n index c45470d9d..fdc231ce1 100644 --- a/apps/code/base.en.i18n +++ b/apps/code/base.en.i18n @@ -1,3 +1,4 @@ Console = "Console" +ConsoleError = "Error" EditProgram = "Edit program" ExecuteProgram = "Execute program" diff --git a/apps/code/base.es.i18n b/apps/code/base.es.i18n index fb36d39d4..3e2584359 100644 --- a/apps/code/base.es.i18n +++ b/apps/code/base.es.i18n @@ -1,3 +1,4 @@ Console = "Console" +ConsoleError = "Error" EditProgram = "Editar el programa" ExecuteProgram = "Ejecutar el programa" diff --git a/apps/code/base.fr.i18n b/apps/code/base.fr.i18n index b9a2ffe32..3542f462e 100644 --- a/apps/code/base.fr.i18n +++ b/apps/code/base.fr.i18n @@ -1,3 +1,4 @@ Console = "Console" +ConsoleError = "Error" EditProgram = "Editer le programme" ExecuteProgram = "Executer le programme" diff --git a/apps/code/base.pt.i18n b/apps/code/base.pt.i18n index 5d78fa2df..b34989fe3 100644 --- a/apps/code/base.pt.i18n +++ b/apps/code/base.pt.i18n @@ -1,3 +1,4 @@ Console = "Console" +ConsoleError = "Error" EditProgram = "Editar programa" ExecuteProgram = "Executar programa" diff --git a/apps/code/console_controller.cpp b/apps/code/console_controller.cpp index fb5a29e92..1ebe3867d 100644 --- a/apps/code/console_controller.cpp +++ b/apps/code/console_controller.cpp @@ -1,13 +1,61 @@ #include "console_controller.h" +#include + +extern "C" { +#include +#include "port.h" +#include "py/mphal.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "py/stackctrl.h" +} namespace Code { +/* mp_hal_stdout_tx_strn_cooked symbol required by micropython at printing + * needs to access information about where to print. This 'context' is provided + * by the global sCurrentConsoleStore that points to the console store. */ + +static ConsoleStore * sCurrentConsoleStore = nullptr; + +extern "C" +void mp_hal_stdout_tx_strn_cooked(const char * str, size_t len) { + assert(sCurrentConsoleStore != nullptr); + sCurrentConsoleStore->pushResult(str, len); +} + +mp_obj_t execute_from_str(const char *str) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_lexer_t *lex = mp_lexer_new_from_str_len(0, str, strlen(str), false); + mp_parse_tree_t pt = mp_parse(lex, MP_PARSE_SINGLE_INPUT); + mp_obj_t module_fun = mp_compile(&pt, lex->source_name, MP_EMIT_OPT_NONE, true); + mp_hal_set_interrupt_char((int)Ion::Keyboard::Key::A6); + mp_call_function_0(module_fun); + mp_hal_set_interrupt_char(-1); // Disable interrupt + nlr_pop(); + return 0; + } else { + // Uncaught exception + return (mp_obj_t) nlr.ret_val; + } +} + ConsoleController::ConsoleController(Responder * parentResponder) : ViewController(parentResponder), TextFieldDelegate(), + m_rowHeight(KDText::charSize(k_fontSize).height()), m_tableView(this, this, 0, 0), m_editCell(this, this) { + assert(sCurrentConsoleStore == nullptr); + sCurrentConsoleStore = &m_consoleStore; + initPython(); +} + +ConsoleController::~ConsoleController() { + stopPython(); } void ConsoleController::viewWillAppear() { @@ -23,7 +71,7 @@ int ConsoleController::numberOfRows() { } KDCoordinate ConsoleController::rowHeight(int j) { - return k_rowHeight; + return m_rowHeight; } KDCoordinate ConsoleController::cumulatedHeightFromIndex(int j) { @@ -82,9 +130,11 @@ bool ConsoleController::textFieldDidReceiveEvent(TextField * textField, Ion::Eve bool ConsoleController::textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) { m_consoleStore.pushCommand(text, strlen(text)); + executePython(text); textField->setText(""); m_tableView.reloadData(); m_tableView.scrollToCell(0, m_consoleStore.numberOfLines()); + m_editCell.setEditing(true); return true; } @@ -92,4 +142,26 @@ Toolbox * ConsoleController::toolboxForTextField(TextField * textFied) { return nullptr; } +void ConsoleController::initPython() { + mp_stack_set_limit(40000); + mp_port_init_stack_top(); + + m_pythonHeap = (char *)malloc(16384); + gc_init(m_pythonHeap, m_pythonHeap + 16384); + + mp_init(); +} + +void ConsoleController::executePython(const char * str) { + if (execute_from_str(str)) { + mp_hal_stdout_tx_strn_cooked(I18n::translate(I18n::Message::ConsoleError), 5); + } + m_consoleStore.deleteLastLineIfEmpty(); +} + +void ConsoleController::stopPython() { + free(m_pythonHeap); + sCurrentConsoleStore = nullptr; +} + } diff --git a/apps/code/console_controller.h b/apps/code/console_controller.h index a517a3a02..2e6f45d0e 100644 --- a/apps/code/console_controller.h +++ b/apps/code/console_controller.h @@ -12,6 +12,11 @@ namespace Code { class ConsoleController : public ViewController, public ListViewDataSource, public ScrollViewDataSource, public TextFieldDelegate { public: ConsoleController(Responder * parentResponder); + ~ConsoleController(); + ConsoleController(const ConsoleController& other) = delete; + ConsoleController(ConsoleController&& other) = delete; + ConsoleController operator=(const ConsoleController& other) = delete; + ConsoleController& operator=(ConsoleController&& other) = delete; // ViewController View * view() override { return &m_tableView; } @@ -34,15 +39,23 @@ public: bool textFieldDidFinishEditing(TextField * textField, const char * text, Ion::Events::Event event) override; Toolbox * toolboxForTextField(TextField * textFied) override; + // Python + void initPython(); + void executePython(const char * str); + void stopPython(); + + // Other + static constexpr KDText::FontSize k_fontSize = KDText::FontSize::Large; private: static constexpr int LineCellType = 0; static constexpr int EditCellType = 1; static constexpr int k_numberOfLineCells = 15; // May change depending on the height - static constexpr int k_rowHeight = 20; + int m_rowHeight; ConsoleStore m_consoleStore; TableView m_tableView; ConsoleLineCell m_cells[k_numberOfLineCells]; ConsoleEditCell m_editCell; + char * m_pythonHeap; }; } diff --git a/apps/code/console_edit_cell.cpp b/apps/code/console_edit_cell.cpp index ea44299c9..674ecbe1a 100644 --- a/apps/code/console_edit_cell.cpp +++ b/apps/code/console_edit_cell.cpp @@ -1,5 +1,7 @@ #include "console_edit_cell.h" +#include "console_controller.h" #include +#include #include namespace Code { @@ -7,21 +9,28 @@ namespace Code { ConsoleEditCell::ConsoleEditCell(Responder * parentResponder, TextFieldDelegate * delegate) : HighlightCell(), Responder(parentResponder), - m_textField(this, m_textBuffer, m_draftTextBuffer, TextField::maxBufferSize(), delegate) + m_promptView(ConsoleController::k_fontSize, I18n::Message::ConsolePrompt, 0, 0.5), + m_textField(this, m_textBuffer, m_draftTextBuffer, TextField::maxBufferSize(), delegate, true, ConsoleController::k_fontSize) { } int ConsoleEditCell::numberOfSubviews() const { - return 1; + return 2; } View * ConsoleEditCell::subviewAtIndex(int index) { - assert(index == 0); - return &m_textField; + assert(index == 0 || index ==1); + if (index == 0) { + return &m_promptView; + } else { + return &m_textField; + } } void ConsoleEditCell::layoutSubviews() { - m_textField.setFrame(bounds()); + KDSize chevronsSize = KDText::stringSize(I18n::translate(I18n::Message::ConsolePrompt), ConsoleController::k_fontSize); + m_promptView.setFrame(KDRect(KDPointZero, chevronsSize.width(), bounds().height())); + m_textField.setFrame(KDRect(KDPoint(chevronsSize.width(), KDCoordinate(0)), bounds().width() - chevronsSize.width(), bounds().height())); } void ConsoleEditCell::didBecomeFirstResponder() { diff --git a/apps/code/console_edit_cell.h b/apps/code/console_edit_cell.h index a68441b35..034202573 100644 --- a/apps/code/console_edit_cell.h +++ b/apps/code/console_edit_cell.h @@ -5,6 +5,8 @@ #include #include #include +#include +#include "console_line.h" namespace Code { @@ -26,6 +28,7 @@ public: private: char m_textBuffer[TextField::maxBufferSize()]; char m_draftTextBuffer[TextField::maxBufferSize()]; + MessageTextView m_promptView; TextField m_textField; }; diff --git a/apps/code/console_line_cell.cpp b/apps/code/console_line_cell.cpp index 873d0d387..733b00431 100644 --- a/apps/code/console_line_cell.cpp +++ b/apps/code/console_line_cell.cpp @@ -1,4 +1,8 @@ #include "console_line_cell.h" +#include "console_controller.h" +#include +#include +#include namespace Code { @@ -9,8 +13,14 @@ ConsoleLineCell::ConsoleLineCell() : } void ConsoleLineCell::drawRect(KDContext * ctx, KDRect rect) const { - ctx->fillRect(bounds(), KDColorRed); - ctx->drawString(m_line.text(), KDPointZero); + ctx->fillRect(bounds(), KDColorWhite); + if (m_line.type() == ConsoleLine::Type::Command) { + ctx->drawString(I18n::translate(I18n::Message::ConsolePrompt), KDPointZero, ConsoleController::k_fontSize); + KDCoordinate chevronsWidth = KDText::stringSize(I18n::translate(I18n::Message::ConsolePrompt), ConsoleController::k_fontSize).width(); + ctx->drawString(m_line.text(), KDPoint(chevronsWidth, KDCoordinate(0)), ConsoleController::k_fontSize); + } else { + ctx->drawString(m_line.text(), KDPointZero, ConsoleController::k_fontSize); + } } void ConsoleLineCell::setLine(ConsoleLine line) { diff --git a/apps/code/console_store.cpp b/apps/code/console_store.cpp index 6bb10523e..b6c3ad1ea 100644 --- a/apps/code/console_store.cpp +++ b/apps/code/console_store.cpp @@ -50,6 +50,15 @@ void ConsoleStore::pushResult(const char * text, size_t length) { push(ResultMarker, text, length); } + +void ConsoleStore::deleteLastLineIfEmpty() { + ConsoleLine lastLine = lineAtIndex(numberOfLines()-1); + char lastLineFirstChar = lastLine.text()[0]; + if (lastLineFirstChar == 0 || lastLineFirstChar == '\n') { + deleteLastLine(); + } +} + void ConsoleStore::push(const char marker, const char * text, size_t length) { // TODO: Verify that the text field does not accept texts that are bigger than // k_historySize, or put an alert message if the command is too big. @@ -88,14 +97,37 @@ void ConsoleStore::deleteFirstLine() { if (m_history[0] == 0) { return; } - int indexOfSecondLineMarker = 1; - while (m_history[indexOfSecondLineMarker] != 0) { - indexOfSecondLineMarker++; + int secondLineMarkerIndex = 1; + while (m_history[secondLineMarkerIndex] != 0) { + secondLineMarkerIndex++; } - indexOfSecondLineMarker++; - for (int i=0; i -#include "port.h" -#include "py/mphal.h" -#include "py/compile.h" -#include "py/runtime.h" -#include "py/gc.h" -#include "py/stackctrl.h" -} - -namespace Code { - -mp_obj_t execute_from_str(const char *str) { - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_lexer_t *lex = mp_lexer_new_from_str_len(0/*MP_QSTR_*/, str, strlen(str), false); - mp_parse_tree_t pt = mp_parse(lex, MP_PARSE_FILE_INPUT); - mp_obj_t module_fun = mp_compile(&pt, lex->source_name, MP_EMIT_OPT_NONE, false); - mp_hal_set_interrupt_char((int)Ion::Keyboard::Key::A6); - mp_call_function_0(module_fun); - mp_hal_set_interrupt_char(-1); // disable interrupt - nlr_pop(); - return 0; - } else { - // uncaught exception - return (mp_obj_t)nlr.ret_val; - } -} - -/* mp_hal_stdout_tx_strn_cooked symbol required by micropython at printing - * needs to access information about where to print (depending on the strings - * printed before). This 'context' is provided by the global sCurrentView that - * points to the content view only within the drawRect method (as runPython is - * called within drawRect). */ -static const ExecutorController::ContentView * sCurrentView = nullptr; - -extern "C" -void mp_hal_stdout_tx_strn_cooked(const char * str, size_t len) { - assert(sCurrentView != nullptr); - sCurrentView->print(str); -} - -ExecutorController::ContentView::ContentView(Program * program) : - View(), - m_program(program), - m_printLocation(KDPointZero) -{ -} - -void ExecutorController::ContentView::drawRect(KDContext * ctx, KDRect rect) const { - assert(ctx == KDIonContext::sharedContext()); - clearScreen(ctx); - - assert(sCurrentView == nullptr); - sCurrentView = this; - - // Reinitialize the print location - m_printLocation = KDPointZero; - runPython(); - - sCurrentView = nullptr; -} - -void ExecutorController::ContentView::print(const char * str) const { - KDContext * ctx = KDIonContext::sharedContext(); - m_printLocation = ctx->drawString(str, m_printLocation); - if (bounds().height() < m_printLocation.y()) { - clearScreen(ctx); - m_printLocation = KDPoint(m_printLocation.x(), 0); - } -} - -void ExecutorController::ContentView::runPython() const { - // Initialized stack limit - mp_stack_set_limit(40000); - - mp_port_init_stack_top(); - - char * pythonHeap = (char *)malloc(16384); - - gc_init(pythonHeap, pythonHeap + 16384); - - // Initialize interpreter - mp_init(); - - if (execute_from_str(m_program->readOnlyContent())) { - mp_hal_stdout_tx_strn_cooked("Error", 0); - } - - free(pythonHeap); -} - -void ExecutorController::ContentView::clearScreen(KDContext * ctx) const { - ctx->fillRect(bounds(), KDColorWhite); -} - -ExecutorController::ExecutorController(Program * program) : - ViewController(nullptr), - m_view(program) -{ -} - -View * ExecutorController::view() { - return &m_view; -} - -bool ExecutorController::handleEvent(Ion::Events::Event event) { - if (event == Ion::Events::OK) { - app()->dismissModalViewController(); - return true; - } - return false; -} - -} diff --git a/apps/code/executor_controller.h b/apps/code/executor_controller.h deleted file mode 100644 index a1345b8d3..000000000 --- a/apps/code/executor_controller.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef CODE_EXECUTOR_CONTROLLER_H -#define CODE_EXECUTOR_CONTROLLER_H - -#include -#include "program.h" - -namespace Code { - -class ExecutorController : public ViewController { -public: - ExecutorController(Program * program); - View * view() override; - bool handleEvent(Ion::Events::Event event) override; - class ContentView : public View { - public: - ContentView(Program * program); - void drawRect(KDContext * ctx, KDRect rect) const override; - void print(const char * str) const; - private: - void runPython() const; - void clearScreen(KDContext * ctx) const; - Program * m_program; - mutable KDPoint m_printLocation; - }; -private: - ContentView m_view; -}; - -} - -#endif - diff --git a/apps/code/menu_controller.cpp b/apps/code/menu_controller.cpp index 67cd8ecbc..a257dc084 100644 --- a/apps/code/menu_controller.cpp +++ b/apps/code/menu_controller.cpp @@ -8,7 +8,6 @@ MenuController::MenuController(Responder * parentResponder, Program * program) : ViewController(parentResponder), m_selectableTableView(this, this, 0, 1, Metric::CommonTopMargin, Metric::CommonRightMargin, Metric::CommonBottomMargin, Metric::CommonLeftMargin, this), m_editorController(program), - m_executorController(program), m_consoleController(parentResponder) { } @@ -23,7 +22,7 @@ void MenuController::didBecomeFirstResponder() { } bool MenuController::handleEvent(Ion::Events::Event event) { - ViewController * vc[3] = {&m_editorController, &m_executorController, &m_consoleController}; + ViewController * vc[2] = {&m_editorController, &m_consoleController}; if (event == Ion::Events::OK || event == Ion::Events::EXE) { app()->displayModalViewController(vc[selectedRow()], 0.5f, 0.5f); return true; @@ -52,7 +51,7 @@ KDCoordinate MenuController::cellHeight() { void MenuController::willDisplayCellForIndex(HighlightCell * cell, int index) { MessageTableCell * myCell = (MessageTableCell *)cell; - I18n::Message titles[k_totalNumberOfCells] = {I18n::Message::EditProgram, I18n::Message::ExecuteProgram, I18n::Message::Console}; + I18n::Message titles[k_totalNumberOfCells] = {I18n::Message::EditProgram, I18n::Message::Console}; // TODO: translate Console in the .i18n myCell->setMessage(titles[index]); } diff --git a/apps/code/menu_controller.h b/apps/code/menu_controller.h index c20ede7a2..be43ba2ae 100644 --- a/apps/code/menu_controller.h +++ b/apps/code/menu_controller.h @@ -4,7 +4,6 @@ #include #include "console_controller.h" #include "editor_controller.h" -#include "executor_controller.h" #include "program.h" namespace Code { @@ -21,11 +20,10 @@ public: int reusableCellCount() override; void willDisplayCellForIndex(HighlightCell * cell, int index) override; private: - constexpr static int k_totalNumberOfCells = 3; + constexpr static int k_totalNumberOfCells = 2; MessageTableCell m_cells[k_totalNumberOfCells]; SelectableTableView m_selectableTableView; EditorController m_editorController; - ExecutorController m_executorController; ConsoleController m_consoleController; }; diff --git a/apps/shared.universal.i18n b/apps/shared.universal.i18n index 7f8ffce98..38b794ea3 100644 --- a/apps/shared.universal.i18n +++ b/apps/shared.universal.i18n @@ -106,3 +106,4 @@ AtanhCommandWithArg = "atanh(x)" Prediction95CommandWithArg = "prediction95(p,n)" PredictionCommandWithArg = "prediction(p,n)" ConfidenceCommandWithArg = "confidence(f,n)" +ConsolePrompt = ">>> "