1 #include "scripthighlighter.hpp"
2 
3 #include <sstream>
4 
5 #include <components/compiler/scanner.hpp>
6 #include <components/compiler/extensions0.hpp>
7 
8 #include "../../model/prefs/setting.hpp"
9 #include "../../model/prefs/category.hpp"
10 
parseInt(int value,const Compiler::TokenLoc & loc,Compiler::Scanner & scanner)11 bool CSVWorld::ScriptHighlighter::parseInt (int value, const Compiler::TokenLoc& loc,
12     Compiler::Scanner& scanner)
13 {
14     highlight (loc, Type_Int);
15     return true;
16 }
17 
parseFloat(float value,const Compiler::TokenLoc & loc,Compiler::Scanner & scanner)18 bool CSVWorld::ScriptHighlighter::parseFloat (float value, const Compiler::TokenLoc& loc,
19     Compiler::Scanner& scanner)
20 {
21     highlight (loc, Type_Float);
22     return true;
23 }
24 
parseName(const std::string & name,const Compiler::TokenLoc & loc,Compiler::Scanner & scanner)25 bool CSVWorld::ScriptHighlighter::parseName (const std::string& name, const Compiler::TokenLoc& loc,
26     Compiler::Scanner& scanner)
27 {
28     highlight (loc, mContext.isId (name) ? Type_Id : Type_Name);
29     return true;
30 }
31 
parseKeyword(int keyword,const Compiler::TokenLoc & loc,Compiler::Scanner & scanner)32 bool CSVWorld::ScriptHighlighter::parseKeyword (int keyword, const Compiler::TokenLoc& loc,
33     Compiler::Scanner& scanner)
34 {
35     if (((mMode==Mode_Console || mMode==Mode_Dialogue) &&
36         (keyword==Compiler::Scanner::K_begin || keyword==Compiler::Scanner::K_end ||
37         keyword==Compiler::Scanner::K_short || keyword==Compiler::Scanner::K_long ||
38         keyword==Compiler::Scanner::K_float))
39         || (mMode==Mode_Console && (keyword==Compiler::Scanner::K_if ||
40         keyword==Compiler::Scanner::K_endif || keyword==Compiler::Scanner::K_else ||
41         keyword==Compiler::Scanner::K_elseif || keyword==Compiler::Scanner::K_while ||
42         keyword==Compiler::Scanner::K_endwhile)))
43         return parseName (loc.mLiteral, loc, scanner);
44 
45     highlight (loc, Type_Keyword);
46     return true;
47 }
48 
parseSpecial(int code,const Compiler::TokenLoc & loc,Compiler::Scanner & scanner)49 bool CSVWorld::ScriptHighlighter::parseSpecial (int code, const Compiler::TokenLoc& loc,
50     Compiler::Scanner& scanner)
51 {
52     highlight (loc, Type_Special);
53     return true;
54 }
55 
parseComment(const std::string & comment,const Compiler::TokenLoc & loc,Compiler::Scanner & scanner)56 bool CSVWorld::ScriptHighlighter::parseComment (const std::string& comment,
57     const Compiler::TokenLoc& loc, Compiler::Scanner& scanner)
58 {
59     highlight (loc, Type_Comment);
60     return true;
61 }
62 
parseEOF(Compiler::Scanner & scanner)63 void CSVWorld::ScriptHighlighter::parseEOF (Compiler::Scanner& scanner)
64 {}
65 
highlight(const Compiler::TokenLoc & loc,Type type)66 void CSVWorld::ScriptHighlighter::highlight (const Compiler::TokenLoc& loc, Type type)
67 {
68     // We should take in account multibyte characters
69     int length = 0;
70     const char* token = loc.mLiteral.c_str();
71     while (*token) length += (*token++ & 0xc0) != 0x80;
72 
73     int index = loc.mColumn;
74 
75     // compensate for bug in Compiler::Scanner (position of token is the character after the token)
76     index -= length;
77 
78     QTextCharFormat scheme = mScheme[type];
79     if (mMarkOccurrences && type == Type_Name && loc.mLiteral == mMarkedWord)
80         scheme.merge(mScheme[Type_Highlight]);
81 
82     setFormat (index, length, scheme);
83 }
84 
ScriptHighlighter(const CSMWorld::Data & data,Mode mode,QTextDocument * parent)85 CSVWorld::ScriptHighlighter::ScriptHighlighter (const CSMWorld::Data& data, Mode mode,
86     QTextDocument *parent)
87     : QSyntaxHighlighter (parent)
88     , Compiler::Parser (mErrorHandler, mContext)
89     , mContext (data)
90     , mMode (mode)
91     , mMarkOccurrences (false)
92 {
93     QColor color ("black");
94     QTextCharFormat format;
95     format.setForeground (color);
96 
97     for (int i=0; i<=Type_Id; ++i)
98         mScheme.insert (std::make_pair (static_cast<Type> (i), format));
99 
100     // configure compiler
101     Compiler::registerExtensions (mExtensions);
102     mContext.setExtensions (&mExtensions);
103 }
104 
highlightBlock(const QString & text)105 void CSVWorld::ScriptHighlighter::highlightBlock (const QString& text)
106 {
107     std::istringstream stream (text.toUtf8().constData());
108 
109     Compiler::Scanner scanner (mErrorHandler, stream, mContext.getExtensions());
110 
111     try
112     {
113         scanner.scan (*this);
114     }
115     catch (...) {} // ignore syntax errors
116 }
117 
setMarkOccurrences(bool flag)118 void CSVWorld::ScriptHighlighter::setMarkOccurrences(bool flag)
119 {
120     mMarkOccurrences = flag;
121 }
122 
setMarkedWord(const std::string & name)123 void CSVWorld::ScriptHighlighter::setMarkedWord(const std::string& name)
124 {
125     mMarkedWord = name;
126 }
127 
invalidateIds()128 void CSVWorld::ScriptHighlighter::invalidateIds()
129 {
130     mContext.invalidateIds();
131 }
132 
settingChanged(const CSMPrefs::Setting * setting)133 bool CSVWorld::ScriptHighlighter::settingChanged (const CSMPrefs::Setting *setting)
134 {
135     if (setting->getParent()->getKey()=="Scripts")
136     {
137         static const char *const colours[Type_Id+2] =
138         {
139             "colour-int", "colour-float", "colour-name", "colour-keyword",
140             "colour-special", "colour-comment", "colour-highlight", "colour-id",
141             0
142         };
143 
144         for (int i=0; colours[i]; ++i)
145             if (setting->getKey()==colours[i])
146             {
147                 QTextCharFormat format;
148                 if (i == Type_Highlight)
149                     format.setBackground (setting->toColor());
150                 else
151                     format.setForeground (setting->toColor());
152                 mScheme[static_cast<Type> (i)] = format;
153                 return true;
154             }
155     }
156 
157     return false;
158 }
159