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 
6 #ifndef NATIVEPARSER_H
7 #define NATIVEPARSER_H
8 
9 #include "nativeparser_base.h"
10 #include "parser/parser.h"
11 
12 #include <queue>
13 #include <map>
14 #include <memory>
15 #include <unordered_map>
16 
17 #include <wx/event.h>
18 
19 /** debug only variable, used to print the AI match related log message*/
20 extern bool s_DebugSmartSense;
21 
22 extern const int g_EditorActivatedDelay;
23 
24 // forward declaration
25 class cbEditor;
26 class EditorBase;
27 class cbProject;
28 class cbStyledTextCtrl;
29 class ClassBrowser;
30 class Compiler;
31 class Token;
32 
33 // TODO (ollydbg#1#), this class is dirty, I'm going to change its name like CursorLocation
34 /** Search location combination, a pointer to cbStyledTextCtrl and a filename is enough */
35 struct ccSearchData
36 {
37     cbStyledTextCtrl* control;
38     wxString          file;
39 };
40 
41 /** Symbol browser tree showing option */
42 enum BrowserViewMode
43 {
44     bvmRaw = 0,
45     bvmInheritance
46 };
47 
48 /** @brief NativeParser class is just like a manager class to control Parser objects.
49  *
50  * Normally, Each C::B Project (cbp) will have an associated Parser object.
51  * In another mode, all C::B project belong to a C::B workspace share a single Parser object.
52  * Nativeparser will manage all the Parser objects.
53  */
54 class NativeParser : public wxEvtHandler, NativeParserBase
55 {
56 public:
57     /** Constructor */
58     NativeParser();
59 
60     /** Destructor */
61     ~NativeParser();
62 
63     /** return a reference to the currently active Parser object */
GetParser()64     ParserBase& GetParser() { return *m_Parser; }
65 
66     /** return the Parser pointer corresponding to the input C::B project
67      * @param project input C::B project pointer
68      * @return a pointer to parser object
69      */
70     ParserBase* GetParserByProject(cbProject* project);
71 
72     /** return the Parser pointer associated with the input file
73      * If a file belongs to several Parser objects, the first found Parser will returned.
74      * @param filename filename with full path.
75      * @return Parser pointer
76      */
77     ParserBase* GetParserByFilename(const wxString& filename);
78 
79     /** return the C::B project associated with Parser pointer
80      * @param parser Parser pointer
81      * @return C::B Project pointer
82      */
83     cbProject* GetProjectByParser(ParserBase* parser);
84 
85     /** return the C::B project containing the filename
86      * The function first try to match the filename in the active project, next to match other
87      * projects opened, If the file exists in several projects, the first matched project will be
88      * returned.
89      * @param filename input filename
90      * @return project pointer containing the file
91      */
92     cbProject* GetProjectByFilename(const wxString& filename);
93 
94     /** return the C::B project containing the cbEditor pointer
95      * @param editor Any valid cbEditor pointer
96      * @return project pointer
97      */
98     cbProject* GetProjectByEditor(cbEditor* editor);
99 
100     /** Get current project by active editor or just return active project */
101     cbProject* GetCurrentProject();
102 
103     /** Return true if use one Parser per whole workspace */
IsParserPerWorkspace()104     bool IsParserPerWorkspace() const { return m_ParserPerWorkspace; }
105 
106     /** Return true if all the parser's batch-parse stages are finished, otherwise return false*/
107     bool Done();
108 
109     /** Provides images for the Symbol browser (for tree node images) and AutoCompletion list.
110      *  @param maxSize Maximum size that will fit in the UI.
111      */
112     wxImageList* GetImageList(int maxSize);
113 
114     /** Returns the image assigned to a specific token for a symbol browser */
115     int GetTokenKindImage(const Token* token);
116 
117     /** Get the implementation file path if the input is a header file. or Get the header file path
118      * if the input is an implementation file.
119      * Both the implementation file and header file can be in different directories.
120      * @param filename input filename
121      * @return corresponding file paths, in wxArrayString format
122      */
123     wxArrayString GetAllPathsByFilename(const wxString& filename);
124 
125     /** Add the paths to path array, and this will be used in GetAllPathsByFilename() function.
126      *  internally, all the folder paths were recorded in UNIX format.
127      * @param dirs the target dir collection
128      * @param path the new added path
129      * @param hasExt the file path has extensions, such as C:/aaa/bbb.cpp
130      */
131     static void AddPaths(wxArrayString& dirs, const wxString& path, bool hasExt);
132 
133     // the functions below are handling and managing Parser object
134 
135     /** Dynamically allocate a Parser object for the input C::B project, note that while create a
136      * new Parser object, the DoFullParsing() function will be called, which collect the macro
137      * definitions, and start the batch parsing from the thread pool.
138      * @param project C::B project
139      * @return Parser pointer of the project.
140      */
141     ParserBase* CreateParser(cbProject* project);
142 
143     /** delete the Parser object for the input project
144      * @param project C::B project.
145      * @return true if success.
146      */
147     bool DeleteParser(cbProject* project);
148 
149     /** Single file re-parse.
150      * This was happening when you add a single file to project, or a file was modified.
151      * the main logic of this function call is:
152      * 1, once this function is called, the file will be marked as "need to be reparsed" in the
153      *    token tree, and a timer(reparse timer) is started.
154      * 2, on reparse timer hit, we collect all the files marked as "need to be reparsed" from the
155      *    token tree, remove them from the token tree, and call AddParse() to add parsing job, this
156      *    will ticket the On batch timer
157      * 3, when on batch timer hit, it see there are some parsing jobs to do (m_BatchParseFiles is
158      *    not empty), then it will run a thread job ParserThreadedTask
159      * 4, Once the ParserThreadedTask is running, it will create all the Parserthreads and run them
160      *    in the thread pool
161      * @param project C::B project
162      * @param filename filename with full path in the C::B project
163      */
164     bool ReparseFile(cbProject* project, const wxString& filename);
165 
166     /** New file was added to the C::B project, so this will cause a re-parse on the new added file.
167      * @param project C::B project
168      * @param filename filename with full path in the C::B project
169      */
170     bool AddFileToParser(cbProject* project, const wxString& filename, ParserBase* parser = nullptr);
171 
172     /** remove a file from C::B project and Parser
173      * @param project C::B project
174      * @param filename filename with full patch in the C::B project
175      */
176     bool RemoveFileFromParser(cbProject* project, const wxString& filename);
177 
178     /** when user changes the CC option, we should re-read the option */
179     void RereadParserOptions();
180 
181     /** re-parse the active Parser (the project associated with m_Parser member variable */
182     void ReparseCurrentProject();
183 
184     /** re-parse the project select by context menu in projects management panel */
185     void ReparseSelectedProject();
186 
187     /** collect tokens where a code suggestion list can be shown
188      * @param[in] searchData search location, the place where the caret locates
189      * @param[out] result containing all matching result token indexes
190      * @param reallyUseAI true means the context scope information should be considered,
191      *        false if only do a plain word match
192      * @param isPrefix partially match which result all the Tokens' name with the same prefix,
193               otherwise use full-text match
194      * @param caseSensitive case sensitive or not
195      * @param caretPos Where the current caret locates, -1 means we use the current caret position.
196      * @return the matching Token count
197      */
198     size_t MarkItemsByAI(ccSearchData* searchData, TokenIdxSet& result, bool reallyUseAI = true,
199                          bool isPrefix = true, bool caseSensitive = false, int caretPos = -1);
200 
201     /** the same as before, but we don't specify the searchData information, so it will use the active
202      *  editor and current caret information.
203      */
204     size_t MarkItemsByAI(TokenIdxSet& result, bool reallyUseAI = true, bool isPrefix = true,
205                          bool caseSensitive = false, int caretPos = -1);
206 
207     /** Call tips are tips when you are typing function arguments
208      * these tips information could be:
209      * the prototypes information of the current function,
210      * the type information of the variable.
211      * Here are the basic algorithm
212      *
213      * if you have a function declaration like this: int fun(int a, float b, char c);
214      * when user are typing code, the caret is located here
215      * fun(arg1, arg2|
216      *    ^end       ^ begin
217      * we first do a backward search, should find the "fun" as the function name
218      * and later return the string "int fun(int a, float b, char c)" as the call tip
219      * typedCommas is 1, since one argument is already typed.
220      *
221      * @param[out] items array to store the tip results.
222      * @param typedCommas how much comma characters the user has typed in the current line before the cursor.
223      * @param ed the editor
224      * @param pos the location of the caret, if not supplied, the current caret is used
225      * @return The location in the editor of the beginning of the argument list
226      */
227     int GetCallTips(wxArrayString& items, int& typedCommas, cbEditor* ed, int pos = wxNOT_FOUND);
228 
229     /** project search path is used for auto completion for #include <> */
230     wxArrayString ParseProjectSearchDirs(const cbProject &project);
231     void SetProjectSearchDirs(cbProject &project, const wxArrayString &dirs);
232 
233     // The function below is used to manage symbols browser
234     /** return active class browser pointer*/
GetClassBrowser()235     ClassBrowser* GetClassBrowser() const { return m_ClassBrowser; }
236 
237     /** create the class browser */
238     void CreateClassBrowser();
239 
240     /** remove the class browser */
241     void RemoveClassBrowser(bool appShutDown = false);
242 
243     /** update the class browser tree*/
244     void UpdateClassBrowser();
245 
246 protected:
247     /** When a Parser is created, we need a full parsing stage including:
248      * 1, parse the priority header files firstly.
249      * 2, parse all the other project files.
250      */
251     bool DoFullParsing(cbProject* project, ParserBase* parser);
252 
253     /** Switch parser object according the current active editor and filename */
254     bool SwitchParser(cbProject* project, ParserBase* parser);
255 
256     /** Set a new Parser as the active Parser
257      * Set the active parser pointer (m_Parser member variable)
258      * update the ClassBrowser's Parser pointer
259      * re-fresh the symbol browser tree.
260      * if we did switch the parser, we also need to remove the temporary tokens of the old parser.
261      */
262     void SetParser(ParserBase* parser);
263 
264     /** Clear all Parser object*/
265     void ClearParsers();
266 
267     /** Remove all the obsolete Parser object
268      * if the number exceeds the limited number (can be set in the CC's option), then all the
269      * obsolete parser will be removed.
270      */
271     void RemoveObsoleteParsers();
272 
273     /** Get cbProject and Parser pointer, according to the current active editor */
274     std::pair<cbProject*, ParserBase*> GetParserInfoByCurrentEditor();
275 
276     /** set the class browser view mode */
277     void SetCBViewMode(const BrowserViewMode& mode);
278 
279 private:
280     friend class CodeCompletion;
281 
282     /** Start an Artificial Intelligence search algorithm to gather all the matching tokens.
283      * The actual AI is in FindAIMatches() below.
284      * @param result output parameter.
285      * @param searchData cbEditor information.
286      * @param lineText current statement.
287      * @param isPrefix if true, then the result contains all the tokens whose name is a prefix of current lineText.
288      * @param caseSensitive true is case sensitive is enabled on the match.
289      * @param search_scope it is the "parent token" where we match the "search-key".
290      * @param caretPos use current caret position if it is -1.
291      * @return matched token number
292      */
293     size_t AI(TokenIdxSet& result,
294               ccSearchData* searchData,
295               const wxString& lineText = wxEmptyString,
296               bool isPrefix = false,
297               bool caseSensitive = false,
298               TokenIdxSet* search_scope = 0,
299               int caretPos = -1);
300 
301     /** return all the tokens matching the current function(hopefully, just one)
302      * @param editor editor pointer
303      * @param result output result containing all the Token index
304      * @param caretPos -1 if the current caret position is used.
305      * @return number of result Tokens
306      */
307     size_t FindCurrentFunctionToken(ccSearchData* searchData, TokenIdxSet& result, int caretPos = -1);
308 
309     /** returns the position where the current function scope starts.
310      * optionally, returns the function's namespace (ends in double-colon ::), name and token
311      * @param[in] searchData search data struct pointer
312      * @param[out] nameSpace get the namespace modifier
313      * @param[out] procName get the function name
314      * @param[out] functionToken get the token of current function
315      * @param caretPos caret position in cbEditor
316      * @return current function line number
317      */
318     int FindCurrentFunctionStart(ccSearchData* searchData,
319                                  wxString*     nameSpace = 0L,
320                                  wxString*     procName = 0L,
321                                  int*          functionIndex = 0L,
322                                  int           caretPos = -1);
323 
324     /** used in CodeCompletion suggestion list to boost the performance, we use a caches */
LastAISearchWasGlobal()325     bool LastAISearchWasGlobal() const { return m_LastAISearchWasGlobal; }
326 
327     /** The same as above */
LastAIGlobalSearch()328     const wxString& LastAIGlobalSearch() const { return m_LastAIGlobalSearch; }
329 
330     /** collect the using namespace directive in the editor specified by searchData
331      * @param searchData search location
332      * @param search_scope resulting tokens collection
333      * @param caretPos caret position, if not specified, we use the current caret position
334      */
335     bool ParseUsingNamespace(ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos = -1);
336 
337     /** collect the using namespace directive in the buffer specified by searchData
338      * @param buffer code to parse
339      * @param search_scope resulting tokens collection
340      * @param bufferSkipBlocks skip brace sets { }
341      */
342     bool ParseBufferForUsingNamespace(const wxString& buffer, TokenIdxSet& search_scope, bool bufferSkipBlocks = true);
343 
344     /** collect function argument, add them to the token tree (as temporary tokens)
345      * @param searchData search location
346      * @param caretPos caret position, if not specified, we use the current caret position
347      */
348     bool ParseFunctionArguments(ccSearchData* searchData, int caretPos = -1);
349 
350     /** parses from the start of function up to the cursor, this is used to collect local variables.
351      * @param searchData search location
352      * @param search_scope resulting tokens collection of local using namespace
353      * @param caretPos caret position, if not specified, we use the current caret position
354      */
355     bool ParseLocalBlock(ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos = -1);
356 
357     /** collect the header file search directories, those dirs include:
358      *  1, project's base dir, e.g. if you cbp file was c:/bbb/aaa.cbp, then c:/bbb is added.
359      *  2, project's setting search dirs, for a wx project, then c:/wxWidgets2.8.12/include is added.
360      *  3, a project may has some targets, so add search dirs for those targets
361      *  4, compiler's own search path, like: c:/mingw/include
362      */
363     bool AddCompilerDirs(cbProject* project, ParserBase* parser);
364 
365     /** collect compiler specific predefined preprocessor definition, this is usually run a special
366      * compiler command, like GCC -dM for gcc.
367      * @return true if there are some macro definition added, else it is false
368      */
369     bool AddCompilerPredefinedMacros(cbProject* project, ParserBase* parser);
370 
371     /** collect GCC compiler predefined preprocessor definition */
372     bool AddCompilerPredefinedMacrosGCC(const wxString& compilerId, cbProject* project, wxString& defs, ParserBase* parser);
373 
374     /** lookup GCC compiler -std=XXX option */
375     wxString GetCompilerStandardGCC(Compiler* compiler, cbProject* project);
376 
377     /** lookup GCC compiler -std=XXX option for specific GCC options*/
378     wxString GetCompilerUsingStandardGCC(const wxArrayString& compilerOptions);
379 
380     /** collect VC compiler predefined preprocessor definition */
381     bool AddCompilerPredefinedMacrosVC(const wxString& compilerId, wxString& defs, ParserBase* parser);
382 
383     /** collect project (user) defined preprocessor definition, such as for wxWidgets project, the
384      * macro may have "#define wxUSE_UNICODE" defined in its project file.
385      * @return true if there are some macro definition added, else it is false
386      */
387     bool AddProjectDefinedMacros(cbProject* project, ParserBase* parser);
388 
389     /** Add compiler include directories (from search paths) to a parser */
390     void AddCompilerIncludeDirsToParser(const Compiler* compiler, ParserBase* parser);
391 
392     /** Collect the default compiler include file search paths. called by AddCompilerDirs() function*/
393     const wxArrayString& GetGCCCompilerDirs(const wxString& cpp_path, const wxString& cpp_executable);
394 
395     /** Add the collected default GCC compiler include search paths to a parser */
396     void AddGCCCompilerDirs(const wxString& masterPath, const wxString& compilerCpp, ParserBase* parser);
397 
398     /** Add a list of directories to the parser's search directories, normalise to "base" path, if
399      * "base" is not empty. Replaces macros.
400      */
401     void AddIncludeDirsToParser(const wxArrayString& dirs, const wxString& base, ParserBase* parser);
402 
403     /** Runs an app safely (protected against re-entry) and returns output and error */
404     bool SafeExecute(const wxString& app_path, const wxString& app, const wxString& args, wxArrayString &output, wxArrayString &error);
405 
406     /** Event handler when the batch parse starts, print some log information */
407     void OnParserStart(wxCommandEvent& event);
408 
409     /** Event handler when the batch parse finishes, print some log information, check whether the active editor
410      * belong to the current parser, if not, do a parser switch
411      */
412     void OnParserEnd(wxCommandEvent& event);
413 
414     /** If use one parser per whole workspace, we need parse all project one by one, E.g.
415      * If a workspace contains A.cbp, B.cbp and C.cbp, and we are in the mode of one parser for
416      * the whole workspace, we first parse A.cbp, after that we should continue to parse B.cbp. When
417      * finishing parsing B.cbp, we need the timer again to parse the C.cbp.
418      * If we are in the mode of one parser for one project, then after parsing A.cbp, the timer is
419      * kicked, so there is a chance to parse the B.cbp or C.cbp, but only when user opened some file
420      * of B.cbp or C.cbp when the timer event arrived.
421      */
422     void OnParsingOneByOneTimer(wxTimerEvent& event);
423 
424     /** Event handler when an editor activate, *NONE* project is handled here */
425     void OnEditorActivated(EditorBase* editor);
426 
427     /** Event handler when an editor closed, if it is the last editor belong to *NONE* project, then
428      *  the *NONE* Parser will be removed
429      */
430     void OnEditorClosed(EditorBase* editor);
431 
432     /** Init cc search member variables */
433     void InitCCSearchVariables();
434 
435     /** Add one project to the common parser in one parser for the whole workspace mode
436      * @return true means there are some thing (macro and files) need to parse, otherwise it is false
437      */
438     bool AddProjectToParser(cbProject* project);
439 
440     /** Remove cbp from the common parser, this only happens in one parser for whole workspace mode
441      * when a parser is removed from the workspace, we should remove the project from the parser
442      */
443     bool RemoveProjectFromParser(cbProject* project);
444 
445 private:
446     typedef std::pair<cbProject*, ParserBase*> ProjectParserPair;
447     typedef std::list<ProjectParserPair>       ParserList;
448 
449     /** a list holing all the cbp->parser pairs, if in one parser per project mode, there are many
450      * many pairs in this list. In one parser per workspace mode, there is only one pair, and the
451      * m_ParserList.begin()->second is the common parser for all the projects in workspace.
452      */
453     ParserList                   m_ParserList;
454     /** a temp parser object pointer */
455     ParserBase*                  m_TempParser;
456     /** active parser object pointer */
457     ParserBase*                  m_Parser;
458 
459     /** a delay timer to parser every project in sequence */
460     wxTimer                      m_TimerParsingOneByOne;
461     /** symbol browser window */
462     ClassBrowser*                m_ClassBrowser;
463     /** if true, which means m_ClassBrowser is floating (not docked) */
464     bool                         m_ClassBrowserIsFloating;
465 
466     /// Stores image lists for different sizes. See GetImageList.
467     typedef std::unordered_map<int, std::unique_ptr<wxImageList>> SizeToImageList;
468     SizeToImageList m_ImageListMap;
469 
470     /** all the files which opened, but does not belong to any cbp */
471     wxArrayString                m_StandaloneFiles;
472     /**  if true, which means the parser hold tokens of the whole workspace's project, if false
473      * then one parser per a cbp
474      */
475     bool                         m_ParserPerWorkspace;
476     /** only used when m_ParserPerWorkspace is true, and holds all the cbps for the common parser */
477     std::set<cbProject*>         m_ParsedProjects;
478 
479     /* CC Search Member Variables => START */
480     wxString          m_LastAIGlobalSearch;    //!< same case like above, it holds the search string
481     bool              m_LastAISearchWasGlobal; //!< true if the phrase for code-completion is empty or partial text (i.e. no . -> or :: operators)
482     cbStyledTextCtrl* m_LastControl;
483     wxString          m_LastFile;
484     int               m_LastFunctionIndex;
485     int               m_LastFuncTokenIdx;      //!< saved the function token's index, for remove all local variable
486     int               m_LastLine;
487     wxString          m_LastNamespace;
488     wxString          m_LastPROC;
489     int               m_LastResult;
490     /* CC Search Member Variables => END */
491 };
492 
493 #endif // NATIVEPARSER_H
494 
495