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: 6112 $:
22 $Author: cohen@irascible.com $:
23 $Date: 2012-06-28 00:18:10 +0200 (Do, 28. Jun 2012) $
24
25 ********************************************************************/
26
27 #include "syntaxer.h"
28 #include "../debugdialog.h"
29 #include "../utils/textutils.h"
30
31 #include <QRegExp>
32 #include <QXmlStreamReader>
33
34 QHash<QString, QString> Syntaxer::m_listsToFormats;
35
Syntaxer()36 Syntaxer::Syntaxer() : QObject() {
37 m_trieRoot = NULL;
38 }
39
~Syntaxer()40 Syntaxer::~Syntaxer() {
41 if (m_trieRoot) {
42 delete m_trieRoot;
43 }
44 }
45
loadSyntax(const QString & filename)46 bool Syntaxer::loadSyntax(const QString &filename)
47 {
48 QFile file(filename);
49
50 QString errorStr;
51 int errorLine;
52 int errorColumn;
53
54 QDomDocument domDocument;
55 if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
56 return false;
57 }
58
59 QDomElement root = domDocument.documentElement();
60 if (root.isNull()) return false;
61 if (root.tagName() != "language") return false;
62
63 QDomElement highlighting = root.firstChildElement("highlighting");
64 if (highlighting.isNull()) return false;
65
66 QDomElement general = root.firstChildElement("general");
67 if (general.isNull()) return false;
68
69 QDomElement contexts = highlighting.firstChildElement("contexts");
70 if (contexts.isNull()) return false;
71
72 m_hlCStringChar = false;
73 QDomElement context = contexts.firstChildElement("context");
74 while (!context.isNull()) {
75 if (context.attribute("attribute").compare("Normal Text") == 0) {
76 m_stringDelimiter = getStringDelimiter(context);
77 initListsToFormats(context);
78 }
79 else if (context.attribute("attribute").compare("String") == 0) {
80 QDomElement HlCStringChar = context.firstChildElement("HlCStringChar");
81 if (!HlCStringChar.isNull()) {
82 m_hlCStringChar = true;
83 }
84 }
85 context = context.nextSiblingElement("context");
86 }
87
88 m_canProgram = root.attribute("canProgram", "").compare("true", Qt::CaseInsensitive) == 0;
89 m_name = root.attribute("name");
90 QStringList extensions = root.attribute("extensions").split(";", QString::SkipEmptyParts);
91 if (extensions.count() > 0) {
92 m_extensionString = m_name + " " + QObject::tr("files") + " (";
93 foreach (QString ext, extensions) {
94 m_extensionString += ext + " ";
95 int ix = ext.indexOf(".");
96 if (ix > 0) {
97 ext.remove(0, ix);
98 }
99 m_extensions.append(ext);
100 }
101 m_extensionString.chop(1);
102 m_extensionString += ")";
103 }
104
105 m_trieRoot = new TrieNode('\0');
106
107 QDomElement list = highlighting.firstChildElement("list");
108 while (!list.isNull()) {
109 loadList(list);
110 list = list.nextSiblingElement("list");
111 }
112
113 QDomElement comments = general.firstChildElement("comments");
114 if (!comments.isNull()) {
115 Qt::CaseSensitivity caseSensitivity = comments.attribute("casesensitive").compare("1") == 0 ? Qt::CaseSensitive : Qt::CaseInsensitive;
116 QDomElement comment = comments.firstChildElement("comment");
117 while (!comment.isNull()) {
118 CommentInfo * commentInfo = new CommentInfo(comment.attribute("start"), comment.attribute("end"), caseSensitivity);
119 commentInfo->m_index = m_commentInfo.count();
120 m_commentInfo.append(commentInfo);
121 comment = comment.nextSiblingElement("comment");
122 }
123 }
124
125 return true;
126 }
127
parseForName(const QString & filename)128 QString Syntaxer::parseForName(const QString & filename)
129 {
130 QFile file(filename);
131 file.open(QFile::ReadOnly);
132 QXmlStreamReader xml(&file);
133 xml.setNamespaceProcessing(false);
134
135 while (!xml.atEnd()) {
136 switch (xml.readNext()) {
137 case QXmlStreamReader::StartElement:
138 if (xml.name().toString().compare("language") == 0) {
139 return xml.attributes().value("name").toString();
140 }
141 break;
142 default:
143 break;
144 }
145 }
146
147 return "";
148 }
149
loadList(QDomElement & list)150 void Syntaxer::loadList(QDomElement & list) {
151 QString name = list.attribute("name");
152 SyntaxerTrieLeaf * stf = new SyntaxerTrieLeaf(name);
153 QDomElement item = list.firstChildElement("item");
154 while (!item.isNull()) {
155 QString text;
156 if (TextUtils::findText(item, text)) {
157 QString s = text.trimmed();
158 m_trieRoot->addString(s, false, stf);
159 }
160 item = item.nextSiblingElement("item");
161 }
162 }
163
matches(const QString & string,TrieLeaf * & leaf)164 bool Syntaxer::matches(const QString & string, TrieLeaf * & leaf) {
165 if (m_trieRoot == NULL) return false;
166
167 QString temp = string;
168 return m_trieRoot->matches(temp, leaf);
169 }
170
getCommentInfo(int ix)171 const CommentInfo * Syntaxer::getCommentInfo(int ix) {
172 return m_commentInfo.at(ix);
173 }
174
matchCommentStart(const QString & text,int offset,int & result,const CommentInfo * & resultCommentInfo)175 bool Syntaxer::matchCommentStart(const QString & text, int offset, int & result, const CommentInfo * & resultCommentInfo) {
176 result = -1;
177 foreach (CommentInfo * commentInfo, m_commentInfo) {
178 int si = text.indexOf(commentInfo->m_start, offset, commentInfo->m_caseSensitive);
179 if (si >= 0 && (result < 0 || si < result)) {
180 result = si;
181 resultCommentInfo = commentInfo;
182 }
183 }
184
185 return (result >= offset);
186 }
187
matchStringStart(const QString & text,int offset)188 int Syntaxer::matchStringStart(const QString & text, int offset) {
189 if (m_stringDelimiter.isNull()) return -1;
190
191 return text.indexOf(m_stringDelimiter, offset);
192 }
193
matchStringEnd(const QString & text,int offset)194 int Syntaxer::matchStringEnd(const QString & text, int offset) {
195 return matchStringStart(text, offset);
196 }
197
extensions()198 const QStringList & Syntaxer::extensions() {
199 return m_extensions;
200 }
201
extensionString()202 const QString & Syntaxer::extensionString() {
203 return m_extensionString;
204 }
205
getStringDelimiter(QDomElement & context)206 QChar Syntaxer::getStringDelimiter(QDomElement & context) {
207 QDomElement detectChar = context.firstChildElement("DetectChar");
208 while (!detectChar.isNull()) {
209 if (detectChar.attribute("attribute").compare("String") == 0) {
210 QString c = detectChar.attribute("char");
211 if (c.length() > 0) {
212 return c.at(0);
213 }
214 return QChar();
215 }
216 detectChar = detectChar.nextSiblingElement("DetectChar");
217 }
218
219 return QChar();
220 }
221
initListsToFormats(QDomElement & context)222 void Syntaxer::initListsToFormats(QDomElement & context) {
223 QDomElement keyword = context.firstChildElement("keyword");
224 while (!keyword.isNull()) {
225 QString format = keyword.attribute("attribute");
226 QString list = keyword.attribute("String");
227 if (!format.isEmpty() && !list.isEmpty()) {
228 m_listsToFormats.insert(list, format);
229 }
230 keyword = keyword.nextSiblingElement("keyword");
231 }
232 }
233
formatFromList(const QString & list)234 QString Syntaxer::formatFromList(const QString & list) {
235 return m_listsToFormats.value(list, ___emptyString___);
236 }
237
hlCStringChar()238 bool Syntaxer::hlCStringChar() {
239 return m_hlCStringChar;
240 }
241
242
canProgram()243 bool Syntaxer::canProgram() {
244 return m_canProgram;
245 }
246
247
248 //////////////////////////////////////////////
249
SyntaxerTrieLeaf(QString name)250 SyntaxerTrieLeaf::SyntaxerTrieLeaf(QString name) {
251 m_name = name;
252 }
253
~SyntaxerTrieLeaf()254 SyntaxerTrieLeaf::~SyntaxerTrieLeaf()
255 {
256 }
257
name()258 const QString & SyntaxerTrieLeaf::name()
259 {
260 return m_name;
261 }
262
263 //////////////////////////////////////////////
264
CommentInfo(const QString & start,const QString & end,Qt::CaseSensitivity caseSensitive)265 CommentInfo::CommentInfo(const QString & start, const QString & end, Qt::CaseSensitivity caseSensitive) {
266 m_start = start;
267 m_end = end;
268 m_multiLine = !end.isEmpty();
269 m_caseSensitive = caseSensitive;
270 }
271