1 /* 2 Actiona 3 Copyright (C) 2005 Jonathan Mercier-Ganady 4 5 Actiona is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation, either version 3 of the License, or 8 (at your option) any later version. 9 10 Actiona is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 GNU General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 Contact : jmgr@jmgr.info 19 */ 20 // This file uses some code lines from the Ofi Labs X2 project 21 // Copyright (C) 2010 Ariya Hidayat <ariya.hidayat@gmail.com> 22 // Licensed under GNU/GPLv3 23 24 #include "codehighlighter.h" 25 #include "keywords.h" 26 27 #include <QFile> 28 #include <QDebug> 29 30 namespace ActionTools 31 { CodeHighlighter(QTextDocument * parent)32 CodeHighlighter::CodeHighlighter(QTextDocument *parent) 33 : QSyntaxHighlighter(parent) 34 { 35 mFormats[KeywordFormat].setForeground(Qt::darkBlue); 36 mFormats[KeywordFormat].setFontWeight(QFont::Bold); 37 38 mFormats[ReservedFormat].setForeground(Qt::red); 39 mFormats[ReservedFormat].setFontWeight(QFont::Bold); 40 mFormats[ReservedFormat].setFontStrikeOut(true); 41 42 mFormats[CodeObjectsFormat].setForeground(Qt::darkBlue); 43 mFormats[CodeObjectsFormat].setFontWeight(QFont::Bold); 44 45 mFormats[OperatorFormat].setForeground(Qt::red); 46 47 mFormats[NumberFormat].setForeground(Qt::darkMagenta); 48 49 mFormats[CommentFormat].setForeground(Qt::darkGreen); 50 51 mFormats[StringFormat].setForeground(Qt::darkRed); 52 53 for(const QString &keyword: usedKeywords) 54 mUsedKeywords.insert(keyword); 55 56 for(const QString &keyword: reservedKeywords) 57 mReservedKeywords.insert(keyword); 58 } 59 highlightBlock(const QString & text)60 void CodeHighlighter::highlightBlock(const QString &text) 61 { 62 // parsing state 63 enum 64 { 65 Start = 0, 66 Number = 1, 67 Identifier = 2, 68 String = 3, 69 Comment = 4, 70 Regex = 5 71 }; 72 73 QList<int> bracketPositions; 74 75 int blockState = previousBlockState(); 76 int bracketLevel = blockState >> 4; 77 int state = blockState & 15; 78 if (blockState < 0) 79 { 80 bracketLevel = 0; 81 state = Start; 82 } 83 84 int start = 0; 85 int i = 0; 86 while (i <= text.length()) 87 { 88 QChar ch = (i < text.length()) ? text.at(i) : QChar(); 89 QChar next = (i < text.length() - 1) ? text.at(i + 1) : QChar(); 90 91 switch (state) 92 { 93 94 case Start: 95 start = i; 96 if (ch.isSpace()) 97 { 98 ++i; 99 } 100 else if (ch.isDigit()) 101 { 102 ++i; 103 state = Number; 104 } 105 else if (ch.isLetter() || ch == QLatin1Char('_')) 106 { 107 ++i; 108 state = Identifier; 109 } 110 else if (ch == QLatin1Char('\'') || ch == QLatin1Char('\"')) 111 { 112 ++i; 113 state = String; 114 } 115 else if (ch == QLatin1Char('/') && next == QLatin1Char('*')) 116 { 117 ++i; 118 ++i; 119 state = Comment; 120 } 121 else if (ch == QLatin1Char('/') && next == QLatin1Char('/')) 122 { 123 i = text.length(); 124 setFormat(start, text.length(), mFormats[CommentFormat]); 125 } 126 else if (ch == QLatin1Char('/') && next != QLatin1Char('*')) 127 { 128 ++i; 129 state = Regex; 130 } 131 else 132 { 133 if (!QStringLiteral("(){}[]").contains(ch)) 134 setFormat(start, 1, mFormats[OperatorFormat]); 135 if (ch == QLatin1Char('{') || ch == QLatin1Char('}')) 136 { 137 bracketPositions += i; 138 if (ch == QLatin1Char('{')) 139 bracketLevel++; 140 else 141 bracketLevel--; 142 } 143 ++i; 144 state = Start; 145 } 146 break; 147 148 case Number: 149 if (ch.isSpace() || !ch.isDigit()) 150 { 151 setFormat(start, i - start, mFormats[NumberFormat]); 152 state = Start; 153 } 154 else 155 ++i; 156 break; 157 158 case Identifier: 159 if (ch.isSpace() || !(ch.isDigit() || ch.isLetter() || ch == QLatin1Char('_'))) 160 { 161 QString token = text.mid(start, i - start).trimmed(); 162 if (mUsedKeywords.contains(token)) 163 setFormat(start, i - start, mFormats[KeywordFormat]); 164 else if (mReservedKeywords.contains(token)) 165 setFormat(start, i - start, mFormats[ReservedFormat]); 166 else if (mCodeObjects.contains(token)) 167 setFormat(start, i - start, mFormats[CodeObjectsFormat]); 168 state = Start; 169 } 170 else 171 ++i; 172 break; 173 174 case String: 175 if (ch == text.at(start)) 176 { 177 QChar prev = (i > 0) ? text.at(i - 1) : QChar(); 178 if (prev != QLatin1Char('\\')) 179 { 180 ++i; 181 setFormat(start, i - start, mFormats[StringFormat]); 182 state = Start; 183 } 184 else 185 ++i; 186 } 187 else 188 ++i; 189 break; 190 191 case Comment: 192 if (ch == QLatin1Char('*') && next == QLatin1Char('/')) 193 { 194 ++i; 195 ++i; 196 setFormat(start, i - start, mFormats[CommentFormat]); 197 state = Start; 198 } 199 else 200 ++i; 201 break; 202 203 case Regex: 204 if (ch == QLatin1Char('/')) 205 { 206 QChar prev = (i > 0) ? text.at(i - 1) : QChar(); 207 if (prev != QLatin1Char('\\')) 208 { 209 ++i; 210 setFormat(start, i - start, mFormats[StringFormat]); 211 state = Start; 212 } 213 else 214 ++i; 215 } 216 else 217 ++i; 218 break; 219 220 default: 221 state = Start; 222 break; 223 } 224 } 225 226 if (state == Comment) 227 setFormat(start, text.length(), mFormats[CommentFormat]); 228 } 229 addCodeObject(const QString & name)230 void CodeHighlighter::addCodeObject(const QString &name) 231 { 232 mCodeObjects.insert(name); 233 } 234 } 235