298 lines
6.9 KiB
C++
298 lines
6.9 KiB
C++
#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( );
|
|
|
|
#ifndef NDEBUG
|
|
std::cout << line.toStdString( ) << "\n";
|
|
#endif
|
|
|
|
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());
|
|
}
|
|
std::cout << width() << "\n";
|
|
setTextColor( NORMAL_COLOR );
|
|
}
|
|
}
|
|
|
|
// set up the next line on the console
|
|
append("");
|
|
displayPrompt( );
|
|
moveCursorToEnd( );
|
|
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 );
|
|
}
|