diff --git a/limereport/limereport.pri b/limereport/limereport.pri index 649f8f6..385ad4d 100644 --- a/limereport/limereport.pri +++ b/limereport/limereport.pri @@ -62,6 +62,7 @@ SOURCES += \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.cpp \ $$REPORT_PATH/scripteditor/lrscripteditor.cpp \ $$REPORT_PATH/scripteditor/lrcodeeditor.cpp \ + $$REPORT_PATH/scripteditor/lrscripthighlighter.cpp \ $$REPORT_PATH/items/lrsubitemparentpropitem.cpp \ $$REPORT_PATH/items/lralignpropitem.cpp \ $$REPORT_PATH/items/lrhorizontallayout.cpp \ @@ -166,6 +167,7 @@ HEADERS += \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.h \ $$REPORT_PATH/scripteditor/lrscripteditor.h \ $$REPORT_PATH/scripteditor/lrcodeeditor.h \ + $$REPORT_PATH/scripteditor/lrscripthighlighter.h \ $$REPORT_PATH/items/editors/lritemeditorwidget.h \ $$REPORT_PATH/items/editors/lrfonteditorwidget.h \ $$REPORT_PATH/items/editors/lrtextalignmenteditorwidget.h \ diff --git a/limereport/limereport.pro b/limereport/limereport.pro index fa57ae6..e98a9d1 100644 --- a/limereport/limereport.pro +++ b/limereport/limereport.pro @@ -118,10 +118,4 @@ contains(CONFIG,build_translations){ #### EN AUTOMATIC TRANSLATIONS -HEADERS += \ - scripteditor/lrscripthighlighter.h - -SOURCES += \ - scripteditor/lrscripthighlighter.cpp - diff --git a/limereport/scripteditor/lrcodeeditor.cpp b/limereport/scripteditor/lrcodeeditor.cpp index ae3aec9..273a1b3 100644 --- a/limereport/scripteditor/lrcodeeditor.cpp +++ b/limereport/scripteditor/lrcodeeditor.cpp @@ -24,7 +24,9 @@ CodeEditor::CodeEditor(QWidget *parent) updateLineNumberAreaWidth(0); highlightCurrentLine(); - (void) new ScriptHighlighter(document()); + new ScriptHighlighter(document()); + connect(this, SIGNAL(cursorPositionChanged()), + this, SLOT(matchParentheses())); } void CodeEditor::setCompleter(QCompleter *value) @@ -143,6 +145,105 @@ QString CodeEditor::textUnderCursor() const return tc.selectedText(); } +bool CodeEditor::matchLeftParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numLeftParentheses) +{ + TextBlockData *data = static_cast(currentBlock.userData()); + QVector infos = data->parentheses(); + + int docPos = currentBlock.position(); + for (; i < infos.size(); ++i) { + ParenthesisInfo *info = infos.at(i); + + if (info->character == parenthesisType) { + ++numLeftParentheses; + continue; + } + + if (info->character == getParenthesisReverceChar(parenthesisType)){ + if (numLeftParentheses == 0) { + createParenthesisSelection(docPos + info->position); + return true; + } else + --numLeftParentheses; + } + + } + + currentBlock = currentBlock.next(); + if (currentBlock.isValid()) + return matchLeftParenthesis(currentBlock, parenthesisType, 0, numLeftParentheses); + + return false; +} + +bool CodeEditor::matchRightParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numRightParentheses) +{ + TextBlockData *data = static_cast(currentBlock.userData()); + QVector parentheses = data->parentheses(); + + int docPos = currentBlock.position(); + for (; i > -1 && parentheses.size() > 0; --i) { + ParenthesisInfo *info = parentheses.at(i); + if (info->character == parenthesisType) { + ++numRightParentheses; + continue; + } + if (info->character == getParenthesisReverceChar(parenthesisType)){ + if (numRightParentheses == 0) { + createParenthesisSelection(docPos + info->position); + return true; + } else + --numRightParentheses; + } + } + + currentBlock = currentBlock.previous(); + if (currentBlock.isValid()) + return matchRightParenthesis(currentBlock, parenthesisType, 0, numRightParentheses); + + return false; +} + +void CodeEditor::createParenthesisSelection(int pos) +{ + QList selections = extraSelections(); + + QTextEdit::ExtraSelection selection; + QTextCharFormat format = selection.format; + format.setBackground(QColor("#619934")); + format.setForeground(QColor("#ffffff")); + selection.format = format; + + QTextCursor cursor = textCursor(); + cursor.setPosition(pos); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); + selection.cursor = cursor; + + selections.append(selection); + + setExtraSelections(selections); +} + +bool CodeEditor::charIsParenthesis(QChar character, ParenthesisType type) +{ + for (int i = 0; i < PARENHEIS_COUNT; ++i){ + if (character == parenthesisCharacters[type][i]) + return true; + } + return false; +} + +QChar CodeEditor::getParenthesisReverceChar(QChar parenthesisChar) +{ + for (int i = 0; i < PARENHEIS_COUNT; ++i){ + if ( parenthesisCharacters[RightParenthesis][i] == parenthesisChar) + return parenthesisCharacters[LeftParenthesis][i]; + if ( parenthesisCharacters[LeftParenthesis][i] == parenthesisChar) + return parenthesisCharacters[RightParenthesis][i]; + } + return ' '; +} + void CodeEditor::insertCompletion(const QString &completion) { if (m_compleater->widget() != this) @@ -155,7 +256,7 @@ void CodeEditor::insertCompletion(const QString &completion) setTextCursor(tc); } -void CodeEditor::updateLineNumberAreaWidth(int newBlockCount) +void CodeEditor::updateLineNumberAreaWidth(int /*newBlockCount*/) { setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); } @@ -167,7 +268,7 @@ void CodeEditor::highlightCurrentLine() if (!isReadOnly()) { QTextEdit::ExtraSelection selection; - QColor lineColor = QColor(QPalette().background().color()).darker(160); + QColor lineColor = QColor(QPalette().background().color()).darker(100); selection.format.setBackground(lineColor); selection.format.setProperty(QTextFormat::FullWidthSelection, true); @@ -190,4 +291,32 @@ void CodeEditor::updateLineNumberArea(const QRect& rect, int dy) updateLineNumberAreaWidth(0); } +void CodeEditor::matchParentheses() +{ + QList selections; + setExtraSelections(selections); + + TextBlockData *data = static_cast(textCursor().block().userData()); + + if (data) { + QVector infos = data->parentheses(); + + int pos = textCursor().block().position(); + for (int i = 0; i < infos.size(); ++i) { + ParenthesisInfo *info = infos.at(i); + + int curPos = textCursor().position() - textCursor().block().position(); + + if ( (info->position == (curPos - 1)) && charIsParenthesis(info->character, LeftParenthesis)) + { + if (matchLeftParenthesis(textCursor().block(), info->character, i + 1, 0)) + createParenthesisSelection(pos + info->position); + } else if ( (info->position == (curPos - 1)) && charIsParenthesis(info->character, RightParenthesis)) { + if (matchRightParenthesis(textCursor().block(), info->character, i - 1, 0)) + createParenthesisSelection(pos + info->position); + } + } + } +} + } //namespace LimeReport diff --git a/limereport/scripteditor/lrcodeeditor.h b/limereport/scripteditor/lrcodeeditor.h index 83c2dc0..851583f 100644 --- a/limereport/scripteditor/lrcodeeditor.h +++ b/limereport/scripteditor/lrcodeeditor.h @@ -3,6 +3,7 @@ #include #include +#include "lrscripthighlighter.h" QT_BEGIN_NAMESPACE class QWidget; @@ -28,11 +29,17 @@ protected: void resizeEvent(QResizeEvent *event); private: QString textUnderCursor() const; + bool matchLeftParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numLeftParentheses); + bool matchRightParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numRightParentheses); + void createParenthesisSelection(int pos); + bool charIsParenthesis(QChar character, ParenthesisType type); + QChar getParenthesisReverceChar(QChar parenthesisChar); private slots: void insertCompletion(const QString& completion); void updateLineNumberAreaWidth(int newBlockCount); void highlightCurrentLine(); void updateLineNumberArea(const QRect &rect, int dy); + void matchParentheses(); private: QCompleter* m_compleater; QWidget *lineNumberArea; diff --git a/limereport/scripteditor/lrscripthighlighter.cpp b/limereport/scripteditor/lrscripthighlighter.cpp index d85a571..d88e18f 100644 --- a/limereport/scripteditor/lrscripthighlighter.cpp +++ b/limereport/scripteditor/lrscripthighlighter.cpp @@ -1,5 +1,6 @@ #include "lrscripthighlighter.h" #include +#include namespace LimeReport{ @@ -17,25 +18,38 @@ static const char *const keywords[KEYWORDS_COUNT] = { enum LiteralsType{SpaceFound, AlpahabetFound, NumberFound, HashFound, SlashFound, AsterixFound, BracketFound, QuotationFound, ApostropheFound, SeparatorFound, BackSlashFound, LiteralsCount}; -enum States {MayBeKeyWord, Code, MayBeComment, Comment, Comment2, MayBeComment2End, String, String2, MayBeNumber, Separator, StatesCount}; +enum States {Start, MayBeKeyWord, Code, MayBeComment, Comment, Comment2, MayBeComment2End, String, String2, MayBeNumber, Separator, StatesCount}; + +void ScriptHighlighter::createParentheisisInfo(const char& literal, TextBlockData *data, const QString& text) +{ + int pos = text.indexOf(literal); + while (pos != -1) { + ParenthesisInfo *info = new ParenthesisInfo; + info->character = literal; + info->position = pos; + data->insert(info); + pos = text.indexOf(literal, pos + 1); + } +} void ScriptHighlighter::highlightBlock(const QString& text) { int literal = -1; bool lastWasBackSlash = false; - int state = previousBlockState() != -1 ? previousBlockState() : MayBeKeyWord ; + int state = previousBlockState() != -1 ? previousBlockState() : Start ; int oldState = -1; int stateMaschine[StatesCount][LiteralsCount] = { + {Separator, MayBeKeyWord, MayBeNumber, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Separator}, {Separator, MayBeKeyWord, Code, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Separator}, {Separator, Code, Code, Separator, Separator, Separator, Separator, String, String2, Separator, Separator}, - {Separator, Code, Code, Code, Comment, Comment2, Code, String, String2, Separator, Code}, + {Separator, Code, MayBeNumber, Code, Comment, Comment2, Code, String, String2, Separator, Code}, {Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment}, {Comment2, Comment2, Comment2, Comment2, Comment2, MayBeComment2End, Comment2, Comment2, Comment2, Comment2, Comment2}, {Comment2, Comment2, Comment2, Comment2, Separator, Comment2, Comment2, Comment2, Comment2, Comment2, Comment2}, {String, String, String, String, String, String, String, Separator, String, String, String}, {String2, String2, String2, String2, String2, String2, String2, String2, Separator, String2, String2}, {Separator, Code, MayBeNumber, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Code}, - {Separator, MayBeKeyWord, MayBeNumber, Separator, MayBeComment, String, String2, Separator, Separator } + {Separator, MayBeKeyWord, MayBeNumber, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Separator } }; QString buffer; @@ -86,33 +100,127 @@ void ScriptHighlighter::highlightBlock(const QString& text) oldState = state; state = stateMaschine[state][literal]; + buffer += currentChar; + if (oldState != state){ switch( state ){ case MayBeComment: + if (oldState == MayBeNumber){ + setFormat(i-(buffer.length()-1), buffer.length()-1, m_formats[NumberFormat]); + } + buffer.clear(); + buffer += currentChar; + + break; + case MayBeKeyWord: + case MayBeNumber: buffer.clear(); buffer += currentChar; break; case Comment2: - buffer += currentChar; - qDebug()<= text.length()) break; } + + TextBlockData *data = new TextBlockData; + + + for (int i = 0; i < PARENHEIS_COUNT; ++i){ + createParentheisisInfo(parenthesisCharacters[LeftParenthesis][i].toLatin1(), data, text); + createParentheisisInfo(parenthesisCharacters[RightParenthesis][i].toLatin1(), data, text); + } +// createParentheisisInfo('(', data, text); +// createParentheisisInfo(')', data, text); +// createParentheisisInfo('{', data, text); +// createParentheisisInfo('}', data, text); +// createParentheisisInfo('[', data, text); +// createParentheisisInfo(']', data, text); + + setCurrentBlockUserData(data); +} + +bool ScriptHighlighter::isKeyWord(const QString& word) +{ + for (int i = 0; i < KEYWORDS_COUNT-1; ++i){ + if (QLatin1String(keywords[i]) == word) return true; + } + return false; +} + +bool ScriptHighlighter::isDark(QColor color) +{ + double a = 1 - ( 0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255; + return (a < 0.5); +} + +ScriptHighlighter::ScriptHighlighter(QTextDocument* parent): + QSyntaxHighlighter(parent) +{ + + if ( isDark(QPalette().background().color())){ + m_formats[NumberFormat].setForeground(Qt::darkBlue); + m_formats[StringFormat].setForeground(Qt::darkGreen); + m_formats[KeywordFormat].setForeground(Qt::darkYellow); + m_formats[CommentFormat].setForeground(Qt::darkGreen); + m_formats[CommentFormat].setFontItalic(true); + } else { + m_formats[NumberFormat].setForeground(QColor("#ff6aad")); + m_formats[StringFormat].setForeground(QColor("#b27f40")); + m_formats[KeywordFormat].setForeground(QColor("#45c5d5")); + m_formats[CommentFormat].setForeground(QColor("#a1a4a9")); + m_formats[CommentFormat].setFontItalic(true); + } +} + +QVector TextBlockData::parentheses() +{ + return m_parentheses; +} + +void TextBlockData::insert(ParenthesisInfo* info) +{ + int i = 0; + while (i < m_parentheses.size() && + info->position > m_parentheses.at(i)->position) + ++i; + + m_parentheses.insert(i, info); } } // namespace LimeReport diff --git a/limereport/scripteditor/lrscripthighlighter.h b/limereport/scripteditor/lrscripthighlighter.h index 3a54693..4580484 100644 --- a/limereport/scripteditor/lrscripthighlighter.h +++ b/limereport/scripteditor/lrscripthighlighter.h @@ -5,13 +5,46 @@ namespace LimeReport{ +enum ParenthesisType {LeftParenthesis, RightParenthesis, ParenthesisTypeCount}; + +#define PARENHEIS_COUNT 3 +const QChar parenthesisCharacters[ParenthesisTypeCount][PARENHEIS_COUNT] = { + {'(', '{', '['}, + {')', '}', ']'} +}; + +struct ParenthesisInfo +{ + char character; + int position; +}; + +class TextBlockData : public QTextBlockUserData +{ +public: + TextBlockData(){} + QVector parentheses(); + void insert(ParenthesisInfo *info); + +private: + QVector m_parentheses; +}; + class ScriptHighlighter : QSyntaxHighlighter{ public: - ScriptHighlighter(QTextDocument* parent): QSyntaxHighlighter(parent){} - // QSyntaxHighlighter interface + ScriptHighlighter(QTextDocument* parent); protected: void highlightBlock(const QString& text); + enum ScriptFormats { + NumberFormat, StringFormat, KeywordFormat, + CommentFormat, FormatsCount + }; + QTextCharFormat m_formats[FormatsCount]; + bool isKeyWord(const QString& word); + bool isDark(QColor color); + void createParentheisisInfo(const char& literal, TextBlockData *data, const QString& text); }; + } #endif // LRSCRIPTHIGHLIGHTER_H