📄 nativeparser.cpp
字号:
wxFile f(projectCache.GetFullPath(), wxFile::read);
if (!f.IsOpened())
return false;
// read cache file
Manager::Get()->GetMessageManager()->DebugLog(_("Using parser's existing cache: %s"), projectCache.GetFullPath().c_str());
bool ret = false;
try
{
ret = parser->ReadFromCache(&f);
}
catch (cbException& ex)
{
ex.ShowErrorMessage(true);
ret = false;
}
catch (...)
{
// eat it
wxSafeShowMessage(_("Exception thrown!"),_("ERROR"));
ret = false;
}
DisplayStatus(parser, project);
return ret;
}
bool NativeParser::SaveCachedData(Parser* parser, const wxString& projectFilename)
{
if (!parser)
return false;
wxFileName projectCache = projectFilename;
projectCache.SetExt(_T("cbCache"));
wxLogNull ln;
wxFile f(projectCache.GetFullPath(), wxFile::write);
if (!f.IsOpened())
{
Manager::Get()->GetMessageManager()->DebugLog(_("Failed updating parser's cache: %s"), projectCache.GetFullPath().c_str());
return false;
}
// write cache file
Manager::Get()->GetMessageManager()->DebugLog(_("Updating parser's cache: %s"), projectCache.GetFullPath().c_str());
return parser->WriteToCache(&f);
}
void NativeParser::DisplayStatus(Parser* parser, cbProject* project)
{
if (!parser || !project)
return;
long int tim = parser->GetElapsedTime();
Manager::Get()->GetMessageManager()->DebugLog(_("Done parsing project %s (%d total parsed files, %d tokens in %d.%d seconds)."),
project->GetTitle().c_str(),
parser->GetFilesCount(),
parser->GetTokens().GetCount(),
tim / 1000,
tim % 1000);
}
int NativeParser::MarkItemsByAI(bool reallyUseAI)
{
cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
if (!ed)
return 0;
Parser* parser = FindParserFromActiveEditor();
if (!parser)
return 0;
if (!parser->Done())
Manager::Get()->GetMessageManager()->DebugLog(_("C++ Parser is still parsing files..."));
else
{
// clear all temporary tokens
parser->ClearTemporaries();
bool sort = false;
// parse function's arguments
wxString _namespace;
wxString _procedure;
if (FindFunctionNamespace(ed, &_namespace, &_procedure))
{
Token* token = parser->FindTokenByName(_procedure, false, tkFunction);
if (token)
{
if (!token->m_Args.IsEmpty() && !token->m_Args.Matches(_T("()")))
{
wxString buffer = token->m_Args;
buffer.Remove(0, 1); // remove (
buffer.RemoveLast(); // remove )
buffer.Replace(_T(","), _T(";")); // replace commas with semi-colons
buffer << _T(';'); // aid parser ;)
Manager::Get()->GetMessageManager()->DebugLog(_("Parsing arguments: \"%s\""), buffer.c_str());
if (!parser->ParseBuffer(buffer, false))
Manager::Get()->GetMessageManager()->DebugLog(_("ERROR parsing block:\n%s"), buffer.c_str());
sort = true;
}
}
}
else
Manager::Get()->GetMessageManager()->DebugLog(_("Could not find current function's namespace..."));
// parse current code block
int blockStart = FindCurrentBlockStart(ed);
if (blockStart != -1)
{
++blockStart; // skip {
int blockEnd = ed->GetControl()->GetCurrentPos();
wxString buffer = ed->GetControl()->GetTextRange(blockStart, blockEnd);
if (!parser->ParseBuffer(buffer, false))
Manager::Get()->GetMessageManager()->DebugLog(_("ERROR parsing block:\n%s"), buffer.c_str());
sort = true;
}
else
Manager::Get()->GetMessageManager()->DebugLog(_("Could not find current block start..."));
if (sort)
parser->SortAllTokens();
// clear previously marked tokens
const TokensArray& tokens = parser->GetTokens();
for (unsigned int i = 0; i < tokens.GetCount(); ++i)
{
Token* token = tokens[i];
token->m_Bool = !reallyUseAI;
}
if (!reallyUseAI)
return tokens.GetCount();
// AI will mark (m_Bool == true) every token we should include in list
return AI(ed, parser);
}
return 0;
}
const wxString& NativeParser::GetCodeCompletionItems()
{
m_CCItems.Clear();
Parser* parser = FindParserFromActiveEditor();
if (!parser)
return m_CCItems;
int count = MarkItemsByAI();
if (count)
{
const TokensArray& tokens = parser->GetTokens();
for (unsigned int i = 0; i < tokens.GetCount(); ++i)
{
Token* token = tokens[i];
if (!token->m_Bool)
continue; // not marked by AI
token->m_Bool = false; // reset flag for next run
if (!m_CCItems.IsEmpty())
m_CCItems << _T(";");
m_CCItems << token->m_Name << token->m_Args;//" " << token->m_Filename << ":" << token->m_Line;
}
}
return m_CCItems;
}
const wxArrayString& NativeParser::GetCallTips()
{
m_CallTips.Clear();
cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
if (!ed)
return m_CallTips;
Parser* parser = FindParserFromActiveEditor();
if (!parser)
return m_CallTips;
if (!parser->Done())
return m_CallTips;
int line = ed->GetControl()->GetCurrentLine();
wxString lineText = ed->GetControl()->GetLine(line);
int end = lineText.Length();
int nest = 0;
while (end > 0)
{
--end;
if (lineText.GetChar(end) == ')')
--nest;
else if (lineText.GetChar(end) == '(')
{
++nest;
if (nest != 0)
break;
}
}
if (end == 0)
return m_CallTips; // no (
lineText.Remove(end);
Manager::Get()->GetMessageManager()->DebugLog(_("Sending \"%s\" for call-tip"), lineText.c_str());
// clear previously marked tokens
const TokensArray& tokens = parser->GetTokens();
for (unsigned int i = 0; i < tokens.GetCount(); ++i)
{
Token* token = tokens[i];
token->m_Bool = false;
}
// AI will mark (m_Bool == true) every token we should include in list
if (!AI(ed, parser, lineText, true, true))
return m_CallTips;
for (unsigned int i = 0; i < tokens.GetCount(); ++i)
{
Token* token = tokens[i];
if (token->m_Bool && !token->m_Args.Matches(_T("()")))
{
m_CallTips.Add(token->m_Args);
token->m_Bool = false; // reset flag for next run
}
}
return m_CallTips;
}
// helper funcs
unsigned int NativeParser::FindCCTokenStart(const wxString& line)
{
int x = line.Length() - 1;
int nest = 0;
bool repeat = true;
while (repeat)
{
repeat = false;
while (x >= 0 && (isalnum(line.GetChar(x)) || line.GetChar(x) == '_'))
--x;
if (x > 0 &&
(line.GetChar(x) == '>' && line.GetChar(x - 1) == '-') ||
(line.GetChar(x) == ':' && line.GetChar(x - 1) == ':'))
{
x -= 2;
repeat = true;
}
else if (x >= 0 && line.GetChar(x) == '.')
{
--x;
repeat = true;
}
if (repeat)
{
// check for function/cast ()
if (x >= 0 && line.GetChar(x) == ')')
{
++nest;
while (--x >= 0 && nest != 0)
{
switch (line.GetChar(x))
{
case ')': ++nest; break;
case '(': --nest; break;
}
}
if (x > 0 && (isalnum(line.GetChar(x - 1)) || line.GetChar(x - 1) == '_'))
--x;
}
}
}
++x;
if (x < 0)
x = 0;
while (line.GetChar(x) == ' ' || line.GetChar(x) == '\t')
++x;
//Manager::Get()->GetMessageManager()->DebugLog("Starting at %d \"%s\"", x, line.Mid(x).c_str());
return x;
}
wxString NativeParser::GetNextCCToken(const wxString& line, unsigned int& startAt)
{
wxString res;
int nest = 0;
if (startAt < line.Length() && line.GetChar(startAt) == '(')
{
while (startAt < line.Length() &&
(line.GetChar(startAt) == '*' ||
line.GetChar(startAt) == '&' ||
line.GetChar(startAt) == '('))
{
if (line.GetChar(startAt) == '(')
++nest;
++startAt;
}
}
//Manager::Get()->GetMessageManager()->DebugLog("at %d (%c): res=%s", startAt, line.GetChar(startAt), res.c_str());
while (startAt < line.Length() && (isalnum(line.GetChar(startAt)) || line.GetChar(startAt) == '_'))
{
res << line.GetChar(startAt);
++startAt;
}
while (nest > 0 && startAt < line.Length())
{
if (line.GetChar(startAt) == ')')
--nest;
++startAt;
}
//Manager::Get()->GetMessageManager()->DebugLog("Done nest: at %d (%c): res=%s", startAt, line.GetChar(startAt), res.c_str());
if (startAt < line.Length() && line.GetChar(startAt) == '(')
{
++nest;
while (startAt < line.Length() - 1 && nest != 0)
{
++startAt;
switch (line.GetChar(startAt))
{
case ')': --nest; break;
case '(': ++nest; break;
}
}
++startAt;
}
//Manager::Get()->GetMessageManager()->DebugLog("Return at %d (%c): res=%s", startAt, line.GetChar(startAt), res.c_str());
return res;
}
wxString NativeParser::GetCCToken(wxString& line, ParserTokenType& tokenType)
{
// line contains a string on the following form:
// " char* mychar = SomeNamespace::m_SomeVar.SomeMeth"
// first we locate the first non-space char starting from the *end*:
//
// " char* mychar = SomeNamespace::m_SomeVar.SomeMeth"
// ^
// then we remove everything before it.
// after it, what we do here, is (by this example) return "SomeNamespace"
// *and* modify line to become:
// m_SomeVar.SomeMeth
// so that if we 're called again with the (modified) line,
// we 'll return "m_SomeVar" and modify line (again) to become:
// SomeMeth
// and so on and so forth until we return an empty string...
// NOTE: if we find () args in our way, we skip them...
tokenType = pttSearchText;
if (line.IsEmpty())
return wxEmptyString;
unsigned int x = FindCCTokenStart(line);
wxString res = GetNextCCToken(line, x);
//Manager::Get()->GetMessageManager()->DebugLog("FindCCTokenStart returned %d \"%s\"", x, line.c_str());
//Manager::Get()->GetMessageManager()->DebugLog("GetNextCCToken returned %d \"%s\"", x, res.c_str());
if (x == line.Length())
line.Clear();
else
{
//Manager::Get()->GetMessageManager()->DebugLog("Left \"%s\"", line.Mid(x).c_str());
if (line.GetChar(x) == '.')
{
tokenType = pttClass;
line.Remove(0, x + 1);
}
else if ((x < line.Length() - 1 && line.GetChar(x) == '-' && line.GetChar(x + 1) == '>') ||
(x < line.Length() - 1 && line.GetChar(x) == ':' && line.GetChar(x + 1) == ':'))
{
if (line.GetChar(x) == ':')
tokenType = pttNamespace;
else
tokenType = pttClass;
line.Remove(0, x + 2);
}
else
line.Clear();
}
return res;
}
int NativeParser::AI(cbEditor* editor, Parser* parser, const wxString& lineText, bool noPartialMatch, bool caseSensitive)
{
int count = 0;
int pos = editor->GetControl()->GetCurrentPos();
m_EditorStartWord = editor->GetControl()->WordStartPosition(pos, true);
m_EditorEndWord = pos;//editor->GetControl()->WordEndPosition(pos, true);
int line = editor->GetControl()->GetCurrentLine();
wxString searchtext;
//Manager::Get()->GetMessageManager()->DebugLog("********* START **********");
Token* parentToken = 0L;
ParserTokenType tokenType;
wxString actual;
int col;
wxString tabwidth;
tabwidth.Pad(editor->GetControl()->GetTabWidth(), ' ');
if (lineText.IsEmpty())
{
actual = editor->GetControl()->GetLine(line);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -