public class ProjectItemCallbackProvider()

in EnvDTE.Host/Callback/Impl/ProjectModelImpl/ProjectItemCallbackProvider.cs [22:106]


    public class ProjectItemCallbackProvider(
        ILogger logger,
        ISolution solution,
        ProjectModelViewHost host,
        ISimpleLazy<IProjectModelEditor> projectModelEditor)
        : IEnvDteCallbackProvider
    {
        public void RegisterCallbacks(DteProtocolModel model, IScheduler scheduler)
        {
            model.ProjectItem_get_Name.SetWithProjectItemAsync(host, async (lifetime, _, projectItem) =>
                await lifetime.StartReadActionAsync(() => projectItem.Name));

            model.ProjectItem_set_Name.SetWithProjectItemVoidAsync(host, (lifetime, req, projectItem) =>
                lifetime.StartReadActionAsync(() =>
                    solution.InvokeUnderTransaction(cookie => cookie.Rename(projectItem, req.NewName))));

            model.ProjectItem_get_Kind.SetWithProjectItemAsync(host, (lifetime, _, projectItem) =>
                lifetime.StartReadActionAsync(() =>
                    projectItem.Kind switch
                    {
                        ProjectItemKind.PHYSICAL_FILE => ProjectItemKindModel.PhysicalFile,
                        ProjectItemKind.PHYSICAL_DIRECTORY => ProjectItemKindModel.PhysicalFolder,
                        ProjectItemKind.PROJECT => ProjectItemKindModel.Project,
                        ProjectItemKind.VIRTUAL_DIRECTORY => ProjectItemKindModel.VirtualDirectory,
                        _ => ProjectItemKindModel.Unknown,
                    }));

            model.ProjectItem_get_ProjectItems.SetWithProjectItemAsync(host, (lifetime, _, projectItem) =>
                lifetime.StartReadActionAsync(() => GetFilteredProjectItems(projectItem)
                    .Select(item => new ProjectItemModel(host.GetIdByItem(item)))
                    .AsList()));

            model.ProjectItem_get_ProjectItemCount.SetWithProjectItemAsync(host, (lifetime, _, projectItem) =>
                lifetime.StartReadActionAsync(() => GetFilteredProjectItems(projectItem).Count()));

            model.ProjectItem_get_Language.SetWithProjectItemAsync(host, async (lifetime, _, projectItem) =>
            {
                if (projectItem is not IProjectFile projectFile)
                {
                    logger.Warn($"Project item '{projectItem.Name}' is not a file.");
                    return LanguageModel.Unknown;
                }

                return await lifetime.StartReadActionAsync(() =>
                    projectFile.ToSourceFile()?.PrimaryPsiLanguage switch
                    {
                        CSharpLanguage _ => LanguageModel.CSharp,
                        VBLanguage _ => LanguageModel.VB,
                        _ => LanguageModel.Unknown
                    });
            });

            model.ProjectItem_remove.SetWithProjectItemVoidAsync(host, async (lifetime, _, projectItem) =>
            {
                logger.Trace($"Removing project item '{projectItem.Name}'");
                await lifetime.StartMainWrite(() => projectModelEditor.Value.Remove(projectItem));
            });

            model.ProjectItem_get_SubItemIndex.SetWithProjectItemAsync(host, async (lifetime, req, projectItem) =>
            {
                var itemNames = await lifetime.StartReadActionAsync(() =>
                    GetFilteredProjectItems(projectItem).Select(item => item.Name).ToArray());
                var index =  itemNames.IndexOf(req.Name, StringComparer.OrdinalIgnoreCase);
                return index == -1 ? null : index;
            });
        }

        private IEnumerable<IProjectItem> GetFilteredProjectItems(IProjectItem projectItem)
        {
            if (projectItem is not IProjectFolder rootItem) return [];

            // If we are returning subitems of a project, the project file should not be returned
            IProjectFile projectFile = null;
            if (rootItem is IProject project) projectFile = project.ProjectFile;

            // Even though we filter out hidden items, in some edge cases it happens that visible items have an id of 0
            // So we have to check for that as well.
            return rootItem.GetSubItems().Where(item => item switch
            {
                ProjectFolderImpl folder => !folder.IsHidden,
                IProjectFile file => projectFile is null || !file.Equals(projectFile),
                _ => true
            }).Where(item => host.GetIdByItem(item) != 0);
        }
    }