Merge branch 'master' of github.com:kljohann/YouCompleteMe into kljohann-master
This commit is contained in:
commit
95b9eeee18
14
README.md
14
README.md
@ -322,6 +322,10 @@ method in that module which should provide it with the information necessary to
|
|||||||
compile the current file. (You can also provide a path to a global
|
compile the current file. (You can also provide a path to a global
|
||||||
`.ycm_extra_conf.py` file, which will be used as a fallback. See the Options
|
`.ycm_extra_conf.py` file, which will be used as a fallback. See the Options
|
||||||
section for more details.)
|
section for more details.)
|
||||||
|
To prevent the execution of malicious code from a file you didn't write
|
||||||
|
YCM will ask once per module if it is safe to be loaded.
|
||||||
|
(This can be disabled. See the Options section.)
|
||||||
|
|
||||||
|
|
||||||
This system was designed this way so that the user can perform any arbitrary
|
This system was designed this way so that the user can perform any arbitrary
|
||||||
sequence of operations to produce a list of compilation flags YCM should hand
|
sequence of operations to produce a list of compilation flags YCM should hand
|
||||||
@ -657,6 +661,16 @@ Default: `''`
|
|||||||
|
|
||||||
let g:ycm_global_ycm_extra_conf = ''
|
let g:ycm_global_ycm_extra_conf = ''
|
||||||
|
|
||||||
|
### The `g:ycm_confirm_extra_conf` option
|
||||||
|
|
||||||
|
When this option is set to `1` YCM will ask once per '.ycm_extra_conf.py' file
|
||||||
|
if it is safe to be loaded. This is to prevent execution of malicious code
|
||||||
|
from a '.ycm_extra_conf.py' file you didn't write.
|
||||||
|
|
||||||
|
Default: `1`
|
||||||
|
|
||||||
|
let g:ycm_confirm_extra_conf = 1
|
||||||
|
|
||||||
### The `g:ycm_semantic_triggers` option
|
### The `g:ycm_semantic_triggers` option
|
||||||
|
|
||||||
This option controls the character-based triggers for the various semantic
|
This option controls the character-based triggers for the various semantic
|
||||||
|
@ -101,6 +101,9 @@ let g:ycm_key_detailed_diagnostics =
|
|||||||
let g:ycm_global_ycm_extra_conf =
|
let g:ycm_global_ycm_extra_conf =
|
||||||
\ get( g:, 'ycm_global_ycm_extra_conf', '' )
|
\ get( g:, 'ycm_global_ycm_extra_conf', '' )
|
||||||
|
|
||||||
|
let g:ycm_confirm_extra_conf =
|
||||||
|
\ get( g:, 'ycm_confirm_extra_conf', 1 )
|
||||||
|
|
||||||
let g:ycm_semantic_triggers =
|
let g:ycm_semantic_triggers =
|
||||||
\ get( g:, 'ycm_semantic_triggers', {
|
\ get( g:, 'ycm_semantic_triggers', {
|
||||||
\ 'c' : ['->', '.'],
|
\ 'c' : ['->', '.'],
|
||||||
|
@ -87,8 +87,8 @@ class IdentifierCompleter( Completer ):
|
|||||||
def AddBufferIdentifiers( self ):
|
def AddBufferIdentifiers( self ):
|
||||||
filetype = vim.eval( "&filetype" )
|
filetype = vim.eval( "&filetype" )
|
||||||
filepath = vim.eval( "expand('%:p')" )
|
filepath = vim.eval( "expand('%:p')" )
|
||||||
collect_from_comments_and_strings = bool( int( vimsupport.GetVariableValue(
|
collect_from_comments_and_strings = vimsupport.GetBoolValue(
|
||||||
"g:ycm_collect_identifiers_from_comments_and_strings" ) ) )
|
"g:ycm_collect_identifiers_from_comments_and_strings" )
|
||||||
|
|
||||||
if not filetype or not filepath:
|
if not filetype or not filepath:
|
||||||
return
|
return
|
||||||
|
@ -206,7 +206,8 @@ class ClangCompleter( Completer ):
|
|||||||
def DebugInfo( self ):
|
def DebugInfo( self ):
|
||||||
filename = vim.current.buffer.name
|
filename = vim.current.buffer.name
|
||||||
flags = self.flags.FlagsForFile( filename ) or []
|
flags = self.flags.FlagsForFile( filename ) or []
|
||||||
return 'Flags for {0}:\n{1}'.format( filename, list( flags ) )
|
source = self.flags.ModuleForFile( filename )
|
||||||
|
return 'Flags for {0} loaded from {1}:\n{2}'.format( filename, source, list( flags ) )
|
||||||
|
|
||||||
|
|
||||||
# TODO: make these functions module-local
|
# TODO: make these functions module-local
|
||||||
|
@ -29,32 +29,49 @@ YCM_EXTRA_CONF_FILENAME = '.ycm_extra_conf.py'
|
|||||||
NO_EXTRA_CONF_FILENAME_MESSAGE = ('No {0} file detected, so no compile flags '
|
NO_EXTRA_CONF_FILENAME_MESSAGE = ('No {0} file detected, so no compile flags '
|
||||||
'are available. Thus no semantic support for C/C++/ObjC/ObjC++. See the '
|
'are available. Thus no semantic support for C/C++/ObjC/ObjC++. See the '
|
||||||
'docs for details.').format( YCM_EXTRA_CONF_FILENAME )
|
'docs for details.').format( YCM_EXTRA_CONF_FILENAME )
|
||||||
|
CONFIRM_CONF_FILE_MESSAGE = 'Found {0}. Load?'
|
||||||
GLOBAL_YCM_EXTRA_CONF_FILE = os.path.expanduser(
|
GLOBAL_YCM_EXTRA_CONF_FILE = os.path.expanduser(
|
||||||
vimsupport.GetVariableValue( "g:ycm_global_ycm_extra_conf" )
|
vimsupport.GetVariableValue( "g:ycm_global_ycm_extra_conf" )
|
||||||
)
|
)
|
||||||
|
|
||||||
class Flags( object ):
|
class Flags( object ):
|
||||||
|
"""Keeps track of the flags necessary to compile a file.
|
||||||
|
The flags are loaded from user-created python files
|
||||||
|
(hereafter referred to as 'modules') that contain
|
||||||
|
a method FlagsForFile( filename )."""
|
||||||
def __init__( self ):
|
def __init__( self ):
|
||||||
# It's caches all the way down...
|
# It's caches all the way down...
|
||||||
self.flags_for_file = {}
|
self.flags_for_file = {}
|
||||||
self.flags_module_for_file = {}
|
self.module_for_file = {}
|
||||||
self.flags_module_for_flags_module_file = {}
|
self.modules = FlagsModules()
|
||||||
self.special_clang_flags = _SpecialClangIncludes()
|
self.special_clang_flags = _SpecialClangIncludes()
|
||||||
self.no_extra_conf_file_warning_posted = False
|
self.no_extra_conf_file_warning_posted = False
|
||||||
|
|
||||||
|
def ModuleForFile( self, filename ):
|
||||||
|
"""This will try all files returned by _FlagsModuleSourceFilesForFile in
|
||||||
|
order and return the filename of the first module that was allowed to load.
|
||||||
|
If no module was found or allowed to load, None is returned."""
|
||||||
|
|
||||||
|
if not self.module_for_file.has_key( filename ):
|
||||||
|
for flags_module_file in _FlagsModuleSourceFilesForFile( filename ):
|
||||||
|
if self.modules.Load( flags_module_file ):
|
||||||
|
self.module_for_file[ filename ] = flags_module_file
|
||||||
|
break
|
||||||
|
|
||||||
|
return self.module_for_file.setdefault( filename )
|
||||||
|
|
||||||
def FlagsForFile( self, filename ):
|
def FlagsForFile( self, filename ):
|
||||||
try:
|
try:
|
||||||
return self.flags_for_file[ filename ]
|
return self.flags_for_file[ filename ]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
flags_module = self._FlagsModuleForFile( filename )
|
module_file = self.ModuleForFile( filename )
|
||||||
if not flags_module:
|
if not module_file:
|
||||||
if not self.no_extra_conf_file_warning_posted:
|
if not self.no_extra_conf_file_warning_posted:
|
||||||
vimsupport.PostVimMessage( NO_EXTRA_CONF_FILENAME_MESSAGE )
|
vimsupport.PostVimMessage( NO_EXTRA_CONF_FILENAME_MESSAGE )
|
||||||
self.no_extra_conf_file_warning_posted = True
|
self.no_extra_conf_file_warning_posted = True
|
||||||
return None
|
return None
|
||||||
|
|
||||||
results = flags_module.FlagsForFile( filename )
|
results = self.modules[ module_file ].FlagsForFile( filename )
|
||||||
|
|
||||||
if not results.get( 'flags_ready', True ):
|
if not results.get( 'flags_ready', True ):
|
||||||
return None
|
return None
|
||||||
@ -66,58 +83,95 @@ class Flags( object ):
|
|||||||
self.flags_for_file[ filename ] = sanitized_flags
|
self.flags_for_file[ filename ] = sanitized_flags
|
||||||
return sanitized_flags
|
return sanitized_flags
|
||||||
|
|
||||||
|
def ReloadModule( self, module_file ):
|
||||||
def _FlagsModuleForFile( self, filename ):
|
"""Reloads a module file cleaning the flags cache for all files
|
||||||
try:
|
associated with that module. Returns False if reloading failed
|
||||||
return self.flags_module_for_file[ filename ]
|
(for example due to the model not being loaded in the first place)."""
|
||||||
except KeyError:
|
module_file = os.path.abspath(module_file)
|
||||||
flags_module_file = _FlagsModuleSourceFileForFile( filename )
|
if self.modules.Reload( module_file ):
|
||||||
if not flags_module_file:
|
for filename, module in self.module_for_file.iteritems():
|
||||||
return None
|
if module == module_file:
|
||||||
|
del self.flags_for_file[ filename ]
|
||||||
try:
|
return True
|
||||||
flags_module = self.flags_module_for_flags_module_file[
|
return False
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
class FlagsModules( object ):
|
||||||
|
"""Keeps track of modules.
|
||||||
|
Modules are loaded on-demand and cached in self.modules for quick access."""
|
||||||
|
def __init__( self ):
|
||||||
|
self.modules = {}
|
||||||
|
|
||||||
def _FlagsModuleSourceFileForFile( filename ):
|
def Disable( self, module_file ):
|
||||||
"""For a given filename, finds its nearest YCM_EXTRA_CONF_FILENAME file that
|
"""Disables the loading of a module for the current session."""
|
||||||
will compute the flags necessary to compile the file. If no
|
self.modules[ module_file ] = None
|
||||||
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
|
@staticmethod
|
||||||
parent_folder = os.path.dirname( filename )
|
def ShouldLoad( module_file ):
|
||||||
old_parent_folder = ''
|
"""Checks if a module is safe to be loaded.
|
||||||
|
By default this will ask the user for confirmation."""
|
||||||
|
if module_file == GLOBAL_YCM_EXTRA_CONF_FILE:
|
||||||
|
return True
|
||||||
|
if ( vimsupport.GetBoolValue( 'g:ycm_confirm_extra_conf' ) and
|
||||||
|
not vimsupport.Confirm(
|
||||||
|
CONFIRM_CONF_FILE_MESSAGE.format( module_file ) ) ):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
while True:
|
def Load( self, module_file, force = False ):
|
||||||
current_file = os.path.join( parent_folder, YCM_EXTRA_CONF_FILENAME )
|
"""Load and return the module contained in a file.
|
||||||
if os.path.exists( current_file ):
|
Using force = True the module will be loaded regardless
|
||||||
ycm_conf_file = current_file
|
of the criteria in ShouldLoad.
|
||||||
break
|
This will return None if the module was not allowed to be loaded."""
|
||||||
|
if not force:
|
||||||
|
if self.modules.has_key( module_file ):
|
||||||
|
return self.modules[ module_file ]
|
||||||
|
|
||||||
old_parent_folder = parent_folder
|
if not self.ShouldLoad( module_file ):
|
||||||
parent_folder = os.path.dirname( parent_folder )
|
return self.Disable( module_file )
|
||||||
if parent_folder is old_parent_folder:
|
|
||||||
break
|
|
||||||
|
|
||||||
if ( not ycm_conf_file and GLOBAL_YCM_EXTRA_CONF_FILE and
|
sys.path.insert( 0, _DirectoryOfThisScript() )
|
||||||
os.path.exists( GLOBAL_YCM_EXTRA_CONF_FILE ) ):
|
module = imp.load_source( _RandomName(), module_file )
|
||||||
ycm_conf_file = GLOBAL_YCM_EXTRA_CONF_FILE
|
del sys.path[ 0 ]
|
||||||
|
|
||||||
return ycm_conf_file
|
self.modules[ module_file ] = module
|
||||||
|
return module
|
||||||
|
|
||||||
|
def Reload( self, module_file ):
|
||||||
|
"""Reloads the given module. If it has not been loaded yet does nothing.
|
||||||
|
Note that the module will not be subject to the loading criteria again."""
|
||||||
|
if self.modules.get( module_file ):
|
||||||
|
return self.Load( module_file, force = True )
|
||||||
|
|
||||||
|
def __getitem__( self, key ):
|
||||||
|
return self.Load( key )
|
||||||
|
|
||||||
|
|
||||||
|
def _FlagsModuleSourceFilesForFile( filename ):
|
||||||
|
"""For a given filename, search all parent folders for YCM_EXTRA_CONF_FILENAME
|
||||||
|
files that will compute the flags necessary to compile the file.
|
||||||
|
If GLOBAL_YCM_EXTRA_CONF_FILE exists it is returned as a fallback."""
|
||||||
|
for folder in _PathsToAllParentFolders( filename ):
|
||||||
|
candidate = os.path.join( folder, YCM_EXTRA_CONF_FILENAME )
|
||||||
|
if os.path.exists( candidate ):
|
||||||
|
yield candidate
|
||||||
|
if ( GLOBAL_YCM_EXTRA_CONF_FILE
|
||||||
|
and os.path.exists( GLOBAL_YCM_EXTRA_CONF_FILE ) ):
|
||||||
|
yield GLOBAL_YCM_EXTRA_CONF_FILE
|
||||||
|
|
||||||
|
|
||||||
|
def _PathsToAllParentFolders( filename ):
|
||||||
|
"""Build a list of all parent folders of a file.
|
||||||
|
The neares files will be returned first.
|
||||||
|
Example: _PathsToAllParentFolders( '/home/user/projects/test.c' )
|
||||||
|
[ '/home/user/projects', '/home/user', '/home', '/' ]"""
|
||||||
|
parent_folders = os.path.abspath(
|
||||||
|
os.path.dirname( filename ) ).split( os.path.sep )
|
||||||
|
if not parent_folders[0]:
|
||||||
|
parent_folders[0] = os.path.sep
|
||||||
|
parent_folders = [ os.path.join( *parent_folders[:i + 1] )
|
||||||
|
for i in xrange( len( parent_folders ) ) ]
|
||||||
|
return reversed( parent_folders )
|
||||||
|
|
||||||
|
|
||||||
def _RandomName():
|
def _RandomName():
|
||||||
|
@ -44,7 +44,7 @@ def CurrentColumn():
|
|||||||
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 )
|
||||||
return bool( int( vim.eval( to_eval ) ) )
|
return GetBoolValue( to_eval )
|
||||||
|
|
||||||
return ( x for x in vim.buffers if BufferModified( x.number ) )
|
return ( x for x in vim.buffers if BufferModified( x.number ) )
|
||||||
|
|
||||||
@ -59,6 +59,34 @@ def PostVimMessage( message ):
|
|||||||
.format( EscapeForVim( message ) ) )
|
.format( EscapeForVim( message ) ) )
|
||||||
|
|
||||||
|
|
||||||
|
def PresentDialog( message, choices, default_choice_index = 0 ):
|
||||||
|
"""Presents the user with a dialog where a choice can be made.
|
||||||
|
This will be a dialog for gvim users or a question in the message buffer
|
||||||
|
for vim users or if `set guioptions+=c` was used.
|
||||||
|
|
||||||
|
choices is list of alternatives.
|
||||||
|
default_choice_index is the 0-based index of the default element
|
||||||
|
that will get choosen if the user hits <CR>. Use -1 for no default.
|
||||||
|
|
||||||
|
PresentDialog will return a 0-based index into the list
|
||||||
|
or -1 if the dialog was dismissed by using <Esc>, Ctrl-C, etc.
|
||||||
|
|
||||||
|
See also:
|
||||||
|
:help confirm() in vim (Note that vim uses 1-based indexes)
|
||||||
|
|
||||||
|
Example call:
|
||||||
|
PresentDialog("Is this a nice example?", ["Yes", "No", "May&be"])
|
||||||
|
Is this a nice example?
|
||||||
|
[Y]es, (N)o, May(b)e:"""
|
||||||
|
to_eval = "confirm('{0}', '{1}', {2})".format( EscapeForVim( message ),
|
||||||
|
EscapeForVim( "\n" .join( choices ) ), default_choice_index + 1 )
|
||||||
|
return int( vim.eval( to_eval ) ) - 1
|
||||||
|
|
||||||
|
|
||||||
|
def Confirm( message ):
|
||||||
|
return bool( PresentDialog( message, [ "Ok", "Cancel" ] ) == 0 )
|
||||||
|
|
||||||
|
|
||||||
def EchoText( text ):
|
def EchoText( text ):
|
||||||
def EchoLine( text ):
|
def EchoLine( text ):
|
||||||
vim.command( "echom '{0}'".format( EscapeForVim( text ) ) )
|
vim.command( "echom '{0}'".format( EscapeForVim( text ) ) )
|
||||||
@ -78,3 +106,7 @@ def CurrentFiletypes():
|
|||||||
|
|
||||||
def GetVariableValue( variable ):
|
def GetVariableValue( variable ):
|
||||||
return vim.eval( variable )
|
return vim.eval( variable )
|
||||||
|
|
||||||
|
|
||||||
|
def GetBoolValue( variable ):
|
||||||
|
return bool( int( vim.eval( variable ) ) )
|
||||||
|
Loading…
Reference in New Issue
Block a user