Loading .ycm_clang_options files now

These files can contain flags that are passed to clang.
This commit is contained in:
Strahinja Val Markovic 2012-07-22 15:19:28 -07:00
parent b703138cac
commit cdb8dfc86b
11 changed files with 459 additions and 75 deletions

View File

@ -21,10 +21,13 @@
#include "standard.h" #include "standard.h"
#include "CandidateRepository.h" #include "CandidateRepository.h"
#include "ConcurrentLatestValue.h" #include "ConcurrentLatestValue.h"
#include "Utils.h"
#include "ClangUtils.h"
#include <clang-c/Index.h> #include <clang-c/Index.h>
#include <boost/make_shared.hpp> #include <boost/make_shared.hpp>
namespace fs = boost::filesystem;
using boost::packaged_task; using boost::packaged_task;
using boost::bind; using boost::bind;
using boost::unique_future; using boost::unique_future;
@ -258,7 +261,8 @@ void ClangCompleter::SetFileCompileFlags(
const std::string &filename, const std::string &filename,
const std::vector< std::string > &flags ) const std::vector< std::string > &flags )
{ {
flags_for_file_[ filename ] = flags; flags_for_file_[ filename ] =
make_shared< std::vector< std::string > >( flags );
} }
@ -393,7 +397,14 @@ CXTranslationUnit ClangCompleter::CreateTranslationUnit(
const std::string &filename, const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files ) const std::vector< UnsavedFile > &unsaved_files )
{ {
std::vector< const char* > flags = ClangFlagsForFilename( filename ); std::vector< const char* > flags = FlagsForFilename( filename );
flags.reserve( flags.size() + global_flags_.size() );
foreach ( const std::string &flag, global_flags_ )
{
flags.push_back( flag.c_str() );
}
std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles( std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles(
unsaved_files ); unsaved_files );
@ -408,20 +419,30 @@ CXTranslationUnit ClangCompleter::CreateTranslationUnit(
} }
std::vector< const char* > ClangCompleter::ClangFlagsForFilename( // The implementation of this function is somewhat non-obvious because we need
// to make sure that the data pointed to by the const char* pointers returned
// outlives this function. We want to make sure that we are calling c_str on the
// string objects that are actually stored in flags_for_file_
std::vector< const char* > ClangCompleter::FlagsForFilename(
const std::string &filename) const std::string &filename)
{ {
std::vector< const char* > flags; FlagsForFile::iterator it =
flags_for_file_.find( filename );
std::vector< std::string > file_flags = flags_for_file_[ filename ]; if ( it == flags_for_file_.end() )
flags.reserve( file_flags.size() + global_flags_.size() );
foreach ( const std::string &flag, global_flags_ )
{ {
flags.push_back( flag.c_str() ); flags_for_file_[ filename ] = make_shared< std::vector< std::string > >(
SanitizeClangFlags(
SplitFlags(
GetNearestClangOptions( filename ) ) ) );
it = flags_for_file_.find( filename );
} }
foreach ( const std::string &flag, file_flags ) // TODO: assert it != end
std::vector< const char* > flags;
foreach ( const std::string &flag, *it->second )
{ {
flags.push_back( flag.c_str() ); flags.push_back( flag.c_str() );
} }

View File

@ -38,13 +38,15 @@ struct CompletionData;
typedef boost::shared_ptr< std::vector< CompletionData > > AsyncCompletions; typedef boost::shared_ptr< std::vector< CompletionData > > AsyncCompletions;
typedef boost::unordered_map< std::string, std::vector< std::string > > typedef boost::unordered_map< std::string,
FlagsForFile; boost::shared_ptr<
std::vector< std::string > > > FlagsForFile;
typedef boost::unordered_map< std::string, CXTranslationUnit > typedef boost::unordered_map< std::string, CXTranslationUnit >
TranslationUnitForFilename; TranslationUnitForFilename;
// TODO: document that all filename parameters must be absolute paths
class ClangCompleter : boost::noncopyable class ClangCompleter : boost::noncopyable
{ {
public: public:
@ -84,8 +86,7 @@ private:
const std::string &filename, const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files ); const std::vector< UnsavedFile > &unsaved_files );
std::vector< const char* > ClangFlagsForFilename( std::vector< const char* > FlagsForFilename( const std::string &filename );
const std::string &filename );
CXTranslationUnit GetTranslationUnitForFile( CXTranslationUnit GetTranslationUnitForFile(
const std::string &filename, const std::string &filename,

118
cpp/ycm/ClangUtils.cpp Normal file
View File

@ -0,0 +1,118 @@
// 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/>.
#include "ClangUtils.h"
#include "Utils.h"
#include <cctype>
#include <boost/algorithm/string.hpp>
namespace YouCompleteMe
{
const char *CLANG_OPTIONS_FILENAME = ".ycm_clang_options";
std::vector< std::string > SanitizeClangFlags(
const std::vector< std::string > &flags )
{
std::vector< std::string > sanitized_flags = flags;
std::vector< std::string >::iterator it = sanitized_flags.begin();
while ( it != sanitized_flags.end() )
{
if ( *it == "-arch" )
{
if ( it + 1 != sanitized_flags.end() )
sanitized_flags.erase( it, it + 2 );
else
sanitized_flags.erase( it, it + 1 );
}
else
{
++it;
}
}
return sanitized_flags;
}
std::vector< std::string > SplitFlags( const std::string &flags_string )
{
std::vector< std::string > flags;
bool in_quotes = false;
uint flag_start = 0;
uint flag_length = 0;
for ( uint i = 0; i < flags_string.size(); ++i )
{
char current_char = flags_string[ i ];
if ( isspace( current_char ) and !in_quotes )
{
if ( flag_length != 0 )
{
flags.push_back( flags_string.substr( flag_start, flag_length ) );
flag_length = 0;
}
flag_start = i + 1;
continue;
}
if ( current_char == '\'' || current_char == '\"' )
{
in_quotes = !in_quotes;
}
++flag_length;
}
if ( flag_length != 0 )
flags.push_back( flags_string.substr( flag_start, flag_length ) );
return flags;
}
std::string GetNearestClangOptions( const std::string &filename )
{
fs::path parent_folder = fs::path( filename ).parent_path();
fs::path clang_options_filename( CLANG_OPTIONS_FILENAME );
std::string options_file_contents;
fs::path old_parent_folder = parent_folder;
do
{
fs::path current_file = parent_folder / clang_options_filename;
if ( fs::exists( current_file ) )
{
options_file_contents = ReadUtf8File( current_file );
break;
}
old_parent_folder = parent_folder;
parent_folder = parent_folder.parent_path();
}
while ( old_parent_folder != parent_folder );
return options_file_contents;
}
} // namespace YouCompleteMe

40
cpp/ycm/ClangUtils.h Normal file
View File

@ -0,0 +1,40 @@
// 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 CLANGUTILS_H_9MVHQLJS
#define CLANGUTILS_H_9MVHQLJS
#include <vector>
#include <string>
namespace YouCompleteMe
{
// TODO: move most of the anon funcs in ClangCompleter.cpp to here
// Removes flags that are either unnecessary or cause clang to crash
std::vector< std::string > SanitizeClangFlags(
const std::vector< std::string > &flags );
std::vector< std::string > SplitFlags( const std::string &flags_string );
std::string GetNearestClangOptions( const std::string &filename );
} // namespace YouCompleteMe
#endif /* end of include guard: CLANGUTILS_H_9MVHQLJS */

View File

@ -18,6 +18,10 @@
#include "Utils.h" #include "Utils.h"
#include <cmath> #include <cmath>
#include <limits> #include <limits>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
namespace fs = boost::filesystem;
namespace YouCompleteMe namespace YouCompleteMe
{ {
@ -29,4 +33,26 @@ bool AlmostEqual( double a, double b )
std::max( std::abs( a ), std::abs( b ) ) ); std::max( std::abs( a ), std::abs( b ) ) );
} }
std::string ReadUtf8File( const fs::path &filepath )
{
fs::ifstream file( filepath, std::ios::in | std::ios::binary );
std::vector< char > contents( (std::istreambuf_iterator< char >( file )),
std::istreambuf_iterator< char >() );
if ( contents.size() == 0 )
return std::string();
return std::string( contents.begin(), contents.end() );
}
void WriteUtf8File( const fs::path &filepath, const std::string &contents )
{
fs::ofstream file;
file.open( filepath );
file << contents;
file.close();
}
} // namespace YouCompleteMe } // namespace YouCompleteMe

View File

@ -18,15 +18,23 @@
#ifndef UTILS_H_KEPMRPBH #ifndef UTILS_H_KEPMRPBH
#define UTILS_H_KEPMRPBH #define UTILS_H_KEPMRPBH
#include <string> #include <string>
#include <vector> #include <vector>
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
namespace YouCompleteMe namespace YouCompleteMe
{ {
bool AlmostEqual( double a, double b ); bool AlmostEqual( double a, double b );
// Reads the entire contents of the specified file. If the file does not exist,
// an exception is thrown.
std::string ReadUtf8File( const fs::path &filepath );
// Writes the entire contents of the specified file. If the file does not exist,
// an exception is thrown.
void WriteUtf8File( const fs::path &filepath, const std::string &contents );
template <class Container, class Key> template <class Container, class Key>
typename Container::mapped_type & typename Container::mapped_type &

View File

@ -0,0 +1,104 @@
// 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/>.
#include "ClangUtils.h"
#include "Utils.h"
#include "TestUtils.h"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
using ::testing::ElementsAre;
using ::testing::WhenSorted;
namespace YouCompleteMe
{
extern const char *CLANG_OPTIONS_FILENAME;
TEST( ClangUtilsTest, SanitizeClangFlagsWorks )
{
EXPECT_THAT( SanitizeClangFlags( StringVector(
"foo",
"-arch",
"die",
"-arch",
"die2",
"bar" ) ),
ElementsAre( "foo",
"bar" ) );
EXPECT_THAT( SanitizeClangFlags( StringVector(
"foo",
"-arch",
"die" ) ),
ElementsAre( "foo" ) );
EXPECT_THAT( SanitizeClangFlags( StringVector(
"-arch",
"die" ) ),
ElementsAre() );
EXPECT_THAT( SanitizeClangFlags( StringVector(
"-arch" ) ),
ElementsAre() );
}
TEST( ClangUtilsTest, SplitFlagsWorks )
{
EXPECT_THAT( SplitFlags( "-f --bar=qux" ),
ElementsAre( "-f",
"--bar=qux" ) );
EXPECT_THAT( SplitFlags( "foo" ),
ElementsAre( "foo" ) );
EXPECT_THAT( SplitFlags( "foo \n\n\t\v bar" ),
ElementsAre( "foo",
"bar" ) );
EXPECT_THAT( SplitFlags( " a ' a b c ' q " ),
ElementsAre( "a",
"' a b c '",
"q" ) );
EXPECT_THAT( SplitFlags( "-I../a/b/c -I\"foo/b/c\" -I\"a c\" -I'a/b/c'" ),
ElementsAre( "-I../a/b/c",
"-I\"foo/b/c\"",
"-I\"a c\"",
"-I'a/b/c'" ) );
}
TEST( ClangUtilsTest, GetNearestClangOptionsWorks )
{
fs::path temp_root = fs::temp_directory_path() / fs::unique_path();
fs::create_directories( temp_root );
std::string contents = "foo bar";
WriteUtf8File( temp_root / fs::path( CLANG_OPTIONS_FILENAME ), contents );
fs::path parent = temp_root / fs::unique_path();
fs::create_directories( parent );
EXPECT_STREQ( contents.c_str(),
GetNearestClangOptions(
( parent / fs::unique_path() ).string() ).c_str() );
}
} // namespace YouCompleteMe

View File

@ -19,6 +19,7 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "IdentifierCompleter.h" #include "IdentifierCompleter.h"
#include "Utils.h" #include "Utils.h"
#include "TestUtils.h"
using ::testing::ElementsAre; using ::testing::ElementsAre;
using ::testing::WhenSorted; using ::testing::WhenSorted;
@ -26,56 +27,19 @@ using ::testing::WhenSorted;
namespace YouCompleteMe namespace YouCompleteMe
{ {
namespace
{
std::vector< std::string > Candidates( const std::string &a,
const std::string &b = std::string(),
const std::string &c = std::string(),
const std::string &d = std::string(),
const std::string &e = std::string(),
const std::string &f = std::string(),
const std::string &g = std::string(),
const std::string &h = std::string(),
const std::string &i = std::string() )
{
std::vector< std::string > candidates;
candidates.push_back( a );
if ( !b.empty() )
candidates.push_back( b );
if ( !c.empty() )
candidates.push_back( c );
if ( !d.empty() )
candidates.push_back( d );
if ( !e.empty() )
candidates.push_back( e );
if ( !f.empty() )
candidates.push_back( f );
if ( !g.empty() )
candidates.push_back( g );
if ( !h.empty() )
candidates.push_back( h );
if ( !i.empty() )
candidates.push_back( i );
return candidates;
}
} // unnamed namespace
// This differs from what we expect from the ClangCompleter. That one should // This differs from what we expect from the ClangCompleter. That one should
// return results for an empty query. // return results for an empty query.
TEST( IdentifierCompleterTest, EmptyQueryNoResults ) TEST( IdentifierCompleterTest, EmptyQueryNoResults )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"foobar" ) ).CandidatesForQuery( "" ), "foobar" ) ).CandidatesForQuery( "" ),
ElementsAre() ); ElementsAre() );
} }
TEST( IdentifierCompleterTest, NoDuplicatesReturned ) TEST( IdentifierCompleterTest, NoDuplicatesReturned )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"foobar", "foobar",
"foobar", "foobar",
"foobar" ) ).CandidatesForQuery( "foo" ), "foobar" ) ).CandidatesForQuery( "foo" ),
@ -85,14 +49,14 @@ TEST( IdentifierCompleterTest, NoDuplicatesReturned )
TEST( IdentifierCompleterTest, OneCandidate ) TEST( IdentifierCompleterTest, OneCandidate )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"foobar" ) ).CandidatesForQuery( "fbr" ), "foobar" ) ).CandidatesForQuery( "fbr" ),
ElementsAre( "foobar" ) ); ElementsAre( "foobar" ) );
} }
TEST( IdentifierCompleterTest, ManyCandidateSimple ) TEST( IdentifierCompleterTest, ManyCandidateSimple )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"foobar", "foobar",
"foobartest", "foobartest",
"Foobartest" ) ).CandidatesForQuery( "fbr" ), "Foobartest" ) ).CandidatesForQuery( "fbr" ),
@ -103,7 +67,7 @@ TEST( IdentifierCompleterTest, ManyCandidateSimple )
TEST( IdentifierCompleterTest, FirstCharSameAsQueryWins ) TEST( IdentifierCompleterTest, FirstCharSameAsQueryWins )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"foobar", "foobar",
"afoobar" ) ).CandidatesForQuery( "fbr" ), "afoobar" ) ).CandidatesForQuery( "fbr" ),
ElementsAre( "foobar", ElementsAre( "foobar",
@ -112,20 +76,20 @@ TEST( IdentifierCompleterTest, FirstCharSameAsQueryWins )
TEST( IdentifierCompleterTest, CompleteMatchForWordBoundaryCharsWins ) TEST( IdentifierCompleterTest, CompleteMatchForWordBoundaryCharsWins )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"FooBarQux", "FooBarQux",
"FBaqux" ) ).CandidatesForQuery( "fbq" ), "FBaqux" ) ).CandidatesForQuery( "fbq" ),
ElementsAre( "FooBarQux", ElementsAre( "FooBarQux",
"FBaqux" ) ); "FBaqux" ) );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"CompleterTest", "CompleterTest",
"CompleteMatchForWordBoundaryCharsWins" ) ) "CompleteMatchForWordBoundaryCharsWins" ) )
.CandidatesForQuery( "ct" ), .CandidatesForQuery( "ct" ),
ElementsAre( "CompleterTest", ElementsAre( "CompleterTest",
"CompleteMatchForWordBoundaryCharsWins" ) ); "CompleteMatchForWordBoundaryCharsWins" ) );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"FooBar", "FooBar",
"FooBarRux" ) ).CandidatesForQuery( "fbr" ), "FooBarRux" ) ).CandidatesForQuery( "fbr" ),
ElementsAre( "FooBarRux", ElementsAre( "FooBarRux",
@ -134,37 +98,37 @@ TEST( IdentifierCompleterTest, CompleteMatchForWordBoundaryCharsWins )
TEST( IdentifierCompleterTest, RatioUtilizationTieBreak ) TEST( IdentifierCompleterTest, RatioUtilizationTieBreak )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"aGaaFooBarQux", "aGaaFooBarQux",
"aBaafbq" ) ).CandidatesForQuery( "fbq" ), "aBaafbq" ) ).CandidatesForQuery( "fbq" ),
ElementsAre( "aGaaFooBarQux", ElementsAre( "aGaaFooBarQux",
"aBaafbq" ) ); "aBaafbq" ) );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"aFooBarQux", "aFooBarQux",
"afbq" ) ).CandidatesForQuery( "fbq" ), "afbq" ) ).CandidatesForQuery( "fbq" ),
ElementsAre( "aFooBarQux", ElementsAre( "aFooBarQux",
"afbq" ) ); "afbq" ) );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"acaaCaaFooGxx", "acaaCaaFooGxx",
"aCaafoog" ) ).CandidatesForQuery( "caafoo" ), "aCaafoog" ) ).CandidatesForQuery( "caafoo" ),
ElementsAre( "acaaCaaFooGxx", ElementsAre( "acaaCaaFooGxx",
"aCaafoog" ) ); "aCaafoog" ) );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"acaaCaaFooGxx", "acaaCaaFooGxx",
"aCaafoog" ) ).CandidatesForQuery( "caaFoo" ), "aCaafoog" ) ).CandidatesForQuery( "caaFoo" ),
ElementsAre( "acaaCaaFooGxx", ElementsAre( "acaaCaaFooGxx",
"aCaafoog" ) ); "aCaafoog" ) );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"FooBarQux", "FooBarQux",
"FooBarQuxZaa" ) ).CandidatesForQuery( "fbq" ), "FooBarQuxZaa" ) ).CandidatesForQuery( "fbq" ),
ElementsAre( "FooBarQux", ElementsAre( "FooBarQux",
"FooBarQuxZaa" ) ); "FooBarQuxZaa" ) );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"FooBar", "FooBar",
"FooBarRux" ) ).CandidatesForQuery( "fba" ), "FooBarRux" ) ).CandidatesForQuery( "fba" ),
ElementsAre( "FooBar", ElementsAre( "FooBar",
@ -173,7 +137,7 @@ TEST( IdentifierCompleterTest, RatioUtilizationTieBreak )
TEST( IdentifierCompleterTest, QueryPrefixOfCandidateWins ) TEST( IdentifierCompleterTest, QueryPrefixOfCandidateWins )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"foobar", "foobar",
"fbaroo" ) ).CandidatesForQuery( "foo" ), "fbaroo" ) ).CandidatesForQuery( "foo" ),
ElementsAre( "foobar", ElementsAre( "foobar",
@ -182,26 +146,26 @@ TEST( IdentifierCompleterTest, QueryPrefixOfCandidateWins )
TEST( IdentifierCompleterTest, LowerMatchCharIndexSumWins ) TEST( IdentifierCompleterTest, LowerMatchCharIndexSumWins )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"ratio_of_word_boundary_chars_in_query_", "ratio_of_word_boundary_chars_in_query_",
"first_char_same_in_query_and_text_") ) "first_char_same_in_query_and_text_") )
.CandidatesForQuery( "charinq" ), .CandidatesForQuery( "charinq" ),
ElementsAre( "first_char_same_in_query_and_text_", ElementsAre( "first_char_same_in_query_and_text_",
"ratio_of_word_boundary_chars_in_query_") ); "ratio_of_word_boundary_chars_in_query_") );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"barfooq", "barfooq",
"barquxfoo" ) ).CandidatesForQuery( "foo" ), "barquxfoo" ) ).CandidatesForQuery( "foo" ),
ElementsAre( "barfooq", ElementsAre( "barfooq",
"barquxfoo") ); "barquxfoo") );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"xxxxxxabc", "xxxxxxabc",
"xxabcxxxx" ) ).CandidatesForQuery( "abc" ), "xxabcxxxx" ) ).CandidatesForQuery( "abc" ),
ElementsAre( "xxabcxxxx", ElementsAre( "xxabcxxxx",
"xxxxxxabc") ); "xxxxxxabc") );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"FooBarQux", "FooBarQux",
"FaBarQux" ) ).CandidatesForQuery( "fbq" ), "FaBarQux" ) ).CandidatesForQuery( "fbq" ),
ElementsAre( "FaBarQux", ElementsAre( "FaBarQux",
@ -210,13 +174,13 @@ TEST( IdentifierCompleterTest, LowerMatchCharIndexSumWins )
TEST( IdentifierCompleterTest, ShorterCandidateWins ) TEST( IdentifierCompleterTest, ShorterCandidateWins )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"CompleterT", "CompleterT",
"CompleterTest" ) ).CandidatesForQuery( "co" ), "CompleterTest" ) ).CandidatesForQuery( "co" ),
ElementsAre( "CompleterT", ElementsAre( "CompleterT",
"CompleterTest" ) ); "CompleterTest" ) );
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"CompleterT", "CompleterT",
"CompleterTest" ) ).CandidatesForQuery( "plet" ), "CompleterTest" ) ).CandidatesForQuery( "plet" ),
ElementsAre( "CompleterT", ElementsAre( "CompleterT",
@ -225,7 +189,7 @@ TEST( IdentifierCompleterTest, ShorterCandidateWins )
TEST( IdentifierCompleterTest, SameLowercaseCandidateWins ) TEST( IdentifierCompleterTest, SameLowercaseCandidateWins )
{ {
EXPECT_THAT( IdentifierCompleter( Candidates( EXPECT_THAT( IdentifierCompleter( StringVector(
"foobar", "foobar",
"Foobar" ) ).CandidatesForQuery( "foo" ), "Foobar" ) ).CandidatesForQuery( "foo" ),
ElementsAre( "foobar", ElementsAre( "foobar",

View File

@ -0,0 +1,56 @@
// 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/>.
#include "TestUtils.h"
namespace YouCompleteMe
{
std::vector< std::string > StringVector( const std::string &a,
const std::string &b,
const std::string &c,
const std::string &d,
const std::string &e,
const std::string &f,
const std::string &g,
const std::string &h,
const std::string &i )
{
std::vector< std::string > string_vector;
string_vector.push_back( a );
if ( !b.empty() )
string_vector.push_back( b );
if ( !c.empty() )
string_vector.push_back( c );
if ( !d.empty() )
string_vector.push_back( d );
if ( !e.empty() )
string_vector.push_back( e );
if ( !f.empty() )
string_vector.push_back( f );
if ( !g.empty() )
string_vector.push_back( g );
if ( !h.empty() )
string_vector.push_back( h );
if ( !i.empty() )
string_vector.push_back( i );
return string_vector;
}
} // namespace YouCompleteMe

40
cpp/ycm/tests/TestUtils.h Normal file
View File

@ -0,0 +1,40 @@
// 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 TESTUTILS_H_G4RKMGUD
#define TESTUTILS_H_G4RKMGUD
#include <vector>
#include <string>
namespace YouCompleteMe
{
std::vector< std::string > StringVector( const std::string &a,
const std::string &b = std::string(),
const std::string &c = std::string(),
const std::string &d = std::string(),
const std::string &e = std::string(),
const std::string &f = std::string(),
const std::string &g = std::string(),
const std::string &h = std::string(),
const std::string &i = std::string() );
} // namespace YouCompleteMe
#endif /* end of include guard: TESTUTILS_H_G4RKMGUD */

View File

@ -121,6 +121,7 @@ class ClangCompleter( Completer ):
self.contents_holder = [] self.contents_holder = []
self.filename_holder = [] self.filename_holder = []
def CandidatesForQueryAsync( self, query ): def CandidatesForQueryAsync( self, query ):
# TODO: sanitize query # TODO: sanitize query
files = indexer.UnsavedFileVec() files = indexer.UnsavedFileVec()
@ -164,6 +165,11 @@ class ClangCompleter( Completer ):
return [ CompletionDataToDict( x ) for x in self.future.GetResults() ] return [ CompletionDataToDict( x ) for x in self.future.GetResults() ]
def OnFileEnter( self ):
pass
def GetUnsavedBuffers(): def GetUnsavedBuffers():
def BufferModified( buffer_number ): def BufferModified( buffer_number ):
to_eval = 'getbufvar({0}, "&mod")'.format( buffer_number ) to_eval = 'getbufvar({0}, "&mod")'.format( buffer_number )