Skip to content

Commit 6ce280c

Browse files
committed
* Implement basic calltips (no scope resolution yet...)
1 parent e8b8ace commit 6ce280c

File tree

6 files changed

+301
-3
lines changed

6 files changed

+301
-3
lines changed

clangplugin.cpp

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,90 @@ wxString ClangPlugin::GetDocumentation(const CCToken& token)
365365

366366
std::vector<ClangPlugin::CCCallTip> ClangPlugin::GetCallTips(int pos, int style, cbEditor* ed, int& argsPos)
367367
{
368-
return std::vector<CCCallTip>();
368+
std::vector<CCCallTip> tips;
369+
if (ed != m_pLastEditor)
370+
{
371+
m_TranslUnitId = m_Proxy.GetTranslationUnitId(ed->GetFilename());
372+
m_pLastEditor = ed;
373+
}
374+
if (m_TranslUnitId == wxNOT_FOUND)
375+
return tips;
376+
377+
cbStyledTextCtrl* stc = ed->GetControl();
378+
379+
int nest = 0;
380+
int commas = 0;
381+
382+
while (--pos > 0)
383+
{
384+
const int curStyle = stc->GetStyleAt(pos);
385+
if ( stc->IsString(curStyle)
386+
|| stc->IsCharacter(curStyle)
387+
|| stc->IsComment(curStyle) )
388+
{
389+
continue;
390+
}
391+
392+
const wxChar ch = stc->GetCharAt(pos);
393+
if (ch == wxT(';'))
394+
return tips; // error?
395+
else if (ch == wxT(','))
396+
{
397+
if (nest == 0)
398+
++commas;
399+
}
400+
else if (ch == wxT(')'))
401+
--nest;
402+
else if (ch == wxT('('))
403+
{
404+
++nest;
405+
if (nest > 0)
406+
break;
407+
}
408+
}
409+
while (--pos > 0)
410+
{
411+
if ( stc->GetCharAt(pos) <= wxT(' ')
412+
|| stc->IsComment(stc->GetStyleAt(pos)) )
413+
{
414+
continue;
415+
}
416+
break;
417+
}
418+
argsPos = stc->WordEndPosition(pos, true);
419+
if (argsPos != m_LastCallTipPos)
420+
{
421+
m_LastCallTips.clear();
422+
const int line = stc->LineFromPosition(pos);
423+
const int column = pos - stc->PositionFromLine(line);
424+
const wxString& tknText = stc->GetTextRange(stc->WordStartPosition(pos, true), argsPos);
425+
if (!tknText.IsEmpty())
426+
m_Proxy.GetCallTipsAt(ed->GetFilename(), line + 1, column + 1, m_TranslUnitId, tknText, m_LastCallTips);
427+
}
428+
m_LastCallTipPos = argsPos;
429+
for (std::vector<wxStringVec>::const_iterator strVecItr = m_LastCallTips.begin(); strVecItr != m_LastCallTips.end(); ++strVecItr)
430+
{
431+
int strVecSz = strVecItr->size();
432+
if (commas != 0 && strVecSz < commas + 3)
433+
continue;
434+
wxString tip;
435+
int hlStart = wxSCI_INVALID_POSITION;
436+
int hlEnd = wxSCI_INVALID_POSITION;
437+
for (int i = 0; i < strVecSz; ++i)
438+
{
439+
if (i == commas + 1 && strVecSz > 2)
440+
{
441+
hlStart = tip.Length();
442+
hlEnd = hlStart + (*strVecItr)[i].Length();
443+
}
444+
tip += (*strVecItr)[i];
445+
if (i > 0 && i < (strVecSz - 2))
446+
tip += wxT(", ");
447+
}
448+
tips.push_back(CCCallTip(tip, hlStart, hlEnd));
449+
}
450+
451+
return tips;
369452
}
370453

371454
std::vector<ClangPlugin::CCToken> ClangPlugin::GetTokenAt(int pos, cbEditor* ed, bool& allowCallTip)
@@ -436,6 +519,11 @@ void ClangPlugin::DoAutocomplete(const CCToken& token, cbEditor* ed)
436519
stc->ReplaceTarget(tknText);
437520
stc->SetSelectionVoid(moveToPos + offsets.first, moveToPos + offsets.second);
438521
stc->ChooseCaretX();
522+
if (token.category != tcLangKeyword && offsets.first != offsets.second)
523+
{
524+
CodeBlocksEvent evt(cbEVT_SHOW_CALL_TIP);
525+
Manager::Get()->ProcessEvent(evt);
526+
}
439527
}
440528

441529
void ClangPlugin::BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data)
@@ -684,7 +772,7 @@ void ClangPlugin::OnTimer(wxTimerEvent& event)
684772
Compiler* comp = nullptr;
685773
if (pf)
686774
{
687-
target = pf->GetParentProject()->GetBuildTarget(pf->GetbuildTargets()[0]);
775+
target = pf->GetParentProject()->GetBuildTarget(pf->GetBuildTargets()[0]);
688776
comp = CompilerFactory::GetCompiler(target->GetCompilerID());
689777
if (pf->GetUseCustomBuildCommand(target->GetCompilerID()))
690778
{

clangplugin.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class ClangPlugin : public cbCodeCompletionPlugin
6161
cbEditor* m_pLastEditor;
6262
int m_TranslUnitId;
6363
int m_EditorHookId;
64+
int m_LastCallTipPos;
65+
std::vector<wxStringVec> m_LastCallTips;
6466
};
6567

6668
#endif // CLANGPLUGIN_H

clangproxy.cpp

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class TranslationUnit
4545
Reparse(0, nullptr); // seems to improve performance for some reason?
4646

4747
clang_visitChildren(clang_getTranslationUnitCursor(m_ClTranslUnit), ClAST_Visitor, database);
48+
database->Shrink();
4849
}
4950

5051
// move ctor
@@ -617,11 +618,18 @@ ClangProxy::~ClangProxy()
617618
void ClangProxy::CreateTranslationUnit(const wxString& filename, const wxString& commands)
618619
{
619620
wxStringTokenizer tokenizer(commands);
621+
std::vector<wxString> unknownOptions;
622+
unknownOptions.push_back(wxT("-Wno-unused-local-typedefs"));
623+
unknownOptions.push_back(wxT("-Wzero-as-null-pointer-constant"));
624+
std::sort(unknownOptions.begin(), unknownOptions.end());
620625
std::vector<wxCharBuffer> argsBuffer;
621626
std::vector<const char*> args;
622627
while (tokenizer.HasMoreTokens())
623628
{
624-
argsBuffer.push_back(tokenizer.GetNextToken().ToUTF8());
629+
const wxString& compilerSwitch = tokenizer.GetNextToken();
630+
if (std::binary_search(unknownOptions.begin(), unknownOptions.end(), compilerSwitch))
631+
continue;
632+
argsBuffer.push_back(compilerSwitch.ToUTF8());
625633
args.push_back(argsBuffer.back().data());
626634
}
627635
m_TranslUnits.push_back(TranslationUnit(filename, args, m_ClIndex, &m_Database));
@@ -973,6 +981,188 @@ void ClangProxy::RefineTokenType(int translId, int tknId, int& tknType)
973981
}
974982
}
975983

984+
static CXChildVisitResult ClCallTipCtorAST_Visitor(CXCursor cursor, CXCursor parent, CXClientData client_data)
985+
{
986+
switch (cursor.kind)
987+
{
988+
case CXCursor_Constructor:
989+
{
990+
std::vector<CXCursor>* tokenSet = static_cast<std::vector<CXCursor>*>(client_data);
991+
tokenSet->push_back(cursor);
992+
break;
993+
}
994+
995+
case CXCursor_FunctionDecl:
996+
case CXCursor_CXXMethod:
997+
case CXCursor_FunctionTemplate:
998+
{
999+
CXString str = clang_getCursorSpelling(cursor);
1000+
if (strcmp(clang_getCString(str), "operator()") == 0)
1001+
{
1002+
std::vector<CXCursor>* tokenSet = static_cast<std::vector<CXCursor>*>(client_data);
1003+
tokenSet->push_back(cursor);
1004+
}
1005+
clang_disposeString(str);
1006+
break;
1007+
}
1008+
1009+
default:
1010+
break;
1011+
}
1012+
return CXChildVisit_Continue;
1013+
}
1014+
1015+
void ClangProxy::GetCallTipsAt(const wxString& filename, int line, int column, int translId, const wxString& tokenStr, std::vector<wxStringVec>& results)
1016+
{
1017+
std::vector<CXCursor> tokenSet;
1018+
if (column > static_cast<int>(tokenStr.Length()))
1019+
{
1020+
column -= tokenStr.Length() / 2;
1021+
CXCursor token = m_TranslUnits[translId].GetTokensAt(filename, line, column);
1022+
if (!clang_Cursor_isNull(token))
1023+
{
1024+
CXCursor resolve = clang_getCursorDefinition(token);
1025+
if (clang_Cursor_isNull(resolve) || clang_isInvalid(token.kind))
1026+
{
1027+
resolve = clang_getCursorReferenced(token);
1028+
if (!clang_Cursor_isNull(resolve) && !clang_isInvalid(token.kind))
1029+
token = resolve;
1030+
}
1031+
else
1032+
token = resolve;
1033+
tokenSet.push_back(token);
1034+
}
1035+
}
1036+
// TODO: searching the database is very inexact, but necessary, as clang
1037+
// does not resolve the token when the code is invalid (incomplete)
1038+
std::vector<TokenId> tknIds = m_Database.GetTokenMatches(tokenStr);
1039+
for (std::vector<TokenId>::const_iterator itr = tknIds.begin(); itr != tknIds.end(); ++itr)
1040+
{
1041+
const AbstractToken& aTkn = m_Database.GetToken(*itr);
1042+
CXCursor token = m_TranslUnits[translId].GetTokensAt(m_Database.GetFilename(aTkn.fileId), aTkn.line, aTkn.column);
1043+
if (!clang_Cursor_isNull(token) && !clang_isInvalid(token.kind))
1044+
tokenSet.push_back(token);
1045+
}
1046+
std::set<wxString> uniqueTips;
1047+
for (size_t tknIdx = 0; tknIdx < tokenSet.size(); ++tknIdx)
1048+
{
1049+
CXCursor token = tokenSet[tknIdx];
1050+
switch (GetTokenCategory(token.kind, CX_CXXPublic))
1051+
{
1052+
case tcVarPublic:
1053+
{
1054+
token = clang_getTypeDeclaration(clang_getCursorResultType(token));
1055+
if (!clang_Cursor_isNull(token) && !clang_isInvalid(token.kind))
1056+
tokenSet.push_back(token);
1057+
break;
1058+
}
1059+
1060+
case tcTypedefPublic:
1061+
{
1062+
token = clang_getTypeDeclaration(clang_getTypedefDeclUnderlyingType(token));
1063+
if (!clang_Cursor_isNull(token) && !clang_isInvalid(token.kind))
1064+
tokenSet.push_back(token);
1065+
break;
1066+
}
1067+
1068+
case tcClassPublic:
1069+
{
1070+
// search for constructors and 'operator()'
1071+
clang_visitChildren(token, &ClCallTipCtorAST_Visitor, &tokenSet);
1072+
break;
1073+
}
1074+
1075+
case tcCtorPublic:
1076+
{
1077+
if (clang_getCXXAccessSpecifier(token) == CX_CXXPrivate)
1078+
break;
1079+
// fall through
1080+
}
1081+
case tcFuncPublic:
1082+
{
1083+
const CXCompletionString& clCompStr = clang_getCursorCompletionString(token);
1084+
wxStringVec entry;
1085+
int upperBound = clang_getNumCompletionChunks(clCompStr);
1086+
entry.push_back(wxEmptyString);
1087+
for (int chunkIdx = 0; chunkIdx < upperBound; ++chunkIdx)
1088+
{
1089+
CXCompletionChunkKind kind = clang_getCompletionChunkKind(clCompStr, chunkIdx);
1090+
if (kind == CXCompletionChunk_TypedText)
1091+
{
1092+
CXString str = clang_getCompletionParent(clCompStr, nullptr);
1093+
wxString parent = wxString::FromUTF8(clang_getCString(str));
1094+
if (!parent.IsEmpty())
1095+
entry[0] += parent + wxT("::");
1096+
clang_disposeString(str);
1097+
}
1098+
else if (kind == CXCompletionChunk_LeftParen)
1099+
{
1100+
if (entry[0].IsEmpty() || !entry[0].EndsWith(wxT("operator")))
1101+
break;
1102+
}
1103+
CXString str = clang_getCompletionChunkText(clCompStr, chunkIdx);
1104+
entry[0] += wxString::FromUTF8(clang_getCString(str));
1105+
if (kind == CXCompletionChunk_ResultType)
1106+
{
1107+
if (entry[0].Length() > 2 && entry[0][entry[0].Length() - 2] == wxT(' '))
1108+
entry[0].RemoveLast(2) += entry[0].Last();
1109+
entry[0] += wxT(' ');
1110+
}
1111+
clang_disposeString(str);
1112+
}
1113+
entry[0] += wxT('(');
1114+
int numArgs = clang_Cursor_getNumArguments(token);
1115+
for (int argIdx = 0; argIdx < numArgs; ++argIdx)
1116+
{
1117+
CXCursor arg = clang_Cursor_getArgument(token, argIdx);
1118+
1119+
wxString tknStr;
1120+
const CXCompletionString& argStr = clang_getCursorCompletionString(arg);
1121+
upperBound = clang_getNumCompletionChunks(argStr);
1122+
for (int chunkIdx = 0; chunkIdx < upperBound; ++chunkIdx)
1123+
{
1124+
CXCompletionChunkKind kind = clang_getCompletionChunkKind(argStr, chunkIdx);
1125+
if (kind == CXCompletionChunk_TypedText)
1126+
{
1127+
CXString str = clang_getCompletionParent(argStr, nullptr);
1128+
wxString parent = wxString::FromUTF8(clang_getCString(str));
1129+
if (!parent.IsEmpty())
1130+
tknStr += parent + wxT("::");
1131+
clang_disposeString(str);
1132+
}
1133+
CXString str = clang_getCompletionChunkText(argStr, chunkIdx);
1134+
tknStr += wxString::FromUTF8(clang_getCString(str));
1135+
if (kind == CXCompletionChunk_ResultType)
1136+
{
1137+
if (tknStr.Length() > 2 && tknStr[tknStr.Length() - 2] == wxT(' '))
1138+
tknStr.RemoveLast(2) += tknStr.Last();
1139+
tknStr += wxT(' ');
1140+
}
1141+
clang_disposeString(str);
1142+
}
1143+
1144+
entry.push_back(tknStr.Trim());
1145+
}
1146+
entry.push_back(wxT(')'));
1147+
wxString composit;
1148+
for (wxStringVec::const_iterator itr = entry.begin();
1149+
itr != entry.end(); ++itr)
1150+
{
1151+
composit += *itr;
1152+
}
1153+
if (uniqueTips.find(composit) != uniqueTips.end())
1154+
break;
1155+
uniqueTips.insert(composit);
1156+
results.push_back(entry);
1157+
break;
1158+
}
1159+
1160+
default:
1161+
break;
1162+
}
1163+
}
1164+
}
1165+
9761166
void ClangProxy::GetTokensAt(const wxString& filename, int line, int column, int translId, wxStringVec& results)
9771167
{
9781168
CXCursor token = m_TranslUnits[translId].GetTokensAt(filename, line, column);

clangproxy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ class ClangProxy
8383
wxString GetCCInsertSuffix(int translId, int tknId, const wxString& newLine, std::pair<int, int>& offsets);
8484
void RefineTokenType(int translId, int tknId, int& tknType); // TODO: cache TokenId (if resolved) for DocumentCCToken()
8585

86+
void GetCallTipsAt(const wxString& filename, int line, int column, int translId, const wxString& tokenStr, std::vector<wxStringVec>& results);
87+
8688
void GetTokensAt(const wxString& filename, int line, int column, int translId, std::vector<wxString>& results);
8789
void ResolveTokenAt(wxString& filename, int& line, int& column, int translId);
8890

tokendatabase.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,14 @@ AbstractToken& TokenDatabase::GetToken(TokenId tId) const
6161
{
6262
return m_pTokens->GetValue(tId);
6363
}
64+
65+
std::vector<TokenId> TokenDatabase::GetTokenMatches(const wxString& identifier) const
66+
{
67+
return m_pTokens->GetIdSet(identifier);
68+
}
69+
70+
void TokenDatabase::Shrink()
71+
{
72+
m_pFilenames->Shrink();
73+
m_pTokens->Shrink();
74+
}

tokendatabase.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef TOKENDATABASE_H
22
#define TOKENDATABASE_H
33

4+
#include <vector>
5+
46
template<typename _Tp> class TreeMap;
57
class wxString;
68
typedef int FileId;
@@ -29,6 +31,9 @@ class TokenDatabase
2931
TokenId InsertToken(const wxString& identifier, const AbstractToken& token); // duplicate tokens are discarded
3032
TokenId GetTokenId(const wxString& identifier, unsigned tokenHash) const; // returns wxNOT_FOUND on failure
3133
AbstractToken& GetToken(TokenId tId) const;
34+
std::vector<TokenId> GetTokenMatches(const wxString& identifier) const;
35+
36+
void Shrink();
3237

3338
private:
3439
TreeMap<AbstractToken>* m_pTokens;

0 commit comments

Comments
 (0)