Script highlighter has been finished

This commit is contained in:
Arin Alexander 2017-09-19 19:05:38 +03:00
parent aa66b6a057
commit f4dbddafad
6 changed files with 295 additions and 22 deletions

View File

@ -62,6 +62,7 @@ SOURCES += \
$$REPORT_PATH/scriptbrowser/lrscriptbrowser.cpp \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.cpp \
$$REPORT_PATH/scripteditor/lrscripteditor.cpp \ $$REPORT_PATH/scripteditor/lrscripteditor.cpp \
$$REPORT_PATH/scripteditor/lrcodeeditor.cpp \ $$REPORT_PATH/scripteditor/lrcodeeditor.cpp \
$$REPORT_PATH/scripteditor/lrscripthighlighter.cpp \
$$REPORT_PATH/items/lrsubitemparentpropitem.cpp \ $$REPORT_PATH/items/lrsubitemparentpropitem.cpp \
$$REPORT_PATH/items/lralignpropitem.cpp \ $$REPORT_PATH/items/lralignpropitem.cpp \
$$REPORT_PATH/items/lrhorizontallayout.cpp \ $$REPORT_PATH/items/lrhorizontallayout.cpp \
@ -166,6 +167,7 @@ HEADERS += \
$$REPORT_PATH/scriptbrowser/lrscriptbrowser.h \ $$REPORT_PATH/scriptbrowser/lrscriptbrowser.h \
$$REPORT_PATH/scripteditor/lrscripteditor.h \ $$REPORT_PATH/scripteditor/lrscripteditor.h \
$$REPORT_PATH/scripteditor/lrcodeeditor.h \ $$REPORT_PATH/scripteditor/lrcodeeditor.h \
$$REPORT_PATH/scripteditor/lrscripthighlighter.h \
$$REPORT_PATH/items/editors/lritemeditorwidget.h \ $$REPORT_PATH/items/editors/lritemeditorwidget.h \
$$REPORT_PATH/items/editors/lrfonteditorwidget.h \ $$REPORT_PATH/items/editors/lrfonteditorwidget.h \
$$REPORT_PATH/items/editors/lrtextalignmenteditorwidget.h \ $$REPORT_PATH/items/editors/lrtextalignmenteditorwidget.h \

View File

@ -118,10 +118,4 @@ contains(CONFIG,build_translations){
#### EN AUTOMATIC TRANSLATIONS #### EN AUTOMATIC TRANSLATIONS
HEADERS += \
scripteditor/lrscripthighlighter.h
SOURCES += \
scripteditor/lrscripthighlighter.cpp

View File

@ -24,7 +24,9 @@ CodeEditor::CodeEditor(QWidget *parent)
updateLineNumberAreaWidth(0); updateLineNumberAreaWidth(0);
highlightCurrentLine(); highlightCurrentLine();
(void) new ScriptHighlighter(document()); new ScriptHighlighter(document());
connect(this, SIGNAL(cursorPositionChanged()),
this, SLOT(matchParentheses()));
} }
void CodeEditor::setCompleter(QCompleter *value) void CodeEditor::setCompleter(QCompleter *value)
@ -143,6 +145,105 @@ QString CodeEditor::textUnderCursor() const
return tc.selectedText(); return tc.selectedText();
} }
bool CodeEditor::matchLeftParenthesis(QTextBlock currentBlock, QChar parenthesisType, int i, int numLeftParentheses)
{
TextBlockData *data = static_cast<TextBlockData *>(currentBlock.userData());
QVector<ParenthesisInfo *> 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<TextBlockData *>(currentBlock.userData());
QVector<ParenthesisInfo *> 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<QTextEdit::ExtraSelection> 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) void CodeEditor::insertCompletion(const QString &completion)
{ {
if (m_compleater->widget() != this) if (m_compleater->widget() != this)
@ -155,7 +256,7 @@ void CodeEditor::insertCompletion(const QString &completion)
setTextCursor(tc); setTextCursor(tc);
} }
void CodeEditor::updateLineNumberAreaWidth(int newBlockCount) void CodeEditor::updateLineNumberAreaWidth(int /*newBlockCount*/)
{ {
setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); setViewportMargins(lineNumberAreaWidth(), 0, 0, 0);
} }
@ -167,7 +268,7 @@ void CodeEditor::highlightCurrentLine()
if (!isReadOnly()) { if (!isReadOnly()) {
QTextEdit::ExtraSelection selection; 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.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true); selection.format.setProperty(QTextFormat::FullWidthSelection, true);
@ -190,4 +291,32 @@ void CodeEditor::updateLineNumberArea(const QRect& rect, int dy)
updateLineNumberAreaWidth(0); updateLineNumberAreaWidth(0);
} }
void CodeEditor::matchParentheses()
{
QList<QTextEdit::ExtraSelection> selections;
setExtraSelections(selections);
TextBlockData *data = static_cast<TextBlockData *>(textCursor().block().userData());
if (data) {
QVector<ParenthesisInfo *> 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 } //namespace LimeReport

View File

@ -3,6 +3,7 @@
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QSyntaxHighlighter> #include <QSyntaxHighlighter>
#include "lrscripthighlighter.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QWidget; class QWidget;
@ -28,11 +29,17 @@ protected:
void resizeEvent(QResizeEvent *event); void resizeEvent(QResizeEvent *event);
private: private:
QString textUnderCursor() const; 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: private slots:
void insertCompletion(const QString& completion); void insertCompletion(const QString& completion);
void updateLineNumberAreaWidth(int newBlockCount); void updateLineNumberAreaWidth(int newBlockCount);
void highlightCurrentLine(); void highlightCurrentLine();
void updateLineNumberArea(const QRect &rect, int dy); void updateLineNumberArea(const QRect &rect, int dy);
void matchParentheses();
private: private:
QCompleter* m_compleater; QCompleter* m_compleater;
QWidget *lineNumberArea; QWidget *lineNumberArea;

View File

@ -1,5 +1,6 @@
#include "lrscripthighlighter.h" #include "lrscripthighlighter.h"
#include <QDebug> #include <QDebug>
#include <QPalette>
namespace LimeReport{ namespace LimeReport{
@ -17,25 +18,38 @@ static const char *const keywords[KEYWORDS_COUNT] = {
enum LiteralsType{SpaceFound, AlpahabetFound, NumberFound, HashFound, SlashFound, AsterixFound, enum LiteralsType{SpaceFound, AlpahabetFound, NumberFound, HashFound, SlashFound, AsterixFound,
BracketFound, QuotationFound, ApostropheFound, SeparatorFound, BackSlashFound, LiteralsCount}; 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) void ScriptHighlighter::highlightBlock(const QString& text)
{ {
int literal = -1; int literal = -1;
bool lastWasBackSlash = false; bool lastWasBackSlash = false;
int state = previousBlockState() != -1 ? previousBlockState() : MayBeKeyWord ; int state = previousBlockState() != -1 ? previousBlockState() : Start ;
int oldState = -1; int oldState = -1;
int stateMaschine[StatesCount][LiteralsCount] = { 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, MayBeKeyWord, Code, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Separator},
{Separator, Code, Code, Separator, Separator, 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}, {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, Comment2, MayBeComment2End, Comment2, Comment2, Comment2, Comment2, Comment2},
{Comment2, Comment2, Comment2, Comment2, Separator, Comment2, 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}, {String, String, String, String, String, String, String, Separator, String, String, String},
{String2, String2, String2, String2, String2, String2, String2, String2, Separator, String2, String2}, {String2, String2, String2, String2, String2, String2, String2, String2, Separator, String2, String2},
{Separator, Code, MayBeNumber, Separator, MayBeComment, Separator, Separator, String, String2, Separator, Code}, {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; QString buffer;
@ -86,33 +100,127 @@ void ScriptHighlighter::highlightBlock(const QString& text)
oldState = state; oldState = state;
state = stateMaschine[state][literal]; state = stateMaschine[state][literal];
buffer += currentChar;
if (oldState != state){ if (oldState != state){
switch( state ){ switch( state ){
case MayBeComment: 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.clear();
buffer += currentChar; buffer += currentChar;
break; break;
case Comment2: case Comment2:
buffer += currentChar; setCurrentBlockState(Comment2);
qDebug()<<buffer;
break;
case Separator: case Separator:
switch(oldState){ switch(oldState){
case Comment2: case MayBeComment2End:
qDebug()<<buffer; setFormat(i-(buffer.length()-1), buffer.length(), m_formats[CommentFormat]);
setCurrentBlockState(-1);
buffer.clear();
break;
case MayBeKeyWord:
if (isKeyWord(buffer.left(buffer.length()-1))){
setFormat(i-(buffer.length()-1), buffer.length()-1, m_formats[KeywordFormat]);
}
buffer.clear();
break;
case MayBeNumber:
setFormat(i-(buffer.length()-1), buffer.length()-1, m_formats[NumberFormat]);
buffer.clear();
case String:
case String2:
setFormat(i-(buffer.length()-1), buffer.length(), m_formats[StringFormat]);
buffer.clear(); buffer.clear();
break; break;
} }
default: default:
break; break;
} }
} else { }
buffer += currentChar;
if ( state == Comment || state == Comment2 ){
setFormat(i-(buffer.length()-1), buffer.length(), m_formats[CommentFormat]);
}
if ( state == String || state == String2 ){
setFormat(i-(buffer.length()-1), buffer.length(), m_formats[StringFormat]);
} }
i++; i++;
if ( i>= text.length()) break; if ( i>= 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<ParenthesisInfo*> 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 } // namespace LimeReport

View File

@ -5,13 +5,46 @@
namespace LimeReport{ 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<ParenthesisInfo *> parentheses();
void insert(ParenthesisInfo *info);
private:
QVector<ParenthesisInfo *> m_parentheses;
};
class ScriptHighlighter : QSyntaxHighlighter{ class ScriptHighlighter : QSyntaxHighlighter{
public: public:
ScriptHighlighter(QTextDocument* parent): QSyntaxHighlighter(parent){} ScriptHighlighter(QTextDocument* parent);
// QSyntaxHighlighter interface
protected: protected:
void highlightBlock(const QString& text); 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 #endif // LRSCRIPTHIGHLIGHTER_H