1 /*
2 SuperCollider Qt IDE
3 Copyright (c) 2012 Jakob Leben & Tim Blechmann
4 http://www.audiosynth.com
5
6 This program 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 2 of the License, or
9 (at your option) any later version.
10
11 This program 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 this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <cassert>
22 #include <algorithm>
23
24 #include "highlighter.hpp"
25 #include "../../core/main.hpp"
26 #include "../../core/settings/manager.hpp"
27 #include "../../core/settings/theme.hpp"
28
29 #include <QApplication>
30
31 namespace ScIDE {
32
33 SyntaxHighlighterGlobals* SyntaxHighlighterGlobals::mInstance = 0;
34
SyntaxHighlighterGlobals(Main * main,Settings::Manager * settings)35 SyntaxHighlighterGlobals::SyntaxHighlighterGlobals(Main* main, Settings::Manager* settings): QObject(main) {
36 Q_ASSERT(mInstance == 0);
37 mInstance = this;
38
39 ScLexer::initLexicalRules();
40
41 // initialize formats from settings:
42 applySettings(settings);
43
44 connect(main, SIGNAL(applySettingsRequest(Settings::Manager*)), this, SLOT(applySettings(Settings::Manager*)));
45 }
46
applySettings(Settings::Manager * s)47 void SyntaxHighlighterGlobals::applySettings(Settings::Manager* s) {
48 applySettings(s, "whitespace", WhitespaceFormat);
49 applySettings(s, "keyword", KeywordFormat);
50 applySettings(s, "built-in", BuiltinFormat);
51 applySettings(s, "primitive", PrimitiveFormat);
52 applySettings(s, "class", ClassFormat);
53 applySettings(s, "number", NumberFormat);
54 applySettings(s, "symbol", SymbolFormat);
55 applySettings(s, "env-var", EnvVarFormat);
56 applySettings(s, "string", StringFormat);
57 applySettings(s, "char", CharFormat);
58 applySettings(s, "comment", CommentFormat);
59
60 Q_EMIT(syntaxFormatsChanged());
61 }
62
applySettings(Settings::Manager * s,const QString & key,SyntaxFormat type)63 void SyntaxHighlighterGlobals::applySettings(Settings::Manager* s, const QString& key, SyntaxFormat type) {
64 mFormats[type] = s->getThemeVal(key);
65 }
66
SyntaxHighlighter(QTextDocument * parent)67 SyntaxHighlighter::SyntaxHighlighter(QTextDocument* parent): QSyntaxHighlighter(parent) {
68 mGlobals = SyntaxHighlighterGlobals::instance();
69
70 connect(mGlobals, SIGNAL(syntaxFormatsChanged()), this, SLOT(rehighlight()));
71 }
72
highlightBlockInCode(ScLexer & lexer)73 void SyntaxHighlighter::highlightBlockInCode(ScLexer& lexer) {
74 TextBlockData* blockData = static_cast<TextBlockData*>(currentBlockUserData());
75 Q_ASSERT(blockData);
76
77 const QTextCharFormat* formats = mGlobals->formats();
78
79 do {
80 int tokenPosition = lexer.offset();
81 int tokenLength;
82 Token::Type tokenType = lexer.nextToken(tokenLength);
83
84 switch (tokenType) {
85 case Token::WhiteSpace:
86 setFormat(tokenPosition, tokenLength, formats[WhitespaceFormat]);
87 break;
88
89 case Token::Class:
90 setFormat(tokenPosition, tokenLength, formats[ClassFormat]);
91 break;
92
93 case Token::Builtin:
94 setFormat(tokenPosition, tokenLength, formats[BuiltinFormat]);
95 break;
96
97 case Token::Primitive:
98 setFormat(tokenPosition, tokenLength, formats[PrimitiveFormat]);
99 break;
100
101 case Token::Keyword:
102 setFormat(tokenPosition, tokenLength, formats[KeywordFormat]);
103 break;
104
105 case Token::Symbol:
106 setFormat(tokenPosition, tokenLength, formats[SymbolFormat]);
107 break;
108
109 case Token::SymbolArg:
110 // Don't highlight the trailing ':'
111 setFormat(tokenPosition, tokenLength - 1, formats[SymbolFormat]);
112 break;
113
114 case Token::EnvVar:
115 setFormat(tokenPosition, tokenLength, formats[EnvVarFormat]);
116 break;
117
118 case Token::Char:
119 setFormat(tokenPosition, tokenLength, formats[CharFormat]);
120 break;
121
122 case Token::Float:
123 case Token::HexInt:
124 case Token::ScaleDegreeFloat:
125 case Token::RadixFloat:
126 setFormat(tokenPosition, tokenLength, formats[NumberFormat]);
127 break;
128
129 case Token::SingleLineComment:
130 setFormat(tokenPosition, tokenLength, formats[CommentFormat]);
131 break;
132
133 case Token::MultiLineCommentStart:
134 setFormat(tokenPosition, tokenLength, formats[CommentFormat]);
135 break;
136
137 case Token::StringMark:
138 setFormat(tokenPosition, tokenLength, formats[StringFormat]);
139 break;
140
141 case Token::SymbolMark:
142 setFormat(tokenPosition, tokenLength, formats[SymbolFormat]);
143 break;
144
145 default:;
146 }
147
148 if ((tokenType != Token::WhiteSpace) && (tokenType != Token::SingleLineComment)
149 && (tokenType != Token::MultiLineCommentStart)) {
150 Token token(tokenType, tokenPosition, tokenLength);
151 if (token.length == 1)
152 token.character = lexer.text()[tokenPosition].toLatin1();
153 blockData->tokens.push_back(token);
154 }
155
156 } while (lexer.state() == ScLexer::InCode && lexer.offset() < lexer.text().size());
157 }
158
highlightBlockInString(ScLexer & lexer)159 void SyntaxHighlighter::highlightBlockInString(ScLexer& lexer) {
160 int originalOffset = lexer.offset();
161 int tokenLength;
162 Token::Type tokenType = lexer.nextToken(tokenLength);
163 int range = lexer.offset() - originalOffset;
164 setFormat(originalOffset, range, mGlobals->format(StringFormat));
165
166 if (tokenType == Token::Unknown)
167 return;
168
169 Q_ASSERT(tokenType == Token::StringMark);
170 Token token(tokenType, lexer.offset() - 1, 1);
171 token.character = '"';
172
173 TextBlockData* blockData = static_cast<TextBlockData*>(currentBlockUserData());
174 Q_ASSERT(blockData);
175 blockData->tokens.push_back(token);
176 }
177
highlightBlockInSymbol(ScLexer & lexer)178 void SyntaxHighlighter::highlightBlockInSymbol(ScLexer& lexer) {
179 int originalOffset = lexer.offset();
180 int tokenLength;
181 Token::Type tokenType = lexer.nextToken(tokenLength);
182 int range = lexer.offset() - originalOffset;
183 setFormat(originalOffset, range, mGlobals->format(SymbolFormat));
184
185 if (tokenType == Token::Unknown)
186 return;
187
188 Q_ASSERT(tokenType == Token::SymbolMark);
189 Token token(tokenType, lexer.offset() - 1, 1);
190 token.character = '\'';
191
192 TextBlockData* blockData = static_cast<TextBlockData*>(currentBlockUserData());
193 Q_ASSERT(blockData);
194 blockData->tokens.push_back(token);
195 }
196
highlightBlockInComment(ScLexer & lexer)197 void SyntaxHighlighter::highlightBlockInComment(ScLexer& lexer) {
198 int originalOffset = lexer.offset();
199 int tokenLength;
200 lexer.nextToken(tokenLength);
201 int range = lexer.offset() - originalOffset;
202 setFormat(originalOffset, range, mGlobals->format(CommentFormat));
203 }
204
highlightBlock(const QString & text)205 void SyntaxHighlighter::highlightBlock(const QString& text) {
206 TextBlockData* blockData = static_cast<TextBlockData*>(currentBlockUserData());
207 if (!blockData) {
208 blockData = new TextBlockData;
209 blockData->tokens.reserve(8);
210 setCurrentBlockUserData(blockData);
211 } else {
212 blockData->tokens.clear();
213 }
214
215 int previousState = previousBlockState();
216 if (previousState == -1)
217 previousState = ScLexer::InCode;
218
219 ScLexer lexer(text, 0, previousState);
220
221 while (lexer.offset() < text.size()) {
222 switch (lexer.state()) {
223 case ScLexer::InCode:
224 highlightBlockInCode(lexer);
225 break;
226
227 case ScLexer::InString:
228 highlightBlockInString(lexer);
229 break;
230
231 case ScLexer::InSymbol:
232 highlightBlockInSymbol(lexer);
233 break;
234
235 default:
236 if (lexer.state() >= ScLexer::InComment)
237 highlightBlockInComment(lexer);
238 }
239 }
240
241 setCurrentBlockState(lexer.state());
242 }
243
244 }
245