Skip to content

Commit

Permalink
* Implement basic calltips (no scope resolution yet...)
Browse files Browse the repository at this point in the history
  • Loading branch information
alpha0010 committed Feb 18, 2014
1 parent e8b8ace commit 6ce280c
Show file tree
Hide file tree
Showing 6 changed files with 301 additions and 3 deletions.
92 changes: 90 additions & 2 deletions clangplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,90 @@ wxString ClangPlugin::GetDocumentation(const CCToken& token)

std::vector<ClangPlugin::CCCallTip> ClangPlugin::GetCallTips(int pos, int style, cbEditor* ed, int& argsPos)
{
return std::vector<CCCallTip>();
std::vector<CCCallTip> tips;
if (ed != m_pLastEditor)
{
m_TranslUnitId = m_Proxy.GetTranslationUnitId(ed->GetFilename());
m_pLastEditor = ed;
}
if (m_TranslUnitId == wxNOT_FOUND)
return tips;

cbStyledTextCtrl* stc = ed->GetControl();

int nest = 0;
int commas = 0;

while (--pos > 0)
{
const int curStyle = stc->GetStyleAt(pos);
if ( stc->IsString(curStyle)
|| stc->IsCharacter(curStyle)
|| stc->IsComment(curStyle) )
{
continue;
}

const wxChar ch = stc->GetCharAt(pos);
if (ch == wxT(';'))
return tips; // error?
else if (ch == wxT(','))
{
if (nest == 0)
++commas;
}
else if (ch == wxT(')'))
--nest;
else if (ch == wxT('('))
{
++nest;
if (nest > 0)
break;
}
}
while (--pos > 0)
{
if ( stc->GetCharAt(pos) <= wxT(' ')
|| stc->IsComment(stc->GetStyleAt(pos)) )
{
continue;
}
break;
}
argsPos = stc->WordEndPosition(pos, true);
if (argsPos != m_LastCallTipPos)
{
m_LastCallTips.clear();
const int line = stc->LineFromPosition(pos);
const int column = pos - stc->PositionFromLine(line);
const wxString& tknText = stc->GetTextRange(stc->WordStartPosition(pos, true), argsPos);
if (!tknText.IsEmpty())
m_Proxy.GetCallTipsAt(ed->GetFilename(), line + 1, column + 1, m_TranslUnitId, tknText, m_LastCallTips);
}
m_LastCallTipPos = argsPos;
for (std::vector<wxStringVec>::const_iterator strVecItr = m_LastCallTips.begin(); strVecItr != m_LastCallTips.end(); ++strVecItr)
{
int strVecSz = strVecItr->size();
if (commas != 0 && strVecSz < commas + 3)
continue;
wxString tip;
int hlStart = wxSCI_INVALID_POSITION;
int hlEnd = wxSCI_INVALID_POSITION;
for (int i = 0; i < strVecSz; ++i)
{
if (i == commas + 1 && strVecSz > 2)
{
hlStart = tip.Length();
hlEnd = hlStart + (*strVecItr)[i].Length();
}
tip += (*strVecItr)[i];
if (i > 0 && i < (strVecSz - 2))
tip += wxT(", ");
}
tips.push_back(CCCallTip(tip, hlStart, hlEnd));
}

return tips;
}

std::vector<ClangPlugin::CCToken> ClangPlugin::GetTokenAt(int pos, cbEditor* ed, bool& allowCallTip)
Expand Down Expand Up @@ -436,6 +519,11 @@ void ClangPlugin::DoAutocomplete(const CCToken& token, cbEditor* ed)
stc->ReplaceTarget(tknText);
stc->SetSelectionVoid(moveToPos + offsets.first, moveToPos + offsets.second);
stc->ChooseCaretX();
if (token.category != tcLangKeyword && offsets.first != offsets.second)
{
CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
Manager::Get()->ProcessEvent(evt);
}
}

void ClangPlugin::BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data)
Expand Down Expand Up @@ -684,7 +772,7 @@ void ClangPlugin::OnTimer(wxTimerEvent& event)
Compiler* comp = nullptr;
if (pf)
{
target = pf->GetParentProject()->GetBuildTarget(pf->GetbuildTargets()[0]);
target = pf->GetParentProject()->GetBuildTarget(pf->GetBuildTargets()[0]);
comp = CompilerFactory::GetCompiler(target->GetCompilerID());
if (pf->GetUseCustomBuildCommand(target->GetCompilerID()))
{
Expand Down
2 changes: 2 additions & 0 deletions clangplugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ class ClangPlugin : public cbCodeCompletionPlugin
cbEditor* m_pLastEditor;
int m_TranslUnitId;
int m_EditorHookId;
int m_LastCallTipPos;
std::vector<wxStringVec> m_LastCallTips;
};

#endif // CLANGPLUGIN_H
192 changes: 191 additions & 1 deletion clangproxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class TranslationUnit
Reparse(0, nullptr); // seems to improve performance for some reason?

clang_visitChildren(clang_getTranslationUnitCursor(m_ClTranslUnit), ClAST_Visitor, database);
database->Shrink();
}

// move ctor
Expand Down Expand Up @@ -617,11 +618,18 @@ ClangProxy::~ClangProxy()
void ClangProxy::CreateTranslationUnit(const wxString& filename, const wxString& commands)
{
wxStringTokenizer tokenizer(commands);
std::vector<wxString> unknownOptions;
unknownOptions.push_back(wxT("-Wno-unused-local-typedefs"));
unknownOptions.push_back(wxT("-Wzero-as-null-pointer-constant"));
std::sort(unknownOptions.begin(), unknownOptions.end());
std::vector<wxCharBuffer> argsBuffer;
std::vector<const char*> args;
while (tokenizer.HasMoreTokens())
{
argsBuffer.push_back(tokenizer.GetNextToken().ToUTF8());
const wxString& compilerSwitch = tokenizer.GetNextToken();
if (std::binary_search(unknownOptions.begin(), unknownOptions.end(), compilerSwitch))
continue;
argsBuffer.push_back(compilerSwitch.ToUTF8());
args.push_back(argsBuffer.back().data());
}
m_TranslUnits.push_back(TranslationUnit(filename, args, m_ClIndex, &m_Database));
Expand Down Expand Up @@ -973,6 +981,188 @@ void ClangProxy::RefineTokenType(int translId, int tknId, int& tknType)
}
}

static CXChildVisitResult ClCallTipCtorAST_Visitor(CXCursor cursor, CXCursor parent, CXClientData client_data)
{
switch (cursor.kind)
{
case CXCursor_Constructor:
{
std::vector<CXCursor>* tokenSet = static_cast<std::vector<CXCursor>*>(client_data);
tokenSet->push_back(cursor);
break;
}

case CXCursor_FunctionDecl:
case CXCursor_CXXMethod:
case CXCursor_FunctionTemplate:
{
CXString str = clang_getCursorSpelling(cursor);
if (strcmp(clang_getCString(str), "operator()") == 0)
{
std::vector<CXCursor>* tokenSet = static_cast<std::vector<CXCursor>*>(client_data);
tokenSet->push_back(cursor);
}
clang_disposeString(str);
break;
}

default:
break;
}
return CXChildVisit_Continue;
}

void ClangProxy::GetCallTipsAt(const wxString& filename, int line, int column, int translId, const wxString& tokenStr, std::vector<wxStringVec>& results)
{
std::vector<CXCursor> tokenSet;
if (column > static_cast<int>(tokenStr.Length()))
{
column -= tokenStr.Length() / 2;
CXCursor token = m_TranslUnits[translId].GetTokensAt(filename, line, column);
if (!clang_Cursor_isNull(token))
{
CXCursor resolve = clang_getCursorDefinition(token);
if (clang_Cursor_isNull(resolve) || clang_isInvalid(token.kind))
{
resolve = clang_getCursorReferenced(token);
if (!clang_Cursor_isNull(resolve) && !clang_isInvalid(token.kind))
token = resolve;
}
else
token = resolve;
tokenSet.push_back(token);
}
}
// TODO: searching the database is very inexact, but necessary, as clang
// does not resolve the token when the code is invalid (incomplete)
std::vector<TokenId> tknIds = m_Database.GetTokenMatches(tokenStr);
for (std::vector<TokenId>::const_iterator itr = tknIds.begin(); itr != tknIds.end(); ++itr)
{
const AbstractToken& aTkn = m_Database.GetToken(*itr);
CXCursor token = m_TranslUnits[translId].GetTokensAt(m_Database.GetFilename(aTkn.fileId), aTkn.line, aTkn.column);
if (!clang_Cursor_isNull(token) && !clang_isInvalid(token.kind))
tokenSet.push_back(token);
}
std::set<wxString> uniqueTips;
for (size_t tknIdx = 0; tknIdx < tokenSet.size(); ++tknIdx)
{
CXCursor token = tokenSet[tknIdx];
switch (GetTokenCategory(token.kind, CX_CXXPublic))
{
case tcVarPublic:
{
token = clang_getTypeDeclaration(clang_getCursorResultType(token));
if (!clang_Cursor_isNull(token) && !clang_isInvalid(token.kind))
tokenSet.push_back(token);
break;
}

case tcTypedefPublic:
{
token = clang_getTypeDeclaration(clang_getTypedefDeclUnderlyingType(token));
if (!clang_Cursor_isNull(token) && !clang_isInvalid(token.kind))
tokenSet.push_back(token);
break;
}

case tcClassPublic:
{
// search for constructors and 'operator()'
clang_visitChildren(token, &ClCallTipCtorAST_Visitor, &tokenSet);
break;
}

case tcCtorPublic:
{
if (clang_getCXXAccessSpecifier(token) == CX_CXXPrivate)
break;
// fall through
}
case tcFuncPublic:
{
const CXCompletionString& clCompStr = clang_getCursorCompletionString(token);
wxStringVec entry;
int upperBound = clang_getNumCompletionChunks(clCompStr);
entry.push_back(wxEmptyString);
for (int chunkIdx = 0; chunkIdx < upperBound; ++chunkIdx)
{
CXCompletionChunkKind kind = clang_getCompletionChunkKind(clCompStr, chunkIdx);
if (kind == CXCompletionChunk_TypedText)
{
CXString str = clang_getCompletionParent(clCompStr, nullptr);
wxString parent = wxString::FromUTF8(clang_getCString(str));
if (!parent.IsEmpty())
entry[0] += parent + wxT("::");
clang_disposeString(str);
}
else if (kind == CXCompletionChunk_LeftParen)
{
if (entry[0].IsEmpty() || !entry[0].EndsWith(wxT("operator")))
break;
}
CXString str = clang_getCompletionChunkText(clCompStr, chunkIdx);
entry[0] += wxString::FromUTF8(clang_getCString(str));
if (kind == CXCompletionChunk_ResultType)
{
if (entry[0].Length() > 2 && entry[0][entry[0].Length() - 2] == wxT(' '))
entry[0].RemoveLast(2) += entry[0].Last();
entry[0] += wxT(' ');
}
clang_disposeString(str);
}
entry[0] += wxT('(');
int numArgs = clang_Cursor_getNumArguments(token);
for (int argIdx = 0; argIdx < numArgs; ++argIdx)
{
CXCursor arg = clang_Cursor_getArgument(token, argIdx);

wxString tknStr;
const CXCompletionString& argStr = clang_getCursorCompletionString(arg);
upperBound = clang_getNumCompletionChunks(argStr);
for (int chunkIdx = 0; chunkIdx < upperBound; ++chunkIdx)
{
CXCompletionChunkKind kind = clang_getCompletionChunkKind(argStr, chunkIdx);
if (kind == CXCompletionChunk_TypedText)
{
CXString str = clang_getCompletionParent(argStr, nullptr);
wxString parent = wxString::FromUTF8(clang_getCString(str));
if (!parent.IsEmpty())
tknStr += parent + wxT("::");
clang_disposeString(str);
}
CXString str = clang_getCompletionChunkText(argStr, chunkIdx);
tknStr += wxString::FromUTF8(clang_getCString(str));
if (kind == CXCompletionChunk_ResultType)
{
if (tknStr.Length() > 2 && tknStr[tknStr.Length() - 2] == wxT(' '))
tknStr.RemoveLast(2) += tknStr.Last();
tknStr += wxT(' ');
}
clang_disposeString(str);
}

entry.push_back(tknStr.Trim());
}
entry.push_back(wxT(')'));
wxString composit;
for (wxStringVec::const_iterator itr = entry.begin();
itr != entry.end(); ++itr)
{
composit += *itr;
}
if (uniqueTips.find(composit) != uniqueTips.end())
break;
uniqueTips.insert(composit);
results.push_back(entry);
break;
}

default:
break;
}
}
}

void ClangProxy::GetTokensAt(const wxString& filename, int line, int column, int translId, wxStringVec& results)
{
CXCursor token = m_TranslUnits[translId].GetTokensAt(filename, line, column);
Expand Down
2 changes: 2 additions & 0 deletions clangproxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class ClangProxy
wxString GetCCInsertSuffix(int translId, int tknId, const wxString& newLine, std::pair<int, int>& offsets);
void RefineTokenType(int translId, int tknId, int& tknType); // TODO: cache TokenId (if resolved) for DocumentCCToken()

void GetCallTipsAt(const wxString& filename, int line, int column, int translId, const wxString& tokenStr, std::vector<wxStringVec>& results);

void GetTokensAt(const wxString& filename, int line, int column, int translId, std::vector<wxString>& results);
void ResolveTokenAt(wxString& filename, int& line, int& column, int translId);

Expand Down
11 changes: 11 additions & 0 deletions tokendatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,14 @@ AbstractToken& TokenDatabase::GetToken(TokenId tId) const
{
return m_pTokens->GetValue(tId);
}

std::vector<TokenId> TokenDatabase::GetTokenMatches(const wxString& identifier) const
{
return m_pTokens->GetIdSet(identifier);
}

void TokenDatabase::Shrink()
{
m_pFilenames->Shrink();
m_pTokens->Shrink();
}
5 changes: 5 additions & 0 deletions tokendatabase.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifndef TOKENDATABASE_H
#define TOKENDATABASE_H

#include <vector>

template<typename _Tp> class TreeMap;
class wxString;
typedef int FileId;
Expand Down Expand Up @@ -29,6 +31,9 @@ class TokenDatabase
TokenId InsertToken(const wxString& identifier, const AbstractToken& token); // duplicate tokens are discarded
TokenId GetTokenId(const wxString& identifier, unsigned tokenHash) const; // returns wxNOT_FOUND on failure
AbstractToken& GetToken(TokenId tId) const;
std::vector<TokenId> GetTokenMatches(const wxString& identifier) const;

void Shrink();

private:
TreeMap<AbstractToken>* m_pTokens;
Expand Down

0 comments on commit 6ce280c

Please sign in to comment.