4c75f9e
/*
4c75f9e
    Copyright (c) 2017, Lukas Holecek <hluk@email.cz>
4c75f9e
4c75f9e
    This file is part of CopyQ.
4c75f9e
4c75f9e
    CopyQ is free software: you can redistribute it and/or modify
4c75f9e
    it under the terms of the GNU General Public License as published by
4c75f9e
    the Free Software Foundation, either version 3 of the License, or
4c75f9e
    (at your option) any later version.
4c75f9e
4c75f9e
    CopyQ is distributed in the hope that it will be useful,
4c75f9e
    but WITHOUT ANY WARRANTY; without even the implied warranty of
4c75f9e
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4c75f9e
    GNU General Public License for more details.
4c75f9e
4c75f9e
    You should have received a copy of the GNU General Public License
4c75f9e
    along with CopyQ.  If not, see <http://www.gnu.org/licenses/>.
4c75f9e
*/
4c75f9e
4c75f9e
#include "commandedit.h"
4c75f9e
#include "ui_commandedit.h"
4c75f9e
4c75f9e
#include "gui/commandsyntaxhighlighter.h"
4c75f9e
#include "gui/commandcompleter.h"
4c75f9e
4c75f9e
#include <QScriptEngine>
4c75f9e
#include <QTextBlock>
4c75f9e
#include <QTextCursor>
4c75f9e
#include <QTextDocument>
4c75f9e
4c75f9e
CommandEdit::CommandEdit(QWidget *parent)
4c75f9e
    : QWidget(parent)
4c75f9e
    , ui(new Ui::CommandEdit)
4c75f9e
{
4c75f9e
    ui->setupUi(this);
4c75f9e
    ui->labelErrors->hide();
4c75f9e
    ui->plainTextEditCommand->document()->setDefaultFont(commandFont());
4c75f9e
4c75f9e
    setFocusProxy(ui->plainTextEditCommand);
4c75f9e
4c75f9e
    installCommandSyntaxHighlighter(ui->plainTextEditCommand);
4c75f9e
4c75f9e
    new CommandCompleter(ui->plainTextEditCommand);
4c75f9e
}
4c75f9e
4c75f9e
CommandEdit::~CommandEdit()
4c75f9e
{
4c75f9e
    delete ui;
4c75f9e
}
4c75f9e
4c75f9e
void CommandEdit::setCommand(const QString &command) const
4c75f9e
{
4c75f9e
    ui->plainTextEditCommand->setPlainText(command);
4c75f9e
}
4c75f9e
4c75f9e
QString CommandEdit::command() const
4c75f9e
{
4c75f9e
    return ui->plainTextEditCommand->toPlainText();
4c75f9e
}
4c75f9e
4c75f9e
bool CommandEdit::isEmpty() const
4c75f9e
{
4c75f9e
    return ui->plainTextEditCommand->document()->characterCount() == 0;
4c75f9e
}
4c75f9e
4c75f9e
QFont CommandEdit::commandFont() const
4c75f9e
{
4c75f9e
    QFont font("Monospace");
4c75f9e
    font.setStyleHint(QFont::TypeWriter);
4c75f9e
    font.setPointSize(10);
4c75f9e
    return font;
4c75f9e
}
4c75f9e
4c75f9e
void CommandEdit::resizeEvent(QResizeEvent *event)
4c75f9e
{
4c75f9e
    QWidget::resizeEvent(event);
4c75f9e
    updateCommandEditSize();
4c75f9e
}
4c75f9e
4c75f9e
void CommandEdit::on_plainTextEditCommand_textChanged()
4c75f9e
{
4c75f9e
    updateCommandEditSize();
4c75f9e
    emit changed();
4c75f9e
4c75f9e
    QString errors;
4c75f9e
    QList<QTextEdit::ExtraSelection> selections;
4c75f9e
4c75f9e
    QString command = ui->plainTextEditCommand->toPlainText();
4c75f9e
    QRegExp scriptPrefix("(^|\\b)copyq:");
4c75f9e
    const int pos = command.indexOf(scriptPrefix);
4c75f9e
4c75f9e
    if (pos != -1) {
4c75f9e
        const int scriptStartPos = pos + scriptPrefix.matchedLength();
4c75f9e
        command.remove(0, scriptStartPos);
4c75f9e
4c75f9e
        const QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(command);
4c75f9e
4c75f9e
        if (result.state() == QScriptSyntaxCheckResult::Error) {
4c75f9e
            errors = result.errorMessage();
4c75f9e
            if (errors.isEmpty())
4c75f9e
                errors = "Syntax error";
4c75f9e
4c75f9e
            const int line = result.errorLineNumber() - 1;
4c75f9e
            const int column = result.errorColumnNumber() - 1;
4c75f9e
            QTextDocument *doc = ui->plainTextEditCommand->document();
4c75f9e
            const int firstBlockNumber = doc->findBlock(pos).blockNumber();
4c75f9e
            QTextBlock block = doc->findBlockByNumber(firstBlockNumber + line);
4c75f9e
            const int blockStartPos = line == 0 ? scriptStartPos : block.position();
4c75f9e
            const int errorPosition = column + blockStartPos;
4c75f9e
4c75f9e
            QTextCursor cursor = ui->plainTextEditCommand->textCursor();
4c75f9e
            cursor.setPosition(errorPosition);
4c75f9e
            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
4c75f9e
4c75f9e
            QTextEdit::ExtraSelection selection;
4c75f9e
            selection.cursor = cursor;
4c75f9e
            selection.format = cursor.blockCharFormat();
4c75f9e
            selection.format.setUnderlineColor(Qt::red);
4c75f9e
            selection.format.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline);
4c75f9e
            selections.append(selection);
4c75f9e
        }
4c75f9e
    }
4c75f9e
4c75f9e
    ui->plainTextEditCommand->setExtraSelections(selections);
4c75f9e
    ui->labelErrors->setVisible(!errors.isEmpty());
4c75f9e
    ui->labelErrors->setText(errors);
4c75f9e
}
4c75f9e
4c75f9e
void CommandEdit::updateCommandEditSize()
4c75f9e
{
4c75f9e
    const QFontMetrics fm( ui->plainTextEditCommand->document()->defaultFont() );
4c75f9e
    const auto height = static_cast<int>( ui->plainTextEditCommand->document()->size().height() );
4c75f9e
    const int lines = height + 2;
4c75f9e
    const int visibleLines = qBound(3, lines, 20);
4c75f9e
    const int h = visibleLines * fm.lineSpacing();
4c75f9e
    ui->plainTextEditCommand->setMinimumHeight(h);
4c75f9e
}