diff options
author | therzok <marius.ungureanu@xamarin.com> | 2019-02-08 14:24:18 +0300 |
---|---|---|
committer | therzok <marius.ungureanu@xamarin.com> | 2019-02-08 14:24:18 +0300 |
commit | 289a50dc758d11b26c3a4bad78dbd1d458d46f30 (patch) | |
tree | 733c70569ba25cd66313bde1a0374435dde61cd4 /main | |
parent | e61c3b35028cca154aae88a59d42a624cc169926 (diff) | |
parent | e2d4e3b3e7dfafd2ff22e19d6b75ebeb2eb4e1fd (diff) |
Merge remote-tracking branch 'origin/master' into release-8.0
Diffstat (limited to 'main')
90 files changed, 3273 insertions, 877 deletions
diff --git a/main/Main.sln b/main/Main.sln index 469368ab29..62e6985a55 100644 --- a/main/Main.sln +++ b/main/Main.sln @@ -474,6 +474,7 @@ EndProject Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "RoslynIntegration", "external\vs-editor-core\src\RoslynIntegration\RoslynIntegration.shproj", "{A10C6E18-A872-4D43-9BB2-2B28C70C7BF7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NavigationProvidersImpl", "external\vs-editor-core\src\Editor\Text\Impl\NavigationProviders\NavigationProvidersImpl.csproj", "{3E8ABAB5-CE9A-484E-B82B-73B89EA2E51A}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtensionTools", "src\tools\ExtensionTools\ExtensionTools.csproj", "{E33F7901-AA45-4C25-9868-DBB6CC8DC40C}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
@@ -2740,6 +2741,30 @@ Global {3E8ABAB5-CE9A-484E-B82B-73B89EA2E51A}.ReleaseMac|Any CPU.Build.0 = Release|Any CPU
{3E8ABAB5-CE9A-484E-B82B-73B89EA2E51A}.ReleaseWin32|Any CPU.ActiveCfg = Release|Any CPU
{3E8ABAB5-CE9A-484E-B82B-73B89EA2E51A}.ReleaseWin32|Any CPU.Build.0 = Release|Any CPU
+ {44FFFDDA-156F-49F9-AE6A-BA640C198B33}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {44FFFDDA-156F-49F9-AE6A-BA640C198B33}.Debug|x86.Build.0 = Debug|Any CPU
+ {44FFFDDA-156F-49F9-AE6A-BA640C198B33}.Release|x86.ActiveCfg = Release|Any CPU
+ {44FFFDDA-156F-49F9-AE6A-BA640C198B33}.Release|x86.Build.0 = Release|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.DebugGnome|Any CPU.ActiveCfg = Debug|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.DebugGnome|Any CPU.Build.0 = Debug|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.DebugMac|Any CPU.ActiveCfg = DebugMac|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.DebugMac|Any CPU.Build.0 = DebugMac|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.DebugWin32|Any CPU.ActiveCfg = Debug|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.DebugWin32|Any CPU.Build.0 = Debug|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.ReleaseGnome|Any CPU.ActiveCfg = Release|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.ReleaseGnome|Any CPU.Build.0 = Release|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.ReleaseMac|Any CPU.ActiveCfg = ReleaseMac|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.ReleaseMac|Any CPU.Build.0 = ReleaseMac|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.ReleaseWin32|Any CPU.ActiveCfg = Release|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.ReleaseWin32|Any CPU.Build.0 = Release|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.Debug|x86.Build.0 = Debug|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.Release|x86.ActiveCfg = Release|Any CPU
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2960,6 +2985,7 @@ Global {6C7ADE4D-2544-581E-918B-659243920781} = {32539734-6484-4451-9EF3-61610CA25BBF}
{A10C6E18-A872-4D43-9BB2-2B28C70C7BF7} = {5A92B792-BF17-4748-B29E-34C99B2E8722}
{3E8ABAB5-CE9A-484E-B82B-73B89EA2E51A} = {32539734-6484-4451-9EF3-61610CA25BBF}
+ {E33F7901-AA45-4C25-9868-DBB6CC8DC40C} = {5D3F7E65-E55B-45CA-A83B-D1E10040281E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {856CF524-E2A7-4DB0-B80F-8F1B080A2E25}
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.addin.xml b/main/src/addins/CSharpBinding/CSharpBinding.addin.xml index 2d78c132f9..93f01bbc85 100644 --- a/main/src/addins/CSharpBinding/CSharpBinding.addin.xml +++ b/main/src/addins/CSharpBinding/CSharpBinding.addin.xml @@ -186,6 +186,10 @@ <Parser class="MonoDevelop.CSharp.Parser.TypeSystemParser" mimeType = "text/x-csharp" /> </Extension> + <Extension path = "/MonoDevelop/TypeSystem/FoldingParser"> + <Parser class = "MonoDevelop.CSharp.Parser.CSharpFoldingParser" mimeType="text/x-csharp" /> + </Extension> + <Extension path = "/MonoDevelop/TypeSystem/CodeGenerators"> <Generator class="MonoDevelop.CSharp.Refactoring.CSharpCodeGenerator" mimeType = "text/x-csharp" /> </Extension> diff --git a/main/src/addins/CSharpBinding/CSharpBinding.csproj b/main/src/addins/CSharpBinding/CSharpBinding.csproj index 56890dbec4..83b8ee7e48 100644 --- a/main/src/addins/CSharpBinding/CSharpBinding.csproj +++ b/main/src/addins/CSharpBinding/CSharpBinding.csproj @@ -141,6 +141,7 @@ <Compile Include="MonoDevelop.CSharp.Completion\CSharpCompletionTextEditorExtension.cs" /> <Compile Include="MonoDevelop.CSharp.Refactoring\CSharpCodeGenerator.cs" /> <Compile Include="MonoDevelop.CSharp.Refactoring\HelperMethods.cs" /> + <Compile Include="MonoDevelop.CSharp.Parser\CSharpFoldingParser.cs" /> <Compile Include="MonoDevelop.CSharp.CodeGeneration\AbstractGenerateAction.cs" /> <Compile Include="MonoDevelop.CSharp.CodeGeneration\CodeGenerationCommands.cs" /> <Compile Include="MonoDevelop.CSharp.CodeGeneration\CodeGenerationOptions.cs" /> diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormattingPolicy.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormattingPolicy.cs index 1a752ea206..a0277eb99d 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormattingPolicy.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpFormattingPolicy.cs @@ -584,22 +584,6 @@ namespace MonoDevelop.CSharp.Formatting #endregion - #region Code Style options - bool placeSystemDirectiveFirst = true; - [Obsolete("Not used anymore.")] - [ItemProperty] - public bool PlaceSystemDirectiveFirst { - get { - return placeSystemDirectiveFirst; - } - - set { - placeSystemDirectiveFirst = value; - } - } - - #endregion - public CSharpFormattingPolicy () { this.options = TypeSystemService.Workspace?.Options; diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs new file mode 100644 index 0000000000..cf6ba5fb22 --- /dev/null +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Parser/CSharpFoldingParser.cs @@ -0,0 +1,393 @@ +// +// CSharpFoldingParser.cs +// +// Author: +// Mike Krüger <mkrueger@xamarin.com> +// +// Copyright (c) 2012 Xamarin Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using MonoDevelop.Ide.TypeSystem; +using MonoDevelop.Ide.Editor; +using MonoDevelop.Core; + +namespace MonoDevelop.CSharp.Parser +{ + unsafe class CSharpFoldingParser : IFoldingParser + { + #region IFoldingParser implementation + + static unsafe bool StartsIdentifier (char* ptr, char* endPtr, string identifier) + { + fixed (char* startId = identifier) { + char* idPtr = startId; + char* endId = startId + identifier.Length; + while (idPtr < endId) { + if (ptr >= endPtr) + return false; + if (*idPtr != *ptr) + return false; + idPtr++; + ptr++; + } + return true; + } + } + + static unsafe void SkipWhitespaces (ref char* ptr, char* endPtr, ref int column) + { + while (ptr < endPtr) { + char ch = *ptr; + if (ch != ' ' && ch != '\t') + return; + column++; + ptr++; + } + } + + static unsafe string ReadToEol (string content, ref char* ptr, char* endPtr, ref int line, ref int column) + { + char* lineBeginPtr = ptr; + char* lineEndPtr = lineBeginPtr; + + while (ptr < endPtr) { + switch (*ptr) { + case '\n': + if (lineEndPtr == lineBeginPtr) + lineEndPtr = ptr; + line++; + column = 1; + ptr++; + fixed (char* startPtr = content) { + return content.Substring ((int)(lineBeginPtr - startPtr), (int)(lineEndPtr - lineBeginPtr)); + } + case '\r': + lineEndPtr = ptr; + if (ptr + 1 < endPtr && *(ptr + 1) == '\n') + ptr++; + goto case '\n'; + } + column++; + ptr++; + } + return ""; + } + + public unsafe ParsedDocument Parse (string fileName, string content) + { + var regionStack = new Stack<Tuple<string, DocumentLocation>> (); + var result = new DefaultParsedDocument (fileName); + bool inSingleComment = false, inMultiLineComment = false; + bool inString = false, inVerbatimString = false; + bool inChar = false; + bool inLineStart = true, hasStartedAtLine = false; + int line = 1, column = 1; + int bracketDepth = 0; + var startLoc = DocumentLocation.Empty; + + fixed (char* startPtr = content) { + char* endPtr = startPtr + content.Length; + char* ptr = startPtr; + char* beginPtr = ptr; + while (ptr < endPtr) { + switch (*ptr) { + case '{': + if (inString || inChar || inVerbatimString || inMultiLineComment || inSingleComment) + break; + bracketDepth++; + break; + case '}': + if (inString || inChar || inVerbatimString || inMultiLineComment || inSingleComment) + break; + bracketDepth--; + break; + case '#': + if (!inLineStart) + break; + inLineStart = false; + ptr++; + + if (StartsIdentifier (ptr, endPtr, "region")) { + var regionLocation = new DocumentLocation (line, column); + column++; + ptr += "region".Length; + column += "region".Length; + SkipWhitespaces (ref ptr, endPtr, ref column); + regionStack.Push (Tuple.Create (ReadToEol (content, ref ptr, endPtr, ref line, ref column), regionLocation)); + continue; + } else if (StartsIdentifier (ptr, endPtr, "endregion")) { + column++; + ptr += "endregion".Length; + column += "endregion".Length; + if (regionStack.Count > 0) { + var beginRegion = regionStack.Pop (); + result.Add (new FoldingRegion ( + beginRegion.Item1, + new DocumentRegion (beginRegion.Item2.Line, beginRegion.Item2.Column, line, column), + FoldType.UserRegion, + true)); + } + continue; + } else { + column++; + } + break; + case '/': + if (inString || inChar || inVerbatimString || inMultiLineComment || inSingleComment) { + inLineStart = false; + break; + } + if (ptr + 1 < endPtr) { + char nextCh = *(ptr + 1); + if (nextCh == '/') { + hasStartedAtLine = inLineStart; + beginPtr = ptr + 2; + startLoc = new DocumentLocation (line, column); + ptr++; + column++; + inSingleComment = true; + } else if (nextCh == '*') { + hasStartedAtLine = inLineStart; + beginPtr = ptr + 2; + startLoc = new DocumentLocation (line, column); + ptr++; + column++; + inMultiLineComment = true; + } + } + inLineStart = false; + break; + case '*': + inLineStart = false; + if (inString || inChar || inVerbatimString || inSingleComment) + break; + if (inMultiLineComment && ptr + 1 < endPtr) { + if (ptr + 1 < endPtr && *(ptr + 1) == '/') { + ptr += 2; + column += 2; + inMultiLineComment = false; + if (bracketDepth <= 1) { + result.Add (new MonoDevelop.Ide.TypeSystem.Comment () { + Region = new DocumentRegion (startLoc, new DocumentLocation (line, column)), + OpenTag = "/*", + CommentType = MonoDevelop.Ide.TypeSystem.CommentType.Block, + Text = content.Substring ((int)(beginPtr - startPtr), (int)(ptr - beginPtr)), + CommentStartsLine = hasStartedAtLine + }); + } + continue; + } + } + break; + case '@': + inLineStart = false; + if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) + break; + if (ptr + 1 < endPtr && *(ptr + 1) == '"') { + ptr++; + column++; + inVerbatimString = true; + } + break; + case '\n': + if (inSingleComment && hasStartedAtLine) { + bool isDocumentation = *beginPtr == '/'; + if (isDocumentation) + beginPtr++; + if (isDocumentation || bracketDepth <= 1) { + // Doesn't matter much that some comments are not correctly recognized - they'll get added later + // It's important that header comments are in. + result.Add (new MonoDevelop.Ide.TypeSystem.Comment () { + Region = new DocumentRegion (startLoc, new DocumentLocation (line, column)), + CommentType = MonoDevelop.Ide.TypeSystem.CommentType.SingleLine, + OpenTag = "//", + Text = content.Substring ((int)(beginPtr - startPtr), (int)(ptr - beginPtr)), + CommentStartsLine = hasStartedAtLine, + IsDocumentation = isDocumentation + }); + } + inSingleComment = false; + } + inString = false; + inChar = false; + inLineStart = true; + line++; + column = 1; + ptr++; + continue; + case '\r': + if (ptr + 1 < endPtr && *(ptr + 1) == '\n') + ptr++; + goto case '\n'; + case '\\': + if (inString || inChar) + ptr++; + break; + case '"': + if (inSingleComment || inMultiLineComment || inChar) + break; + if (inVerbatimString) { + if (ptr + 1 < endPtr && *(ptr + 1) == '"') { + ptr++; + column++; + break; + } + inVerbatimString = false; + break; + } + inString = !inString; + break; + case '\'': + if (inSingleComment || inMultiLineComment || inString || inVerbatimString) + break; + inChar = !inChar; + break; + default: + inLineStart &= *ptr == ' ' || *ptr == '\t'; + break; + } + + column++; + ptr++; + } + } + foreach (var fold in ToFolds (result.GetCommentsAsync().Result)) { + result.Add (fold); + } + return result; + } + #endregion + + static IEnumerable<FoldingRegion> ToFolds (IReadOnlyList<Comment> comments) + { + for (int i = 0; i < comments.Count; i++) { + Comment comment = comments [i]; + + if (comment.CommentType == CommentType.Block) { + int startOffset = 0; + if (comment.Region.BeginLine == comment.Region.EndLine) + continue; + while (startOffset < comment.Text.Length) { + char ch = comment.Text [startOffset]; + if (!char.IsWhiteSpace (ch) && ch != '*') + break; + startOffset++; + } + int endOffset = startOffset; + while (endOffset < comment.Text.Length) { + char ch = comment.Text [endOffset]; + if (ch == '\r' || ch == '\n' || ch == '*') + break; + endOffset++; + } + + string txt; + if (endOffset > startOffset) { + txt = "/* " + GetFirstLine (comment.Text) + " ..."; + } else { + txt = "/* */"; + } + yield return new FoldingRegion (txt, comment.Region, FoldType.Comment); + continue; + } + + if (!comment.CommentStartsLine) + continue; + int j = i; + int curLine = comment.Region.BeginLine - 1; + var end = comment.Region.End; + var commentText = StringBuilderCache.Allocate (); + for (; j < comments.Count; j++) { + Comment curComment = comments [j]; + if (curComment == null || !curComment.CommentStartsLine + || curComment.CommentType != comment.CommentType + || curLine + 1 != curComment.Region.BeginLine) + break; + commentText.Append (curComment.Text); + end = curComment.Region.End; + curLine = curComment.Region.BeginLine; + } + + if (j - i > 1 || (comment.IsDocumentation && comment.Region.BeginLine < comment.Region.EndLine)) { + string txt = null; + if (comment.IsDocumentation) { + string cmtText = commentText.ToString (); + int idx = cmtText.IndexOf ("<summary>", StringComparison.Ordinal); + if (idx >= 0) { + int maxOffset = cmtText.IndexOf ("</summary>", StringComparison.Ordinal); + while (maxOffset > 0 && cmtText [maxOffset - 1] == ' ') + maxOffset--; + if (maxOffset < 0) + maxOffset = cmtText.Length; + int startOffset = idx + "<summary>".Length; + while (startOffset < maxOffset) { + char ch = cmtText [startOffset]; + if (!char.IsWhiteSpace (ch) && ch != '/') + break; + startOffset++; + } + int endOffset = startOffset; + while (endOffset < maxOffset) { + char ch = cmtText [endOffset]; + if (ch == '\r' || ch == '\n') + break; + endOffset++; + } + if (endOffset > startOffset) + txt = "/// <summary> " + cmtText.Substring (startOffset, endOffset - startOffset).Trim () + " ..."; + } + if (txt == null) + txt = "/// " + comment.Text.Trim () + " ..."; + } else { + txt = "// " + comment.Text.Trim () + " ..."; + } + StringBuilderCache.Free (commentText); + yield return new FoldingRegion (txt, + new DocumentRegion (comment.Region.Begin, end), + FoldType.Comment); + i = j - 1; + } + } + } + + static string GetFirstLine (string text) + { + int start = 0; + while (start < text.Length) { + char ch = text [start]; + if (ch != ' ' && ch != '\t') + break; + start++; + } + int end = start; + + while (end < text.Length) { + char ch = text [end]; + if (MonoDevelop.Core.Text.NewLine.IsNewLine (ch)) + break; + end++; + } + if (end <= start) + return ""; + return text.Substring (start, end - start); + } + } +}
\ No newline at end of file diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs index eecaeb2247..f02a5db1c4 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpCompilerParameters.cs @@ -186,11 +186,10 @@ namespace MonoDevelop.CSharp.Project var items = warnings.Split (new [] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries).Distinct (); foreach (string warning in items) { - if (warning.StartsWith ("CS", StringComparison.OrdinalIgnoreCase)) { - yield return warning; - } else { + if (int.TryParse (warning, out _)) yield return "CS" + warning; - } + else + yield return warning; } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs index 29fa4a5f5d..28f5ec22b8 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs @@ -167,7 +167,7 @@ namespace MonoDevelop.CSharp } } - if (alreadyAddedReference.Any (reference => SystemAssemblyService.ContainsReferenceToSystemRuntime (reference))) { + if (alreadyAddedReference.Any (reference => SystemAssemblyService.RequiresFacadeAssembliesAsync (reference).WaitAndGetResult (monitor.CancellationToken))) { LoggingService.LogInfo ("Found PCLv2 assembly."); var facades = runtime.FindFacadeAssembliesForPCL (project.TargetFramework); foreach (var facade in facades) diff --git a/main/src/addins/GnomePlatform/GnomePlatform.cs b/main/src/addins/GnomePlatform/GnomePlatform.cs index 88d6e8fa31..d24bd6f705 100644 --- a/main/src/addins/GnomePlatform/GnomePlatform.cs +++ b/main/src/addins/GnomePlatform/GnomePlatform.cs @@ -165,8 +165,10 @@ namespace MonoDevelop.Platform CreateNoWindow = true, UseShellExecute = false, }; - foreach (var env in environmentVariables) - psi.EnvironmentVariables [env.Key] = env.Value; + if (environmentVariables != null) { + foreach (var env in environmentVariables) + psi.EnvironmentVariables [env.Key] = env.Value; + } ProcessWrapper proc = new ProcessWrapper (); if (terminal_command.Contains ("gnome-terminal")) { diff --git a/main/src/addins/MacPlatform/MacPlatform.cs b/main/src/addins/MacPlatform/MacPlatform.cs index fa2c985018..da6dc40138 100644 --- a/main/src/addins/MacPlatform/MacPlatform.cs +++ b/main/src/addins/MacPlatform/MacPlatform.cs @@ -266,6 +266,7 @@ namespace MonoDevelop.MacIntegration e.Reply = NSApplicationTerminateReply.Now; } }; + appDelegate.ShowDockMenu += AppDelegate_ShowDockMenu; } // Listen to the AtkCocoa notification for the presence of VoiceOver @@ -298,6 +299,27 @@ namespace MonoDevelop.MacIntegration return loaded; } + void AppDelegate_ShowDockMenu (object sender, ShowDockMenuArgs e) + { + if (((FilePath)NSBundle.MainBundle.BundlePath).Extension != ".app") + return; + var menu = new NSMenu (); + var newInstanceMenuItem = new NSMenuItem (); + newInstanceMenuItem.Title = GettextCatalog.GetString ("New Instance"); + newInstanceMenuItem.Activated += NewInstanceMenuItem_Activated; + menu.AddItem (newInstanceMenuItem); + e.DockMenu = menu; + } + + static void NewInstanceMenuItem_Activated (object sender, EventArgs e) + { + var bundlePath = NSBundle.MainBundle.BundlePath; + NSWorkspace.SharedWorkspace.LaunchApplication (NSUrl.FromFilename (bundlePath), NSWorkspaceLaunchOptions.NewInstance, new NSDictionary (), out NSError error); + if (error != null) + LoggingService.LogError ($"Failed to start new instance: {error.LocalizedDescription}"); + } + + const string EnabledKey = "com.monodevelop.AccessibilityEnabled"; static void ShowVoiceOverNotice () { diff --git a/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs b/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs index d28760db4c..6eb52e52e5 100644 --- a/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs +++ b/main/src/addins/MacPlatform/MainToolbar/SelectorView.cs @@ -562,6 +562,28 @@ namespace MonoDevelop.MacIntegration.MainToolbar int focusedCellIndex = 0; NSPathComponentCellFocusable focusedItem; + bool UpdatePreviousCellForResponderChain (int fromPosition) + { + for (focusedCellIndex = fromPosition; focusedCellIndex >= 0; focusedCellIndex--) { + var cell = Cells [focusedCellIndex].Cell; + if (PathComponentCells.Contains (cell) && cell.Enabled) { + return true; + } + } + return false; + } + + bool UpdateNextCellForResponderChain (int fromPosition) + { + for (focusedCellIndex = fromPosition; focusedCellIndex < Cells.Length; focusedCellIndex++) { + var cell = Cells [focusedCellIndex].Cell; + if (PathComponentCells.Contains (cell) && cell.Enabled) { + return true; + } + } + return false; + } + public override void KeyDown (NSEvent theEvent) { if (theEvent.KeyCode == (ushort) KeyCodes.Space) { @@ -580,9 +602,10 @@ namespace MonoDevelop.MacIntegration.MainToolbar focusedItem = null; } } else { - focusedCellIndex--; - SetSelection (); - return; + if (UpdatePreviousCellForResponderChain (focusedCellIndex - 1)) { + SetSelection (); + return; + } } } else { if (focusedCellIndex >= VisibleCellIds.Length - 1) { @@ -592,9 +615,10 @@ namespace MonoDevelop.MacIntegration.MainToolbar focusedItem = null; } } else { - focusedCellIndex++; - SetSelection (); - return; + if (UpdateNextCellForResponderChain (focusedCellIndex + 1)) { + SetSelection (); + return; + } } } } @@ -632,9 +656,9 @@ namespace MonoDevelop.MacIntegration.MainToolbar if (currentEvent.Type == NSEventType.KeyDown) { if (currentEvent.KeyCode == (ushort) KeyCodes.Tab) { if ((currentEvent.ModifierFlags & NSEventModifierMask.ShiftKeyMask) == NSEventModifierMask.ShiftKeyMask) { - focusedCellIndex = Cells.Length - 1; + UpdatePreviousCellForResponderChain (Cells.Length - 1); } else { - focusedCellIndex = 0; + UpdateNextCellForResponderChain (0); } } } diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs index d3d79b584e..0dd7160a94 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/ExceptionCaughtDialog.cs @@ -41,6 +41,8 @@ using MonoDevelop.Ide.Gui.Content; using MonoDevelop.Ide.Editor.Extension; using MonoDevelop.Ide.Fonts; using System.Collections.Generic; +using System.Drawing; +using System.Text; namespace MonoDevelop.Debugger { @@ -765,7 +767,7 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander"" Context = ctx; } - string GetMethodMarkup (bool selected) + string GetMethodMarkup (bool selected, string foregroundColor) { if (Markup != null) return $"<span foreground='{Styles.ExceptionCaughtDialog.ExternalCodeTextColor.ToHexString (false)}'>{Markup}</span>"; @@ -777,30 +779,28 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander"" var markup = $"<b>{GLib.Markup.EscapeText (methodName)}</b> {GLib.Markup.EscapeText (parameters)}"; - if (selected) - markup = $"<span foreground='#FFFFFF'>{markup}</span>"; - else { - var textColor = IsUserCode ? Ide.Gui.Styles.BaseForegroundColor.ToHexString (false) : Styles.ExceptionCaughtDialog.ExternalCodeTextColor.ToHexString (false); - markup = $"<span foreground='{textColor}'>{markup}</span>"; + if (string.IsNullOrEmpty (foregroundColor)) { + return markup; } - - return markup; + return $"<span foreground='{foregroundColor}'>{markup}</span>"; } - string GetFileMarkup (bool selected) + string GetFileMarkup (bool selected, string foregroundColor) { if (Frame == null || string.IsNullOrEmpty (Frame.File)) { return ""; } - var markup = string.Format ("<span foreground='{0}'>{1}", selected ? "#FFFFFF" : Styles.ExceptionCaughtDialog.LineNumberTextColor.ToHexString (false), GLib.Markup.EscapeText (Path.GetFileName (Frame.File))); + var markup = GLib.Markup.EscapeText (Path.GetFileName (Frame.File)); if (Frame.Line > 0) { markup += ":" + Frame.Line; if (Frame.Column > 0) markup += "," + Frame.Column; } - markup += "</span>"; - return markup; + if (string.IsNullOrEmpty (foregroundColor)) { + return markup; + } + return $"<span foreground='{foregroundColor}'>{markup}</span>"; } public override void GetSize (Widget widget, ref Gdk.Rectangle cell_area, out int x_offset, out int y_offset, out int width, out int height) @@ -808,7 +808,11 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander"" using (var layout = new Pango.Layout (Context)) { Pango.Rectangle ink, logical; layout.FontDescription = font; - layout.SetMarkup (GetMethodMarkup (false)); + + var selected = false; + var foregroundColor = Styles.GetStackFrameForegroundHexColor (selected, IsUserCode); + + layout.SetMarkup (GetMethodMarkup (selected, foregroundColor)); layout.GetPixelExtents (out ink, out logical); height = logical.Height; @@ -821,10 +825,20 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander"" protected override void Render (Gdk.Drawable window, Widget widget, Gdk.Rectangle background_area, Gdk.Rectangle cell_area, Gdk.Rectangle expose_area, CellRendererState flags) { using (var cr = Gdk.CairoHelper.Create (window)) { + if (!widget.HasFocus) { + cr.Rectangle (background_area.ToCairoRect ()); + cr.SetSourceColor (Styles.ObjectValueTreeDisabledBackgroundColor); + cr.Fill (); + } + Pango.Rectangle ink, logical; using (var layout = new Pango.Layout (Context)) { layout.FontDescription = font; - layout.SetMarkup (GetFileMarkup ((flags & CellRendererState.Selected) != 0)); + + var selected = (flags & CellRendererState.Selected) != 0; + var foregroundColor = Styles.GetStackFrameForegroundHexColor (selected, IsUserCode); + + layout.SetMarkup (GetFileMarkup (selected, foregroundColor)); layout.GetPixelExtents (out ink, out logical); var width = widget.Allocation.Width; cr.Translate (width - logical.Width - 10, cell_area.Y); @@ -832,7 +846,7 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander"" cr.IdentityMatrix (); - layout.SetMarkup (GetMethodMarkup ((flags & CellRendererState.Selected) != 0)); + layout.SetMarkup (GetMethodMarkup (selected, foregroundColor)); layout.Width = (int)((width - logical.Width - 35) * Pango.Scale.PangoScale); layout.Ellipsize = Pango.EllipsizeMode.Middle; cr.Translate (cell_area.X + 10, cell_area.Y); @@ -1105,12 +1119,19 @@ widget ""*.exception_dialog_expander"" style ""exception-dialog-expander"" { public override bool KeyPress (KeyDescriptor descriptor) { - if (descriptor.SpecialKey == SpecialKey.Escape && DebuggingService.ExceptionCaughtMessage != null && + if (DebuggingService.ExceptionCaughtMessage != null && !DebuggingService.ExceptionCaughtMessage.IsMinimized && DebuggingService.ExceptionCaughtMessage.File.CanonicalPath == new FilePath (DocumentContext.Name).CanonicalPath) { - DebuggingService.ExceptionCaughtMessage.ShowMiniButton (); - return true; + if (descriptor.SpecialKey == SpecialKey.Escape) { + DebuggingService.ExceptionCaughtMessage.ShowMiniButton (); + return true; + } + + if (descriptor.SpecialKey == SpecialKey.Return) { + DebuggingService.ExceptionCaughtMessage.ShowDialog (); + return false; + } } return base.KeyPress (descriptor); diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs index 411e8c5eda..e65c855147 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Styles.cs @@ -26,6 +26,7 @@ using MonoDevelop.Ide; using Xwt.Drawing; +using MonoDevelop.Components; namespace MonoDevelop.Debugger { @@ -94,7 +95,13 @@ namespace MonoDevelop.Debugger ExceptionCaughtDialog.ValueTreeBackgroundColor = Color.FromName ("#525252"); } + //Disabled state + ObjectValueTreeDisabledBackgroundColor = new Cairo.Color (0.64f, 0.64f, 0.64f); + // Shared + ObjectValueTreeSelectedTextColor = Ide.Gui.Styles.BaseSelectionTextColor.ToHexString (false); + ObjectValueTreeForegroundTextColor = Ide.Gui.Styles.BaseSelectionTextColor.ToHexString (false); + ObjectValueTreeExternalCodeForegroundTextColor = ExceptionCaughtDialog.ExternalCodeTextColor.ToHexString (false); ObjectValueTreeValueErrorText = Ide.Gui.Styles.WarningForegroundColor; @@ -102,6 +109,20 @@ namespace MonoDevelop.Debugger PreviewVisualizerTextColor = Ide.Gui.Styles.PopoverWindow.DefaultTextColor; PreviewVisualizerHeaderTextColor = Ide.Gui.Styles.PopoverWindow.DefaultTextColor; } + + public static string ObjectValueTreeSelectedTextColor { get; private set; } + public static string ObjectValueTreeForegroundTextColor { get; private set; } + public static string ObjectValueTreeExternalCodeForegroundTextColor { get; private set; } + + public static Cairo.Color ObjectValueTreeDisabledBackgroundColor { get; private set; } + + internal static string GetStackFrameForegroundHexColor (bool selected, bool isUserCode) + { + if (selected) { + return ObjectValueTreeSelectedTextColor; + } + return isUserCode ? ObjectValueTreeForegroundTextColor : ObjectValueTreeExternalCodeForegroundTextColor; + } } } diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxWidget.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxWidget.cs index 23045937ef..ae61656bc9 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxWidget.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/IToolboxWidget.cs @@ -40,7 +40,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox IEnumerable<ToolboxWidgetCategory> Categories { get; } IEnumerable<ToolboxWidgetItem> AllItems { get; } - event EventHandler SelectedItemChanged; event EventHandler ActivateSelectedItem; void AddCategory (ToolboxWidgetCategory category); diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs index 470addae0c..4a3633ed96 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolbox.cs @@ -62,7 +62,7 @@ namespace MonoDevelop.DesignerSupport.Toolbox public event EventHandler<Gtk.TargetEntry []> DragSourceSet; public event EventHandler ContentFocused; - public ItemToolboxNode selectedNode; + public ItemToolboxNode SelectedNode => toolboxWidget.SelectedItem?.Node; NativeViews.ToggleButton catToggleButton; NativeViews.ToggleButton compactModeToggleButton; @@ -180,7 +180,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox filterEntry.Changed += FilterEntry_Changed; - toolboxWidget.SelectedItemChanged += ToolboxWidget_SelectedItemChanged; toolboxWidget.DragBegin += ToolboxWidget_DragBegin; toolboxWidget.MouseDownActivated += ToolboxWidget_MouseDownActivated; toolboxWidget.ActivateSelectedItem += ToolboxWidget_ActivateSelectedItem; @@ -205,15 +204,13 @@ namespace MonoDevelop.DesignerSupport.Toolbox Refresh (); } - void ToolboxWidget_SelectedItemChanged (object sender, EventArgs e) - { - selectedNode = this.toolboxWidget.SelectedItem != null ? this.toolboxWidget.SelectedItem.Tag as ItemToolboxNode : null; - toolboxService.SelectItem (selectedNode); - } - void ToolboxWidget_ActivateSelectedItem (object sender, EventArgs e) { - toolboxService.UseSelectedItem (); + var selectedNode = SelectedNode; + if (selectedNode != null) { + DesignerSupport.Service.ToolboxService.SelectItem (selectedNode); + toolboxService.UseSelectedItem (); + } } void FilterEntry_Changed (object sender, EventArgs e) @@ -438,13 +435,17 @@ namespace MonoDevelop.DesignerSupport.Toolbox [CommandHandler (MonoDevelop.Ide.Commands.EditCommands.Delete)] internal void OnDeleteItem () { - if (MessageService.Confirm (GettextCatalog.GetString ("Are you sure you want to remove the selected Item?"), AlertButton.Delete)) - toolboxService.RemoveUserItem (selectedNode); + var selectedNode = SelectedNode; + if (selectedNode != null) { + if (MessageService.Confirm (GettextCatalog.GetString ("Are you sure you want to remove the selected Item?"), AlertButton.Delete)) + toolboxService.RemoveUserItem (SelectedNode); + } } [CommandUpdateHandler (MonoDevelop.Ide.Commands.EditCommands.Delete)] internal void OnUpdateDeleteItem (CommandInfo info) { + var selectedNode = SelectedNode; // Hack manually filter out gtk# widgets & container since they cannot be re added // because they're missing the toolbox attributes. info.Enabled = selectedNode != null && toolboxService.CanRemoveUserItem (selectedNode) @@ -540,7 +541,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox toolboxAddButton.Activated -= ToolboxAddButton_Clicked; toolboxAddButton.Focused -= ToolboxAddButton_Focused; - toolboxWidget.SelectedItemChanged -= ToolboxWidget_SelectedItemChanged; toolboxWidget.ActivateSelectedItem -= ToolboxWidget_ActivateSelectedItem; toolboxWidget.MenuOpened -= ToolboxWidget_MenuOpened; toolboxWidget.MouseDownActivated -= ToolboxWidget_MouseDownActivated; @@ -560,12 +560,12 @@ namespace MonoDevelop.DesignerSupport.Toolbox object IPropertyPadProvider.GetActiveComponent () { - return selectedNode; + return SelectedNode; } object IPropertyPadProvider.GetProvider () { - return selectedNode; + return SelectedNode; } void IPropertyPadProvider.OnEndEditing (object obj) diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolboxWidget.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolboxWidget.cs index 361798aca1..e52c52eb18 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolboxWidget.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/MacToolboxWidget.cs @@ -56,7 +56,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox public event EventHandler Focused; public event EventHandler DragBegin; public event EventHandler<CGPoint> MenuOpened; - public event EventHandler SelectedItemChanged; public event EventHandler ActivateSelectedItem; public Action<NSEvent> MouseDownActivated { get; set; } @@ -74,8 +73,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox internal void PerformActivateSelectedItem () => OnActivateSelectedItem (EventArgs.Empty); void OnActivateSelectedItem (EventArgs args) => ActivateSelectedItem?.Invoke (this, args); - - void OnSelectedItemChanged (EventArgs args) => SelectedItemChanged?.Invoke (this, args); NSIndexPath selectedIndexPath; public NSIndexPath SelectedIndexPath { @@ -85,7 +82,6 @@ namespace MonoDevelop.DesignerSupport.Toolbox set { if (selectedIndexPath != value) { selectedIndexPath = value; - OnSelectedItemChanged (EventArgs.Empty); } } } diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/ToolboxWidget.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/ToolboxWidget.cs index 18d9ca7f3d..70a7f4462c 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/ToolboxWidget.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport.Toolbox/ToolboxWidget.cs @@ -1149,8 +1149,7 @@ namespace MonoDevelop.DesignerSupport.Toolbox } } - [Obsolete("This class should never have been public")] - public class Item : IComparable<Item> + class Item : IComparable<Item> { ToolboxWidgetItem inner; diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/DesignerSupportService.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/DesignerSupportService.cs index 5af30a213a..10f6e33171 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/DesignerSupportService.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/DesignerSupportService.cs @@ -36,12 +36,11 @@ using System; using System.Collections; using Mono.Addins; +using MonoDevelop.Core; using MonoDevelop.Ide; namespace MonoDevelop.DesignerSupport { - - public class DesignerSupportService { PropertyPad propertyPad = null; @@ -78,10 +77,17 @@ namespace MonoDevelop.DesignerSupport propertyPad.PropertyGrid.Changed += OnPropertyGridChanged; } else if (lastCustomProvider != null) { - propertyPad.UseCustomWidget (lastCustomProvider.GetCustomPropertyWidget ()); - var customizer = lastCustomProvider as IPropertyPadCustomizer; - if (customizer != null) - customizer.Customize (pad.PadWindow, null); + try { + var currentCustomWidget = lastCustomProvider.GetCustomPropertyWidget (); + if (currentCustomWidget != null) { + propertyPad.UseCustomWidget (currentCustomWidget); + if (lastCustomProvider is IPropertyPadCustomizer customizer) + customizer.Customize (pad.PadWindow, null); + } + } catch (Exception ex) { + LoggingService.LogInternalError ($"There was an error trying to GetCustomPropertyWidget from '{lastCustomProvider.GetType ()}' provider", ex); + ReSetPad (); + } } } } @@ -174,12 +180,24 @@ namespace MonoDevelop.DesignerSupport lastCustomProvider = provider; if (propertyPad != null) { - propertyPad.UseCustomWidget (provider.GetCustomPropertyWidget ()); - propertyPad.CommandRouteOrigin = commandRouteOrigin; - - var customizer = provider as IPropertyPadCustomizer; - if (customizer != null) - customizer.Customize (propertyPad.PadWindow, null); + try { + var customWidget = provider.GetCustomPropertyWidget (); + if (customWidget != null) { + propertyPad.UseCustomWidget (customWidget); + propertyPad.CommandRouteOrigin = commandRouteOrigin; + + var customizer = provider as IPropertyPadCustomizer; + if (customizer != null) + customizer.Customize (propertyPad.PadWindow, null); + } else { + propertyPad?.BlankPad (); + return; + } + } catch (Exception ex) { + LoggingService.LogInternalError ($"There was an error trying to GetCustomPropertyWidget from '{lastCustomProvider.GetType ()}' provider", ex); + propertyPad?.BlankPad (); + return; + } } } else { diff --git a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxPad.cs b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxPad.cs index 1be634b3a0..b69333c078 100644 --- a/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxPad.cs +++ b/main/src/addins/MonoDevelop.DesignerSupport/MonoDevelop.DesignerSupport/ToolboxPad.cs @@ -82,14 +82,17 @@ namespace MonoDevelop.DesignerSupport targets.AddTable (e); }; toolbox.DragBegin += (object sender, EventArgs e) => { - if (!isDragging) { + var selectedNode = toolbox.SelectedNode; + if (!isDragging && selectedNode != null) { + + DesignerSupport.Service.ToolboxService.SelectItem (selectedNode); Gtk.Drag.SourceUnset (widget); // Gtk.Application.CurrentEvent and other copied gdk_events seem to have a problem // when used as they use gdk_event_copy which seems to crash on de-allocating the private slice. IntPtr currentEvent = GtkWorkarounds.GetCurrentEventHandle (); - Gtk.Drag.Begin (widget, targets, Gdk.DragAction.Copy | Gdk.DragAction.Move, 1, new Gdk.Event (currentEvent)); + Gtk.Drag.Begin (widget, targets, Gdk.DragAction.Copy | Gdk.DragAction.Move, 1, new Gdk.Event (currentEvent, false)); // gtk_drag_begin does not store the event, so we're okay GtkWorkarounds.FreeEvent (currentEvent); diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectSupportedTargetFrameworks.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectSupportedTargetFrameworks.cs index 97be6b2649..9863575fb1 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectSupportedTargetFrameworks.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreProjectSupportedTargetFrameworks.cs @@ -94,7 +94,7 @@ namespace MonoDevelop.DotNetCore public static IEnumerable<TargetFramework> GetNetCoreAppTargetFrameworks () { foreach (Version runtimeVersion in GetMajorRuntimeVersions ()) { - if (runtimeVersion.Major == 3 && runtimeVersion.Minor > 0) { + if (runtimeVersion.Major > 3 || (runtimeVersion.Major == 3 && runtimeVersion.Minor > 0)) { // Skip versions > 3.0 since this is not currently supported. continue; } diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdk.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdk.cs index 3da4bca039..534c810f88 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdk.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/DotNetCoreSdk.cs @@ -36,11 +36,10 @@ namespace MonoDevelop.DotNetCore public static class DotNetCoreSdk { static readonly Version DotNetCoreVersion2_1 = new Version (2, 1, 0); - static readonly DotNetCoreSdkPaths sdkPaths; static DotNetCoreSdk () { - sdkPaths = new DotNetCoreSdkPaths (); + var sdkPaths = new DotNetCoreSdkPaths (); sdkPaths.ResolveSDK (); Update (sdkPaths); } @@ -84,6 +83,8 @@ namespace MonoDevelop.DotNetCore internal static DotNetCoreSdkPaths FindSdkPaths (string[] sdks) { + var sdkPaths = new DotNetCoreSdkPaths (); + sdkPaths.ResolveSDK (); sdkPaths.FindSdkPaths (sdks); return sdkPaths; } diff --git a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/MonoRuntimeInfoExtensions.cs b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/MonoRuntimeInfoExtensions.cs index f336abfb60..2e9a399908 100644 --- a/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/MonoRuntimeInfoExtensions.cs +++ b/main/src/addins/MonoDevelop.DotNetCore/MonoDevelop.DotNetCore/MonoRuntimeInfoExtensions.cs @@ -34,7 +34,7 @@ namespace MonoDevelop.DotNetCore static readonly Version MonoVersion5_4 = new Version (5, 4, 0); static readonly Version DotNetCore2_1 = new Version (2, 1); - internal static Version CurrentRuntimeVersion { get; set; } = MonoRuntimeInfo.FromCurrentRuntime ().RuntimeVersion; + internal static Version CurrentRuntimeVersion { get; set; } = MonoRuntimeInfo.FromCurrentRuntime ()?.RuntimeVersion ?? new Version (); public static bool SupportsNetStandard20 (this Version monoVersion) { diff --git a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreRestoreTests.cs b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreRestoreTests.cs index 13e91f8a09..b501bd500e 100644 --- a/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreRestoreTests.cs +++ b/main/src/addins/MonoDevelop.PackageManagement/MonoDevelop.PackageManagement.Tests/MonoDevelop.PackageManagement.Tests/DotNetCoreRestoreTests.cs @@ -63,7 +63,7 @@ namespace MonoDevelop.PackageManagement.Tests } [Test] - public async Task OfflineRestore_NetCore22Project () + public async Task OfflineRestore_NetCore21Project () { FilePath solutionFileName = Util.GetSampleProject ("restore-netcore-offline", "dotnetcoreconsole.sln"); solution = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solutionFileName); diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs index 2f9a69c8dd..601bb3173e 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.CodeActions/CodeFixMenuService.cs @@ -186,11 +186,11 @@ namespace MonoDevelop.CodeActions var item = new CodeFixMenuEntry (label, async delegate { // Task.Run here so we don't end up binding the whole document on popping the menu, also there is no cancellation token support - var fix = await Task.Run (() => { - var context = fixState.CreateFixAllContext (new RoslynProgressTracker (), token); - return provider.GetFixAsync (context); - }); + Microsoft.CodeAnalysis.Text.TextChange[] result = await Task.Run (async () => { + var context = fixState.CreateFixAllContext (new RoslynProgressTracker (), token); + var fix = await provider.GetFixAsync (context); + var previewOperations = await fix.GetPreviewOperationsAsync (token); return await Runtime.RunInMainThread (() => { var engine = Xwt.Toolkit.CurrentEngine; // NativeEngine diff --git a/main/src/addins/MonoDevelop.UnitTesting/Commands/UnitTestCommands.cs b/main/src/addins/MonoDevelop.UnitTesting/Commands/UnitTestCommands.cs index 5f776ccc60..6d0a84150d 100644 --- a/main/src/addins/MonoDevelop.UnitTesting/Commands/UnitTestCommands.cs +++ b/main/src/addins/MonoDevelop.UnitTesting/Commands/UnitTestCommands.cs @@ -44,7 +44,7 @@ namespace MonoDevelop.UnitTesting.Commands GoToFailure, RerunTest, } - + public enum TestChartCommands { ShowResults, @@ -55,13 +55,20 @@ namespace MonoDevelop.UnitTesting.Commands ShowFailedTests, ShowIgnoredTests } - + public enum NUnitProjectCommands { AddAssembly } - - class RunAllTestsHandler: CommandHandler + + public enum TextEditorCommands + { + RunTestAtCaret, + DebugTestAtCaret, + SelectTestAtCaret + } + + class RunAllTestsHandler : CommandHandler { protected override void Run () { diff --git a/main/src/addins/MonoDevelop.UnitTesting/MonoDevelop.UnitTesting.addin.xml b/main/src/addins/MonoDevelop.UnitTesting/MonoDevelop.UnitTesting.addin.xml index c2158343b2..4fd591a678 100644 --- a/main/src/addins/MonoDevelop.UnitTesting/MonoDevelop.UnitTesting.addin.xml +++ b/main/src/addins/MonoDevelop.UnitTesting/MonoDevelop.UnitTesting.addin.xml @@ -75,6 +75,9 @@ <Command id = "MonoDevelop.UnitTesting.Commands.TestChartCommands.ShowSuccessfulTests" _label = "Show successful tests" type="check"/> <Command id = "MonoDevelop.UnitTesting.Commands.TestChartCommands.ShowFailedTests" _label = "Show failed tests" type="check"/> <Command id = "MonoDevelop.UnitTesting.Commands.TestChartCommands.ShowIgnoredTests" _label = "Show ignored tests" type="check"/> + <Command id = "MonoDevelop.UnitTesting.Commands.TextEditorCommands.RunTestAtCaret" _label = "Run Test at Caret"/> + <Command id = "MonoDevelop.UnitTesting.Commands.TextEditorCommands.DebugTestAtCaret" _label = "Debug Test at Caret"/> + <Command id = "MonoDevelop.UnitTesting.Commands.TextEditorCommands.SelectTestAtCaret" _label = "Select Test at Caret"/> </Category> </Extension> diff --git a/main/src/addins/MonoDevelop.UnitTesting/Services/AbstractUnitTestEditorExtension.cs b/main/src/addins/MonoDevelop.UnitTesting/Services/AbstractUnitTestEditorExtension.cs index 58449a06a8..27464ad5b2 100644 --- a/main/src/addins/MonoDevelop.UnitTesting/Services/AbstractUnitTestEditorExtension.cs +++ b/main/src/addins/MonoDevelop.UnitTesting/Services/AbstractUnitTestEditorExtension.cs @@ -37,6 +37,8 @@ using MonoDevelop.Ide.Editor.Extension; using MonoDevelop.Ide.Editor; using MonoDevelop.Projects; using Mono.Addins; +using MonoDevelop.Components.Commands; +using MonoDevelop.UnitTesting.Commands; namespace MonoDevelop.UnitTesting { @@ -137,6 +139,36 @@ namespace MonoDevelop.UnitTesting List<IUnitTestMarker> currentMarker = new List<IUnitTestMarker>(); + TestRunner GetTestRunnerAtCaret (bool debug) + { + var line = Editor.GetLine (Editor.CaretLine); + if (line == null) + return null; + foreach (var marker in Editor.GetLineMarkers (line)) { + if (marker is IUnitTestMarker result) + return new TestRunner (result.UnitTest.UnitTestIdentifier, DocumentContext.Project, debug); + } + return null; + } + + [CommandHandler (TextEditorCommands.RunTestAtCaret)] + protected void OnRunTestAtCaret () + { + GetTestRunnerAtCaret (false)?.Run (this, EventArgs.Empty); + } + + [CommandHandler (TextEditorCommands.DebugTestAtCaret)] + protected void OnDebugTestAtCaret () + { + GetTestRunnerAtCaret (true)?.Run (this, EventArgs.Empty); + } + + [CommandHandler (TextEditorCommands.SelectTestAtCaret)] + protected void OnSelectTestAtCaret () + { + GetTestRunnerAtCaret (false)?.Select (this, EventArgs.Empty); + } + class UnitTestMarkerHostImpl : UnitTestMarkerHost { readonly AbstractUnitTestTextEditorExtension ext; @@ -277,82 +309,70 @@ namespace MonoDevelop.UnitTesting #endregion - class TestRunner - { - readonly string testCase; - readonly bool debug; - IBuildTarget project; - - public TestRunner (string testCase, IBuildTarget project, bool debug) - { - this.testCase = testCase; - this.debug = debug; - this.project = project; - } - - bool TimeoutHandler () - { - var test = UnitTestService.SearchTestByDocumentId (testCase); - if (test != null) { - RunTest (test); - timeoutHandler = 0; - } else { - return true; - } - return false; - } + } + class TestRunner + { + readonly string testCase; + readonly bool debug; + IBuildTarget project; - internal async void Run (object sender, EventArgs e) - { - if (IdeApp.ProjectOperations.IsBuilding (IdeApp.ProjectOperations.CurrentSelectedSolution) || - IdeApp.ProjectOperations.IsRunning (IdeApp.ProjectOperations.CurrentSelectedSolution)) - return; + public TestRunner (string testCase, IBuildTarget project, bool debug) + { + this.testCase = testCase; + this.debug = debug; + this.project = project; + } - var foundTest = UnitTestService.SearchTestByDocumentId (testCase); - if (foundTest != null) { - RunTest (foundTest); - return; - } + internal async void Run (object sender, EventArgs e) + { + if (IdeApp.ProjectOperations.IsBuilding (IdeApp.ProjectOperations.CurrentSelectedSolution) || + IdeApp.ProjectOperations.IsRunning (IdeApp.ProjectOperations.CurrentSelectedSolution)) + return; - bool buildBeforeExecuting = IdeApp.Preferences.BuildBeforeRunningTests; + var foundTest = UnitTestService.SearchTestByDocumentId (testCase); + if (foundTest != null) { + RunTest (foundTest); + return; + } - if (buildBeforeExecuting) { - await IdeApp.ProjectOperations.Build (project).Task; - await UnitTestService.RefreshTests (CancellationToken.None); - } + bool buildBeforeExecuting = IdeApp.Preferences.BuildBeforeRunningTests; - foundTest = UnitTestService.SearchTestByDocumentId (testCase); - if (foundTest != null) - RunTest (foundTest); - else - UnitTestService.ReportExecutionError (GettextCatalog.GetString ("Unit test '{0}' could not be loaded.", testCase)); + if (buildBeforeExecuting) { + await IdeApp.ProjectOperations.Build (project).Task; + await UnitTestService.RefreshTests (CancellationToken.None); } - internal void Select (object sender, EventArgs e) - { - var test = UnitTestService.SearchTestByDocumentId (testCase); - if (test == null) - return; - UnitTestService.CurrentSelectedTest = test; - } + foundTest = UnitTestService.SearchTestByDocumentId (testCase); + if (foundTest != null) + RunTest (foundTest); + else + UnitTestService.ReportExecutionError (GettextCatalog.GetString ("Unit test '{0}' could not be loaded.", testCase)); + } - void RunTest (UnitTest test) - { - var debugModeSet = Runtime.ProcessService.GetDebugExecutionMode (); - Core.Execution.IExecutionHandler ctx = null; - if (debug && debugModeSet != null) { - foreach (var executionMode in debugModeSet.ExecutionModes) { - if (test.CanRun (executionMode.ExecutionHandler)) { - ctx = executionMode.ExecutionHandler; - break; - } + internal void Select (object sender, EventArgs e) + { + var test = UnitTestService.SearchTestByDocumentId (testCase); + if (test == null) + return; + UnitTestService.CurrentSelectedTest = test; + } + + void RunTest (UnitTest test) + { + var debugModeSet = Runtime.ProcessService.GetDebugExecutionMode (); + Core.Execution.IExecutionHandler ctx = null; + if (debug && debugModeSet != null) { + foreach (var executionMode in debugModeSet.ExecutionModes) { + if (test.CanRun (executionMode.ExecutionHandler)) { + ctx = executionMode.ExecutionHandler; + break; } } - // NUnitService.Instance.RunTest (test, ctx); - var pad = IdeApp.Workbench.GetPad<TestPad> (); - var content = (TestPad)pad.Content; - content.RunTest (test, ctx); } + // NUnitService.Instance.RunTest (test, ctx); + var pad = IdeApp.Workbench.GetPad<TestPad> (); + var content = (TestPad)pad.Content; + content.RunTest (test, ctx); } } } diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogWidget.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogWidget.cs index 4801dc0450..6ce42aff48 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogWidget.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/LogWidget.cs @@ -36,6 +36,7 @@ using Mono.TextEditor; using System.Linq; using MonoDevelop.Ide.Editor; using MonoDevelop.Ide.Fonts; +using Humanizer; namespace MonoDevelop.VersionControl.Views { @@ -512,21 +513,15 @@ namespace MonoDevelop.VersionControl.Views static void DateFunc (Gtk.TreeViewColumn tree_column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) { - CellRendererText renderer = (CellRendererText)cell; - var rev = (Revision)model.GetValue (iter, 0); - string day; - + var renderer = (CellRendererText)cell; + var revision = (Revision)model.GetValue (iter, 0); // Grab today's day and the start of tomorrow's day to make Today/Yesterday calculations. var now = DateTime.Now; - var age = new DateTime (now.Year, now.Month, now.Day).AddDays(1) - rev.Time; - if (age.Days >= 0 && age.Days < 1) { // Check whether it's a commit that's less than a day away. Also discard future commits. - day = GettextCatalog.GetString ("Today"); - } else if (age.Days < 2) { // Check whether it's a commit from yesterday. - day = GettextCatalog.GetString ("Yesterday"); - } else { - day = rev.Time.ToShortDateString (); - } - renderer.Text = string.Format ("{0} {1:HH:mm}", day, rev.Time); + var age = new DateTime (now.Year, now.Month, now.Day).AddDays (1) - revision.Time; + + renderer.Text = age.Days >= 2 ? + revision.Time.ToShortDateString () : + revision.Time.Humanize (utcDate: false, dateToCompareAgainst: now); } static void GraphFunc (Gtk.TreeViewColumn tree_column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter) diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj index 63b2eef317..790a4e46b0 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.csproj @@ -78,6 +78,10 @@ <HintPath>..\..\..\..\build\bin\Microsoft.VisualStudio.Text.UI.dll</HintPath> <Private>False</Private> </Reference> + <Reference Include="Humanizer"> + <HintPath>..\..\..\..\packages\Humanizer.Core.2.2.0\lib\netstandard1.0\Humanizer.dll</HintPath> + <Private>False</Private> + </Reference> </ItemGroup> <ItemGroup> <EmbeddedResource Include="icons\added-overlay-16.png" /> @@ -516,10 +520,14 @@ <Compile Include="Gui\MonoDevelop.VersionControl.UrlBasedRepositoryEditor.cs" /> <Compile Include="Gui\MonoDevelop.VersionControl.Views.DiffWidget.cs" /> <Compile Include="Gui\MonoDevelop.VersionControl.Views.LogWidget.cs" /> + <Compile Include="MonoDevelop.VersionControl\Instrumentation.cs" /> </ItemGroup> <ItemGroup> <InternalsVisibleTo Include="MonoDevelop.VersionControl.Git.Tests" /> <InternalsVisibleTo Include="MonoDevelop.MacDev" /> </ItemGroup> + <ItemGroup> + <None Include="packages.config" /> + </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> </Project> diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Instrumentation.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Instrumentation.cs new file mode 100644 index 0000000000..402ec0183a --- /dev/null +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Instrumentation.cs @@ -0,0 +1,163 @@ +// +// Counters.cs +// +// Author: +// Vsevolod Kukol <sevoku@microsoft.com> +// +// Copyright (c) 2019 (c) Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using MonoDevelop.Core.Instrumentation; +using System.Threading; +using MonoDevelop.Core; + +namespace MonoDevelop.VersionControl +{ + static class Instrumentation + { + const string Category = "Version Control"; + const string Identifier = "VersionControl"; + + public static readonly Counter<RepositoryMetadata> Repositories = InstrumentationService.CreateCounter<RepositoryMetadata> ("VersionControl.RepositoryOpened", Category, id: $"{Identifier}.RepositoryOpened"); + public static readonly TimerCounter<MultipathOperationMetadata> CheckoutCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Checkout", Category, id: $"{Identifier}.Checkout"); + public static readonly TimerCounter<MultipathOperationMetadata> CommitCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Commit", Category, id: $"{Identifier}.Commit"); + public static readonly TimerCounter<RevertMetadata> RevertCounter = InstrumentationService.CreateTimerCounter<RevertMetadata> ("Revert", Category, id: $"{Identifier}.Revert"); + public static readonly TimerCounter<PublishMetadata> PublishCounter = InstrumentationService.CreateTimerCounter<PublishMetadata> ("Publish", Category, id: $"{Identifier}.Publish"); + public static readonly TimerCounter<MultipathOperationMetadata> UpdateCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Update", Category, id: $"{Identifier}.Update"); + public static readonly TimerCounter<MultipathOperationMetadata> AddCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Add", Category, id: $"{Identifier}.Add"); + public static readonly TimerCounter<MoveMetadata> MoveCounter = InstrumentationService.CreateTimerCounter<MoveMetadata> ("Move", Category, id: $"{Identifier}.Move"); + public static readonly TimerCounter<DeleteMetadata> DeleteCounter = InstrumentationService.CreateTimerCounter<DeleteMetadata> ("Delete", Category, id: $"{Identifier}.Delete"); + public static readonly TimerCounter<MultipathOperationMetadata> LockCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Lock", Category, id: $"{Identifier}.Lock"); + public static readonly TimerCounter<MultipathOperationMetadata> UnlockCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Unlock", Category, id: $"{Identifier}.Unlock"); + public static readonly TimerCounter<MultipathOperationMetadata> IgnoreCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Ignore", Category, id: $"{Identifier}.Ignore"); + public static readonly TimerCounter<MultipathOperationMetadata> UnignoreCounter = InstrumentationService.CreateTimerCounter<MultipathOperationMetadata> ("Unignore", Category, id: $"{Identifier}.Unignore"); + + public static readonly TimerCounter<RepositoryMetadata> GetRevisionChangesCounter = InstrumentationService.CreateTimerCounter<RepositoryMetadata> ("Get Revision Changes", Category, id: $"{Identifier}.GetRevisionChanges"); + public static readonly TimerCounter<RepositoryMetadata> GetHistoryCounter = InstrumentationService.CreateTimerCounter<RepositoryMetadata> ("Get History", Category, id: $"{Identifier}.GetHistory"); + } + + class MultipathOperationMetadata : RepositoryMetadata + { + public MultipathOperationMetadata () + { + } + + public MultipathOperationMetadata (VersionControlSystem versionControl) : base (versionControl) + { + } + + public int PathsCount { + get => GetProperty<int> (); + set => SetProperty (value); + } + + public bool Recursive { + get => GetProperty<bool> (); + set => SetProperty (value); + } + } + + class RevertMetadata : MultipathOperationMetadata + { + public enum RevertType + { + LocalChanges, + ToRevision, + SpecificRevision + }; + + public RevertMetadata () + { + } + + public RevertMetadata (VersionControlSystem versionControl) : base (versionControl) + { + } + + public RevertType OperationType { + get => GetProperty<RevertType> (); + set => SetProperty (value); + } + } + + class PublishMetadata : MultipathOperationMetadata + { + public PublishMetadata () + { + } + + public PublishMetadata (VersionControlSystem versionControl) : base (versionControl) + { + } + + public bool SubFolder { + get => GetProperty<bool> (); + set => SetProperty (value); + } + } + + class DeleteMetadata : MultipathOperationMetadata + { + public DeleteMetadata () + { + } + + public DeleteMetadata (VersionControlSystem versionControl) : base (versionControl) + { + } + + public bool Force { + get => GetProperty<bool> (); + set => SetProperty (value); + } + + public bool KeepLocal { + get => GetProperty<bool> (); + set => SetProperty (value); + } + } + + class MoveMetadata : RepositoryMetadata + { + public enum MoveType + { + File, + Directory + } + + public MoveMetadata () + { + } + + public MoveMetadata (VersionControlSystem versionControl) : base (versionControl) + { + } + + public bool Force { + get => GetProperty<bool> (); + set => SetProperty (value); + } + + public MoveType OperationType { + get => GetProperty<MoveType> (); + set => SetProperty (value); + } + } +} diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs index 12f642acf8..f5b4c4b020 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/Repository.cs @@ -468,7 +468,14 @@ namespace MonoDevelop.VersionControl /// </param> public RevisionPath[] GetRevisionChanges (Revision revision) { - return OnGetRevisionChanges (revision); + using (var tracker = Instrumentation.GetRevisionChangesCounter.BeginTiming (new RepositoryMetadata (VersionControlSystem))) { + try { + return OnGetRevisionChanges (revision); + } catch { + tracker.Metadata.SetFailure (); + throw; + } + } } @@ -478,7 +485,14 @@ namespace MonoDevelop.VersionControl // Returns the revision history of a file public Revision[] GetHistory (FilePath localFile, Revision since) { - return OnGetHistory (localFile, since); + using (var tracker = Instrumentation.GetHistoryCounter.BeginTiming (new RepositoryMetadata (VersionControlSystem))) { + try { + return OnGetHistory (localFile, since); + } catch { + tracker.Metadata.SetFailure (); + throw; + } + } } protected abstract Revision[] OnGetHistory (FilePath localFile, Revision since); @@ -505,9 +519,17 @@ namespace MonoDevelop.VersionControl // repository directory (must use absolute local paths). public Repository Publish (string serverPath, FilePath localPath, FilePath[] files, string message, ProgressMonitor monitor) { - var res = OnPublish (serverPath, localPath, files, message, monitor); - ClearCachedVersionInfo (localPath); - return res; + var metadata = new PublishMetadata (VersionControlSystem) { PathsCount = files.Length, SubFolder = localPath.IsChildPathOf (RootPath) }; + using (var tracker = Instrumentation.PublishCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + var res = OnPublish (serverPath, localPath, files, message, monitor); + ClearCachedVersionInfo (localPath); + return res; + } catch { + metadata.SetFailure (); + throw; + } + } } protected abstract Repository OnPublish (string serverPath, FilePath localPath, FilePath[] files, string message, ProgressMonitor monitor); @@ -521,8 +543,16 @@ namespace MonoDevelop.VersionControl public void Update (FilePath[] localPaths, bool recurse, ProgressMonitor monitor) { - OnUpdate (localPaths, recurse, monitor); - ClearCachedVersionInfo (localPaths); + var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Recursive = recurse }; + using (var tracker = Instrumentation.UpdateCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + OnUpdate (localPaths, recurse, monitor); + ClearCachedVersionInfo (localPaths); + } catch { + metadata.SetFailure (); + throw; + } + } } protected abstract void OnUpdate (FilePath[] localPaths, bool recurse, ProgressMonitor monitor); @@ -553,8 +583,16 @@ namespace MonoDevelop.VersionControl // Commits changes in a set of files or directories into the repository public void Commit (ChangeSet changeSet, ProgressMonitor monitor) { - ClearCachedVersionInfo (changeSet.BaseLocalPath); - OnCommit (changeSet, monitor); + var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = changeSet.Count }; + using (var tracker = Instrumentation.CommitCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + ClearCachedVersionInfo (changeSet.BaseLocalPath); + OnCommit (changeSet, monitor); + } catch { + metadata.SetFailure (); + throw; + } + } } protected abstract void OnCommit (ChangeSet changeSet, ProgressMonitor monitor); @@ -567,16 +605,37 @@ namespace MonoDevelop.VersionControl public void Checkout (FilePath targetLocalPath, Revision rev, bool recurse, ProgressMonitor monitor) { - ClearCachedVersionInfo (targetLocalPath); - OnCheckout (targetLocalPath, rev, recurse, monitor); + var metadata = new MultipathOperationMetadata (VersionControlSystem) { Recursive = recurse }; + using (var tracker = Instrumentation.CheckoutCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + ClearCachedVersionInfo (targetLocalPath); + OnCheckout (targetLocalPath, rev, recurse, monitor); + + if (!Directory.Exists (targetLocalPath)) + metadata.SetFailure (); + else + metadata.SetSuccess (); + } catch { + metadata.SetFailure (); + throw; + } + } } protected abstract void OnCheckout (FilePath targetLocalPath, Revision rev, bool recurse, ProgressMonitor monitor); public void Revert (FilePath[] localPaths, bool recurse, ProgressMonitor monitor) { - ClearCachedVersionInfo (localPaths); - OnRevert (localPaths, recurse, monitor); + var metadata = new RevertMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Recursive = recurse, OperationType = RevertMetadata.RevertType.LocalChanges }; + using (var tracker = Instrumentation.RevertCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + ClearCachedVersionInfo (localPaths); + OnRevert (localPaths, recurse, monitor); + } catch { + metadata.SetFailure (); + throw; + } + } } public void Revert (FilePath localPath, bool recurse, ProgressMonitor monitor) @@ -588,16 +647,32 @@ namespace MonoDevelop.VersionControl public void RevertRevision (FilePath localPath, Revision revision, ProgressMonitor monitor) { - ClearCachedVersionInfo (localPath); - OnRevertRevision (localPath, revision, monitor); + var metadata = new RevertMetadata (VersionControlSystem) { PathsCount = 1, Recursive = true, OperationType = RevertMetadata.RevertType.SpecificRevision }; + using (var tracker = Instrumentation.RevertCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + ClearCachedVersionInfo (localPath); + OnRevertRevision (localPath, revision, monitor); + } catch { + metadata.SetFailure (); + throw; + } + } } protected abstract void OnRevertRevision (FilePath localPath, Revision revision, ProgressMonitor monitor); public void RevertToRevision (FilePath localPath, Revision revision, ProgressMonitor monitor) { - ClearCachedVersionInfo (localPath); - OnRevertToRevision (localPath, revision, monitor); + var metadata = new RevertMetadata (VersionControlSystem) { PathsCount = 1, Recursive = true, OperationType = RevertMetadata.RevertType.ToRevision }; + using (var tracker = Instrumentation.RevertCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + ClearCachedVersionInfo (localPath); + OnRevertToRevision (localPath, revision, monitor); + } catch { + metadata.SetFailure (); + throw; + } + } } protected abstract void OnRevertToRevision (FilePath localPath, Revision revision, ProgressMonitor monitor); @@ -610,12 +685,17 @@ namespace MonoDevelop.VersionControl public void Add (FilePath[] localPaths, bool recurse, ProgressMonitor monitor) { - try { - OnAdd (localPaths, recurse, monitor); - } catch (Exception e) { - LoggingService.LogError ("Failed to add file", e); + var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Recursive = recurse }; + using (var tracker = Instrumentation.AddCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + OnAdd (localPaths, recurse, monitor); + } catch (Exception e) { + LoggingService.LogError ("Failed to add file", e); + metadata.SetFailure (); + } finally { + ClearCachedVersionInfo (localPaths); + } } - ClearCachedVersionInfo (localPaths); } protected abstract void OnAdd (FilePath[] localPaths, bool recurse, ProgressMonitor monitor); @@ -635,11 +715,15 @@ namespace MonoDevelop.VersionControl public void MoveFile (FilePath localSrcPath, FilePath localDestPath, bool force, ProgressMonitor monitor) { ClearCachedVersionInfo (localSrcPath, localDestPath); - try { - OnMoveFile (localSrcPath, localDestPath, force, monitor); - } catch (Exception e) { - LoggingService.LogError ("Failed to move file", e); - File.Move (localSrcPath, localDestPath); + var metadata = new MoveMetadata (VersionControlSystem) { Force = force, OperationType = MoveMetadata.MoveType.File }; + using (var tracker = Instrumentation.MoveCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + OnMoveFile (localSrcPath, localDestPath, force, monitor); + } catch (Exception e) { + LoggingService.LogError ("Failed to move file", e); + metadata.SetFailure (); + File.Move (localSrcPath, localDestPath); + } } } @@ -653,11 +737,15 @@ namespace MonoDevelop.VersionControl public void MoveDirectory (FilePath localSrcPath, FilePath localDestPath, bool force, ProgressMonitor monitor) { ClearCachedVersionInfo (localSrcPath, localDestPath); - try { - OnMoveDirectory (localSrcPath, localDestPath, force, monitor); - } catch (Exception e) { - LoggingService.LogError ("Failed to move directory", e); - FileService.SystemDirectoryRename (localSrcPath, localDestPath); + var metadata = new MoveMetadata (VersionControlSystem) { Force = force, OperationType = MoveMetadata.MoveType.Directory }; + using (var tracker = Instrumentation.MoveCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + OnMoveDirectory (localSrcPath, localDestPath, force, monitor); + } catch (Exception e) { + LoggingService.LogError ("Failed to move directory", e); + metadata.SetFailure (); + FileService.SystemDirectoryRename (localSrcPath, localDestPath); + } } } @@ -675,13 +763,17 @@ namespace MonoDevelop.VersionControl public void DeleteFiles (FilePath[] localPaths, bool force, ProgressMonitor monitor, bool keepLocal = true) { - try { - OnDeleteFiles (localPaths, force, monitor, keepLocal); - } catch (Exception e) { - LoggingService.LogError ("Failed to delete file", e); - if (!keepLocal) - foreach (var path in localPaths) - File.Delete (path); + var metadata = new DeleteMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Force = force, KeepLocal = keepLocal }; + using (var tracker = Instrumentation.DeleteCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + OnDeleteFiles (localPaths, force, monitor, keepLocal); + } catch (Exception e) { + LoggingService.LogError ("Failed to delete file", e); + metadata.SetFailure (); + if (!keepLocal) + foreach (var path in localPaths) + File.Delete (path); + } } ClearCachedVersionInfo (localPaths); } @@ -695,13 +787,17 @@ namespace MonoDevelop.VersionControl public void DeleteDirectories (FilePath[] localPaths, bool force, ProgressMonitor monitor, bool keepLocal = true) { - try { - OnDeleteDirectories (localPaths, force, monitor, keepLocal); - } catch (Exception e) { - LoggingService.LogError ("Failed to delete directory", e); - if (!keepLocal) - foreach (var path in localPaths) - Directory.Delete (path, true); + var metadata = new DeleteMetadata (VersionControlSystem) { PathsCount = localPaths.Length, Force = force, KeepLocal = keepLocal }; + using (var tracker = Instrumentation.DeleteCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + OnDeleteDirectories (localPaths, force, monitor, keepLocal); + } catch (Exception e) { + LoggingService.LogError ("Failed to delete directory", e); + metadata.SetFailure (); + if (!keepLocal) + foreach (var path in localPaths) + Directory.Delete (path, true); + } } ClearCachedVersionInfo (localPaths); } @@ -733,8 +829,16 @@ namespace MonoDevelop.VersionControl // Locks a file in the repository so no other users can change it public void Lock (ProgressMonitor monitor, params FilePath[] localPaths) { - ClearCachedVersionInfo (localPaths); - OnLock (monitor, localPaths); + var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPaths.Length }; + using (var tracker = Instrumentation.LockCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + ClearCachedVersionInfo (localPaths); + OnLock (monitor, localPaths); + } catch { + metadata.SetFailure (); + throw; + } + } } // Locks a file in the repository so no other users can change it @@ -746,8 +850,16 @@ namespace MonoDevelop.VersionControl // Unlocks a file in the repository so other users can change it public void Unlock (ProgressMonitor monitor, params FilePath[] localPaths) { - ClearCachedVersionInfo (localPaths); - OnUnlock (monitor, localPaths); + var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPaths.Length }; + using (var tracker = Instrumentation.UnlockCounter.BeginTiming (metadata, monitor.CancellationToken)) { + try { + ClearCachedVersionInfo (localPaths); + OnUnlock (monitor, localPaths); + } catch { + metadata.SetFailure (); + throw; + } + } } protected virtual void OnUnlock (ProgressMonitor monitor, params FilePath[] localPaths) @@ -882,8 +994,16 @@ namespace MonoDevelop.VersionControl // Ignores a file for version control operations. public void Ignore (FilePath[] localPath) { - ClearCachedVersionInfo (localPath); - OnIgnore (localPath); + var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPath.Length }; + using (var tracker = Instrumentation.IgnoreCounter.BeginTiming (metadata)) { + try { + ClearCachedVersionInfo (localPath); + OnIgnore (localPath); + } catch { + metadata.SetFailure (); + throw; + } + } } protected abstract void OnIgnore (FilePath[] localPath); @@ -891,8 +1011,16 @@ namespace MonoDevelop.VersionControl // Unignores a file for version control operations. public void Unignore (FilePath[] localPath) { - ClearCachedVersionInfo (localPath); - OnUnignore (localPath); + var metadata = new MultipathOperationMetadata (VersionControlSystem) { PathsCount = localPath.Length }; + using (var tracker = Instrumentation.UnignoreCounter.BeginTiming (metadata)) { + try { + ClearCachedVersionInfo (localPath); + OnUnignore (localPath); + } catch { + metadata.SetFailure (); + throw; + } + } } protected abstract void OnUnignore (FilePath[] localPath); diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs index 6f43ccae81..aa25a066f2 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl/VersionControlService.cs @@ -226,7 +226,6 @@ namespace MonoDevelop.VersionControl return repo; } - readonly static Counter<RepositoryMetadata> Repositories = InstrumentationService.CreateCounter<RepositoryMetadata> ("VersionControl.RepositoryOpened", "Version Control", id:"VersionControl.RepositoryOpened"); internal static readonly Dictionary<FilePath,Repository> repositoryCache = new Dictionary<FilePath,Repository> (); public static Repository GetRepositoryReference (string path, string id) { @@ -257,10 +256,7 @@ namespace MonoDevelop.VersionControl var repo = detectedVCS?.GetRepositoryReference (bestMatch, id); if (repo != null) { repositoryCache.Add (bestMatch, repo); - Repositories.Inc (new RepositoryMetadata { - Type = detectedVCS.Name, - Version = detectedVCS.Version, - }); + Instrumentation.Repositories.Inc (new RepositoryMetadata (detectedVCS)); } return repo; } catch (Exception e) { @@ -841,10 +837,17 @@ namespace MonoDevelop.VersionControl Other } - public class RepositoryMetadata : CounterMetadata + class RepositoryMetadata : CounterMetadata { public RepositoryMetadata () { + throw new InvalidOperationException (); + } + + public RepositoryMetadata (VersionControlSystem versionControl) + { + Type = versionControl?.Name; + Version = versionControl?.Version; } public string Type { diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/packages.config b/main/src/addins/VersionControl/MonoDevelop.VersionControl/packages.config new file mode 100644 index 0000000000..f3aeebbbc7 --- /dev/null +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/packages.config @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<packages> + <package id="Humanizer.Core" version="2.2.0" targetFramework="net471" /> +</packages>
\ No newline at end of file diff --git a/main/src/addins/Xml/Completion/XmlSchemaCompletionData.cs b/main/src/addins/Xml/Completion/XmlSchemaCompletionData.cs index 0ecc94b5a9..27562c546d 100644 --- a/main/src/addins/Xml/Completion/XmlSchemaCompletionData.cs +++ b/main/src/addins/Xml/Completion/XmlSchemaCompletionData.cs @@ -66,17 +66,6 @@ namespace MonoDevelop.Xml.Completion }
/// <summary>
- /// Creates completion data from the schema passed in
- /// via the reader object.
- /// </summary> - [Obsolete ("Please pass in a TextReader instead")]
- public XmlSchemaCompletionData(XmlTextReader reader)
- {
- reader.XmlResolver = new LocalOnlyXmlResolver ();
- ReadSchema(reader);
- }
-
- /// <summary>
/// Creates the completion data from the specified schema file.
/// </summary>
public XmlSchemaCompletionData (string fileName) : this (String.Empty, fileName)
diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs index 4ac4cf29c3..6138d7529e 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/TextDocument.cs @@ -1,22 +1,22 @@ -// +// // TextDocument.cs -// +// // Author: // Mike Krüger <mkrueger@xamarin.com> -// +// // Copyright (c) 2007 Novell, Inc (http://www.novell.com) // Copyright (c) 2012 Xamarin Inc. (http://xamarin.com) -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -42,6 +42,9 @@ using MonoDevelop.Core.Text; using MonoDevelop.Ide;
using MonoDevelop.Ide.Editor; using MonoDevelop.Ide.Editor.Highlighting; +using Microsoft.VisualStudio.Platform; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; namespace Mono.TextEditor {
@@ -133,7 +136,7 @@ namespace Mono.TextEditor { if (e == MonoDevelop.Ide.Editor.LineEventArgs.AllLines) { CommitUpdateAll (true); - } else { + } else { CommitMultipleLineUpdate (e.Line.LineNumber, e.Line.LineNumber, true); }
}
@@ -235,8 +238,8 @@ namespace Mono.TextEditor }
var textChange = new TextChangeEventArgs(changes); - InterruptFoldWorker(); TextChanging?.Invoke (this, textChange); + // After TextChanging notification has been sent, we can update the cached snapshot
this.currentSnapshot = args.After; }
@@ -372,7 +375,7 @@ namespace Mono.TextEditor { ReplaceText (offset, count, (string)null); } - + public void RemoveText (ISegment segment) { RemoveText (segment.Offset, segment.Length); @@ -430,17 +433,17 @@ namespace Mono.TextEditor { return GetTextBetween (LocationToOffset (start), LocationToOffset (end)); } - + public string GetTextBetween (int startLine, int startColumn, int endLine, int endColumn) { return GetTextBetween (LocationToOffset (startLine, startColumn), LocationToOffset (endLine, endColumn)); } - + public string GetTextAt (int offset, int count) { return this.currentSnapshot.GetText(offset, count); } - + public string GetTextAt (DocumentRegion region) { return GetTextAt (region.GetSegment (this)); @@ -465,13 +468,13 @@ namespace Mono.TextEditor var lineSegment = GetLine (line); return lineSegment != null ? GetTextAt (lineSegment.Offset, lineSegment.Length) : null; } - + public string GetLineText (int line, bool includeDelimiter) { var lineSegment = GetLine (line); return GetTextAt (lineSegment.Offset, includeDelimiter ? lineSegment.LengthIncludingDelimiter : lineSegment.Length); } - + public char GetCharAt (int offset) {
return this.currentSnapshot[offset]; @@ -563,7 +566,7 @@ namespace Mono.TextEditor { return Text.IndexOfAny (anyOf, startIndex, count); } - + /// <summary> /// Gets the index of the first occurrence of the specified search text in this text source. /// </summary> @@ -576,7 +579,7 @@ namespace Mono.TextEditor { return Text.IndexOf (searchText, startIndex, count, comparisonType); } - + /// <summary> /// Gets the index of the last occurrence of the specified character in this text source. /// </summary> @@ -590,7 +593,7 @@ namespace Mono.TextEditor { return Text.LastIndexOf (c, startIndex, count); } - + /// <summary> /// Gets the index of the last occurrence of the specified search text in this text source. /// </summary> @@ -655,12 +658,12 @@ namespace Mono.TextEditor DocumentLine documentLine = GetLine(line); return System.Math.Min(Length, documentLine.Offset + System.Math.Max(0, System.Math.Min(documentLine.Length, column - 1))); } - + public int LocationToOffset (DocumentLocation location) { return LocationToOffset(location.Line, location.Column); } - + public DocumentLocation OffsetToLocation (int offset) {
IDocumentLine line = this.GetLineByOffset(offset);
@@ -675,19 +678,19 @@ namespace Mono.TextEditor { return GetLineIndent (GetLine (lineNumber)); } - + public string GetLineIndent (DocumentLine segment) { if (segment == null) return ""; return segment.GetIndentation (this); } - + public DocumentLine GetLine (int lineNumber) { if (lineNumber < DocumentLocation.MinLine) return null; - + return this.Get (lineNumber); } @@ -751,12 +754,12 @@ namespace Mono.TextEditor return changes; } } - + public object Tag { get; set; } - + protected UndoOperation() { // The type is in the consolidated editor implementation assembly @@ -785,7 +788,7 @@ namespace Mono.TextEditor if (fireEvent) OnUndoDone (); } - + public virtual void Redo (TextDocument doc, bool fireEvent = true) {
if (this.Changes.Count > 0) {
@@ -800,14 +803,14 @@ namespace Mono.TextEditor if (fireEvent) OnRedoDone (); } - + protected virtual void OnUndoDone () { if (UndoDone != null) UndoDone (this, EventArgs.Empty); } public event EventHandler UndoDone; - + protected virtual void OnRedoDone () { if (RedoDone != null) @@ -815,7 +818,7 @@ namespace Mono.TextEditor } public event EventHandler RedoDone; } - + class AtomicUndoOperation : UndoOperation { OperationType operationType; @@ -826,7 +829,7 @@ namespace Mono.TextEditor return operationType; } } - + public List<UndoOperation> Operations { get { return operations; @@ -843,18 +846,18 @@ namespace Mono.TextEditor { this.operationType = operationType; } - + public void Insert (int index, UndoOperation operation) { operations.Insert (index, operation); } - + public void Add (UndoOperation operation) { operations.Add (operation); } - + public override void Undo (TextDocument doc, bool fireEvent = true) { doc.BeginAtomicUndo (operationType); @@ -869,7 +872,7 @@ namespace Mono.TextEditor if (fireEvent) OnUndoDone (); } - + public override void Redo (TextDocument doc, bool fireEvent = true) { doc.BeginAtomicUndo (operationType); @@ -885,11 +888,11 @@ namespace Mono.TextEditor OnRedoDone (); } } - + class KeyboardStackUndo : AtomicUndoOperation { bool isClosed = false; - + public bool IsClosed { get { return isClosed; @@ -905,7 +908,7 @@ namespace Mono.TextEditor } } } - + bool isInUndo = false; Stack<UndoOperation> undoStack = new Stack<UndoOperation> (); Stack<UndoOperation> redoStack = new Stack<UndoOperation> (); @@ -942,7 +945,7 @@ namespace Mono.TextEditor return this.undoStack.Count > 0 || currentAtomicOperation != null; } } - + UndoOperation[] savePoint = null; public bool IsDirty { get { @@ -960,7 +963,7 @@ namespace Mono.TextEditor return false; } } - + public enum LineState { Unchanged, Dirty, @@ -977,13 +980,13 @@ namespace Mono.TextEditor diffTracker = value; } } - + public LineState GetLineState (DocumentLine line) { return diffTracker.GetLineState (line); } - - + + /// <summary> /// Marks the document not dirty at this point (should be called after save). /// </summary> @@ -995,7 +998,7 @@ namespace Mono.TextEditor this.CommitUpdateAll (); DiffTracker.SetBaseDocument (CreateDocumentSnapshot ()); } - + public void OptimizeTypedUndo () { if (undoStack.Count == 0) @@ -1030,12 +1033,12 @@ namespace Mono.TextEditor undoStack.Push (keyUndo); } } - + public int GetCurrentUndoDepth () { return undoStack.Count; } - + public void StackUndoToDepth (int depth) { if (undoStack.Count == depth) @@ -1046,7 +1049,7 @@ namespace Mono.TextEditor } undoStack.Push (atomicUndo); } - + public void MergeUndoOperations (int number) { number = System.Math.Min (number, undoStack.Count); @@ -1056,7 +1059,7 @@ namespace Mono.TextEditor } undoStack.Push (atomicUndo); } - + public void Undo () { if (undoStack.Count <= 0) @@ -1088,7 +1091,7 @@ namespace Mono.TextEditor } public event EventHandler<UndoOperationEventArgs> Undone; - + internal protected virtual void OnBeforeUndoOperation (EventArgs e) { var handler = this.BeforeUndoOperation; @@ -1103,7 +1106,7 @@ namespace Mono.TextEditor return this.redoStack.Count > 0; } } - + public void Redo () { if (redoStack.Count <= 0) @@ -1115,16 +1118,16 @@ namespace Mono.TextEditor isInUndo = false; OnRedone (new UndoOperationEventArgs (operation)); } - + internal protected virtual void OnRedone (UndoOperationEventArgs e) { EventHandler<UndoOperationEventArgs> handler = this.Redone; if (handler != null) handler (this, e); } - + public event EventHandler<UndoOperationEventArgs> Redone; - + Stack<OperationType> currentAtomicUndoOperationType = new Stack<OperationType> (); int atomicUndoLevel; @@ -1139,11 +1142,11 @@ namespace Mono.TextEditor return currentAtomicUndoOperationType.Count > 0 ? currentAtomicUndoOperationType.Peek () : OperationType.Undefined; } } - + class UndoGroup : IDisposable { TextDocument doc; - + public UndoGroup (TextDocument doc, OperationType operationType) { if (doc == null) @@ -1160,7 +1163,7 @@ namespace Mono.TextEditor } } } - + public IDisposable OpenUndoGroup() { return OpenUndoGroup(OperationType.Undefined); @@ -1175,7 +1178,7 @@ namespace Mono.TextEditor { currentAtomicUndoOperationType.Push (operationType); if (currentAtomicOperation == null) { - Debug.Assert (atomicUndoLevel == 0); + Debug.Assert (atomicUndoLevel == 0); currentAtomicOperation = new AtomicUndoOperation (operationType); OnBeginUndo (); } @@ -1187,8 +1190,8 @@ namespace Mono.TextEditor if (atomicUndoLevel <= 0) throw new InvalidOperationException ("There is no atomic undo operation running."); atomicUndoLevel--; - Debug.Assert (atomicUndoLevel >= 0); - + Debug.Assert (atomicUndoLevel >= 0); + if (atomicUndoLevel == 0 && currentAtomicOperation != null) { var cuao = currentAtomicOperation; currentAtomicOperation = null; @@ -1207,19 +1210,19 @@ namespace Mono.TextEditor } currentAtomicUndoOperationType.Pop (); } - + protected virtual void OnBeginUndo () { - if (BeginUndo != null) + if (BeginUndo != null) BeginUndo (this, EventArgs.Empty); } - + public void ClearUndoBuffer () { undoStack.Clear (); redoStack.Clear (); } - + [Serializable] public sealed class UndoOperationEventArgs : EventArgs { @@ -1229,41 +1232,41 @@ namespace Mono.TextEditor { this.Operation = operation; } - + } - + protected virtual void OnEndUndo (UndoOperationEventArgs e) { EventHandler<UndoOperationEventArgs> handler = this.EndUndo; if (handler != null) handler (this, e); } - + public event EventHandler BeginUndo; public event EventHandler<UndoOperationEventArgs> EndUndo; #endregion - + #region Folding - + SegmentTree<FoldSegment> foldSegmentTree = new SegmentTree<FoldSegment> (); - + public bool IgnoreFoldings { get; set; } - + public bool HasFoldSegments { get { return FoldSegments.Any (); } } - + public IEnumerable<FoldSegment> FoldSegments { get { return foldSegmentTree.Segments; } } - + readonly object syncObject = new object(); CancellationTokenSource foldSegmentSrc; @@ -1309,17 +1312,13 @@ namespace Mono.TextEditor }, token); } } - + void RemoveFolding (FoldSegment folding) { folding.isAttached = false; - if (folding.isFolded) { - foldedSegments.Remove (folding); - CommitUpdateAll (); - } foldSegmentTree.Remove (folding); } - + /// <summary> /// Updates the fold segments in a background worker thread. Don't call this method outside of a background worker. /// Use UpdateFoldSegments instead. @@ -1328,7 +1327,7 @@ namespace Mono.TextEditor { var oldSegments = new List<FoldSegment> (FoldSegments); int oldIndex = 0; - bool foldedSegmentAdded = false; + bool foldedSegmentAdded = false, foldedFoldingRemoved = false; var newSegments = segments.ToList (); newSegments.Sort (); var newFoldedSegments = new HashSet<FoldSegment> (); @@ -1341,6 +1340,7 @@ namespace Mono.TextEditor int offset = newFoldSegment.Offset; while (oldIndex < oldSegments.Count && offset > oldSegments [oldIndex].Offset) { RemoveFolding (oldSegments [oldIndex]); + foldedFoldingRemoved |= oldSegments [oldIndex].IsCollapsed; oldIndex++; }
if (oldIndex < oldSegments.Count && offset == oldSegments [oldIndex].Offset) { @@ -1374,57 +1374,43 @@ namespace Mono.TextEditor return null; } RemoveFolding (oldSegments [oldIndex]); + foldedFoldingRemoved |= oldSegments [oldIndex].IsCollapsed; oldIndex++; } bool countChanged = foldedSegments.Count != newFoldedSegments.Count; - update = foldedSegmentAdded || countChanged; + update = foldedSegmentAdded || countChanged || foldedFoldingRemoved; return newFoldedSegments; } - - public void WaitForFoldUpdateFinished () - { - if (foldSegmentTask != null) { - try { - foldSegmentTask.Wait (5000); - } catch (AggregateException e) { - e.Flatten ().Handle (x => x is OperationCanceledException); - } catch (OperationCanceledException) { - - } - foldSegmentTask = null; - } - } - + internal void InterruptFoldWorker () { if (foldSegmentSrc == null) return; foldSegmentSrc.Cancel (); - WaitForFoldUpdateFinished (); foldSegmentSrc = null; } - + public void ClearFoldSegments () { InterruptFoldWorker (); foldSegmentTree = new SegmentTree<FoldSegment> (); - foldSegmentTree.tree.NodeRemoved += HandleFoldSegmentTreetreeNodeRemoved; + foldSegmentTree.tree.NodeRemoved += HandleFoldSegmentTreetreeNodeRemoved; foldedSegments.Clear (); InformFoldTreeUpdated (); } - + public IEnumerable<FoldSegment> GetFoldingsFromOffset (int offset) { if (offset < 0 || offset >= Length) return new FoldSegment[0]; return foldSegmentTree.GetSegmentsAt (offset); } - + public IEnumerable<FoldSegment> GetFoldingContaining (int lineNumber) { return GetFoldingContaining(this.GetLine (lineNumber)); } - + public IEnumerable<FoldSegment> GetFoldingContaining (DocumentLine line) { if (line == null) @@ -1441,7 +1427,7 @@ namespace Mono.TextEditor { return GetStartFoldings (this.GetLine (lineNumber)); } - + public IEnumerable<FoldSegment> GetStartFoldings (DocumentLine line) { if (line == null) @@ -1461,7 +1447,7 @@ namespace Mono.TextEditor { return GetStartFoldings (this.GetLine (lineNumber)); } - + public IEnumerable<FoldSegment> GetEndFoldings (DocumentLine line) {
var lineOffset = line.Offset;
@@ -1480,7 +1466,7 @@ namespace Mono.TextEditor { return segment.GetEndLine (this).LineNumber - segment.GetStartLine (this).LineNumber; } - + public void EnsureOffsetIsUnfolded (int offset) { foreach (FoldSegment fold in GetFoldingsFromOffset (offset).Where (f => f.IsCollapsed && f.Offset < offset && offset < f.EndOffset)) { @@ -1506,7 +1492,7 @@ namespace Mono.TextEditor handler (this, EventArgs.Empty); } public event EventHandler FoldTreeUpdated; - + HashSet<FoldSegment> foldedSegments = new HashSet<FoldSegment> (); public IEnumerable<FoldSegment> FoldedSegments { @@ -1548,7 +1534,7 @@ namespace Mono.TextEditor handler (this, e); } - + List<TextLineMarker> extendingTextMarkers = new List<TextLineMarker> (); public IEnumerable<DocumentLine> LinesWithExtendingTextMarkers { get { @@ -1556,15 +1542,15 @@ namespace Mono.TextEditor var line = marker.LineSegment; if (line != null) yield return line; - } + } } } - + public void AddMarker (int lineNumber, TextLineMarker marker) { AddMarker (this.GetLine (lineNumber), marker); } - + public void AddMarker (DocumentLine line, TextLineMarker marker) { AddMarker (line, marker, true); @@ -1628,7 +1614,7 @@ namespace Mono.TextEditor if (line == null) return; foreach (var marker in GetTextSegmentMarkersAt (line).OfType<DocumentLineTextSegmentMarker> ()) - RemoveMarker (marker); + RemoveMarker (marker); } public void AddMarker (DocumentLine line, TextLineMarker marker, bool commitUpdate, int idx = -1) @@ -1647,19 +1633,19 @@ namespace Mono.TextEditor if (commitUpdate) this.CommitLineUpdate (line); } - + static int CompareMarkers (TextLineMarker left, TextLineMarker right) { if (left.LineSegment == null || right.LineSegment == null) return 0; return left.LineSegment.Offset.CompareTo (right.LineSegment.Offset); } - + public void RemoveMarker (TextLineMarker marker) { RemoveMarker (marker, true); } - + public void RemoveMarker (TextLineMarker marker, bool updateLine) { if (marker == null) @@ -1688,17 +1674,17 @@ namespace Mono.TextEditor if (updateLine) this.CommitLineUpdate (line); } - + public void RemoveMarker (int lineNumber, Type type) { RemoveMarker (this.GetLine (lineNumber), type); } - + public void RemoveMarker (DocumentLine line, Type type) { RemoveMarker (line, type, true); } - + public void RemoveMarker (DocumentLine line, Type type, bool updateLine) { if (line == null || type == null) @@ -1772,7 +1758,7 @@ namespace Mono.TextEditor textMarkerCacheOffset = offset; return textMarkerSegmentCache = textSegmentMarkerTree.GetSegmentsAt (offset).ToList (); } - + public void AddMarker (TextSegmentMarker marker) { @@ -1807,16 +1793,16 @@ namespace Mono.TextEditor { return new TextSegment (0, Length).Contains (offset); } - + public bool Contains (ISegment segment) { return new TextSegment (0, Length).Contains (segment); } - - + + #region Update logic List<DocumentUpdateRequest> updateRequests = new List<DocumentUpdateRequest> (); - + public IEnumerable<DocumentUpdateRequest> UpdateRequests { get { return updateRequests; @@ -1836,7 +1822,7 @@ namespace Mono.TextEditor updateRequests.Add (request); } } - + public void CommitDocumentUpdate () { lock (syncObject) { @@ -1845,13 +1831,13 @@ namespace Mono.TextEditor updateRequests.Clear (); } } - + public void CommitLineUpdate (int line) { RequestUpdate (new LineUpdate (line)); CommitDocumentUpdate (); } - + public void CommitLineUpdate (DocumentLine line) { CommitLineUpdate (line.LineNumber); @@ -1889,12 +1875,12 @@ namespace Mono.TextEditor #region Helper functions public const string openBrackets = "([{<"; public const string closingBrackets = ")]}>"; - + public static bool IsBracket (char ch) { return (openBrackets + closingBrackets).IndexOf (ch) >= 0; } - + public static bool IsWordSeparator (char ch) { return !(char.IsLetterOrDigit (ch) || ch == '_'); @@ -1905,12 +1891,12 @@ namespace Mono.TextEditor return (offset == 0 || IsWordSeparator (GetCharAt (offset - 1))) && (offset + length == Length || IsWordSeparator (GetCharAt (offset + length))); } - + public bool IsEmptyLine (DocumentLine line) { for (int i = 0; i < line.Length; i++) { char ch = GetCharAt (line.Offset + i); - if (!Char.IsWhiteSpace (ch)) + if (!Char.IsWhiteSpace (ch)) return false; } return true; @@ -1924,7 +1910,7 @@ namespace Mono.TextEditor IdentifierPart } - + public static CharacterClass GetCharacterClass (char ch) @@ -1937,7 +1923,7 @@ namespace Mono.TextEditor return CharacterClass.Unknown; } - + public static void RemoveTrailingWhitespaces (TextEditorData data, DocumentLine line) { if (line == null) @@ -1950,7 +1936,7 @@ namespace Mono.TextEditor break; } } - + if (whitespaces > 0) { var removeOffset = line.Offset + line.Length - whitespaces; data.Remove (removeOffset, whitespaces); @@ -1963,7 +1949,7 @@ namespace Mono.TextEditor return isInUndo; } } - + #region Diff @@ -1982,7 +1968,7 @@ namespace Mono.TextEditor } return result; } - + public IEnumerable<Hunk> Diff (TextDocument changedDocument, bool includeEol = true) { var codeDictionary = new Dictionary<string, int> (); @@ -1992,18 +1978,18 @@ namespace Mono.TextEditor } #endregion - -#region ContentLoaded + +#region ContentLoaded // The problem: Action to perform on a newly opened text editor, but content didn't get loaded because autosave file exist. // At this point the document is open, but the content didn't yet have loaded - therefore the action on the conent can't be perfomed. - // Solution: Perform the action after the user did choose load autosave or not. + // Solution: Perform the action after the user did choose load autosave or not. // This is done by the RunWhenLoaded method. Text editors should call the InformLoadComplete () when the content has successfully been loaded // at that point the outstanding actions are run. bool isLoaded; List<Action> loadedActions = new List<Action> (); List<Action> realizedActions = new List<Action> (); - + /// <summary> /// Gets a value indicating whether this instance is loaded. /// </summary> @@ -2018,7 +2004,7 @@ namespace Mono.TextEditor get; private set; } - + /// <summary> /// Informs the document when the content is loaded. All outstanding actions are executed. /// </summary> @@ -2040,7 +2026,7 @@ namespace Mono.TextEditor realizedActions.ForEach (act => act ()); realizedActions = null; } - + /// <summary> /// Performs an action when the content is loaded. /// </summary> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/AssemblyUtilities.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/AssemblyUtilities.cs index d4eb34291f..bcb83e52a9 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/AssemblyUtilities.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/AssemblyUtilities.cs @@ -38,44 +38,65 @@ using MonoDevelop.Core.AddIns; using MonoDevelop.Core.Serialization; using Mono.Addins; using Mono.PkgConfig; +using System.Reflection.PortableExecutable; +using System.Reflection.Metadata; namespace MonoDevelop.Core.Assemblies { public static class AssemblyUtilities { - static bool Is64BitPE (Mono.Cecil.TargetArchitecture machine) + static bool Is64BitPE (Machine machine) { - return machine == Mono.Cecil.TargetArchitecture.AMD64 || - machine == Mono.Cecil.TargetArchitecture.IA64 || - machine == Mono.Cecil.TargetArchitecture.ARM64; + return machine == Machine.Amd64 || machine == Machine.Arm64 || machine == Machine.IA64 || machine == Machine.Alpha64; } - public static ProcessExecutionArchitecture GetProcessExecutionArchitectureForAssembly (string assemblyPath) + static bool TryReadPEHeaders (string assemblyPath, out PortableExecutableKinds peKind, out Machine machine) { - if (string.IsNullOrEmpty (assemblyPath)) - throw new ArgumentNullException (nameof (assemblyPath)); + peKind = default; + machine = default; try { - Mono.Cecil.ModuleAttributes peKind; - Mono.Cecil.TargetArchitecture machine; if (!File.Exists (assemblyPath)) - return ProcessExecutionArchitecture.Unspecified; - try { - using (var adef = Mono.Cecil.AssemblyDefinition.ReadAssembly (assemblyPath)) { - peKind = adef.MainModule.Attributes; - machine = adef.MainModule.Architecture; - } - } catch { - peKind = Mono.Cecil.ModuleAttributes.ILOnly; - machine = Mono.Cecil.TargetArchitecture.I386; + return false; + + using (var reader = new PEReader (File.OpenRead (assemblyPath))) { + var peHeaders = reader.PEHeaders; + + var corFlags = peHeaders.CorHeader.Flags; + if ((corFlags & CorFlags.ILOnly) != 0) + peKind |= PortableExecutableKinds.ILOnly; + + if ((corFlags & CorFlags.Prefers32Bit) != 0) + peKind |= PortableExecutableKinds.Preferred32Bit; + else if ((corFlags & CorFlags.Requires32Bit) != 0) + peKind |= PortableExecutableKinds.Required32Bit; + + if (peHeaders.PEHeader.Magic == PEMagic.PE32Plus) + peKind |= PortableExecutableKinds.PE32Plus; + + machine = peHeaders.CoffHeader.Machine; } - if ((peKind & (Mono.Cecil.ModuleAttributes.Required32Bit | Mono.Cecil.ModuleAttributes.Preferred32Bit)) != 0) - return ProcessExecutionArchitecture.X86; - if (Is64BitPE (machine)) - return ProcessExecutionArchitecture.X64; + + return true; } catch (Exception e) { LoggingService.LogError ("Error while determining 64/32 bit assembly.", e); + return false; } + } + + public static ProcessExecutionArchitecture GetProcessExecutionArchitectureForAssembly (string assemblyPath) + { + if (string.IsNullOrEmpty (assemblyPath)) + throw new ArgumentNullException (nameof (assemblyPath)); + + if (TryReadPEHeaders (assemblyPath, out var peKind, out var machine)) { + if ((peKind & (PortableExecutableKinds.Preferred32Bit | PortableExecutableKinds.Required32Bit)) != 0) + return ProcessExecutionArchitecture.X86; + + if ((peKind & PortableExecutableKinds.PE32Plus) != 0 || Is64BitPE (machine)) + return ProcessExecutionArchitecture.X64; + } + return ProcessExecutionArchitecture.Unspecified; } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/SystemAssemblyService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/SystemAssemblyService.cs index 4ec62572f9..95290ab3ed 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/SystemAssemblyService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/SystemAssemblyService.cs @@ -30,12 +30,14 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Reflection; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; using System.Threading; using Mono.Addins; -using Mono.Cecil; using MonoDevelop.Core.AddIns; namespace MonoDevelop.Core.Assemblies @@ -369,29 +371,50 @@ namespace MonoDevelop.Core.Assemblies { if (!File.Exists (file)) return TargetFrameworkMoniker.UNKNOWN; - AssemblyDefinition assembly = null; + try { - assembly = AssemblyDefinition.ReadAssembly (file); - var att = assembly.CustomAttributes.FirstOrDefault (a => - a.AttributeType.FullName == "System.Runtime.Versioning.TargetFrameworkAttribute" - ); - if (att != null) { - if (att.ConstructorArguments.Count == 1) { - var v = att.ConstructorArguments[0].Value as string; - TargetFrameworkMoniker m; - if (v != null && TargetFrameworkMoniker.TryParse (v, out m)) { + using (var reader = new PEReader (File.OpenRead (file))) { + var mr = reader.GetMetadataReader (); + + foreach (var customAttributeHandle in mr.GetAssemblyDefinition ().GetCustomAttributes ()) { + var customAttribute = mr.GetCustomAttribute (customAttributeHandle); + + var ctor = mr.GetMemberReference ((MemberReferenceHandle)customAttribute.Constructor); + var attrType = mr.GetTypeReference ((TypeReferenceHandle)ctor.Parent); + + var ns = mr.GetString (attrType.Namespace); + if (ns != "System.Runtime.Versioning") + continue; + + var typeName = mr.GetString (attrType.Name); + if (typeName != "TargetFrameworkAttribute") + continue; + + var provider = new StringParameterValueTypeProvider (mr, customAttribute.Value); + var signature = ctor.DecodeMethodSignature (provider, null); + var parameterTypes = signature.ParameterTypes; + if (parameterTypes.Length != 1) + continue; + + var value = parameterTypes [0]; + if (value != null && TargetFrameworkMoniker.TryParse (value, out var m)) { return m; } + LoggingService.LogError ("Invalid TargetFrameworkAttribute in assembly {0} - {1}", file, value); } - LoggingService.LogError ("Invalid TargetFrameworkAttribute in assembly {0}", file); - } - if (tr != null) { - foreach (var r in assembly.MainModule.AssemblyReferences) { - if (r.Name == "mscorlib") { + + if (tr != null) { + foreach (var assemblyReferenceHandle in mr.AssemblyReferences) { + var assemblyReference = mr.GetAssemblyReference (assemblyReferenceHandle); + + var name = mr.GetString (assemblyReference.Name); + if (name != "mscorlib") + continue; + TargetFramework compatibleFramework = null; // If there are several frameworks that can run the file, pick one that is installed foreach (TargetFramework tf in GetKnownFrameworks ()) { - if (tf.GetCorlibVersion () == r.Version.ToString ()) { + if (tf.GetCorlibVersion () == assemblyReference.Version.ToString ()) { compatibleFramework = tf; if (tr.IsInstalled (tf)) return tf.Id; @@ -405,125 +428,66 @@ namespace MonoDevelop.Core.Assemblies } } catch (Exception ex) { LoggingService.LogError ("Error determining target framework for assembly {0}: {1}", file, ex); - return TargetFrameworkMoniker.UNKNOWN; - } finally { - assembly?.Dispose (); } - LoggingService.LogError ("Failed to determine target framework for assembly {0}", file); return TargetFrameworkMoniker.UNKNOWN; } /// <summary> /// Simply get all assembly reference names from an assembly given it's file name. /// </summary> - public static IEnumerable<string> GetAssemblyReferences (string fileName) - {
- AssemblyDefinition assembly = null;
- try {
- try {
- assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (fileName);
- } catch {
- return Enumerable.Empty<string> ();
- }
- return assembly.MainModule.AssemblyReferences.Select (x => x.Name);
- } finally {
- assembly?.Dispose ();
- }
- } + public static ImmutableArray<string> GetAssemblyReferences (string fileName) + { + try { + using (var reader = new PEReader (File.OpenRead (fileName))) { + var mr = reader.GetMetadataReader (); + var assemblyReferences = mr.AssemblyReferences; - static Dictionary<string, bool> referenceDict = new Dictionary<string, bool> (); + var builder = ImmutableArray.CreateBuilder<string> (assemblyReferences.Count); - static bool ContainsReferenceToSystemRuntimeInternal (string fileName) - { - bool result;
- if (referenceDict.TryGetValue (fileName, out result))
- return result;
-
- //const int cacheLimit = 4096;
- //if (referenceDict.Count > cacheLimit)
- // referenceDict = ImmutableDictionary<string, bool>.Empty
-
- AssemblyDefinition assembly = null;
- try {
- try {
- assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (fileName);
- } catch {
- return false;
- }
- foreach (var r in assembly.MainModule.AssemblyReferences) {
- // Don't compare the version number since it may change depending on the version of .net standard
- if (r.Name.Equals ("System.Runtime")) {
- referenceDict [fileName] = true; ;
- return true;
- }
- }
- } finally {
- assembly?.Dispose ();
- }
- referenceDict [fileName] = false;
- return false; + foreach (var assemblyReferenceHandle in assemblyReferences) { + var assemblyReference = mr.GetAssemblyReference (assemblyReferenceHandle); + builder.Add (mr.GetString (assemblyReference.Name)); + } + + return builder.MoveToImmutable(); + } + } catch { + return ImmutableArray<string>.Empty; + } } static Dictionary<string, bool> facadeReferenceDict = new Dictionary<string, bool> (); static bool RequiresFacadeAssembliesInternal (string fileName) { - bool result; - if (facadeReferenceDict.TryGetValue (fileName, out result)) + if (facadeReferenceDict.TryGetValue (fileName, out var result)) return result; - AssemblyDefinition assembly = null; try { - try { - assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (fileName); - } catch { - return false; - } - foreach (var r in assembly.MainModule.AssemblyReferences) { - // Don't compare the version number since it may change depending on the version of .net standard - if (r.Name.Equals ("System.Runtime") || r.Name.Equals ("netstandard")) { - facadeReferenceDict [fileName] = true; ; - return true; + using (var reader = new PEReader (File.OpenRead (fileName))) { + var mr = reader.GetMetadataReader (); + + foreach (var assemblyReferenceHandle in mr.AssemblyReferences) { + var assemblyReference = mr.GetAssemblyReference (assemblyReferenceHandle); + var name = mr.GetString (assemblyReference.Name); + + // Don't compare the version number since it may change depending on the version of .net standard + if (name.Equals ("System.Runtime") || name.Equals ("netstandard")) { + facadeReferenceDict [fileName] = true; + return true; + } } } - } finally { - assembly?.Dispose (); + } catch { + return false; } + facadeReferenceDict [fileName] = false; return false; } - static object referenceLock = new object (); - - [Obsolete ("Use RequiresFacadeAssemblies (string fileName)")] - public static bool ContainsReferenceToSystemRuntime (string fileName) - { - lock (referenceLock) { - return ContainsReferenceToSystemRuntimeInternal (fileName); - } - } - - static SemaphoreSlim referenceLockAsync = new SemaphoreSlim (1, 1); - - [Obsolete ("Use RequiresFacadeAssembliesAsync (string fileName)")] - public static async System.Threading.Tasks.Task<bool> ContainsReferenceToSystemRuntimeAsync (string filename) - { - try { - await referenceLockAsync.WaitAsync ().ConfigureAwait (false); - return ContainsReferenceToSystemRuntimeInternal (filename); - } finally { - referenceLockAsync.Release (); - } - } - - internal static bool RequiresFacadeAssemblies (string fileName) - { - lock (referenceLock) { - return RequiresFacadeAssembliesInternal (fileName); - } - } - - internal static async System.Threading.Tasks.Task<bool> RequiresFacadeAssembliesAsync (string filename) + static readonly SemaphoreSlim referenceLockAsync = new SemaphoreSlim (1, 1); + public static async System.Threading.Tasks.Task<bool> RequiresFacadeAssembliesAsync (string filename) { try { await referenceLockAsync.WaitAsync ().ConfigureAwait (false); @@ -556,25 +520,51 @@ namespace MonoDevelop.Core.Assemblies /// Simply get all assembly manifest resources from an assembly given it's file name. /// </summary> public static IEnumerable<ManifestResource> GetAssemblyManifestResources (string fileName) - {
- AssemblyDefinition assembly = null;
- try {
- try {
- assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly (fileName);
- } catch {
- yield break;
- }
- foreach (var r in assembly.MainModule.Resources) {
- if (r.ResourceType == ResourceType.Embedded) {
- var er = (EmbeddedResource)r;
-
- // Explicitly create a capture and query it here so the stream isn't queried after the module is disposed.
- var rs = er.GetResourceStream ();
- yield return new ManifestResource (er.Name, () => rs);
- }
- }
- } finally {
- assembly?.Dispose ();
+ { + using (var reader = new PEReader (File.OpenRead (fileName))) { + var mr = reader.GetMetadataReader (); + + var headers = reader.PEHeaders; + var resources = headers.CorHeader.ResourcesDirectory; + var sectionData = reader.GetSectionData (resources.RelativeVirtualAddress); + if (sectionData.Length == 0) + return Array.Empty<ManifestResource> (); // RVA could not be found in any section + + var sectionReader = sectionData.GetReader (); + var manifestResources = mr.ManifestResources; + var result = new List<ManifestResource> (manifestResources.Count); + + foreach (var manifestResourceHandle in manifestResources) { + var manifestResource = mr.GetManifestResource (manifestResourceHandle); + + // This means the type is Embedded. + var isEmbeddedResource = manifestResource.Implementation.IsNil; + if (!isEmbeddedResource) + continue; + + int offset = (int)manifestResource.Offset; + sectionReader.Offset += offset; + try { + int length = sectionReader.ReadInt32 (); + if ((uint)length > sectionReader.RemainingBytes) { + LoggingService.LogError ("Resource stream invalid length {0}", length.ToString ()); + continue; + } + + var name = mr.GetString (manifestResource.Name); + unsafe { + using (var unmanagedStream = new UnmanagedMemoryStream (sectionReader.CurrentPointer, length, length, FileAccess.Read)) { + var memoryStream = new MemoryStream (length); + unmanagedStream.CopyTo (memoryStream); + memoryStream.Position = 0; + result.Add (new ManifestResource (name, () => memoryStream)); + } + } + } finally { + sectionReader.Offset -= offset; + } + } + return result; } } @@ -583,5 +573,37 @@ namespace MonoDevelop.Core.Assemblies { return Runtime.LoadAssemblyFrom (asmPath); } + + sealed class StringParameterValueTypeProvider : ISignatureTypeProvider<string, object> + { + readonly BlobReader valueReader; + + public StringParameterValueTypeProvider (MetadataReader reader, BlobHandle value) + { + valueReader = reader.GetBlobReader (value); + + var prolog = valueReader.ReadUInt16 (); + if (prolog != 1) + throw new BadImageFormatException ("Invalid custom attribute prolog."); + } + + public string GetPrimitiveType (PrimitiveTypeCode typeCode) => typeCode != PrimitiveTypeCode.String ? "" : valueReader.ReadSerializedString (); + public string GetArrayType (string elementType, ArrayShape shape) => ""; + public string GetByReferenceType (string elementType) => ""; + public string GetFunctionPointerType (MethodSignature<string> signature) => ""; + public string GetGenericInstance (string genericType, ImmutableArray<string> typestrings) => ""; + public string GetGenericInstantiation (string genericType, ImmutableArray<string> typeArguments) { throw new NotImplementedException (); } + public string GetGenericMethodParameter (int index) => ""; + public string GetGenericMethodParameter (object genericContext, int index) { throw new NotImplementedException (); } + public string GetGenericTypeParameter (int index) => ""; + public string GetGenericTypeParameter (object genericContext, int index) { throw new NotImplementedException (); } + public string GetModifiedType (string modifier, string unmodifiedType, bool isRequired) => ""; + public string GetPinnedType (string elementType) => ""; + public string GetPointerType (string elementType) => ""; + public string GetSZArrayType (string elementType) => ""; + public string GetTypeFromDefinition (MetadataReader reader, TypeDefinitionHandle handle, byte rawTypeKind) => ""; + public string GetTypeFromReference (MetadataReader reader, TypeReferenceHandle handle, byte rawTypeKind) => ""; + public string GetTypeFromSpecification (MetadataReader reader, object genericContext, TypeSpecificationHandle handle, byte rawTypeKind) => ""; + } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs index aa974f8028..2d804cf738 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/RemoteProcessConnection.cs @@ -156,15 +156,6 @@ namespace MonoDevelop.Core.Execution } } - [Obsolete ("Use Disconnect()")] - public void Disconnect (bool waitUntilDone) - { - if (waitUntilDone) - Disconnect ().Wait (TimeSpan.FromSeconds (7)); - else - Disconnect ().Ignore (); - } - public async Task Disconnect () { StopPinger (); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj index 6f4a10936f..aa35162478 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj @@ -89,7 +89,7 @@ <PackageReference Include="Microsoft.CodeAnalysis.ExternalAccess.MonoDevelop" Version="$(NuGetVersionRoslyn)" PrivateAssets="runtime" /> <PackageReference Include="Microsoft.VisualStudio.CodingConventions" Version="1.1.20180503.2" PrivateAssets="runtime" /> <PackageReference Include="Microsoft.VisualStudio.Composition" Version="$(NuGetVersionVSComposition)" ExcludeAssets="all" /> - <!-- Need the net45 version, see https://github.com/mono/mono/issues/12461 --> + <!-- Need the net45 version, see https://github.com/mono/mono/issues/12461 --> <Reference Include="$(NuGetPackageRoot)microsoft.visualstudio.composition/$(NuGetVersionVSComposition)/lib/net45/Microsoft.VisualStudio.Composition.dll"> <Private>true</Private> </Reference> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs index e35886fdfe..57f4a1f42b 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildFileFormat.cs @@ -37,7 +37,7 @@ using System.Linq; namespace MonoDevelop.Projects.MSBuild { - public abstract class MSBuildFileFormat + public abstract class MSBuildFileFormat : IComparable<MSBuildFileFormat>, IEquatable<MSBuildFileFormat> { readonly SlnFileFormat slnFileFormat; @@ -51,9 +51,6 @@ namespace MonoDevelop.Projects.MSBuild public static readonly MSBuildFileFormat VS2010 = new MSBuildFileFormatVS10 (); public static readonly MSBuildFileFormat VS2012 = new MSBuildFileFormatVS12 (); - [Obsolete("This is the same as VS2012")] - public static readonly MSBuildFileFormat VS2017 = VS2012; - public static IEnumerable<MSBuildFileFormat> GetSupportedFormats () { yield return VS2012; @@ -68,12 +65,6 @@ namespace MonoDevelop.Projects.MSBuild } public static MSBuildFileFormat DefaultFormat => VS2012; - - [Obsolete ("Use ProductDescription or ID")] - public string Name => "MSBuild"; - - [Obsolete] - public abstract Version Version { get; } internal SlnFileFormat SlnFileFormat { get { return slnFileFormat; } @@ -229,17 +220,41 @@ namespace MonoDevelop.Projects.MSBuild } return string.Empty; } - + public abstract string Id { get; } + + #region IComparable<MSBuildFileFormat> implementation and overloads + + public override bool Equals (object obj) => obj is MSBuildFileFormat other && Equals (other); + public bool Equals (MSBuildFileFormat other) => other != null && Id == other.Id; + public override int GetHashCode () => Id.GetHashCode (); + + public int CompareTo (MSBuildFileFormat other) => Version.Parse (SlnVersion).CompareTo (Version.Parse (other.SlnVersion)); + + public static bool operator == (MSBuildFileFormat a, MSBuildFileFormat b) + { + if (ReferenceEquals (a, b)) + return true; + + if (a is null) + return b is null; + + return a.Equals (b); + } + + public static bool operator != (MSBuildFileFormat a, MSBuildFileFormat b) => !(a == b); + public static bool operator < (MSBuildFileFormat a, MSBuildFileFormat b) => a.CompareTo (b) < 0; + public static bool operator > (MSBuildFileFormat a, MSBuildFileFormat b) => a.CompareTo (b) > 0; + public static bool operator <= (MSBuildFileFormat a, MSBuildFileFormat b) => a.CompareTo (b) <= 0; + public static bool operator >= (MSBuildFileFormat a, MSBuildFileFormat b) => a.CompareTo (b) >= 0; + + #endregion } class MSBuildFileFormatVS05 : MSBuildFileFormat { public override string Id => "MSBuild05"; - [Obsolete("Unused")] - public override Version Version => new Version ("2005"); - public override string DefaultProductVersion => "8.0.50727"; public override string DefaultToolsVersion => "2.0"; public override string DefaultSchemaVersion => "2.0"; @@ -255,9 +270,6 @@ namespace MonoDevelop.Projects.MSBuild { public override string Id => "MSBuild08"; - [Obsolete ("Unused")] - public override Version Version => new Version ("2008"); - public override string DefaultProductVersion => "9.0.21022"; public override string DefaultToolsVersion => "3.5"; public override string DefaultSchemaVersion => "2.0"; @@ -279,9 +291,6 @@ namespace MonoDevelop.Projects.MSBuild { public override string Id => "MSBuild10"; - [Obsolete ("Unused")] - public override Version Version => new Version ("2010"); - public override string DefaultProductVersion => "8.0.30703"; public override string DefaultSchemaVersion => "2.0"; public override string DefaultToolsVersion => "4.0"; @@ -294,9 +303,6 @@ namespace MonoDevelop.Projects.MSBuild { public override string Id => "MSBuild12"; - [Obsolete ("Unused")] - public override Version Version => new Version ("2012"); - // This is mostly irrelevant, the builder always uses the latest // tools version. It's only used for new projects created with // the old project template engine. diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProcessService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProcessService.cs index 2062fae1a8..40cf37edf2 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProcessService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProcessService.cs @@ -63,9 +63,19 @@ namespace MonoDevelop.Projects.MSBuild return MSBuildProjectService.GetMSBuildBinPath (Runtime.SystemAssemblyService.CurrentRuntime); } + static FilePath GetMSBuildBinDirectory (TargetRuntime runtime) + { + return MSBuildProjectService.GetMSBuildBinPath (runtime); + } + static FilePath GetMSBuildBinPath () { - FilePath binDirectory = GetMSBuildBinDirectory (); + return GetMSBuildBinPath (Runtime.SystemAssemblyService.CurrentRuntime); + } + + internal static FilePath GetMSBuildBinPath (TargetRuntime runtime) + { + FilePath binDirectory = GetMSBuildBinDirectory (runtime); FilePath binPath = binDirectory.Combine ("MSBuild.dll"); if (File.Exists (binPath)) { return binPath; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs index 9cd247fc19..28484ef612 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/SdkResolution.cs @@ -3,13 +3,13 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.Serialization; using Microsoft.Build.Framework; using MonoDevelop.Core.Assemblies; -using MonoDevelop.Core; namespace MonoDevelop.Projects.MSBuild { @@ -24,6 +24,7 @@ namespace MonoDevelop.Projects.MSBuild readonly object _lockObject = new object (); IList<SdkResolver> _resolvers; TargetRuntime runtime; + Version msbuildVersion; internal SdkResolution (TargetRuntime runtime) { @@ -58,7 +59,7 @@ namespace MonoDevelop.Projects.MSBuild try { var buildEngineLogger = new SdkLoggerImpl (logger, buildEventContext); foreach (var sdkResolver in _resolvers) { - var context = new SdkResolverContextImpl (buildEngineLogger, projectFile, solutionPath); + var context = new SdkResolverContextImpl (buildEngineLogger, projectFile, solutionPath, msbuildVersion); var resultFactory = new SdkResultFactoryImpl (sdk); try { var result = (SdkResultImpl)sdkResolver.Resolve (sdk, context, resultFactory); @@ -96,10 +97,21 @@ namespace MonoDevelop.Projects.MSBuild { lock (_lockObject) { if (_resolvers != null) return; + msbuildVersion = GetMSBuildVersion (); _resolvers = LoadResolvers (logger); } } + Version GetMSBuildVersion () + { + var msbuildFileName = MSBuildProcessService.GetMSBuildBinPath (runtime); + if (!File.Exists (msbuildFileName)) + return null; + + var versionInfo = FileVersionInfo.GetVersionInfo (msbuildFileName); + return new Version (versionInfo.FileMajorPart, versionInfo.FileMinorPart, versionInfo.FileBuildPart, versionInfo.FilePrivatePart); + } + IList<SdkResolver> LoadResolvers (ILoggingService logger) { // Add the MonoDevelop resolver, which resolves SDKs registered by add-ins. @@ -287,7 +299,7 @@ namespace MonoDevelop.Projects.MSBuild public IEnumerable<string> Warnings { get; } } - class SdkResultFactoryImpl : SdkResultFactory + internal class SdkResultFactoryImpl : SdkResultFactory { readonly SdkReference _sdkReference; @@ -309,11 +321,12 @@ namespace MonoDevelop.Projects.MSBuild sealed class SdkResolverContextImpl : SdkResolverContext { - public SdkResolverContextImpl (SdkLogger logger, string projectFilePath, string solutionPath) + public SdkResolverContextImpl (SdkLogger logger, string projectFilePath, string solutionPath, Version msbuildVersion) { Logger = logger; ProjectFilePath = projectFilePath; SolutionFilePath = solutionPath; + MSBuildVersion = msbuildVersion; } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs index 7631c0cb30..66a96402f4 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs @@ -1915,8 +1915,14 @@ namespace MonoDevelop.Projects { bool externalConsole = false, pauseConsole = false; - var dotNetExecutionCommand = executionCommand as DotNetExecutionCommand; - if (dotNetExecutionCommand != null) {
+ if (executionCommand is ProcessExecutionCommand processExecutionCommand) { + if (!Directory.Exists (processExecutionCommand.WorkingDirectory)) { + monitor.ReportError (GettextCatalog.GetString ("Can not execute. The run configuration working directory doesn't exist at {0}", processExecutionCommand.WorkingDirectory), null); + return; + } + } + + if (executionCommand is DotNetExecutionCommand dotNetExecutionCommand) {
dotNetExecutionCommand.UserAssemblyPaths = GetUserAssemblyPaths (configuration); externalConsole = dotNetExecutionCommand.ExternalConsole; pauseConsole = dotNetExecutionCommand.PauseConsoleOutput; diff --git a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml index cd0d4d9e31..333f953015 100644 --- a/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml +++ b/main/src/core/MonoDevelop.Ide/ExtensionModel/Commands.addin.xml @@ -256,7 +256,7 @@ _description = "Adds and existing folder and its contents" _label = "_Add Existing Folder..." /> <Command id = "MonoDevelop.Ide.Commands.ProjectCommands.NewFolder" - _label = "New _Folder" + _label = "New _Folder…" _description = "Create a new folder" icon = "md-new-folder" /> <Command id = "MonoDevelop.Ide.Commands.ProjectCommands.IncludeToProject" diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs index 98cd671d2c..a6c2af0429 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/GtkWorkarounds.cs @@ -1538,7 +1538,11 @@ namespace MonoDevelop.Components [DllImport ("libgdk-win32-2.0-0.dll", CallingConvention = CallingConvention.Cdecl)] static extern void gdk_event_free (IntPtr raw); - public static void FreeEvent (IntPtr raw) => gdk_event_free (raw); + public static void FreeEvent (IntPtr raw) + { + if (raw != IntPtr.Zero) + gdk_event_free (raw); + } } public readonly struct KeyboardShortcut : IEquatable<KeyboardShortcut> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs index 9e7fef13ea..a954a6d4b2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/CompletionListWindow.cs @@ -263,12 +263,6 @@ namespace MonoDevelop.Ide.CodeCompletion controller.HideWindow (); } - [Obsolete("Use CompletionWindowManager.ToggleCategoryMode")] - public void ToggleCategoryMode () - { - controller.ToggleCategoryMode (); - } - /// <summary> /// Gets or sets a value indicating that shift was pressed during enter. /// </summary> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.Caching.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.Caching.cs index 8793e3596a..9e71fd04a2 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.Caching.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.Caching.cs @@ -27,7 +27,9 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; +using System.Reflection;
+using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; using System.Threading.Tasks; using Microsoft.VisualStudio.Composition; using Mono.Addins; @@ -52,18 +54,20 @@ namespace MonoDevelop.Ide.Composition /// </summary> internal class Caching { - internal static bool writeCache; readonly ICachingFaultInjector cachingFaultInjector;
Task saveTask; - public HashSet<Assembly> Assemblies { get; } + readonly HashSet<Assembly> loadedAssemblies;
+ public HashSet<Assembly> MefAssemblies { get; }
internal string MefCacheFile { get; } internal string MefCacheControlFile { get; } - public Caching (HashSet<Assembly> assemblies, Func<string, string> getCacheFilePath = null, ICachingFaultInjector cachingFaultInjector = null) + public Caching (HashSet<Assembly> mefAssemblies, Func<string, string> getCacheFilePath = null, ICachingFaultInjector cachingFaultInjector = null) { getCacheFilePath = getCacheFilePath ?? (file => Path.Combine (AddinManager.CurrentAddin.PrivateDataPath, file)); - Assemblies = assemblies; + loadedAssemblies = new HashSet<Assembly> (AppDomain.CurrentDomain.GetAssemblies ());
+
+ MefAssemblies = mefAssemblies; MefCacheFile = getCacheFilePath ("mef-cache"); MefCacheControlFile = getCacheFilePath ("mef-cache-control"); this.cachingFaultInjector = cachingFaultInjector; @@ -119,37 +123,58 @@ namespace MonoDevelop.Ide.Composition LoggingService.LogError ("MEF cache control deserialized as null"); DeleteFiles (); return false;
- } + }
- var currentAssemblies = new HashSet<string> (Assemblies.Select (asm => asm.Location)); - - // Short-circuit on number of assemblies change - if (controlCache.AssemblyInfos.Length != currentAssemblies.Count) + try { + // Short-circuit on number of assemblies change
+ if (controlCache.MefAssemblyInfos.Count != MefAssemblies.Count) + return false; +
+ if (!ValidateAssemblyCacheListIntegrity (MefAssemblies, controlCache.MefAssemblyInfos, cachingFaultInjector))
+ return false; +
+ if (!ValidateAssemblyCacheListIntegrity (loadedAssemblies, controlCache.AdditionalInputAssemblyInfos, cachingFaultInjector))
+ return false; + } catch (Exception e) {
+ LoggingService.LogError ("MEF cache validation failed", e); return false; + }
- // Validate that the assemblies match and we have the same time stamps on them. - foreach (var assemblyInfo in controlCache.AssemblyInfos) { + return true; + }
+
+ static bool ValidateAssemblyCacheListIntegrity (HashSet<Assembly> assemblies, List<MefControlCacheAssemblyInfo> cachedAssemblyInfos, ICachingFaultInjector cachingFaultInjector) + {
+ var currentAssemblies = new Dictionary<string, Guid> (assemblies.Count);
+ foreach (var asm in assemblies) {
+ if (asm.IsDynamic)
+ continue;
+ + currentAssemblies.Add (asm.Location, asm.ManifestModule.ModuleVersionId); + }
+
+ foreach (var assemblyInfo in cachedAssemblyInfos) { cachingFaultInjector?.FaultAssemblyInfo (assemblyInfo); - if (!currentAssemblies.Contains (assemblyInfo.Location)) - return false; - if (File.GetLastWriteTimeUtc (assemblyInfo.Location) != assemblyInfo.LastWriteTimeUtc) + if (!currentAssemblies.TryGetValue (assemblyInfo.Location, out var mvid)) return false; - } + if (mvid != assemblyInfo.ModuleVersionId) + return false; + }
return true; } - internal Task Write (RuntimeComposition runtimeComposition, CachedComposition cacheManager) + internal Task Write (RuntimeComposition runtimeComposition, ComposableCatalog catalog, CachedComposition cacheManager) { return Runtime.RunInMainThread (async () => { IdeApp.Exiting += IdeApp_Exiting; saveTask = Task.Run (async () => { try { - await WriteMefCache (runtimeComposition, cacheManager); + await WriteMefCache (runtimeComposition, catalog, cacheManager); } catch (Exception ex) { - LoggingService.LogError ("Failed to write MEF cache", ex); + LoggingService.LogInternalError ("Failed to write MEF cache", ex); } }); await saveTask; @@ -159,10 +184,10 @@ namespace MonoDevelop.Ide.Composition }); } - async Task WriteMefCache (RuntimeComposition runtimeComposition, CachedComposition cacheManager) + async Task WriteMefCache (RuntimeComposition runtimeComposition, ComposableCatalog catalog, CachedComposition cacheManager) { using (var timer = Counters.CompositionSave.BeginTiming ()) { - WriteMefCacheControl (timer); + WriteMefCacheControl (catalog, timer); // Serialize the MEF cache. using (var stream = File.Open (MefCacheFile, FileMode.Create)) { @@ -171,14 +196,41 @@ namespace MonoDevelop.Ide.Composition } } - void WriteMefCacheControl (ITimeTracker timer) - { + void WriteMefCacheControl (ComposableCatalog catalog, ITimeTracker timer) + {
+ var mefAssemblyNames = new HashSet<string> ();
+ var mefAssemblyInfos = new List<MefControlCacheAssemblyInfo> ();
+
+ foreach (var assembly in MefAssemblies) { + mefAssemblyNames.Add (assembly.GetName ().ToString ());
+
+ mefAssemblyInfos.Add (new MefControlCacheAssemblyInfo { + Location = assembly.Location, + ModuleVersionId = assembly.ManifestModule.ModuleVersionId, + }); + }
+
+ var additionalInputAssemblies = new List<MefControlCacheAssemblyInfo> ();
+ var loadedMap = loadedAssemblies.ToDictionary (x => x.GetName ().ToString (), x => x);
+
+ foreach (var asm in catalog.GetInputAssemblies ()) {
+ var assemblyName = asm.ToString (); + if (mefAssemblyNames.Contains (assemblyName))
+ continue;
+
+ bool found = loadedMap.TryGetValue (assemblyName, out var assembly);
+ System.Diagnostics.Debug.Assert (found);
+
+ additionalInputAssemblies.Add (new MefControlCacheAssemblyInfo { + Location = assembly.Location,
+ ModuleVersionId = assembly.ManifestModule.ModuleVersionId, + }); + } + // Create cache control data. var controlCache = new MefControlCache { - AssemblyInfos = Assemblies.Select (asm => new MefControlCacheAssemblyInfo { - Location = asm.Location, - LastWriteTimeUtc = File.GetLastWriteTimeUtc (asm.Location), - }).ToArray (), + MefAssemblyInfos = mefAssemblyInfos,
+ AdditionalInputAssemblyInfos = additionalInputAssemblies, }; var serializer = new JsonSerializer (); @@ -195,7 +247,10 @@ namespace MonoDevelop.Ide.Composition class MefControlCache { [JsonRequired] - public MefControlCacheAssemblyInfo [] AssemblyInfos; + public List<MefControlCacheAssemblyInfo> MefAssemblyInfos; + + [JsonRequired]
+ public List<MefControlCacheAssemblyInfo> AdditionalInputAssemblyInfos; } [Serializable] @@ -205,7 +260,7 @@ namespace MonoDevelop.Ide.Composition public string Location; [JsonRequired] - public DateTime LastWriteTimeUtc; + public Guid ModuleVersionId; } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs index fce217127d..8fe37a6e6e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Composition/CompositionManager.cs @@ -1,4 +1,4 @@ -// CompositionManager.cs +// CompositionManager.cs // // Author: // Kirill Osenkov <https://github.com/KirillOsenkov> @@ -131,16 +131,17 @@ namespace MonoDevelop.Ide.Composition var fullTimer = System.Diagnostics.Stopwatch.StartNew (); var stepTimer = System.Diagnostics.Stopwatch.StartNew (); - var assemblies = ReadAssembliesFromAddins (timer); + var mefAssemblies = ReadAssembliesFromAddins (timer); timings ["ReadFromAddins"] = stepTimer.ElapsedMilliseconds; stepTimer.Restart (); - var caching = new Caching (assemblies); + var caching = new Caching (mefAssemblies); // Try to use cached MEF data var canUse = metadata.ValidCache = caching.CanUse (); if (canUse) { + LoggingService.LogInfo ("Creating MEF composition from cache"); RuntimeComposition = await TryCreateRuntimeCompositionFromCache (caching); } timings ["LoadFromCache"] = stepTimer.ElapsedMilliseconds; @@ -148,10 +149,12 @@ namespace MonoDevelop.Ide.Composition // Otherwise fallback to runtime discovery. if (RuntimeComposition == null) { - RuntimeComposition = await CreateRuntimeCompositionFromDiscovery (caching, timer); + LoggingService.LogInfo ("Creating MEF composition from runtime"); + var (runtimeComposition, catalog) = await CreateRuntimeCompositionFromDiscovery (caching, timer); + RuntimeComposition = runtimeComposition; CachedComposition cacheManager = new CachedComposition (); - caching.Write (RuntimeComposition, cacheManager).Ignore (); + caching.Write (RuntimeComposition, catalog, cacheManager).Ignore (); } timings ["LoadRuntimeComposition"] = stepTimer.ElapsedMilliseconds; stepTimer.Restart (); @@ -167,7 +170,7 @@ namespace MonoDevelop.Ide.Composition internal static async Task<RuntimeComposition> TryCreateRuntimeCompositionFromCache (Caching caching) { - CachedComposition cacheManager = new CachedComposition (); + var cacheManager = new CachedComposition (); try { using (var cacheStream = caching.OpenCacheStream ()) { @@ -180,9 +183,9 @@ namespace MonoDevelop.Ide.Composition return null; } - internal static async Task<RuntimeComposition> CreateRuntimeCompositionFromDiscovery (Caching caching, ITimeTracker timer = null) + internal static async Task<(RuntimeComposition, ComposableCatalog)> CreateRuntimeCompositionFromDiscovery (Caching caching, ITimeTracker timer = null) { - var parts = await Discovery.CreatePartsAsync (caching.Assemblies); + var parts = await Discovery.CreatePartsAsync (caching.MefAssemblies); timer?.Trace ("Composition parts discovered"); ComposableCatalog catalog = ComposableCatalog.Create (StandardResolver) @@ -216,7 +219,7 @@ namespace MonoDevelop.Ide.Composition var runtimeComposition = RuntimeComposition.CreateRuntimeComposition (configuration); timer?.Trace ("Composition created"); - return runtimeComposition; + return (runtimeComposition, catalog); } internal static HashSet<Assembly> ReadAssembliesFromAddins (ITimeTracker<CompositionLoadMetadata> timer = null) @@ -240,6 +243,7 @@ namespace MonoDevelop.Ide.Composition string assemblyName = assemblyNode.FileName; // Make sure the add-in that registered the assembly is loaded, since it can bring other // other assemblies required to load this one + AddinManager.LoadAddin (null, id); var assemblyFilePath = assemblyNode.Addin.GetFilePath (assemblyNode.FileName); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs index b4a27fa0df..160df00400 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/TextEditorViewContent.cs @@ -384,8 +384,10 @@ namespace MonoDevelop.Ide.Editor } #endregion - - + public override void GrabFocus () + { + textEditor.GrabFocus (); + } } }
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/NewFolderDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/NewFolderDialog.cs new file mode 100644 index 0000000000..03785e23a7 --- /dev/null +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Dialogs/NewFolderDialog.cs @@ -0,0 +1,221 @@ +// +// NewFolderDialog.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2019 Microsoft +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.IO; +using System.Threading.Tasks; +using MonoDevelop.Components; +using MonoDevelop.Components.AtkCocoaHelper; +using MonoDevelop.Core; +using MonoDevelop.Ide.Gui.Pads.ProjectPad; +using MonoDevelop.Ide.Tasks; +using Xwt; + +namespace MonoDevelop.Ide.Gui.Dialogs +{ + class NewFolderDialog : Xwt.Dialog + { + readonly FilePath parentFolder; + TextEntry folderNameTextEntry; + DialogButton addButton; + InformationPopoverWidget warningPopover; + + public NewFolderDialog (FilePath parentFolder) + { + this.parentFolder = parentFolder; + + Build (); + + folderNameTextEntry.Text = GetDefaultFolderName (); + folderNameTextEntry.SetFocus (); + folderNameTextEntry.Changed += FolderNameTextEntryChanged; + addButton.Clicked += AddButtonClicked; + } + + public FilePath NewFolderCreated { get; private set; } + + void Build () + { + Padding = 0; + Resizable = false; + Width = 320; + Title = GettextCatalog.GetString ("New Folder"); + + var mainVBox = new VBox (); + + var folderNameHBox = new HBox (); + folderNameHBox.Margin = 20; + var folderNameLabel = new Label (); + folderNameLabel.Text = GettextCatalog.GetString ("Folder Name:"); + folderNameHBox.PackStart (folderNameLabel); + + folderNameTextEntry = new TextEntry (); + folderNameHBox.PackStart (folderNameTextEntry, true, true); + folderNameTextEntry.SetCommonAccessibilityAttributes ( + "NewFolderDialog.FolderNameTextEntry", + folderNameLabel.Text, + GettextCatalog.GetString ("Enter the name for the new folder")); + + warningPopover = new InformationPopoverWidget (); + warningPopover.Visible = false; + folderNameHBox.PackStart (warningPopover); + + mainVBox.PackStart (folderNameHBox); + + var cancelButton = new DialogButton (Command.Cancel); + Buttons.Add (cancelButton); + + addButton = new DialogButton (Command.Add); + Buttons.Add (addButton); + + DefaultCommand = addButton.Command; + + Content = mainVBox; + } + + protected override void Dispose (bool disposing) + { + base.Dispose (disposing); + if (disposing) { + folderNameTextEntry.Changed -= FolderNameTextEntryChanged; + addButton.Clicked -= AddButtonClicked; + } + } + + internal static Task<FilePath> Open (FilePath parentFolder) + { + var result = new TaskCompletionSource<FilePath> (); + Toolkit.NativeEngine.Invoke (delegate { + using (var dialog = new NewFolderDialog (parentFolder)) { + dialog.Run (MessageDialog.RootWindow); + result.SetResult (dialog.NewFolderCreated); + } + }); + return result.Task; + } + + string GetDefaultFolderName () + { + string childFolderName = GettextCatalog.GetString ("New Folder"); + string directoryName = Path.Combine (parentFolder, childFolderName); + int index = -1; + + if (Directory.Exists (directoryName)) { + while (Directory.Exists (directoryName + (++index + 1))) { + } + } + + if (index >= 0) { + return childFolderName += index + 1; + } + return childFolderName; + } + + void FolderNameTextEntryChanged (object sender, EventArgs e) + { + FilePath directoryPath = GetNewFolderPath (); + + if (folderNameTextEntry.Text.Length == 0) { + addButton.Sensitive = false; + HidePopoverMessage (); + } else if (!IsValidFolderName (directoryPath, folderNameTextEntry.Text)) { + ShowWarning (GettextCatalog.GetString ("The name you have chosen contains illegal characters. Please choose a different name.")); + addButton.Sensitive = false; + } else if (Directory.Exists (directoryPath)) { + ShowWarning (GettextCatalog.GetString ("Folder name is already in use. Please choose a different name.")); + addButton.Sensitive = false; + } else { + addButton.Sensitive = true; + HidePopoverMessage (); + } + } + + void AddButtonClicked (object sender, EventArgs e) + { + try { + bool canClose = AddNewFolder (); + if (canClose) { + Close (); + } + } catch (Exception ex) { + LoggingService.LogError ("Could not create folder", ex); + ShowError (GettextCatalog.GetString ("An error occurred creating the folder. {0}", ex.Message)); + } + } + + FilePath GetNewFolderPath () + { + return parentFolder.Combine (folderNameTextEntry.Text); + } + + bool AddNewFolder () + { + FilePath directoryPath = GetNewFolderPath (); + + Directory.CreateDirectory (directoryPath); + NewFolderCreated = directoryPath; + + return true; + } + + bool IsValidFolderName (FilePath folderPath, string folderName) + { + return FileService.IsValidPath (folderPath) && + !ProjectFolderCommandHandler.ContainsDirectorySeparator (folderName); + } + + protected override void OnCommandActivated (Command cmd) + { + if (cmd == Command.Add) { + // Prevent dialog closing after Add button is activated since an alert message dialog may have been shown. + return; + } + base.OnCommandActivated (cmd); + } + + void ShowWarning (string message) + { + ShowPopoverMessage (message, TaskSeverity.Warning); + } + + void ShowError (string message) + { + ShowPopoverMessage (message, TaskSeverity.Error); + } + + void ShowPopoverMessage (string message, TaskSeverity severity) + { + warningPopover.Message = message; + warningPopover.Severity = severity; + warningPopover.Show (); + } + + void HidePopoverMessage () + { + warningPopover.Hide (); + } + } +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/FolderNodeBuilder.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/FolderNodeBuilder.cs index ac19f26664..73a8f2c1c8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/FolderNodeBuilder.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui.Pads.ProjectPad/FolderNodeBuilder.cs @@ -438,7 +438,6 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad void OnFileInserted (ITreeNavigator nav) { nav.Selected = true; - Tree.StartLabelEdit (); } ///<summary>Imports files and folders from a target folder into the current folder</summary> @@ -549,27 +548,20 @@ namespace MonoDevelop.Ide.Gui.Pads.ProjectPad // project node is collapsed and Refresh was used the project node would not expand and the new folder // node would not be selected. CurrentNode.Expanded = true; - Project project = CurrentNode.GetParentDataItem (typeof(Project), true) as Project; - + + var project = CurrentNode.GetParentDataItem (typeof (Project), true) as Project; string baseFolderPath = GetFolderPath (CurrentNode.DataItem); - string directoryName = Path.Combine (baseFolderPath, GettextCatalog.GetString("New Folder")); - int index = -1; - if (Directory.Exists(directoryName)) { - while (Directory.Exists(directoryName + (++index + 1))) ; - } - - if (index >= 0) { - directoryName += index + 1; - } - - Directory.CreateDirectory (directoryName); - - ProjectFile newFolder = new ProjectFile (directoryName); + FilePath folder = await NewFolderDialog.Open (baseFolderPath); + + if (folder.IsNull) + return; + + var newFolder = new ProjectFile (folder); newFolder.Subtype = Subtype.Directory; project.Files.Add (newFolder); - Tree.AddNodeInsertCallback (new ProjectFolder (directoryName, project), new TreeNodeCallback (OnFileInserted)); + Tree.AddNodeInsertCallback (new ProjectFolder (folder, project), new TreeNodeCallback (OnFileInserted)); await IdeApp.ProjectOperations.SaveAsync (project); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/BaseViewContent.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/BaseViewContent.cs index e7c991caf4..028ad662c3 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/BaseViewContent.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/BaseViewContent.cs @@ -33,6 +33,7 @@ using MonoDevelop.Components; using MonoDevelop.Core; using System.Collections.Immutable; using MonoDevelop.Projects; +using Gtk; namespace MonoDevelop.Ide.Gui { @@ -159,6 +160,61 @@ namespace MonoDevelop.Ide.Gui /// </summary> /// <value>The display binding used to create this view.</value> public IDisplayBinding Binding { get; internal set; } + + public virtual void GrabFocus () + { + Widget widget = Control; + Widget first = null; + foreach (var f in GetFocusableWidgets (widget)) { + if (f.HasFocus) + return; + + if (first == null) + first = f; + } + if (first != null) { + first.GrabFocus (); + } + } + + static IEnumerable<Gtk.Widget> GetFocusableWidgets (Gtk.Widget widget) + { + if (widget.CanFocus) { + yield return widget; + } + + if (widget is Container c) { + foreach (var f in c.FocusChain.SelectMany (x => GetFocusableWidgets (x))) { + yield return f; + } + + if (c.Children is var children) { + foreach (var f in children) { + if (f is Container container) { + foreach (var child in GetFocusableChildren (container)) { + yield return child; + } + } + } + } + } + } + + static IEnumerable<Gtk.Widget> GetFocusableChildren (Gtk.Container container) + { + if (container.CanFocus) { + yield return container; + } + + foreach (var f in container.Children) { + if (f is Container c) { + foreach (var child in GetFocusableChildren (c)) { + yield return child; + } + } + } + } + } public enum ProjectReloadCapability diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs index 5aff4156f8..2a6c8e1d7a 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/DefaultWorkbench.cs @@ -79,6 +79,7 @@ namespace MonoDevelop.Ide.Gui IWorkbenchWindow lastActive; bool closeAll; + bool? fullScreenState = null; Rectangle normalBounds = new Rectangle(0, 0, MinimumWidth, MinimumHeight); @@ -146,13 +147,20 @@ namespace MonoDevelop.Ide.Gui public DockFrame DockFrame { get { return dock; } } - + public bool FullScreen { get { return DesktopService.GetIsFullscreen (this); } set { - DesktopService.SetIsFullscreen (this, value); + // If this window is not visible, don't set full screen mode + // until it is, as that would conflict with other windows we + // might be opening before (Start Window, for instance) + if (Visible) { + DesktopService.SetIsFullscreen (this, value); + } else { + fullScreenState = value; + } } } @@ -738,6 +746,15 @@ namespace MonoDevelop.Ide.Gui } } + protected override void OnShown () + { + base.OnShown (); + if (fullScreenState != null && fullScreenState != DesktopService.GetIsFullscreen (this)) { + DesktopService.SetIsFullscreen (this, (bool) fullScreenState); + fullScreenState = null; + } + } + bool closing; protected /*override*/ async void OnClosing(object o, Gtk.DeleteEventArgs e) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/SdiWorkspaceWindow.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/SdiWorkspaceWindow.cs index ed23c84b9d..a1f25edd5e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/SdiWorkspaceWindow.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/SdiWorkspaceWindow.cs @@ -262,9 +262,10 @@ namespace MonoDevelop.Ide.Gui { if (subViewToolbar != null) subViewToolbar.Tabs [subViewToolbar.ActiveTab].Activate (); + SelectWindow (); } - public void SelectWindow() + public void SelectWindow () { var window = tabControl.Toplevel as Gtk.Window; if (window != null) { @@ -287,11 +288,10 @@ namespace MonoDevelop.Ide.Gui // The tab change must be done now to ensure that the content is created // before exiting this method. tabControl.CurrentTabIndex = tab.Index; - // Focus the tab in the next iteration since presenting the window may take some time Application.Invoke ((o, args) => { DockNotebook.ActiveNotebook = tabControl; - DeepGrabFocus (this.ActiveViewContent.Control); + ActiveViewContent.GrabFocus (); }); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs index f1846c03ff..2669def837 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Gui/Workbench.cs @@ -418,12 +418,6 @@ namespace MonoDevelop.Ide.Gui AlertButton.CloseWithoutSave, AlertButton.Cancel, doc.Window.ViewContent.IsUntitled ? AlertButton.SaveAs : AlertButton.Save); } - [Obsolete("Use CloseAllDocumentsAsync")] - public void CloseAllDocuments (bool leaveActiveDocumentOpen) - { - CloseAllDocumentsAsync (leaveActiveDocumentOpen).Ignore (); - } - public async Task CloseAllDocumentsAsync (bool leaveActiveDocumentOpen) { Document[] docs = new Document [Documents.Count]; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs index c301f2cbbf..5e6d401e43 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs @@ -31,6 +31,7 @@ using MonoDevelop.Core; using System.Collections.Generic; using System.Text; using MonoDevelop.Components; +using MonoDevelop.Components.AtkCocoaHelper; namespace MonoDevelop.Ide.Projects { @@ -59,6 +60,22 @@ namespace MonoDevelop.Ide.Projects combPolicies.Active = 0; OnRadioCustomToggled (null, null); UpdateContentLabels (); + + combPolicies.Accessible.Name = "ApplyPolicyDialog.PolicyCombo"; + combPolicies.SetAccessibilityLabelRelationship (label2); + CombPolicies_Changed (null, null); + combPolicies.Changed += CombPolicies_Changed; + } + + protected override void OnDestroyed () + { + combPolicies.Changed -= CombPolicies_Changed; + base.OnDestroyed (); + } + + void CombPolicies_Changed (object sender, EventArgs e) + { + combPolicies.Accessible.Description = GettextCatalog.GetString ("Select policy, current: {0}", combPolicies.ActiveText); } protected void OnRadioCustomToggled (object sender, System.EventArgs e) @@ -189,7 +206,7 @@ namespace MonoDevelop.Ide.Projects public PoliciesListSummaryTree () : base (new Gtk.ListStore (typeof (string))) { - CanFocus = false; + CanFocus = true; HeadersVisible = false; store = (Gtk.ListStore) Model; this.AppendColumn ("", new Gtk.CellRendererText (), "text", 0); @@ -204,6 +221,8 @@ namespace MonoDevelop.Ide.Projects var win = evnt.Window; win.Clear (); if (string.IsNullOrEmpty (message)) { + if (ShowEmptyItem) + return base.OnExposeEvent (evnt); return true; } @@ -233,7 +252,9 @@ namespace MonoDevelop.Ide.Projects } } } - + + bool ShowEmptyItem { get; set; } + public void SetPolicies (PolicyContainer pset) { if (pset == null) { @@ -279,6 +300,13 @@ namespace MonoDevelop.Ide.Projects } StringBuilderCache.Free (sb); HasPolicies = sorted.Count > 0; + if (!HasPolicies) { + store.AppendValues (GettextCatalog.GetString ("No policies")); + ShowEmptyItem = true; + } + if (store.GetIterFirst (out var iter)) { + Selection.SelectIter (iter); + } } } -}
\ No newline at end of file +} diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportProjectPolicyDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportProjectPolicyDialog.cs index b10e87b681..a8574cac8c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportProjectPolicyDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ExportProjectPolicyDialog.cs @@ -64,7 +64,6 @@ namespace MonoDevelop.Ide.Projects tree.SetPolicies (policyProvider.Policies); if (!tree.HasPolicies) { - tree.Message = GettextCatalog.GetString ("No policies"); buttonOk.Sensitive = false; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs index f5cc57498c..48ca40df10 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopPersistentStorageLocationService.cs @@ -40,36 +40,124 @@ using Microsoft.CodeAnalysis.SQLite; using Microsoft.CodeAnalysis.Storage; using Microsoft.CodeAnalysis.SolutionSize; using MonoDevelop.Core; +using System.Diagnostics.Contracts; +using System.Diagnostics; namespace MonoDevelop.Ide.TypeSystem { [ExportWorkspaceService (typeof (IPersistentStorageLocationService), ServiceLayer.Host), Shared] class MonoDevelopPersistentStorageLocationService : IPersistentStorageLocationService { + private readonly object _gate = new object (); + private WorkspaceId primaryWorkspace = WorkspaceId.Empty; + private SolutionId _currentSolutionId = null; + private string _currentWorkingFolderPath = null; + + public event EventHandler<PersistentStorageLocationChangingEventArgs> StorageLocationChanging; + + [ImportingConstructor] + [Obsolete (MefConstruction.ImportingConstructorMessage, error: true)] + public MonoDevelopPersistentStorageLocationService () + { + } + + public IDisposable RegisterPrimaryWorkspace (WorkspaceId id) + { + if (primaryWorkspace.Equals (WorkspaceId.Empty)) { + primaryWorkspace = id; + return new WorkspaceRegistration (this); + } + return null; + } + + class WorkspaceRegistration : IDisposable + { + readonly MonoDevelopPersistentStorageLocationService service; + bool disposed; + + public WorkspaceRegistration (MonoDevelopPersistentStorageLocationService service) => this.service = service; + + public void Dispose () + { + if (!disposed) { + service.DisconnectCurrentStorage (); + disposed = true; + } + } + } + public bool IsSupported (Workspace workspace) => workspace is MonoDevelopWorkspace; - // PERF: cache for the solution location. This is needed due to roslyn querying GetStorageLocation a lot of times. - internal ConditionalWeakTable<SolutionId, string> storageMap = new ConditionalWeakTable<SolutionId, string> (); + public string TryGetStorageLocation (SolutionId solutionId) + { + lock (_gate) { + if (solutionId == _currentSolutionId) { + return _currentWorkingFolderPath; + } + } - public event EventHandler<PersistentStorageLocationChangingEventArgs> StorageLocationChanging; + return null; + } - internal void NotifyStorageLocationChanging (SolutionId sol, string path) + internal void SetupSolution (MonoDevelopWorkspace visualStudioWorkspace) { - lock (storageMap) { - if (storageMap.TryGetValue (sol, out string cached) && path == cached) + lock (_gate) { + // Don't trigger events for workspaces other than those we want to inspect. + if (!primaryWorkspace.Equals (visualStudioWorkspace.Id)) + return; + + if (visualStudioWorkspace.CurrentSolution.Id == _currentSolutionId && _currentWorkingFolderPath != null) { return; + } - StorageLocationChanging?.Invoke (this, new PersistentStorageLocationChangingEventArgs (sol, path, true)); - storageMap.Remove (sol); - storageMap.Add (sol, path); + var solution = visualStudioWorkspace.MonoDevelopSolution; + solution.Modified += OnSolutionModified; + if (string.IsNullOrWhiteSpace (solution.BaseDirectory)) + return; + + var workingFolderPath = solution.GetPreferencesDirectory (); + + try { + if (!string.IsNullOrWhiteSpace (workingFolderPath)) { + OnWorkingFolderChanging_NoLock ( + new PersistentStorageLocationChangingEventArgs ( + visualStudioWorkspace.CurrentSolution.Id, + workingFolderPath, + mustUseNewStorageLocationImmediately: false)); + } + } catch { + // don't crash just because solution having problem getting working folder information + } } } - public string TryGetStorageLocation (SolutionId solutionId) + async void OnSolutionModified (object sender, MonoDevelop.Projects.WorkspaceItemEventArgs args) + { + var sol = (MonoDevelop.Projects.Solution)args.Item; + var workspace = await TypeSystemService.GetWorkspaceAsync (sol, CancellationToken.None); + if (workspace.Id.Equals (primaryWorkspace)) { + DisconnectCurrentStorage (); + } + } + + private void OnWorkingFolderChanging_NoLock (PersistentStorageLocationChangingEventArgs eventArgs) + { + StorageLocationChanging?.Invoke (this, eventArgs); + + _currentSolutionId = eventArgs.SolutionId; + _currentWorkingFolderPath = eventArgs.NewStorageLocation; + } + + void DisconnectCurrentStorage () { - lock (storageMap) { - storageMap.TryGetValue (solutionId, out var path); - return path; + lock (_gate) { + // We want to make sure everybody synchronously detaches + OnWorkingFolderChanging_NoLock ( + new PersistentStorageLocationChangingEventArgs ( + _currentSolutionId, + newStorageLocation: null, + mustUseNewStorageLocationImmediately: true)); + primaryWorkspace = WorkspaceId.Empty; } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectData.cs index ff49e10dd0..3d62435d25 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectData.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectData.cs @@ -48,9 +48,10 @@ namespace MonoDevelop.Ide.TypeSystem DocumentData = new DocumentMap (projectId); this.metadataReferences = new List<MonoDevelopMetadataReference> (metadataReferences.Length); - System.Diagnostics.Debug.Assert (Monitor.IsEntered (ws.updatingProjectDataLock)); - foreach (var metadataReference in metadataReferences) { - AddMetadataReference_NoLock (metadataReference, ws); + lock (this.metadataReferences) { + foreach (var metadataReference in metadataReferences) { + AddMetadataReference_NoLock (metadataReference, ws); + } } } @@ -61,7 +62,7 @@ namespace MonoDevelop.Ide.TypeSystem if (!workspaceRef.TryGetTarget (out var workspace)) return; - lock (workspace.updatingProjectDataLock) { + lock (metadataReferences) { if (!RemoveMetadataReference_NoLock (reference, workspace)) return; workspace.OnMetadataReferenceRemoved (projectId, args.OldSnapshot); @@ -73,7 +74,7 @@ namespace MonoDevelop.Ide.TypeSystem void AddMetadataReference_NoLock (MonoDevelopMetadataReference metadataReference, MonoDevelopWorkspace ws) { - System.Diagnostics.Debug.Assert (Monitor.IsEntered (ws.updatingProjectDataLock)); + System.Diagnostics.Debug.Assert (Monitor.IsEntered (metadataReferences)); metadataReferences.Add (metadataReference); metadataReference.SnapshotUpdated += OnMetadataReferenceUpdated; @@ -81,7 +82,7 @@ namespace MonoDevelop.Ide.TypeSystem bool RemoveMetadataReference_NoLock (MonoDevelopMetadataReference metadataReference, MonoDevelopWorkspace ws) { - System.Diagnostics.Debug.Assert (Monitor.IsEntered (ws.updatingProjectDataLock)); + System.Diagnostics.Debug.Assert (Monitor.IsEntered (metadataReferences)); metadataReference.SnapshotUpdated -= OnMetadataReferenceUpdated; return metadataReferences.Remove (metadataReference); @@ -92,9 +93,10 @@ namespace MonoDevelop.Ide.TypeSystem if (!workspaceRef.TryGetTarget (out var ws)) return; - System.Diagnostics.Debug.Assert (Monitor.IsEntered (ws.updatingProjectDataLock)); - foreach (var reference in metadataReferences) - reference.SnapshotUpdated -= OnMetadataReferenceUpdated; + lock (metadataReferences) { + foreach (var reference in metadataReferences) + reference.SnapshotUpdated -= OnMetadataReferenceUpdated; + } } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectDataMap.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectDataMap.cs index 3cdf74b32b..0d0561eb5b 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectDataMap.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectDataMap.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; using Microsoft.CodeAnalysis; namespace MonoDevelop.Ide.TypeSystem @@ -39,8 +40,8 @@ namespace MonoDevelop.Ide.TypeSystem ImmutableDictionary<ProjectId, MonoDevelop.Projects.Project> projectIdToMdProjectMap = ImmutableDictionary<ProjectId, MonoDevelop.Projects.Project>.Empty; readonly Dictionary<MonoDevelop.Projects.Project, ProjectId> projectIdMap = new Dictionary<MonoDevelop.Projects.Project, ProjectId> (); - // FIXME: Make this private - internal readonly Dictionary<ProjectId, ProjectData> projectDataMap = new Dictionary<ProjectId, ProjectData> (); + readonly Dictionary<ProjectId, ProjectData> projectDataMap = new Dictionary<ProjectId, ProjectData> (); + readonly object updatingProjectDataLock = new object (); public ProjectDataMap (MonoDevelopWorkspace workspace) { @@ -92,7 +93,7 @@ namespace MonoDevelop.Ide.TypeSystem projectIdToMdProjectMap = projectIdToMdProjectMap.Remove (val); } - lock (Workspace.updatingProjectDataLock) { + lock (updatingProjectDataLock) { projectDataMap.Remove (id); } } @@ -106,14 +107,14 @@ namespace MonoDevelop.Ide.TypeSystem internal bool Contains (ProjectId projectId) { - lock (Workspace.updatingProjectDataLock) { + lock (updatingProjectDataLock) { return projectDataMap.ContainsKey (projectId); } } internal ProjectData GetData (ProjectId id) { - lock (Workspace.updatingProjectDataLock) { + lock (updatingProjectDataLock) { projectDataMap.TryGetValue (id, out ProjectData result); return result; } @@ -121,7 +122,7 @@ namespace MonoDevelop.Ide.TypeSystem internal ProjectData RemoveData (ProjectId id) { - lock (Workspace.updatingProjectDataLock) { + lock (updatingProjectDataLock) { if (projectDataMap.TryGetValue (id, out ProjectData result)) { projectDataMap.Remove (id); result.Disconnect (); @@ -132,12 +133,18 @@ namespace MonoDevelop.Ide.TypeSystem internal ProjectData CreateData (ProjectId id, ImmutableArray<MonoDevelopMetadataReference> metadataReferences) { - lock (Workspace.updatingProjectDataLock) { + lock (updatingProjectDataLock) { var result = new ProjectData (id, metadataReferences, Workspace); projectDataMap [id] = result; return result; } } + internal ProjectId[] GetProjectIds () + { + lock (updatingProjectDataLock) { + return projectDataMap.Keys.ToArray (); + } + } } } }
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectSystemHandler.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectSystemHandler.cs index 66d10598f3..e1a5ef532d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectSystemHandler.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.ProjectSystemHandler.cs @@ -43,7 +43,7 @@ namespace MonoDevelop.Ide.TypeSystem { public partial class MonoDevelopWorkspace { - internal class ProjectSystemHandler + internal class ProjectSystemHandler : IDisposable { readonly MonoDevelopWorkspace workspace; readonly ProjectDataMap projectMap; @@ -51,6 +51,8 @@ namespace MonoDevelop.Ide.TypeSystem readonly Lazy<MetadataReferenceHandler> metadataHandler; readonly Lazy<HostDiagnosticUpdateSource> hostDiagnosticUpdateSource; readonly HackyWorkspaceFilesCache hackyCache; + readonly List<MonoDevelopAnalyzer> analyzersToDispose = new List<MonoDevelopAnalyzer> (); + IDisposable persistentStorageLocationServiceRegistration; bool added; readonly object addLock = new object (); @@ -66,6 +68,10 @@ namespace MonoDevelop.Ide.TypeSystem metadataHandler = new Lazy<MetadataReferenceHandler> (() => new MetadataReferenceHandler (workspace.MetadataReferenceManager, projectMap)); hostDiagnosticUpdateSource = new Lazy<HostDiagnosticUpdateSource> (() => new HostDiagnosticUpdateSource (workspace, Composition.CompositionManager.GetExportedValue<IDiagnosticUpdateSourceRegistrationService> ())); + + var persistentStorageLocationService = (MonoDevelopPersistentStorageLocationService)workspace.Services.GetService<IPersistentStorageLocationService> (); + if (workspace.MonoDevelopSolution != null) + persistentStorageLocationServiceRegistration = persistentStorageLocationService.RegisterPrimaryWorkspace (workspace.Id); } #region Solution mapping @@ -84,26 +90,6 @@ namespace MonoDevelop.Ide.TypeSystem return result; } } - - static async void OnSolutionModified (object sender, MonoDevelop.Projects.WorkspaceItemEventArgs args) - { - var sol = (MonoDevelop.Projects.Solution)args.Item; - var workspace = await TypeSystemService.GetWorkspaceAsync (sol, CancellationToken.None); - var solId = workspace.ProjectHandler.GetSolutionId (sol); - if (solId == null) - return; - - NotifySolutionModified (sol, solId, workspace); - } - - static void NotifySolutionModified (MonoDevelop.Projects.Solution sol, SolutionId solId, MonoDevelopWorkspace workspace) - { - if (string.IsNullOrWhiteSpace (sol.BaseDirectory)) - return; - - var locService = (MonoDevelopPersistentStorageLocationService)workspace.Services.GetService<IPersistentStorageLocationService> (); - locService.NotifyStorageLocationChanging (solId, sol.GetPreferencesDirectory ()); - } #endregion class SolutionData @@ -122,7 +108,7 @@ namespace MonoDevelop.Ide.TypeSystem fileName = new FilePath (p.Name + ".dll"); if (!hackyCache.TryGetCachedItems (p, workspace.MetadataReferenceManager, projectMap, out var sourceFiles, out var analyzerFiles, out var references, out var projectReferences)) { - (references, projectReferences) = await metadataHandler.Value.CreateReferences (p, token); + (references, projectReferences) = await metadataHandler.Value.CreateReferences (p, token).ConfigureAwait (false); if (token.IsCancellationRequested) return null; @@ -141,37 +127,46 @@ namespace MonoDevelop.Ide.TypeSystem var loader = workspace.Services.GetService<IAnalyzerService> ().GetLoader (); - lock (workspace.updatingProjectDataLock) { + ProjectData projectData, oldProjectData; + List<DocumentInfo> mainDocuments, additionalDocuments; + try { + await workspace.LoadLock.WaitAsync ().ConfigureAwait (false); //when reloading e.g. after a save, preserve document IDs - var oldProjectData = projectMap.RemoveData (projectId); - var projectData = projectMap.CreateData (projectId, references); + oldProjectData = projectMap.RemoveData (projectId); + projectData = projectMap.CreateData (projectId, references); - var documents = CreateDocuments (projectData, p, token, sourceFiles, oldProjectData); + var documents = await CreateDocuments (projectData, p, token, sourceFiles, oldProjectData).ConfigureAwait (false); if (documents == null) return null; - // TODO: Pass in the WorkspaceMetadataFileReferenceResolver - var info = ProjectInfo.Create ( - projectId, - VersionStamp.Create (), - p.Name, - fileName.FileNameWithoutExtension, - (p as MonoDevelop.Projects.DotNetProject)?.RoslynLanguageName ?? LanguageNames.CSharp, - p.FileName, - fileName, - cp?.CreateCompilationOptions (), - cp?.CreateParseOptions (config), - documents.Item1, - projectReferences, - references.Select (x => x.CurrentSnapshot), - analyzerReferences: analyzerFiles.SelectAsArray (x => { - var analyzer = new MonoDevelopAnalyzer (x, hostDiagnosticUpdateSource.Value, projectId, workspace, loader, LanguageNames.CSharp); - return analyzer.GetReference (); - }), - additionalDocuments: documents.Item2 - ); - return info; + mainDocuments = documents.Item1; + additionalDocuments = documents.Item2; + } finally { + workspace.LoadLock.Release (); } + + // TODO: Pass in the WorkspaceMetadataFileReferenceResolver + var info = ProjectInfo.Create ( + projectId, + VersionStamp.Create (), + p.Name, + fileName.FileNameWithoutExtension, + (p as MonoDevelop.Projects.DotNetProject)?.RoslynLanguageName ?? LanguageNames.CSharp, + p.FileName, + fileName, + cp?.CreateCompilationOptions (), + cp?.CreateParseOptions (config), + mainDocuments, + projectReferences, + references.Select (x => x.CurrentSnapshot), + analyzerReferences: analyzerFiles.SelectAsArray (x => { + var analyzer = new MonoDevelopAnalyzer (x, hostDiagnosticUpdateSource.Value, projectId, workspace, loader, LanguageNames.CSharp); + analyzersToDispose.Add (analyzer); + return analyzer.GetReference (); + }), + additionalDocuments: additionalDocuments + ); + return info; } async Task<ConcurrentBag<ProjectInfo>> CreateProjectInfos (IEnumerable<MonoDevelop.Projects.Project> mdProjects, CancellationToken token) @@ -278,9 +273,6 @@ namespace MonoDevelop.Ide.TypeSystem lock (addLock) { if (!added) { added = true; - solution.Modified += OnSolutionModified; - NotifySolutionModified (solution, solutionId, workspace); - workspace.OnSolutionAdded (solutionInfo); OnSolutionOpened (workspace, solutionInfo); } } @@ -296,6 +288,11 @@ namespace MonoDevelop.Ide.TypeSystem void OnSolutionOpened (MonoDevelopWorkspace workspace, SolutionInfo solutionInfo) { + var service = (MonoDevelopPersistentStorageLocationService)workspace.Services.GetService<IPersistentStorageLocationService> (); + service.SetupSolution (workspace); + + workspace.OnSolutionAdded (solutionInfo); + AssignOpenDocumentsToWorkspace (workspace); OpenGeneratedFiles (workspace); } @@ -336,7 +333,7 @@ namespace MonoDevelop.Ide.TypeSystem return node.Parser.CanGenerateAnalysisDocument (mimeType, f.BuildAction, p.SupportedLanguages); } - Tuple<List<DocumentInfo>, List<DocumentInfo>> CreateDocuments (ProjectData projectData, MonoDevelop.Projects.Project p, CancellationToken token, ImmutableArray<MonoDevelop.Projects.ProjectFile> sourceFiles, ProjectData oldProjectData) + async Task<Tuple<List<DocumentInfo>, List<DocumentInfo>>> CreateDocuments (ProjectData projectData, MonoDevelop.Projects.Project p, CancellationToken token, ImmutableArray<MonoDevelop.Projects.ProjectFile> sourceFiles, ProjectData oldProjectData) { var documents = new List<DocumentInfo> (); // We don' add additionalDocuments anymore because they were causing slowdown of compilation generation @@ -357,7 +354,7 @@ namespace MonoDevelop.Ide.TypeSystem continue; documents.Add (CreateDocumentInfo (solutionData, p.Name, projectData, f)); } else { - foreach (var projectedDocument in GenerateProjections (f, projectData.DocumentData, p, oldProjectData, null)) { + foreach (var projectedDocument in await GenerateProjections (f, projectData.DocumentData, p, token, oldProjectData, null)) { var projectedId = projectData.DocumentData.GetOrCreate (projectedDocument.FilePath, oldProjectData?.DocumentData); if (!duplicates.Add (projectedId)) continue; @@ -375,41 +372,44 @@ namespace MonoDevelop.Ide.TypeSystem return Tuple.Create (documents, additionalDocuments); } - IEnumerable<DocumentInfo> GenerateProjections (MonoDevelop.Projects.ProjectFile f, DocumentMap documentMap, MonoDevelop.Projects.Project p, ProjectData oldProjectData, HashSet<DocumentId> duplicates) + async Task<List<DocumentInfo>> GenerateProjections (MonoDevelop.Projects.ProjectFile f, DocumentMap documentMap, MonoDevelop.Projects.Project p, CancellationToken token, ProjectData oldProjectData, HashSet<DocumentId> duplicates) { var mimeType = DesktopService.GetMimeTypeForUri (f.FilePath); var node = TypeSystemService.GetTypeSystemParserNode (mimeType, f.BuildAction); if (node == null || !node.Parser.CanGenerateProjection (mimeType, f.BuildAction, p.SupportedLanguages)) - yield break; + return new List<DocumentInfo> (); + var options = new ParseOptions { FileName = f.FilePath, Project = p, Content = TextFileProvider.Instance.GetReadOnlyTextEditorData (f.FilePath), }; - var generatedProjections = node.Parser.GenerateProjections (options); + var generatedProjections = await node.Parser.GenerateProjections (options, token); var list = new List<Projection> (); var entry = new ProjectionEntry { File = f, Projections = list, }; - foreach (var projection in generatedProjections.Result) { + var result = new List<DocumentInfo> (generatedProjections.Count); + foreach (var projection in generatedProjections) { list.Add (projection); if (duplicates != null && !duplicates.Add (documentMap.GetOrCreate (projection.Document.FileName, oldProjectData?.DocumentData))) continue; var plainName = projection.Document.FileName.FileName; var folders = GetFolders (p.Name, f); - yield return DocumentInfo.Create ( + result.Add(DocumentInfo.Create ( documentMap.GetOrCreate (projection.Document.FileName, oldProjectData?.DocumentData), plainName, folders, SourceCodeKind.Regular, TextLoader.From (TextAndVersion.Create (new MonoDevelopSourceText (projection.Document), VersionStamp.Create (), projection.Document.FileName)), projection.Document.FileName, - false + false) ); } projections.AddProjectionEntry (entry); + return result; } static DocumentInfo CreateDocumentInfo (SolutionData data, string projectName, ProjectData id, MonoDevelop.Projects.ProjectFile f) @@ -434,6 +434,17 @@ namespace MonoDevelop.Ide.TypeSystem { return new [] { projectName }.Concat (f.ProjectVirtualPath.ParentDirectory.ToString ().Split (Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)); } + + public void Dispose () + { + persistentStorageLocationServiceRegistration?.Dispose (); + + solutionIdMap.Clear (); + + foreach (var analyzer in analyzersToDispose) + analyzer.Dispose (); + analyzersToDispose.Clear (); + } } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs index 485885b12b..bb7c9bcb68 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs @@ -76,8 +76,9 @@ namespace MonoDevelop.Ide.TypeSystem internal readonly WorkspaceId Id;
CancellationTokenSource src = new CancellationTokenSource ();
- bool disposed;
- readonly object updatingProjectDataLock = new object ();
+ bool disposed; + + internal readonly SemaphoreSlim LoadLock = new SemaphoreSlim (1, 1);
Lazy<MonoDevelopMetadataReferenceManager> manager;
Lazy<MetadataReferenceHandler> metadataHandler;
ProjectionData Projections { get; }
@@ -254,12 +255,12 @@ namespace MonoDevelop.Ide.TypeSystem protected override void Dispose (bool finalize)
{
- base.Dispose (finalize);
if (disposed)
return;
disposed = true;
+ ProjectHandler.Dispose ();
MetadataReferenceManager.ClearCache ();
IdeApp.Preferences.EnableSourceAnalysis.Changed -= OnEnableSourceAnalysisChanged;
@@ -281,8 +282,10 @@ namespace MonoDevelop.Ide.TypeSystem if (backgroundCompiler != null) {
backgroundCompiler.Dispose ();
- backgroundCompiler = null; // PartialSemanticsEnabled will now return false
+ backgroundCompiler = null; // PartialSemanticsEnabled will now return false }
+
+ base.Dispose (finalize);
}
internal void InformDocumentTextChange (DocumentId id, SourceText text)
@@ -723,9 +726,9 @@ namespace MonoDevelop.Ide.TypeSystem var projections = await node.Parser.GenerateProjections (options);
UpdateProjectionEntry (file, projections);
var projectId = GetProjectId (project);
- var projectdata = ProjectMap.GetData (projectId);
- foreach (var projected in projections) {
- OnDocumentTextChanged (projectdata.DocumentData.Get (projected.Document.FileName), new MonoDevelopSourceText (projected.Document), PreservationMode.PreserveValue);
+ var projectdata = ProjectMap.GetData (projectId); + foreach (var projected in projections) { + OnDocumentTextChanged (projectdata.DocumentData.Get (projected.Document.FileName), new MonoDevelopSourceText (projected.Document), PreservationMode.PreserveValue); }
}
}
@@ -1091,41 +1094,43 @@ namespace MonoDevelop.Ide.TypeSystem return project.GetAdditionalDocument (documentId);
}
- internal Task UpdateFileContent (string fileName, string text)
+ internal async Task UpdateFileContent (string fileName, string text)
{
SourceText newText = SourceText.From (text);
var tasks = new List<Task> ();
- lock (updatingProjectDataLock) {
- foreach (var kv in ProjectMap.projectDataMap) {
- var projectId = kv.Key;
- var docId = this.GetDocumentId (projectId, fileName);
- if (docId != null) {
- try {
- if (this.GetDocument (docId) != null) {
- OnDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity);
- } else if (this.GetAdditionalDocument (docId) != null) {
- base.OnAdditionalDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity);
- }
- } catch (Exception e) {
- LoggingService.LogWarning ("Roslyn error on text change", e);
- }
- }
- var monoProject = GetMonoProject (projectId);
- if (monoProject != null) {
- var pf = monoProject.GetProjectFile (fileName);
- if (pf != null) {
- var mimeType = DesktopService.GetMimeTypeForUri (fileName);
- if (TypeSystemService.CanParseProjections (monoProject, mimeType, fileName)) {
- var parseOptions = new ParseOptions { Project = monoProject, FileName = fileName, Content = new StringTextSource (text), BuildAction = pf.BuildAction };
- var task = TypeSystemService.ParseProjection (parseOptions, mimeType);
- tasks.Add (task);
- }
- }
- }
- }
+ try { + await LoadLock.WaitAsync ();
+ foreach (var projectId in ProjectMap.GetProjectIds ()) { + var docId = this.GetDocumentId (projectId, fileName); + if (docId != null) { + try { + if (this.GetDocument (docId) != null) { + base.OnDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity); + } else if (this.GetAdditionalDocument (docId) != null) { + base.OnAdditionalDocumentTextChanged (docId, newText, PreservationMode.PreserveIdentity); + } + } catch (Exception e) { + LoggingService.LogWarning ("Roslyn error on text change", e); + } + } + var monoProject = GetMonoProject (projectId); + if (monoProject != null) { + var pf = monoProject.GetProjectFile (fileName); + if (pf != null) { + var mimeType = DesktopService.GetMimeTypeForUri (fileName); + if (TypeSystemService.CanParseProjections (monoProject, mimeType, fileName)) { + var parseOptions = new ParseOptions { Project = monoProject, FileName = fileName, Content = new StringTextSource (text), BuildAction = pf.BuildAction }; + var task = TypeSystemService.ParseProjection (parseOptions, mimeType); + tasks.Add (task); + } + } + } + } + } finally { + LoadLock.Release (); }
- return Task.WhenAll (tasks);
+ await Task.WhenAll (tasks);
}
internal void RemoveProject (MonoDevelop.Projects.Project project)
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs index fbd88c1e4c..3124ad9a0d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemParserNode.cs @@ -84,16 +84,5 @@ namespace MonoDevelop.Ide.TypeSystem } return false; } - - [Obsolete ("Use p.IsCompileable")] - public static bool IsCompileableFile (ProjectFile file, out Microsoft.CodeAnalysis.SourceCodeKind sck) - => IsCompileableFile (null, file, out sck); - - [Obsolete ("Use p.IsCompileable")] - public static bool IsCompileableFile (MonoDevelop.Projects.Project p, ProjectFile file, out Microsoft.CodeAnalysis.SourceCodeKind sck) - { - sck = file.SourceCodeKind; - return p.IsCompileable (file.FilePath); - } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs index 5a10d45e8b..541589e497 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs @@ -237,11 +237,16 @@ namespace MonoDevelop.Ide.TypeSystem var projectFile = options.Project.GetProjectFile (options.FileName); if (projectFile != null) { ws.UpdateProjectionEntry (projectFile, result.Projections); - foreach (var projection in result.Projections) { - var docId = ws.GetDocumentId (projectId, projection.Document.FileName); - if (docId != null) { - ws.InformDocumentTextChange (docId, new MonoDevelopSourceText (projection.Document)); + await ws.LoadLock.WaitAsync (); + try { + foreach (var projection in result.Projections) { + var docId = ws.GetDocumentId (projectId, projection.Document.FileName); + if (docId != null) { + ws.InformDocumentTextChange (docId, new MonoDevelopSourceText (projection.Document)); + } } + } finally { + ws.LoadLock.Release (); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj index f7dd7ddd67..a81b6a6027 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.csproj @@ -4195,6 +4195,9 @@ <Compile Include="MonoDevelop.Components\Mac\NSStackViewExtensions.cs" /> <Compile Include="MonoDevelop.Components\Mac\NSLabel.cs" /> <Compile Include="MonoDevelop.Components\Mac\NSLine.cs" /> + <Compile Include="MonoDevelop.Ide.Projects\NewSolutionRunConfigurationDialog.cs" /> + <Compile Include="MonoDevelop.Ide.TypeSystem\HackyWorkspaceFilesCache.cs" /> + <Compile Include="MonoDevelop.Ide.Gui.Dialogs\NewFolderDialog.cs" /> </ItemGroup> <ItemGroup> <Data Include="options\DefaultEditingLayout.xml" /> @@ -4294,4 +4297,4 @@ <Target Name="AfterBuild"> <Copy SourceFiles="@(Data)" DestinationFolder="..\..\..\build\data\%(Data.RelativeDir)" SkipUnchangedFiles="true" /> </Target> -</Project>
\ No newline at end of file +</Project> diff --git a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.MSBuildResolver/Resolver.cs b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.MSBuildResolver/Resolver.cs index 5deb40613d..d899b0fcf1 100644 --- a/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.MSBuildResolver/Resolver.cs +++ b/main/src/core/MonoDevelop.Projects.Formats.MSBuild/MonoDevelop.MSBuildResolver/Resolver.cs @@ -82,7 +82,7 @@ namespace MonoDevelop.Projects.MSBuild // Pick the SDK with the highest version foreach (var sdk in sdkFetcher ()) { - if (sdk.Name == sdkReference.Name) { + if (StringComparer.OrdinalIgnoreCase.Equals (sdk.Name, sdkReference.Name)) { if (sdk.Version != null) { // If the sdk has a version, it must satisfy the min version requirement if (minVersion != null && sdk.Version < minVersion) diff --git a/main/src/tools/ExtensionTools/AddinDependencyTreeWidget.cs b/main/src/tools/ExtensionTools/AddinDependencyTreeWidget.cs new file mode 100644 index 0000000000..311f852b83 --- /dev/null +++ b/main/src/tools/ExtensionTools/AddinDependencyTreeWidget.cs @@ -0,0 +1,144 @@ +// +// AddinDependencyTreeWidget.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using Mono.Addins; +using Mono.Addins.Description; +using Xwt; +namespace MonoDevelop.ExtensionTools +{ + class AddinDependencyTreeWidget : Widget + { + readonly TreeStore treeStore; + readonly TreeView treeView; + readonly DataField<string> labelField = new DataField<string> (); + readonly Label summary = new Label (); + + public AddinDependencyTreeWidget (Addin[] addins = null) + { + addins = addins ?? AddinManager.Registry.GetAllAddins (); + + treeStore = new TreeStore (labelField); + treeView = new TreeView (treeStore); + + var col = treeView.Columns.Add ("Name", labelField); + col.Expands = true; + + FillData (addins); + treeView.ExpandAll (); + + var vbox = new VBox (); + vbox.PackStart (summary); + vbox.PackStart (treeView, true); + Content = vbox; + } + + void FillData(Addin[] addins) + { + var roots = MakeDependencyTree (addins); + var node = treeStore.AddNode (); + + node.SetValue (labelField, "Root"); + int depth = BuildTree (node, roots, new HashSet<AddinNode> (), 1); + + summary.Text = $"Depth: {depth}"; + } + + int BuildTree (TreeNavigator currentPosition, IEnumerable<AddinNode> addins, HashSet<AddinNode> visited, int currentDepth) + { + int maxDepth = currentDepth; + + foreach (var addinNode in addins) { + if (!visited.Add (addinNode)) + continue; + + var node = currentPosition.Clone ().AddChild (); + node.SetValue (labelField, addinNode.Label); + + var childDepth = BuildTree (node, addinNode.Children, visited, currentDepth + 1); + maxDepth = Math.Max (maxDepth, childDepth); + } + + return maxDepth; + } + + List<AddinNode> MakeDependencyTree (Addin[] addins) + { + var cache = new Dictionary<Addin, AddinNode> (); + var roots = new List<AddinNode> (); + + foreach (var addin in addins) { + var addinNode = GetOrCreateNode (addin); + + if (addin.Description.IsRoot) + roots.Add (addinNode); + } + + foreach (var kvp in cache) { + var addin = kvp.Key; + var addinNode = kvp.Value; + + // TODO: handle optional dependencies and other modules + foreach (Dependency dep in addin.Description.MainModule.Dependencies) { + if (dep is AddinDependency adep) { + string adepid = Addin.GetFullId (addin.Namespace, adep.AddinId, adep.Version); + + var addinDep = AddinManager.Registry.GetAddin (adepid); + + cache [addinDep].Children.Add (addinNode); + } + } + } + + return roots; + + AddinNode GetOrCreateNode (Addin addin) + { + if (!cache.TryGetValue (addin, out var addinNode)) { + var addinLabel = Addin.GetIdName (addin.Id); + cache [addin] = addinNode = new AddinNode (addinLabel); + } + return addinNode; + } + } + + class AddinNode : IEquatable<AddinNode> + { + public HashSet<AddinNode> Children { get; } = new HashSet<AddinNode> (); + public string Label { get; } + + public AddinNode (string label) + { + Label = label; + } + + public bool Equals (AddinNode other) + { + return Label == other.Label; + } + } + } +} diff --git a/main/src/tools/ExtensionTools/AddinInfo.cs b/main/src/tools/ExtensionTools/AddinInfo.cs new file mode 100644 index 0000000000..2ca91262e8 --- /dev/null +++ b/main/src/tools/ExtensionTools/AddinInfo.cs @@ -0,0 +1,15 @@ + +using System; +using Mono.Addins; +using Mono.Addins.Description; + +[assembly:Addin ("ExtensionTool", + Namespace = "MonoDevelop", + Version = MonoDevelop.BuildInfo.Version, + Category = "MonoDevelop Core")] + +[assembly:AddinName ("Extension Developer Tools")] +[assembly:AddinDescription ("Tools used to analyze the extension model")] +[assembly:AddinFlags (AddinFlags.Hidden)] + +[assembly:AddinDependency ("Core", MonoDevelop.BuildInfo.Version)] diff --git a/main/src/tools/ExtensionTools/AddinListWidget.cs b/main/src/tools/ExtensionTools/AddinListWidget.cs new file mode 100644 index 0000000000..d842be1e14 --- /dev/null +++ b/main/src/tools/ExtensionTools/AddinListWidget.cs @@ -0,0 +1,68 @@ +// +// AddinListWidget.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Mono.Addins; +using Xwt; + +namespace MonoDevelop.ExtensionTools +{ + class AddinListWidget : Widget + { + readonly ListStore listStore; + readonly ListView listView; + readonly DataField<string> labelField = new DataField<string> (); + readonly Label summary = new Label (); + + public AddinListWidget (Addin[] addins = null) + { + addins = addins ?? AddinManager.Registry.GetAllAddins (x => x.Name); + + listStore = new ListStore (labelField); + listView = new ListView (listStore); + + var col = listView.Columns.Add ("Name", labelField); + col.Expands = true; + + FillData (addins); + + var vbox = new VBox (); + vbox.PackStart (summary, false); + vbox.PackStart (listView, true); + Content = vbox; + } + + void FillData (Addin[] addins) + { + summary.Text = $"Count: {addins.Length}"; + + foreach (var addin in addins) { + int row = listStore.AddRow (); + listStore.SetValue (row, labelField, addin.Name); + } + // TODO: clicking a node should open addin info tab + } + } +} diff --git a/main/src/tools/ExtensionTools/AddinRegistryExtensions.cs b/main/src/tools/ExtensionTools/AddinRegistryExtensions.cs new file mode 100644 index 0000000000..493c421d59 --- /dev/null +++ b/main/src/tools/ExtensionTools/AddinRegistryExtensions.cs @@ -0,0 +1,64 @@ +// +// AddinRegistryExtensions.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using Mono.Addins; + +namespace MonoDevelop.ExtensionTools +{ + static class AddinRegistryExtensions + { + public static Addin[] GetAllAddins (this AddinRegistry registry, Func<Addin, string> sortItemSelector = null) + { + if (sortItemSelector == null) + sortItemSelector = x => x.Id; + + var array = registry.GetModules (AddinSearchFlags.IncludeAll | AddinSearchFlags.LatestVersionsOnly); + + var comparer = new NameComparer (sortItemSelector); + Array.Sort (array, comparer); + + return array; + } + + class NameComparer : IComparer<Addin> + { + readonly Func<Addin, string> selector; + + public NameComparer (Func<Addin, string> selector) + { + this.selector = selector; + } + + public int Compare (Addin x, Addin y) + { + var xString = selector (x); + var yString = selector (y); + return string.Compare (xString, yString, StringComparison.Ordinal); + } + } + } +} diff --git a/main/src/tools/ExtensionTools/Application.cs b/main/src/tools/ExtensionTools/Application.cs new file mode 100644 index 0000000000..7403015edb --- /dev/null +++ b/main/src/tools/ExtensionTools/Application.cs @@ -0,0 +1,97 @@ +// +// MyClass.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Mono.Addins; +using MonoDevelop.Core; +using Xwt; + +namespace MonoDevelop.ExtensionTools +{ + public class Application: IApplication + { + // HACK: Make this proper + internal static LazyNotebook MainNotebook; + + public Task<int> Run (string[] arguments) + { + Xwt.Application.Initialize (ToolkitType.Gtk); +#if MAC + var dir = Path.GetDirectoryName (typeof (Application).Assembly.Location); + if (ObjCRuntime.Dlfcn.dlopen (Path.Combine (dir, "libxammac.dylib"), 0) == IntPtr.Zero) { + LoggingService.LogFatalError ("Unable to load libxammac"); + return Task.FromResult (1); + } +#endif + LoggingService.LogInfo ("Initialized toolkit"); + + Xwt.Toolkit.NativeEngine.Invoke (() => { + using (CreateWindow ()) { + LoggingService.LogInfo ("Showing main window"); + Xwt.Application.Run (); + } + }); + + return Task.FromResult (0); + } + + static Window CreateWindow () + { + var window = new Window { + Content = CreateWindowContent (), + Width = 800, + Height = 800, + }; + + window.Closed += (_, __) => Xwt.Application.Exit (); + window.Show (); + + return window; + } + + static Widget CreateWindowContent () + { + var nb = MainNotebook = new LazyNotebook (); + + foreach (var (widgetFunc, title) in GetTabs ()) { + nb.Add (widgetFunc, title); + } + + return nb; + } + + static IEnumerable<(Func<Widget> widgetFunc, string title)> GetTabs () + { + yield return (() => new AddinListWidget (), "List"); + yield return (() => new AddinDependencyTreeWidget (), "Dependency Tree"); + yield return (() => new ExtensionPointsWidget (), "Extension Points"); + } + } +} diff --git a/main/src/tools/ExtensionTools/ExtensionNodesWidget.cs b/main/src/tools/ExtensionTools/ExtensionNodesWidget.cs new file mode 100644 index 0000000000..d8a63369cc --- /dev/null +++ b/main/src/tools/ExtensionTools/ExtensionNodesWidget.cs @@ -0,0 +1,120 @@ +// +// ExtensionNodesWidget.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Addins; +using Mono.Addins.Description; +using Xwt; + +namespace MonoDevelop.ExtensionTools +{ + class ExtensionNodesWidget : Widget + { + readonly TreeStore treeStore; + readonly TreeView treeView; + readonly DataField<string> labelField = new DataField<string> (); + readonly Label summary = new Label (); + + public ExtensionNodesWidget (string path, Addin[] addins = null) + { + addins = addins ?? AddinManager.Registry.GetAllAddins (); + + treeStore = new TreeStore (labelField); + treeView = new TreeView (treeStore); + + var col = treeView.Columns.Add ("Name", labelField); + col.Expands = true; + + FillData (path, addins); + treeView.ExpandAll (); + + var vbox = new VBox (); + vbox.PackStart (summary, false); + vbox.PackStart (treeView, true); + Content = vbox; + } + + void FillData (string path, Addin[] addins) + { + // TODO: add group by addin support. + var allNodes = addins + .SelectMany (x => x.Description.AllModules) + .SelectMany (x => x.Extensions) + .Where (x => x.Path == path) + .Select (x => x.ExtensionNodes); + + var nav = treeStore.AddNode (); + + int maxDepth = 0; + int count = 0; + foreach (var node in allNodes) { + int depth = BuildTree (nav, node, 1, ref count); + maxDepth = Math.Max (maxDepth, depth); + } + + summary.Text = $"'{path}' Count: {count} Depth: {maxDepth}"; + } + + int BuildTree (TreeNavigator currentPosition, ExtensionNodeDescriptionCollection nodes, int currentDepth, ref int count) + { + int maxDepth = currentDepth; + + // TODO: insertbefore/after + + foreach (ExtensionNodeDescription node in nodes) { + count++; + var pos = currentPosition.Clone ().AddChild (); + + var label = GetLabelForNode (node); + pos.SetValue (labelField, label); + + var childDepth = BuildTree (pos, node.ChildNodes, currentDepth + 1, ref count); + maxDepth = Math.Max (maxDepth, childDepth); + } + + return maxDepth; + } + + string GetLabelForNode (ExtensionNodeDescription node) + { + if (node.IsCondition) { + var value = node.GetAttribute ("value"); + if (!string.IsNullOrEmpty (value)) + return $"Condition: {node.Id} == {value}"; + return $"Condition: {node.Id}"; + } + + var type = node.GetAttribute ("class"); + if (!string.IsNullOrEmpty (type)) + return type; + + return node.Id; + } + + // TODO: add full attribute visualization + } +} diff --git a/main/src/tools/ExtensionTools/ExtensionPointsWidget.cs b/main/src/tools/ExtensionTools/ExtensionPointsWidget.cs new file mode 100644 index 0000000000..0784222b45 --- /dev/null +++ b/main/src/tools/ExtensionTools/ExtensionPointsWidget.cs @@ -0,0 +1,97 @@ +// +// ExtensionPointsWidget.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Addins; +using Mono.Addins.Description; +using Xwt; +namespace MonoDevelop.ExtensionTools +{ + class ExtensionPointsWidget : Widget + { + readonly ListStore listStore; + readonly ListView listView; + readonly DataField<string> labelField = new DataField<string> (); + readonly Label summary = new Label (); + + public ExtensionPointsWidget (Addin[] addins = null) + { + addins = addins ?? AddinManager.Registry.GetAllAddins (); + + listStore = new ListStore (labelField); + listView = new ListView (listStore); + listView.RowActivated += ListView_RowActivated; + + var col = listView.Columns.Add ("Name", labelField); + col.Expands = true; + + FillData (addins); + + var vbox = new VBox (); + vbox.PackStart (summary, false); + vbox.PackStart (listView, true); + Content = vbox; + } + + void ListView_RowActivated (object sender, ListViewRowEventArgs e) + { + var value = listStore.GetValue (e.RowIndex, labelField); + + var window = new Window { + Content = new ExtensionNodesWidget (value), + Height = 800, + Width = 600, + }; + window.Show (); + } + + void FillData (Addin[] addins) + { + var points = GatherExtensionPoints (addins); + + summary.Text = $"Count: {points.Length}"; + + foreach (var point in points) { + int row = listStore.AddRow (); + listStore.SetValue (row, labelField, point); + } + } + + string[] GatherExtensionPoints (Addin[] addins) + { + var points = new HashSet<string> (); + + foreach (var addin in addins) { + foreach (ExtensionPoint extensionPoint in addin.Description.ExtensionPoints) { + points.Add (extensionPoint.Path); + } + } + + return points.ToSortedArray (); + } + } +} diff --git a/main/src/tools/ExtensionTools/ExtensionTools.csproj b/main/src/tools/ExtensionTools/ExtensionTools.csproj new file mode 100644 index 0000000000..5d45e69fc3 --- /dev/null +++ b/main/src/tools/ExtensionTools/ExtensionTools.csproj @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="..\..\..\MonoDevelop.props" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{E33F7901-AA45-4C25-9868-DBB6CC8DC40C}</ProjectGuid> + <TargetFrameworkVersion>$(MDFrameworkVersion)</TargetFrameworkVersion> + <OutputPath>..\..\..\build\bin</OutputPath> + <RootNamespace>MonoDevelop.ExtensionTools</RootNamespace> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " /> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'DebugMac|AnyCPU' " /> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " /> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'ReleaseMac|AnyCPU' " /> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="Xamarin.Mac" Condition=" '$(Configuration)' == 'DebugMac' OR '$(Configuration)' == 'ReleaseMac' "> + <HintPath>..\..\..\build\bin\Xamarin.Mac.dll</HintPath> + <Private>False</Private> + </Reference> + </ItemGroup> + <ItemGroup> + <Compile Include="Application.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + <Compile Include="AddinInfo.cs" /> + <Compile Include="AddinDependencyTreeWidget.cs" /> + <Compile Include="AddinListWidget.cs" /> + <Compile Include="AddinRegistryExtensions.cs" /> + <Compile Include="ExtensionPointsWidget.cs" /> + <Compile Include="LazyNotebook.cs" /> + <Compile Include="ExtensionNodesWidget.cs" /> + <Compile Include="HashSetExtensions.cs" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> + <Project>{7525BB88-6142-4A26-93B9-A30C6983390A}</Project> + <Name>MonoDevelop.Core</Name> + <Private>False</Private> + </ProjectReference> + <ProjectReference Include="..\..\..\external\mono-addins\Mono.Addins\Mono.Addins.csproj"> + <Project>{91DD5A2D-9FE3-4C3C-9253-876141874DAD}</Project> + <Name>Mono.Addins</Name> + <Private>False</Private> + </ProjectReference> + <ProjectReference Include="..\..\..\external\xwt\Xwt\Xwt.csproj"> + <Project>{92494904-35FA-4DC9-BDE9-3A3E87AC49D3}</Project> + <Name>Xwt</Name> + <Private>False</Private> + </ProjectReference> + </ItemGroup> + <ItemGroup> + <EmbeddedResource Include="MonoDevelop.ExtensionTools.addin.xml" /> + </ItemGroup> + <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> +</Project>
\ No newline at end of file diff --git a/main/src/tools/ExtensionTools/HashSetExtensions.cs b/main/src/tools/ExtensionTools/HashSetExtensions.cs new file mode 100644 index 0000000000..138ee4c378 --- /dev/null +++ b/main/src/tools/ExtensionTools/HashSetExtensions.cs @@ -0,0 +1,41 @@ +// +// HashSetExtensions.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MonoDevelop.ExtensionTools +{ + static class HashSetExtensions + { + public static T[] ToSortedArray<T> (this HashSet<T> set) + { + var arr = set.ToArray (); + Array.Sort (arr); + return arr; + } + } +} diff --git a/main/src/tools/ExtensionTools/LazyNotebook.cs b/main/src/tools/ExtensionTools/LazyNotebook.cs new file mode 100644 index 0000000000..bdba85338c --- /dev/null +++ b/main/src/tools/ExtensionTools/LazyNotebook.cs @@ -0,0 +1,71 @@ +// +// LazyNotebook.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System; +using Xwt; + +namespace MonoDevelop.ExtensionTools +{ + class LazyNotebook : Notebook + { + public void Add (Func<Widget> createWidget, string label) + { + var createWidgetChain = OnAdd (createWidget); + var widget = new LazyWidget (createWidgetChain); + Add (widget, label); + } + + protected override void OnCurrentTabChanged (EventArgs e) + { + base.OnCurrentTabChanged (e); + Xwt.Application.TimeoutInvoke (0, () => { + Toolkit.NativeEngine.Invoke (() => { + var tab = Tabs [CurrentTabIndex]; + if (tab.Child is LazyWidget lazy) + lazy.CreateContent (); + }); + return false; + }); + } + + protected virtual Func<Widget> OnAdd (Func<Widget> createWidget) => createWidget; + + class LazyWidget : Widget + { + readonly Func<Widget> createWidget; + + public LazyWidget (Func<Widget> createWidget) + { + this.createWidget = createWidget; + } + + public void CreateContent () + { + if (Content == null) + Content = createWidget (); + } + } + } +} diff --git a/main/src/tools/ExtensionTools/MonoDevelop.ExtensionTools.addin.xml b/main/src/tools/ExtensionTools/MonoDevelop.ExtensionTools.addin.xml new file mode 100644 index 0000000000..25998713d8 --- /dev/null +++ b/main/src/tools/ExtensionTools/MonoDevelop.ExtensionTools.addin.xml @@ -0,0 +1,7 @@ +<ExtensionModel> + + <Extension path = "/MonoDevelop/Core/Applications"> + <Application id = "extension-tool" class = "MonoDevelop.ExtensionTools.Application" description = "Developer overview over the extension model"/> + </Extension> + +</ExtensionModel> diff --git a/main/src/tools/ExtensionTools/Properties/AssemblyInfo.cs b/main/src/tools/ExtensionTools/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..061f89aac8 --- /dev/null +++ b/main/src/tools/ExtensionTools/Properties/AssemblyInfo.cs @@ -0,0 +1,51 @@ +// +// AssemblyInfo.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 Microsoft Inc. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle ("ExtensionTools")] +[assembly: AssemblyDescription ("")] +[assembly: AssemblyConfiguration ("")] +[assembly: AssemblyCompany ("Microsoft")] +[assembly: AssemblyProduct ("")] +[assembly: AssemblyCopyright ("Microsoft Inc.")] +[assembly: AssemblyTrademark ("")] +[assembly: AssemblyCulture ("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion ("1.0.0")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide.Composition/CompositionManager.CachingTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide.Composition/CompositionManager.CachingTests.cs index 74d5219df9..a198a31855 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide.Composition/CompositionManager.CachingTests.cs +++ b/main/tests/Ide.Tests/MonoDevelop.Ide.Composition/CompositionManager.CachingTests.cs @@ -26,6 +26,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading.Tasks; @@ -38,35 +39,28 @@ namespace MonoDevelop.Ide.Composition [TestFixture] public class CompositionManagerCachingTests { - internal class NewerStampCachingFaultInjector : CompositionManager.ICachingFaultInjector - { - public void FaultAssemblyInfo (CompositionManager.MefControlCacheAssemblyInfo info) - { - info.LastWriteTimeUtc = info.LastWriteTimeUtc.Subtract (TimeSpan.FromSeconds (1)); - } - } - - internal class OlderStampCachingFaultInjector : CompositionManager.ICachingFaultInjector + internal class LocationCachingFaultInjector : CompositionManager.ICachingFaultInjector { public void FaultAssemblyInfo (CompositionManager.MefControlCacheAssemblyInfo info) { - info.LastWriteTimeUtc = info.LastWriteTimeUtc.Add (TimeSpan.FromSeconds (1)); + // Change one of the assemblies' path + info.Location = typeof (LocationCachingFaultInjector).Assembly.Location; } } - internal class LocationCachingFaultInjector : CompositionManager.ICachingFaultInjector + internal class ModuleVersionIdCachingFaultInjector : CompositionManager.ICachingFaultInjector { public void FaultAssemblyInfo (CompositionManager.MefControlCacheAssemblyInfo info) { - // Change one of the assemblies' paths - info.Location = typeof (LocationCachingFaultInjector).Assembly.Location; + // Change one of the assemblies' mvid + info.ModuleVersionId = Guid.NewGuid (); } } static CompositionManager.Caching GetCaching (CompositionManager.ICachingFaultInjector faultInjector = null, Action<string> onCacheFileRequested = null, [CallerMemberName] string testName = null) { - var assemblies = CompositionManager.ReadAssembliesFromAddins (); - var caching = new CompositionManager.Caching (assemblies, file => { + var mefAssemblies = CompositionManager.ReadAssembliesFromAddins (); + var caching = new CompositionManager.Caching (mefAssemblies, file => { onCacheFileRequested?.Invoke (file); var tmpDir = Path.Combine (Util.TmpDir, "mef", testName); @@ -82,10 +76,10 @@ namespace MonoDevelop.Ide.Composition static async Task<RuntimeComposition> CreateAndWrite (CompositionManager.Caching caching) { - var composition = await CompositionManager.CreateRuntimeCompositionFromDiscovery (caching); + var (composition, catalog) = await CompositionManager.CreateRuntimeCompositionFromDiscovery (caching); var cacheManager = new CachedComposition (); - await caching.Write (composition, cacheManager); + await caching.Write (composition, catalog, cacheManager); return composition; } @@ -233,27 +227,41 @@ namespace MonoDevelop.Ide.Composition public async Task TestControlCacheFileStaleList () { var caching = GetCaching (); - var composition = await CompositionManager.CreateRuntimeCompositionFromDiscovery (caching); + var (composition, catalog) = await CompositionManager.CreateRuntimeCompositionFromDiscovery (caching); var cacheManager = new CachedComposition (); - await caching.Write (composition, cacheManager); + await caching.Write (composition, catalog, cacheManager); - caching.Assemblies.Add (typeof (Console).Assembly); + caching.MefAssemblies.Add (typeof (CompositionManagerCachingTests).Assembly); Assert.IsFalse (caching.CanUse ()); } - [TestCase (typeof (OlderStampCachingFaultInjector))] - [TestCase (typeof (NewerStampCachingFaultInjector))] + [Test] + public async Task TestCacheControlDataIntegrity () + { + var caching = GetCaching (); + + var (composition, catalog) = await CompositionManager.CreateRuntimeCompositionFromDiscovery (caching); + + var inputAssemblies = catalog.GetInputAssemblies ().Select (x => x.ToString ()).ToArray (); + Assert.That (caching.MefAssemblies, Contains.Item (typeof (CompositionManager).Assembly)); + Assert.That (inputAssemblies, Contains.Item (typeof (CompositionManager).Assembly.GetName ().ToString ())); + + Assert.That (caching.MefAssemblies, Is.Not.Contains (typeof (Console).Assembly)); + Assert.That (inputAssemblies, Contains.Item (typeof (Console).Assembly.GetName ().ToString ())); + } + [TestCase (typeof (LocationCachingFaultInjector))] + [TestCase (typeof (ModuleVersionIdCachingFaultInjector))] public async Task TestControlCacheFaultInjection (Type injectorType) { var injector = (CompositionManager.ICachingFaultInjector)Activator.CreateInstance (injectorType); var caching = GetCaching (injector); - var composition = await CompositionManager.CreateRuntimeCompositionFromDiscovery (caching); + var (composition, catalog) = await CompositionManager.CreateRuntimeCompositionFromDiscovery (caching); var cacheManager = new CachedComposition (); - await caching.Write (composition, cacheManager); + await caching.Write (composition, catalog, cacheManager); Assert.IsFalse (caching.CanUse ()); } diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Assemblies/SystemAssemblyServiceTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Assemblies/SystemAssemblyServiceTests.cs index 184c23f1f1..8775319221 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Assemblies/SystemAssemblyServiceTests.cs +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Assemblies/SystemAssemblyServiceTests.cs @@ -1,4 +1,4 @@ -// +// // SystemAssemblyServiceTests.cs // // Author: @@ -25,6 +25,7 @@ // THE SOFTWARE. using System; using System.IO; +using System.Linq; using System.Threading.Tasks; using MonoDevelop.Projects; using NUnit.Framework; @@ -35,38 +36,9 @@ namespace MonoDevelop.Core.Assemblies [TestFixture] public class SystemAssemblyServiceTests { - static string GetDllPath(string dllName) - { - var directory = Path.GetDirectoryName (typeof(Runtime).Assembly.Location); - return Path.Combine (directory, dllName); - } - - [TestCase(true, "Humanizer.dll")] - [TestCase(false, "MonoDevelop.Core.dll")] - public void ImmutableCollectionsContainReferenceToSystemRuntime (bool withSystemRuntime, string relativeDllPath) - { - var result = SystemAssemblyService.ContainsReferenceToSystemRuntime(GetDllPath (relativeDllPath)); - Assert.That(result, Is.EqualTo(withSystemRuntime)); - } - - [TestCase(true, "Humanizer.dll")] - [TestCase(false, "MonoDevelop.Core.dll")] - public async Task ImmutableCollectionsContainReferenceToSystemRuntimeAsync (bool withSystemRuntime, string relativeDllPath) - { - var result = await SystemAssemblyService.ContainsReferenceToSystemRuntimeAsync(GetDllPath (relativeDllPath)); - Assert.That(result, Is.EqualTo(withSystemRuntime)); - } - - [TestCase (true, "System.Collections.Immutable.dll")] - [TestCase (false, "MonoDevelop.Core.dll")] - public void RequiresFacadeAssemblies (bool addFacades, string relativeDllPath) - { - var result = SystemAssemblyService.RequiresFacadeAssemblies (GetDllPath (relativeDllPath)); - Assert.That (result, Is.EqualTo (addFacades)); - } - [TestCase (true, "System.Collections.Immutable.dll")] [TestCase (false, "MonoDevelop.Core.dll")] + [TestCase (false, "NonExistingDll.dll")] public async Task RequiresFacadeAssembliesAsync (bool addFacades, string relativeDllPath) { var result = await SystemAssemblyService.RequiresFacadeAssembliesAsync (GetDllPath (relativeDllPath)); @@ -86,5 +58,51 @@ namespace MonoDevelop.Core.Assemblies var references = SystemAssemblyService.GetAssemblyReferences(cecilPath); Assert.That(references, Is.EquivalentTo(names)); } + + [Test] + public void CheckAssemblyReferences () + { + var monoAddinsPath = GetDllPath ("Mono.Addins.dll"); + var result = SystemAssemblyService.GetAssemblyReferences (monoAddinsPath); + + Assert.AreEqual (4, result.Length); + Assert.That (result, Contains.Item ("mscorlib")); + Assert.That (result, Contains.Item ("System")); + Assert.That (result, Contains.Item ("System.Core")); + Assert.That (result, Contains.Item ("System.Xml")); + } + + [Test] + public void GetManifestResources () + { + var mdCorePath = GetDllPath ("MonoDevelop.Core.dll"); + var result = SystemAssemblyService.GetAssemblyManifestResources (mdCorePth).ToArray (); + + Assert.That (result.Length, Is.GreaterThanOrEqualTo (1)); + + var addinXml = result.SingleOrDefault (x => x.Name == "MonoDevelop.Core.addin.xml"); + Assert.IsNotNull (addinXml); + + string fromReader, actual; + + using (var streamReader = new StreamReader (addinXml.Open ())) { + fromReader = streamReader.ReadToEnd (); + } + using (var streamReader = new StreamReader (typeof (SystemAssemblyService).Assembly.GetManifestResourceStream ("MonoDevelop.Core.addin.xml"))) { + actual = streamReader.ReadToEnd (); + } + + Assert.AreEqual (actual, fromReader); + } + + [Test] + public void TestFrameworkVersion () + { + var xwtPath = GetDllPath ("Xwt.dll"); + var result = new SystemAssemblyService ().GetTargetFrameworkForAssembly (null, xwtPath); + + Assert.AreEqual (TargetFrameworkMoniker.ID_NET_FRAMEWORK, result.Identifier); + Assert.AreEqual ("4.0", result.Version); + } } } diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj index b2f1c84d33..d6ef7f69db 100644 --- a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core.Tests.csproj @@ -83,6 +83,7 @@ <Compile Include="MonoDevelop.Core.Instrumentation\BucketTimingsTests.cs" /> <Compile Include="MonoDevelop.Utilities\RaceCheckerTests.cs" /> <Compile Include="MonoDevelop.Core\FeatureSwitchServiceTests.cs" /> + <Compile Include="MonoDevelop.Core\SdkResolverTests.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\src\core\MonoDevelop.Core\MonoDevelop.Core.csproj"> diff --git a/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/SdkResolverTests.cs b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/SdkResolverTests.cs new file mode 100644 index 0000000000..729051a4f0 --- /dev/null +++ b/main/tests/MonoDevelop.Core.Tests/MonoDevelop.Core/SdkResolverTests.cs @@ -0,0 +1,107 @@ +// +// SdkResolverTests.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2019 Microsoft +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.Build.Framework; +using MonoDevelop.Projects.MSBuild; +using NUnit.Framework; +using UnitTests; + +namespace MonoDevelop.Core +{ + [TestFixture] + public class SdkResolverTests : TestBase + { + /// <summary> + /// Tests that the MSBuild version is passed to sdk resolvers. The DotNetMSBuildSdkResolver throws an + /// ArgumentNullException if it is not set. Have to use an unknown sdk otherwise other resolvers will + /// be used and the DotNetMSBuildSdkResolver will not be used. + /// </summary> + [Test] + public void UnknownSdk_DotNetMSBuildSdkResolverDoesNotFatalReportError () + { + var resolution = SdkResolution.GetResolver (Runtime.SystemAssemblyService.CurrentRuntime); + // Using a sdk with an invalid version to prevent the NuGet sdk resolver causing a test crash. + // Invalid version numbers cause the NuGet sdk resolver to not try to resolve the sdk. The crash + // only seems to happen with this test - using the IDE does not trigger the crash. There is a + // separate NuGet sdk resolver test that runs the resolver. Crash error: + // NuGet.Configuration.NuGetPathContext doesn't implement interface NuGet.Common.INuGetPathContext + var sdkReference = new SdkReference ("MonoDevelop.Unknown.Test.Sdk", "InvalidVersion", null); + var logger = new TestLoggingService (); + var context = new MSBuildContext (); + var result = resolution.GetSdkPath (sdkReference, logger, context, null, null); + + var error = logger.FatalBuildErrors.FirstOrDefault (); + Assert.AreEqual (0, logger.FatalBuildErrors.Count, "First error: " + error); + Assert.IsNull (result); + } + + [Test] + public void SdkReference_DifferentCase_StillFindsMatchWithMonoDevelopResolver () + { + var sdks = new List<SdkInfo> (); + var info = new SdkInfo ("MonoDevelop.Test.NET.Sdk", SdkVersion.Parse ("1.2.3"), null); + sdks.Add (info); + + var resolver = new Resolver (() => sdks); + + var sdkReference = new SdkReference ("monodevelop.test.net.sdk", null, null); + var factory = new SdkResolution.SdkResultFactoryImpl (sdkReference); + var result = resolver.Resolve (sdkReference, null, factory); + + Assert.IsTrue (result.Success); + Assert.AreEqual (result.Version, info.Version.ToString ()); + } + + class TestLoggingService : ILoggingService + { + public List<Exception> FatalBuildErrors = new List<Exception> (); + + public void LogCommentFromText (MSBuildContext buildEventContext, MessageImportance messageImportance, string message) + { + } + + public void LogErrorFromText (MSBuildContext buildEventContext, object subcategoryResourceName, object errorCode, object helpKeyword, string file, string message) + { + } + + public void LogFatalBuildError (MSBuildContext buildEventContext, Exception e, string projectFile) + { + FatalBuildErrors.Add (e); + } + + public void LogWarning (string message) + { + } + + public void LogWarningFromText (MSBuildContext bec, object p1, object p2, object p3, string projectFile, string warning) + { + } + } + } +} diff --git a/main/tests/StressTest/Mono.Profiling.Log/LogEnums.cs b/main/tests/StressTest/Mono.Profiling.Log/LogEnums.cs index 8d9c7fb33f..e5535f9fb8 100644 --- a/main/tests/StressTest/Mono.Profiling.Log/LogEnums.cs +++ b/main/tests/StressTest/Mono.Profiling.Log/LogEnums.cs @@ -66,6 +66,7 @@ namespace Mono.Profiler.Log { RuntimeJitHelper = 1 << 4, MetaSynchronizationPoint = 0 << 4, + MetaAotId = 1 << 4, } // mono/profiler/log.h : TYPE_* @@ -122,17 +123,18 @@ namespace Mono.Profiler.Log { // mono/metadata/profiler.h : MonoProfilerCodeBufferType public enum LogJitHelper { - Unknown = 0, - Method = 1, - MethodTrampoline = 2, - UnboxTrampoline = 3, - ImtTrampoline = 4, - GenericsTrampoline = 5, - SpecificTrampoline = 6, - Helper = 7, - Monitor = 8, - DelegateInvoke = 9, - ExceptionHandling = 10, + Method = 0, + [Obsolete ("This value is no longer produced.")] + MethodTrampoline = 1, + UnboxTrampoline = 2, + ImtTrampoline = 3, + GenericsTrampoline = 4, + SpecificTrampoline = 5, + Helper = 6, + [Obsolete ("This value is no longer produced.")] + Monitor = 7, + DelegateInvoke = 8, + ExceptionHandling = 9, } // mono/metadata/profiler.h : MonoProfilerGCRootType @@ -168,7 +170,8 @@ namespace Mono.Profiler.Log { Marshal = 11, ThreadPool = 12, Debugger = 13, - RuntimeHandle = 14, + Handle = 14, + Ephemeron = 15, } // mono/profiler/log.h : MonoProfilerMonitorEvent diff --git a/main/tests/StressTest/Mono.Profiling.Log/LogEventVisitor.cs b/main/tests/StressTest/Mono.Profiling.Log/LogEventVisitor.cs index 79d6140523..1e5f338839 100644 --- a/main/tests/StressTest/Mono.Profiling.Log/LogEventVisitor.cs +++ b/main/tests/StressTest/Mono.Profiling.Log/LogEventVisitor.cs @@ -189,5 +189,9 @@ namespace Mono.Profiler.Log { public virtual void Visit (SynchronizationPointEvent ev) { } + + public virtual void Visit (AotIdEvent ev) + { + } } } diff --git a/main/tests/StressTest/Mono.Profiling.Log/LogEvents.cs b/main/tests/StressTest/Mono.Profiling.Log/LogEvents.cs index ba6887c0c7..16a216a44f 100644 --- a/main/tests/StressTest/Mono.Profiling.Log/LogEvents.cs +++ b/main/tests/StressTest/Mono.Profiling.Log/LogEvents.cs @@ -101,6 +101,8 @@ namespace Mono.Profiler.Log { public string Name { get; internal set; } + public Guid ModuleVersionId { get; internal set; } + internal override void Accept (LogEventVisitor visitor) { visitor.Visit (this); @@ -260,6 +262,8 @@ namespace Mono.Profiler.Log { public long ObjectSize { get; internal set; } + public int Generation { get; internal set; } + public IReadOnlyList<HeapObjectReference> References { get; internal set; } internal override void Accept (LogEventVisitor visitor) @@ -272,7 +276,7 @@ namespace Mono.Profiler.Log { public struct HeapRoot { - public long AddressPointer { get; internal set; } + public long SlotPointer { get; internal set; } public long ObjectPointer { get; internal set; } @@ -314,7 +318,7 @@ namespace Mono.Profiler.Log { public sealed class HeapRootUnregisterEvent : LogEvent { - public long StartPointer { get; internal set; } + public long RootPointer { get; internal set; } internal override void Accept (LogEventVisitor visitor) { @@ -326,7 +330,7 @@ namespace Mono.Profiler.Log { public LogGCEvent Type { get; internal set; } - public byte Generation { get; internal set; } + public int Generation { get; internal set; } internal override void Accept (LogEventVisitor visitor) { @@ -554,6 +558,7 @@ namespace Mono.Profiler.Log { } } + [Obsolete ("This event is no longer produced.")] public sealed class UnmanagedBinaryEvent : LogEvent { public long SegmentPointer { get; internal set; } @@ -593,4 +598,14 @@ namespace Mono.Profiler.Log { visitor.Visit (this); } } + + public sealed class AotIdEvent : LogEvent { + + public Guid AotId { get; internal set; } + + internal override void Accept (LogEventVisitor visitor) + { + visitor.Visit (this); + } + } } diff --git a/main/tests/StressTest/Mono.Profiling.Log/LogProcessor.cs b/main/tests/StressTest/Mono.Profiling.Log/LogProcessor.cs index 161199b2fc..e792b1dfb3 100644 --- a/main/tests/StressTest/Mono.Profiling.Log/LogProcessor.cs +++ b/main/tests/StressTest/Mono.Profiling.Log/LogProcessor.cs @@ -52,6 +52,16 @@ namespace Mono.Profiler.Log { } } + void ProcessEvents (List<LogEvent> events, CancellationToken token) + { + foreach (var ev in events.OrderBy (x => x.Timestamp)) { + token.ThrowIfCancellationRequested (); + ProcessEvent (SortedVisitor, ev); + } + + events.Clear (); + } + public void Process (CancellationToken token) { if (_used) @@ -62,6 +72,8 @@ namespace Mono.Profiler.Log { StreamHeader = new LogStreamHeader (_reader); + var events = new List<LogEvent> (Environment.ProcessorCount * 1000); + while (!Stream.EndOfStream) { token.ThrowIfCancellationRequested (); @@ -87,11 +99,17 @@ namespace Mono.Profiler.Log { var ev = ReadEvent (); ProcessEvent (ImmediateVisitor, ev); + events.Add (ev); + + if (ev is SynchronizationPointEvent) + ProcessEvents (events, token); } _reader = oldReader; } } + + ProcessEvents (events, token); } LogEvent ReadEvent () @@ -214,10 +232,18 @@ namespace Mono.Profiler.Log { break; case LogMetadataType.Image: if (load) { - ev = new ImageLoadEvent { + var ile = new ImageLoadEvent { ImagePointer = ReadPointer (), Name = _reader.ReadCString (), }; + + if (StreamHeader.FormatVersion >= 16) { + var guid = _reader.ReadCString (); + + ile.ModuleVersionId = guid == string.Empty ? Guid.Empty : Guid.Parse (guid); + } + + ev = ile; } else if (unload) { ev = new ImageUnloadEvent { ImagePointer = ReadPointer (), @@ -390,6 +416,7 @@ namespace Mono.Profiler.Log { ClassPointer = StreamHeader.FormatVersion < 15 ? ReadPointer () : 0, VTablePointer = StreamHeader.FormatVersion >= 15 ? ReadPointer () : 0, ObjectSize = (long) _reader.ReadULeb128 (), + Generation = StreamHeader.FormatVersion >= 16 ? _reader.ReadByte () : 0, }; var list = new HeapObjectEvent.HeapObjectReference [(int) _reader.ReadULeb128 ()]; @@ -416,7 +443,7 @@ namespace Mono.Profiler.Log { for (var i = 0; i < list.Length; i++) { list [i] = new HeapRootsEvent.HeapRoot { - AddressPointer = StreamHeader.FormatVersion >= 15 ? ReadPointer () : 0, + SlotPointer = StreamHeader.FormatVersion >= 15 ? ReadPointer () : 0, ObjectPointer = ReadObject (), Attributes = StreamHeader.FormatVersion < 15 ? (StreamHeader.FormatVersion == 13 ? @@ -443,7 +470,7 @@ namespace Mono.Profiler.Log { break; case LogEventType.HeapRootUnregister: ev = new HeapRootUnregisterEvent { - StartPointer = ReadPointer (), + RootPointer = ReadPointer (), }; break; default: @@ -557,6 +584,9 @@ namespace Mono.Profiler.Log { case LogEventType.RuntimeJitHelper: { var helperType = (LogJitHelper) _reader.ReadByte (); + if (StreamHeader.FormatVersion < 14) + helperType--; + ev = new JitHelperEvent { Type = helperType, BufferPointer = ReadPointer (), @@ -576,6 +606,11 @@ namespace Mono.Profiler.Log { Type = (LogSynchronizationPoint) _reader.ReadByte (), }; break; + case LogEventType.MetaAotId: + ev = new AotIdEvent { + AotId = Guid.Parse (_reader.ReadCString ()), + }; + break; default: throw new LogException ($"Invalid extended event type ({extType})."); } diff --git a/main/tests/StressTest/Mono.Profiling.Log/LogStreamHeader.cs b/main/tests/StressTest/Mono.Profiling.Log/LogStreamHeader.cs index aa86b75444..e00030c493 100644 --- a/main/tests/StressTest/Mono.Profiling.Log/LogStreamHeader.cs +++ b/main/tests/StressTest/Mono.Profiling.Log/LogStreamHeader.cs @@ -9,7 +9,8 @@ namespace Mono.Profiler.Log { public sealed class LogStreamHeader { const int MinVersion = 13; - const int MaxVersion = 15; + + const int MaxVersion = 17; const int Id = 0x4d505a01; @@ -21,6 +22,8 @@ namespace Mono.Profiler.Log { public ulong StartupTime { get; } + public ulong TimestampStartupTime { get; } + public int TimerOverhead { get; } public int Flags { get; } @@ -50,6 +53,10 @@ namespace Mono.Profiler.Log { PointerSize = reader.ReadByte (); StartupTime = reader.ReadUInt64 (); + + if (Version.Major >= 3) + TimestampStartupTime = reader.ReadUInt64 (); + TimerOverhead = reader.ReadInt32 (); Flags = reader.ReadInt32 (); ProcessId = reader.ReadInt32 (); diff --git a/main/tests/StressTest/MonoDevelop.StressTest/ProfilerProcessor.cs b/main/tests/StressTest/MonoDevelop.StressTest/ProfilerProcessor.cs index 6b5c2548f4..7f94b27d4c 100644 --- a/main/tests/StressTest/MonoDevelop.StressTest/ProfilerProcessor.cs +++ b/main/tests/StressTest/MonoDevelop.StressTest/ProfilerProcessor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -166,9 +166,9 @@ namespace MonoDevelop.StressTest { switch (Options.Type) { case StressTestOptions.ProfilerOptions.ProfilerType.HeapOnly: - return $"--profile=log:heapshot=ondemand,noalloc,nocalls,maxframes=0,output=\"{Options.MlpdOutputPath}\""; + return $"--profile=log:nodefaults,heapshot=ondemand,output=\"{Options.MlpdOutputPath}\""; case StressTestOptions.ProfilerOptions.ProfilerType.All: - return $"--profile=log:heapshot=ondemand,alloc,nocalls,maxframes={Options.MaxFrames},output=\"{Options.MlpdOutputPath}\""; + return $"--profile=log:nodefaults,heapshot-on-shutdown,heapshot=ondemand,gcalloc,gcmove,gcroot,counter,maxframes={Options.MaxFrames},output=\"{Options.MlpdOutputPath}\""; case StressTestOptions.ProfilerOptions.ProfilerType.Custom: return Options.CustomProfilerArguments; default: diff --git a/main/tests/StressTest/MonoDevelop.StressTest/StressTestApp.cs b/main/tests/StressTest/MonoDevelop.StressTest/StressTestApp.cs index f46f99016c..557c299009 100644 --- a/main/tests/StressTest/MonoDevelop.StressTest/StressTestApp.cs +++ b/main/tests/StressTest/MonoDevelop.StressTest/StressTestApp.cs @@ -1,4 +1,4 @@ -// +// // StressTestApp.cs // // Author: @@ -67,6 +67,8 @@ namespace MonoDevelop.StressTest if (ProfilerOptions.Type != StressTestOptions.ProfilerOptions.ProfilerType.Disabled) { if (ProfilerOptions.MlpdOutputPath == null) ProfilerOptions.MlpdOutputPath = Path.Combine (profilePath, "profiler.mlpd"); + if (File.Exists (ProfilerOptions.MlpdOutputPath)) + File.Delete (ProfilerOptions.MlpdOutputPath); profilerProcessor = new ProfilerProcessor (ProfilerOptions); string monoPath = Environment.GetEnvironmentVariable ("PATH") .Split (Path.PathSeparator) @@ -83,6 +85,7 @@ namespace MonoDevelop.StressTest scenario = TestScenarioProvider.GetTestScenario (); + ReportMemoryUsage (-1); for (int i = 0; i < Iterations; ++i) { scenario.Run (); ReportMemoryUsage (i); diff --git a/main/tests/StressTest/StressTest.csproj b/main/tests/StressTest/StressTest.csproj index 45559639c6..29ede46fa2 100644 --- a/main/tests/StressTest/StressTest.csproj +++ b/main/tests/StressTest/StressTest.csproj @@ -5,7 +5,7 @@ <ProjectGuid>{A1F9B020-6573-4BB2-A887-7F26561A9D18}</ProjectGuid> <OutputType>Exe</OutputType> <RootNamespace>MonoDevelop.StressTest</RootNamespace> - <TargetFrameworkVersion>4.6.1</TargetFrameworkVersion> + <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' "> <DebugSymbols>true</DebugSymbols> @@ -95,6 +95,6 @@ </ItemGroup> <Copy SourceFiles="@(TestProjectFiles)" DestinationFolder="$(OutputPath)\TestProject\%(RecursiveDir)" SkipUnchangedFiles="true" /> <Copy SourceFiles="@(MonoAddinsFiles)" DestinationFolder="$(OutputPath)\TestProject\mono-addins\Mono.Addins\%(RecursiveDir)" SkipUnchangedFiles="true" /> - <Copy SourceFiles="$(MonoAddinsDir)\Version.props;$(MonoAddinsDir)\mono-addins.snk" DestinationFolder="$(OutputPath)\TestProject\mono-addins" SkipUnchangedFiles="true" /> + <Copy SourceFiles="$(MonoAddinsDir)\Version.props;$(MonoAddinsDir)\TargetFrameworks.props;$(MonoAddinsDir)\mono-addins.snk" DestinationFolder="$(OutputPath)\TestProject\mono-addins" SkipUnchangedFiles="true" /> </Target> </Project>
\ No newline at end of file diff --git a/main/tests/test-projects/restore-netcore-offline/dotnetcoreconsole.csproj b/main/tests/test-projects/restore-netcore-offline/dotnetcoreconsole.csproj index 21dff5ca2e..23df6047ff 100755 --- a/main/tests/test-projects/restore-netcore-offline/dotnetcoreconsole.csproj +++ b/main/tests/test-projects/restore-netcore-offline/dotnetcoreconsole.csproj @@ -2,7 +2,7 @@ <PropertyGroup> <OutputType>Exe</OutputType> - <TargetFramework>netcoreapp2.2</TargetFramework> + <TargetFramework>netcoreapp2.1</TargetFramework> </PropertyGroup> </Project> |