Decoupling completers from Vim; still WIP & broken
Note to self: squash this commit before merging into master.
This commit is contained in:
parent
bd374a7096
commit
29bb90a6b4
@ -41,6 +41,7 @@ function! youcompleteme#Enable()
|
||||
py import vim
|
||||
exe 'python sys.path.insert( 0, "' . s:script_folder_path . '/../python" )'
|
||||
py from ycm import base
|
||||
py from ycm import vimsupport
|
||||
py from ycm import user_options_store
|
||||
py user_options_store.SetAll( base.BuildServerConf() )
|
||||
py from ycm import extra_conf_store
|
||||
@ -260,7 +261,7 @@ function! s:OnCursorHold()
|
||||
call s:SetUpCompleteopt()
|
||||
" Order is important here; we need to extract any done diagnostics before
|
||||
" reparsing the file again
|
||||
call s:UpdateDiagnosticNotifications()
|
||||
" call s:UpdateDiagnosticNotifications()
|
||||
call s:OnFileReadyToParse()
|
||||
endfunction
|
||||
|
||||
@ -327,7 +328,7 @@ function! s:OnCursorMovedNormalMode()
|
||||
return
|
||||
endif
|
||||
|
||||
call s:UpdateDiagnosticNotifications()
|
||||
" call s:UpdateDiagnosticNotifications()
|
||||
call s:OnFileReadyToParse()
|
||||
endfunction
|
||||
|
||||
@ -338,7 +339,7 @@ function! s:OnInsertLeave()
|
||||
endif
|
||||
|
||||
let s:omnifunc_mode = 0
|
||||
call s:UpdateDiagnosticNotifications()
|
||||
" call s:UpdateDiagnosticNotifications()
|
||||
call s:OnFileReadyToParse()
|
||||
py ycm_state.OnInsertLeave()
|
||||
if g:ycm_autoclose_preview_window_after_completion ||
|
||||
@ -572,7 +573,9 @@ command! YcmShowDetailedDiagnostic call s:ShowDetailedDiagnostic()
|
||||
" required (currently that's on buffer save) OR when the SyntasticCheck command
|
||||
" is invoked
|
||||
function! youcompleteme#CurrentFileDiagnostics()
|
||||
return pyeval( 'ycm_state.GetDiagnosticsForCurrentFile()' )
|
||||
" TODO: Make this work again.
|
||||
" return pyeval( 'ycm_state.GetDiagnosticsForCurrentFile()' )
|
||||
return []
|
||||
endfunction
|
||||
|
||||
|
||||
@ -595,28 +598,24 @@ function! s:CompleterCommand(...)
|
||||
" to select the omni completer or "ft=ycm:ident" to select the identifier
|
||||
" completer. The remaining arguments will passed to the completer.
|
||||
let arguments = copy(a:000)
|
||||
let completer = ''
|
||||
|
||||
if a:0 > 0 && strpart(a:1, 0, 3) == 'ft='
|
||||
if a:1 == 'ft=ycm:omni'
|
||||
py completer = ycm_state.GetOmniCompleter()
|
||||
let completer = 'omni'
|
||||
elseif a:1 == 'ft=ycm:ident'
|
||||
py completer = ycm_state.GetGeneralCompleter()
|
||||
else
|
||||
py completer = ycm_state.GetFiletypeCompleterForFiletype(
|
||||
\ vim.eval('a:1').lstrip('ft=') )
|
||||
let completer = 'identifier'
|
||||
endif
|
||||
let arguments = arguments[1:]
|
||||
elseif pyeval( 'ycm_state.NativeFiletypeCompletionAvailable()' )
|
||||
py completer = ycm_state.GetFiletypeCompleter()
|
||||
else
|
||||
echohl WarningMsg |
|
||||
\ echomsg "No native completer found for current buffer." |
|
||||
\ echomsg "Use ft=... as the first argument to specify a completer." |
|
||||
\ echohl None
|
||||
return
|
||||
endif
|
||||
|
||||
py completer.OnUserCommand( vim.eval( 'l:arguments' ) )
|
||||
py << EOF
|
||||
response = ycm_state.SendCommandRequest( vim.eval( 'l:arguments' ),
|
||||
vim.eval( 'l:completer' ) )
|
||||
if not response.Valid():
|
||||
vimsupport.PostVimMessage( 'No native completer found for current buffer. ' +
|
||||
'Use ft=... as the first argument to specify a completer.')
|
||||
EOF
|
||||
endfunction
|
||||
|
||||
|
||||
|
@ -103,7 +103,7 @@ void ClangCompleter::EnableThreading() {
|
||||
|
||||
|
||||
std::vector< Diagnostic > ClangCompleter::DiagnosticsForFile(
|
||||
const std::string &filename ) {
|
||||
std::string filename ) {
|
||||
shared_ptr< TranslationUnit > unit = translation_unit_store_.Get( filename );
|
||||
|
||||
if ( !unit )
|
||||
@ -127,9 +127,9 @@ bool ClangCompleter::UpdatingTranslationUnit( const std::string &filename ) {
|
||||
|
||||
|
||||
void ClangCompleter::UpdateTranslationUnit(
|
||||
const std::string &filename,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags ) {
|
||||
std::string filename,
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags ) {
|
||||
bool translation_unit_created;
|
||||
shared_ptr< TranslationUnit > unit = translation_unit_store_.GetOrCreate(
|
||||
filename,
|
||||
@ -182,11 +182,11 @@ Future< void > ClangCompleter::UpdateTranslationUnitAsync(
|
||||
|
||||
std::vector< CompletionData >
|
||||
ClangCompleter::CandidatesForLocationInFile(
|
||||
const std::string &filename,
|
||||
std::string filename,
|
||||
int line,
|
||||
int column,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags ) {
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags ) {
|
||||
shared_ptr< TranslationUnit > unit =
|
||||
translation_unit_store_.GetOrCreate( filename, unsaved_files, flags );
|
||||
|
||||
@ -201,12 +201,12 @@ ClangCompleter::CandidatesForLocationInFile(
|
||||
|
||||
Future< AsyncCompletions >
|
||||
ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
|
||||
const std::string &query,
|
||||
const std::string &filename,
|
||||
std::string query,
|
||||
std::string filename,
|
||||
int line,
|
||||
int column,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags ) {
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags ) {
|
||||
// TODO: throw exception when threading is not enabled and this is called
|
||||
if ( !threading_enabled_ )
|
||||
return Future< AsyncCompletions >();
|
||||
@ -238,7 +238,11 @@ ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
|
||||
CreateSortingTask( query, future );
|
||||
|
||||
if ( skip_clang_result_cache ) {
|
||||
CreateClangTask( filename, line, column, unsaved_files, flags );
|
||||
CreateClangTask( boost::move( filename ),
|
||||
line,
|
||||
column,
|
||||
boost::move( unsaved_files ),
|
||||
boost::move( flags ) );
|
||||
}
|
||||
|
||||
return Future< AsyncCompletions >( boost::move( future ) );
|
||||
@ -246,11 +250,11 @@ ClangCompleter::CandidatesForQueryAndLocationInFileAsync(
|
||||
|
||||
|
||||
Location ClangCompleter::GetDeclarationLocation(
|
||||
const std::string &filename,
|
||||
std::string filename,
|
||||
int line,
|
||||
int column,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags ) {
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags ) {
|
||||
shared_ptr< TranslationUnit > unit =
|
||||
translation_unit_store_.GetOrCreate( filename, unsaved_files, flags );
|
||||
|
||||
@ -263,11 +267,11 @@ Location ClangCompleter::GetDeclarationLocation(
|
||||
|
||||
|
||||
Location ClangCompleter::GetDefinitionLocation(
|
||||
const std::string &filename,
|
||||
std::string filename,
|
||||
int line,
|
||||
int column,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags ) {
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags ) {
|
||||
shared_ptr< TranslationUnit > unit =
|
||||
translation_unit_store_.GetOrCreate( filename, unsaved_files, flags );
|
||||
|
||||
@ -279,7 +283,7 @@ Location ClangCompleter::GetDefinitionLocation(
|
||||
}
|
||||
|
||||
|
||||
void ClangCompleter::DeleteCachesForFileAsync( const std::string &filename ) {
|
||||
void ClangCompleter::DeleteCachesForFileAsync( std::string filename ) {
|
||||
file_cache_delete_stack_.Push( filename );
|
||||
}
|
||||
|
||||
|
@ -59,18 +59,23 @@ public:
|
||||
|
||||
void EnableThreading();
|
||||
|
||||
std::vector< Diagnostic > DiagnosticsForFile( const std::string &filename );
|
||||
std::vector< Diagnostic > DiagnosticsForFile( std::string filename );
|
||||
|
||||
bool UpdatingTranslationUnit( const std::string &filename );
|
||||
|
||||
// NOTE: params are taken by value on purpose! With a C++11 compiler we can
|
||||
// avoid internal copies if params are taken by value (move ctors FTW), and we
|
||||
// need to ensure we own the memory.
|
||||
// TODO: Change some of these params back to const ref where possible after we
|
||||
// get the server up.
|
||||
// TODO: Remove the async methods and the threads when the server is ready.
|
||||
|
||||
// Public because of unit tests (gtest is not very thread-friendly)
|
||||
void UpdateTranslationUnit(
|
||||
const std::string &filename,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags );
|
||||
std::string filename,
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags );
|
||||
|
||||
// NOTE: params are taken by value on purpose! With a C++11 compiler we can
|
||||
// avoid internal copies if params are taken by value (move ctors FTW)
|
||||
Future< void > UpdateTranslationUnitAsync(
|
||||
std::string filename,
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
@ -78,35 +83,35 @@ public:
|
||||
|
||||
// Public because of unit tests (gtest is not very thread-friendly)
|
||||
std::vector< CompletionData > CandidatesForLocationInFile(
|
||||
const std::string &filename,
|
||||
std::string filename,
|
||||
int line,
|
||||
int column,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags );
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags );
|
||||
|
||||
Future< AsyncCompletions > CandidatesForQueryAndLocationInFileAsync(
|
||||
const std::string &query,
|
||||
const std::string &filename,
|
||||
std::string query,
|
||||
std::string filename,
|
||||
int line,
|
||||
int column,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags );
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags );
|
||||
|
||||
Location GetDeclarationLocation(
|
||||
const std::string &filename,
|
||||
std::string filename,
|
||||
int line,
|
||||
int column,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags );
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags );
|
||||
|
||||
Location GetDefinitionLocation(
|
||||
const std::string &filename,
|
||||
std::string filename,
|
||||
int line,
|
||||
int column,
|
||||
const std::vector< UnsavedFile > &unsaved_files,
|
||||
const std::vector< std::string > &flags );
|
||||
std::vector< UnsavedFile > unsaved_files,
|
||||
std::vector< std::string > flags );
|
||||
|
||||
void DeleteCachesForFileAsync( const std::string &filename );
|
||||
void DeleteCachesForFileAsync( std::string filename );
|
||||
|
||||
private:
|
||||
|
||||
|
@ -143,8 +143,8 @@ let g:ycm_extra_conf_globlist =
|
||||
let g:ycm_filepath_completion_use_working_dir =
|
||||
\ get( g:, 'ycm_filepath_completion_use_working_dir', 0 )
|
||||
|
||||
" Default semantic triggers are in python/ycm/completers/completer.py, these
|
||||
" just append new triggers to the default dict.
|
||||
" Default semantic triggers are in python/ycm/completers/completer_utils.py
|
||||
" these just append new triggers to the default dict.
|
||||
let g:ycm_semantic_triggers =
|
||||
\ get( g:, 'ycm_semantic_triggers', {} )
|
||||
|
||||
|
@ -25,6 +25,7 @@ from ycm.completers.general_completer import GeneralCompleter
|
||||
from ycm.completers.general import syntax_parse
|
||||
from ycm import vimsupport
|
||||
from ycm import utils
|
||||
from ycm import server_responses
|
||||
|
||||
MAX_IDENTIFIER_COMPLETIONS_RETURNED = 10
|
||||
SYNTAX_FILENAME = 'YCM_PLACEHOLDER_FOR_SYNTAX'
|
||||
@ -39,15 +40,14 @@ class IdentifierCompleter( GeneralCompleter ):
|
||||
self.filetypes_with_keywords_loaded = set()
|
||||
|
||||
|
||||
def ShouldUseNow( self, start_column, unused_current_line ):
|
||||
return self.QueryLengthAboveMinThreshold( start_column )
|
||||
def ShouldUseNow( self, request_data ):
|
||||
return self.QueryLengthAboveMinThreshold( request_data )
|
||||
|
||||
|
||||
def CandidatesForQueryAsync( self, query, unused_start_column ):
|
||||
filetype = vim.eval( "&filetype" )
|
||||
def CandidatesForQueryAsync( self, request_data ):
|
||||
self.completions_future = self.completer.CandidatesForQueryAndTypeAsync(
|
||||
utils.SanitizeQuery( query ),
|
||||
filetype )
|
||||
utils.SanitizeQuery( request_data[ 'query' ] ),
|
||||
request_data[ 'filetypes' ][ 0 ] )
|
||||
|
||||
|
||||
def AddIdentifier( self, identifier ):
|
||||
@ -83,17 +83,16 @@ class IdentifierCompleter( GeneralCompleter ):
|
||||
self.AddIdentifier( stripped_cursor_identifier )
|
||||
|
||||
|
||||
def AddBufferIdentifiers( self ):
|
||||
# TODO: use vimsupport.GetFiletypes; also elsewhere in file
|
||||
filetype = vim.eval( "&filetype" )
|
||||
filepath = vim.eval( "expand('%:p')" )
|
||||
def AddBufferIdentifiers( self, request_data ):
|
||||
filetype = request_data[ 'filetypes' ][ 0 ]
|
||||
filepath = request_data[ 'filepath' ]
|
||||
collect_from_comments_and_strings = bool( self.user_options[
|
||||
'collect_identifiers_from_comments_and_strings' ] )
|
||||
|
||||
if not filetype or not filepath:
|
||||
return
|
||||
|
||||
text = "\n".join( vim.current.buffer )
|
||||
text = request_data[ 'file_data' ][ filepath ][ 'contents' ]
|
||||
self.completer.AddIdentifiersToDatabaseFromBufferAsync(
|
||||
text,
|
||||
filetype,
|
||||
@ -147,14 +146,15 @@ class IdentifierCompleter( GeneralCompleter ):
|
||||
filepath )
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
self.AddBufferIdentifiers()
|
||||
def OnFileReadyToParse( self, request_data ):
|
||||
self.AddBufferIdentifiers( request_data )
|
||||
|
||||
if self.user_options[ 'collect_identifiers_from_tags_files' ]:
|
||||
self.AddIdentifiersFromTagFiles()
|
||||
# TODO: make these work again
|
||||
# if self.user_options[ 'collect_identifiers_from_tags_files' ]:
|
||||
# self.AddIdentifiersFromTagFiles()
|
||||
|
||||
if self.user_options[ 'seed_identifiers_with_syntax' ]:
|
||||
self.AddIdentifiersFromSyntax()
|
||||
# if self.user_options[ 'seed_identifiers_with_syntax' ]:
|
||||
# self.AddIdentifiersFromSyntax()
|
||||
|
||||
|
||||
def OnInsertLeave( self ):
|
||||
@ -174,11 +174,7 @@ class IdentifierCompleter( GeneralCompleter ):
|
||||
completions = _RemoveSmallCandidates(
|
||||
completions, self.user_options[ 'min_num_identifier_candidate_chars' ] )
|
||||
|
||||
# We will never have duplicates in completions so with 'dup':1 we tell Vim
|
||||
# to add this candidate even if it's a duplicate of an existing one (which
|
||||
# will never happen). This saves us some expensive string matching
|
||||
# operations in Vim.
|
||||
return [ { 'word': x, 'dup': 1 } for x in completions ]
|
||||
return [ server_responses.BuildCompletionData( x ) for x in completions ]
|
||||
|
||||
|
||||
def _PreviousIdentifier( min_num_completion_start_chars ):
|
||||
|
@ -40,29 +40,27 @@ class OmniCompleter( Completer ):
|
||||
return bool( self.user_options[ 'cache_omnifunc' ] )
|
||||
|
||||
|
||||
def ShouldUseNow( self, start_column, current_line ):
|
||||
def ShouldUseNow( self, request_data ):
|
||||
if self.ShouldUseCache():
|
||||
return super( OmniCompleter, self ).ShouldUseNow( start_column,
|
||||
current_line )
|
||||
return self.ShouldUseNowInner( start_column, current_line )
|
||||
return super( OmniCompleter, self ).ShouldUseNow( request_data )
|
||||
return self.ShouldUseNowInner( request_data )
|
||||
|
||||
|
||||
def ShouldUseNowInner( self, start_column, current_line ):
|
||||
def ShouldUseNowInner( self, request_data ):
|
||||
if not self.omnifunc:
|
||||
return False
|
||||
return super( OmniCompleter, self ).ShouldUseNowInner( start_column,
|
||||
current_line )
|
||||
return super( OmniCompleter, self ).ShouldUseNowInner( request_data )
|
||||
|
||||
|
||||
def CandidatesForQueryAsync( self, query, unused_start_column ):
|
||||
def CandidatesForQueryAsync( self, request_data ):
|
||||
if self.ShouldUseCache():
|
||||
return super( OmniCompleter, self ).CandidatesForQueryAsync(
|
||||
query, unused_start_column )
|
||||
request_data )
|
||||
else:
|
||||
return self.CandidatesForQueryAsyncInner( query, unused_start_column )
|
||||
return self.CandidatesForQueryAsyncInner( request_data )
|
||||
|
||||
|
||||
def CandidatesForQueryAsyncInner( self, query, unused_start_column ):
|
||||
def CandidatesForQueryAsyncInner( self, request_data ):
|
||||
if not self.omnifunc:
|
||||
self.stored_candidates = None
|
||||
return
|
||||
@ -75,7 +73,7 @@ class OmniCompleter( Completer ):
|
||||
|
||||
omnifunc_call = [ self.omnifunc,
|
||||
"(0,'",
|
||||
vimsupport.EscapeForVim( query ),
|
||||
vimsupport.EscapeForVim( request_data[ 'query' ] ),
|
||||
"')" ]
|
||||
|
||||
items = vim.eval( ''.join( omnifunc_call ) )
|
||||
@ -98,7 +96,7 @@ class OmniCompleter( Completer ):
|
||||
return True
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
def OnFileReadyToParse( self, request_data ):
|
||||
self.omnifunc = vim.eval( '&omnifunc' )
|
||||
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
import abc
|
||||
import ycm_core
|
||||
from ycm import vimsupport
|
||||
from ycm.completers.completer_utils import TriggersForFiletype
|
||||
|
||||
NO_USER_COMMANDS = 'This completer does not define any commands.'
|
||||
@ -116,32 +115,35 @@ class Completer( object ):
|
||||
def __init__( self, user_options ):
|
||||
self.user_options = user_options
|
||||
self.min_num_chars = user_options[ 'min_num_of_chars_for_completion' ]
|
||||
self.triggers_for_filetype = TriggersForFiletype()
|
||||
self.triggers_for_filetype = TriggersForFiletype(
|
||||
user_options[ 'semantic_triggers' ] )
|
||||
self.completions_future = None
|
||||
self.completions_cache = None
|
||||
self.completion_start_column = None
|
||||
|
||||
|
||||
# It's highly likely you DON'T want to override this function but the *Inner
|
||||
# version of it.
|
||||
def ShouldUseNow( self, start_column, current_line ):
|
||||
inner_says_yes = self.ShouldUseNowInner( start_column, current_line )
|
||||
def ShouldUseNow( self, request_data ):
|
||||
inner_says_yes = self.ShouldUseNowInner( request_data )
|
||||
if not inner_says_yes:
|
||||
self.completions_cache = None
|
||||
|
||||
previous_results_were_empty = ( self.completions_cache and
|
||||
self.completions_cache.CacheValid(
|
||||
start_column ) and
|
||||
request_data[ 'line_num' ],
|
||||
request_data[ 'start_column' ] ) and
|
||||
not self.completions_cache.raw_completions )
|
||||
return inner_says_yes and not previous_results_were_empty
|
||||
|
||||
|
||||
def ShouldUseNowInner( self, start_column, current_line ):
|
||||
def ShouldUseNowInner( self, request_data ):
|
||||
current_line = request_data[ 'line_value' ]
|
||||
start_column = request_data[ 'start_column' ]
|
||||
line_length = len( current_line )
|
||||
if not line_length or start_column - 1 >= line_length:
|
||||
return False
|
||||
|
||||
filetype = self._CurrentFiletype()
|
||||
filetype = self._CurrentFiletype( request_data[ 'filetypes' ] )
|
||||
triggers = self.triggers_for_filetype[ filetype ]
|
||||
|
||||
for trigger in triggers:
|
||||
@ -158,52 +160,61 @@ class Completer( object ):
|
||||
return False
|
||||
|
||||
|
||||
def QueryLengthAboveMinThreshold( self, start_column ):
|
||||
query_length = vimsupport.CurrentColumn() - start_column
|
||||
def QueryLengthAboveMinThreshold( self, request_data ):
|
||||
query_length = request_data[ 'column_num' ] - request_data[ 'start_column' ]
|
||||
return query_length >= self.min_num_chars
|
||||
|
||||
|
||||
# It's highly likely you DON'T want to override this function but the *Inner
|
||||
# version of it.
|
||||
def CandidatesForQueryAsync( self, query, start_column ):
|
||||
self.completion_start_column = start_column
|
||||
def CandidatesForQueryAsync( self, request_data ):
|
||||
self.request_data = request_data
|
||||
|
||||
if query and self.completions_cache and self.completions_cache.CacheValid(
|
||||
start_column ):
|
||||
if ( request_data[ 'query' ] and
|
||||
self.completions_cache and
|
||||
self.completions_cache.CacheValid( request_data[ 'line_num' ],
|
||||
request_data[ 'start_column' ] ) ):
|
||||
self.completions_cache.filtered_completions = (
|
||||
self.FilterAndSortCandidates(
|
||||
self.completions_cache.raw_completions,
|
||||
query ) )
|
||||
request_data[ 'query' ] ) )
|
||||
else:
|
||||
self.completions_cache = None
|
||||
self.CandidatesForQueryAsyncInner( query, start_column )
|
||||
self.CandidatesForQueryAsyncInner( request_data )
|
||||
|
||||
|
||||
def DefinedSubcommands( self ):
|
||||
return []
|
||||
|
||||
|
||||
def EchoUserCommandsHelpMessage( self ):
|
||||
def UserCommandsHelpMessage( self ):
|
||||
subcommands = self.DefinedSubcommands()
|
||||
if subcommands:
|
||||
vimsupport.EchoText( 'Supported commands are:\n' +
|
||||
'\n'.join( subcommands ) +
|
||||
'\nSee the docs for information on what they do.' )
|
||||
return ( 'Supported commands are:\n' +
|
||||
'\n'.join( subcommands ) +
|
||||
'\nSee the docs for information on what they do.' )
|
||||
else:
|
||||
vimsupport.EchoText( 'No supported subcommands' )
|
||||
return 'No supported subcommands'
|
||||
|
||||
|
||||
def FilterAndSortCandidates( self, candidates, query ):
|
||||
if not candidates:
|
||||
return []
|
||||
|
||||
if hasattr( candidates, 'words' ):
|
||||
candidates = candidates.words
|
||||
items_are_objects = 'word' in candidates[ 0 ]
|
||||
# We need to handle both an omni_completer style completer and a server
|
||||
# style completer
|
||||
if 'words' in candidates:
|
||||
candidates = candidates[ 'words' ]
|
||||
|
||||
sort_property = ''
|
||||
if 'word' in candidates[ 0 ]:
|
||||
sort_property = 'word'
|
||||
elif 'insertion_text' in candidates[ 0 ]:
|
||||
sort_property = 'insertion_text'
|
||||
|
||||
matches = ycm_core.FilterAndSortCandidates(
|
||||
candidates,
|
||||
'word' if items_are_objects else '',
|
||||
sort_property,
|
||||
query )
|
||||
|
||||
return matches
|
||||
@ -238,8 +249,8 @@ class Completer( object ):
|
||||
else:
|
||||
self.completions_cache = CompletionsCache()
|
||||
self.completions_cache.raw_completions = self.CandidatesFromStoredRequestInner()
|
||||
self.completions_cache.line, _ = vimsupport.CurrentLineAndColumn()
|
||||
self.completions_cache.column = self.completion_start_column
|
||||
self.completions_cache.line = self.request_data[ 'line_num' ]
|
||||
self.completions_cache.column = self.request_data[ 'start_column' ]
|
||||
return self.completions_cache.raw_completions
|
||||
|
||||
|
||||
@ -249,7 +260,7 @@ class Completer( object ):
|
||||
return self.completions_future.GetResults()
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
def OnFileReadyToParse( self, request_data ):
|
||||
pass
|
||||
|
||||
|
||||
@ -269,8 +280,8 @@ class Completer( object ):
|
||||
pass
|
||||
|
||||
|
||||
def OnUserCommand( self, arguments ):
|
||||
vimsupport.PostVimMessage( NO_USER_COMMANDS )
|
||||
def OnUserCommand( self, arguments, request_data ):
|
||||
raise NotImplementedError( NO_USER_COMMANDS )
|
||||
|
||||
|
||||
def OnCurrentIdentifierFinished( self ):
|
||||
@ -285,7 +296,7 @@ class Completer( object ):
|
||||
return []
|
||||
|
||||
|
||||
def ShowDetailedDiagnostic( self ):
|
||||
def GetDetailedDiagnostic( self ):
|
||||
pass
|
||||
|
||||
|
||||
@ -293,8 +304,7 @@ class Completer( object ):
|
||||
return False
|
||||
|
||||
|
||||
def _CurrentFiletype( self ):
|
||||
filetypes = vimsupport.CurrentFiletypes()
|
||||
def _CurrentFiletype( self, filetypes ):
|
||||
supported = self.SupportedFiletypes()
|
||||
|
||||
for filetype in filetypes:
|
||||
@ -321,9 +331,7 @@ class CompletionsCache( object ):
|
||||
self.filtered_completions = []
|
||||
|
||||
|
||||
def CacheValid( self, start_column ):
|
||||
completion_line, _ = vimsupport.CurrentLineAndColumn()
|
||||
completion_column = start_column
|
||||
return completion_line == self.line and completion_column == self.column
|
||||
def CacheValid( self, current_line, start_column ):
|
||||
return current_line == self.line and start_column == self.column
|
||||
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
from collections import defaultdict
|
||||
from copy import deepcopy
|
||||
import vim
|
||||
|
||||
DEFAULT_FILETYPE_TRIGGERS = {
|
||||
'c' : ['->', '.'],
|
||||
@ -58,12 +57,9 @@ def _FiletypeDictUnion( dict_one, dict_two ):
|
||||
return final_dict
|
||||
|
||||
|
||||
def TriggersForFiletype():
|
||||
user_triggers = _FiletypeTriggerDictFromSpec(
|
||||
vim.eval( 'g:ycm_semantic_triggers' ) )
|
||||
|
||||
def TriggersForFiletype( user_triggers ):
|
||||
default_triggers = _FiletypeTriggerDictFromSpec(
|
||||
DEFAULT_FILETYPE_TRIGGERS )
|
||||
|
||||
return _FiletypeDictUnion( default_triggers, user_triggers )
|
||||
return _FiletypeDictUnion( default_triggers, dict( user_triggers ) )
|
||||
|
||||
|
@ -18,14 +18,23 @@
|
||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from collections import defaultdict
|
||||
import vim
|
||||
import ycm_core
|
||||
from ycm import vimsupport
|
||||
import logging
|
||||
from ycm import server_responses
|
||||
from ycm import extra_conf_store
|
||||
from ycm.completers.completer import Completer
|
||||
from ycm.completers.cpp.flags import Flags
|
||||
|
||||
CLANG_FILETYPES = set( [ 'c', 'cpp', 'objc', 'objcpp' ] )
|
||||
MIN_LINES_IN_FILE_TO_PARSE = 5
|
||||
PARSING_FILE_MESSAGE = 'Still parsing file, no completions yet.'
|
||||
NO_COMPILE_FLAGS_MESSAGE = 'Still no compile flags, no completions yet.'
|
||||
NO_COMPLETIONS_MESSAGE = 'No completions found; errors in the file?'
|
||||
INVALID_FILE_MESSAGE = 'File is invalid.'
|
||||
FILE_TOO_SHORT_MESSAGE = (
|
||||
'File is less than {} lines long; not compiling.'.format(
|
||||
MIN_LINES_IN_FILE_TO_PARSE ) )
|
||||
NO_DIAGNOSTIC_MESSAGE = 'No diagnostic for current line!'
|
||||
|
||||
|
||||
class ClangCompleter( Completer ):
|
||||
@ -35,12 +44,11 @@ class ClangCompleter( Completer ):
|
||||
'max_diagnostics_to_display' ]
|
||||
self.completer = ycm_core.ClangCompleter()
|
||||
self.completer.EnableThreading()
|
||||
self.contents_holder = []
|
||||
self.filename_holder = []
|
||||
self.last_prepared_diagnostics = []
|
||||
self.parse_future = None
|
||||
self.flags = Flags()
|
||||
self.diagnostic_store = None
|
||||
self._logger = logging.getLogger( __name__ )
|
||||
|
||||
# We set this flag when a compilation request comes in while one is already
|
||||
# in progress. We use this to trigger the pending request after the previous
|
||||
@ -53,63 +61,52 @@ class ClangCompleter( Completer ):
|
||||
return CLANG_FILETYPES
|
||||
|
||||
|
||||
def GetUnsavedFilesVector( self ):
|
||||
# CAREFUL HERE! For UnsavedFile filename and contents we are referring
|
||||
# directly to Python-allocated and -managed memory since we are accepting
|
||||
# pointers to data members of python objects. We need to ensure that those
|
||||
# objects outlive our UnsavedFile objects. This is why we need the
|
||||
# contents_holder and filename_holder lists, to make sure the string objects
|
||||
# are still around when we call CandidatesForQueryAndLocationInFile. We do
|
||||
# this to avoid an extra copy of the entire file contents.
|
||||
|
||||
def GetUnsavedFilesVector( self, request_data ):
|
||||
files = ycm_core.UnsavedFileVec()
|
||||
self.contents_holder = []
|
||||
self.filename_holder = []
|
||||
for buffer in vimsupport.GetUnsavedBuffers():
|
||||
if not ClangAvailableForBuffer( buffer ):
|
||||
for filename, file_data in request_data[ 'file_data' ].iteritems():
|
||||
if not ClangAvailableForFiletypes( file_data[ 'filetypes' ] ):
|
||||
continue
|
||||
contents = '\n'.join( buffer )
|
||||
name = buffer.name
|
||||
if not contents or not name:
|
||||
contents = file_data[ 'contents' ]
|
||||
if not contents or not filename:
|
||||
continue
|
||||
self.contents_holder.append( contents )
|
||||
self.filename_holder.append( name )
|
||||
|
||||
unsaved_file = ycm_core.UnsavedFile()
|
||||
unsaved_file.contents_ = self.contents_holder[ -1 ]
|
||||
unsaved_file.length_ = len( self.contents_holder[ -1 ] )
|
||||
unsaved_file.filename_ = self.filename_holder[ -1 ]
|
||||
unsaved_file.contents_ = contents
|
||||
unsaved_file.length_ = len( contents )
|
||||
unsaved_file.filename_ = filename
|
||||
|
||||
files.append( unsaved_file )
|
||||
|
||||
return files
|
||||
|
||||
|
||||
def CandidatesForQueryAsync( self, query, start_column ):
|
||||
filename = vim.current.buffer.name
|
||||
def CandidatesForQueryAsync( self, request_data ):
|
||||
filename = request_data[ 'filepath' ]
|
||||
|
||||
if not filename:
|
||||
return
|
||||
|
||||
if self.completer.UpdatingTranslationUnit( filename ):
|
||||
vimsupport.PostVimMessage( 'Still parsing file, no completions yet.' )
|
||||
self.completions_future = None
|
||||
return
|
||||
self._logger.info( PARSING_FILE_MESSAGE )
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
PARSING_FILE_MESSAGE )
|
||||
|
||||
flags = self.flags.FlagsForFile( filename )
|
||||
if not flags:
|
||||
vimsupport.PostVimMessage( 'Still no compile flags, no completions yet.' )
|
||||
self.completions_future = None
|
||||
return
|
||||
self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
NO_COMPILE_FLAGS_MESSAGE )
|
||||
|
||||
# TODO: sanitize query, probably in C++ code
|
||||
|
||||
files = ycm_core.UnsavedFileVec()
|
||||
query = request_data[ 'query' ]
|
||||
if not query:
|
||||
files = self.GetUnsavedFilesVector()
|
||||
files = self.GetUnsavedFilesVector( request_data )
|
||||
|
||||
line, _ = vim.current.window.cursor
|
||||
column = start_column + 1
|
||||
line = request_data[ 'line_num' ] + 1
|
||||
column = request_data[ 'start_column' ] + 1
|
||||
self.completions_future = (
|
||||
self.completer.CandidatesForQueryAndLocationInFileAsync(
|
||||
query,
|
||||
@ -123,10 +120,11 @@ class ClangCompleter( Completer ):
|
||||
def CandidatesFromStoredRequest( self ):
|
||||
if not self.completions_future:
|
||||
return []
|
||||
results = [ CompletionDataToDict( x ) for x in
|
||||
results = [ ConvertCompletionData( x ) for x in
|
||||
self.completions_future.GetResults() ]
|
||||
if not results:
|
||||
vimsupport.PostVimMessage( 'No completions found; errors in the file?' )
|
||||
self._logger.warning( NO_COMPLETIONS_MESSAGE )
|
||||
raise RuntimeError( NO_COMPLETIONS_MESSAGE )
|
||||
return results
|
||||
|
||||
|
||||
@ -137,37 +135,37 @@ class ClangCompleter( Completer ):
|
||||
'ClearCompilationFlagCache']
|
||||
|
||||
|
||||
def OnUserCommand( self, arguments ):
|
||||
def OnUserCommand( self, arguments, request_data ):
|
||||
if not arguments:
|
||||
self.EchoUserCommandsHelpMessage()
|
||||
return
|
||||
raise ValueError( self.UserCommandsHelpMessage() )
|
||||
|
||||
command = arguments[ 0 ]
|
||||
if command == 'GoToDefinition':
|
||||
self._GoToDefinition()
|
||||
self._GoToDefinition( request_data )
|
||||
elif command == 'GoToDeclaration':
|
||||
self._GoToDeclaration()
|
||||
self._GoToDeclaration( request_data )
|
||||
elif command == 'GoToDefinitionElseDeclaration':
|
||||
self._GoToDefinitionElseDeclaration()
|
||||
self._GoToDefinitionElseDeclaration( request_data )
|
||||
elif command == 'ClearCompilationFlagCache':
|
||||
self._ClearCompilationFlagCache()
|
||||
self._ClearCompilationFlagCache( request_data )
|
||||
|
||||
|
||||
def _LocationForGoTo( self, goto_function ):
|
||||
filename = vim.current.buffer.name
|
||||
def _LocationForGoTo( self, goto_function, request_data ):
|
||||
filename = request_data[ 'filepath' ]
|
||||
if not filename:
|
||||
return None
|
||||
self._logger.warning( INVALID_FILE_MESSAGE )
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
INVALID_FILE_MESSAGE )
|
||||
|
||||
flags = self.flags.FlagsForFile( filename )
|
||||
if not flags:
|
||||
vimsupport.PostVimMessage( 'Still no compile flags, can\'t compile.' )
|
||||
return None
|
||||
self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
NO_COMPILE_FLAGS_MESSAGE )
|
||||
|
||||
files = self.GetUnsavedFilesVector()
|
||||
line, column = vimsupport.CurrentLineAndColumn()
|
||||
# Making the line & column 1-based instead of 0-based
|
||||
line += 1
|
||||
column += 1
|
||||
line = request_data[ 'line_num' ] + 1
|
||||
column = request_data[ 'start_column' ] + 1
|
||||
return getattr( self.completer, goto_function )(
|
||||
filename,
|
||||
line,
|
||||
@ -176,39 +174,37 @@ class ClangCompleter( Completer ):
|
||||
flags )
|
||||
|
||||
|
||||
def _GoToDefinition( self ):
|
||||
def _GoToDefinition( self, request_data ):
|
||||
location = self._LocationForGoTo( 'GetDefinitionLocation' )
|
||||
if not location or not location.IsValid():
|
||||
vimsupport.PostVimMessage( 'Can\'t jump to definition.' )
|
||||
return
|
||||
raise RuntimeError( 'Can\'t jump to definition.' )
|
||||
|
||||
vimsupport.JumpToLocation( location.filename_,
|
||||
location.line_number_,
|
||||
location.column_number_ )
|
||||
return server_responses.BuildGoToResponse( location.filename_,
|
||||
location.line_number_,
|
||||
location.column_number_ )
|
||||
|
||||
|
||||
def _GoToDeclaration( self ):
|
||||
def _GoToDeclaration( self, request_data ):
|
||||
location = self._LocationForGoTo( 'GetDeclarationLocation' )
|
||||
if not location or not location.IsValid():
|
||||
vimsupport.PostVimMessage( 'Can\'t jump to declaration.' )
|
||||
return
|
||||
raise RuntimeError( 'Can\'t jump to declaration.' )
|
||||
|
||||
vimsupport.JumpToLocation( location.filename_,
|
||||
location.line_number_,
|
||||
location.column_number_ )
|
||||
return server_responses.BuildGoToResponse( location.filename_,
|
||||
location.line_number_,
|
||||
location.column_number_ )
|
||||
|
||||
|
||||
def _GoToDefinitionElseDeclaration( self ):
|
||||
def _GoToDefinitionElseDeclaration( self, request_data ):
|
||||
location = self._LocationForGoTo( 'GetDefinitionLocation' )
|
||||
if not location or not location.IsValid():
|
||||
location = self._LocationForGoTo( 'GetDeclarationLocation' )
|
||||
if not location or not location.IsValid():
|
||||
vimsupport.PostVimMessage( 'Can\'t jump to definition or declaration.' )
|
||||
return
|
||||
raise RuntimeError( 'Can\'t jump to definition or declaration.' )
|
||||
|
||||
return server_responses.BuildGoToResponse( location.filename_,
|
||||
location.line_number_,
|
||||
location.column_number_ )
|
||||
|
||||
vimsupport.JumpToLocation( location.filename_,
|
||||
location.line_number_,
|
||||
location.column_number_ )
|
||||
|
||||
|
||||
def _ClearCompilationFlagCache( self ):
|
||||
@ -216,14 +212,18 @@ class ClangCompleter( Completer ):
|
||||
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
if vimsupport.NumLinesInBuffer( vim.current.buffer ) < 5:
|
||||
def OnFileReadyToParse( self, request_data ):
|
||||
filename = request_data[ 'filepath' ]
|
||||
contents = request_data[ 'file_data' ][ filename ][ 'contents' ]
|
||||
if contents.count( '\n' ) < MIN_LINES_IN_FILE_TO_PARSE:
|
||||
self.parse_future = None
|
||||
return
|
||||
self._logger.warning( FILE_TOO_SHORT_MESSAGE )
|
||||
raise ValueError( FILE_TOO_SHORT_MESSAGE )
|
||||
|
||||
filename = vim.current.buffer.name
|
||||
if not filename:
|
||||
return
|
||||
self._logger.warning( INVALID_FILE_MESSAGE )
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
INVALID_FILE_MESSAGE )
|
||||
|
||||
if self.completer.UpdatingTranslationUnit( filename ):
|
||||
self.extra_parse_desired = True
|
||||
@ -232,11 +232,13 @@ class ClangCompleter( Completer ):
|
||||
flags = self.flags.FlagsForFile( filename )
|
||||
if not flags:
|
||||
self.parse_future = None
|
||||
return
|
||||
self._logger.info( NO_COMPILE_FLAGS_MESSAGE )
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
NO_COMPILE_FLAGS_MESSAGE )
|
||||
|
||||
self.parse_future = self.completer.UpdateTranslationUnitAsync(
|
||||
filename,
|
||||
self.GetUnsavedFilesVector(),
|
||||
self.GetUnsavedFilesVector( request_data ),
|
||||
flags )
|
||||
|
||||
self.extra_parse_desired = False
|
||||
@ -253,16 +255,18 @@ class ClangCompleter( Completer ):
|
||||
return self.parse_future.ResultsReady()
|
||||
|
||||
|
||||
def GettingCompletions( self ):
|
||||
return self.completer.UpdatingTranslationUnit( vim.current.buffer.name )
|
||||
def GettingCompletions( self, request_data ):
|
||||
return self.completer.UpdatingTranslationUnit( request_data[ 'filepath' ] )
|
||||
|
||||
|
||||
def GetDiagnosticsForCurrentFile( self ):
|
||||
def GetDiagnosticsForCurrentFile( self, request_data ):
|
||||
filename = request_data[ 'filepath' ]
|
||||
if self.DiagnosticsForCurrentFileReady():
|
||||
diagnostics = self.completer.DiagnosticsForFile( vim.current.buffer.name )
|
||||
diagnostics = self.completer.DiagnosticsForFile( filename )
|
||||
self.diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
|
||||
self.last_prepared_diagnostics = [ DiagnosticToDict( x ) for x in
|
||||
diagnostics[ : self.max_diagnostics_to_display ] ]
|
||||
self.last_prepared_diagnostics = [
|
||||
server_responses.BuildDiagnosticData( x ) for x in
|
||||
diagnostics[ : self.max_diagnostics_to_display ] ]
|
||||
self.parse_future = None
|
||||
|
||||
if self.extra_parse_desired:
|
||||
@ -271,23 +275,19 @@ class ClangCompleter( Completer ):
|
||||
return self.last_prepared_diagnostics
|
||||
|
||||
|
||||
def ShowDetailedDiagnostic( self ):
|
||||
current_line, current_column = vimsupport.CurrentLineAndColumn()
|
||||
|
||||
# CurrentLineAndColumn() numbers are 0-based, clang numbers are 1-based
|
||||
current_line += 1
|
||||
current_column += 1
|
||||
|
||||
current_file = vim.current.buffer.name
|
||||
def GetDetailedDiagnostic( self, request_data ):
|
||||
current_line = request_data[ 'line_num' ] + 1
|
||||
current_column = request_data[ 'column_num' ] + 1
|
||||
current_file = request_data[ 'filepath' ]
|
||||
|
||||
if not self.diagnostic_store:
|
||||
vimsupport.PostVimMessage( "No diagnostic for current line!" )
|
||||
return
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
NO_DIAGNOSTIC_MESSAGE )
|
||||
|
||||
diagnostics = self.diagnostic_store[ current_file ][ current_line ]
|
||||
if not diagnostics:
|
||||
vimsupport.PostVimMessage( "No diagnostic for current line!" )
|
||||
return
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
NO_DIAGNOSTIC_MESSAGE )
|
||||
|
||||
closest_diagnostic = None
|
||||
distance_to_closest_diagnostic = 999
|
||||
@ -298,50 +298,61 @@ class ClangCompleter( Completer ):
|
||||
distance_to_closest_diagnostic = distance
|
||||
closest_diagnostic = diagnostic
|
||||
|
||||
vimsupport.EchoText( closest_diagnostic.long_formatted_text_ )
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
closest_diagnostic.long_formatted_text_ )
|
||||
|
||||
|
||||
def ShouldUseNow( self, start_column, current_line ):
|
||||
def ShouldUseNow( self, request_data ):
|
||||
# We don't want to use the Completer API cache, we use one in the C++ code.
|
||||
return self.ShouldUseNowInner( start_column, current_line )
|
||||
return self.ShouldUseNowInner( request_data )
|
||||
|
||||
|
||||
def DebugInfo( self ):
|
||||
filename = vim.current.buffer.name
|
||||
def DebugInfo( self, request_data ):
|
||||
filename = request_data[ 'filepath' ]
|
||||
if not filename:
|
||||
return ''
|
||||
flags = self.flags.FlagsForFile( filename ) or []
|
||||
source = extra_conf_store.ModuleFileForSourceFile( filename )
|
||||
return 'Flags for {0} loaded from {1}:\n{2}'.format( filename,
|
||||
source,
|
||||
list( flags ) )
|
||||
return server_responses.BuildDisplayMessageResponse(
|
||||
'Flags for {0} loaded from {1}:\n{2}'.format( filename,
|
||||
source,
|
||||
list( flags ) ) )
|
||||
|
||||
|
||||
# TODO: make these functions module-local
|
||||
def CompletionDataToDict( completion_data ):
|
||||
# see :h complete-items for a description of the dictionary fields
|
||||
return {
|
||||
'word' : completion_data.TextToInsertInBuffer(),
|
||||
'abbr' : completion_data.MainCompletionText(),
|
||||
'menu' : completion_data.ExtraMenuInfo(),
|
||||
'kind' : completion_data.kind_,
|
||||
'info' : completion_data.DetailedInfoForPreviewWindow(),
|
||||
'dup' : 1,
|
||||
}
|
||||
# def CompletionDataToDict( completion_data ):
|
||||
# # see :h complete-items for a description of the dictionary fields
|
||||
# return {
|
||||
# 'word' : completion_data.TextToInsertInBuffer(),
|
||||
# 'abbr' : completion_data.MainCompletionText(),
|
||||
# 'menu' : completion_data.ExtraMenuInfo(),
|
||||
# 'kind' : completion_data.kind_,
|
||||
# 'info' : completion_data.DetailedInfoForPreviewWindow(),
|
||||
# 'dup' : 1,
|
||||
# }
|
||||
|
||||
|
||||
def DiagnosticToDict( diagnostic ):
|
||||
# see :h getqflist for a description of the dictionary fields
|
||||
return {
|
||||
# TODO: wrap the bufnr generation into a function
|
||||
'bufnr' : int( vim.eval( "bufnr('{0}', 1)".format(
|
||||
diagnostic.filename_ ) ) ),
|
||||
'lnum' : diagnostic.line_number_,
|
||||
'col' : diagnostic.column_number_,
|
||||
'text' : diagnostic.text_,
|
||||
'type' : diagnostic.kind_,
|
||||
'valid' : 1
|
||||
}
|
||||
# def DiagnosticToDict( diagnostic ):
|
||||
# # see :h getqflist for a description of the dictionary fields
|
||||
# return {
|
||||
# # TODO: wrap the bufnr generation into a function
|
||||
# 'bufnr' : int( vim.eval( "bufnr('{0}', 1)".format(
|
||||
# diagnostic.filename_ ) ) ),
|
||||
# 'lnum' : diagnostic.line_number_,
|
||||
# 'col' : diagnostic.column_number_,
|
||||
# 'text' : diagnostic.text_,
|
||||
# 'type' : diagnostic.kind_,
|
||||
# 'valid' : 1
|
||||
# }
|
||||
|
||||
|
||||
def ConvertCompletionData( completion_data ):
|
||||
return server_responses.BuildCompletionData(
|
||||
insertion_text = completion_data.TextToInsertInBuffer(),
|
||||
menu_text = completion_data.MainCompletionText(),
|
||||
extra_menu_info = completion_data.ExtraMenuInfo(),
|
||||
kind = completion_data.kind_,
|
||||
detailed_info = completion_data.DetailedInfoForPreviewWindow() )
|
||||
|
||||
|
||||
def DiagnosticsToDiagStructure( diagnostics ):
|
||||
@ -352,12 +363,9 @@ def DiagnosticsToDiagStructure( diagnostics ):
|
||||
return structure
|
||||
|
||||
|
||||
def ClangAvailableForBuffer( buffer_object ):
|
||||
filetypes = vimsupport.FiletypesForBuffer( buffer_object )
|
||||
def ClangAvailableForFiletypes( filetypes ):
|
||||
return any( [ filetype in CLANG_FILETYPES for filetype in filetypes ] )
|
||||
|
||||
|
||||
def InCFamilyFile():
|
||||
return any( [ filetype in CLANG_FILETYPES for filetype in
|
||||
vimsupport.CurrentFiletypes() ] )
|
||||
|
||||
def InCFamilyFile( filetypes ):
|
||||
return ClangAvailableForFiletypes( filetypes )
|
||||
|
@ -19,12 +19,11 @@
|
||||
|
||||
import ycm_core
|
||||
import os
|
||||
from ycm import vimsupport
|
||||
from ycm import extra_conf_store
|
||||
|
||||
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++. Go READ THE '
|
||||
'DOCS *NOW*, DON\'T file a bug report.').format(
|
||||
'DOCS *NOW*, DON\'T file a bug report.' ).format(
|
||||
extra_conf_store.YCM_EXTRA_CONF_FILENAME )
|
||||
|
||||
|
||||
@ -37,7 +36,6 @@ class Flags( object ):
|
||||
# It's caches all the way down...
|
||||
self.flags_for_file = {}
|
||||
self.special_clang_flags = _SpecialClangIncludes()
|
||||
self.no_extra_conf_file_warning_posted = False
|
||||
|
||||
|
||||
def FlagsForFile( self, filename, add_special_clang_flags = True ):
|
||||
@ -46,10 +44,7 @@ class Flags( object ):
|
||||
except KeyError:
|
||||
module = extra_conf_store.ModuleForSourceFile( filename )
|
||||
if not 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
|
||||
raise RuntimeError( NO_EXTRA_CONF_FILENAME_MESSAGE )
|
||||
|
||||
results = module.FlagsForFile( filename )
|
||||
|
||||
|
@ -18,18 +18,18 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import vim
|
||||
import os
|
||||
from sys import platform
|
||||
import glob
|
||||
from ycm.completers.threaded_completer import ThreadedCompleter
|
||||
from ycm import vimsupport
|
||||
from ycm import server_responses
|
||||
import urllib2
|
||||
import urllib
|
||||
import urlparse
|
||||
import json
|
||||
import subprocess
|
||||
import tempfile
|
||||
import logging
|
||||
|
||||
|
||||
SERVER_NOT_FOUND_MSG = ( 'OmniSharp server binary not found at {0}. ' +
|
||||
@ -44,9 +44,10 @@ class CsharpCompleter( ThreadedCompleter ):
|
||||
def __init__( self, user_options ):
|
||||
super( CsharpCompleter, self ).__init__( user_options )
|
||||
self._omnisharp_port = None
|
||||
self._logger = logging.getLogger(__name__)
|
||||
|
||||
if self.user_options[ 'auto_start_csharp_server' ]:
|
||||
self._StartServer()
|
||||
# if self.user_options[ 'auto_start_csharp_server' ]:
|
||||
# self._StartServer()
|
||||
|
||||
|
||||
def OnVimLeave( self ):
|
||||
@ -60,11 +61,12 @@ class CsharpCompleter( ThreadedCompleter ):
|
||||
return [ 'cs' ]
|
||||
|
||||
|
||||
def ComputeCandidates( self, unused_query, unused_start_column ):
|
||||
return [ { 'word': str( completion[ 'CompletionText' ] ),
|
||||
'menu': str( completion[ 'DisplayText' ] ),
|
||||
'info': str( completion[ 'Description' ] ) }
|
||||
for completion in self._GetCompletions() ]
|
||||
def ComputeCandidates( self, request_data ):
|
||||
return [ server_responses.BuildCompletionData(
|
||||
completion[ 'CompletionText' ],
|
||||
completion[ 'DisplayText' ],
|
||||
completion[ 'Description' ] )
|
||||
for completion in self._GetCompletions( request_data ) ]
|
||||
|
||||
|
||||
def DefinedSubcommands( self ):
|
||||
@ -76,24 +78,23 @@ class CsharpCompleter( ThreadedCompleter ):
|
||||
'GoToDefinitionElseDeclaration' ]
|
||||
|
||||
|
||||
def OnUserCommand( self, arguments ):
|
||||
def OnUserCommand( self, arguments, request_data ):
|
||||
if not arguments:
|
||||
self.EchoUserCommandsHelpMessage()
|
||||
return
|
||||
raise ValueError( self.UserCommandsHelpMessage() )
|
||||
|
||||
command = arguments[ 0 ]
|
||||
if command == 'StartServer':
|
||||
self._StartServer()
|
||||
self._StartServer( request_data )
|
||||
elif command == 'StopServer':
|
||||
self._StopServer()
|
||||
elif command == 'RestartServer':
|
||||
if self._ServerIsRunning():
|
||||
self._StopServer()
|
||||
self._StartServer()
|
||||
self._StartServer( request_data )
|
||||
elif command in [ 'GoToDefinition',
|
||||
'GoToDeclaration',
|
||||
'GoToDefinitionElseDeclaration' ]:
|
||||
self._GoToDefinition()
|
||||
return self._GoToDefinition( request_data )
|
||||
|
||||
|
||||
def DebugInfo( self ):
|
||||
@ -104,35 +105,27 @@ class CsharpCompleter( ThreadedCompleter ):
|
||||
return 'Server is not running'
|
||||
|
||||
|
||||
def _StartServer( self ):
|
||||
def _StartServer( self, request_data ):
|
||||
""" Start the OmniSharp server """
|
||||
self._omnisharp_port = self._FindFreePort()
|
||||
solutionfiles, folder = _FindSolutionFiles()
|
||||
solutionfiles, folder = _FindSolutionFiles( request_data[ 'filepath' ] )
|
||||
|
||||
if len( solutionfiles ) == 0:
|
||||
vimsupport.PostVimMessage(
|
||||
'Error starting OmniSharp server: no solutionfile found' )
|
||||
return
|
||||
raise RuntimeError(
|
||||
'Error starting OmniSharp server: no solutionfile found' )
|
||||
elif len( solutionfiles ) == 1:
|
||||
solutionfile = solutionfiles[ 0 ]
|
||||
else:
|
||||
choice = vimsupport.PresentDialog(
|
||||
'Which solutionfile should be loaded?',
|
||||
[ str( i ) + " " + solution for i, solution in
|
||||
enumerate( solutionfiles ) ] )
|
||||
if choice == -1:
|
||||
vimsupport.PostVimMessage( 'OmniSharp not started' )
|
||||
return
|
||||
else:
|
||||
solutionfile = solutionfiles[ choice ]
|
||||
raise RuntimeError(
|
||||
'Found multiple solution files instead of one!\n{}'.format(
|
||||
solutionfiles ) )
|
||||
|
||||
omnisharp = os.path.join(
|
||||
os.path.abspath( os.path.dirname( __file__ ) ),
|
||||
'OmniSharpServer/OmniSharp/bin/Debug/OmniSharp.exe' )
|
||||
|
||||
if not os.path.isfile( omnisharp ):
|
||||
vimsupport.PostVimMessage( SERVER_NOT_FOUND_MSG.format( omnisharp ) )
|
||||
return
|
||||
raise RuntimeError( SERVER_NOT_FOUND_MSG.format( omnisharp ) )
|
||||
|
||||
if not platform.startswith( 'win' ):
|
||||
omnisharp = 'mono ' + omnisharp
|
||||
@ -154,40 +147,44 @@ class CsharpCompleter( ThreadedCompleter ):
|
||||
with open( self._filename_stdout, 'w' ) as fstdout:
|
||||
subprocess.Popen( command, stdout=fstdout, stderr=fstderr, shell=True )
|
||||
|
||||
vimsupport.PostVimMessage( 'Starting OmniSharp server' )
|
||||
self._logger.info( 'Starting OmniSharp server' )
|
||||
|
||||
|
||||
def _StopServer( self ):
|
||||
""" Stop the OmniSharp server """
|
||||
self._GetResponse( '/stopserver' )
|
||||
self._omnisharp_port = None
|
||||
vimsupport.PostVimMessage( 'Stopping OmniSharp server' )
|
||||
self._logger.info( 'Stopping OmniSharp server' )
|
||||
|
||||
|
||||
def _GetCompletions( self ):
|
||||
def _GetCompletions( self, request_data ):
|
||||
""" Ask server for completions """
|
||||
completions = self._GetResponse( '/autocomplete', self._DefaultParameters() )
|
||||
completions = self._GetResponse( '/autocomplete',
|
||||
self._DefaultParameters( request_data ) )
|
||||
return completions if completions != None else []
|
||||
|
||||
|
||||
def _GoToDefinition( self ):
|
||||
def _GoToDefinition( self, request_data ):
|
||||
""" Jump to definition of identifier under cursor """
|
||||
definition = self._GetResponse( '/gotodefinition', self._DefaultParameters() )
|
||||
definition = self._GetResponse( '/gotodefinition',
|
||||
self._DefaultParameters( request_data ) )
|
||||
if definition[ 'FileName' ] != None:
|
||||
vimsupport.JumpToLocation( definition[ 'FileName' ],
|
||||
definition[ 'Line' ],
|
||||
definition[ 'Column' ] )
|
||||
return server_responses.BuildGoToResponse( definition[ 'FileName' ],
|
||||
definition[ 'Line' ],
|
||||
definition[ 'Column' ] )
|
||||
else:
|
||||
vimsupport.PostVimMessage( 'Can\'t jump to definition' )
|
||||
raise RuntimeError( 'Can\'t jump to definition' )
|
||||
|
||||
|
||||
def _DefaultParameters( self ):
|
||||
def _DefaultParameters( self, request_data ):
|
||||
""" Some very common request parameters """
|
||||
line, column = vimsupport.CurrentLineAndColumn()
|
||||
parameters = {}
|
||||
parameters[ 'line' ], parameters[ 'column' ] = line + 1, column + 1
|
||||
parameters[ 'buffer' ] = '\n'.join( vim.current.buffer )
|
||||
parameters[ 'filename' ] = vim.current.buffer.name
|
||||
parameters[ 'line' ] = request_data[ 'line_num' ] + 1
|
||||
parameters[ 'column' ] = request_data[ 'column_num' ] + 1
|
||||
filepath = request_data[ 'filepath' ]
|
||||
parameters[ 'buffer' ] = request_data[ 'file_data' ][ filepath ][
|
||||
'contents' ]
|
||||
parameters[ 'filename' ] = filepath
|
||||
return parameters
|
||||
|
||||
|
||||
@ -215,20 +212,16 @@ class CsharpCompleter( ThreadedCompleter ):
|
||||
|
||||
def _GetResponse( self, endPoint, parameters={}, silent=False, port=None ):
|
||||
""" Handle communication with server """
|
||||
# TODO: Replace usage of urllib with Requests
|
||||
target = urlparse.urljoin( self._PortToHost( port ), endPoint )
|
||||
parameters = urllib.urlencode( parameters )
|
||||
try:
|
||||
response = urllib2.urlopen( target, parameters )
|
||||
return json.loads( response.read() )
|
||||
except Exception:
|
||||
# TODO: Add logging for this case. We can't post a Vim message because Vim
|
||||
# crashes when that's done from a no-GUI thread.
|
||||
return None
|
||||
response = urllib2.urlopen( target, parameters )
|
||||
return json.loads( response.read() )
|
||||
|
||||
|
||||
def _FindSolutionFiles():
|
||||
def _FindSolutionFiles( filepath ):
|
||||
""" Find solution files by searching upwards in the file tree """
|
||||
folder = os.path.dirname( vim.current.buffer.name )
|
||||
folder = os.path.dirname( filepath )
|
||||
solutionfiles = glob.glob1( folder, '*.sln' )
|
||||
while not solutionfiles:
|
||||
lastfolder = folder
|
||||
|
@ -16,13 +16,13 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import vim
|
||||
import os
|
||||
import re
|
||||
|
||||
from ycm.completers.threaded_completer import ThreadedCompleter
|
||||
from ycm.completers.cpp.clang_completer import InCFamilyFile
|
||||
from ycm.completers.cpp.flags import Flags
|
||||
from ycm import server_responses
|
||||
|
||||
class FilenameCompleter( ThreadedCompleter ):
|
||||
"""
|
||||
@ -52,23 +52,32 @@ class FilenameCompleter( ThreadedCompleter ):
|
||||
self._include_regex = re.compile( include_regex_common )
|
||||
|
||||
|
||||
def AtIncludeStatementStart( self, start_column ):
|
||||
return ( InCFamilyFile() and
|
||||
def AtIncludeStatementStart( self, request_data ):
|
||||
start_column = request_data[ 'start_column' ]
|
||||
current_line = request_data[ 'line_value' ]
|
||||
filepath = request_data[ 'filepath' ]
|
||||
filetypes = request_data[ 'file_data' ][ filepath ][ 'filetypes' ]
|
||||
return ( InCFamilyFile( filetypes ) and
|
||||
self._include_start_regex.match(
|
||||
vim.current.line[ :start_column ] ) )
|
||||
current_line[ :start_column ] ) )
|
||||
|
||||
|
||||
def ShouldUseNowInner( self, start_column, current_line ):
|
||||
def ShouldUseNowInner( self, request_data ):
|
||||
start_column = request_data[ 'start_column' ]
|
||||
current_line = request_data[ 'line_value' ]
|
||||
return ( start_column and ( current_line[ start_column - 1 ] == '/' or
|
||||
self.AtIncludeStatementStart( start_column ) ) )
|
||||
self.AtIncludeStatementStart( request_data ) ) )
|
||||
|
||||
|
||||
def SupportedFiletypes( self ):
|
||||
return []
|
||||
|
||||
|
||||
def ComputeCandidates( self, unused_query, start_column ):
|
||||
line = vim.current.line[ :start_column ]
|
||||
def ComputeCandidates( self, request_data ):
|
||||
current_line = request_data[ 'line_value' ]
|
||||
start_column = request_data[ 'start_column' ]
|
||||
filepath = request_data[ 'filepath' ]
|
||||
line = current_line[ :start_column ]
|
||||
|
||||
if InCFamilyFile():
|
||||
include_match = self._include_regex.search( line )
|
||||
@ -78,22 +87,26 @@ class FilenameCompleter( ThreadedCompleter ):
|
||||
# http://gcc.gnu.org/onlinedocs/cpp/Include-Syntax.html
|
||||
include_current_file_dir = '<' not in include_match.group()
|
||||
return _GenerateCandidatesForPaths(
|
||||
self.GetPathsIncludeCase( path_dir, include_current_file_dir ) )
|
||||
self.GetPathsIncludeCase( path_dir,
|
||||
include_current_file_dir,
|
||||
filepath ) )
|
||||
|
||||
path_match = self._path_regex.search( line )
|
||||
path_dir = os.path.expanduser( path_match.group() ) if path_match else ''
|
||||
|
||||
return _GenerateCandidatesForPaths(
|
||||
_GetPathsStandardCase( path_dir, self.user_options[
|
||||
'filepath_completion_use_working_dir' ] ) )
|
||||
_GetPathsStandardCase(
|
||||
path_dir,
|
||||
self.user_options[ 'filepath_completion_use_working_dir' ],
|
||||
filepath ) )
|
||||
|
||||
|
||||
def GetPathsIncludeCase( self, path_dir, include_current_file_dir ):
|
||||
def GetPathsIncludeCase( self, path_dir, include_current_file_dir, filepath ):
|
||||
paths = []
|
||||
include_paths = self._flags.UserIncludePaths( vim.current.buffer.name )
|
||||
include_paths = self._flags.UserIncludePaths( filepath )
|
||||
|
||||
if include_current_file_dir:
|
||||
include_paths.append( os.path.dirname( vim.current.buffer.name ) )
|
||||
include_paths.append( os.path.dirname( filepath ) )
|
||||
|
||||
for include_path in include_paths:
|
||||
try:
|
||||
@ -107,9 +120,9 @@ class FilenameCompleter( ThreadedCompleter ):
|
||||
return sorted( set( paths ) )
|
||||
|
||||
|
||||
def _GetPathsStandardCase( path_dir, use_working_dir ):
|
||||
def _GetPathsStandardCase( path_dir, use_working_dir, filepath ):
|
||||
if not use_working_dir and not path_dir.startswith( '/' ):
|
||||
path_dir = os.path.join( os.path.dirname( vim.current.buffer.name ),
|
||||
path_dir = os.path.join( os.path.dirname( filepath ),
|
||||
path_dir )
|
||||
|
||||
try:
|
||||
@ -132,8 +145,8 @@ def _GenerateCandidatesForPaths( absolute_paths ):
|
||||
seen_basenames.add( basename )
|
||||
|
||||
is_dir = os.path.isdir( absolute_path )
|
||||
completion_dicts.append( { 'word': basename,
|
||||
'dup': 1,
|
||||
'menu': '[Dir]' if is_dir else '[File]' } )
|
||||
completion_dicts.append(
|
||||
server_responses.BuildCompletionData( basename,
|
||||
'[Dir]' if is_dir else '[File]' ) )
|
||||
|
||||
return completion_dicts
|
||||
|
@ -58,18 +58,17 @@ class GeneralCompleterStore( Completer ):
|
||||
return set()
|
||||
|
||||
|
||||
def ShouldUseNow( self, start_column, current_line ):
|
||||
def ShouldUseNow( self, request_data ):
|
||||
self._current_query_completers = []
|
||||
|
||||
if self._filename_completer.ShouldUseNow( start_column, current_line ):
|
||||
if self._filename_completer.ShouldUseNow( request_data ):
|
||||
self._current_query_completers = [ self._filename_completer ]
|
||||
return True
|
||||
|
||||
should_use_now = False
|
||||
|
||||
for completer in self._non_filename_completers:
|
||||
should_use_this_completer = completer.ShouldUseNow( start_column,
|
||||
current_line )
|
||||
should_use_this_completer = completer.ShouldUseNow( request_data )
|
||||
should_use_now = should_use_now or should_use_this_completer
|
||||
|
||||
if should_use_this_completer:
|
||||
@ -78,9 +77,9 @@ class GeneralCompleterStore( Completer ):
|
||||
return should_use_now
|
||||
|
||||
|
||||
def CandidatesForQueryAsync( self, query, start_column ):
|
||||
def CandidatesForQueryAsync( self, request_data ):
|
||||
for completer in self._current_query_completers:
|
||||
completer.CandidatesForQueryAsync( query, start_column )
|
||||
completer.CandidatesForQueryAsync( request_data )
|
||||
|
||||
|
||||
def AsyncCandidateRequestReady( self ):
|
||||
@ -96,9 +95,9 @@ class GeneralCompleterStore( Completer ):
|
||||
return candidates
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
def OnFileReadyToParse( self, request_data ):
|
||||
for completer in self._all_completers:
|
||||
completer.OnFileReadyToParse()
|
||||
completer.OnFileReadyToParse( request_data )
|
||||
|
||||
|
||||
def OnBufferVisit( self ):
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
from ycm.completers.general_completer import GeneralCompleter
|
||||
from UltiSnips import UltiSnips_Manager
|
||||
from ycm import server_responses
|
||||
|
||||
|
||||
class UltiSnipsCompleter( GeneralCompleter ):
|
||||
@ -33,13 +34,13 @@ class UltiSnipsCompleter( GeneralCompleter ):
|
||||
self._filtered_candidates = None
|
||||
|
||||
|
||||
def ShouldUseNowInner( self, start_column, unused_current_line ):
|
||||
return self.QueryLengthAboveMinThreshold( start_column )
|
||||
def ShouldUseNowInner( self, request_data ):
|
||||
return self.QueryLengthAboveMinThreshold( request_data )
|
||||
|
||||
|
||||
def CandidatesForQueryAsync( self, query, unused_start_column ):
|
||||
self._filtered_candidates = self.FilterAndSortCandidates( self._candidates,
|
||||
query )
|
||||
def CandidatesForQueryAsync( self, request_data ):
|
||||
self._filtered_candidates = self.FilterAndSortCandidates(
|
||||
self._candidates, request_data[ 'query' ] )
|
||||
|
||||
|
||||
def AsyncCandidateRequestReady( self ):
|
||||
@ -61,8 +62,9 @@ def _GetCandidates():
|
||||
# UltiSnips_Manager._snips() returns a class instance where:
|
||||
# class.trigger - name of snippet trigger word ( e.g. defn or testcase )
|
||||
# class.description - description of the snippet
|
||||
return [ { 'word': str( snip.trigger ),
|
||||
'menu': str( '<snip> ' + snip.description.encode('utf-8') ) }
|
||||
for snip in rawsnips ]
|
||||
return [ server_responses.BuildCompletionData(
|
||||
str( snip.trigger ),
|
||||
str( '<snip> ' + snip.description.encode( 'utf-8' ) ) )
|
||||
for snip in rawsnips ]
|
||||
except:
|
||||
return []
|
||||
|
@ -19,9 +19,8 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import vim
|
||||
from ycm.completers.threaded_completer import ThreadedCompleter
|
||||
from ycm import vimsupport
|
||||
from ycm import server_responses
|
||||
|
||||
import sys
|
||||
from os.path import join, abspath, dirname
|
||||
@ -33,7 +32,7 @@ sys.path.insert( 0, join( abspath( dirname( __file__ ) ), 'jedi' ) )
|
||||
try:
|
||||
import jedi
|
||||
except ImportError:
|
||||
vimsupport.PostVimMessage(
|
||||
raise ImportError(
|
||||
'Error importing jedi. Make sure the jedi submodule has been checked out. '
|
||||
'In the YouCompleteMe folder, run "git submodule update --init --recursive"')
|
||||
sys.path.pop( 0 )
|
||||
@ -54,113 +53,108 @@ class JediCompleter( ThreadedCompleter ):
|
||||
return [ 'python' ]
|
||||
|
||||
|
||||
def _GetJediScript( self ):
|
||||
contents = '\n'.join( vim.current.buffer )
|
||||
line, column = vimsupport.CurrentLineAndColumn()
|
||||
def _GetJediScript( self, request_data ):
|
||||
filename = request_data[ 'filepath' ]
|
||||
contents = request_data[ 'file_data' ][ filename ][ 'contents' ]
|
||||
# Jedi expects lines to start at 1, not 0
|
||||
line += 1
|
||||
filename = vim.current.buffer.name
|
||||
line = request_data[ 'line_num' ] + 1
|
||||
column = request_data[ 'column_num' ]
|
||||
print contents
|
||||
|
||||
return jedi.Script( contents, line, column, filename )
|
||||
|
||||
|
||||
def ComputeCandidates( self, unused_query, unused_start_column ):
|
||||
script = self._GetJediScript()
|
||||
|
||||
return [ { 'word': str( completion.name ),
|
||||
'menu': str( completion.description ),
|
||||
'info': str( completion.doc ) }
|
||||
def ComputeCandidates( self, request_data ):
|
||||
script = self._GetJediScript( request_data )
|
||||
return [ server_responses.BuildCompletionData( completion.name,
|
||||
completion.description,
|
||||
completion.doc )
|
||||
for completion in script.completions() ]
|
||||
|
||||
|
||||
def DefinedSubcommands( self ):
|
||||
return [ "GoToDefinition",
|
||||
"GoToDeclaration",
|
||||
"GoToDefinitionElseDeclaration" ]
|
||||
|
||||
|
||||
def OnUserCommand( self, arguments ):
|
||||
def OnUserCommand( self, arguments, request_data ):
|
||||
if not arguments:
|
||||
self.EchoUserCommandsHelpMessage()
|
||||
return
|
||||
raise ValueError( self.UserCommandsHelpMessage() )
|
||||
|
||||
command = arguments[ 0 ]
|
||||
if command == 'GoToDefinition':
|
||||
self._GoToDefinition()
|
||||
return self._GoToDefinition( request_data )
|
||||
elif command == 'GoToDeclaration':
|
||||
self._GoToDeclaration()
|
||||
return self._GoToDeclaration( request_data )
|
||||
elif command == 'GoToDefinitionElseDeclaration':
|
||||
self._GoToDefinitionElseDeclaration()
|
||||
return self._GoToDefinitionElseDeclaration( request_data )
|
||||
|
||||
|
||||
def _GoToDefinition( self ):
|
||||
definitions = self._GetDefinitionsList()
|
||||
def _GoToDefinition( self, request_data ):
|
||||
definitions = self._GetDefinitionsList( request_data )
|
||||
if definitions:
|
||||
self._JumpToLocation( definitions )
|
||||
return self._BuildGoToResponse( definitions )
|
||||
else:
|
||||
vimsupport.PostVimMessage( 'Can\'t jump to definition.' )
|
||||
raise RuntimeError( 'Can\'t jump to definition.' )
|
||||
|
||||
|
||||
def _GoToDeclaration( self ):
|
||||
definitions = self._GetDefinitionsList( declaration = True )
|
||||
def _GoToDeclaration( self, request_data ):
|
||||
definitions = self._GetDefinitionsList( request_data, declaration = True )
|
||||
if definitions:
|
||||
self._JumpToLocation( definitions )
|
||||
return self._BuildGoToResponse( definitions )
|
||||
else:
|
||||
vimsupport.PostVimMessage( 'Can\'t jump to declaration.' )
|
||||
raise RuntimeError( 'Can\'t jump to declaration.' )
|
||||
|
||||
|
||||
def _GoToDefinitionElseDeclaration( self ):
|
||||
def _GoToDefinitionElseDeclaration( self, request_data ):
|
||||
definitions = self._GetDefinitionsList() or \
|
||||
self._GetDefinitionsList( declaration = True )
|
||||
self._GetDefinitionsList( request_data, declaration = True )
|
||||
if definitions:
|
||||
self._JumpToLocation( definitions )
|
||||
return self._BuildGoToResponse( definitions )
|
||||
else:
|
||||
vimsupport.PostVimMessage( 'Can\'t jump to definition or declaration.' )
|
||||
raise RuntimeError( 'Can\'t jump to definition or declaration.' )
|
||||
|
||||
|
||||
def _GetDefinitionsList( self, declaration = False ):
|
||||
def _GetDefinitionsList( self, request_data, declaration = False ):
|
||||
definitions = []
|
||||
script = self._GetJediScript()
|
||||
script = self._GetJediScript( request_data )
|
||||
try:
|
||||
if declaration:
|
||||
definitions = script.goto_definitions()
|
||||
else:
|
||||
definitions = script.goto_assignments()
|
||||
except jedi.NotFoundError:
|
||||
vimsupport.PostVimMessage(
|
||||
"Cannot follow nothing. Put your cursor on a valid name." )
|
||||
except Exception as e:
|
||||
vimsupport.PostVimMessage(
|
||||
"Caught exception, aborting. Full error: " + str( e ) )
|
||||
raise RuntimeError(
|
||||
'Cannot follow nothing. Put your cursor on a valid name.' )
|
||||
|
||||
return definitions
|
||||
|
||||
|
||||
def _JumpToLocation( self, definition_list ):
|
||||
def _BuildGoToResponse( self, definition_list ):
|
||||
if len( definition_list ) == 1:
|
||||
definition = definition_list[ 0 ]
|
||||
if definition.in_builtin_module():
|
||||
if definition.is_keyword:
|
||||
vimsupport.PostVimMessage(
|
||||
"Cannot get the definition of Python keywords." )
|
||||
raise RuntimeError(
|
||||
'Cannot get the definition of Python keywords.' )
|
||||
else:
|
||||
vimsupport.PostVimMessage( "Builtin modules cannot be displayed." )
|
||||
raise RuntimeError( 'Builtin modules cannot be displayed.' )
|
||||
else:
|
||||
vimsupport.JumpToLocation( definition.module_path,
|
||||
definition.line,
|
||||
definition.column + 1 )
|
||||
return server_responses.BuildGoToResponse( definition.module_path,
|
||||
definition.line -1,
|
||||
definition.column )
|
||||
else:
|
||||
# multiple definitions
|
||||
defs = []
|
||||
for definition in definition_list:
|
||||
if definition.in_builtin_module():
|
||||
defs.append( {'text': 'Builtin ' + \
|
||||
definition.description.encode( 'utf-8' ) } )
|
||||
defs.append( server_responses.BuildDescriptionOnlyGoToResponse(
|
||||
'Builting ' + definition.description ) )
|
||||
else:
|
||||
defs.append( {'filename': definition.module_path.encode( 'utf-8' ),
|
||||
'lnum': definition.line,
|
||||
'col': definition.column + 1,
|
||||
'text': definition.description.encode( 'utf-8' ) } )
|
||||
defs.append(
|
||||
server_responses.BuildGoToResponse( definition.module_path,
|
||||
definition.line -1,
|
||||
definition.column,
|
||||
definition.description ) )
|
||||
return defs
|
||||
|
||||
vim.eval( 'setqflist( %s )' % repr( defs ) )
|
||||
vim.eval( 'youcompleteme#OpenGoToList()' )
|
||||
|
@ -52,11 +52,10 @@ class ThreadedCompleter( Completer ):
|
||||
self._completion_thread.start()
|
||||
|
||||
|
||||
def CandidatesForQueryAsyncInner( self, query, start_column ):
|
||||
def CandidatesForQueryAsyncInner( self, request_data ):
|
||||
self._candidates = None
|
||||
self._candidates_ready.clear()
|
||||
self._query = query
|
||||
self._start_column = start_column
|
||||
self._request_data = request_data
|
||||
self._query_ready.set()
|
||||
|
||||
|
||||
@ -69,7 +68,7 @@ class ThreadedCompleter( Completer ):
|
||||
|
||||
|
||||
@abc.abstractmethod
|
||||
def ComputeCandidates( self, query, start_column ):
|
||||
def ComputeCandidates( self, request_data ):
|
||||
"""This function should compute the candidates to show to the user.
|
||||
The return value should be of the same type as that for
|
||||
CandidatesFromStoredRequest()."""
|
||||
@ -80,8 +79,7 @@ class ThreadedCompleter( Completer ):
|
||||
while True:
|
||||
try:
|
||||
WaitAndClearIfSet( self._query_ready )
|
||||
self._candidates = self.ComputeCandidates( self._query,
|
||||
self._start_column )
|
||||
self._candidates = self.ComputeCandidates( self._request_data )
|
||||
except:
|
||||
self._query_ready.clear()
|
||||
self._candidates = []
|
||||
|
@ -68,6 +68,7 @@ def CallExtraConfVimCloseIfExists():
|
||||
def _CallExtraConfMethod( function_name ):
|
||||
vim_current_working_directory = vim.eval( 'getcwd()' )
|
||||
path_to_dummy = os.path.join( vim_current_working_directory, 'DUMMY_FILE' )
|
||||
# The dummy file in the Vim CWD ensures we find the correct extra conf file
|
||||
module = ModuleForSourceFile( path_to_dummy )
|
||||
if not module or not hasattr( module, function_name ):
|
||||
return
|
||||
|
76
python/ycm/server_responses.py
Normal file
76
python/ycm/server_responses.py
Normal file
@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2013 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/>.
|
||||
|
||||
|
||||
def BuildGoToResponse( filepath, line_num, column_num, description = None ):
|
||||
response = {
|
||||
'filepath': filepath,
|
||||
'line_num': line_num,
|
||||
'column_num': column_num
|
||||
}
|
||||
|
||||
if description:
|
||||
response[ 'description' ] = description
|
||||
return response
|
||||
|
||||
|
||||
def BuildDescriptionOnlyGoToResponse( text ):
|
||||
return {
|
||||
'description': text,
|
||||
}
|
||||
|
||||
|
||||
def BuildDisplayMessageResponse( text ):
|
||||
return {
|
||||
'message': text
|
||||
}
|
||||
|
||||
|
||||
def BuildCompletionData( insertion_text,
|
||||
extra_menu_info = None,
|
||||
detailed_info = None,
|
||||
menu_text = None,
|
||||
kind = None ):
|
||||
completion_data = {
|
||||
'insertion_text': insertion_text
|
||||
}
|
||||
|
||||
if extra_menu_info:
|
||||
completion_data[ 'extra_menu_info' ] = extra_menu_info
|
||||
if menu_text:
|
||||
completion_data[ 'menu_text' ] = menu_text
|
||||
if detailed_info:
|
||||
completion_data[ 'detailed_info' ] = detailed_info
|
||||
if kind:
|
||||
completion_data[ 'kind' ] = kind
|
||||
return completion_data
|
||||
|
||||
|
||||
def BuildDiagnosticData( filepath,
|
||||
line_num,
|
||||
column_num,
|
||||
text,
|
||||
kind ):
|
||||
return {
|
||||
'filepath': filepath,
|
||||
'line_num': line_num,
|
||||
'column_num': column_num,
|
||||
'text': text,
|
||||
'kind': kind
|
||||
}
|
@ -46,12 +46,33 @@ def TextAfterCursor():
|
||||
return vim.current.line[ CurrentColumn(): ]
|
||||
|
||||
|
||||
def GetUnsavedBuffers():
|
||||
def BufferModified( buffer_number ):
|
||||
to_eval = 'getbufvar({0}, "&mod")'.format( buffer_number )
|
||||
return GetBoolValue( to_eval )
|
||||
# Note the difference between buffer OPTIONS and VARIABLES; the two are not
|
||||
# the same.
|
||||
def GetBufferOption( buffer_object, option ):
|
||||
# The 'options' property is only available in recent (7.4+) Vim builds
|
||||
if hasattr( buffer_object, 'options' ):
|
||||
return buffer_object.options[ option ]
|
||||
|
||||
return ( x for x in vim.buffers if BufferModified( x.number ) )
|
||||
to_eval = 'getbufvar({0}, "&{1}")'.format( buffer.number, option )
|
||||
return GetVariableValue( to_eval )
|
||||
|
||||
|
||||
def GetUnsavedAndCurrentBufferData():
|
||||
def BufferModified( buffer_object ):
|
||||
return bool( int( GetBufferOption( buffer_object, 'mod' ) ) )
|
||||
|
||||
buffers_data = {}
|
||||
for buffer_object in vim.buffers:
|
||||
if not ( BufferModified( buffer_object ) or
|
||||
buffer_object == vim.current.buffer ):
|
||||
continue
|
||||
|
||||
buffers_data[ buffer_object.name ] = {
|
||||
'contents': '\n'.join( buffer_object ),
|
||||
'filetypes': FiletypesForBuffer( buffer_object )
|
||||
}
|
||||
|
||||
return buffers_data
|
||||
|
||||
|
||||
# Both |line| and |column| need to be 1-based
|
||||
@ -73,9 +94,9 @@ def JumpToLocation( filename, line, column ):
|
||||
vim.command( 'normal! zz' )
|
||||
|
||||
|
||||
def NumLinesInBuffer( buffer ):
|
||||
def NumLinesInBuffer( buffer_object ):
|
||||
# This is actually less than obvious, that's why it's wrapped in a function
|
||||
return len( buffer )
|
||||
return len( buffer_object )
|
||||
|
||||
|
||||
def PostVimMessage( message ):
|
||||
@ -128,15 +149,13 @@ def EscapeForVim( text ):
|
||||
|
||||
|
||||
def CurrentFiletypes():
|
||||
ft_string = vim.eval( "&filetype" )
|
||||
return ft_string.split( '.' )
|
||||
return vim.eval( "&filetype" ).split( '.' )
|
||||
|
||||
|
||||
def FiletypesForBuffer( buffer_object ):
|
||||
# NOTE: Getting &ft for other buffers only works when the buffer has been
|
||||
# visited by the user at least once, which is true for modified buffers
|
||||
ft_string = vim.eval( 'getbufvar({0}, "&ft")'.format( buffer_object.number ) )
|
||||
return ft_string.split( '.' )
|
||||
return GetBufferOption( buffer_object, 'ft' ).split( '.' )
|
||||
|
||||
|
||||
def GetVariableValue( variable ):
|
||||
|
@ -19,20 +19,25 @@
|
||||
|
||||
import imp
|
||||
import os
|
||||
import time
|
||||
import vim
|
||||
import ycm_core
|
||||
import logging
|
||||
import tempfile
|
||||
from ycm import vimsupport
|
||||
from ycm import base
|
||||
from ycm.completers.all.omni_completer import OmniCompleter
|
||||
from ycm.completers.general.general_completer_store import GeneralCompleterStore
|
||||
|
||||
|
||||
# TODO: Put the Request classes in separate files
|
||||
class CompletionRequest( object ):
|
||||
def __init__( self, ycm_state ):
|
||||
self._completion_start_column = base.CompletionStartColumn()
|
||||
self._ycm_state = ycm_state
|
||||
self._request_data = _BuildRequestData( self._completion_start_column )
|
||||
self._do_filetype_completion = self._ycm_state.ShouldUseFiletypeCompleter(
|
||||
self._completion_start_column )
|
||||
self._request_data )
|
||||
self._completer = ( self._ycm_state.GetFiletypeCompleter() if
|
||||
self._do_filetype_completion else
|
||||
self._ycm_state.GetGeneralCompleter() )
|
||||
@ -40,8 +45,7 @@ class CompletionRequest( object ):
|
||||
|
||||
def ShouldComplete( self ):
|
||||
return ( self._do_filetype_completion or
|
||||
self._ycm_state.ShouldUseGeneralCompleter(
|
||||
self._completion_start_column ) )
|
||||
self._ycm_state.ShouldUseGeneralCompleter( self._request_data ) )
|
||||
|
||||
|
||||
def CompletionStartColumn( self ):
|
||||
@ -49,20 +53,80 @@ class CompletionRequest( object ):
|
||||
|
||||
|
||||
def Start( self, query ):
|
||||
self._completer.CandidatesForQueryAsync( query,
|
||||
self._completion_start_column )
|
||||
self._request_data[ 'query' ] = query
|
||||
self._completer.CandidatesForQueryAsync( self._request_data )
|
||||
|
||||
def Done( self ):
|
||||
return self._completer.AsyncCandidateRequestReady()
|
||||
|
||||
|
||||
def Results( self ):
|
||||
return self._completer.CandidatesFromStoredRequest()
|
||||
try:
|
||||
return [ _ConvertCompletionDataToVimData( x )
|
||||
for x in self._completer.CandidatesFromStoredRequest() ]
|
||||
except Exception as e:
|
||||
vimsupport.PostVimMessage( str( e ) )
|
||||
return []
|
||||
|
||||
|
||||
|
||||
class CommandRequest( object ):
|
||||
class ServerResponse( object ):
|
||||
def __init__( self ):
|
||||
pass
|
||||
|
||||
def Valid( self ):
|
||||
return True
|
||||
|
||||
def __init__( self, ycm_state, arguments, completer_target = None ):
|
||||
if not completer_target:
|
||||
completer_target = 'filetpe_default'
|
||||
|
||||
if completer_target == 'omni':
|
||||
self._completer = ycm_state.GetOmniCompleter()
|
||||
elif completer_target == 'identifier':
|
||||
self._completer = ycm_state.GetGeneralCompleter()
|
||||
else:
|
||||
self._completer = ycm_state.GetFiletypeCompleter()
|
||||
self._arguments = arguments
|
||||
|
||||
|
||||
def Start( self ):
|
||||
self._completer.OnUserCommand( self._arguments,
|
||||
_BuildRequestData() )
|
||||
|
||||
def Done( self ):
|
||||
return True
|
||||
|
||||
|
||||
def Response( self ):
|
||||
# TODO: Call vimsupport.JumpToLocation if the user called a GoTo command...
|
||||
# we may want to have specific subclasses of CommandRequest so that a
|
||||
# GoToRequest knows it needs to jump after the data comes back.
|
||||
#
|
||||
# Also need to run the following on GoTo data:
|
||||
# CAREFUL about line/column number 0-based/1-based confusion!
|
||||
#
|
||||
# defs = []
|
||||
# defs.append( {'filename': definition.module_path.encode( 'utf-8' ),
|
||||
# 'lnum': definition.line,
|
||||
# 'col': definition.column + 1,
|
||||
# 'text': definition.description.encode( 'utf-8' ) } )
|
||||
# vim.eval( 'setqflist( %s )' % repr( defs ) )
|
||||
# vim.eval( 'youcompleteme#OpenGoToList()' )
|
||||
return self.ServerResponse()
|
||||
|
||||
|
||||
|
||||
class YouCompleteMe( object ):
|
||||
def __init__( self, user_options ):
|
||||
# TODO: This should go into the server
|
||||
# TODO: Use more logging like we do in cs_completer
|
||||
self._logfile = tempfile.NamedTemporaryFile()
|
||||
logging.basicConfig( format='%(asctime)s - %(levelname)s - %(message)s',
|
||||
filename=self._logfile.name,
|
||||
level=logging.DEBUG )
|
||||
|
||||
self._user_options = user_options
|
||||
self._gencomp = GeneralCompleterStore( user_options )
|
||||
self._omnicomp = OmniCompleter( user_options )
|
||||
@ -78,6 +142,16 @@ class YouCompleteMe( object ):
|
||||
return self._current_completion_request
|
||||
|
||||
|
||||
def SendCommandRequest( self, arguments, completer ):
|
||||
# TODO: This should be inside a method in a command_request module
|
||||
request = CommandRequest( self, arguments, completer )
|
||||
request.Start()
|
||||
while not request.Done():
|
||||
time.sleep( 0.1 )
|
||||
|
||||
return request.Response()
|
||||
|
||||
|
||||
def GetCurrentCompletionRequest( self ):
|
||||
return self._current_completion_request
|
||||
|
||||
@ -131,14 +205,13 @@ class YouCompleteMe( object ):
|
||||
return completer
|
||||
|
||||
|
||||
def ShouldUseGeneralCompleter( self, start_column ):
|
||||
return self._gencomp.ShouldUseNow( start_column, vim.current.line )
|
||||
def ShouldUseGeneralCompleter( self, request_data ):
|
||||
return self._gencomp.ShouldUseNow( request_data )
|
||||
|
||||
|
||||
def ShouldUseFiletypeCompleter( self, start_column ):
|
||||
def ShouldUseFiletypeCompleter( self, request_data ):
|
||||
if self.FiletypeCompletionUsable():
|
||||
return self.GetFiletypeCompleter().ShouldUseNow(
|
||||
start_column, vim.current.line )
|
||||
return self.GetFiletypeCompleter().ShouldUseNow( request_data )
|
||||
return False
|
||||
|
||||
|
||||
@ -162,10 +235,10 @@ class YouCompleteMe( object ):
|
||||
|
||||
|
||||
def OnFileReadyToParse( self ):
|
||||
self._gencomp.OnFileReadyToParse()
|
||||
self._gencomp.OnFileReadyToParse( _BuildRequestData() )
|
||||
|
||||
if self.FiletypeCompletionUsable():
|
||||
self.GetFiletypeCompleter().OnFileReadyToParse()
|
||||
self.GetFiletypeCompleter().OnFileReadyToParse( _BuildRequestData() )
|
||||
|
||||
|
||||
def OnBufferUnload( self, deleted_buffer_file ):
|
||||
@ -208,9 +281,9 @@ class YouCompleteMe( object ):
|
||||
return []
|
||||
|
||||
|
||||
def ShowDetailedDiagnostic( self ):
|
||||
def GetDetailedDiagnostic( self ):
|
||||
if self.FiletypeCompletionUsable():
|
||||
return self.GetFiletypeCompleter().ShowDetailedDiagnostic()
|
||||
return self.GetFiletypeCompleter().GetDetailedDiagnostic()
|
||||
|
||||
|
||||
def GettingCompletions( self ):
|
||||
@ -263,3 +336,37 @@ def _PathToFiletypeCompleterPluginLoader( filetype ):
|
||||
return os.path.join( _PathToCompletersFolder(), filetype, 'hook.py' )
|
||||
|
||||
|
||||
def _BuildRequestData( start_column = None, query = None ):
|
||||
line, column = vimsupport.CurrentLineAndColumn()
|
||||
request_data = {
|
||||
'filetypes': vimsupport.CurrentFiletypes(),
|
||||
'line_num': line,
|
||||
'column_num': column,
|
||||
'start_column': start_column,
|
||||
'line_value': vim.current.line,
|
||||
'filepath': vim.current.buffer.name,
|
||||
'file_data': vimsupport.GetUnsavedAndCurrentBufferData()
|
||||
}
|
||||
|
||||
if query:
|
||||
request_data[ 'query' ] = query
|
||||
|
||||
return request_data
|
||||
|
||||
def _ConvertCompletionDataToVimData( completion_data ):
|
||||
# see :h complete-items for a description of the dictionary fields
|
||||
vim_data = {
|
||||
'word' : completion_data[ 'insertion_text' ],
|
||||
'dup' : 1,
|
||||
}
|
||||
|
||||
if 'menu_text' in completion_data:
|
||||
vim_data[ 'abbr' ] = completion_data[ 'menu_text' ]
|
||||
if 'extra_menu_info' in completion_data:
|
||||
vim_data[ 'menu' ] = completion_data[ 'extra_menu_info' ]
|
||||
if 'kind' in completion_data:
|
||||
vim_data[ 'kind' ] = completion_data[ 'kind' ]
|
||||
if 'detailed_info' in completion_data:
|
||||
vim_data[ 'info' ] = completion_data[ 'detailed_info' ]
|
||||
|
||||
return vim_data
|
||||
|
Loading…
Reference in New Issue
Block a user