implement move deletion and add tests for dnd kitten drop

Agent-Logs-Url: https://github.com/kovidgoyal/kitty/sessions/e2238746-2319-43e5-9b01-4899e7f06b50

Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-05-05 09:37:50 +00:00 committed by GitHub
parent 9a3f3ed14e
commit fe6807c9b0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 48 additions and 2 deletions

View file

@ -449,8 +449,21 @@ func (dnd *dnd) end_drop(success bool) {
if dnd.drop_status.reading_data {
dnd.lp.QueueDnDData(DC{
Type: 'r', Operation: utils.IfElse(success, dnd.drop_status.action, 0)}) // end drop
if success && dnd.drop_status.action == 2 && !dnd.drop_status.is_remote_client {
// TODO: delete all dropped files/dirs/symlinks after successful move
if success && dnd.drop_status.action == move_on_drop && !dnd.drop_status.is_remote_client {
for _, path := range dnd.drop_status.uri_list {
if path == "" {
continue
}
st, err := os.Lstat(path)
if err != nil {
continue
}
if st.IsDir() {
os.RemoveAll(path)
} else {
os.Remove(path)
}
}
}
}
dnd.reset_drop()

View file

@ -323,6 +323,39 @@ class TestDnDKitten(BaseTest):
do_overwrite_drop(enter_content, '\x1b[13u')
do_overwrite_drop(b'overwrite-esc-test-content', '\x1b[27u', 0) # ]]]
# ---- move operation: source items deleted for local client only ----
move_file = jn(self.src_data_dir, 'move_test.txt')
move_dir = jn(self.src_data_dir, 'move_test_dir')
move_link = jn(self.src_data_dir, 'move_test_link')
os.makedirs(move_dir)
with open(jn(move_dir, 'inside.txt'), 'wb') as f:
f.write(b'nested file in move dir')
with open(move_file, 'wb') as f:
f.write(b'move test content')
os.symlink('nonexistent_target', move_link)
move_uri = (
as_file_url(self.src_data_dir, 'move_test.txt') + '\r\n' +
as_file_url(self.src_data_dir, 'move_test_dir') + '\r\n' +
as_file_url(self.src_data_dir, 'move_test_link') + '\r\n'
).encode()
dnd_test_fake_drop_event(self.capture.window_id, False, ['text/uri-list'], move[0]+1, move[1]+1)
self.wait_for_state('drop_action', GLFW_DRAG_OPERATION_MOVE)
dnd_test_fake_drop_event(self.capture.window_id, True, ['text/uri-list'], move[0]+1, move[1]+1)
self.wait_for_state('drop_data_requests', ((1, 0, 0),))
dnd_test_fake_drop_data(self.capture.window_id, 'text/uri-list', move_uri)
self.wait_for_state('last_drop_action', GLFW_DRAG_OPERATION_MOVE)
self.wait_for_state('drop_action', 0)
if remote_client:
# Remote move: source items must NOT be deleted (the source app handles deletion)
self.assertTrue(os.path.exists(move_file), 'remote move: source file must not be deleted')
self.assertTrue(os.path.isdir(move_dir), 'remote move: source directory must not be deleted')
self.assertTrue(os.path.lexists(move_link), 'remote move: source symlink must not be deleted')
else:
# Local move: source items must be deleted after successful move
self.assertFalse(os.path.exists(move_file), 'local move: source file must be deleted')
self.assertFalse(os.path.exists(move_dir), 'local move: source directory must be deleted')
self.assertFalse(os.path.lexists(move_link), 'local move: source symlink must be deleted')
def assert_files_have_same_content(self, a, b):
with open(a, 'rb') as fa, open(b, 'rb') as fb:
self.assertEqual(fa.read(), fb.read(), f'{a} ({os.path.getsize(a)}) != {b} ({os.path.getsize(b)})')