diff --git a/python/ycm/client/command_request.py b/python/ycm/client/command_request.py index a9045686d8..1362e2fe45 100644 --- a/python/ycm/client/command_request.py +++ b/python/ycm/client/command_request.py @@ -15,6 +15,7 @@ # You should have received a copy of the GNU General Public License # along with YouCompleteMe. If not, see . +import re from ycm.client.base_request import ( BaseRequest, BuildRequestData, BuildRequestDataForLocation ) @@ -142,11 +143,40 @@ def StringResponse( self ): return "" + def _GetClassNameFromJdtURI( self, uri ): + if not isinstance( uri, str ): + return None + + matches = re.findall( r'([^\\(]+\.class)', uri ) + if matches: + return matches[ -1 ] + return None + + def _HandleGotoResponse( self, buffer_command, modifiers ): if isinstance( self._response, list ): vimsupport.SetQuickFixList( [ vimsupport.BuildQfListItem( x ) for x in self._response ] ) vimsupport.OpenQuickFixList( focus = True, autoclose = True ) + elif 'jdt_contents' in self._response: + class_name = self._GetClassNameFromJdtURI( self._response.get( 'uri' ) ) + + range = self._response.get( 'range' ) + range_start = range.get( 'start' ) + + line_num = range_start[ 'line' ] if range_start else 0 + column_num = range_start[ 'character' ] if range_start else 0 + + contents = self._response.get( 'jdt_contents' ) + + if not contents: + raise RuntimeError( 'JDT contents not available.' ) + + vimsupport.CreateTemporaryReadonlyBuffer( contents=contents, + buffer_name=class_name, + syntax='java', + line=line_num, + column=column_num ) elif self._response.get( 'file_only' ): vimsupport.JumpToLocation( self._response[ 'filepath' ], None, diff --git a/python/ycm/tests/client/command_request_test.py b/python/ycm/tests/client/command_request_test.py index 368b87c4ec..2c2945845c 100644 --- a/python/ycm/tests/client/command_request_test.py +++ b/python/ycm/tests/client/command_request_test.py @@ -51,12 +51,35 @@ def GoToListTest( command, response ): assert_that( open_qf_list.called ) +def GoToJdt( command, response ): + with patch( 'ycm.vimsupport.CreateTemporaryReadonlyBuffer' ) as temp_buffer: + request = CommandRequest( [ command ] ) + request._response = response + request.RunPostCommandActionsIfNeeded( '' ) + temp_buffer.assert_called_once_with( + contents=response[ 'jdt_contents' ], + buffer_name=JDT_GOTO[ 'uri' ], + syntax='java', + line=JDT_GOTO[ 'range' ][ 'start' ][ 'line' ], + column=JDT_GOTO[ 'range' ][ 'start' ][ 'character' ] ) + + BASIC_GOTO = { 'filepath': 'test', 'line_num': 10, 'column_num': 100, } +JDT_GOTO = { + 'jdt_contents': 'public class Member {}', + 'uri': 'jdt://contents/stuff/whatever/Member.class', + 'range': { + 'start': { + 'line': 1, + 'character': 5 + } + } +} BASIC_FIXIT = { 'fixits': [ { @@ -298,6 +321,7 @@ def test_GoTo_Single( self ): for test, command, response in [ [ GoToTest, 'AnythingYouLike', BASIC_GOTO ], [ GoToTest, 'GoTo', BASIC_GOTO ], + [ GoToJdt, 'GoTo', JDT_GOTO ], [ GoToTest, 'FindAThing', BASIC_GOTO ], [ GoToTest, 'FixItGoto', BASIC_GOTO ], [ GoToListTest, 'AnythingYouLike', [ BASIC_GOTO ] ], @@ -306,3 +330,28 @@ def test_GoTo_Single( self ): ]: with self.subTest( test = test, command = command, response = response ): test( command, response ) + + +class JdtUriParsingTest( TestCase ): + def setUp( self ): + self.cmd_req = CommandRequest( [] ) + + def test_get_class_name_from_jdt_uri_with_valid_uri( self ): + uri = "jdt://contents/JDA-5.6.1.jar/net.dv8tion.jda.api."\ + "entities/Member.class?\\u003dapplication/%5C/Users%5C/user"\ + "%5C/.gradle%5C/caches%5C/modules-2%5C/files-2.1%5C/net."\ + "dv8tion%5C/JDA%5C/5.6.1%5C/e69e9bf2049f96bcad99e0bbe4"\ + "ddce9c67947cf6%5C/JDA-5.6.1.jar\\u003d/gradle_used_by_"\ + "scope\\u003d/main,test\\u003d/%3Cnet.dv8tion.jda.api.en"\ + "tities(Member.class" + result = self.cmd_req._GetClassNameFromJdtURI( uri ) + self.assertEqual( result, "Member.class" ) + + def test_get_class_name_from_jdt_uri_with_no_class( self ): + uri = "jdt://contents/com/example/Member" + result = self.cmd_req._GetClassNameFromJdtURI( uri ) + self.assertIsNone( result ) + + def test_get_class_name_from_jdt_uri_with_non_string( self ): + result = self.cmd_req._GetClassNameFromJdtURI( None ) + self.assertIsNone( result ) diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index 2a29b1ede1..ef80fcaa53 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -1497,3 +1497,44 @@ def BuildQfListItem( goto_data_item ): qf_item[ 'col' ] = goto_data_item[ 'column_num' ] return qf_item + + +def CreateTemporaryReadonlyBuffer( contents=None, + buffer_name='[YCM-Temp-Readonly]', + syntax=None, + line=None, + column=None ): + """ + Create a new temporary readonly buffer in Vim, optionally filled + with contents. The buffer will not be associated with a file and + will be readonly and unmodifiable. + """ + vim.command( 'enew' ) + buf = vim.current.buffer + + vim.command( f"file { buffer_name }" ) + + if contents: + buf.options[ 'modifiable' ] = True + buf[ : ] = contents.splitlines() + buf.options[ 'modifiable' ] = False + + buf.options[ 'readonly' ] = True + buf.options[ 'modifiable' ] = False + buf.options[ 'bufhidden' ] = 'wipe' + buf.options[ 'buftype' ] = 'nofile' + buf.options[ 'buflisted' ] = False + + vim.command( 'setlocal noswapfile' ) + vim.command( 'setlocal nobuflisted' ) + vim.command( 'setlocal nobuflisted' ) + + if syntax: + vim.command( f'set syntax={ syntax }' ) + + if line is not None and column is not None: + vim.current.window.cursor = ( line + 1, column ) + + vim.command( 'normal! zz' ) + + return buf