From 44b671c2c0c218319ac0812c68427e9962a25287 Mon Sep 17 00:00:00 2001 From: Strahinja Val Markovic Date: Thu, 19 Jul 2012 21:17:39 -0700 Subject: [PATCH] ClangCompleter now returs extra data This data is used to show more information about the completions in the completion menu. --- autoload/youcompleteme.vim | 2 +- cpp/ycm/CandidateRepository.cpp | 28 ++++- cpp/ycm/CandidateRepository.h | 4 + cpp/ycm/ClangCompleter.cpp | 197 ++++++++++++++++++++++++-------- cpp/ycm/ClangCompleter.h | 15 ++- cpp/ycm/CompletionData.h | 69 +++++++++++ cpp/ycm/Future.h | 7 -- cpp/ycm/IdentifierCompleter.cpp | 2 +- cpp/ycm/IdentifierCompleter.h | 7 ++ cpp/ycm/Result.cpp | 15 +++ cpp/ycm/Result.h | 1 + cpp/ycm/indexer.cpp | 18 +++ python/ycm.py | 15 +++ 13 files changed, 317 insertions(+), 63 deletions(-) create mode 100644 cpp/ycm/CompletionData.h diff --git a/autoload/youcompleteme.vim b/autoload/youcompleteme.vim index c9f51dfb..2a989925 100644 --- a/autoload/youcompleteme.vim +++ b/autoload/youcompleteme.vim @@ -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 ) diff --git a/cpp/ycm/CandidateRepository.cpp b/cpp/ycm/CandidateRepository.cpp index f22a1208..b1689a9b 100644 --- a/cpp/ycm/CandidateRepository.cpp +++ b/cpp/ycm/CandidateRepository.cpp @@ -17,6 +17,7 @@ #include "CandidateRepository.h" #include "Candidate.h" +#include "CompletionData.h" #include "standard.h" #include "Utils.h" @@ -43,7 +44,7 @@ CandidateRepository& CandidateRepository::Instance() std::vector< const Candidate* > CandidateRepository::GetCandidatesForStrings( - const std::vector< std::string > &strings) + const std::vector< std::string > &strings ) { std::vector< const Candidate* > candidates; candidates.reserve( strings.size() ); @@ -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, diff --git a/cpp/ycm/CandidateRepository.h b/cpp/ycm/CandidateRepository.h index d643c0d3..8b108b59 100644 --- a/cpp/ycm/CandidateRepository.h +++ b/cpp/ycm/CandidateRepository.h @@ -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(); diff --git a/cpp/ycm/ClangCompleter.cpp b/cpp/ycm/ClangCompleter.cpp index f5c1bd03..b7e11d3a 100644 --- a/cpp/ycm/ClangCompleter.cpp +++ b/cpp/ycm/ClangCompleter.cpp @@ -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,31 +102,110 @@ bool CompletionStringAvailable( CXCompletionString completion_string ) } -std::vector< std::string > ToStringVector( CXCodeCompleteResults *results ) +bool IsChunkKindForExtraMenuInfo( CXCompletionChunkKind kind ) { - std::vector< std::string > completions; + 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; + +} + + +char CursorKindToVimKind( CXCursorKind kind ) +{ + switch ( kind ) + { + case CXCursor_UnexposedDecl: + case CXCursor_StructDecl: + case CXCursor_UnionDecl: + case CXCursor_ClassDecl: + case CXCursor_EnumDecl: + case CXCursor_TypedefDecl: + return 't'; + + 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 ) + { + CXCompletionChunkKind kind = clang_getCompletionChunkKind( + completion_string, j ); + + if ( IsChunkKindForExtraMenuInfo( kind ) ) + { + 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 ) { - CXCompletionString completion_string = - results->Results[ i ].CompletionString; + CXCompletionResult completion_result = results->Results[ i ]; - if ( !CompletionStringAvailable( completion_string ) ) + if ( !CompletionStringAvailable( completion_result.CompletionString ) ) continue; - uint num_chunks = clang_getNumCompletionChunks( completion_string ); - for ( uint j = 0; j < num_chunks; ++j ) - { - CXCompletionChunkKind kind = clang_getCompletionChunkKind( - completion_string, j ); - - if ( kind == CXCompletionChunk_TypedText ) - { - completions.push_back( ChunkToString( completion_string, j ) ); - break; - } - } + 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 ); + { + CompletionDataAndResult data_and_result( &completion_datas[ i ], result ); + data_and_results.push_back( data_and_result ); + } } - std::sort( results.begin(), results.end() ); + std::sort( data_and_results.begin(), data_and_results.end() ); - std::vector< std::string > sorted_candidates; - sorted_candidates.reserve( results.size() ); + std::vector< CompletionData > sorted_completion_datas; + sorted_completion_datas.reserve( data_and_results.size() ); - foreach ( const Result& result, results ) + foreach ( const CompletionDataAndResult& data_and_result, data_and_results ) { - sorted_candidates.push_back( *result.Text() ); + sorted_completion_datas.push_back( *data_and_result.completion_data_ ); } - return sorted_candidates; + 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( diff --git a/cpp/ycm/ClangCompleter.h b/cpp/ycm/ClangCompleter.h index 674065bb..5539ca2e 100644 --- a/cpp/ycm/ClangCompleter.h +++ b/cpp/ycm/ClangCompleter.h @@ -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 diff --git a/cpp/ycm/CompletionData.h b/cpp/ycm/CompletionData.h new file mode 100644 index 00000000..8f862a9a --- /dev/null +++ b/cpp/ycm/CompletionData.h @@ -0,0 +1,69 @@ +// Copyright (C) 2011, 2012 Strahinja Val Markovic +// +// 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 . + +#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 */ diff --git a/cpp/ycm/Future.h b/cpp/ycm/Future.h index 8c292f76..94020546 100644 --- a/cpp/ycm/Future.h +++ b/cpp/ycm/Future.h @@ -20,7 +20,6 @@ #include #include -#include #include #include @@ -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 ) diff --git a/cpp/ycm/IdentifierCompleter.cpp b/cpp/ycm/IdentifierCompleter.cpp index 19f2f9aa..7939cfa4 100644 --- a/cpp/ycm/IdentifierCompleter.cpp +++ b/cpp/ycm/IdentifierCompleter.cpp @@ -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 ) ); } diff --git a/cpp/ycm/IdentifierCompleter.h b/cpp/ycm/IdentifierCompleter.h index ee920948..5139cfe8 100644 --- a/cpp/ycm/IdentifierCompleter.h +++ b/cpp/ycm/IdentifierCompleter.h @@ -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, diff --git a/cpp/ycm/Result.cpp b/cpp/ycm/Result.cpp index 888acee6..576ae60e 100644 --- a/cpp/ycm/Result.cpp +++ b/cpp/ycm/Result.cpp @@ -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, diff --git a/cpp/ycm/Result.h b/cpp/ycm/Result.h index b64e90c0..1f8ce1b2 100644 --- a/cpp/ycm/Result.h +++ b/cpp/ycm/Result.h @@ -26,6 +26,7 @@ namespace YouCompleteMe class Result { public: + Result(); explicit Result( bool is_subsequence ); Result( bool is_subsequence, diff --git a/cpp/ycm/indexer.cpp b/cpp/ycm/indexer.cpp index c12fca09..00678d8c 100644 --- a/cpp/ycm/indexer.cpp +++ b/cpp/ycm/indexer.cpp @@ -18,6 +18,7 @@ #include "IdentifierCompleter.h" #include "ClangCompleter.h" #include "Future.h" +#include "CompletionData.h" #include #include @@ -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", diff --git a/python/ycm.py b/python/ycm.py index c1eff7a5..159c741d 100644 --- a/python/ycm.py +++ b/python/ycm.py @@ -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."""