Repository: godotengine/godot
File: editor/scene/sprite_frames_editor_plugin.cpp (C++)

Code before:
	clipboard_anim->animation_loop = frames->get_animation_loop(edited_anim);
	clipboard_anim->is_copy = true;

	int frame_count = frames->get_frame_count(edited_anim);
	for (int i = 0; i < frame_count; ++i) {
		ClipboardSpriteFrames::Frame frame;
		frame.texture = frames->get_frame_texture(edited_anim, i);
		frame.duration = frames->get_frame_duration(edited_anim, i);
		clipboard_anim->frames.push_back(frame);
	}

	EditorSettings::get_singleton()->set_resource_clipboard(clipboard_anim);
}
void SpriteFramesEditor::_animation_paste() {
	Ref<ClipboardAnimation> clipboard_anim = EditorSettings::get_singleton()->get_resource_clipboard();
	if (!clipboard_anim.is_valid()) {
		return;
	}

	if (updating) {
		return;
	}

	if (!clipboard_anim->is_copy) {
		EditorSettings::get_singleton()->set_resource_clipboard(nullptr);
	}

	int counter = 1;
	String new_name = edited_anim;
	PackedStringArray name_component = new_name.rsplit("_", true, 1);
	String base_name = name_component[0];
	if (name_component.size() > 1 && name_component[1].is_valid_int() && name_component[1].to_int() >= 0) {
		counter = name_component[1].to_int();
	}
	new_name = base_name + "_" + itos(counter);
	while (frames->has_animation(new_name)) {
		counter++;
		new_name = base_name + "_" + itos(counter);
	}

	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
	undo_redo->create_action(TTR("Paste Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene());
	undo_redo->add_do_method(frames.ptr(), "add_animation", new_name);
	undo_redo->add_undo_method(frames.ptr(), "remove_animation", new_name);
	undo_redo->add_do_method(frames.ptr(), "set_animation_speed", new_name, clipboard_anim->animation_speed);
	undo_redo->add_do_method(frames.ptr(), "set_animation_loop", new_name, clipboard_anim->animation_loop);

	for (ClipboardSpriteFrames::Frame &frame : clipboard_anim->frames) {
		undo_redo->add_do_method(frames.ptr(), "add_frame", new_name, frame.texture, frame.duration);
	}


Review comment (bug):
I'm not sure the logic for clearing the buffer after pasting the cut animation is necessary. I couldn't find any other instances of `EditorSettings::get_singleton()->set_resource_clipboard(nullptr);`. In text editors, you can also paste the cut fragment an unlimited number of times.

Code after:
	}

	undo_redo->add_do_method(this, "_select_animation", new_name);
	undo_redo->add_undo_method(this, "_select_animation", edited_anim);
	undo_redo->add_do_method(this, "_update_library");
	undo_redo->add_undo_method(this, "_update_library");
	undo_redo->commit_action();
}

void SpriteFramesEditor::_animation_remove() {
	if (updating) {
		return;
	}

	if (!frames->has_animation(edited_anim)) {
		return;
	}

	delete_dialog->set_text(TTRC("Delete Animation?"));
	delete_dialog->popup_centered();
}

void SpriteFramesEditor::_animation_remove_confirmed() {
	_animation_remove_undo_redo(TTR("Remove Animation"), nullptr);
}

void SpriteFramesEditor::_animation_search_text_changed(const String &p_text) {
	_update_library();
}

void SpriteFramesEditor::_animation_loop_changed() {
	if (updating) {
		return;
	}

	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
	undo_redo->create_action(TTR("Change Animation Loop"), UndoRedo::MERGE_DISABLE, frames.ptr());
	undo_redo->add_do_method(frames.ptr(), "set_animation_loop", edited_anim, anim_loop->is_pressed());
	undo_redo->add_undo_method(frames.ptr(), "set_animation_loop", edited_anim, frames->get_animation_loop(edited_anim));
	undo_redo->add_do_method(this, "_update_library", true);
	undo_redo->add_undo_method(this, "_update_library", true);
	undo_redo->commit_action();
}

void SpriteFramesEditor::_animation_speed_resized() {
	anim_speed->update_minimum_size();
}

void SpriteFramesEditor::_animation_speed_changed(double p_value) {
	if (frames.is_null()) {
		return;