Docs for the Completer API

This commit is contained in:
Strahinja Val Markovic 2013-02-12 20:01:22 -08:00
parent 452f7d1fec
commit 21dac46ecc

View File

@ -24,23 +24,80 @@ import ycm_core
from collections import defaultdict from collections import defaultdict
class CompletionsCache( object ):
def __init__( self ):
self.line = -1
self.column = -1
self.raw_completions = []
self.filtered_completions = []
def CacheValid( self ):
completion_line, _ = vimsupport.CurrentLineAndColumn()
completion_column = int( vim.eval( "s:completion_start_column" ) )
return completion_line == self.line and completion_column == self.column
class Completer( object ): class Completer( object ):
__metaclass__ = abc.ABCMeta """A base class for all Completers in YCM.
Here's several important things you need to know if you're writing a custom
Completer. First, there are several very important functions that the Vim part
of YCM will be calling on your Completer.
ShouldUseNow() is called with the start column of where a potential completion
string should start. For instance, if the user's input is 'foo.bar' and the
cursor is on the 'r' in 'bar', start_column will be the 0-based index of 'b'
in the line. Your implementation of ShouldUseNow() should return True if your
semantic completer should be used and False otherwise.
This is important to get right. You want to return False if you can't provide
completions because then the identifier completer will kick in, and that's
better than nothing.
Note that it's HIGHLY likely that you want to override the ShouldUseNowInner()
function instead of ShouldUseNow() directly. ShouldUseNow() will call your
*Inner version of the function and will also make sure that the completion
cache is taken into account. You'll see this pattern repeated throughout the
Completer API; YCM calls the "main" version of the function and that function
calls the *Inner version while taking into account the cache.
The cache is important and is a nice performance boost. When the user types in
"foo.", your completer will return a list of all member functions and
variables that can be accessed on the "foo" object. The Completer API caches
this list. The user will then continue typing, let's say "foo.ba". On every
keystroke after the dot, the Completer API will take the cache into account
and will NOT re-query your completer but will in fact provide fuzzy-search on
the candidate strings that were stored in the cache.
CandidatesForQueryAsync() is the main entry point when the user types. For
"foo.bar", the user query is "bar" and completions matching this string should
be shown. The job of CandidatesForQueryAsync() is to merely initiate this
request, hopefully in the background with a thread.
AsyncCandidateRequestReady() is the function that is repeatedly polled until
it returns True. If CandidatesForQueryAsync() started a background task of
collecting the required completions, AsyncCandidateRequestReady() would check
the state of that task and return False until it was completed.
CandidatesFormStoredRequest() should return the list of candidates. This is
what YCM calls after AsyncCandidateRequestReady() returns True. The format of
the result can be a list of strings or a more complicated list of
dictionaries. See ':h complete-items' for the format, and clang_completer.py
to see how its used in practice.
You also need to implement the SupportedFiletypes() function which should
return a list of strings, where the strings are Vim filetypes your completer
supports.
clang_completer.py is a good example of a "complicated" completer that
maintains its own internal cache and therefore directly overrides the "main"
functions in the API instead of the *Inner versions. A good example of a
simple completer that does not do this is omni_completer.py.
If you're confident your completer doesn't need a background task (think
again, you probably do) because you can "certainly" furnish a response in
under 10ms, then you can perform your backend processing in a synchronous
fashion. You may also need to do this because of technical restrictions (much
like omni_completer.py has to do it because accessing Vim internals is not
thread-safe). But even if you're certain, still try to do the processing in a
background thread. Your completer is unlikely to be merged if it does not,
because synchronous processing will block Vim's GUI thread and that's a very,
VERY bad thing (so try not to do it!).
The On* functions are provided for your convenience. They are called when
their specific events occur. For instance, the identifier completer collects
all the identifiers in the file in OnFileReadyToParse() which gets called when
the user stops typing for 2 seconds (Vim's CursorHold and CursorHoldI events).
"""
__metaclass__ = abc.ABCMeta
def __init__( self ): def __init__( self ):
self.triggers_for_filetype = TriggersForFiletype() self.triggers_for_filetype = TriggersForFiletype()
@ -48,6 +105,8 @@ class Completer( object ):
self.completions_cache = None self.completions_cache = None
# It's highly likely you DON'T want to override this function but the *Inner
# version of it.
def ShouldUseNow( self, start_column ): def ShouldUseNow( self, start_column ):
inner_says_yes = self.ShouldUseNowInner( start_column ) inner_says_yes = self.ShouldUseNowInner( start_column )
if not inner_says_yes: if not inner_says_yes:
@ -82,6 +141,8 @@ class Completer( object ):
return False return False
# It's highly likely you DON'T want to override this function but the *Inner
# version of it.
def CandidatesForQueryAsync( self, query ): def CandidatesForQueryAsync( self, query ):
if query and self.completions_cache and self.completions_cache.CacheValid(): if query and self.completions_cache and self.completions_cache.CacheValid():
self.completions_cache.filtered_completions = ( self.completions_cache.filtered_completions = (
@ -111,6 +172,8 @@ class Completer( object ):
pass pass
# It's highly likely you DON'T want to override this function but the *Inner
# version of it.
def AsyncCandidateRequestReady( self ): def AsyncCandidateRequestReady( self ):
if self.completions_cache: if self.completions_cache:
return True return True
@ -126,6 +189,8 @@ class Completer( object ):
return self.completions_future.ResultsReady() return self.completions_future.ResultsReady()
# It's highly likely you DON'T want to override this function but the *Inner
# version of it.
def CandidatesFromStoredRequest( self ): def CandidatesFromStoredRequest( self ):
if self.completions_cache: if self.completions_cache:
return self.completions_cache.filtered_completions return self.completions_cache.filtered_completions
@ -196,6 +261,20 @@ class Completer( object ):
return '' return ''
class CompletionsCache( object ):
def __init__( self ):
self.line = -1
self.column = -1
self.raw_completions = []
self.filtered_completions = []
def CacheValid( self ):
completion_line, _ = vimsupport.CurrentLineAndColumn()
completion_column = int( vim.eval( "s:completion_start_column" ) )
return completion_line == self.line and completion_column == self.column
def TriggersForFiletype(): def TriggersForFiletype():
triggers = vim.eval( 'g:ycm_semantic_triggers' ) triggers = vim.eval( 'g:ycm_semantic_triggers' )
triggers_for_filetype = defaultdict( list ) triggers_for_filetype = defaultdict( list )