From 903452e85584dfef62bea93be268ab5eba0da515 Mon Sep 17 00:00:00 2001 From: Strahinja Val Markovic Date: Sun, 29 Apr 2012 19:51:20 -0700 Subject: [PATCH] Candidates are now stored per type and filepath --- cpp/Completer.cpp | 87 +++++++++++++++++++++++++++--------- cpp/Completer.h | 39 +++++++++++++--- cpp/Utils.h | 2 + cpp/indexer.cpp | 10 ++--- cpp/tests/Completer_test.cpp | 51 +++++++++++---------- python/ycm.py | 8 +++- 6 files changed, 138 insertions(+), 59 deletions(-) diff --git a/cpp/Completer.cpp b/cpp/Completer.cpp index bde4b29e..2af49b53 100644 --- a/cpp/Completer.cpp +++ b/cpp/Completer.cpp @@ -19,18 +19,24 @@ #include "Completer.h" #include "Utils.h" +using boost::python::len; +using boost::python::extract; + namespace YouCompleteMe { + Completer::Completer( const Pylist &candidates ) { - AddCandidatesToDatabase( candidates, "" ); + AddCandidatesToDatabase( candidates, "", "" ); } -Completer::Completer( const Pylist &candidates, const std::string &filepath) +Completer::Completer( const Pylist &candidates, + const std::string &filetype, + const std::string &filepath) { - AddCandidatesToDatabase( candidates, filepath ); + AddCandidatesToDatabase( candidates, filetype, filepath ); } @@ -44,43 +50,64 @@ Completer::~Completer() } -void Completer::AddCandidatesToDatabase( const Pylist &candidates, +void Completer::AddCandidatesToDatabase( const Pylist &new_candidates, + const std::string &filetype, const std::string &filepath ) { + std::vector< Candidate *> &candidates = + GetCandidateVector( filetype, filepath ); + + int num_candidates = len( new_candidates ); + candidates.clear(); + candidates.reserve( num_candidates ); + std::string candidate_text; - for (int i = 0; i < boost::python::len( candidates ); ++i) + for (int i = 0; i < num_candidates; ++i) { - candidate_text = boost::python::extract< std::string >( candidates[ i ] ); + candidate_text = extract< std::string >( new_candidates[ i ] ); Candidate *&candidate = GetValueElseInsert( candidate_repository_, candidate_text, NULL ); if ( !candidate ) - { candidate = new Candidate( candidate_text ); - candidates_.insert( candidate ); - } + + candidates.push_back( candidate ); } } -void Completer::GetCandidatesForQuery( - const std::string &query, Pylist &candidates ) const +void Completer::CandidatesForQuery( const std::string &query, + Pylist &candidates ) const { + CandidatesForQueryAndType( query, "", candidates ); +} + + +void Completer::CandidatesForQueryAndType( const std::string &query, + const std::string &filetype, + Pylist &candidates ) 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 ( Candidate* candidate, candidates_ ) + foreach ( const FilepathToCandidates::value_type &path_and_candidates, + *it->second ) { - if ( !candidate->MatchesQueryBitset( query_bitset ) ) - continue; + foreach ( Candidate* candidate, *path_and_candidates.second ) + { + if ( !candidate->MatchesQueryBitset( query_bitset ) ) + continue; - Result result = candidate->QueryMatchResult( query ); - if ( result.IsSubsequence() ) - results.push_back( result ); + Result result = candidate->QueryMatchResult( query ); + if ( result.IsSubsequence() ) + results.push_back( result ); + } } - // Needs to be stable to preserve the lexical sort of the candidates from the - // candidates_ container - std::stable_sort( results.begin(), results.end() ); + std::sort( results.begin(), results.end() ); foreach ( const Result& result, results ) { @@ -89,4 +116,24 @@ void Completer::GetCandidatesForQuery( } +std::vector< Candidate* >& Completer::GetCandidateVector( + 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::vector< Candidate* > > &candidates = + (*path_to_candidates)[ filepath ]; + + if ( !candidates ) + candidates.reset( new std::vector< Candidate* >() ); + + return *candidates; +} + + } // namespace YouCompleteMe diff --git a/cpp/Completer.h b/cpp/Completer.h index c6528421..c1ecd515 100644 --- a/cpp/Completer.h +++ b/cpp/Completer.h @@ -23,8 +23,8 @@ #include #include #include +#include -#include #include #include @@ -32,24 +32,49 @@ namespace YouCompleteMe { typedef boost::python::list Pylist; + +// candidate text string -> candidate objects typedef boost::unordered_map< std::string, Candidate* > CandidateRepository; +// filepath -> *( *candidate ) +typedef boost::unordered_map< std::string, + boost::shared_ptr< std::vector< Candidate* > > > FilepathToCandidates; + +// filetype -> *( filepath -> *( *candidate ) ) +typedef boost::unordered_map< std::string, + boost::shared_ptr< FilepathToCandidates > > FiletypeMap; + + +// TODO: resolve problems with noncopyable // class Completer : boost::noncopyable class Completer { public: Completer() {} Completer( const Pylist &candidates ); - Completer( const Pylist &candidates, const std::string &filepath ); + Completer( const Pylist &candidates, + const std::string &filetype, + const std::string &filepath ); ~Completer(); - void AddCandidatesToDatabase( const Pylist &candidates, + void AddCandidatesToDatabase( const Pylist &new_candidates, + const std::string &filetype, const std::string &filepath ); - void GetCandidatesForQuery( - const std::string &query, Pylist &candidates ) const; + // Only provided for tests! + void CandidatesForQuery( const std::string &query, + Pylist &candidates ) const; + + void CandidatesForQueryAndType( const std::string &query, + const std::string &filetype, + Pylist &candidates ) const; private: + + std::vector< Candidate* >& GetCandidateVector( + const std::string &filetype, + const std::string &filepath ); + struct CandidatePointerLess { bool operator() ( const Candidate *first, const Candidate *second ) @@ -58,8 +83,10 @@ private: } }; + // This data structure owns all the Candidate pointers CandidateRepository candidate_repository_; - std::set< Candidate*, CandidatePointerLess > candidates_; + + FiletypeMap filetype_map_; }; } // namespace YouCompleteMe diff --git a/cpp/Utils.h b/cpp/Utils.h index e02fd419..e5c8c71f 100644 --- a/cpp/Utils.h +++ b/cpp/Utils.h @@ -27,6 +27,7 @@ namespace YouCompleteMe bool AlmostEqual( double a, double b ); + template typename Container::mapped_type & GetValueElseInsert( Container &container, @@ -37,6 +38,7 @@ GetValueElseInsert( Container &container, .first->second; } + template bool ContainsKey( Container &container, Key const& key) { diff --git a/cpp/indexer.cpp b/cpp/indexer.cpp index d9a0c7ff..52918049 100644 --- a/cpp/indexer.cpp +++ b/cpp/indexer.cpp @@ -21,10 +21,10 @@ BOOST_PYTHON_MODULE(indexer) { - using namespace boost::python; - using namespace YouCompleteMe; + using namespace boost::python; + using namespace YouCompleteMe; - class_( "Completer" ) - .def( "AddCandidatesToDatabase", &Completer::AddCandidatesToDatabase ) - .def( "GetCandidatesForQuery", &Completer::GetCandidatesForQuery ); + class_( "Completer" ) + .def( "AddCandidatesToDatabase", &Completer::AddCandidatesToDatabase ) + .def( "CandidatesForQueryAndType", &Completer::CandidatesForQueryAndType ); } diff --git a/cpp/tests/Completer_test.cpp b/cpp/tests/Completer_test.cpp index b9d9cb7a..95d42ca4 100644 --- a/cpp/tests/Completer_test.cpp +++ b/cpp/tests/Completer_test.cpp @@ -89,7 +89,7 @@ class CompleterTest : public ::testing::Test TEST_F( CompleterTest, OneCandidate ) { Pylist results; - Completer( Candidates( "foobar" ) ).GetCandidatesForQuery( "fbr", results ); + Completer( Candidates( "foobar" ) ).CandidatesForQuery( "fbr", results ); EXPECT_THAT( ToStringVector( results ), ElementsAre( "foobar" ) ); } @@ -98,9 +98,9 @@ TEST_F( CompleterTest, ManyCandidateSimple ) { Pylist results; Completer( Candidates( - "foobar", - "foobartest", - "Foobartest" ) ).GetCandidatesForQuery( "fbr", results ); + "foobar", + "foobartest", + "Foobartest" ) ).CandidatesForQuery( "fbr", results ); EXPECT_THAT( ToStringVector( results ), WhenSorted( ElementsAre( "Foobartest", @@ -112,8 +112,8 @@ TEST_F( CompleterTest, FirstCharSameAsQueryWins ) { Pylist results; Completer( Candidates( - "foobar", - "afoobar" ) ).GetCandidatesForQuery( "fbr", results ); + "foobar", + "afoobar" ) ).CandidatesForQuery( "fbr", results ); EXPECT_THAT( ToStringVector( results ), ElementsAre( "foobar", @@ -124,8 +124,8 @@ TEST_F( CompleterTest, CompleteMatchForWordBoundaryCharsWins ) { Pylist results; Completer( Candidates( - "FooBarQux", - "FBaqux" ) ).GetCandidatesForQuery( "fbq", results ); + "FooBarQux", + "FBaqux" ) ).CandidatesForQuery( "fbq", results ); EXPECT_THAT( ToStringVector( results ), ElementsAre( "FooBarQux", @@ -133,9 +133,9 @@ TEST_F( CompleterTest, CompleteMatchForWordBoundaryCharsWins ) Pylist results2; Completer( Candidates( - "CompleterTest", - "CompleteMatchForWordBoundaryCharsWins" - ) ).GetCandidatesForQuery( "ct", results2 ); + "CompleterTest", + "CompleteMatchForWordBoundaryCharsWins" + ) ).CandidatesForQuery( "ct", results2 ); EXPECT_THAT( ToStringVector( results2 ), ElementsAre( "CompleterTest", @@ -143,9 +143,8 @@ TEST_F( CompleterTest, CompleteMatchForWordBoundaryCharsWins ) Pylist results3; Completer( Candidates( - "FooBar", - "FooBarRux" - ) ).GetCandidatesForQuery( "fbr", results3 ); + "FooBar", + "FooBarRux" ) ).CandidatesForQuery( "fbr", results3 ); EXPECT_THAT( ToStringVector( results3 ), ElementsAre( "FooBarRux", @@ -156,8 +155,8 @@ TEST_F( CompleterTest, RatioUtilizationTieBreak ) { Pylist results; Completer( Candidates( - "FooBarQux", - "FooBarQuxZaa" ) ).GetCandidatesForQuery( "fbq", results ); + "FooBarQux", + "FooBarQuxZaa" ) ).CandidatesForQuery( "fbq", results ); EXPECT_THAT( ToStringVector( results ), ElementsAre( "FooBarQux", @@ -165,8 +164,8 @@ TEST_F( CompleterTest, RatioUtilizationTieBreak ) Pylist results2; Completer( Candidates( - "FooBar", - "FooBarRux" ) ).GetCandidatesForQuery( "fba", results2 ); + "FooBar", + "FooBarRux" ) ).CandidatesForQuery( "fba", results2 ); EXPECT_THAT( ToStringVector( results2 ), ElementsAre( "FooBar", @@ -177,8 +176,8 @@ TEST_F( CompleterTest, QueryPrefixOfCandidateWins ) { Pylist results; Completer( Candidates( - "foobar", - "fbaroo" ) ).GetCandidatesForQuery( "foo", results ); + "foobar", + "fbaroo" ) ).CandidatesForQuery( "foo", results ); EXPECT_THAT( ToStringVector( results ), ElementsAre( "foobar", @@ -189,8 +188,8 @@ TEST_F( CompleterTest, ShorterCandidateWins ) { Pylist results; Completer( Candidates( - "FooBarQux", - "FaBarQux" ) ).GetCandidatesForQuery( "fbq", results ); + "FooBarQux", + "FaBarQux" ) ).CandidatesForQuery( "fbq", results ); EXPECT_THAT( ToStringVector( results ), ElementsAre( "FaBarQux", @@ -198,8 +197,8 @@ TEST_F( CompleterTest, ShorterCandidateWins ) Pylist results2; Completer( Candidates( - "CompleterT", - "CompleterTest" ) ).GetCandidatesForQuery( "co", results2 ); + "CompleterT", + "CompleterTest" ) ).CandidatesForQuery( "co", results2 ); EXPECT_THAT( ToStringVector( results2 ), ElementsAre( "CompleterT", @@ -210,8 +209,8 @@ TEST_F( CompleterTest, SameLowercaseCandidateWins ) { Pylist results; Completer( Candidates( - "foobar", - "Foobar" ) ).GetCandidatesForQuery( "foo", results ); + "foobar", + "Foobar" ) ).CandidatesForQuery( "foo", results ); EXPECT_THAT( ToStringVector( results ), ElementsAre( "foobar", diff --git a/python/ycm.py b/python/ycm.py index e5eac7cd..9add3d86 100644 --- a/python/ycm.py +++ b/python/ycm.py @@ -30,7 +30,10 @@ class CompletionSystem( object ): def CompletionCandidatesForQuery( self, query ): candidates = [] - self.completer.GetCandidatesForQuery( SanitizeQuery( query ), candidates ) + filetype = vim.eval( "&filetype" ) + self.completer.CandidatesForQueryAndType( SanitizeQuery( query ), + filetype, + candidates ) return candidates def AddBufferIdentifiers( self ): @@ -38,8 +41,9 @@ class CompletionSystem( object ): text = RemoveIdentFreeText( text ) idents = re.findall( self.pattern, text ) + filetype = vim.eval( "&filetype" ) filepath = vim.eval( "expand('%:p')" ) - self.completer.AddCandidatesToDatabase( idents, filepath ) + self.completer.AddCandidatesToDatabase( idents, filetype, filepath ) def CurrentColumn(): # vim's columns start at 1 while vim.current.line columns start at 0