1 /*******************************************************************
2
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Fritzing is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
18
19 ********************************************************************
20
21 $Revision: 6904 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $
24
25 ********************************************************************/
26
27 #include "highlighter.h"
28 #include "syntaxer.h"
29
30 #include "../debugdialog.h"
31
32 #include <QRegExp>
33 #include <stdlib.h>
34
35 #define STRINGOFFSET 10
36 #define COMMENTOFFSET 100
37 static const QChar CEscapeChar('\\');
38
39 QHash <QString, QTextCharFormat *> Highlighter::m_styleFormats;
40
Highlighter(QTextEdit * textEdit)41 Highlighter::Highlighter(QTextEdit * textEdit) : QSyntaxHighlighter(textEdit)
42 {
43 m_syntaxer = NULL;
44 }
45
~Highlighter()46 Highlighter::~Highlighter()
47 {
48 }
49
loadStyles(const QString & filename)50 void Highlighter::loadStyles(const QString & filename) {
51 QFile file(filename);
52
53 QString errorStr;
54 int errorLine;
55 int errorColumn;
56
57 QDomDocument domDocument;
58 if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
59 return;
60 }
61
62 QDomElement root = domDocument.documentElement();
63 if (root.isNull()) return;
64 if (root.tagName() != "styles") return;
65
66 QDomElement style = root.firstChildElement("style");
67 while (!style.isNull()) {
68 QTextCharFormat * tcf = new QTextCharFormat();
69 QColor color(Qt::black);
70 QString colorString = style.attribute("color");
71 if (!colorString.isEmpty()) {
72 color.setNamedColor(colorString);
73 tcf->setForeground(QBrush(color));
74 }
75 QString italicString = style.attribute("italic");
76 if (italicString.compare("1") == 0) {
77 tcf->setFontItalic(true);
78 }
79 QString boldString = style.attribute("bold");
80 if (boldString.compare("1") == 0) {
81 tcf->setFontWeight(QFont::Bold);
82 }
83 QString underlineString = style.attribute("underline");
84 if (underlineString.compare("1") == 0) {
85 tcf->setFontUnderline(true);
86 }
87
88 m_styleFormats.insert(style.attribute("name"), tcf);
89
90 style = style.nextSiblingElement("style");
91 }
92 }
93
setSyntaxer(Syntaxer * syntaxer)94 void Highlighter::setSyntaxer(Syntaxer * syntaxer) {
95 m_syntaxer = syntaxer;
96 }
97
syntaxer()98 Syntaxer * Highlighter::syntaxer() {
99 return m_syntaxer;
100 }
101
highlightBlock(const QString & text)102 void Highlighter::highlightBlock(const QString &text)
103 {
104 if (!m_syntaxer) return;
105
106 if (text.isEmpty()) {
107 setCurrentBlockState(previousBlockState());
108 return;
109 }
110
111 setCurrentBlockState(0);
112 int startCommentIndex = -1;
113 int startStringIndex = -1;
114 const CommentInfo * currentCommentInfo = NULL;
115 int pbs = previousBlockState();
116 if (pbs <= 0) {
117 m_syntaxer->matchCommentStart(text, 0, startCommentIndex, currentCommentInfo);
118 }
119 else if (pbs >= COMMENTOFFSET) {
120 currentCommentInfo = m_syntaxer->getCommentInfo(previousBlockState() - COMMENTOFFSET);
121 startCommentIndex = 0;
122 }
123 else if (pbs == STRINGOFFSET) {
124 startStringIndex = 0;
125 }
126
127 QString noComment = text;
128
129 while (startCommentIndex >= 0) {
130 int endIndex = currentCommentInfo->m_multiLine ? text.indexOf(currentCommentInfo->m_end, startCommentIndex, currentCommentInfo->m_caseSensitive) : text.length();
131 int commentLength;
132 if (endIndex == -1) {
133 setCurrentBlockState(currentCommentInfo->m_index + COMMENTOFFSET);
134 commentLength = text.length() - startCommentIndex;
135 }
136 else {
137 commentLength = endIndex - startCommentIndex + currentCommentInfo->m_end.length();
138 }
139 noComment.replace(startCommentIndex, commentLength, QString(commentLength, ' '));
140 QTextCharFormat * cf = m_styleFormats.value("Comment", NULL);
141 if (cf != NULL) {
142 setFormat(startCommentIndex, commentLength, *cf);
143 }
144 m_syntaxer->matchCommentStart(text, startCommentIndex + commentLength, startCommentIndex, currentCommentInfo);
145 }
146
147 highlightStrings(startStringIndex, noComment);
148 highlightTerms(noComment);
149 }
150
highlightStrings(int startStringIndex,QString & text)151 void Highlighter::highlightStrings(int startStringIndex, QString & text) {
152 if (startStringIndex < 0) {
153 startStringIndex = m_syntaxer->matchStringStart(text, 0);
154 }
155
156 // TODO: not handling "" as a way to escape-quote
157 while (startStringIndex >= 0) {
158 int endIndex = -1;
159 int ssi = startStringIndex;
160 while (true) {
161 endIndex = m_syntaxer->matchStringEnd(text, ssi + 1);
162 if (!m_syntaxer->hlCStringChar()) {
163 // only some languages use \ to escape
164 break;
165 }
166
167 if (endIndex == -1) {
168 break;
169 }
170
171 // TODO: escape char is backslash only; are there others in other compilers?
172 if (text.at(endIndex - 1) != CEscapeChar) {
173 break;
174 }
175 ssi = endIndex;
176 }
177 int stringLength;
178 if (endIndex == -1) {
179 setCurrentBlockState(STRINGOFFSET);
180 stringLength = text.length() - startStringIndex;
181 }
182 else {
183 stringLength = endIndex - startStringIndex + 1;
184 }
185 text.replace(startStringIndex, stringLength, QString(stringLength, ' '));
186 QTextCharFormat * sf = m_styleFormats.value("String", NULL);
187 if (sf != NULL) {
188 setFormat(startStringIndex, stringLength, *sf);
189 }
190 startStringIndex = m_syntaxer->matchStringStart(text, startStringIndex + stringLength);
191 }
192 }
193
highlightTerms(const QString & text)194 void Highlighter::highlightTerms(const QString & text) {
195 int lastWordBreak = 0;
196 int textLength = text.length();
197 int b;
198 while (lastWordBreak < textLength) {
199 for (b = lastWordBreak; b < textLength; b++) {
200 if (!isWordChar(text.at(b))) break;
201 }
202
203 if (b > lastWordBreak) {
204 TrieLeaf * leaf = NULL;
205 if (m_syntaxer->matches(text.mid(lastWordBreak, b - lastWordBreak), leaf)) {
206 SyntaxerTrieLeaf * stl = dynamic_cast<SyntaxerTrieLeaf *>(leaf);
207 if (stl != NULL) {
208 QString format = Syntaxer::formatFromList(stl->name());
209 QTextCharFormat * tcf = m_styleFormats.value(format, NULL);
210 if (tcf != NULL) {
211 setFormat(lastWordBreak, b - lastWordBreak, *tcf);
212 }
213 }
214 }
215 }
216
217 lastWordBreak = b + 1;
218 }
219 }
220
isWordChar(QChar c)221 bool Highlighter::isWordChar(QChar c) {
222 return c.isLetterOrNumber() || c == '#' || c == '_';
223 }
224