1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "highlightdefinitionhandler.h"
35 #include "highlightdefinition.h"
36 #include "specificrules.h"
37 #include "itemdata.h"
38 #include "keywordlist.h"
39 #include "context.h"
40 #include "reuse.h"
41 #include "manager2.h"
42 #include "highlighterexception.h"
43 #include <QDebug>
44
45 #include <QtCore/QLatin1String>
46
47 using namespace TextEditor;
48 using namespace Internal;
49
50 namespace {
51 static const QLatin1String kName("name");
52 static const QLatin1String kList("list");
53 static const QLatin1String kItem("item");
54 static const QLatin1String kContext("context");
55 static const QLatin1String kAttribute("attribute");
56 static const QLatin1String kDynamic("dynamic");
57 static const QLatin1String kFallthrough("fallthrough");
58 static const QLatin1String kLineEndContext("lineEndContext");
59 static const QLatin1String kLineBeginContext("lineBeginContext");
60 static const QLatin1String kFallthroughContext("fallthroughContext");
61 static const QLatin1String kBeginRegion("beginRegion");
62 static const QLatin1String kEndRegion("endRegion");
63 static const QLatin1String kLookAhead("lookAhead");
64 static const QLatin1String kFirstNonSpace("firstNonSpace");
65 static const QLatin1String kColumn("column");
66 static const QLatin1String kItemData("itemData");
67 static const QLatin1String kDefStyleNum("defStyleNum");
68 static const QLatin1String kColor("color");
69 static const QLatin1String kSelColor("selColor");
70 static const QLatin1String kItalic("italic");
71 static const QLatin1String kBold("bold");
72 static const QLatin1String kUnderline("underline");
73 static const QLatin1String kStrikeout("strikeOut");
74 static const QLatin1String kSpellChecking("spellChecking");
75 static const QLatin1String kChar("char");
76 static const QLatin1String kChar1("char1");
77 static const QLatin1String kString("String");
78 static const QLatin1String kInsensitive("insensitive");
79 static const QLatin1String kMinimal("minimal");
80 static const QLatin1String kKeywords("keywords");
81 static const QLatin1String kCaseSensitive("casesensitive");
82 static const QLatin1String kWeakDeliminator("weakDeliminator");
83 static const QLatin1String kAdditionalDeliminator("additionalDeliminator");
84 static const QLatin1String kWordWrapDeliminator("wordWrapDeliminator");
85 static const QLatin1String kComment("comment");
86 static const QLatin1String kPosition("position");
87 static const QLatin1String kSingleLine("singleline");
88 static const QLatin1String kMultiLine("multiline");
89 static const QLatin1String kStart("start");
90 static const QLatin1String kEnd("end");
91 static const QLatin1String kRegion("region");
92 static const QLatin1String kDetectChar("DetectChar");
93 static const QLatin1String kDetect2Chars("Detect2Chars");
94 static const QLatin1String kAnyChar("AnyChar");
95 static const QLatin1String kStringDetect("StringDetect");
96 static const QLatin1String kRegExpr("RegExpr");
97 static const QLatin1String kKeyword("keyword");
98 static const QLatin1String kInt("Int");
99 static const QLatin1String kFloat("Float");
100 static const QLatin1String kHlCOct("HlCOct");
101 static const QLatin1String kHlCHex("HlCHex");
102 static const QLatin1String kHlCStringChar("HlCStringChar");
103 static const QLatin1String kHlCChar("HlCChar");
104 static const QLatin1String kRangeDetect("RangeDetect");
105 static const QLatin1String kLineContinue("LineContinue");
106 static const QLatin1String kIncludeRules("IncludeRules");
107 static const QLatin1String kDetectSpaces("DetectSpaces");
108 static const QLatin1String kDetectIdentifier("DetectIdentifier");
109 static const QLatin1String kLanguage("language");
110 static const QLatin1String kExtensions("extensions");
111 static const QLatin1String kIncludeAttrib("includeAttrib");
112 static const QLatin1String kFolding("folding");
113 static const QLatin1String kIndentationSensitive("indentationsensitive");
114 static const QLatin1String kHash("#");
115 static const QLatin1String kDoubleHash("##");
116 }
117
118 HighlightDefinitionHandler::
HighlightDefinitionHandler(const QSharedPointer<HighlightDefinition> & definition)119 HighlightDefinitionHandler(const QSharedPointer<HighlightDefinition> &definition) :
120 m_definition(definition),
121 m_processingKeyword(false),
122 m_initialContext(true)
123 {}
124
~HighlightDefinitionHandler()125 HighlightDefinitionHandler::~HighlightDefinitionHandler()
126 {}
127
startDocument()128 bool HighlightDefinitionHandler::startDocument()
129 {
130 return true;
131 }
132
endDocument()133 bool HighlightDefinitionHandler::endDocument()
134 {
135 processIncludeRules();
136 return true;
137 }
138
startElement(const QString &,const QString &,const QString & qName,const QXmlAttributes & atts)139 bool HighlightDefinitionHandler::startElement(const QString &,
140 const QString &,
141 const QString &qName,
142 const QXmlAttributes &atts)
143 {
144 if (qName == kList) {
145 listElementStarted(atts);
146 } else if (qName == kItem) {
147 itemElementStarted();
148 } else if (qName == kContext) {
149 contextElementStarted(atts);
150 } else if (qName == kItemData) {
151 itemDataElementStarted(atts);
152 } else if (qName == kComment) {
153 commentElementStarted(atts);
154 } else if (qName == kKeywords) {
155 keywordsElementStarted(atts);
156 } else if (qName == kFolding) {
157 foldingElementStarted(atts);
158 } else if (qName == kDetectChar) {
159 detectCharStarted(atts);
160 } else if (qName == kDetect2Chars) {
161 detect2CharsStarted(atts);
162 } else if (qName == kAnyChar) {
163 anyCharStarted(atts);
164 } else if (qName == kStringDetect) {
165 stringDetectedStarted(atts);
166 } else if (qName == kRegExpr) {
167 regExprStarted(atts);
168 } else if (qName == kKeyword) {
169 keywordStarted(atts);
170 } else if (qName == kInt) {
171 intStarted(atts);
172 } else if (qName == kFloat) {
173 floatStarted(atts);
174 } else if (qName == kHlCOct) {
175 hlCOctStarted(atts);
176 } else if (qName == kHlCHex) {
177 hlCHexStarted(atts);
178 } else if (qName == kHlCStringChar) {
179 hlCStringCharStarted(atts);
180 } else if (qName == kHlCChar) {
181 hlCCharStarted(atts);
182 } else if (qName == kRangeDetect) {
183 rangeDetectStarted(atts);
184 } else if (qName == kLineContinue) {
185 lineContinue(atts);
186 } else if (qName == kIncludeRules) {
187 includeRulesStarted(atts);
188 } else if (qName == kDetectSpaces) {
189 detectSpacesStarted(atts);
190 } else if (qName == kDetectIdentifier) {
191 detectIdentifier(atts);
192 }
193
194 return true;
195 }
196
endElement(const QString &,const QString &,const QString & qName)197 bool HighlightDefinitionHandler::endElement(const QString &, const QString &, const QString &qName)
198 {
199 if (qName == kItem) {
200 m_currentList->addKeyword(m_currentKeyword.trimmed());
201 m_processingKeyword = false;
202 } else if (qName == kDetectChar || qName == kDetect2Chars || qName == kAnyChar ||
203 qName == kStringDetect || qName == kRegExpr || qName == kKeyword || qName == kInt ||
204 qName == kFloat || qName == kHlCOct || qName == kHlCHex || qName == kHlCStringChar ||
205 qName == kHlCChar || qName == kRangeDetect || qName == kLineContinue ||
206 qName == kDetectSpaces || qName == kDetectIdentifier) {
207 m_currentRule.pop();
208 }
209
210 return true;
211 }
212
characters(const QString & ch)213 bool HighlightDefinitionHandler::characters(const QString& ch)
214 {
215 // Character data of an element may be reported in more than one chunk.
216 if (m_processingKeyword)
217 m_currentKeyword.append(ch);
218
219 return true;
220 }
221
listElementStarted(const QXmlAttributes & atts)222 void HighlightDefinitionHandler::listElementStarted(const QXmlAttributes &atts)
223 {
224 m_currentList = m_definition->createKeywordList(atts.value(kName));
225 }
226
itemElementStarted()227 void HighlightDefinitionHandler::itemElementStarted()
228 {
229 m_currentKeyword.clear();
230 m_processingKeyword = true;
231 }
232
contextElementStarted(const QXmlAttributes & atts)233 void HighlightDefinitionHandler::contextElementStarted(const QXmlAttributes &atts)
234 {
235 m_currentContext = m_definition->createContext(atts.value(kName), m_initialContext);
236 m_currentContext->setDefinition(m_definition);
237 m_currentContext->setItemData(atts.value(kAttribute));
238 m_currentContext->setDynamic(atts.value(kDynamic));
239 m_currentContext->setFallthrough(atts.value(kFallthrough));
240 m_currentContext->setFallthroughContext(atts.value(kFallthroughContext));
241 m_currentContext->setLineBeginContext(atts.value(kLineBeginContext));
242 m_currentContext->setLineEndContext(atts.value(kLineEndContext));
243
244 m_initialContext = false;
245 }
246
ruleElementStarted(const QXmlAttributes & atts,const QSharedPointer<Rule> & rule)247 void HighlightDefinitionHandler::ruleElementStarted(const QXmlAttributes &atts,
248 const QSharedPointer<Rule> &rule)
249 {
250 // The definition of a rule is not necessarily the same of its enclosing context because of
251 // externally included rules.
252 rule->setDefinition(m_definition);
253 rule->setItemData(atts.value(kAttribute));
254 rule->setContext(atts.value(kContext));
255 rule->setBeginRegion(atts.value(kBeginRegion));
256 rule->setEndRegion(atts.value(kEndRegion));
257 rule->setLookAhead(atts.value(kLookAhead));
258 rule->setFirstNonSpace(atts.value(kFirstNonSpace));
259 rule->setColumn(atts.value(kColumn));
260
261 if (m_currentRule.isEmpty())
262 m_currentContext->addRule(rule);
263 else
264 m_currentRule.top()->addChild(rule);
265
266 m_currentRule.push(rule);
267 }
268
itemDataElementStarted(const QXmlAttributes & atts) const269 void HighlightDefinitionHandler::itemDataElementStarted(const QXmlAttributes &atts) const
270 {
271 QSharedPointer<ItemData> itemData = m_definition->createItemData(atts.value(kName));
272 itemData->setStyle(atts.value(kDefStyleNum));
273 itemData->setColor(atts.value(kColor));
274 itemData->setSelectionColor(atts.value(kSelColor));
275 itemData->setItalic(atts.value(kItalic));
276 itemData->setBold(atts.value(kBold));
277 itemData->setUnderlined(atts.value(kUnderline));
278 itemData->setStrikeOut(atts.value(kStrikeout));
279 itemData->setSpellChecking(atts.value(kSpellChecking));
280 }
281
commentElementStarted(const QXmlAttributes & atts) const282 void HighlightDefinitionHandler::commentElementStarted(const QXmlAttributes &atts) const
283 {
284 const QString &commentType = atts.value(kName);
285 if (commentType.compare(kSingleLine, Qt::CaseInsensitive) == 0) {
286 m_definition->setSingleLineComment(atts.value(kStart));
287 m_definition->setCommentAfterWhitespaces(atts.value(kPosition));
288 } else if (commentType.compare(kMultiLine, Qt::CaseInsensitive) == 0) {
289 m_definition->setMultiLineCommentStart(atts.value(kStart));
290 m_definition->setMultiLineCommentEnd(atts.value(kEnd));
291 m_definition->setMultiLineCommentRegion(atts.value(kRegion));
292 }
293 }
294
keywordsElementStarted(const QXmlAttributes & atts) const295 void HighlightDefinitionHandler::keywordsElementStarted(const QXmlAttributes &atts) const
296 {
297 // Global case sensitivity appears last in the document (required by dtd) and is set here.
298 m_definition->setKeywordsSensitive(atts.value(kCaseSensitive));
299 m_definition->removeDelimiters(atts.value(kWeakDeliminator));
300 m_definition->addDelimiters(atts.value(kAdditionalDeliminator));
301 //@todo: wordWrapDelimiters?
302 }
303
foldingElementStarted(const QXmlAttributes & atts) const304 void HighlightDefinitionHandler::foldingElementStarted(const QXmlAttributes &atts) const
305 {
306 m_definition->setIndentationBasedFolding(atts.value(kIndentationSensitive));
307 }
308
detectCharStarted(const QXmlAttributes & atts)309 void HighlightDefinitionHandler::detectCharStarted(const QXmlAttributes &atts)
310 {
311 DetectCharRule *rule = new DetectCharRule;
312 rule->setChar(atts.value(kChar));
313 rule->setActive(atts.value(kDynamic));
314 ruleElementStarted(atts, QSharedPointer<Rule>(rule));
315 }
316
detect2CharsStarted(const QXmlAttributes & atts)317 void HighlightDefinitionHandler::detect2CharsStarted(const QXmlAttributes &atts)
318 {
319 Detect2CharsRule *rule = new Detect2CharsRule;
320 rule->setChar(atts.value(kChar));
321 rule->setChar1(atts.value(kChar1));
322 rule->setActive(atts.value(kDynamic));
323 ruleElementStarted(atts, QSharedPointer<Rule>(rule));
324 }
325
anyCharStarted(const QXmlAttributes & atts)326 void HighlightDefinitionHandler::anyCharStarted(const QXmlAttributes &atts)
327 {
328 AnyCharRule *rule = new AnyCharRule;
329 rule->setCharacterSet(atts.value(kString));
330 ruleElementStarted(atts, QSharedPointer<Rule>(rule));
331 }
332
stringDetectedStarted(const QXmlAttributes & atts)333 void HighlightDefinitionHandler::stringDetectedStarted(const QXmlAttributes &atts)
334 {
335 StringDetectRule *rule = new StringDetectRule;
336 rule->setString(atts.value(kString));
337 rule->setInsensitive(atts.value(kInsensitive));
338 rule->setActive(atts.value(kDynamic));
339 ruleElementStarted(atts, QSharedPointer<Rule>(rule));
340 }
341
regExprStarted(const QXmlAttributes & atts)342 void HighlightDefinitionHandler::regExprStarted(const QXmlAttributes &atts)
343 {
344 RegExprRule *rule = new RegExprRule;
345 rule->setPattern(atts.value(kString));
346 rule->setMinimal(atts.value(kMinimal));
347 rule->setInsensitive(atts.value(kInsensitive));
348 rule->setActive(atts.value(kDynamic));
349 ruleElementStarted(atts, QSharedPointer<Rule>(rule));
350 }
351
keywordStarted(const QXmlAttributes & atts)352 void HighlightDefinitionHandler::keywordStarted(const QXmlAttributes &atts)
353 {
354 KeywordRule *rule = new KeywordRule(m_definition);
355 rule->setList(atts.value(kString));
356 rule->setInsensitive(atts.value(kInsensitive));
357 ruleElementStarted(atts, QSharedPointer<Rule>(rule));
358 }
359
intStarted(const QXmlAttributes & atts)360 void HighlightDefinitionHandler::intStarted(const QXmlAttributes &atts)
361 {
362 ruleElementStarted(atts, QSharedPointer<Rule>(new IntRule));
363 }
364
floatStarted(const QXmlAttributes & atts)365 void HighlightDefinitionHandler::floatStarted(const QXmlAttributes &atts)
366 {
367 ruleElementStarted(atts, QSharedPointer<Rule>(new FloatRule));
368 }
369
hlCOctStarted(const QXmlAttributes & atts)370 void HighlightDefinitionHandler::hlCOctStarted(const QXmlAttributes &atts)
371 {
372 ruleElementStarted(atts, QSharedPointer<Rule>(new HlCOctRule));
373 }
374
hlCHexStarted(const QXmlAttributes & atts)375 void HighlightDefinitionHandler::hlCHexStarted(const QXmlAttributes &atts)
376 {
377 ruleElementStarted(atts, QSharedPointer<Rule>(new HlCHexRule));
378 }
379
hlCStringCharStarted(const QXmlAttributes & atts)380 void HighlightDefinitionHandler::hlCStringCharStarted(const QXmlAttributes &atts)
381 {
382 ruleElementStarted(atts, QSharedPointer<Rule>(new HlCStringCharRule));
383 }
384
hlCCharStarted(const QXmlAttributes & atts)385 void HighlightDefinitionHandler::hlCCharStarted(const QXmlAttributes &atts)
386 {
387 ruleElementStarted(atts, QSharedPointer<Rule>(new HlCCharRule));
388 }
389
rangeDetectStarted(const QXmlAttributes & atts)390 void HighlightDefinitionHandler::rangeDetectStarted(const QXmlAttributes &atts)
391 {
392 RangeDetectRule *rule = new RangeDetectRule;
393 rule->setChar(atts.value(kChar));
394 rule->setChar1(atts.value(kChar1));
395 ruleElementStarted(atts, QSharedPointer<Rule>(rule));
396 }
397
lineContinue(const QXmlAttributes & atts)398 void HighlightDefinitionHandler::lineContinue(const QXmlAttributes &atts)
399 {
400 ruleElementStarted(atts, QSharedPointer<Rule>(new LineContinueRule));
401 }
402
includeRulesStarted(const QXmlAttributes & atts)403 void HighlightDefinitionHandler::includeRulesStarted(const QXmlAttributes &atts)
404 {
405 // Include rules are treated as instructions for latter processing.
406 IncludeRulesInstruction instruction(atts.value(kContext), m_currentContext->rules().size(),
407 atts.value(kIncludeAttrib));
408
409 // Include rules (as many others) are not allowed as a child.
410 m_currentContext->addIncludeRulesInstruction(instruction);
411 }
412
detectSpacesStarted(const QXmlAttributes & atts)413 void HighlightDefinitionHandler::detectSpacesStarted(const QXmlAttributes &atts)
414 {
415 ruleElementStarted(atts, QSharedPointer<Rule>(new DetectSpacesRule));
416 }
417
detectIdentifier(const QXmlAttributes & atts)418 void HighlightDefinitionHandler::detectIdentifier(const QXmlAttributes &atts)
419 {
420 ruleElementStarted(atts, QSharedPointer<Rule>(new DetectIdentifierRule));
421 }
422
processIncludeRules() const423 void HighlightDefinitionHandler::processIncludeRules() const
424 {
425 const QHash<QString, QSharedPointer<Context> > &allContexts = m_definition->contexts();
426 foreach (const QSharedPointer<Context> &context, allContexts)
427 processIncludeRules(context);
428 }
429
processIncludeRules(const QSharedPointer<Context> & context) const430 void HighlightDefinitionHandler::processIncludeRules(const QSharedPointer<Context> &context) const
431 {
432 if (context->includeRulesInstructions().isEmpty())
433 return;
434
435 int rulesIncluded = 0;
436 const QList<IncludeRulesInstruction> &instructions = context->includeRulesInstructions();
437 foreach (const IncludeRulesInstruction &instruction, instructions) {
438
439 QSharedPointer<Context> sourceContext;
440 const QString &sourceName = instruction.sourceContext();
441 if (sourceName.startsWith(kDoubleHash)) {
442 // This refers to an external definition. The rules included are the ones from its
443 // initial context. Others contexts and rules from the external definition will work
444 // transparently to the highlighter. This is because contexts and rules know the
445 // definition they are from.
446 QString externalName = QString::fromRawData(sourceName.unicode() + 2,
447 sourceName.length() - 2);
448 const QString &id = Manager2::instance()->definitionIdByName(externalName);
449
450 // If there is an incorrect circular dependency among definitions this is skipped.
451 if (Manager2::instance()->isBuildingDefinition(id))
452 continue;
453
454 const QSharedPointer<HighlightDefinition> &externalDefinition =
455 Manager2::instance()->definition(id);
456 if (externalDefinition.isNull())
457 continue;
458
459 sourceContext = externalDefinition->initialContext();
460 } else if (!sourceName.startsWith(kHash)) {
461 sourceContext = m_definition->context(sourceName);
462
463 // Recursion is done only for context direct rules. Child rules are not processed
464 // because they cannot be include rules.
465 processIncludeRules(sourceContext);
466 } else {
467 continue;
468 }
469
470 if (instruction.replaceItemData()) {
471 context->setItemData(sourceContext->itemData());
472 context->setDefinition(sourceContext->definition());
473 }
474
475 foreach (QSharedPointer<Rule> rule, sourceContext->rules()) {
476 context->addRule(rule, instruction.indexHint() + rulesIncluded);
477 ++rulesIncluded;
478 }
479 }
480 context->clearIncludeRulesInstructions();
481 }
482