void CompletionJob::execute() { StopWatch timer; CXUnsavedFile unsavedFile = { mUnsaved.isEmpty() ? 0 : mPath.constData(), mUnsaved.isEmpty() ? 0 : mUnsaved.constData(), static_cast<unsigned long>(mUnsaved.size()) }; if (!mUnit) { String clangLine; RTags::parseTranslationUnit(mPath, mArgs, mUnit, Server::instance()->clangIndex(), clangLine, 0, 0, &unsavedFile, 1); mParseCount = 1; if (!mUnit) { error() << "Failed to parse" << mPath << "Can't complete"; return; } } // error() << "Completing" << mPath << mParseCount; assert(mParseCount >= 1 && mParseCount <= 2); if (mParseCount == 1) { RTags::reparseTranslationUnit(mUnit, &unsavedFile, 1); if (!mUnit) { mFinished(mPath, id()); return; } else { ++mParseCount; } } CXCodeCompleteResults *results = clang_codeCompleteAt(mUnit, mPath.constData(), mLine, mColumn, &unsavedFile, mUnsaved.isEmpty() ? 0 : 1, CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns); if (results) { CompletionNode *nodes = new CompletionNode[results->NumResults]; int nodeCount = 0; Map<Token, int> tokens; if (!mUnsaved.isEmpty()) { tokenize(mUnsaved.constData(), mUnsaved.size(), tokens); // for (Map<Token, int>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) { // error() << String(it->first.data, it->first.length) << it->second; // } } for (unsigned i = 0; i < results->NumResults; ++i) { const CXCursorKind kind = results->Results[i].CursorKind; if (kind == CXCursor_Destructor) continue; const CXCompletionString &string = results->Results[i].CompletionString; const CXAvailabilityKind availabilityKind = clang_getCompletionAvailability(string); if (availabilityKind != CXAvailability_Available) continue; const int priority = clang_getCompletionPriority(string); if (priority >= 75) continue; CompletionNode &node = nodes[nodeCount]; node.priority = priority; node.signature.reserve(256); const int chunkCount = clang_getNumCompletionChunks(string); bool ok = true; for (int j=0; j<chunkCount; ++j) { const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j); if (chunkKind == CXCompletionChunk_TypedText) { node.completion = RTags::eatString(clang_getCompletionChunkText(string, j)); if (node.completion.size() > 8 && node.completion.startsWith("operator") && !isPartOfSymbol(node.completion.at(8))) { ok = false; break; } node.signature.append(node.completion); } else { node.signature.append(RTags::eatString(clang_getCompletionChunkText(string, j))); if (chunkKind == CXCompletionChunk_ResultType) node.signature.append(' '); } } if (ok) { int ws = node.completion.size() - 1; while (ws >= 0 && isspace(node.completion.at(ws))) --ws; if (ws >= 0) { node.completion.truncate(ws + 1); node.signature.replace("\n", ""); node.distance = tokens.value(Token(node.completion.constData(), node.completion.size()), -1); ++nodeCount; continue; } } node.completion.clear(); node.signature.clear(); } if (nodeCount) { if (nodeCount > SendThreshold) { write("`"); } else { qsort(nodes, nodeCount, sizeof(CompletionNode), compareCompletionNode); if (mType == Stream) { write<128>("`%s %s", nodes[0].completion.constData(), nodes[0].signature.constData()); } else { write<128>("%s %s", nodes[0].completion.constData(), nodes[0].signature.constData()); } for (int i=1; i<nodeCount; ++i) { write<128>("%s %s", nodes[i].completion.constData(), nodes[i].signature.constData()); } } } warning() << "Wrote" << ((nodeCount > SendThreshold) ? -1 : nodeCount) << "completions for" << String::format<128>("%s:%d:%d", mPath.constData(), mLine, mColumn) << "in" << timer.elapsed() << "ms" << mArgs; // const unsigned diagnosticCount = clang_getNumDiagnostics(mUnit); // for (unsigned i=0; i<diagnosticCount; ++i) { // CXDiagnostic diagnostic = clang_getDiagnostic(mUnit, i); // const CXDiagnosticSeverity severity = clang_getDiagnosticSeverity(diagnostic); // const String msg = RTags::eatString(clang_getDiagnosticSpelling(diagnostic)); // CXFile file; // unsigned line, col; // clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file, &line, &col, 0); // error() << i << diagnosticCount << severity << msg // << String::format<128>("%s:%d:%d", RTags::eatString(clang_getFileName(file)).constData(), // line, col); // clang_disposeDiagnostic(diagnostic); // } delete[] nodes; //processDiagnostics(results); clang_disposeCodeCompleteResults(results); std::shared_ptr<Project> proj = project(); if (proj) { // error() << "Adding to cache" << mParseCount << mPath; proj->addToCache(mPath, mArgs, mUnit, mParseCount); } } mFinished(mPath, id()); }
void CompletionThread::process(Request *request) { // if (!request->unsaved.isEmpty()) { // int line = request->location.line(); // int pos = 0; // while (line > 1) { // int p = request->unsaved.indexOf('\n', pos); // if (p == -1) { // pos = -1; // break; // } // pos = p + 1; // --line; // } // if (pos != -1) { // int end = request->unsaved.indexOf('\n', pos); // if (end == -1) // end = request->unsaved.size(); // error("Completing at %s:%d:%d line: [%s]\n", // request->location.path().constData(), // request->location.line(), // request->location.column(), // request->unsaved.mid(pos, end - pos).constData()); // } // } StopWatch sw; int parseTime = 0; int reparseTime = 0; int completeTime = 0; int processTime = 0; SourceFile *&cache = mCacheMap[request->source.fileId]; if (cache && cache->source != request->source) { delete cache; cache = 0; } if (!cache) { cache = new SourceFile; mCacheList.append(cache); while (mCacheMap.size() > mCacheSize) { SourceFile *c = mCacheList.removeFirst(); mCacheMap.remove(c->source.fileId); delete c; } } else { mCacheList.moveToEnd(cache); } const bool sendDebug = testLog(LogLevel::Debug); if (cache->translationUnit && cache->source != request->source) { clang_disposeTranslationUnit(cache->translationUnit); cache->translationUnit = 0; cache->source = request->source; } else if (!cache->translationUnit) { cache->source = request->source; } const Path sourceFile = request->source.sourceFile(); CXUnsavedFile unsaved = { sourceFile.constData(), request->unsaved.constData(), static_cast<unsigned long>(request->unsaved.size()) }; size_t hash = 0; uint64_t lastModified = 0; if (request->unsaved.size()) { std::hash<String> h; hash = h(request->unsaved); } else { lastModified = sourceFile.lastModifiedMs(); } const auto &options = Server::instance()->options(); if (!cache->translationUnit) { cache->completionsMap.clear(); cache->completionsList.deleteAll(); sw.restart(); Flags<CXTranslationUnit_Flags> flags = static_cast<CXTranslationUnit_Flags>(clang_defaultEditingTranslationUnitOptions()); flags |= CXTranslationUnit_PrecompiledPreamble; flags |= CXTranslationUnit_CacheCompletionResults; flags |= CXTranslationUnit_SkipFunctionBodies; for (const auto &inc : options.includePaths) { request->source.includePaths << inc; } request->source.defines << options.defines; String clangLine; RTags::parseTranslationUnit(sourceFile, request->source.toCommandLine(Source::Default|Source::ExcludeDefaultArguments), cache->translationUnit, mIndex, &unsaved, request->unsaved.size() ? 1 : 0, flags, &clangLine); // error() << "PARSING" << clangLine; parseTime = sw.restart(); if (cache->translationUnit) { RTags::reparseTranslationUnit(cache->translationUnit, &unsaved, request->unsaved.size() ? 1 : 0); } reparseTime = sw.elapsed(); if (!cache->translationUnit) return; cache->unsavedHash = hash; cache->lastModified = lastModified; } else if (cache->unsavedHash != hash || cache->lastModified != lastModified) { cache->completionsMap.clear(); cache->completionsList.deleteAll(); cache->unsavedHash = hash; cache->lastModified = lastModified; } else if (!(request->flags & Refresh)) { const auto it = cache->completionsMap.find(request->location); if (it != cache->completionsMap.end()) { cache->completionsList.moveToEnd(it->second); error("Found completions (%d) in cache %s:%d:%d", it->second->candidates.size(), sourceFile.constData(), request->location.line(), request->location.column()); printCompletions(it->second->candidates, request); return; } } sw.restart(); const unsigned int completionFlags = (CXCodeComplete_IncludeMacros|CXCodeComplete_IncludeCodePatterns); CXCodeCompleteResults *results = clang_codeCompleteAt(cache->translationUnit, sourceFile.constData(), request->location.line(), request->location.column(), &unsaved, unsaved.Length ? 1 : 0, completionFlags); completeTime = sw.restart(); if (results) { std::vector<Completions::Candidate> nodes; nodes.reserve(results->NumResults); int nodeCount = 0; Map<Token, int> tokens; if (!request->unsaved.isEmpty()) { tokens = Token::tokenize(request->unsaved.constData(), request->unsaved.size()); // for (Map<Token, int>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) { // error() << String(it->first.data, it->first.length) << it->second; // } } for (unsigned int i = 0; i < results->NumResults; ++i) { const CXCursorKind kind = results->Results[i].CursorKind; if (!(options.options & Server::CompletionsNoFilter) && kind == CXCursor_Destructor) continue; const CXCompletionString &string = results->Results[i].CompletionString; const CXAvailabilityKind availabilityKind = clang_getCompletionAvailability(string); if (!(options.options & Server::CompletionsNoFilter) && availabilityKind != CXAvailability_Available) continue; const int priority = clang_getCompletionPriority(string); if (size_t(nodeCount) == nodes.size()) nodes.emplace_back(); Completions::Candidate &node = nodes.back(); node.cursorKind = kind; node.priority = priority; node.signature.reserve(256); const int chunkCount = clang_getNumCompletionChunks(string); bool ok = true; for (int j=0; j<chunkCount; ++j) { const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j); if (chunkKind == CXCompletionChunk_TypedText) { node.completion = RTags::eatString(clang_getCompletionChunkText(string, j)); if (node.completion.size() > 8 && node.completion.startsWith("operator") && !isPartOfSymbol(node.completion.at(8))) { ok = false; break; } node.signature.append(node.completion); } else { node.signature.append(RTags::eatString(clang_getCompletionChunkText(string, j))); if (chunkKind == CXCompletionChunk_ResultType) node.signature.append(' '); } } if (ok) { int ws = node.completion.size() - 1; while (ws >= 0 && isspace(node.completion.at(ws))) --ws; if (ws >= 0) { node.completion.truncate(ws + 1); node.signature.replace("\n", ""); node.distance = tokens.value(Token(node.completion.constData(), node.completion.size()), -1); if (sendDebug) debug() << node.signature << node.priority << kind << node.distance << clang_getCompletionAvailability(string); ++nodeCount; continue; } } node.completion.clear(); node.signature.clear(); } if (nodeCount) { // Sort pointers instead of shuffling candidates around std::vector<Completions::Candidate*> nodesPtr; nodesPtr.reserve(nodeCount); for (auto& n : nodes) nodesPtr.push_back(&n); std::sort(nodesPtr.begin(), nodesPtr.end(), compareCompletionCandidates); Completions *&c = cache->completionsMap[request->location]; if (c) { cache->completionsList.moveToEnd(c); } else { enum { MaxCompletionCache = 10 }; // ### configurable? c = new Completions(request->location); cache->completionsList.append(c); while (cache->completionsMap.size() > MaxCompletionCache) { Completions *cc = cache->completionsList.takeFirst(); cache->completionsMap.remove(cc->location); delete cc; } } c->candidates.resize(nodeCount); for (int i=0; i<nodeCount; ++i) c->candidates[i] = std::move(*nodesPtr[i]); printCompletions(c->candidates, request); processTime = sw.elapsed(); warning("Processed %s, parse %d/%d, complete %d, process %d => %d completions (unsaved %d)", sourceFile.constData(), parseTime, reparseTime, completeTime, processTime, nodeCount, request->unsaved.size()); } else { printCompletions(List<Completions::Candidate>(), request); error() << "No completion results available" << request->location << results->NumResults; } clang_disposeCodeCompleteResults(results); } }
void CompletionJob::execute() { CXUnsavedFile unsavedFile = { mUnsaved.isEmpty() ? 0 : mPath.constData(), mUnsaved.isEmpty() ? 0 : mUnsaved.constData(), static_cast<unsigned long>(mUnsaved.size()) }; CXCodeCompleteResults *results = clang_codeCompleteAt(mUnit, mPath.constData(), mLine, mColumn, &unsavedFile, mUnsaved.isEmpty() ? 0 : 1, CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeCodePatterns); if (results) { CompletionNode *nodes = new CompletionNode[results->NumResults]; int nodeCount = 0; Map<Token, int> tokens; if (!mUnsaved.isEmpty()) { tokenize(mUnsaved.constData(), mUnsaved.size(), tokens); // for (Map<Token, int>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) { // error() << ByteArray(it->first.data, it->first.length) << it->second; // } } for (unsigned i = 0; i < results->NumResults; ++i) { const CXCursorKind kind = results->Results[i].CursorKind; if (kind == CXCursor_Destructor) continue; const CXCompletionString &string = results->Results[i].CompletionString; const CXAvailabilityKind availabilityKind = clang_getCompletionAvailability(string); if (availabilityKind != CXAvailability_Available) continue; const int priority = clang_getCompletionPriority(string); if (priority >= 75) continue; CompletionNode &node = nodes[nodeCount]; node.priority = priority; node.signature.reserve(256); const int chunkCount = clang_getNumCompletionChunks(string); bool ok = true; for (int j=0; j<chunkCount; ++j) { const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(string, j); if (chunkKind == CXCompletionChunk_TypedText) { node.completion = RTags::eatString(clang_getCompletionChunkText(string, j)); if (node.completion.size() > 8 && node.completion.startsWith("operator") && !isPartOfSymbol(node.completion.at(8))) { ok = false; break; } node.signature.append(node.completion); } else { node.signature.append(RTags::eatString(clang_getCompletionChunkText(string, j))); if (chunkKind == CXCompletionChunk_ResultType) node.signature.append(' '); } } if (ok) { int ws = node.completion.size() - 1; while (ws >= 0 && isspace(node.completion.at(ws))) --ws; if (ws >= 0) { node.completion.truncate(ws + 1); node.signature.replace("\n", ""); node.distance = tokens.value(Token(node.completion.constData(), node.completion.size()), -1); ++nodeCount; continue; } } node.completion.clear(); node.signature.clear(); } if (nodeCount) { qsort(nodes, nodeCount, sizeof(CompletionNode), compareCompletionNode); write<128>("`%s %s", nodes[0].completion.constData(), nodes[0].signature.constData()); for (int i=1; i<nodeCount; ++i) { write<128>("%s %s", nodes[i].completion.constData(), nodes[i].signature.constData()); } } delete[] nodes; //processDiagnostics(results); clang_disposeCodeCompleteResults(results); shared_ptr<Project> proj = project(); if (proj) proj->addToCache(mPath, mArgs, mIndex, mUnit); } mFinished(mPath); }