void CodeCompletionsExtractor::extractHasParameters()
{
    const uint completionChunkCount = clang_getNumCompletionChunks(currentCxCodeCompleteResult.CompletionString);
    for (uint chunkIndex = 0; chunkIndex < completionChunkCount; ++chunkIndex) {
        const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(currentCxCodeCompleteResult.CompletionString, chunkIndex);
        if (chunkKind == CXCompletionChunk_LeftParen) {
            const CXCompletionChunkKind nextChunkKind = clang_getCompletionChunkKind(currentCxCodeCompleteResult.CompletionString, chunkIndex + 1);
            currentCodeCompletion_.setHasParameters(nextChunkKind != CXCompletionChunk_RightParen);
            return;
        }
    }
}
Esempio n. 2
0
void print_completion_string(CXCompletionString completion_string, FILE *file) {
  int I, N;

  N = clang_getNumCompletionChunks(completion_string);
  for (I = 0; I != N; ++I) {
    CXString text;
    const char *cstr;
    enum CXCompletionChunkKind Kind
      = clang_getCompletionChunkKind(completion_string, I);

    if (Kind == CXCompletionChunk_Optional) {
      fprintf(file, "{Optional ");
      print_completion_string(
                clang_getCompletionChunkCompletionString(completion_string, I),
                              file);
      fprintf(file, "}");
      continue;
    }

    text = clang_getCompletionChunkText(completion_string, I);
    cstr = clang_getCString(text);
    fprintf(file, "{%s %s}",
            clang_getCompletionChunkKindSpelling(Kind),
            cstr ? cstr : "");
    clang_disposeString(text);
  }

}
Esempio n. 3
0
void CompletionData::ExtractDataFromChunk( CXCompletionString completion_string,
                                           size_t chunk_num,
                                           bool &saw_left_paren,
                                           bool &saw_function_params,
                                           bool &saw_placeholder ) {
  CXCompletionChunkKind kind = clang_getCompletionChunkKind(
                                 completion_string, chunk_num );

  if ( IsMainCompletionTextInfo( kind ) ) {
    if ( kind == CXCompletionChunk_LeftParen ) {
      saw_left_paren = true;
    } else if ( saw_left_paren &&
                !saw_function_params &&
                kind != CXCompletionChunk_RightParen &&
                kind != CXCompletionChunk_Informative ) {
      saw_function_params = true;
      everything_except_return_type_.append( " " );
    } else if ( saw_function_params && kind == CXCompletionChunk_RightParen ) {
      everything_except_return_type_.append( " " );
    }

    if ( kind == CXCompletionChunk_Optional ) {
      everything_except_return_type_.append(
        OptionalChunkToString( completion_string, chunk_num ) );
    } else {
      everything_except_return_type_.append(
        ChunkToString( completion_string, chunk_num ) );
    }
  }

  switch ( kind ) {
    case CXCompletionChunk_ResultType:
      return_type_ = ChunkToString( completion_string, chunk_num );
      break;

    case CXCompletionChunk_Placeholder:
      saw_placeholder = true;
      break;

    case CXCompletionChunk_TypedText:
    case CXCompletionChunk_Text:

      // need to add paren to insert string
      // when implementing inherited methods or declared methods in objc.
    case CXCompletionChunk_LeftParen:
    case CXCompletionChunk_RightParen:
    case CXCompletionChunk_HorizontalSpace:
      if ( !saw_placeholder ) {
        original_string_ += ChunkToString( completion_string, chunk_num );
      }

      break;

    default:
      break;
  }
}
void CodeCompletionsExtractor::extractText()
{
    const uint completionChunkCount = clang_getNumCompletionChunks(currentCxCodeCompleteResult.CompletionString);
    for (uint chunkIndex = 0; chunkIndex < completionChunkCount; ++chunkIndex) {
        const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(currentCxCodeCompleteResult.CompletionString, chunkIndex);
        if (chunkKind == CXCompletionChunk_TypedText) {
            currentCodeCompletion_.setText(CodeCompletionChunkConverter::chunkText(currentCxCodeCompleteResult.CompletionString, chunkIndex));
            break;
        }
    }
}
Esempio n. 5
0
static CXString get_result_typed_text(CXCompletionResult *r)
{
	unsigned int chunks_n = clang_getNumCompletionChunks(r->CompletionString);
	for (unsigned int i = 0; i < chunks_n; ++i) {
		enum CXCompletionChunkKind kind;
		kind = clang_getCompletionChunkKind(r->CompletionString, i);
		if (kind == CXCompletionChunk_TypedText)
			return clang_getCompletionChunkText(r->CompletionString, i);
	}
	CXString empty = {0,0};
	return empty;
}
bool CodeCompletionsExtractor::hasText(const Utf8String &text, CXCompletionString cxCompletionString) const
{
    const uint completionChunkCount = clang_getNumCompletionChunks(cxCompletionString);

    for (uint chunkIndex = 0; chunkIndex < completionChunkCount; ++chunkIndex) {
        const CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(cxCompletionString, chunkIndex);
        if (chunkKind == CXCompletionChunk_TypedText) {
            const ClangString currentText(clang_getCompletionChunkText(cxCompletionString, chunkIndex));
            return text == currentText;
        }
    }

    return false;
}
Esempio n. 7
0
static size_t count_type_chars(CXCompletionResult *r)
{
	unsigned int chars = 0;
	unsigned int chunks_n = clang_getNumCompletionChunks(r->CompletionString);
	for (unsigned int i = 0; i < chunks_n; ++i) {
		enum CXCompletionChunkKind kind;
		kind = clang_getCompletionChunkKind(r->CompletionString, i);
		if (kind == CXCompletionChunk_ResultType) {
			CXString s = clang_getCompletionChunkText(r->CompletionString, i);
			chars += strlen(clang_getCString(s));
			clang_disposeString(s);
		}
	}
	return chars;
}
void CodeCompletionsExtractor::extractMacroCompletionKind()
{
    CXCompletionString cxCompletionString = cxCodeCompleteResults->Results[cxCodeCompleteResultIndex].CompletionString;

    const uint completionChunkCount = clang_getNumCompletionChunks(cxCompletionString);

    for (uint chunkIndex = 0; chunkIndex < completionChunkCount; ++chunkIndex) {
        CXCompletionChunkKind kind = clang_getCompletionChunkKind(cxCompletionString, chunkIndex);
        if (kind == CXCompletionChunk_Placeholder) {
            currentCodeCompletion_.setCompletionKind(CodeCompletion::FunctionCompletionKind);
            return;
        }
    }

    currentCodeCompletion_.setCompletionKind(CodeCompletion::PreProcessorCompletionKind);
}
Esempio n. 9
0
const char*
cc_result_entryname(CXCompletionString cs) {
  int num = clang_getNumCompletionChunks(cs);
  if(clang_getCompletionAvailability(cs) == CXAvailability_Available && num >= 1) {
    int i;
    for(i=0; i<num; i++) {
      int type = clang_getCompletionChunkKind(cs, i);
      if(type == CXCompletionChunk_TypedText){
        CXString str = clang_getCompletionChunkText(cs, i);
        return clang_getCString(str);
      }
    }
    return NULL;
  }else {
    return NULL;
  }
}
Esempio n. 10
0
std::string
CompletionData::OptionalChunkToString( CXCompletionString completion_string,
                                       size_t chunk_num ) {
  std::string final_string;

  if ( !completion_string )
    return final_string;

  CXCompletionString optional_completion_string =
    clang_getCompletionChunkCompletionString( completion_string, chunk_num );

  if ( !optional_completion_string )
    return final_string;

  size_t optional_num_chunks = clang_getNumCompletionChunks(
                               optional_completion_string );

  for ( size_t j = 0; j < optional_num_chunks; ++j ) {
    CXCompletionChunkKind kind = clang_getCompletionChunkKind(
                                   optional_completion_string, j );

    if ( kind == CXCompletionChunk_Optional ) {
      final_string.append( OptionalChunkToString( optional_completion_string,
                                                  j ) );
    }

    else if ( kind == CXCompletionChunk_Placeholder ) {
      final_string.append(
          "[" + ChunkToString( optional_completion_string, j ) + "]" );
    }

    else if ( kind == CXCompletionChunk_CurrentParameter ) {
      current_arg_ = "[" + ChunkToString( optional_completion_string, j ) + "]";
      final_string.append( "☞  " + current_arg_ );
    }

    else {
      final_string.append( ChunkToString( optional_completion_string, j ) );
    }
  }

  return final_string;
}
Esempio n. 11
0
OovStringVec Tokenizer::codeComplete(size_t offset)
    {
    CLangAutoLock lock(mCLangLock, __LINE__, this);
    OovStringVec strs;
    unsigned options = 0;
// This gets more than we want.
//    unsigned options = clang_defaultCodeCompleteOptions();
    unsigned int line;
    unsigned int column;
    getLineColumn(offset, line, column);
    CXCodeCompleteResults *results = clang_codeCompleteAt(mTransUnit,
            mSourceFilename.getStr(), line, column,
            nullptr, 0, options);
    if(results)
        {
        clang_sortCodeCompletionResults(&results->Results[0], results->NumResults);
        for(size_t ri=0; ri<results->NumResults /*&& ri < 50*/; ri++)
            {
            OovString str;
            CXCompletionString compStr = results->Results[ri].CompletionString;
            size_t numChunks = clang_getNumCompletionChunks(compStr);
            for(size_t ci=0; ci<numChunks && ci < 30; ci++)
                {
                CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(compStr, ci);
                // We will discard return values from functions, so the first
                // chunk returned will be the identifier or function name.  Function
                // arguments will be returned after a space, so they can be
                // discarded easily.
                if(chunkKind == CXCompletionChunk_TypedText || str.length())
                    {
                    std::string chunkStr = getDisposedString(clang_getCompletionChunkText(compStr, ci));
                    if(str.length() != 0)
                    str += ' ';
                    str += chunkStr;
                    }
                }
            strs.push_back(str);
            }
        clang_disposeCodeCompleteResults(results);
        }
    return strs;
    }
/* Print the completion line except the header term (COMPLETION: TypedText),
 * the output format should be identical with the result of clang -cc1
 * -code-completion-at. Here are some sample outputs from the clang code
 * completion process:

     COMPLETION: short
     COMPLETION: signed
     COMPLETION: static
     COMPLETION: Pattern : static_cast<<#type#>>(<#expression#>)
     COMPLETION: struct

 * However, here we don't handle Pattern explicitly because the emacs
 * script would simply drop those pattern lines with an regexp T T
 */
static void completion_printAllCompletionTerms(
    CXCompletionString completion_string, FILE *fp)
{
    int i_chunk  = 0;
    int n_chunks = clang_getNumCompletionChunks(completion_string);

    CXString chk_text;
    enum CXCompletionChunkKind chk_kind;

    for ( ; i_chunk < n_chunks; i_chunk++)
    {
        /* get the type and completion text of this chunk */
        chk_kind = clang_getCompletionChunkKind(completion_string, i_chunk);
        chk_text = clang_getCompletionChunkText(completion_string, i_chunk);
        
        /* differenct kinds of chunks has various output formats */
        switch (chk_kind)
        {
        case CXCompletionChunk_Placeholder:
            fprintf(fp, "<#%s#>", clang_getCString(chk_text));
            break;
                
        case CXCompletionChunk_ResultType:
            fprintf(fp, "[#%s#]", clang_getCString(chk_text));
            break;

        case CXCompletionChunk_Optional:
            /* print optional term in a recursive way */
            fprintf(fp, "{#");
            completion_printAllCompletionTerms(
                clang_getCompletionChunkCompletionString(completion_string, i_chunk),
                fp);
            fprintf(fp, "#}");
            break;
                
        default:
            fprintf(fp, "%s", clang_getCString(chk_text));
        }

        clang_disposeString(chk_text);
    }
}
Esempio n. 13
0
void GmacsPreprocessor::codeCompletion(const QTextCursor &cursor)
{
	int line = cursor.blockNumber();
	int column = cursor.columnNumber();
	//const char *name = filename->toLocal8Bit().data();
	const char *name = clang_getCString(clang_getTranslationUnitSpelling(unit));
	//qDebug() << cursor.document()->toPlainText();
	//qDebug() << cursor.document()->toPlainText().size();
	QString document = cursor.document()->toPlainText();
	QString text = cursor.block().text();
	QRegExp exp("\t");
	int tab_count = text.count(exp);
	fprintf(stderr, "column = [%d]\n", cursor.positionInBlock());
	//unsaved_file->Filename = name;
	//unsaved_file->Contents = document.toLocal8Bit().data();
	//unsaved_file->Length = document.size();
	if (!unit) return;
	CLANG_REPARSE(unit, NULL);//unsaved_file);
	fprintf(stderr, "line = [%d], column = [%d]\n", line+1, column + tab_count);
	fprintf(stderr, "name = [%s]\n", name);
	CXCodeCompleteResults *res = CLANG_CODE_COMPLETION(unit, name, line+1, column + tab_count);
	if (!res) fprintf(stderr, "ERROR: could not complete\n");
	for (size_t i = 0; i < clang_codeCompleteGetNumDiagnostics(res); i++) {
		const CXDiagnostic &diag = clang_codeCompleteGetDiagnostic(res, i);
		const CXString &s = clang_getDiagnosticSpelling(diag);
		fprintf(stderr, "%s\n", clang_getCString(s));
	}
	unsigned num_results = res->NumResults;
	fprintf(stderr, "num_results = [%d]\n");
	for (unsigned i = 0; i < num_results; i++) {
		const CXCompletionString& str = res->Results[i].CompletionString;
		unsigned chunks = clang_getNumCompletionChunks(str);
		for (unsigned j = 0; j < chunks; j++) {
			const CXString& out = clang_getCompletionChunkText(str, j);
			//std::cout << clang_getCString(out) << " ";
			if (clang_getCompletionChunkKind(str, j) != CXCompletionChunk_TypedText)
				continue;
		}
		//std::cout << std::endl;
	}
	clang_disposeCodeCompleteResults(res);
}
Esempio n. 14
0
static int make_ac_proposal(struct make_ac_ctx *ctx, struct ac_proposal *p,
			    CXCompletionResult *r, str_t *fmt)
{
	unsigned int chunks_n;

	chunks_n = clang_getNumCompletionChunks(r->CompletionString);
	str_clear(ctx->word);
	str_clear(ctx->abbr);
	str_clear(ctx->type);
	str_clear(ctx->text);

	for (unsigned int i = 0; i < chunks_n; ++i) {
		enum CXCompletionChunkKind kind;
		CXString s;

		kind = clang_getCompletionChunkKind(r->CompletionString, i);
		s = clang_getCompletionChunkText(r->CompletionString, i);
		switch (kind) {
		case CXCompletionChunk_ResultType:
			str_add_printf(&ctx->type, "%s", clang_getCString(s));
			break;
		case CXCompletionChunk_TypedText:
			str_add_cstr(&ctx->word, clang_getCString(s));
		default:
			str_add_cstr(&ctx->text, clang_getCString(s));
			break;
		}
		clang_disposeString(s);
	}

	if (ctx->type->len > MAX_TYPE_CHARS) {
		ctx->type->len = MAX_TYPE_CHARS-1;
		str_add_cstr(&ctx->type, "…");
	}
	str_add_printf(&ctx->abbr, fmt->data,
		       ctx->type->data, ctx->text->data);

	p->abbr = strdup(ctx->abbr->data);
	p->word = strdup(ctx->word->data);
	return 1;
}
Esempio n. 15
0
void CompletionData::ExtractDataFromChunk( CXCompletionString completion_string,
                                           uint chunk_num,
                                           bool &saw_left_paren,
                                           bool &saw_function_params ) {
  CXCompletionChunkKind kind = clang_getCompletionChunkKind(
                                 completion_string, chunk_num );

  if ( IsMainCompletionTextInfo( kind ) ) {
    if ( kind == CXCompletionChunk_LeftParen ) {
      saw_left_paren = true;
    }

    else if ( saw_left_paren &&
              !saw_function_params &&
              kind != CXCompletionChunk_RightParen &&
              kind != CXCompletionChunk_Informative ) {
      saw_function_params = true;
      everything_except_return_type_.append( " " );
    }

    else if ( saw_function_params && kind == CXCompletionChunk_RightParen ) {
      everything_except_return_type_.append( " " );
    }

    if ( kind == CXCompletionChunk_Optional ) {
      everything_except_return_type_.append(
        OptionalChunkToString( completion_string, chunk_num ) );
    }

    else {
      everything_except_return_type_.append(
        ChunkToString( completion_string, chunk_num ) );
    }
  }

  if ( kind == CXCompletionChunk_ResultType )
    return_type_ = ChunkToString( completion_string, chunk_num );

  if ( kind == CXCompletionChunk_TypedText )
    original_string_ = ChunkToString( completion_string, chunk_num );
}
Esempio n. 16
0
static int completion_printCompletionHeadTerm(
    CXCompletionString completion_string, FILE *fp)
{
    int i_chunk  = 0;
    int n_chunks = clang_getNumCompletionChunks(completion_string);
    CXString ac_string;

    /* inspect all chunks only to find the TypedText chunk */
    for ( ; i_chunk < n_chunks; i_chunk++)
    {
        if (clang_getCompletionChunkKind(completion_string, i_chunk)
            == CXCompletionChunk_TypedText)
        {
            /* We got it, just dump it to fp */
            ac_string = clang_getCompletionChunkText(completion_string, i_chunk);
            fprintf(fp, "COMPLETION: %s", clang_getCString(ac_string));
            clang_disposeString(ac_string);
            return n_chunks;    /* care package on the way */
        }
    }

    return -1;   /* We haven't found TypedText chunk in completion_string */
}
/* Print "COMPLETION: " followed by the TypedText chunk of the completion
 * string to fp, that's the text that a user would be expected to type to get
 * this code-completion result. TypedText is the keyword for the client program
 * (emacs script in this case) to filter completion results.
 *
 * Only prints a completion if it matches the given prefix.
 * 
 * This function returns the number of matching completion chunks on success, or it
 * would return an -1 if no matching TypedText chunk was found.
 */
static int completion_printCompletionHeadTerm(
    CXCompletionString completion_string, FILE *fp, char *prefix)
{
    int i_chunk  = 0;
    int n_chunks = clang_getNumCompletionChunks(completion_string);
    CXString ac_string;

    /* inspect all chunks only to find the TypedText chunk */
    for ( ; i_chunk < n_chunks; i_chunk++) {
        if (clang_getCompletionChunkKind(completion_string, i_chunk) == CXCompletionChunk_TypedText) {
            /* We got it, dump it to fp if it matches the prefix */
            ac_string = clang_getCompletionChunkText(completion_string, i_chunk);
            char *cstring = (char *)clang_getCString(ac_string);
            if (isPrefix(prefix, cstring) > 0) {
              return -1;
            }
            fprintf(fp, "COMPLETION: %s", cstring);
            clang_disposeString(ac_string);
            return n_chunks;    /* care package on the way */
        }
    }

    return -1;   /* We haven't found TypedText chunk in completion_string */
}
Esempio n. 18
0
void ClangDriver::DoParseCompletionString(CXCompletionString str,
                                          int depth,
                                          wxString& entryName,
                                          wxString& signature,
                                          wxString& completeString,
                                          wxString& returnValue)
{

    bool collectingSignature = false;
    int numOfChunks = clang_getNumCompletionChunks(str);
    for(int j = 0; j < numOfChunks; j++) {

        CXString chunkText = clang_getCompletionChunkText(str, j);
        CXCompletionChunkKind chunkKind = clang_getCompletionChunkKind(str, j);

        switch(chunkKind) {
        case CXCompletionChunk_TypedText:
            entryName = wxString(clang_getCString(chunkText), wxConvUTF8);
            completeString += entryName;
            break;

        case CXCompletionChunk_ResultType:
            completeString += wxString(clang_getCString(chunkText), wxConvUTF8);
            completeString += wxT(" ");
            returnValue = wxString(clang_getCString(chunkText), wxConvUTF8);
            break;

        case CXCompletionChunk_Optional: {
            // Optional argument
            CXCompletionString optStr = clang_getCompletionChunkCompletionString(str, j);
            wxString optionalString;
            wxString dummy;
            // Once we hit the 'Optional Chunk' only the 'completeString' is matter
            DoParseCompletionString(optStr, depth + 1, dummy, dummy, optionalString, dummy);
            if(collectingSignature) {
                signature += optionalString;
            }
            completeString += optionalString;
        } break;
        case CXCompletionChunk_LeftParen:
            collectingSignature = true;
            signature += wxT("(");
            completeString += wxT("(");
            break;

        case CXCompletionChunk_RightParen:
            collectingSignature = true;
            signature += wxT(")");
            completeString += wxT(")");
            break;

        default:
            if(collectingSignature) {
                signature += wxString(clang_getCString(chunkText), wxConvUTF8);
            }
            completeString += wxString(clang_getCString(chunkText), wxConvUTF8);
            break;
        }
        clang_disposeString(chunkText);
    }

    // To make this tag compatible with ctags one, we need to place
    // a /^ and $/ in the pattern string (we add this only to the top level completionString)
    if(depth == 0) {
        completeString.Prepend(wxT("/^ "));
        completeString.Append(wxT(" $/"));
    }
}
Esempio n. 19
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);

    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);
    }
}
CodeCompletionChunk::Kind CodeCompletionChunkConverter::chunkKind(CXCompletionString completionString, uint chunkIndex)
{
    return CodeCompletionChunk::Kind(clang_getCompletionChunkKind(completionString, chunkIndex));
}
Esempio n. 21
0
CXCompletionChunkKind code_completion_string::chunkKind(unsigned idx) const
{
    return clang_getCompletionChunkKind(str, idx);
}
QList<ClangCodeCompletionItem> TranslationUnit::completeAt(
    const int line
  , const int column
  , const unsigned completion_flags
  , const clang::unsaved_files_list& unsaved_files
  , const PluginConfiguration::sanitize_rules_list_type& sanitize_rules
  )
{
    auto files = unsaved_files.get();
#ifndef NDEBUG
    for (auto& item : files)
        assert(
            "Sanity check"
          && item.Filename
          && std::strlen(item.Filename)
          && item.Contents
          && item.Length
          );
#endif

    clang::DCXCodeCompleteResults res = {
        clang_codeCompleteAt(
            m_unit
          , m_filename.constData()
          , unsigned(line)
          , unsigned(column)
          , files.data()
          , files.size()
          , completion_flags
          )
      };
    if (!res)
    {
        throw Exception::CompletionFailure(
            i18nc("@item:intext", "Unable to perform code completion").toAscii().constData()
          );
    }

#if 0
    clang_sortCodeCompletionResults(res->Results, res->NumResults);
#endif

    // Collect some diagnostic SPAM
    for (auto i = 0u; i < clang_codeCompleteGetNumDiagnostics(res); ++i)
    {
        clang::DCXDiagnostic diag = {clang_codeCompleteGetDiagnostic(res, i)};
        appendDiagnostic(diag);
    }

    QList<ClangCodeCompletionItem> completions;
    completions.reserve(res->NumResults);                   // Peallocate enough space for completion results

    // Lets look what we've got...
    for (auto i = 0u; i < res->NumResults; ++i)
    {
        const auto str = res->Results[i].CompletionString;
        const auto priority = clang_getCompletionPriority(str);
        const auto cursor_kind = res->Results[i].CursorKind;
        debugShowCompletionResult(i, priority, str, cursor_kind);

        // Skip unusable completions
        // 0) check availability
        const auto availability = clang_getCompletionAvailability(str);
        if (availability != CXAvailability_Available && availability != CXAvailability_Deprecated)
        {
            kDebug(DEBUG_AREA) << "!! Skip result" << i << "as not available";
            continue;
        }
        // 1) check usefulness
        /// \todo Make it configurable
        if (cursor_kind == CXCursor_NotImplemented)
            continue;

        // Collect all completion chunks and from a format string
        QString text_before;
        QString typed_text;
        QString text_after;
        QStringList placeholders;
        int optional_placeholers_start_position = -1;
        // A lambda to append given text to different parts
        // of future completion string, depending on already processed text
        auto appender = [&](const QString& text)
        {
            if (typed_text.isEmpty())
                text_before += text;
            else
                text_after += text;
        };
        auto skip_this_item = false;
        for (
            auto j = 0u
          , chunks = clang_getNumCompletionChunks(str)
          ; j < chunks && !skip_this_item
          ; ++j
          )
        {
            auto kind = clang_getCompletionChunkKind(str, j);
            auto text = toString(clang::DCXString{clang_getCompletionChunkText(str, j)});
            switch (kind)
            {
                // Text that a user would be expected to type to get this code-completion result
                case CXCompletionChunk_TypedText:
                // Text that should be inserted as part of a code-completion result
                case CXCompletionChunk_Text:
                {
                    auto p = sanitize(text, sanitize_rules);// Pipe given piece of text through sanitizer
                    if (p.first)
                        typed_text += p.second;
                    else                                    // Go for next completion item
                        skip_this_item = true;
                    break;
                }
                // Placeholder text that should be replaced by the user
                case CXCompletionChunk_Placeholder:
                {
                    auto p = sanitize(text, sanitize_rules);// Pipe given piece of text through sanitizer
                    if (p.first)
                    {
                        appender(
                            QLatin1String{"%"}
                          + QString::number(placeholders.size() + 1)
                          + QLatin1String{"%"}
                          );
                        placeholders.push_back(p.second);
                    }
                    else                                    // Go for next completion item
                        skip_this_item = true;
                    break;
                }
                // A code-completion string that describes "optional" text that
                // could be a part of the template (but is not required)
                case CXCompletionChunk_Optional:
                {
                    auto ostr = clang_getCompletionChunkCompletionString(str, j);
                    for (
                        auto oci = 0u
                      , ocn = clang_getNumCompletionChunks(ostr)
                      ; oci < ocn
                      ; ++oci
                      )
                    {
                        auto otext = toString(clang::DCXString{clang_getCompletionChunkText(ostr, oci)});
                        // Pipe given piece of text through sanitizer
                        auto p = sanitize(otext, sanitize_rules);
                        if (p.first)
                        {
                            auto okind = clang::kind_of(ostr, oci);
                            if (okind == CXCompletionChunk_Placeholder)
                            {
                                appender(
                                    QLatin1String{"%"}
                                  + QString::number(placeholders.size() + 1)
                                  + QLatin1String{"%"}
                                  );
                                placeholders.push_back(p.second);
                                optional_placeholers_start_position = placeholders.size();
                            }
                            else appender(p.second);
                        }
                        else
                        {
                            skip_this_item = true;
                            break;
                        }
                    }
                    break;
                }
                case CXCompletionChunk_ResultType:
                case CXCompletionChunk_LeftParen:
                case CXCompletionChunk_RightParen:
                case CXCompletionChunk_LeftBracket:
                case CXCompletionChunk_RightBracket:
                case CXCompletionChunk_LeftBrace:
                case CXCompletionChunk_RightBrace:
                case CXCompletionChunk_LeftAngle:
                case CXCompletionChunk_RightAngle:
                case CXCompletionChunk_Comma:
                case CXCompletionChunk_Colon:
                case CXCompletionChunk_SemiColon:
                case CXCompletionChunk_Equal:
                case CXCompletionChunk_CurrentParameter:
                case CXCompletionChunk_HorizontalSpace:
                /// \todo Kate can't handle \c '\n' well in completions list
                case CXCompletionChunk_VerticalSpace:
                {
                    auto p = sanitize(text, sanitize_rules);// Pipe given piece of text through sanitizer
                    if (p.first)
                        appender(p.second);
                    else                                    // Go for next completion item
                        skip_this_item = true;
                    break;
                }
                // Informative text that should be displayed but never inserted
                // as part of the template
                case CXCompletionChunk_Informative:
                    // Informative text before CXCompletionChunk_TypedText usually
                    // just a method scope (i.e. long name of an owner class)
                    // and it's useless for completer cuz it can group items
                    // by parent already...
                    if (!typed_text.isEmpty())
                    {
                        // Pipe given piece of text through sanitizer
                        auto p = sanitize(text, sanitize_rules);
                        if (p.first)
                            appender(p.second);
                        else                                // Go for next completion item
                            skip_this_item = true;
                    }
                    break;
                default:
                    break;
            }
        }
        // Does it pass the completion items sanitizer?
        if (skip_this_item) continue;                       // No! Skip it!

        assert("Priority expected to be less than 100" && priority < 101u);

        const auto comment = toString(clang::DCXString{clang_getCompletionBriefComment(str)});
        //
        completions.push_back({
            makeParentText(str, cursor_kind)
          , text_before
          , typed_text
          , text_after
          , placeholders
          , optional_placeholers_start_position
          , priority
          , cursor_kind
          , comment
          , availability == CXAvailability_Deprecated
          });
    }

    return completions;
}
    completion_list translation_unit::complete_at(uint32_t row, uint32_t col) {
        completion_list ret;
        CXCodeCompleteResults *res;

        if (mCxUnsaved) {
            res = clang_codeCompleteAt(mUnit, mName.c_str(), row, col, mCxUnsaved, 1, 0);
        }
        else
            res = clang_codeCompleteAt(mUnit, mName.c_str(), row, col, nullptr, 0, 0);

        for (uint32_t i = 0; i < res->NumResults; ++i) {
            // skip all private members
            if (clang_getCompletionAvailability(res->Results[i].CompletionString) == CXAvailability_NotAccessible)
                continue;

            // number of completion chunks for the current result
            completion_result r;
            uint32_t nChunks = clang_getNumCompletionChunks(res->Results[i].CompletionString);

            // function to handle a single chunk
            auto handle_chunk = [&](CXCompletionChunkKind k, uint32_t num) {
                CXString txt = clang_getCompletionChunkText(res->Results[i].CompletionString, num);
                switch (k) {
                    case CXCompletionChunk_ResultType:
                        r.return_type = cx2std(txt);
                        break;
                    case CXCompletionChunk_TypedText:
                        r.name = cx2std(txt);
                        break;
                    case CXCompletionChunk_Placeholder:
                        r.args.push_back(cx2std(txt));
                        break;
                    case CXCompletionChunk_Optional:
                    case CXCompletionChunk_LeftParen:
                    case CXCompletionChunk_RightParen:
                    case CXCompletionChunk_RightBracket:
                    case CXCompletionChunk_LeftBracket:
                    case CXCompletionChunk_LeftBrace:
                    case CXCompletionChunk_RightBrace:
                    case CXCompletionChunk_RightAngle:
                    case CXCompletionChunk_LeftAngle:
                    case CXCompletionChunk_Comma:
                    case CXCompletionChunk_Colon:
                    case CXCompletionChunk_SemiColon:
                    case CXCompletionChunk_Equal:
                    case CXCompletionChunk_Informative:
                    case CXCompletionChunk_HorizontalSpace:
                        break;
                    default:
                        break;
                }
            };

            for (uint32_t k = 0; k < nChunks; ++k) {
                handle_chunk(clang_getCompletionChunkKind(res->Results[i].CompletionString, k), k);
            }

            // fill additional info and append to result set
            r.brief = cx2std(clang_getCompletionBriefComment(res->Results[i].CompletionString));
            r.priority = clang_getCompletionPriority(res->Results[i].CompletionString);
            r.type = cursor2completion(res->Results[i].CursorKind);

            // @todo: once clang forwards the CXCursor of a completion result, we should get
            //        the full documentation for each entry

            ret.push_back(r);
        }

        clang_disposeCodeCompleteResults(res);
        return ret;
    }
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);
}
Esempio n. 25
0
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());
}
static void
ide_clang_completion_item_lazy_init (IdeClangCompletionItem *self)
{
  CXCompletionResult *result;
  g_autoptr(IdeSourceSnippet) snippet = NULL;
  GdkPixbuf *icon = NULL;
  GString *markup = NULL;
  unsigned num_chunks;
  unsigned i;
  guint tab_stop = 0;

  g_assert (IDE_IS_CLANG_COMPLETION_ITEM (self));

  if (G_LIKELY (self->initialized))
    return;

  result = ide_clang_completion_item_get_result (self);
  num_chunks = clang_getNumCompletionChunks (result);
  snippet = ide_source_snippet_new (NULL, NULL);
  markup = g_string_new (NULL);

  g_assert (result);
  g_assert (num_chunks);
  g_assert (IDE_IS_SOURCE_SNIPPET (snippet));
  g_assert (markup);

  /*
   * Try to determine the icon to use for this result.
   */
  switch ((int)result->CursorKind)
    {
    case CXCursor_CXXMethod:
    case CXCursor_Constructor:
    case CXCursor_Destructor:
    case CXCursor_MemberRef:
    case CXCursor_MemberRefExpr:
    case CXCursor_ObjCClassMethodDecl:
    case CXCursor_ObjCInstanceMethodDecl:
      icon = get_icon ("lang-method-symbolic");
      break;

    case CXCursor_ConversionFunction:
    case CXCursor_FunctionDecl:
    case CXCursor_FunctionTemplate:
      icon = get_icon ("lang-function-symbolic");
      break;

    case CXCursor_FieldDecl:
      icon = get_icon ("struct-field-symbolic");
      break;

    case CXCursor_VarDecl:
      /* local? */
    case CXCursor_ParmDecl:
    case CXCursor_ObjCIvarDecl:
    case CXCursor_ObjCPropertyDecl:
    case CXCursor_ObjCSynthesizeDecl:
    case CXCursor_NonTypeTemplateParameter:
    case CXCursor_Namespace:
    case CXCursor_NamespaceAlias:
    case CXCursor_NamespaceRef:
      break;

    case CXCursor_StructDecl:
      icon = get_icon ("lang-struct-symbolic");
      break;

    case CXCursor_UnionDecl:
    case CXCursor_ClassDecl:
    case CXCursor_TypeRef:
    case CXCursor_TemplateRef:
    case CXCursor_TypedefDecl:
    case CXCursor_ClassTemplate:
    case CXCursor_ClassTemplatePartialSpecialization:
    case CXCursor_ObjCClassRef:
    case CXCursor_ObjCInterfaceDecl:
    case CXCursor_ObjCImplementationDecl:
    case CXCursor_ObjCCategoryDecl:
    case CXCursor_ObjCCategoryImplDecl:
    case CXCursor_ObjCProtocolDecl:
    case CXCursor_ObjCProtocolRef:
    case CXCursor_TemplateTypeParameter:
    case CXCursor_TemplateTemplateParameter:
      icon = get_icon ("lang-class-symbolic");
      break;

    case CXCursor_EnumConstantDecl:
      icon = get_icon ("lang-enum-value-symbolic");
      break;

    case CXCursor_EnumDecl:
      icon = get_icon ("lang-enum-symbolic");
      break;

    case CXCursor_NotImplemented:
    default:
      break;
    }

  /*
   * Walk the chunks, creating our snippet for insertion as well as our markup
   * for the row in the completion window.
   */
  for (i = 0; i < num_chunks; i++)
    {
      enum CXCompletionChunkKind kind;
      IdeSourceSnippetChunk *chunk;
      const gchar *text;
      g_autofree gchar *escaped = NULL;
      CXString cxstr;

      kind = clang_getCompletionChunkKind (result->CompletionString, i);
      cxstr = clang_getCompletionChunkText (result->CompletionString, i);
      text = clang_getCString (cxstr);

      if (text)
        escaped = g_markup_escape_text (text, -1);
      else
        escaped = g_strdup ("");

      switch (kind)
        {
        case CXCompletionChunk_Optional:
          break;

        case CXCompletionChunk_TypedText:
          g_string_append_printf (markup, "<b>%s</b>", escaped);
          chunk = ide_source_snippet_chunk_new ();
          ide_source_snippet_chunk_set_text (chunk, text);
          ide_source_snippet_chunk_set_text_set (chunk, TRUE);
          ide_source_snippet_add_chunk (snippet, chunk);
          g_clear_object (&chunk);
          break;

        case CXCompletionChunk_Text:
          g_string_append (markup, escaped);
          chunk = ide_source_snippet_chunk_new ();
          ide_source_snippet_chunk_set_text (chunk, text);
          ide_source_snippet_chunk_set_text_set (chunk, TRUE);
          ide_source_snippet_add_chunk (snippet, chunk);
          g_clear_object (&chunk);
          break;

        case CXCompletionChunk_Placeholder:
          g_string_append (markup, escaped);
          chunk = ide_source_snippet_chunk_new ();
          ide_source_snippet_chunk_set_text (chunk, text);
          ide_source_snippet_chunk_set_text_set (chunk, TRUE);
          ide_source_snippet_chunk_set_tab_stop (chunk, ++tab_stop);
          ide_source_snippet_add_chunk (snippet, chunk);
          g_clear_object (&chunk);
          break;

        case CXCompletionChunk_Informative:
          if (0 == g_strcmp0 (text, "const "))
            g_string_append (markup, text);
          break;

        case CXCompletionChunk_CurrentParameter:
          break;

        case CXCompletionChunk_LeftParen:
          g_string_append (markup, " ");
          chunk = ide_source_snippet_chunk_new ();
          ide_source_snippet_chunk_set_text (chunk, " ");
          ide_source_snippet_chunk_set_text_set (chunk, TRUE);
          ide_source_snippet_add_chunk (snippet, chunk);
          g_clear_object (&chunk);
          /* fall through */
        case CXCompletionChunk_RightParen:
        case CXCompletionChunk_LeftBracket:
        case CXCompletionChunk_RightBracket:
        case CXCompletionChunk_LeftBrace:
        case CXCompletionChunk_RightBrace:
        case CXCompletionChunk_LeftAngle:
        case CXCompletionChunk_RightAngle:
        case CXCompletionChunk_Comma:
        case CXCompletionChunk_Colon:
        case CXCompletionChunk_SemiColon:
        case CXCompletionChunk_Equal:
        case CXCompletionChunk_HorizontalSpace:
          g_string_append (markup, escaped);
          chunk = ide_source_snippet_chunk_new ();
          ide_source_snippet_chunk_set_text (chunk, text);
          ide_source_snippet_chunk_set_text_set (chunk, TRUE);
          ide_source_snippet_add_chunk (snippet, chunk);
          g_clear_object (&chunk);
          break;

        case CXCompletionChunk_VerticalSpace:
          g_string_append (markup, escaped);
          /* insert the vertical space */
          chunk = ide_source_snippet_chunk_new ();
          ide_source_snippet_chunk_set_text (chunk, text);
          ide_source_snippet_chunk_set_text_set (chunk, TRUE);
          ide_source_snippet_add_chunk (snippet, chunk);
          g_clear_object (&chunk);
          /* now perform indentation */
          chunk = ide_source_snippet_chunk_new ();
          ide_source_snippet_chunk_set_text (chunk, "\t");
          ide_source_snippet_chunk_set_text_set (chunk, TRUE);
          ide_source_snippet_add_chunk (snippet, chunk);
          g_clear_object (&chunk);
          break;

        case CXCompletionChunk_ResultType:
          g_string_append_printf (markup, "%s ", escaped);
          break;

        default:
          break;
        }
    }

  self->snippet = g_object_ref (snippet);
  self->markup = g_string_free (markup, FALSE);
  self->icon = icon ? g_object_ref (icon) : NULL;
}
/**
 * ide_clang_completion_item_get_typed_text:
 * @self: An #IdeClangCompletionItem.
 *
 * Gets the text that would be expected to be typed to insert this completion
 * item into the text editor.
 *
 * Returns: A string which should not be modified or freed.
 */
const gchar *
ide_clang_completion_item_get_typed_text (IdeClangCompletionItem *self)
{
  CXCompletionResult *result;
  CXString cxstr;

  g_return_val_if_fail (IDE_IS_CLANG_COMPLETION_ITEM (self), NULL);

  if (self->typed_text)
    return self->typed_text;

  result = ide_clang_completion_item_get_result (self);

  /*
   * Determine the index of the typed text. Each completion result should have
   * exaction one of these.
   */
  if (G_UNLIKELY (self->typed_text_index == -1))
    {
      guint num_chunks;
      guint i;

      num_chunks = clang_getNumCompletionChunks (result);

      for (i = 0; i < num_chunks; i++)
        {
          enum CXCompletionChunkKind kind;

          kind = clang_getCompletionChunkKind (result->CompletionString, i);
          if (kind == CXCompletionChunk_TypedText)
            {
              self->typed_text_index = i;
              break;
            }
        }
    }

  if (self->typed_text_index == -1)
    {
      /*
       * FIXME:
       *
       * This seems like an implausible result, but we are definitely
       * hitting it occasionally.
       */
      return g_strdup ("");
    }

#ifdef IDE_ENABLE_TRACE
  {
    enum CXCompletionChunkKind kind;
    unsigned num_chunks;

    g_assert (self->typed_text_index >= 0);

    num_chunks = clang_getNumCompletionChunks (result->CompletionString);
    g_assert (num_chunks > self->typed_text_index);

    kind = clang_getCompletionChunkKind (result->CompletionString, self->typed_text_index);
    g_assert (kind == CXCompletionChunk_TypedText);
  }
#endif

  cxstr = clang_getCompletionChunkText (result->CompletionString, self->typed_text_index);
  self->typed_text = g_strdup (clang_getCString (cxstr));
  clang_disposeString (cxstr);

  return self->typed_text;
}