diff --git a/FUnreal/Sources/Core/VS/FUnrealDTE.cs b/FUnreal/Sources/Core/VS/FUnrealDTE.cs index 25cb6ef..12483cb 100644 --- a/FUnreal/Sources/Core/VS/FUnrealDTE.cs +++ b/FUnreal/Sources/Core/VS/FUnrealDTE.cs @@ -4,7 +4,6 @@ using Microsoft.VisualStudio.Shell; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -42,7 +41,7 @@ public async Task InitializeAsync() } UENatvisPath = FindUENatvisPath(); - await ReloadProjectAsync(); + await ReloadProjectAsync(); if (UENatvisPath == null) { @@ -263,16 +262,26 @@ private string FindUENatvisPath() //Microsoft.VisualStudio.CommonIDE.Solutions //DteMiscProject + //NOTE: DTE Collections (ex. ProjectItems, FileNames) are 1-based (start index is 1 and not 0!!!) if (project.Name.Equals("Visualizers", StringComparison.OrdinalIgnoreCase)) { - if (project.ProjectItems.Count != 1) return null; - ProjectItem item = project.ProjectItems.Item(1); //Collection 1-based (not starting from 0!!!) + if (project.ProjectItems.Count == 0) return null; - if (item.FileCount != 1) return null; - string absFilePath = item.FileNames[1]; //Collection 1-based (not starting from 0!!!) - Debug.Print(" Natvis file path: {0}", absFilePath); - - return absFilePath; + foreach (ProjectItem item in project.ProjectItems) + { + string absFilePath = item.FileNames[1]; //Collection 1-based (not starting from 0!!!) + XDebug.Info("Visualizers file path: {0}", absFilePath); + + if (!XFilesystem.HasExtension(absFilePath, ".natvis")) + { + continue; + } + else + { + XDebug.Info("Visualizers natvis file found: {0}", absFilePath); + return absFilePath; + } + } } } @@ -287,7 +296,7 @@ private string FindUENatvisPath() //Example of api, to select multiple elment (like shift+selection). Not useful now, but just to keep it. //SolutionExplorerNode.SelectDown(vsUISelectionType.vsUISelectionTypeSetCaret, 1); - public async Task TryToSelectSolutionExplorerItemAsync(string[] relPathAfterProjectName) + public async Task TryToSelectSolutionExplorerItemAsync(string[] relPathAfterProjectName) { await XThread.SwitchToUIThreadIfItIsNotAsync(); @@ -295,7 +304,7 @@ public async Task TryToSelectSolutionExplorerItemAsync(string[] relPathAfterProj //NOTE: GetItem works only if item is expanded on Solution Explorer View (otherwise throws exception) //UIHierarchyItem foundItem = SolutionExplorerNode.GetItem(@"UENLOpt\Games\UENLOpt\Source\UENLOpt\MyActor.h"); - + //string[] parts = new string[] { "Source", "UENLOpt", "MyActor.h" }; UIHierarchyItem lastItem = GetProjectUIHierarchItem(); @@ -310,7 +319,7 @@ public async Task TryToSelectSolutionExplorerItemAsync(string[] relPathAfterProj { found = TryFindUIHierItemChild(lastItem, part, out UIHierarchyItem child); if (!found) break; - + child.UIHierarchyItems.Expanded = true; lastItem = child; } @@ -336,7 +345,7 @@ private UIHierarchyItem GetProjectUIHierarchItem() UIHierarchy SolutionExplorerNode = _DTE2.ToolWindows.SolutionExplorer; if (!SolutionExplorerNode.UIHierarchyItems.Expanded) SolutionExplorerNode.UIHierarchyItems.Expanded = true; if (SolutionExplorerNode.UIHierarchyItems.Count == 0) return null; - + UIHierarchyItem solutionNode = SolutionExplorerNode.UIHierarchyItems.Item(1); if (!solutionNode.UIHierarchyItems.Expanded) solutionNode.UIHierarchyItems.Expanded = true; if (solutionNode.UIHierarchyItems.Count == 0) return null; @@ -386,7 +395,8 @@ public async Task AddSubpathToProjectAsync(string[] relPathAfterProjectName) } lastItem = child; - } else + } + else { //Adding folder to the project item var newPItem = GetProjectItemsFrom(lastItem.Object).AddFolder(part, VSConstants.ItemTypeGuid.VirtualFolder_string); @@ -398,15 +408,16 @@ public async Task AddSubpathToProjectAsync(string[] relPathAfterProjectName) if (childFound) { lastItem = newChild; - } else + } + else { //shold not happen XDebug.Erro($"For the ProjectItem '{part}' just added, was not found the respective UIHierarchyItem!"); return; } } - } - + } + if (firstNotExpanded != null) { //by the fact the only way to explor UIHierarchyItem is to expand them, @@ -435,7 +446,7 @@ private bool TryFindUIHierItems(string[] relPathAfterProject, out List(); bool found = false; - foreach(var part in relPathAfterProject) + foreach (var part in relPathAfterProject) { found = false; bool originalExpanded = lastItem.UIHierarchyItems.Expanded; @@ -452,13 +463,14 @@ private bool TryFindUIHierItems(string[] relPathAfterProject, out List=0; i--) + for (int i = uiItemParents.Count - 1; i >= 0; i--) { uiItemParents[i].UIHierarchyItems.Expanded = originalExpandState[i]; } @@ -466,7 +478,8 @@ private bool TryFindUIHierItems(string[] relPathAfterProject, out List/Engine) if (enginePath == null || !XFilesystem.DirExists(enginePath)) { - unrealVS.Output.Erro("Cannot detect a valid UE path for: ", uprjFilePath); + unrealVS.Output.Erro("Cannot detect a valid UE path for: {0}", uprjFilePath); return null; } @@ -78,7 +78,8 @@ public static FUnrealService Create(IFUnrealVS unrealVS) { //Example UE4: C:\Program Files\Epic Games\UE_4.27\Engine\Binaries\DotNET\UnrealBuildTool.exe ubtBin = XFilesystem.PathCombine(enginePath, "Binaries/DotNET/UnrealBuildTool.exe"); - } else if (version.Major >= 5) //5+ + } + else if (version.Major >= 5) //5+ { //Example UE5: C:\Program Files\Epic Games\UE_5.0\Engine\Binaries\DotNET\UnrealBuildTool\UnrealBuildTool.exe ubtBin = XFilesystem.PathCombine(enginePath, "Binaries/DotNET/UnrealBuildTool/UnrealBuildTool.exe"); @@ -114,13 +115,13 @@ public static FUnrealService Create(IFUnrealVS unrealVS) } FUnrealTemplates templates = FUnrealTemplates.Load(templateDescPath); - + //Doing this validation now (by the fact is a fatal error), can avoid check on Controller side. string ueMajorVer = engine.Version.Major.ToString(); if (templates.GetTemplates(FUnrealTemplateCtx.PLUGINS, ueMajorVer).Count == 0) { unrealVS.Output.Erro("Cannot load plugin templates from path: {0}", templateDescPath); - return null; + return null; } if (templates.GetTemplates(FUnrealTemplateCtx.MODULES, ueMajorVer).Count == 0) { @@ -141,7 +142,7 @@ public static FUnrealService Create(IFUnrealVS unrealVS) private string _engineMajorVer; private string _uprjFileAbsPath; - + private string _prjPath; private string _pluginsPath; private string _sourcePath; @@ -158,7 +159,7 @@ public FUnrealService(FUnrealEngine engine, string uprjAbsPath, FUnrealTemplates _uprjFileAbsPath = uprjAbsPath; ProjectName = XFilesystem.GetFilenameNoExt(uprjAbsPath); _templates = templates; - + _prjPath = XFilesystem.PathParent(uprjAbsPath); _pluginsPath = XFilesystem.PathCombine(_prjPath, "Plugins"); _sourcePath = XFilesystem.PathCombine(_prjPath, "Source"); @@ -200,9 +201,9 @@ public async Task UpdateEmptyFoldersAsync() } - public string ProjectName { get; } + public string ProjectName { get; } - public bool IsSourceCodePath(string fullPath, bool afterModuleDir=false) + public bool IsSourceCodePath(string fullPath, bool afterModuleDir = false) { var module = GetUProject().AllModules.FindByBelongingPath(fullPath); if (module == null) return false; @@ -259,7 +260,7 @@ public FUnrealSourceType TypeForSourcePath(string path) string modulePath = ModulePathFromSourceCodePath(path); string afterModulePath = XFilesystem.PathSubtract(path, modulePath); - + string firstModuleSubfolder = XFilesystem.PathSplit(afterModulePath)[0]; if (firstModuleSubfolder == "Public") @@ -338,7 +339,7 @@ private string AbsProjectSourceFolderPath() { return _sourcePath; } - + // UI Only public string ProjectRelativePathForPlugin(string pluginName) { @@ -397,7 +398,7 @@ public string ProjectRelativePathForModule(string modName) public bool ExistsPlugin(string pluginName) { - return GetUProject().Plugins.Exists(pluginName); + return GetUProject().Plugins.Exists(pluginName); } public bool ExistsModule(string moduleName) @@ -415,7 +416,7 @@ private FUnrealModule ModuleNamed(string moduleName) var project = GetUProject(); return project.AllModules[moduleName]; } - + public string ModuleNameFromSourceCodePath(string path) { var found = GetUProject().AllModules.FindByBelongingPath(path); @@ -511,7 +512,7 @@ public bool IsPluginFolder(string fullPath) public bool IsPluginDescriptorFile(string fullPath) { - var found = GetUProject().Plugins.FindByBelongingPath(fullPath); + var found = GetUProject().Plugins.FindByBelongingPath(fullPath); if (found == null) return false; return fullPath == found.DescriptorFilePath; } @@ -532,7 +533,7 @@ public bool IsPluginModulePath(string fullPath) { string pluginName = PluginNameFromSourceCodePath(fullPath); string moduleName = ModuleNameFromSourceCodePath(fullPath); - + bool isTrue = pluginName != null && moduleName != null; return isTrue; } @@ -590,7 +591,7 @@ public async Task AddPluginAsync(string templeName, strategy.AddFileExtension(".cpp", ".h", ".cs", ".uplugin", "vcxproj"); //vcxproj from thirdpartylibrary strategy.AddPlaceholder(pluginNamePH, pluginName); - if (moduleNameOrNull != null) + if (moduleNameOrNull != null) { string fileName = moduleNameOrNull; if (!fileName.EndsWith("Module")) @@ -648,19 +649,21 @@ public async Task DeletePluginAsync(string pluginNam //Update uproject file var uprojectJson = new FUnrealUProjectFile(_uprjFileAbsPath); - if (uprojectJson.Plugins) { + if (uprojectJson.Plugins) + { var pluginJson = uprojectJson.Plugins[pluginName]; - if (pluginJson) { + if (pluginJson) + { notifier.Info(XDialogLib.Ctx_UpdatingProject, XDialogLib.Info_UpdatingProjectDescriptorFile, _uprjFileAbsPath); pluginJson.Remove(); uprojectJson.Plugins.RemoveIfEmpty(); uprojectJson.Save(); } } - + // Remove plugin modules in dependent .Build.cs (if configured there) taskSuccess = await FUnrealServiceTasks.Plugin_DeleteModuleDependencyAsync(plugin, GetUProject().AllModules, notifier); - + // Remove plugin in dependent .uplugin (if configured there) taskSuccess = await FUnrealServiceTasks.Plugin_DeleteDependencyAsync(plugin, GetUProject().Plugins, notifier); if (!taskSuccess) return false; @@ -816,7 +819,8 @@ public async Task AddPluginModuleAsync(string temple string upluginFilePath = plugin.DescriptorFilePath; notifier.Info(XDialogLib.Ctx_UpdatingPlugin, XDialogLib.Info_UpdatingPluginDescriptorFile, upluginFilePath); var upluginFile = new FUnrealUPluginJsonFile(upluginFilePath); - upluginFile.Modules.Add(new FUnrealUPluginModuleJson() { + upluginFile.Modules.Add(new FUnrealUPluginModuleJson() + { Name = moduleName, Type = metaType, LoadingPhase = metaPhase @@ -883,7 +887,7 @@ public async Task RenamePluginModuleAsync(string plu //6. Rename module in .uplugin taskSuccess = FUnrealServiceTasks.Plugin_RenameModuleInDescriptor(plugin, module, newModuleName, notifier); if (!taskSuccess) return false; - + //7. Regen VS Solution taskSuccess = await FUnrealServiceTasks.Project_RegenSolutionFilesAsync(project, _engineUbt, notifier); if (!taskSuccess) return false; @@ -906,7 +910,8 @@ public async Task DeletePluginModuleAsync(string plu var plugin = PluginByName(pluginName); FUnrealModule module = plugin.Modules[moduleName]; - if (module == null) { + if (module == null) + { notifier.Erro(XDialogLib.Ctx_CheckProjectPlayout, XDialogLib.Error_PluginModuleNotFound, pluginName, moduleName); return false; } @@ -931,8 +936,8 @@ public async Task DeletePluginModuleAsync(string plu moduleJson.Remove(); upluginFile.Save(); } - - + + //X. Regen VS Project taskSuccess = await FUnrealServiceTasks.Project_RegenSolutionFilesAsync(GetUProject(), _engineUbt, notifier); if (!taskSuccess) return false; @@ -956,8 +961,8 @@ public async Task AddSourceClassAsync(string te return false; } - ComputeSourceCodePaths(absBasePath, className, classType, - out string headerPath, + ComputeSourceCodePaths(absBasePath, className, classType, + out string headerPath, out string sourcePath, out string sourceRelPath ); @@ -978,7 +983,7 @@ out string sourceRelPath string headerFileME = tpl.GetMeta("header"); string sourceFileME = tpl.GetMeta("source"); - + string tplHeaderPath = XFilesystem.PathCombine(tpl.BasePath, headerFileME); string tplSourcePath = XFilesystem.PathCombine(tpl.BasePath, sourceFileME); if (!XFilesystem.FileExists(tplHeaderPath) && !XFilesystem.FileExists(tplSourcePath)) @@ -987,12 +992,12 @@ out string sourceRelPath return false; } - - string moduleApiPH = "@{TPL_MODU_API}"; + + string moduleApiPH = "@{TPL_MODU_API}"; string incluPathPH = "@{TPL_SOUR_INCL}"; string classNamePH = "@{TPL_SOUR_CLASS}"; string moduleApi = classType == FUnrealSourceType.PUBLIC ? $"{module.ApiMacro} " : ""; //Final space to separate from Class Name - + string incluPath = XFilesystem.PathToUnixStyle(sourceRelPath); if (incluPath != "") incluPath += "/"; //Final Path separator to separate from Class Name @@ -1004,7 +1009,7 @@ out string sourceRelPath PlaceHolderReplaceVisitor strategy = new PlaceHolderReplaceVisitor(); strategy.AddFileExtension(".h", ".cpp"); - strategy.AddPlaceholder(moduleApiPH, moduleApi); + strategy.AddPlaceholder(moduleApiPH, moduleApi); strategy.AddPlaceholder(incluPathPH, incluPath); strategy.AddPlaceholder(classNamePH, className); strategy.HandleFileContent(headerPath); @@ -1037,13 +1042,13 @@ public async Task DeleteSourcesAsync(List sou List files = new List(); List parentPaths = new List(); - foreach(string sourcePath in sourcePaths) + foreach (string sourcePath in sourcePaths) { if (ExistsSourceFile(sourcePath)) { parentPaths.Add(XFilesystem.PathParent(sourcePath)); files.Add(sourcePath); - } + } else if (ExistsSourceDirectory(sourcePath)) { parentPaths.Add(XFilesystem.PathParent(sourcePath)); @@ -1148,7 +1153,7 @@ public async Task AddGameModuleAsync(string templeNa if (metaTarget == "Game") { targetName = FUnrealTargets.GAME; - } + } else { targetName = metaTarget; //Should check if its valid @@ -1171,9 +1176,9 @@ public async Task AddGameModuleAsync(string templeNa FUnrealUProjectFile uprojectJson = new FUnrealUProjectFile(_uprjFileAbsPath); uprojectJson.Modules.Add(new FUnrealUProjectModuleJson() { - Name = moduleName, - Type = metaType, - LoadingPhase = metaPhase + Name = moduleName, + Type = metaType, + LoadingPhase = metaPhase }); uprojectJson.Save(); @@ -1225,7 +1230,7 @@ public async Task RenameGameModuleAsync(string modul //3. Update MODULENAME_API macro in all .h files under Public/** taskSuccess = FUnrealServiceTasks.Module_UpdateApiMacro(module, newModuleName, notifier); if (!taskSuccess) return false; - + //4. Rename Module Folder taskSuccess = await FUnrealServiceTasks.Module_RenameFolderAsync(module, newModuleName, notifier); if (!taskSuccess) return false; @@ -1272,13 +1277,13 @@ public async Task DeleteGameModuleAsync(string modul if (!taskSuccess) return false; //2. Delete module path - { + { notifier.Info(XDialogLib.Ctx_DeletingModule, XDialogLib.Info_DeletingModuleFolder, module.FullPath); XFilesystem.DirDelete(module.FullPath); } //3. Update .uproject removing the module - { + { notifier.Info(XDialogLib.Ctx_UpdatingProject, XDialogLib.Info_UpdatingProjectDescriptorFile, project.DescriptorFilePath); var descrFile = new FUnrealUProjectFile(project.DescriptorFilePath); var moduleJson = descrFile.Modules[moduleName]; @@ -1365,7 +1370,7 @@ public async Task RenameFileAsync(string filePath, str // - il vecchio filename finisce per .h var project = GetUProject(); - + bool isHeaderFileScenario = XFilesystem.HasExtension(filePath, ".h") && XFilesystem.HasExtension(newFileNameWithExt, ".h"); if (isHeaderFileScenario) { @@ -1436,7 +1441,7 @@ public async Task RenameFolderAsync(string folderPath, var project = GetUProject(); var module = project.AllModules.FindByBelongingPath(folderPath); - if (module == null) + if (module == null) { notifier.Erro(XDialogLib.Ctx_CheckProjectPlayout, XDialogLib.Error_ModuleNotFound, $"for path {folderPath}"); return false; @@ -1452,7 +1457,7 @@ public async Task RenameFolderAsync(string folderPath, bool needIncludeUpdate = false; bool hasPublicHeaderInvolved = false; //handle only Public or Private specific case. In case of Custom folder is threated always as private.... (eventually could check in .Build.cs) - if (folderPath != module.PublicPath && folderPath != module.PrivatePath) + if (folderPath != module.PublicPath && folderPath != module.PrivatePath) { needIncludeUpdate = true; hasPublicHeaderInvolved = XFilesystem.FileExists(folderPath, true, "*.h", filePath => @@ -1497,5 +1502,5 @@ public void ComputeSourceCodePaths(string absPathSelected, string className, FUn var module = ModuleFromSourceCodePath(absPathSelected); FUnrealServiceTasks.Module_ComputeSourceCodePaths(module, absPathSelected, className, sourceType, out headerPath, out sourcePath, out sourceRelPath); } - } + } } diff --git a/FUnreal/Templates/UEC/Sources/Classes/SceneComponent.cpp b/FUnreal/Templates/UEC/Sources/Classes/SceneComponent.cpp index 0ef3bfa..f5d55db 100644 --- a/FUnreal/Templates/UEC/Sources/Classes/SceneComponent.cpp +++ b/FUnreal/Templates/UEC/Sources/Classes/SceneComponent.cpp @@ -12,7 +12,7 @@ U@{TPL_SOUR_CLASS}::U@{TPL_SOUR_CLASS}() // Called when the game starts -void U@{TPL_SOUR_CLASS}()::BeginPlay() +void U@{TPL_SOUR_CLASS}::BeginPlay() { Super::BeginPlay(); @@ -22,7 +22,7 @@ void U@{TPL_SOUR_CLASS}()::BeginPlay() // Called every frame -void U@{TPL_SOUR_CLASS}()::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) +void U@{TPL_SOUR_CLASS}::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); diff --git a/FUnreal/source.extension.vsixmanifest b/FUnreal/source.extension.vsixmanifest index db182fa..b829b9e 100644 --- a/FUnreal/source.extension.vsixmanifest +++ b/FUnreal/source.extension.vsixmanifest @@ -1,7 +1,7 @@ - + FUnreal Visual Studio extension for smoothing the workflow of Unreal Engine C++ developers https://github.com/fdefelici/vs-funreal diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 4e50d08..8fd339a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,4 +1,10 @@ # FUnreal Changelog +## v0.1.0: October 25, 2023 +### Improvements +* Update UE version dection strategy to support UE v5.3 +### Bug Fix +* Fix USceneComponent class template + ## v0.0.9: July 15, 2023 ### Bug Fix * Template misconfiguration prevented to add some module (blank, blueprint library) to a plugin