YouCompleteMe/python/completers/cpp/flags.py
Ola Jeppsson 3d1a86c382 Let local YCM config file override global config file
Currently, when VIM opens a source file, YCM always defaults to
'g:global_ycm_extra_conf_file' if it exists.

This commit changes YCM's behaviour so that it first tries to find the config
file in the source file's folder (or any of its parents folder), before
falling back to 'g:global_ycm_extra_conf_file'.
2013-02-17 19:06:48 +01:00

159 lines
4.8 KiB
Python

#!/usr/bin/env python
#
# 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/>.
import imp
import os
import ycm_core
import random
import string
import sys
import vimsupport
YCM_EXTRA_CONF_FILENAME = '.ycm_extra_conf.py'
NO_EXTRA_CONF_FILENAME_MESSAGE = ('No {0} file detected, so no compile flags '
'are available. Thus no semantic support for C/C++/ObjC/ObjC++.').format(
YCM_EXTRA_CONF_FILENAME )
GLOBAL_YCM_EXTRA_CONF_FILE = vimsupport.GetVariableValue(
"g:ycm_global_ycm_extra_conf" )
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 = {}
self.special_clang_flags = _SpecialClangIncludes()
self.no_extra_conf_file_warning_posted = False
def FlagsForFile( self, filename ):
try:
return self.flags_for_file[ filename ]
except KeyError:
flags_module = self._FlagsModuleForFile( filename )
if not flags_module:
if not self.no_extra_conf_file_warning_posted:
vimsupport.PostVimMessage( NO_EXTRA_CONF_FILENAME_MESSAGE )
self.no_extra_conf_file_warning_posted = True
return None
results = flags_module.FlagsForFile( filename )
if not results.get( 'flags_ready', True ):
return None
results[ 'flags' ] += self.special_clang_flags
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:
sys.path.insert( 0, _DirectoryOfThisScript() )
flags_module = imp.load_source( _RandomName(), flags_module_file )
del sys.path[ 0 ]
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 ):
"""For a given filename, finds its nearest YCM_EXTRA_CONF_FILENAME file that
will compute the flags necessary to compile the file. If no
YCM_EXTRA_CONF_FILENAME file could be found, try to use
GLOBAL_YCM_EXTRA_CONF_FILE instead. If that also fails, return None.
Uses the global ycm_extra_conf file if one is set."""
ycm_conf_file = None
parent_folder = os.path.dirname( filename )
old_parent_folder = ''
while True:
current_file = os.path.join( parent_folder, YCM_EXTRA_CONF_FILENAME )
if os.path.exists( current_file ):
ycm_conf_file = current_file
break
old_parent_folder = parent_folder
parent_folder = os.path.dirname( parent_folder )
if parent_folder is old_parent_folder:
break
if ( not ycm_conf_file and GLOBAL_YCM_EXTRA_CONF_FILE and
os.path.exists( GLOBAL_YCM_EXTRA_CONF_FILE ) ):
ycm_conf_file = GLOBAL_YCM_EXTRA_CONF_FILE
return ycm_conf_file
def _RandomName():
"""Generates a random module name."""
return ''.join( random.choice( string.ascii_lowercase ) for x in range( 15 ) )
def _SanitizeFlags( flags ):
"""Drops unsafe flags. Currently these are only -arch flags; they tend to
crash libclang."""
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 = ycm_core.StringVec()
for flag in sanitized_flags:
vector.append( flag )
return vector
def _SpecialClangIncludes():
libclang_dir = os.path.dirname( ycm_core.__file__ )
path_to_includes = os.path.join( libclang_dir, 'clang_includes' )
return [ '-I', path_to_includes ]
def _DirectoryOfThisScript():
return os.path.dirname( os.path.abspath( __file__ ) )