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