From e47a87a290b9a20096f5f04dee3be2e2d6da6ed9 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Sun, 13 Oct 2024 09:08:23 +0200 Subject: [PATCH 1/4] Handle file operations that are represented by new fixit chunk kinds --- python/ycm/vimsupport.py | 83 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 8 deletions(-) diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index 2a29b1ede..80935c00b 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -935,7 +935,12 @@ def _SortChunksByFile( chunks ): chunks_by_file = defaultdict( list ) for chunk in chunks: - filepath = chunk[ 'range' ][ 'start' ][ 'filepath' ] + if 'range' in chunk: + filepath = chunk[ 'range' ][ 'start' ][ 'filepath' ] + elif 'old_file' in chunk: + filepath = chunk[ 'old_file' ] + else: + filepath = chunk[ 'file' ] chunks_by_file[ filepath ].append( chunk ) return chunks_by_file @@ -1078,17 +1083,40 @@ def ReplaceChunksInBuffer( chunks, vim_buffer ): # reverse order. chunks.reverse() chunks.sort( key = lambda chunk: ( - chunk[ 'range' ][ 'start' ][ 'line_num' ], - chunk[ 'range' ][ 'start' ][ 'column_num' ] + chunk.get( 'range', {} ).get( 'start', {} ).get( 'line_num', 1 ), + chunk.get( 'range', {} ).get( 'start', {} ).get( 'column_num', 1 ) ), reverse = True ) # However, we still want to display the locations from the top of the buffer # to its bottom. - return reversed( [ ReplaceChunk( chunk[ 'range' ][ 'start' ], - chunk[ 'range' ][ 'end' ], - chunk[ 'replacement_text' ], - vim_buffer ) - for chunk in chunks ] ) + replace_chunks = [] + for chunk in chunks: + if 'range' in chunk: + replace_chunks.append( + ReplaceChunk( + chunk[ 'range' ][ 'start' ], + chunk[ 'range' ][ 'end' ], + chunk[ 'replacement_text' ], + vim_buffer ) ) + elif 'old_file' in chunk: + replace_chunks.append( + RenameChunk( + chunk[ 'old_file' ], + chunk[ 'new_file' ], + vim_buffer ) ) + elif chunk[ 'kind' ] == 'create': + replace_chunks.append( + CreateChunk( + chunk[ 'file' ], + vim_buffer, + chunk[ 'kind' ] ) ) + elif chunk[ 'kind' ] == 'delete': + replace_chunks.append( + DeleteChunk( + chunk[ 'file' ], + vim_buffer, + chunk[ 'kind' ] ) ) + return reversed( replace_chunks ) def SplitLines( contents ): @@ -1172,6 +1200,45 @@ def ReplaceChunk( start, end, replacement_text, vim_buffer ): } +def RenameChunk( old_file, new_file, vim_buffer, kind = 'rename' ): + OpenFilename( old_file ) + vim.command( f'silent! saveas { new_file }' ) + vim.command( f'silent! bw! old_file' ) + os.remove( old_file ) + return { + 'bufnr': vim_buffer.number, + 'filename': new_file, + 'lnum': 1, + 'col': 1, + 'text': '', + 'type': 'F', + } + + +def CreateChunk( file, vim_buffer, kind = 'create' ): + return { + 'bufnr': vim_buffer.number, + 'filename': file, + 'lnum': 1, + 'col': 1, + 'text': '', + 'type': 'F', + } + + +def DeleteChunk( file, vim_buffer, kind = 'delete' ): + vim.command( f'silent! bw! { vim_buffer }' ) + os.remove( file ) + return { + 'bufnr': vim_buffer.number, + 'filename': file, + 'lnum': 1, + 'col': 1, + 'text': '', + 'type': 'F', + } + + def InsertNamespace( namespace ): if VariableExists( 'g:ycm_csharp_insert_namespace_expr' ): expr = GetVariableValue( 'g:ycm_csharp_insert_namespace_expr' ) From 7b517518bee49c56a0238851567206961b3b5e62 Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Sun, 13 Oct 2024 09:15:26 +0200 Subject: [PATCH 2/4] typo --- python/ycm/vimsupport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index 80935c00b..0da175851 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -1203,7 +1203,7 @@ def ReplaceChunk( start, end, replacement_text, vim_buffer ): def RenameChunk( old_file, new_file, vim_buffer, kind = 'rename' ): OpenFilename( old_file ) vim.command( f'silent! saveas { new_file }' ) - vim.command( f'silent! bw! old_file' ) + vim.command( f'silent! bw! { old_file }' ) os.remove( old_file ) return { 'bufnr': vim_buffer.number, From 810dee59591259732314952cca961b72f67e9b2b Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Sun, 13 Oct 2024 21:09:19 +0200 Subject: [PATCH 3/4] Make package rename work in java --- python/ycm/vimsupport.py | 60 ++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 20 deletions(-) diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index 0da175851..36e744b2e 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -16,6 +16,8 @@ # along with YouCompleteMe. If not, see . import vim +import pathlib +import shutil import os import json import re @@ -935,12 +937,13 @@ def _SortChunksByFile( chunks ): chunks_by_file = defaultdict( list ) for chunk in chunks: - if 'range' in chunk: + kind = chunk[ 'kind' ] + if kind == 'change': filepath = chunk[ 'range' ][ 'start' ][ 'filepath' ] - elif 'old_file' in chunk: - filepath = chunk[ 'old_file' ] + elif kind == 'rename': + filepath = chunk[ 'old_filepath' ] else: - filepath = chunk[ 'file' ] + filepath = chunk[ 'filepath' ] chunks_by_file[ filepath ].append( chunk ) return chunks_by_file @@ -1091,30 +1094,34 @@ def ReplaceChunksInBuffer( chunks, vim_buffer ): # to its bottom. replace_chunks = [] for chunk in chunks: - if 'range' in chunk: + kind = chunk[ 'kind' ] + if kind == 'change': replace_chunks.append( ReplaceChunk( chunk[ 'range' ][ 'start' ], chunk[ 'range' ][ 'end' ], chunk[ 'replacement_text' ], vim_buffer ) ) - elif 'old_file' in chunk: + elif kind == 'rename': replace_chunks.append( RenameChunk( - chunk[ 'old_file' ], - chunk[ 'new_file' ], + chunk[ 'old_filepath' ], + chunk[ 'new_filepath' ], + chunk[ 'options' ], vim_buffer ) ) - elif chunk[ 'kind' ] == 'create': + elif kind == 'create': replace_chunks.append( CreateChunk( - chunk[ 'file' ], + chunk[ 'filepath' ], vim_buffer, + chunk[ 'options' ], chunk[ 'kind' ] ) ) - elif chunk[ 'kind' ] == 'delete': + elif kind == 'delete': replace_chunks.append( DeleteChunk( - chunk[ 'file' ], + chunk[ 'filepath' ], vim_buffer, + chunk[ 'options' ], chunk[ 'kind' ] ) ) return reversed( replace_chunks ) @@ -1200,11 +1207,14 @@ def ReplaceChunk( start, end, replacement_text, vim_buffer ): } -def RenameChunk( old_file, new_file, vim_buffer, kind = 'rename' ): - OpenFilename( old_file ) - vim.command( f'silent! saveas { new_file }' ) - vim.command( f'silent! bw! { old_file }' ) - os.remove( old_file ) +def RenameChunk( old_file, new_file, vim_buffer, options, kind = 'rename' ): + os.rename( old_file, new_file ) + vim_buffer.name = new_file + vim_buffer.options[ 'modified' ] = True + old_current = vim.current.buffer + vim.current.buffer = vim_buffer + vim.command( "write!" ) + vim.current.buffer = old_current return { 'bufnr': vim_buffer.number, 'filename': new_file, @@ -1215,7 +1225,11 @@ def RenameChunk( old_file, new_file, vim_buffer, kind = 'rename' ): } -def CreateChunk( file, vim_buffer, kind = 'create' ): +def CreateChunk( file, vim_buffer, options, kind = 'create' ): + filepath = pathlib.Path( file ) + directory = filepath.parent() + os.makedirs( directory, exist_ok = True ) + open( file, 'a' ).close() return { 'bufnr': vim_buffer.number, 'filename': file, @@ -1226,9 +1240,15 @@ def CreateChunk( file, vim_buffer, kind = 'create' ): } -def DeleteChunk( file, vim_buffer, kind = 'delete' ): +def DeleteChunk( file, vim_buffer, options, kind = 'delete' ): vim.command( f'silent! bw! { vim_buffer }' ) - os.remove( file ) + try: + if options.get( 'recursive' ): + shutil.rmtree( file ) + else: + os.remove( file ) + except FileNotFoundError: + pass return { 'bufnr': vim_buffer.number, 'filename': file, From 773af93507c5c8b0a57c5689419c6734654ede7d Mon Sep 17 00:00:00 2001 From: Boris Staletic Date: Sun, 13 Oct 2024 23:55:03 +0200 Subject: [PATCH 4/4] Update the fixit confirm prompt --- python/ycm/vimsupport.py | 46 ++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/python/ycm/vimsupport.py b/python/ycm/vimsupport.py index 36e744b2e..1ae3d7e20 100644 --- a/python/ycm/vimsupport.py +++ b/python/ycm/vimsupport.py @@ -21,6 +21,7 @@ import os import json import re +import io from collections import defaultdict, namedtuple from functools import lru_cache as memoize from ycmd.utils import ( ByteOffsetToCodepointOffset, @@ -40,10 +41,13 @@ 'new-tab' : 'tabedit' } FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT = ( - 'The requested operation will apply changes to {0} files which are not ' - 'currently open. This will therefore open {0} new files in the hidden ' - 'buffers. The quickfix list can then be used to review the changes. No ' - 'files will be written to disk. Do you wish to continue?' ) + 'The requested operation will apply the following changes:\n\n' + '{0}\n' + 'Create, Rename and Delete operations change the state on disk.\n' + 'Change operations are only reflected inside vim, unless followed by ' + 'another kind of operation relating to the same file.\n' + 'The results will be placed in the location list.\n' + 'Do you wish to continue?' ) NO_SELECTION_MADE_MSG = "No valid selection was made; aborting." @@ -949,12 +953,32 @@ def _SortChunksByFile( chunks ): return chunks_by_file -def _GetNumNonVisibleFiles( file_list ): +def _GetNumNonVisibleFiles( chunks ): """Returns the number of file in the iterable list of files |file_list| which are not curerntly open in visible windows""" - return len( - [ f for f in file_list - if not BufferIsVisible( GetBufferNumberForFilename( f ) ) ] ) + warn = False + operations = io.StringIO() + for chunk in chunks: + kind = chunk[ 'kind' ] + if kind in ( 'create', 'rename', 'delete' ): + warn = True + if kind == 'change': + filepath = chunk[ 'range' ][ 'start' ][ 'filepath' ] + elif kind == 'rename': + filepath = chunk[ 'old_filepath' ] + else: + filepath = chunk[ 'filepath' ] + if not BufferIsVisible( GetBufferNumberForFilename( filepath ) ): + warn = True + if kind != 'rename': + operations.write( f' - { kind.title() }: { filepath }\n' ) + else: + old = chunk[ 'old_filepath' ] + new = chunk[ 'new_filepath' ] + operations.write( f' - { kind.title() }: { old }\n' + f' to: { new }\n' ) + + return warn, operations.getvalue() def _OpenFileInSplitIfNeeded( filepath ): @@ -1033,11 +1057,11 @@ def ReplaceChunks( chunks, silent=False ): if not silent: # Make sure the user is prepared to have her screen mutilated by the new # buffers. - num_files_to_open = _GetNumNonVisibleFiles( sorted_file_list ) + warn, format_operations = _GetNumNonVisibleFiles( chunks ) - if num_files_to_open > 0: + if warn: if not Confirm( - FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( num_files_to_open ) ): + FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( format_operations ) ): return # Store the list of locations where we applied changes. We use this to display