Diagnostics work again... somewhat.
There appear to be timing issues for the diag requests. Somehow, we're sending out-of-date diagnostics and then not updating the UI when things change. That needs to be fixed.
This commit is contained in:
parent
e44fd87582
commit
b9bb788a2a
@ -260,7 +260,7 @@ function! s:OnCursorHold()
|
|||||||
call s:SetUpCompleteopt()
|
call s:SetUpCompleteopt()
|
||||||
" Order is important here; we need to extract any done diagnostics before
|
" Order is important here; we need to extract any done diagnostics before
|
||||||
" reparsing the file again
|
" reparsing the file again
|
||||||
" call s:UpdateDiagnosticNotifications()
|
call s:UpdateDiagnosticNotifications()
|
||||||
call s:OnFileReadyToParse()
|
call s:OnFileReadyToParse()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@ -272,6 +272,7 @@ function! s:OnFileReadyToParse()
|
|||||||
|
|
||||||
let buffer_changed = b:changedtick != b:ycm_changedtick.file_ready_to_parse
|
let buffer_changed = b:changedtick != b:ycm_changedtick.file_ready_to_parse
|
||||||
if buffer_changed
|
if buffer_changed
|
||||||
|
py ycm_state.RequestDiagnosticsForCurrentFile()
|
||||||
py ycm_state.OnFileReadyToParse()
|
py ycm_state.OnFileReadyToParse()
|
||||||
endif
|
endif
|
||||||
let b:ycm_changedtick.file_ready_to_parse = b:changedtick
|
let b:ycm_changedtick.file_ready_to_parse = b:changedtick
|
||||||
@ -327,7 +328,7 @@ function! s:OnCursorMovedNormalMode()
|
|||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" call s:UpdateDiagnosticNotifications()
|
call s:UpdateDiagnosticNotifications()
|
||||||
call s:OnFileReadyToParse()
|
call s:OnFileReadyToParse()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@ -338,7 +339,7 @@ function! s:OnInsertLeave()
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
let s:omnifunc_mode = 0
|
let s:omnifunc_mode = 0
|
||||||
" call s:UpdateDiagnosticNotifications()
|
call s:UpdateDiagnosticNotifications()
|
||||||
call s:OnFileReadyToParse()
|
call s:OnFileReadyToParse()
|
||||||
py ycm_state.OnInsertLeave()
|
py ycm_state.OnInsertLeave()
|
||||||
if g:ycm_autoclose_preview_window_after_completion ||
|
if g:ycm_autoclose_preview_window_after_completion ||
|
||||||
@ -408,10 +409,16 @@ endfunction
|
|||||||
|
|
||||||
|
|
||||||
function! s:UpdateDiagnosticNotifications()
|
function! s:UpdateDiagnosticNotifications()
|
||||||
if get( g:, 'loaded_syntastic_plugin', 0 ) &&
|
let should_display_diagnostics =
|
||||||
\ pyeval( 'ycm_state.NativeFiletypeCompletionUsable()' ) &&
|
\ get( g:, 'loaded_syntastic_plugin', 0 ) &&
|
||||||
\ pyeval( 'ycm_state.DiagnosticsForCurrentFileReady()' ) &&
|
\ g:ycm_register_as_syntastic_checker &&
|
||||||
\ g:ycm_register_as_syntastic_checker
|
\ pyeval( 'ycm_state.NativeFiletypeCompletionUsable()' )
|
||||||
|
|
||||||
|
if !should_display_diagnostics
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if pyeval( 'ycm_state.DiagnosticsForCurrentFileReady()' )
|
||||||
SyntasticCheck
|
SyntasticCheck
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
@ -566,9 +573,7 @@ command! YcmShowDetailedDiagnostic call s:ShowDetailedDiagnostic()
|
|||||||
" required (currently that's on buffer save) OR when the SyntasticCheck command
|
" required (currently that's on buffer save) OR when the SyntasticCheck command
|
||||||
" is invoked
|
" is invoked
|
||||||
function! youcompleteme#CurrentFileDiagnostics()
|
function! youcompleteme#CurrentFileDiagnostics()
|
||||||
" TODO: Make this work again.
|
return pyeval( 'ycm_state.GetDiagnosticsFromStoredRequest()' )
|
||||||
" return pyeval( 'ycm_state.GetDiagnosticsForCurrentFile()' )
|
|
||||||
return []
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
@ -92,25 +92,11 @@ void TranslationUnit::Destroy() {
|
|||||||
|
|
||||||
|
|
||||||
std::vector< Diagnostic > TranslationUnit::LatestDiagnostics() {
|
std::vector< Diagnostic > TranslationUnit::LatestDiagnostics() {
|
||||||
std::vector< Diagnostic > diagnostics;
|
|
||||||
|
|
||||||
if ( !clang_translation_unit_ )
|
if ( !clang_translation_unit_ )
|
||||||
return diagnostics;
|
return std::vector< Diagnostic >();
|
||||||
|
|
||||||
unique_lock< mutex > lock( diagnostics_mutex_ );
|
unique_lock< mutex > lock( diagnostics_mutex_ );
|
||||||
|
return latest_diagnostics_;
|
||||||
// We don't need the latest diags after we return them once so we swap the
|
|
||||||
// internal data with a new, empty diag vector. This vector is then returned
|
|
||||||
// and on C++11 compilers a move ctor is invoked, thus no copy is created.
|
|
||||||
// Theoretically, just returning the value of a
|
|
||||||
// [boost::|std::]move(latest_diagnostics_) call _should_ leave the
|
|
||||||
// latest_diagnostics_ vector in an emtpy, valid state but I'm not going to
|
|
||||||
// rely on that. I just had to look this up in the standard to be sure, and
|
|
||||||
// future readers of this code (myself included) should not be forced to do
|
|
||||||
// that to understand what the hell is going on.
|
|
||||||
|
|
||||||
std::swap( latest_diagnostics_, diagnostics );
|
|
||||||
return diagnostics;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class CommandRequest( BaseRequest ):
|
|||||||
self._completer_target = ( completer_target if completer_target
|
self._completer_target = ( completer_target if completer_target
|
||||||
else 'filetype_default' )
|
else 'filetype_default' )
|
||||||
self._is_goto_command = (
|
self._is_goto_command = (
|
||||||
True if arguments and arguments[ 0 ].startswith( 'GoTo' ) else False )
|
arguments and arguments[ 0 ].startswith( 'GoTo' ) )
|
||||||
self._response = None
|
self._response = None
|
||||||
|
|
||||||
|
|
||||||
|
75
python/ycm/client/diagnostics_request.py
Normal file
75
python/ycm/client/diagnostics_request.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#!/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/>.
|
||||||
|
|
||||||
|
import traceback
|
||||||
|
from ycm import vimsupport
|
||||||
|
from ycm.client.base_request import ( BaseRequest, BuildRequestData,
|
||||||
|
JsonFromFuture )
|
||||||
|
|
||||||
|
|
||||||
|
class DiagnosticsRequest( BaseRequest ):
|
||||||
|
def __init__( self ):
|
||||||
|
super( DiagnosticsRequest, self ).__init__()
|
||||||
|
self._cached_response = None
|
||||||
|
|
||||||
|
|
||||||
|
def Start( self ):
|
||||||
|
request_data = BuildRequestData()
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._response_future = self.PostDataToHandlerAsync( request_data,
|
||||||
|
'diagnostics' )
|
||||||
|
except:
|
||||||
|
vimsupport.EchoText( traceback.format_exc() )
|
||||||
|
|
||||||
|
|
||||||
|
def Done( self ):
|
||||||
|
return self._response_future.done()
|
||||||
|
|
||||||
|
|
||||||
|
def Response( self ):
|
||||||
|
if self._cached_response:
|
||||||
|
return self._cached_response
|
||||||
|
|
||||||
|
if not self._response_future:
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
self._cached_response = [ _ConvertDiagnosticDataToVimData( x )
|
||||||
|
for x in JsonFromFuture(
|
||||||
|
self._response_future ) ]
|
||||||
|
return self._cached_response
|
||||||
|
except Exception as e:
|
||||||
|
vimsupport.PostVimMessage( str( e ) )
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _ConvertDiagnosticDataToVimData( diagnostic ):
|
||||||
|
# see :h getqflist for a description of the dictionary fields
|
||||||
|
# Note that, as usual, Vim is completely inconsistent about whether
|
||||||
|
# line/column numbers are 1 or 0 based in its various APIs. Here, it wants
|
||||||
|
# them to be 1-based.
|
||||||
|
return {
|
||||||
|
'bufnr' : vimsupport.GetBufferNumberForFilename( diagnostic[ 'filepath' ]),
|
||||||
|
'lnum' : diagnostic[ 'line_num' ] + 1,
|
||||||
|
'col' : diagnostic[ 'column_num' ] + 1,
|
||||||
|
'text' : diagnostic[ 'text' ],
|
||||||
|
'type' : diagnostic[ 'kind' ],
|
||||||
|
'valid' : 1
|
||||||
|
}
|
||||||
|
|
@ -233,11 +233,7 @@ class Completer( object ):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def DiagnosticsForCurrentFileReady( self ):
|
def GetDiagnosticsForCurrentFile( self, request_data ):
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def GetDiagnosticsForCurrentFile( self ):
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,7 +44,6 @@ class ClangCompleter( Completer ):
|
|||||||
self._max_diagnostics_to_display = user_options[
|
self._max_diagnostics_to_display = user_options[
|
||||||
'max_diagnostics_to_display' ]
|
'max_diagnostics_to_display' ]
|
||||||
self._completer = ycm_core.ClangCompleter()
|
self._completer = ycm_core.ClangCompleter()
|
||||||
self._last_prepared_diagnostics = []
|
|
||||||
self._flags = Flags()
|
self._flags = Flags()
|
||||||
self._diagnostic_store = None
|
self._diagnostic_store = None
|
||||||
self._logger = logging.getLogger( __name__ )
|
self._logger = logging.getLogger( __name__ )
|
||||||
@ -213,13 +212,6 @@ class ClangCompleter( Completer ):
|
|||||||
ToUtf8IfNeeded( request_data[ 'unloaded_buffer' ] ) )
|
ToUtf8IfNeeded( request_data[ 'unloaded_buffer' ] ) )
|
||||||
|
|
||||||
|
|
||||||
def DiagnosticsForCurrentFileReady( self ):
|
|
||||||
# if not self.parse_future:
|
|
||||||
# return False
|
|
||||||
# return self.parse_future.ResultsReady()
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def GettingCompletions( self, request_data ):
|
def GettingCompletions( self, request_data ):
|
||||||
return self._completer.UpdatingTranslationUnit(
|
return self._completer.UpdatingTranslationUnit(
|
||||||
ToUtf8IfNeeded( request_data[ 'filepath' ] ) )
|
ToUtf8IfNeeded( request_data[ 'filepath' ] ) )
|
||||||
@ -227,19 +219,11 @@ class ClangCompleter( Completer ):
|
|||||||
|
|
||||||
def GetDiagnosticsForCurrentFile( self, request_data ):
|
def GetDiagnosticsForCurrentFile( self, request_data ):
|
||||||
filename = request_data[ 'filepath' ]
|
filename = request_data[ 'filepath' ]
|
||||||
if self.DiagnosticsForCurrentFileReady():
|
diagnostics = self._completer.DiagnosticsForFile(
|
||||||
diagnostics = self._completer.DiagnosticsForFile(
|
ToUtf8IfNeeded( filename ) )
|
||||||
ToUtf8IfNeeded( filename ) )
|
self._diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
|
||||||
self._diagnostic_store = DiagnosticsToDiagStructure( diagnostics )
|
return [ ConvertToDiagnosticResponse( x ) for x in
|
||||||
self._last_prepared_diagnostics = [
|
diagnostics[ : self._max_diagnostics_to_display ] ]
|
||||||
responses.BuildDiagnosticData( x ) for x in
|
|
||||||
diagnostics[ : self._max_diagnostics_to_display ] ]
|
|
||||||
# self.parse_future = None
|
|
||||||
|
|
||||||
# if self.extra_parse_desired:
|
|
||||||
# self.OnFileReadyToParse( request_data )
|
|
||||||
|
|
||||||
return self._last_prepared_diagnostics
|
|
||||||
|
|
||||||
|
|
||||||
def GetDetailedDiagnostic( self, request_data ):
|
def GetDetailedDiagnostic( self, request_data ):
|
||||||
@ -279,6 +263,7 @@ class ClangCompleter( Completer ):
|
|||||||
source,
|
source,
|
||||||
list( flags ) )
|
list( flags ) )
|
||||||
|
|
||||||
|
|
||||||
def _FlagsForRequest( self, request_data ):
|
def _FlagsForRequest( self, request_data ):
|
||||||
filename = request_data[ 'filepath' ]
|
filename = request_data[ 'filepath' ]
|
||||||
if 'compilation_flags' in request_data:
|
if 'compilation_flags' in request_data:
|
||||||
@ -286,20 +271,6 @@ class ClangCompleter( Completer ):
|
|||||||
filename )
|
filename )
|
||||||
return self._flags.FlagsForFile( filename )
|
return self._flags.FlagsForFile( filename )
|
||||||
|
|
||||||
# TODO: Make this work again
|
|
||||||
# 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 ):
|
def ConvertCompletionData( completion_data ):
|
||||||
return responses.BuildCompletionData(
|
return responses.BuildCompletionData(
|
||||||
@ -326,3 +297,11 @@ def InCFamilyFile( filetypes ):
|
|||||||
return ClangAvailableForFiletypes( filetypes )
|
return ClangAvailableForFiletypes( filetypes )
|
||||||
|
|
||||||
|
|
||||||
|
def ConvertToDiagnosticResponse( diagnostic ):
|
||||||
|
return responses.BuildDiagnosticData( diagnostic.filename_,
|
||||||
|
diagnostic.line_number_ - 1,
|
||||||
|
diagnostic.column_number_ - 1,
|
||||||
|
diagnostic.text_,
|
||||||
|
diagnostic.kind_ )
|
||||||
|
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
{
"filepath_completion_use_working_dir": 0,
"min_num_of_chars_for_completion": 2,
"semantic_triggers": {},
"collect_identifiers_from_comments_and_strings": 0,
"filetype_specific_completion_to_disable": {},
"collect_identifiers_from_tags_files": 0,
"extra_conf_globlist": [],
"global_ycm_extra_conf": "",
"confirm_extra_conf": 1,
"complete_in_comments": 0,
"complete_in_strings": 1,
"min_num_identifier_candidate_chars": 0,
"max_diagnostics_to_display": 30,
"auto_stop_csharp_server": 1,
"seed_identifiers_with_syntax": 0,
"csharp_server_port": 2000,
"filetype_whitelist": {
"*": "1"
},
"auto_start_csharp_server": 1,
"filetype_blacklist": {
"tagbar": "1",
"qf": "1",
"gitcommit": "1",
"notes": "1",
"markdown": "1",
"unite": "1",
"text": "1"
}
}
|
{
"filepath_completion_use_working_dir": 0,
"min_num_of_chars_for_completion": 2,
"semantic_triggers": {},
"collect_identifiers_from_comments_and_strings": 0,
"filetype_specific_completion_to_disable": {
"gitcommit": 1,
},
"collect_identifiers_from_tags_files": 0,
"extra_conf_globlist": [],
"global_ycm_extra_conf": "",
"confirm_extra_conf": 1,
"complete_in_comments": 0,
"complete_in_strings": 1,
"min_num_identifier_candidate_chars": 0,
"max_diagnostics_to_display": 30,
"auto_stop_csharp_server": 1,
"seed_identifiers_with_syntax": 0,
"csharp_server_port": 2000,
"filetype_whitelist": {
"*": "1"
},
"auto_start_csharp_server": 1,
"filetype_blacklist": {
"tagbar": "1",
"qf": "1",
"notes": "1",
"markdown": "1",
"unite": "1",
"text": "1"
}
}
|
@ -21,7 +21,8 @@ from webtest import TestApp
|
|||||||
from .. import ycmd
|
from .. import ycmd
|
||||||
from ..responses import BuildCompletionData
|
from ..responses import BuildCompletionData
|
||||||
from nose.tools import ok_, eq_, with_setup
|
from nose.tools import ok_, eq_, with_setup
|
||||||
from hamcrest import assert_that, has_items, has_entry
|
from hamcrest import ( assert_that, has_items, has_entry, contains,
|
||||||
|
contains_string, has_entries )
|
||||||
import bottle
|
import bottle
|
||||||
|
|
||||||
bottle.debug( True )
|
bottle.debug( True )
|
||||||
@ -281,6 +282,47 @@ def DefinedSubcommands_WorksWhenNoExplicitCompleterTargetSpecified_test():
|
|||||||
app.post_json( '/defined_subcommands', subcommands_data ).json )
|
app.post_json( '/defined_subcommands', subcommands_data ).json )
|
||||||
|
|
||||||
|
|
||||||
|
@with_setup( Setup )
|
||||||
|
def GetDiagnostics_ClangCompleter_ZeroBasedLineAndColumn_test():
|
||||||
|
app = TestApp( ycmd.app )
|
||||||
|
contents = """
|
||||||
|
struct Foo {
|
||||||
|
int x // semicolon missing here!
|
||||||
|
int y;
|
||||||
|
int c;
|
||||||
|
int d;
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
filename = '/foo.cpp'
|
||||||
|
diag_data = {
|
||||||
|
'compilation_flags': ['-x', 'c++'],
|
||||||
|
'line_num': 0,
|
||||||
|
'column_num': 0,
|
||||||
|
'filetypes': ['cpp'],
|
||||||
|
'filepath': filename,
|
||||||
|
'file_data': {
|
||||||
|
filename: {
|
||||||
|
'contents': contents,
|
||||||
|
'filetypes': ['cpp']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event_data = diag_data.copy()
|
||||||
|
event_data.update( {
|
||||||
|
'event_name': 'FileReadyToParse',
|
||||||
|
} )
|
||||||
|
|
||||||
|
app.post_json( '/event_notification', event_data )
|
||||||
|
results = app.post_json( '/diagnostics', diag_data ).json
|
||||||
|
assert_that( results,
|
||||||
|
contains(
|
||||||
|
has_entries( { 'text': contains_string( "expected ';'" ),
|
||||||
|
'line_num': 2,
|
||||||
|
'column_num': 7 } ) ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
@with_setup( Setup )
|
||||||
def FiletypeCompletionAvailable_Works_test():
|
def FiletypeCompletionAvailable_Works_test():
|
||||||
app = TestApp( ycmd.app )
|
app = TestApp( ycmd.app )
|
||||||
|
@ -94,18 +94,35 @@ def GetCompletions():
|
|||||||
return _JsonResponse( completer.ComputeCandidates( request_data ) )
|
return _JsonResponse( completer.ComputeCandidates( request_data ) )
|
||||||
|
|
||||||
|
|
||||||
|
@app.post( '/diagnostics' )
|
||||||
|
def GetDiagnostics():
|
||||||
|
LOGGER.info( 'Received diagnostics request')
|
||||||
|
request_data = request.json
|
||||||
|
completer = _GetCompleterForRequestData( request_data )
|
||||||
|
|
||||||
|
return _JsonResponse( completer.GetDiagnosticsForCurrentFile(
|
||||||
|
request_data ) )
|
||||||
|
|
||||||
|
|
||||||
@app.get( '/user_options' )
|
@app.get( '/user_options' )
|
||||||
def GetUserOptions():
|
def GetUserOptions():
|
||||||
LOGGER.info( 'Received user options GET request')
|
LOGGER.info( 'Received user options GET request')
|
||||||
return _JsonResponse( dict( SERVER_STATE.user_options ) )
|
return _JsonResponse( dict( SERVER_STATE.user_options ) )
|
||||||
|
|
||||||
|
|
||||||
|
@app.get( '/healthy' )
|
||||||
|
def GetHealthy():
|
||||||
|
LOGGER.info( 'Received health request')
|
||||||
|
return _JsonResponse( True )
|
||||||
|
|
||||||
|
|
||||||
@app.post( '/user_options' )
|
@app.post( '/user_options' )
|
||||||
def SetUserOptions():
|
def SetUserOptions():
|
||||||
LOGGER.info( 'Received user options POST request')
|
LOGGER.info( 'Received user options POST request')
|
||||||
_SetUserOptions( request.json )
|
_SetUserOptions( request.json )
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Rename this to 'semantic_completion_available'
|
||||||
@app.post( '/filetype_completion_available')
|
@app.post( '/filetype_completion_available')
|
||||||
def FiletypeCompletionAvailable():
|
def FiletypeCompletionAvailable():
|
||||||
LOGGER.info( 'Received filetype completion available request')
|
LOGGER.info( 'Received filetype completion available request')
|
||||||
|
@ -21,6 +21,7 @@ import tempfile
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import signal
|
import signal
|
||||||
|
import functools
|
||||||
|
|
||||||
def IsIdentifierChar( char ):
|
def IsIdentifierChar( char ):
|
||||||
return char.isalnum() or char == '_'
|
return char.isalnum() or char == '_'
|
||||||
@ -63,4 +64,13 @@ def AddThirdPartyFoldersToSysPath():
|
|||||||
sys.path.insert( 0, os.path.realpath( os.path.join( path_to_third_party,
|
sys.path.insert( 0, os.path.realpath( os.path.join( path_to_third_party,
|
||||||
folder ) ) )
|
folder ) ) )
|
||||||
|
|
||||||
|
def Memoize( obj ):
|
||||||
|
cache = obj.cache = {}
|
||||||
|
|
||||||
|
@functools.wraps( obj )
|
||||||
|
def memoizer( *args, **kwargs ):
|
||||||
|
key = str( args ) + str( kwargs )
|
||||||
|
if key not in cache:
|
||||||
|
cache[ key ] = obj( *args, **kwargs )
|
||||||
|
return cache[ key ]
|
||||||
|
return memoizer
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import vim
|
import vim
|
||||||
|
import os
|
||||||
|
|
||||||
def CurrentLineAndColumn():
|
def CurrentLineAndColumn():
|
||||||
"""Returns the 0-based current line and 0-based current column."""
|
"""Returns the 0-based current line and 0-based current column."""
|
||||||
@ -75,6 +76,12 @@ def GetUnsavedAndCurrentBufferData():
|
|||||||
return buffers_data
|
return buffers_data
|
||||||
|
|
||||||
|
|
||||||
|
def GetBufferNumberForFilename( filename, open_file_if_needed = True ):
|
||||||
|
return int( vim.eval( "bufnr('{0}', {1})".format(
|
||||||
|
os.path.realpath( filename ),
|
||||||
|
int( open_file_if_needed ) ) ) )
|
||||||
|
|
||||||
|
|
||||||
# Both |line| and |column| need to be 1-based
|
# Both |line| and |column| need to be 1-based
|
||||||
def JumpToLocation( filename, line, column ):
|
def JumpToLocation( filename, line, column ):
|
||||||
# Add an entry to the jumplist
|
# Add an entry to the jumplist
|
||||||
|
@ -29,6 +29,7 @@ from ycm.completers.general import syntax_parse
|
|||||||
from ycm.client.base_request import BaseRequest, BuildRequestData
|
from ycm.client.base_request import BaseRequest, BuildRequestData
|
||||||
from ycm.client.command_request import SendCommandRequest
|
from ycm.client.command_request import SendCommandRequest
|
||||||
from ycm.client.completion_request import CompletionRequest
|
from ycm.client.completion_request import CompletionRequest
|
||||||
|
from ycm.client.diagnostics_request import DiagnosticsRequest
|
||||||
from ycm.client.event_notification import SendEventNotificationAsync
|
from ycm.client.event_notification import SendEventNotificationAsync
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -43,7 +44,8 @@ class YouCompleteMe( object ):
|
|||||||
def __init__( self, user_options ):
|
def __init__( self, user_options ):
|
||||||
self._user_options = user_options
|
self._user_options = user_options
|
||||||
self._omnicomp = OmniCompleter( user_options )
|
self._omnicomp = OmniCompleter( user_options )
|
||||||
self._current_completion_request = None
|
self._latest_completion_request = None
|
||||||
|
self._latest_diagnostics_request = None
|
||||||
self._server_stdout = None
|
self._server_stdout = None
|
||||||
self._server_stderr = None
|
self._server_stderr = None
|
||||||
self._server_popen = None
|
self._server_popen = None
|
||||||
@ -91,8 +93,8 @@ class YouCompleteMe( object ):
|
|||||||
# We have to store a reference to the newly created CompletionRequest
|
# We have to store a reference to the newly created CompletionRequest
|
||||||
# because VimScript can't store a reference to a Python object across
|
# because VimScript can't store a reference to a Python object across
|
||||||
# function calls... Thus we need to keep this request somewhere.
|
# function calls... Thus we need to keep this request somewhere.
|
||||||
self._current_completion_request = CompletionRequest()
|
self._latest_completion_request = CompletionRequest()
|
||||||
return self._current_completion_request
|
return self._latest_completion_request
|
||||||
|
|
||||||
|
|
||||||
def SendCommandRequest( self, arguments, completer ):
|
def SendCommandRequest( self, arguments, completer ):
|
||||||
@ -105,7 +107,7 @@ class YouCompleteMe( object ):
|
|||||||
|
|
||||||
|
|
||||||
def GetCurrentCompletionRequest( self ):
|
def GetCurrentCompletionRequest( self ):
|
||||||
return self._current_completion_request
|
return self._latest_completion_request
|
||||||
|
|
||||||
|
|
||||||
def GetOmniCompleter( self ):
|
def GetOmniCompleter( self ):
|
||||||
@ -114,8 +116,7 @@ class YouCompleteMe( object ):
|
|||||||
|
|
||||||
def NativeFiletypeCompletionAvailable( self ):
|
def NativeFiletypeCompletionAvailable( self ):
|
||||||
try:
|
try:
|
||||||
return BaseRequest.PostDataToHandler( BuildRequestData(),
|
return _NativeFiletypeCompletionAvailableForFile( vim.current.buffer.name )
|
||||||
'filetype_completion_available')
|
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -174,17 +175,25 @@ class YouCompleteMe( object ):
|
|||||||
SendEventNotificationAsync( 'CurrentIdentifierFinished' )
|
SendEventNotificationAsync( 'CurrentIdentifierFinished' )
|
||||||
|
|
||||||
|
|
||||||
# TODO: Make this work again.
|
|
||||||
def DiagnosticsForCurrentFileReady( self ):
|
def DiagnosticsForCurrentFileReady( self ):
|
||||||
# if self.FiletypeCompletionUsable():
|
return bool( self._latest_diagnostics_request and
|
||||||
# return self.GetFiletypeCompleter().DiagnosticsForCurrentFileReady()
|
self._latest_diagnostics_request.Done() )
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
# TODO: Make this work again.
|
def RequestDiagnosticsForCurrentFile( self ):
|
||||||
def GetDiagnosticsForCurrentFile( self ):
|
self._latest_diagnostics_request = DiagnosticsRequest()
|
||||||
# if self.FiletypeCompletionUsable():
|
self._latest_diagnostics_request.Start()
|
||||||
# return self.GetFiletypeCompleter().GetDiagnosticsForCurrentFile()
|
|
||||||
|
|
||||||
|
def GetDiagnosticsFromStoredRequest( self ):
|
||||||
|
if self._latest_diagnostics_request:
|
||||||
|
to_return = self._latest_diagnostics_request.Response()
|
||||||
|
# We set the diagnostics request to None because we want to prevent
|
||||||
|
# Syntastic from repeatedly refreshing the buffer with the same diags.
|
||||||
|
# Setting this to None makes DiagnosticsForCurrentFileReady return False
|
||||||
|
# until the next request is created.
|
||||||
|
self._latest_diagnostics_request = None
|
||||||
|
return to_return
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
@ -260,3 +269,12 @@ def _AddUltiSnipsDataIfNeeded( extra_data ):
|
|||||||
} for x in rawsnips ]
|
} for x in rawsnips ]
|
||||||
|
|
||||||
|
|
||||||
|
# 'filepath' is here only as a key for Memoize
|
||||||
|
# This can't be a nested function inside NativeFiletypeCompletionAvailable
|
||||||
|
# because then the Memoize decorator wouldn't work (nested functions are
|
||||||
|
# re-created on every call to the outer function).
|
||||||
|
@utils.Memoize
|
||||||
|
def _NativeFiletypeCompletionAvailableForFile( filepath ):
|
||||||
|
return BaseRequest.PostDataToHandler( BuildRequestData(),
|
||||||
|
'filetype_completion_available')
|
||||||
|
|
||||||
|
80
third_party/retries/retries.py
vendored
Normal file
80
third_party/retries/retries.py
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#
|
||||||
|
# Copyright 2012 by Jeff Laughlin Consulting LLC
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
# Source: https://gist.github.com/n1ywb/2570004
|
||||||
|
|
||||||
|
def example_exc_handler(tries_remaining, exception, delay):
|
||||||
|
"""Example exception handler; prints a warning to stderr.
|
||||||
|
|
||||||
|
tries_remaining: The number of tries remaining.
|
||||||
|
exception: The exception instance which was raised.
|
||||||
|
"""
|
||||||
|
print >> sys.stderr, "Caught '%s', %d tries remaining, sleeping for %s seconds" % (exception, tries_remaining, delay)
|
||||||
|
|
||||||
|
|
||||||
|
def retries(max_tries, delay=1, backoff=2, exceptions=(Exception,), hook=None):
|
||||||
|
"""Function decorator implementing retrying logic.
|
||||||
|
|
||||||
|
delay: Sleep this many seconds * backoff * try number after failure
|
||||||
|
backoff: Multiply delay by this factor after each failure
|
||||||
|
exceptions: A tuple of exception classes; default (Exception,)
|
||||||
|
hook: A function with the signature myhook(tries_remaining, exception);
|
||||||
|
default None
|
||||||
|
|
||||||
|
The decorator will call the function up to max_tries times if it raises
|
||||||
|
an exception.
|
||||||
|
|
||||||
|
By default it catches instances of the Exception class and subclasses.
|
||||||
|
This will recover after all but the most fatal errors. You may specify a
|
||||||
|
custom tuple of exception classes with the 'exceptions' argument; the
|
||||||
|
function will only be retried if it raises one of the specified
|
||||||
|
exceptions.
|
||||||
|
|
||||||
|
Additionally you may specify a hook function which will be called prior
|
||||||
|
to retrying with the number of remaining tries and the exception instance;
|
||||||
|
see given example. This is primarily intended to give the opportunity to
|
||||||
|
log the failure. Hook is not called after failure if no retries remain.
|
||||||
|
"""
|
||||||
|
def dec(func):
|
||||||
|
def f2(*args, **kwargs):
|
||||||
|
mydelay = delay
|
||||||
|
tries = range(max_tries)
|
||||||
|
tries.reverse()
|
||||||
|
for tries_remaining in tries:
|
||||||
|
try:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
except exceptions as e:
|
||||||
|
if tries_remaining > 0:
|
||||||
|
if hook is not None:
|
||||||
|
hook(tries_remaining, e, mydelay)
|
||||||
|
sleep(mydelay)
|
||||||
|
mydelay = mydelay * backoff
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return f2
|
||||||
|
return dec
|
Loading…
Reference in New Issue
Block a user