From 0be058d91cf8678e4a063d15a30dad51e9ceef37 Mon Sep 17 00:00:00 2001 From: Malware Date: Sun, 7 Jul 2019 09:12:57 +0200 Subject: [PATCH] Removed debug code that for some reason crashed runtime. _Shouldn't_ crash even if it was there though. Did I somehow manage to release a debug version?Added MDK dev console and relevant output Changed how solution load status is detected (attempt to fix #141 and #156 --- .../ProjectTemplate.csproj | 1 + .../Build/Solution/ProgramDocumentComposer.cs | 5 - .../MDK/Commands/ProjectDependentCommand.cs | 11 +- Source/MDK/MDKPackage.GeneratedInfo.cs | 4 +- Source/MDK/MDKPackage.cs | 59 +++++++-- Source/MDK/VisualStudio/SolutionManager.cs | 112 +++++------------- Source/MDK/other.xml | 2 +- Source/MDK/source.extension.vsixmanifest | 2 +- Source/MDKServices/HealthAnalysis.cs | 36 +++++- Source/MDKServices/HealthAnalysisOptions.cs | 5 + .../MDKProjectProperties.GeneratedInfo.cs | 2 +- .../MDKProjectProperties.cs | 8 +- 12 files changed, 145 insertions(+), 102 deletions(-) diff --git a/Source/IngameScriptTemplate/ProjectTemplate.csproj b/Source/IngameScriptTemplate/ProjectTemplate.csproj index d008ae4..f87d285 100644 --- a/Source/IngameScriptTemplate/ProjectTemplate.csproj +++ b/Source/IngameScriptTemplate/ProjectTemplate.csproj @@ -25,6 +25,7 @@ 6 + diff --git a/Source/MDK/Build/Solution/ProgramDocumentComposer.cs b/Source/MDK/Build/Solution/ProgramDocumentComposer.cs index 238d29a..6512ff2 100644 --- a/Source/MDK/Build/Solution/ProgramDocumentComposer.cs +++ b/Source/MDK/Build/Solution/ProgramDocumentComposer.cs @@ -78,11 +78,6 @@ bool IsDebugDocument(string filePath, MDKProjectProperties config) if (fileName.IndexOf(".debug.", StringComparison.CurrentCultureIgnoreCase) >= 0) return true; - if (filePath.Contains("Bootstrapper.cs")) - { - Debugger.Break(); - } - return config.IsIgnoredFilePath(filePath); } diff --git a/Source/MDK/Commands/ProjectDependentCommand.cs b/Source/MDK/Commands/ProjectDependentCommand.cs index 9ddf3e3..617f9e1 100644 --- a/Source/MDK/Commands/ProjectDependentCommand.cs +++ b/Source/MDK/Commands/ProjectDependentCommand.cs @@ -20,6 +20,8 @@ protected override void OnBeforeQueryStatus() protected bool TryGetValidProject(out Project project, out MDKProjectProperties projectProperties) { + var package = (MDKPackage)Package; + package.Echo("MDK Command", Id, "is attempting to retrieve an MDK project"); var dte2 = (EnvDTE80.DTE2)Package.DTE; project = ((IEnumerable)dte2.ToolWindows.SolutionExplorer.SelectedItems) .OfType() @@ -28,11 +30,18 @@ protected bool TryGetValidProject(out Project project, out MDKProjectProperties .FirstOrDefault(); if (project == null) { + package.Echo("MDK Command", Id, "failed because no project could be found"); projectProperties = null; return false; } projectProperties = MDKProjectProperties.Load(project.FullName, project.Name); - return projectProperties.IsValid; + if (projectProperties.IsValid) + { + package.Echo("MDK Command", Id, "retrieved a valid project"); + return true; + } + package.Echo("MDK Command", Id, "did not retrieve a valid project"); + return false; } protected bool TryGetValidProject(out MDKProjectProperties projectProperties) diff --git a/Source/MDK/MDKPackage.GeneratedInfo.cs b/Source/MDK/MDKPackage.GeneratedInfo.cs index 0fa0b21..b9b9b19 100644 --- a/Source/MDK/MDKPackage.GeneratedInfo.cs +++ b/Source/MDK/MDKPackage.GeneratedInfo.cs @@ -10,7 +10,7 @@ public partial class MDKPackage /// /// The current package version /// - public static readonly Version Version = new Version("1.2.15"); + public static readonly Version Version = new Version("1.2.21"); /// /// The required IDE version @@ -20,7 +20,7 @@ public partial class MDKPackage /// /// Determines whether this version is a prerelease version /// - public const bool IsPrerelease = false; + public const bool IsPrerelease = true; /// /// Gets the help page navigation URL diff --git a/Source/MDK/MDKPackage.cs b/Source/MDK/MDKPackage.cs index 1fe478e..ee98f2d 100644 --- a/Source/MDK/MDKPackage.cs +++ b/Source/MDK/MDKPackage.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using EnvDTE; +using EnvDTE80; using Malware.MDKServices; using MDK.Build; using MDK.Commands; @@ -109,11 +110,8 @@ protected override void Dispose(bool disposing) protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) { await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + Echo("Initializing MDK"); _solutionManager = new SolutionManager(this); - _solutionManager.BeginRecording(); - _solutionManager.ProjectLoaded += OnProjectLoaded; - _solutionManager.SolutionLoaded += OnSolutionLoaded; - _solutionManager.SolutionClosed += OnSolutionClosed; // Make sure the dialog page is loaded, since the options are needed in other threads and not preloading it // here will cause a threading exception. @@ -141,9 +139,17 @@ void OnUpdateDetected(Version detectedVersion) UpdateDetectedDialog.ShowDialog(new UpdateDetectedDialogModel(detectedVersion)); } - void OnShellActivated() + async void OnShellActivated() { - _solutionManager.EndRecording(); + await JoinableTaskFactory.SwitchToMainThreadAsync(); + if (_solutionManager.Status == SolutionStatus.Loaded) + { + Echo("A solution is already loaded on start"); + OnSolutionLoaded(DTE.Solution); + } + _solutionManager.ProjectLoaded += OnProjectLoaded; + _solutionManager.SolutionLoaded += OnSolutionLoaded; + _solutionManager.SolutionClosed += OnSolutionClosed; } void OnSolutionClosed(object sender, EventArgs e) @@ -153,6 +159,7 @@ void OnSolutionClosed(object sender, EventArgs e) void OnSolutionLoaded(object sender, EventArgs e) { + Echo("Detected the loading of a solution"); OnSolutionLoaded(DTE.Solution); } @@ -164,6 +171,7 @@ void OnProjectLoaded(object sender, ProjectLoadedEventArgs e) async void OnProjectLoaded(Project project) { + Echo("Detected the loading of a project"); HealthAnalysis analysis; try { @@ -273,7 +281,8 @@ HealthAnalysisOptions GetAnalysisOptions() => GameAssemblyNames = GameAssemblyNames, GameFiles = GameFiles, UtilityAssemblyNames = UtilityAssemblyNames, - UtilityFiles = UtilityFiles + UtilityFiles = UtilityFiles, + Echo = v => Echo(v) }; void PresentAnalysisResults(params HealthAnalysis[] analysis) @@ -411,5 +420,41 @@ public void ShowError(string title, string description, Exception exception) }; ErrorDialog.ShowDialog(errorDialogModel); } + + static readonly Guid OutputGuid = new Guid("9D599C61-0B50-46FD-9230-7709866F32B7"); + + /// + /// Echoes output information to the MDK dev console. + /// + /// + public void Echo(params object[] values) + { + string message; + if (values == null || values.Length == 0) + message = ""; + else + message = string.Join(" ", values.Select(TranslateForEcho)); + + EchoAsync(message); + } + + async void EchoAsync(string message) + { + var output = (IVsOutputWindow)await GetServiceAsync(typeof(SVsOutputWindow)); + var paneGuid = OutputGuid; + output.GetPane(ref paneGuid, out var pane); + if (pane == null) + { + output.CreatePane(ref paneGuid, "MDK Dev Console", 1, 1); + output.GetPane(ref paneGuid, out pane); + } + + pane.OutputString($"{message}\n"); + } + + string TranslateForEcho(object value) + { + return (value == null) ? "(null)" : value.ToString(); + } } } diff --git a/Source/MDK/VisualStudio/SolutionManager.cs b/Source/MDK/VisualStudio/SolutionManager.cs index 0adc9b4..c4d6b75 100644 --- a/Source/MDK/VisualStudio/SolutionManager.cs +++ b/Source/MDK/VisualStudio/SolutionManager.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Concurrent; -using System.Collections.Generic; using EnvDTE; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Shell; @@ -56,7 +54,22 @@ public SolutionManager(IServiceProvider serviceProvider) /// /// Gets the current solution status /// - public SolutionStatus Status { get; private set; } + public SolutionStatus Status + { + get + { + _solutionCtl.GetProperty((int)__VSPROPID4.VSPROPID_IsSolutionFullyLoaded, out var isFullyLoadedV); + _solutionCtl.GetProperty((int)__VSPROPID.VSPROPID_IsSolutionOpening, out var isOpeningV); + _solutionCtl.GetProperty((int)__VSPROPID2.VSPROPID_IsSolutionClosing, out var isClosingV); + if ((bool)isOpeningV) + return SolutionStatus.Loading; + if ((bool)isClosingV) + return SolutionStatus.Closing; + if ((bool)isFullyLoadedV) + return SolutionStatus.Loaded; + return SolutionStatus.Closed; + } + } /// ~SolutionManager() @@ -84,9 +97,6 @@ protected virtual void Dispose(bool disposing) _solutionCtl.UnadviseSolutionEvents(_solutionEventsCookie); } - ConcurrentQueue _recordedEvents = new ConcurrentQueue(); - bool _recordEvents = false; - int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) { // I cannot rely on the fAdded argument, because it's behavior changes between normal and lightweight solution load, @@ -99,90 +109,70 @@ int IVsSolutionEvents.OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) if (project == null) return VSConstants.S_OK; - void raiseEvent() => OnProjectLoaded((Project)objProj, Status != SolutionStatus.Loading); - - if (_recordEvents) - _recordedEvents.Enqueue(raiseEvent); - else - raiseEvent(); + OnProjectLoaded((Project)objProj, Status != SolutionStatus.Loading); return VSConstants.S_OK; } int IVsSolutionEvents.OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionEvents.OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionEvents.OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionEvents.OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionEvents.OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionEvents.OnAfterOpenSolution(object pUnkReserved, int fNewSolution) { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionEvents.OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionEvents.OnBeforeCloseSolution(object pUnkReserved) { - void raiseEvent() => OnSolutionClosing(); - - if (_recordEvents) - _recordedEvents.Enqueue(raiseEvent); - else - raiseEvent(); + OnSolutionClosing(); return VSConstants.S_OK; } int IVsSolutionEvents.OnAfterCloseSolution(object pUnkReserved) { - void raiseEvent() => OnSolutionClosed(); - - if (_recordEvents) - _recordedEvents.Enqueue(raiseEvent); - else - raiseEvent(); + OnSolutionClosed(); return VSConstants.S_OK; } int IVsSolutionLoadEvents.OnBeforeOpenSolution(string pszSolutionFilename) { - void raiseEvent() => OnSolutionLoading(); - - if (_recordEvents) - _recordedEvents.Enqueue(raiseEvent); - else - raiseEvent(); + OnSolutionLoading(); return VSConstants.S_OK; } int IVsSolutionLoadEvents.OnBeforeBackgroundSolutionLoadBegins() { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionLoadEvents.OnQueryBackgroundLoadProjectBatch(out bool pfShouldDelayLoadToNextIdle) @@ -193,22 +183,17 @@ int IVsSolutionLoadEvents.OnQueryBackgroundLoadProjectBatch(out bool pfShouldDel int IVsSolutionLoadEvents.OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch) { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionLoadEvents.OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch) { - return VSConstants.S_OK; + return VSConstants.E_NOTIMPL; } int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete() { - void raiseEvent() => OnSolutionLoaded(); - - if (_recordEvents) - _recordedEvents.Enqueue(raiseEvent); - else - raiseEvent(); + OnSolutionLoaded(); return VSConstants.S_OK; } @@ -218,7 +203,6 @@ int IVsSolutionLoadEvents.OnAfterBackgroundSolutionLoadComplete() /// protected virtual void OnSolutionLoading() { - Status = SolutionStatus.Loading; SolutionLoading?.Invoke(this, EventArgs.Empty); } @@ -227,7 +211,6 @@ protected virtual void OnSolutionLoading() /// protected virtual void OnSolutionLoaded() { - Status = SolutionStatus.Loaded; SolutionLoaded?.Invoke(this, EventArgs.Empty); } @@ -236,7 +219,6 @@ protected virtual void OnSolutionLoaded() /// protected virtual void OnSolutionClosing() { - Status = SolutionStatus.Closing; SolutionClosing?.Invoke(this, EventArgs.Empty); } @@ -245,7 +227,6 @@ protected virtual void OnSolutionClosing() /// protected virtual void OnSolutionClosed() { - Status = SolutionStatus.Closed; SolutionClosed?.Invoke(this, EventArgs.Empty); } @@ -258,36 +239,5 @@ protected virtual void OnProjectLoaded(Project project, bool isStandalone) { ProjectLoaded?.Invoke(this, new ProjectLoadedEventArgs(project, isStandalone)); } - - /// - /// Begins suppressing and recording all incoming events for later playback. - /// - public void BeginRecording() - { - _recordEvents = true; - } - - /// - /// Ends suppression and recording of all incoming events. - /// - /// If true, playback all events immediately. Otherwise the queue remains filled for later manual playback. - public void EndRecording(bool playback = true) - { - _recordEvents = false; - if (playback) - Playback(); - } - - /// - /// Plays back all recorded events. - /// - public void Playback() - { - while (!_recordedEvents.IsEmpty) - { - if (_recordedEvents.TryDequeue(out var action)) - action(); - } - } } } diff --git a/Source/MDK/other.xml b/Source/MDK/other.xml index 13de23b..2392568 100644 --- a/Source/MDK/other.xml +++ b/Source/MDK/other.xml @@ -1,6 +1,6 @@  - False + True https://github.com/malware-dev/MDK-SE/wiki https://github.com/malware-dev/MDK-SE/releases https://github.com/malware-dev/MDK-SE/issues diff --git a/Source/MDK/source.extension.vsixmanifest b/Source/MDK/source.extension.vsixmanifest index 53c37fe..5b234b3 100644 --- a/Source/MDK/source.extension.vsixmanifest +++ b/Source/MDK/source.extension.vsixmanifest @@ -1,7 +1,7 @@  - + MDK/SE A toolkit to help with ingame script (programmable block) development for Keen Software House's space sandbox Space Engineers. diff --git a/Source/MDKServices/HealthAnalysis.cs b/Source/MDKServices/HealthAnalysis.cs index 5675835..896db90 100644 --- a/Source/MDKServices/HealthAnalysis.cs +++ b/Source/MDKServices/HealthAnalysis.cs @@ -33,35 +33,58 @@ public class HealthAnalysis static HealthAnalysis Analyze(Project project, HealthAnalysisOptions options) { + options.Echo?.Invoke("Analyzing project..."); if (!project.IsLoaded()) + { + options.Echo?.Invoke("Project is not loaded."); return new HealthAnalysis(project, null, options); - var projectInfo = MDKProjectProperties.Load(project.FullName, project.Name); + } + + var projectInfo = MDKProjectProperties.Load(project.FullName, project.Name, options.Echo); if (!projectInfo.IsValid && !(projectInfo.Options?.IsValid ?? false)) + { + options.Echo?.Invoke($"{project.Name} contains invalid data."); return new HealthAnalysis(project, null, options); + } var analysis = new HealthAnalysis(project, projectInfo, options); if (projectInfo.Options.Version < new Version(1, 2)) - analysis._problems.Add(new HealthProblem(HealthCode.Outdated, HealthSeverity.Critical, "This project format is outdated.")); + { + options.Echo?.Invoke($"{project.Name}: This project format is outdated."); + analysis._problems.Add(new HealthProblem(HealthCode.Outdated, HealthSeverity.Critical, "This project format is outdated")); + } var whitelistFileName = Path.Combine(Path.GetDirectoryName(project.FullName), "mdk\\whitelist.cache"); if (!projectInfo.Paths.IsValid) + { + options.Echo?.Invoke($"{project.Name}: Missing paths file."); analysis._problems.Add(new HealthProblem(HealthCode.MissingPathsFile, HealthSeverity.Critical, "Missing paths file")); + } else { var installPath = projectInfo.Paths.InstallPath.TrimEnd('/', '\\'); var expectedInstallPath = options.InstallPath.TrimEnd('/', '\\'); if (!string.Equals(installPath, expectedInstallPath, StringComparison.CurrentCultureIgnoreCase)) + { + options.Echo?.Invoke($"{project.Name}: Invalid install path."); analysis._problems.Add(new HealthProblem(HealthCode.BadInstallPath, HealthSeverity.Warning, "Invalid install path")); + } var vrageRef = Path.Combine(projectInfo.Paths.GameBinPath, "vrage.dll"); if (!File.Exists(vrageRef)) + { + options.Echo?.Invoke($"{project.Name}: Invalid game path."); analysis._problems.Add(new HealthProblem(HealthCode.BadGamePath, HealthSeverity.Critical, "Invalid game path")); + } var outputPath = projectInfo.Paths.OutputPath.TrimEnd('/', '\\'); if (!Directory.Exists(outputPath)) + { + options.Echo?.Invoke($"{project.Name}: Invalid output path."); analysis._problems.Add(new HealthProblem(HealthCode.BadOutputPath, HealthSeverity.Warning, "Invalid output path")); + } var whitelistCacheFileName = Path.Combine(expectedInstallPath, "Analyzers\\whitelist.cache"); if (File.Exists(whitelistCacheFileName)) @@ -69,12 +92,18 @@ static HealthAnalysis Analyze(Project project, HealthAnalysisOptions options) var cacheDate = File.GetLastWriteTime(whitelistCacheFileName); var currentDate = File.GetLastWriteTime(whitelistFileName); if (cacheDate > currentDate) + { + options.Echo?.Invoke($"{project.Name}: The whitelist cache must be updated."); analysis._problems.Add(new HealthProblem(HealthCode.OutdatedWhitelist, HealthSeverity.Warning, "The whitelist cache must be updated")); + } } } if (!File.Exists(whitelistFileName)) + { + options.Echo?.Invoke($"{project.Name}: Missing Whitelist Cache."); analysis._problems.Add(new HealthProblem(HealthCode.MissingWhitelist, HealthSeverity.Critical, "Missing Whitelist Cache")); + } return analysis; } @@ -97,7 +126,10 @@ static HealthAnalysis Analyze(Project project, HealthAnalysisOptions options) IsMDKProject = properties != null; Problems = new ReadOnlyCollection(_problems); if (!IsMDKProject) + { + analysisOptions.Echo?.Invoke($"{project.Name}: This is not an MDK project."); _problems.Add(new HealthProblem(HealthCode.NotAnMDKProject, HealthSeverity.Critical, "This is not an MDK project.")); + } } /// diff --git a/Source/MDKServices/HealthAnalysisOptions.cs b/Source/MDKServices/HealthAnalysisOptions.cs index d195b3b..72bf1f1 100644 --- a/Source/MDKServices/HealthAnalysisOptions.cs +++ b/Source/MDKServices/HealthAnalysisOptions.cs @@ -47,5 +47,10 @@ public struct HealthAnalysisOptions /// A list of the utility files included by script projects /// public ImmutableArray UtilityFiles; + + /// + /// An optional method to echo progress + /// + public Action Echo; } } diff --git a/Source/Mixin.MDKProjectProperties/MDKProjectProperties.GeneratedInfo.cs b/Source/Mixin.MDKProjectProperties/MDKProjectProperties.GeneratedInfo.cs index 3f377a5..fa9f25b 100644 --- a/Source/Mixin.MDKProjectProperties/MDKProjectProperties.GeneratedInfo.cs +++ b/Source/Mixin.MDKProjectProperties/MDKProjectProperties.GeneratedInfo.cs @@ -7,6 +7,6 @@ partial class MDKProjectProperties /// /// The current package version this utility assembly targets /// - public static readonly Version TargetPackageVersion = new Version("1.2.15"); + public static readonly Version TargetPackageVersion = new Version("1.2.21"); } } diff --git a/Source/Mixin.MDKProjectProperties/MDKProjectProperties.cs b/Source/Mixin.MDKProjectProperties/MDKProjectProperties.cs index 6492d15..55cb092 100644 --- a/Source/Mixin.MDKProjectProperties/MDKProjectProperties.cs +++ b/Source/Mixin.MDKProjectProperties/MDKProjectProperties.cs @@ -21,13 +21,16 @@ public partial class MDKProjectProperties : INotifyPropertyChanged /// The file name of this project /// The display name of this project /// - public static MDKProjectProperties Load([NotNull] string projectFileName, string projectName = null) + public static MDKProjectProperties Load([NotNull] string projectFileName, string projectName = null, Action echo = null) { if (string.IsNullOrEmpty(projectFileName)) throw new ArgumentException("Value cannot be null or empty.", nameof(projectFileName)); if (!File.Exists(projectFileName) || Regex.IsMatch(projectFileName, @"\w+://")) + { + echo?.Invoke($"{projectFileName} does not exist, or it's a network file (not supported)"); return new MDKProjectProperties(projectFileName, null, null, null); + } var fileName = Path.GetFullPath(projectFileName); var name = projectName ?? Path.GetFileNameWithoutExtension(projectFileName); @@ -42,16 +45,19 @@ public static MDKProjectProperties Load([NotNull] string projectFileName, string options = MDKProjectOptions.Load(mdkOptionsFileName); paths = MDKProjectPaths.Load(mdkPathsFileName); + echo?.Invoke($"{projectName ?? projectFileName} is a valid MDK project."); return new MDKProjectProperties(projectFileName, name, options, paths); } if (File.Exists(legacyOptionsFileName)) { + echo?.Invoke($"{projectName ?? projectFileName} is a legacy MDK project."); ImportLegacy_1_1(projectFileName, ref options, mdkOptionsFileName, ref paths, mdkPathsFileName); if (options != null && paths != null) return new MDKProjectProperties(projectFileName, name, options, paths); } + echo?.Invoke($"{projectName ?? projectFileName} not an MDK project."); return new MDKProjectProperties(projectFileName, null, null, null); }