add QCodeEditor
This commit is contained in:
parent
bccee9bd36
commit
2f3069a388
316 changed files with 98016 additions and 0 deletions
179
external/QCodeEditor/src/internal/QCXXHighlighter.cpp
vendored
Normal file
179
external/QCodeEditor/src/internal/QCXXHighlighter.cpp
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
// QCodeEditor
|
||||
#include <QCXXHighlighter>
|
||||
#include <QSyntaxStyle>
|
||||
#include <QLanguage>
|
||||
|
||||
// Qt
|
||||
#include <QFile>
|
||||
|
||||
|
||||
QCXXHighlighter::QCXXHighlighter(QTextDocument* document) :
|
||||
QStyleSyntaxHighlighter(document),
|
||||
m_highlightRules (),
|
||||
m_includePattern (QRegularExpression(R"(^\s*#\s*include\s*([<"][^:?"<>\|]+[">]))")),
|
||||
m_functionPattern (QRegularExpression(R"(\b([_a-zA-Z][_a-zA-Z0-9]*\s+)?((?:[_a-zA-Z][_a-zA-Z0-9]*\s*::\s*)*[_a-zA-Z][_a-zA-Z0-9]*)(?=\s*\())")),
|
||||
m_defTypePattern (QRegularExpression(R"(\b([_a-zA-Z][_a-zA-Z0-9]*)\s+[_a-zA-Z][_a-zA-Z0-9]*\s*[;=])")),
|
||||
m_commentStartPattern(QRegularExpression(R"(/\*)")),
|
||||
m_commentEndPattern (QRegularExpression(R"(\*/)"))
|
||||
{
|
||||
Q_INIT_RESOURCE(qcodeeditor_resources);
|
||||
QFile fl(":/languages/cpp.xml");
|
||||
|
||||
if (!fl.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QLanguage language(&fl);
|
||||
|
||||
if (!language.isLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto keys = language.keys();
|
||||
for (auto&& key : keys)
|
||||
{
|
||||
auto names = language.names(key);
|
||||
for (auto&& name : names)
|
||||
{
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(QString(R"(\b%1\b)").arg(name)),
|
||||
key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Numbers
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"((?<=\b|\s|^)(?i)(?:(?:(?:(?:(?:\d+(?:'\d+)*)?\.(?:\d+(?:'\d+)*)(?:e[+-]?(?:\d+(?:'\d+)*))?)|(?:(?:\d+(?:'\d+)*)\.(?:e[+-]?(?:\d+(?:'\d+)*))?)|(?:(?:\d+(?:'\d+)*)(?:e[+-]?(?:\d+(?:'\d+)*)))|(?:0x(?:[0-9a-f]+(?:'[0-9a-f]+)*)?\.(?:[0-9a-f]+(?:'[0-9a-f]+)*)(?:p[+-]?(?:\d+(?:'\d+)*)))|(?:0x(?:[0-9a-f]+(?:'[0-9a-f]+)*)\.?(?:p[+-]?(?:\d+(?:'\d+)*))))[lf]?)|(?:(?:(?:[1-9]\d*(?:'\d+)*)|(?:0[0-7]*(?:'[0-7]+)*)|(?:0x[0-9a-f]+(?:'[0-9a-f]+)*)|(?:0b[01]+(?:'[01]+)*))(?:u?l{0,2}|l{0,2}u?)))(?=\b|\s|$))"),
|
||||
"Number"
|
||||
});
|
||||
|
||||
// Strings
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"("[^\n"]*")"),
|
||||
"String"
|
||||
});
|
||||
|
||||
// Define
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(#[a-zA-Z_]+)"),
|
||||
"Preprocessor"
|
||||
});
|
||||
|
||||
// Single line
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(//[^\n]*)"),
|
||||
"Comment"
|
||||
});
|
||||
}
|
||||
|
||||
void QCXXHighlighter::highlightBlock(const QString& text)
|
||||
{
|
||||
// Checking for include
|
||||
{
|
||||
auto matchIterator = m_includePattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat("Preprocessor")
|
||||
);
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(1),
|
||||
match.capturedLength(1),
|
||||
syntaxStyle()->getFormat("String")
|
||||
);
|
||||
}
|
||||
}
|
||||
// Checking for function
|
||||
{
|
||||
auto matchIterator = m_functionPattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat("Type")
|
||||
);
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(2),
|
||||
match.capturedLength(2),
|
||||
syntaxStyle()->getFormat("Function")
|
||||
);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto matchIterator = m_defTypePattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(1),
|
||||
match.capturedLength(1),
|
||||
syntaxStyle()->getFormat("Type")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& rule : m_highlightRules)
|
||||
{
|
||||
auto matchIterator = rule.pattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat(rule.formatName)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentBlockState(0);
|
||||
|
||||
int startIndex = 0;
|
||||
if (previousBlockState() != 1)
|
||||
{
|
||||
startIndex = text.indexOf(m_commentStartPattern);
|
||||
}
|
||||
|
||||
while (startIndex >= 0)
|
||||
{
|
||||
auto match = m_commentEndPattern.match(text, startIndex);
|
||||
|
||||
int endIndex = match.capturedStart();
|
||||
int commentLength = 0;
|
||||
|
||||
if (endIndex == -1)
|
||||
{
|
||||
setCurrentBlockState(1);
|
||||
commentLength = text.length() - startIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
commentLength = endIndex - startIndex + match.capturedLength();
|
||||
}
|
||||
|
||||
setFormat(
|
||||
startIndex,
|
||||
commentLength,
|
||||
syntaxStyle()->getFormat("Comment")
|
||||
);
|
||||
startIndex = text.indexOf(m_commentStartPattern, startIndex + commentLength);
|
||||
}
|
||||
}
|
||||
742
external/QCodeEditor/src/internal/QCodeEditor.cpp
vendored
Normal file
742
external/QCodeEditor/src/internal/QCodeEditor.cpp
vendored
Normal file
|
|
@ -0,0 +1,742 @@
|
|||
// QCodeEditor
|
||||
#include <QLineNumberArea>
|
||||
#include <QSyntaxStyle>
|
||||
#include <QCodeEditor>
|
||||
#include <QStyleSyntaxHighlighter>
|
||||
#include <QFramedTextAttribute>
|
||||
#include <QCXXHighlighter>
|
||||
|
||||
|
||||
// Qt
|
||||
#include <QTextBlock>
|
||||
#include <QPaintEvent>
|
||||
#include <QFontDatabase>
|
||||
#include <QScrollBar>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QTextCharFormat>
|
||||
#include <QCursor>
|
||||
#include <QCompleter>
|
||||
#include <QAbstractItemView>
|
||||
#include <QShortcut>
|
||||
#include <QMimeData>
|
||||
|
||||
static QVector<QPair<QString, QString>> parentheses = {
|
||||
{"(", ")"},
|
||||
{"{", "}"},
|
||||
{"[", "]"},
|
||||
{"\"", "\""},
|
||||
{"'", "'"}
|
||||
};
|
||||
|
||||
QCodeEditor::QCodeEditor(QWidget* widget) :
|
||||
QTextEdit(widget),
|
||||
m_highlighter(nullptr),
|
||||
m_syntaxStyle(nullptr),
|
||||
m_lineNumberArea(new QLineNumberArea(this)),
|
||||
m_completer(nullptr),
|
||||
m_framedAttribute(new QFramedTextAttribute(this)),
|
||||
m_autoIndentation(true),
|
||||
m_autoParentheses(true),
|
||||
m_replaceTab(true),
|
||||
m_tabReplace(QString(4, ' '))
|
||||
{
|
||||
initDocumentLayoutHandlers();
|
||||
initFont();
|
||||
performConnections();
|
||||
|
||||
setSyntaxStyle(QSyntaxStyle::defaultStyle());
|
||||
}
|
||||
|
||||
void QCodeEditor::initDocumentLayoutHandlers()
|
||||
{
|
||||
document()
|
||||
->documentLayout()
|
||||
->registerHandler(
|
||||
QFramedTextAttribute::type(),
|
||||
m_framedAttribute
|
||||
);
|
||||
}
|
||||
|
||||
void QCodeEditor::initFont()
|
||||
{
|
||||
auto fnt = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
fnt.setFixedPitch(true);
|
||||
fnt.setPointSize(10);
|
||||
|
||||
setFont(fnt);
|
||||
}
|
||||
|
||||
void QCodeEditor::performConnections()
|
||||
{
|
||||
connect(
|
||||
document(),
|
||||
&QTextDocument::blockCountChanged,
|
||||
this,
|
||||
&QCodeEditor::updateLineNumberAreaWidth
|
||||
);
|
||||
|
||||
connect(
|
||||
verticalScrollBar(),
|
||||
&QScrollBar::valueChanged,
|
||||
[this](int){ m_lineNumberArea->update(); }
|
||||
);
|
||||
|
||||
connect(
|
||||
this,
|
||||
&QTextEdit::cursorPositionChanged,
|
||||
this,
|
||||
&QCodeEditor::updateExtraSelection
|
||||
);
|
||||
|
||||
connect(
|
||||
this,
|
||||
&QTextEdit::selectionChanged,
|
||||
this,
|
||||
&QCodeEditor::onSelectionChanged
|
||||
);
|
||||
}
|
||||
|
||||
void QCodeEditor::setHighlighter(QStyleSyntaxHighlighter* highlighter)
|
||||
{
|
||||
if (m_highlighter)
|
||||
{
|
||||
m_highlighter->setDocument(nullptr);
|
||||
}
|
||||
|
||||
m_highlighter = highlighter;
|
||||
|
||||
if (m_highlighter)
|
||||
{
|
||||
m_highlighter->setSyntaxStyle(m_syntaxStyle);
|
||||
m_highlighter->setDocument(document());
|
||||
}
|
||||
}
|
||||
|
||||
void QCodeEditor::setSyntaxStyle(QSyntaxStyle* style)
|
||||
{
|
||||
m_syntaxStyle = style;
|
||||
|
||||
m_framedAttribute->setSyntaxStyle(m_syntaxStyle);
|
||||
m_lineNumberArea->setSyntaxStyle(m_syntaxStyle);
|
||||
|
||||
if (m_highlighter)
|
||||
{
|
||||
m_highlighter->setSyntaxStyle(m_syntaxStyle);
|
||||
}
|
||||
|
||||
updateStyle();
|
||||
}
|
||||
|
||||
void QCodeEditor::updateStyle()
|
||||
{
|
||||
if (m_highlighter)
|
||||
{
|
||||
m_highlighter->rehighlight();
|
||||
}
|
||||
|
||||
if (m_syntaxStyle)
|
||||
{
|
||||
auto currentPalette = palette();
|
||||
|
||||
// Setting text format/color
|
||||
currentPalette.setColor(
|
||||
QPalette::ColorRole::Text,
|
||||
m_syntaxStyle->getFormat("Text").foreground().color()
|
||||
);
|
||||
|
||||
// Setting common background
|
||||
currentPalette.setColor(
|
||||
QPalette::Base,
|
||||
m_syntaxStyle->getFormat("Text").background().color()
|
||||
);
|
||||
|
||||
// Setting selection color
|
||||
currentPalette.setColor(
|
||||
QPalette::Highlight,
|
||||
m_syntaxStyle->getFormat("Selection").background().color()
|
||||
);
|
||||
|
||||
setPalette(currentPalette);
|
||||
}
|
||||
|
||||
updateExtraSelection();
|
||||
}
|
||||
|
||||
void QCodeEditor::onSelectionChanged()
|
||||
{
|
||||
auto selected = textCursor().selectedText();
|
||||
|
||||
auto cursor = textCursor();
|
||||
|
||||
// Cursor is null if setPlainText was called.
|
||||
if (cursor.isNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cursor.movePosition(QTextCursor::MoveOperation::Left);
|
||||
cursor.select(QTextCursor::SelectionType::WordUnderCursor);
|
||||
|
||||
QSignalBlocker blocker(this);
|
||||
m_framedAttribute->clear(cursor);
|
||||
|
||||
if (selected.size() > 1 &&
|
||||
cursor.selectedText() == selected)
|
||||
{
|
||||
auto backup = textCursor();
|
||||
|
||||
// Perform search selecting
|
||||
handleSelectionQuery(cursor);
|
||||
|
||||
setTextCursor(backup);
|
||||
}
|
||||
}
|
||||
|
||||
void QCodeEditor::resizeEvent(QResizeEvent* e)
|
||||
{
|
||||
QTextEdit::resizeEvent(e);
|
||||
|
||||
updateLineGeometry();
|
||||
}
|
||||
|
||||
void QCodeEditor::updateLineGeometry()
|
||||
{
|
||||
QRect cr = contentsRect();
|
||||
m_lineNumberArea->setGeometry(
|
||||
QRect(cr.left(),
|
||||
cr.top(),
|
||||
m_lineNumberArea->sizeHint().width(),
|
||||
cr.height()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void QCodeEditor::updateLineNumberAreaWidth(int)
|
||||
{
|
||||
setViewportMargins(m_lineNumberArea->sizeHint().width(), 0, 0, 0);
|
||||
}
|
||||
|
||||
void QCodeEditor::updateLineNumberArea(const QRect& rect)
|
||||
{
|
||||
m_lineNumberArea->update(
|
||||
0,
|
||||
rect.y(),
|
||||
m_lineNumberArea->sizeHint().width(),
|
||||
rect.height()
|
||||
);
|
||||
updateLineGeometry();
|
||||
|
||||
if (rect.contains(viewport()->rect()))
|
||||
{
|
||||
updateLineNumberAreaWidth(0);
|
||||
}
|
||||
}
|
||||
|
||||
void QCodeEditor::handleSelectionQuery(QTextCursor cursor)
|
||||
{
|
||||
|
||||
auto searchIterator = cursor;
|
||||
searchIterator.movePosition(QTextCursor::Start);
|
||||
searchIterator = document()->find(cursor.selectedText(), searchIterator);
|
||||
while (searchIterator.hasSelection())
|
||||
{
|
||||
m_framedAttribute->frame(searchIterator);
|
||||
|
||||
searchIterator = document()->find(cursor.selectedText(), searchIterator);
|
||||
}
|
||||
}
|
||||
|
||||
void QCodeEditor::updateExtraSelection()
|
||||
{
|
||||
QList<QTextEdit::ExtraSelection> extra;
|
||||
|
||||
highlightCurrentLine(extra);
|
||||
highlightParenthesis(extra);
|
||||
|
||||
setExtraSelections(extra);
|
||||
}
|
||||
|
||||
void QCodeEditor::highlightParenthesis(QList<QTextEdit::ExtraSelection>& extraSelection)
|
||||
{
|
||||
auto currentSymbol = charUnderCursor();
|
||||
auto prevSymbol = charUnderCursor(-1);
|
||||
|
||||
for (auto& pair : parentheses)
|
||||
{
|
||||
int direction;
|
||||
|
||||
QChar counterSymbol;
|
||||
QChar activeSymbol;
|
||||
auto position = textCursor().position();
|
||||
|
||||
if (pair.first == currentSymbol)
|
||||
{
|
||||
direction = 1;
|
||||
counterSymbol = pair.second[0];
|
||||
activeSymbol = currentSymbol;
|
||||
}
|
||||
else if (pair.second == prevSymbol)
|
||||
{
|
||||
direction = -1;
|
||||
counterSymbol = pair.first[0];
|
||||
activeSymbol = prevSymbol;
|
||||
position--;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto counter = 1;
|
||||
|
||||
while (counter != 0 &&
|
||||
position > 0 &&
|
||||
position < (document()->characterCount() - 1))
|
||||
{
|
||||
// Moving position
|
||||
position += direction;
|
||||
|
||||
auto character = document()->characterAt(position);
|
||||
// Checking symbol under position
|
||||
if (character == activeSymbol)
|
||||
{
|
||||
++counter;
|
||||
}
|
||||
else if (character == counterSymbol)
|
||||
{
|
||||
--counter;
|
||||
}
|
||||
}
|
||||
|
||||
auto format = m_syntaxStyle->getFormat("Parentheses");
|
||||
|
||||
// Found
|
||||
if (counter == 0)
|
||||
{
|
||||
ExtraSelection selection{};
|
||||
|
||||
auto directionEnum =
|
||||
direction < 0 ?
|
||||
QTextCursor::MoveOperation::Left
|
||||
:
|
||||
QTextCursor::MoveOperation::Right;
|
||||
|
||||
selection.format = format;
|
||||
selection.cursor = textCursor();
|
||||
selection.cursor.clearSelection();
|
||||
selection.cursor.movePosition(
|
||||
directionEnum,
|
||||
QTextCursor::MoveMode::MoveAnchor,
|
||||
std::abs(textCursor().position() - position)
|
||||
);
|
||||
|
||||
selection.cursor.movePosition(
|
||||
QTextCursor::MoveOperation::Right,
|
||||
QTextCursor::MoveMode::KeepAnchor,
|
||||
1
|
||||
);
|
||||
|
||||
extraSelection.append(selection);
|
||||
|
||||
selection.cursor = textCursor();
|
||||
selection.cursor.clearSelection();
|
||||
selection.cursor.movePosition(
|
||||
directionEnum,
|
||||
QTextCursor::MoveMode::KeepAnchor,
|
||||
1
|
||||
);
|
||||
|
||||
extraSelection.append(selection);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QCodeEditor::highlightCurrentLine(QList<QTextEdit::ExtraSelection>& extraSelection)
|
||||
{
|
||||
if (!isReadOnly())
|
||||
{
|
||||
QTextEdit::ExtraSelection selection{};
|
||||
|
||||
selection.format = m_syntaxStyle->getFormat("CurrentLine");
|
||||
selection.format.setForeground(QBrush());
|
||||
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
|
||||
selection.cursor = textCursor();
|
||||
selection.cursor.clearSelection();
|
||||
|
||||
extraSelection.append(selection);
|
||||
}
|
||||
}
|
||||
|
||||
void QCodeEditor::paintEvent(QPaintEvent* e)
|
||||
{
|
||||
updateLineNumberArea(e->rect());
|
||||
QTextEdit::paintEvent(e);
|
||||
}
|
||||
|
||||
int QCodeEditor::getFirstVisibleBlock()
|
||||
{
|
||||
// Detect the first block for which bounding rect - once translated
|
||||
// in absolute coordinated - is contained by the editor's text area
|
||||
|
||||
// Costly way of doing but since "blockBoundingGeometry(...)" doesn't
|
||||
// exists for "QTextEdit"...
|
||||
|
||||
QTextCursor curs = QTextCursor(document());
|
||||
curs.movePosition(QTextCursor::Start);
|
||||
for(int i=0; i < document()->blockCount(); ++i)
|
||||
{
|
||||
QTextBlock block = curs.block();
|
||||
|
||||
QRect r1 = viewport()->geometry();
|
||||
QRect r2 = document()
|
||||
->documentLayout()
|
||||
->blockBoundingRect(block)
|
||||
.translated(
|
||||
viewport()->geometry().x(),
|
||||
viewport()->geometry().y() - verticalScrollBar()->sliderPosition()
|
||||
).toRect();
|
||||
|
||||
if (r1.intersects(r2))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
curs.movePosition(QTextCursor::NextBlock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool QCodeEditor::proceedCompleterBegin(QKeyEvent *e)
|
||||
{
|
||||
if (m_completer &&
|
||||
m_completer->popup()->isVisible())
|
||||
{
|
||||
switch (e->key())
|
||||
{
|
||||
case Qt::Key_Enter:
|
||||
case Qt::Key_Return:
|
||||
case Qt::Key_Escape:
|
||||
case Qt::Key_Tab:
|
||||
case Qt::Key_Backtab:
|
||||
e->ignore();
|
||||
return true; // let the completer do default behavior
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: Replace with modifiable QShortcut
|
||||
auto isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_Space);
|
||||
|
||||
return !(!m_completer || !isShortcut);
|
||||
|
||||
}
|
||||
|
||||
void QCodeEditor::proceedCompleterEnd(QKeyEvent *e)
|
||||
{
|
||||
auto ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
|
||||
|
||||
if (!m_completer ||
|
||||
(ctrlOrShift && e->text().isEmpty()) ||
|
||||
e->key() == Qt::Key_Delete)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static QString eow(R"(~!@#$%^&*()_+{}|:"<>?,./;'[]\-=)");
|
||||
|
||||
auto isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_Space);
|
||||
auto completionPrefix = wordUnderCursor();
|
||||
|
||||
if (!isShortcut &&
|
||||
(e->text().isEmpty() ||
|
||||
completionPrefix.length() < 2 ||
|
||||
eow.contains(e->text().right(1))))
|
||||
{
|
||||
m_completer->popup()->hide();
|
||||
return;
|
||||
}
|
||||
|
||||
if (completionPrefix != m_completer->completionPrefix())
|
||||
{
|
||||
m_completer->setCompletionPrefix(completionPrefix);
|
||||
m_completer->popup()->setCurrentIndex(m_completer->completionModel()->index(0, 0));
|
||||
}
|
||||
|
||||
auto cursRect = cursorRect();
|
||||
cursRect.setWidth(
|
||||
m_completer->popup()->sizeHintForColumn(0) +
|
||||
m_completer->popup()->verticalScrollBar()->sizeHint().width()
|
||||
);
|
||||
|
||||
m_completer->complete(cursRect);
|
||||
}
|
||||
|
||||
void QCodeEditor::keyPressEvent(QKeyEvent* e) {
|
||||
#if QT_VERSION >= 0x050A00
|
||||
const int defaultIndent = tabStopDistance() / fontMetrics().averageCharWidth();
|
||||
#else
|
||||
const int defaultIndent = tabStopWidth() / fontMetrics().averageCharWidth();
|
||||
#endif
|
||||
|
||||
auto completerSkip = proceedCompleterBegin(e);
|
||||
|
||||
if (!completerSkip) {
|
||||
if (m_replaceTab && e->key() == Qt::Key_Tab &&
|
||||
e->modifiers() == Qt::NoModifier) {
|
||||
insertPlainText(m_tabReplace);
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto indentation
|
||||
int indentationLevel = getIndentationSpaces();
|
||||
|
||||
#if QT_VERSION >= 0x050A00
|
||||
int tabCounts =
|
||||
indentationLevel * fontMetrics().averageCharWidth() / tabStopDistance();
|
||||
#else
|
||||
int tabCounts =
|
||||
indentationLevel * fontMetrics().averageCharWidth() / tabStopWidth();
|
||||
#endif
|
||||
|
||||
// Have Qt Edior like behaviour, if {|} and enter is pressed indent the two
|
||||
// parenthesis
|
||||
if (m_autoIndentation &&
|
||||
(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) &&
|
||||
charUnderCursor() == '}' && charUnderCursor(-1) == '{')
|
||||
{
|
||||
int charsBack = 0;
|
||||
insertPlainText("\n");
|
||||
|
||||
if (m_replaceTab)
|
||||
insertPlainText(QString(indentationLevel + defaultIndent, ' '));
|
||||
else
|
||||
insertPlainText(QString(tabCounts + 1, '\t'));
|
||||
|
||||
insertPlainText("\n");
|
||||
charsBack++;
|
||||
|
||||
if (m_replaceTab)
|
||||
{
|
||||
insertPlainText(QString(indentationLevel, ' '));
|
||||
charsBack += indentationLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
insertPlainText(QString(tabCounts, '\t'));
|
||||
charsBack += tabCounts;
|
||||
}
|
||||
|
||||
while (charsBack--)
|
||||
moveCursor(QTextCursor::MoveOperation::Left);
|
||||
return;
|
||||
}
|
||||
|
||||
// Shortcut for moving line to left
|
||||
if (m_replaceTab && e->key() == Qt::Key_Backtab) {
|
||||
indentationLevel = std::min(indentationLevel, (int) m_tabReplace.size());
|
||||
|
||||
auto cursor = textCursor();
|
||||
|
||||
cursor.movePosition(QTextCursor::MoveOperation::StartOfLine);
|
||||
cursor.movePosition(QTextCursor::MoveOperation::Right,
|
||||
QTextCursor::MoveMode::KeepAnchor,
|
||||
indentationLevel);
|
||||
|
||||
cursor.removeSelectedText();
|
||||
return;
|
||||
}
|
||||
|
||||
QTextEdit::keyPressEvent(e);
|
||||
|
||||
if (m_autoIndentation && (e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)) {
|
||||
if (m_replaceTab)
|
||||
insertPlainText(QString(indentationLevel, ' '));
|
||||
else
|
||||
insertPlainText(QString(tabCounts, '\t'));
|
||||
}
|
||||
|
||||
if (m_autoParentheses)
|
||||
{
|
||||
for (auto&& el : parentheses)
|
||||
{
|
||||
// Inserting closed brace
|
||||
if (el.first == e->text())
|
||||
{
|
||||
insertPlainText(el.second);
|
||||
moveCursor(QTextCursor::MoveOperation::Left);
|
||||
break;
|
||||
}
|
||||
|
||||
// If it's close brace - check parentheses
|
||||
if (el.second == e->text())
|
||||
{
|
||||
auto symbol = charUnderCursor();
|
||||
|
||||
if (symbol == el.second)
|
||||
{
|
||||
textCursor().deletePreviousChar();
|
||||
moveCursor(QTextCursor::MoveOperation::Right);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
proceedCompleterEnd(e);
|
||||
}
|
||||
|
||||
void QCodeEditor::setAutoIndentation(bool enabled)
|
||||
{
|
||||
m_autoIndentation = enabled;
|
||||
}
|
||||
|
||||
bool QCodeEditor::autoIndentation() const
|
||||
{
|
||||
return m_autoIndentation;
|
||||
}
|
||||
|
||||
void QCodeEditor::setAutoParentheses(bool enabled)
|
||||
{
|
||||
m_autoParentheses = enabled;
|
||||
}
|
||||
|
||||
bool QCodeEditor::autoParentheses() const
|
||||
{
|
||||
return m_autoParentheses;
|
||||
}
|
||||
|
||||
void QCodeEditor::setTabReplace(bool enabled)
|
||||
{
|
||||
m_replaceTab = enabled;
|
||||
}
|
||||
|
||||
bool QCodeEditor::tabReplace() const
|
||||
{
|
||||
return m_replaceTab;
|
||||
}
|
||||
|
||||
void QCodeEditor::setTabReplaceSize(int val)
|
||||
{
|
||||
m_tabReplace.clear();
|
||||
|
||||
m_tabReplace.fill(' ', val);
|
||||
}
|
||||
|
||||
int QCodeEditor::tabReplaceSize() const
|
||||
{
|
||||
return m_tabReplace.size();
|
||||
}
|
||||
|
||||
void QCodeEditor::setCompleter(QCompleter *completer)
|
||||
{
|
||||
if (m_completer)
|
||||
{
|
||||
disconnect(m_completer, nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
m_completer = completer;
|
||||
|
||||
if (!m_completer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_completer->setWidget(this);
|
||||
m_completer->setCompletionMode(QCompleter::CompletionMode::PopupCompletion);
|
||||
|
||||
connect(
|
||||
m_completer,
|
||||
QOverload<const QString&>::of(&QCompleter::activated),
|
||||
this,
|
||||
&QCodeEditor::insertCompletion
|
||||
);
|
||||
}
|
||||
|
||||
void QCodeEditor::focusInEvent(QFocusEvent *e)
|
||||
{
|
||||
if (m_completer)
|
||||
{
|
||||
m_completer->setWidget(this);
|
||||
}
|
||||
|
||||
QTextEdit::focusInEvent(e);
|
||||
}
|
||||
|
||||
void QCodeEditor::insertCompletion(QString s)
|
||||
{
|
||||
if (m_completer->widget() != this)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto tc = textCursor();
|
||||
tc.select(QTextCursor::SelectionType::WordUnderCursor);
|
||||
tc.insertText(s);
|
||||
setTextCursor(tc);
|
||||
}
|
||||
|
||||
QCompleter *QCodeEditor::completer() const
|
||||
{
|
||||
return m_completer;
|
||||
}
|
||||
|
||||
QChar QCodeEditor::charUnderCursor(int offset) const
|
||||
{
|
||||
auto block = textCursor().blockNumber();
|
||||
auto index = textCursor().positionInBlock();
|
||||
auto text = document()->findBlockByNumber(block).text();
|
||||
|
||||
index += offset;
|
||||
|
||||
if (index < 0 || index >= text.size())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return text[index];
|
||||
}
|
||||
|
||||
QString QCodeEditor::wordUnderCursor() const
|
||||
{
|
||||
auto tc = textCursor();
|
||||
tc.select(QTextCursor::WordUnderCursor);
|
||||
return tc.selectedText();
|
||||
}
|
||||
|
||||
void QCodeEditor::insertFromMimeData(const QMimeData* source)
|
||||
{
|
||||
insertPlainText(source->text());
|
||||
}
|
||||
|
||||
int QCodeEditor::getIndentationSpaces()
|
||||
{
|
||||
auto blockText = textCursor().block().text();
|
||||
|
||||
int indentationLevel = 0;
|
||||
|
||||
for (auto i = 0;
|
||||
i < blockText.size() && QString("\t ").contains(blockText[i]);
|
||||
++i)
|
||||
{
|
||||
if (blockText[i] == ' ')
|
||||
{
|
||||
indentationLevel++;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if QT_VERSION >= 0x050A00
|
||||
indentationLevel += tabStopDistance() / fontMetrics().averageCharWidth();
|
||||
#else
|
||||
indentationLevel += tabStopWidth() / fontMetrics().averageCharWidth();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return indentationLevel;
|
||||
}
|
||||
112
external/QCodeEditor/src/internal/QFramedTextAttribute.cpp
vendored
Normal file
112
external/QCodeEditor/src/internal/QFramedTextAttribute.cpp
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// QCodeEditor
|
||||
#include <QFramedTextAttribute>
|
||||
#include <QSyntaxStyle>
|
||||
|
||||
// Qt
|
||||
#include <QFontMetrics>
|
||||
#include <QPainter>
|
||||
#include <QDebug>
|
||||
#include <QTextBlock>
|
||||
|
||||
int QFramedTextAttribute::type()
|
||||
{
|
||||
return QTextFormat::UserFormat + 1;
|
||||
}
|
||||
|
||||
QFramedTextAttribute::QFramedTextAttribute(QObject* parent) :
|
||||
QObject(parent),
|
||||
m_style(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void QFramedTextAttribute::setSyntaxStyle(QSyntaxStyle* style)
|
||||
{
|
||||
m_style = style;
|
||||
}
|
||||
|
||||
QSyntaxStyle* QFramedTextAttribute::syntaxStyle() const
|
||||
{
|
||||
return m_style;
|
||||
}
|
||||
|
||||
QSizeF QFramedTextAttribute::intrinsicSize(QTextDocument*, int, const QTextFormat&)
|
||||
{
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
void QFramedTextAttribute::drawObject(QPainter* painter,
|
||||
const QRectF& rect,
|
||||
QTextDocument*,
|
||||
int,
|
||||
const QTextFormat& format)
|
||||
{
|
||||
// Casting
|
||||
auto textCharFormat = reinterpret_cast<const QTextCharFormat&>(format);
|
||||
|
||||
// Getting font data
|
||||
auto font = textCharFormat.font();
|
||||
QFontMetrics metrics(font);
|
||||
|
||||
// Getting required size
|
||||
auto string = format.property(FramedString).toString();
|
||||
auto stringSize = metrics.boundingRect(string).size();
|
||||
|
||||
// Creating frame rect
|
||||
QRectF drawRect(rect.topLeft(), stringSize);
|
||||
drawRect.moveTop(rect.top() - stringSize.height());
|
||||
drawRect.adjust(0, 4, 0, 4);
|
||||
|
||||
// Drawing
|
||||
painter->setPen(m_style->getFormat("Occurrences").background().color());
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
painter->drawRoundedRect(drawRect, 4, 4);
|
||||
}
|
||||
|
||||
void QFramedTextAttribute::frame(QTextCursor cursor)
|
||||
{
|
||||
auto text = cursor.document()->findBlockByNumber(cursor.blockNumber()).text();
|
||||
|
||||
QTextCharFormat format;
|
||||
format.setObjectType(type());
|
||||
format.setProperty(FramedString, cursor.selectedText());
|
||||
|
||||
if (cursor.selectionEnd() > cursor.selectionStart())
|
||||
{
|
||||
cursor.setPosition(cursor.selectionStart());
|
||||
}
|
||||
else
|
||||
{
|
||||
cursor.setPosition(cursor.selectionEnd());
|
||||
}
|
||||
|
||||
cursor.insertText(
|
||||
QString(QChar::ObjectReplacementCharacter),
|
||||
format
|
||||
);
|
||||
}
|
||||
|
||||
void QFramedTextAttribute::clear(QTextCursor cursor)
|
||||
{
|
||||
auto doc = cursor.document();
|
||||
|
||||
for (auto blockIndex = 0;
|
||||
blockIndex < doc->blockCount();
|
||||
++blockIndex)
|
||||
{
|
||||
auto block = doc->findBlockByNumber(blockIndex);
|
||||
|
||||
auto formats = block.textFormats();
|
||||
int offset = 0;
|
||||
|
||||
for (auto& format : formats)
|
||||
{
|
||||
if (format.format.objectType() == type())
|
||||
{
|
||||
cursor.setPosition(block.position() + format.start - offset);
|
||||
cursor.deleteChar();
|
||||
++offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
external/QCodeEditor/src/internal/QGLSLCompleter.cpp
vendored
Normal file
42
external/QCodeEditor/src/internal/QGLSLCompleter.cpp
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// QCodeEditor
|
||||
#include <QGLSLCompleter>
|
||||
#include <QLanguage>
|
||||
|
||||
// Qt
|
||||
#include <QStringListModel>
|
||||
#include <QFile>
|
||||
|
||||
QGLSLCompleter::QGLSLCompleter(QObject *parent) :
|
||||
QCompleter(parent)
|
||||
{
|
||||
// Setting up GLSL types
|
||||
QStringList list;
|
||||
|
||||
Q_INIT_RESOURCE(qcodeeditor_resources);
|
||||
QFile fl(":/languages/glsl.xml");
|
||||
|
||||
if (!fl.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QLanguage language(&fl);
|
||||
|
||||
if (!language.isLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto keys = language.keys();
|
||||
for (auto&& key : keys)
|
||||
{
|
||||
auto names = language.names(key);
|
||||
list.append(names);
|
||||
}
|
||||
|
||||
setModel(new QStringListModel(list, this));
|
||||
setCompletionColumn(0);
|
||||
setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
||||
setCaseSensitivity(Qt::CaseSensitive);
|
||||
setWrapAround(true);
|
||||
}
|
||||
162
external/QCodeEditor/src/internal/QGLSLHighlighter.cpp
vendored
Normal file
162
external/QCodeEditor/src/internal/QGLSLHighlighter.cpp
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// QCodeEditor
|
||||
#include <QGLSLHighlighter>
|
||||
#include <QLanguage>
|
||||
#include <QSyntaxStyle>
|
||||
|
||||
// Qt
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
QGLSLHighlighter::QGLSLHighlighter(QTextDocument* document) :
|
||||
QStyleSyntaxHighlighter(document),
|
||||
m_highlightRules (),
|
||||
m_includePattern (QRegularExpression(R"(#include\s+([<"][a-zA-Z0-9*._]+[">]))")),
|
||||
m_functionPattern (QRegularExpression(R"(\b([A-Za-z0-9_]+(?:\s+|::))*([A-Za-z0-9_]+)(?=\())")),
|
||||
m_defTypePattern (QRegularExpression(R"(\b([A-Za-z0-9_]+)\s+[A-Za-z]{1}[A-Za-z0-9_]+\s*[;=])")),
|
||||
m_commentStartPattern(QRegularExpression(R"(/\*)")),
|
||||
m_commentEndPattern (QRegularExpression(R"(\*/)"))
|
||||
{
|
||||
Q_INIT_RESOURCE(qcodeeditor_resources);
|
||||
QFile fl(":/languages/glsl.xml");
|
||||
|
||||
if (!fl.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QLanguage language(&fl);
|
||||
|
||||
if (!language.isLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto keys = language.keys();
|
||||
for (auto&& key : keys)
|
||||
{
|
||||
auto names = language.names(key);
|
||||
for (auto&& name : names)
|
||||
{
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(QString(R"(\b%1\b)").arg(name)),
|
||||
key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Following rules has higher priority to display
|
||||
// than language specific keys
|
||||
// So they must be applied at last.
|
||||
// Numbers
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(\b(0b|0x){0,1}[\d.']+\b)"),
|
||||
"Number"
|
||||
});
|
||||
|
||||
// Define
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(#[a-zA-Z_]+)"),
|
||||
"Preprocessor"
|
||||
});
|
||||
|
||||
// Single line
|
||||
m_highlightRules.append({
|
||||
QRegularExpression("//[^\n]*"),
|
||||
"Comment"
|
||||
});
|
||||
}
|
||||
|
||||
void QGLSLHighlighter::highlightBlock(const QString& text)
|
||||
{
|
||||
|
||||
{
|
||||
auto matchIterator = m_includePattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat("Preprocessor")
|
||||
);
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(1),
|
||||
match.capturedLength(1),
|
||||
syntaxStyle()->getFormat("String")
|
||||
);
|
||||
}
|
||||
}
|
||||
// Checking for function
|
||||
{
|
||||
auto matchIterator = m_functionPattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat("Type")
|
||||
);
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(2),
|
||||
match.capturedLength(2),
|
||||
syntaxStyle()->getFormat("Function")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& rule : m_highlightRules)
|
||||
{
|
||||
auto matchIterator = rule.pattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat(rule.formatName)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentBlockState(0);
|
||||
|
||||
int startIndex = 0;
|
||||
if (previousBlockState() != 1)
|
||||
{
|
||||
startIndex = text.indexOf(m_commentStartPattern);
|
||||
}
|
||||
|
||||
while (startIndex >= 0)
|
||||
{
|
||||
auto match = m_commentEndPattern.match(text, startIndex);
|
||||
|
||||
int endIndex = match.capturedStart();
|
||||
int commentLength = 0;
|
||||
|
||||
if (endIndex == -1)
|
||||
{
|
||||
setCurrentBlockState(1);
|
||||
commentLength = text.length() - startIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
commentLength = endIndex - startIndex + match.capturedLength();
|
||||
}
|
||||
|
||||
setFormat(
|
||||
startIndex,
|
||||
commentLength,
|
||||
syntaxStyle()->getFormat("Comment")
|
||||
);
|
||||
startIndex = text.indexOf(m_commentStartPattern, startIndex + commentLength);
|
||||
}
|
||||
}
|
||||
66
external/QCodeEditor/src/internal/QJSONHighlighter.cpp
vendored
Normal file
66
external/QCodeEditor/src/internal/QJSONHighlighter.cpp
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// QCodeEditor
|
||||
#include <QJSONHighlighter>
|
||||
#include <QSyntaxStyle>
|
||||
|
||||
|
||||
QJSONHighlighter::QJSONHighlighter(QTextDocument* document) :
|
||||
QStyleSyntaxHighlighter(document),
|
||||
m_highlightRules(),
|
||||
m_keyRegex(R"(("[^\r\n:]+?")\s*:)")
|
||||
{
|
||||
auto keywords = QStringList()
|
||||
<< "null" << "true" << "false";
|
||||
|
||||
for (auto&& keyword : keywords)
|
||||
{
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(QString(R"(\b%1\b)").arg(keyword)),
|
||||
"Keyword"
|
||||
});
|
||||
}
|
||||
|
||||
// Numbers
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(\b(0b|0x){0,1}[\d.']+\b)"),
|
||||
"Number"
|
||||
});
|
||||
|
||||
// Strings
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"("[^\n"]*")"),
|
||||
"String"
|
||||
});
|
||||
}
|
||||
|
||||
void QJSONHighlighter::highlightBlock(const QString& text)
|
||||
{
|
||||
for (auto&& rule : m_highlightRules)
|
||||
{
|
||||
auto matchIterator = rule.pattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat(rule.formatName)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Special treatment for key regex
|
||||
auto matchIterator = m_keyRegex.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(1),
|
||||
match.capturedLength(1),
|
||||
syntaxStyle()->getFormat("Keyword")
|
||||
);
|
||||
}
|
||||
}
|
||||
79
external/QCodeEditor/src/internal/QLanguage.cpp
vendored
Normal file
79
external/QCodeEditor/src/internal/QLanguage.cpp
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
// QCodeEditor
|
||||
#include <QLanguage>
|
||||
|
||||
// Qt
|
||||
#include <QIODevice>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
QLanguage::QLanguage(QIODevice* device, QObject* parent) :
|
||||
QObject(parent),
|
||||
m_loaded(false),
|
||||
m_list()
|
||||
{
|
||||
load(device);
|
||||
}
|
||||
|
||||
bool QLanguage::load(QIODevice* device)
|
||||
{
|
||||
if (device == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
QXmlStreamReader reader(device);
|
||||
|
||||
QString name;
|
||||
QStringList list;
|
||||
bool readText = false;
|
||||
|
||||
while (!reader.atEnd() && !reader.hasError())
|
||||
{
|
||||
auto type = reader.readNext();
|
||||
|
||||
if (type == QXmlStreamReader::TokenType::StartElement)
|
||||
{
|
||||
if (reader.name().toString() == "section") {
|
||||
if (!list.empty())
|
||||
{
|
||||
m_list[name] = list;
|
||||
list.clear();
|
||||
}
|
||||
|
||||
name = reader.attributes().value("name").toString();
|
||||
} else if (reader.name().toString() == "name") {
|
||||
readText = true;
|
||||
}
|
||||
}
|
||||
else if (type == QXmlStreamReader::TokenType::Characters &&
|
||||
readText)
|
||||
{
|
||||
list << reader.text().toString();
|
||||
readText = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!list.empty())
|
||||
{
|
||||
m_list[name] = list;
|
||||
}
|
||||
|
||||
m_loaded = !reader.hasError();
|
||||
|
||||
return m_loaded;
|
||||
}
|
||||
|
||||
QStringList QLanguage::keys()
|
||||
{
|
||||
return m_list.keys();
|
||||
}
|
||||
|
||||
QStringList QLanguage::names(const QString& key)
|
||||
{
|
||||
return m_list[key];
|
||||
}
|
||||
|
||||
bool QLanguage::isLoaded() const
|
||||
{
|
||||
return m_loaded;
|
||||
}
|
||||
100
external/QCodeEditor/src/internal/QLineNumberArea.cpp
vendored
Normal file
100
external/QCodeEditor/src/internal/QLineNumberArea.cpp
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// QCodeEditor
|
||||
#include <QLineNumberArea>
|
||||
#include <QSyntaxStyle>
|
||||
#include <QCodeEditor>
|
||||
|
||||
// Qt
|
||||
#include <QTextEdit>
|
||||
#include <QPainter>
|
||||
#include <QPaintEvent>
|
||||
#include <QTextBlock>
|
||||
#include <QScrollBar>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
|
||||
QLineNumberArea::QLineNumberArea(QCodeEditor* parent) :
|
||||
QWidget(parent),
|
||||
m_syntaxStyle(nullptr),
|
||||
m_codeEditParent(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QSize QLineNumberArea::sizeHint() const
|
||||
{
|
||||
if (m_codeEditParent == nullptr)
|
||||
{
|
||||
return QWidget::sizeHint();
|
||||
}
|
||||
|
||||
// Calculating width
|
||||
int digits = 1;
|
||||
int max = qMax(1, m_codeEditParent->document()->blockCount());
|
||||
while (max >= 10) {
|
||||
max /= 10;
|
||||
++digits;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= 0x050B00
|
||||
int space = 13 + m_codeEditParent->fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
|
||||
#else
|
||||
int space = 13 + m_codeEditParent->fontMetrics().width(QLatin1Char('9')) * digits;
|
||||
#endif
|
||||
|
||||
return {space, 0};
|
||||
}
|
||||
|
||||
void QLineNumberArea::setSyntaxStyle(QSyntaxStyle* style)
|
||||
{
|
||||
m_syntaxStyle = style;
|
||||
}
|
||||
|
||||
QSyntaxStyle* QLineNumberArea::syntaxStyle() const
|
||||
{
|
||||
return m_syntaxStyle;
|
||||
}
|
||||
|
||||
void QLineNumberArea::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
// Clearing rect to update
|
||||
painter.fillRect(
|
||||
event->rect(),
|
||||
m_syntaxStyle->getFormat("Text").background().color()
|
||||
);
|
||||
|
||||
auto blockNumber = m_codeEditParent->getFirstVisibleBlock();
|
||||
auto block = m_codeEditParent->document()->findBlockByNumber(blockNumber);
|
||||
auto top = (int) m_codeEditParent->document()->documentLayout()->blockBoundingRect(block).translated(0, -m_codeEditParent->verticalScrollBar()->value()).top();
|
||||
auto bottom = top + (int) m_codeEditParent->document()->documentLayout()->blockBoundingRect(block).height();
|
||||
|
||||
auto currentLine = m_syntaxStyle->getFormat("CurrentLineNumber").foreground().color();
|
||||
auto otherLines = m_syntaxStyle->getFormat("LineNumber").foreground().color();
|
||||
|
||||
painter.setFont(m_codeEditParent->font());
|
||||
|
||||
while (block.isValid() && top <= event->rect().bottom())
|
||||
{
|
||||
if (block.isVisible() && bottom >= event->rect().top())
|
||||
{
|
||||
QString number = QString::number(blockNumber + 1);
|
||||
|
||||
auto isCurrentLine = m_codeEditParent->textCursor().blockNumber() == blockNumber;
|
||||
painter.setPen(isCurrentLine ? currentLine : otherLines);
|
||||
|
||||
painter.drawText(
|
||||
-5,
|
||||
top,
|
||||
sizeHint().width(),
|
||||
m_codeEditParent->fontMetrics().height(),
|
||||
Qt::AlignRight,
|
||||
number
|
||||
);
|
||||
}
|
||||
|
||||
block = block.next();
|
||||
top = bottom;
|
||||
bottom = top + (int) m_codeEditParent->document()->documentLayout()->blockBoundingRect(block).height();
|
||||
++blockNumber;
|
||||
}
|
||||
}
|
||||
42
external/QCodeEditor/src/internal/QLuaCompleter.cpp
vendored
Normal file
42
external/QCodeEditor/src/internal/QLuaCompleter.cpp
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// QCodeEditor
|
||||
#include <QLuaCompleter>
|
||||
#include <QLanguage>
|
||||
|
||||
// Qt
|
||||
#include <QStringListModel>
|
||||
#include <QFile>
|
||||
|
||||
QLuaCompleter::QLuaCompleter(QObject *parent) :
|
||||
QCompleter(parent)
|
||||
{
|
||||
// Setting up GLSL types
|
||||
QStringList list;
|
||||
|
||||
Q_INIT_RESOURCE(qcodeeditor_resources);
|
||||
QFile fl(":/languages/lua.xml");
|
||||
|
||||
if (!fl.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QLanguage language(&fl);
|
||||
|
||||
if (!language.isLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto keys = language.keys();
|
||||
for (auto&& key : keys)
|
||||
{
|
||||
auto names = language.names(key);
|
||||
list.append(names);
|
||||
}
|
||||
|
||||
setModel(new QStringListModel(list, this));
|
||||
setCompletionColumn(0);
|
||||
setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
||||
setCaseSensitivity(Qt::CaseSensitive);
|
||||
setWrapAround(true);
|
||||
}
|
||||
196
external/QCodeEditor/src/internal/QLuaHighlighter.cpp
vendored
Normal file
196
external/QCodeEditor/src/internal/QLuaHighlighter.cpp
vendored
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
// QCodeEditor
|
||||
#include <QLuaHighlighter>
|
||||
#include <QSyntaxStyle>
|
||||
#include <QLanguage>
|
||||
|
||||
// Qt
|
||||
#include <QFile>
|
||||
|
||||
|
||||
QLuaHighlighter::QLuaHighlighter(QTextDocument* document) :
|
||||
QStyleSyntaxHighlighter(document),
|
||||
m_highlightRules(),
|
||||
m_highlightBlockRules(),
|
||||
m_requirePattern(QRegularExpression(R"(require\s*([("'][a-zA-Z0-9*._]+['")]))")),
|
||||
m_functionPattern(QRegularExpression(R"(\b([A-Za-z0-9_]+(?:\s+|::))*([A-Za-z0-9_]+)(?=\())")),
|
||||
m_defTypePattern(QRegularExpression(R"(\b([A-Za-z0-9_]+)\s+[A-Za-z]{1}[A-Za-z0-9_]+\s*[=])"))
|
||||
{
|
||||
Q_INIT_RESOURCE(qcodeeditor_resources);
|
||||
QFile fl(":/languages/lua.xml");
|
||||
|
||||
if (!fl.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QLanguage language(&fl);
|
||||
|
||||
if (!language.isLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto keys = language.keys();
|
||||
for (auto&& key : keys)
|
||||
{
|
||||
auto names = language.names(key);
|
||||
for (auto&& name : names)
|
||||
{
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(QString(R"(\b\s{0,1}%1\s{0,1}\b)").arg(name)),
|
||||
key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Numbers
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(\b(0b|0x){0,1}[\d.']+\b)"),
|
||||
"Number"
|
||||
});
|
||||
|
||||
// Strings
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(["'][^\n"]*["'])"),
|
||||
"String"
|
||||
});
|
||||
|
||||
// Preprocessor
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(#\![a-zA-Z_]+)"),
|
||||
"Preprocessor"
|
||||
});
|
||||
|
||||
// Single line
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(--[^\n]*)"),
|
||||
"Comment"
|
||||
});
|
||||
|
||||
// Multiline comments
|
||||
m_highlightBlockRules.append({
|
||||
QRegularExpression(R"(--\[\[)"),
|
||||
QRegularExpression(R"(--\]\])"),
|
||||
"Comment"
|
||||
});
|
||||
|
||||
// Multiline string
|
||||
m_highlightBlockRules.append({
|
||||
QRegularExpression(R"(\[\[)"),
|
||||
QRegularExpression(R"(\]\])"),
|
||||
"String"
|
||||
});
|
||||
}
|
||||
|
||||
void QLuaHighlighter::highlightBlock(const QString& text)
|
||||
{
|
||||
{ // Checking for require
|
||||
auto matchIterator = m_requirePattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat("Preprocessor")
|
||||
);
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(1),
|
||||
match.capturedLength(1),
|
||||
syntaxStyle()->getFormat("String")
|
||||
);
|
||||
}
|
||||
}
|
||||
{ // Checking for function
|
||||
auto matchIterator = m_functionPattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat("Type")
|
||||
);
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(2),
|
||||
match.capturedLength(2),
|
||||
syntaxStyle()->getFormat("Function")
|
||||
);
|
||||
}
|
||||
}
|
||||
{ // checking for type
|
||||
auto matchIterator = m_defTypePattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(1),
|
||||
match.capturedLength(1),
|
||||
syntaxStyle()->getFormat("Type")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& rule : m_highlightRules)
|
||||
{
|
||||
auto matchIterator = rule.pattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat(rule.formatName)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentBlockState(0);
|
||||
int startIndex = 0;
|
||||
int highlightRuleId = previousBlockState();
|
||||
if (highlightRuleId < 1 || highlightRuleId > m_highlightBlockRules.size()) {
|
||||
for(int i = 0; i < m_highlightBlockRules.size(); ++i) {
|
||||
startIndex = text.indexOf(m_highlightBlockRules.at(i).startPattern);
|
||||
if (startIndex >= 0) {
|
||||
highlightRuleId = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (startIndex >= 0)
|
||||
{
|
||||
const auto &blockRules = m_highlightBlockRules.at(highlightRuleId - 1);
|
||||
auto match = blockRules.endPattern.match(text, startIndex);
|
||||
|
||||
int endIndex = match.capturedStart();
|
||||
int matchLength = 0;
|
||||
|
||||
if (endIndex == -1)
|
||||
{
|
||||
setCurrentBlockState(highlightRuleId);
|
||||
matchLength = text.length() - startIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
matchLength = endIndex - startIndex + match.capturedLength();
|
||||
}
|
||||
|
||||
setFormat(
|
||||
startIndex,
|
||||
matchLength,
|
||||
syntaxStyle()->getFormat(blockRules.formatName)
|
||||
);
|
||||
startIndex = text.indexOf(blockRules.startPattern, startIndex + matchLength);
|
||||
}
|
||||
}
|
||||
42
external/QCodeEditor/src/internal/QPythonCompleter.cpp
vendored
Normal file
42
external/QCodeEditor/src/internal/QPythonCompleter.cpp
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// QCodeEditor
|
||||
#include <QPythonCompleter>
|
||||
#include <QLanguage>
|
||||
|
||||
// Qt
|
||||
#include <QStringListModel>
|
||||
#include <QFile>
|
||||
|
||||
QPythonCompleter::QPythonCompleter(QObject *parent) :
|
||||
QCompleter(parent)
|
||||
{
|
||||
// Setting up Python types
|
||||
QStringList list;
|
||||
|
||||
Q_INIT_RESOURCE(qcodeeditor_resources);
|
||||
QFile fl(":/languages/python.xml");
|
||||
|
||||
if (!fl.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QLanguage language(&fl);
|
||||
|
||||
if (!language.isLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto keys = language.keys();
|
||||
for (auto&& key : keys)
|
||||
{
|
||||
auto names = language.names(key);
|
||||
list.append(names);
|
||||
}
|
||||
|
||||
setModel(new QStringListModel(list, this));
|
||||
setCompletionColumn(0);
|
||||
setModelSorting(QCompleter::CaseInsensitivelySortedModel);
|
||||
setCaseSensitivity(Qt::CaseSensitive);
|
||||
setWrapAround(true);
|
||||
}
|
||||
163
external/QCodeEditor/src/internal/QPythonHighlighter.cpp
vendored
Normal file
163
external/QCodeEditor/src/internal/QPythonHighlighter.cpp
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// QCodeEditor
|
||||
#include <QPythonHighlighter>
|
||||
#include <QLanguage>
|
||||
#include <QSyntaxStyle>
|
||||
|
||||
// Qt
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
QPythonHighlighter::QPythonHighlighter(QTextDocument* document) :
|
||||
QStyleSyntaxHighlighter(document),
|
||||
m_highlightRules (),
|
||||
m_highlightBlockRules(),
|
||||
m_includePattern (QRegularExpression(R"(import \w+)")),
|
||||
m_functionPattern (QRegularExpression(R"(\b([A-Za-z0-9_]+(?:\.))*([A-Za-z0-9_]+)(?=\())")),
|
||||
m_defTypePattern (QRegularExpression(R"(\b([A-Za-z0-9_]+)\s+[A-Za-z]{1}[A-Za-z0-9_]+\s*[;=])"))
|
||||
{
|
||||
Q_INIT_RESOURCE(qcodeeditor_resources);
|
||||
QFile fl(":/languages/python.xml");
|
||||
|
||||
if (!fl.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QLanguage language(&fl);
|
||||
|
||||
if (!language.isLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto keys = language.keys();
|
||||
for (auto&& key : keys)
|
||||
{
|
||||
auto names = language.names(key);
|
||||
for (auto&& name : names)
|
||||
{
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(QString(R"(\b%1\b)").arg(name)),
|
||||
key
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Following rules has higher priority to display
|
||||
// than language specific keys
|
||||
// So they must be applied at last.
|
||||
// Numbers
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"(\b(0b|0x){0,1}[\d.']+\b)"),
|
||||
"Number"
|
||||
});
|
||||
|
||||
// Strings
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"("[^\n"]*")"),
|
||||
"String"
|
||||
});
|
||||
m_highlightRules.append({
|
||||
QRegularExpression(R"('[^\n"]*')"),
|
||||
"String"
|
||||
});
|
||||
|
||||
// Single line comment
|
||||
m_highlightRules.append({
|
||||
QRegularExpression("#[^\n]*"),
|
||||
"Comment"
|
||||
});
|
||||
|
||||
// Multiline string
|
||||
m_highlightBlockRules.append({
|
||||
QRegularExpression("(''')"),
|
||||
QRegularExpression("(''')"),
|
||||
"String"
|
||||
});
|
||||
m_highlightBlockRules.append({
|
||||
QRegularExpression("(\"\"\")"),
|
||||
QRegularExpression("(\"\"\")"),
|
||||
"String"
|
||||
});
|
||||
}
|
||||
|
||||
void QPythonHighlighter::highlightBlock(const QString& text)
|
||||
{
|
||||
// Checking for function
|
||||
{
|
||||
auto matchIterator = m_functionPattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat("Type")
|
||||
);
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(2),
|
||||
match.capturedLength(2),
|
||||
syntaxStyle()->getFormat("Function")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& rule : m_highlightRules)
|
||||
{
|
||||
auto matchIterator = rule.pattern.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat(rule.formatName)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentBlockState(0);
|
||||
int startIndex = 0;
|
||||
int highlightRuleId = previousBlockState();
|
||||
if (highlightRuleId < 1 || highlightRuleId > m_highlightBlockRules.size()) {
|
||||
for(int i = 0; i < m_highlightBlockRules.size(); ++i) {
|
||||
startIndex = text.indexOf(m_highlightBlockRules.at(i).startPattern);
|
||||
|
||||
if (startIndex >= 0) {
|
||||
highlightRuleId = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (startIndex >= 0)
|
||||
{
|
||||
const auto &blockRules = m_highlightBlockRules.at(highlightRuleId - 1);
|
||||
auto match = blockRules.endPattern.match(text, startIndex+1); // Should be + length of start pattern
|
||||
|
||||
int endIndex = match.capturedStart();
|
||||
int matchLength = 0;
|
||||
|
||||
if (endIndex == -1)
|
||||
{
|
||||
setCurrentBlockState(highlightRuleId);
|
||||
matchLength = text.length() - startIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
matchLength = endIndex - startIndex + match.capturedLength();
|
||||
}
|
||||
|
||||
setFormat(
|
||||
startIndex,
|
||||
matchLength,
|
||||
syntaxStyle()->getFormat(blockRules.formatName)
|
||||
);
|
||||
startIndex = text.indexOf(blockRules.startPattern, startIndex + matchLength);
|
||||
}
|
||||
}
|
||||
19
external/QCodeEditor/src/internal/QStyleSyntaxHighlighter.cpp
vendored
Normal file
19
external/QCodeEditor/src/internal/QStyleSyntaxHighlighter.cpp
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// QCodeEditor
|
||||
#include <QStyleSyntaxHighlighter>
|
||||
|
||||
QStyleSyntaxHighlighter::QStyleSyntaxHighlighter(QTextDocument* document) :
|
||||
QSyntaxHighlighter(document),
|
||||
m_syntaxStyle(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void QStyleSyntaxHighlighter::setSyntaxStyle(QSyntaxStyle* style)
|
||||
{
|
||||
m_syntaxStyle = style;
|
||||
}
|
||||
|
||||
QSyntaxStyle* QStyleSyntaxHighlighter::syntaxStyle() const
|
||||
{
|
||||
return m_syntaxStyle;
|
||||
}
|
||||
155
external/QCodeEditor/src/internal/QSyntaxStyle.cpp
vendored
Normal file
155
external/QCodeEditor/src/internal/QSyntaxStyle.cpp
vendored
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
// QCodeEditor
|
||||
#include <QSyntaxStyle>
|
||||
|
||||
// Qt
|
||||
#include <QDebug>
|
||||
#include <QXmlStreamReader>
|
||||
#include <QFile>
|
||||
|
||||
QSyntaxStyle::QSyntaxStyle(QObject* parent) :
|
||||
QObject(parent),
|
||||
m_name(),
|
||||
m_data(),
|
||||
m_loaded(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool QSyntaxStyle::load(QString fl)
|
||||
{
|
||||
QXmlStreamReader reader(fl);
|
||||
|
||||
while (!reader.atEnd() && !reader.hasError())
|
||||
{
|
||||
auto token = reader.readNext();
|
||||
|
||||
if(token == QXmlStreamReader::StartElement)
|
||||
{
|
||||
if (reader.name().toString() == "style-scheme") {
|
||||
if (reader.attributes().hasAttribute("name"))
|
||||
{
|
||||
m_name = reader.attributes().value("name").toString();
|
||||
}
|
||||
} else if (reader.name().toString() == "style") {
|
||||
auto attributes = reader.attributes();
|
||||
|
||||
auto name = attributes.value("name");
|
||||
|
||||
QTextCharFormat format;
|
||||
|
||||
if (attributes.hasAttribute("background"))
|
||||
{
|
||||
format.setBackground(QColor(attributes.value("background").toString()));
|
||||
}
|
||||
|
||||
if (attributes.hasAttribute("foreground"))
|
||||
{
|
||||
format.setForeground(QColor(attributes.value("foreground").toString()));
|
||||
}
|
||||
|
||||
if (attributes.hasAttribute("bold")
|
||||
&& attributes.value("bold").toString() == "true") {
|
||||
format.setFontWeight(QFont::Weight::Bold);
|
||||
}
|
||||
|
||||
if (attributes.hasAttribute("italic")
|
||||
&& attributes.value("italic").toString() == "true") {
|
||||
format.setFontItalic(true);
|
||||
}
|
||||
|
||||
if (attributes.hasAttribute("underlineStyle"))
|
||||
{
|
||||
auto underline = attributes.value("underlineStyle").toString();
|
||||
|
||||
auto s = QTextCharFormat::UnderlineStyle::NoUnderline;
|
||||
|
||||
if (underline == "SingleUnderline")
|
||||
{
|
||||
s = QTextCharFormat::UnderlineStyle::SingleUnderline;
|
||||
}
|
||||
else if (underline == "DashUnderline")
|
||||
{
|
||||
s = QTextCharFormat::UnderlineStyle::DashUnderline;
|
||||
}
|
||||
else if (underline == "DotLine")
|
||||
{
|
||||
s = QTextCharFormat::UnderlineStyle::DotLine;
|
||||
}
|
||||
else if (underline == "DashDotLine")
|
||||
{
|
||||
s = QTextCharFormat::DashDotLine;
|
||||
}
|
||||
else if (underline == "DashDotDotLine")
|
||||
{
|
||||
s = QTextCharFormat::DashDotDotLine;
|
||||
}
|
||||
else if (underline == "WaveUnderline")
|
||||
{
|
||||
s = QTextCharFormat::WaveUnderline;
|
||||
}
|
||||
else if (underline == "SpellCheckUnderline")
|
||||
{
|
||||
s = QTextCharFormat::SpellCheckUnderline;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Unknown underline value " << underline;
|
||||
}
|
||||
|
||||
format.setUnderlineStyle(s);
|
||||
}
|
||||
|
||||
m_data[name.toString()] = format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_loaded = !reader.hasError();
|
||||
|
||||
return m_loaded;
|
||||
}
|
||||
|
||||
QString QSyntaxStyle::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
QTextCharFormat QSyntaxStyle::getFormat(QString name) const
|
||||
{
|
||||
auto result = m_data.find(name);
|
||||
|
||||
if (result == m_data.end())
|
||||
{
|
||||
return QTextCharFormat();
|
||||
}
|
||||
|
||||
return result.value();
|
||||
}
|
||||
|
||||
bool QSyntaxStyle::isLoaded() const
|
||||
{
|
||||
return m_loaded;
|
||||
}
|
||||
|
||||
QSyntaxStyle* QSyntaxStyle::defaultStyle()
|
||||
{
|
||||
static QSyntaxStyle style;
|
||||
|
||||
if (!style.isLoaded())
|
||||
{
|
||||
Q_INIT_RESOURCE(qcodeeditor_resources);
|
||||
QFile fl(":/default_style.xml");
|
||||
|
||||
if (!fl.open(QIODevice::ReadOnly))
|
||||
{
|
||||
return &style;
|
||||
}
|
||||
|
||||
if (!style.load(fl.readAll()))
|
||||
{
|
||||
qDebug() << "Can't load default style.";
|
||||
}
|
||||
}
|
||||
|
||||
return &style;
|
||||
}
|
||||
111
external/QCodeEditor/src/internal/QXMLHighlighter.cpp
vendored
Normal file
111
external/QCodeEditor/src/internal/QXMLHighlighter.cpp
vendored
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// QCodeEditor
|
||||
#include <QXMLHighlighter>
|
||||
#include <QSyntaxStyle>
|
||||
|
||||
|
||||
QXMLHighlighter::QXMLHighlighter(QTextDocument* document) :
|
||||
QStyleSyntaxHighlighter(document),
|
||||
m_xmlKeywordRegexes (),
|
||||
m_xmlElementRegex (R"(<[\s]*[/]?[\s]*([^\n][a-zA-Z-_:]*)(?=[\s/>]))"),
|
||||
m_xmlAttributeRegex (R"(\w+(?=\=))"),
|
||||
m_xmlValueRegex (R"("[^\n"]+"(?=\??[\s/>]))"),
|
||||
m_xmlCommentBeginRegex(R"(<!--)"),
|
||||
m_xmlCommentEndRegex (R"(-->)")
|
||||
{
|
||||
m_xmlKeywordRegexes
|
||||
<< QRegularExpression("<\\?")
|
||||
<< QRegularExpression("/>")
|
||||
<< QRegularExpression(">")
|
||||
<< QRegularExpression("<")
|
||||
<< QRegularExpression("</")
|
||||
<< QRegularExpression("\\?>");
|
||||
}
|
||||
|
||||
void QXMLHighlighter::highlightBlock(const QString& text)
|
||||
{
|
||||
// Special treatment for xml element regex as we use captured text to emulate lookbehind
|
||||
auto matchIterator = m_xmlElementRegex.globalMatch(text);
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
syntaxStyle()->getFormat("Keyword") // XML ELEMENT FORMAT
|
||||
);
|
||||
}
|
||||
|
||||
// Highlight xml keywords *after* xml elements to fix any occasional / captured into the enclosing element
|
||||
|
||||
for (auto&& regex : m_xmlKeywordRegexes)
|
||||
{
|
||||
highlightByRegex(
|
||||
syntaxStyle()->getFormat("Keyword"),
|
||||
regex,
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
highlightByRegex(
|
||||
syntaxStyle()->getFormat("Text"),
|
||||
m_xmlAttributeRegex,
|
||||
text
|
||||
);
|
||||
|
||||
setCurrentBlockState(0);
|
||||
|
||||
int startIndex = 0;
|
||||
if (previousBlockState() != 1)
|
||||
{
|
||||
startIndex = text.indexOf(m_xmlCommentBeginRegex);
|
||||
}
|
||||
|
||||
while (startIndex >= 0)
|
||||
{
|
||||
auto match = m_xmlCommentEndRegex.match(text, startIndex);
|
||||
|
||||
int endIndex = match.capturedStart();
|
||||
int commentLength = 0;
|
||||
|
||||
if (endIndex == -1)
|
||||
{
|
||||
setCurrentBlockState(1);
|
||||
commentLength = text.length() - startIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
commentLength = endIndex - startIndex + match.capturedLength();
|
||||
}
|
||||
|
||||
setFormat(
|
||||
startIndex,
|
||||
commentLength,
|
||||
syntaxStyle()->getFormat("Comment")
|
||||
);
|
||||
|
||||
startIndex = text.indexOf(m_xmlCommentBeginRegex, startIndex + commentLength);
|
||||
}
|
||||
|
||||
highlightByRegex(
|
||||
syntaxStyle()->getFormat("String"),
|
||||
m_xmlValueRegex,
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
void QXMLHighlighter::highlightByRegex(const QTextCharFormat& format, const QRegularExpression& regex, const QString& text)
|
||||
{
|
||||
auto matchIterator = regex.globalMatch(text);
|
||||
|
||||
while (matchIterator.hasNext())
|
||||
{
|
||||
auto match = matchIterator.next();
|
||||
|
||||
setFormat(
|
||||
match.capturedStart(),
|
||||
match.capturedLength(),
|
||||
format
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue