C++与QML编写Lua的简易集成开发环境(一)

本文介绍了作者使用C++和QML为Lua开发的简易集成开发环境的初步成果,包括本地文件读取和代码高亮显示功能。通过文件路径读取实现本地文件的加载,并使用QSyntaxHighlighter类进行代码高亮,避免了污染本地文件的问题。目前项目已实现的功能包括在TextEdit中显示文件内容和高亮,下一步计划实现行号显示。

        人是新人,已经大四临近毕业。今天的毕业设计组会开完,在老师的提醒下,我就想写两篇文章,以纪念我的大学生活和惨不忍睹毕设项目。(我知道自己的程序写的很烂,第一次发文,也很紧张)

        我的毕业设计是实现一个简易的Lua集成开发环境,最主要的要求是实现能够代码编辑,高亮显示,断点调试和单步调试。老师没有规定语言,但是我只有C++摸得比较熟,同时也是想挑战一下自己吧,想做点不一样的,在网上搜索的时候看到很多QML与C++的信息,瞬间感觉qml好厉害,所以选择了QML与C++来编写Lua的ide。使用的编辑器是Qt Creator。

        到目前为止,我已经实现了本地文件读取,高亮,行号。下面是目前功能和代码的展示。

一.本地读取

        本地读取是我在编写编译器时的第一个难点,首先我们要了解QML读取文件的两种方式:一种是通过qrc添加资源,另一种是通过文件路径读取。我选择了通过文件路径读取,编写一个组件File,在选择文件后将其中数据显示到控制台中的TextEdit区域。

        以下为部分代码:

        File.h

#ifndef QT_HUB_FILE_H
#define QT_HUB_FILE_H

#include <QObject>

class File : public QObject
{
    Q_OBJECT
public:
    File();

    Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
    Q_PROPERTY(QString text   READ text   WRITE setText   NOTIFY textChanged)

    QString source() const;
    void setSource(const QString &source);

    QString text() const;
    void setText(const QString &text);


signals:
    void sourceChanged();
    void textChanged();

private slots:
    void readFile();

private:
    QString m_source;
    QString m_text;
};

#endif // FILE_H

File.cpp

#include "File.h"

#include <QFile>
#include <QDebug>

File::File()
{
    connect(this, SIGNAL(sourceChanged()), this, SLOT(readFile()));
}

void File::setSource(const QString &source)
{
    m_source = source;
    emit sourceChanged();
}

QString File::source() const
{
    return m_source;
}

void File::setText(const QString &text)
{
    QFile file(m_source);
    if (!file.open(QIODevice::WriteOnly)) {
        m_text = "";
        qDebug() << "Error:" << m_source << "open failed!";
    }
    else {
        qint64 byte = file.write(text.toUtf8());
        if (byte != text.toUtf8().size()) {
            m_text = text.toUtf8().left(byte);
            qDebug() << "Error:" << m_source << "open failed!";
        }
        else {
            m_text = text;
        }

        file.close();
    }

    emit textChanged();
}

void File::readFile()
{
    QFile file(m_source);
    if (!file.open(QIODevice::ReadOnly)) {
        m_text = "";
        qDebug() << "Error:" << m_source << "open failed!";
    }

    m_text = file.readAll();
    emit textChanged();
}



QString File::text() const
{
    return m_text;
}

注册对象后,在main.qml中的引用:

File {
    id: file
    source: "F:/Luap/learn/选择路径.lua"
}

然后在TextEdit中引用file.text,在组件加载完和文字改变时修改text和file.text即可。

二.代码高亮

        在完成这个功能时查了很多资料,发现实现 代码高亮有很多方法。其中我尝试过使用和设置TextEdit自带的textFormat中的Richtext,发现它会将输入的字符串自动加标签(类似于Html),从而修改显示文字的字体,颜色和大小等属性。但是由于我实现了本地读取,在组件加载后我实现了即时保存功能,这就导致我在创建调试程序后,TextEdit在读取xxx.lua时会污染本地文件,同时无法显示文本内容。截图如下:

        所以我没有使用该类方法。我选择使用QSyntaxHighlighter类库。QSyntaxHighlighter类允许我们定义语法突出显示规则,此外,我们还可以使用该类来查询文档的当前格式或用户数据。我个人理解是先创建多个包含相关元素的 List,同时规定其中元素的字体和颜色,然后一行行读入文本中的信息,与List中的信息进行比对,如果对比匹配成功,换字体,没匹配上,就跳过,默认颜色和字体。

        以下为部分代码:

    HighLighter.h
#ifndef CODEHIGHLIGHTER_H
#define CODEHIGHLIGHTER_H

#include <QSyntaxHighlighter>
#include <QTextCharFormat>
#include<QQuickTextDocument>

class HighLighter: public QSyntaxHighlighter
{
    Q_OBJECT

public:
    HighLighter(QTextDocument *parent = 0);
    Q_INVOKABLE void setDocument(QQuickTextDocument *pDoc);

protected:
    Q_INVOKABLE void highlightBlock(const QString &text) override;

private:
    struct HighlightingRule
    {
        QRegExp pattern;
        QTextCharFormat format;
    };
    QVector<HighlightingRule> highlightingRules;

    QRegExp commentStartExpression;         //多行注释开始标识符
    QRegExp commentEndExpression;           //多行注释结束标识符

    QTextCharFormat keywordFormat;          //关键字
    QTextCharFormat classFormat;            //类
    QTextCharFormat singleLineKey;          //单行关键字
    QTextCharFormat singleLineValue;        //单行值
    QTextCharFormat singleLineCommentFormat;//单行注释
    QTextCharFormat multiLineCommentFormat; //多行注释
    QTextCharFormat quotationFormat;        //字符串标识符
    QTextCharFormat functionFormat;         //方法标识符
};

#endif // CODEHIGHLIGHTER_H

  HighLighter.cpp

#include "Highlighter.h"
#include <QtDebug>
#include<QQuickTextDocument>

HighLighter::HighLighter(QTextDocument *parent)
    : QSyntaxHighlighter(parent)
{
    HighlightingRule rule;

    //对于下面正则表达式,标记为紫色,类名称
    classFormat.setFontWeight(QFont::Bold);
    classFormat.setForeground(Qt::darkMagenta);
    rule.pattern = QRegExp("\\b[A-Za-z]+:\\b");
    rule.format = classFormat;
    highlightingRules.append(rule);
    rule.pattern = QRegExp("\\b[A-Za-z]+\\.\\b");
    rule.format = classFormat;
    highlightingRules.append(rule);

    //字符串,标记深红色
    quotationFormat.setForeground(Qt::darkRed);
    rule.pattern = QRegExp("\".*\"");
    rule.format = quotationFormat;
    highlightingRules.append(rule);
    rule.pattern = QRegExp("'.*'");
    rule.format = quotationFormat;
    highlightingRules.append(rule);

    //函数标记为斜体蓝色
    functionFormat.setFontItalic(true);
    functionFormat.setForeground(Qt::blue);
    rule.pattern = QRegExp("\\b[A-Za-z0-9_]+(?=\\()");
    rule.format = functionFormat;
    highlightingRules.append(rule);

    //关键字
    QStringList keywords = {
        "and", "break", "do", "else", "elseif", "end", "false",
        "for", "function", "if", "in", "local", "nil", "not", "or",
        "repeat", "return", "then", "true", "unitl", "while", "goto"
    };
    //对于下面关键字部分,标记为深蓝色
    keywordFormat.setForeground(Qt::darkBlue);
    keywordFormat.setFontWeight(QFont::Bold);
    QStringList keywordPatterns;
    for(int i=0; i<keywords.length(); i++)
    {
        QString pattern = "\\b" + keywords[i] + "\\b";
        rule.pattern = QRegExp(pattern);
        rule.format = keywordFormat;
        highlightingRules.append(rule);
    }

    //对于下面正则表达式,单行注释标记为绿色
    singleLineCommentFormat.setForeground(Qt::darkGreen);
    singleLineCommentFormat.setFontItalic(true);
    //rule.pattern = QRegExp("//[^\n]*");
    rule.pattern = QRegExp("--[^\n]*");
    rule.format = singleLineCommentFormat;
    highlightingRules.append(rule);

    //对于多行注释
    multiLineCommentFormat.setForeground(Qt::darkGreen);
    multiLineCommentFormat.setFontItalic(true);
    //commentStartExpression = QRegExp("/\\*");
    //commentEndExpression = QRegExp("\\*/");
    //Lua 多行注释 --[[xx]]
    commentStartExpression = QRegExp("--\\[\\[");
    commentEndExpression = QRegExp("\\]\\]");
}

void HighLighter::highlightBlock(const QString &text)
{
    foreach (const HighlightingRule &rule, highlightingRules) {
        QRegExp expression(rule.pattern);
        int index = expression.indexIn(text);
        qDebug()<<text;
        while (index >= 0) {
            int length = expression.matchedLength();
            setFormat(index, length, rule.format);
            index = expression.indexIn(text, index + length);
        }
    }

    setCurrentBlockState(0);

    int startIndex = 0;
    if (previousBlockState() != 1)
        startIndex = commentStartExpression.indexIn(text);


    while (startIndex >= 0) {
        int endIndex = commentEndExpression.indexIn(text, startIndex);
        int commentLength=0;
        if (endIndex == -1) {
            setCurrentBlockState(1);
            commentLength = text.length() - startIndex;
        } else {
            commentLength = endIndex - startIndex
                    + commentEndExpression.matchedLength();
        }
        setFormat(startIndex, commentLength, multiLineCommentFormat);
        startIndex = commentStartExpression.indexIn(text, startIndex + commentLength);
    }
}

void HighLighter::setDocument(QQuickTextDocument *pDoc)
{
    QSyntaxHighlighter::setDocument(pDoc->textDocument());
}
    main.qml中的引用:
TextEdit {
                id:t1
                anchors.rightMargin: -73
                anchors.leftMargin: 44
                anchors.topMargin: 8
                anchors.bottomMargin: -8
                anchors.fill: parent
                font.pixelSize: 30
                font.family:heiti.name
                //textFormat: TextEdit.RichText
                selectedTextColor: "#00BFFF"
                onTextChanged: {
                    file.text = text
                }
                Component.onCompleted: {
                    text = file.text
                    h1.setDocument(t1.textDocument)                    
                    //console.log(t1.lineCount)
                }
                selectByMouse: true
                onCursorRectangleChanged: flicka.ensureVisible(cursorRectangle)
            }

        以上为我当前毕业项目进度,明天会发如何显示行号。欢迎大家评论。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值