diff --git a/build.py b/build.py
index e83875cd0a..1e0f9a20d1 100755
--- a/build.py
+++ b/build.py
@@ -89,10 +89,10 @@ def Exit( self ):
)$
"""
-JDTLS_MILESTONE = '1.26.0'
-JDTLS_BUILD_STAMP = '202307271613'
+JDTLS_MILESTONE = '1.31.0'
+JDTLS_BUILD_STAMP = '202401111522'
JDTLS_SHA256 = (
- 'ba5fe5ee3b2a8395287e24aef20ce6e17834cf8e877117e6caacac6a688a6c53'
+ '6c25f62d0b74d1dd92ab19afbafbe5041eb06c2b853eab57f7f42fe6feb01f7c'
)
DEFAULT_RUST_TOOLCHAIN = 'nightly-2023-08-18'
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index 08315f4084..526491c96f 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -237,6 +237,7 @@ find_package( Python3 3.6 REQUIRED COMPONENTS Interpreter Development )
#############################################################################
set( CMAKE_POSITION_INDEPENDENT_CODE ON )
+set( ABSL_PROPAGATE_CXX_STD ON )
if ( USE_SYSTEM_ABSEIL )
find_package( absl REQUIRED )
else()
@@ -244,14 +245,10 @@ else()
FetchContent_Declare(
absl
GIT_REPOSITORY https://github.com/abseil/abseil-cpp
- GIT_TAG 3b4a16abad2c2ddc494371cc39a2946e36d35d11
+ GIT_TAG fb3621f4f897824c0dbe0615fa94543df6192f30
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/absl
)
- FetchContent_GetProperties( absl )
- if ( NOT absl_POPULATED )
- FetchContent_Populate( absl )
- endif()
- add_subdirectory( absl )
+ FetchContent_MakeAvailable( absl )
endif()
add_subdirectory( ycm )
diff --git a/cpp/ycm/benchmarks/CMakeLists.txt b/cpp/ycm/benchmarks/CMakeLists.txt
index 6009a0a872..75adde7b54 100644
--- a/cpp/ycm/benchmarks/CMakeLists.txt
+++ b/cpp/ycm/benchmarks/CMakeLists.txt
@@ -33,11 +33,7 @@ else()
GIT_TAG 3b19d7222db7babfdc9b3949408b2294c3bbb540
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/benchmark
)
- FetchContent_GetProperties( benchmark )
- if ( NOT benchmark_POPULATED )
- FetchContent_Populate( benchmark )
- endif()
- add_subdirectory( benchmark )
+ FetchContent_MakeAvailable( benchmark )
endif()
file( GLOB SOURCES *.h *.cpp )
diff --git a/cpp/ycm/tests/CMakeLists.txt b/cpp/ycm/tests/CMakeLists.txt
index 0c52e668ed..0415d717be 100644
--- a/cpp/ycm/tests/CMakeLists.txt
+++ b/cpp/ycm/tests/CMakeLists.txt
@@ -45,11 +45,7 @@ else()
GIT_TAG f5e592d8ee5ffb1d9af5be7f715ce3576b8bf9c4
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gmock
)
- FetchContent_GetProperties( gmock )
- if ( NOT benchmark_POPULATED )
- FetchContent_Populate( gmock )
- endif()
- add_subdirectory( gmock )
+ FetchContent_MakeAvailable( gmock )
endif()
file( GLOB SOURCES *.h *.cpp )
diff --git a/ycmd/completers/cs/cs_completer.py b/ycmd/completers/cs/cs_completer.py
index 484d548166..f8b6cd5cc3 100644
--- a/ycmd/completers/cs/cs_completer.py
+++ b/ycmd/completers/cs/cs_completer.py
@@ -241,6 +241,9 @@ def GetSubcommandsMap( self ):
self._SolutionSubcommand( request_data,
method = '_RefactorRename',
args = args ) ),
+ 'GoToDocumentOutline' : ( lambda self, request_data, args:
+ self._SolutionSubcommand( request_data,
+ method = '_GoToDocumentOutline' ) ),
}
@@ -631,36 +634,48 @@ def _GoToSymbol( self, request_data, args ):
if quickfixes:
if len( quickfixes ) == 1:
ref = quickfixes[ 0 ]
- ref_file = ref[ 'FileName' ]
- ref_line = ref[ 'Line' ]
- lines = GetFileLines( request_data, ref_file )
- line = lines[ min( len( lines ), ref_line - 1 ) ]
return responses.BuildGoToResponseFromLocation(
_BuildLocation(
request_data,
- ref_file,
- ref_line,
+ ref[ 'FileName' ],
+ ref[ 'Line' ],
ref[ 'Column' ] ),
- line )
+ ref[ 'Text' ] )
else:
goto_locations = []
for ref in quickfixes:
- ref_file = ref[ 'FileName' ]
- ref_line = ref[ 'Line' ]
- lines = GetFileLines( request_data, ref_file )
- line = lines[ min( len( lines ), ref_line - 1 ) ]
goto_locations.append(
responses.BuildGoToResponseFromLocation(
_BuildLocation( request_data,
- ref_file,
- ref_line,
+ ref[ 'FileName' ],
+ ref[ 'Line' ],
ref[ 'Column' ] ),
- line ) )
+ ref[ 'Text' ] ) )
return goto_locations
else:
raise RuntimeError( 'No symbols found' )
+
+ def _GoToDocumentOutline( self, request_data ):
+ request = self._DefaultParameters( request_data )
+ response = self._GetResponse( '/currentfilemembersasflat', request )
+ if response is not None and len( response ) > 0:
+ goto_locations = []
+ for ref in response:
+ goto_locations.append(
+ responses.BuildGoToResponseFromLocation(
+ _BuildLocation( request_data,
+ ref[ 'FileName' ],
+ ref[ 'Line' ],
+ ref[ 'Column' ] ),
+ ref[ 'Text' ] ) )
+ if len( goto_locations ) > 1:
+ return goto_locations
+ return goto_locations[ 0 ]
+ else:
+ raise RuntimeError( 'No symbols found' )
+
def _GoToReferences( self, request_data ):
""" Jump to references of identifier under cursor """
# _GetResponse can throw. Original code by @mispencer
diff --git a/ycmd/completers/language_server/language_server_completer.py b/ycmd/completers/language_server/language_server_completer.py
index 8667e41b7c..2f46e43de6 100644
--- a/ycmd/completers/language_server/language_server_completer.py
+++ b/ycmd/completers/language_server/language_server_completer.py
@@ -2536,9 +2536,7 @@ def _GoToRequest( self, request_data, handler ):
except ResponseFailedException:
result = None
- if not result:
- raise RuntimeError( 'Cannot jump to location' )
- if not isinstance( result, list ):
+ if result and not isinstance( result, list ):
return [ result ]
return result
@@ -2553,15 +2551,18 @@ def GoTo( self, request_data, handlers ):
self._UpdateServerWithFileContents( request_data )
- if len( handlers ) == 1:
- result = self._GoToRequest( request_data, handlers[ 0 ] )
- else:
- for handler in handlers:
- result = self._GoToRequest( request_data, handler )
- if len( result ) > 1 or not _CursorInsideLocation( request_data,
- result[ 0 ] ):
- break
+ result = []
+ for handler in handlers:
+ new_result = self._GoToRequest( request_data, handler )
+ if new_result:
+ result = new_result
+ if len( result ) > 1 or ( result and
+ not _CursorInsideLocation( request_data,
+ result[ 0 ] ) ):
+ break
+ if not result:
+ raise RuntimeError( 'Cannot jump to location' )
return _LocationListToGoTo( request_data, result )
diff --git a/ycmd/completers/typescript/typescript_completer.py b/ycmd/completers/typescript/typescript_completer.py
index 99439421da..0d4cd699ca 100644
--- a/ycmd/completers/typescript/typescript_completer.py
+++ b/ycmd/completers/typescript/typescript_completer.py
@@ -911,7 +911,12 @@ def _GetDoc( self, request_data ):
'offset': request_data[ 'column_codepoint' ]
} )
+ extra_info = _AggregateTagsFromDocstring( info )
+
message = f'{ info[ "displayString" ] }\n\n{ info[ "documentation" ] }'
+ if extra_info:
+ message += f'\n\n{ extra_info }'
+
return responses.BuildDetailedInfoResponse( message )
@@ -1117,6 +1122,16 @@ def _LogLevel():
return 'verbose' if LOGGER.isEnabledFor( logging.DEBUG ) else 'normal'
+def _AggregateTagsFromDocstring( info ):
+ extra_info = []
+ for tag in info.get( 'tags', [] ):
+ tag_name = tag[ 'name' ]
+ tag_text = tag.get( 'text' )
+ formated_tag = tag_name + ( f': { tag_text }' if tag_text else '' )
+ extra_info.append( formated_tag )
+ return '\n'.join( extra_info )
+
+
def _BuildCompletionExtraMenuAndDetailedInfo( request_data, entry ):
signature = _DisplayPartsToString( entry[ 'displayParts' ] )
if entry[ 'name' ] == signature:
@@ -1130,6 +1145,11 @@ def _BuildCompletionExtraMenuAndDetailedInfo( request_data, entry ):
docs = entry.get( 'documentation', [] )
detailed_info += [ doc[ 'text' ].strip() for doc in docs if doc ]
+
+ extra_info = _AggregateTagsFromDocstring( entry )
+ if extra_info:
+ detailed_info.append( extra_info )
+
detailed_info = '\n\n'.join( detailed_info )
return extra_menu_info, detailed_info
diff --git a/ycmd/tests/cs/subcommands_test.py b/ycmd/tests/cs/subcommands_test.py
index 3205714f52..0f40c91d4a 100644
--- a/ycmd/tests/cs/subcommands_test.py
+++ b/ycmd/tests/cs/subcommands_test.py
@@ -909,3 +909,68 @@ def test_Subcommands_OrganizeImports( self, app ):
LocationMatcher( filepath, 3, 1 ),
)
) } ) ) } ) )
+
+
+ @SharedYcmd
+ def test_Subcommands_GoToDocumentOutline( self, app ):
+
+ for filepath, expected in [
+ ( PathToTestFile( 'testy', 'Empty.cs' ),
+ ErrorMatcher( RuntimeError, 'No symbols found' ) ),
+ ( PathToTestFile( 'testy', 'SingleEntity.cs' ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'SingleEntity.cs' ), 6, 8 ) ),
+ ( PathToTestFile( 'testy', 'GotoTestCase.cs' ),
+ has_items(
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 6, 8 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 26, 12 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 30, 8 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 35, 12 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 39, 12 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 43, 8 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 48, 8 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 8, 15 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 13, 15 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 17, 15 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 21, 15 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 27, 8 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 31, 15 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 36, 8 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 40, 8 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 44, 15 ),
+ LocationMatcher(
+ PathToTestFile( 'testy', 'GotoTestCase.cs' ), 49, 15 ) ) )
+ ]:
+ with self.subTest( filepath = filepath, expected = expected ):
+ with WrapOmniSharpServer( app, filepath ):
+
+ # the command name and file are the only relevant arguments for this
+ # subcommand. our current cursor position in the file doesn't matter.
+ request = BuildRequest( command_arguments = [ 'GoToDocumentOutline' ],
+ line_num = 0,
+ column_num = 0,
+ contents = ReadFile( filepath ),
+ filetype = 'cs',
+ filepath = filepath )
+
+ response = app.post_json( '/run_completer_command',
+ request,
+ expect_errors = True ).json
+ print( 'completer response = ', response )
+ assert_that( response, expected )
diff --git a/ycmd/tests/cs/testdata/testy/Empty.cs b/ycmd/tests/cs/testdata/testy/Empty.cs
new file mode 100644
index 0000000000..fbdadd554e
--- /dev/null
+++ b/ycmd/tests/cs/testdata/testy/Empty.cs
@@ -0,0 +1,6 @@
+using System;
+using System.Data;
+
+namespace testy
+{
+}
diff --git a/ycmd/tests/cs/testdata/testy/SingleEntity.cs b/ycmd/tests/cs/testdata/testy/SingleEntity.cs
new file mode 100644
index 0000000000..594f1a8fea
--- /dev/null
+++ b/ycmd/tests/cs/testdata/testy/SingleEntity.cs
@@ -0,0 +1,9 @@
+using System;
+using System.Data;
+
+namespace testy
+{
+ class GotoTestCase
+ {
+ }
+}
diff --git a/ycmd/tests/cs/testdata/testy/testy.csproj b/ycmd/tests/cs/testdata/testy/testy.csproj
index 7bba3cb813..6e2787b5ba 100644
--- a/ycmd/tests/cs/testdata/testy/testy.csproj
+++ b/ycmd/tests/cs/testdata/testy/testy.csproj
@@ -48,6 +48,8 @@
+
+
diff --git a/ycmd/tests/java/subcommands_test.py b/ycmd/tests/java/subcommands_test.py
index 02af0ae03f..d31657b265 100644
--- a/ycmd/tests/java/subcommands_test.py
+++ b/ycmd/tests/java/subcommands_test.py
@@ -537,6 +537,37 @@ def test_Subcommands_GetType_LiteralValue( self, app ):
ErrorMatcher( RuntimeError, 'Unknown type' ) )
+ @SharedYcmd
+ def test_Subcommands_GoToDeclaration_NoLocation( self, app ):
+ filepath = PathToTestFile( 'simple_eclipse_project',
+ 'src',
+ 'com',
+ 'test',
+ 'TestLauncher.java' )
+ contents = ReadFile( filepath )
+
+ # Virtual call of getWidgetInfo - don't know the concrete implementation.
+ # Here GoToDefinition jumps to the interface method declaration and
+ # GoToDeclaration does nothing...
+ event_data = BuildRequest( filepath = filepath,
+ filetype = 'java',
+ line_num = 34,
+ column_num = 59,
+ contents = contents,
+ command_arguments = [ 'GoToDeclaration' ],
+ completer_target = 'filetype_default' )
+
+ response = app.post_json( '/run_completer_command',
+ event_data,
+ expect_errors = True )
+
+ assert_that( response.status_code,
+ equal_to( requests.codes.internal_server_error ) )
+
+ assert_that( response.json,
+ ErrorMatcher( RuntimeError, 'Cannot jump to location' ) )
+
+
@WithRetry()
@SharedYcmd
def test_Subcommands_GoTo_NoLocation( self, app ):
@@ -1020,9 +1051,11 @@ def test_Subcommands_FixIt_SingleDiag_MultipleOption_Insertion( self, app ):
'text': "Create constant 'Wibble'",
'kind': 'quickfix',
'chunks': contains_exactly(
- ChunkMatcher( '\n\nprivate static final String Wibble = null;',
+ ChunkMatcher( '\n\nprivate static final String Wibble = null;'
+ '\n\n private void Wimble( Wibble w ) {'
+ '\n if ( w == Wibble',
LocationMatcher( filepath, 16, 4 ),
- LocationMatcher( filepath, 16, 4 ) ),
+ LocationMatcher( filepath, 19, 21 ) ),
),
} ),
has_entries( {
@@ -1056,27 +1089,29 @@ def test_Subcommands_FixIt_SingleDiag_MultipleOption_Insertion( self, app ):
'text': "Create local variable 'Wibble'",
'kind': 'quickfix',
'chunks': contains_exactly(
- ChunkMatcher( 'Object Wibble;\n ',
+ ChunkMatcher( 'Object Wibble;\n if ( w == Wibble',
LocationMatcher( filepath, 19, 5 ),
- LocationMatcher( filepath, 19, 5 ) ),
+ LocationMatcher( filepath, 19, 21 ) ),
),
} ),
has_entries( {
'text': "Create field 'Wibble'",
'kind': 'quickfix',
'chunks': contains_exactly(
- ChunkMatcher( '\n\nprivate Object Wibble;',
+ ChunkMatcher( '\n\nprivate Object Wibble;'
+ '\n\n private void Wimble( Wibble w ) {'
+ '\n if ( w == Wibble',
LocationMatcher( filepath, 16, 4 ),
- LocationMatcher( filepath, 16, 4 ) ),
+ LocationMatcher( filepath, 19, 21 ) ),
),
} ),
has_entries( {
'text': "Create parameter 'Wibble'",
'kind': 'quickfix',
'chunks': contains_exactly(
- ChunkMatcher( ', Object Wibble',
+ ChunkMatcher( ', Object Wibble ) {\n if ( w == Wibble',
LocationMatcher( filepath, 18, 32 ),
- LocationMatcher( filepath, 18, 32 ) ),
+ LocationMatcher( filepath, 19, 21 ) ),
),
} ),
has_entries( {
@@ -1485,6 +1520,10 @@ def test_Subcommands_FixIt_Range( self, app ):
has_entries( {
'text': "Sort Members for 'TestLauncher.java'"
} ),
+ has_entries( {
+ 'text': 'Surround with try/catch',
+ 'chunks': instance_of( list )
+ } ),
)
} )
}
@@ -1545,8 +1584,17 @@ def test_Subcommands_FixIt_Unicode( self, app ):
'text': "Create method 'doUnicødeTes(String)'",
'kind': 'quickfix',
'chunks': contains_exactly(
- ChunkMatcher( 'private void doUnicødeTes(String test2) {\n}\n\n\n',
- LocationMatcher( TEST_JAVA, 20, 3 ),
+ ChunkMatcher(
+ 'doUnicødeTes( test );\n\n'
+ ' TéstClass tésting_with_unicøde = new TéstClass();\n'
+ ' return tésting_with_unicøde.a_test;\n'
+ ' }\n\n\n'
+ ' private void doUnicødeTes(String test2) {\n'
+ ' // TODO Auto-generated method stub\n'
+ ' throw new UnsupportedOperationException('
+ '"Unimplemented method \'doUnicødeTes\'");\n'
+ '}\n\n\n',
+ LocationMatcher( TEST_JAVA, 13, 10 ),
LocationMatcher( TEST_JAVA, 20, 3 ) ),
),
} ),
@@ -2012,7 +2060,7 @@ def test_Subcommands_GoTo( self, app ):
'filepath': TEST_JAVA },
'description': 'GoTo works for unicode identifiers' }
],
- [ 'GoTo', 'GoToDefinition', 'GoToDeclaration' ] ):
+ [ 'GoTo', 'GoToDefinition' ] ):
with self.subTest( command = command, test = test ):
RunGoToTest( app,
test[ 'description' ],
@@ -2023,6 +2071,31 @@ def test_Subcommands_GoTo( self, app ):
has_entries( test[ 'response' ] ) )
+ @SharedYcmd
+ def test_Subcommands_GoToDeclaration( self, app ):
+ source = PathToTestFile( 'simple_eclipse_project',
+ 'src',
+ 'com',
+ 'test',
+ 'TestWidgetImpl.java' )
+ destination = PathToTestFile( 'simple_eclipse_project',
+ 'src',
+ 'com',
+ 'test',
+ 'AbstractTestWidget.java' )
+ # Seems to be the only place GoToDeclaration actually works.
+ RunGoToTest( app,
+ 'GoToDeclaration works on an override definition.',
+ source,
+ 23,
+ 17,
+ 'GoToDeclaration',
+ has_entries( {
+ 'line_num': 17,
+ 'column_num': 17,
+ 'filepath': destination } ) )
+
+
@SharedYcmd
def test_Subcommands_GoToType( self, app ):
for test in [
diff --git a/ycmd/tests/javascriptreact/get_completions_test.py b/ycmd/tests/javascriptreact/get_completions_test.py
index 958413df05..40b9993489 100644
--- a/ycmd/tests/javascriptreact/get_completions_test.py
+++ b/ycmd/tests/javascriptreact/get_completions_test.py
@@ -75,7 +75,11 @@ def test_GetCompletions_JavaScriptReact_DefaultTriggers( self, app ):
'detailed_info': '(property) Document.alinkColor: string\n'
'\n'
'Sets or gets the color of all active links '
- 'in the document.',
+ 'in the document.\n'
+ '\n'
+ 'deprecated: [MDN Reference]'
+ '(https://developer.mozilla.org/docs/Web/'
+ 'API/Document/alinkColor)',
'kind': 'property',
} ) )
} )
diff --git a/ycmd/tests/typescript/get_completions_test.py b/ycmd/tests/typescript/get_completions_test.py
index 8a8f961ad7..2956f19800 100644
--- a/ycmd/tests/typescript/get_completions_test.py
+++ b/ycmd/tests/typescript/get_completions_test.py
@@ -357,3 +357,34 @@ def test_GetCompletions_TypeScriptReact_DefaultTriggers( self, app ):
} )
}
} )
+
+
+ @SharedYcmd
+ def test_GetCompletions_WithTags( self, app ):
+ filepath = PathToTestFile( 'signatures.ts' )
+ RunTest( app, {
+ 'description': 'No need to force after a semantic trigger',
+ 'request': {
+ 'line_num': 101,
+ 'column_num': 24,
+ 'filepath': filepath,
+ 'filetype': 'typescript'
+ },
+ 'expect': {
+ 'response': requests.codes.ok,
+ 'data': has_entries( {
+ 'completions': has_item( has_entries( {
+ 'insertion_text': 'single_argument_with_doc',
+ 'extra_data': {},
+ 'extra_menu_info':
+ 'function single_argument_with_doc(a: string): string',
+ 'detailed_info':
+ 'function single_argument_with_doc(a: string): string\n\n'
+ 'A function with a single argument\n\n'
+ 'param: a - The argument\n'
+ 'returns: - The hashed input',
+ 'kind': 'function'
+ } ) )
+ } )
+ }
+ } )
diff --git a/ycmd/tests/typescript/subcommands_test.py b/ycmd/tests/typescript/subcommands_test.py
index e64acb2546..33464090df 100644
--- a/ycmd/tests/typescript/subcommands_test.py
+++ b/ycmd/tests/typescript/subcommands_test.py
@@ -610,6 +610,29 @@ def test_Subcommands_GetDoc_Class_Unicode( self, app ):
} )
+ @SharedYcmd
+ def test_Subcommands_GetDoc_FreeFunction_WithTags( self, app ):
+ RunTest( app, {
+ 'description': 'GetDoc shows documentation of param and returns tags',
+ 'request': {
+ 'command': 'GetDoc',
+ 'line_num': 101,
+ 'column_num': 12,
+ 'filepath': PathToTestFile( 'signatures.ts' ),
+ },
+ 'expect': {
+ 'response': requests.codes.ok,
+ 'data': has_entries( {
+ 'detailed_info':
+ 'function single_argument_with_doc(a: string): string\n\n'
+ 'A function with a single argument\n\n'
+ 'param: a - The argument\n'
+ 'returns: - The hashed input'
+ } )
+ }
+ } )
+
+
@SharedYcmd
def test_Subcommands_GoToReferences( self, app ):
RunTest( app, {