Permalink
Browse files

Completion suggestions are now fetched async

  • Loading branch information...
Valloric committed May 6, 2012
1 parent 7468a5a commit 7cf580a4472b831243c662700798c239960f2035
Showing with 343 additions and 33 deletions.
  1. +18 −6 autoload/youcompleteme.vim
  2. +1 −1 cpp/CMakeLists.txt
  3. +97 −7 cpp/Completer.cpp
  4. +32 −11 cpp/Completer.h
  5. +66 −0 cpp/ConcurrentStack.h
  6. +55 −0 cpp/Future.cpp
  7. +47 −0 cpp/Future.h
  8. +10 −2 cpp/indexer.cpp
  9. +17 −6 python/ycm.py
View
@@ -102,13 +102,27 @@ function! youcompleteme#Complete(findstart, base)
return start_column
else
let s:old_cursor_text = a:base
- let results = []
if strlen( a:base ) < g:ycm_min_num_of_chars_for_completion
- return results
+ return []
endif
+ py csystem.CandidatesForQueryAsync( vim.eval('a:base') )
+
+ let l:results_ready = 0
+ while !l:results_ready
+ py << EOF
+results_ready = csystem.AsyncCandidateRequestReady()
+if results_ready:
+ vim.command( 'let l:results_ready = 1' )
+EOF
+ if complete_check()
+ return { 'words' : [], 'refresh' : 'always'}
+ endif
+ endwhile
+
+ let l:results = []
py << EOF
-results = csystem.CompletionCandidatesForQuery( vim.eval('a:base') )
+results = csystem.CandidatesFromStoredRequest()
if results:
vim.command( 'let l:results = ' + str( results ) )
EOF
@@ -117,10 +131,8 @@ EOF
" keystroke. The problem is still present in vim 7.3.390 but is fixed in
" 7.3.475. It's possible that patch 404 was the one that fixed this issue,
" but I haven't tested this assumption.
- let dict = { 'words' : results }
" A bug in vim causes the '.' register to break when we use set this... sigh
- let dict.refresh = 'always'
- return dict
+ return { 'words' : l:results, 'refresh' : 'always'}
endif
endfunction
View
@@ -86,7 +86,7 @@ endif()
if( CMAKE_COMPILER_IS_GNUCXX OR COMPILER_IS_CLANG )
# We want all warnings, and warnings should be treated as errors
- #add_definitions( -Wall -pedantic -Werror )
+ # TODO: -Wextra?
add_definitions( -Wall -Werror )
endif()
View
@@ -19,22 +19,51 @@
#include "Completer.h"
#include "Utils.h"
+#include <boost/bind.hpp>
+#include <boost/make_shared.hpp>
+#include <algorithm>
+
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( TaskStack &task_stack )
+{
+ while ( true )
+ {
+ ( *task_stack.Pop() )();
+ }
+}
+
+} // unnamed namespace
+
Completer::Completer( const Pylist &candidates )
+ : threading_enabled_( false )
{
AddCandidatesToDatabase( candidates, "", "" );
}
Completer::Completer( const Pylist &candidates,
const std::string &filetype,
- const std::string &filepath)
+ const std::string &filepath )
+ : threading_enabled_( false )
{
AddCandidatesToDatabase( candidates, filetype, filepath );
}
@@ -50,6 +79,15 @@ Completer::~Completer()
}
+// 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 Completer::EnableThreading()
+{
+ threading_enabled_ = true;
+ InitThreads();
+}
+
+
void Completer::AddCandidatesToDatabase( const Pylist &new_candidates,
const std::string &filetype,
const std::string &filepath )
@@ -85,13 +123,57 @@ void Completer::CandidatesForQuery( const std::string &query,
void Completer::CandidatesForQueryAndType( const std::string &query,
const std::string &filetype,
Pylist &candidates ) const
+{
+ std::vector< Result > results;
+ ResultsForQueryAndType( query, filetype, results );
+
+ foreach ( const Result& result, results )
+ {
+ candidates.append( *result.Text() );
+ }
+}
+
+
+Future Completer::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( &Completer::ResultsForQueryAndType,
+ boost::cref( *this ),
+ query,
+ filetype ) );
+
+ unique_future< AsyncResults > future = task->get_future();
+
+ task_stack_.Push( task );
+ return Future( move( future ) );
+}
+
+AsyncResults Completer::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 Completer::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 );
- std::vector< Result > results;
foreach ( const FilepathToCandidates::value_type &path_and_candidates,
*it->second )
@@ -108,11 +190,6 @@ void Completer::CandidatesForQueryAndType( const std::string &query,
}
std::sort( results.begin(), results.end() );
-
- foreach ( const Result& result, results )
- {
- candidates.append( *result.Text() );
- }
}
@@ -136,4 +213,17 @@ std::vector< Candidate* >& Completer::GetCandidateVector(
}
+void Completer::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( task_stack_ ) ) );
+ }
+}
+
+
} // namespace YouCompleteMe
View
@@ -19,6 +19,8 @@
#define COMPLETER_H_7AR4UGXE
#include "Candidate.h"
+#include "ConcurrentStack.h"
+#include "Future.h"
#include <boost/utility.hpp>
#include <boost/python.hpp>
@@ -28,6 +30,7 @@
#include <vector>
#include <string>
+
namespace YouCompleteMe
{
@@ -44,10 +47,12 @@ typedef boost::unordered_map< std::string,
typedef boost::unordered_map< std::string,
boost::shared_ptr< FilepathToCandidates > > FiletypeMap;
+typedef ConcurrentStack<
+ boost::shared_ptr<
+ boost::packaged_task< AsyncResults > > > TaskStack;
+
-// TODO: resolve problems with noncopyable
-// class Completer : boost::noncopyable
-class Completer
+class Completer : boost::noncopyable
{
public:
Completer() {}
@@ -57,6 +62,8 @@ class Completer
const std::string &filepath );
~Completer();
+ void EnableThreading();
+
void AddCandidatesToDatabase( const Pylist &new_candidates,
const std::string &filetype,
const std::string &filepath );
@@ -69,27 +76,41 @@ class Completer
const std::string &filetype,
Pylist &candidates ) const;
+ Future CandidatesForQueryAndTypeAsync( const std::string &query,
+ const std::string &filetype ) const;
+
private:
+ AsyncResults ResultsForQueryAndType( const std::string &query,
+ const std::string &filetype ) const;
+
+ void ResultsForQueryAndType( const std::string &query,
+ const std::string &filetype,
+ std::vector< Result > &results ) const;
+
std::vector< Candidate* >& GetCandidateVector(
const std::string &filetype,
const std::string &filepath );
- struct CandidatePointerLess
- {
- bool operator() ( const Candidate *first, const Candidate *second )
- {
- return first->Text() < second->Text();
- }
- };
+ void InitThreads();
+
+
+ /////////////////////////////
+ // PRIVATE MEMBER VARIABLES
+ /////////////////////////////
// This data structure owns all the Candidate pointers
CandidateRepository candidate_repository_;
FiletypeMap filetype_map_;
+
+ mutable TaskStack task_stack_;
+
+ bool threading_enabled_;
+
+ boost::thread_group threads_;
};
} // namespace YouCompleteMe
#endif /* end of include guard: COMPLETER_H_7AR4UGXE */
-
View
@@ -0,0 +1,66 @@
+// 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 CONCURRENTSTACK_H_SYF1JPPG
+#define CONCURRENTSTACK_H_SYF1JPPG
+
+#include <stack>
+#include <boost/thread.hpp>
+#include <boost/utility.hpp>
+
+namespace YouCompleteMe
+{
+
+template <typename T>
+class ConcurrentStack : boost::noncopyable
+{
+public:
+
+ void Push( const T& data )
+ {
+ {
+ boost::unique_lock< boost::mutex > lock( mutex_ );
+ stack_.push( data );
+ }
+
+ condition_variable_.notify_one();
+ }
+
+ T Pop()
+ {
+ boost::unique_lock< boost::mutex > lock( mutex_ );
+
+ while ( stack_.empty() )
+ {
+ condition_variable_.wait( lock );
+ }
+
+ T result = stack_.top();
+ stack_.pop();
+ return result;
+ }
+
+private:
+ std::stack<T> stack_;
+ boost::mutex mutex_;
+ boost::condition_variable condition_variable_;
+
+};
+
+} // namespace YouCompleteMe
+
+#endif /* end of include guard: CONCURRENTSTACK_H_SYF1JPPG */
Oops, something went wrong.

0 comments on commit 7cf580a

Please sign in to comment.