mirror of
https://github.com/Gericom/teak-llvm.git
synced 2025-06-24 05:55:43 -04:00

declaring methods and when sending messages to them, by bringing all of the selector into TypedCheck chunks in the completion result. This way, we can improve the sorting of these results to account for the full selector name rather than just the first chunk. llvm-svn: 116746
604 lines
21 KiB
C++
604 lines
21 KiB
C++
//===- CIndexCodeCompletion.cpp - Code Completion API hooks ---------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the Clang-C Source Indexing library hooks for
|
|
// code completion.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CIndexer.h"
|
|
#include "CIndexDiagnostic.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "clang/Basic/FileManager.h"
|
|
#include "clang/Frontend/ASTUnit.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
|
#include "clang/Sema/CodeCompleteConsumer.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/CrashRecoveryContext.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/Timer.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/System/Program.h"
|
|
#include <cstdlib>
|
|
#include <cstdio>
|
|
|
|
|
|
#ifdef UDP_CODE_COMPLETION_LOGGER
|
|
#include "clang/Basic/Version.h"
|
|
#include <arpa/inet.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
using namespace clang;
|
|
using namespace clang::cxstring;
|
|
|
|
namespace {
|
|
/// \brief Stored representation of a completion string.
|
|
///
|
|
/// This is the representation behind a CXCompletionString.
|
|
class CXStoredCodeCompletionString : public CodeCompletionString {
|
|
unsigned Priority;
|
|
CXAvailabilityKind Availability;
|
|
|
|
public:
|
|
CXStoredCodeCompletionString(unsigned Priority,
|
|
CXAvailabilityKind Availability)
|
|
: Priority(Priority), Availability(Availability) { }
|
|
|
|
unsigned getPriority() const { return Priority; }
|
|
CXAvailabilityKind getAvailability() const { return Availability; }
|
|
};
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
enum CXCompletionChunkKind
|
|
clang_getCompletionChunkKind(CXCompletionString completion_string,
|
|
unsigned chunk_number) {
|
|
CXStoredCodeCompletionString *CCStr
|
|
= (CXStoredCodeCompletionString *)completion_string;
|
|
if (!CCStr || chunk_number >= CCStr->size())
|
|
return CXCompletionChunk_Text;
|
|
|
|
switch ((*CCStr)[chunk_number].Kind) {
|
|
case CodeCompletionString::CK_TypedText:
|
|
return CXCompletionChunk_TypedText;
|
|
case CodeCompletionString::CK_Text:
|
|
return CXCompletionChunk_Text;
|
|
case CodeCompletionString::CK_Optional:
|
|
return CXCompletionChunk_Optional;
|
|
case CodeCompletionString::CK_Placeholder:
|
|
return CXCompletionChunk_Placeholder;
|
|
case CodeCompletionString::CK_Informative:
|
|
return CXCompletionChunk_Informative;
|
|
case CodeCompletionString::CK_ResultType:
|
|
return CXCompletionChunk_ResultType;
|
|
case CodeCompletionString::CK_CurrentParameter:
|
|
return CXCompletionChunk_CurrentParameter;
|
|
case CodeCompletionString::CK_LeftParen:
|
|
return CXCompletionChunk_LeftParen;
|
|
case CodeCompletionString::CK_RightParen:
|
|
return CXCompletionChunk_RightParen;
|
|
case CodeCompletionString::CK_LeftBracket:
|
|
return CXCompletionChunk_LeftBracket;
|
|
case CodeCompletionString::CK_RightBracket:
|
|
return CXCompletionChunk_RightBracket;
|
|
case CodeCompletionString::CK_LeftBrace:
|
|
return CXCompletionChunk_LeftBrace;
|
|
case CodeCompletionString::CK_RightBrace:
|
|
return CXCompletionChunk_RightBrace;
|
|
case CodeCompletionString::CK_LeftAngle:
|
|
return CXCompletionChunk_LeftAngle;
|
|
case CodeCompletionString::CK_RightAngle:
|
|
return CXCompletionChunk_RightAngle;
|
|
case CodeCompletionString::CK_Comma:
|
|
return CXCompletionChunk_Comma;
|
|
case CodeCompletionString::CK_Colon:
|
|
return CXCompletionChunk_Colon;
|
|
case CodeCompletionString::CK_SemiColon:
|
|
return CXCompletionChunk_SemiColon;
|
|
case CodeCompletionString::CK_Equal:
|
|
return CXCompletionChunk_Equal;
|
|
case CodeCompletionString::CK_HorizontalSpace:
|
|
return CXCompletionChunk_HorizontalSpace;
|
|
case CodeCompletionString::CK_VerticalSpace:
|
|
return CXCompletionChunk_VerticalSpace;
|
|
}
|
|
|
|
// Should be unreachable, but let's be careful.
|
|
return CXCompletionChunk_Text;
|
|
}
|
|
|
|
CXString clang_getCompletionChunkText(CXCompletionString completion_string,
|
|
unsigned chunk_number) {
|
|
CXStoredCodeCompletionString *CCStr
|
|
= (CXStoredCodeCompletionString *)completion_string;
|
|
if (!CCStr || chunk_number >= CCStr->size())
|
|
return createCXString(0);
|
|
|
|
switch ((*CCStr)[chunk_number].Kind) {
|
|
case CodeCompletionString::CK_TypedText:
|
|
case CodeCompletionString::CK_Text:
|
|
case CodeCompletionString::CK_Placeholder:
|
|
case CodeCompletionString::CK_CurrentParameter:
|
|
case CodeCompletionString::CK_Informative:
|
|
case CodeCompletionString::CK_LeftParen:
|
|
case CodeCompletionString::CK_RightParen:
|
|
case CodeCompletionString::CK_LeftBracket:
|
|
case CodeCompletionString::CK_RightBracket:
|
|
case CodeCompletionString::CK_LeftBrace:
|
|
case CodeCompletionString::CK_RightBrace:
|
|
case CodeCompletionString::CK_LeftAngle:
|
|
case CodeCompletionString::CK_RightAngle:
|
|
case CodeCompletionString::CK_Comma:
|
|
case CodeCompletionString::CK_ResultType:
|
|
case CodeCompletionString::CK_Colon:
|
|
case CodeCompletionString::CK_SemiColon:
|
|
case CodeCompletionString::CK_Equal:
|
|
case CodeCompletionString::CK_HorizontalSpace:
|
|
case CodeCompletionString::CK_VerticalSpace:
|
|
return createCXString((*CCStr)[chunk_number].Text, false);
|
|
|
|
case CodeCompletionString::CK_Optional:
|
|
// Note: treated as an empty text block.
|
|
return createCXString("");
|
|
}
|
|
|
|
// Should be unreachable, but let's be careful.
|
|
return createCXString(0);
|
|
}
|
|
|
|
|
|
CXCompletionString
|
|
clang_getCompletionChunkCompletionString(CXCompletionString completion_string,
|
|
unsigned chunk_number) {
|
|
CXStoredCodeCompletionString *CCStr
|
|
= (CXStoredCodeCompletionString *)completion_string;
|
|
if (!CCStr || chunk_number >= CCStr->size())
|
|
return 0;
|
|
|
|
switch ((*CCStr)[chunk_number].Kind) {
|
|
case CodeCompletionString::CK_TypedText:
|
|
case CodeCompletionString::CK_Text:
|
|
case CodeCompletionString::CK_Placeholder:
|
|
case CodeCompletionString::CK_CurrentParameter:
|
|
case CodeCompletionString::CK_Informative:
|
|
case CodeCompletionString::CK_LeftParen:
|
|
case CodeCompletionString::CK_RightParen:
|
|
case CodeCompletionString::CK_LeftBracket:
|
|
case CodeCompletionString::CK_RightBracket:
|
|
case CodeCompletionString::CK_LeftBrace:
|
|
case CodeCompletionString::CK_RightBrace:
|
|
case CodeCompletionString::CK_LeftAngle:
|
|
case CodeCompletionString::CK_RightAngle:
|
|
case CodeCompletionString::CK_Comma:
|
|
case CodeCompletionString::CK_ResultType:
|
|
case CodeCompletionString::CK_Colon:
|
|
case CodeCompletionString::CK_SemiColon:
|
|
case CodeCompletionString::CK_Equal:
|
|
case CodeCompletionString::CK_HorizontalSpace:
|
|
case CodeCompletionString::CK_VerticalSpace:
|
|
return 0;
|
|
|
|
case CodeCompletionString::CK_Optional:
|
|
// Note: treated as an empty text block.
|
|
return (*CCStr)[chunk_number].Optional;
|
|
}
|
|
|
|
// Should be unreachable, but let's be careful.
|
|
return 0;
|
|
}
|
|
|
|
unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) {
|
|
CXStoredCodeCompletionString *CCStr
|
|
= (CXStoredCodeCompletionString *)completion_string;
|
|
return CCStr? CCStr->size() : 0;
|
|
}
|
|
|
|
unsigned clang_getCompletionPriority(CXCompletionString completion_string) {
|
|
CXStoredCodeCompletionString *CCStr
|
|
= (CXStoredCodeCompletionString *)completion_string;
|
|
return CCStr? CCStr->getPriority() : unsigned(CCP_Unlikely);
|
|
}
|
|
|
|
enum CXAvailabilityKind
|
|
clang_getCompletionAvailability(CXCompletionString completion_string) {
|
|
CXStoredCodeCompletionString *CCStr
|
|
= (CXStoredCodeCompletionString *)completion_string;
|
|
return CCStr? CCStr->getAvailability() : CXAvailability_Available;
|
|
}
|
|
|
|
/// \brief The CXCodeCompleteResults structure we allocate internally;
|
|
/// the client only sees the initial CXCodeCompleteResults structure.
|
|
struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults {
|
|
AllocatedCXCodeCompleteResults();
|
|
~AllocatedCXCodeCompleteResults();
|
|
|
|
/// \brief Diagnostics produced while performing code completion.
|
|
llvm::SmallVector<StoredDiagnostic, 8> Diagnostics;
|
|
|
|
/// \brief Diag object
|
|
llvm::IntrusiveRefCntPtr<Diagnostic> Diag;
|
|
|
|
/// \brief Language options used to adjust source locations.
|
|
LangOptions LangOpts;
|
|
|
|
/// \brief Source manager, used for diagnostics.
|
|
SourceManager SourceMgr;
|
|
|
|
/// \brief File manager, used for diagnostics.
|
|
FileManager FileMgr;
|
|
|
|
/// \brief Temporary files that should be removed once we have finished
|
|
/// with the code-completion results.
|
|
std::vector<llvm::sys::Path> TemporaryFiles;
|
|
|
|
/// \brief Temporary buffers that will be deleted once we have finished with the code-completion results.
|
|
llvm::SmallVector<const llvm::MemoryBuffer *, 1> TemporaryBuffers;
|
|
};
|
|
|
|
AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults()
|
|
: CXCodeCompleteResults(), Diag(new Diagnostic), SourceMgr(*Diag) { }
|
|
|
|
AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() {
|
|
for (unsigned I = 0, N = NumResults; I != N; ++I)
|
|
delete (CXStoredCodeCompletionString *)Results[I].CompletionString;
|
|
delete [] Results;
|
|
|
|
for (unsigned I = 0, N = TemporaryFiles.size(); I != N; ++I)
|
|
TemporaryFiles[I].eraseFromDisk();
|
|
for (unsigned I = 0, N = TemporaryBuffers.size(); I != N; ++I)
|
|
delete TemporaryBuffers[I];
|
|
}
|
|
|
|
} // end extern "C"
|
|
|
|
namespace {
|
|
class CaptureCompletionResults : public CodeCompleteConsumer {
|
|
AllocatedCXCodeCompleteResults &AllocatedResults;
|
|
llvm::SmallVector<CXCompletionResult, 16> StoredResults;
|
|
|
|
public:
|
|
explicit CaptureCompletionResults(AllocatedCXCodeCompleteResults &Results)
|
|
: CodeCompleteConsumer(true, false, true, false),
|
|
AllocatedResults(Results) { }
|
|
~CaptureCompletionResults() { Finish(); }
|
|
|
|
virtual void ProcessCodeCompleteResults(Sema &S,
|
|
CodeCompletionContext Context,
|
|
CodeCompletionResult *Results,
|
|
unsigned NumResults) {
|
|
StoredResults.reserve(StoredResults.size() + NumResults);
|
|
for (unsigned I = 0; I != NumResults; ++I) {
|
|
CXStoredCodeCompletionString *StoredCompletion
|
|
= new CXStoredCodeCompletionString(Results[I].Priority,
|
|
Results[I].Availability);
|
|
(void)Results[I].CreateCodeCompletionString(S, StoredCompletion);
|
|
|
|
CXCompletionResult R;
|
|
R.CursorKind = Results[I].CursorKind;
|
|
R.CompletionString = StoredCompletion;
|
|
StoredResults.push_back(R);
|
|
}
|
|
}
|
|
|
|
virtual void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
|
|
OverloadCandidate *Candidates,
|
|
unsigned NumCandidates) {
|
|
StoredResults.reserve(StoredResults.size() + NumCandidates);
|
|
for (unsigned I = 0; I != NumCandidates; ++I) {
|
|
// FIXME: Set priority, availability appropriately.
|
|
CXStoredCodeCompletionString *StoredCompletion
|
|
= new CXStoredCodeCompletionString(1, CXAvailability_Available);
|
|
(void)Candidates[I].CreateSignatureString(CurrentArg, S,
|
|
StoredCompletion);
|
|
|
|
CXCompletionResult R;
|
|
R.CursorKind = CXCursor_NotImplemented;
|
|
R.CompletionString = StoredCompletion;
|
|
StoredResults.push_back(R);
|
|
}
|
|
}
|
|
|
|
private:
|
|
void Finish() {
|
|
AllocatedResults.Results = new CXCompletionResult [StoredResults.size()];
|
|
AllocatedResults.NumResults = StoredResults.size();
|
|
std::memcpy(AllocatedResults.Results, StoredResults.data(),
|
|
StoredResults.size() * sizeof(CXCompletionResult));
|
|
StoredResults.clear();
|
|
}
|
|
};
|
|
}
|
|
|
|
extern "C" {
|
|
struct CodeCompleteAtInfo {
|
|
CXTranslationUnit TU;
|
|
const char *complete_filename;
|
|
unsigned complete_line;
|
|
unsigned complete_column;
|
|
struct CXUnsavedFile *unsaved_files;
|
|
unsigned num_unsaved_files;
|
|
unsigned options;
|
|
CXCodeCompleteResults *result;
|
|
};
|
|
void clang_codeCompleteAt_Impl(void *UserData) {
|
|
CodeCompleteAtInfo *CCAI = static_cast<CodeCompleteAtInfo*>(UserData);
|
|
CXTranslationUnit TU = CCAI->TU;
|
|
const char *complete_filename = CCAI->complete_filename;
|
|
unsigned complete_line = CCAI->complete_line;
|
|
unsigned complete_column = CCAI->complete_column;
|
|
struct CXUnsavedFile *unsaved_files = CCAI->unsaved_files;
|
|
unsigned num_unsaved_files = CCAI->num_unsaved_files;
|
|
unsigned options = CCAI->options;
|
|
CCAI->result = 0;
|
|
|
|
#ifdef UDP_CODE_COMPLETION_LOGGER
|
|
#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
|
|
const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime();
|
|
#endif
|
|
#endif
|
|
|
|
bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != 0;
|
|
|
|
ASTUnit *AST = static_cast<ASTUnit *>(TU);
|
|
if (!AST)
|
|
return;
|
|
|
|
ASTUnit::ConcurrencyCheck Check(*AST);
|
|
|
|
// Perform the remapping of source files.
|
|
llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles;
|
|
for (unsigned I = 0; I != num_unsaved_files; ++I) {
|
|
llvm::StringRef Data(unsaved_files[I].Contents, unsaved_files[I].Length);
|
|
const llvm::MemoryBuffer *Buffer
|
|
= llvm::MemoryBuffer::getMemBufferCopy(Data, unsaved_files[I].Filename);
|
|
RemappedFiles.push_back(std::make_pair(unsaved_files[I].Filename,
|
|
Buffer));
|
|
}
|
|
|
|
if (EnableLogging) {
|
|
// FIXME: Add logging.
|
|
}
|
|
|
|
// Parse the resulting source file to find code-completion results.
|
|
AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults;
|
|
Results->Results = 0;
|
|
Results->NumResults = 0;
|
|
|
|
// Create a code-completion consumer to capture the results.
|
|
CaptureCompletionResults Capture(*Results);
|
|
|
|
// Perform completion.
|
|
AST->CodeComplete(complete_filename, complete_line, complete_column,
|
|
RemappedFiles.data(), RemappedFiles.size(),
|
|
(options & CXCodeComplete_IncludeMacros),
|
|
(options & CXCodeComplete_IncludeCodePatterns),
|
|
Capture,
|
|
*Results->Diag, Results->LangOpts, Results->SourceMgr,
|
|
Results->FileMgr, Results->Diagnostics,
|
|
Results->TemporaryBuffers);
|
|
|
|
|
|
|
|
#ifdef UDP_CODE_COMPLETION_LOGGER
|
|
#ifdef UDP_CODE_COMPLETION_LOGGER_PORT
|
|
const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime();
|
|
llvm::SmallString<256> LogResult;
|
|
llvm::raw_svector_ostream os(LogResult);
|
|
|
|
// Figure out the language and whether or not it uses PCH.
|
|
const char *lang = 0;
|
|
bool usesPCH = false;
|
|
|
|
for (std::vector<const char*>::iterator I = argv.begin(), E = argv.end();
|
|
I != E; ++I) {
|
|
if (*I == 0)
|
|
continue;
|
|
if (strcmp(*I, "-x") == 0) {
|
|
if (I + 1 != E) {
|
|
lang = *(++I);
|
|
continue;
|
|
}
|
|
}
|
|
else if (strcmp(*I, "-include") == 0) {
|
|
if (I+1 != E) {
|
|
const char *arg = *(++I);
|
|
llvm::SmallString<512> pchName;
|
|
{
|
|
llvm::raw_svector_ostream os(pchName);
|
|
os << arg << ".pth";
|
|
}
|
|
pchName.push_back('\0');
|
|
struct stat stat_results;
|
|
if (stat(pchName.data(), &stat_results) == 0)
|
|
usesPCH = true;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
os << "{ ";
|
|
os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime());
|
|
os << ", \"numRes\": " << Results->NumResults;
|
|
os << ", \"diags\": " << Results->Diagnostics.size();
|
|
os << ", \"pch\": " << (usesPCH ? "true" : "false");
|
|
os << ", \"lang\": \"" << (lang ? lang : "<unknown>") << '"';
|
|
const char *name = getlogin();
|
|
os << ", \"user\": \"" << (name ? name : "unknown") << '"';
|
|
os << ", \"clangVer\": \"" << getClangFullVersion() << '"';
|
|
os << " }";
|
|
|
|
llvm::StringRef res = os.str();
|
|
if (res.size() > 0) {
|
|
do {
|
|
// Setup the UDP socket.
|
|
struct sockaddr_in servaddr;
|
|
bzero(&servaddr, sizeof(servaddr));
|
|
servaddr.sin_family = AF_INET;
|
|
servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT);
|
|
if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER,
|
|
&servaddr.sin_addr) <= 0)
|
|
break;
|
|
|
|
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (sockfd < 0)
|
|
break;
|
|
|
|
sendto(sockfd, res.data(), res.size(), 0,
|
|
(struct sockaddr *)&servaddr, sizeof(servaddr));
|
|
close(sockfd);
|
|
}
|
|
while (false);
|
|
}
|
|
#endif
|
|
#endif
|
|
CCAI->result = Results;
|
|
}
|
|
CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU,
|
|
const char *complete_filename,
|
|
unsigned complete_line,
|
|
unsigned complete_column,
|
|
struct CXUnsavedFile *unsaved_files,
|
|
unsigned num_unsaved_files,
|
|
unsigned options) {
|
|
CodeCompleteAtInfo CCAI = { TU, complete_filename, complete_line,
|
|
complete_column, unsaved_files, num_unsaved_files,
|
|
options, 0 };
|
|
llvm::CrashRecoveryContext CRC;
|
|
|
|
if (!CRC.RunSafely(clang_codeCompleteAt_Impl, &CCAI)) {
|
|
fprintf(stderr, "libclang: crash detected in code completion\n");
|
|
static_cast<ASTUnit *>(TU)->setUnsafeToFree(true);
|
|
return 0;
|
|
}
|
|
|
|
return CCAI.result;
|
|
}
|
|
|
|
unsigned clang_defaultCodeCompleteOptions(void) {
|
|
return CXCodeComplete_IncludeMacros;
|
|
}
|
|
|
|
void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) {
|
|
if (!ResultsIn)
|
|
return;
|
|
|
|
AllocatedCXCodeCompleteResults *Results
|
|
= static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn);
|
|
delete Results;
|
|
}
|
|
|
|
unsigned
|
|
clang_codeCompleteGetNumDiagnostics(CXCodeCompleteResults *ResultsIn) {
|
|
AllocatedCXCodeCompleteResults *Results
|
|
= static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn);
|
|
if (!Results)
|
|
return 0;
|
|
|
|
return Results->Diagnostics.size();
|
|
}
|
|
|
|
CXDiagnostic
|
|
clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *ResultsIn,
|
|
unsigned Index) {
|
|
AllocatedCXCodeCompleteResults *Results
|
|
= static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn);
|
|
if (!Results || Index >= Results->Diagnostics.size())
|
|
return 0;
|
|
|
|
return new CXStoredDiagnostic(Results->Diagnostics[Index], Results->LangOpts);
|
|
}
|
|
|
|
|
|
} // end extern "C"
|
|
|
|
/// \brief Simple utility function that appends a \p New string to the given
|
|
/// \p Old string, using the \p Buffer for storage.
|
|
///
|
|
/// \param Old The string to which we are appending. This parameter will be
|
|
/// updated to reflect the complete string.
|
|
///
|
|
///
|
|
/// \param New The string to append to \p Old.
|
|
///
|
|
/// \param Buffer A buffer that stores the actual, concatenated string. It will
|
|
/// be used if the old string is already-non-empty.
|
|
static void AppendToString(llvm::StringRef &Old, llvm::StringRef New,
|
|
llvm::SmallString<256> &Buffer) {
|
|
if (Old.empty()) {
|
|
Old = New;
|
|
return;
|
|
}
|
|
|
|
if (Buffer.empty())
|
|
Buffer.append(Old.begin(), Old.end());
|
|
Buffer.append(New.begin(), New.end());
|
|
Old = Buffer.str();
|
|
}
|
|
|
|
/// \brief Get the typed-text blocks from the given code-completion string
|
|
/// and return them as a single string.
|
|
///
|
|
/// \param String The code-completion string whose typed-text blocks will be
|
|
/// concatenated.
|
|
///
|
|
/// \param Buffer A buffer used for storage of the completed name.
|
|
static llvm::StringRef GetTypedName(CodeCompletionString *String,
|
|
llvm::SmallString<256> &Buffer) {
|
|
llvm::StringRef Result;
|
|
for (CodeCompletionString::iterator C = String->begin(), CEnd = String->end();
|
|
C != CEnd; ++C) {
|
|
if (C->Kind == CodeCompletionString::CK_TypedText)
|
|
AppendToString(Result, C->Text, Buffer);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
namespace {
|
|
struct OrderCompletionResults {
|
|
bool operator()(const CXCompletionResult &XR,
|
|
const CXCompletionResult &YR) const {
|
|
CXStoredCodeCompletionString *X
|
|
= (CXStoredCodeCompletionString *)XR.CompletionString;
|
|
CXStoredCodeCompletionString *Y
|
|
= (CXStoredCodeCompletionString *)YR.CompletionString;
|
|
|
|
llvm::SmallString<256> XBuffer;
|
|
llvm::StringRef XText = GetTypedName(X, XBuffer);
|
|
llvm::SmallString<256> YBuffer;
|
|
llvm::StringRef YText = GetTypedName(Y, YBuffer);
|
|
|
|
if (XText.empty() || YText.empty())
|
|
return !XText.empty();
|
|
|
|
int result = XText.compare_lower(YText);
|
|
if (result < 0)
|
|
return true;
|
|
if (result > 0)
|
|
return false;
|
|
|
|
result = XText.compare(YText);
|
|
return result < 0;
|
|
}
|
|
};
|
|
}
|
|
|
|
extern "C" {
|
|
void clang_sortCodeCompletionResults(CXCompletionResult *Results,
|
|
unsigned NumResults) {
|
|
std::stable_sort(Results, Results + NumResults, OrderCompletionResults());
|
|
}
|
|
}
|