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