Moved parse request and diagnostics handling to python.
Moved File parse request handling and diagnostic extraction flow into python to simplify flow and allow easier addition of new parse request handlers such as semantic highlighter. Refactored base_test to patch separate vimsupport functions instead of the whole module, and interfering the test results afterwards. Added new tests for diagnostic sign place/unplace and error/warning count extraction API.
This commit is contained in:
parent
8ad4044203
commit
4d97437872
@ -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