diff --git a/ycmd/completers/language_server/language_server_completer.py b/ycmd/completers/language_server/language_server_completer.py index 489e15d08c..493701d996 100644 --- a/ycmd/completers/language_server/language_server_completer.py +++ b/ycmd/completers/language_server/language_server_completer.py @@ -3590,8 +3590,8 @@ def WorkspaceEditToFixIt( request_data, if not workspace_edit: return None + chunks = [] if 'changes' in workspace_edit: - chunks = [] # We sort the filenames to make the response stable. Edits are applied in # strict sequence within a file, but apply to files in arbitrary order. # However, it's important for the response to be stable for the tests. @@ -3599,12 +3599,26 @@ def WorkspaceEditToFixIt( request_data, chunks.extend( TextEditToChunks( request_data, uri, workspace_edit[ 'changes' ][ uri ] ) ) - else: - chunks = [] + if 'documentChanges' in workspace_edit: for text_document_edit in workspace_edit[ 'documentChanges' ]: - uri = text_document_edit[ 'textDocument' ][ 'uri' ] - edits = text_document_edit[ 'edits' ] - chunks.extend( TextEditToChunks( request_data, uri, edits ) ) + kind = text_document_edit.get( 'kind', '' ) + if 'edits' in text_document_edit: + uri = text_document_edit[ 'textDocument' ][ 'uri' ] + edits = text_document_edit[ 'edits' ] + chunks.extend( TextEditToChunks( request_data, uri, edits ) ) + elif kind == 'rename': + chunks.append( + responses.RenameChunk( + old_filepath = lsp.UriToFilePath( text_document_edit[ 'oldUri' ] ), + new_filepath = lsp.UriToFilePath( text_document_edit[ 'newUri' ] ) ) ) + elif kind == 'delete': + chunks.append( + responses.DeleteChunk( + lsp.UriToFilePath( text_document_edit[ 'uri' ] ) ) ) + elif kind == 'create': + chunks.append( + responses.CreateChunk( + lsp.UriToFilePath( text_document_edit[ 'uri' ] ) ) ) return responses.FixIt( responses.Location( request_data[ 'line_num' ], request_data[ 'column_num' ], diff --git a/ycmd/completers/language_server/language_server_protocol.py b/ycmd/completers/language_server/language_server_protocol.py index 365f8a04c5..14ea74f62d 100644 --- a/ycmd/completers/language_server/language_server_protocol.py +++ b/ycmd/completers/language_server/language_server_protocol.py @@ -307,7 +307,10 @@ def Initialize( request_id, 'didChangeWatchedFiles': { 'dynamicRegistration': True }, - 'workspaceEdit': { 'documentChanges': True, }, + 'workspaceEdit': { + 'resourceOperations': [ 'create', 'rename', 'delete'], + 'documentChanges': True, + }, 'symbol': { 'symbolKind': { 'valueSet': list( range( 1, len( SYMBOL_KIND ) ) ), diff --git a/ycmd/responses.py b/ycmd/responses.py index de87a57f83..ffe33f1c90 100644 --- a/ycmd/responses.py +++ b/ycmd/responses.py @@ -268,6 +268,22 @@ def __init__( self, location: Location, chunks, text = '', kind = None ): self.kind = kind +class DeleteChunk: + def __init__( self, filepath ): + self.filepath = filepath + self.kind = 'delete' + +class CreateChunk: + def __init__( self, filepath ): + self.filepath = filepath + self.kind = 'create' + +class RenameChunk: + def __init__( self, old_filepath, new_filepath ): + self.old_filepath = old_filepath + self.new_filepath = new_filepath + self.kind = 'rename' + class FixItChunk: """An individual replacement within a FixIt (aka Refactor)""" @@ -315,10 +331,22 @@ def BuildFixItResponse( fixits ): both quick fix and refactor operations""" def BuildFixitChunkData( chunk ): - return { - 'replacement_text': chunk.replacement_text, - 'range': BuildRangeData( chunk.range ), - } + if hasattr( chunk, 'replacement_text' ): + return { + 'replacement_text': chunk.replacement_text, + 'range': BuildRangeData( chunk.range ), + } + elif hasattr( chunk, 'new_filepath' ): + return { + 'new_filepath': chunk.new_filepath, + 'old_filepath': chunk.old_filepath, + 'kind': chunk.kind + } + else: + return { + 'filepath': chunk.filepath, + 'kind': chunk.kind + } def BuildFixItData( fixit ): if hasattr( fixit, 'resolve' ):