使用QPlainTextEdit完成一个代码编辑器
关键词:Qt: QPlainTextEdit、CommentHighlighter、QCompleter
一个代码编辑器基本要素如下:
- 文本编辑
- 显示行号
- 高亮编辑去
- 代码自动补全
文本编辑
文本编辑肯定是一个编辑器的最基本的要素。在Qt中,有QTextEdit和QPlainTextEdit都是可以进行文本编辑的控件。但是,QPlainTextEdit控件对纯文本的支持进行了优化,并且对大文本支持比QTextEdit好,性能更优秀,因此,这里选择QPlainTextEdit作为代码编辑器的底层文本编辑组件。
显示行号
显示行号是一个编辑器比较好用的一个有力支撑功能。但是QPlainTextEdit控件本身是不支持行号显示的,但是幸好Qt的example给出了一个显示行号的例子CodeEditor,并且更好的消息是CodeEditor也是用QPlainTextEdit实现的。我这里就直接拿来用了。
声明
Qt的显示行号例子CodeEditor声明如下:
//![codeeditordefinition]
class CodeEditor : public QPlainTextEdit
{
Q_OBJECT
public:
CodeEditor(QWidget *parent = nullptr);
void lineNumberAreaPaintEvent(QPaintEvent *event);
int lineNumberAreaWidth();
protected:
void resizeEvent(QResizeEvent *event) override;
private slots:
void updateLineNumberAreaWidth(int newBlockCount);
void highlightCurrentLine();
void updateLineNumberArea(const QRect &rect, int dy);
private:
QWidget *lineNumberArea;
};
//![codeeditordefinition]
//![extraarea]
应用
编辑器应用CodeEditor如下,直接使用了继承的方式
class SqlEditor : public CodeEditor
{
Q_OBJECT
public:
SqlEditor(QWidget *parent = nullptr);
~SqlEditor();
}
高亮编辑器
Qt有QSyntaxHighlighter对象提供实现编辑器高亮的功能,若要按照要求自定义高亮规则,则实现重新实现highlightBlock方法即可。
在代码编辑器中,最少应该有三种高亮规则:普通文本、语法、注释。这里,普通文本采用默认显示,针对语法和注释分别进行不同的高亮。
声明
封装实现自己的高亮功能如下:
// header file
class CommentHighlighter : public QSyntaxHighlighter
{
public:
CommentHighlighter(QTextDocument *parent = 0);
protected:
void highlightBlock(const QString &text) override;
private:
struct HighlightRule
{
QRegularExpression exp;
QTextCharFormat format;
};
std::vector<HighlightRule> _highlightingRules;
QRegularExpression commentExpression;
QTextCharFormat syntaxFormat; // 语法高亮格式
QTextCharFormat commentFormat; // 注释肝功格式
};
实现
// cpp file
CommentHighlighter::CommentHighlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent)
{
commentFormat.setForeground(Qt::gray);
commentExpression = QRegularExpression(QStringLiteral("(%1)[^\n]*")
.arg(regExpFactory().commentSymbol()));
syntaxFormat.setForeground(Qt::blue);
syntaxFormat.setFontWeight(QFont::Bold);
for (auto &pattern : regExpFactory().keywords()) {
HighlightRule rule;
rule.exp = QRegularExpression(QString("\\b%1\\b").arg(pattern), QRegularExpression::CaseInsensitiveOption);
rule.format = syntaxFormat;
_highlightingRules.emplace_back(rule);
}
}
void CommentHighlighter::highlightBlock(const QString &text)
{
for(auto& value : _highlightingRules)
{
auto matchIterator = value.exp.globalMatch(text);
while (matchIterator.hasNext()) {
auto match = matchIterator.next();
setFormat(match.capturedStart(), match.capturedLength(), value.format);
}
}
auto matchIterator = commentExpression.globalMatch(text);
while (matchIterator.hasNext()) {
auto match = matchIterator.next();
setFormat(match.capturedStart(), match.capturedLength(), commentFormat);
}
}
应用
SqlEditor::SqlEditor(QWidget *parent)
: CodeEditor(parent)
{
_highligter = new CommentHighlighter(document());
}
代码自动补全
代码补充功能可以使用QCompleter对象来实现。
实现
SqlEditor::SqlEditor(QWidget *parent)
: CodeEditor(parent)
, _completer{nullptr}
, _lastTableNames{}
{
_highligter = new CommentHighlighter(document());
// syntax complete
_completer = new QCompleter(regExpFactory().keywords(), this);
_completer->setCaseSensitivity(Qt::CaseInsensitive);
_completer->setWidget(this);
connect(_completer, SIGNAL(activated(const QString&)), this, SLOT(slotCompleterActivated(const QString&)));
}
void SqlEditor::slotCompleterActivated(const QString &text)
{
QString completionPrefix = currentWord();
QString shouldReplaceText = text;
QTextCursor cursor = textCursor();
if (!shouldReplaceText.contains(completionPrefix))
{
cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, completionPrefix.size());
cursor.removeSelectedText();
}
else
{
shouldReplaceText = shouldReplaceText.replace(
shouldReplaceText.indexOf(completionPrefix), completionPrefix.size(), "");
}
cursor.insertText(shouldReplaceText);
}
QString SqlEditor::currentWord()
{
auto cursor = textCursor();
while (cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor)) {
QString text = cursor.selectedText();
if (text.contains(" ") || cursor.atBlockStart()) {
break;
}
}
return cursor.selectedText().simplified();
}
自动补全关键字更新
在以上实现中,我们完全了关键字的自动补全功能,但QCompleter的能力不止如此,比如说我们在编辑中可以更新关键字:添加或者减少关键字,形成一种智能编辑的能力,怎么实现呢?
void SqlEditor::updateCompleterMode(QStringList words)
{
QStringList allWords = regExpFactory().keywords();
for (auto& [key, value] : _mapTables) {
allWords.append(value);
}
if(words.size() > 0)
{
allWords.append(words);
}
_completer->setModel(new QStringListModel{allWords});
}
总结
- 完整源代码参考github
- Qt Assistant真的挺好用,学会查询和尽量使用Qt Assistant,能是你在使用Qt中事半功倍。
本文含有隐藏内容,请 开通VIP 后查看