| #include "Console.h" | |
| #include "Interpreter.h" | |
| #include "ColumnFormatter.h" | |
| #include <iostream> | |
| #include <QKeyEvent> | |
| #include <QFont> | |
| #include "Utils.h" | |
| const QString Console::PROMPT = ">>> "; | |
| const QString Console::MULTILINE_PROMPT = "... "; | |
| const QColor Console::NORMAL_COLOR = QColor::fromRgbF( 0, 0, 0 ); | |
| const QColor Console::ERROR_COLOR = QColor::fromRgbF( 1.0, 0, 0 ); | |
| const QColor Console::OUTPUT_COLOR = QColor::fromRgbF( 0, 0, 1.0 ); | |
| Console::Console( QWidget* parent ): | |
| QTextEdit( parent ), | |
| m_interpreter( new Interpreter ) | |
| { | |
| QFont font; | |
| font.setFamily("Courier New"); | |
| setFont(font); | |
| m_parseHelper.subscribe( this ); | |
| displayPrompt( ); | |
| } | |
| Console::~Console( ) | |
| { | |
| delete m_interpreter; | |
| } | |
| void Console::keyPressEvent( QKeyEvent* e ) | |
| { | |
| switch ( e->key() ) | |
| { | |
| case Qt::Key_Return: | |
| handleReturnKeyPress( ); | |
| return; | |
| case Qt::Key_Tab: | |
| autocomplete( ); | |
| return; | |
| case Qt::Key_Backspace: | |
| if ( ! canBackspace( ) ) | |
| return; | |
| break; | |
| case Qt::Key_Up: | |
| previousHistory( ); | |
| return; | |
| case Qt::Key_Down: | |
| nextHistory( ); | |
| return; | |
| case Qt::Key_Left: | |
| if ( ! canGoLeft( ) ) | |
| return; | |
| } | |
| QTextEdit::keyPressEvent( e ); | |
| } | |
| void Console::handleReturnKeyPress( ) | |
| { | |
| if ( ! cursorIsOnInputLine( ) ) | |
| { | |
| return; | |
| } | |
| QString line = getLine( ); | |
| m_parseHelper.process( line.toStdString( ) ); | |
| if ( m_parseHelper.buffered( ) ) | |
| { | |
| append(""); | |
| displayPrompt( ); | |
| } | |
| if ( line.size( ) ) | |
| { | |
| m_historyBuffer.push_back( line.toStdString( ) ); | |
| m_historyIt = m_historyBuffer.end(); | |
| } | |
| moveCursorToEnd( ); | |
| } | |
| void Console::parseEvent( const ParseMessage& message ) | |
| { | |
| // handle invalid user input | |
| if ( message.errorCode ) | |
| { | |
| setTextColor( ERROR_COLOR ); | |
| append(message.message.c_str()); | |
| setTextColor( NORMAL_COLOR ); | |
| append(""); | |
| displayPrompt( ); | |
| return; | |
| } | |
| // interpret valid user input | |
| int errorCode; | |
| std::string res; | |
| if ( message.message.size() ) | |
| res = m_interpreter->interpret( message.message, &errorCode ); | |
| if ( errorCode ) | |
| { | |
| setTextColor( ERROR_COLOR ); | |
| } | |
| else | |
| { | |
| setTextColor( OUTPUT_COLOR ); | |
| } | |
| if ( res.size( ) ) | |
| { | |
| append(res.c_str()); | |
| } | |
| setTextColor( NORMAL_COLOR ); | |
| // set up the next line on the console | |
| append(""); | |
| displayPrompt( ); | |
| } | |
| QString Console::getLine( ) | |
| { | |
| QTextCursor cursor = textCursor(); | |
| cursor.movePosition( QTextCursor::StartOfLine ); | |
| cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, Console::PROMPT.size( ) ); | |
| cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor ); | |
| QString line = cursor.selectedText( ); | |
| cursor.clearSelection( ); | |
| return line; | |
| } | |
| bool Console::cursorIsOnInputLine( ) | |
| { | |
| int cursorBlock = textCursor( ).blockNumber( ); | |
| QTextCursor bottomCursor = textCursor( ); | |
| bottomCursor.movePosition( QTextCursor::End ); | |
| int bottomBlock = bottomCursor.blockNumber( ); | |
| return ( cursorBlock == bottomBlock ); | |
| } | |
| bool Console::inputLineIsEmpty( ) | |
| { | |
| QTextCursor bottomCursor = textCursor( ); | |
| bottomCursor.movePosition( QTextCursor::End ); | |
| int col = bottomCursor.columnNumber( ); | |
| return ( col == Console::PROMPT.size( ) ); | |
| } | |
| bool Console::canBackspace( ) | |
| { | |
| if ( ! cursorIsOnInputLine( ) ) | |
| { | |
| return false; | |
| } | |
| if ( inputLineIsEmpty( ) ) | |
| { | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool Console::canGoLeft( ) | |
| { | |
| if ( cursorIsOnInputLine( ) ) | |
| { | |
| QTextCursor bottomCursor = textCursor( ); | |
| int col = bottomCursor.columnNumber( ); | |
| return (col > Console::PROMPT.size( )); | |
| } | |
| return true; | |
| } | |
| void Console::displayPrompt( ) | |
| { | |
| QTextCursor cursor = textCursor(); | |
| cursor.movePosition( QTextCursor::End ); | |
| if ( m_parseHelper.buffered( ) ) | |
| { | |
| cursor.insertText( Console::MULTILINE_PROMPT ); | |
| } | |
| else | |
| { | |
| cursor.insertText( Console::PROMPT ); | |
| } | |
| cursor.movePosition( QTextCursor::EndOfLine ); | |
| } | |
| void Console::autocomplete( ) | |
| { | |
| if ( ! cursorIsOnInputLine( ) ) | |
| return; | |
| QString line = getLine( ); | |
| const std::list<std::string>& suggestions = | |
| m_interpreter->suggest( line.toStdString( ) ); | |
| if (suggestions.size() == 1) | |
| { | |
| line = suggestions.back().c_str(); | |
| } | |
| else | |
| { | |
| // try to complete to longest common prefix | |
| std::string prefix = | |
| LongestCommonPrefix(suggestions.begin(), suggestions.end()); | |
| if (prefix.size() > line.size()) | |
| { | |
| line = prefix.c_str(); | |
| } | |
| else | |
| { | |
| ColumnFormatter fmt; | |
| fmt.setItems(suggestions.begin(), suggestions.end()); | |
| fmt.format(width() / 10); | |
| setTextColor( OUTPUT_COLOR ); | |
| const std::list<std::string>& formatted = fmt.formattedOutput(); | |
| for (std::list<std::string>::const_iterator it = formatted.begin(); | |
| it != formatted.end(); ++it) | |
| { | |
| append(it->c_str()); | |
| } | |
| setTextColor( NORMAL_COLOR ); | |
| } | |
| } | |
| // set up the next line on the console | |
| append(""); | |
| displayPrompt( ); | |
| moveCursorToEnd( ); | |
| QTextCursor cursor = textCursor(); | |
| cursor.insertText( line ); | |
| moveCursorToEnd( ); | |
| } | |
| void Console::previousHistory( ) | |
| { | |
| if ( ! cursorIsOnInputLine( ) ) | |
| return; | |
| if ( ! m_historyBuffer.size( ) ) | |
| return; | |
| QTextCursor cursor = textCursor(); | |
| cursor.movePosition( QTextCursor::StartOfLine ); | |
| cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, Console::PROMPT.size( ) ); | |
| cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor ); | |
| cursor.removeSelectedText( ); | |
| if ( m_historyIt != m_historyBuffer.begin( ) ) | |
| { | |
| --m_historyIt; | |
| } | |
| cursor.insertText( m_historyIt->c_str() ); | |
| } | |
| void Console::nextHistory( ) | |
| { | |
| if ( ! cursorIsOnInputLine( ) ) | |
| return; | |
| if ( ! m_historyBuffer.size( ) ) | |
| return; | |
| if ( m_historyIt == m_historyBuffer.end( ) ) | |
| { | |
| return; | |
| } | |
| QTextCursor cursor = textCursor(); | |
| cursor.movePosition( QTextCursor::StartOfLine ); | |
| cursor.movePosition( QTextCursor::Right, QTextCursor::MoveAnchor, Console::PROMPT.size( ) ); | |
| cursor.movePosition( QTextCursor::EndOfLine, QTextCursor::KeepAnchor ); | |
| cursor.removeSelectedText( ); | |
| ++m_historyIt; | |
| if ( m_historyIt == m_historyBuffer.end( ) ) | |
| { | |
| return; | |
| } | |
| cursor.insertText( m_historyIt->c_str() ); | |
| } | |
| void Console::moveCursorToEnd( ) | |
| { | |
| QTextCursor cursor = textCursor(); | |
| cursor.movePosition( QTextCursor::End ); | |
| setTextCursor( cursor ); | |
| } |