public class ProjectItemsCallbackProvider()

in EnvDTE.Host/Callback/Impl/ProjectModelImpl/ProjectItemsCallbackProvider.cs [21:107]


public class ProjectItemsCallbackProvider(
    ILogger logger,
    ISolution solution,
    ProjectModelViewHost host,
    ISimpleLazy<IProjectModelEditor> projectModelEditor) : IEnvDteCallbackProvider
{
    public void RegisterCallbacks(DteProtocolModel model, IScheduler scheduler)
    {
        model.ProjectItems_addFolder.SetWithProjectFolderAsync(host, AddFolderAsync);
        model.ProjectItems_addFromFile.SetWithProjectFolderAsync(host, (lifetime, request, projectFolder) =>
            AddExistingItemAsync(lifetime, request, projectFolder));
        model.ProjectItems_addFromDirectory.SetWithProjectFolderAsync(host, (lifetime, request , projectFolder) =>
            AddExistingItemAsync(lifetime, request, projectFolder, copyBeforeAdd: true));
        model.ProjectItems_addFromFileCopy.SetWithProjectFolderAsync(host, (lifetime, request, projectFolder) =>
            AddExistingItemAsync(lifetime, request, projectFolder, copyBeforeAdd: true));
    }

    private async Task<ProjectItemModel> AddFolderAsync(Lifetime lifetime, ProjectItems_addFolderRequest request,
        IProjectFolder parentFolder)
    {
        logger.Trace($"Adding folder '{request.Name}' to '{parentFolder.Location}'");

        var result = await lifetime.StartMainWrite(() =>
            projectModelEditor.Value.AddFolder(parentFolder, request.Name));

        return result is null
            ? null
            : new ProjectItemModel(host.GetIdByItem(result));
    }

    private async Task<ProjectItemModel> AddExistingItemAsync(
        Lifetime lifetime,
        AddExistingItemRequest request,
        IProjectFolder parentFolder,
        bool copyBeforeAdd = false)
    {
        var sourcePath = VirtualFileSystemPath.Parse(request.Path, InteractionContext.SolutionContext);
        if (request.IsDirectory && !sourcePath.ExistsDirectory || !request.IsDirectory && !sourcePath.ExistsFile)
            throw new InvalidOperationException("Cannot add non-existing item");

        if (sourcePath.Equals(parentFolder.Location))
            throw new InvalidOperationException("Cannot add item to itself");
        if (sourcePath.IsPrefixOf(parentFolder.Location))
            throw new InvalidOperationException("Cannot add item to its subfolder");

        if (copyBeforeAdd)
        {
            var destinationPath = parentFolder.Location / sourcePath.Name;
            logger.Trace($"Copying {sourcePath} to {destinationPath}");

            try
            {
                sourcePath.Copy(destinationPath, false);
            }
            catch (Exception e)
            {
                logger.Error(e, $"Failed to copy {sourcePath} to {destinationPath}");
                throw new IOException("Failed to copy the item");
            }

            sourcePath = destinationPath;
        }

        logger.Trace($"Adding existing item from '{sourcePath}' to '{parentFolder.Location}'");

        // Based on `JetBrains.RdBackend.Common.Features.ProjectModel.View.ProjectModelTaskHandler.AddItemsHandler`
        IProjectItem result = null;
        await lifetime.StartMainWrite(() =>
            // TODO: Maybe ordering context is necessary
            solution.InvokeUnderTransaction(cookie =>
            {
                AddItemTaskAction action;
                if (parentFolder.IsSolutionFolder())
                    action = new AddItemInSolutionFolder(lifetime, cookie, parentFolder, sourcePath);
                else if (parentFolder.Location.IsPrefixOf(sourcePath))
                    action = new AddItemPreserveDirectoriesTaskAction(lifetime, cookie, parentFolder, sourcePath);
                else
                    action = new AddItemAsLinkTaskAction(lifetime, cookie, parentFolder, sourcePath);

                result = action.Execute();
            }));

        return result is null
            ? null
            : new ProjectItemModel(host.GetIdByItem(result));
    }
}