using System; using System.IO; using System.Threading.Tasks; using JetBrains.Application.Components; using JetBrains.Application.Parts; using JetBrains.Collections.Viewable; using JetBrains.DocumentManagers.Transactions; using JetBrains.DocumentManagers.Transactions.ProjectHostActions.Modifications; using JetBrains.EnvDTE.Host.Callback.Util; using JetBrains.Lifetimes; using JetBrains.ProjectModel; using JetBrains.RdBackend.Common.Features.ProjectModel; using JetBrains.RdBackend.Common.Features.ProjectModel.View; using JetBrains.ReSharper.Resources.Shell; using JetBrains.Rider.Model; using JetBrains.Util; namespace JetBrains.EnvDTE.Host.Callback.Impl.ProjectModelImpl; [SolutionComponent(Instantiation.DemandAnyThreadSafe)] public class ProjectItemsCallbackProvider( ILogger logger, ISolution solution, ProjectModelViewHost host, ISimpleLazy 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 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 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)); } }