ClangCompleter now returs extra data

This data is used to show more information about the completions in the
completion menu.
This commit is contained in:
Strahinja Val Markovic 2012-07-19 21:17:39 -07:00
parent e56080ea56
commit 44b671c2c0
13 changed files with 317 additions and 63 deletions

View File

@ -217,7 +217,7 @@ EOF
let l:results = []
py << EOF
results = clangcomp.CandidatesFromStoredRequest()
result_string = ycm.StringVectorToString( results )
result_string = ycm.CompletionDataVectorToString( results )
if results:
vim.command( 'let l:results = ' + result_string )

View File

@ -17,6 +17,7 @@
#include "CandidateRepository.h"
#include "Candidate.h"
#include "CompletionData.h"
#include "standard.h"
#include "Utils.h"
@ -67,6 +68,31 @@ std::vector< const Candidate* > CandidateRepository::GetCandidatesForStrings(
}
std::vector< const Candidate* > CandidateRepository::GetCandidatesForStrings(
const std::vector< CompletionData > &datas )
{
std::vector< const Candidate* > candidates;
candidates.reserve( datas.size() );
{
boost::lock_guard< boost::mutex > locker( holder_mutex_ );
foreach ( const CompletionData &data, datas )
{
const Candidate *&candidate = GetValueElseInsert( candidate_holder_,
data.original_string_,
NULL );
if ( !candidate )
candidate = new Candidate( data.original_string_ );
candidates.push_back( candidate );
}
}
return candidates;
}
CandidateRepository::~CandidateRepository()
{
foreach ( const CandidateHolder::value_type &pair,

View File

@ -29,6 +29,7 @@ namespace YouCompleteMe
{
class Candidate;
struct CompletionData;
typedef boost::unordered_map< std::string, const Candidate* >
CandidateHolder;
@ -41,6 +42,9 @@ public:
std::vector< const Candidate* > GetCandidatesForStrings(
const std::vector< std::string > &strings );
std::vector< const Candidate* > GetCandidatesForStrings(
const std::vector< CompletionData > &datas );
private:
CandidateRepository() {};
~CandidateRepository();

View File

@ -17,6 +17,7 @@
#include "ClangCompleter.h"
#include "Candidate.h"
#include "CompletionData.h"
#include "standard.h"
#include "CandidateRepository.h"
#include "ConcurrentLatestValue.h"
@ -35,12 +36,30 @@ using boost::thread;
namespace YouCompleteMe
{
typedef boost::function< std::vector< CompletionData >() >
FunctionReturnsCompletionDataVector;
extern const unsigned int MAX_ASYNC_THREADS;
extern const unsigned int MIN_ASYNC_THREADS;
namespace
{
struct CompletionDataAndResult
{
CompletionDataAndResult( const CompletionData *completion_data,
const Result &result )
: completion_data_( completion_data ), result_( result ) {}
bool operator< ( const CompletionDataAndResult &other ) const
{
return result_ < other.result_;
}
const CompletionData *completion_data_;
Result result_;
};
std::vector< CXUnsavedFile > ToCXUnsavedFiles(
const std::vector< UnsavedFile > &unsaved_files )
@ -83,18 +102,67 @@ bool CompletionStringAvailable( CXCompletionString completion_string )
}
std::vector< std::string > ToStringVector( CXCodeCompleteResults *results )
bool IsChunkKindForExtraMenuInfo( CXCompletionChunkKind kind )
{
std::vector< std::string > completions;
completions.reserve( results->NumResults );
return
kind == CXCompletionChunk_Optional ||
kind == CXCompletionChunk_TypedText ||
kind == CXCompletionChunk_Placeholder ||
kind == CXCompletionChunk_LeftParen ||
kind == CXCompletionChunk_RightParen ||
kind == CXCompletionChunk_RightBracket ||
kind == CXCompletionChunk_LeftBracket ||
kind == CXCompletionChunk_LeftBrace ||
kind == CXCompletionChunk_RightBrace ||
kind == CXCompletionChunk_RightAngle ||
kind == CXCompletionChunk_LeftAngle ||
kind == CXCompletionChunk_Comma ||
kind == CXCompletionChunk_ResultType ||
kind == CXCompletionChunk_Colon ||
kind == CXCompletionChunk_SemiColon ||
kind == CXCompletionChunk_Equal ||
kind == CXCompletionChunk_HorizontalSpace;
for ( uint i = 0; i < results->NumResults; ++i )
}
char CursorKindToVimKind( CXCursorKind kind )
{
CXCompletionString completion_string =
results->Results[ i ].CompletionString;
switch ( kind )
{
case CXCursor_UnexposedDecl:
case CXCursor_StructDecl:
case CXCursor_UnionDecl:
case CXCursor_ClassDecl:
case CXCursor_EnumDecl:
case CXCursor_TypedefDecl:
return 't';
if ( !CompletionStringAvailable( completion_string ) )
continue;
case CXCursor_FieldDecl:
return 'm';
case CXCursor_FunctionDecl:
case CXCursor_CXXMethod:
case CXCursor_FunctionTemplate:
return 'f';
case CXCursor_VarDecl:
return 'v';
case CXCursor_MacroDefinition:
return 'd';
default:
return 'u'; // for 'unknown', 'unsupported'... whatever you like
}
}
CompletionData CompletionResultToCompletionData(
const CXCompletionResult &completion_result )
{
CompletionData data;
CXCompletionString completion_string = completion_result.CompletionString;
uint num_chunks = clang_getNumCompletionChunks( completion_string );
for ( uint j = 0; j < num_chunks; ++j )
@ -102,12 +170,42 @@ std::vector< std::string > ToStringVector( CXCodeCompleteResults *results )
CXCompletionChunkKind kind = clang_getCompletionChunkKind(
completion_string, j );
if ( kind == CXCompletionChunk_TypedText )
if ( IsChunkKindForExtraMenuInfo( kind ) )
{
completions.push_back( ChunkToString( completion_string, j ) );
break;
data.extra_menu_info_.append( ChunkToString( completion_string, j ) );
// by default, there's no space after the return type
if ( kind == CXCompletionChunk_ResultType )
data.extra_menu_info_.append( " " );
}
if ( kind == CXCompletionChunk_TypedText )
data.original_string_ = ChunkToString( completion_string, j );
if ( kind == CXCompletionChunk_Informative )
data.detailed_info_ = ChunkToString( completion_string, j );
}
data.kind_ = CursorKindToVimKind( completion_result.CursorKind );
return data;
}
std::vector< CompletionData > ToCompletionDataVector(
CXCodeCompleteResults *results )
{
std::vector< CompletionData > completions;
completions.reserve( results->NumResults );
for ( uint i = 0; i < results->NumResults; ++i )
{
CXCompletionResult completion_result = results->Results[ i ];
if ( !CompletionStringAvailable( completion_result.CompletionString ) )
continue;
completions.push_back(
CompletionResultToCompletionData( completion_result ) );
}
return completions;
@ -189,7 +287,7 @@ void ClangCompleter::UpdateTranslationUnit(
}
std::vector< std::string > ClangCompleter::CandidatesForLocationInFile(
std::vector< CompletionData > ClangCompleter::CandidatesForLocationInFile(
const std::string &filename,
int line,
int column,
@ -217,13 +315,14 @@ std::vector< std::string > ClangCompleter::CandidatesForLocationInFile(
cxunsaved_files.size(),
clang_defaultCodeCompleteOptions());
std::vector< std::string > candidates = ToStringVector( results );
std::vector< CompletionData > candidates = ToCompletionDataVector( results );
clang_disposeCodeCompleteResults( results );
return candidates;
}
Future< AsyncResults > ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
Future< AsyncCompletions >
ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
const std::string &query,
const std::string &filename,
int line,
@ -232,7 +331,7 @@ Future< AsyncResults > ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
{
// TODO: throw exception when threading is not enabled and this is called
if ( !threading_enabled_ )
return Future< AsyncResults >();
return Future< AsyncCompletions >();
if ( query.empty() )
{
@ -251,23 +350,24 @@ Future< AsyncResults > ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
// case the clang task finishes (and therefore notifies a sorting thread to
// consume a sorting task) before the sorting task is set
FunctionReturnsStringVector sort_candidates_for_query_functor =
FunctionReturnsCompletionDataVector sort_candidates_for_query_functor =
bind( &ClangCompleter::SortCandidatesForQuery,
boost::ref( *this ),
query,
boost::cref( latest_clang_results_ ) );
shared_ptr< packaged_task< AsyncResults > > task =
make_shared< packaged_task< AsyncResults > >(
bind( ReturnValueAsShared< std::vector< std::string > >,
shared_ptr< packaged_task< AsyncCompletions > > task =
make_shared< packaged_task< AsyncCompletions > >(
bind( ReturnValueAsShared< std::vector< CompletionData > >,
sort_candidates_for_query_functor ) );
unique_future< AsyncResults > future = task->get_future();
unique_future< AsyncCompletions > future = task->get_future();
sorting_task_.Set( task );
if ( query.empty() )
{
FunctionReturnsStringVector candidates_for_location_in_file_functor =
FunctionReturnsCompletionDataVector
candidates_for_location_in_file_functor =
bind( &ClangCompleter::CandidatesForLocationInFile,
boost::ref( *this ),
filename,
@ -275,15 +375,15 @@ Future< AsyncResults > ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
column,
unsaved_files );
shared_ptr< packaged_task< AsyncResults > > task =
make_shared< packaged_task< AsyncResults > >(
bind( ReturnValueAsShared< std::vector< std::string > >,
shared_ptr< packaged_task< AsyncCompletions > > task =
make_shared< packaged_task< AsyncCompletions > >(
bind( ReturnValueAsShared< std::vector< CompletionData > >,
candidates_for_location_in_file_functor ) );
clang_task_.Set( task );
}
return Future< AsyncResults >( move( future ) );
return Future< AsyncCompletions >( boost::move( future ) );
}
@ -344,39 +444,42 @@ CXTranslationUnit ClangCompleter::GetTranslationUnitForFile(
}
std::vector< std::string > ClangCompleter::SortCandidatesForQuery(
std::vector< CompletionData > ClangCompleter::SortCandidatesForQuery(
const std::string &query,
const std::vector< std::string > &candidates )
const std::vector< CompletionData > &completion_datas )
{
Bitset query_bitset = LetterBitsetFromString( query );
std::vector< const Candidate* > repository_candidates =
candidate_repository_.GetCandidatesForStrings( candidates );
candidate_repository_.GetCandidatesForStrings( completion_datas );
std::vector< Result > results;
std::vector< CompletionDataAndResult > data_and_results;
// This loop needs to be a separate function
foreach ( const Candidate* candidate, repository_candidates )
for ( uint i = 0; i < repository_candidates.size(); ++i )
{
const Candidate* candidate = repository_candidates[ i ];
if ( !candidate->MatchesQueryBitset( query_bitset ) )
continue;
Result result = candidate->QueryMatchResult( query );
if ( result.IsSubsequence() )
results.push_back( result );
}
std::sort( results.begin(), results.end() );
std::vector< std::string > sorted_candidates;
sorted_candidates.reserve( results.size() );
foreach ( const Result& result, results )
{
sorted_candidates.push_back( *result.Text() );
CompletionDataAndResult data_and_result( &completion_datas[ i ], result );
data_and_results.push_back( data_and_result );
}
}
return sorted_candidates;
std::sort( data_and_results.begin(), data_and_results.end() );
std::vector< CompletionData > sorted_completion_datas;
sorted_completion_datas.reserve( data_and_results.size() );
foreach ( const CompletionDataAndResult& data_and_result, data_and_results )
{
sorted_completion_datas.push_back( *data_and_result.completion_data_ );
}
return sorted_completion_datas;
}
@ -404,9 +507,9 @@ void ClangCompleter::ClangThreadMain( LatestTask &clang_task )
{
while ( true )
{
shared_ptr< packaged_task< AsyncResults > > task = clang_task.Get();
shared_ptr< packaged_task< AsyncCompletions > > task = clang_task.Get();
( *task )();
unique_future< AsyncResults > future = task->get_future();
unique_future< AsyncCompletions > future = task->get_future();
{
boost::unique_lock< boost::shared_mutex > writer_lock(
@ -439,7 +542,7 @@ void ClangCompleter::SortingThreadMain( LatestTask &sorting_task )
}
}
shared_ptr< packaged_task< AsyncResults > > task = sorting_task.Get();
shared_ptr< packaged_task< AsyncCompletions > > task = sorting_task.Get();
{
boost::shared_lock< boost::shared_mutex > reader_lock(

View File

@ -33,7 +33,9 @@ namespace YouCompleteMe
{
class CandidateRepository;
struct CompletionData;
// TODO: put this in a separated header file
struct UnsavedFile
{
UnsavedFile() : filename_( NULL ), contents_( NULL ), length_( 0 ) {}
@ -55,6 +57,7 @@ struct UnsavedFile
}
};
typedef boost::shared_ptr< std::vector< CompletionData > > AsyncCompletions;
typedef boost::unordered_map< std::string, std::vector< std::string > >
FlagsForFile;
@ -79,13 +82,13 @@ public:
void UpdateTranslationUnit( const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files );
std::vector< std::string > CandidatesForLocationInFile(
std::vector< CompletionData > CandidatesForLocationInFile(
const std::string &filename,
int line,
int column,
const std::vector< UnsavedFile > &unsaved_files );
Future< AsyncResults > CandidatesForQueryAndLocationInFileAsync(
Future< AsyncCompletions > CandidatesForQueryAndLocationInFileAsync(
const std::string &query,
const std::string &filename,
int line,
@ -95,7 +98,7 @@ public:
private:
typedef ConcurrentLatestValue<
boost::shared_ptr<
boost::packaged_task< AsyncResults > > > LatestTask;
boost::packaged_task< AsyncCompletions > > > LatestTask;
// caller takes ownership of translation unit
CXTranslationUnit CreateTranslationUnit(
@ -109,9 +112,9 @@ private:
const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files );
std::vector< std::string > SortCandidatesForQuery(
std::vector< CompletionData > SortCandidatesForQuery(
const std::string &query,
const std::vector< std::string > &candidates );
const std::vector< CompletionData > &completion_datas );
void InitThreads();
@ -145,7 +148,7 @@ private:
boost::mutex clang_data_ready_mutex_;
boost::condition_variable clang_data_ready_condition_variable_;
std::vector< std::string > latest_clang_results_;
std::vector< CompletionData > latest_clang_results_;
boost::shared_mutex latest_clang_results_shared_mutex_;
// Unfortunately clang is not thread-safe so we can only ever use one thread

69
cpp/ycm/CompletionData.h Normal file
View File

@ -0,0 +1,69 @@
// Copyright (C) 2011, 2012 Strahinja Val Markovic <val@markovic.io>
//
// This file is part of YouCompleteMe.
//
// YouCompleteMe is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// YouCompleteMe is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
#ifndef COMPLETIONDATA_H_2JCTF1NU
#define COMPLETIONDATA_H_2JCTF1NU
namespace YouCompleteMe
{
struct CompletionData
{
// What should actually be inserted into the buffer. For functions, this
// should be the original string plus "("
std::string TextToInsertInBuffer()
{
if ( kind_ == 'f' )
return original_string_ + "(";
return original_string_;
}
bool operator== ( const CompletionData &other ) const
{
return
kind_ == other.kind_ &&
original_string_ == other.original_string_ &&
extra_menu_info_ == other.extra_menu_info_;
// detailed_info_ doesn't matter
}
// This is used to show extra information in vim's preview window
std::string detailed_info_;
// This is extra info shown in the pop-up completion menu, after the
// completion text and the kind
std::string extra_menu_info_;
// Vim's completion string "kind"
// 'v' -> variable
// 'f' -> function or method
// 'm' -> member of struct/class (data member)
// 't' -> typedef (but we're going to use it for types in general)
// 'd' -> #define or macro
char kind_;
// The original, raw completion string. For a function like "int foo(int x)",
// the original string is "foo". This corresponds to clang's TypedText chunk
// of the completion string.
std::string original_string_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: COMPLETIONDATA_H_2JCTF1NU */

View File

@ -20,7 +20,6 @@
#include <boost/thread.hpp>
#include <boost/make_shared.hpp>
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
@ -30,12 +29,6 @@ namespace YouCompleteMe
class Result;
template< typename T > class ConcurrentLatestValue;
typedef boost::python::list Pylist;
typedef boost::shared_ptr< std::vector< std::string > > AsyncResults;
typedef boost::function< std::vector< std::string >() >
FunctionReturnsStringVector;
template< typename T >
boost::shared_ptr< T > ReturnValueAsShared(
boost::function< T() > func )

View File

@ -158,7 +158,7 @@ Future< AsyncResults > IdentifierCompleter::CandidatesForQueryAndTypeAsync(
unique_future< AsyncResults > future = task->get_future();
latest_task_.Set( task );
return Future< AsyncResults >( move( future ) );
return Future< AsyncResults >( boost::move( future ) );
}

View File

@ -35,6 +35,13 @@ namespace YouCompleteMe
class Candidate;
class CandidateRepository;
typedef boost::shared_ptr< std::vector< std::string > > AsyncResults;
// TODO: cpp?
typedef boost::function< std::vector< std::string >() >
FunctionReturnsStringVector;
// TODO: move to private
// filepath -> *( *candidate )
typedef boost::unordered_map< std::string,

View File

@ -69,6 +69,20 @@ int NumWordBoundaryCharMatches( const std::string &query,
} // unnamed namespace
Result::Result()
:
query_is_empty_( true ),
is_subsequence_( false ),
first_char_same_in_query_and_text_( false ),
ratio_of_word_boundary_chars_in_query_( 0 ),
word_boundary_char_utilization_( 0 ),
query_is_candidate_prefix_( false ),
text_is_lowercase_( false ),
char_match_index_sum_( 0 ),
text_( NULL )
{
}
Result::Result( bool is_subsequence )
:
@ -84,6 +98,7 @@ Result::Result( bool is_subsequence )
{
}
Result::Result( bool is_subsequence,
const std::string *text,
bool text_is_lowercase,

View File

@ -26,6 +26,7 @@ namespace YouCompleteMe
class Result
{
public:
Result();
explicit Result( bool is_subsequence );
Result( bool is_subsequence,

View File

@ -18,6 +18,7 @@
#include "IdentifierCompleter.h"
#include "ClangCompleter.h"
#include "Future.h"
#include "CompletionData.h"
#include <boost/python.hpp>
#include <boost/utility.hpp>
@ -28,14 +29,31 @@ BOOST_PYTHON_MODULE(indexer)
using namespace boost::python;
using namespace YouCompleteMe;
// TODO: rename these *Vec classes to *Vector; don't forget the python file
class_< std::vector< std::string >,
boost::shared_ptr< std::vector< std::string > > >( "StringVec" )
.def( vector_indexing_suite< std::vector< std::string > >() );
class_< CompletionData >( "CompletionData" )
.def( "TextToInsertInBuffer", &CompletionData::TextToInsertInBuffer )
.def_readonly( "detailed_info_", &CompletionData::detailed_info_ )
.def_readonly( "extra_menu_info_", &CompletionData::extra_menu_info_ )
.def_readonly( "kind_", &CompletionData::kind_ )
.def_readonly( "original_string_", &CompletionData::original_string_ );
class_< std::vector< CompletionData >,
boost::shared_ptr< std::vector< CompletionData > > >(
"CompletionVec" )
.def( vector_indexing_suite< std::vector< CompletionData > >() );
class_< Future< AsyncResults > >( "Future" )
.def( "ResultsReady", &Future< AsyncResults >::ResultsReady )
.def( "GetResults", &Future< AsyncResults >::GetResults );
class_< Future< AsyncCompletions > >( "Future" )
.def( "ResultsReady", &Future< AsyncCompletions >::ResultsReady )
.def( "GetResults", &Future< AsyncCompletions >::GetResults );
class_< IdentifierCompleter, boost::noncopyable >( "IdentifierCompleter" )
.def( "EnableThreading", &IdentifierCompleter::EnableThreading )
.def( "AddCandidatesToDatabase",

View File

@ -151,6 +151,21 @@ def StringVectorToString( stringvec ):
return ''.join( result )
def CompletionDataToDict( completion_data ):
# see :h complete-items for a description of the dictionary fields
return {
'word' : completion_data.TextToInsertInBuffer(),
'abbr' : completion_data.original_string_,
'menu' : completion_data.extra_menu_info_,
'kind' : completion_data.kind_,
# TODO: add detailed_info_ as 'info'
}
def CompletionDataVectorToString( datavec ):
return str( [ CompletionDataToDict( x ) for x in datavec ] )
def CurrentColumn():
"""Do NOT access the CurrentColumn in vim.current.line. It doesn't exist yet.
Only the chars before the current column exist in vim.current.line."""