3d1a86c382
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'.
159 lines
4.8 KiB
Python
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__ ) )
|