From 77d333a69980e4c357007d6654335f40e5b66b16 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Mon, 12 Aug 2024 07:54:00 +0200 Subject: [PATCH] Add support for DocumentSymbol in document outline requests We are intentionally not advertising the capability. We do want a flat response, so receiving a DocumentSymbol is a pessimisation. Not advertising the capability means that conforming servers take the faster code path and the likes of OmniSharp, that assume capabilities, still work. Yes, it's messy, but so is LSP. --- .../language_server_completer.py | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/ycmd/completers/language_server/language_server_completer.py b/ycmd/completers/language_server/language_server_completer.py index cd2b1317bc..9d6cf347f6 100644 --- a/ycmd/completers/language_server/language_server_completer.py +++ b/ycmd/completers/language_server/language_server_completer.py @@ -2655,10 +2655,10 @@ def GoToDocumentOutline( self, request_data ): result = response.get( 'result' ) or [] - # We should only receive SymbolInformation (not DocumentSymbol) if any( 'range' in s for s in result ): - raise ValueError( - "Invalid server response; DocumentSymbol not supported" ) + LOGGER.debug( 'Hierarchical DocumentSymbol not supported.' ) + result = _FlattenDocumentSymbolHierarchy( result ) + return _DocumentSymboListToGoTo( request_data, result ) return _SymbolInfoListToGoTo( request_data, result ) @@ -3427,6 +3427,50 @@ def BuildGoToLocationFromSymbol( symbol ): return locations +def _FlattenDocumentSymbolHierarchy( symbols ): + result = [] + for s in symbols: + partial_results = [ s ] + if s.get( 'children' ): + partial_results.extend( + _FlattenDocumentSymbolHierarchy( s[ 'children' ] ) ) + result.extend( partial_results ) + return result + + +def _DocumentSymboListToGoTo( request_data, symbols ): + """Convert a list of LSP DocumentSymbol into a YCM GoTo response""" + + print(1) + def BuildGoToLocationFromSymbol( symbol ): + symbol[ 'uri' ] = lsp.FilePathToUri( request_data[ 'filepath' ] ) + location, line_value = _LspLocationToLocationAndDescription( + request_data, + symbol ) + + description = ( f'{ lsp.SYMBOL_KIND[ symbol[ "kind" ] ] }: ' + f'{ symbol[ "name" ] }' ) + + goto = responses.BuildGoToResponseFromLocation( location, + description ) + goto[ 'extra_data' ] = { + 'kind': lsp.SYMBOL_KIND[ symbol[ 'kind' ] ], + 'name': symbol[ 'name' ], + } + return goto + + locations = [ BuildGoToLocationFromSymbol( s ) for s in + sorted( symbols, + key = lambda s: ( s[ 'kind' ], s[ 'name' ] ) ) ] + + if not locations: + raise RuntimeError( "Symbol not found" ) + elif len( locations ) == 1: + return locations[ 0 ] + else: + return locations + + def _LspLocationToLocationAndDescription( request_data, location, range_property = 'range' ):