Auto merge of #1905 - davits:easy_handler, r=Valloric
[READY] Simplify parse request handling Moved file parse request handling into python, trying to simplify the overall flow, and allow easier additions of additional post handlers such as semantic token extraction. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/valloric/youcompleteme/1905) <!-- Reviewable:end -->
This commit is contained in:
commit
7b8b1acb5e
@ -29,14 +29,6 @@ let s:cursor_moved = 0
|
|||||||
let s:moved_vertically_in_insert_mode = 0
|
let s:moved_vertically_in_insert_mode = 0
|
||||||
let s:previous_num_chars_on_current_line = strlen( getline('.') )
|
let s:previous_num_chars_on_current_line = strlen( getline('.') )
|
||||||
|
|
||||||
let s:diagnostic_ui_filetypes = {
|
|
||||||
\ 'cpp': 1,
|
|
||||||
\ 'cs': 1,
|
|
||||||
\ 'c': 1,
|
|
||||||
\ 'objc': 1,
|
|
||||||
\ 'objcpp': 1,
|
|
||||||
\ }
|
|
||||||
|
|
||||||
|
|
||||||
function! youcompleteme#Enable()
|
function! youcompleteme#Enable()
|
||||||
" When vim is in diff mode, don't run
|
" When vim is in diff mode, don't run
|
||||||
@ -308,11 +300,6 @@ function! s:TurnOffSyntasticForCFamily()
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
function! s:DiagnosticUiSupportedForCurrentFiletype()
|
|
||||||
return get( s:diagnostic_ui_filetypes, &filetype, 0 )
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
function! s:AllowedToCompleteInCurrentFile()
|
function! s:AllowedToCompleteInCurrentFile()
|
||||||
if empty( &filetype ) ||
|
if empty( &filetype ) ||
|
||||||
\ getbufvar( winbufnr( winnr() ), "&buftype" ) ==# 'nofile' ||
|
\ getbufvar( winbufnr( winnr() ), "&buftype" ) ==# 'nofile' ||
|
||||||
@ -462,11 +449,11 @@ function! s:OnFileReadyToParse()
|
|||||||
" happen for special buffers.
|
" happen for special buffers.
|
||||||
call s:SetUpYcmChangedTick()
|
call s:SetUpYcmChangedTick()
|
||||||
|
|
||||||
" Order is important here; we need to extract any done diagnostics before
|
" Order is important here; we need to extract any information before
|
||||||
" reparsing the file again. If we sent the new parse request first, then
|
" reparsing the file again. If we sent the new parse request first, then
|
||||||
" the response would always be pending when we called
|
" the response would always be pending when we called
|
||||||
" UpdateDiagnosticNotifications.
|
" HandleFileParseRequest.
|
||||||
call s:UpdateDiagnosticNotifications()
|
py ycm_state.HandleFileParseRequest()
|
||||||
|
|
||||||
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
|
||||||
@ -612,19 +599,6 @@ function! s:ClosePreviewWindowIfNeeded()
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
function! s:UpdateDiagnosticNotifications()
|
|
||||||
let should_display_diagnostics = g:ycm_show_diagnostics_ui &&
|
|
||||||
\ s:DiagnosticUiSupportedForCurrentFiletype()
|
|
||||||
|
|
||||||
if !should_display_diagnostics
|
|
||||||
py ycm_state.ValidateParseRequest()
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
py ycm_state.UpdateDiagnosticInterface()
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
|
|
||||||
function! s:IdentifierFinishedOperations()
|
function! s:IdentifierFinishedOperations()
|
||||||
if !pyeval( 'base.CurrentIdentifierFinished()' )
|
if !pyeval( 'base.CurrentIdentifierFinished()' )
|
||||||
return
|
return
|
||||||
@ -853,15 +827,8 @@ function! s:ForceCompile()
|
|||||||
|
|
||||||
echom "Forcing compilation, this will block Vim until done."
|
echom "Forcing compilation, this will block Vim until done."
|
||||||
py ycm_state.OnFileReadyToParse()
|
py ycm_state.OnFileReadyToParse()
|
||||||
while 1
|
py ycm_state.HandleFileParseRequest( True )
|
||||||
let diagnostics_ready = pyeval(
|
|
||||||
\ 'ycm_state.DiagnosticsForCurrentFileReady()' )
|
|
||||||
if diagnostics_ready
|
|
||||||
break
|
|
||||||
endif
|
|
||||||
|
|
||||||
sleep 100m
|
|
||||||
endwhile
|
|
||||||
return 1
|
return 1
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@ -871,8 +838,6 @@ function! s:ForceCompileAndDiagnostics()
|
|||||||
if !compilation_succeeded
|
if !compilation_succeeded
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call s:UpdateDiagnosticNotifications()
|
|
||||||
echom "Diagnostics refreshed."
|
echom "Diagnostics refreshed."
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@ -883,11 +848,7 @@ function! s:ShowDiagnostics()
|
|||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let diags = pyeval(
|
if pyeval( 'ycm_state.PopulateLocationListWithLatestDiagnostics()' )
|
||||||
\ 'ycm_state.GetDiagnosticsFromStoredRequest( qflist_format = True )' )
|
|
||||||
if !empty( diags )
|
|
||||||
call setloclist( 0, diags )
|
|
||||||
|
|
||||||
if g:ycm_open_loclist_on_ycm_diags
|
if g:ycm_open_loclist_on_ycm_diags
|
||||||
lopen
|
lopen
|
||||||
endif
|
endif
|
||||||
|
@ -50,6 +50,11 @@ class DiagnosticInterface( object ):
|
|||||||
return len( self._FilterDiagnostics( _DiagnosticIsWarning ) )
|
return len( self._FilterDiagnostics( _DiagnosticIsWarning ) )
|
||||||
|
|
||||||
|
|
||||||
|
def PopulateLocationList( self, diags ):
|
||||||
|
vimsupport.SetLocationList(
|
||||||
|
vimsupport.ConvertDiagnosticsToQfList( diags ) )
|
||||||
|
|
||||||
|
|
||||||
def UpdateWithNewDiagnostics( self, diags ):
|
def UpdateWithNewDiagnostics( self, diags ):
|
||||||
normalized_diags = [ _NormalizeDiagnostic( x ) for x in diags ]
|
normalized_diags = [ _NormalizeDiagnostic( x ) for x in diags ]
|
||||||
self._buffer_number_to_line_to_diags = _ConvertDiagListToDict(
|
self._buffer_number_to_line_to_diags = _ConvertDiagListToDict(
|
||||||
@ -65,8 +70,7 @@ class DiagnosticInterface( object ):
|
|||||||
_UpdateSquiggles( self._buffer_number_to_line_to_diags )
|
_UpdateSquiggles( self._buffer_number_to_line_to_diags )
|
||||||
|
|
||||||
if self._user_options[ 'always_populate_location_list' ]:
|
if self._user_options[ 'always_populate_location_list' ]:
|
||||||
vimsupport.SetLocationList(
|
self.PopulateLocationList( normalized_diags )
|
||||||
vimsupport.ConvertDiagnosticsToQfList( normalized_diags ) )
|
|
||||||
|
|
||||||
def _EchoDiagnosticForLine( self, line_num ):
|
def _EchoDiagnosticForLine( self, line_num ):
|
||||||
buffer_num = vim.current.buffer.number
|
buffer_num = vim.current.buffer.number
|
||||||
|
@ -17,85 +17,85 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
# along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from nose.tools import eq_, ok_, with_setup
|
import contextlib
|
||||||
from mock import MagicMock
|
from nose.tools import eq_, ok_
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
from ycm.test_utils import MockVimModule
|
from ycm.test_utils import MockVimModule
|
||||||
vim_mock = MockVimModule()
|
vim_mock = MockVimModule()
|
||||||
from ycm import base
|
from ycm import base
|
||||||
from ycm import vimsupport
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# column is 0-based
|
|
||||||
def SetVimCurrentColumnAndLineValue( column, line_value ):
|
|
||||||
vimsupport.CurrentColumn = MagicMock( return_value = column )
|
|
||||||
vimsupport.CurrentLineContents = MagicMock( return_value = line_value )
|
|
||||||
|
|
||||||
|
|
||||||
def Setup():
|
@contextlib.contextmanager
|
||||||
sys.modules[ 'ycm.vimsupport' ] = MagicMock()
|
def MockCurrentFiletypes( filetypes = [''] ):
|
||||||
vimsupport.CurrentFiletypes = MagicMock( return_value = [''] )
|
with patch( 'ycm.vimsupport.CurrentFiletypes', return_value = filetypes ):
|
||||||
vimsupport.CurrentColumn = MagicMock( return_value = 1 )
|
yield
|
||||||
vimsupport.CurrentLineContents = MagicMock( return_value = '' )
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def MockCurrentColumnAndLineContents( column, line_contents ):
|
||||||
|
with patch( 'ycm.vimsupport.CurrentColumn', return_value = column ):
|
||||||
|
with patch( 'ycm.vimsupport.CurrentLineContents',
|
||||||
|
return_value = line_contents ):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def MockTextAfterCursor( text ):
|
||||||
|
with patch( 'ycm.vimsupport.TextAfterCursor', return_value = text ):
|
||||||
|
yield
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_Basic_test():
|
def AdjustCandidateInsertionText_Basic_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar' )
|
with MockTextAfterCursor( 'bar' ):
|
||||||
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
||||||
base.AdjustCandidateInsertionText( [ 'foobar' ] ) )
|
base.AdjustCandidateInsertionText( [ 'foobar' ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_ParenInTextAfterCursor_test():
|
def AdjustCandidateInsertionText_ParenInTextAfterCursor_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar(zoo' )
|
with MockTextAfterCursor( 'bar(zoo' ):
|
||||||
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
||||||
base.AdjustCandidateInsertionText( [ 'foobar' ] ) )
|
base.AdjustCandidateInsertionText( [ 'foobar' ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_PlusInTextAfterCursor_test():
|
def AdjustCandidateInsertionText_PlusInTextAfterCursor_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar+zoo' )
|
with MockTextAfterCursor( 'bar+zoo' ):
|
||||||
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
||||||
base.AdjustCandidateInsertionText( [ 'foobar' ] ) )
|
base.AdjustCandidateInsertionText( [ 'foobar' ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_WhitespaceInTextAfterCursor_test():
|
def AdjustCandidateInsertionText_WhitespaceInTextAfterCursor_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar zoo' )
|
with MockTextAfterCursor( 'bar zoo' ):
|
||||||
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
||||||
base.AdjustCandidateInsertionText( [ 'foobar' ] ) )
|
base.AdjustCandidateInsertionText( [ 'foobar' ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_MoreThanWordMatchingAfterCursor_test():
|
def AdjustCandidateInsertionText_MoreThanWordMatchingAfterCursor_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar.h' )
|
with MockTextAfterCursor( 'bar.h' ):
|
||||||
eq_( [ { 'abbr': 'foobar.h', 'word': 'foo' } ],
|
eq_( [ { 'abbr': 'foobar.h', 'word': 'foo' } ],
|
||||||
base.AdjustCandidateInsertionText( [ 'foobar.h' ] ) )
|
base.AdjustCandidateInsertionText( [ 'foobar.h' ] ) )
|
||||||
|
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar(zoo' )
|
with MockTextAfterCursor( 'bar(zoo' ):
|
||||||
eq_( [ { 'abbr': 'foobar(zoo', 'word': 'foo' } ],
|
eq_( [ { 'abbr': 'foobar(zoo', 'word': 'foo' } ],
|
||||||
base.AdjustCandidateInsertionText( [ 'foobar(zoo' ] ) )
|
base.AdjustCandidateInsertionText( [ 'foobar(zoo' ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_NotSuffix_test():
|
def AdjustCandidateInsertionText_NotSuffix_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar' )
|
with MockTextAfterCursor( 'bar' ):
|
||||||
eq_( [ { 'abbr': 'foofoo', 'word': 'foofoo' } ],
|
eq_( [ { 'abbr': 'foofoo', 'word': 'foofoo' } ],
|
||||||
base.AdjustCandidateInsertionText( [ 'foofoo' ] ) )
|
base.AdjustCandidateInsertionText( [ 'foofoo' ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_NothingAfterCursor_test():
|
def AdjustCandidateInsertionText_NothingAfterCursor_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = '' )
|
with MockTextAfterCursor( '' ):
|
||||||
eq_( [ 'foofoo',
|
eq_( [ 'foofoo',
|
||||||
'zobar' ],
|
'zobar' ],
|
||||||
base.AdjustCandidateInsertionText( [ 'foofoo',
|
base.AdjustCandidateInsertionText( [ 'foofoo',
|
||||||
'zobar' ] ) )
|
'zobar' ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_MultipleStrings_test():
|
def AdjustCandidateInsertionText_MultipleStrings_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar' )
|
with MockTextAfterCursor( 'bar' ):
|
||||||
eq_( [ { 'abbr': 'foobar', 'word': 'foo' },
|
eq_( [ { 'abbr': 'foobar', 'word': 'foo' },
|
||||||
{ 'abbr': 'zobar', 'word': 'zo' },
|
{ 'abbr': 'zobar', 'word': 'zo' },
|
||||||
{ 'abbr': 'qbar', 'word': 'q' },
|
{ 'abbr': 'qbar', 'word': 'q' },
|
||||||
@ -107,172 +107,158 @@ def AdjustCandidateInsertionText_MultipleStrings_test():
|
|||||||
'bar' ] ) )
|
'bar' ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_DictInput_test():
|
def AdjustCandidateInsertionText_DictInput_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar' )
|
with MockTextAfterCursor( 'bar' ):
|
||||||
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
eq_( [ { 'abbr': 'foobar', 'word': 'foo' } ],
|
||||||
base.AdjustCandidateInsertionText(
|
base.AdjustCandidateInsertionText(
|
||||||
[ { 'word': 'foobar' } ] ) )
|
[ { 'word': 'foobar' } ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def AdjustCandidateInsertionText_DontTouchAbbr_test():
|
def AdjustCandidateInsertionText_DontTouchAbbr_test():
|
||||||
vimsupport.TextAfterCursor = MagicMock( return_value = 'bar' )
|
with MockTextAfterCursor( 'bar' ):
|
||||||
eq_( [ { 'abbr': '1234', 'word': 'foo' } ],
|
eq_( [ { 'abbr': '1234', 'word': 'foo' } ],
|
||||||
base.AdjustCandidateInsertionText(
|
base.AdjustCandidateInsertionText(
|
||||||
[ { 'abbr': '1234', 'word': 'foobar' } ] ) )
|
[ { 'abbr': '1234', 'word': 'foobar' } ] ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def OverlapLength_Basic_test():
|
def OverlapLength_Basic_test():
|
||||||
eq_( 3, base.OverlapLength( 'foo bar', 'bar zoo' ) )
|
eq_( 3, base.OverlapLength( 'foo bar', 'bar zoo' ) )
|
||||||
eq_( 3, base.OverlapLength( 'foobar', 'barzoo' ) )
|
eq_( 3, base.OverlapLength( 'foobar', 'barzoo' ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def OverlapLength_BasicWithUnicode_test():
|
def OverlapLength_BasicWithUnicode_test():
|
||||||
eq_( 3, base.OverlapLength( u'bar fäö', u'fäö bar' ) )
|
eq_( 3, base.OverlapLength( u'bar fäö', u'fäö bar' ) )
|
||||||
eq_( 3, base.OverlapLength( u'zoofäö', u'fäözoo' ) )
|
eq_( 3, base.OverlapLength( u'zoofäö', u'fäözoo' ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def OverlapLength_OneCharOverlap_test():
|
def OverlapLength_OneCharOverlap_test():
|
||||||
eq_( 1, base.OverlapLength( 'foo b', 'b zoo' ) )
|
eq_( 1, base.OverlapLength( 'foo b', 'b zoo' ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def OverlapLength_SameStrings_test():
|
def OverlapLength_SameStrings_test():
|
||||||
eq_( 6, base.OverlapLength( 'foobar', 'foobar' ) )
|
eq_( 6, base.OverlapLength( 'foobar', 'foobar' ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def OverlapLength_Substring_test():
|
def OverlapLength_Substring_test():
|
||||||
eq_( 6, base.OverlapLength( 'foobar', 'foobarzoo' ) )
|
eq_( 6, base.OverlapLength( 'foobar', 'foobarzoo' ) )
|
||||||
eq_( 6, base.OverlapLength( 'zoofoobar', 'foobar' ) )
|
eq_( 6, base.OverlapLength( 'zoofoobar', 'foobar' ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def OverlapLength_LongestOverlap_test():
|
def OverlapLength_LongestOverlap_test():
|
||||||
eq_( 7, base.OverlapLength( 'bar foo foo', 'foo foo bar' ) )
|
eq_( 7, base.OverlapLength( 'bar foo foo', 'foo foo bar' ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def OverlapLength_EmptyInput_test():
|
def OverlapLength_EmptyInput_test():
|
||||||
eq_( 0, base.OverlapLength( '', 'goobar' ) )
|
eq_( 0, base.OverlapLength( '', 'goobar' ) )
|
||||||
eq_( 0, base.OverlapLength( 'foobar', '' ) )
|
eq_( 0, base.OverlapLength( 'foobar', '' ) )
|
||||||
eq_( 0, base.OverlapLength( '', '' ) )
|
eq_( 0, base.OverlapLength( '', '' ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def OverlapLength_NoOverlap_test():
|
def OverlapLength_NoOverlap_test():
|
||||||
eq_( 0, base.OverlapLength( 'foobar', 'goobar' ) )
|
eq_( 0, base.OverlapLength( 'foobar', 'goobar' ) )
|
||||||
eq_( 0, base.OverlapLength( 'foobar', '(^($@#$#@' ) )
|
eq_( 0, base.OverlapLength( 'foobar', '(^($@#$#@' ) )
|
||||||
eq_( 0, base.OverlapLength( 'foo bar zoo', 'foo zoo bar' ) )
|
eq_( 0, base.OverlapLength( 'foo bar zoo', 'foo zoo bar' ) )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def LastEnteredCharIsIdentifierChar_Basic_test():
|
def LastEnteredCharIsIdentifierChar_Basic_test():
|
||||||
SetVimCurrentColumnAndLineValue( 3, 'abc' )
|
with MockCurrentFiletypes():
|
||||||
|
with MockCurrentColumnAndLineContents( 3, 'abc' ):
|
||||||
ok_( base.LastEnteredCharIsIdentifierChar() )
|
ok_( base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 2, 'abc' )
|
with MockCurrentColumnAndLineContents( 2, 'abc' ):
|
||||||
ok_( base.LastEnteredCharIsIdentifierChar() )
|
ok_( base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 1, 'abc' )
|
with MockCurrentColumnAndLineContents( 1, 'abc' ):
|
||||||
ok_( base.LastEnteredCharIsIdentifierChar() )
|
ok_( base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def LastEnteredCharIsIdentifierChar_FiletypeHtml_test():
|
def LastEnteredCharIsIdentifierChar_FiletypeHtml_test():
|
||||||
SetVimCurrentColumnAndLineValue( 3, 'ab-' )
|
with MockCurrentFiletypes( ['html'] ):
|
||||||
vimsupport.CurrentFiletypes = MagicMock( return_value = ['html'] )
|
with MockCurrentColumnAndLineContents( 3, 'ab-' ):
|
||||||
ok_( base.LastEnteredCharIsIdentifierChar() )
|
ok_( base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def LastEnteredCharIsIdentifierChar_ColumnIsZero_test():
|
def LastEnteredCharIsIdentifierChar_ColumnIsZero_test():
|
||||||
SetVimCurrentColumnAndLineValue( 0, 'abc' )
|
with MockCurrentColumnAndLineContents( 0, 'abc' ):
|
||||||
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def LastEnteredCharIsIdentifierChar_LineEmpty_test():
|
def LastEnteredCharIsIdentifierChar_LineEmpty_test():
|
||||||
SetVimCurrentColumnAndLineValue( 3, '' )
|
with MockCurrentFiletypes():
|
||||||
|
with MockCurrentColumnAndLineContents( 3, '' ):
|
||||||
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 0, '' )
|
with MockCurrentColumnAndLineContents( 0, '' ):
|
||||||
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def LastEnteredCharIsIdentifierChar_NotIdentChar_test():
|
def LastEnteredCharIsIdentifierChar_NotIdentChar_test():
|
||||||
SetVimCurrentColumnAndLineValue( 3, 'ab;' )
|
with MockCurrentFiletypes():
|
||||||
|
with MockCurrentColumnAndLineContents( 3, 'ab;' ):
|
||||||
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 1, ';' )
|
with MockCurrentColumnAndLineContents( 1, ';' ):
|
||||||
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 3, 'ab-' )
|
with MockCurrentColumnAndLineContents( 3, 'ab-' ):
|
||||||
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
ok_( not base.LastEnteredCharIsIdentifierChar() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def CurrentIdentifierFinished_Basic_test():
|
def CurrentIdentifierFinished_Basic_test():
|
||||||
SetVimCurrentColumnAndLineValue( 3, 'ab;' )
|
with MockCurrentFiletypes():
|
||||||
|
with MockCurrentColumnAndLineContents( 3, 'ab;' ):
|
||||||
ok_( base.CurrentIdentifierFinished() )
|
ok_( base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 2, 'ab;' )
|
with MockCurrentColumnAndLineContents( 2, 'ab;' ):
|
||||||
ok_( not base.CurrentIdentifierFinished() )
|
ok_( not base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 1, 'ab;' )
|
with MockCurrentColumnAndLineContents( 1, 'ab;' ):
|
||||||
ok_( not base.CurrentIdentifierFinished() )
|
ok_( not base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def CurrentIdentifierFinished_NothingBeforeColumn_test():
|
def CurrentIdentifierFinished_NothingBeforeColumn_test():
|
||||||
SetVimCurrentColumnAndLineValue( 0, 'ab;' )
|
with MockCurrentColumnAndLineContents( 0, 'ab;' ):
|
||||||
ok_( base.CurrentIdentifierFinished() )
|
ok_( base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 0, '' )
|
with MockCurrentColumnAndLineContents( 0, '' ):
|
||||||
ok_( base.CurrentIdentifierFinished() )
|
ok_( base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def CurrentIdentifierFinished_InvalidColumn_test():
|
def CurrentIdentifierFinished_InvalidColumn_test():
|
||||||
SetVimCurrentColumnAndLineValue( 5, '' )
|
with MockCurrentFiletypes():
|
||||||
|
with MockCurrentColumnAndLineContents( 5, '' ):
|
||||||
ok_( not base.CurrentIdentifierFinished() )
|
ok_( not base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 5, 'abc' )
|
with MockCurrentColumnAndLineContents( 5, 'abc' ):
|
||||||
ok_( not base.CurrentIdentifierFinished() )
|
ok_( not base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def CurrentIdentifierFinished_InMiddleOfLine_test():
|
def CurrentIdentifierFinished_InMiddleOfLine_test():
|
||||||
SetVimCurrentColumnAndLineValue( 4, 'bar.zoo' )
|
with MockCurrentFiletypes():
|
||||||
|
with MockCurrentColumnAndLineContents( 4, 'bar.zoo' ):
|
||||||
ok_( base.CurrentIdentifierFinished() )
|
ok_( base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 4, 'bar(zoo' )
|
with MockCurrentColumnAndLineContents( 4, 'bar(zoo' ):
|
||||||
ok_( base.CurrentIdentifierFinished() )
|
ok_( base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 4, 'bar-zoo' )
|
with MockCurrentColumnAndLineContents( 4, 'bar-zoo' ):
|
||||||
ok_( base.CurrentIdentifierFinished() )
|
ok_( base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def CurrentIdentifierFinished_Html_test():
|
def CurrentIdentifierFinished_Html_test():
|
||||||
SetVimCurrentColumnAndLineValue( 4, 'bar-zoo' )
|
with MockCurrentFiletypes( ['html'] ):
|
||||||
vimsupport.CurrentFiletypes = MagicMock( return_value = ['html'] )
|
with MockCurrentColumnAndLineContents( 4, 'bar-zoo' ):
|
||||||
ok_( not base.CurrentIdentifierFinished() )
|
ok_( not base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
|
|
||||||
@with_setup( Setup )
|
|
||||||
def CurrentIdentifierFinished_WhitespaceOnly_test():
|
def CurrentIdentifierFinished_WhitespaceOnly_test():
|
||||||
SetVimCurrentColumnAndLineValue( 1, '\n' )
|
with MockCurrentFiletypes():
|
||||||
|
with MockCurrentColumnAndLineContents( 1, '\n' ):
|
||||||
ok_( base.CurrentIdentifierFinished() )
|
ok_( base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 3, '\n ' )
|
with MockCurrentColumnAndLineContents( 3, '\n ' ):
|
||||||
ok_( base.CurrentIdentifierFinished() )
|
ok_( base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
SetVimCurrentColumnAndLineValue( 3, '\t\t\t\t' )
|
with MockCurrentColumnAndLineContents( 3, '\t\t\t\t' ):
|
||||||
ok_( base.CurrentIdentifierFinished() )
|
ok_( base.CurrentIdentifierFinished() )
|
||||||
|
|
||||||
|
@ -23,9 +23,11 @@ import os
|
|||||||
|
|
||||||
from ycm.youcompleteme import YouCompleteMe
|
from ycm.youcompleteme import YouCompleteMe
|
||||||
from ycmd import user_options_store
|
from ycmd import user_options_store
|
||||||
from ycmd.responses import UnknownExtraConf
|
from ycmd.responses import ( BuildDiagnosticData, Diagnostic, Location, Range,
|
||||||
|
UnknownExtraConf )
|
||||||
|
|
||||||
from mock import call, MagicMock, patch
|
from mock import call, MagicMock, patch
|
||||||
|
from nose.tools import eq_, ok_
|
||||||
|
|
||||||
|
|
||||||
# The default options which are only relevant to the client, not the server and
|
# The default options which are only relevant to the client, not the server and
|
||||||
@ -34,6 +36,10 @@ from mock import call, MagicMock, patch
|
|||||||
DEFAULT_CLIENT_OPTIONS = {
|
DEFAULT_CLIENT_OPTIONS = {
|
||||||
'server_log_level': 'info',
|
'server_log_level': 'info',
|
||||||
'extra_conf_vim_data': [],
|
'extra_conf_vim_data': [],
|
||||||
|
'show_diagnostics_ui': 1,
|
||||||
|
'enable_diagnostic_signs': 1,
|
||||||
|
'enable_diagnostic_highlighting': 0,
|
||||||
|
'always_populate_location_list': 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -51,6 +57,17 @@ def PresentDialog_Confirm_Call( message ):
|
|||||||
return call( message, [ 'Ok', 'Cancel' ] )
|
return call( message, [ 'Ok', 'Cancel' ] )
|
||||||
|
|
||||||
|
|
||||||
|
def PlaceSign_Call( sign_id, line_num, buffer_num, is_error ):
|
||||||
|
sign_name = 'YcmError' if is_error else 'YcmWarning'
|
||||||
|
return call( 'sign place {0} line={1} name={2} buffer={3}'
|
||||||
|
.format( sign_id, line_num, sign_name, buffer_num ) )
|
||||||
|
|
||||||
|
|
||||||
|
def UnplaceSign_Call( sign_id, buffer_num ):
|
||||||
|
return call( 'try | exec "sign unplace {0} buffer={1}" |'
|
||||||
|
' catch /E158/ | endtry'.format( sign_id, buffer_num ) )
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def MockArbitraryBuffer( filetype, native_available = True ):
|
def MockArbitraryBuffer( filetype, native_available = True ):
|
||||||
"""Used via the with statement, set up mocked versions of the vim module such
|
"""Used via the with statement, set up mocked versions of the vim module such
|
||||||
@ -72,6 +89,12 @@ def MockArbitraryBuffer( filetype, native_available = True ):
|
|||||||
if value == 'getbufvar(0, "&ft")' or value == '&filetype':
|
if value == 'getbufvar(0, "&ft")' or value == '&filetype':
|
||||||
return filetype
|
return filetype
|
||||||
|
|
||||||
|
if value.startswith( 'bufnr(' ):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if value.startswith( 'bufwinnr(' ):
|
||||||
|
return 0
|
||||||
|
|
||||||
raise ValueError( 'Unexpected evaluation' )
|
raise ValueError( 'Unexpected evaluation' )
|
||||||
|
|
||||||
# Arbitrary, but valid, cursor position
|
# Arbitrary, but valid, cursor position
|
||||||
@ -82,6 +105,7 @@ def MockArbitraryBuffer( filetype, native_available = True ):
|
|||||||
current_buffer.number = 0
|
current_buffer.number = 0
|
||||||
current_buffer.filename = os.path.realpath( 'TEST_BUFFER' )
|
current_buffer.filename = os.path.realpath( 'TEST_BUFFER' )
|
||||||
current_buffer.name = 'TEST_BUFFER'
|
current_buffer.name = 'TEST_BUFFER'
|
||||||
|
current_buffer.window = 0
|
||||||
|
|
||||||
# The rest just mock up the Vim module so that our single arbitrary buffer
|
# The rest just mock up the Vim module so that our single arbitrary buffer
|
||||||
# makes sense to vimsupport module.
|
# makes sense to vimsupport module.
|
||||||
@ -144,8 +168,8 @@ class EventNotification_test( object ):
|
|||||||
|
|
||||||
@patch( 'vim.command', new_callable = ExtendedMock )
|
@patch( 'vim.command', new_callable = ExtendedMock )
|
||||||
def FileReadyToParse_NonDiagnostic_Error_test( self, vim_command ):
|
def FileReadyToParse_NonDiagnostic_Error_test( self, vim_command ):
|
||||||
# This test validates the behaviour of YouCompleteMe.ValidateParseRequest in
|
# This test validates the behaviour of YouCompleteMe.HandleFileParseRequest
|
||||||
# combination with YouCompleteMe.OnFileReadyToParse when the completer
|
# in combination with YouCompleteMe.OnFileReadyToParse when the completer
|
||||||
# raises an exception handling FileReadyToParse event notification
|
# raises an exception handling FileReadyToParse event notification
|
||||||
ERROR_TEXT = 'Some completer response text'
|
ERROR_TEXT = 'Some completer response text'
|
||||||
|
|
||||||
@ -155,8 +179,8 @@ class EventNotification_test( object ):
|
|||||||
with MockArbitraryBuffer( 'javascript' ):
|
with MockArbitraryBuffer( 'javascript' ):
|
||||||
with MockEventNotification( ErrorResponse ):
|
with MockEventNotification( ErrorResponse ):
|
||||||
self.server_state.OnFileReadyToParse()
|
self.server_state.OnFileReadyToParse()
|
||||||
assert self.server_state.DiagnosticsForCurrentFileReady()
|
assert self.server_state.FileParseRequestReady()
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
|
|
||||||
# The first call raises a warning
|
# The first call raises a warning
|
||||||
vim_command.assert_has_exact_calls( [
|
vim_command.assert_has_exact_calls( [
|
||||||
@ -164,15 +188,15 @@ class EventNotification_test( object ):
|
|||||||
] )
|
] )
|
||||||
|
|
||||||
# Subsequent calls don't re-raise the warning
|
# Subsequent calls don't re-raise the warning
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
vim_command.assert_has_exact_calls( [
|
vim_command.assert_has_exact_calls( [
|
||||||
PostVimMessage_Call( ERROR_TEXT ),
|
PostVimMessage_Call( ERROR_TEXT ),
|
||||||
] )
|
] )
|
||||||
|
|
||||||
# But it does if a subsequent event raises again
|
# But it does if a subsequent event raises again
|
||||||
self.server_state.OnFileReadyToParse()
|
self.server_state.OnFileReadyToParse()
|
||||||
assert self.server_state.DiagnosticsForCurrentFileReady()
|
assert self.server_state.FileParseRequestReady()
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
vim_command.assert_has_exact_calls( [
|
vim_command.assert_has_exact_calls( [
|
||||||
PostVimMessage_Call( ERROR_TEXT ),
|
PostVimMessage_Call( ERROR_TEXT ),
|
||||||
PostVimMessage_Call( ERROR_TEXT ),
|
PostVimMessage_Call( ERROR_TEXT ),
|
||||||
@ -184,7 +208,7 @@ class EventNotification_test( object ):
|
|||||||
with MockArbitraryBuffer( 'javascript' ):
|
with MockArbitraryBuffer( 'javascript' ):
|
||||||
with MockEventNotification( None, False ):
|
with MockEventNotification( None, False ):
|
||||||
self.server_state.OnFileReadyToParse()
|
self.server_state.OnFileReadyToParse()
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
vim_command.assert_not_called()
|
vim_command.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
@ -198,8 +222,8 @@ class EventNotification_test( object ):
|
|||||||
load_extra_conf,
|
load_extra_conf,
|
||||||
*args ):
|
*args ):
|
||||||
|
|
||||||
# This test validates the behaviour of YouCompleteMe.ValidateParseRequest in
|
# This test validates the behaviour of YouCompleteMe.HandleFileParseRequest
|
||||||
# combination with YouCompleteMe.OnFileReadyToParse when the completer
|
# in combination with YouCompleteMe.OnFileReadyToParse when the completer
|
||||||
# raises the (special) UnknownExtraConf exception
|
# raises the (special) UnknownExtraConf exception
|
||||||
|
|
||||||
FILE_NAME = 'a_file'
|
FILE_NAME = 'a_file'
|
||||||
@ -217,8 +241,8 @@ class EventNotification_test( object ):
|
|||||||
return_value = 0,
|
return_value = 0,
|
||||||
new_callable = ExtendedMock ) as present_dialog:
|
new_callable = ExtendedMock ) as present_dialog:
|
||||||
self.server_state.OnFileReadyToParse()
|
self.server_state.OnFileReadyToParse()
|
||||||
assert self.server_state.DiagnosticsForCurrentFileReady()
|
assert self.server_state.FileParseRequestReady()
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
|
|
||||||
present_dialog.assert_has_exact_calls( [
|
present_dialog.assert_has_exact_calls( [
|
||||||
PresentDialog_Confirm_Call( MESSAGE ),
|
PresentDialog_Confirm_Call( MESSAGE ),
|
||||||
@ -228,7 +252,7 @@ class EventNotification_test( object ):
|
|||||||
] )
|
] )
|
||||||
|
|
||||||
# Subsequent calls don't re-raise the warning
|
# Subsequent calls don't re-raise the warning
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
|
|
||||||
present_dialog.assert_has_exact_calls( [
|
present_dialog.assert_has_exact_calls( [
|
||||||
PresentDialog_Confirm_Call( MESSAGE )
|
PresentDialog_Confirm_Call( MESSAGE )
|
||||||
@ -239,8 +263,8 @@ class EventNotification_test( object ):
|
|||||||
|
|
||||||
# But it does if a subsequent event raises again
|
# But it does if a subsequent event raises again
|
||||||
self.server_state.OnFileReadyToParse()
|
self.server_state.OnFileReadyToParse()
|
||||||
assert self.server_state.DiagnosticsForCurrentFileReady()
|
assert self.server_state.FileParseRequestReady()
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
|
|
||||||
present_dialog.assert_has_exact_calls( [
|
present_dialog.assert_has_exact_calls( [
|
||||||
PresentDialog_Confirm_Call( MESSAGE ),
|
PresentDialog_Confirm_Call( MESSAGE ),
|
||||||
@ -256,8 +280,8 @@ class EventNotification_test( object ):
|
|||||||
return_value = 1,
|
return_value = 1,
|
||||||
new_callable = ExtendedMock ) as present_dialog:
|
new_callable = ExtendedMock ) as present_dialog:
|
||||||
self.server_state.OnFileReadyToParse()
|
self.server_state.OnFileReadyToParse()
|
||||||
assert self.server_state.DiagnosticsForCurrentFileReady()
|
assert self.server_state.FileParseRequestReady()
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
|
|
||||||
present_dialog.assert_has_exact_calls( [
|
present_dialog.assert_has_exact_calls( [
|
||||||
PresentDialog_Confirm_Call( MESSAGE ),
|
PresentDialog_Confirm_Call( MESSAGE ),
|
||||||
@ -267,7 +291,7 @@ class EventNotification_test( object ):
|
|||||||
] )
|
] )
|
||||||
|
|
||||||
# Subsequent calls don't re-raise the warning
|
# Subsequent calls don't re-raise the warning
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
|
|
||||||
present_dialog.assert_has_exact_calls( [
|
present_dialog.assert_has_exact_calls( [
|
||||||
PresentDialog_Confirm_Call( MESSAGE )
|
PresentDialog_Confirm_Call( MESSAGE )
|
||||||
@ -278,8 +302,8 @@ class EventNotification_test( object ):
|
|||||||
|
|
||||||
# But it does if a subsequent event raises again
|
# But it does if a subsequent event raises again
|
||||||
self.server_state.OnFileReadyToParse()
|
self.server_state.OnFileReadyToParse()
|
||||||
assert self.server_state.DiagnosticsForCurrentFileReady()
|
assert self.server_state.FileParseRequestReady()
|
||||||
self.server_state.ValidateParseRequest()
|
self.server_state.HandleFileParseRequest()
|
||||||
|
|
||||||
present_dialog.assert_has_exact_calls( [
|
present_dialog.assert_has_exact_calls( [
|
||||||
PresentDialog_Confirm_Call( MESSAGE ),
|
PresentDialog_Confirm_Call( MESSAGE ),
|
||||||
@ -289,3 +313,92 @@ class EventNotification_test( object ):
|
|||||||
call( FILE_NAME ),
|
call( FILE_NAME ),
|
||||||
call( FILE_NAME ),
|
call( FILE_NAME ),
|
||||||
] )
|
] )
|
||||||
|
|
||||||
|
|
||||||
|
def FileReadyToParse_Diagnostic_Error_Native_test( self ):
|
||||||
|
self._Check_FileReadyToParse_Diagnostic_Error()
|
||||||
|
self._Check_FileReadyToParse_Diagnostic_Warning()
|
||||||
|
self._Check_FileReadyToParse_Diagnostic_Clean()
|
||||||
|
|
||||||
|
|
||||||
|
@patch( 'vim.command' )
|
||||||
|
def _Check_FileReadyToParse_Diagnostic_Error( self, vim_command ):
|
||||||
|
# Tests Vim sign placement and error/warning count python API
|
||||||
|
# when one error is returned.
|
||||||
|
def DiagnosticResponse( *args ):
|
||||||
|
start = Location( 1, 2, 'TEST_BUFFER' )
|
||||||
|
end = Location( 1, 4, 'TEST_BUFFER' )
|
||||||
|
extent = Range( start, end )
|
||||||
|
diagnostic = Diagnostic( [], start, extent, 'expected ;', 'ERROR' )
|
||||||
|
return [ BuildDiagnosticData( diagnostic ) ]
|
||||||
|
|
||||||
|
with MockArbitraryBuffer( 'cpp' ):
|
||||||
|
with MockEventNotification( DiagnosticResponse ):
|
||||||
|
self.server_state.OnFileReadyToParse()
|
||||||
|
ok_( self.server_state.FileParseRequestReady() )
|
||||||
|
self.server_state.HandleFileParseRequest()
|
||||||
|
vim_command.assert_has_calls( [
|
||||||
|
PlaceSign_Call( 1, 1, 0, True )
|
||||||
|
] )
|
||||||
|
eq_( self.server_state.GetErrorCount(), 1 )
|
||||||
|
eq_( self.server_state.GetWarningCount(), 0 )
|
||||||
|
|
||||||
|
# Consequent calls to HandleFileParseRequest shouldn't mess with
|
||||||
|
# existing diagnostics, when there is no new parse request.
|
||||||
|
vim_command.reset_mock()
|
||||||
|
ok_( not self.server_state.FileParseRequestReady() )
|
||||||
|
self.server_state.HandleFileParseRequest()
|
||||||
|
vim_command.assert_not_called()
|
||||||
|
eq_( self.server_state.GetErrorCount(), 1 )
|
||||||
|
eq_( self.server_state.GetWarningCount(), 0 )
|
||||||
|
|
||||||
|
|
||||||
|
@patch( 'vim.command' )
|
||||||
|
def _Check_FileReadyToParse_Diagnostic_Warning( self, vim_command ):
|
||||||
|
# Tests Vim sign placement/unplacement and error/warning count python API
|
||||||
|
# when one warning is returned.
|
||||||
|
# Should be called after _Check_FileReadyToParse_Diagnostic_Error
|
||||||
|
def DiagnosticResponse( *args ):
|
||||||
|
start = Location( 2, 2, 'TEST_BUFFER' )
|
||||||
|
end = Location( 2, 4, 'TEST_BUFFER' )
|
||||||
|
extent = Range( start, end )
|
||||||
|
diagnostic = Diagnostic( [], start, extent, 'cast', 'WARNING' )
|
||||||
|
return [ BuildDiagnosticData( diagnostic ) ]
|
||||||
|
|
||||||
|
with MockArbitraryBuffer( 'cpp' ):
|
||||||
|
with MockEventNotification( DiagnosticResponse ):
|
||||||
|
self.server_state.OnFileReadyToParse()
|
||||||
|
ok_( self.server_state.FileParseRequestReady() )
|
||||||
|
self.server_state.HandleFileParseRequest()
|
||||||
|
vim_command.assert_has_calls( [
|
||||||
|
PlaceSign_Call( 2, 2, 0, False ),
|
||||||
|
UnplaceSign_Call( 1, 0 )
|
||||||
|
] )
|
||||||
|
eq_( self.server_state.GetErrorCount(), 0 )
|
||||||
|
eq_( self.server_state.GetWarningCount(), 1 )
|
||||||
|
|
||||||
|
# Consequent calls to HandleFileParseRequest shouldn't mess with
|
||||||
|
# existing diagnostics, when there is no new parse request.
|
||||||
|
vim_command.reset_mock()
|
||||||
|
ok_( not self.server_state.FileParseRequestReady() )
|
||||||
|
self.server_state.HandleFileParseRequest()
|
||||||
|
vim_command.assert_not_called()
|
||||||
|
eq_( self.server_state.GetErrorCount(), 0 )
|
||||||
|
eq_( self.server_state.GetWarningCount(), 1 )
|
||||||
|
|
||||||
|
|
||||||
|
@patch( 'vim.command' )
|
||||||
|
def _Check_FileReadyToParse_Diagnostic_Clean( self, vim_command ):
|
||||||
|
# Tests Vim sign unplacement and error/warning count python API
|
||||||
|
# when there are no errors/warnings left.
|
||||||
|
# Should be called after _Check_FileReadyToParse_Diagnostic_Warning
|
||||||
|
with MockArbitraryBuffer( 'cpp' ):
|
||||||
|
with MockEventNotification( MagicMock( return_value = [] ) ):
|
||||||
|
self.server_state.OnFileReadyToParse()
|
||||||
|
self.server_state.HandleFileParseRequest()
|
||||||
|
vim_command.assert_has_calls( [
|
||||||
|
UnplaceSign_Call( 2, 0 )
|
||||||
|
] )
|
||||||
|
eq_( self.server_state.GetErrorCount(), 0 )
|
||||||
|
eq_( self.server_state.GetWarningCount(), 0 )
|
||||||
|
|
||||||
|
@ -75,6 +75,7 @@ SERVER_CRASH_MESSAGE_STDERR_FILE_DELETED = (
|
|||||||
"Logfile was deleted; set 'g:ycm_server_keep_logfiles' to see errors "
|
"Logfile was deleted; set 'g:ycm_server_keep_logfiles' to see errors "
|
||||||
"in the future." )
|
"in the future." )
|
||||||
SERVER_IDLE_SUICIDE_SECONDS = 10800 # 3 hours
|
SERVER_IDLE_SUICIDE_SECONDS = 10800 # 3 hours
|
||||||
|
DIAGNOSTIC_UI_FILETYPES = set( [ 'cpp', 'cs', 'c', 'objc', 'objcpp' ] )
|
||||||
|
|
||||||
|
|
||||||
class YouCompleteMe( object ):
|
class YouCompleteMe( object ):
|
||||||
@ -85,6 +86,7 @@ class YouCompleteMe( object ):
|
|||||||
self._omnicomp = OmniCompleter( user_options )
|
self._omnicomp = OmniCompleter( user_options )
|
||||||
self._latest_file_parse_request = None
|
self._latest_file_parse_request = None
|
||||||
self._latest_completion_request = None
|
self._latest_completion_request = None
|
||||||
|
self._latest_diagnostics = []
|
||||||
self._server_stdout = None
|
self._server_stdout = None
|
||||||
self._server_stderr = None
|
self._server_stderr = None
|
||||||
self._server_popen = None
|
self._server_popen = None
|
||||||
@ -447,56 +449,64 @@ class YouCompleteMe( object ):
|
|||||||
return None
|
return None
|
||||||
return completion[ "extra_data" ][ "required_namespace_import" ]
|
return completion[ "extra_data" ][ "required_namespace_import" ]
|
||||||
|
|
||||||
|
|
||||||
def GetErrorCount( self ):
|
def GetErrorCount( self ):
|
||||||
return self._diag_interface.GetErrorCount()
|
return self._diag_interface.GetErrorCount()
|
||||||
|
|
||||||
|
|
||||||
def GetWarningCount( self ):
|
def GetWarningCount( self ):
|
||||||
return self._diag_interface.GetWarningCount()
|
return self._diag_interface.GetWarningCount()
|
||||||
|
|
||||||
def DiagnosticsForCurrentFileReady( self ):
|
|
||||||
return bool( self._latest_file_parse_request and
|
def DiagnosticUiSupportedForCurrentFiletype( self ):
|
||||||
self._latest_file_parse_request.Done() )
|
return any( [ x in DIAGNOSTIC_UI_FILETYPES
|
||||||
|
for x in vimsupport.CurrentFiletypes() ] )
|
||||||
|
|
||||||
|
|
||||||
def GetDiagnosticsFromStoredRequest( self, qflist_format = False ):
|
def ShouldDisplayDiagnostics( self ):
|
||||||
if self.DiagnosticsForCurrentFileReady():
|
return bool( self._user_options[ 'show_diagnostics_ui' ] and
|
||||||
diagnostics = self._latest_file_parse_request.Response()
|
self.DiagnosticUiSupportedForCurrentFiletype() )
|
||||||
# We set the diagnostics request to None because we want to prevent
|
|
||||||
# repeated refreshing of the buffer with the same diags. Setting this to
|
|
||||||
# None makes DiagnosticsForCurrentFileReady return False until the next
|
def PopulateLocationListWithLatestDiagnostics( self ):
|
||||||
# request is created.
|
# Do nothing if loc list is already populated by diag_interface
|
||||||
self._latest_file_parse_request = None
|
if not self._user_options[ 'always_populate_location_list' ]:
|
||||||
if qflist_format:
|
self._diag_interface.PopulateLocationList( self._latest_diagnostics )
|
||||||
return vimsupport.ConvertDiagnosticsToQfList( diagnostics )
|
return bool( self._latest_diagnostics )
|
||||||
else:
|
|
||||||
return diagnostics
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def UpdateDiagnosticInterface( self ):
|
def UpdateDiagnosticInterface( self ):
|
||||||
if ( self.DiagnosticsForCurrentFileReady() and
|
self._diag_interface.UpdateWithNewDiagnostics( self._latest_diagnostics )
|
||||||
self.NativeFiletypeCompletionUsable() ):
|
|
||||||
self._diag_interface.UpdateWithNewDiagnostics(
|
|
||||||
self.GetDiagnosticsFromStoredRequest() )
|
|
||||||
|
|
||||||
|
|
||||||
def ValidateParseRequest( self ):
|
def FileParseRequestReady( self, block = False ):
|
||||||
if ( self.DiagnosticsForCurrentFileReady() and
|
return bool( self._latest_file_parse_request and
|
||||||
|
( block or self._latest_file_parse_request.Done() ) )
|
||||||
|
|
||||||
|
|
||||||
|
def HandleFileParseRequest( self, block = False ):
|
||||||
|
# Order is important here:
|
||||||
|
# FileParseRequestReady has a low cost, while
|
||||||
|
# NativeFiletypeCompletionUsable is a blocking server request
|
||||||
|
if ( self.FileParseRequestReady( block ) and
|
||||||
self.NativeFiletypeCompletionUsable() ):
|
self.NativeFiletypeCompletionUsable() ):
|
||||||
|
|
||||||
# YCM client has a hard-coded list of filetypes which are known to support
|
if self.ShouldDisplayDiagnostics():
|
||||||
# diagnostics. These are found in autoload/youcompleteme.vim in
|
self._latest_diagnostics = self._latest_file_parse_request.Response()
|
||||||
# s:diagnostic_ui_filetypes.
|
self.UpdateDiagnosticInterface()
|
||||||
|
else:
|
||||||
|
# YCM client has a hard-coded list of filetypes which are known
|
||||||
|
# to support diagnostics, self.DiagnosticUiSupportedForCurrentFiletype()
|
||||||
#
|
#
|
||||||
# For filetypes which don't support diagnostics, we just want to check the
|
# For filetypes which don't support diagnostics, we just want to check
|
||||||
# _latest_file_parse_request for any exception or UnknownExtraConf
|
# the _latest_file_parse_request for any exception or UnknownExtraConf
|
||||||
# response, to allow the server to raise configuration warnings, etc.
|
# response, to allow the server to raise configuration warnings, etc.
|
||||||
# to the user. We ignore any other supplied data.
|
# to the user. We ignore any other supplied data.
|
||||||
self._latest_file_parse_request.Response()
|
self._latest_file_parse_request.Response()
|
||||||
|
|
||||||
# We set the diagnostics request to None because we want to prevent
|
# We set the file parse request to None because we want to prevent
|
||||||
# repeated issuing of the same warnings/errors/prompts. Setting this to
|
# repeated issuing of the same warnings/errors/prompts. Setting this to
|
||||||
# None makes DiagnosticsForCurrentFileReady return False until the next
|
# None makes FileParseRequestReady return False until the next
|
||||||
# request is created.
|
# request is created.
|
||||||
#
|
#
|
||||||
# Note: it is the server's responsibility to determine the frequency of
|
# Note: it is the server's responsibility to determine the frequency of
|
||||||
|
Loading…
Reference in New Issue
Block a user