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