Blob Blame History Raw
/*
    Copyright (c) 2017, Lukas Holecek <hluk@email.cz>

    This file is part of CopyQ.

    CopyQ is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CopyQ is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CopyQ.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "commandedit.h"
#include "ui_commandedit.h"

#include "gui/commandsyntaxhighlighter.h"
#include "gui/commandcompleter.h"

#include <QScriptEngine>
#include <QTextBlock>
#include <QTextCursor>
#include <QTextDocument>

CommandEdit::CommandEdit(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::CommandEdit)
{
    ui->setupUi(this);
    ui->labelErrors->hide();
    ui->plainTextEditCommand->document()->setDefaultFont(commandFont());

    setFocusProxy(ui->plainTextEditCommand);

    installCommandSyntaxHighlighter(ui->plainTextEditCommand);

    new CommandCompleter(ui->plainTextEditCommand);
}

CommandEdit::~CommandEdit()
{
    delete ui;
}

void CommandEdit::setCommand(const QString &command) const
{
    ui->plainTextEditCommand->setPlainText(command);
}

QString CommandEdit::command() const
{
    return ui->plainTextEditCommand->toPlainText();
}

bool CommandEdit::isEmpty() const
{
    return ui->plainTextEditCommand->document()->characterCount() == 0;
}

QFont CommandEdit::commandFont() const
{
    QFont font("Monospace");
    font.setStyleHint(QFont::TypeWriter);
    font.setPointSize(10);
    return font;
}

void CommandEdit::resizeEvent(QResizeEvent *event)
{
    QWidget::resizeEvent(event);
    updateCommandEditSize();
}

void CommandEdit::on_plainTextEditCommand_textChanged()
{
    updateCommandEditSize();
    emit changed();

    QString errors;
    QList<QTextEdit::ExtraSelection> selections;

    QString command = ui->plainTextEditCommand->toPlainText();
    QRegExp scriptPrefix("(^|\\b)copyq:");
    const int pos = command.indexOf(scriptPrefix);

    if (pos != -1) {
        const int scriptStartPos = pos + scriptPrefix.matchedLength();
        command.remove(0, scriptStartPos);

        const QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(command);

        if (result.state() == QScriptSyntaxCheckResult::Error) {
            errors = result.errorMessage();
            if (errors.isEmpty())
                errors = "Syntax error";

            const int line = result.errorLineNumber() - 1;
            const int column = result.errorColumnNumber() - 1;
            QTextDocument *doc = ui->plainTextEditCommand->document();
            const int firstBlockNumber = doc->findBlock(pos).blockNumber();
            QTextBlock block = doc->findBlockByNumber(firstBlockNumber + line);
            const int blockStartPos = line == 0 ? scriptStartPos : block.position();
            const int errorPosition = column + blockStartPos;

            QTextCursor cursor = ui->plainTextEditCommand->textCursor();
            cursor.setPosition(errorPosition);
            cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);

            QTextEdit::ExtraSelection selection;
            selection.cursor = cursor;
            selection.format = cursor.blockCharFormat();
            selection.format.setUnderlineColor(Qt::red);
            selection.format.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline);
            selections.append(selection);
        }
    }

    ui->plainTextEditCommand->setExtraSelections(selections);
    ui->labelErrors->setVisible(!errors.isEmpty());
    ui->labelErrors->setText(errors);
}

void CommandEdit::updateCommandEditSize()
{
    const QFontMetrics fm( ui->plainTextEditCommand->document()->defaultFont() );
    const auto height = static_cast<int>( ui->plainTextEditCommand->document()->size().height() );
    const int lines = height + 2;
    const int visibleLines = qBound(3, lines, 20);
    const int h = visibleLines * fm.lineSpacing();
    ui->plainTextEditCommand->setMinimumHeight(h);
}