// 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 . #include "IdentifierCompleter.h" #include "standard.h" #include "CandidateRepository.h" #include "Candidate.h" #include "Utils.h" #include #include #include using boost::python::len; using boost::python::extract; using boost::packaged_task; using boost::bind; using boost::unique_future; using boost::make_shared; using boost::shared_ptr; using boost::bind; using boost::thread; namespace YouCompleteMe { namespace { const unsigned int MAX_ASYNC_THREADS = 4; const unsigned int MIN_ASYNC_THREADS = 2; void ThreadMain( LatestTask &latest_task ) { while ( true ) { ( *latest_task.Get() )(); } } } // unnamed namespace IdentifierCompleter::IdentifierCompleter() : candidate_repository_( CandidateRepository::Instance() ), threading_enabled_( false ) { } IdentifierCompleter::IdentifierCompleter( const std::vector< std::string > &candidates ) : candidate_repository_( CandidateRepository::Instance() ), threading_enabled_( false ) { AddCandidatesToDatabase( candidates, "", "", true ); } IdentifierCompleter::IdentifierCompleter( const std::vector< std::string > &candidates, const std::string &filetype, const std::string &filepath ) : candidate_repository_( CandidateRepository::Instance() ), threading_enabled_( false ) { AddCandidatesToDatabase( candidates, filetype, filepath, true ); } // We need this mostly so that we can not use it in tests. Apparently the // GoogleTest framework goes apeshit on us if we enable threads by default. void IdentifierCompleter::EnableThreading() { threading_enabled_ = true; InitThreads(); } void IdentifierCompleter::AddCandidatesToDatabase( const std::vector< std::string > &new_candidates, const std::string &filetype, const std::string &filepath, bool clear_database ) { std::list< const Candidate *> &candidates = GetCandidateList( filetype, filepath ); if ( clear_database ) candidates.clear(); std::vector< const Candidate* > repository_candidates = candidate_repository_.GetCandidatesForStrings( new_candidates ); candidates.insert( candidates.end(), repository_candidates.begin(), repository_candidates.end() ); } std::vector< std::string > IdentifierCompleter::CandidatesForQuery( const std::string &query ) const { return CandidatesForQueryAndType( query, "" ); } std::vector< std::string > IdentifierCompleter::CandidatesForQueryAndType( const std::string &query, const std::string &filetype ) const { std::vector< Result > results; ResultsForQueryAndType( query, filetype, results ); std::vector< std::string > candidates; candidates.reserve( results.size() ); foreach ( const Result& result, results ) { candidates.push_back( *result.Text() ); } return candidates; } Future IdentifierCompleter::CandidatesForQueryAndTypeAsync( const std::string &query, const std::string &filetype ) const { // TODO: throw exception when threading is not enabled and this is called if (!threading_enabled_) return Future(); // Try not to look at this too hard, it may burn your eyes. shared_ptr< packaged_task< AsyncResults > > task = make_shared< packaged_task< AsyncResults > >( bind( &IdentifierCompleter::ResultsForQueryAndType, boost::cref( *this ), query, filetype ) ); unique_future< AsyncResults > future = task->get_future(); latest_task_.Set( task ); return Future( move( future ) ); } AsyncResults IdentifierCompleter::ResultsForQueryAndType( const std::string &query, const std::string &filetype ) const { AsyncResults results = boost::make_shared< std::vector< Result > >(); ResultsForQueryAndType( query, filetype, *results ); return results; } void IdentifierCompleter::ResultsForQueryAndType( const std::string &query, const std::string &filetype, std::vector< Result > &results ) const { FiletypeMap::const_iterator it = filetype_map_.find( filetype ); if ( it == filetype_map_.end() ) return; Bitset query_bitset = LetterBitsetFromString( query ); foreach ( const FilepathToCandidates::value_type &path_and_candidates, *it->second ) { foreach ( const Candidate* candidate, *path_and_candidates.second ) { 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::list< const Candidate* >& IdentifierCompleter::GetCandidateList( const std::string &filetype, const std::string &filepath ) { boost::shared_ptr< FilepathToCandidates > &path_to_candidates = filetype_map_[ filetype ]; if ( !path_to_candidates ) path_to_candidates.reset( new FilepathToCandidates() ); boost::shared_ptr< std::list< const Candidate* > > &candidates = (*path_to_candidates)[ filepath ]; if ( !candidates ) candidates.reset( new std::list< const Candidate* >() ); return *candidates; } void IdentifierCompleter::InitThreads() { int threads_to_create = std::max( MIN_ASYNC_THREADS, std::min( MAX_ASYNC_THREADS, thread::hardware_concurrency() ) ); for ( int i = 0; i < threads_to_create; ++i ) { threads_.create_thread( bind( ThreadMain, boost::ref( latest_task_ ) ) ); } } } // namespace YouCompleteMe