1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3  * http://www.gnu.org/licenses/lgpl-3.0.html
4  *
5  * $Revision: 11482 $
6  * $Id: editorlexerloader.cpp 11482 2018-09-29 12:20:40Z fuscated $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/editorlexerloader.cpp $
8  */
9 
10 #include "sdk_precomp.h"
11 
12 #ifndef CB_PRECOMP
13     #include "globals.h"
14     #include "manager.h"
15     #include "logmanager.h"
16     #include <wx/dynarray.h>
17     #include <wx/regex.h>
18     #include <wx/wxscintilla.h>
19 #endif
20 
21 #include "editorcolourset.h"
22 #include "editorlexerloader.h"
23 #include "filemanager.h"
24 
EditorLexerLoader(EditorColourSet * target)25 EditorLexerLoader::EditorLexerLoader(EditorColourSet* target)
26     : m_pTarget(target)
27 {
28     //ctor
29 }
30 
~EditorLexerLoader()31 EditorLexerLoader::~EditorLexerLoader()
32 {
33     //dtor
34 }
35 
Load(LoaderBase * loader)36 void EditorLexerLoader::Load(LoaderBase* loader)
37 {
38     Manager::Get()->GetLogManager()->Log(_("Loading ") + wxFileName(loader->FileName()).GetName());
39 
40     TiXmlDocument doc;
41     doc.Parse(loader->GetData());
42 
43     if (doc.Error())
44     {
45         Manager::Get()->GetLogManager()->Log(_("Failed."));
46         Manager::Get()->GetLogManager()->Log(_("TinyXML error: ") + cbC2U(doc.ErrorDesc()));
47         return;
48     }
49 
50     TiXmlElement* root;
51     TiXmlElement* lexer;
52 
53     root = doc.FirstChildElement("CodeBlocks_lexer_properties");
54     if (!root)
55     {
56         // old tag
57         root = doc.FirstChildElement("Code::Blocks_lexer_properties");
58         if (!root)
59         {
60             Manager::Get()->GetLogManager()->Log(_("Not a valid Code::Blocks lexer file..."));
61             return;
62         }
63     }
64     lexer = root->FirstChildElement("Lexer");
65     if (!lexer)
66     {
67         Manager::Get()->GetLogManager()->Log(_("No 'Lexer' element in file..."));
68         return;
69     }
70 
71     DoLexer(lexer);
72 }
73 
DoLexer(TiXmlElement * node)74 void EditorLexerLoader::DoLexer(TiXmlElement* node)
75 {
76     if (!node->Attribute("name") || !node->Attribute("index"))
77     {
78         Manager::Get()->GetLogManager()->Log(_("No name or index..."));
79         return;
80     }
81 
82     wxString name = wxString( node->Attribute("name"), wxConvUTF8 );
83     int lexer = atol(node->Attribute("index"));
84     wxString masks = wxString ( node->Attribute("filemasks"), wxConvUTF8 );
85     HighlightLanguage style = m_pTarget->AddHighlightLanguage(lexer, name);
86     if (style == HL_NONE)
87         return; // wasn't added
88     m_pTarget->SetFileMasks(style, masks);
89 //    LOGSTREAM << "Found lexer: " << name << " (" << style << ")\n";
90 
91     DoStyles(style, node);
92     DoKeywords(style, node);
93     DoSampleCode(style, node);
94     DoLangAttributes(style, node);
95 }
96 
DoStyles(HighlightLanguage language,TiXmlElement * node)97 void EditorLexerLoader::DoStyles(HighlightLanguage language, TiXmlElement* node)
98 {
99     bool foundSelection = false, foundActiveLine = false;
100     bool foundMatchBrace = false, foundBraceError = false;
101     bool foundIndentationGuide = false;
102 
103     TiXmlElement* style = node->FirstChildElement("Style");
104     while (style)
105     {
106         if (style->Attribute("name") && style->Attribute("index"))
107         {
108             wxString name = wxString ( style->Attribute("name"), wxConvUTF8 );
109             wxString index = wxString ( style->Attribute("index"), wxConvUTF8 ); // comma-separated indices
110             wxString fg = wxString ( style->Attribute("fg"), wxConvUTF8 );
111             wxString bg = wxString ( style->Attribute("bg"), wxConvUTF8 );
112             bool bold = style->Attribute("bold") ? atol(style->Attribute("bold")) != 0 : false;
113             bool italics = style->Attribute("italics") ? atol(style->Attribute("italics")) != 0 : false;
114             bool underlined = style->Attribute("underlined") ? atol(style->Attribute("underlined")) != 0 : false;
115 
116             // break-up arrays
117             wxArrayString indices = GetArrayFromString(index, _T(","));
118             wxArrayString fgarray = GetArrayFromString(fg, _T(","));
119             wxArrayString bgarray = GetArrayFromString(bg, _T(","));
120 
121             wxColour fgcolour = wxNullColour;
122             if (fgarray.GetCount() == 3)
123             {
124                 long R=0, G=0, B=0;
125                 fgarray[0].ToLong(&R);
126                 fgarray[1].ToLong(&G);
127                 fgarray[2].ToLong(&B);
128                 fgcolour.Set((unsigned char)R,(unsigned char)G,(unsigned char)B);
129             }
130             wxColour bgcolour = wxNullColour;
131             if (bgarray.GetCount() == 3)
132             {
133                 long R=0, G=0, B=0;
134                 bgarray[0].ToLong(&R);
135                 bgarray[1].ToLong(&G);
136                 bgarray[2].ToLong(&B);
137                 bgcolour.Set((unsigned char)R,(unsigned char)G,(unsigned char)B);
138             }
139 
140             for (size_t i = 0; i < indices.GetCount(); ++i)
141             {
142                 if (indices[i].IsEmpty())
143                     continue;
144                 long value = 0;
145                 indices[i].ToLong(&value);
146 
147                 switch (value)
148                 {
149                 case cbSELECTION:
150                     foundSelection = true;
151                     break;
152                 case cbHIGHLIGHT_LINE:
153                     foundActiveLine = true;
154                     break;
155                 case wxSCI_STYLE_BRACELIGHT:
156                     foundMatchBrace = true;
157                     break;
158                 case wxSCI_STYLE_BRACEBAD:
159                     foundBraceError = true;
160                     break;
161                 case wxSCI_STYLE_INDENTGUIDE:
162                     foundIndentationGuide = true;
163                     break;
164                 }
165 
166 //                LOGSTREAM << _("Adding style: ") << name << _T("(") << value << _T(")\n");
167                 m_pTarget->AddOption(language, name, value,
168                                     fgcolour,
169                                     bgcolour,
170                                     bold,
171                                     italics,
172                                     underlined,
173                                     value >= 0);
174             }
175         }
176         style = style->NextSiblingElement("Style");
177     }
178 
179     if (!foundSelection)
180     {
181         m_pTarget->AddOption(language, wxT("Selection"), cbSELECTION, wxNullColour,
182                              wxColour(217, 217, 217), false, false, false, false);
183     }
184     if (!foundActiveLine)
185     {
186         m_pTarget->AddOption(language, wxT("Active line"), cbHIGHLIGHT_LINE, wxNullColour,
187                              wxColour(255, 255, 160), false, false, false, false);
188     }
189     if (!foundMatchBrace)
190     {
191         m_pTarget->AddOption(language, wxT("Matching brace highlight"), wxSCI_STYLE_BRACELIGHT,
192                              wxColour(0, 0, 0), wxColour(128, 255, 255), true, false, false, true);
193     }
194     if (!foundBraceError)
195     {
196         m_pTarget->AddOption(language, wxT("No matching brace highlight"), wxSCI_STYLE_BRACEBAD,
197                              wxColour(255, 255, 255), wxColour(255, 0, 0), true, false, false, true);
198     }
199     if (!foundIndentationGuide)
200     {
201         m_pTarget->AddOption(language, wxT("Indentation guide"), wxSCI_STYLE_INDENTGUIDE,
202                              wxColour(55, 55, 55), wxNullColour, false, false, false, true);
203     }
204 }
205 
DoKeywords(HighlightLanguage language,TiXmlElement * node)206 void EditorLexerLoader::DoKeywords(HighlightLanguage language, TiXmlElement* node)
207 {
208     TiXmlElement* keywords = node->FirstChildElement("Keywords");
209     if (!keywords)
210         return;
211     DoSingleKeywordNode(language, keywords, _T("Language"));
212     DoSingleKeywordNode(language, keywords, _T("Documentation"));
213     DoSingleKeywordNode(language, keywords, _T("User"));
214     DoSingleKeywordNode(language, keywords, _T("Set"));
215 }
216 
DoSingleKeywordNode(HighlightLanguage language,TiXmlElement * node,const wxString & nodename)217 void EditorLexerLoader::DoSingleKeywordNode(HighlightLanguage language, TiXmlElement* node, const wxString& nodename)
218 {
219     TiXmlElement* keywords = node->FirstChildElement(nodename.mb_str());
220     while (keywords)
221     {
222     //    LOGSTREAM << "Found " << nodename << '\n';
223         int keyidx = keywords->Attribute("index") ? atol(keywords->Attribute("index")) : -1;
224     //    LOGSTREAM << "keyidx=" << keyidx << '\n';
225         if (keyidx != -1)
226         {
227             // the lexer file contains keywords indented - remove the extra spacing and EOLs
228             wxRegEx regex(_T("[[:space:]]+"));
229             wxString value(keywords->Attribute("value"), wxConvUTF8);
230             regex.Replace(&value, _T(" "));
231 
232             #if wxCHECK_VERSION(3, 0, 0)
233             m_pTarget->SetKeywords(language, keyidx, value );
234             #else
235             m_pTarget->SetKeywords(language, keyidx, wxString ( value, wxConvUTF8 ) );
236             #endif
237         }
238 
239         keywords = keywords->NextSiblingElement(nodename.mb_str());
240     }
241 }
242 
DoSampleCode(HighlightLanguage language,TiXmlElement * node)243 void EditorLexerLoader::DoSampleCode(HighlightLanguage language, TiXmlElement* node)
244 {
245     TiXmlElement* sample = node->FirstChildElement("SampleCode");
246     if (!sample)
247         return;
248     wxString code = wxString ( sample->Attribute("value"), wxConvUTF8 );
249     if (code.IsEmpty())
250         return;
251     int breakLine = sample->Attribute("breakpoint_line") ? atol(sample->Attribute("breakpoint_line")) : -1;
252     int debugLine = sample->Attribute("debug_line") ? atol(sample->Attribute("debug_line")) : -1;
253     int errorLine = sample->Attribute("error_line") ? atol(sample->Attribute("error_line")) : -1;
254     m_pTarget->SetSampleCode(language, code, breakLine, debugLine, errorLine);
255 }
256 
DoLangAttributes(HighlightLanguage language,TiXmlElement * node)257 void EditorLexerLoader::DoLangAttributes(HighlightLanguage language, TiXmlElement* node)
258 {
259     TiXmlElement* attribs = node->FirstChildElement("LanguageAttributes");
260     if ( !attribs )
261         return;
262 
263     bool CaseSensitive = attribs->Attribute("CaseSensitive") ? atol(attribs->Attribute("CaseSensitive")) != 0 : false;
264     m_pTarget->SetCaseSensitivity(language, CaseSensitive);
265 
266 
267     CommentToken token;
268     token.lineComment = wxString( attribs->Attribute("LineComment"), wxConvUTF8 );
269     token.doxygenLineComment = wxString( attribs->Attribute("DoxygenLineComment"), wxConvUTF8 );
270     token.streamCommentStart = wxString( attribs->Attribute("StreamCommentStart"), wxConvUTF8 );
271     token.streamCommentEnd = wxString( attribs->Attribute("StreamCommentEnd"), wxConvUTF8 );
272     token.doxygenStreamCommentStart = wxString( attribs->Attribute("DoxygenStreamCommentStart"), wxConvUTF8 );
273     token.doxygenStreamCommentEnd = wxString( attribs->Attribute("DoxygenStreamCommentEnd"), wxConvUTF8 );
274     token.boxCommentStart = wxString( attribs->Attribute("BoxCommentStart"), wxConvUTF8 );
275     token.boxCommentMid = wxString( attribs->Attribute("BoxCommentMid"), wxConvUTF8 );
276     token.boxCommentEnd = wxString( attribs->Attribute("BoxCommentEnd"), wxConvUTF8 );
277 
278     m_pTarget->SetCommentToken(language, token);
279 
280     std::set<int> CommentLexerStyles, CharacterLexerStyles, StringLexerStyles, PreprocessorLexerStyles;
281     bool hasLexerStylesSet = false;
282     hasLexerStylesSet |= DoLangAttributesLexerStyles(attribs, "LexerCommentStyles", CommentLexerStyles);
283     hasLexerStylesSet |= DoLangAttributesLexerStyles(attribs, "LexerCharacterStyles", CharacterLexerStyles);
284     hasLexerStylesSet |= DoLangAttributesLexerStyles(attribs, "LexerStringStyles", StringLexerStyles);
285     hasLexerStylesSet |= DoLangAttributesLexerStyles(attribs, "LexerPreprocessorStyles", PreprocessorLexerStyles);
286 
287     // only set styles if configured. Since different languages use the same lexer.
288     // So if any of the languages has these styles configured we use them.
289     // If another language has not configured them the previously defined wont get lost.
290     if ( hasLexerStylesSet )
291     {
292         m_pTarget->SetCommentLexerStyles(language, CommentLexerStyles);
293         m_pTarget->SetStringLexerStyles(language, StringLexerStyles);
294         m_pTarget->SetCharacterLexerStyles(language, CharacterLexerStyles);
295         m_pTarget->SetPreprocessorLexerStyles(language, PreprocessorLexerStyles);
296     }
297 }
298 
DoLangAttributesLexerStyles(TiXmlElement * attribs,const char * attributeName,std::set<int> & styles)299 bool EditorLexerLoader::DoLangAttributesLexerStyles(TiXmlElement* attribs, const char *attributeName, std::set<int> &styles)
300 {
301     styles.clear();
302     wxString str = wxString ( attribs->Attribute(attributeName), wxConvUTF8 );
303     wxArrayString strarray = GetArrayFromString(str, _T(","));
304 
305     for ( unsigned int i = 0; i < strarray.Count(); ++i )
306     {
307         long style;
308         strarray[i].ToLong(&style);
309         styles.insert((unsigned int)style);
310     }
311 
312     return !str.IsEmpty();
313 }
314