Now possible to sent Vim data to FlagsForFile func

Fixes #612.
This commit is contained in:
Strahinja Val Markovic 2013-10-26 16:22:43 -07:00
parent dda1a83c09
commit 6e782508b3
12 changed files with 147 additions and 20 deletions

View File

@ -851,6 +851,31 @@ Default: `0`
let g:ycm_seed_identifiers_with_syntax = 0
### The `g:ycm_extra_conf_vim_data` option
If you're using semantic completion for C-family files, this option might come
handy; it's a way of sending data from Vim to your `FlagsForFile` function in
your `.ycm_extra_conf.py` file.
This option is supposed to be a list of VimScript expression strings that are
evaluated for every request to the `ycmd` server and then passed to your
`FlagsForFile` function as a `client_data` keyword argument.
For instance, if you set this option to `['v:version']`, your `FlagsForFile`
function will be called like this:
```python
# The '704' value is of course contingent on Vim 7.4; in 7.3 it would be '703'
FlagsForFile(filename, client_data = {'v:version': 704})
```
So the `client_data` parameter is a dictionary mapping Vim expression strings to
their values at the time of the request.
Default: `[]`
let g:ycm_extra_conf_vim_data = []
### The `g:ycm_server_use_vim_stdout` option
By default, the `ycmd` completion server writes logs to logfiles. When this
@ -1162,6 +1187,20 @@ Default: `1`
FAQ
---
### I used to be able to `import vim` in `.ycm_extra_conf.py`, but now can't
YCM was rewritten to use a client-server architecture where most of the logic is
in the `ycmd` server. So the magic `vim` module you could have previously
imported in your `.ycm_extra_conf.py` files doesn't exist anymore.
To be fair, importing the magic `vim` module in extra conf files was never
supported in the first place; it only ever worked by accident and was never a
part of the extra conf API.
But fear not, you should be able to tweak your extra conf files to continue
working by using the `g:ycm_extra_conf_vim_data` option. See the docs on that
option for details.
### I get a linker warning regarding `libpython` on Mac when compiling YCM
If the warning is `ld: warning: path '/usr/lib/libpython2.7.dylib' following -L

View File

@ -43,6 +43,8 @@ flags = [
'-Wno-variadic-macros',
'-fexceptions',
'-DNDEBUG',
# You 100% do NOT need -DUSE_CLANG_COMPLETER in your flags; only the YCM
# source code needs it.
'-DUSE_CLANG_COMPLETER',
# THIS IS IMPORTANT! Without a "-std=<something>" flag, clang won't know which
# language to use when compiling headers. So it will guess. Badly. So C++
@ -128,7 +130,7 @@ def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
return new_flags
def FlagsForFile( filename ):
def FlagsForFile( filename, **kwargs ):
if database:
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
# python list, but a "list-like" StringVec object

View File

@ -112,6 +112,9 @@ let g:ycm_server_keep_logfiles =
let g:ycm_server_idle_suicide_seconds =
\ get( g:, 'ycm_server_idle_suicide_seconds', 43200 )
let g:ycm_extra_conf_vim_data =
\ get( g:, 'ycm_extra_conf_vim_data', [] )
" On-demand loading. Let's use the autoload folder and not slow down vim's
" startup procedure.

View File

@ -25,15 +25,15 @@ from ycm.client.base_request import ( BaseRequest, BuildRequestData,
TIMEOUT_SECONDS = 0.5
class CompletionRequest( BaseRequest ):
def __init__( self, force_semantic = False ):
def __init__( self, extra_data = None ):
super( CompletionRequest, self ).__init__()
self._completion_start_column = base.CompletionStartColumn()
# This field is also used by the omni_completion_request subclass
self.request_data = BuildRequestData( self._completion_start_column )
if force_semantic:
self.request_data[ 'force_semantic' ] = True
if extra_data:
self.request_data.update( extra_data )
def CompletionStartColumn( self ):

View File

@ -247,7 +247,8 @@ class ClangCompleter( Completer ):
if 'compilation_flags' in request_data:
return PrepareFlagsForClang( request_data[ 'compilation_flags' ],
filename )
return self._flags.FlagsForFile( filename )
client_data = request_data.get( 'extra_conf_data', None )
return self._flags.FlagsForFile( filename, client_data = client_data )
def ConvertCompletionData( completion_data ):

View File

@ -19,6 +19,7 @@
import ycm_core
import os
import inspect
from ycm import extra_conf_store
from ycm.utils import ToUtf8IfNeeded
@ -40,7 +41,10 @@ class Flags( object ):
self.no_extra_conf_file_warning_posted = False
def FlagsForFile( self, filename, add_special_clang_flags = True ):
def FlagsForFile( self,
filename,
add_special_clang_flags = True,
client_data = None ):
try:
return self.flags_for_file[ filename ]
except KeyError:
@ -51,7 +55,9 @@ class Flags( object ):
raise RuntimeError( NO_EXTRA_CONF_FILENAME_MESSAGE )
return None
results = module.FlagsForFile( filename )
results = _CallExtraConfFlagsForFile( module,
filename,
client_data )
if not results.get( 'flags_ready', True ):
return None
@ -95,6 +101,15 @@ class Flags( object ):
self.flags_for_file.clear()
def _CallExtraConfFlagsForFile( module, filename, client_data ):
# For the sake of backwards compatibility, we need to first check whether the
# FlagsForFile function in the extra conf module even allows keyword args.
if inspect.getargspec( module.FlagsForFile ).keywords:
return module.FlagsForFile( filename, client_data = client_data )
else:
return module.FlagsForFile( filename )
def PrepareFlagsForClang( flags, filename ):
flags = _RemoveUnusedFlags( flags, filename )
flags = _SanitizeFlags( flags )

View File

@ -18,8 +18,6 @@
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
from nose.tools import eq_
from ycm.test_utils import MockVimModule
vim_mock = MockVimModule()
from .. import flags

View File

@ -24,7 +24,7 @@ import httplib
from .test_utils import Setup, BuildRequest, PathToTestFile
from webtest import TestApp
from nose.tools import eq_, with_setup
from hamcrest import ( assert_that, has_items, has_entry,
from hamcrest import ( assert_that, has_item, has_items, has_entry,
contains_inanyorder )
from ..responses import BuildCompletionData, UnknownExtraConf
from .. import handlers
@ -211,6 +211,28 @@ def GetCompletions_ForceSemantic_Works_test():
CompletionEntryMatcher( 'bool' ) ) )
@with_setup( Setup )
def GetCompletions_ClangCompleter_ClientDataGivenToExtraConf_test():
app = TestApp( handlers.app )
app.post_json( '/load_extra_conf_file',
{ 'filepath': PathToTestFile(
'client_data/.ycm_extra_conf.py' ) } )
filepath = PathToTestFile( 'client_data/main.cpp' )
completion_data = BuildRequest( filepath = filepath,
filetype = 'cpp',
contents = open( filepath ).read(),
line_num = 8,
column_num = 6,
start_column = 6,
extra_conf_data = {
'flags': ['-x', 'c++']
})
results = app.post_json( '/completions', completion_data ).json
assert_that( results, has_item( CompletionEntryMatcher( 'x' ) ) )
@with_setup( Setup )
def GetCompletions_IdentifierCompleter_SyntaxKeywordsAdded_test():
app = TestApp( handlers.app )

View File

@ -0,0 +1,5 @@
def FlagsForFile( filename, **kwargs ):
return {
'flags': kwargs['client_data']['flags'],
'do_cache': True
}

View File

@ -0,0 +1,11 @@
struct Foo {
int x;
};
int main()
{
Foo foo;
// The location after the dot is line 9, col 7
foo.
}

View File

@ -118,6 +118,16 @@ def GetReadOnlyVimGlobals( force_python_objects = False ):
return vim.eval( 'g:' )
def VimExpressionToPythonType( vim_expression ):
result = vim.eval( vim_expression )
if not isinstance( result, basestring ):
return result
try:
return int( result )
except ValueError:
return result
# Both |line| and |column| need to be 1-based
def JumpToLocation( filename, line, column ):
# Add an entry to the jumplist

View File

@ -132,7 +132,12 @@ class YouCompleteMe( object ):
self._omnicomp.ShouldUseNow() ):
self._latest_completion_request = OmniCompletionRequest( self._omnicomp )
else:
self._latest_completion_request = ( CompletionRequest( force_semantic )
extra_data = {}
self._AddExtraConfDataIfNeeded( extra_data )
if force_semantic:
extra_data[ 'force_semantic' ] = True
self._latest_completion_request = ( CompletionRequest( extra_data )
if self._IsServerAlive() else
None )
return self._latest_completion_request
@ -176,11 +181,9 @@ class YouCompleteMe( object ):
self._NotifyUserIfServerCrashed()
extra_data = {}
if self._user_options[ 'collect_identifiers_from_tags_files' ]:
extra_data[ 'tag_files' ] = _GetTagFiles()
if self._user_options[ 'seed_identifiers_with_syntax' ]:
self._AddSyntaxDataIfNeeded( extra_data )
self._AddTagsFilesIfNeeded( extra_data )
self._AddSyntaxDataIfNeeded( extra_data )
self._AddExtraConfDataIfNeeded( extra_data )
self._latest_file_parse_request = EventNotification( 'FileReadyToParse',
extra_data )
@ -280,6 +283,8 @@ class YouCompleteMe( object ):
def _AddSyntaxDataIfNeeded( self, extra_data ):
if not self._user_options[ 'seed_identifiers_with_syntax' ]:
return
filetype = vimsupport.CurrentFiletypes()[ 0 ]
if filetype in self._filetypes_with_keywords_loaded:
return
@ -289,10 +294,26 @@ class YouCompleteMe( object ):
syntax_parse.SyntaxKeywordsForCurrentBuffer() )
def _GetTagFiles():
tag_files = vim.eval( 'tagfiles()' )
current_working_directory = os.getcwd()
return [ os.path.join( current_working_directory, x ) for x in tag_files ]
def _AddTagsFilesIfNeeded( self, extra_data ):
def GetTagFiles():
tag_files = vim.eval( 'tagfiles()' )
current_working_directory = os.getcwd()
return [ os.path.join( current_working_directory, x ) for x in tag_files ]
if not self._user_options[ 'collect_identifiers_from_tags_files' ]:
return
extra_data[ 'tag_files' ] = GetTagFiles()
def _AddExtraConfDataIfNeeded( self, extra_data ):
def BuildExtraConfData( extra_conf_vim_data ):
return dict( ( expr, vimsupport.VimExpressionToPythonType( expr ) )
for expr in extra_conf_vim_data )
extra_conf_vim_data = self._user_options[ 'extra_conf_vim_data' ]
if extra_conf_vim_data:
extra_data[ 'extra_conf_data' ] = BuildExtraConfData(
extra_conf_vim_data )
def _PathToServerScript():