/**
 * Get human readable text representation of a parent context of the given completion string
 */
QString TranslationUnit::makeParentText(CXCompletionString str, const CXCursorKind cur_kind)
{
    CXCursorKind parent_ck;
    const clang::DCXString parent_clstr = {clang_getCompletionParent(str, &parent_ck)};
    const auto parent_cstr = clang_getCString(parent_clstr);
    if (parent_cstr)
    {
        const auto parent_str = QString{parent_cstr}.trimmed();
        if (parent_str.isEmpty())
            return GLOBAL_NS_GROUP_STR;
        QString prefix;
        switch (parent_ck)
        {
            case CXCursor_StructDecl:
                prefix = STRUCT_NS_STR;
                break;
            case CXCursor_UnionDecl:
                prefix = UNION_NS_STR;
                break;
            case CXCursor_ClassDecl:
                prefix = CLASS_NS_STR;
                break;
            case CXCursor_EnumDecl:
                prefix = ENUM_NS_STR;
                break;
            case CXCursor_Namespace:
                prefix = NAMESPACE_NS_STR;
                break;
            /// \todo More types?
            default:
                break;
        }
        if (!prefix.isEmpty())
            prefix += ' ';
        return prefix + parent_str;
    }
    else if (cur_kind == CXCursor_MacroDefinition)
        return PREPROCESSOR_GROUP_STR;
    return GLOBAL_NS_GROUP_STR;
}
Esempio n. 2
0
std::string code_completion_string::parent() const
{
    return string(clang_getCompletionParent(str, nullptr)).str();
}
Esempio n. 3
0
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);

    assert(!cache->translationUnit || cache->source == request->source);
    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;
        flags |= CXTranslationUnit_DetailedPreprocessingRecord;
        flags |= CXTranslationUnit_Incomplete;
        flags |= CXTranslationUnit_IncludeBriefCommentsInCodeCompletion;

        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 = cache->parseTime = sw.restart();
        if (cache->translationUnit) {
            RTags::reparseTranslationUnit(cache->translationUnit, &unsaved, request->unsaved.size() ? 1 : 0);
        }
        reparseTime = cache->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 (%zu) 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();
    unsigned int completionFlags = (CXCodeComplete_IncludeCodePatterns|CXCodeComplete_IncludeBriefComments);
    if (request->flags & CodeCompleteIncludeMacros)
        completionFlags |= CXCodeComplete_IncludeMacros;

    CXCodeCompleteResults *results = clang_codeCompleteAt(cache->translationUnit, sourceFile.constData(),
                                                          request->location.line(), request->location.column(),
                                                          &unsaved, unsaved.Length ? 1 : 0, completionFlags);
    completeTime = cache->codeCompleteTime = sw.restart();
    ++cache->completions;
    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)) {
                switch (availabilityKind) {
                case CXAvailability_Available:
                    break;
                case CXAvailability_Deprecated:
                    break;
                case CXAvailability_NotAccessible:
                    break;
                case CXAvailability_NotAvailable: // protected members are erroneously flagged as NotAvailable in clang 3.6
                    continue;
                }
            }

            const int priority = clang_getCompletionPriority(string);

            if (static_cast<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.isEmpty()
                        || (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) {
                const unsigned int annotations = clang_getCompletionNumAnnotations(string);
                for (unsigned i=0; i<annotations; ++i) {
                    const CXStringScope annotation = clang_getCompletionAnnotation(string, i);
                    const char *cstr = clang_getCString(annotation);
                    if (const int len = strlen(cstr)) {
                        if (!node.annotation.isEmpty())
                            node.annotation.append(' ');
                        node.annotation.append(cstr, len);
                    }
                }
                node.parent = RTags::eatString(clang_getCompletionParent(string, 0));
                node.briefComment = RTags::eatString(clang_getCompletionBriefComment(string));

                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.isEmpty() ? -1 : 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 %zu)%s",
                    request->location.toString().constData(),
                    parseTime, reparseTime, completeTime, processTime, nodeCount, request->unsaved.size(),
                    request->flags & Refresh ? " Refresh" : "");
        } else {
            printCompletions(List<Completions::Candidate>(), request);
            error() << "No completion results available" << request->location << results->NumResults;
        }
        clang_disposeCodeCompleteResults(results);
    }
}