New system for specifying clang flags

Now the .ycm_clang_options file is a python script that needs to implement our
API. This enables the user to do arbitrary things when computing flags.
This commit is contained in:
Strahinja Val Markovic 2012-07-31 22:01:41 -07:00
parent 11a52d018c
commit eab70838f0
6 changed files with 153 additions and 93 deletions

View File

@ -322,22 +322,6 @@ void ClangCompleter::EnableThreading()
} }
void ClangCompleter::SetGlobalCompileFlags(
const std::vector< std::string > &flags )
{
global_flags_ = flags;
}
void ClangCompleter::SetFileCompileFlags(
const std::string &filename,
const std::vector< std::string > &flags )
{
flags_for_file_[ filename ] =
make_shared< std::vector< std::string > >( flags );
}
std::vector< Diagnostic > ClangCompleter::DiagnosticsForFile( std::vector< Diagnostic > ClangCompleter::DiagnosticsForFile(
const std::string &filename ) const std::string &filename )
{ {
@ -377,7 +361,8 @@ bool ClangCompleter::UpdatingTranslationUnit()
void ClangCompleter::UpdateTranslationUnit( void ClangCompleter::UpdateTranslationUnit(
const std::string &filename, const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files ) const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags )
{ {
TranslationUnitForFilename::iterator it = TranslationUnitForFilename::iterator it =
filename_to_translation_unit_.find( filename ); filename_to_translation_unit_.find( filename );
@ -397,20 +382,22 @@ void ClangCompleter::UpdateTranslationUnit(
else else
{ {
filename_to_translation_unit_[ filename ] = filename_to_translation_unit_[ filename ] =
CreateTranslationUnit( filename, unsaved_files ); CreateTranslationUnit( filename, unsaved_files, flags );
} }
} }
void ClangCompleter::UpdateTranslationUnitAsync( void ClangCompleter::UpdateTranslationUnitAsync(
std::string filename, std::string filename,
std::vector< UnsavedFile > unsaved_files ) std::vector< UnsavedFile > unsaved_files,
std::vector< std::string > flags )
{ {
boost::function< void() > functor = boost::function< void() > functor =
bind( &ClangCompleter::UpdateTranslationUnit, bind( &ClangCompleter::UpdateTranslationUnit,
boost::ref( *this ), boost::ref( *this ),
boost::move( filename ), boost::move( filename ),
boost::move( unsaved_files ) ); boost::move( unsaved_files ),
boost::move( flags ) );
boost::lock_guard< boost::mutex > lock( file_parse_task_mutex_ ); boost::lock_guard< boost::mutex > lock( file_parse_task_mutex_ );
@ -428,7 +415,8 @@ std::vector< CompletionData > ClangCompleter::CandidatesForLocationInFile(
const std::string &filename, const std::string &filename,
int line, int line,
int column, int column,
const std::vector< UnsavedFile > &unsaved_files ) const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags )
{ {
std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles( std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles(
unsaved_files ); unsaved_files );
@ -444,7 +432,9 @@ std::vector< CompletionData > ClangCompleter::CandidatesForLocationInFile(
// call reparse*, but parse* which is even less efficient. // call reparse*, but parse* which is even less efficient.
CXCodeCompleteResults *results = CXCodeCompleteResults *results =
clang_codeCompleteAt( GetTranslationUnitForFile( filename, unsaved_files ), clang_codeCompleteAt( GetTranslationUnitForFile( filename,
unsaved_files,
flags ),
filename.c_str(), filename.c_str(),
line, line,
column, column,
@ -460,11 +450,12 @@ std::vector< CompletionData > ClangCompleter::CandidatesForLocationInFile(
Future< AsyncCompletions > Future< AsyncCompletions >
ClangCompleter::CandidatesForQueryAndLocationInFileAsync( ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
const std::string &query, std::string query,
const std::string &filename, std::string filename,
int line, int line,
int column, int column,
const std::vector< UnsavedFile > &unsaved_files ) std::vector< UnsavedFile > unsaved_files,
std::vector< std::string > flags )
{ {
// TODO: throw exception when threading is not enabled and this is called // TODO: throw exception when threading is not enabled and this is called
if ( !threading_enabled_ ) if ( !threading_enabled_ )
@ -511,10 +502,11 @@ ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
candidates_for_location_in_file_functor = candidates_for_location_in_file_functor =
bind( &ClangCompleter::CandidatesForLocationInFile, bind( &ClangCompleter::CandidatesForLocationInFile,
boost::ref( *this ), boost::ref( *this ),
filename, boost::move( filename ),
line, line,
column, column,
unsaved_files ); boost::move( unsaved_files ),
boost::move( flags ) );
shared_ptr< packaged_task< AsyncCompletions > > task = shared_ptr< packaged_task< AsyncCompletions > > task =
make_shared< packaged_task< AsyncCompletions > >( make_shared< packaged_task< AsyncCompletions > >(
@ -530,14 +522,15 @@ ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
CXTranslationUnit ClangCompleter::CreateTranslationUnit( CXTranslationUnit ClangCompleter::CreateTranslationUnit(
const std::string &filename, const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files ) const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags )
{ {
std::vector< const char* > flags = FlagsForFilename( filename ); std::vector< const char* > pointer_flags;
flags.reserve( flags.size() + global_flags_.size() ); pointer_flags.reserve( flags.size() );
foreach ( const std::string &flag, global_flags_ ) foreach ( const std::string &flag, flags )
{ {
flags.push_back( flag.c_str() ); pointer_flags.push_back( flag.c_str() );
} }
std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles( std::vector< CXUnsavedFile > cxunsaved_files = ToCXUnsavedFiles(
@ -546,8 +539,8 @@ CXTranslationUnit ClangCompleter::CreateTranslationUnit(
CXTranslationUnit unit = clang_parseTranslationUnit( CXTranslationUnit unit = clang_parseTranslationUnit(
clang_index_, clang_index_,
filename.c_str(), filename.c_str(),
&flags[ 0 ], &pointer_flags[ 0 ],
flags.size(), pointer_flags.size(),
&cxunsaved_files[ 0 ], &cxunsaved_files[ 0 ],
cxunsaved_files.size(), cxunsaved_files.size(),
clang_defaultEditingTranslationUnitOptions() ); clang_defaultEditingTranslationUnitOptions() );
@ -564,41 +557,10 @@ CXTranslationUnit ClangCompleter::CreateTranslationUnit(
} }
// 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)
{
FlagsForFile::iterator it =
flags_for_file_.find( filename );
if ( it == flags_for_file_.end() )
{
flags_for_file_[ filename ] = make_shared< std::vector< std::string > >(
SanitizeClangFlags(
SplitFlags(
GetNearestClangOptions( filename ) ) ) );
it = flags_for_file_.find( filename );
}
// TODO: assert it != end
std::vector< const char* > flags;
foreach ( const std::string &flag, *it->second )
{
flags.push_back( flag.c_str() );
}
return flags;
}
CXTranslationUnit ClangCompleter::GetTranslationUnitForFile( CXTranslationUnit ClangCompleter::GetTranslationUnitForFile(
const std::string &filename, const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files ) const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags )
{ {
TranslationUnitForFilename::iterator it = TranslationUnitForFilename::iterator it =
filename_to_translation_unit_.find( filename ); filename_to_translation_unit_.find( filename );
@ -606,7 +568,9 @@ CXTranslationUnit ClangCompleter::GetTranslationUnitForFile(
if ( it != filename_to_translation_unit_.end() ) if ( it != filename_to_translation_unit_.end() )
return it->second; return it->second;
CXTranslationUnit unit = CreateTranslationUnit( filename, unsaved_files ); CXTranslationUnit unit = CreateTranslationUnit( filename,
unsaved_files,
flags );
filename_to_translation_unit_[ filename ] = unit; filename_to_translation_unit_[ filename ] = unit;
return unit; return unit;
} }

View File

@ -56,34 +56,37 @@ public:
void EnableThreading(); void EnableThreading();
void SetGlobalCompileFlags( const std::vector< std::string > &flags );
void SetFileCompileFlags( const std::string &filename,
const std::vector< std::string > &flags );
std::vector< Diagnostic > DiagnosticsForFile( const std::string &filename ); std::vector< Diagnostic > DiagnosticsForFile( const std::string &filename );
bool UpdatingTranslationUnit(); bool UpdatingTranslationUnit();
void UpdateTranslationUnit( const std::string &filename, void UpdateTranslationUnit( const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files ); const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags );
// NOTE: params are taken by value on purpose! With a C++11 compiler we can
// avoid internal copies if params are taken by value (move ctors FTW)
void UpdateTranslationUnitAsync( void UpdateTranslationUnitAsync(
std::string filename, std::string filename,
std::vector< UnsavedFile > unsaved_files ); std::vector< UnsavedFile > unsaved_files,
std::vector< std::string > flags );
std::vector< CompletionData > CandidatesForLocationInFile( std::vector< CompletionData > CandidatesForLocationInFile(
const std::string &filename, const std::string &filename,
int line, int line,
int column, int column,
const std::vector< UnsavedFile > &unsaved_files ); const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags );
// NOTE: params are taken by value on purpose! With a C++11 compiler we can
// avoid internal copies if params are taken by value (move ctors FTW)
Future< AsyncCompletions > CandidatesForQueryAndLocationInFileAsync( Future< AsyncCompletions > CandidatesForQueryAndLocationInFileAsync(
const std::string &query, std::string query,
const std::string &filename, std::string filename,
int line, int line,
int column, int column,
const std::vector< UnsavedFile > &unsaved_files ); std::vector< UnsavedFile > unsaved_files,
std::vector< std::string > flags );
private: private:
typedef ConcurrentLatestValue< typedef ConcurrentLatestValue<
@ -93,13 +96,13 @@ private:
// caller takes ownership of translation unit // caller takes ownership of translation unit
CXTranslationUnit CreateTranslationUnit( CXTranslationUnit CreateTranslationUnit(
const std::string &filename, const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files ); const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags );
std::vector< const char* > FlagsForFilename( const std::string &filename );
CXTranslationUnit GetTranslationUnitForFile( CXTranslationUnit GetTranslationUnitForFile(
const std::string &filename, const std::string &filename,
const std::vector< UnsavedFile > &unsaved_files ); const std::vector< UnsavedFile > &unsaved_files,
const std::vector< std::string > &flags );
std::vector< CompletionData > SortCandidatesForQuery( std::vector< CompletionData > SortCandidatesForQuery(
const std::string &query, const std::string &query,
@ -120,12 +123,8 @@ private:
CXIndex clang_index_; CXIndex clang_index_;
FlagsForFile flags_for_file_;
TranslationUnitForFilename filename_to_translation_unit_; TranslationUnitForFilename filename_to_translation_unit_;
std::vector< std::string > global_flags_;
CandidateRepository &candidate_repository_; CandidateRepository &candidate_repository_;
// TODO: move everything thread-related into a separated helper class // TODO: move everything thread-related into a separated helper class

View File

@ -25,6 +25,7 @@ namespace YouCompleteMe
{ {
// TODO: move most of the anon funcs in ClangCompleter.cpp to here // TODO: move most of the anon funcs in ClangCompleter.cpp to here
// TODO: remove these functions
// Removes flags that are either unnecessary or cause clang to crash // Removes flags that are either unnecessary or cause clang to crash
std::vector< std::string > SanitizeClangFlags( std::vector< std::string > SanitizeClangFlags(

View File

@ -82,7 +82,7 @@ public:
const std::string &filepath ); const std::string &filepath );
// NOTE: params are taken by value on purpose! With a C++11 compiler we can // NOTE: params are taken by value on purpose! With a C++11 compiler we can
// avoid an expensive move of buffer_contents if the param is taken by value // avoid an expensive copy of buffer_contents if the param is taken by value
// (move ctors FTW) // (move ctors FTW)
void AddCandidatesToDatabaseFromBufferAsync( void AddCandidatesToDatabaseFromBufferAsync(
std::string buffer_contents, std::string buffer_contents,

View File

@ -95,8 +95,6 @@ BOOST_PYTHON_MODULE(indexer)
class_< ClangCompleter, boost::noncopyable >( "ClangCompleter" ) class_< ClangCompleter, boost::noncopyable >( "ClangCompleter" )
.def( "EnableThreading", &ClangCompleter::EnableThreading ) .def( "EnableThreading", &ClangCompleter::EnableThreading )
.def( "SetGlobalCompileFlags", &ClangCompleter::SetGlobalCompileFlags )
.def( "SetFileCompileFlags", &ClangCompleter::SetFileCompileFlags )
.def( "DiagnosticsForFile", &ClangCompleter::DiagnosticsForFile ) .def( "DiagnosticsForFile", &ClangCompleter::DiagnosticsForFile )
.def( "UpdatingTranslationUnit", &ClangCompleter::UpdatingTranslationUnit ) .def( "UpdatingTranslationUnit", &ClangCompleter::UpdatingTranslationUnit )
.def( "UpdateTranslationUnit", &ClangCompleter::UpdateTranslationUnit ) .def( "UpdateTranslationUnit", &ClangCompleter::UpdateTranslationUnit )

View File

@ -20,11 +20,16 @@
import vim import vim
import indexer import indexer
import abc import abc
import imp
import string
import random
import os
MIN_NUM_CHARS = int( vim.eval( "g:ycm_min_num_of_chars_for_completion" ) ) MIN_NUM_CHARS = int( vim.eval( "g:ycm_min_num_of_chars_for_completion" ) )
CLANG_COMPLETION_ENABLED = int( vim.eval( "g:ycm_clang_completion_enabled" ) ) CLANG_COMPLETION_ENABLED = int( vim.eval( "g:ycm_clang_completion_enabled" ) )
CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] ) CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] )
MAX_IDENTIFIER_COMPLETIONS_RETURNED = 10 MAX_IDENTIFIER_COMPLETIONS_RETURNED = 10
CLANG_OPTIONS_FILENAME = '.ycm_clang_options'
class Completer( object ): class Completer( object ):
@ -134,6 +139,7 @@ class ClangCompleter( Completer ):
self.filename_holder = [] self.filename_holder = []
self.last_diagnostics = [] self.last_diagnostics = []
self.possibly_new_diagnostics = False self.possibly_new_diagnostics = False
self.flags = Flags()
def GetUnsavedFilesVector( self ): def GetUnsavedFilesVector( self ):
@ -188,7 +194,8 @@ class ClangCompleter( Completer ):
current_buffer.name, current_buffer.name,
line, line,
column, column,
files ) files,
self.flags.FlagsForFile( current_buffer.name ) )
def CandidatesFromStoredRequest( self ): def CandidatesFromStoredRequest( self ):
@ -203,9 +210,14 @@ class ClangCompleter( Completer ):
def OnFileReadyToParse( self ): def OnFileReadyToParse( self ):
if NumLinesInBuffer( vim.current.buffer ) < 5: if NumLinesInBuffer( vim.current.buffer ) < 5:
return return
self.possibly_new_diagnostics = True self.possibly_new_diagnostics = True
self.completer.UpdateTranslationUnitAsync( vim.current.buffer.name,
self.GetUnsavedFilesVector() ) filename = vim.current.buffer.name
self.completer.UpdateTranslationUnitAsync(
filename,
self.GetUnsavedFilesVector(),
self.flags.FlagsForFile( filename ) )
def DiagnosticsForCurrentFileReady( self ): def DiagnosticsForCurrentFileReady( self ):
@ -222,6 +234,92 @@ class ClangCompleter( Completer ):
return self.last_diagnostics return self.last_diagnostics
class Flags( object ):
def __init__( self ):
# It's caches all the way down...
self.flags_for_file = {}
self.flags_module_for_file = {}
self.flags_module_for_flags_module_file = {}
def FlagsForFile( self, filename ):
try:
return self.flags_for_file[ filename ]
except KeyError:
flags_module = self.FlagsModuleForFile( filename )
if not flags_module:
return indexer.StringVec()
results = flags_module.FlagsForFile( filename )
sanitized_flags = SanitizeFlags( results[ 'flags' ] )
if results[ 'do_cache' ]:
self.flags_for_file[ filename ] = sanitized_flags
return sanitized_flags
def FlagsModuleForFile( self, filename ):
try:
return self.flags_module_for_file[ filename ]
except KeyError:
flags_module_file = FlagsModuleSourceFileForFile( filename )
if not flags_module_file:
return None
try:
flags_module = self.flags_module_for_flags_module_file[
flags_module_file ]
except KeyError:
flags_module = imp.load_source( RandomName(), flags_module_file )
self.flags_module_for_flags_module_file[
flags_module_file ] = flags_module
self.flags_module_for_file[ filename ] = flags_module
return flags_module
def FlagsModuleSourceFileForFile( filename ):
parent_folder = os.path.dirname( filename )
old_parent_folder = ''
while True:
current_file = os.path.join( parent_folder, CLANG_OPTIONS_FILENAME )
if os.path.exists( current_file ):
return current_file
old_parent_folder = parent_folder
parent_folder = os.path.dirname( parent_folder )
if parent_folder == old_parent_folder:
return None
def RandomName():
return ''.join( random.choice( string.ascii_lowercase ) for x in range( 15 ) )
def SanitizeFlags( flags ):
sanitized_flags = []
saw_arch = False
for i, flag in enumerate( flags ):
if flag == '-arch':
saw_arch = True
continue
elif flag.startswith( '-arch' ):
continue
elif saw_arch:
saw_arch = False
continue
sanitized_flags.append( flag )
vector = indexer.StringVec()
for flag in sanitized_flags:
vector.append( flag )
return vector
def NumLinesInBuffer( buffer ): def NumLinesInBuffer( buffer ):
# This is actually less than obvious, that's why it's wrapped in a function # This is actually less than obvious, that's why it's wrapped in a function
return len( buffer ) return len( buffer )