1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3 * http://www.gnu.org/licenses/gpl-3.0.html
4 *
5 * $Revision: 11898 $
6 * $Id: nativeparser.cpp 11898 2019-11-04 19:35:16Z fuscated $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/plugins/codecompletion/nativeparser.cpp $
8 */
9
10 #include <sdk.h>
11
12 #ifndef CB_PRECOMP
13 #include <cctype>
14
15 #include <wx/dir.h>
16 #include <wx/log.h> // for wxSafeShowMessage()
17 #include <wx/regex.h>
18 #include <wx/wfstream.h>
19
20 #include <cbauibook.h>
21 #include <cbeditor.h>
22 #include <cbexception.h>
23 #include <cbproject.h>
24 #include <compilerfactory.h>
25 #include <configmanager.h>
26 #include <editormanager.h>
27 #include <logmanager.h>
28 #include <macrosmanager.h>
29 #include <manager.h>
30 #include <pluginmanager.h>
31 #include <prep.h> // nullptr
32 #include <projectmanager.h>
33
34 #include <tinyxml/tinyxml.h>
35 #endif
36
37 #include <wx/tokenzr.h>
38
39 #include <cbstyledtextctrl.h>
40 #include <compilercommandgenerator.h>
41
42 #include "nativeparser.h"
43 #include "classbrowser.h"
44 #include "parser/parser.h"
45 #include "parser/profiletimer.h"
46
47 #define CC_NATIVEPARSER_DEBUG_OUTPUT 0
48
49 #if defined (CC_GLOBAL_DEBUG_OUTPUT)
50 #if CC_GLOBAL_DEBUG_OUTPUT == 1
51 #undef CC_NATIVEPARSER_DEBUG_OUTPUT
52 #define CC_NATIVEPARSER_DEBUG_OUTPUT 1
53 #elif CC_GLOBAL_DEBUG_OUTPUT == 2
54 #undef CC_NATIVEPARSER_DEBUG_OUTPUT
55 #define CC_NATIVEPARSER_DEBUG_OUTPUT 2
56 #endif
57 #endif
58
59 #if CC_NATIVEPARSER_DEBUG_OUTPUT == 1
60 #define TRACE(format, args...) \
61 CCLogger::Get()->DebugLog(F(format, ##args))
62 #define TRACE2(format, args...)
63 #elif CC_NATIVEPARSER_DEBUG_OUTPUT == 2
64 #define TRACE(format, args...) \
65 do \
66 { \
67 if (g_EnableDebugTrace) \
68 CCLogger::Get()->DebugLog(F(format, ##args)); \
69 } \
70 while (false)
71 #define TRACE2(format, args...) \
72 CCLogger::Get()->DebugLog(F(format, ##args))
73 #else
74 #define TRACE(format, args...)
75 #define TRACE2(format, args...)
76 #endif
77
78 /*
79 * (Recursive) functions that are surrounded by a critical section:
80 * GenerateResultSet() -> AddChildrenOfUnnamed
81 * GetCallTips() -> PrettyPrintToken (recursive function)
82 * FindCurrentFunctionToken() -> ParseFunctionArguments, FindAIMatches (recursive function)
83 * GenerateResultSet (recursive function):
84 * FindAIMatches(), ResolveActualType(), ResolveExpression(),
85 * FindCurrentFunctionToken(), ResolveOperator()
86 * FindCurrentFunctionStart() -> GetTokenFromCurrentLine
87 */
88
89 namespace NativeParserHelper
90 {
91 class ParserDirTraverser : public wxDirTraverser
92 {
93 public:
ParserDirTraverser(const wxString & excludePath,wxArrayString & files)94 ParserDirTraverser(const wxString& excludePath, wxArrayString& files) :
95 m_ExcludeDir(excludePath),
96 m_Files(files)
97 {}
98
OnFile(const wxString & filename)99 wxDirTraverseResult OnFile(const wxString& filename) override
100 {
101 if (ParserCommon::FileType(filename) != ParserCommon::ftOther)
102 m_Files.Add(filename);
103 return wxDIR_CONTINUE;
104 }
105
OnDir(const wxString & dirname)106 wxDirTraverseResult OnDir(const wxString& dirname) override
107 {
108 if (dirname == m_ExcludeDir)
109 return wxDIR_IGNORE;
110 if (m_Files.GetCount() == 1)
111 return wxDIR_STOP;
112 m_Files.Clear();
113 return wxDIR_CONTINUE;
114 }
115
116 private:
117 const wxString& m_ExcludeDir;
118 wxArrayString& m_Files;
119 };
120 }// namespace NativeParserHelper
121
122 /** event id for the sequence project parsing timer */
123 int idTimerParsingOneByOne = wxNewId();
124
125 /** if this option is enabled, there will be many log messages when doing semantic match */
126 bool s_DebugSmartSense = false;
127
AddToImageList(wxImageList * list,const wxString & path)128 static void AddToImageList(wxImageList *list, const wxString &path)
129 {
130 wxBitmap bmp = cbLoadBitmap(path, wxBITMAP_TYPE_PNG);
131 if (!bmp.IsOk())
132 {
133 printf("failed to load: %s\n", path.utf8_str().data());
134 }
135 list->Add(bmp);
136 }
137
LoadImageList(int size)138 static wxImageList* LoadImageList(int size)
139 {
140 wxImageList *list = new wxImageList(size, size);
141 wxBitmap bmp;
142 const wxString prefix = ConfigManager::GetDataFolder()
143 + wxString::Format(_T("/codecompletion.zip#zip:images/%dx%d/"), size,
144 size);
145
146 // Bitmaps must be added by order of PARSER_IMG_* consts.
147 AddToImageList(list, prefix + _T("class_folder.png")); // PARSER_IMG_CLASS_FOLDER
148 AddToImageList(list, prefix + _T("class.png")); // PARSER_IMG_CLASS
149 AddToImageList(list, prefix + _T("class_private.png")); // PARSER_IMG_CLASS_PRIVATE
150 AddToImageList(list, prefix + _T("class_protected.png")); // PARSER_IMG_CLASS_PROTECTED
151 AddToImageList(list, prefix + _T("class_public.png")); // PARSER_IMG_CLASS_PUBLIC
152 AddToImageList(list, prefix + _T("ctor_private.png")); // PARSER_IMG_CTOR_PRIVATE
153 AddToImageList(list, prefix + _T("ctor_protected.png")); // PARSER_IMG_CTOR_PROTECTED
154 AddToImageList(list, prefix + _T("ctor_public.png")); // PARSER_IMG_CTOR_PUBLIC
155 AddToImageList(list, prefix + _T("dtor_private.png")); // PARSER_IMG_DTOR_PRIVATE
156 AddToImageList(list, prefix + _T("dtor_protected.png")); // PARSER_IMG_DTOR_PROTECTED
157 AddToImageList(list, prefix + _T("dtor_public.png")); // PARSER_IMG_DTOR_PUBLIC
158 AddToImageList(list, prefix + _T("method_private.png")); // PARSER_IMG_FUNC_PRIVATE
159 AddToImageList(list, prefix + _T("method_protected.png")); // PARSER_IMG_FUNC_PRIVATE
160 AddToImageList(list, prefix + _T("method_public.png")); // PARSER_IMG_FUNC_PUBLIC
161 AddToImageList(list, prefix + _T("var_private.png")); // PARSER_IMG_VAR_PRIVATE
162 AddToImageList(list, prefix + _T("var_protected.png")); // PARSER_IMG_VAR_PROTECTED
163 AddToImageList(list, prefix + _T("var_public.png")); // PARSER_IMG_VAR_PUBLIC
164 AddToImageList(list, prefix + _T("macro_def.png")); // PARSER_IMG_MACRO_DEF
165 AddToImageList(list, prefix + _T("enum.png")); // PARSER_IMG_ENUM
166 AddToImageList(list, prefix + _T("enum_private.png")); // PARSER_IMG_ENUM_PRIVATE
167 AddToImageList(list, prefix + _T("enum_protected.png")); // PARSER_IMG_ENUM_PROTECTED
168 AddToImageList(list, prefix + _T("enum_public.png")); // PARSER_IMG_ENUM_PUBLIC
169 AddToImageList(list, prefix + _T("enumerator.png")); // PARSER_IMG_ENUMERATOR
170 AddToImageList(list, prefix + _T("namespace.png")); // PARSER_IMG_NAMESPACE
171 AddToImageList(list, prefix + _T("typedef.png")); // PARSER_IMG_TYPEDEF
172 AddToImageList(list, prefix + _T("typedef_private.png")); // PARSER_IMG_TYPEDEF_PRIVATE
173 AddToImageList(list, prefix + _T("typedef_protected.png")); // PARSER_IMG_TYPEDEF_PROTECTED
174 AddToImageList(list, prefix + _T("typedef_public.png")); // PARSER_IMG_TYPEDEF_PUBLIC
175 AddToImageList(list, prefix + _T("symbols_folder.png")); // PARSER_IMG_SYMBOLS_FOLDER
176 AddToImageList(list, prefix + _T("vars_folder.png")); // PARSER_IMG_VARS_FOLDER
177 AddToImageList(list, prefix + _T("funcs_folder.png")); // PARSER_IMG_FUNCS_FOLDER
178 AddToImageList(list, prefix + _T("enums_folder.png")); // PARSER_IMG_ENUMS_FOLDER
179 AddToImageList(list, prefix + _T("macro_def_folder.png")); // PARSER_IMG_MACRO_DEF_FOLDER
180 AddToImageList(list, prefix + _T("others_folder.png")); // PARSER_IMG_OTHERS_FOLDER
181 AddToImageList(list, prefix + _T("typedefs_folder.png")); // PARSER_IMG_TYPEDEF_FOLDER
182 AddToImageList(list, prefix + _T("macro_use.png")); // PARSER_IMG_MACRO_USE
183 AddToImageList(list, prefix + _T("macro_use_private.png")); // PARSER_IMG_MACRO_USE_PRIVATE
184 AddToImageList(list, prefix + _T("macro_use_protected.png")); // PARSER_IMG_MACRO_USE_PROTECTED
185 AddToImageList(list, prefix + _T("macro_use_public.png")); // PARSER_IMG_MACRO_USE_PUBLIC
186 AddToImageList(list, prefix + _T("macro_use_folder.png")); // PARSER_IMG_MACRO_USE_FOLDER
187
188 return list;
189 }
190
NativeParser()191 NativeParser::NativeParser() :
192 m_TimerParsingOneByOne(this, idTimerParsingOneByOne),
193 m_ClassBrowser(nullptr),
194 m_ClassBrowserIsFloating(false),
195 m_ParserPerWorkspace(false),
196 m_LastAISearchWasGlobal(false),
197 m_LastControl(nullptr),
198 m_LastFunctionIndex(-1),
199 m_LastFuncTokenIdx(-1),
200 m_LastLine(-1),
201 m_LastResult(-1)
202 {
203 m_TempParser = new Parser(this, nullptr);
204 m_Parser = m_TempParser;
205
206 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
207 m_ParserPerWorkspace = cfg->ReadBool(_T("/parser_per_workspace"), false);
208
209 Connect(ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(NativeParser::OnParserStart));
210 Connect(ParserCommon::idParserEnd, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(NativeParser::OnParserEnd));
211 Connect(idTimerParsingOneByOne, wxEVT_TIMER, wxTimerEventHandler(NativeParser::OnParsingOneByOneTimer));
212 }
213
~NativeParser()214 NativeParser::~NativeParser()
215 {
216 Disconnect(ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(NativeParser::OnParserStart));
217 Disconnect(ParserCommon::idParserEnd, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(NativeParser::OnParserEnd));
218 Disconnect(idTimerParsingOneByOne, wxEVT_TIMER, wxTimerEventHandler(NativeParser::OnParsingOneByOneTimer));
219 RemoveClassBrowser();
220 ClearParsers();
221 Delete(m_TempParser);
222 }
223
GetParserByProject(cbProject * project)224 ParserBase* NativeParser::GetParserByProject(cbProject* project)
225 {
226 if (m_ParserPerWorkspace)
227 {
228 std::set<cbProject*>::iterator it = m_ParsedProjects.find(project);
229 if (it != m_ParsedProjects.end())
230 return m_ParserList.begin()->second;
231 }
232 else
233 {
234 for (ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it)
235 {
236 if (it->first == project)
237 return it->second;
238 }
239 }
240
241 TRACE(_T("NativeParser::GetParserByProject: Returning nullptr."));
242 return nullptr;
243 }
244
GetParserByFilename(const wxString & filename)245 ParserBase* NativeParser::GetParserByFilename(const wxString& filename)
246 {
247 cbProject* project = GetProjectByFilename(filename);
248 return GetParserByProject(project);
249 }
250
GetProjectByParser(ParserBase * parser)251 cbProject* NativeParser::GetProjectByParser(ParserBase* parser)
252 {
253 for (ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it)
254 {
255 if (it->second == parser)
256 return it->first;
257 }
258
259 TRACE(_T("NativeParser::GetProjectByParser: Returning NULL."));
260 return NULL;
261 }
262
GetProjectByFilename(const wxString & filename)263 cbProject* NativeParser::GetProjectByFilename(const wxString& filename)
264 {
265 TRACE(_T("NativeParser::GetProjectByFilename: %s"), filename.wx_str());
266 cbProject* activeProject = Manager::Get()->GetProjectManager()->GetActiveProject();
267 if (activeProject)
268 {
269 ParserBase* parser = GetParserByProject(activeProject);
270 if ( ( parser
271 && parser->IsFileParsed(filename) )
272 || activeProject->GetFileByFilename(filename, false, true) )
273 {
274 return activeProject;
275 }
276 else
277 {
278 ProjectsArray* projs = Manager::Get()->GetProjectManager()->GetProjects();
279 for (size_t i = 0; i < projs->GetCount(); ++i)
280 {
281 cbProject* project = projs->Item(i);
282 if (!project || project == activeProject)
283 continue;
284
285 parser = GetParserByProject(project);
286 if ( ( parser
287 && parser->IsFileParsed(filename) )
288 || project->GetFileByFilename(filename, false, true) )
289 {
290 return project;
291 }
292 }
293 }
294 }
295
296 return nullptr;
297 }
298
GetProjectByEditor(cbEditor * editor)299 cbProject* NativeParser::GetProjectByEditor(cbEditor* editor)
300 {
301 if (!editor)
302 return nullptr;
303 ProjectFile* pf = editor->GetProjectFile();
304 if (pf && pf->GetParentProject())
305 return pf->GetParentProject();
306 return GetProjectByFilename(editor->GetFilename());
307 }
308
GetCurrentProject()309 cbProject* NativeParser::GetCurrentProject()
310 {
311 cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
312 cbProject* project = GetProjectByEditor(editor);
313 if (!project)
314 project = Manager::Get()->GetProjectManager()->GetActiveProject();
315 return project;
316 }
317
Done()318 bool NativeParser::Done()
319 {
320 bool done = true;
321 for (ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it)
322 {
323 if (!it->second->Done())
324 {
325 done = false;
326 break;
327 }
328 }
329 TRACE(_T("NativeParser::Done: %s"), done ? _T("true"): _T("false"));
330 return done;
331 }
332
GetImageList(int maxSize)333 wxImageList* NativeParser::GetImageList(int maxSize)
334 {
335 const int size = cbFindMinSize16to64(maxSize);
336
337 SizeToImageList::iterator it = m_ImageListMap.find(size);
338 if (it == m_ImageListMap.end())
339 {
340 wxImageList *list = LoadImageList(size);
341 m_ImageListMap.insert(SizeToImageList::value_type(size, std::unique_ptr<wxImageList>(list)));
342 return list;
343 }
344 else
345 return it->second.get();
346 }
347
GetTokenKindImage(const Token * token)348 int NativeParser::GetTokenKindImage(const Token* token)
349 {
350 if (!token)
351 return PARSER_IMG_NONE;
352
353 switch (token->m_TokenKind)
354 {
355 case tkMacroDef: return PARSER_IMG_MACRO_DEF;
356
357 case tkEnum:
358 switch (token->m_Scope)
359 {
360 case tsPublic: return PARSER_IMG_ENUM_PUBLIC;
361 case tsProtected: return PARSER_IMG_ENUM_PROTECTED;
362 case tsPrivate: return PARSER_IMG_ENUM_PRIVATE;
363 case tsUndefined:
364 default: return PARSER_IMG_ENUM;
365 }
366
367 case tkEnumerator: return PARSER_IMG_ENUMERATOR;
368
369 case tkClass:
370 switch (token->m_Scope)
371 {
372 case tsPublic: return PARSER_IMG_CLASS_PUBLIC;
373 case tsProtected: return PARSER_IMG_CLASS_PROTECTED;
374 case tsPrivate: return PARSER_IMG_CLASS_PRIVATE;
375 case tsUndefined:
376 default: return PARSER_IMG_CLASS;
377 }
378
379 case tkNamespace: return PARSER_IMG_NAMESPACE;
380
381 case tkTypedef:
382 switch (token->m_Scope)
383 {
384 case tsPublic: return PARSER_IMG_TYPEDEF_PUBLIC;
385 case tsProtected: return PARSER_IMG_TYPEDEF_PROTECTED;
386 case tsPrivate: return PARSER_IMG_TYPEDEF_PRIVATE;
387 case tsUndefined:
388 default: return PARSER_IMG_TYPEDEF;
389 }
390
391 case tkMacroUse:
392 switch (token->m_Scope)
393 {
394 case tsPublic: return PARSER_IMG_MACRO_USE_PUBLIC;
395 case tsProtected: return PARSER_IMG_MACRO_USE_PROTECTED;
396 case tsPrivate: return PARSER_IMG_MACRO_USE_PRIVATE;
397 case tsUndefined:
398 default: return PARSER_IMG_MACRO_USE;
399 }
400
401 case tkConstructor:
402 switch (token->m_Scope)
403 {
404 case tsProtected: return PARSER_IMG_CTOR_PROTECTED;
405 case tsPrivate: return PARSER_IMG_CTOR_PRIVATE;
406 case tsUndefined:
407 case tsPublic:
408 default: return PARSER_IMG_CTOR_PUBLIC;
409 }
410
411 case tkDestructor:
412 switch (token->m_Scope)
413 {
414 case tsProtected: return PARSER_IMG_DTOR_PROTECTED;
415 case tsPrivate: return PARSER_IMG_DTOR_PRIVATE;
416 case tsUndefined:
417 case tsPublic:
418 default: return PARSER_IMG_DTOR_PUBLIC;
419 }
420
421 case tkFunction:
422 switch (token->m_Scope)
423 {
424 case tsProtected: return PARSER_IMG_FUNC_PROTECTED;
425 case tsPrivate: return PARSER_IMG_FUNC_PRIVATE;
426 case tsUndefined:
427 case tsPublic:
428 default: return PARSER_IMG_FUNC_PUBLIC;
429 }
430
431 case tkVariable:
432 switch (token->m_Scope)
433 {
434 case tsProtected: return PARSER_IMG_VAR_PROTECTED;
435 case tsPrivate: return PARSER_IMG_VAR_PRIVATE;
436 case tsUndefined:
437 case tsPublic:
438 default: return PARSER_IMG_VAR_PUBLIC;
439 }
440
441 case tkAnyContainer:
442 case tkAnyFunction:
443 case tkUndefined:
444 default: return PARSER_IMG_NONE;
445 }
446 }
447
GetAllPathsByFilename(const wxString & filename)448 wxArrayString NativeParser::GetAllPathsByFilename(const wxString& filename)
449 {
450 TRACE(_T("NativeParser::GetAllPathsByFilename: Enter"));
451
452 wxArrayString dirs;
453 const wxFileName fn(filename);
454
455 wxDir dir(fn.GetPath());
456 if (!dir.IsOpened())
457 return wxArrayString();
458
459 wxArrayString files;
460 NativeParserHelper::ParserDirTraverser traverser(wxEmptyString, files);
461 const wxString filespec = fn.HasExt() ? fn.GetName() + _T(".*") : fn.GetName();
462 CCLogger::Get()->DebugLog(_T("NativeParser::GetAllPathsByFilename: Traversing '") + fn.GetPath() + _T("' for: ") + filespec);
463
464 // search in the same directory of the input file
465 dir.Traverse(traverser, filespec, wxDIR_FILES);
466
467 // only find one file in the dir, which is the input file itself, try searching in other places
468 if (files.GetCount() == 1)
469 {
470 cbProject* project = IsParserPerWorkspace() ? GetCurrentProject()
471 : GetProjectByParser(m_Parser);
472 // search in the project
473 if (project)
474 {
475 const wxString prjPath = project->GetCommonTopLevelPath();
476 wxString priorityPath;
477 if (fn.HasExt() && (fn.GetExt().StartsWith(_T("h")) || fn.GetExt().StartsWith(_T("c"))))
478 {
479 wxFileName priFn(prjPath);
480 // hard-coded candidate path, the ./sdk or ./include under the project top level folder
481 priFn.AppendDir(fn.GetExt().StartsWith(_T("h")) ? _T("sdk") : _T("include"));
482 if (priFn.DirExists())
483 {
484 priorityPath = priFn.GetFullPath();
485 wxDir priorityDir(priorityPath);
486 if ( priorityDir.IsOpened() )
487 {
488 wxArrayString priorityPathSub;
489 NativeParserHelper::ParserDirTraverser traverser_2(wxEmptyString, priorityPathSub);
490 CCLogger::Get()->DebugLog(_T("NativeParser::GetAllPathsByFilename: Traversing '") + priorityPath + _T("' for: ") + filespec);
491 priorityDir.Traverse(traverser_2, filespec, wxDIR_FILES | wxDIR_DIRS);
492 if (priorityPathSub.GetCount() == 1)
493 AddPaths(dirs, priorityPathSub[0], fn.HasExt());
494 }
495 }
496 }
497
498 if (dirs.IsEmpty())
499 {
500 wxDir prjDir(prjPath);
501 if (prjDir.IsOpened())
502 {
503 // try to search the project top level folder
504 wxArrayString prjDirSub;
505 NativeParserHelper::ParserDirTraverser traverser_2(priorityPath, prjDirSub);
506 CCLogger::Get()->DebugLog(_T("NativeParser::GetAllPathsByFilename: Traversing '") + priorityPath + wxT(" - ") + prjPath + _T("' for: ") + filespec);
507 prjDir.Traverse(traverser_2, filespec, wxDIR_FILES | wxDIR_DIRS);
508 if (prjDirSub.GetCount() == 1)
509 AddPaths(dirs, prjDirSub[0], fn.HasExt());
510 }
511 }
512 }
513 }
514
515 CCLogger::Get()->DebugLog(F(_T("NativeParser::GetAllPathsByFilename: Found %lu files:"), static_cast<unsigned long>(files.GetCount())));
516 for (size_t i=0; i<files.GetCount(); i++)
517 CCLogger::Get()->DebugLog(F(_T("- %s"), files[i].wx_str()));
518
519 if (!files.IsEmpty())
520 AddPaths(dirs, files[0], fn.HasExt());
521
522 TRACE(_T("NativeParser::GetAllPathsByFilename: Leave"));
523 return dirs;
524 }
525
AddPaths(wxArrayString & dirs,const wxString & path,bool hasExt)526 void NativeParser::AddPaths(wxArrayString& dirs, const wxString& path, bool hasExt)
527 {
528 wxString s;
529 if (hasExt)
530 s = UnixFilename(path.BeforeLast(_T('.'))) + _T(".");
531 else
532 s = UnixFilename(path);
533
534 if (dirs.Index(s, false) == wxNOT_FOUND)
535 dirs.Add(s);
536 }
537
CreateParser(cbProject * project)538 ParserBase* NativeParser::CreateParser(cbProject* project)
539 {
540 if ( GetParserByProject(project) )
541 {
542 CCLogger::Get()->DebugLog(_T("NativeParser::CreateParser: Parser for this project already exists!"));
543 return nullptr;
544 }
545
546 // Easy case for "one parser per workspace" that has already been created:
547 if (m_ParserPerWorkspace && !m_ParsedProjects.empty())
548 return m_ParserList.begin()->second;
549
550 TRACE(_T("NativeParser::CreateParser: Calling DoFullParsing()"));
551
552 ParserBase* parser = new Parser(this, project);
553 if ( !DoFullParsing(project, parser) )
554 {
555 CCLogger::Get()->DebugLog(_T("NativeParser::CreateParser: Full parsing failed!"));
556 delete parser;
557 return nullptr;
558 }
559
560 if (m_Parser == m_TempParser)
561 SetParser(parser); // Also updates class browser
562
563 if (m_ParserPerWorkspace)
564 m_ParsedProjects.insert(project);
565
566 m_ParserList.push_back(std::make_pair(project, parser));
567
568 wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
569 wxString log(F(_("NativeParser::CreateParser: Finish creating a new parser for project '%s'"), prj.wx_str()));
570 CCLogger::Get()->Log(log);
571 CCLogger::Get()->DebugLog(log);
572
573 RemoveObsoleteParsers();
574
575 return parser;
576 }
577
DeleteParser(cbProject * project)578 bool NativeParser::DeleteParser(cbProject* project)
579 {
580 wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
581
582 ParserList::iterator it = m_ParserList.begin();
583 if (!m_ParserPerWorkspace)
584 {
585 for (; it != m_ParserList.end(); ++it)
586 {
587 if (it->first == project)
588 break;
589 }
590 }
591
592 if (it == m_ParserList.end())
593 {
594 CCLogger::Get()->DebugLog(F(_T("NativeParser::DeleteParser: Parser does not exist for delete '%s'!"), prj.wx_str()));
595 return false;
596 }
597
598 bool removeProjectFromParser = false;
599 if (m_ParserPerWorkspace)
600 removeProjectFromParser = RemoveProjectFromParser(project);
601
602 if (m_ParsedProjects.empty()) // this indicates we are in one parser per one project mode
603 {
604 wxString log(F(_("NativeParser::DeleteParser: Deleting parser for project '%s'!"), prj.wx_str()));
605 CCLogger::Get()->Log(log);
606 CCLogger::Get()->DebugLog(log);
607
608 // the logic here is : firstly delete the parser instance, then see whether we need an
609 // active parser switch (call SetParser())
610 delete it->second;
611
612 // if the active parser is deleted, set the active parser to nullptr
613 if (it->second == m_Parser)
614 {
615 m_Parser = nullptr;
616 SetParser(m_TempParser); // Also updates class browser
617 }
618
619 m_ParserList.erase(it);
620
621 return true;
622 }
623
624 if (removeProjectFromParser)
625 return true;
626
627 CCLogger::Get()->DebugLog(_T("NativeParser::DeleteParser: Deleting parser failed!"));
628 return false;
629 }
630
ReparseFile(cbProject * project,const wxString & filename)631 bool NativeParser::ReparseFile(cbProject* project, const wxString& filename)
632 {
633 if (ParserCommon::FileType(filename) == ParserCommon::ftOther)
634 return false;
635
636 ParserBase* parser = GetParserByProject(project);
637 if (!parser)
638 return false;
639
640 if (!parser->UpdateParsingProject(project))
641 return false;
642
643 TRACE(_T("NativeParser::ReparseFile: Calling Parser::Reparse()"));
644
645 return parser->Reparse(filename);
646 }
647
AddFileToParser(cbProject * project,const wxString & filename,ParserBase * parser)648 bool NativeParser::AddFileToParser(cbProject* project, const wxString& filename, ParserBase* parser)
649 {
650 if (ParserCommon::FileType(filename) == ParserCommon::ftOther)
651 return false;
652
653 if (!parser)
654 {
655 parser = GetParserByProject(project);
656 if (!parser)
657 return false;
658 }
659
660 if (!parser->UpdateParsingProject(project))
661 return false;
662
663 TRACE(_T("NativeParser::AddFileToParser: Calling Parser::AddFile()"));
664
665 return parser->AddFile(filename, project);
666 }
667
RemoveFileFromParser(cbProject * project,const wxString & filename)668 bool NativeParser::RemoveFileFromParser(cbProject* project, const wxString& filename)
669 {
670 ParserBase* parser = GetParserByProject(project);
671 if (!parser)
672 return false;
673
674 TRACE(_T("NativeParser::RemoveFileFromParser: Calling Parser::RemoveFile()"));
675
676 return parser->RemoveFile(filename);
677 }
678
RereadParserOptions()679 void NativeParser::RereadParserOptions()
680 {
681 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
682 #if wxCHECK_VERSION(3, 0, 0)
683 bool useSymbolBrowser = false;
684 #else
685 bool useSymbolBrowser = cfg->ReadBool(_T("/use_symbols_browser"), true);
686 #endif // wxCHECK_VERSION
687
688 if (useSymbolBrowser)
689 {
690 if (!m_ClassBrowser)
691 {
692 CreateClassBrowser();
693 UpdateClassBrowser();
694 }
695 // change class-browser docking settings
696 else if (m_ClassBrowserIsFloating != cfg->ReadBool(_T("/as_floating_window"), false))
697 {
698 RemoveClassBrowser();
699 CreateClassBrowser();
700 // force re-update
701 UpdateClassBrowser();
702 }
703 }
704 else if (!useSymbolBrowser && m_ClassBrowser)
705 RemoveClassBrowser();
706
707 const bool parserPerWorkspace = cfg->ReadBool(_T("/parser_per_workspace"), false);
708 if (m_Parser == m_TempParser)
709 {
710 m_ParserPerWorkspace = parserPerWorkspace;
711 return;
712 }
713
714 RemoveObsoleteParsers();
715
716 // re-parse if settings changed
717 ParserOptions opts = m_Parser->Options();
718 m_Parser->ReadOptions();
719 bool reparse = false;
720 cbProject* project = GetCurrentProject();
721 if ( opts.followLocalIncludes != m_Parser->Options().followLocalIncludes
722 || opts.followGlobalIncludes != m_Parser->Options().followGlobalIncludes
723 || opts.wantPreprocessor != m_Parser->Options().wantPreprocessor
724 || opts.parseComplexMacros != m_Parser->Options().parseComplexMacros
725 || opts.platformCheck != m_Parser->Options().platformCheck
726 || m_ParserPerWorkspace != parserPerWorkspace )
727 {
728 // important options changed... flag for reparsing
729 if (cbMessageBox(_("You changed some class parser options. Do you want to "
730 "reparse your projects now, using the new options?"),
731 _("Reparse?"), wxYES_NO | wxICON_QUESTION) == wxID_YES)
732 {
733 reparse = true;
734 }
735 }
736
737 if (reparse)
738 ClearParsers();
739
740 m_ParserPerWorkspace = parserPerWorkspace;
741
742 if (reparse)
743 CreateParser(project);
744 }
745
ReparseCurrentProject()746 void NativeParser::ReparseCurrentProject()
747 {
748 cbProject* project = GetCurrentProject();
749 if (project)
750 {
751 TRACE(_T("NativeParser::ReparseCurrentProject: Calling DeleteParser() and CreateParser()"));
752 DeleteParser(project);
753 CreateParser(project);
754 }
755 }
756
ReparseSelectedProject()757 void NativeParser::ReparseSelectedProject()
758 {
759 wxTreeCtrl* tree = Manager::Get()->GetProjectManager()->GetUI().GetTree();
760 if (!tree)
761 return;
762
763 wxTreeItemId treeItem = Manager::Get()->GetProjectManager()->GetUI().GetTreeSelection();
764 if (!treeItem.IsOk())
765 return;
766
767 const FileTreeData* data = static_cast<FileTreeData*>(tree->GetItemData(treeItem));
768 if (!data)
769 return;
770
771 if (data->GetKind() == FileTreeData::ftdkProject)
772 {
773 cbProject* project = data->GetProject();
774 if (project)
775 {
776 TRACE(_T("NativeParser::ReparseSelectedProject: Calling DeleteParser() and CreateParser()"));
777 DeleteParser(project);
778 CreateParser(project);
779 }
780 }
781 }
782
783 // Here, we collect the "using namespace XXXX" directives
784 // Also, we locate the current caret in which function, then, add the function parameters to Token trie
785 // Also, the variables in the function body( local block ) was add to the Token trie
MarkItemsByAI(ccSearchData * searchData,TokenIdxSet & result,bool reallyUseAI,bool isPrefix,bool caseSensitive,int caretPos)786 size_t NativeParser::MarkItemsByAI(ccSearchData* searchData,
787 TokenIdxSet& result,
788 bool reallyUseAI,
789 bool isPrefix,
790 bool caseSensitive,
791 int caretPos)
792 {
793 result.clear();
794
795 if (!m_Parser->Done())
796 {
797 wxString msg(_("The Parser is still parsing files."));
798 msg += m_Parser->NotDoneReason();
799 CCLogger::Get()->DebugLog(msg);
800 return 0;
801 }
802
803 TRACE(_T("NativeParser::MarkItemsByAI_2()"));
804
805 TokenTree* tree = m_Parser->GetTempTokenTree();
806
807 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
808
809 // remove old temporaries
810 tree->Clear();
811
812 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
813
814 RemoveLastFunctionChildren(m_Parser->GetTokenTree(), m_LastFuncTokenIdx);
815
816 // find "using namespace" directives in the file
817 TokenIdxSet search_scope;
818 ParseUsingNamespace(searchData, search_scope, caretPos);
819
820 // parse function's arguments
821 ParseFunctionArguments(searchData, caretPos);
822
823 // parse current code block (from the start of function up to the cursor)
824 ParseLocalBlock(searchData, search_scope, caretPos);
825
826 if (!reallyUseAI)
827 {
828 tree = m_Parser->GetTokenTree();
829
830 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
831
832 // all tokens, no AI whatsoever
833 for (size_t i = 0; i < tree->size(); ++i)
834 result.insert(i);
835
836 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
837
838 return result.size();
839 }
840
841 // we have correctly collected all the tokens, so we will do the artificial intelligence search
842 return AI(result, searchData, wxEmptyString, isPrefix, caseSensitive, &search_scope, caretPos);
843 }
844
MarkItemsByAI(TokenIdxSet & result,bool reallyUseAI,bool isPrefix,bool caseSensitive,int caretPos)845 size_t NativeParser::MarkItemsByAI(TokenIdxSet& result,
846 bool reallyUseAI,
847 bool isPrefix,
848 bool caseSensitive,
849 int caretPos)
850 {
851 if (s_DebugSmartSense)
852 CCLogger::Get()->DebugLog(F(_T("MarkItemsByAI_1()")));
853
854 cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
855 if (!editor)
856 return 0;
857
858 ccSearchData searchData = { editor->GetControl(), editor->GetFilename() };
859 if (!searchData.control)
860 return 0;
861
862 TRACE(_T("NativeParser::MarkItemsByAI_1()"));
863
864 return MarkItemsByAI(&searchData, result, reallyUseAI, isPrefix, caseSensitive, caretPos);
865 }
866
GetCallTips(wxArrayString & items,int & typedCommas,cbEditor * ed,int pos)867 int NativeParser::GetCallTips(wxArrayString& items, int& typedCommas, cbEditor* ed, int pos)
868 {
869 items.Clear();
870 typedCommas = 0;
871 int commas = 0;
872
873 if (!ed || !m_Parser->Done())
874 {
875 items.Add(wxT("Parsing at the moment..."));
876 return wxSCI_INVALID_POSITION;
877 }
878
879 TRACE(_T("NativeParser::GetCallTips()"));
880
881 ccSearchData searchData = { ed->GetControl(), ed->GetFilename() };
882 if (pos == wxNOT_FOUND)
883 pos = searchData.control->GetCurrentPos();
884 int nest = 0;
885 while (--pos > 0)
886 {
887 const int style = searchData.control->GetStyleAt(pos);
888 if ( searchData.control->IsString(style)
889 || searchData.control->IsCharacter(style)
890 || searchData.control->IsComment(style) )
891 {
892 continue;
893 }
894
895 const wxChar ch = searchData.control->GetCharAt(pos);
896 if (ch == _T(';'))
897 return wxSCI_INVALID_POSITION;
898 else if (ch == _T(','))
899 {
900 if (nest == 0)
901 ++commas;
902 }
903 else if (ch == _T(')'))
904 --nest;
905 else if (ch == _T('('))
906 {
907 ++nest;
908 if (nest > 0)
909 break;
910 }
911 }// while
912
913 // strip un-wanted
914 while (--pos > 0)
915 {
916 if ( searchData.control->GetCharAt(pos) <= _T(' ')
917 || searchData.control->IsComment(searchData.control->GetStyleAt(pos)) )
918 {
919 continue;
920 }
921 break;
922 }
923
924 const int start = searchData.control->WordStartPosition(pos, true);
925 const int end = searchData.control->WordEndPosition(pos, true);
926 const wxString target = searchData.control->GetTextRange(start, end);
927 TRACE(_T("Sending \"%s\" for call-tip"), target.wx_str());
928 if (target.IsEmpty())
929 return wxSCI_INVALID_POSITION;
930
931 TokenIdxSet result;
932 MarkItemsByAI(result, true, false, true, end);
933
934 ComputeCallTip(m_Parser->GetTokenTree(), result, items);
935
936 typedCommas = commas;
937 TRACE(_T("NativeParser::GetCallTips: typedCommas=%d"), typedCommas);
938 items.Sort();
939 return end;
940 }
941
ParseProjectSearchDirs(const cbProject & project)942 wxArrayString NativeParser::ParseProjectSearchDirs(const cbProject &project)
943 {
944
945 const TiXmlNode *extensionNode = project.GetExtensionsNode();
946 if (!extensionNode)
947 return wxArrayString();
948 const TiXmlElement* elem = extensionNode->ToElement();
949 if (!elem)
950 return wxArrayString();
951
952 wxArrayString pdirs;
953 const TiXmlElement* CCConf = elem->FirstChildElement("code_completion");
954 if (CCConf)
955 {
956 const TiXmlElement* pathsElem = CCConf->FirstChildElement("search_path");
957 while (pathsElem)
958 {
959 if (pathsElem->Attribute("add"))
960 {
961 wxString dir = cbC2U(pathsElem->Attribute("add"));
962 if (pdirs.Index(dir) == wxNOT_FOUND)
963 pdirs.Add(dir);
964 }
965
966 pathsElem = pathsElem->NextSiblingElement("search_path");
967 }
968 }
969 return pdirs;
970 }
971
SetProjectSearchDirs(cbProject & project,const wxArrayString & dirs)972 void NativeParser::SetProjectSearchDirs(cbProject &project, const wxArrayString &dirs)
973 {
974 TiXmlNode *extensionNode = project.GetExtensionsNode();
975 if (!extensionNode)
976 return;
977 TiXmlElement* elem = extensionNode->ToElement();
978 if (!elem)
979 return;
980
981 // since rev4332, the project keeps a copy of the <Extensions> element
982 // and re-uses it when saving the project (so to avoid losing entries in it
983 // if plugins that use that element are not loaded atm).
984 // so, instead of blindly inserting the element, we must first check it's
985 // not already there (and if it is, clear its contents)
986 TiXmlElement* node = elem->FirstChildElement("code_completion");
987 if (!node)
988 node = elem->InsertEndChild(TiXmlElement("code_completion"))->ToElement();
989 if (node)
990 {
991 node->Clear();
992 for (size_t i = 0; i < dirs.GetCount(); ++i)
993 {
994 TiXmlElement* path = node->InsertEndChild(TiXmlElement("search_path"))->ToElement();
995 if (path) path->SetAttribute("add", cbU2C(dirs[i]));
996 }
997 }
998 }
999
CreateClassBrowser()1000 void NativeParser::CreateClassBrowser()
1001 {
1002 #if wxCHECK_VERSION(3, 0, 0)
1003 return;
1004 #endif // wxCHECK_VERSION
1005
1006 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
1007 if (m_ClassBrowser || !cfg->ReadBool(_T("/use_symbols_browser"), true))
1008 return;
1009
1010 TRACE(_T("NativeParser::CreateClassBrowser: Enter"));
1011
1012 m_ClassBrowserIsFloating = cfg->ReadBool(_T("/as_floating_window"), false);
1013
1014 if (m_ClassBrowserIsFloating)
1015 {
1016 m_ClassBrowser = new ClassBrowser(Manager::Get()->GetAppWindow(), this);
1017
1018 // make this a free floating/docking window
1019 CodeBlocksDockEvent evt(cbEVT_ADD_DOCK_WINDOW);
1020
1021 evt.name = _T("SymbolsBrowser");
1022 evt.title = _("Symbols browser");
1023 evt.pWindow = m_ClassBrowser;
1024 evt.dockSide = CodeBlocksDockEvent::dsRight;
1025 evt.desiredSize.Set(200, 250);
1026 evt.floatingSize.Set(200, 250);
1027 evt.minimumSize.Set(150, 150);
1028 evt.shown = true;
1029 evt.hideable = true;
1030 Manager::Get()->ProcessEvent(evt);
1031 m_ClassBrowser->UpdateSash();
1032 }
1033 else
1034 {
1035 // make this a tab in projectmanager notebook
1036 m_ClassBrowser = new ClassBrowser(Manager::Get()->GetProjectManager()->GetUI().GetNotebook(), this);
1037 Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->AddPage(m_ClassBrowser, _("Symbols"));
1038 m_ClassBrowser->UpdateSash();
1039 }
1040
1041 // Dreaded DDE-open bug related: do not touch unless for a good reason
1042 // TODO (Morten): ? what's bug? I test it, it's works well now.
1043 m_ClassBrowser->SetParser(m_Parser); // Also updates class browser
1044
1045 TRACE(_T("NativeParser::CreateClassBrowser: Leave"));
1046 }
1047
RemoveClassBrowser(cb_unused bool appShutDown)1048 void NativeParser::RemoveClassBrowser(cb_unused bool appShutDown)
1049 {
1050 if (!m_ClassBrowser)
1051 return;
1052
1053 TRACE(_T("NativeParser::RemoveClassBrowser()"));
1054
1055 if (m_ClassBrowserIsFloating)
1056 {
1057 CodeBlocksDockEvent evt(cbEVT_REMOVE_DOCK_WINDOW);
1058 evt.pWindow = m_ClassBrowser;
1059 Manager::Get()->ProcessEvent(evt);
1060 }
1061 else
1062 {
1063 int idx = Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->GetPageIndex(m_ClassBrowser);
1064 if (idx != -1)
1065 Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->RemovePage(idx);
1066 }
1067 m_ClassBrowser->Destroy();
1068 m_ClassBrowser = NULL;
1069 }
1070
UpdateClassBrowser()1071 void NativeParser::UpdateClassBrowser()
1072 {
1073 if (!m_ClassBrowser)
1074 return;
1075
1076 TRACE(_T("NativeParser::UpdateClassBrowser()"));
1077
1078 if ( m_Parser != m_TempParser
1079 && m_Parser->Done()
1080 && !Manager::IsAppShuttingDown() )
1081 {
1082 m_ClassBrowser->UpdateClassBrowserView();
1083 }
1084 }
1085
DoFullParsing(cbProject * project,ParserBase * parser)1086 bool NativeParser::DoFullParsing(cbProject* project, ParserBase* parser)
1087 {
1088 wxStopWatch timer;
1089 if (!parser)
1090 return false;
1091
1092 TRACE(_T("NativeParser::DoFullParsing: Enter"));
1093
1094 if (!AddCompilerDirs(project, parser))
1095 CCLogger::Get()->DebugLog(_T("NativeParser::DoFullParsing: AddCompilerDirs failed!"));
1096
1097 if (!AddCompilerPredefinedMacros(project, parser))
1098 CCLogger::Get()->DebugLog(_T("NativeParser::DoFullParsing: AddCompilerPredefinedMacros failed!"));
1099
1100 if (!AddProjectDefinedMacros(project, parser))
1101 CCLogger::Get()->DebugLog(_T("NativeParser::DoFullParsing: AddProjectDefinedMacros failed!"));
1102
1103 // add per-project dirs
1104 if (project)
1105 {
1106 if ( !parser->Options().platformCheck
1107 || (parser->Options().platformCheck && project->SupportsCurrentPlatform()) )
1108 {
1109 // Note: This parses xml data to get the search directories. It might be expensive if
1110 // the list of directories is too large.
1111 AddIncludeDirsToParser(ParseProjectSearchDirs(*project),
1112 project->GetBasePath(), parser);
1113 }
1114 }
1115
1116 StringList localSources;
1117
1118 if (project)
1119 {
1120 for (FilesList::const_iterator fl_it = project->GetFilesList().begin();
1121 fl_it != project->GetFilesList().end(); ++fl_it)
1122 {
1123 ProjectFile* pf = *fl_it;
1124 if (!pf)
1125 continue;
1126 // check the file types in the project files
1127 ParserCommon::EFileType ft = ParserCommon::FileType(pf->relativeFilename);
1128 if (ft == ParserCommon::ftSource) // parse source files
1129 {
1130 localSources.push_back(pf->file.GetFullPath());
1131 }
1132 }
1133 }
1134
1135 CCLogger::Get()->DebugLog(_T("NativeParser::DoFullParsing: Adding cpp/c files to batch-parser"));
1136
1137 // parse priority files
1138 wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
1139
1140
1141 if (!localSources.empty())
1142 {
1143 CCLogger::Get()->DebugLog(F(_T("NativeParser::DoFullParsing: Added %lu source file(s) for project '%s' to batch-parser..."),
1144 static_cast<unsigned long>( localSources.size()), prj.wx_str()));
1145
1146 // local source files added to Parser
1147 parser->AddBatchParse(localSources);
1148 }
1149
1150 TRACE(_T("NativeParser::DoFullParsing: Leave"));
1151
1152 long time = timer.Time();
1153 if (time >= 50)
1154 Manager::Get()->GetLogManager()->Log(F(wxT("NativeParser::DoFullParsing took: %.3f seconds."),
1155 time / 1000.0f));
1156 return true;
1157 }
1158
SwitchParser(cbProject * project,ParserBase * parser)1159 bool NativeParser::SwitchParser(cbProject* project, ParserBase* parser)
1160 {
1161 if (!parser || parser == m_Parser || GetParserByProject(project) != parser)
1162 {
1163 TRACE(_T("NativeParser::SwitchParser: No need to / cannot switch."));
1164 return false;
1165 }
1166
1167 TRACE(_T("NativeParser::SwitchParser()"));
1168
1169 SetParser(parser); // Also updates class browser
1170
1171 wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
1172 wxString log(F(_("Switch parser to project '%s'"), prj.wx_str()));
1173 CCLogger::Get()->Log(log);
1174 CCLogger::Get()->DebugLog(log);
1175
1176 return true;
1177 }
1178
SetParser(ParserBase * parser)1179 void NativeParser::SetParser(ParserBase* parser)
1180 {
1181 // the active parser is the same as the old active parser, nothing need to be done
1182 if (m_Parser == parser)
1183 return;
1184
1185 // a new parser is active, so remove the old parser's local variable tokens.
1186 // if m_Parser == nullptr, this means the active parser is already deleted.
1187 if (m_Parser)
1188 RemoveLastFunctionChildren(m_Parser->GetTokenTree(), m_LastFuncTokenIdx);
1189
1190 // refresh code completion related variables
1191 InitCCSearchVariables();
1192
1193 // switch the active parser
1194 m_Parser = parser;
1195
1196 if (m_ClassBrowser)
1197 m_ClassBrowser->SetParser(parser); // Also updates class browser
1198 }
1199
ClearParsers()1200 void NativeParser::ClearParsers()
1201 {
1202 TRACE(_T("NativeParser::ClearParsers()"));
1203
1204 if (m_ParserPerWorkspace)
1205 {
1206 while (!m_ParsedProjects.empty() && DeleteParser(*m_ParsedProjects.begin()))
1207 ;
1208 }
1209 else
1210 {
1211 while (!m_ParserList.empty() && DeleteParser(m_ParserList.begin()->first))
1212 ;
1213 }
1214 }
1215
RemoveObsoleteParsers()1216 void NativeParser::RemoveObsoleteParsers()
1217 {
1218 TRACE(_T("NativeParser::RemoveObsoleteParsers: Enter"));
1219
1220 ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
1221 const size_t maxParsers = cfg->ReadInt(_T("/max_parsers"), 5);
1222 wxArrayString removedProjectNames;
1223 std::pair<cbProject*, ParserBase*> info = GetParserInfoByCurrentEditor();
1224
1225 while (m_ParserList.size() > maxParsers)
1226 {
1227 bool deleted = false;
1228 for (ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it)
1229 {
1230 if (it->second == info.second)
1231 continue;
1232
1233 wxString prj = (it->first ? it->first->GetTitle() : _T("*NONE*"));
1234 if ( DeleteParser(it->first) )
1235 {
1236 // Please note that DeleteParser() may erase one element of the m_ParserList, so
1237 // do NOT use the constant iterator here again, as the element pointed by it may be
1238 // destroyed in DeleteParser().
1239 removedProjectNames.Add(prj);
1240 deleted = true;
1241 break;
1242 }
1243 }
1244
1245 if (!deleted)
1246 break;
1247 }
1248
1249 for (size_t i = 0; i < removedProjectNames.GetCount(); ++i)
1250 {
1251 wxString log(F(_("NativeParser::RemoveObsoleteParsers:Removed obsolete parser of '%s'"), removedProjectNames[i].wx_str()));
1252 CCLogger::Get()->Log(log);
1253 CCLogger::Get()->DebugLog(log);
1254 }
1255
1256 TRACE(_T("NativeParser::RemoveObsoleteParsers: Leave"));
1257 }
1258
GetParserInfoByCurrentEditor()1259 std::pair<cbProject*, ParserBase*> NativeParser::GetParserInfoByCurrentEditor()
1260 {
1261 std::pair<cbProject*, ParserBase*> info(nullptr, nullptr);
1262 cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
1263
1264 if ( editor ) //No need to check editor->GetFilename, because a built-in editor always have a filename
1265 {
1266 info.first = GetProjectByEditor(editor);
1267 info.second = GetParserByProject(info.first);
1268 }
1269
1270 return info;
1271 }
1272
SetCBViewMode(const BrowserViewMode & mode)1273 void NativeParser::SetCBViewMode(const BrowserViewMode& mode)
1274 {
1275 m_Parser->ClassBrowserOptions().showInheritance = (mode == bvmInheritance) ? true : false;
1276 UpdateClassBrowser();
1277 }
1278
1279 // helper funcs
1280
1281 // Start an Artificial Intelligence (!) sequence to gather all the matching tokens..
1282 // The actual AI is in FindAIMatches() below...
AI(TokenIdxSet & result,ccSearchData * searchData,const wxString & lineText,bool isPrefix,bool caseSensitive,TokenIdxSet * search_scope,int caretPos)1283 size_t NativeParser::AI(TokenIdxSet& result,
1284 ccSearchData* searchData,
1285 const wxString& lineText,
1286 bool isPrefix,
1287 bool caseSensitive,
1288 TokenIdxSet* search_scope,
1289 int caretPos)
1290 {
1291 m_LastAISearchWasGlobal = false;
1292 m_LastAIGlobalSearch.Clear();
1293
1294 int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
1295 if (pos < 0 || pos > searchData->control->GetLength())
1296 return 0;
1297
1298 int line = searchData->control->LineFromPosition(pos);
1299
1300 // Get the actual search text, such as "objA.m_aaa.m_bbb"
1301 wxString actual_search(lineText);
1302 if (actual_search.IsEmpty())
1303 {
1304 // Get the position at the start of current line
1305 const int startPos = searchData->control->PositionFromLine(line);
1306 actual_search = searchData->control->GetTextRange(startPos, pos).Trim();
1307 }
1308
1309 // Do the whole job here
1310 if (s_DebugSmartSense)
1311 {
1312 CCLogger::Get()->DebugLog(_T("AI() ========================================================="));
1313 CCLogger::Get()->DebugLog(F(_T("AI() Doing AI for '%s':"), actual_search.wx_str()));
1314 }
1315 TRACE(_T("NativeParser::AI()"));
1316
1317 TokenTree* tree = m_Parser->GetTokenTree();
1318
1319 // find current function's namespace so we can include local scope's tokens
1320 // we ' ll get the function's token (all matches) and add its parent namespace
1321 TokenIdxSet proc_result;
1322 size_t found_at = FindCurrentFunctionToken(searchData, proc_result, pos);
1323
1324 TokenIdxSet scope_result;
1325 if (found_at)
1326 FindCurrentFunctionScope(tree, proc_result, scope_result);
1327
1328 // add additional search scopes???
1329 // for example, we are here:
1330 /* void ClassA::FunctionB(int paraC){
1331 m_aaa
1332 */
1333 // then, ClassA should be added as a search_scope, the global scope should be added too.
1334
1335 // if search_scope is already defined, then, add scope_result to search_scope
1336 // otherwise we just set search_scope as scope_result
1337 if (!search_scope)
1338 search_scope = &scope_result;
1339 else
1340 {
1341 // add scopes, "tis" refer to "token index set"
1342 for (TokenIdxSet::const_iterator tis_it = scope_result.begin(); tis_it != scope_result.end(); ++tis_it)
1343 search_scope->insert(*tis_it);
1344 }
1345
1346 // remove non-namespace/class tokens
1347 CleanupSearchScope(tree, search_scope);
1348
1349 // find all other matches
1350 std::queue<ParserComponent> components;
1351 BreakUpComponents(actual_search, components);
1352
1353 m_LastAISearchWasGlobal = components.size() <= 1;
1354 if (!components.empty())
1355 m_LastAIGlobalSearch = components.front().component;
1356
1357 ResolveExpression(tree, components, *search_scope, result, caseSensitive, isPrefix);
1358
1359 if (s_DebugSmartSense)
1360 CCLogger::Get()->DebugLog(F(_T("AI() AI leave, returned %lu results"),static_cast<unsigned long>(result.size())));
1361
1362 return result.size();
1363 }
1364
1365 // find a function where current caret located.
1366 // We need to find extra class scope, otherwise, we will failed do the cc in a class declaration
FindCurrentFunctionToken(ccSearchData * searchData,TokenIdxSet & result,int caretPos)1367 size_t NativeParser::FindCurrentFunctionToken(ccSearchData* searchData, TokenIdxSet& result, int caretPos)
1368 {
1369 TokenIdxSet scope_result;
1370 wxString procName;
1371 wxString scopeName;
1372 FindCurrentFunctionStart(searchData, &scopeName, &procName, nullptr, caretPos);
1373
1374 if (procName.IsEmpty())
1375 return 0;
1376
1377 // add current scope
1378 if (!scopeName.IsEmpty())
1379 {
1380 // _namespace ends with double-colon (::). remove it
1381 scopeName.RemoveLast();
1382 scopeName.RemoveLast();
1383
1384 // search for namespace
1385 std::queue<ParserComponent> ns;
1386 BreakUpComponents(scopeName, ns);
1387
1388 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1389
1390 // No critical section needed in this recursive function!
1391 // All functions that call this recursive FindAIMatches function, should already entered a critical section.
1392 FindAIMatches(m_Parser->GetTokenTree(), ns, scope_result, -1,
1393 true, true, false, tkNamespace | tkClass | tkTypedef);
1394
1395 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1396 }
1397
1398 // if no scope, use global scope
1399 if (scope_result.empty())
1400 scope_result.insert(-1);
1401
1402 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1403
1404 for (TokenIdxSet::const_iterator tis_it = scope_result.begin(); tis_it != scope_result.end(); ++tis_it)
1405 {
1406 GenerateResultSet(m_Parser->GetTokenTree(), procName, *tis_it, result,
1407 true, false, tkAnyFunction | tkClass);
1408 }
1409
1410 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1411
1412 return result.size();
1413 }
1414
1415 // returns current function's position (not line) in the editor
FindCurrentFunctionStart(ccSearchData * searchData,wxString * nameSpace,wxString * procName,int * functionIndex,int caretPos)1416 int NativeParser::FindCurrentFunctionStart(ccSearchData* searchData,
1417 wxString* nameSpace,
1418 wxString* procName,
1419 int* functionIndex,
1420 int caretPos)
1421 {
1422 // cache last result for optimization
1423 int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
1424 if ((pos < 0) || (pos > searchData->control->GetLength()))
1425 {
1426 if (s_DebugSmartSense)
1427 CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Cannot determine position. caretPos=%d, control=%d"),
1428 caretPos, searchData->control->GetCurrentPos()));
1429 return -1;
1430 }
1431
1432 TRACE(_T("NativeParser::FindCurrentFunctionStart()"));
1433
1434 const int curLine = searchData->control->LineFromPosition(pos) + 1;
1435 if ( (curLine == m_LastLine)
1436 && ( (searchData->control == m_LastControl) && (!searchData->control->GetModify()) )
1437 && (searchData->file == m_LastFile) )
1438 {
1439 if (nameSpace) *nameSpace = m_LastNamespace;
1440 if (procName) *procName = m_LastPROC;
1441 if (functionIndex) *functionIndex = m_LastFunctionIndex;
1442
1443 if (s_DebugSmartSense)
1444 CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Cached namespace='%s', cached proc='%s' (returning %d)"),
1445 m_LastNamespace.wx_str(), m_LastPROC.wx_str(), m_LastResult));
1446
1447 return m_LastResult;
1448 }
1449
1450 if (s_DebugSmartSense)
1451 CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Looking for tokens in '%s'"),
1452 searchData->file.wx_str()));
1453 m_LastFile = searchData->file;
1454 m_LastControl = searchData->control;
1455 m_LastLine = curLine;
1456
1457 // we have all the tokens in the current file, then just do a loop on all
1458 // the tokens, see if the line is in the token's imp.
1459 TokenIdxSet result;
1460 size_t num_results = m_Parser->FindTokensInFile(searchData->file, result, tkAnyFunction | tkClass);
1461 if (s_DebugSmartSense)
1462 CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Found %lu results"), static_cast<unsigned long>(num_results)));
1463
1464 TokenTree* tree = m_Parser->GetTokenTree();
1465
1466 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1467
1468 const int idx = GetTokenFromCurrentLine(tree, result, curLine, searchData->file);
1469 const Token* token = tree->at(idx);
1470 if (token)
1471 {
1472 // got it :)
1473 if (s_DebugSmartSense)
1474 CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Current function: '%s' (at line %u)"),
1475 token->DisplayName().wx_str(),
1476 token->m_ImplLine));
1477
1478 m_LastNamespace = token->GetNamespace();
1479 m_LastPROC = token->m_Name;
1480 m_LastFunctionIndex = token->m_Index;
1481 m_LastResult = searchData->control->PositionFromLine(token->m_ImplLine - 1);
1482
1483 // locate function's opening brace
1484 if (token->m_TokenKind & tkAnyFunction)
1485 {
1486 while (m_LastResult < searchData->control->GetTextLength())
1487 {
1488 wxChar ch = searchData->control->GetCharAt(m_LastResult);
1489 if (ch == _T('{'))
1490 break;
1491 else if (ch == 0)
1492 {
1493 if (s_DebugSmartSense)
1494 CCLogger::Get()->DebugLog(_T("FindCurrentFunctionStart() Can't determine functions opening brace..."));
1495
1496 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1497 return -1;
1498 }
1499
1500 ++m_LastResult;
1501 }
1502 }
1503
1504 if (nameSpace) *nameSpace = m_LastNamespace;
1505 if (procName) *procName = m_LastPROC;
1506 if (functionIndex) *functionIndex = token->m_Index;
1507
1508 if (s_DebugSmartSense)
1509 CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Namespace='%s', proc='%s' (returning %d)"),
1510 m_LastNamespace.wx_str(), m_LastPROC.wx_str(), m_LastResult));
1511
1512 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1513 return m_LastResult;
1514 }
1515
1516 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1517
1518 if (s_DebugSmartSense)
1519 CCLogger::Get()->DebugLog(_T("FindCurrentFunctionStart() Can't determine current function..."));
1520
1521 m_LastResult = -1;
1522 return -1;
1523 }
1524
ParseUsingNamespace(ccSearchData * searchData,TokenIdxSet & search_scope,int caretPos)1525 bool NativeParser::ParseUsingNamespace(ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos)
1526 {
1527 if (s_DebugSmartSense)
1528 CCLogger::Get()->DebugLog(_T("ParseUsingNamespace() Parse file scope for \"using namespace\""));
1529 TRACE(_T("NativeParser::ParseUsingNamespace()"));
1530
1531 int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
1532 if (pos < 0 || pos > searchData->control->GetLength())
1533 return false;
1534
1535 // Get the buffer from begin of the editor to the current caret position
1536 wxString buffer = searchData->control->GetTextRange(0, pos);
1537
1538 return ParseBufferForUsingNamespace(buffer, search_scope);
1539 }
1540
ParseBufferForUsingNamespace(const wxString & buffer,TokenIdxSet & search_scope,bool bufferSkipBlocks)1541 bool NativeParser::ParseBufferForUsingNamespace(const wxString& buffer, TokenIdxSet& search_scope, bool bufferSkipBlocks)
1542 {
1543 wxArrayString ns;
1544 m_Parser->ParseBufferForUsingNamespace(buffer, ns, bufferSkipBlocks);
1545
1546 TokenTree* tree = m_Parser->GetTokenTree();
1547
1548 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1549
1550 for (size_t i = 0; i < ns.GetCount(); ++i)
1551 {
1552 std::queue<ParserComponent> components;
1553 BreakUpComponents(ns[i], components);
1554
1555 int parentIdx = -1;
1556 while (!components.empty())
1557 {
1558 ParserComponent pc = components.front();
1559 components.pop();
1560
1561 int id = tree->TokenExists(pc.component, parentIdx, tkNamespace);
1562 if (id == -1)
1563 {
1564 parentIdx = -1;
1565 break;
1566 }
1567 parentIdx = id;
1568 }
1569
1570 if (s_DebugSmartSense && parentIdx != -1)
1571 {
1572 const Token* token = tree->at(parentIdx);
1573 if (token)
1574 CCLogger::Get()->DebugLog(F(_T("ParseUsingNamespace() Found %s%s"),
1575 token->GetNamespace().wx_str(), token->m_Name.wx_str()));
1576 }
1577 search_scope.insert(parentIdx);
1578 }
1579
1580 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1581
1582 return true;
1583 }
1584
ParseFunctionArguments(ccSearchData * searchData,int caretPos)1585 bool NativeParser::ParseFunctionArguments(ccSearchData* searchData, int caretPos)
1586 {
1587 if (s_DebugSmartSense)
1588 CCLogger::Get()->DebugLog(_T("ParseFunctionArguments() Parse function arguments"));
1589 TRACE(_T("NativeParser::ParseFunctionArguments()"));
1590
1591 TokenIdxSet proc_result;
1592
1593 TokenTree* tree = m_Parser->GetTokenTree(); // the one used inside FindCurrentFunctionToken, FindAIMatches and GenerateResultSet
1594
1595 size_t found_at = FindCurrentFunctionToken(searchData, proc_result, caretPos);
1596 if (!found_at)
1597 {
1598 if (s_DebugSmartSense)
1599 CCLogger::Get()->DebugLog(_T("ParseFunctionArguments() Could not determine current function's namespace..."));
1600 TRACE(_T("ParseFunctionArguments() Could not determine current function's namespace..."));
1601 return false;
1602 }
1603
1604 const int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
1605 const unsigned int curLine = searchData->control->LineFromPosition(pos) + 1;
1606
1607 bool locked = false;
1608 for (TokenIdxSet::const_iterator tis_it = proc_result.begin(); tis_it != proc_result.end(); ++tis_it)
1609 {
1610 wxString buffer;
1611 int initLine = -1;
1612 int tokenIdx = -1;
1613
1614 if (locked)
1615 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1616
1617 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1618 locked = true;
1619
1620 const Token* token = tree->at(*tis_it);
1621
1622 if (!token)
1623 continue;
1624 if (curLine < token->m_ImplLineStart || curLine > token->m_ImplLineEnd)
1625 continue;
1626
1627 if (s_DebugSmartSense)
1628 CCLogger::Get()->DebugLog(_T("ParseFunctionArguments() + Function match: ") + token->m_Name);
1629 TRACE(_T("ParseFunctionArguments() + Function match: ") + token->m_Name);
1630
1631 if (!token->m_Args.IsEmpty() && !token->m_Args.Matches(_T("()")))
1632 {
1633 buffer = token->m_Args;
1634 // Now we have something like "(int my_int, const TheClass* my_class, float f)"
1635 buffer.Remove(0, 1); // remove (
1636 buffer.RemoveLast(); // remove )
1637 // Now we have "int my_int, const TheClass* my_class, float f"
1638 buffer.Replace(_T(","), _T(";")); // replace commas with semi-colons
1639 // Now we have "int my_int; const TheClass* my_class; float f"
1640 buffer << _T(';'); // aid parser ;)
1641 // Finally we have "int my_int; const TheClass* my_class; float f;"
1642 buffer.Trim();
1643
1644 if (s_DebugSmartSense)
1645 CCLogger::Get()->DebugLog(F(_T("ParseFunctionArguments() Parsing arguments: \"%s\""), buffer.wx_str()));
1646
1647 if (!buffer.IsEmpty())
1648 {
1649 const int textLength= searchData->control->GetLength();
1650 if (textLength == -1)
1651 continue;
1652 int paraPos = searchData->control->PositionFromLine(token->m_ImplLine - 1);
1653 if (paraPos == -1)
1654 continue;
1655 while (paraPos < textLength && searchData->control->GetCharAt(paraPos++) != _T('('))
1656 ;
1657 while (paraPos < textLength && searchData->control->GetCharAt(paraPos++) < _T(' '))
1658 ;
1659 initLine = searchData->control->LineFromPosition(paraPos) + 1;
1660 if (initLine == -1)
1661 continue;
1662 tokenIdx = token->m_Index;
1663 }
1664 }
1665
1666 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1667 locked = false;
1668
1669 if ( !buffer.IsEmpty()
1670 && !m_Parser->ParseBuffer(buffer, false, false, true, searchData->file, tokenIdx, initLine)
1671 && s_DebugSmartSense)
1672 {
1673 CCLogger::Get()->DebugLog(_T("ParseFunctionArguments() Error parsing arguments."));
1674 }
1675 }
1676
1677 if (locked)
1678 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1679
1680 return true;
1681 }
1682
ParseLocalBlock(ccSearchData * searchData,TokenIdxSet & search_scope,int caretPos)1683 bool NativeParser::ParseLocalBlock(ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos)
1684 {
1685 if (s_DebugSmartSense)
1686 CCLogger::Get()->DebugLog(_T("ParseLocalBlock() Parse local block"));
1687 TRACE(_T("NativeParser::ParseLocalBlock()"));
1688
1689 int parentIdx = -1;
1690 int blockStart = FindCurrentFunctionStart(searchData, nullptr, nullptr, &parentIdx, caretPos);
1691 int initLine = 0;
1692 if (parentIdx != -1)
1693 {
1694 TokenTree* tree = m_Parser->GetTokenTree();
1695
1696 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1697
1698 const Token* parent = tree->at(parentIdx);
1699 if (parent && (parent->m_TokenKind & tkAnyFunction))
1700 {
1701 m_LastFuncTokenIdx = parent->m_Index;
1702 initLine = parent->m_ImplLineStart;
1703 }
1704
1705 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1706
1707 // only need to parse the function body, other type of Tokens' body such as class declaration
1708 // should not be parsed.
1709 if (!parent || !(parent->m_TokenKind & tkAnyFunction))
1710 return false;
1711 }
1712
1713 if (blockStart != -1)
1714 {
1715 cbStyledTextCtrl* stc = searchData->control;
1716 // if we are in a function body, then blockStart points to the '{', so we just skip the '{'.
1717 if (stc->GetCharAt(blockStart) == wxT('{'))
1718 ++blockStart;
1719 const int pos = (caretPos == -1 ? stc->GetCurrentPos() : caretPos);
1720 const int line = stc->LineFromPosition(pos);
1721 const int blockEnd = stc->GetLineEndPosition(line);
1722 if (blockEnd < 0 || blockEnd > stc->GetLength())
1723 {
1724 if (s_DebugSmartSense)
1725 {
1726 CCLogger::Get()->DebugLog(F(_T("ParseLocalBlock() ERROR blockEnd=%d and edLength=%d?!"),
1727 blockEnd, stc->GetLength()));
1728 }
1729 return false;
1730 }
1731
1732 if (blockStart >= blockEnd)
1733 blockStart = blockEnd;
1734
1735 // wxString buffer = searchData->control->GetTextRange(blockStart, blockEnd);
1736 wxString buffer;
1737 // condense out-of-scope braces {...}
1738 int scanPos = blockEnd;
1739 for (int curPos = pos; curPos > blockStart; --curPos)
1740 {
1741 if (stc->GetCharAt(curPos) != wxT('}'))
1742 continue;
1743 const int style = stc->GetStyleAt(curPos);
1744 if ( stc->IsString(style)
1745 || stc->IsCharacter(style)
1746 || stc->IsComment(style))
1747 {
1748 continue;
1749 }
1750 const int scopeStart = stc->BraceMatch(curPos);
1751 if (scopeStart < blockStart)
1752 break;
1753 buffer.Prepend(stc->GetTextRange(curPos, scanPos));
1754 int startLn = stc->LineFromPosition(scopeStart);
1755 int endLn = stc->LineFromPosition(curPos);
1756 if (startLn < endLn) // maintain correct line numbers for parsed tokens
1757 buffer.Prepend( wxString(wxT('\n'), endLn - startLn) );
1758 scanPos = scopeStart + 1;
1759 curPos = scopeStart;
1760
1761 // condense out-of-scope for/if/while declarations
1762 int prevCharIdx = scopeStart - 1;
1763 for (; prevCharIdx > blockStart; --prevCharIdx)
1764 {
1765 if (stc->IsComment(stc->GetStyleAt(prevCharIdx)))
1766 continue;
1767 if (!wxIsspace(stc->GetCharAt(prevCharIdx)))
1768 break;
1769 }
1770 if (stc->GetCharAt(prevCharIdx) != wxT(')'))
1771 continue;
1772 const int paramStart = stc->BraceMatch(prevCharIdx);
1773 if (paramStart < blockStart)
1774 continue;
1775 for (prevCharIdx = paramStart - 1; prevCharIdx > blockStart; --prevCharIdx)
1776 {
1777 if (stc->IsComment(stc->GetStyleAt(prevCharIdx)))
1778 continue;
1779 if (!wxIsspace(stc->GetCharAt(prevCharIdx)))
1780 break;
1781 }
1782 const wxString text = stc->GetTextRange(stc->WordStartPosition(prevCharIdx, true),
1783 stc->WordEndPosition( prevCharIdx, true));
1784 if (text == wxT("for"))
1785 buffer.Prepend(wxT("(;;){"));
1786 else if (text == wxT("if") || text == wxT("while") || text == wxT("catch"))
1787 buffer.Prepend(wxT("(0){"));
1788 else
1789 continue;
1790 startLn = stc->LineFromPosition(prevCharIdx);
1791 endLn = stc->LineFromPosition(scopeStart);
1792 if (startLn < endLn)
1793 buffer.Prepend( wxString(wxT('\n'), endLn - startLn) );
1794 curPos = stc->WordStartPosition(prevCharIdx, true);
1795 scanPos = stc->WordEndPosition( prevCharIdx, true);
1796 }
1797 buffer.Prepend(stc->GetTextRange(blockStart, scanPos));
1798
1799 buffer.Trim();
1800
1801 ParseBufferForUsingNamespace(buffer, search_scope, false);
1802
1803 if ( !buffer.IsEmpty()
1804 && !m_Parser->ParseBuffer(buffer, false, false, true, searchData->file, m_LastFuncTokenIdx, initLine) )
1805 {
1806 if (s_DebugSmartSense)
1807 CCLogger::Get()->DebugLog(_T("ParseLocalBlock() ERROR parsing block:\n") + buffer);
1808 }
1809 else
1810 {
1811 if (s_DebugSmartSense)
1812 {
1813 CCLogger::Get()->DebugLog(F(_T("ParseLocalBlock() Block:\n%s"), buffer.wx_str()));
1814 CCLogger::Get()->DebugLog(_T("ParseLocalBlock() Local tokens:"));
1815
1816 TokenTree* tree = m_Parser->GetTokenTree();
1817
1818 CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1819
1820 for (size_t i = 0; i < tree->size(); ++i)
1821 {
1822 const Token* token = tree->at(i);
1823 if (token && token->m_IsTemp)
1824 {
1825 wxString log(wxString::Format(_T(" + %s (%d)"), token->DisplayName().wx_str(), token->m_Index));
1826 const Token* parent = tree->at(token->m_ParentIndex);
1827 if (parent)
1828 log += wxString::Format(_T("; Parent = %s (%d)"), parent->m_Name.wx_str(), token->m_ParentIndex);
1829 CCLogger::Get()->DebugLog(log);
1830 }
1831 }
1832
1833 CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1834 }
1835 return true;
1836 }
1837 }
1838 else
1839 {
1840 if (s_DebugSmartSense)
1841 CCLogger::Get()->DebugLog(_T("ParseLocalBlock() Could not determine current block start..."));
1842 }
1843 return false;
1844 }
1845
AddCompilerDirs(cbProject * project,ParserBase * parser)1846 bool NativeParser::AddCompilerDirs(cbProject* project, ParserBase* parser)
1847 {
1848 if (!parser)
1849 return false;
1850
1851 TRACE(_T("NativeParser::AddCompilerDirs: Enter"));
1852
1853 // If there is no project, work on default compiler
1854 if (!project)
1855 {
1856 AddCompilerIncludeDirsToParser(CompilerFactory::GetDefaultCompiler(), parser);
1857 TRACE(_T("NativeParser::AddCompilerDirs: Leave"));
1858 return true;
1859 }
1860
1861 // Otherwise (if there is a project), work on the project's compiler...
1862 wxString base = project->GetBasePath();
1863 parser->AddIncludeDir(base); // add project's base path
1864 TRACE(_T("NativeParser::AddCompilerDirs: Adding project base dir to parser: ") + base);
1865
1866 // ...so we can access post-processed project's search dirs
1867 Compiler* compiler = CompilerFactory::GetCompiler(project->GetCompilerID());
1868 cb::shared_ptr<CompilerCommandGenerator> generator(compiler ? compiler->GetCommandGenerator(project) : nullptr);
1869
1870 // get project include dirs
1871 if ( !parser->Options().platformCheck
1872 || (parser->Options().platformCheck && project->SupportsCurrentPlatform()) )
1873 {
1874 AddIncludeDirsToParser(project->GetIncludeDirs(), base, parser);
1875 }
1876
1877 // alloc array for project compiler AND "no. of targets" times target compilers
1878 int nCompilers = 1 + project->GetBuildTargetsCount();
1879 Compiler** Compilers = new Compiler* [nCompilers];
1880 memset(Compilers, 0, sizeof(Compiler*) * nCompilers);
1881 nCompilers = 0; // reset , use as insert index in the next for loop
1882
1883 // get targets include dirs
1884 for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
1885 {
1886 ProjectBuildTarget* target = project->GetBuildTarget(i);
1887 if (!target) continue;
1888
1889 if ( !parser->Options().platformCheck
1890 || (parser->Options().platformCheck && target->SupportsCurrentPlatform()) )
1891 {
1892 // post-processed search dirs (from build scripts)
1893 if (compiler && generator)
1894 AddIncludeDirsToParser(generator->GetCompilerSearchDirs(target), base, parser);
1895
1896 // apply target vars
1897 // target->GetCustomVars().ApplyVarsToEnvironment();
1898 AddIncludeDirsToParser(target->GetIncludeDirs(), base, parser);
1899
1900 // get the compiler
1901 wxString CompilerIndex = target->GetCompilerID();
1902 Compiler* tgtCompiler = CompilerFactory::GetCompiler(CompilerIndex);
1903 if (tgtCompiler)
1904 {
1905 Compilers[nCompilers] = tgtCompiler;
1906 ++nCompilers;
1907 }
1908 } // if (target)
1909 } // end loop over the targets
1910
1911 // add the project compiler to the array of compilers
1912 if (compiler)
1913 { // note it might be possible that this compiler is already in the list
1914 // no need to worry since the compiler list of the parser will filter out duplicate
1915 // entries in the include dir list
1916 Compilers[nCompilers++] = compiler;
1917 }
1918
1919 // add compiler include dirs
1920 for (int idxCompiler = 0; idxCompiler < nCompilers; ++idxCompiler)
1921 AddCompilerIncludeDirsToParser(Compilers[idxCompiler], parser);
1922
1923 if (!nCompilers)
1924 CCLogger::Get()->DebugLog(_T("NativeParser::AddCompilerDirs: No compilers found!"));
1925
1926 delete [] Compilers;
1927 TRACE(_T("NativeParser::AddCompilerDirs: Leave"));
1928 return true;
1929 }
1930
AddCompilerPredefinedMacros(cbProject * project,ParserBase * parser)1931 bool NativeParser::AddCompilerPredefinedMacros(cbProject* project, ParserBase* parser)
1932 {
1933 if (!parser)
1934 return false;
1935
1936 if (!parser->Options().wantPreprocessor)
1937 return false;
1938
1939 TRACE(_T("NativeParser::AddCompilerPredefinedMacros: Enter"));
1940
1941 // Default compiler is used for for single file parser (non project)
1942 wxString compilerId = project ? project->GetCompilerID() : CompilerFactory::GetDefaultCompilerID();
1943
1944 wxString defs;
1945 // gcc
1946 if (compilerId.Contains(_T("gcc")))
1947 {
1948 if ( !AddCompilerPredefinedMacrosGCC(compilerId, project, defs, parser) )
1949 return false;
1950 }
1951 // vc
1952 else if (compilerId.StartsWith(_T("msvc")))
1953 {
1954 if ( !AddCompilerPredefinedMacrosVC(compilerId, defs, parser) )
1955 return false;
1956 }
1957
1958 TRACE(_T("NativeParser::AddCompilerPredefinedMacros: Add compiler predefined preprocessor macros:\n%s"), defs.wx_str());
1959 parser->AddPredefinedMacros(defs);
1960
1961 TRACE(_T("NativeParser::AddCompilerPredefinedMacros: Leave"));
1962 if ( defs.IsEmpty() )
1963 return false;
1964
1965 return true;
1966 }
1967
AddCompilerPredefinedMacrosGCC(const wxString & compilerId,cbProject * project,wxString & defs,ParserBase * parser)1968 bool NativeParser::AddCompilerPredefinedMacrosGCC(const wxString& compilerId, cbProject* project, wxString& defs, ParserBase* parser)
1969 {
1970 Compiler* compiler = CompilerFactory::GetCompiler(compilerId);
1971 if (!compiler)
1972 return false;
1973
1974 if (parser->Options().platformCheck && !compiler->SupportsCurrentPlatform())
1975 {
1976 TRACE(_T("NativeParser::AddCompilerPredefinedMacrosGCC: Not supported on current platform!"));
1977 return false;
1978 }
1979
1980 wxString sep = (platform::windows ? _T("\\") : _T("/"));
1981 wxString cpp_compiler = compiler->GetMasterPath() + sep + _T("bin") + sep + compiler->GetPrograms().CPP;
1982 Manager::Get()->GetMacrosManager()->ReplaceMacros(cpp_compiler);
1983
1984 static std::map<wxString, wxString> gccDefsMap;
1985 if ( gccDefsMap[cpp_compiler].IsEmpty() )
1986 {
1987 // Check if user set language standard version to use
1988 wxString standard = GetCompilerStandardGCC(compiler, project);
1989
1990 // Different command on Windows and other OSes
1991 #ifdef __WXMSW__
1992 const wxString args(wxString::Format(_T(" -E -dM -x c++ %s nul"), standard.wx_str()) );
1993 #else
1994 const wxString args(wxString::Format(_T(" -E -dM -x c++ %s /dev/null"), standard.wx_str()) );
1995 #endif
1996
1997 wxArrayString output, error;
1998 if ( !SafeExecute(compiler->GetMasterPath(), compiler->GetPrograms().CPP, args, output, error) )
1999 return false;
2000
2001 // wxExecute can be a long action and C::B might have been shutdown in the meantime...
2002 if ( Manager::IsAppShuttingDown() )
2003 return false;
2004
2005 wxString& gccDefs = gccDefsMap[cpp_compiler];
2006 for (size_t i = 0; i < output.Count(); ++i)
2007 gccDefs += output[i] + _T("\n");
2008
2009 CCLogger::Get()->DebugLog(_T("NativeParser::AddCompilerPredefinedMacrosGCC: Caching predefined macros for compiler '")
2010 + cpp_compiler + _T("':\n") + gccDefs);
2011 }
2012
2013 defs = gccDefsMap[cpp_compiler];
2014
2015 return true;
2016 }
2017
GetCompilerStandardGCC(Compiler * compiler,cbProject * project)2018 wxString NativeParser::GetCompilerStandardGCC(Compiler* compiler, cbProject* project)
2019 {
2020 // Check if user set language standard version to use
2021 // 1.) Global compiler settings are first to search in
2022 wxString standard = GetCompilerUsingStandardGCC(compiler->GetCompilerOptions());
2023 if (standard.IsEmpty() && project)
2024 {
2025 // 2.) Project compiler setting are second
2026 standard = GetCompilerUsingStandardGCC(project->GetCompilerOptions());
2027
2028 // 3.) And targets are third in row to look for standard
2029 // NOTE: If two targets use different standards, only the one we
2030 // encounter first (eg. c++98) will be used, and any other
2031 // disregarded (even if it would be c++1y)
2032 if (standard.IsEmpty())
2033 {
2034 for (int i=0; i<project->GetBuildTargetsCount(); ++i)
2035 {
2036 ProjectBuildTarget* target = project->GetBuildTarget(i);
2037 standard = GetCompilerUsingStandardGCC(target->GetCompilerOptions());
2038
2039 if (!standard.IsEmpty())
2040 break;
2041 }
2042 }
2043 }
2044 return standard;
2045 }
2046
GetCompilerUsingStandardGCC(const wxArrayString & compilerOptions)2047 wxString NativeParser::GetCompilerUsingStandardGCC(const wxArrayString& compilerOptions)
2048 {
2049 wxString standard;
2050 for (wxArrayString::size_type i=0; i<compilerOptions.Count(); ++i)
2051 {
2052 if (compilerOptions[i].StartsWith(_T("-std=")))
2053 {
2054 standard = compilerOptions[i];
2055 CCLogger::Get()->DebugLog(wxString::Format(_T("NativeParser::GetCompilerUsingStandardGCC: Using language standard: %s"), standard.wx_str()));
2056 break;
2057 }
2058 }
2059 return standard;
2060 }
2061
AddCompilerPredefinedMacrosVC(const wxString & compilerId,wxString & defs,ParserBase * parser)2062 bool NativeParser::AddCompilerPredefinedMacrosVC(const wxString& compilerId, wxString& defs, ParserBase* parser)
2063 {
2064 static wxString vcDefs;
2065 static bool firstExecute = true;
2066
2067 if (!firstExecute)
2068 {
2069 defs = vcDefs;
2070 return true;
2071 }
2072
2073 firstExecute = false;
2074 Compiler* compiler = CompilerFactory::GetCompiler(compilerId);
2075 if (!compiler)
2076 return false;
2077
2078 if (parser->Options().platformCheck && !compiler->SupportsCurrentPlatform())
2079 {
2080 TRACE(_T("NativeParser::AddCompilerPredefinedMacrosVC: Not supported on current platform!"));
2081 return false;
2082 }
2083
2084 wxArrayString output, error;
2085 if ( !SafeExecute(compiler->GetMasterPath(), compiler->GetPrograms().C, wxEmptyString, output, error) )
2086 return false;
2087
2088 // wxExecute can be a long action and C::B might have been shutdown in the meantime...
2089 if ( Manager::IsAppShuttingDown() )
2090 return false;
2091
2092 if (error.IsEmpty())
2093 {
2094 TRACE(_T("NativeParser::AddCompilerPredefinedMacrosVC: Can't get pre-defined macros for MSVC."));
2095 return false;
2096 }
2097
2098 wxString compilerVersionInfo = error[0];
2099 wxString tmp(_T("Microsoft (R) "));
2100 int pos = compilerVersionInfo.Find(tmp);
2101 if (pos != wxNOT_FOUND)
2102 {
2103 // in earlier versions of MSVC the compiler shows "32 bit" or "64 bit"
2104 // in more recent MSVC version the architecture (x86 or x64) is shown instead
2105 wxString bit = compilerVersionInfo.Mid(pos + tmp.Length(), 2);
2106 if ( (bit.IsSameAs(_T("32"))) || compilerVersionInfo.Contains(_T("x86")) )
2107 defs += _T("#define _WIN32") _T("\n");
2108 else if ( (bit.IsSameAs(_T("64"))) || compilerVersionInfo.Contains(_T("x64")) )
2109 defs += _T("#define _WIN64") _T("\n");
2110 }
2111
2112 tmp = _T("Compiler Version ");
2113 pos = compilerVersionInfo.Find(tmp);
2114 if (pos != wxNOT_FOUND)
2115 {
2116 wxString ver = compilerVersionInfo.Mid(pos + tmp.Length(), 4); // is i.e. 12.0
2117 pos = ver.Find(_T('.'));
2118 if (pos != wxNOT_FOUND)
2119 {
2120 // out of "12.0" make "1200" for the #define
2121 ver[pos] = ver[pos + 1]; // move the mintor version first number to the dot position
2122 ver[pos + 1] = _T('0'); // add another zero at the end
2123 defs += _T("#define _MSC_VER ") + ver;
2124 // Known to now (see https://en.wikipedia.org/wiki/Visual_C%2B%2B):
2125 // MSVC++ 12.0 _MSC_VER = 1800 (Visual Studio 2013)
2126 // MSVC++ 11.0 _MSC_VER = 1700 (Visual Studio 2012)
2127 // MSVC++ 10.0 _MSC_VER = 1600 (Visual Studio 2010)
2128 // MSVC++ 9.0 _MSC_VER = 1500 (Visual Studio 2008)
2129 // MSVC++ 8.0 _MSC_VER = 1400 (Visual Studio 2005)
2130 // MSVC++ 7.1 _MSC_VER = 1310 (Visual Studio 2003)
2131 // MSVC++ 7.0 _MSC_VER = 1300
2132 // MSVC++ 6.0 _MSC_VER = 1200
2133 // MSVC++ 5.0 _MSC_VER = 1100
2134 }
2135 }
2136
2137 defs = vcDefs;
2138 return true;
2139 }
2140
AddProjectDefinedMacros(cbProject * project,ParserBase * parser)2141 bool NativeParser::AddProjectDefinedMacros(cbProject* project, ParserBase* parser)
2142 {
2143 if (!parser)
2144 return false;
2145
2146 if (!project)
2147 return true;
2148
2149 TRACE(_T("NativeParser::AddProjectDefinedMacros: Enter"));
2150
2151 wxString compilerId = project->GetCompilerID();
2152 wxString defineCompilerSwitch(wxEmptyString);
2153 if (compilerId.Contains(_T("gcc")))
2154 defineCompilerSwitch = _T("-D");
2155 else if (compilerId.StartsWith(_T("msvc")))
2156 defineCompilerSwitch = _T("/D");
2157
2158 if (defineCompilerSwitch.IsEmpty())
2159 return false; // no compiler options, return false
2160
2161 wxString defs;
2162 wxArrayString opts;
2163 if ( !parser->Options().platformCheck
2164 || (parser->Options().platformCheck && project->SupportsCurrentPlatform()) )
2165 {
2166 opts = project->GetCompilerOptions();
2167 }
2168
2169 ProjectBuildTarget* target = project->GetBuildTarget(project->GetActiveBuildTarget());
2170 if (target != NULL)
2171 {
2172 if ( !parser->Options().platformCheck
2173 || (parser->Options().platformCheck && target->SupportsCurrentPlatform()) )
2174 {
2175 wxArrayString targetOpts = target->GetCompilerOptions();
2176 for (size_t i = 0; i < targetOpts.GetCount(); ++i)
2177 opts.Add(targetOpts[i]);
2178 }
2179 }
2180 // In case of virtual targets, collect the defines from all child targets.
2181 wxArrayString targets = project->GetExpandedVirtualBuildTargetGroup(project->GetActiveBuildTarget());
2182 for (size_t i = 0; i < targets.GetCount(); ++i)
2183 {
2184 target = project->GetBuildTarget(targets[i]);
2185 if (target != NULL)
2186 {
2187 if ( !parser->Options().platformCheck
2188 || (parser->Options().platformCheck && target->SupportsCurrentPlatform()) )
2189 {
2190 wxArrayString targetOpts = target->GetCompilerOptions();
2191 for (size_t j = 0; j < targetOpts.GetCount(); ++j)
2192 opts.Add(targetOpts[j]);
2193 }
2194 }
2195 }
2196
2197 for (size_t i = 0; i < opts.GetCount(); ++i)
2198 {
2199 wxString def = opts[i];
2200 Manager::Get()->GetMacrosManager()->ReplaceMacros(def);
2201 if ( !def.StartsWith(defineCompilerSwitch) )
2202 continue;
2203
2204 def = def.Right(def.Length() - defineCompilerSwitch.Length());
2205 int pos = def.Find(_T('='));
2206 if (pos != wxNOT_FOUND)
2207 def[pos] = _T(' ');
2208
2209 defs += _T("#define ") + def + _T("\n");
2210 }
2211
2212 TRACE(_T("Add project and current build target defined preprocessor macros:\n%s"), defs.wx_str());
2213 parser->AddPredefinedMacros(defs);
2214 TRACE(_T("NativeParser::AddProjectDefinedMacros: Leave"));
2215 if ( defs.IsEmpty() )
2216 return false;
2217
2218 return true;
2219 }
2220
AddCompilerIncludeDirsToParser(const Compiler * compiler,ParserBase * parser)2221 void NativeParser::AddCompilerIncludeDirsToParser(const Compiler* compiler, ParserBase* parser)
2222 {
2223 if (!compiler || !parser) return;
2224
2225 if ( !parser->Options().platformCheck
2226 || (parser->Options().platformCheck && compiler->SupportsCurrentPlatform()) )
2227 {
2228 // these dirs were the user's compiler include search dirs
2229 AddIncludeDirsToParser(compiler->GetIncludeDirs(), wxEmptyString, parser);
2230
2231 // find out which compiler, if gnu, do the special trick
2232 // to find it's internal include paths
2233 // but do only once per C::B session, thus cache for later calls
2234 if (compiler->GetID().Contains(_T("gcc")))
2235 AddGCCCompilerDirs(compiler->GetMasterPath(), compiler->GetPrograms().CPP, parser);
2236 }
2237 }
2238
2239 // These dirs are the built-in search dirs of the compiler itself (GCC).
2240 // Such as when you install your MinGW GCC in E:/code/MinGW/bin
2241 // The built-in search dir may contain: E:/code/MinGW/include
GetGCCCompilerDirs(const wxString & cpp_path,const wxString & cpp_executable)2242 const wxArrayString& NativeParser::GetGCCCompilerDirs(const wxString& cpp_path, const wxString& cpp_executable)
2243 {
2244 wxString sep = (platform::windows ? _T("\\") : _T("/"));
2245 wxString cpp_compiler = cpp_path + sep + _T("bin") + sep + cpp_executable;
2246 Manager::Get()->GetMacrosManager()->ReplaceMacros(cpp_compiler);
2247
2248 // keep the gcc compiler path's once if found across C::B session
2249 // makes opening workspaces a *lot* faster by avoiding endless calls to the compiler
2250 static std::map<wxString, wxArrayString> dirs;
2251 static wxArrayString cached_result; // avoid accessing "dirs" too often (re-entry)
2252 cached_result = dirs[cpp_compiler];
2253 if ( !cached_result.IsEmpty() )
2254 return cached_result;
2255
2256 TRACE(_T("NativeParser::GetGCCCompilerDirs: Enter"));
2257
2258 // for starters, only do this for gnu compiler
2259 //CCLogger::Get()->DebugLog(_T("CompilerID ") + CompilerID);
2260 //
2261 // Windows: mingw32-g++ -v -E -x c++ nul
2262 // Linux : g++ -v -E -x c++ /dev/null
2263 // do the trick only for c++, not needed then for C (since this is a subset of C++)
2264
2265 // Different command on Windows and other OSes
2266 #ifdef __WXMSW__
2267 const wxString args(_T(" -v -E -x c++ nul"));
2268 #else
2269 const wxString args(_T(" -v -E -x c++ /dev/null"));
2270 #endif
2271
2272 wxArrayString output, error;
2273 if ( !SafeExecute(cpp_path, cpp_executable, args, output, error) )
2274 return cached_result;
2275
2276 // wxExecute can be a long action and C::B might have been shutdown in the meantime...
2277 if ( Manager::IsAppShuttingDown() )
2278 return cached_result;
2279
2280 // start from "#include <...>", and the path followed
2281 // let's hope this does not change too quickly, otherwise we need
2282 // to adjust our search code (for several versions ...)
2283 bool start = false;
2284 for (size_t idxCount = 0; idxCount < error.GetCount(); ++idxCount)
2285 {
2286 wxString path = error[idxCount].Trim(true).Trim(false);
2287 if (!start)
2288 {
2289 if (!path.StartsWith(_T("#include <...>")))
2290 continue; // Next for-loop
2291 path = error[++idxCount].Trim(true).Trim(false);
2292 start = true;
2293 }
2294
2295 wxFileName fname(path, wxEmptyString);
2296 fname.Normalize();
2297 fname.SetVolume(fname.GetVolume().MakeUpper());
2298 if (!fname.DirExists())
2299 break;
2300
2301 dirs[cpp_compiler].Add(fname.GetPath());
2302
2303 CCLogger::Get()->DebugLog(_T("NativeParser::GetGCCCompilerDirs: Caching GCC default include dir: ") + fname.GetPath());
2304 }
2305
2306 TRACE(_T("NativeParser::GetGCCCompilerDirs: Leave"));
2307 return dirs[cpp_compiler];
2308 }
2309
AddGCCCompilerDirs(const wxString & masterPath,const wxString & compilerCpp,ParserBase * parser)2310 void NativeParser::AddGCCCompilerDirs(const wxString& masterPath, const wxString& compilerCpp, ParserBase* parser)
2311 {
2312 const wxArrayString& gccDirs = GetGCCCompilerDirs(masterPath, compilerCpp);
2313 TRACE(_T("NativeParser::AddGCCCompilerDirs: Adding %lu cached gcc dirs to parser..."), static_cast<unsigned long>(gccDirs.GetCount()));
2314 for (size_t i=0; i<gccDirs.GetCount(); ++i)
2315 {
2316 parser->AddIncludeDir(gccDirs[i]);
2317 TRACE(_T("NativeParser::AddGCCCompilerDirs: Adding cached compiler dir to parser: ") + gccDirs[i]);
2318 }
2319 }
2320
AddIncludeDirsToParser(const wxArrayString & dirs,const wxString & base,ParserBase * parser)2321 void NativeParser::AddIncludeDirsToParser(const wxArrayString& dirs, const wxString& base, ParserBase* parser)
2322 {
2323 for (unsigned int i = 0; i < dirs.GetCount(); ++i)
2324 {
2325 wxString dir = dirs[i];
2326 Manager::Get()->GetMacrosManager()->ReplaceMacros(dir);
2327 if ( !base.IsEmpty() )
2328 {
2329 wxFileName fn(dir);
2330 if ( NormalizePath(fn, base) )
2331 {
2332 parser->AddIncludeDir(fn.GetFullPath());
2333 TRACE(_T("NativeParser::AddIncludeDirsToParser: Adding directory to parser: ") + fn.GetFullPath());
2334 }
2335 else
2336 CCLogger::Get()->DebugLog(F(_T("NativeParser::AddIncludeDirsToParser: Error normalizing path: '%s' from '%s'"), dir.wx_str(), base.wx_str()));
2337 }
2338 else
2339 parser->AddIncludeDir(dir); // no base path, nothing to normalise
2340 }
2341 }
2342
SafeExecute(const wxString & app_path,const wxString & app,const wxString & args,wxArrayString & output,wxArrayString & error)2343 bool NativeParser::SafeExecute(const wxString& app_path, const wxString& app, const wxString& args, wxArrayString& output, wxArrayString& error)
2344 {
2345 wxString sep = (platform::windows ? _T("\\") : _T("/"));
2346 wxString pth = (app_path.IsEmpty() ? _T("") : (app_path + sep + _T("bin") + sep));
2347 Manager::Get()->GetMacrosManager()->ReplaceMacros(pth);
2348 wxString cmd = pth + app;
2349 Manager::Get()->GetMacrosManager()->ReplaceMacros(cmd);
2350 // CCLogger::Get()->DebugLog(_T("NativeParser::SafeExecute: Application command: ") + cmd + _T(", path (in): ") + app_path + _T(", path (set): ") + pth + _T(", args: ") + args);
2351
2352 if ( !wxFileExists(cmd) )
2353 {
2354 CCLogger::Get()->DebugLog(_T("NativeParser::SafeExecute: Invalid application command: ") + cmd);
2355 return false;
2356 }
2357
2358 static bool reentry = false;
2359 if (reentry)
2360 {
2361 CCLogger::Get()->DebugLog(_T("NativeParser::SafeExecute: Re-Entry protection."));
2362 return false;
2363 }
2364 reentry = true;
2365
2366 // Update PATH environment variable
2367 wxString path_env;
2368 if ( !pth.IsEmpty() && wxGetEnv(_T("PATH"), &path_env) )
2369 {
2370 wxString tmp_path_env = pth + (platform::windows ? _T(";") : _T(":")) + path_env;
2371 if ( !wxSetEnv(_T("PATH"), tmp_path_env) )
2372 { CCLogger::Get()->DebugLog(_T("NativeParser::SafeExecute: Could not set PATH environment variable: ") + tmp_path_env); }
2373 }
2374
2375 if ( wxExecute(cmd + args, output, error, wxEXEC_SYNC | wxEXEC_NODISABLE) == -1 )
2376 {
2377 CCLogger::Get()->DebugLog(_T("NativeParser::SafeExecute: Failed application call: ") + cmd + args);
2378 reentry = false;
2379 return false;
2380 }
2381
2382 if ( !pth.IsEmpty() && !wxSetEnv(_T("PATH"), path_env) )
2383 { CCLogger::Get()->DebugLog(_T("NativeParser::SafeExecute: Could not restore PATH environment variable: ") + path_env); }
2384
2385 reentry = false;
2386
2387 return true;
2388 }
2389
OnParserStart(wxCommandEvent & event)2390 void NativeParser::OnParserStart(wxCommandEvent& event)
2391 {
2392 TRACE(_T("NativeParser::OnParserStart: Enter"));
2393
2394 cbProject* project = static_cast<cbProject*>(event.GetClientData());
2395 wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
2396 const ParserCommon::ParserState state = static_cast<ParserCommon::ParserState>(event.GetInt());
2397
2398 switch (state)
2399 {
2400 case ParserCommon::ptCreateParser:
2401 CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart: Starting batch parsing for project '%s'..."), prj.wx_str()));
2402 {
2403 std::pair<cbProject*, ParserBase*> info = GetParserInfoByCurrentEditor();
2404 if (info.second && m_Parser != info.second)
2405 {
2406 CCLogger::Get()->DebugLog(_T("NativeParser::OnParserStart: Start switch from OnParserStart::ptCreateParser"));
2407 SwitchParser(info.first, info.second); // Calls SetParser() which also calls UpdateClassBrowserView()
2408 }
2409 }
2410 break;
2411
2412 case ParserCommon::ptAddFileToParser:
2413 CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart: Starting add file parsing for project '%s'..."), prj.wx_str()));
2414 break;
2415
2416 case ParserCommon::ptReparseFile:
2417 CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart: Starting re-parsing for project '%s'..."), prj.wx_str()));
2418 break;
2419
2420 case ParserCommon::ptUndefined:
2421 if (event.GetString().IsEmpty())
2422 CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart: Batch parsing error in project '%s'"), prj.wx_str()));
2423 else
2424 CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart: %s in project '%s'"), event.GetString().wx_str(), prj.wx_str()));
2425 return;
2426
2427 default:
2428 break;
2429 }
2430
2431 event.Skip();
2432
2433 TRACE(_T("NativeParser::OnParserStart: Leave"));
2434 }
2435
OnParserEnd(wxCommandEvent & event)2436 void NativeParser::OnParserEnd(wxCommandEvent& event)
2437 {
2438 TRACE(_T("NativeParser::OnParserEnd: Enter"));
2439
2440 ParserBase* parser = reinterpret_cast<ParserBase*>(event.GetEventObject());
2441 cbProject* project = static_cast<cbProject*>(event.GetClientData());
2442 wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
2443 const ParserCommon::ParserState state = static_cast<ParserCommon::ParserState>(event.GetInt());
2444
2445 switch (state)
2446 {
2447 case ParserCommon::ptCreateParser:
2448 {
2449 wxString log(F(_("NativeParser::OnParserEnd: Project '%s' parsing stage done!"), prj.wx_str()));
2450 CCLogger::Get()->Log(log);
2451 CCLogger::Get()->DebugLog(log);
2452 }
2453 break;
2454
2455 case ParserCommon::ptAddFileToParser:
2456 break;
2457
2458 case ParserCommon::ptReparseFile:
2459 if (parser != m_Parser)
2460 {
2461 std::pair<cbProject*, ParserBase*> info = GetParserInfoByCurrentEditor();
2462 if (info.second && info.second != m_Parser)
2463 {
2464 CCLogger::Get()->DebugLog(_T("NativeParser::OnParserEnd: Start switch from OnParserEnd::ptReparseFile"));
2465 SwitchParser(info.first, info.second); // Calls SetParser() which also calls UpdateClassBrowserView()
2466 }
2467 }
2468 break;
2469
2470 case ParserCommon::ptUndefined:
2471 CCLogger::Get()->DebugLog(F(_T("NativeParser::OnParserEnd: Parser event handling error of project '%s'"), prj.wx_str()));
2472 return;
2473
2474 default:
2475 break;
2476 }
2477
2478 if (!event.GetString().IsEmpty())
2479 CCLogger::Get()->DebugLog(event.GetString());
2480
2481 UpdateClassBrowser();
2482
2483 // In this case, the parser will record all the cbprojects' token, so this will start parsing
2484 // the next cbproject.
2485 TRACE(_T("NativeParser::OnParserEnd: Starting m_TimerParsingOneByOne."));
2486 m_TimerParsingOneByOne.Start(500, wxTIMER_ONE_SHOT);
2487
2488 // both NativeParser and CodeCompletion class need to handle this event
2489 event.Skip();
2490 TRACE(_T("NativeParser::OnParserEnd: Leave"));
2491 }
2492
OnParsingOneByOneTimer(cb_unused wxTimerEvent & event)2493 void NativeParser::OnParsingOneByOneTimer(cb_unused wxTimerEvent& event)
2494 {
2495 TRACE(_T("NativeParser::OnParsingOneByOneTimer: Enter"));
2496
2497 std::pair<cbProject*, ParserBase*> info = GetParserInfoByCurrentEditor();
2498 if (m_ParserPerWorkspace)
2499 {
2500 // If there is no parser and an active editor file can be obtained, parse the file according the active project
2501 if (!info.second && Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor())
2502 {
2503 // NOTE (Morten#1#): Shouldn't this actually be a temp parser??? I think this screws things with re-opening files on load of a projects...
2504 AddProjectToParser(info.first);
2505 CCLogger::Get()->DebugLog(_T("NativeParser::OnParsingOneByOneTimer: Add foreign active editor to current active project's parser."));
2506 }
2507 // Otherwise, there is a parser already present
2508 else
2509 {
2510 // First: try to parse the active project (if any)
2511 cbProject* activeProject = Manager::Get()->GetProjectManager()->GetActiveProject();
2512 if (m_ParsedProjects.find(activeProject) == m_ParsedProjects.end())
2513 {
2514 AddProjectToParser(activeProject);
2515 CCLogger::Get()->DebugLog(_T("NativeParser::OnParsingOneByOneTimer: Add new (un-parsed) active project to parser."));
2516 }
2517 // Else: add remaining projects one-by-one (if any)
2518 else
2519 {
2520 ProjectsArray* projs = Manager::Get()->GetProjectManager()->GetProjects();
2521 // loop on the whole workspace, and only add a new project to the parser
2522 // here the "new" means a project haven't been parsed. Once it was parsed, it is
2523 // added to the m_ParsedProjects
2524 for (size_t i = 0; i < projs->GetCount(); ++i)
2525 {
2526 // Only add, if the project is not already parsed
2527 if (m_ParsedProjects.find(projs->Item(i)) == m_ParsedProjects.end())
2528 {
2529 // AddProjectToParser return true means there are something need to parse, otherwise, it is false
2530 if (!AddProjectToParser(projs->Item(i)))
2531 {
2532 CCLogger::Get()->Log(_T("NativeParser::OnParsingOneByOneTimer: nothing need to parse in this project, try next project."));
2533 continue;
2534 }
2535
2536 CCLogger::Get()->DebugLog(_T("NativeParser::OnParsingOneByOneTimer: Add additional (next) project to parser."));
2537 break;
2538 }
2539 }
2540 }
2541 }
2542 }
2543 else if (info.first && !info.second)
2544 {
2545 info.second = CreateParser(info.first);
2546 if (info.second && info.second != m_Parser)
2547 {
2548 CCLogger::Get()->DebugLog(_T("NativeParser::OnParsingOneByOneTimer: Start switch from OnParsingOneByOneTimer"));
2549 SwitchParser(info.first, info.second); // Calls SetParser() which also calls UpdateClassBrowserView()
2550 }
2551 }
2552 TRACE(_T("NativeParser::OnParsingOneByOneTimer: Leave"));
2553 }
2554
OnEditorActivated(EditorBase * editor)2555 void NativeParser::OnEditorActivated(EditorBase* editor)
2556 {
2557 cbEditor* curEditor = Manager::Get()->GetEditorManager()->GetBuiltinEditor(editor);
2558 if (!curEditor)
2559 return;
2560
2561 const wxString& activatedFile = editor->GetFilename();
2562 if ( !wxFile::Exists(activatedFile) )
2563 return;
2564
2565 cbProject* project = GetProjectByEditor(curEditor);
2566 const int pos = m_StandaloneFiles.Index(activatedFile);
2567 if (project && pos != wxNOT_FOUND)
2568 {
2569 m_StandaloneFiles.RemoveAt(pos);
2570 if (m_StandaloneFiles.IsEmpty())
2571 DeleteParser(NULL);
2572 else
2573 RemoveFileFromParser(NULL, activatedFile);
2574 }
2575
2576 ParserBase* parser = GetParserByProject(project);
2577 if (!parser)
2578 {
2579 ParserCommon::EFileType ft = ParserCommon::FileType(activatedFile);
2580 if (ft != ParserCommon::ftOther && (parser = CreateParser(project)))
2581 {
2582 if (!project && AddFileToParser(project, activatedFile, parser) )
2583 {
2584 wxFileName file(activatedFile);
2585 parser->AddIncludeDir(file.GetPath());
2586 m_StandaloneFiles.Add(activatedFile);
2587 }
2588 }
2589 else
2590 parser = m_TempParser; // do *not* instead by SetParser(m_TempParser)
2591 }
2592 else if (!project)
2593 {
2594 if ( !parser->IsFileParsed(activatedFile)
2595 && m_StandaloneFiles.Index(activatedFile) == wxNOT_FOUND
2596 && AddFileToParser(project, activatedFile, parser) )
2597 {
2598 wxFileName file(activatedFile);
2599 parser->AddIncludeDir(file.GetPath());
2600 m_StandaloneFiles.Add(activatedFile);
2601 }
2602 }
2603
2604 if (parser != m_Parser)
2605 {
2606 CCLogger::Get()->DebugLog(_T("Start switch from OnEditorActivatedTimer"));
2607 SwitchParser(project, parser); // Calls SetParser() which also calls UpdateClassBrowserView()
2608 }
2609
2610 if (m_ClassBrowser)
2611 {
2612 if (m_Parser->ClassBrowserOptions().displayFilter == bdfFile)
2613 m_ClassBrowser->UpdateClassBrowserView(true); // check header and implementation file swap
2614 else if ( m_ParserPerWorkspace // project view only available in case of one parser per WS
2615 && m_Parser->ClassBrowserOptions().displayFilter == bdfProject)
2616 m_ClassBrowser->UpdateClassBrowserView();
2617 }
2618 }
2619
OnEditorClosed(EditorBase * editor)2620 void NativeParser::OnEditorClosed(EditorBase* editor)
2621 {
2622 // the caller of the function should guarantee its a built-in editor
2623 wxString filename = editor->GetFilename();
2624 const int pos = m_StandaloneFiles.Index(filename);
2625 if (pos != wxNOT_FOUND)
2626 {
2627 m_StandaloneFiles.RemoveAt(pos);
2628 if (m_StandaloneFiles.IsEmpty())
2629 DeleteParser(NULL);
2630 else
2631 RemoveFileFromParser(NULL, filename);
2632 }
2633 }
2634
InitCCSearchVariables()2635 void NativeParser::InitCCSearchVariables()
2636 {
2637 m_LastControl = nullptr;
2638 m_LastFunctionIndex = -1;
2639 m_LastLine = -1;
2640 m_LastResult = -1;
2641 m_LastFile.Clear();
2642 m_LastNamespace.Clear();
2643 m_LastPROC.Clear();
2644
2645 Reset();
2646 }
2647
AddProjectToParser(cbProject * project)2648 bool NativeParser::AddProjectToParser(cbProject* project)
2649 {
2650 wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
2651 ParserBase* parser = GetParserByProject(project);
2652 if (parser)
2653 return false;
2654
2655 if (m_ParsedProjects.empty())
2656 return false;
2657
2658 m_ParsedProjects.insert(project);
2659 parser = GetParserByProject(project);
2660 if (!parser)
2661 return false;
2662 else if (!parser->UpdateParsingProject(project))
2663 {
2664 m_ParsedProjects.erase(project);
2665 return false;
2666 }
2667
2668 // TODO (ollydbg#1#) did exactly the same thing as the function NativeParser::DoFullParsing()?
2669 wxString log(F(_("NativeParser::AddProjectToParser: Add project (%s) to parser"), prj.wx_str()));
2670 CCLogger::Get()->Log(log);
2671 CCLogger::Get()->DebugLog(log);
2672
2673 bool needParseMacros = false;
2674
2675 if (!AddCompilerDirs(project, parser))
2676 CCLogger::Get()->DebugLog(_T("NativeParser::AddProjectToParser: AddCompilerDirs failed!"));
2677
2678 if (!AddCompilerPredefinedMacros(project, parser))
2679 CCLogger::Get()->DebugLog(_T("NativeParser::AddProjectToParser: AddCompilerPredefinedMacros failed!"));
2680 else
2681 needParseMacros = true;
2682
2683 if (!AddProjectDefinedMacros(project, parser))
2684 CCLogger::Get()->DebugLog(_T("NativeParser::AddProjectToParser: AddProjectDefinedMacros failed!"));
2685 else
2686 {
2687 if(!needParseMacros)
2688 needParseMacros = true;
2689 }
2690
2691 if (project)
2692 {
2693 size_t fileCount = 0;
2694 for (FilesList::const_iterator fl_it = project->GetFilesList().begin(); fl_it != project->GetFilesList().end(); ++fl_it)
2695 {
2696 ProjectFile* pf = *fl_it;
2697 if (pf && FileTypeOf(pf->relativeFilename) == ftHeader)
2698 {
2699 if ( AddFileToParser(project, pf->file.GetFullPath(), parser) )
2700 ++fileCount;
2701 }
2702 }
2703 for (FilesList::const_iterator fl_it = project->GetFilesList().begin(); fl_it != project->GetFilesList().end(); ++fl_it)
2704 {
2705 ProjectFile* pf = *fl_it;
2706 if (pf && (FileTypeOf(pf->relativeFilename) == ftSource || FileTypeOf(pf->relativeFilename) == ftTemplateSource) )
2707 {
2708 if ( AddFileToParser(project, pf->file.GetFullPath(), parser) )
2709 fileCount++;
2710 }
2711 }
2712
2713 CCLogger::Get()->DebugLog(F(_("NativeParser::AddProjectToParser: Done adding %lu files of project (%s) to parser."), static_cast<unsigned long>(fileCount), prj.wx_str()));
2714
2715 // in some cases, all the files were already be parsed, so fileCount is still 0
2716 return ((fileCount>0) || needParseMacros);
2717 }
2718 else
2719 {
2720 EditorBase* editor = Manager::Get()->GetEditorManager()->GetActiveEditor();
2721 if (editor && AddFileToParser(project, editor->GetFilename(), parser))
2722 {
2723 wxFileName file(editor->GetFilename());
2724 parser->AddIncludeDir(file.GetPath());
2725 m_StandaloneFiles.Add(editor->GetFilename());
2726
2727 CCLogger::Get()->DebugLog(F(_("NativeParser::AddProjectToParser: Done adding stand-alone file (%s) of editor to parser."), editor->GetFilename().wx_str()));
2728 return true;
2729 }
2730 }
2731 return false;
2732 }
2733
RemoveProjectFromParser(cbProject * project)2734 bool NativeParser::RemoveProjectFromParser(cbProject* project)
2735 {
2736 ParserBase* parser = GetParserByProject(project);
2737 if (!parser)
2738 return false;
2739
2740 // Remove from the cbProject set
2741 m_ParsedProjects.erase(project);
2742
2743 if (!project || m_ParsedProjects.empty())
2744 return true;
2745
2746 wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
2747 wxString log(F(_("Remove project (%s) from parser"), prj.wx_str()));
2748 CCLogger::Get()->Log(log);
2749 CCLogger::Get()->DebugLog(log);
2750
2751 for (FilesList::const_iterator fl_it = project->GetFilesList().begin(); fl_it != project->GetFilesList().end(); ++fl_it)
2752 {
2753 ProjectFile* pf = *fl_it;
2754 if (pf && ParserCommon::FileType(pf->relativeFilename) != ParserCommon::ftOther)
2755 RemoveFileFromParser(project, pf->file.GetFullPath());
2756 }
2757
2758 return true;
2759 }
2760