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: 11838 $
6  * $Id: codecompletion.cpp 11838 2019-09-02 19:27:23Z pecanh $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/plugins/codecompletion/codecompletion.cpp $
8  */
9 
10 #include <sdk.h>
11 
12 #ifndef CB_PRECOMP
13     #include <algorithm>
14     #include <iterator>
15     #include <set> // for handling unique items in some places
16 
17     #include <wx/choicdlg.h>
18     #include <wx/choice.h>
19     #include <wx/dir.h>
20     #include <wx/filename.h>
21     #include <wx/fs_zip.h>
22     #include <wx/menu.h>
23     #include <wx/mimetype.h>
24     #include <wx/msgdlg.h>
25     #include <wx/regex.h>
26     #include <wx/tipwin.h>
27     #include <wx/toolbar.h>
28     #include <wx/utils.h>
29     #include <wx/xrc/xmlres.h>
30     #include <wx/wxscintilla.h>
31 
32     #include <cbeditor.h>
33     #include <configmanager.h>
34     #include <editorcolourset.h>
35     #include <editormanager.h>
36     #include <globals.h>
37     #include <logmanager.h>
38     #include <macrosmanager.h>
39     #include <manager.h>
40     #include <projectmanager.h>
41     #include <sdk_events.h>
42 #endif
43 
44 #include <wx/tokenzr.h>
45 #include <wx/html/htmlwin.h>
46 
47 #include <cbstyledtextctrl.h>
48 #include <editor_hooks.h>
49 #include <filegroupsandmasks.h>
50 #include <multiselectdlg.h>
51 
52 #include "codecompletion.h"
53 
54 #include "ccoptionsdlg.h"
55 #include "ccoptionsprjdlg.h"
56 #include "insertclassmethoddlg.h"
57 #include "selectincludefile.h"
58 #include "parser/ccdebuginfo.h"
59 #include "parser/cclogger.h"
60 #include "parser/parser.h"
61 #include "parser/tokenizer.h"
62 #include "doxygen_parser.h" // for DocumentationPopup and DoxygenParser
63 #include "gotofunctiondlg.h"
64 
65 #define CC_CODECOMPLETION_DEBUG_OUTPUT 0
66 
67 // let the global debug macro overwrite the local debug macro value
68 #if defined(CC_GLOBAL_DEBUG_OUTPUT)
69     #undef CC_CODECOMPLETION_DEBUG_OUTPUT
70     #define CC_CODECOMPLETION_DEBUG_OUTPUT CC_GLOBAL_DEBUG_OUTPUT
71 #endif
72 
73 #if CC_CODECOMPLETION_DEBUG_OUTPUT == 1
74     #define TRACE(format, args...) \
75         CCLogger::Get()->DebugLog(F(format, ##args))
76     #define TRACE2(format, args...)
77 #elif CC_CODECOMPLETION_DEBUG_OUTPUT == 2
78     #define TRACE(format, args...)                                              \
79         do                                                                      \
80         {                                                                       \
81             if (g_EnableDebugTrace)                                             \
82                 CCLogger::Get()->DebugLog(F(format, ##args));                   \
83         }                                                                       \
84         while (false)
85     #define TRACE2(format, args...) \
86         CCLogger::Get()->DebugLog(F(format, ##args))
87 #else
88     #define TRACE(format, args...)
89     #define TRACE2(format, args...)
90 #endif
91 
92 /// Scopes choice name for global functions in CC's toolbar.
93 static wxString g_GlobalScope(_T("<global>"));
94 
95 // this auto-registers the plugin
96 namespace
97 {
98     PluginRegistrant<CodeCompletion> reg(_T("CodeCompletion"));
99 }
100 
101 namespace CodeCompletionHelper
102 {
103     // compare method for the sort algorithm for our FunctionScope struct
LessFunctionScope(const CodeCompletion::FunctionScope & fs1,const CodeCompletion::FunctionScope & fs2)104     inline bool LessFunctionScope(const CodeCompletion::FunctionScope& fs1, const CodeCompletion::FunctionScope& fs2)
105     {
106         int result = wxStricmp(fs1.Scope, fs2.Scope);
107         if (result == 0)
108         {
109             result = wxStricmp(fs1.Name, fs2.Name);
110             if (result == 0)
111                 result = fs1.StartLine - fs2.StartLine;
112         }
113 
114         return result < 0;
115     }
116 
EqualFunctionScope(const CodeCompletion::FunctionScope & fs1,const CodeCompletion::FunctionScope & fs2)117     inline bool EqualFunctionScope(const CodeCompletion::FunctionScope& fs1, const CodeCompletion::FunctionScope& fs2)
118     {
119         int result = wxStricmp(fs1.Scope, fs2.Scope);
120         if (result == 0)
121             result = wxStricmp(fs1.Name, fs2.Name);
122 
123         return result == 0;
124     }
125 
LessNameSpace(const NameSpace & ns1,const NameSpace & ns2)126     inline bool LessNameSpace(const NameSpace& ns1, const NameSpace& ns2)
127     {
128         return ns1.Name < ns2.Name;
129     }
130 
EqualNameSpace(const NameSpace & ns1,const NameSpace & ns2)131     inline bool EqualNameSpace(const NameSpace& ns1, const NameSpace& ns2)
132     {
133         return ns1.Name == ns2.Name;
134     }
135 
136     /// for OnGotoFunction(), search backward
137     /// @code
138     /// xxxxx  /* yyy */
139     ///     ^             ^
140     ///     result        begin
141     /// @endcode
GetLastNonWhitespaceChar(cbStyledTextCtrl * control,int position)142     inline wxChar GetLastNonWhitespaceChar(cbStyledTextCtrl* control, int position)
143     {
144         if (!control)
145             return 0;
146 
147         while (--position > 0)
148         {
149             const int style = control->GetStyleAt(position);
150             if (control->IsComment(style))
151                 continue;
152 
153             const wxChar ch = control->GetCharAt(position);
154             if (ch <= _T(' '))
155                 continue;
156 
157             return ch;
158         }
159 
160         return 0;
161     }
162 
163     /// for OnGotoFunction(), search forward
164     ///        /* yyy */  xxxxx
165     ///     ^             ^
166     ///     begin         result
GetNextNonWhitespaceChar(cbStyledTextCtrl * control,int position)167     inline wxChar GetNextNonWhitespaceChar(cbStyledTextCtrl* control, int position)
168     {
169         if (!control)
170             return 0;
171 
172         const int totalLength = control->GetLength();
173         --position;
174         while (++position < totalLength)
175         {
176             const int style = control->GetStyleAt(position);
177             if (control->IsComment(style))
178                 continue;
179 
180             const wxChar ch = control->GetCharAt(position);
181             if (ch <= _T(' '))
182                 continue;
183 
184             return ch;
185         }
186 
187         return 0;
188     }
189 
190     /**  Sorting in GetLocalIncludeDirs() */
CompareStringLen(const wxString & first,const wxString & second)191     inline int CompareStringLen(const wxString& first, const wxString& second)
192     {
193         return second.Len() - first.Len();
194     }
195 
196     /**  for CodeCompleteIncludes()
197      * a line has some pattern like below
198      @code
199         # [space or tab] include
200      @endcode
201      */
TestIncludeLine(wxString const & line)202     inline bool TestIncludeLine(wxString const &line)
203     {
204         size_t index = line.find(_T('#'));
205         if (index == wxString::npos)
206             return false;
207         ++index;
208 
209         for (; index < line.length(); ++index)
210         {
211             if (line[index] != _T(' ') && line[index] != _T('\t'))
212             {
213                 if (line.Mid(index, 7) == _T("include"))
214                     return true;
215                 break;
216             }
217         }
218         return false;
219     }
220 
221     /** return identifier like token string under the current cursor pointer
222      * @param[out] NameUnderCursor the identifier like token string
223      * @param[out] IsInclude true if it is a #include command
224      * @return true if the underlining text is a #include command, or a normal identifier
225      */
EditorHasNameUnderCursor(wxString & NameUnderCursor,bool & IsInclude)226     inline bool EditorHasNameUnderCursor(wxString& NameUnderCursor, bool& IsInclude)
227     {
228         bool ReturnValue = false;
229         if (cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor())
230         {
231             cbStyledTextCtrl* control = ed->GetControl();
232             const int pos = control->GetCurrentPos();
233             const wxString line = control->GetLine(control->LineFromPosition(pos));
234             const wxRegEx reg(_T("^[ \t]*#[ \t]*include[ \t]+[\"<]([^\">]+)[\">]"));
235             wxString inc;
236             if (reg.Matches(line))
237                 inc = reg.GetMatch(line, 1);
238 
239             if (!inc.IsEmpty())
240             {
241                 NameUnderCursor = inc;
242                 ReturnValue = true;
243                 IsInclude = true;
244             }
245             else
246             {
247                 const int start = control->WordStartPosition(pos, true);
248                 const int end = control->WordEndPosition(pos, true);
249                 const wxString word = control->GetTextRange(start, end);
250                 if (!word.IsEmpty())
251                 {
252                     NameUnderCursor.Clear();
253                     NameUnderCursor << word;
254                     ReturnValue = true;
255                     IsInclude = false;
256                 }
257             }
258         }
259         return ReturnValue;
260     }
261     /** used to record the position of a token when user click find declaration or implementation */
262     struct GotoDeclarationItem
263     {
264         wxString filename;
265         unsigned line;
266     };
267 
268     /** when user select one item in the suggestion list, the selected contains the full display
269      * name, for example, "function_name():function_return_type", and we only need to insert the
270      * "function_name" to the editor, so this function just get the actual inserted text.
271      * @param selected a full display name of the selected token in the suggestion list
272      * @return the stripped text which are used to insert to the editor
273      */
AutocompGetName(const wxString & selected)274     static wxString AutocompGetName(const wxString& selected)
275     {
276         size_t nameEnd = selected.find_first_of(_T("(: "));
277         return selected.substr(0,nameEnd);
278     }
279 
280 }//namespace CodeCompletionHelper
281 
282 // menu IDs
283 // just because we don't know other plugins' used identifiers,
284 // we use wxNewId() to generate a guaranteed unique ID ;), instead of enum
285 // (don't forget that, especially in a plugin)
286 // used in the wxFrame's main menu
287 int idMenuGotoFunction          = wxNewId();
288 int idMenuGotoPrevFunction      = wxNewId();
289 int idMenuGotoNextFunction      = wxNewId();
290 int idMenuGotoDeclaration       = wxNewId();
291 int idMenuGotoImplementation    = wxNewId();
292 int idMenuOpenIncludeFile       = wxNewId();
293 int idMenuFindReferences        = wxNewId();
294 int idMenuRenameSymbols         = wxNewId();
295 int idViewClassBrowser          = wxNewId();
296 // used in context menu
297 int idCurrentProjectReparse     = wxNewId();
298 int idSelectedProjectReparse    = wxNewId();
299 int idSelectedFileReparse       = wxNewId();
300 int idEditorSubMenu             = wxNewId();
301 int idClassMethod               = wxNewId();
302 int idUnimplementedClassMethods = wxNewId();
303 int idGotoDeclaration           = wxNewId();
304 int idGotoImplementation        = wxNewId();
305 int idOpenIncludeFile           = wxNewId();
306 
307 int idRealtimeParsingTimer      = wxNewId();
308 int idToolbarTimer              = wxNewId();
309 int idProjectSavedTimer         = wxNewId();
310 int idReparsingTimer            = wxNewId();
311 int idEditorActivatedTimer      = wxNewId();
312 
313 // all the below delay time is in milliseconds units
314 // when the user enables the parsing while typing option, this is the time delay when parsing
315 // would happen after the editor has changed.
316 #define REALTIME_PARSING_DELAY    500
317 
318 // there are many reasons to trigger the refreshing of CC toolbar. But to avoid refreshing
319 // the toolbar too often, we add a timer to delay the refresh, this is just like a mouse dwell
320 // event, which means we do the real job when the editor is stable for a while (no event
321 // happens in the delay time period).
322 #define TOOLBAR_REFRESH_DELAY     150
323 
324 // the time delay between an editor activated event and the updating of the CC toolbar.
325 // Note that we are only interest in a stable activated editor, so if another editor is activated
326 // during the time delay, the timer will be restarted.
327 #define EDITOR_ACTIVATED_DELAY    300
328 
BEGIN_EVENT_TABLE(CodeCompletion,cbCodeCompletionPlugin)329 BEGIN_EVENT_TABLE(CodeCompletion, cbCodeCompletionPlugin)
330     EVT_UPDATE_UI_RANGE(idMenuGotoFunction, idCurrentProjectReparse, CodeCompletion::OnUpdateUI)
331 
332     EVT_MENU(idMenuGotoFunction,                   CodeCompletion::OnGotoFunction             )
333     EVT_MENU(idMenuGotoPrevFunction,               CodeCompletion::OnGotoPrevFunction         )
334     EVT_MENU(idMenuGotoNextFunction,               CodeCompletion::OnGotoNextFunction         )
335     EVT_MENU(idMenuGotoDeclaration,                CodeCompletion::OnGotoDeclaration          )
336     EVT_MENU(idMenuGotoImplementation,             CodeCompletion::OnGotoDeclaration          )
337     EVT_MENU(idMenuFindReferences,                 CodeCompletion::OnFindReferences           )
338     EVT_MENU(idMenuRenameSymbols,                  CodeCompletion::OnRenameSymbols            )
339     EVT_MENU(idClassMethod,                        CodeCompletion::OnClassMethod              )
340     EVT_MENU(idUnimplementedClassMethods,          CodeCompletion::OnUnimplementedClassMethods)
341     EVT_MENU(idGotoDeclaration,                    CodeCompletion::OnGotoDeclaration          )
342     EVT_MENU(idGotoImplementation,                 CodeCompletion::OnGotoDeclaration          )
343     EVT_MENU(idOpenIncludeFile,                    CodeCompletion::OnOpenIncludeFile          )
344     EVT_MENU(idMenuOpenIncludeFile,                CodeCompletion::OnOpenIncludeFile          )
345 
346     EVT_MENU(idViewClassBrowser,                   CodeCompletion::OnViewClassBrowser      )
347     EVT_MENU(idCurrentProjectReparse,              CodeCompletion::OnCurrentProjectReparse )
348     EVT_MENU(idSelectedProjectReparse,             CodeCompletion::OnSelectedProjectReparse)
349     EVT_MENU(idSelectedFileReparse,                CodeCompletion::OnSelectedFileReparse   )
350 
351     // CC's toolbar
352     EVT_CHOICE(XRCID("chcCodeCompletionScope"),    CodeCompletion::OnScope   )
353     EVT_CHOICE(XRCID("chcCodeCompletionFunction"), CodeCompletion::OnFunction)
354 END_EVENT_TABLE()
355 
356 CodeCompletion::CodeCompletion() :
357     m_InitDone(false),
358     m_CodeRefactoring(m_NativeParser),
359     m_EditorHookId(0),
360     m_TimerRealtimeParsing(this, idRealtimeParsingTimer),
361     m_TimerToolbar(this, idToolbarTimer),
362     m_TimerProjectSaved(this, idProjectSavedTimer),
363     m_TimerReparsing(this, idReparsingTimer),
364     m_TimerEditorActivated(this, idEditorActivatedTimer),
365     m_LastEditor(0),
366     m_ToolBar(0),
367     m_Function(0),
368     m_Scope(0),
369     m_ToolbarNeedRefresh(true),
370     m_ToolbarNeedReparse(false),
371     m_CurrentLine(0),
372     m_NeedReparse(false),
373     m_CurrentLength(-1),
374     m_NeedsBatchColour(true),
375     m_CCMaxMatches(16384),
376     m_CCAutoAddParentheses(true),
377     m_CCDetectImplementation(false),
378     m_CCEnableHeaders(false),
379     m_CCEnablePlatformCheck(true),
380     m_SystemHeadersThreadCS(),
381     m_DocHelper(this)
382 {
383     // CCLogger are the log event bridges, those events were finally handled by its parent, here
384     // it is the CodeCompletion plugin ifself.
385     CCLogger::Get()->Init(this, g_idCCLogger, g_idCCDebugLogger);
386 
387     if (!Manager::LoadResource(_T("codecompletion.zip")))
388         NotifyMissingFile(_T("codecompletion.zip"));
389 
390     // handling events send from CCLogger
391     Connect(g_idCCLogger,                wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler(CodeCompletion::OnCCLogger)     );
392     Connect(g_idCCDebugLogger,           wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler(CodeCompletion::OnCCDebugLogger));
393 
394     // the two events below were generated from NativeParser, as currently, CodeCompletionPlugin is
395     // set as the next event handler for m_NativeParser, so it get chance to handle them.
396     Connect(ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CodeCompletion::OnParserStart)  );
397     Connect(ParserCommon::idParserEnd,   wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CodeCompletion::OnParserEnd)    );
398 
399     Connect(idRealtimeParsingTimer, wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnRealtimeParsingTimer));
400     Connect(idToolbarTimer,         wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnToolbarTimer)        );
401     Connect(idProjectSavedTimer,    wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnProjectSavedTimer)   );
402     Connect(idReparsingTimer,       wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnReparsingTimer)      );
403     Connect(idEditorActivatedTimer, wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnEditorActivatedTimer));
404 
405     Connect(idSystemHeadersThreadMessage, wxEVT_COMMAND_MENU_SELECTED,
406             CodeBlocksThreadEventHandler(CodeCompletion::OnSystemHeadersThreadMessage));
407     Connect(idSystemHeadersThreadFinish, wxEVT_COMMAND_MENU_SELECTED,
408             CodeBlocksThreadEventHandler(CodeCompletion::OnSystemHeadersThreadFinish));
409 }
410 
~CodeCompletion()411 CodeCompletion::~CodeCompletion()
412 {
413     Disconnect(g_idCCLogger,                wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler(CodeCompletion::OnCCLogger));
414     Disconnect(g_idCCDebugLogger,           wxEVT_COMMAND_MENU_SELECTED, CodeBlocksThreadEventHandler(CodeCompletion::OnCCDebugLogger));
415     Disconnect(ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CodeCompletion::OnParserStart));
416     Disconnect(ParserCommon::idParserEnd,   wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(CodeCompletion::OnParserEnd));
417 
418     Disconnect(idRealtimeParsingTimer, wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnRealtimeParsingTimer));
419     Disconnect(idToolbarTimer,         wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnToolbarTimer)        );
420     Disconnect(idProjectSavedTimer,    wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnProjectSavedTimer)   );
421     Disconnect(idReparsingTimer,       wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnReparsingTimer)      );
422     Disconnect(idEditorActivatedTimer, wxEVT_TIMER, wxTimerEventHandler(CodeCompletion::OnEditorActivatedTimer));
423 
424     Disconnect(idSystemHeadersThreadMessage, wxEVT_COMMAND_MENU_SELECTED,
425                CodeBlocksThreadEventHandler(CodeCompletion::OnSystemHeadersThreadMessage));
426     Disconnect(idSystemHeadersThreadFinish, wxEVT_COMMAND_MENU_SELECTED,
427                CodeBlocksThreadEventHandler(CodeCompletion::OnSystemHeadersThreadFinish));
428 
429     // clean up all the running thread
430     while (!m_SystemHeadersThreads.empty())
431     {
432         SystemHeadersThread* thread = m_SystemHeadersThreads.front();
433         thread->Wait();
434         delete thread;
435         m_SystemHeadersThreads.pop_front();
436     }
437 }
438 
OnAttach()439 void CodeCompletion::OnAttach()
440 {
441     m_EditMenu    = 0;
442     m_SearchMenu  = 0;
443     m_ViewMenu    = 0;
444     m_ProjectMenu = 0;
445     // toolbar related variables
446     m_ToolBar     = 0;
447     m_Function    = 0;
448     m_Scope       = 0;
449     m_FunctionsScope.clear();
450     m_NameSpaces.clear();
451     m_AllFunctionsScopes.clear();
452     m_ToolbarNeedRefresh = true; // by default
453 
454     m_LastFile.clear();
455 
456     // read options from configure file
457     RereadOptions();
458 
459     // Events which m_NativeParser does not handle will go to the the next event
460     // handler which is the instance of a CodeCompletion.
461     m_NativeParser.SetNextHandler(this);
462 
463     m_NativeParser.CreateClassBrowser();
464 
465     // hook to editors
466     // both ccmanager and cc have hooks, but they don't conflict. ccmanager are mainly
467     // hooking to the event such as key stroke or mouse dwell events, so the code completion, call tip
468     // and tool tip will be handled in ccmanager. The other cases such as caret movement triggers
469     // updating the CC's toolbar, modifying the editor causing the real time content reparse will be
470     // handled inside cc's own editor hook.
471     EditorHooks::HookFunctorBase* myhook = new EditorHooks::HookFunctor<CodeCompletion>(this, &CodeCompletion::EditorEventHook);
472     m_EditorHookId = EditorHooks::RegisterHook(myhook);
473 
474     // register event sinks
475     Manager* pm = Manager::Get();
476 
477     pm->RegisterEventSink(cbEVT_APP_STARTUP_DONE,     new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnAppDoneStartup));
478 
479     pm->RegisterEventSink(cbEVT_WORKSPACE_CHANGED,    new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnWorkspaceChanged));
480 
481     pm->RegisterEventSink(cbEVT_PROJECT_ACTIVATE,     new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectActivated));
482     pm->RegisterEventSink(cbEVT_PROJECT_CLOSE,        new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectClosed));
483     pm->RegisterEventSink(cbEVT_PROJECT_SAVE,         new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectSaved));
484     pm->RegisterEventSink(cbEVT_PROJECT_FILE_ADDED,   new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectFileAdded));
485     pm->RegisterEventSink(cbEVT_PROJECT_FILE_REMOVED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectFileRemoved));
486     pm->RegisterEventSink(cbEVT_PROJECT_FILE_CHANGED, new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnProjectFileChanged));
487 
488     pm->RegisterEventSink(cbEVT_EDITOR_SAVE,          new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnEditorSave));
489     pm->RegisterEventSink(cbEVT_EDITOR_OPEN,          new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnEditorOpen));
490     pm->RegisterEventSink(cbEVT_EDITOR_ACTIVATED,     new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnEditorActivated));
491     pm->RegisterEventSink(cbEVT_EDITOR_CLOSE,         new cbEventFunctor<CodeCompletion, CodeBlocksEvent>(this, &CodeCompletion::OnEditorClosed));
492 
493     m_DocHelper.OnAttach();
494 }
495 
OnRelease(bool appShutDown)496 void CodeCompletion::OnRelease(bool appShutDown)
497 {
498     m_NativeParser.RemoveClassBrowser(appShutDown);
499     m_NativeParser.ClearParsers();
500 
501     // remove chained handler
502     m_NativeParser.SetNextHandler(nullptr);
503 
504     // unregister hook
505     // 'true' will delete the functor too
506     EditorHooks::UnregisterHook(m_EditorHookId, true);
507 
508     // remove registered event sinks
509     Manager::Get()->RemoveAllEventSinksFor(this);
510 
511     m_FunctionsScope.clear();
512     m_NameSpaces.clear();
513     m_AllFunctionsScopes.clear();
514     m_ToolbarNeedRefresh = false;
515 
516 /* TODO (mandrav#1#): Delete separator line too... */
517     if (m_EditMenu)
518         m_EditMenu->Delete(idMenuRenameSymbols);
519 
520     if (m_SearchMenu)
521     {
522         m_SearchMenu->Delete(idMenuGotoFunction);
523         m_SearchMenu->Delete(idMenuGotoPrevFunction);
524         m_SearchMenu->Delete(idMenuGotoNextFunction);
525         m_SearchMenu->Delete(idMenuGotoDeclaration);
526         m_SearchMenu->Delete(idMenuGotoImplementation);
527         m_SearchMenu->Delete(idMenuFindReferences);
528         m_SearchMenu->Delete(idMenuOpenIncludeFile);
529     }
530 
531     m_DocHelper.OnRelease();
532 }
533 
GetConfigurationPanel(wxWindow * parent)534 cbConfigurationPanel* CodeCompletion::GetConfigurationPanel(wxWindow* parent)
535 {
536     return new CCOptionsDlg(parent, &m_NativeParser, this, &m_DocHelper);
537 }
538 
GetProjectConfigurationPanel(wxWindow * parent,cbProject * project)539 cbConfigurationPanel* CodeCompletion::GetProjectConfigurationPanel(wxWindow* parent, cbProject* project)
540 {
541     return new CCOptionsProjectDlg(parent, project, &m_NativeParser);
542 }
543 
BuildMenu(wxMenuBar * menuBar)544 void CodeCompletion::BuildMenu(wxMenuBar* menuBar)
545 {
546     // if not attached, exit
547     if (!IsAttached())
548         return;
549 
550     int pos = menuBar->FindMenu(_("&Edit"));
551     if (pos != wxNOT_FOUND)
552     {
553         m_EditMenu = menuBar->GetMenu(pos);
554         m_EditMenu->AppendSeparator();
555         m_EditMenu->Append(idMenuRenameSymbols, _("Rename symbols\tAlt-N"));
556     }
557     else
558         CCLogger::Get()->DebugLog(_T("Could not find Edit menu!"));
559 
560     pos = menuBar->FindMenu(_("Sea&rch"));
561     if (pos != wxNOT_FOUND)
562     {
563         m_SearchMenu = menuBar->GetMenu(pos);
564         m_SearchMenu->Append(idMenuGotoFunction,       _("Goto function...\tCtrl-Shift-G"));
565         m_SearchMenu->Append(idMenuGotoPrevFunction,   _("Goto previous function\tCtrl-PgUp"));
566         m_SearchMenu->Append(idMenuGotoNextFunction,   _("Goto next function\tCtrl-PgDn"));
567         m_SearchMenu->Append(idMenuGotoDeclaration,    _("Goto declaration\tCtrl-Shift-."));
568         m_SearchMenu->Append(idMenuGotoImplementation, _("Goto implementation\tCtrl-."));
569         m_SearchMenu->Append(idMenuFindReferences,     _("Find references\tAlt-."));
570         m_SearchMenu->Append(idMenuOpenIncludeFile,    _("Open include file"));
571     }
572     else
573         CCLogger::Get()->DebugLog(_T("Could not find Search menu!"));
574 
575     // add the classbrowser window in the "View" menu
576     int idx = menuBar->FindMenu(_("&View"));
577     if (idx != wxNOT_FOUND)
578     {
579         m_ViewMenu = menuBar->GetMenu(idx);
580         wxMenuItemList& items = m_ViewMenu->GetMenuItems();
581         bool inserted = false;
582 
583         // find the first separator and insert before it
584         for (size_t i = 0; i < items.GetCount(); ++i)
585         {
586             if (items[i]->IsSeparator())
587             {
588                 m_ViewMenu->InsertCheckItem(i, idViewClassBrowser, _("Symbols browser"), _("Toggle displaying the symbols browser"));
589                 inserted = true;
590                 break;
591             }
592         }
593 
594         // not found, just append
595         if (!inserted)
596             m_ViewMenu->AppendCheckItem(idViewClassBrowser, _("Symbols browser"), _("Toggle displaying the symbols browser"));
597     }
598     else
599         CCLogger::Get()->DebugLog(_T("Could not find View menu!"));
600 
601     // add Reparse item in the "Project" menu
602     idx = menuBar->FindMenu(_("&Project"));
603     if (idx != wxNOT_FOUND)
604     {
605         m_ProjectMenu = menuBar->GetMenu(idx);
606         wxMenuItemList& items = m_ProjectMenu->GetMenuItems();
607         bool inserted = false;
608 
609         // find the first separator and insert before it
610         for (size_t i = items.GetCount() - 1; i > 0; --i)
611         {
612             if (items[i]->IsSeparator())
613             {
614                 m_ProjectMenu->InsertSeparator(i);
615                 m_ProjectMenu->Insert(i + 1, idCurrentProjectReparse, _("Reparse current project"), _("Reparse of the final switched project"));
616                 inserted = true;
617                 break;
618             }
619         }
620 
621         // not found, just append
622         if (!inserted)
623         {
624             m_ProjectMenu->AppendSeparator();
625             m_ProjectMenu->Append(idCurrentProjectReparse, _("Reparse current project"), _("Reparse of the final switched project"));
626         }
627     }
628     else
629         CCLogger::Get()->DebugLog(_T("Could not find Project menu!"));
630 }
631 
BuildModuleMenu(const ModuleType type,wxMenu * menu,const FileTreeData * data)632 void CodeCompletion::BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data)
633 {
634     // if not attached, exit
635     if (!menu || !IsAttached() || !m_InitDone)
636         return;
637 
638     if (type == mtEditorManager)
639     {
640         if (cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor())
641         {
642             if ( !IsProviderFor(ed) )
643                 return;
644         }
645 
646         wxString NameUnderCursor;
647         bool IsInclude = false;
648         const bool nameUnderCursor = CodeCompletionHelper::EditorHasNameUnderCursor(NameUnderCursor, IsInclude);
649         if (nameUnderCursor)
650         {
651             PluginManager *pluginManager = Manager::Get()->GetPluginManager();
652 
653             if (IsInclude)
654             {
655                 wxString msg;
656                 msg.Printf(_("Open #include file: '%s'"), NameUnderCursor.wx_str());
657                 menu->Insert(0, idOpenIncludeFile, msg);
658                 menu->Insert(1, wxID_SEPARATOR, wxEmptyString);
659                 pluginManager->RegisterFindMenuItems(true, 2);
660             }
661             else
662             {
663                 int initialPos = pluginManager->GetFindMenuItemFirst();
664                 int pos = initialPos;
665                 wxString msg;
666                 msg.Printf(_("Find declaration of: '%s'"), NameUnderCursor.wx_str());
667                 menu->Insert(pos++, idGotoDeclaration, msg);
668 
669                 msg.Printf(_("Find implementation of: '%s'"), NameUnderCursor.wx_str());
670                 menu->Insert(pos++, idGotoImplementation, msg);
671 
672                 if (m_NativeParser.GetParser().Done())
673                 {
674                     msg.Printf(_("Find references of: '%s'"), NameUnderCursor.wx_str());
675                     menu->Insert(pos++, idMenuFindReferences, msg);
676                 }
677                 pluginManager->RegisterFindMenuItems(false, pos - initialPos);
678             }
679         }
680 
681         const int insertId = menu->FindItem(_("Insert/Refactor"));
682         if (insertId != wxNOT_FOUND)
683         {
684             if (wxMenuItem* insertMenu = menu->FindItem(insertId, 0))
685             {
686                 if (wxMenu* subMenu = insertMenu->GetSubMenu())
687                 {
688                     subMenu->Append(idClassMethod, _("Class method declaration/implementation..."));
689                     subMenu->Append(idUnimplementedClassMethods, _("All class methods without implementation..."));
690 
691                     subMenu->AppendSeparator();
692 
693                     const bool enableRename = (m_NativeParser.GetParser().Done() && nameUnderCursor && !IsInclude);
694                     subMenu->Append(idMenuRenameSymbols, _("Rename symbols"), _("Rename symbols under cursor"));
695                     subMenu->Enable(idMenuRenameSymbols, enableRename);
696                 }
697                 else
698                     CCLogger::Get()->DebugLog(_T("Could not find Insert menu 3!"));
699             }
700             else
701                 CCLogger::Get()->DebugLog(_T("Could not find Insert menu 2!"));
702         }
703         else
704             CCLogger::Get()->DebugLog(_T("Could not find Insert menu!"));
705     }
706     else if (type == mtProjectManager)
707     {
708         if (data)
709         {
710             if (data->GetKind() == FileTreeData::ftdkProject)
711             {
712                 size_t position = menu->GetMenuItemCount();
713                 int id = menu->FindItem(_("Build"));
714                 if (id != wxNOT_FOUND)
715                     menu->FindChildItem(id, &position);
716                 menu->Insert(position, idSelectedProjectReparse, _("Reparse this project"),
717                              _("Reparse current actived project"));
718             }
719             else if (data->GetKind() == FileTreeData::ftdkFile)
720                 menu->Append(idSelectedFileReparse, _("Reparse this file"), _("Reparse current selected file"));
721         }
722     }
723 }
724 
BuildToolBar(wxToolBar * toolBar)725 bool CodeCompletion::BuildToolBar(wxToolBar* toolBar)
726 {
727     // load the toolbar resource
728     Manager::Get()->AddonToolBar(toolBar,_T("codecompletion_toolbar"));
729     // get the wxChoice control pointers
730     m_Function = XRCCTRL(*toolBar, "chcCodeCompletionFunction", wxChoice);
731     m_Scope    = XRCCTRL(*toolBar, "chcCodeCompletionScope",    wxChoice);
732 
733     m_ToolBar = toolBar;
734 
735     // set the wxChoice and best toolbar size
736     UpdateToolBar();
737 
738     // disable the wxChoices
739     EnableToolbarTools(false);
740 
741     return true;
742 }
743 
GetProviderStatusFor(cbEditor * ed)744 CodeCompletion::CCProviderStatus CodeCompletion::GetProviderStatusFor(cbEditor* ed)
745 {
746     EditorColourSet *colour_set = ed->GetColourSet();
747     if (colour_set && ed->GetLanguage() == colour_set->GetHighlightLanguage(wxT("C/C++")))
748         return ccpsActive;
749 
750     switch (ParserCommon::FileType(ed->GetFilename()))
751     {
752         case ParserCommon::ftHeader:
753         case ParserCommon::ftSource:
754             return ccpsActive;
755 
756         case ParserCommon::ftOther:
757         default:
758             break;
759     }
760     return ccpsUniversal;
761 }
762 
GetAutocompList(bool isAuto,cbEditor * ed,int & tknStart,int & tknEnd)763 std::vector<CodeCompletion::CCToken> CodeCompletion::GetAutocompList(bool isAuto, cbEditor* ed, int& tknStart, int& tknEnd)
764 {
765     std::vector<CCToken> tokens;
766 
767     if (!IsAttached() || !m_InitDone)
768         return tokens;
769 
770     cbStyledTextCtrl* stc = ed->GetControl();
771     const int style = stc->GetStyleAt(tknEnd);
772     const wxChar curChar = stc->GetCharAt(tknEnd - 1);
773 
774     if (isAuto) // filter illogical cases of auto-launch
775     {
776         // AutocompList can be prompt after user typed "::" or "->"
777         // or if in preprocessor directive, after user typed "<" or "\"" or "/"
778         if (   (   curChar == wxT(':') // scope operator
779                 && stc->GetCharAt(tknEnd - 2) != wxT(':') )
780             || (   curChar == wxT('>') // '->'
781                 && stc->GetCharAt(tknEnd - 2) != wxT('-') )
782             || (   wxString(wxT("<\"/")).Find(curChar) != wxNOT_FOUND // #include directive
783                 && !stc->IsPreprocessor(style) ) )
784         {
785             return tokens;
786         }
787     }
788 
789     const int lineIndentPos = stc->GetLineIndentPosition(stc->GetCurrentLine());
790     const wxChar lineFirstChar = stc->GetCharAt(lineIndentPos);
791 
792     if (lineFirstChar == wxT('#'))
793     {
794         const int startPos = stc->WordStartPosition(lineIndentPos + 1, true);
795         const int endPos = stc->WordEndPosition(lineIndentPos + 1, true);
796         const wxString str = stc->GetTextRange(startPos, endPos);
797 
798         if (str == wxT("include") && tknEnd > endPos)
799         {
800             DoCodeCompleteIncludes(ed, tknStart, tknEnd, tokens);
801         }
802         else if (endPos >= tknEnd && tknEnd > lineIndentPos)
803             DoCodeCompletePreprocessor(tknStart, tknEnd, ed, tokens);
804         else if ( (   str == wxT("define")
805                    || str == wxT("if")
806                    || str == wxT("ifdef")
807                    || str == wxT("ifndef")
808                    || str == wxT("elif")
809                    || str == wxT("elifdef")
810                    || str == wxT("elifndef")
811                    || str == wxT("undef") )
812                  && tknEnd > endPos )
813         {
814             DoCodeComplete(tknEnd, ed, tokens, true);
815         }
816         return tokens;
817     }
818     else if (curChar == wxT('#'))
819         return tokens;
820     else if (lineFirstChar == wxT(':') && curChar == _T(':'))
821         return tokens;
822 
823     if (   stc->IsString(style)
824         || stc->IsComment(style)
825         || stc->IsCharacter(style)
826         || stc->IsPreprocessor(style) )
827     {
828         return tokens;
829     }
830 
831     DoCodeComplete(tknEnd, ed, tokens);
832     return tokens;
833 }
834 
CalcStcFontSize(cbStyledTextCtrl * stc)835 static int CalcStcFontSize(cbStyledTextCtrl *stc)
836 {
837     wxFont defaultFont = stc->StyleGetFont(wxSCI_STYLE_DEFAULT);
838     defaultFont.SetPointSize(defaultFont.GetPointSize() + stc->GetZoom());
839     int fontSize;
840     stc->GetTextExtent(wxT("A"), nullptr, &fontSize, nullptr, nullptr, &defaultFont);
841     return fontSize;
842 }
843 
DoCodeComplete(int caretPos,cbEditor * ed,std::vector<CCToken> & tokens,bool preprocessorOnly)844 void CodeCompletion::DoCodeComplete(int caretPos, cbEditor* ed, std::vector<CCToken>& tokens, bool preprocessorOnly)
845 {
846     const bool caseSens = m_NativeParser.GetParser().Options().caseSensitive;
847     cbStyledTextCtrl* stc = ed->GetControl();
848 
849     TokenIdxSet result;
850     if (   m_NativeParser.MarkItemsByAI(result, m_NativeParser.GetParser().Options().useSmartSense, true, caseSens, caretPos)
851         || m_NativeParser.LastAISearchWasGlobal() ) // enter even if no match (code-complete C++ keywords)
852     {
853         if (s_DebugSmartSense)
854             CCLogger::Get()->DebugLog(F(wxT("%lu results"), static_cast<unsigned long>(result.size())));
855         TRACE(F(wxT("%lu results"), static_cast<unsigned long>(result.size())));
856 
857         if (result.size() <= m_CCMaxMatches)
858         {
859             if (s_DebugSmartSense)
860                 CCLogger::Get()->DebugLog(wxT("Generating tokens list..."));
861 
862             const int fontSize = CalcStcFontSize(stc);
863             wxImageList* ilist = m_NativeParser.GetImageList(fontSize);
864             stc->ClearRegisteredImages();
865 
866             tokens.reserve(result.size());
867             std::set<int> alreadyRegistered;
868             StringSet uniqueStrings; // ignore keywords with same name as parsed tokens
869 
870             TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
871 
872             CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
873 
874             for (TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it)
875             {
876                 const Token* token = tree->at(*it);
877                 if (!token || token->m_Name.IsEmpty())
878                     continue;
879 
880                 if (preprocessorOnly && token->m_TokenKind != tkMacroDef)
881                     continue;
882 
883                 int iidx = m_NativeParser.GetTokenKindImage(token);
884                 if (alreadyRegistered.find(iidx) == alreadyRegistered.end())
885                 {
886                     stc->RegisterImage(iidx, ilist->GetBitmap(iidx));
887                     alreadyRegistered.insert(iidx);
888                 }
889 
890                 wxString dispStr;
891                 if (token->m_TokenKind & tkAnyFunction)
892                 {
893                     if (m_DocHelper.IsEnabled())
894                         dispStr = wxT("(): ") + token->m_FullType;
895                     else
896                         dispStr = token->GetFormattedArgs() << _T(": ") << token->m_FullType;
897                 }
898                 else if (token->m_TokenKind == tkVariable)
899                     dispStr = wxT(": ") + token->m_FullType;
900                 tokens.push_back(CCToken(token->m_Index, token->m_Name + dispStr, token->m_Name, token->m_IsTemp ? 0 : 5, iidx));
901                 uniqueStrings.insert(token->m_Name);
902 
903                 if (token->m_TokenKind == tkNamespace && token->m_Aliases.size())
904                 {
905                     for (size_t i = 0; i < token->m_Aliases.size(); ++i)
906                     {
907                         // dispStr will currently be empty, but contain something in the future...
908                         tokens.push_back(CCToken(token->m_Index, token->m_Aliases[i] + dispStr, token->m_Aliases[i], 5, iidx));
909                         uniqueStrings.insert(token->m_Aliases[i]);
910                     }
911                 }
912             }
913 
914             CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
915 
916             if (m_NativeParser.LastAISearchWasGlobal() && !preprocessorOnly)
917             {
918                 // empty or partial search phrase: add theme keywords in search list
919                 if (s_DebugSmartSense)
920                     CCLogger::Get()->DebugLog(_T("Last AI search was global: adding theme keywords in list"));
921 
922                 EditorColourSet* colour_set = ed->GetColourSet();
923                 if (colour_set)
924                 {
925                     wxString lastSearch = m_NativeParser.LastAIGlobalSearch().Lower();
926                     int iidx = ilist->GetImageCount();
927                     FileType fTp = FileTypeOf(ed->GetShortName());
928                     bool isC = (fTp == ftHeader || fTp == ftSource|| fTp == ftTemplateSource);
929                     // theme keywords
930                     HighlightLanguage lang = ed->GetLanguage();
931                     if (lang == HL_NONE)
932                         lang = colour_set->GetLanguageForFilename(ed->GetFilename());
933                     wxString strLang = colour_set->GetLanguageName(lang);
934                     // if its sourcecode/header file and a known fileformat, show the corresponding icon
935                     if (isC && strLang == wxT("C/C++"))
936                         stc->RegisterImage(iidx, GetImage(ImageId::KeywordCPP, fontSize));
937                     else if (isC && strLang == wxT("D"))
938                         stc->RegisterImage(iidx, GetImage(ImageId::KeywordD, fontSize));
939                     else
940                         stc->RegisterImage(iidx, GetImage(ImageId::Unknown, fontSize));
941                     // the first two keyword sets are the primary and secondary keywords (for most lexers at least)
942                     // but this is now configurable in global settings
943                     for (int i = 0; i <= wxSCI_KEYWORDSET_MAX; ++i)
944                     {
945                         if (!m_LexerKeywordsToInclude[i])
946                             continue;
947 
948                         wxString keywords = colour_set->GetKeywords(lang, i);
949                         wxStringTokenizer tkz(keywords, wxT(" \t\r\n"), wxTOKEN_STRTOK);
950                         while (tkz.HasMoreTokens())
951                         {
952                             wxString kw = tkz.GetNextToken();
953                             if (   kw.Lower().StartsWith(lastSearch)
954                                 && uniqueStrings.find(kw) == uniqueStrings.end() )
955                             {
956                                 tokens.push_back(CCToken(wxNOT_FOUND, kw, iidx));
957                             }
958                         }
959                     }
960                 }
961             }
962         }
963         else if (!stc->CallTipActive())
964         {
965             wxString msg = _("Too many results.\n"
966                              "Please edit results' limit in code-completion options,\n"
967                              "or type at least one more character to narrow the scope down.");
968             stc->CallTipShow(stc->GetCurrentPos(), msg);
969         }
970     }
971     else if (!stc->CallTipActive())
972     {
973         if (s_DebugSmartSense)
974             CCLogger::Get()->DebugLog(wxT("0 results"));
975 
976         if (!m_NativeParser.GetParser().Done())
977         {
978             wxString msg = _("The Parser is still parsing files.");
979             stc->CallTipShow(stc->GetCurrentPos(), msg);
980             msg += m_NativeParser.GetParser().NotDoneReason();
981             CCLogger::Get()->DebugLog(msg);
982         }
983     }
984 }
985 
DoCodeCompletePreprocessor(int tknStart,int tknEnd,cbEditor * ed,std::vector<CCToken> & tokens)986 void CodeCompletion::DoCodeCompletePreprocessor(int tknStart, int tknEnd, cbEditor* ed, std::vector<CCToken>& tokens)
987 {
988     cbStyledTextCtrl* stc = ed->GetControl();
989     if (stc->GetLexer() != wxSCI_LEX_CPP)
990     {
991         const FileType fTp = FileTypeOf(ed->GetShortName());
992         if (   fTp != ftSource
993             && fTp != ftHeader
994             && fTp != ftTemplateSource
995             && fTp != ftResource )
996         {
997             return; // not C/C++
998         }
999     }
1000     const wxString text = stc->GetTextRange(tknStart, tknEnd);
1001 
1002     wxStringVec macros;
1003     macros.push_back(wxT("define"));
1004     macros.push_back(wxT("elif"));
1005     macros.push_back(wxT("elifdef"));
1006     macros.push_back(wxT("elifndef"));
1007     macros.push_back(wxT("else"));
1008     macros.push_back(wxT("endif"));
1009     macros.push_back(wxT("error"));
1010     macros.push_back(wxT("if"));
1011     macros.push_back(wxT("ifdef"));
1012     macros.push_back(wxT("ifndef"));
1013     macros.push_back(wxT("include"));
1014     macros.push_back(wxT("line"));
1015     macros.push_back(wxT("pragma"));
1016     macros.push_back(wxT("undef"));
1017     const wxString idxStr = F(wxT("\n%d"), PARSER_IMG_MACRO_DEF);
1018     for (size_t i = 0; i < macros.size(); ++i)
1019     {
1020         if (text.IsEmpty() || macros[i][0] == text[0]) // ignore tokens that start with a different letter
1021             tokens.push_back(CCToken(wxNOT_FOUND, macros[i], PARSER_IMG_MACRO_DEF));
1022     }
1023     stc->ClearRegisteredImages();
1024     const int fontSize = CalcStcFontSize(stc);
1025     stc->RegisterImage(PARSER_IMG_MACRO_DEF,
1026                        m_NativeParser.GetImageList(fontSize)->GetBitmap(PARSER_IMG_MACRO_DEF));
1027 }
1028 
DoCodeCompleteIncludes(cbEditor * ed,int & tknStart,int tknEnd,std::vector<CCToken> & tokens)1029 void CodeCompletion::DoCodeCompleteIncludes(cbEditor* ed, int& tknStart, int tknEnd, std::vector<CCToken>& tokens)
1030 {
1031     if (!m_CCEnableHeaders)
1032         return;
1033 
1034     const wxString curFile(ed->GetFilename());
1035     const wxString curPath(wxFileName(curFile).GetPath());
1036 
1037     FileType ft = FileTypeOf(ed->GetShortName());
1038     if ( ft != ftHeader && ft != ftSource && ft != ftTemplateSource) // only parse source/header files
1039         return;
1040 
1041     cbStyledTextCtrl* stc = ed->GetControl();
1042     const int lineStartPos = stc->PositionFromLine(stc->GetCurrentLine());
1043     wxString line = stc->GetCurLine();
1044     line.Trim();
1045     if (line.IsEmpty() || !CodeCompletionHelper::TestIncludeLine(line))
1046         return;
1047 
1048     int keyPos = line.Find(wxT('"'));
1049     if (keyPos == wxNOT_FOUND || keyPos >= tknEnd - lineStartPos)
1050         keyPos = line.Find(wxT('<'));
1051     if (keyPos == wxNOT_FOUND || keyPos >= tknEnd - lineStartPos)
1052         return;
1053     ++keyPos;
1054 
1055     // now, we are after the quote prompt, "filename" is the text we already typed after the
1056     // #include directive, such as #include <abc|  , so that we need to prompt all the header files
1057     // which has the prefix "abc"
1058     wxString filename = line.SubString(keyPos, tknEnd - lineStartPos - 1);
1059     filename.Replace(wxT("\\"), wxT("/"), true);
1060     if (!filename.empty() && (filename.Last() == wxT('"') || filename.Last() == wxT('>')))
1061         filename.RemoveLast();
1062 
1063     size_t maxFiles = m_CCMaxMatches;
1064     if (filename.IsEmpty() && maxFiles > 3000)
1065         maxFiles = 3000; // do not try to collect too many files if there is no context (prevent hang)
1066 
1067     // fill a list of matching files
1068     StringSet files;
1069 
1070     // #include < or #include "
1071     cbProject* project = m_NativeParser.GetProjectByEditor(ed);
1072 
1073     // since we are going to access the m_SystemHeadersMap, we add a locker here
1074     // here we collect all the header files names which is under "system include search dirs"
1075 #if wxCHECK_VERSION(3, 0, 0)
1076     if (m_SystemHeadersThreadCS.TryEnter())
1077     {
1078 #else
1079     {
1080         m_SystemHeadersThreadCS.Enter();
1081 #endif // wxCHECK_VERSION(3, 0, 0)
1082         // if the project get modified, fetch the dirs again, otherwise, use cached dirs
1083         wxArrayString& incDirs = GetSystemIncludeDirs(project, project ? project->GetModified() : true);
1084         for (size_t i = 0; i < incDirs.GetCount(); ++i)
1085         {
1086             // shm_it means system_header_map_iterator
1087             SystemHeadersMap::const_iterator shm_it = m_SystemHeadersMap.find(incDirs[i]);
1088             if (shm_it != m_SystemHeadersMap.end())
1089             {
1090                 const StringSet& headers = shm_it->second;
1091                 for (StringSet::const_iterator ss_it = headers.begin(); ss_it != headers.end(); ++ss_it)
1092                 {
1093                     const wxString& file = *ss_it;
1094                     // if find a value matches already typed "filename", add to the result
1095                     if (file.StartsWith(filename))
1096                     {
1097                         files.insert(file);
1098                         if (files.size() > maxFiles)
1099                             break; // exit inner loop
1100                     }
1101                 }
1102                 if (files.size() > maxFiles)
1103                     break; // exit outer loop
1104             }
1105         }
1106         m_SystemHeadersThreadCS.Leave();
1107     }
1108 
1109     // #include "
1110     if (project)
1111     {
1112 #if wxCHECK_VERSION(3, 0, 0)
1113         if (m_SystemHeadersThreadCS.TryEnter())
1114         {
1115 #else
1116         {
1117             m_SystemHeadersThreadCS.Enter();
1118 #endif // wxCHECK_VERSION(3, 0, 0)
1119             wxArrayString buildTargets;
1120             ProjectFile* pf = project ? project->GetFileByFilename(curFile, false) : 0;
1121             if (pf)
1122                 buildTargets = pf->buildTargets;
1123 
1124             const wxArrayString localIncludeDirs = GetLocalIncludeDirs(project, buildTargets);
1125             for (FilesList::const_iterator it = project->GetFilesList().begin();
1126                                            it != project->GetFilesList().end(); ++it)
1127             {
1128                 pf = *it;
1129                 if (pf && FileTypeOf(pf->relativeFilename) == ftHeader)
1130                 {
1131                     wxString file = pf->file.GetFullPath();
1132                     wxString header;
1133                     for (size_t j = 0; j < localIncludeDirs.GetCount(); ++j)
1134                     {
1135                         const wxString& dir = localIncludeDirs[j];
1136                         if (file.StartsWith(dir))
1137                         {
1138                             header = file.Mid(dir.Len());
1139                             header.Replace(wxT("\\"), wxT("/"));
1140                             break;
1141                         }
1142                     }
1143 
1144                     if (header.IsEmpty())
1145                     {
1146                         if (pf->buildTargets != buildTargets)
1147                             continue;
1148 
1149                         wxFileName fn(file);
1150                         fn.MakeRelativeTo(curPath);
1151                         header = fn.GetFullPath(wxPATH_UNIX);
1152                     }
1153 
1154                     if (header.StartsWith(filename))
1155                     {
1156                         files.insert(header);
1157                         if (files.size() > maxFiles)
1158                             break;
1159                     }
1160                 }
1161             }
1162             m_SystemHeadersThreadCS.Leave();
1163         }
1164     }
1165 
1166     if (!files.empty())
1167     {
1168         tknStart = lineStartPos + keyPos;
1169         tokens.reserve(files.size());
1170         for (StringSet::const_iterator ssIt = files.begin(); ssIt != files.end(); ++ssIt)
1171             tokens.push_back(CCToken(wxNOT_FOUND, *ssIt, 0));
1172         stc->ClearRegisteredImages();
1173         const int fontSize = CalcStcFontSize(stc);
1174         stc->RegisterImage(0, GetImage(ImageId::HeaderFile, fontSize));
1175     }
1176 }
1177 
1178 std::vector<CodeCompletion::CCCallTip> CodeCompletion::GetCallTips(int pos, int style, cbEditor* ed, int& argsPos)
1179 {
1180     std::vector<CCCallTip> tips;
1181     if (!IsAttached() || !m_InitDone || style == wxSCI_C_WXSMITH || !m_NativeParser.GetParser().Done())
1182         return tips;
1183 
1184     int typedCommas = 0;
1185     wxArrayString items;
1186     argsPos = m_NativeParser.GetCallTips(items, typedCommas, ed, pos);
1187     StringSet uniqueTips; // check against this before inserting a new tip in the list
1188     for (size_t i = 0; i < items.GetCount(); ++i)
1189     {
1190         // allow only unique, non-empty items with equal or more commas than what the user has already typed
1191         if (   uniqueTips.find(items[i]) == uniqueTips.end() // unique
1192             && !items[i].IsEmpty() // non-empty
1193             && typedCommas <= m_NativeParser.CountCommas(items[i], 0) ) // commas satisfied
1194         {
1195             uniqueTips.insert(items[i]);
1196             int hlStart = wxSCI_INVALID_POSITION;
1197             int hlEnd   = wxSCI_INVALID_POSITION;
1198             m_NativeParser.GetCallTipHighlight(items[i], &hlStart, &hlEnd, typedCommas);
1199             tips.push_back(CCCallTip(items[i], hlStart, hlEnd));
1200         }
1201     }
1202     return tips;
1203 }
1204 
1205 wxString CodeCompletion::GetDocumentation(const CCToken& token)
1206 {
1207     return m_DocHelper.GenerateHTML(token.id, m_NativeParser.GetParser().GetTokenTree());
1208 }
1209 
1210 std::vector<CodeCompletion::CCToken> CodeCompletion::GetTokenAt(int pos, cbEditor* ed, bool& WXUNUSED(allowCallTip))
1211 {
1212     std::vector<CCToken> tokens;
1213     if (!IsAttached() || !m_InitDone)
1214         return tokens;
1215 
1216     // ignore comments, strings, preprocessors, etc
1217     cbStyledTextCtrl* stc = ed->GetControl();
1218     const int style = stc->GetStyleAt(pos);
1219     if (   stc->IsString(style)
1220         || stc->IsComment(style)
1221         || stc->IsCharacter(style)
1222         || stc->IsPreprocessor(style) )
1223     {
1224         return tokens;
1225     }
1226 
1227     TokenIdxSet result;
1228     int endOfWord = stc->WordEndPosition(pos, true);
1229     if (m_NativeParser.MarkItemsByAI(result, true, false, true, endOfWord))
1230     {
1231         TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
1232 
1233         CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1234 
1235         for (TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it)
1236         {
1237             const Token* token = tree->at(*it);
1238             if (token)
1239             {
1240                 tokens.push_back(CCToken(*it, token->DisplayName()));
1241                 if (tokens.size() > 32)
1242                     break;
1243             }
1244         }
1245 
1246         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1247     }
1248 
1249     return tokens;
1250 }
1251 
1252 wxString CodeCompletion::OnDocumentationLink(wxHtmlLinkEvent& event, bool& dismissPopup)
1253 {
1254     return m_DocHelper.OnDocumentationLink(event, dismissPopup);
1255 }
1256 
1257 void CodeCompletion::DoAutocomplete(const CCToken& token, cbEditor* ed)
1258 {
1259     wxString itemText = CodeCompletionHelper::AutocompGetName(token.displayName);
1260     cbStyledTextCtrl* stc = ed->GetControl();
1261 
1262     int curPos = stc->GetCurrentPos();
1263     int startPos = stc->WordStartPosition(curPos, true);
1264     if (   itemText.GetChar(0) == _T('~') // special handle for dtor
1265         && startPos > 0
1266         && stc->GetCharAt(startPos - 1) == _T('~'))
1267     {
1268         --startPos;
1269     }
1270     bool needReparse = false;
1271 
1272     if (stc->IsPreprocessor(stc->GetStyleAt(curPos)))
1273     {
1274         curPos = stc->GetLineEndPosition(stc->GetCurrentLine()); // delete rest of line
1275         bool addComment = (itemText == wxT("endif"));
1276         for (int i = stc->GetCurrentPos(); i < curPos; ++i)
1277         {
1278             if (stc->IsComment(stc->GetStyleAt(i)))
1279             {
1280                 curPos = i; // preserve line comment
1281                 if (wxIsspace(stc->GetCharAt(i - 1)))
1282                     --curPos; // preserve a space before the comment
1283                 addComment = false;
1284                 break;
1285             }
1286         }
1287         if (addComment) // search backwards for the #if*
1288         {
1289             wxRegEx ppIf(wxT("^[ \t]*#[ \t]*if"));
1290             wxRegEx ppEnd(wxT("^[ \t]*#[ \t]*endif"));
1291             int depth = -1;
1292             for (int ppLine = stc->GetCurrentLine() - 1; ppLine >= 0; --ppLine)
1293             {
1294                 if (stc->GetLine(ppLine).Find(wxT('#')) != wxNOT_FOUND) // limit testing due to performance cost
1295                 {
1296                     if (ppIf.Matches(stc->GetLine(ppLine))) // ignore else's, elif's, ...
1297                         ++depth;
1298                     else if (ppEnd.Matches(stc->GetLine(ppLine)))
1299                         --depth;
1300                 }
1301                 if (depth == 0)
1302                 {
1303                     wxRegEx pp(wxT("^[ \t]*#[ \t]*[a-z]*([ \t]+([a-zA-Z0-9_]+)|())"));
1304                     pp.Matches(stc->GetLine(ppLine));
1305                     if (!pp.GetMatch(stc->GetLine(ppLine), 2).IsEmpty())
1306                         itemText.Append(wxT(" // ") + pp.GetMatch(stc->GetLine(ppLine), 2));
1307                     break;
1308                 }
1309             }
1310         }
1311         needReparse = true;
1312 
1313         int   pos = startPos - 1;
1314         wxChar ch = stc->GetCharAt(pos);
1315         while (ch != _T('<') && ch != _T('"') && ch != _T('#') && (pos>0))
1316             ch = stc->GetCharAt(--pos);
1317         if (ch == _T('<') || ch == _T('"'))
1318             startPos = pos + 1;
1319 
1320         if (ch == _T('"'))
1321             itemText << _T('"');
1322         else if (ch == _T('<'))
1323             itemText << _T('>');
1324     }
1325     else
1326     {
1327         const int endPos = stc->WordEndPosition(curPos, true);
1328         const wxString& alreadyText = stc->GetTextRange(curPos, endPos);
1329         if (!alreadyText.IsEmpty() && itemText.EndsWith(alreadyText))
1330             curPos = endPos;
1331     }
1332 
1333     int positionModificator = 0;
1334     bool insideParentheses = false;
1335     if (token.id != -1 && m_CCAutoAddParentheses)
1336     {
1337         CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1338 
1339         TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
1340         const Token* tkn = tree->at(token.id);
1341 
1342         if (!tkn)
1343         {   CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex) }
1344         else
1345         {
1346             bool addParentheses = tkn->m_TokenKind & tkAnyFunction;
1347             if (!addParentheses && (tkn->m_TokenKind & tkMacroDef))
1348             {
1349                 if (tkn->m_Args.size() > 0)
1350                     addParentheses = true;
1351             }
1352             // cache args to avoid locking
1353             wxString tokenArgs = tkn->GetStrippedArgs();
1354 
1355             CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1356 
1357             if (addParentheses)
1358             {
1359                 bool insideFunction = true;
1360                 if (m_CCDetectImplementation)
1361                 {
1362                     ccSearchData searchData = { stc, ed->GetFilename() };
1363                     int funcToken;
1364                     if (m_NativeParser.FindCurrentFunctionStart(&searchData, 0, 0, &funcToken) == -1)
1365                     {
1366                         // global scope
1367                         itemText += tokenArgs;
1368                         insideFunction = false;
1369                     }
1370                     else // Found something, but result may be false positive.
1371                     {
1372                         CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1373 
1374                         const Token* parent = tree->at(funcToken);
1375                         // Make sure that parent is not container (class, etc)
1376                         if (parent && (parent->m_TokenKind & tkAnyFunction) == 0)
1377                         {
1378                             // class scope
1379                             itemText += tokenArgs;
1380                             insideFunction = false;
1381                         }
1382 
1383                         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1384                     }
1385                 }
1386 
1387                 if (insideFunction)
1388                 {
1389                     // Inside block
1390                     // Check if there are brace behind the target
1391                     if (stc->GetCharAt(curPos) != _T('('))
1392                     {
1393                         itemText += _T("()");
1394                         if (tokenArgs.size() > 2) // more than '()'
1395                         {
1396                             positionModificator = -1;
1397                             insideParentheses = true;
1398                         }
1399                     }
1400                     else
1401                         positionModificator = 1; // Set caret after '('
1402                 }
1403             }
1404         } // if tkn
1405     } // if token.id
1406 
1407     stc->SetTargetStart(startPos);
1408     stc->SetTargetEnd(curPos);
1409 
1410     stc->AutoCompCancel();
1411     if (stc->GetTextRange(startPos, curPos) != itemText)
1412         stc->ReplaceTarget(itemText);
1413     stc->GotoPos(startPos + itemText.Length() + positionModificator);
1414 
1415     if (insideParentheses)
1416     {
1417         stc->EnableTabSmartJump();
1418         int tooltipMode = Manager::Get()->GetConfigManager(wxT("ccmanager"))->ReadInt(wxT("/tooltip_mode"), 1);
1419         if (tooltipMode != 3) // keybound only
1420         {
1421             CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
1422             Manager::Get()->ProcessEvent(evt);
1423         }
1424     }
1425 
1426     if (needReparse)
1427     {
1428         TRACE(_T("CodeCompletion::EditorEventHook: Starting m_TimerRealtimeParsing."));
1429         m_TimerRealtimeParsing.Start(1, wxTIMER_ONE_SHOT);
1430     }
1431     stc->ChooseCaretX();
1432 }
1433 
1434 wxArrayString CodeCompletion::GetLocalIncludeDirs(cbProject* project, const wxArrayString& buildTargets)
1435 {
1436     wxArrayString dirs;
1437     // Do not try to operate include directories if the project is not for this platform
1438     if (m_CCEnablePlatformCheck && !project->SupportsCurrentPlatform())
1439         return dirs;
1440     // add project level compiler include search paths
1441     const wxString prjPath = project->GetCommonTopLevelPath();
1442     GetAbsolutePath(prjPath, project->GetIncludeDirs(), dirs);
1443     // add target level compiler include search paths
1444     for (size_t i = 0; i < buildTargets.GetCount(); ++i)
1445     {
1446         ProjectBuildTarget* tgt = project->GetBuildTarget(buildTargets[i]);
1447         if (!tgt)
1448             continue;
1449         // Do not try to operate include directories if the target is not for this platform
1450         if (!m_CCEnablePlatformCheck || tgt->SupportsCurrentPlatform())
1451         {
1452             GetAbsolutePath(prjPath, tgt->GetIncludeDirs(), dirs);
1453         }
1454     }
1455 
1456     // if a path has prefix with the project's path, it is a local include search dir
1457     // other wise, it is a system level include search dir, we try to collect all the system dirs
1458     wxArrayString sysDirs;
1459     for (size_t i = 0; i < dirs.GetCount();)
1460     {
1461         // if the dir with the prefix of project path, then it is a "local dir"
1462         if (dirs[i].StartsWith(prjPath))
1463             ++i;
1464         else // otherwise, it is a "system dir", so add to "sysDirs"
1465         {
1466             if (m_SystemHeadersMap.find(dirs[i]) == m_SystemHeadersMap.end())
1467                 sysDirs.Add(dirs[i]);
1468             // remove the system dir in dirs
1469             dirs.RemoveAt(i);
1470         }
1471     }
1472 
1473     if (!sysDirs.IsEmpty())
1474     {
1475         cbAssert(m_CCEnableHeaders);
1476         // Create a worker thread associated with "sysDirs". Put it in a queue and run it.
1477         SystemHeadersThread* thread = new SystemHeadersThread(this, &m_SystemHeadersThreadCS,
1478                                                               m_SystemHeadersMap, sysDirs);
1479         m_SystemHeadersThreads.push_back(thread);
1480         thread->Run();
1481     }
1482 
1483     dirs.Sort(CodeCompletionHelper::CompareStringLen);
1484     return dirs; // return all the local dirs
1485 }
1486 
1487 wxArrayString& CodeCompletion::GetSystemIncludeDirs(cbProject* project, bool force)
1488 {
1489     static cbProject*    lastProject = nullptr;
1490     static wxArrayString incDirs;
1491 
1492     if (!force && project == lastProject) // force == false means we can use the cached dirs
1493         return incDirs;
1494     else
1495     {
1496         incDirs.Clear();
1497         lastProject = project;
1498     }
1499 
1500     wxString prjPath;
1501     if (project)
1502         prjPath = project->GetCommonTopLevelPath();
1503 
1504     ParserBase* parser = m_NativeParser.GetParserByProject(project);
1505     if (!parser)
1506         return incDirs;
1507 
1508     incDirs = parser->GetIncludeDirs();
1509     // we try to remove the dirs which belong to the project
1510     for (size_t i = 0; i < incDirs.GetCount();)
1511     {
1512         if (incDirs[i].Last() != wxFILE_SEP_PATH)
1513             incDirs[i].Append(wxFILE_SEP_PATH);
1514         // the dirs which have prjPath prefix are local dirs, so they should be removed
1515         if (project && incDirs[i].StartsWith(prjPath))
1516             incDirs.RemoveAt(i);
1517         else
1518             ++i;
1519     }
1520 
1521     return incDirs;
1522 }
1523 
1524 void CodeCompletion::GetAbsolutePath(const wxString& basePath, const wxArrayString& targets, wxArrayString& dirs)
1525 {
1526     for (size_t i = 0; i < targets.GetCount(); ++i)
1527     {
1528         wxString includePath = targets[i];
1529         Manager::Get()->GetMacrosManager()->ReplaceMacros(includePath);
1530         wxFileName fn(includePath, wxEmptyString);
1531         if (fn.IsRelative())
1532         {
1533             const wxArrayString oldDirs = fn.GetDirs();
1534             fn.SetPath(basePath);
1535             for (size_t j = 0; j < oldDirs.GetCount(); ++j)
1536                 fn.AppendDir(oldDirs[j]);
1537         }
1538 
1539         // Detect if this directory is for the file system root and skip it. Sometimes macro
1540         // replacements create such paths and we don't want to scan whole disks because of this.
1541         if (fn.IsAbsolute() && fn.GetDirCount() == 0)
1542             continue;
1543 
1544         const wxString path = fn.GetFullPath();
1545         if (dirs.Index(path) == wxNOT_FOUND)
1546             dirs.Add(path);
1547     }
1548 }
1549 
1550 void CodeCompletion::EditorEventHook(cbEditor* editor, wxScintillaEvent& event)
1551 {
1552     if (!IsAttached() || !m_InitDone)
1553     {
1554         event.Skip();
1555         return;
1556     }
1557 
1558     if ( !IsProviderFor(editor) )
1559     {
1560         event.Skip();
1561         return;
1562     }
1563 
1564     cbStyledTextCtrl* control = editor->GetControl();
1565 
1566     if      (event.GetEventType() == wxEVT_SCI_CHARADDED)
1567     {   TRACE(_T("wxEVT_SCI_CHARADDED")); }
1568     else if (event.GetEventType() == wxEVT_SCI_CHANGE)
1569     {   TRACE(_T("wxEVT_SCI_CHANGE")); }
1570     else if (event.GetEventType() == wxEVT_SCI_MODIFIED)
1571     {   TRACE(_T("wxEVT_SCI_MODIFIED")); }
1572     else if (event.GetEventType() == wxEVT_SCI_AUTOCOMP_SELECTION)
1573     {   TRACE(_T("wxEVT_SCI_AUTOCOMP_SELECTION")); }
1574     else if (event.GetEventType() == wxEVT_SCI_AUTOCOMP_CANCELLED)
1575     {   TRACE(_T("wxEVT_SCI_AUTOCOMP_CANCELLED")); }
1576 
1577     // if the user is modifying the editor, then CC should try to reparse the editor's content
1578     // and update the token tree.
1579     if (   m_NativeParser.GetParser().Options().whileTyping
1580         && (   (event.GetModificationType() & wxSCI_MOD_INSERTTEXT)
1581             || (event.GetModificationType() & wxSCI_MOD_DELETETEXT) ) )
1582     {
1583         m_NeedReparse = true;
1584     }
1585 
1586     if (control->GetCurrentLine() != m_CurrentLine)
1587     {
1588         // reparsing the editor only happens in the condition that the caret's line number
1589         // is changed.
1590         if (m_NeedReparse)
1591         {
1592             TRACE(_T("CodeCompletion::EditorEventHook: Starting m_TimerRealtimeParsing."));
1593             m_TimerRealtimeParsing.Start(REALTIME_PARSING_DELAY, wxTIMER_ONE_SHOT);
1594             m_CurrentLength = control->GetLength();
1595             m_NeedReparse = false;
1596         }
1597         // wxEVT_SCI_UPDATEUI will be sent on caret's motion, but we are only interested in the
1598         // cases where line number is changed. Then we need to update the CC's toolbar.
1599         if (event.GetEventType() == wxEVT_SCI_UPDATEUI)
1600         {
1601             m_ToolbarNeedRefresh = true;
1602             TRACE(_T("CodeCompletion::EditorEventHook: Starting m_TimerToolbar."));
1603             if (m_TimerEditorActivated.IsRunning())
1604                 m_TimerToolbar.Start(EDITOR_ACTIVATED_DELAY + 1, wxTIMER_ONE_SHOT);
1605             else
1606                 m_TimerToolbar.Start(TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT);
1607         }
1608     }
1609 
1610     // allow others to handle this event
1611     event.Skip();
1612 }
1613 
1614 void CodeCompletion::RereadOptions()
1615 {
1616     // Keep this in sync with CCOptionsDlg::CCOptionsDlg and CCOptionsDlg::OnApply
1617 
1618     ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
1619 
1620     m_LexerKeywordsToInclude[0] = cfg->ReadBool(_T("/lexer_keywords_set1"), true);
1621     m_LexerKeywordsToInclude[1] = cfg->ReadBool(_T("/lexer_keywords_set2"), true);
1622     m_LexerKeywordsToInclude[2] = cfg->ReadBool(_T("/lexer_keywords_set3"), false);
1623     m_LexerKeywordsToInclude[3] = cfg->ReadBool(_T("/lexer_keywords_set4"), false);
1624     m_LexerKeywordsToInclude[4] = cfg->ReadBool(_T("/lexer_keywords_set5"), false);
1625     m_LexerKeywordsToInclude[5] = cfg->ReadBool(_T("/lexer_keywords_set6"), false);
1626     m_LexerKeywordsToInclude[6] = cfg->ReadBool(_T("/lexer_keywords_set7"), false);
1627     m_LexerKeywordsToInclude[7] = cfg->ReadBool(_T("/lexer_keywords_set8"), false);
1628     m_LexerKeywordsToInclude[8] = cfg->ReadBool(_T("/lexer_keywords_set9"), false);
1629 
1630     // for CC
1631     m_CCMaxMatches           = cfg->ReadInt(_T("/max_matches"),            16384);
1632     m_CCAutoAddParentheses   = cfg->ReadBool(_T("/auto_add_parentheses"),  true);
1633     m_CCDetectImplementation = cfg->ReadBool(_T("/detect_implementation"), false); //depends on auto_add_parentheses
1634     m_CCFillupChars          = cfg->Read(_T("/fillup_chars"),              wxEmptyString);
1635     m_CCEnableHeaders        = cfg->ReadBool(_T("/enable_headers"),        true);
1636     m_CCEnablePlatformCheck  = cfg->ReadBool(_T("/platform_check"),        true);
1637 
1638     // update the CC toolbar option, and tick the timer for toolbar
1639     // NOTE (ollydbg#1#12/06/14): why?
1640     if (m_ToolBar)
1641     {
1642         UpdateToolBar();
1643         CodeBlocksLayoutEvent evt(cbEVT_UPDATE_VIEW_LAYOUT);
1644         Manager::Get()->ProcessEvent(evt);
1645         m_ToolbarNeedReparse = true;
1646         TRACE(_T("CodeCompletion::RereadOptions: Starting m_TimerToolbar."));
1647         m_TimerToolbar.Start(TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT);
1648     }
1649 
1650     m_DocHelper.RereadOptions(cfg);
1651 }
1652 
1653 void CodeCompletion::UpdateToolBar()
1654 {
1655     ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
1656     const bool showScope = cfg->ReadBool(_T("/scope_filter"), true);
1657     const int scopeLength = cfg->ReadInt(_T("/toolbar_scope_length"), 280);
1658     const int functionLength = cfg->ReadInt(_T("/toolbar_function_length"), 660);
1659 
1660     if (showScope && !m_Scope)
1661     {
1662         // Show the scope choice
1663         m_Scope = new wxChoice(m_ToolBar, XRCID("chcCodeCompletionScope"), wxPoint(0, 0), wxSize(scopeLength, -1), 0, 0);
1664         m_ToolBar->InsertControl(0, m_Scope);
1665     }
1666     else if (!showScope && m_Scope)
1667     {
1668         // Hide the scope choice
1669         m_ToolBar->DeleteTool(m_Scope->GetId());
1670         m_Scope = nullptr;
1671     }
1672     else if (m_Scope)
1673     {
1674         // Just apply new size to scope choice
1675         m_Scope->SetSize(wxSize(scopeLength, -1));
1676     }
1677 
1678     m_Function->SetSize(wxSize(functionLength, -1));
1679 
1680     m_ToolBar->Realize();
1681     m_ToolBar->SetInitialSize();
1682 }
1683 
1684 void CodeCompletion::OnUpdateUI(wxUpdateUIEvent& event)
1685 {
1686     wxString NameUnderCursor;
1687     bool IsInclude = false;
1688     const bool HasNameUnderCursor = CodeCompletionHelper::EditorHasNameUnderCursor(NameUnderCursor, IsInclude);
1689 
1690     const bool HasEd = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor() != 0;
1691     if (m_EditMenu)
1692     {
1693         const bool RenameEnable = HasNameUnderCursor && !IsInclude && m_NativeParser.GetParser().Done();
1694         m_EditMenu->Enable(idMenuRenameSymbols, RenameEnable);
1695     }
1696 
1697     if (m_SearchMenu)
1698     {
1699         m_SearchMenu->Enable(idMenuGotoFunction,       HasEd);
1700         m_SearchMenu->Enable(idMenuGotoPrevFunction,   HasEd);
1701         m_SearchMenu->Enable(idMenuGotoNextFunction,   HasEd);
1702 
1703         const bool GotoEnable = HasNameUnderCursor && !IsInclude;
1704         m_SearchMenu->Enable(idMenuGotoDeclaration,    GotoEnable);
1705         m_SearchMenu->Enable(idMenuGotoImplementation, GotoEnable);
1706         const bool FindEnable = HasNameUnderCursor && !IsInclude && m_NativeParser.GetParser().Done();
1707         m_SearchMenu->Enable(idMenuFindReferences, FindEnable);
1708         const bool IncludeEnable = HasNameUnderCursor && IsInclude;
1709         m_SearchMenu->Enable(idMenuOpenIncludeFile, IncludeEnable);
1710     }
1711 
1712     if (m_ViewMenu)
1713     {
1714         bool isVis = IsWindowReallyShown((wxWindow*)m_NativeParser.GetClassBrowser());
1715         m_ViewMenu->Check(idViewClassBrowser, isVis);
1716     }
1717 
1718     if (m_ProjectMenu)
1719     {
1720         cbProject* project = m_NativeParser.GetCurrentProject();
1721         m_ProjectMenu->Enable(idCurrentProjectReparse, project);
1722     }
1723 
1724     // must do...
1725     event.Skip();
1726 }
1727 
1728 void CodeCompletion::OnViewClassBrowser(wxCommandEvent& event)
1729 {
1730 #if wxCHECK_VERSION(3, 0, 0)
1731     (void)event;
1732     cbMessageBox(_("The symbols browser is disabled in wx3.x builds.\n"
1733                     "We've done this because it causes crashes."), _("Information"), wxICON_INFORMATION);
1734     return;
1735 #else
1736     if (!Manager::Get()->GetConfigManager(_T("code_completion"))->ReadBool(_T("/use_symbols_browser"), true))
1737     {
1738         cbMessageBox(_("The symbols browser is disabled in code-completion options.\n"
1739                         "Please enable it there first..."), _("Information"), wxICON_INFORMATION);
1740         return;
1741     }
1742     CodeBlocksDockEvent evt(event.IsChecked() ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW);
1743     evt.pWindow = (wxWindow*)m_NativeParser.GetClassBrowser();
1744     Manager::Get()->ProcessEvent(evt);
1745 #endif // wxCHECK_VERSION
1746 }
1747 
1748 void CodeCompletion::OnGotoFunction(cb_unused wxCommandEvent& event)
1749 {
1750     EditorManager* edMan = Manager::Get()->GetEditorManager();
1751     cbEditor* ed = edMan->GetBuiltinActiveEditor();
1752     if (!ed)
1753         return;
1754 
1755     TRACE(_T("OnGotoFunction"));
1756 
1757     m_NativeParser.GetParser().ParseBufferForFunctions(ed->GetControl()->GetText());
1758 
1759 
1760     TokenTree* tree = m_NativeParser.GetParser().GetTempTokenTree();
1761 
1762     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1763 
1764     if (tree->empty())
1765     {
1766         cbMessageBox(_("No functions parsed in this file..."));
1767         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1768     }
1769     else
1770     {
1771         GotoFunctionDlg::Iterator iterator;
1772 
1773         for (size_t i = 0; i < tree->size(); i++)
1774         {
1775             Token* token = tree->at(i);
1776             if (token && token->m_TokenKind & tkAnyFunction)
1777             {
1778                 GotoFunctionDlg::FunctionToken ft;
1779                 // We need to clone the internal data of the strings to make them thread safe.
1780                 ft.displayName = wxString(token->DisplayName().c_str());
1781                 ft.name = wxString(token->m_Name.c_str());
1782                 ft.line = token->m_Line;
1783                 ft.implLine = token->m_ImplLine;
1784                 if (!token->m_FullType.empty())
1785                     ft.paramsAndreturnType = wxString((token->m_Args + wxT(" -> ") + token->m_FullType).c_str());
1786                 else
1787                     ft.paramsAndreturnType = wxString(token->m_Args.c_str());
1788                 ft.funcName = wxString((token->GetNamespace() + token->m_Name).c_str());
1789 
1790                 iterator.AddToken(ft);
1791             }
1792         }
1793 
1794         tree->clear();
1795 
1796         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
1797 
1798         iterator.Sort();
1799         GotoFunctionDlg dlg(Manager::Get()->GetAppWindow(), &iterator);
1800         PlaceWindow(&dlg);
1801         if (dlg.ShowModal() == wxID_OK)
1802         {
1803             int selection = dlg.GetSelection();
1804             if (selection != wxNOT_FOUND) {
1805                 const GotoFunctionDlg::FunctionToken *ft = iterator.GetToken(selection);
1806                 if (ed && ft)
1807                 {
1808                     TRACE(F(_T("OnGotoFunction() : Token '%s' found at line %u."), ft->name.wx_str(), ft->line));
1809                     ed->GotoTokenPosition(ft->implLine - 1, ft->name);
1810                 }
1811             }
1812         }
1813     }
1814 }
1815 
1816 void CodeCompletion::OnGotoPrevFunction(cb_unused wxCommandEvent& event)
1817 {
1818     GotoFunctionPrevNext(); // prev function
1819 }
1820 
1821 void CodeCompletion::OnGotoNextFunction(cb_unused wxCommandEvent& event)
1822 {
1823     GotoFunctionPrevNext(true); // next function
1824 }
1825 
1826 void CodeCompletion::OnClassMethod(cb_unused wxCommandEvent& event)
1827 {
1828     DoClassMethodDeclImpl();
1829 }
1830 
1831 void CodeCompletion::OnUnimplementedClassMethods(cb_unused wxCommandEvent& event)
1832 {
1833     DoAllMethodsImpl();
1834 }
1835 
1836 void CodeCompletion::OnGotoDeclaration(wxCommandEvent& event)
1837 {
1838     EditorManager* edMan  = Manager::Get()->GetEditorManager();
1839     cbEditor*      editor = edMan->GetBuiltinActiveEditor();
1840     if (!editor)
1841         return;
1842 
1843     TRACE(_T("OnGotoDeclaration"));
1844 
1845     const int pos      = editor->GetControl()->GetCurrentPos();
1846     const int startPos = editor->GetControl()->WordStartPosition(pos, true);
1847     const int endPos   = editor->GetControl()->WordEndPosition(pos, true);
1848     wxString target;
1849     // if there is a tilde "~", the token can either be a destructor or an Bitwise NOT (One's
1850     // Complement) operator
1851     bool isDestructor = false;
1852     if (CodeCompletionHelper::GetLastNonWhitespaceChar(editor->GetControl(), startPos) == _T('~'))
1853         isDestructor = true;
1854     target << editor->GetControl()->GetTextRange(startPos, endPos);
1855     if (target.IsEmpty())
1856         return;
1857 
1858     // prepare a boolean filter for declaration/implementation
1859     bool isDecl = event.GetId() == idGotoDeclaration    || event.GetId() == idMenuGotoDeclaration;
1860     bool isImpl = event.GetId() == idGotoImplementation || event.GetId() == idMenuGotoImplementation;
1861 
1862     // get the matching set
1863     TokenIdxSet result;
1864     m_NativeParser.MarkItemsByAI(result, true, false, true, endPos);
1865 
1866     TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
1867 
1868     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
1869 
1870     // handle destructor function, first we try to see if it is a destructor, we simply do a semantic
1871     // check of the token under cursor, otherwise, it is a variable.
1872     if (isDestructor)
1873     {
1874         TokenIdxSet tmp = result;
1875         result.clear();
1876 
1877         for (TokenIdxSet::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
1878         {
1879             const Token* token = tree->at(*it);
1880             if (token && token->m_TokenKind == tkClass)
1881             {
1882                 token = tree->at(tree->TokenExists(_T("~") + target, token->m_Index, tkDestructor));
1883                 if (token)
1884                     result.insert(token->m_Index);
1885             }
1886         }
1887 
1888         // no destructor found, this could be a variable.
1889         if (result.empty())
1890             result = tmp;
1891     }
1892     else // handle constructor and functions
1893     {
1894         // AAA * p = new AAA();
1895         // ^^^--------------------------case1:go to class definition kind tokens
1896         //               ^^^------------case2:go to function kind tokens such as constructors
1897         // if a token is followed by a '(', it is regarded as a function
1898         const bool isFunction = CodeCompletionHelper::GetNextNonWhitespaceChar(editor->GetControl(), endPos)   == _T('(');
1899         // copy the token index set for a fall back case later
1900         TokenIdxSet savedResult = result;
1901         // loop the result, and strip unrelated tokens
1902         for (TokenIdxSet::const_iterator it = result.begin(); it != result.end();)
1903         {
1904             const Token* token = tree->at(*it);
1905             if (isFunction && token && token->m_TokenKind == tkClass)
1906                 result.erase(it++); // a class kind token is removed since we need a function
1907             else if (!isFunction && token && token->m_TokenKind == tkConstructor)
1908                 result.erase(it++); // a constructor token is removed since we don't need a function
1909             else
1910                 ++it;
1911         }
1912         // fall back: restore the saved result in a special case that a class doesn't have a constructor
1913         // defined (implicitly defined by compiler)
1914         // E.g. hover on case2 go to class AAA token since no AAA::AAA(); is defined or declared.
1915         if (!result.size())
1916             result = savedResult;
1917     }
1918 
1919     // handle function overloading
1920     if (result.size() > 1)
1921     {
1922         const size_t curLine = editor->GetControl()->GetCurrentLine() + 1;
1923         for (TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it)
1924         {
1925             const Token* token = tree->at(*it);
1926             if (token && (token->m_Line == curLine || token->m_ImplLine == curLine) )
1927             {
1928                 const int theOnlyOne = *it;
1929                 result.clear();
1930                 result.insert(theOnlyOne);
1931                 break;
1932             }
1933         }
1934     }
1935 
1936     // data for the choice dialog
1937     std::deque<CodeCompletionHelper::GotoDeclarationItem> foundItems;
1938     wxArrayString selections;
1939 
1940     wxString editorFilename;
1941     unsigned editorLine = -1;
1942     bool     tokenFound = false;
1943 
1944     // one match
1945     if (result.size() == 1)
1946     {
1947         Token* token = NULL;
1948         Token* sel = tree->at(*(result.begin()));
1949         if (   (isImpl && !sel->GetImplFilename().IsEmpty())
1950             || (isDecl && !sel->GetFilename().IsEmpty()) )
1951         {
1952             token = sel;
1953         }
1954         if (token)
1955         {
1956             // FIXME: implement this correctly, because now it is not showing full results
1957             if (   wxGetKeyState(WXK_CONTROL)
1958                 && wxGetKeyState(WXK_SHIFT)
1959                 && (  event.GetId() == idGotoDeclaration
1960                    || event.GetId() == idGotoImplementation ) )
1961             {
1962                 // FIXME: this  code can lead to a deadlock (because of double locking from single thread)
1963                 CCDebugInfo info(nullptr, &m_NativeParser.GetParser(), token);
1964                 info.ShowModal();
1965             }
1966             else if (isImpl)
1967             {
1968                 editorFilename = token->GetImplFilename();
1969                 editorLine     = token->m_ImplLine - 1;
1970             }
1971             else if (isDecl)
1972             {
1973                 editorFilename = token->GetFilename();
1974                 editorLine     = token->m_Line - 1;
1975             }
1976 
1977             tokenFound = true;
1978         }
1979     }
1980     // if more than one match, display a selection dialog
1981     else if (result.size() > 1)
1982     {
1983         // TODO: we could parse the line containing the text so
1984         // if namespaces were included, we could limit the results (and be more accurate)
1985         for (TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it)
1986         {
1987             const Token* token = tree->at(*it);
1988             if (token)
1989             {
1990                 CodeCompletionHelper::GotoDeclarationItem item;
1991 
1992                 if (isImpl)
1993                 {
1994                     item.filename = token->GetImplFilename();
1995                     item.line     = token->m_ImplLine - 1;
1996                 }
1997                 else if (isDecl)
1998                 {
1999                     item.filename = token->GetFilename();
2000                     item.line     = token->m_Line - 1;
2001                 }
2002 
2003                 // only match tokens that have filename info
2004                 if (!item.filename.empty())
2005                 {
2006                     selections.Add(token->DisplayName());
2007                     foundItems.push_back(item);
2008                 }
2009             }
2010         }
2011     }
2012 
2013     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
2014 
2015     if (selections.GetCount() > 1)
2016     {
2017         int sel = cbGetSingleChoiceIndex(_("Please make a selection:"), _("Multiple matches"), selections,
2018                                          Manager::Get()->GetAppWindow(), wxSize(400, 400));
2019         if (sel == -1)
2020             return;
2021 
2022         const CodeCompletionHelper::GotoDeclarationItem &item = foundItems[sel];
2023         editorFilename = item.filename;
2024         editorLine     = item.line;
2025         tokenFound     = true;
2026     }
2027     else if (selections.GetCount() == 1)
2028     {
2029         // number of selections can be < result.size() due to the if tests, so in case we fall
2030         // back on 1 entry no need to show a selection
2031         const CodeCompletionHelper::GotoDeclarationItem &item = foundItems.front();
2032         editorFilename = item.filename;
2033         editorLine     = item.line;
2034         tokenFound     = true;
2035     }
2036 
2037     // do we have a token?
2038     if (tokenFound)
2039     {
2040         cbEditor* targetEditor = edMan->Open(editorFilename);
2041         if (targetEditor)
2042             targetEditor->GotoTokenPosition(editorLine, target);
2043         else
2044         {
2045             if (isImpl)
2046                 cbMessageBox(wxString::Format(_("Implementation not found: %s"), target.wx_str()),
2047                              _("Warning"), wxICON_WARNING);
2048             else if (isDecl)
2049                 cbMessageBox(wxString::Format(_("Declaration not found: %s"), target.wx_str()),
2050                              _("Warning"), wxICON_WARNING);
2051         }
2052     }
2053     else
2054         cbMessageBox(wxString::Format(_("Not found: %s"), target.wx_str()), _("Warning"), wxICON_WARNING);
2055 }
2056 
2057 void CodeCompletion::OnFindReferences(cb_unused wxCommandEvent& event)
2058 {
2059     m_CodeRefactoring.FindReferences();
2060 }
2061 
2062 void CodeCompletion::OnRenameSymbols(cb_unused wxCommandEvent& event)
2063 {
2064     m_CodeRefactoring.RenameSymbols();
2065 }
2066 
2067 void CodeCompletion::OnOpenIncludeFile(cb_unused wxCommandEvent& event)
2068 {
2069     wxString lastIncludeFileFrom;
2070     cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
2071     if (editor)
2072         lastIncludeFileFrom = editor->GetFilename();
2073 
2074     // check one more time because menu entries are enabled only when it makes sense
2075     // but the shortcut accelerator can always be executed
2076     bool MoveOn = false;
2077     wxString NameUnderCursor;
2078     bool IsInclude = false;
2079     if (CodeCompletionHelper::EditorHasNameUnderCursor(NameUnderCursor, IsInclude))
2080     {
2081         if (IsInclude)
2082             MoveOn = true;
2083     }
2084 
2085     if (!MoveOn)
2086         return; // nothing under cursor or thing under cursor is not an include
2087 
2088     TRACE(_T("OnOpenIncludeFile"));
2089 
2090     wxArrayString foundSet = m_NativeParser.GetParser().FindFileInIncludeDirs(NameUnderCursor); // search in all parser's include dirs
2091 
2092     // look in the same dir as the source file
2093     wxFileName fname = NameUnderCursor;
2094     wxFileName base = lastIncludeFileFrom;
2095     NormalizePath(fname, base.GetPath());
2096     if (wxFileExists(fname.GetFullPath()) )
2097         foundSet.Add(fname.GetFullPath());
2098 
2099     // search for the file in project files
2100     cbProject* project = m_NativeParser.GetProjectByEditor(editor);
2101     if (project)
2102     {
2103         for (FilesList::const_iterator it = project->GetFilesList().begin();
2104                                        it != project->GetFilesList().end(); ++it)
2105         {
2106             ProjectFile* pf = *it;
2107             if (!pf)
2108                 continue;
2109 
2110             if ( IsSuffixOfPath(NameUnderCursor, pf->file.GetFullPath()) )
2111                 foundSet.Add(pf->file.GetFullPath());
2112         }
2113     }
2114 
2115     // Remove duplicates
2116     for (int i = 0; i < (int)foundSet.Count() - 1; i++)
2117     {
2118         for (int j = i + 1; j < (int)foundSet.Count(); )
2119         {
2120             if (foundSet.Item(i) == foundSet.Item(j))
2121                 foundSet.RemoveAt(j);
2122             else
2123                 j++;
2124         }
2125     }
2126 
2127     wxString selectedFile;
2128     if (foundSet.GetCount() > 1)
2129     {    // more than 1 hit : let the user choose
2130         SelectIncludeFile Dialog(Manager::Get()->GetAppWindow());
2131         Dialog.AddListEntries(foundSet);
2132         PlaceWindow(&Dialog);
2133         if (Dialog.ShowModal() == wxID_OK)
2134             selectedFile = Dialog.GetIncludeFile();
2135         else
2136             return; // user cancelled the dialog...
2137     }
2138     else if (foundSet.GetCount() == 1)
2139         selectedFile = foundSet[0];
2140 
2141     if (!selectedFile.IsEmpty())
2142     {
2143         EditorManager* edMan = Manager::Get()->GetEditorManager();
2144         edMan->Open(selectedFile);
2145         return;
2146     }
2147 
2148     cbMessageBox(wxString::Format(_("Not found: %s"), NameUnderCursor.c_str()), _("Warning"), wxICON_WARNING);
2149 }
2150 
2151 void CodeCompletion::OnCurrentProjectReparse(wxCommandEvent& event)
2152 {
2153     m_NativeParser.ReparseCurrentProject();
2154     event.Skip();
2155 }
2156 
2157 void CodeCompletion::OnSelectedProjectReparse(wxCommandEvent& event)
2158 {
2159     m_NativeParser.ReparseSelectedProject();
2160     event.Skip();
2161 }
2162 
2163 void CodeCompletion::OnSelectedFileReparse(wxCommandEvent& event)
2164 {
2165     wxTreeCtrl* tree = Manager::Get()->GetProjectManager()->GetUI().GetTree();
2166     if (!tree)
2167         return;
2168 
2169     wxTreeItemId treeItem = Manager::Get()->GetProjectManager()->GetUI().GetTreeSelection();
2170     if (!treeItem.IsOk())
2171         return;
2172 
2173     const FileTreeData* data = static_cast<FileTreeData*>(tree->GetItemData(treeItem));
2174     if (!data)
2175         return;
2176 
2177     if (data->GetKind() == FileTreeData::ftdkFile)
2178     {
2179         cbProject* project = data->GetProject();
2180         ProjectFile* pf = data->GetProjectFile();
2181         if (pf && m_NativeParser.ReparseFile(project, pf->file.GetFullPath()))
2182         {
2183              CCLogger::Get()->DebugLog(_T("Reparsing the selected file ") +
2184                                        pf->file.GetFullPath());
2185         }
2186     }
2187 
2188     event.Skip();
2189 }
2190 
2191 void CodeCompletion::OnAppDoneStartup(CodeBlocksEvent& event)
2192 {
2193     if (!m_InitDone)
2194         DoParseOpenedProjectAndActiveEditor();
2195 
2196     event.Skip();
2197 }
2198 
2199 void CodeCompletion::OnWorkspaceChanged(CodeBlocksEvent& event)
2200 {
2201     // EVT_WORKSPACE_CHANGED is a powerful event, it's sent after any project
2202     // has finished loading or closing. It's the *LAST* event to be sent when
2203     // the workspace has been changed. So it's the ideal time to parse files
2204     // and update your widgets.
2205     if (IsAttached() && m_InitDone)
2206     {
2207         cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
2208         // if we receive a workspace changed event, but the project is NULL, this means two condition
2209         // could happen.
2210         // (1) the user try to close the application, so we don't need to update the UI here.
2211         // (2) the user just open a new project after cb started up
2212         if (project)
2213         {
2214             if (!m_NativeParser.GetParserByProject(project))
2215                 m_NativeParser.CreateParser(project);
2216 
2217             // Update the Function toolbar
2218             TRACE(_T("CodeCompletion::OnWorkspaceChanged: Starting m_TimerToolbar."));
2219             m_TimerToolbar.Start(TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT);
2220 
2221             // Update the class browser
2222             if (m_NativeParser.GetParser().ClassBrowserOptions().displayFilter == bdfProject)
2223                 m_NativeParser.UpdateClassBrowser();
2224         }
2225     }
2226     event.Skip();
2227 }
2228 
2229 void CodeCompletion::OnProjectActivated(CodeBlocksEvent& event)
2230 {
2231     // The Class browser shouldn't be updated if we're in the middle of loading/closing
2232     // a project/workspace, because the class browser would need to be updated again.
2233     // So we need to update it with the EVT_WORKSPACE_CHANGED event, which gets
2234     // triggered after everything's finished loading/closing.
2235     if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone)
2236     {
2237         cbProject* project = event.GetProject();
2238         if (project && !m_NativeParser.GetParserByProject(project) && project->GetFilesCount() > 0)
2239             m_NativeParser.CreateParser(project);
2240 
2241         if (m_NativeParser.GetParser().ClassBrowserOptions().displayFilter == bdfProject)
2242             m_NativeParser.UpdateClassBrowser();
2243     }
2244 
2245     m_NeedsBatchColour = true;
2246 
2247     event.Skip();
2248 }
2249 
2250 void CodeCompletion::OnProjectClosed(CodeBlocksEvent& event)
2251 {
2252     // After this, the Class Browser needs to be updated. It will happen
2253     // when we receive the next EVT_PROJECT_ACTIVATED event.
2254     if (IsAttached() && m_InitDone)
2255     {
2256         cbProject* project = event.GetProject();
2257         if (project && m_NativeParser.GetParserByProject(project))
2258         {
2259             // there may be some pending files to be reparsed in m_ReparsingMap
2260             // so just remove them
2261             ReparsingMap::iterator it = m_ReparsingMap.find(project);
2262             if (it != m_ReparsingMap.end())
2263                 m_ReparsingMap.erase(it);
2264 
2265             // remove the Parser instance associated with the project
2266             m_NativeParser.DeleteParser(project);
2267         }
2268     }
2269     event.Skip();
2270 }
2271 
2272 void CodeCompletion::OnProjectSaved(CodeBlocksEvent& event)
2273 {
2274     // reparse project (compiler search dirs might have changed)
2275     m_TimerProjectSaved.SetClientData(event.GetProject());
2276     // we need more time for waiting wxExecute in NativeParser::AddCompilerPredefinedMacros
2277     TRACE(_T("CodeCompletion::OnProjectSaved: Starting m_TimerProjectSaved."));
2278     m_TimerProjectSaved.Start(200, wxTIMER_ONE_SHOT);
2279 
2280     event.Skip();
2281 }
2282 
2283 void CodeCompletion::OnProjectFileAdded(CodeBlocksEvent& event)
2284 {
2285     if (IsAttached() && m_InitDone)
2286         m_NativeParser.AddFileToParser(event.GetProject(), event.GetString());
2287     event.Skip();
2288 }
2289 
2290 void CodeCompletion::OnProjectFileRemoved(CodeBlocksEvent& event)
2291 {
2292     if (IsAttached() && m_InitDone)
2293         m_NativeParser.RemoveFileFromParser(event.GetProject(), event.GetString());
2294     event.Skip();
2295 }
2296 
2297 void CodeCompletion::OnProjectFileChanged(CodeBlocksEvent& event)
2298 {
2299     if (IsAttached() && m_InitDone)
2300     {
2301         // TODO (Morten#5#) make sure the event.GetProject() is valid.
2302         cbProject* project = event.GetProject();
2303         wxString filename = event.GetString();
2304         if (!project)
2305             project = m_NativeParser.GetProjectByFilename(filename);
2306         if (project && m_NativeParser.ReparseFile(project, filename))
2307             CCLogger::Get()->DebugLog(_T("Reparsing when file changed: ") + filename);
2308     }
2309     event.Skip();
2310 }
2311 
2312 void CodeCompletion::OnEditorSave(CodeBlocksEvent& event)
2313 {
2314     if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone && event.GetEditor())
2315     {
2316         cbProject* project = event.GetProject();
2317 
2318         // we know which project the editor belongs to, so put a (project, file) pair to the
2319         // m_ReparsingMap
2320         ReparsingMap::iterator it = m_ReparsingMap.find(project);
2321         if (it == m_ReparsingMap.end())
2322             it = m_ReparsingMap.insert(std::make_pair(project, wxArrayString())).first;
2323 
2324         const wxString& filename = event.GetEditor()->GetFilename();
2325         if (it->second.Index(filename) == wxNOT_FOUND)
2326             it->second.Add(filename);
2327 
2328         // start the timer, so that it will be handled in timer event handler
2329         TRACE(_T("CodeCompletion::OnEditorSave: Starting m_TimerReparsing."));
2330         m_TimerReparsing.Start(EDITOR_ACTIVATED_DELAY + it->second.GetCount() * 10, wxTIMER_ONE_SHOT);
2331     }
2332 
2333     event.Skip();
2334 }
2335 
2336 void CodeCompletion::OnEditorOpen(CodeBlocksEvent& event)
2337 {
2338     if (!Manager::IsAppShuttingDown() && IsAttached() && m_InitDone)
2339     {
2340         cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinEditor(event.GetEditor());
2341         if (ed)
2342         {
2343             FunctionsScopePerFile* funcdata = &(m_AllFunctionsScopes[ed->GetFilename()]);
2344             funcdata->parsed = false;
2345         }
2346     }
2347 
2348     event.Skip();
2349 }
2350 
2351 void CodeCompletion::OnEditorActivated(CodeBlocksEvent& event)
2352 {
2353     TRACE(_T("CodeCompletion::OnEditorActivated(): Enter"));
2354 
2355     if (!ProjectManager::IsBusy() && IsAttached() && m_InitDone && event.GetEditor())
2356     {
2357         m_LastEditor = Manager::Get()->GetEditorManager()->GetBuiltinEditor(event.GetEditor());
2358 
2359         TRACE(_T("CodeCompletion::OnEditorActivated(): Starting m_TimerEditorActivated."));
2360         m_TimerEditorActivated.Start(EDITOR_ACTIVATED_DELAY, wxTIMER_ONE_SHOT);
2361 
2362         if (m_TimerToolbar.IsRunning())
2363             m_TimerToolbar.Stop();
2364     }
2365 
2366     event.Skip();
2367     TRACE(_T("CodeCompletion::OnEditorActivated(): Leave"));
2368 }
2369 
2370 void CodeCompletion::OnEditorClosed(CodeBlocksEvent& event)
2371 {
2372     EditorManager* edm = Manager::Get()->GetEditorManager();
2373     if (!edm)
2374     {
2375         event.Skip();
2376         return;
2377     }
2378 
2379     wxString activeFile;
2380     EditorBase* eb = edm->GetActiveEditor();
2381     if (eb)
2382         activeFile = eb->GetFilename();
2383 
2384     TRACE(_T("CodeCompletion::OnEditorClosed(): Closed editor's file is %s"), activeFile.wx_str());
2385 
2386     if (m_LastEditor == event.GetEditor())
2387     {
2388         m_LastEditor = nullptr;
2389         if (m_TimerEditorActivated.IsRunning())
2390             m_TimerEditorActivated.Stop();
2391     }
2392 
2393     // tell m_NativeParser that a builtin editor was closed
2394     if ( edm->GetBuiltinEditor(event.GetEditor()) )
2395         m_NativeParser.OnEditorClosed(event.GetEditor());
2396 
2397     m_LastFile.Clear();
2398 
2399     // we need to clear CC toolbar only when we are closing last editor
2400     // in other situations OnEditorActivated does this job
2401     // If no editors were opened, or a non-buildin-editor was active, disable the CC toolbar
2402     if (edm->GetEditorsCount() == 0 || !edm->GetActiveEditor() || !edm->GetActiveEditor()->IsBuiltinEditor())
2403     {
2404         EnableToolbarTools(false);
2405 
2406         // clear toolbar when closing last editor
2407         if (m_Scope)
2408             m_Scope->Clear();
2409         if (m_Function)
2410             m_Function->Clear();
2411 
2412         cbEditor* ed = edm->GetBuiltinEditor(event.GetEditor());
2413         wxString filename;
2414         if (ed)
2415             filename = ed->GetFilename();
2416 
2417         m_AllFunctionsScopes[filename].m_FunctionsScope.clear();
2418         m_AllFunctionsScopes[filename].m_NameSpaces.clear();
2419         m_AllFunctionsScopes[filename].parsed = false;
2420         if (m_NativeParser.GetParser().ClassBrowserOptions().displayFilter == bdfFile)
2421             m_NativeParser.UpdateClassBrowser();
2422     }
2423 
2424     event.Skip();
2425 }
2426 
2427 void CodeCompletion::OnCCLogger(CodeBlocksThreadEvent& event)
2428 {
2429     if (!Manager::IsAppShuttingDown())
2430         Manager::Get()->GetLogManager()->Log(event.GetString());
2431 }
2432 
2433 void CodeCompletion::OnCCDebugLogger(CodeBlocksThreadEvent& event)
2434 {
2435     if (!Manager::IsAppShuttingDown())
2436         Manager::Get()->GetLogManager()->DebugLog(event.GetString());
2437 }
2438 
2439 void CodeCompletion::OnParserStart(wxCommandEvent& event)
2440 {
2441     cbProject*                project = static_cast<cbProject*>(event.GetClientData());
2442     ParserCommon::ParserState state   = static_cast<ParserCommon::ParserState>(event.GetInt());
2443     // Parser::OnBatchTimer will send this Parser Start event
2444     // If it starts a full parsing(ptCreateParser), we should prepare some data for the header
2445     // file crawler
2446     if (state == ParserCommon::ptCreateParser)
2447     {
2448         if (m_CCEnableHeaders)
2449         {
2450             wxArrayString &dirs = GetSystemIncludeDirs(project, true); // true means update the cache
2451             if (!dirs.empty())
2452             {
2453                 SystemHeadersThread* thread = new SystemHeadersThread(this,
2454                                                                       &m_SystemHeadersThreadCS,
2455                                                                       m_SystemHeadersMap, dirs);
2456                 m_SystemHeadersThreads.push_back(thread);
2457                 thread->Run();
2458             }
2459         }
2460 
2461         cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
2462         if (m_NativeParser.GetProjectByEditor(editor) == project)
2463             EnableToolbarTools(false);
2464     }
2465 }
2466 
2467 void CodeCompletion::OnParserEnd(wxCommandEvent& event)
2468 {
2469     EditorManager* edMan = Manager::Get()->GetEditorManager();
2470     cbEditor* editor = edMan->GetBuiltinActiveEditor();
2471     if (editor)
2472     {
2473         m_ToolbarNeedReparse = true;
2474         TRACE(_T("CodeCompletion::OnParserEnd: Starting m_TimerToolbar."));
2475         m_TimerToolbar.Start(TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT);
2476     }
2477 
2478     if (m_NeedsBatchColour)
2479     {
2480         for (int edIdx = edMan->GetEditorsCount() - 1; edIdx >= 0; --edIdx)
2481         {
2482             editor = edMan->GetBuiltinEditor(edIdx);
2483             if (editor)
2484                 UpdateEditorSyntax(editor);
2485         }
2486         m_NeedsBatchColour = false;
2487     }
2488 
2489     event.Skip();
2490 }
2491 
2492 void CodeCompletion::OnSystemHeadersThreadMessage(CodeBlocksThreadEvent& event)
2493 {
2494     CCLogger::Get()->DebugLog(event.GetString());
2495 }
2496 
2497 void CodeCompletion::OnSystemHeadersThreadFinish(CodeBlocksThreadEvent& event)
2498 {
2499     if (m_SystemHeadersThreads.empty())
2500         return;
2501     // Wait for the current thread to finish and remove it from the thread list.
2502     SystemHeadersThread* thread = static_cast<SystemHeadersThread*>(event.GetClientData());
2503 
2504     for (std::list<SystemHeadersThread*>::iterator it = m_SystemHeadersThreads.begin();
2505          it != m_SystemHeadersThreads.end();
2506          ++it)
2507     {
2508         if (*it == thread)
2509         {
2510             if (!event.GetString().IsEmpty())
2511                 CCLogger::Get()->DebugLog(event.GetString());
2512             thread->Wait();
2513             delete thread;
2514             m_SystemHeadersThreads.erase(it);
2515             break;
2516         }
2517     }
2518 }
2519 
2520 int CodeCompletion::DoClassMethodDeclImpl()
2521 {
2522     if (!IsAttached() || !m_InitDone)
2523         return -1;
2524 
2525     EditorManager* edMan = Manager::Get()->GetEditorManager();
2526     cbEditor* ed = edMan->GetBuiltinActiveEditor();
2527     if (!ed)
2528         return -3;
2529 
2530     FileType ft = FileTypeOf(ed->GetShortName());
2531     if ( ft != ftHeader && ft != ftSource && ft != ftTemplateSource) // only parse source/header files
2532         return -4;
2533 
2534     if (!m_NativeParser.GetParser().Done())
2535     {
2536         wxString msg = _("The Parser is still parsing files.");
2537         msg += m_NativeParser.GetParser().NotDoneReason();
2538         CCLogger::Get()->DebugLog(msg);
2539         return -5;
2540     }
2541 
2542     int success = -6;
2543 
2544 //    TokenTree* tree = m_NativeParser.GetParser().GetTokenTree(); // The one used inside InsertClassMethodDlg
2545 
2546     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
2547 
2548     // open the insert class dialog
2549     wxString filename = ed->GetFilename();
2550     InsertClassMethodDlg dlg(Manager::Get()->GetAppWindow(), &m_NativeParser.GetParser(), filename);
2551     PlaceWindow(&dlg);
2552     if (dlg.ShowModal() == wxID_OK)
2553     {
2554         cbStyledTextCtrl* control = ed->GetControl();
2555         int pos = control->GetCurrentPos();
2556         int line = control->LineFromPosition(pos);
2557         control->GotoPos(control->PositionFromLine(line));
2558 
2559         wxArrayString result = dlg.GetCode();
2560         for (unsigned int i = 0; i < result.GetCount(); ++i)
2561         {
2562             pos = control->GetCurrentPos();
2563             line = control->LineFromPosition(pos);
2564             // get the indent string from previous line
2565             wxString str = ed->GetLineIndentString(line - 1) + result[i];
2566             MatchCodeStyle(str, control->GetEOLMode(), ed->GetLineIndentString(line - 1), control->GetUseTabs(), control->GetTabWidth());
2567             control->SetTargetStart(pos);
2568             control->SetTargetEnd(pos);
2569             control->ReplaceTarget(str);
2570             control->GotoPos(pos + str.Length());// - 3);
2571         }
2572         success = 0;
2573     }
2574 
2575     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
2576 
2577     return success;
2578 }
2579 
2580 int CodeCompletion::DoAllMethodsImpl()
2581 {
2582     if (!IsAttached() || !m_InitDone)
2583         return -1;
2584 
2585     EditorManager* edMan = Manager::Get()->GetEditorManager();
2586     cbEditor* ed = edMan->GetBuiltinActiveEditor();
2587     if (!ed)
2588         return -3;
2589 
2590     FileType ft = FileTypeOf(ed->GetShortName());
2591     if ( ft != ftHeader && ft != ftSource && ft != ftTemplateSource) // only parse source/header files
2592         return -4;
2593 
2594     wxArrayString paths = m_NativeParser.GetAllPathsByFilename(ed->GetFilename());
2595     TokenTree*    tree  = m_NativeParser.GetParser().GetTokenTree();
2596 
2597     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
2598 
2599     // get all filenames' indices matching our mask
2600     TokenFileSet result;
2601     for (size_t i = 0; i < paths.GetCount(); ++i)
2602     {
2603         CCLogger::Get()->DebugLog(_T("CodeCompletion::DoAllMethodsImpl(): Trying to find matches for: ") + paths[i]);
2604         TokenFileSet result_file;
2605         tree->GetFileMatches(paths[i], result_file, true, true);
2606         for (TokenFileSet::const_iterator it = result_file.begin(); it != result_file.end(); ++it)
2607             result.insert(*it);
2608     }
2609 
2610     if (result.empty())
2611     {
2612         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
2613 
2614         cbMessageBox(_("Could not find any file match in parser's database."), _("Warning"), wxICON_WARNING);
2615         return -5;
2616     }
2617 
2618     // loop matching files, loop tokens in file and get list of un-implemented functions
2619     wxArrayString arr; // for selection (keeps strings)
2620     wxArrayInt arrint; // for selection (keeps indices)
2621     typedef std::map<int, std::pair<int, wxString> > ImplMap;
2622     ImplMap im;
2623     for (TokenFileSet::const_iterator itf = result.begin(); itf != result.end(); ++itf)
2624     {
2625         const TokenIdxSet* tokens = tree->GetTokensBelongToFile(*itf);
2626         if (!tokens) continue;
2627 
2628         // loop tokens in file
2629         for (TokenIdxSet::const_iterator its = tokens->begin(); its != tokens->end(); ++its)
2630         {
2631             const Token* token = tree->at(*its);
2632             if (   token // valid token
2633                 && (token->m_TokenKind & (tkFunction | tkConstructor | tkDestructor)) // is method
2634                 && token->m_ImplLine == 0 ) // is un-implemented
2635             {
2636                 im[token->m_Line] = std::make_pair(*its, token->DisplayName());
2637             }
2638         }
2639     }
2640 
2641     for (ImplMap::const_iterator it = im.begin(); it != im.end(); ++it)
2642     {
2643         arrint.Add(it->second.first);
2644         arr.Add(it->second.second);
2645     }
2646 
2647     if (arr.empty())
2648     {
2649         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
2650 
2651         cbMessageBox(_("No classes declared or no un-implemented class methods found."), _("Warning"), wxICON_WARNING);
2652         return -5;
2653     }
2654 
2655     int success = -5;
2656 
2657     // select tokens
2658     MultiSelectDlg dlg(Manager::Get()->GetAppWindow(), arr, true);
2659     if (dlg.ShowModal() == wxID_OK)
2660     {
2661         cbStyledTextCtrl* control = ed->GetControl();
2662         int pos = control->GetCurrentPos();
2663         int line = control->LineFromPosition(pos);
2664         control->GotoPos(control->PositionFromLine(line));
2665 
2666         bool addDoxgenComment = Manager::Get()->GetConfigManager(_T("code_completion"))->ReadBool(_T("/add_doxgen_comment"), false);
2667 
2668         wxArrayInt indices = dlg.GetSelectedIndices();
2669         for (size_t i = 0; i < indices.GetCount(); ++i)
2670         {
2671             const Token* token = tree->at(arrint[indices[i]]);
2672             if (!token)
2673                 continue;
2674 
2675             pos  = control->GetCurrentPos();
2676             line = control->LineFromPosition(pos);
2677 
2678             // actual code generation
2679             wxString str;
2680             if (i > 0)
2681                 str << _T("\n");
2682             else
2683                 str << ed->GetLineIndentString(line - 1);
2684             if (addDoxgenComment)
2685                 str << _T("/** @brief ") << token->m_Name << _T("\n  *\n  * @todo: document this function\n  */\n");
2686             wxString type = token->m_FullType;
2687             if (!type.IsEmpty())
2688             {
2689                 // "int *" or "int &" ->  "int*" or "int&"
2690                 if (   (type.Last() == _T('&') || type.Last() == _T('*'))
2691                     && type[type.Len() - 2] == _T(' '))
2692                 {
2693                     type[type.Len() - 2] = type.Last();
2694                     type.RemoveLast();
2695                 }
2696                 str << type << _T(" ");
2697             }
2698             if (token->m_ParentIndex != -1)
2699             {
2700                 const Token* parent = tree->at(token->m_ParentIndex);
2701                 if (parent)
2702                     str << parent->m_Name << _T("::");
2703             }
2704             str << token->m_Name << token->GetStrippedArgs();
2705             if (token->m_IsConst)
2706                 str << _T(" const");
2707             if (token->m_IsNoExcept)
2708                 str << _T(" noexcept");
2709             str << _T("\n{\n\t\n}\n");
2710 
2711             MatchCodeStyle(str, control->GetEOLMode(), ed->GetLineIndentString(line - 1), control->GetUseTabs(), control->GetTabWidth());
2712 
2713             // add code in editor
2714             control->SetTargetStart(pos);
2715             control->SetTargetEnd(pos);
2716             control->ReplaceTarget(str);
2717             control->GotoPos(pos + str.Length());
2718         }
2719         if (!indices.IsEmpty())
2720         {
2721             pos  = control->GetCurrentPos();
2722             line = control->LineFromPosition(pos);
2723             control->GotoPos(control->GetLineEndPosition(line - 2));
2724         }
2725         success = 0;
2726     }
2727 
2728     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
2729 
2730     return success;
2731 }
2732 
2733 void CodeCompletion::MatchCodeStyle(wxString& str, int eolStyle, const wxString& indent, bool useTabs, int tabSize)
2734 {
2735     str.Replace(wxT("\n"), GetEOLStr(eolStyle) + indent);
2736     if (!useTabs)
2737         str.Replace(wxT("\t"), wxString(wxT(' '), tabSize));
2738     if (!indent.IsEmpty())
2739         str.RemoveLast(indent.Length());
2740 }
2741 
2742 // help method in finding the function position in the vector for the function containing the current line
2743 void CodeCompletion::FunctionPosition(int &scopeItem, int &functionItem) const
2744 {
2745     scopeItem = -1;
2746     functionItem = -1;
2747 
2748     for (unsigned int idxSc = 0; idxSc < m_ScopeMarks.size(); ++idxSc)
2749     {
2750         // this is the start and end of a scope
2751         unsigned int start = m_ScopeMarks[idxSc];
2752         unsigned int end = (idxSc + 1 < m_ScopeMarks.size()) ? m_ScopeMarks[idxSc + 1] : m_FunctionsScope.size();
2753 
2754         // the scope could have many functions, so loop on the functions
2755         for (int idxFn = 0; start + idxFn < end; ++idxFn)
2756         {
2757             const FunctionScope fs = m_FunctionsScope[start + idxFn];
2758             if (m_CurrentLine >= fs.StartLine && m_CurrentLine <= fs.EndLine)
2759             {
2760                 scopeItem = idxSc;
2761                 functionItem = idxFn;
2762             }
2763         }
2764     }
2765 }
2766 
2767 void CodeCompletion::GotoFunctionPrevNext(bool next /* = false */)
2768 {
2769     EditorManager* edMan = Manager::Get()->GetEditorManager();
2770     cbEditor* ed = edMan->GetBuiltinActiveEditor();
2771     if (!ed)
2772         return;
2773 
2774     int current_line = ed->GetControl()->GetCurrentLine();
2775 
2776     if (!m_FunctionsScope.size())
2777         return;
2778 
2779     // search previous/next function from current line, default: previous
2780     int          line            = -1;
2781     unsigned int best_func       = 0;
2782     bool         found_best_func = false;
2783     for (unsigned int idx_func=0; idx_func<m_FunctionsScope.size(); ++idx_func)
2784     {
2785         int best_func_line  = m_FunctionsScope[best_func].StartLine;
2786         int func_start_line = m_FunctionsScope[idx_func].StartLine;
2787         if (next)
2788         {
2789             if         (best_func_line  > current_line)     // candidate: is after current line
2790             {
2791                 if (   (func_start_line > current_line  )   // another candidate
2792                     && (func_start_line < best_func_line) ) // decide which is more near
2793                 { best_func = idx_func; found_best_func = true; }
2794             }
2795             else if    (func_start_line > current_line)     // candidate: is after current line
2796             {     best_func = idx_func; found_best_func = true; }
2797         }
2798         else // prev
2799         {
2800             if         (best_func_line  < current_line)     // candidate: is before current line
2801             {
2802                 if (   (func_start_line < current_line  )   // another candidate
2803                     && (func_start_line > best_func_line) ) // decide which is closer
2804                 { best_func = idx_func; found_best_func = true; }
2805             }
2806             else if    (func_start_line < current_line)     // candidate: is before current line
2807             {     best_func = idx_func; found_best_func = true; }
2808         }
2809     }
2810 
2811     if      (found_best_func)
2812     { line = m_FunctionsScope[best_func].StartLine; }
2813     else if ( next && m_FunctionsScope[best_func].StartLine>current_line)
2814     { line = m_FunctionsScope[best_func].StartLine; }
2815     else if (!next && m_FunctionsScope[best_func].StartLine<current_line)
2816     { line = m_FunctionsScope[best_func].StartLine; }
2817 
2818     if (line != -1)
2819     {
2820         ed->GotoLine(line);
2821         ed->SetFocus();
2822     }
2823 }
2824 
2825 // help method in finding the namespace position in the vector for the namespace containing the current line
2826 int CodeCompletion::NameSpacePosition() const
2827 {
2828     int pos = -1;
2829     int startLine = -1;
2830     for (unsigned int idxNs = 0; idxNs < m_NameSpaces.size(); ++idxNs)
2831     {
2832         const NameSpace& ns = m_NameSpaces[idxNs];
2833         if (m_CurrentLine >= ns.StartLine && m_CurrentLine <= ns.EndLine && ns.StartLine > startLine)
2834         {
2835             // got one, maybe there might be a better fitting namespace
2836             // (embedded namespaces) so keep on looking
2837             pos = static_cast<int>(idxNs);
2838             startLine = ns.StartLine;
2839         }
2840     }
2841 
2842     return pos;
2843 }
2844 
2845 void CodeCompletion::OnScope(wxCommandEvent&)
2846 {
2847     int sel = m_Scope->GetSelection();
2848     if (sel != -1 && sel < static_cast<int>(m_ScopeMarks.size()))
2849         UpdateFunctions(sel);
2850 }
2851 
2852 void CodeCompletion::OnFunction(cb_unused wxCommandEvent& event)
2853 {
2854     int selSc = (m_Scope) ? m_Scope->GetSelection() : 0;
2855     if (selSc != -1 && selSc < static_cast<int>(m_ScopeMarks.size()))
2856     {
2857         int idxFn = m_ScopeMarks[selSc] + m_Function->GetSelection();
2858         if (idxFn != -1 && idxFn < static_cast<int>(m_FunctionsScope.size()))
2859         {
2860             cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
2861             if (ed)
2862                 ed->GotoTokenPosition(m_FunctionsScope[idxFn].StartLine,
2863                                       m_FunctionsScope[idxFn].ShortName);
2864         }
2865     }
2866 }
2867 
2868 /** Here is the expansion of how the two wxChoices are constructed.
2869  * for a file have such contents below
2870  * @code{.cpp}
2871  *  Line  0     void g_func1(){
2872  *  Line  1     }
2873  *  Line  2
2874  *  Line  3     void ClassA::func1(){
2875  *  Line  4     }
2876  *  Line  5
2877  *  Line  6     void ClassA::func2(){
2878  *  Line  7     }
2879  *  Line  8
2880  *  Line  9     void ClassB::func1(){
2881  *  Line 10     }
2882  *  Line 11
2883  *  Line 12     void ClassB::func2(){
2884  *  Line 13     }
2885  *  Line 14
2886  *  Line 15     namespace NamespaceA{
2887  *  Line 16         void func3(){
2888  *  Line 17         }
2889  *  Line 18
2890  *  Line 19         class ClassC {
2891  *  Line 20
2892  *  Line 21             void func4(){
2893  *  Line 22             }
2894  *  Line 23         }
2895  *  Line 24     }
2896  *  Line 25
2897  *
2898  * @endcode
2899  *
2900  * The two key variable will be constructed like below
2901  * @code
2902  *  m_FunctionsScope is std::vector of length 9, capacity 9 =
2903  *  {
2904  *  {StartLine = 0, EndLine = 1, ShortName = L"g_func1", Name = L"g_func1() : void", Scope = L"<global>"},
2905  *  {StartLine = 3, EndLine = 4, ShortName = L"func1", Name = L"func1() : void", Scope = L"ClassA::"},
2906  *  {StartLine = 6, EndLine = 7, ShortName = L"func2", Name = L"func2() : void", Scope = L"ClassA::"},
2907  *  {StartLine = 9, EndLine = 10, ShortName = L"func1", Name = L"func1() : void", Scope = L"ClassB::"},
2908  *  {StartLine = 12, EndLine = 13, ShortName = L"func2", Name = L"func2() : void", Scope = L"ClassB::"},
2909  *  {StartLine = 14, EndLine = 23, ShortName = L"", Name = L"", Scope = L"NamespaceA::"},
2910  *  {StartLine = 16, EndLine = 17, ShortName = L"func3", Name = L"func3() : void", Scope = L"NamespaceA::"},
2911  *  {StartLine = 19, EndLine = 23, ShortName = L"", Name = L"", Scope = L"NamespaceA::ClassC::"},
2912  *  {StartLine = 21, EndLine = 22, ShortName = L"func4", Name = L"func4() : void", Scope = L"NamespaceA::ClassC::"}
2913  *  }
2914  *
2915  *  m_NameSpaces is std::vector of length 1, capacity 1 =
2916  *  {{Name = L"NamespaceA::", StartLine = 14, EndLine = 23}}
2917  *
2918  *  m_ScopeMarks is std::vector of length 5, capacity 8 = {0, 1, 3, 5, 7}
2919  * which is the start of Scope "<global>", Scope "ClassA::" and Scope "ClassB::",
2920  * "NamespaceA::" and "NamespaceA::ClassC::"
2921  * @endcode
2922  *
2923  * Then we have wxChoice Scopes and Functions like below
2924  * @code
2925  *      <global>          ClassA::        ClassB::
2926  *        |- g_func1()      |- func1()      |- func1()
2927  *                          |- func2()      |- func2()
2928  *
2929  *      NamespaceA::      NamespaceA::ClassC::
2930  *        |- func3()        |- func4()
2931  * @endcode
2932  */
2933 void CodeCompletion::ParseFunctionsAndFillToolbar()
2934 {
2935     TRACE(_T("ParseFunctionsAndFillToolbar() : m_ToolbarNeedReparse=%d, m_ToolbarNeedRefresh=%d, "),
2936           m_ToolbarNeedReparse?1:0, m_ToolbarNeedRefresh?1:0);
2937     EditorManager* edMan = Manager::Get()->GetEditorManager();
2938     if (!edMan) // Closing the app?
2939         return;
2940 
2941     cbEditor* ed = edMan->GetBuiltinActiveEditor();
2942     if (!ed || !ed->GetControl())
2943     {
2944         if (m_Function)
2945             m_Function->Clear();
2946         if (m_Scope)
2947             m_Scope->Clear();
2948 
2949         EnableToolbarTools(false);
2950         m_LastFile.Clear();
2951         return;
2952     }
2953 
2954     const wxString filename = ed->GetFilename();
2955     if (filename.IsEmpty())
2956         return;
2957 
2958     bool fileParseFinished = m_NativeParser.GetParser().IsFileParsed(filename);
2959 
2960     // FunctionsScopePerFile contains all the function and namespace information for
2961     // a specified file, m_AllFunctionsScopes[filename] will implicitly insert an new element in
2962     // the map if no such key(filename) is found.
2963     FunctionsScopePerFile* funcdata = &(m_AllFunctionsScopes[filename]);
2964 
2965     // *** Part 1: Parse the file (if needed) ***
2966     if (m_ToolbarNeedReparse || !funcdata->parsed)
2967     {
2968         if (m_ToolbarNeedReparse)
2969             m_ToolbarNeedReparse = false;
2970 
2971         funcdata->m_FunctionsScope.clear();
2972         funcdata->m_NameSpaces.clear();
2973 
2974         // collect the function implementation information, just find the specified tokens in the TokenTree
2975         TokenIdxSet result;
2976         m_NativeParser.GetParser().FindTokensInFile(filename, result,
2977                                                     tkAnyFunction | tkEnum | tkClass | tkNamespace);
2978         if (!result.empty())
2979             funcdata->parsed = true;    // if the file did have some containers, flag it as parsed
2980         else
2981             fileParseFinished = false;  // this indicates the batch parser does not finish parsing for the current file
2982 
2983         TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
2984 
2985         CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
2986 
2987         for (TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it)
2988         {
2989             const Token* token = tree->at(*it);
2990             if (token && token->m_ImplLine != 0)
2991             {
2992                 FunctionScope fs;
2993                 fs.StartLine = token->m_ImplLine    - 1;
2994                 fs.EndLine   = token->m_ImplLineEnd - 1;
2995                 const size_t fileIdx = tree->InsertFileOrGetIndex(filename);
2996                 if (token->m_TokenKind & tkAnyFunction && fileIdx == token->m_ImplFileIdx)
2997                 {
2998                     fs.Scope = token->GetNamespace();
2999                     if (fs.Scope.IsEmpty())
3000                         fs.Scope = g_GlobalScope;
3001                     wxString result_str = token->m_Name;
3002                     fs.ShortName = result_str;
3003                     result_str << token->GetFormattedArgs();
3004                     if (!token->m_BaseType.IsEmpty())
3005                         result_str << _T(" : ") << token->m_BaseType;
3006                     fs.Name = result_str;
3007                     funcdata->m_FunctionsScope.push_back(fs);
3008                 }
3009                 else if (token->m_TokenKind & (tkEnum | tkClass | tkNamespace))
3010                 {
3011                     fs.Scope = token->GetNamespace() + token->m_Name + _T("::");
3012                     funcdata->m_FunctionsScope.push_back(fs);
3013                 }
3014             }
3015         }
3016 
3017         CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
3018 
3019         FunctionsScopeVec& functionsScopes = funcdata->m_FunctionsScope;
3020         NameSpaceVec& nameSpaces = funcdata->m_NameSpaces;
3021 
3022         // collect the namespace information in the current file, this is done by running a parserthread
3023         // on the editor's buffer
3024         m_NativeParser.GetParser().ParseBufferForNamespaces(ed->GetControl()->GetText(), nameSpaces);
3025         std::sort(nameSpaces.begin(), nameSpaces.end(), CodeCompletionHelper::LessNameSpace);
3026 
3027         // copy the namespace information collected in ParseBufferForNamespaces() to
3028         // the functionsScopes, note that the element type FunctionScope has a constructor
3029         // FunctionScope(const NameSpace& ns), type conversion is done automatically
3030         std::copy(nameSpaces.begin(), nameSpaces.end(), back_inserter(functionsScopes));
3031         std::sort(functionsScopes.begin(), functionsScopes.end(), CodeCompletionHelper::LessFunctionScope);
3032 
3033         // remove consecutive duplicates
3034         FunctionsScopeVec::const_iterator it;
3035         it = unique(functionsScopes.begin(), functionsScopes.end(), CodeCompletionHelper::EqualFunctionScope);
3036         functionsScopes.resize(it - functionsScopes.begin());
3037 
3038         TRACE(F(_T("Found %lu namespace locations"), static_cast<unsigned long>(nameSpaces.size())));
3039 #if CC_CODECOMPLETION_DEBUG_OUTPUT == 1
3040         for (unsigned int i = 0; i < nameSpaces.size(); ++i)
3041             CCLogger::Get()->DebugLog(F(_T("\t%s (%d:%d)"),
3042                 nameSpaces[i].Name.wx_str(), nameSpaces[i].StartLine, nameSpaces[i].EndLine));
3043 #endif
3044 
3045         if (!m_ToolbarNeedRefresh)
3046             m_ToolbarNeedRefresh = true;
3047     }
3048 
3049     // *** Part 2: Fill the toolbar ***
3050     m_FunctionsScope = funcdata->m_FunctionsScope;
3051     m_NameSpaces     = funcdata->m_NameSpaces;
3052 
3053     m_ScopeMarks.clear();
3054     unsigned int fsSize = m_FunctionsScope.size();
3055     if (!m_FunctionsScope.empty())
3056     {
3057         m_ScopeMarks.push_back(0);
3058 
3059         if (m_Scope) // show scope wxChoice
3060         {
3061             wxString lastScope = m_FunctionsScope[0].Scope;
3062             for (unsigned int idx = 1; idx < fsSize; ++idx)
3063             {
3064                 const wxString& currentScope = m_FunctionsScope[idx].Scope;
3065 
3066                 // if the scope name has changed, push a new index
3067                 if (lastScope != currentScope)
3068                 {
3069                     m_ScopeMarks.push_back(idx);
3070                     lastScope = currentScope;
3071                 }
3072             }
3073         }
3074     }
3075 
3076     TRACE(F(_T("Parsed %lu functionscope items"), static_cast<unsigned long>(m_FunctionsScope.size())));
3077 #if CC_CODECOMPLETION_DEBUG_OUTPUT == 1
3078     for (unsigned int i = 0; i < m_FunctionsScope.size(); ++i)
3079         CCLogger::Get()->DebugLog(F(_T("\t%s%s (%d:%d)"),
3080             m_FunctionsScope[i].Scope.wx_str(), m_FunctionsScope[i].Name.wx_str(),
3081             m_FunctionsScope[i].StartLine, m_FunctionsScope[i].EndLine));
3082 #endif
3083 
3084     // Does the toolbar need a refresh?
3085     if (m_ToolbarNeedRefresh || m_LastFile != filename)
3086     {
3087         // Update the last editor and changed flag...
3088         if (m_ToolbarNeedRefresh)
3089             m_ToolbarNeedRefresh = false;
3090         if (m_LastFile != filename)
3091         {
3092             TRACE(_T("ParseFunctionsAndFillToolbar() : Update last file is %s"), filename.wx_str());
3093             m_LastFile = filename;
3094         }
3095 
3096         // ...and refresh the toolbars.
3097         m_Function->Clear();
3098 
3099         if (m_Scope)
3100         {
3101             m_Scope->Freeze();
3102             m_Scope->Clear();
3103 
3104             // add to the choice controls
3105             for (unsigned int idxSc = 0; idxSc < m_ScopeMarks.size(); ++idxSc)
3106             {
3107                 int idxFn = m_ScopeMarks[idxSc];
3108                 const FunctionScope& fs = m_FunctionsScope[idxFn];
3109                 m_Scope->Append(fs.Scope);
3110             }
3111 
3112             m_Scope->Thaw();
3113         }
3114         else
3115         {
3116             m_Function->Freeze();
3117 
3118             for (unsigned int idxFn = 0; idxFn < m_FunctionsScope.size(); ++idxFn)
3119             {
3120                 const FunctionScope& fs = m_FunctionsScope[idxFn];
3121                 if (fs.Name != wxEmptyString)
3122                     m_Function->Append(fs.Scope + fs.Name);
3123                 else if (fs.Scope.EndsWith(wxT("::")))
3124                     m_Function->Append(fs.Scope.substr(0, fs.Scope.length()-2));
3125                 else
3126                     m_Function->Append(fs.Scope);
3127             }
3128 
3129             m_Function->Thaw();
3130         }
3131     }
3132 
3133     // Find the current function and update
3134     FindFunctionAndUpdate(ed->GetControl()->GetCurrentLine());
3135 
3136     // Control the toolbar state, if the batch parser does not finish parsing the file, no need to update CC toolbar.
3137     EnableToolbarTools(fileParseFinished);
3138 }
3139 
3140 void CodeCompletion::FindFunctionAndUpdate(int currentLine)
3141 {
3142     if (currentLine == -1)
3143         return;
3144 
3145     m_CurrentLine = currentLine;
3146 
3147     int selSc, selFn;
3148     FunctionPosition(selSc, selFn);
3149 
3150     if (m_Scope)
3151     {
3152         if (selSc != -1 && selSc != m_Scope->GetSelection())
3153         {
3154             m_Scope->SetSelection(selSc);
3155             UpdateFunctions(selSc);
3156         }
3157         else if (selSc == -1)
3158             m_Scope->SetSelection(-1);
3159     }
3160 
3161     if (selFn != -1 && selFn != m_Function->GetSelection())
3162         m_Function->SetSelection(selFn);
3163     else if (selFn == -1)
3164     {
3165         m_Function->SetSelection(-1);
3166 
3167         wxChoice* choice = (m_Scope) ? m_Scope : m_Function;
3168 
3169         int NsSel = NameSpacePosition();
3170         if (NsSel != -1)
3171             choice->SetStringSelection(m_NameSpaces[NsSel].Name);
3172         else if (!m_Scope)
3173             choice->SetSelection(-1);
3174         else
3175         {
3176             choice->SetStringSelection(g_GlobalScope);
3177             wxCommandEvent evt(wxEVT_COMMAND_CHOICE_SELECTED, XRCID("chcCodeCompletionScope"));
3178             wxPostEvent(this, evt);
3179         }
3180     }
3181 }
3182 
3183 void CodeCompletion::UpdateFunctions(unsigned int scopeItem)
3184 {
3185     m_Function->Freeze();
3186     m_Function->Clear();
3187 
3188     unsigned int idxEnd = (scopeItem + 1 < m_ScopeMarks.size()) ? m_ScopeMarks[scopeItem + 1] : m_FunctionsScope.size();
3189     for (unsigned int idxFn = m_ScopeMarks[scopeItem]; idxFn < idxEnd; ++idxFn)
3190     {
3191         const wxString &name = m_FunctionsScope[idxFn].Name;
3192         m_Function->Append(name);
3193     }
3194 
3195     m_Function->Thaw();
3196 }
3197 
3198 void CodeCompletion::EnableToolbarTools(bool enable)
3199 {
3200     if (m_Scope)
3201         m_Scope->Enable(enable);
3202     if (m_Function)
3203         m_Function->Enable(enable);
3204 }
3205 
3206 void CodeCompletion::DoParseOpenedProjectAndActiveEditor()
3207 {
3208     // Let the app startup before parsing
3209     // This is to prevent the Splash Screen from delaying so much. By adding
3210     // the timer, the splash screen is closed and Code::Blocks doesn't take
3211     // so long in starting.
3212     m_InitDone = true;
3213 
3214     // Dreaded DDE-open bug related: do not touch the following lines unless for a good reason
3215 
3216     // parse any projects opened through DDE or the command-line
3217     cbProject* curProject = Manager::Get()->GetProjectManager()->GetActiveProject();
3218     if (curProject && !m_NativeParser.GetParserByProject(curProject))
3219         m_NativeParser.CreateParser(curProject);
3220 
3221     // parse any files opened through DDE or the command-line
3222     EditorBase* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
3223     if (editor)
3224         m_NativeParser.OnEditorActivated(editor);
3225 }
3226 
3227 void CodeCompletion::UpdateEditorSyntax(cbEditor* ed)
3228 {
3229     if (!Manager::Get()->GetConfigManager(wxT("code_completion"))->ReadBool(wxT("/semantic_keywords"), false))
3230         return;
3231     if (!ed)
3232         ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
3233     if (!ed || ed->GetControl()->GetLexer() != wxSCI_LEX_CPP)
3234         return;
3235 
3236     TokenIdxSet result;
3237     int flags = tkAnyContainer | tkAnyFunction;
3238     if (ed->GetFilename().EndsWith(wxT(".c")))
3239         flags |= tkVariable;
3240     m_NativeParser.GetParser().FindTokensInFile(ed->GetFilename(), result, flags);
3241     TokenTree* tree = m_NativeParser.GetParser().GetTokenTree();
3242 
3243     std::set<wxString> varList;
3244     TokenIdxSet parsedTokens;
3245 
3246     CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
3247     for (TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it)
3248     {
3249         Token* token = tree->at(*it);
3250         if (!token)
3251             continue;
3252         if (token->m_TokenKind == tkVariable) // global var - only added in C
3253         {
3254             varList.insert(token->m_Name);
3255             continue;
3256         }
3257         else if (token->m_TokenKind & tkAnyFunction) // find parent class
3258         {
3259             if (token->m_ParentIndex == wxNOT_FOUND)
3260                 continue;
3261             else
3262                 token = tree->at(token->m_ParentIndex);
3263         }
3264         if (!token || parsedTokens.find(token->m_Index) != parsedTokens.end())
3265             continue; // no need to check the same token multiple times
3266         parsedTokens.insert(token->m_Index);
3267         for (TokenIdxSet::const_iterator chIt = token->m_Children.begin();
3268              chIt != token->m_Children.end(); ++chIt)
3269         {
3270             const Token* chToken = tree->at(*chIt);
3271             if (chToken && chToken->m_TokenKind == tkVariable)
3272             {
3273                 varList.insert(chToken->m_Name);
3274             }
3275         }
3276         // inherited members
3277         if (token->m_Ancestors.empty())
3278             tree->RecalcInheritanceChain(token);
3279         for (TokenIdxSet::const_iterator ancIt = token->m_Ancestors.begin();
3280              ancIt != token->m_Ancestors.end(); ++ancIt)
3281         {
3282             const Token* ancToken = tree->at(*ancIt);
3283             if (!ancToken || parsedTokens.find(ancToken->m_Index) != parsedTokens.end())
3284                 continue;
3285             for (TokenIdxSet::const_iterator chIt = ancToken->m_Children.begin();
3286                  chIt != ancToken->m_Children.end(); ++chIt)
3287             {
3288                 const Token* chToken = tree->at(*chIt);
3289                 if (   chToken && chToken->m_TokenKind == tkVariable
3290                     && chToken->m_Scope != tsPrivate) // cannot inherit these...
3291                 {
3292                     varList.insert(chToken->m_Name);
3293                 }
3294             }
3295         }
3296     }
3297     CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
3298 
3299     EditorColourSet* colour_set = Manager::Get()->GetEditorManager()->GetColourSet();
3300     if (!colour_set)
3301         return;
3302 
3303     wxString keywords = colour_set->GetKeywords(ed->GetLanguage(), 3);
3304     for (std::set<wxString>::const_iterator keyIt = varList.begin();
3305          keyIt != varList.end(); ++keyIt)
3306     {
3307         keywords += wxT(" ") + *keyIt;
3308     }
3309     ed->GetControl()->SetKeyWords(3, keywords);
3310     ed->GetControl()->Colourise(0, -1);
3311 }
3312 
3313 void CodeCompletion::OnToolbarTimer(cb_unused wxTimerEvent& event)
3314 {
3315     TRACE(_T("CodeCompletion::OnToolbarTimer(): Enter"));
3316 
3317     if (!ProjectManager::IsBusy())
3318         ParseFunctionsAndFillToolbar();
3319     else
3320     {
3321         TRACE(_T("CodeCompletion::OnToolbarTimer(): Starting m_TimerToolbar."));
3322         m_TimerToolbar.Start(TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT);
3323     }
3324 
3325     TRACE(_T("CodeCompletion::OnToolbarTimer(): Leave"));
3326 }
3327 
3328 void CodeCompletion::OnRealtimeParsingTimer(cb_unused wxTimerEvent& event)
3329 {
3330     cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
3331     if (!editor)
3332         return;
3333 
3334     TRACE(_T("OnRealtimeParsingTimer"));
3335 
3336     // the real time parsing timer event has arrived, but the document size has changed, in this
3337     // case, we should fire another timer event, and do the parsing job later
3338     const int curLen = editor->GetControl()->GetLength();
3339     if (curLen != m_CurrentLength)
3340     {
3341         m_CurrentLength = curLen;
3342         TRACE(_T("CodeCompletion::OnRealtimeParsingTimer: Starting m_TimerRealtimeParsing."));
3343         m_TimerRealtimeParsing.Start(REALTIME_PARSING_DELAY, wxTIMER_ONE_SHOT);
3344         return;
3345     }
3346 
3347     cbProject* project = m_NativeParser.GetProjectByEditor(editor);
3348     if (project && !project->GetFileByFilename(m_LastFile, false, true))
3349         return;
3350     if (m_NativeParser.ReparseFile(project, m_LastFile))
3351         CCLogger::Get()->DebugLog(_T("Reparsing when typing for editor ") + m_LastFile);
3352 }
3353 
3354 void CodeCompletion::OnProjectSavedTimer(cb_unused wxTimerEvent& event)
3355 {
3356     cbProject* project = static_cast<cbProject*>(m_TimerProjectSaved.GetClientData());
3357     m_TimerProjectSaved.SetClientData(NULL);
3358 
3359     ProjectsArray* projs = Manager::Get()->GetProjectManager()->GetProjects();
3360     if (projs->Index(project) == wxNOT_FOUND)
3361         return;
3362 
3363     if (IsAttached() && m_InitDone && project)
3364     {
3365         TRACE(_T("OnProjectSavedTimer"));
3366         if (project &&  m_NativeParser.GetParserByProject(project))
3367         {
3368             ReparsingMap::iterator it = m_ReparsingMap.find(project);
3369             if (it != m_ReparsingMap.end())
3370                 m_ReparsingMap.erase(it);
3371             if (m_NativeParser.DeleteParser(project))
3372             {
3373                 CCLogger::Get()->DebugLog(_T("Reparsing project."));
3374                 m_NativeParser.CreateParser(project);
3375             }
3376         }
3377     }
3378 }
3379 
3380 void CodeCompletion::OnReparsingTimer(cb_unused wxTimerEvent& event)
3381 {
3382     if (ProjectManager::IsBusy() || !IsAttached() || !m_InitDone)
3383     {
3384         m_ReparsingMap.clear();
3385         CCLogger::Get()->DebugLog(_T("Reparsing files failed!"));
3386         return;
3387     }
3388 
3389     TRACE(_T("OnReparsingTimer"));
3390 
3391     ReparsingMap::iterator it = m_ReparsingMap.begin();
3392     if (it != m_ReparsingMap.end() && m_NativeParser.Done())
3393     {
3394         cbProject* project = it->first;
3395         wxArrayString& files = it->second;
3396         if (!project)
3397             project = m_NativeParser.GetProjectByFilename(files[0]);
3398 
3399         if (project && Manager::Get()->GetProjectManager()->IsProjectStillOpen(project))
3400         {
3401             wxString curFile;
3402             EditorBase* editor = Manager::Get()->GetEditorManager()->GetActiveEditor();
3403             if (editor)
3404                 curFile = editor->GetFilename();
3405 
3406             size_t reparseCount = 0;
3407             while (!files.IsEmpty())
3408             {
3409                 if (m_NativeParser.ReparseFile(project, files.Last()))
3410                 {
3411                     ++reparseCount;
3412                     TRACE(_T("OnReparsingTimer: Reparsing file : ") + files.Last());
3413                     if (files.Last() == curFile)
3414                     {
3415                         m_ToolbarNeedReparse = true;
3416                         TRACE(_T("CodeCompletion::OnReparsingTimer: Starting m_TimerToolbar."));
3417                         m_TimerToolbar.Start(TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT);
3418                     }
3419                 }
3420 
3421                 files.RemoveAt(files.GetCount() - 1);
3422             }
3423 
3424             if (reparseCount)
3425                 CCLogger::Get()->DebugLog(F(_T("Re-parsed %lu files."), static_cast<unsigned long>(reparseCount)));
3426         }
3427 
3428         if (files.IsEmpty())
3429             m_ReparsingMap.erase(it);
3430     }
3431 
3432     if (!m_ReparsingMap.empty())
3433     {
3434         TRACE(_T("CodeCompletion::OnReparsingTimer: Starting m_TimerReparsing."));
3435         m_TimerReparsing.Start(EDITOR_ACTIVATED_DELAY, wxTIMER_ONE_SHOT);
3436     }
3437 }
3438 
3439 void CodeCompletion::OnEditorActivatedTimer(cb_unused wxTimerEvent& event)
3440 {
3441     // the m_LastEditor variable was updated in CodeCompletion::OnEditorActivated, after that,
3442     // the editor-activated-timer was started. So, here in the timer handler, we need to check
3443     // whether the saved editor and the current editor are the same, otherwise, no need to update
3444     // the toolbar, because there must be another editor activated before the timer hits.
3445     // Note: only the builtin editor was considered.
3446     EditorBase* editor  = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
3447     if (!editor || editor != m_LastEditor)
3448     {
3449         TRACE(_T("CodeCompletion::OnEditorActivatedTimer(): Not a builtin editor."));
3450         //m_LastEditor = nullptr;
3451         EnableToolbarTools(false);
3452         return;
3453     }
3454 
3455     const wxString& curFile = editor->GetFilename();
3456     // if the same file was activated, no need to update the toolbar
3457     if ( !m_LastFile.IsEmpty() && m_LastFile == curFile )
3458     {
3459         TRACE(_T("CodeCompletion::OnEditorActivatedTimer(): Same as the last activated file(%s)."), curFile.wx_str());
3460         return;
3461     }
3462 
3463     TRACE(_T("CodeCompletion::OnEditorActivatedTimer(): Need to notify NativeParser and Refresh toolbar."));
3464 
3465     m_NativeParser.OnEditorActivated(editor);
3466     TRACE(_T("CodeCompletion::OnEditorActivatedTimer: Starting m_TimerToolbar."));
3467     m_TimerToolbar.Start(TOOLBAR_REFRESH_DELAY, wxTIMER_ONE_SHOT);
3468     TRACE(_T("CodeCompletion::OnEditorActivatedTimer(): Current activated file is %s"), curFile.wx_str());
3469     UpdateEditorSyntax();
3470 }
3471 
3472 wxBitmap CodeCompletion::GetImage(ImageId::Id id, int fontSize)
3473 {
3474     const int size = cbFindMinSize16to64(fontSize);
3475     const ImageId key(id, size);
3476     ImagesMap::const_iterator it = m_images.find(key);
3477     if (it == m_images.end())
3478     {
3479         const wxString prefix = ConfigManager::GetDataFolder()
3480                               + wxString::Format(_T("/codecompletion.zip#zip:images/%dx%d/"), size,
3481                                                  size);
3482 
3483         wxString filename;
3484         switch (id)
3485         {
3486             case ImageId::HeaderFile:
3487                 filename = prefix + wxT("header.png");
3488                 break;
3489             case ImageId::KeywordCPP:
3490                 filename = prefix + wxT("keyword_cpp.png");
3491                 break;
3492             case ImageId::KeywordD:
3493                 filename = prefix + wxT("keyword_d.png");
3494                 break;
3495             case ImageId::Unknown:
3496                 filename = prefix + wxT("unknown.png");
3497                 break;
3498 
3499             case ImageId::Last:
3500             default:
3501                 ;
3502         }
3503 
3504         if (!filename.empty())
3505         {
3506             wxBitmap bitmap = cbLoadBitmap(filename);
3507             if (!bitmap.IsOk())
3508             {
3509                 const wxString msg = wxString::Format(_("Cannot load image: '%s'!"),
3510                                                       filename.wx_str());
3511                 Manager::Get()->GetLogManager()->LogError(msg);
3512             }
3513             m_images[key] = bitmap;
3514             return bitmap;
3515         }
3516         else
3517         {
3518             m_images[key] = wxNullBitmap;
3519             return wxNullBitmap;
3520         }
3521     }
3522     else
3523         return it->second;
3524 }
3525