Skip to content

Instantly share code, notes, and snippets.

@thuzhf
Last active August 29, 2015 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thuzhf/732e6910485daee43b2c to your computer and use it in GitHub Desktop.
Save thuzhf/732e6910485daee43b2c to your computer and use it in GitHub Desktop.
/*
* This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
* http://www.gnu.org/licenses/gpl-3.0.html
*
* $Revision: 9441 $
* $Id: nativeparser.cpp 9441 2013-11-09 12:59:13Z mortenmacfly $
* $HeadURL: http://svn.code.sf.net/p/codeblocks/code/branches/release-xx.yy/src/plugins/codecompletion/nativeparser.cpp $
*/
#include <sdk.h>
#ifndef CB_PRECOMP
#include <cctype>
#include <wx/dir.h>
#include <wx/log.h> // for wxSafeShowMessage()
#include <wx/regex.h>
#include <wx/wfstream.h>
#include <cbauibook.h>
#include <cbeditor.h>
#include <cbexception.h>
#include <cbproject.h>
#include <compilerfactory.h>
#include <configmanager.h>
#include <editormanager.h>
#include <logmanager.h>
#include <macrosmanager.h>
#include <manager.h>
#include <pluginmanager.h>
#include <prep.h> // nullptr
#include <projectmanager.h>
#include <tinyxml/tinyxml.h>
#endif
#include <wx/tokenzr.h>
#include <cbstyledtextctrl.h>
#include <compilercommandgenerator.h>
#include <projectloader_hooks.h>
#include "nativeparser.h"
#include "classbrowser.h"
#include "parser/parser.h"
#include "parser/profiletimer.h"
#define CC_NATIVEPARSER_DEBUG_OUTPUT 0
#if defined (CC_GLOBAL_DEBUG_OUTPUT)
#if CC_GLOBAL_DEBUG_OUTPUT == 1
#undef CC_NATIVEPARSER_DEBUG_OUTPUT
#define CC_NATIVEPARSER_DEBUG_OUTPUT 1
#elif CC_GLOBAL_DEBUG_OUTPUT == 2
#undef CC_NATIVEPARSER_DEBUG_OUTPUT
#define CC_NATIVEPARSER_DEBUG_OUTPUT 2
#endif
#endif
#if CC_NATIVEPARSER_DEBUG_OUTPUT == 1
#define TRACE(format, args...) \
CCLogger::Get()->DebugLog(F(format, ##args))
#define TRACE2(format, args...)
#elif CC_NATIVEPARSER_DEBUG_OUTPUT == 2
#define TRACE(format, args...) \
do \
{ \
if (g_EnableDebugTrace) \
CCLogger::Get()->DebugLog(F(format, ##args)); \
} \
while (false)
#define TRACE2(format, args...) \
CCLogger::Get()->DebugLog(F(format, ##args))
#else
#define TRACE(format, args...)
#define TRACE2(format, args...)
#endif
/*
* (Recursive) functions that are surrounded by a critical section:
* GenerateResultSet() -> AddChildrenOfUnnamed
* GetCallTips() -> PrettyPrintToken (recursive function)
* FindCurrentFunctionToken() -> ParseFunctionArguments, FindAIMatches (recursive function)
* GenerateResultSet (recursive function):
* FindAIMatches(), ResolveActualType(), ResolveExpression(),
* FindCurrentFunctionToken(), ResolveOperator()
* FindCurrentFunctionStart() -> GetTokenFromCurrentLine
*/
namespace NativeParserHelper
{
class ParserDirTraverser : public wxDirTraverser
{
public:
ParserDirTraverser(const wxString& excludePath, wxArrayString& files) :
m_ExcludeDir(excludePath),
m_Files(files)
{}
virtual wxDirTraverseResult OnFile(const wxString& filename)
{
if (ParserCommon::FileType(filename) != ParserCommon::ftOther)
m_Files.Add(filename);
return wxDIR_CONTINUE;
}
virtual wxDirTraverseResult OnDir(const wxString& dirname)
{
if (dirname == m_ExcludeDir)
return wxDIR_IGNORE;
if (m_Files.GetCount() == 1)
return wxDIR_STOP;
m_Files.Clear();
return wxDIR_CONTINUE;
}
private:
const wxString& m_ExcludeDir;
wxArrayString& m_Files;
};
}// namespace NativeParserHelper
int idTimerParsingOneByOne = wxNewId();
bool s_DebugSmartSense = false;
NativeParser::NativeParser() :
m_TimerParsingOneByOne(this, idTimerParsingOneByOne),
m_ClassBrowser(nullptr),
m_ClassBrowserIsFloating(false),
m_ImageList(nullptr),
m_ParserPerWorkspace(false),
m_EditorStartWord(-1),
m_EditorEndWord(-1),
m_LastAISearchWasGlobal(false),
m_LastControl(nullptr),
m_LastFunctionIndex(-1),
m_LastFuncTokenIdx(-1),
m_LastLine(-1),
m_LastResult(-1)
{
m_TempParser = new ParserBase;
m_Parser = m_TempParser;
m_ImageList = new wxImageList(16, 16);
wxBitmap bmp;
wxString prefix;
prefix = ConfigManager::GetDataFolder() + _T("/images/codecompletion/");
// bitmaps must be added by order of PARSER_IMG_* consts
bmp = cbLoadBitmap(prefix + _T("class_folder.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_CLASS_FOLDER
bmp = cbLoadBitmap(prefix + _T("class.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_CLASS
bmp = cbLoadBitmap(prefix + _T("class_private.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_CLASS_PRIVATE
bmp = cbLoadBitmap(prefix + _T("class_protected.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_CLASS_PROTECTED
bmp = cbLoadBitmap(prefix + _T("class_public.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_CLASS_PUBLIC
bmp = cbLoadBitmap(prefix + _T("ctor_private.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_CTOR_PRIVATE
bmp = cbLoadBitmap(prefix + _T("ctor_protected.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_CTOR_PROTECTED
bmp = cbLoadBitmap(prefix + _T("ctor_public.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_CTOR_PUBLIC
bmp = cbLoadBitmap(prefix + _T("dtor_private.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_DTOR_PRIVATE
bmp = cbLoadBitmap(prefix + _T("dtor_protected.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_DTOR_PROTECTED
bmp = cbLoadBitmap(prefix + _T("dtor_public.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_DTOR_PUBLIC
bmp = cbLoadBitmap(prefix + _T("method_private.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_FUNC_PRIVATE
bmp = cbLoadBitmap(prefix + _T("method_protected.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_FUNC_PRIVATE
bmp = cbLoadBitmap(prefix + _T("method_public.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_FUNC_PUBLIC
bmp = cbLoadBitmap(prefix + _T("var_private.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_VAR_PRIVATE
bmp = cbLoadBitmap(prefix + _T("var_protected.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_VAR_PROTECTED
bmp = cbLoadBitmap(prefix + _T("var_public.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_VAR_PUBLIC
bmp = cbLoadBitmap(prefix + _T("preproc.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_PREPROCESSOR
bmp = cbLoadBitmap(prefix + _T("enum.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_ENUM
bmp = cbLoadBitmap(prefix + _T("enum_private.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_ENUM_PRIVATE
bmp = cbLoadBitmap(prefix + _T("enum_protected.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_ENUM_PROTECTED
bmp = cbLoadBitmap(prefix + _T("enum_public.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_ENUM_PUBLIC
bmp = cbLoadBitmap(prefix + _T("enumerator.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_ENUMERATOR
bmp = cbLoadBitmap(prefix + _T("namespace.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_NAMESPACE
bmp = cbLoadBitmap(prefix + _T("typedef.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_TYPEDEF
bmp = cbLoadBitmap(prefix + _T("typedef_private.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_TYPEDEF_PRIVATE
bmp = cbLoadBitmap(prefix + _T("typedef_protected.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_TYPEDEF_PROTECTED
bmp = cbLoadBitmap(prefix + _T("typedef_public.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_TYPEDEF_PUBLIC
bmp = cbLoadBitmap(prefix + _T("symbols_folder.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_SYMBOLS_FOLDER
bmp = cbLoadBitmap(prefix + _T("vars_folder.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_VARS_FOLDER
bmp = cbLoadBitmap(prefix + _T("funcs_folder.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_FUNCS_FOLDER
bmp = cbLoadBitmap(prefix + _T("enums_folder.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_ENUMS_FOLDER
bmp = cbLoadBitmap(prefix + _T("preproc_folder.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_PREPROC_FOLDER
bmp = cbLoadBitmap(prefix + _T("others_folder.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_OTHERS_FOLDER
bmp = cbLoadBitmap(prefix + _T("typedefs_folder.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_TYPEDEF_FOLDER
bmp = cbLoadBitmap(prefix + _T("macro.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_MACRO
bmp = cbLoadBitmap(prefix + _T("macro_private.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_MACRO_PRIVATE
bmp = cbLoadBitmap(prefix + _T("macro_protected.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_MACRO_PROTECTED
bmp = cbLoadBitmap(prefix + _T("macro_public.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_MACRO_PUBLIC
bmp = cbLoadBitmap(prefix + _T("macro_folder.png"), wxBITMAP_TYPE_PNG);
m_ImageList->Add(bmp); // PARSER_IMG_MACRO_FOLDER
ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
m_ParserPerWorkspace = cfg->ReadBool(_T("/parser_per_workspace"), false);
// hook to project loading procedure
ProjectLoaderHooks::HookFunctorBase* myhook = new ProjectLoaderHooks::HookFunctor<NativeParser>(this, &NativeParser::OnProjectLoadingHook);
m_HookId = ProjectLoaderHooks::RegisterHook(myhook);
Connect(ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(NativeParser::OnParserStart));
Connect(ParserCommon::idParserEnd, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(NativeParser::OnParserEnd));
Connect(idTimerParsingOneByOne, wxEVT_TIMER, wxTimerEventHandler(NativeParser::OnParsingOneByOneTimer));
}
NativeParser::~NativeParser()
{
Disconnect(ParserCommon::idParserStart, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(NativeParser::OnParserStart));
Disconnect(ParserCommon::idParserEnd, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(NativeParser::OnParserEnd));
Disconnect(idTimerParsingOneByOne, wxEVT_TIMER, wxTimerEventHandler(NativeParser::OnParsingOneByOneTimer));
ProjectLoaderHooks::UnregisterHook(m_HookId, true);
RemoveClassBrowser();
ClearParsers();
Delete(m_ImageList);
Delete(m_TempParser);
}
ParserBase* NativeParser::GetParserByProject(cbProject* project)
{
if (m_ParserPerWorkspace)
{
std::set<cbProject*>::iterator it = m_ParsedProjects.find(project);
if (it != m_ParsedProjects.end())
return m_ParserList.begin()->second;
}
else
{
for (ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it)
{
if (it->first == project)
return it->second;
}
}
TRACE(_T("NativeParser::GetParserByProject(): Returning nullptr."));
return nullptr;
}
ParserBase* NativeParser::GetParserByFilename(const wxString& filename)
{
cbProject* project = GetProjectByFilename(filename);
return GetParserByProject(project);
}
cbProject* NativeParser::GetProjectByParser(ParserBase* parser)
{
for (ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it)
{
if (it->second == parser)
return it->first;
}
TRACE(_T("NativeParser::GetProjectByParser(): Returning NULL."));
return NULL;
}
cbProject* NativeParser::GetProjectByFilename(const wxString& filename)
{
TRACE(_T("NativeParser::GetProjectByFilename(): %s"), filename.wx_str());
cbProject* activeProject = Manager::Get()->GetProjectManager()->GetActiveProject();
if (activeProject)
{
ParserBase* parser = GetParserByProject(activeProject);
if ( ( parser
&& parser->IsFileParsed(filename) )
|| activeProject->GetFileByFilename(filename, false, true) )
{
return activeProject;
}
else
{
ProjectsArray* projs = Manager::Get()->GetProjectManager()->GetProjects();
for (size_t i = 0; i < projs->GetCount(); ++i)
{
cbProject* project = projs->Item(i);
if (!project || project == activeProject)
continue;
parser = GetParserByProject(project);
if ( ( parser
&& parser->IsFileParsed(filename) )
|| project->GetFileByFilename(filename, false, true) )
{
return project;
}
}
}
}
return nullptr;
}
cbProject* NativeParser::GetProjectByEditor(cbEditor* editor)
{
if (!editor)
return nullptr;
ProjectFile* pf = editor->GetProjectFile();
if (pf && pf->GetParentProject())
return pf->GetParentProject();
return GetProjectByFilename(editor->GetFilename());
}
cbProject* NativeParser::GetCurrentProject()
{
cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
cbProject* project = GetProjectByEditor(editor);
if (!project)
project = Manager::Get()->GetProjectManager()->GetActiveProject();
return project;
}
bool NativeParser::Done()
{
bool done = true;
for (ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it)
{
if (!it->second->Done())
{
done = false;
break;
}
}
TRACE(_T("NativeParser::Done(): %s"), done ? _T("true"): _T("false"));
return done;
}
int NativeParser::GetTokenKindImage(const Token* token)
{
if (!token)
return PARSER_IMG_NONE;
switch (token->m_TokenKind)
{
case tkPreprocessor: return PARSER_IMG_PREPROCESSOR;
case tkEnum:
switch (token->m_Scope)
{
case tsPublic: return PARSER_IMG_ENUM_PUBLIC;
case tsProtected: return PARSER_IMG_ENUM_PROTECTED;
case tsPrivate: return PARSER_IMG_ENUM_PRIVATE;
case tsUndefined:
default: return PARSER_IMG_ENUM;
}
case tkEnumerator: return PARSER_IMG_ENUMERATOR;
case tkClass:
switch (token->m_Scope)
{
case tsPublic: return PARSER_IMG_CLASS_PUBLIC;
case tsProtected: return PARSER_IMG_CLASS_PROTECTED;
case tsPrivate: return PARSER_IMG_CLASS_PRIVATE;
case tsUndefined:
default: return PARSER_IMG_CLASS;
}
case tkNamespace: return PARSER_IMG_NAMESPACE;
case tkTypedef:
switch (token->m_Scope)
{
case tsPublic: return PARSER_IMG_TYPEDEF_PUBLIC;
case tsProtected: return PARSER_IMG_TYPEDEF_PROTECTED;
case tsPrivate: return PARSER_IMG_TYPEDEF_PRIVATE;
case tsUndefined:
default: return PARSER_IMG_TYPEDEF;
}
case tkMacro:
switch (token->m_Scope)
{
case tsPublic: return PARSER_IMG_MACRO_PUBLIC;
case tsProtected: return PARSER_IMG_MACRO_PROTECTED;
case tsPrivate: return PARSER_IMG_MACRO_PRIVATE;
case tsUndefined:
default: return PARSER_IMG_MACRO;
}
case tkConstructor:
switch (token->m_Scope)
{
case tsProtected: return PARSER_IMG_CTOR_PROTECTED;
case tsPrivate: return PARSER_IMG_CTOR_PRIVATE;
case tsUndefined:
case tsPublic:
default: return PARSER_IMG_CTOR_PUBLIC;
}
case tkDestructor:
switch (token->m_Scope)
{
case tsProtected: return PARSER_IMG_DTOR_PROTECTED;
case tsPrivate: return PARSER_IMG_DTOR_PRIVATE;
case tsUndefined:
case tsPublic:
default: return PARSER_IMG_DTOR_PUBLIC;
}
case tkFunction:
switch (token->m_Scope)
{
case tsProtected: return PARSER_IMG_FUNC_PROTECTED;
case tsPrivate: return PARSER_IMG_FUNC_PRIVATE;
case tsUndefined:
case tsPublic:
default: return PARSER_IMG_FUNC_PUBLIC;
}
case tkVariable:
switch (token->m_Scope)
{
case tsProtected: return PARSER_IMG_VAR_PROTECTED;
case tsPrivate: return PARSER_IMG_VAR_PRIVATE;
case tsUndefined:
case tsPublic:
default: return PARSER_IMG_VAR_PUBLIC;
}
case tkAnyContainer:
case tkAnyFunction:
case tkUndefined:
default: return PARSER_IMG_NONE;
}
}
wxArrayString NativeParser::GetAllPathsByFilename(const wxString& filename)
{
TRACE(_T("NativeParser::GetAllPathsByFilename(): Enter"));
wxArrayString dirs;
const wxFileName fn(filename);
wxDir dir(fn.GetPath());
if (!dir.IsOpened())
return wxArrayString();
wxArrayString files;
NativeParserHelper::ParserDirTraverser traverser(wxEmptyString, files);
const wxString filespec = fn.HasExt() ? fn.GetName() + _T(".*") : fn.GetName();
CCLogger::Get()->DebugLog(_T("NativeParser::GetAllPathsByFilename(): Traversing '") + fn.GetPath() + _T("' for: ") + filespec);
dir.Traverse(traverser, filespec, wxDIR_FILES);
// only find one file in the dir, go other place
if (files.GetCount() == 1)
{
cbProject* project = IsParserPerWorkspace() ? GetCurrentProject()
: GetProjectByParser(m_Parser);
// search in the project
if (project)
{
const wxString prjPath = project->GetCommonTopLevelPath();
wxString priorityPath;
if (fn.HasExt() && (fn.GetExt().StartsWith(_T("h")) || fn.GetExt().StartsWith(_T("c"))))
{
wxFileName priFn(prjPath);
// hard-coded candidate path, the ./sdk or ./include under the project top level folder
priFn.AppendDir(fn.GetExt().StartsWith(_T("h")) ? _T("sdk") : _T("include"));
if (priFn.DirExists())
{
priorityPath = priFn.GetFullPath();
wxDir priorityDir(priorityPath);
if ( priorityDir.IsOpened() )
{
wxArrayString priorityPathSub;
NativeParserHelper::ParserDirTraverser traverser_2(wxEmptyString, priorityPathSub);
CCLogger::Get()->DebugLog(_T("NativeParser::GetAllPathsByFilename(): Traversing '") + priorityPath + _T("' for: ") + filespec);
priorityDir.Traverse(traverser_2, filespec, wxDIR_FILES | wxDIR_DIRS);
if (priorityPathSub.GetCount() == 1)
AddPaths(dirs, priorityPathSub[0], fn.HasExt());
}
}
}
if (dirs.IsEmpty())
{
wxDir prjDir(prjPath);
if (prjDir.IsOpened())
{
// try to search the project top level folder
wxArrayString prjDirSub;
NativeParserHelper::ParserDirTraverser traverser_2(priorityPath, prjDirSub);
CCLogger::Get()->DebugLog(_T("NativeParser::GetAllPathsByFilename(): Traversing '") + priorityPath + wxT(" - ") + prjPath + _T("' for: ") + filespec);
prjDir.Traverse(traverser_2, filespec, wxDIR_FILES | wxDIR_DIRS);
if (prjDirSub.GetCount() == 1)
AddPaths(dirs, prjDirSub[0], fn.HasExt());
}
}
}
}
CCLogger::Get()->DebugLog(F(_T("NativeParser::GetAllPathsByFilename(): Found %lu files:"), static_cast<unsigned long>(files.GetCount())));
for (size_t i=0; i<files.GetCount(); i++)
CCLogger::Get()->DebugLog(F(_T("- %s"), files[i].wx_str()));
if (!files.IsEmpty())
AddPaths(dirs, files[0], fn.HasExt());
TRACE(_T("NativeParser::GetAllPathsByFilename(): Leave"));
return dirs;
}
void NativeParser::AddPaths(wxArrayString& dirs, const wxString& path, bool hasExt)
{
wxString s;
if (hasExt)
s = UnixFilename(path.BeforeLast(_T('.'))) + _T(".");
else
s = UnixFilename(path);
if (dirs.Index(s, false) == wxNOT_FOUND)
dirs.Add(s);
}
ParserBase* NativeParser::CreateParser(cbProject* project)
{
if ( GetParserByProject(project) )
{
CCLogger::Get()->DebugLog(_T("NativeParser::CreateParser(): Parser for this project already exists!"));
return nullptr;
}
// Easy case for "one parser per workspace" that has already been created:
if (m_ParserPerWorkspace && !m_ParsedProjects.empty())
return m_ParserList.begin()->second;
TRACE(_T("NativeParser::CreateParser(): Calling DoFullParsing()"));
ParserBase* parser = new Parser(this, project);
if ( !DoFullParsing(project, parser) )
{
CCLogger::Get()->DebugLog(_T("NativeParser::CreateParser(): Full parsing failed!"));
delete parser;
return nullptr;
}
if (m_Parser == m_TempParser)
SetParser(parser); // Also updates class browser
if (m_ParserPerWorkspace)
m_ParsedProjects.insert(project);
m_ParserList.push_back(std::make_pair(project, parser));
wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
wxString log(F(_("NativeParser::CreateParser(): Finish creating a new parser for project '%s'"), prj.wx_str()));
CCLogger::Get()->Log(log);
CCLogger::Get()->DebugLog(log);
RemoveObsoleteParsers();
return parser;
}
bool NativeParser::DeleteParser(cbProject* project)
{
wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
ParserList::iterator it = m_ParserList.begin();
if (!m_ParserPerWorkspace)
{
for (; it != m_ParserList.end(); ++it)
{
if (it->first == project)
break;
}
}
if (it == m_ParserList.end())
{
CCLogger::Get()->DebugLog(F(_T("NativeParser::DeleteParser(): Parser does not exist for delete '%s'!"), prj.wx_str()));
return false;
}
bool removeProjectFromParser = false;
if (m_ParserPerWorkspace)
removeProjectFromParser = RemoveProjectFromParser(project);
if (m_ParsedProjects.empty())
{
if (it->second == m_Parser)
SetParser(m_TempParser); // Also updates class browser
wxString log(F(_("NativeParser::DeleteParser(): Deleting parser for project '%s'!"), prj.wx_str()));
CCLogger::Get()->Log(log);
CCLogger::Get()->DebugLog(log);
delete it->second;
m_ParserList.erase(it);
return true;
}
if (removeProjectFromParser)
return true;
CCLogger::Get()->DebugLog(_T("NativeParser::DeleteParser(): Deleting parser failed!"));
return false;
}
bool NativeParser::ReparseFile(cbProject* project, const wxString& filename)
{
if (ParserCommon::FileType(filename) == ParserCommon::ftOther)
return false;
ParserBase* parser = GetParserByProject(project);
if (!parser)
return false;
if (!parser->UpdateParsingProject(project))
return false;
TRACE(_T("NativeParser::ReparseFile(): Calling Parser::Reparse()"));
return parser->Reparse(filename);
}
bool NativeParser::AddFileToParser(cbProject* project, const wxString& filename, ParserBase* parser)
{
if (ParserCommon::FileType(filename) == ParserCommon::ftOther)
return false;
if (!parser)
{
parser = GetParserByProject(project);
if (!parser)
return false;
}
if (!parser->UpdateParsingProject(project))
return false;
TRACE(_T("NativeParser::AddFileToParser(): Calling Parser::AddFile()"));
return parser->AddFile(filename, project);
}
bool NativeParser::RemoveFileFromParser(cbProject* project, const wxString& filename)
{
ParserBase* parser = GetParserByProject(project);
if (!parser)
return false;
TRACE(_T("NativeParser::RemoveFileFromParser(): Calling Parser::RemoveFile()"));
return parser->RemoveFile(filename);
}
void NativeParser::RereadParserOptions()
{
ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
if (cfg->ReadBool(_T("/use_symbols_browser"), true))
{
if (!m_ClassBrowser)
{
CreateClassBrowser();
UpdateClassBrowser();
}
// change class-browser docking settings
else if (m_ClassBrowserIsFloating != cfg->ReadBool(_T("/as_floating_window"), false))
{
RemoveClassBrowser();
CreateClassBrowser();
// force re-update
UpdateClassBrowser();
}
}
else if (!cfg->ReadBool(_T("/use_symbols_browser"), true) && m_ClassBrowser)
RemoveClassBrowser();
const bool parserPerWorkspace = cfg->ReadBool(_T("/parser_per_workspace"), false);
if (m_Parser == m_TempParser)
{
m_ParserPerWorkspace = parserPerWorkspace;
return;
}
RemoveObsoleteParsers();
// re-parse if settings changed
ParserOptions opts = m_Parser->Options();
m_Parser->ReadOptions();
bool reparse = false;
cbProject* project = GetCurrentProject();
if ( opts.followLocalIncludes != m_Parser->Options().followLocalIncludes
|| opts.followGlobalIncludes != m_Parser->Options().followGlobalIncludes
|| opts.wantPreprocessor != m_Parser->Options().wantPreprocessor
|| opts.parseComplexMacros != m_Parser->Options().parseComplexMacros
|| m_ParserPerWorkspace != parserPerWorkspace )
{
// important options changed... flag for reparsing
if (cbMessageBox(_("You changed some class parser options. Do you want to "
"reparse your projects now, using the new options?"),
_("Reparse?"), wxYES_NO | wxICON_QUESTION) == wxID_YES)
{
reparse = true;
}
}
if (reparse)
ClearParsers();
m_ParserPerWorkspace = parserPerWorkspace;
if (reparse)
CreateParser(project);
}
void NativeParser::ReparseCurrentProject()
{
cbProject* project = GetCurrentProject();
if (project)
{
TRACE(_T("NativeParser::ReparseCurrentProject(): Calling DeleteParser() and CreateParser()"));
DeleteParser(project);
CreateParser(project);
}
}
void NativeParser::ReparseSelectedProject()
{
wxTreeCtrl* tree = Manager::Get()->GetProjectManager()->GetUI().GetTree();
if (!tree)
return;
wxTreeItemId treeItem = Manager::Get()->GetProjectManager()->GetUI().GetTreeSelection();
if (!treeItem.IsOk())
return;
const FileTreeData* data = static_cast<FileTreeData*>(tree->GetItemData(treeItem));
if (!data)
return;
if (data->GetKind() == FileTreeData::ftdkProject)
{
cbProject* project = data->GetProject();
if (project)
{
TRACE(_T("NativeParser::ReparseSelectedProject(): Calling DeleteParser() and CreateParser()"));
DeleteParser(project);
CreateParser(project);
}
}
}
// Here, we collect the "using namespace XXXX" directives
// Also, we locate the current caret in which function, then, add the function parameters to Token trie
// Also, the variables in the function body( local block ) was add to the Token trie
size_t NativeParser::MarkItemsByAI(ccSearchData* searchData,
TokenIdxSet& result,
bool reallyUseAI,
bool isPrefix,
bool caseSensitive,
int caretPos)
{
result.clear();
if (!m_Parser->Done())
{
wxString msg(_("The Parser is still parsing files."));
msg += m_Parser->NotDoneReason();
CCLogger::Get()->DebugLog(msg);
return 0;
}
TRACE(_T("NativeParser::MarkItemsByAI_2()"));
TokenTree* tree = m_Parser->GetTempTokenTree();
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
// remove old temporaries
tree->Clear();
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
RemoveLastFunctionChildren(m_Parser->GetTokenTree(), m_LastFuncTokenIdx);
// find "using namespace" directives in the file
TokenIdxSet search_scope;
ParseUsingNamespace(searchData, search_scope, caretPos);
// parse function's arguments
ParseFunctionArguments(searchData, caretPos);
// parse current code block (from the start of function up to the cursor)
ParseLocalBlock(searchData, search_scope, caretPos);
if (!reallyUseAI)
{
tree = m_Parser->GetTokenTree();
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
// all tokens, no AI whatsoever
for (size_t i = 0; i < tree->size(); ++i)
result.insert(i);
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
return result.size();
}
// we have correctly collected all the tokens, so we will do the artificial intelligence search
return AI(result, searchData, wxEmptyString, isPrefix, caseSensitive, &search_scope, caretPos);
}
size_t NativeParser::MarkItemsByAI(TokenIdxSet& result,
bool reallyUseAI,
bool isPrefix,
bool caseSensitive,
int caretPos)
{
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(F(_T("MarkItemsByAI_1()")));
cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
if (!editor)
return 0;
ccSearchData searchData = { editor->GetControl(), editor->GetFilename() };
if (!searchData.control)
return 0;
TRACE(_T("NativeParser::MarkItemsByAI_1()"));
return MarkItemsByAI(&searchData, result, reallyUseAI, isPrefix, caseSensitive, caretPos);
}
void NativeParser::GetCallTips(int chars_per_line, wxArrayString &items, int& typedCommas, int pos)
{
items.Clear();
typedCommas = 0;
int commas = 0;
cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
if (!ed || !m_Parser->Done())
{
items.Add(wxT("Parsing at the moment..."));
return;
}
TRACE(_T("NativeParser::GetCallTips()"));
ccSearchData searchData = { ed->GetControl(), ed->GetFilename() };
if (pos == wxNOT_FOUND)
pos = searchData.control->GetCurrentPos();
int nest = 0;
while (--pos > 0)
{
const int style = searchData.control->GetStyleAt(pos);
if ( searchData.control->IsString(style)
|| searchData.control->IsCharacter(style)
|| searchData.control->IsComment(style) )
{
continue;
}
const wxChar ch = searchData.control->GetCharAt(pos);
if (ch == _T(';'))
return;
else if (ch == _T(','))
{
if (nest == 0)
++commas;
}
else if (ch == _T(')'))
--nest;
else if (ch == _T('('))
{
++nest;
if (nest > 0)
break;
}
}// while
// strip un-wanted
while (--pos > 0)
{
if ( searchData.control->GetCharAt(pos) <= _T(' ')
|| searchData.control->IsComment(searchData.control->GetStyleAt(pos)) )
{
continue;
}
break;
}
const int start = searchData.control->WordStartPosition(pos, true);
const int end = searchData.control->WordEndPosition(pos, true);
const wxString target = searchData.control->GetTextRange(start, end);
TRACE(_T("Sending \"%s\" for call-tip"), target.c_str());
if (target.IsEmpty())
return;
TokenIdxSet result;
MarkItemsByAI(result, true, false, true, end);
ComputeCallTip(m_Parser->GetTokenTree(), result, chars_per_line, items);
typedCommas = commas;
TRACE(_T("NativeParser::GetCallTips(): typedCommas=%d"), typedCommas);
items.Sort();
}
wxArrayString& NativeParser::GetProjectSearchDirs(cbProject* project)
{
ProjectSearchDirsMap::iterator it;
it = m_ProjectSearchDirsMap.find(project);
if (it == m_ProjectSearchDirsMap.end())
it = m_ProjectSearchDirsMap.insert(m_ProjectSearchDirsMap.end(), std::make_pair(project, wxArrayString()));
return it->second;
}
void NativeParser::CreateClassBrowser()
{
ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
if (m_ClassBrowser || !cfg->ReadBool(_T("/use_symbols_browser"), true))
return;
TRACE(_T("NativeParser::CreateClassBrowser(): Enter"));
m_ClassBrowserIsFloating = cfg->ReadBool(_T("/as_floating_window"), false);
if (m_ClassBrowserIsFloating)
{
m_ClassBrowser = new ClassBrowser(Manager::Get()->GetAppWindow(), this);
// make this a free floating/docking window
CodeBlocksDockEvent evt(cbEVT_ADD_DOCK_WINDOW);
evt.name = _T("SymbolsBrowser");
evt.title = _("Symbols browser");
evt.pWindow = m_ClassBrowser;
evt.dockSide = CodeBlocksDockEvent::dsRight;
evt.desiredSize.Set(200, 250);
evt.floatingSize.Set(200, 250);
evt.minimumSize.Set(150, 150);
evt.shown = true;
evt.hideable = true;
Manager::Get()->ProcessEvent(evt);
m_ClassBrowser->UpdateSash();
}
else
{
// make this a tab in projectmanager notebook
m_ClassBrowser = new ClassBrowser(Manager::Get()->GetProjectManager()->GetUI().GetNotebook(), this);
Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->AddPage(m_ClassBrowser, _("Symbols"));
m_ClassBrowser->UpdateSash();
}
// Dreaded DDE-open bug related: do not touch unless for a good reason
// TODO (Loaden) ? what's bug? I test it, it's works well now.
m_ClassBrowser->SetParser(m_Parser); // Also updates class browser
TRACE(_T("NativeParser::CreateClassBrowser(): Leave"));
}
void NativeParser::RemoveClassBrowser(cb_unused bool appShutDown)
{
if (!m_ClassBrowser)
return;
TRACE(_T("NativeParser::RemoveClassBrowser()"));
if (m_ClassBrowserIsFloating)
{
CodeBlocksDockEvent evt(cbEVT_REMOVE_DOCK_WINDOW);
evt.pWindow = m_ClassBrowser;
Manager::Get()->ProcessEvent(evt);
}
else
{
int idx = Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->GetPageIndex(m_ClassBrowser);
if (idx != -1)
Manager::Get()->GetProjectManager()->GetUI().GetNotebook()->RemovePage(idx);
}
m_ClassBrowser->Destroy();
m_ClassBrowser = NULL;
}
void NativeParser::UpdateClassBrowser()
{
if (!m_ClassBrowser)
return;
TRACE(_T("NativeParser::UpdateClassBrowser()"));
if ( m_Parser != m_TempParser
&& m_Parser->Done()
&& !Manager::IsAppShuttingDown())
{
m_ClassBrowser->UpdateClassBrowserView();
}
}
bool NativeParser::DoFullParsing(cbProject* project, ParserBase* parser)
{
if (!parser)
return false;
TRACE(_T("NativeParser::DoFullParsing(): Enter"));
if (!AddCompilerDirs(project, parser))
CCLogger::Get()->DebugLog(_T("NativeParser::DoFullParsing(): AddCompilerDirs failed!"));
if (!AddCompilerPredefinedMacros(project, parser))
CCLogger::Get()->DebugLog(_T("NativeParser::DoFullParsing(): AddCompilerPredefinedMacros failed!"));
if (!AddProjectDefinedMacros(project, parser))
CCLogger::Get()->DebugLog(_T("NativeParser::DoFullParsing(): AddProjectDefinedMacros failed!"));
// add per-project dirs
if (project)
{
AddIncludeDirsToParser(GetProjectSearchDirs(project),
project->GetBasePath(), parser);
}
// basically, we have to parse three level of files
// 1, the priority headers (include system priority headers and local priority headers)
// 2, non priority headers
// 3, sources (Note, surely they are local because they belong to a cbp project file)
//
// priority headers:
// Those files are normally header files and should be parsed before any other kinds of files.
// The idea of the priority file is that our parser does not do a full preprocessor(like expand
// the #include directive), but we did calculation on the conditional preprocessor directive.
// E.g. "#ifdefined XXX ....". Here, as an assumption, the macro definition of
// XXX should be already in TokenTree, otherwise, the default value (0) is used (this results the
// the conditional code branch was abandoned by the parser)
// If we put the file containing the definition of XXX as the priority file, then we can confirm
// our assumption, because this file will be parsed before any other files.
// e.g. "sdk.h" can be a local priority header, and <w32api.h> can be a system priority header
//
// non priority headers:
// These files are generally files with .h extension
// sources:
// These files are normally with .cpp, .cxx, .c extension in the C::B project
StringList priorityHeaders;
StringList nonPriorityLocalHeaders;
StringList localSources;
// read the user defined and our default priority files
ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
// NOTE (Morten#1#): Keep this in sync with files in the XRC file (settings.xrc) and ccoptionsdlg.cpp
const wxString default_priority_headers =
_T("<cstddef>, <w32api.h>, ")
_T("<wx/defs.h>, <wx/dlimpexp.h>, <wx/toplevel.h>, ")
_T("<boost/config.hpp>, <boost/filesystem/config.hpp>, ")
_T("\"pch.h\", \"sdk.h\", \"stdafx.h\"");
wxString priority_headers = cfg->Read(_T("/priority_headers"), default_priority_headers);
// if the configure file have more user added priority header files, append to the default ones.
if (!priority_headers.StartsWith(default_priority_headers))
{
wxStringTokenizer default_ph(default_priority_headers, _T(","));
wxArrayString default_phs;
while (default_ph.HasMoreTokens())
default_phs.Add(default_ph.GetNextToken().Trim(false).Trim(true));
wxStringTokenizer user(priority_headers, _T(","));
wxArrayString users;
while (user.HasMoreTokens())
users.Add(user.GetNextToken().Trim(false).Trim(true));
priority_headers = default_priority_headers;
for (size_t i = 0; i < users.GetCount(); ++i)
{
if (!users[i].IsEmpty() && default_phs.Index(users[i], false) == wxNOT_FOUND)
priority_headers.Append(_T(", ") + users[i]);
}
cfg->Write(_T("/priority_headers"), priority_headers);
}
typedef std::map<int, wxString> PriorityMap;
PriorityMap systemPriorityMap;//record the include file surround with <>
PriorityMap localPriorityMap; //record the include file surround with ""
int priorityCnt = 0; //file counters
// priority header files should be separated by comma and surround with either "" or <>
wxStringTokenizer tkz(priority_headers, _T(","));
while (tkz.HasMoreTokens())
{
wxString token = tkz.GetNextToken().Trim(false).Trim(true);
if (token.Len() <= 2) // error, at least "" or <> is required
continue;
if ( parser->Options().followLocalIncludes
&& token[0] == _T('"')
&& token[token.Len() - 1] == _T('"') )
{
// remove the surrounding double-quote, and add to localPriorityMap
localPriorityMap[++priorityCnt] = token.SubString(1, token.Len() - 2).Trim(false).Trim(true);
}
// TODO (ollydbg#2), should we check the Options().followGlobalIncludes here?
else if ( parser->Options().followLocalIncludes
&& token[0] == _T('<')
&& token[token.Len() - 1] == _T('>') )
{
// remove the surrounding <>
token = token.SubString(1, token.Len() - 2).Trim(false).Trim(true);
// try to see a priority header file is find in the include dirs
wxArrayString inc_file = parser->FindFileInIncludeDirs(token);
// if find any, put them in the systemPriorityMap (note, postfix a ", 1" string
for (size_t i = 0; i < inc_file.GetCount(); ++i)
systemPriorityMap[++priorityCnt] = inc_file[i] + _T(", 1");
}
}
if (project)
{
for (FilesList::const_iterator fl_it = project->GetFilesList().begin();
fl_it != project->GetFilesList().end(); ++fl_it)
{
ProjectFile* pf = *fl_it;
if (!pf)
continue;
// check the file types in the project files
ParserCommon::EFileType ft = ParserCommon::FileType(pf->relativeFilename);
if (ft == ParserCommon::ftHeader) // parse header files
{
bool isPriorityFile = false;
// localPriorityMap contains all the local priority header files
// if the project files matches on in localPriorityMap, then add them
// to systemPriorityMap with postfix string ", 0".
for (PriorityMap::iterator pm_it = localPriorityMap.begin();
pm_it != localPriorityMap.end(); ++pm_it)
{
if (pm_it->second.IsSameAs(pf->file.GetFullName(), false))
{
isPriorityFile = true;
systemPriorityMap[pm_it->first] = pf->file.GetFullPath() + _T(", 0");
localPriorityMap.erase(pm_it);
break;
}
}
// nonPriorityLocalHeaders are non priority but local files
if (!isPriorityFile)
nonPriorityLocalHeaders.push_back(pf->file.GetFullPath());
}
else if (ft == ParserCommon::ftSource) // parse source files
{
localSources.push_back(pf->file.GetFullPath());
}
}
}
// put all the priority files to a single container
for (PriorityMap::iterator pm_it = systemPriorityMap.begin(); pm_it != systemPriorityMap.end(); ++pm_it)
priorityHeaders.push_back(pm_it->second);
CCLogger::Get()->DebugLog(_T("NativeParser::DoFullParsing(): Adding three kind of files to batch-parser"));
// parse priority files
wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
if (!priorityHeaders.empty())
{
for (StringList::iterator sl_it = priorityHeaders.begin();
sl_it != priorityHeaders.end(); ++sl_it)
{
// strip the post fix string of ", 1"
wxString& file = *sl_it;
// if it is a system header file, then is should have a post fix string of ", 1"
// otherwise, it is a localHeaderFile with post fix string of ", 0"
const bool isSystemHeader = (file.Last() == _T('1'));
const int pos = file.Find(_T(','), true);
file = file.Left(pos);
CCLogger::Get()->DebugLog(F(_T("NativeParser::DoFullParsing(): Add priority header file: '%s'"), file.wx_str()));
// put the file to priority header containers
parser->AddPriorityHeaders(file, isSystemHeader);
}
CCLogger::Get()->DebugLog(F(_T("NativeParser::DoFullParsing(): Add %lu priority file(s) for project '%s'..."),
static_cast<unsigned long>(priorityHeaders.size()), prj.wx_str()));
}
if (!nonPriorityLocalHeaders.empty() || !localSources.empty())
{
CCLogger::Get()->DebugLog(F(_T("NativeParser::DoFullParsing(): Added %lu header&source file(s) for project '%s' to batch-parser..."),
static_cast<unsigned long>(nonPriorityLocalHeaders.size() + localSources.size()), prj.wx_str()));
// local header files (not priority header) added to Parser
parser->AddBatchParse(nonPriorityLocalHeaders);
// local source files added to Parser
parser->AddBatchParse(localSources);
}
TRACE(_T("NativeParser::DoFullParsing(): Leave"));
return true;
}
bool NativeParser::SwitchParser(cbProject* project, ParserBase* parser)
{
if (!parser || parser == m_Parser || GetParserByProject(project) != parser)
{
TRACE(_T("NativeParser::SwitchParser(): No need to / cannot switch."));
return false;
}
TRACE(_T("NativeParser::SwitchParser()"));
SetParser(parser); // Also updates class browser
wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
wxString log(F(_("Switch parser to project '%s'"), prj.wx_str()));
CCLogger::Get()->Log(log);
CCLogger::Get()->DebugLog(log);
return true;
}
void NativeParser::SetParser(ParserBase* parser)
{
if (m_Parser == parser)
return;
RemoveLastFunctionChildren(m_Parser->GetTokenTree(), m_LastFuncTokenIdx);
InitCCSearchVariables();
m_Parser = parser;
if (m_ClassBrowser)
m_ClassBrowser->SetParser(parser); // Also updates class browser
}
void NativeParser::ClearParsers()
{
TRACE(_T("NativeParser::ClearParsers()"));
if (m_ParserPerWorkspace)
{
while (!m_ParsedProjects.empty() && DeleteParser(*m_ParsedProjects.begin()))
;
}
else
{
while (!m_ParserList.empty() && DeleteParser(m_ParserList.begin()->first))
;
}
}
void NativeParser::RemoveObsoleteParsers()
{
TRACE(_T("NativeParser::RemoveObsoleteParsers(): Enter"));
ConfigManager* cfg = Manager::Get()->GetConfigManager(_T("code_completion"));
const size_t maxParsers = cfg->ReadInt(_T("/max_parsers"), 5);
wxArrayString removedProjectNames;
std::pair<cbProject*, ParserBase*> info = GetParserInfoByCurrentEditor();
while (m_ParserList.size() > maxParsers)
{
bool deleted = false;
for (ParserList::const_iterator it = m_ParserList.begin(); it != m_ParserList.end(); ++it)
{
if (it->second == info.second)
continue;
wxString prj = (it->first ? it->first->GetTitle() : _T("*NONE*"));
if ( DeleteParser(it->first) )
{
// Please note that DeleteParser() may erase one element of the m_ParserList, so
// do NOT use the constant iterator here again, as the element pointed by it may be
// destroyed in DeleteParser().
removedProjectNames.Add(prj);
deleted = true;
break;
}
}
if (!deleted)
break;
}
for (size_t i = 0; i < removedProjectNames.GetCount(); ++i)
{
wxString log(F(_("NativeParser::RemoveObsoleteParsers():Removed obsolete parser of '%s'"), removedProjectNames[i].wx_str()));
CCLogger::Get()->Log(log);
CCLogger::Get()->DebugLog(log);
}
TRACE(_T("NativeParser::RemoveObsoleteParsers(): Leave"));
}
std::pair<cbProject*, ParserBase*> NativeParser::GetParserInfoByCurrentEditor()
{
std::pair<cbProject*, ParserBase*> info(nullptr, nullptr);
cbEditor* editor = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
if ( editor ) //No need to check editor->GetFilename, because a built-in editor always have a filename
{
info.first = GetProjectByEditor(editor);
info.second = GetParserByProject(info.first);
}
return info;
}
#ifdef __WXMSW__
void NativeParser::SetTokenKindImage(int kind, const wxBitmap& bitmap, const wxBitmap& mask)
{
if (kind < PARSER_IMG_MIN || kind > PARSER_IMG_MAX)
return;
m_ImageList->Replace(kind, bitmap, mask);
}
#else
void NativeParser::SetTokenKindImage(int WXUNUSED(kind), const wxBitmap& WXUNUSED(bitmap), const wxBitmap& WXUNUSED(mask))
{
return;
}
#endif
void NativeParser::SetTokenKindImage(int kind, const wxBitmap& bitmap, cb_unused const wxColour& maskColour)
{
if (kind < PARSER_IMG_MIN || kind > PARSER_IMG_MAX)
return;
m_ImageList->Replace(kind, bitmap);//, maskColour);
}
void NativeParser::SetTokenKindImage(int kind, const wxIcon& icon)
{
if (kind < PARSER_IMG_MIN || kind > PARSER_IMG_MAX)
return;
m_ImageList->Replace(kind, icon);
}
void NativeParser::SetCBViewMode(const BrowserViewMode& mode)
{
m_Parser->ClassBrowserOptions().showInheritance = (mode == bvmInheritance) ? true : false;
UpdateClassBrowser();
}
void NativeParser::OnProjectLoadingHook(cbProject* project, TiXmlElement* elem, bool loading)
{
if (loading)
{
// Hook called when loading project file.
wxArrayString& pdirs = GetProjectSearchDirs(project);
TiXmlElement* CCConf = elem->FirstChildElement("code_completion");
if (CCConf)
{
TiXmlElement* pathsElem = CCConf->FirstChildElement("search_path");
while (pathsElem)
{
if (pathsElem->Attribute("add"))
{
wxString dir = cbC2U(pathsElem->Attribute("add"));
if (pdirs.Index(dir) == wxNOT_FOUND)
pdirs.Add(dir);
}
pathsElem = pathsElem->NextSiblingElement("search_path");
}
}
}
else
{
// Hook called when saving project file.
wxArrayString& pdirs = GetProjectSearchDirs(project);
// since rev4332, the project keeps a copy of the <Extensions> element
// and re-uses it when saving the project (so to avoid losing entries in it
// if plugins that use that element are not loaded atm).
// so, instead of blindly inserting the element, we must first check it's
// not already there (and if it is, clear its contents)
TiXmlElement* node = elem->FirstChildElement("code_completion");
if (!node)
node = elem->InsertEndChild(TiXmlElement("code_completion"))->ToElement();
if (node)
{
node->Clear();
for (size_t i = 0; i < pdirs.GetCount(); ++i)
{
TiXmlElement* path = node->InsertEndChild(TiXmlElement("search_path"))->ToElement();
if (path) path->SetAttribute("add", cbU2C(pdirs[i]));
}
}
}
}
// helper funcs
// Start an Artificial Intelligence (!) sequence to gather all the matching tokens..
// The actual AI is in FindAIMatches() below...
size_t NativeParser::AI(TokenIdxSet& result,
ccSearchData* searchData,
const wxString& lineText,
bool isPrefix,
bool caseSensitive,
TokenIdxSet* search_scope,
int caretPos)
{
m_LastAISearchWasGlobal = false;
m_LastAIGlobalSearch.Clear();
int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
if (pos < 0 || pos > searchData->control->GetLength())
return 0;
m_EditorStartWord = searchData->control->WordStartPosition(pos, true);
m_EditorEndWord = pos; //editor->GetControl()->WordEndPosition(pos, true);
int line = searchData->control->LineFromPosition(pos);
// Get the actual search text, such as "objA.m_aaa.m_bbb"
wxString actual_search(lineText);
if (actual_search.IsEmpty())
{
// Get the position at the start of current line
const int startPos = searchData->control->PositionFromLine(line);
actual_search = searchData->control->GetTextRange(startPos, pos).Trim();
}
// Do the whole job here
if (s_DebugSmartSense)
{
CCLogger::Get()->DebugLog(_T("AI() ========================================================="));
CCLogger::Get()->DebugLog(F(_T("AI() Doing AI for '%s':"), actual_search.wx_str()));
}
TRACE(_T("NativeParser::AI()"));
TokenTree* tree = m_Parser->GetTokenTree();
// find current function's namespace so we can include local scope's tokens
// we ' ll get the function's token (all matches) and add its parent namespace
TokenIdxSet proc_result;
size_t found_at = FindCurrentFunctionToken(searchData, proc_result, pos);
TokenIdxSet scope_result;
if (found_at)
FindCurrentFunctionScope(tree, proc_result, scope_result);
// add additional search scopes???
// for example, we are here:
/* void ClassA::FunctionB(int paraC){
m_aaa
*/
// then, ClassA should be added as a search_scope, the global scope should be added too.
// if search_scope is already defined, then, add scope_result to search_scope
// otherwise we just set search_scope as scope_result
if (!search_scope)
search_scope = &scope_result;
else
{
// add scopes
for (TokenIdxSet::const_iterator tis_it = scope_result.begin(); tis_it != scope_result.end(); ++tis_it)
search_scope->insert(*tis_it);
}
// remove non-namespace/class tokens
CleanupSearchScope(tree, search_scope);
// find all other matches
std::queue<ParserComponent> components;
BreakUpComponents(actual_search, components);
m_LastAISearchWasGlobal = components.size() <= 1;
if (!components.empty())
m_LastAIGlobalSearch = components.front().component;
ResolveExpression(tree, components, *search_scope, result, caseSensitive, isPrefix);
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(F(_T("AI() AI leave, returned %lu results"),static_cast<unsigned long>(result.size())));
return result.size();
}
// find a function where current caret located.
// We need to find extra class scope, otherwise, we will failed do the cc in a class declaration
size_t NativeParser::FindCurrentFunctionToken(ccSearchData* searchData, TokenIdxSet& result, int caretPos)
{
TokenIdxSet scope_result;
wxString procName;
wxString scopeName;
FindCurrentFunctionStart(searchData, &scopeName, &procName, nullptr, caretPos);
if (procName.IsEmpty())
return 0;
// add current scope
if (!scopeName.IsEmpty())
{
// _namespace ends with double-colon (::). remove it
scopeName.RemoveLast();
scopeName.RemoveLast();
// search for namespace
std::queue<ParserComponent> ns;
BreakUpComponents(scopeName, ns);
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
// No critical section needed in this recursive function!
// All functions that call this recursive FindAIMatches function, should already entered a critical section.
FindAIMatches(m_Parser->GetTokenTree(), ns, scope_result, -1,
true, true, false, tkNamespace | tkClass | tkTypedef);
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
}
// if no scope, use global scope
if (scope_result.empty())
scope_result.insert(-1);
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
for (TokenIdxSet::const_iterator tis_it = scope_result.begin(); tis_it != scope_result.end(); ++tis_it)
{
GenerateResultSet(m_Parser->GetTokenTree(), procName, *tis_it, result,
true, false, tkAnyFunction | tkClass);
}
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
return result.size();
}
// returns current function's position (not line) in the editor
int NativeParser::FindCurrentFunctionStart(ccSearchData* searchData,
wxString* nameSpace,
wxString* procName,
int* functionIndex,
int caretPos)
{
// cache last result for optimization
int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
if ((pos < 0) || (pos > searchData->control->GetLength()))
{
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Cannot determine position. caretPos=%d, control=%d"),
caretPos, searchData->control->GetCurrentPos()));
return -1;
}
TRACE(_T("NativeParser::FindCurrentFunctionStart()"));
const int curLine = searchData->control->LineFromPosition(pos) + 1;
if ( (curLine == m_LastLine)
&& ( (searchData->control == m_LastControl) && (!searchData->control->GetModify()) )
&& (searchData->file == m_LastFile) )
{
if (nameSpace) *nameSpace = m_LastNamespace;
if (procName) *procName = m_LastPROC;
if (functionIndex) *functionIndex = m_LastFunctionIndex;
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Cached namespace='%s', cached proc='%s' (returning %d)"),
m_LastNamespace.wx_str(), m_LastPROC.wx_str(), m_LastResult));
return m_LastResult;
}
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Looking for tokens in '%s'"),
searchData->file.wx_str()));
m_LastFile = searchData->file;
m_LastControl = searchData->control;
m_LastLine = curLine;
// we have all the tokens in the current file, then just do a loop on all
// the tokens, see if the line is in the token's imp.
TokenIdxSet result;
size_t num_results = m_Parser->FindTokensInFile(searchData->file, result, tkAnyFunction | tkClass);
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Found %lu results"), static_cast<unsigned long>(num_results)));
TokenTree* tree = m_Parser->GetTokenTree();
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
const int idx = GetTokenFromCurrentLine(tree, result, curLine, searchData->file);
const Token* token = tree->at(idx);
if (token)
{
// got it :)
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Current function: '%s' (at line %u)"),
token->DisplayName().wx_str(),
token->m_ImplLine));
m_LastNamespace = token->GetNamespace();
m_LastPROC = token->m_Name;
m_LastFunctionIndex = token->m_Index;
m_LastResult = searchData->control->PositionFromLine(token->m_ImplLine - 1);
// locate function's opening brace
if (token->m_TokenKind & tkAnyFunction)
{
while (m_LastResult < searchData->control->GetTextLength())
{
wxChar ch = searchData->control->GetCharAt(m_LastResult);
if (ch == _T('{'))
break;
else if (ch == 0)
{
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(_T("FindCurrentFunctionStart() Can't determine functions opening brace..."));
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
return -1;
}
++m_LastResult;
}
}
if (nameSpace) *nameSpace = m_LastNamespace;
if (procName) *procName = m_LastPROC;
if (functionIndex) *functionIndex = token->m_Index;
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(F(_T("FindCurrentFunctionStart() Namespace='%s', proc='%s' (returning %d)"),
m_LastNamespace.wx_str(), m_LastPROC.wx_str(), m_LastResult));
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
return m_LastResult;
}
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(_T("FindCurrentFunctionStart() Can't determine current function..."));
m_LastResult = -1;
return -1;
}
bool NativeParser::SkipWhitespaceForward(cbEditor* editor, int& pos)
{
if (!editor)
return false;
wxChar ch = editor->GetControl()->GetCharAt(pos);
int len = editor->GetControl()->GetLength() - 1;
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
{
while (pos < len && (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'))
{
++pos;
ch = editor->GetControl()->GetCharAt(pos);
}
return true;
}
return false;
}
bool NativeParser::SkipWhitespaceBackward(cbEditor* editor, int& pos)
{
if (!editor)
return false;
wxChar ch = editor->GetControl()->GetCharAt(pos);
if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
{
while (pos > 0 && (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'))
{
--pos;
ch = editor->GetControl()->GetCharAt(pos);
}
return true;
}
return false;
}
bool NativeParser::ParseUsingNamespace(ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos)
{
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(_T("ParseUsingNamespace() Parse file scope for \"using namespace\""));
TRACE(_T("NativeParser::ParseUsingNamespace()"));
int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
if (pos < 0 || pos > searchData->control->GetLength())
return false;
// Get the buffer from begin of the editor to the current caret position
wxString buffer = searchData->control->GetTextRange(0, pos);
return ParseBufferForUsingNamespace(buffer, search_scope);
}
bool NativeParser::ParseBufferForUsingNamespace(const wxString& buffer, TokenIdxSet& search_scope, bool bufferSkipBlocks)
{
wxArrayString ns;
m_Parser->ParseBufferForUsingNamespace(buffer, ns, bufferSkipBlocks);
TokenTree* tree = m_Parser->GetTokenTree();
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
for (size_t i = 0; i < ns.GetCount(); ++i)
{
std::queue<ParserComponent> components;
BreakUpComponents(ns[i], components);
int parentIdx = -1;
while (!components.empty())
{
ParserComponent pc = components.front();
components.pop();
int id = tree->TokenExists(pc.component, parentIdx, tkNamespace);
if (id == -1)
{
parentIdx = -1;
break;
}
parentIdx = id;
}
if (s_DebugSmartSense && parentIdx != -1)
{
const Token* token = tree->at(parentIdx);
if (token)
CCLogger::Get()->DebugLog(F(_T("ParseUsingNamespace() Found %s%s"),
token->GetNamespace().wx_str(), token->m_Name.wx_str()));
}
search_scope.insert(parentIdx);
}
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
return true;
}
bool NativeParser::ParseFunctionArguments(ccSearchData* searchData, int caretPos)
{
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(_T("ParseFunctionArguments() Parse function arguments"));
TRACE(_T("NativeParser::ParseFunctionArguments()"));
TokenIdxSet proc_result;
TokenTree* tree = m_Parser->GetTokenTree(); // the one used inside FindCurrentFunctionToken, FindAIMatches and GenerateResultSet
size_t found_at = FindCurrentFunctionToken(searchData, proc_result, caretPos);
if (!found_at)
{
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(_T("ParseFunctionArguments() Could not determine current function's namespace..."));
TRACE(_T("ParseFunctionArguments() Could not determine current function's namespace..."));
return false;
}
const int pos = caretPos == -1 ? searchData->control->GetCurrentPos() : caretPos;
const unsigned int curLine = searchData->control->LineFromPosition(pos) + 1;
bool locked = false;
for (TokenIdxSet::const_iterator tis_it = proc_result.begin(); tis_it != proc_result.end(); ++tis_it)
{
wxString buffer;
int initLine = -1;
int tokenIdx = -1;
if (locked)
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
locked = true;
const Token* token = tree->at(*tis_it);
if (!token)
continue;
if (curLine < token->m_ImplLineStart || curLine > token->m_ImplLineEnd)
continue;
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(_T("ParseFunctionArguments() + Function match: ") + token->m_Name);
TRACE(_T("ParseFunctionArguments() + Function match: ") + token->m_Name);
if (!token->m_Args.IsEmpty() && !token->m_Args.Matches(_T("()")))
{
buffer = token->m_Args;
// Now we have something like "(int my_int, const TheClass* my_class, float f)"
buffer.Remove(0, 1); // remove (
buffer.RemoveLast(); // remove )
// Now we have "int my_int, const TheClass* my_class, float f"
buffer.Replace(_T(","), _T(";")); // replace commas with semi-colons
// Now we have "int my_int; const TheClass* my_class; float f"
buffer << _T(';'); // aid parser ;)
// Finally we have "int my_int; const TheClass* my_class; float f;"
buffer.Trim();
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(F(_T("ParseFunctionArguments() Parsing arguments: \"%s\""), buffer.wx_str()));
if (!buffer.IsEmpty())
{
const int textLength= searchData->control->GetLength();
if (textLength == -1)
continue;
int paraPos = searchData->control->PositionFromLine(token->m_ImplLine - 1);
if (paraPos == -1)
continue;
while (paraPos < textLength && searchData->control->GetCharAt(paraPos++) != _T('('))
;
while (paraPos < textLength && searchData->control->GetCharAt(paraPos++) < _T(' '))
;
initLine = searchData->control->LineFromPosition(paraPos) + 1;
if (initLine == -1)
continue;
tokenIdx = token->m_Index;
}
}
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
locked = false;
if ( !buffer.IsEmpty()
&& !m_Parser->ParseBuffer(buffer, false, false, true, searchData->file, tokenIdx, initLine)
&& s_DebugSmartSense)
{
CCLogger::Get()->DebugLog(_T("ParseFunctionArguments() Error parsing arguments."));
}
}
if (locked)
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
return true;
}
bool NativeParser::ParseLocalBlock(ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos)
{
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(_T("ParseLocalBlock() Parse local block"));
TRACE(_T("NativeParser::ParseLocalBlock()"));
int parentIdx = -1;
int blockStart = FindCurrentFunctionStart(searchData, nullptr, nullptr, &parentIdx, caretPos);
int initLine = 0;
if (parentIdx != -1)
{
TokenTree* tree = m_Parser->GetTokenTree();
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
const Token* parent = tree->at(parentIdx);
if (parent && (parent->m_TokenKind & tkAnyFunction))
{
m_LastFuncTokenIdx = parent->m_Index;
initLine = parent->m_ImplLineStart;
}
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
if (!parent)
return false;
}
if (blockStart != -1)
{
cbStyledTextCtrl* stc = searchData->control;
// if we are in a function body, then blockStart points to the '{', so we just skip the '{'.
if (stc->GetCharAt(blockStart) == wxT('{'))
++blockStart;
const int pos = (caretPos == -1 ? stc->GetCurrentPos() : caretPos);
const int line = stc->LineFromPosition(pos);
const int blockEnd = stc->GetLineEndPosition(line);
if (blockEnd < 0 || blockEnd > stc->GetLength())
{
if (s_DebugSmartSense)
{
CCLogger::Get()->DebugLog(F(_T("ParseLocalBlock() ERROR blockEnd=%d and edLength=%d?!"),
blockEnd, stc->GetLength()));
}
return false;
}
if (blockStart >= blockEnd)
blockStart = blockEnd;
// wxString buffer = searchData->control->GetTextRange(blockStart, blockEnd);
wxString buffer;
// condense out-of-scope braces {...}
int scanPos = blockEnd;
for (int curPos = pos; curPos > blockStart; --curPos)
{
if (stc->GetCharAt(curPos) != wxT('}'))
continue;
const int style = stc->GetStyleAt(curPos);
if ( stc->IsString(style)
|| stc->IsCharacter(style)
|| stc->IsComment(style))
{
continue;
}
const int scopeStart = stc->BraceMatch(curPos);
if (scopeStart < blockStart)
break;
buffer.Prepend(stc->GetTextRange(curPos, scanPos));
int startLn = stc->LineFromPosition(scopeStart);
int endLn = stc->LineFromPosition(curPos);
if (startLn < endLn) // maintain correct line numbers for parsed tokens
buffer.Prepend( wxString(wxT('\n'), endLn - startLn) );
scanPos = scopeStart + 1;
curPos = scopeStart;
// condense out-of-scope for/if/while declarations
int prevCharIdx = scopeStart - 1;
for (; prevCharIdx > blockStart; --prevCharIdx)
{
if (stc->IsComment(stc->GetStyleAt(prevCharIdx)))
continue;
if (!wxIsspace(stc->GetCharAt(prevCharIdx)))
break;
}
if (stc->GetCharAt(prevCharIdx) != wxT(')'))
continue;
const int paramStart = stc->BraceMatch(prevCharIdx);
if (paramStart < blockStart)
continue;
for (prevCharIdx = paramStart - 1; prevCharIdx > blockStart; --prevCharIdx)
{
if (stc->IsComment(stc->GetStyleAt(prevCharIdx)))
continue;
if (!wxIsspace(stc->GetCharAt(prevCharIdx)))
break;
}
const wxString text = stc->GetTextRange(stc->WordStartPosition(prevCharIdx, true),
stc->WordEndPosition( prevCharIdx, true));
if (text == wxT("for"))
buffer.Prepend(wxT("(;;){"));
else if (text == wxT("if") || text == wxT("while"))
buffer.Prepend(wxT("(0){"));
else
continue;
startLn = stc->LineFromPosition(prevCharIdx);
endLn = stc->LineFromPosition(scopeStart);
if (startLn < endLn)
buffer.Prepend( wxString(wxT('\n'), endLn - startLn) );
curPos = stc->WordStartPosition(prevCharIdx, true);
scanPos = stc->WordEndPosition( prevCharIdx, true);
}
buffer.Prepend(stc->GetTextRange(blockStart, scanPos));
buffer.Trim();
ParseBufferForUsingNamespace(buffer, search_scope, false);
if ( !buffer.IsEmpty()
&& !m_Parser->ParseBuffer(buffer, false, false, true, searchData->file, m_LastFuncTokenIdx, initLine) )
{
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(_T("ParseLocalBlock() ERROR parsing block:\n") + buffer);
}
else
{
if (s_DebugSmartSense)
{
CCLogger::Get()->DebugLog(F(_T("ParseLocalBlock() Block:\n%s"), buffer.wx_str()));
CCLogger::Get()->DebugLog(_T("ParseLocalBlock() Local tokens:"));
TokenTree* tree = m_Parser->GetTokenTree();
CC_LOCKER_TRACK_TT_MTX_LOCK(s_TokenTreeMutex)
for (size_t i = 0; i < tree->size(); ++i)
{
const Token* token = tree->at(i);
if (token && token->m_IsTemp)
{
wxString log(wxString::Format(_T(" + %s (%d)"), token->DisplayName().wx_str(), token->m_Index));
const Token* parent = tree->at(token->m_ParentIndex);
if (parent)
log += wxString::Format(_T("; Parent = %s (%d)"), parent->m_Name.wx_str(), token->m_ParentIndex);
CCLogger::Get()->DebugLog(log);
}
}
CC_LOCKER_TRACK_TT_MTX_UNLOCK(s_TokenTreeMutex)
}
return true;
}
}
else
{
if (s_DebugSmartSense)
CCLogger::Get()->DebugLog(_T("ParseLocalBlock() Could not determine current block start..."));
}
return false;
}
bool NativeParser::AddCompilerDirs(cbProject* project, ParserBase* parser)
{
if (!parser)
return false;
TRACE(_T("NativeParser::AddCompilerDirs(): Enter"));
// If there is no project, work on default compiler
if (!project)
{
Compiler* compiler = CompilerFactory::GetDefaultCompiler();
if (compiler)
{
// these dirs were the user's compiler include search dirs
AddIncludeDirsToParser(compiler->GetIncludeDirs(), wxEmptyString, parser);
if (compiler->GetID().Contains(_T("gcc")))
AddGCCCompilerDirs(compiler->GetMasterPath(), compiler->GetPrograms().CPP, parser);
}
TRACE(_T("NativeParser::AddCompilerDirs(): Leave"));
return true;
}
// Otherwise (if there is a project), work on the project's compiler...
wxString base = project->GetBasePath();
parser->AddIncludeDir(base); // add project's base path
TRACE(_T("NativeParser::AddCompilerDirs(): Adding project base dir to parser: ") + base);
// ...so we can access post-processed project's search dirs
Compiler* compiler = CompilerFactory::GetCompiler(project->GetCompilerID());
cb::shared_ptr<CompilerCommandGenerator> generator(compiler ? compiler->GetCommandGenerator(project) : nullptr);
if (compiler && generator)
generator->Init(project);
// get project include dirs
AddIncludeDirsToParser(project->GetIncludeDirs(), base, parser);
// alloc array for project compiler AND "no. of targets" times target compilers
int nCompilers = 1 + project->GetBuildTargetsCount();
Compiler** Compilers = new Compiler* [nCompilers];
memset(Compilers, 0, sizeof(Compiler*) * nCompilers);
nCompilers = 0; // reset , use as insert index in the next for loop
// get targets include dirs
for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
{
ProjectBuildTarget* target = project->GetBuildTarget(i);
if (target && target->SupportsCurrentPlatform())
{
// post-processed search dirs (from build scripts)
if (compiler && generator)
AddIncludeDirsToParser(generator->GetCompilerSearchDirs(target), base, parser);
// apply target vars
// target->GetCustomVars().ApplyVarsToEnvironment();
AddIncludeDirsToParser(target->GetIncludeDirs(), base, parser);
// get the compiler
wxString CompilerIndex = target->GetCompilerID();
Compiler* tgtCompiler = CompilerFactory::GetCompiler(CompilerIndex);
if (tgtCompiler)
{
Compilers[nCompilers] = tgtCompiler;
++nCompilers;
}
} // if (target)
} // end loop over the targets
// add the project compiler to the array of compilers
if (compiler)
{ // note it might be possible that this compiler is already in the list
// no need to worry since the compiler list of the parser will filter out duplicate
// entries in the include dir list
Compilers[nCompilers++] = compiler;
}
// add compiler include dirs
for (int idxCompiler = 0; idxCompiler < nCompilers; ++idxCompiler)
{
compiler = Compilers[idxCompiler];
if (!compiler) continue;
AddIncludeDirsToParser(compiler->GetIncludeDirs(), base, parser);
// find out which compiler, if gnu, do the special trick
// to find it's internal include paths
// but do only once per C::B session, thus cache for later calls
wxString CompilerID = compiler->GetID();
if (CompilerID.Contains(_T("gcc")))
AddGCCCompilerDirs(Compilers[idxCompiler]->GetMasterPath(), compiler->GetPrograms().CPP, parser);
} // end of while loop over the found compilers
if (!nCompilers)
CCLogger::Get()->DebugLog(_T("NativeParser::AddCompilerDirs(): No compilers found!"));
delete [] Compilers;
TRACE(_T("NativeParser::AddCompilerDirs(): Leave"));
return true;
}
bool NativeParser::AddCompilerPredefinedMacros(cbProject* project, ParserBase* parser)
{
if (!parser)
return false;
if (!parser->Options().wantPreprocessor)
return true;
TRACE(_T("NativeParser::AddCompilerPredefinedMacros(): Enter"));
// Default compiler is used for for single file parser (non project)
wxString compilerId = project ? project->GetCompilerID() : CompilerFactory::GetDefaultCompilerID();
wxString defs;
// gcc
if (compilerId.Contains(_T("gcc")))
{
if ( !AddCompilerPredefinedMacrosGCC(compilerId, project, defs) )
return false;
}
// vc
else if (compilerId.StartsWith(_T("msvc")))
{
if ( !AddCompilerPredefinedMacrosVC(compilerId, defs) )
return false;
}
TRACE(_T("NativeParser::AddCompilerPredefinedMacros(): Add compiler predefined preprocessor macros:\n%s"), defs.wx_str());
parser->AddPredefinedMacros(defs);
TRACE(_T("NativeParser::AddCompilerPredefinedMacros(): Leave"));
return true;
}
bool NativeParser::AddCompilerPredefinedMacrosGCC(const wxString& compilerId, cbProject* project, wxString& defs)
{
Compiler* compiler = CompilerFactory::GetCompiler(compilerId);
if (!compiler)
return false;
wxString masterPath = compiler->GetMasterPath();
Manager::Get()->GetMacrosManager()->ReplaceMacros(masterPath);
const wxString cpp_compiler = masterPath + _T("\\bin\\") + compiler->GetPrograms().CPP;
if ( !wxFileName::FileExists(cpp_compiler) )
return false;
static std::map<wxString, wxString> gccDefsMap;
if (gccDefsMap[cpp_compiler].IsEmpty())
{
static bool reentry = false;
if (reentry)
return false;
#ifdef __WXMSW__
const wxString args(_T(" -E -dM -x c++ nul"));
#else
const wxString args(_T(" -E -dM -x c++ /dev/null"));
#endif
wxArrayString output;
reentry = true;
if ( wxExecute(cpp_compiler + args, output, wxEXEC_SYNC | wxEXEC_NODISABLE) == -1 )
{
TRACE(_T("AddCompilerPredefinedMacrosGCC::wxExecute failed!"));
reentry = false;
return false;
}
reentry = false;
// wxExecute can be a long action and C::B might have been shutdown in the meantime...
if ( Manager::IsAppShuttingDown() )
return false;
wxString& gccDefs = gccDefsMap[cpp_compiler];
for (size_t i = 0; i < output.Count(); ++i)
gccDefs += output[i] + _T("\n");
}
static const wxString cxx0xOption(_T("-std=c++0x"));
static const wxString gnu0xOption(_T("-std=gnu++0x"));
bool useCxx0x = false;
if (project)
{
const wxArrayString& options = project->GetCompilerOptions();
if ( options.Index(cxx0xOption) != wxNOT_FOUND
|| options.Index(gnu0xOption) != wxNOT_FOUND )
{
useCxx0x = true;
}
else
{
for (int i = 0; i < project->GetBuildTargetsCount(); ++i)
{
ProjectBuildTarget* target = project->GetBuildTarget(i);
const wxArrayString& targetOptions = target->GetCompilerOptions();
if ( targetOptions.Index(cxx0xOption) != wxNOT_FOUND
|| targetOptions.Index(gnu0xOption) != wxNOT_FOUND )
{
useCxx0x = true;
break;
}
}
}
}
if (useCxx0x)
defs = gccDefsMap[cpp_compiler] + _T("#define __GXX_EXPERIMENTAL_CXX0X__ 1\n");
else
defs = gccDefsMap[cpp_compiler];
return true;
}
bool NativeParser::AddCompilerPredefinedMacrosVC(const wxString& compilerId, wxString& defs)
{
static wxString vcDefs;
static bool firstExecute = true;
if (firstExecute)
{
firstExecute = false;
Compiler* compiler = CompilerFactory::GetCompiler(compilerId);
if (!compiler)
return false;
wxString masterPath = compiler->GetMasterPath();
Manager::Get()->GetMacrosManager()->ReplaceMacros(masterPath);
const wxString c_compiler = masterPath + _T("\\bin\\") + compiler->GetPrograms().C;
if ( !wxFileName::FileExists(c_compiler) )
return false;
static bool reentry = false;
if (reentry)
return false;
wxArrayString output, error;
reentry = true;
// Just run the compiler which shows e.g.:
// "Microsoft (R) C/C++ Optimizing Compiler Version 12.00.8804, for x86"
// ...and extract platform information (32/64 bit) and compiler version (12) out of it
if ( wxExecute(c_compiler, output, error, wxEXEC_SYNC | wxEXEC_NODISABLE) == -1 )
{
TRACE(_T("AddCompilerPredefinedMacrosVC::wxExecute failed!"));
reentry = false;
return false;
}
reentry = false;
// wxExecute can be a long action and C::B might have been shutdown in the meantime...
if ( Manager::IsAppShuttingDown() )
return false;
if (error.IsEmpty())
{
TRACE(_T("AddCompilerPredefinedMacrosVC:: Can't get pre-defined macros for MSVC."));
return false;
}
wxString str = error[0];
wxString tmp(_T("Microsoft (R) "));
int pos = str.Find(tmp);
if (pos != wxNOT_FOUND)
{
wxString bit = str.Mid(pos + tmp.Length(), 2);
if (bit == _T("32"))
defs += _T("#define _WIN32") _T("\n");
else if (bit == _T("64"))
defs += _T("#define _WIN64") _T("\n");
}
tmp = _T("Compiler Version ");
pos = str.Find(tmp);
if (pos != wxNOT_FOUND)
{
wxString ver = str.Mid(pos + tmp.Length(), 4); // is i.e. 12.0
pos = ver.Find(_T('.'));
if (pos != wxNOT_FOUND)
{
// out of "12.0" make "1200" for the #define
ver[pos] = ver[pos + 1]; // move the mintor version first number to the dot position
ver[pos + 1] = _T('0'); // add another zero at the end
defs += _T("#define _MSC_VER ") + ver;
// Known to now:
// MSVC++ 11.0 _MSC_VER = 1700 (Visual Studio 2012)
// MSVC++ 10.0 _MSC_VER = 1600 (Visual Studio 2010)
// MSVC++ 9.0 _MSC_VER = 1500 (Visual Studio 2008)
// MSVC++ 8.0 _MSC_VER = 1400 (Visual Studio 2005)
// MSVC++ 7.1 _MSC_VER = 1310 (Visual Studio 2003)
// MSVC++ 7.0 _MSC_VER = 1300
// MSVC++ 6.0 _MSC_VER = 1200
// MSVC++ 5.0 _MSC_VER = 1100
}
}
}
defs = vcDefs;
return true;
}
bool NativeParser::AddProjectDefinedMacros(cbProject* project, ParserBase* parser)
{
if (!parser)
return false;
if (!project)
return true;
TRACE(_T("NativeParser::AddProjectDefinedMacros(): Enter"));
wxString compilerId = project->GetCompilerID();
wxString param;
if (compilerId.Contains(_T("gcc")))
param = _T("-D");
else if (compilerId.StartsWith(_T("msvc")))
param = _T("/D");
if (param.IsEmpty())
return true;
wxString defs;
wxArrayString opts = project->GetCompilerOptions();
ProjectBuildTarget* target = project->GetBuildTarget(project->GetActiveBuildTarget());
if (target != NULL)
{
wxArrayString targetOpts = target->GetCompilerOptions();
for (size_t i = 0; i < targetOpts.GetCount(); ++i)
opts.Add(targetOpts[i]);
}
for (size_t i = 0; i < opts.GetCount(); ++i)
{
wxString def = opts[i];
Manager::Get()->GetMacrosManager()->ReplaceMacros(def);
if (!def.StartsWith(param))
continue;
def = def.Right(def.Length() - param.Length());
int pos = def.Find(_T('='));
if (pos != wxNOT_FOUND)
def[pos] = _T(' ');
defs += _T("#define ") + def + _T("\n");
}
TRACE(_T("Add project and current build target defined preprocessor macros:\n%s"), defs.wx_str());
parser->AddPredefinedMacros(defs);
TRACE(_T("NativeParser::AddProjectDefinedMacros(): Leave"));
return true;
}
// These dirs are the built-in search dirs of the compiler itself (GCC).
// Such as when you install your MinGW GCC in E:/code/MinGW/bin
// The buildin search dir may contains: E:/code/MinGW/include
const wxArrayString& NativeParser::GetGCCCompilerDirs(const wxString &cpp_compiler)
{
// keep the gcc compiler path's once if found across C::B session
// makes opening workspaces a *lot* faster by avoiding endless calls to the compiler
static std::map<wxString, wxArrayString> dirs;
if (!dirs[cpp_compiler].IsEmpty())
return dirs[cpp_compiler];
// wxExecute can be a long action and C::B might have been shutdown in the meantime...
// This is here, to protect at re-entry:
if (Manager::IsAppShuttingDown())
return dirs[cpp_compiler];
TRACE(_T("NativeParser::GetGCCCompilerDirs(): Enter"));
// for starters , only do this for gnu compiler
//CCLogger::Get()->DebugLog(_T("CompilerID ") + CompilerID);
//
// Windows: mingw32-g++ -v -E -x c++ nul
// Linux : g++ -v -E -x c++ /dev/null
// do the trick only for c++, not needed then for C (since this is a subset of C++)
// let's construct the command
// use a null file handler
// both works fine in Windows and Linux
wxString Command(cpp_compiler + _T(" -v -E -x c++ /dev/null"));
if (platform::windows)
Command = cpp_compiler + _T(" -v -E -x c++ nul"); // on Windows, its different
static bool flag = false;
if (flag)
return dirs[cpp_compiler];
// action time (everything shows up on the error stream
wxArrayString Output, Errors;
flag = true;
if ( wxExecute(Command, Output, Errors, wxEXEC_SYNC | wxEXEC_NODISABLE) == -1 )
{
TRACE(_T("NativeParser::GetGCCCompilerDirs(): GetGCCCompilerDirs::wxExecute failed!"));
flag = false;
return dirs[cpp_compiler];
}
flag = false;
// wxExecute can be a long action and C::B might have been shutdown in the meantime...
// This is here, to protect a long run:
if ( Manager::IsAppShuttingDown() )
return dirs[cpp_compiler];
// start from "#include <...>", and the path followed
// let's hope this does not change too quickly, otherwise we need
// to adjust our search code (for several versions ...)
bool start = false;
for (size_t idxCount = 0; idxCount < Errors.GetCount(); ++idxCount)
{
wxString path = Errors[idxCount].Trim(true).Trim(false);
if (!start)
{
if (!path.StartsWith(_T("#include <...>")))
continue;
path = Errors[++idxCount].Trim(true).Trim(false);
start = true;
}
wxFileName fname(path, wxEmptyString);
fname.Normalize();
fname.SetVolume(fname.GetVolume().MakeUpper());
if (!fname.DirExists())
break;
CCLogger::Get()->DebugLog(_T("NativeParser::GetGCCCompilerDirs(): Caching GCC default include dir: ") + fname.GetPath());
dirs[cpp_compiler].Add(fname.GetPath());
}
TRACE(_T("NativeParser::GetGCCCompilerDirs(): Leave"));
return dirs[cpp_compiler];
}
void NativeParser::AddGCCCompilerDirs(const wxString& masterPath, const wxString& compilerCpp, ParserBase* parser)
{
wxFileName fn(wxEmptyString, compilerCpp);
wxString masterPathNoMacros(masterPath);
Manager::Get()->GetMacrosManager()->ReplaceMacros(masterPathNoMacros);
fn.SetPath(masterPathNoMacros);
fn.AppendDir(_T("bin"));
const wxArrayString& gccDirs = GetGCCCompilerDirs(fn.GetFullPath());
TRACE(_T("NativeParser::AddGCCCompilerDirs(): Adding %lu cached gcc dirs to parser..."), static_cast<unsigned long>(gccDirs.GetCount()));
for (size_t i=0; i<gccDirs.GetCount(); ++i)
{
parser->AddIncludeDir(gccDirs[i]);
TRACE(_T("NativeParser::AddGCCCompilerDirs(): Adding cached compiler dir to parser: ") + gccDirs[i]);
}
}
void NativeParser::AddIncludeDirsToParser(const wxArrayString& dirs, const wxString& base, ParserBase* parser)
{
for (unsigned int i = 0; i < dirs.GetCount(); ++i)
{
wxString dir = dirs[i];
Manager::Get()->GetMacrosManager()->ReplaceMacros(dir);
if ( !base.IsEmpty() )
{
wxFileName fn(dir);
if ( NormalizePath(fn, base) )
{
parser->AddIncludeDir(fn.GetFullPath());
TRACE(_T("NativeParser::AddIncludeDirsToParser(): Adding directory to parser: ") + fn.GetFullPath());
}
else
CCLogger::Get()->DebugLog(F(_T("NativeParser::AddIncludeDirsToParser(): Error normalizing path: '%s' from '%s'"), dir.wx_str(), base.wx_str()));
}
else
parser->AddIncludeDir(dir); // no base path, nothing to normalise
}
}
void NativeParser::OnParserStart(wxCommandEvent& event)
{
TRACE(_T("NativeParser::OnParserStart(): Enter"));
cbProject* project = static_cast<cbProject*>(event.GetClientData());
wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
const ParserCommon::ParserState state = static_cast<ParserCommon::ParserState>(event.GetInt());
switch (state)
{
case ParserCommon::ptCreateParser:
CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart(): Starting batch parsing for project '%s'..."), prj.wx_str()));
{
std::pair<cbProject*, ParserBase*> info = GetParserInfoByCurrentEditor();
if (info.second && m_Parser != info.second)
{
CCLogger::Get()->DebugLog(_T("NativeParser::OnParserStart(): Start switch from OnParserStart::ptCreateParser"));
SwitchParser(info.first, info.second); // Calls SetParser() which also calls UpdateClassBrowserView()
}
}
break;
case ParserCommon::ptAddFileToParser:
CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart(): Starting add file parsing for project '%s'..."), prj.wx_str()));
break;
case ParserCommon::ptReparseFile:
CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart(): Starting re-parsing for project '%s'..."), prj.wx_str()));
break;
case ParserCommon::ptUndefined:
if (event.GetString().IsEmpty())
CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart(): Batch parsing error in project '%s'"), prj.wx_str()));
else
CCLogger::Get()->DebugLog(F(_("NativeParser::OnParserStart(): %s in project '%s'"), event.GetString().wx_str(), prj.wx_str()));
return;
default:
break;
}
event.Skip();
TRACE(_T("NativeParser::OnParserStart(): Leave"));
}
void NativeParser::OnParserEnd(wxCommandEvent& event)
{
TRACE(_T("NativeParser::OnParserEnd(): Enter"));
ParserBase* parser = reinterpret_cast<ParserBase*>(event.GetEventObject());
cbProject* project = static_cast<cbProject*>(event.GetClientData());
wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
const ParserCommon::ParserState state = static_cast<ParserCommon::ParserState>(event.GetInt());
switch (state)
{
case ParserCommon::ptCreateParser:
{
wxString log(F(_("NativeParser::OnParserEnd(): Project '%s' parsing stage done!"), prj.wx_str()));
CCLogger::Get()->Log(log);
CCLogger::Get()->DebugLog(log);
}
break;
case ParserCommon::ptAddFileToParser:
break;
case ParserCommon::ptReparseFile:
if (parser != m_Parser)
{
std::pair<cbProject*, ParserBase*> info = GetParserInfoByCurrentEditor();
if (info.second && info.second != m_Parser)
{
CCLogger::Get()->DebugLog(_T("NativeParser::OnParserEnd(): Start switch from OnParserEnd::ptReparseFile"));
SwitchParser(info.first, info.second); // Calls SetParser() which also calls UpdateClassBrowserView()
}
}
break;
case ParserCommon::ptUndefined:
CCLogger::Get()->DebugLog(F(_T("NativeParser::OnParserEnd(): Parser event handling error of project '%s'"), prj.wx_str()));
return;
default:
break;
}
if (!event.GetString().IsEmpty())
CCLogger::Get()->DebugLog(event.GetString());
UpdateClassBrowser();
// In this case, the parser will record all the cbprojects' token, so this will start parsing
// the next cbproject.
TRACE(_T("NativeParser::OnParserEnd(): Starting m_TimerParsingOneByOne."));
m_TimerParsingOneByOne.Start(500, wxTIMER_ONE_SHOT);
// both NativeParser and CodeCompletion class need to handle this event
event.Skip();
TRACE(_T("NativeParser::OnParserEnd(): Leave"));
}
void NativeParser::OnParsingOneByOneTimer(cb_unused wxTimerEvent& event)
{
TRACE(_T("NativeParser::OnParsingOneByOneTimer(): Enter"));
std::pair<cbProject*, ParserBase*> info = GetParserInfoByCurrentEditor();
if (m_ParserPerWorkspace)
{
// If there is no parser and an active editor file can be obtained, parse the file according the active project
if (!info.second && Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor())
{
// NOTE (Morten#1#): Shouldn't this actually be a temp parser??? I think this screws things with re-opening files on load of a projects...
AddProjectToParser(info.first);
CCLogger::Get()->DebugLog(_T("NativeParser::OnParsingOneByOneTimer(): Add foreign active editor to current active project's parser."));
}
// Otherwise, there is a parser already present
else
{
// First: try to parse the active project (if any)
cbProject* activeProject = Manager::Get()->GetProjectManager()->GetActiveProject();
if (m_ParsedProjects.find(activeProject) == m_ParsedProjects.end())
{
AddProjectToParser(activeProject);
CCLogger::Get()->DebugLog(_T("NativeParser::OnParsingOneByOneTimer(): Add new (un-parsed) active project to parser."));
}
// Else: add remaining projects one-by-one (if any)
else
{
ProjectsArray* projs = Manager::Get()->GetProjectManager()->GetProjects();
for (size_t i = 0; i < projs->GetCount(); ++i)
{
// Only add, if the project is not already parsed
if (m_ParsedProjects.find(projs->Item(i)) == m_ParsedProjects.end())
{
AddProjectToParser(projs->Item(i));
CCLogger::Get()->DebugLog(_T("NativeParser::OnParsingOneByOneTimer(): Add additional (next) project to parser."));
break;
}
}
}
}
}
else if (info.first && !info.second)
{
info.second = CreateParser(info.first);
if (info.second && info.second != m_Parser)
{
CCLogger::Get()->DebugLog(_T("NativeParser::OnParsingOneByOneTimer(): Start switch from OnParsingOneByOneTimer"));
SwitchParser(info.first, info.second); // Calls SetParser() which also calls UpdateClassBrowserView()
}
}
TRACE(_T("NativeParser::OnParsingOneByOneTimer(): Leave"));
}
void NativeParser::OnEditorActivated(EditorBase* editor)
{
cbEditor* curEditor = Manager::Get()->GetEditorManager()->GetBuiltinEditor(editor);
if (!curEditor)
return;
const wxString& activatedFile = editor->GetFilename();
if ( !wxFile::Exists(activatedFile) )
return;
cbProject* project = GetProjectByEditor(curEditor);
const int pos = m_StandaloneFiles.Index(activatedFile);
if (project && pos != wxNOT_FOUND)
{
m_StandaloneFiles.RemoveAt(pos);
if (m_StandaloneFiles.IsEmpty())
DeleteParser(NULL);
else
RemoveFileFromParser(NULL, activatedFile);
}
ParserBase* parser = GetParserByProject(project);
if (!parser)
{
ParserCommon::EFileType ft = ParserCommon::FileType(activatedFile);
if (ft != ParserCommon::ftOther && (parser = CreateParser(project)))
{
if (!project && AddFileToParser(project, activatedFile, parser) )
{
wxFileName file(activatedFile);
parser->AddIncludeDir(file.GetPath());
m_StandaloneFiles.Add(activatedFile);
}
}
else
parser = m_TempParser; // do *not* instead by SetParser(m_TempParser)
}
else if (!project)
{
if ( !parser->IsFileParsed(activatedFile)
&& m_StandaloneFiles.Index(activatedFile) == wxNOT_FOUND
&& AddFileToParser(project, activatedFile, parser) )
{
wxFileName file(activatedFile);
parser->AddIncludeDir(file.GetPath());
m_StandaloneFiles.Add(activatedFile);
}
}
if (parser != m_Parser)
{
CCLogger::Get()->DebugLog(_T("Start switch from OnEditorActivatedTimer"));
SwitchParser(project, parser); // Calls SetParser() which also calls UpdateClassBrowserView()
}
if (m_ClassBrowser)
{
if (m_Parser->ClassBrowserOptions().displayFilter == bdfFile)
m_ClassBrowser->UpdateClassBrowserView(true); // check header and implementation file swap
else if ( m_ParserPerWorkspace // project view only available in case of one parser per WS
&& m_Parser->ClassBrowserOptions().displayFilter == bdfProject)
m_ClassBrowser->UpdateClassBrowserView();
}
}
void NativeParser::OnEditorClosed(EditorBase* editor)
{
// the caller of the function should guarantee its a built-in editor
wxString filename = editor->GetFilename();
const int pos = m_StandaloneFiles.Index(filename);
if (pos != wxNOT_FOUND)
{
m_StandaloneFiles.RemoveAt(pos);
if (m_StandaloneFiles.IsEmpty())
DeleteParser(NULL);
else
RemoveFileFromParser(NULL, filename);
}
}
void NativeParser::InitCCSearchVariables()
{
m_LastControl = nullptr;
m_LastFunctionIndex = -1;
m_EditorStartWord = -1;
m_EditorEndWord = -1;
m_LastLine = -1;
m_LastResult = -1;
m_LastFile.Clear();
m_LastNamespace.Clear();
m_LastPROC.Clear();
Reset();
}
void NativeParser::AddProjectToParser(cbProject* project)
{
wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
ParserBase* parser = GetParserByProject(project);
if (parser)
return;
if (m_ParsedProjects.empty())
return;
m_ParsedProjects.insert(project);
parser = GetParserByProject(project);
if (!parser)
return;
else if (!parser->UpdateParsingProject(project))
{
m_ParsedProjects.erase(project);
return;
}
// TODO (ollydbg#1#) did exactly the same thing as the function NativeParser::DoFullParsing()?
wxString log(F(_("NativeParser::AddProjectToParser(): Add project (%s) to parser"), prj.wx_str()));
CCLogger::Get()->Log(log);
CCLogger::Get()->DebugLog(log);
if (!AddCompilerDirs(project, parser))
CCLogger::Get()->DebugLog(_T("NativeParser::AddProjectToParser(): AddCompilerDirs failed!"));
if (!AddCompilerPredefinedMacros(project, parser))
CCLogger::Get()->DebugLog(_T("NativeParser::AddProjectToParser(): AddCompilerPredefinedMacros failed!"));
if (!AddProjectDefinedMacros(project, parser))
CCLogger::Get()->DebugLog(_T("NativeParser::AddProjectToParser(): AddProjectDefinedMacros failed!"));
if (project)
{
size_t fileCount = 0;
for (FilesList::const_iterator fl_it = project->GetFilesList().begin(); fl_it != project->GetFilesList().end(); ++fl_it)
{
ProjectFile* pf = *fl_it;
if (pf && FileTypeOf(pf->relativeFilename) == ftHeader)
{
if (AddFileToParser(project, pf->file.GetFullPath(), parser))
++fileCount;
}
}
for (FilesList::const_iterator fl_it = project->GetFilesList().begin(); fl_it != project->GetFilesList().end(); ++fl_it)
{
ProjectFile* pf = *fl_it;
if (pf && FileTypeOf(pf->relativeFilename) == ftSource)
{
if (AddFileToParser(project, pf->file.GetFullPath(), parser))
fileCount++;
}
}
CCLogger::Get()->DebugLog(F(_("NativeParser::AddProjectToParser(): Done adding %lu files of project (%s) to parser."), static_cast<unsigned long>(fileCount), prj.wx_str()));
}
else
{
EditorBase* editor = Manager::Get()->GetEditorManager()->GetActiveEditor();
if (editor && AddFileToParser(project, editor->GetFilename(), parser))
{
wxFileName file(editor->GetFilename());
parser->AddIncludeDir(file.GetPath());
m_StandaloneFiles.Add(editor->GetFilename());
CCLogger::Get()->DebugLog(F(_("NativeParser::AddProjectToParser(): Done adding stand-alone file (%s) of editor to parser."), editor->GetFilename().wx_str()));
}
}
}
bool NativeParser::RemoveProjectFromParser(cbProject* project)
{
ParserBase* parser = GetParserByProject(project);
if (!parser)
return false;
// Remove from the cbProject set
m_ParsedProjects.erase(project);
if (!project || m_ParsedProjects.empty())
return true;
wxString prj = (project ? project->GetTitle() : _T("*NONE*"));
wxString log(F(_("Remove project (%s) from parser"), prj.wx_str()));
CCLogger::Get()->Log(log);
CCLogger::Get()->DebugLog(log);
for (FilesList::const_iterator fl_it = project->GetFilesList().begin(); fl_it != project->GetFilesList().end(); ++fl_it)
{
ProjectFile* pf = *fl_it;
if (pf && ParserCommon::FileType(pf->relativeFilename) != ParserCommon::ftOther)
RemoveFileFromParser(project, pf->file.GetFullPath());
}
return true;
}
/// Decide whether it's a number
bool NativeParser::IsNumber(int &pos) {
cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
if (!ed || !m_Parser->Done()) {
return false;
}
ccSearchData searchData = { ed->GetControl(), ed->GetFilename() };
while(pos-- > 0) {
const wxChar ch = searchData.control->GetCharAt(pos);
int dotnum = 0;
if (ch >= _T('0') && ch <= _T('9')) {
continue;
}
else if (ch == _T('.')) {
dotnum ++;
if (dotnum > 1) {
return false;
}
continue;
}
else if (ch == _T(' ') || ch == _T('+') || ch == _T('-') ||ch == _T('/') ||ch == _T('>') ||ch == _T('<')
|| ch == _T('=') || ch == _T('*') || ch == _T('%') ) {
pos ++;
return true;
}
else {
pos++;
return false;
}
}
}
/// Added by Fujun Luan:
/// We process all the conditions, and return a
/// type for the CodeCompletion function.
/// If no tips found, the return vector size is zero
/// If multiple tips found, iter over the return vector
std::vector<wxString> NativeParser::GetCodeTypeTips(int pos /*= wxNOT_FOUND*/) {
std::vector<wxString> typeList;
int commas = 0;
cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
if (!ed || !m_Parser->Done()) {
return typeList;
}
TRACE(_T("NativeParser::GetCodeTypeTips()"));
ccSearchData searchData = { ed->GetControl(), ed->GetFilename() };
if (pos == wxNOT_FOUND) {
pos = searchData.control->GetCurrentPos();
}
/** runs here, we have the true position of current index **/
int nest = 0;
while (--pos > 0)
{
const int style = searchData.control->GetStyleAt(pos);
if ( searchData.control->IsString(style) // + "a string.."
|| searchData.control->IsComment(style) ) // comment
{
continue;
}
if (searchData.control->IsCharacter(style))
{
const wxChar ch = searchData.control->GetCharAt(pos);
if ( ! (ch == _T('+') || ch == _T('-') ||ch == _T('/') ||ch == _T('>') ||ch == _T('<')
|| ch == _T('=') || ch == _T('*') || ch == _T('%') ) )
{
continue;
}
}
const wxChar ch = searchData.control->GetCharAt(pos);
if (ch == _T(';')) {
return typeList;
}
else if (ch == _T('+') || ch == _T('*')||ch == _T('-') ||ch == _T('/') ||ch == _T('>') ||ch == _T('<') ||ch==_T('=')) {
if (ch == _T('=')) {
const wxChar ch2 = searchData.control->GetCharAt(pos-1);
if (ch2 == _T('+') || ch2 == _T('-') ||ch2 == _T('/') ||ch2 == _T('>') ||ch2 == _T('<') ||ch2==_T('=') ||
ch2 == _T('*') ||ch2 == _T('!') ||ch2 == _T('%') )
pos--;
}
bool shouldContinue = false;
while(--pos > 0) {
if ( searchData.control->IsString(searchData.control->GetStyleAt(pos)) )
{
typeList.push_back(wxString(_T("string")));
typeList.push_back(wxString(_T("char*")));
return typeList;
}
if ( searchData.control->GetCharAt(pos) <= _T(' ')
|| searchData.control->IsComment(searchData.control->GetStyleAt(pos)))
{
continue;
}
if (IsNumber(pos)) {
pos++;
shouldContinue = true;
break;
}
break;
}
if (shouldContinue) {
continue;
}
// runs here, it's token
TokenIdxSet result;
MarkItemsByAI(result, true, false, true, pos);
for (TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it)
{
const Token* token = m_Parser->GetTokenTree()->at(*it);
//m_FullType+m_TemplateArgument'
wxString theType = token->m_BaseType + token->m_TemplateArgument;
typeList.push_back(theType);
}
return typeList;
}
else if (ch == _T('%')) {
typeList.push_back(wxString(_T("int")));
return typeList;
}
else if (ch == _T('*')) {
}
else if (ch == _T(','))
{
if (nest == 0)
++commas;
}
else if (ch == _T(')'))
--nest;
else if (ch == _T('('))
{
++nest;
if (nest > 0)
break;
}
}// while
// strip un-wanted
while (--pos > 0)
{
if ( searchData.control->GetCharAt(pos) <= _T(' ')
|| searchData.control->IsComment(searchData.control->GetStyleAt(pos)) )
{
continue;
}
break;
}
const int start = searchData.control->WordStartPosition(pos, true);
const int end = searchData.control->WordEndPosition(pos, true);
const wxString target = searchData.control->GetTextRange(start, end);
TRACE(_T("Sending \"%s\" for GetCodeTypeTips"), target.c_str());
if (target.IsEmpty())
return typeList;
TokenIdxSet result;
MarkItemsByAI(result, true, false, true, end);
for (TokenIdxSet::const_iterator it = result.begin(); it != result.end(); ++it)
{
const Token* token = m_Parser->GetTokenTree()->at(*it);
wxString baseArgs = token->m_BaseArgs;
// func(a, b, c) ->(int,int,int)
int remainCommas = commas+1;
int typeStrStartInd = -1;
for (int strInd = 0; strInd < baseArgs.size(); strInd++) {
if (remainCommas == 0) {
typeStrStartInd = strInd;
break;
}
if (baseArgs[strInd] == _T(',') || baseArgs[strInd] == _T('(')) {
remainCommas --;
}
}
if (typeStrStartInd == -1) {
continue;
}
int typeStrEndInd = typeStrStartInd;
while (baseArgs[typeStrEndInd] != _T(')') && baseArgs[typeStrEndInd] != _T(',')) {
typeStrEndInd++;
}
wxString theType = baseArgs.substr(typeStrStartInd, typeStrEndInd-typeStrStartInd);
typeList.push_back(theType);
}
return typeList;
//ComputeCallTip(m_Parser->GetTokenTree(), result, chars_per_line, items);
//TRACE(_T("NativeParser::GetCodeTypeTips(): typedCommas=%d"), typedCommas);
//items.Sort();
//items.
}
/// Added Done!
/*
* This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
* http://www.gnu.org/licenses/gpl-3.0.html
*/
#ifndef NATIVEPARSER_H
#define NATIVEPARSER_H
#include "nativeparser_base.h"
#include "parser/parser.h"
#include <queue>
#include <map>
#include <wx/event.h>
#include <wx/hashmap.h> // TODO: replace with std::map
/** debug only variable, used to print the AI match related log message*/
extern bool s_DebugSmartSense;
extern const int g_EditorActivatedDelay;
// forward declaration
class cbEditor;
class EditorBase;
class cbProject;
class ClassBrowser;
class Compiler;
class Token;
WX_DECLARE_HASH_MAP(cbProject*, ParserBase*, wxPointerHash, wxPointerEqual, ParsersMap);
WX_DECLARE_HASH_MAP(cbProject*, wxString, wxPointerHash, wxPointerEqual, ParsersFilenameMap);
typedef std::map<cbProject*, wxArrayString> ProjectSearchDirsMap;
//TODO (ollydbg#1#), this class is dirty, I'm going to change its name like CursorLocation
/** Search location combination, a pointer to cbStyledTextCtrl and a filename is enough */
struct ccSearchData
{
cbStyledTextCtrl* control;
wxString file;
};
/** Symbol browser tree showing option */
enum BrowserViewMode
{
bvmRaw = 0,
bvmInheritance
};
/** @brief NativeParser class is just like a manager class to control Parser objects.
*
* Normally, Each C::B Project (cbp) will have an associated Parser object.
* In another mode, all C::B project belong to a C::B workspace share a single Parser object.
* Nativeparser will manage all the Parser objects.
*/
class NativeParser : public wxEvtHandler, NativeParserBase
{
public:
/** Constructor */
NativeParser();
/** Destructor */
~NativeParser();
/** return a reference to the currently active Parser object */
ParserBase& GetParser() { return *m_Parser; }
/** return the Parser pointer corresponding to the input C::B project
* @param project input C::B project pointer
* @return a pointer to parser object
*/
ParserBase* GetParserByProject(cbProject* project);
/** return the Parser pointer associated with the input file
* Internally this function first find the project containing the input file,
* then return the Parser pointer by the project.
* @param filename filename with full path.
* @return Parser pointer
*/
ParserBase* GetParserByFilename(const wxString& filename);
/** return the C::B project associated with Parser pointer
* @param parser Parser pointer
* @return C::B Project pointer
*/
cbProject* GetProjectByParser(ParserBase* parser);
/** return the C::B project containing the filename
* The function first try to match the filename in the active project, next to match other
* projects opened, If the file exists in several projects, the first matched project will be returned.
* @param filename input filename
* @return project pointer containing the file
*/
cbProject* GetProjectByFilename(const wxString& filename);
/** return the C::B project containing the cbEditor pointer
* @param editor Any valid cbEditor pointer
* @return project pointer
*/
cbProject* GetProjectByEditor(cbEditor* editor);
/** Get current project by active editor or just return active project */
cbProject* GetCurrentProject();
/** Return true if use one Parser per whole workspace */
bool IsParserPerWorkspace() const { return m_ParserPerWorkspace; }
/** Return true if all the parser's batch-parse stages are finished, otherwise return false*/
bool Done();
/** Used to support Symbol browser and codecompletion UI
* Image list is used to initialize the symbol browser tree node image.
*/
wxImageList* GetImageList() { return m_ImageList; }
/** Returns the image assigned to a specific token for a symbol browser
*/
int GetTokenKindImage(const Token* token);
/** Get the implementation file path if the input is a header file. or Get the header file path
* if the input is an implementation file.
* Both the implementation file and header file can be in different directories.
* @param filename input filename
* @return corresponding file paths, in wxArrayString format
*/
wxArrayString GetAllPathsByFilename(const wxString& filename);
/** Add the paths to path array, and this will be used in GetAllPathsByFilename() function.
* internally, all the folder paths were recorded in UNIX format.
*/
void AddPaths(wxArrayString& dirs, const wxString& path, bool hasExt);
// the functions below are handling and managing Parser object
/** Dynamically allocate a Parser object for the input C::B project
* @param project C::B project
* @return Parser pointer of the project.
*/
ParserBase* CreateParser(cbProject* project);
/** delete the Parser object for the input project
* @param project C::B project.
* @return true if success.
*/
bool DeleteParser(cbProject* project);
/** Single file re-parse.
* This was happening when you add a single file to project, or a file was modified.
* @param project C::B project
* @param filename filename with full patch in the C::B project
*/
bool ReparseFile(cbProject* project, const wxString& filename);
/** New file was added to the C::B project, so this will cause a re-parse on the new added file.
* @param project C::B project
* @param filename filename with full path in the C::B project
*/
bool AddFileToParser(cbProject* project, const wxString& filename, ParserBase* parser = nullptr);
/** remove a file from C::B project and Parser
* @param project C::B project
* @param filename filename with full patch in the C::B project
*/
bool RemoveFileFromParser(cbProject* project, const wxString& filename);
/** when user changes the CC option, we should re-read the option */
void RereadParserOptions();
/** re-parse the active Parser (the project associated with m_Parser member variable */
void ReparseCurrentProject();
/** re-parse the project select by context menu in projects management panel */
void ReparseSelectedProject();
/** collect tokens where a code suggestion list can be shown
* @param searchData input variable, search location
* @param result output variable, containing all matching result token indexes
* @param reallyUseAI true means the context scope information should be considered,
* false if only do a plain word match
* @param isPrefix partially match which result all the Tokens' name with the same prefix,
otherwise use full-text match
* @param caseSensitive case sensitive or not
* @param caretPos Where the current caret locates, -1 means we use the current caret position.
* @return the matching Token count
*/
size_t MarkItemsByAI(ccSearchData* searchData, TokenIdxSet& result, bool reallyUseAI = true,
bool isPrefix = true, bool caseSensitive = false, int caretPos = -1);
/** the same as before, but we don't specify the searchData information, so it will use the active
* editor and current caret information.
*/
size_t MarkItemsByAI(TokenIdxSet& result, bool reallyUseAI = true, bool isPrefix = true,
bool caseSensitive = false, int caretPos = -1);
/** Call tips are when you mouse pointer hover some statement and show the information of statement below caret.
* these tips information could be:
* the prototypes information of the current function,
* the type information of the variable...
*
* @param chars_per_line specify the char number per one line of the call-tip window, so it can restrict the width.
* @param items array to store result in.
* @param typedCommas how much comma characters the user has typed in the current line before the cursor.
*/
void GetCallTips(int chars_per_line, wxArrayString& items, int &typedCommas, int pos = wxNOT_FOUND);
/** Word start position in the editor
* @return position index
*/
int GetEditorStartWord() const { return m_EditorStartWord; }
/** Word end position in the editor
* @return position index
*/
int GetEditorEndWord() const { return m_EditorEndWord; }
/** project search path is used for auto completion for #include <> */
wxArrayString& GetProjectSearchDirs(cbProject* project);
// The function below is used to manage symbols browser
/** return active class browser pointer*/
ClassBrowser* GetClassBrowser() const { return m_ClassBrowser; }
/** create the class browser */
void CreateClassBrowser();
/** remove the class browser */
void RemoveClassBrowser(bool appShutDown = false);
/** update the class browser tree*/
void UpdateClassBrowser();
protected:
/** When a Parser is created, we need a full parsing stage including:
* 1, parse the priority header files firstly.
* 2, parse all the other project files.
*/
bool DoFullParsing(cbProject* project, ParserBase* parser);
/** Switch parser object according the current active editor and filename */
bool SwitchParser(cbProject* project, ParserBase* parser);
/** Set a new Parser as the active Parser
* Set the active parser pointer (m_Parser member variable)
* update the ClassBrowser's Parser pointer
* re-fresh the symbol browser tree.
*/
void SetParser(ParserBase* parser);
/** Clear all Parser object*/
void ClearParsers();
/** Remove all the obsolete Parser object
* if the number exceeds the limited number (can be set in the CC's option), then all the
* obsolete parser will be removed.
*/
void RemoveObsoleteParsers();
/** Get cbProject and Parser pointer, according to the current active editor*/
std::pair<cbProject*, ParserBase*> GetParserInfoByCurrentEditor();
/** Used to support Symbol browser and codecompletion UI
* Image list is used to initialize the symbol browser tree node image.
*/
void SetTokenKindImage(int kind, const wxBitmap& bitmap, const wxBitmap& mask = wxNullBitmap);
void SetTokenKindImage(int kind, const wxBitmap& bitmap, const wxColour& maskColour);
void SetTokenKindImage(int kind, const wxIcon& icon);
/** set the class browser view mode*/
void SetCBViewMode(const BrowserViewMode& mode);
private:
friend class CodeCompletion;
/** Read project CC options when a C::B project is loading */
void OnProjectLoadingHook(cbProject* project, TiXmlElement* elem, bool loading);
/** Start an Artificial Intelligence search algorithm to gather all the matching tokens.
* The actual AI is in FindAIMatches() below.
* @param result output parameter.
* @param searchData cbEditor information.
* @param lineText current statement.
* @param isPrefix if true, then the result contains all the tokens whose name is a prefix of current lineText.
* @param caseSensitive true is case sensitive is enabled on the match.
* @param search_scope it is the "parent token" where we match the "search-key".
* @param caretPos use current caret position if it is -1.
* @return match token number
*/
size_t AI(TokenIdxSet& result,
ccSearchData* searchData,
const wxString& lineText = wxEmptyString,
bool isPrefix = false,
bool caseSensitive = false,
TokenIdxSet* search_scope = 0,
int caretPos = -1);
/** return all the tokens matching the current function(hopefully, just one)
* @param editor editor pointer
* @param result output result containing all the Token index
* @param caretPos -1 if the current caret position is used.
* @return number of result Tokens
*/
size_t FindCurrentFunctionToken(ccSearchData* searchData, TokenIdxSet& result, int caretPos = -1);
/** returns the editor's position where the current function scope starts.
* optionally, returns the function's namespace (ends in double-colon ::) and name and token
* @param searchData search data struct pointer
* @param nameSpace get the namespace modifier
* @param procName get the function name
* @param functionToken get the token of current function
* @param caretPos caret position in cbeditor
* @return current function line number
*/
int FindCurrentFunctionStart(ccSearchData* searchData,
wxString* nameSpace = 0L,
wxString* procName = 0L,
int* functionIndex = 0L,
int caretPos = -1);
/** helper function for statement parsing*/
bool SkipWhitespaceForward(cbEditor* editor, int& pos);
/** helper function for statement parsing*/
bool SkipWhitespaceBackward(cbEditor* editor, int& pos);
/** used in CodeCompletion suggestion list to boost the performance, we use a caches*/
bool LastAISearchWasGlobal() const { return m_LastAISearchWasGlobal; }
/** The same as above*/
const wxString& LastAIGlobalSearch() const { return m_LastAIGlobalSearch; }
/** collect the using namespace directive in the editor specified by searchData
* @param searchData search location
* @param search_scope resulting tokens collection
* @param caretPos caret position, if not specified, we use the current caret position
*/
bool ParseUsingNamespace(ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos = -1);
/** collect the using namespace directive in the buffer specified by searchData
* @param buffer code to parse
* @param search_scope resulting tokens collection
* @param bufferSkipBlocks skip brace sets { }
*/
bool ParseBufferForUsingNamespace(const wxString& buffer, TokenIdxSet& search_scope, bool bufferSkipBlocks = true);
/** collect function argument, add them to the token tree (as temporary tokens)
* @param searchData search location
* @param caretPos caret position, if not specified, we use the current caret position
*/
bool ParseFunctionArguments(ccSearchData* searchData, int caretPos = -1);
/** parses from the start of function up to the cursor, this is used to collect local variables.
* @param searchData search location
* @param search_scope resulting tokens collection of local using namespace
* @param caretPos caret position, if not specified, we use the current caret position
*/
bool ParseLocalBlock(ccSearchData* searchData, TokenIdxSet& search_scope, int caretPos = -1);
/** collect the header file search directories, those dirs include:
* 1, project's base dir, e.g. if you cbp file was c:/bbb/aaa.cbp, then c:/bbb is added.
* 2, project's setting search dirs, for a wx project, then c:/wxWidgets2.8.12/include is added.
* 3, a project may has some targets, so add search dirs for those targets
* 4, compiler's own search path, like: c:/mingw/include
*/
bool AddCompilerDirs(cbProject* project, ParserBase* parser);
/** collect compiler specific predefined preprocessor definition, this is usually run a special
* compiler command, like GCC -dM for gcc.
*/
bool AddCompilerPredefinedMacros(cbProject* project, ParserBase* parser);
/** collect GCC compiler predefined preprocessor definition */
bool AddCompilerPredefinedMacrosGCC(const wxString& compilerId, cbProject* project, wxString& defs);
/** collect VC compiler predefined preprocessor definition */
bool AddCompilerPredefinedMacrosVC(const wxString& compilerId, wxString& defs);
/** collect project (user) defined preprocessor definition, such as for wxWidgets project, the
* macro may have "#define wxUSE_UNICODE" defined in its project file.
*/
bool AddProjectDefinedMacros(cbProject* project, ParserBase* parser);
/** Collect the default compiler include file search paths. called by AddCompilerDirs() function*/
const wxArrayString& GetGCCCompilerDirs(const wxString &cpp_compiler);
/** Add the collected default GCC compiler include file search paths to a parser */
void AddGCCCompilerDirs(const wxString& masterPath, const wxString& compilerCpp, ParserBase* parser);
/** Add a list of directories to the parser's search directories, normalise to "base" path, if "base" is not empty. Replaces macros. */
void AddIncludeDirsToParser(const wxArrayString& dirs, const wxString& base, ParserBase* parser);
/** Event handler when the batch parse starts, print some log information */
void OnParserStart(wxCommandEvent& event);
/** Event handler when the batch parse finishes, print some log information, check whether the active editor
* belong to the current parser, if not, do a parser switch */
void OnParserEnd(wxCommandEvent& event);
/** If use one parser per whole workspace, we need parse all project one by one */
void OnParsingOneByOneTimer(wxTimerEvent& event);
/** Event handler when an editor activate, *NONE* project is handled here */
void OnEditorActivated(EditorBase* editor);
/** Event handler when an editor closed, if it is the last editor belong to *NONE* project, then
* the *NONE* Parser will be removed
*/
void OnEditorClosed(EditorBase* editor);
/** Init cc search member variables */
void InitCCSearchVariables();
/** Add all project files to parser */
void AddProjectToParser(cbProject* project);
/** Remove cbp from the common parser, this only happens in one parser for whole workspace mode
* when a parser is removed from the workspace, we should remove the project from the parser
*/
bool RemoveProjectFromParser(cbProject* project);
std::vector<wxString> GetCodeTypeTips(int pos /*= wxNOT_FOUND*/);
bool IsNumber(int &pos);
private:
typedef std::pair<cbProject*, ParserBase*> ProjectParserPair;
typedef std::list<ProjectParserPair> ParserList;
/** a list holing all the cbp->parser pairs, if in one parser per project mode, there are many
* many pairs in this list. In one parser per workspace mode, there is only one pair, and the
* m_ParserList.begin()->second is the common parser for all the projects in workspace.
*/
ParserList m_ParserList;
/** a temp parser object pointer*/
ParserBase* m_TempParser;
/** active parser object pointer*/
ParserBase* m_Parser;
/** a delay timer to parser every project in sequence*/
wxTimer m_TimerParsingOneByOne;
/** symbol browser window*/
ClassBrowser* m_ClassBrowser;
/** if true, which means m_ClassBrowser is floating (not docked)*/
bool m_ClassBrowserIsFloating;
/** a map: project pointer -> C/C++ parser search paths for this project */
ProjectSearchDirsMap m_ProjectSearchDirsMap;
int m_HookId; //!< project loader hook ID
wxImageList* m_ImageList; //!< Images for class browser
/// all the files which opened, but does not belong to any cbp
wxArrayString m_StandaloneFiles;
/** if true, which means the parser hold tokens of the whole workspace's project, if false
* then one parser per a cbp
*/
bool m_ParserPerWorkspace;
/// only used when m_ParserPerWorkspace is true, and holds all the cbps for the common parser
std::set<cbProject*> m_ParsedProjects;
/* CC Search Member Variables => START */
int m_EditorStartWord;
int m_EditorEndWord;
wxString m_LastAIGlobalSearch; //!< same case like above, it holds the search string
bool m_LastAISearchWasGlobal; //!< true if the phrase for code-completion is empty or partial text (i.e. no . -> or :: operators)
cbStyledTextCtrl* m_LastControl;
wxString m_LastFile;
int m_LastFunctionIndex;
int m_LastFuncTokenIdx; //!< saved the function token's index, for remove all local variable
int m_LastLine;
wxString m_LastNamespace;
wxString m_LastPROC;
int m_LastResult;
/* CC Search Member Variables => END */
};
#endif // NATIVEPARSER_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment