diff options
author | David Karlaš <david.karlas@microsoft.com> | 2018-02-27 21:01:17 +0300 |
---|---|---|
committer | David Karlaš <david.karlas@microsoft.com> | 2018-02-27 21:01:17 +0300 |
commit | ba7091918ed939889a1892fcac4dfa4725fc5b4a (patch) | |
tree | 47400acc6db48458af80a3f848bf3aabd626749e /main/src | |
parent | 035ab975ea02b0ef52a11f4839cafdc402dc9a2b (diff) | |
parent | 3fb90913786983ac9a62c28aa0d0942a91a9a921 (diff) |
Merge master into editorFeatures
Diffstat (limited to 'main/src')
121 files changed, 1889 insertions, 547 deletions
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.csproj b/main/src/addins/CSharpBinding/CSharpBinding.csproj index a0321335e6..dedf92b2be 100644 --- a/main/src/addins/CSharpBinding/CSharpBinding.csproj +++ b/main/src/addins/CSharpBinding/CSharpBinding.csproj @@ -1,4 +1,4 @@ -<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> <Import Project="..\..\..\MonoDevelop.props" /> <Import Project="$(ReferencesVSEditor)" /> <Import Project="$(ReferencesRoslyn)" /> @@ -385,6 +385,8 @@ <Compile Include="MonoDevelop.CSharp.Completion\CSharpCompletionData.cs" /> <Compile Include="MonoDevelop.CSharp.Completion\CompletionProvider\ProtocolMemberCompletionProvider.cs" /> <Compile Include="MonoDevelop.CSharp.Tooltips\QuickInfoProvider.cs" /> + <Compile Include="MonoDevelop.CSharp.Navigation\Counters.cs" /> + <Compile Include="MonoDevelop.CSharp.Project\CSharpLanguageVersionHelper.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/AbstractGenerateAction.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/AbstractGenerateAction.cs index c51a585252..6a49c74505 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/AbstractGenerateAction.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/AbstractGenerateAction.cs @@ -38,6 +38,8 @@ using MonoDevelop.Ide.Editor; using MonoDevelop.Core.Text; using MonoDevelop.CSharp.Completion; using MonoDevelop.CSharp.Formatting; +using Microsoft.CodeAnalysis.Formatting; +using MonoDevelop.Core; namespace MonoDevelop.CodeGeneration { @@ -119,12 +121,12 @@ namespace MonoDevelop.CodeGeneration static string AddIndent (string text, string indent) { var doc = TextEditorFactory.CreateNewReadonlyDocument (new StringTextSource (text), ""); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); foreach (var line in doc.GetLines ()) { result.Append (indent); result.Append (doc.GetTextAt (line.SegmentIncludingDelimiter)); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public void GenerateCode (Gtk.TreeView treeView) @@ -143,7 +145,7 @@ namespace MonoDevelop.CodeGeneration includedMembers.Add (store.GetValue (iter, 3)); } } - var output = new StringBuilder (); + var output = StringBuilderCache.Allocate (); string indent = options.Editor.GetVirtualIndentationString (options.Editor.CaretLine); foreach (string nodeText in GenerateCode (includedMembers)) { if (output.Length > 0) { @@ -157,7 +159,7 @@ namespace MonoDevelop.CodeGeneration var data = options.Editor; data.EnsureCaretIsNotVirtual (); int offset = data.CaretOffset; - var text = output.ToString ().TrimStart (); + var text = StringBuilderCache.ReturnAndFree (output).TrimStart (); using (var undo = data.OpenUndoGroup ()) { data.InsertAtCaret (text); OnTheFlyFormatter.Format (data, options.DocumentContext, offset, offset + text.Length); diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/ToStringGenerator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/ToStringGenerator.cs index 516a34c15c..e7f601ad23 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/ToStringGenerator.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/ToStringGenerator.cs @@ -100,7 +100,7 @@ namespace MonoDevelop.CodeGeneration string GetFormatString (IEnumerable<object> includedMembers) { - var format = new StringBuilder (); + var format = StringBuilderCache.Allocate (); format.Append ("["); format.Append (Options.EnclosingType.Name); format.Append (": "); @@ -114,7 +114,7 @@ namespace MonoDevelop.CodeGeneration format.Append ("}"); } format.Append ("]"); - return format.ToString (); + return StringBuilderCache.ReturnAndFree (format); } protected override IEnumerable<string> GenerateCode (List<object> includedMembers) diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/WriteLineGenerator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/WriteLineGenerator.cs index f7e4f1e7b7..775cfce169 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/WriteLineGenerator.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeGeneration/WriteLineGenerator.cs @@ -124,7 +124,7 @@ namespace MonoDevelop.CodeGeneration protected override IEnumerable<string> GenerateCode (List<object> includedMembers) { - var format = new StringBuilder (); + var format = StringBuilderCache.Allocate (); int i = 0; format.Append ("$\""); foreach (var member in includedMembers) { @@ -138,7 +138,7 @@ namespace MonoDevelop.CodeGeneration } format.Append ("\""); var arguments = new List<ArgumentSyntax> (); - arguments.Add (SyntaxFactory.Argument (SyntaxFactory.ParseExpression (format.ToString ()))); + arguments.Add (SyntaxFactory.Argument (SyntaxFactory.ParseExpression (StringBuilderCache.ReturnAndFree (format)))); var node = SyntaxFactory.ExpressionStatement ( SyntaxFactory.InvocationExpression ( diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs index fd51996c8f..7f55dd6bea 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.cs @@ -59,6 +59,8 @@ using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.VisualStudio.Platform; +using Counters = MonoDevelop.Ide.Counters; + namespace MonoDevelop.CSharp.Completion { sealed class CSharpCompletionTextEditorExtension : CompletionTextEditorExtension, IDebuggerExpressionResolver @@ -307,7 +309,7 @@ namespace MonoDevelop.CSharp.Completion syntaxTree.GetContainingTypeOrEnumDeclaration (position, cancellationToken) is EnumDeclarationSyntax || syntaxTree.IsPreProcessorDirectiveContext (position, cancellationToken)) return; - + var extensionMethodImport = syntaxTree.IsRightOfDotOrArrowOrColonColon (position, cancellationToken); ITypeSymbol extensionType = null; @@ -334,7 +336,6 @@ namespace MonoDevelop.CSharp.Completion syntaxTree.IsStatementContext (position, tokenLeftOfPosition, cancellationToken) || syntaxTree.IsTypeContext (position, cancellationToken) || syntaxTree.IsTypeDeclarationContext (position, tokenLeftOfPosition, cancellationToken) || - syntaxTree.IsNamespaceContext (position, cancellationToken) || syntaxTree.IsMemberDeclarationContext (position, tokenLeftOfPosition, cancellationToken) || syntaxTree.IsLabelContext (position, cancellationToken)) { var usedNamespaces = new HashSet<string> (); diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/DelegateCompletionProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/DelegateCompletionProvider.cs index c0b197c715..abe99b891e 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/DelegateCompletionProvider.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/DelegateCompletionProvider.cs @@ -144,7 +144,7 @@ namespace MonoDevelop.CSharp.Completion.Provider static string ToPascalCase (string str) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); result.Append (char.ToUpper (str [0])); bool nextUpper = false; for (int i = 1; i < str.Length; i++) { @@ -158,7 +158,7 @@ namespace MonoDevelop.CSharp.Completion.Provider nextUpper = true; } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } //public static CompletionCategory category = new DelegateCreationCategory (); @@ -218,8 +218,8 @@ namespace MonoDevelop.CSharp.Completion.Provider context.AddItem (item); } - var sb = new StringBuilder ("("); - var sbWithoutTypes = new StringBuilder ("("); + var sb = StringBuilderCache.Allocate ("("); + var sbWithoutTypes = StringBuilderCache.Allocate ("("); for (int k = 0; k < delegateMethod.Parameters.Length; k++) { if (k > 0) { sb.Append (", "); @@ -231,7 +231,7 @@ namespace MonoDevelop.CSharp.Completion.Provider sb.Append (")"); sbWithoutTypes.Append (")"); - var signature = sb.ToString () + var signature = StringBuilderCache.ReturnAndFree (sb) .Replace (", params ", ", ") .Replace ("(params ", "("); @@ -290,6 +290,8 @@ namespace MonoDevelop.CSharp.Completion.Provider //} } } + + StringBuilderCache.Free (sbWithoutTypes); item = CreateNewMethodCreationItem (parent, semanticModel, delegateType, position, optDelegateName, delegateMethod, cancellationToken); // item.CompletionCategory = category; if (!context.Items.Any (i => i.DisplayText == item.DisplayText)) { @@ -300,7 +302,7 @@ namespace MonoDevelop.CSharp.Completion.Provider CompletionItem CreateNewMethodCreationItem (SyntaxNode parent, SemanticModel semanticModel, ITypeSymbol delegateType, int position, string optDelegateName, IMethodSymbol delegateMethod, CancellationToken cancellationToken) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); string varName = optDelegateName ?? "Handle" + delegateType.Name; var curType = semanticModel.GetEnclosingSymbol<INamedTypeSymbol> (position, cancellationToken); @@ -308,7 +310,7 @@ namespace MonoDevelop.CSharp.Completion.Provider var pDict = ImmutableDictionary<string, string>.Empty; pDict = pDict.Add ("RightSideMarkup", "<span size='small'>" + GettextCatalog.GetString ("Creates new method") + "</span>"); var indent = "\t"; - sb = new StringBuilder (); + var enclosingSymbol = semanticModel.GetEnclosingSymbol (position, default (CancellationToken)); if (enclosingSymbol != null && enclosingSymbol.IsStatic) sb.Append ("static "); @@ -337,7 +339,7 @@ namespace MonoDevelop.CSharp.Completion.Provider sb.Append ("}"); sb.Append (eolMarker); pDict = pDict.Add ("Position", position.ToString ()); - pDict = pDict.Add ("NewMethod", sb.ToString ()); + pDict = pDict.Add ("NewMethod", StringBuilderCache.ReturnAndFree (sb)); pDict = pDict.Add ("MethodName", varName); return CompletionItem.Create (uniqueName, properties: pDict, tags: newMethodTags, rules: NewMethodRules); diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/ProtocolMemberCompletionProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/ProtocolMemberCompletionProvider.cs index 3907ae0484..c54a3274d7 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/ProtocolMemberCompletionProvider.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CompletionProvider/ProtocolMemberCompletionProvider.cs @@ -37,15 +37,18 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.LanguageServices; using MonoDevelop.Core; using MonoDevelop.CSharp.Refactoring; using MonoDevelop.Ide; using MonoDevelop.Ide.TypeSystem; +using System.Text; +using Microsoft.CodeAnalysis.DocumentationComments; namespace MonoDevelop.CSharp.Completion.Provider { [ExportCompletionProvider ("ProtocolMemberCompletionProvider", LanguageNames.CSharp)] - class ProtocolMemberCompletionProvider : CommonCompletionProvider + class ProtocolMemberCompletionProvider : CompletionProvider { public override async Task ProvideCompletionsAsync (Microsoft.CodeAnalysis.Completion.CompletionContext context) { @@ -111,7 +114,7 @@ namespace MonoDevelop.CSharp.Completion.Provider } - protected void CreateCompletionData (CompletionContext context, SemanticModel semanticModel, int position, ITypeSymbol returnType, Accessibility seenAccessibility, SyntaxToken startToken, SyntaxToken tokenBeforeReturnType, bool afterKeyword, CancellationToken cancellationToken) + protected async void CreateCompletionData (CompletionContext context, SemanticModel semanticModel, int position, ITypeSymbol returnType, Accessibility seenAccessibility, SyntaxToken startToken, SyntaxToken tokenBeforeReturnType, bool afterKeyword, CancellationToken cancellationToken) { if (!TryDetermineOverridableProtocolMembers (semanticModel, tokenBeforeReturnType, seenAccessibility, out ISet<ISymbol> overridableMembers, cancellationToken)) { return; @@ -144,18 +147,72 @@ namespace MonoDevelop.CSharp.Completion.Provider var lastRegion = result.BodyRegions.LastOrDefault (); var region = lastRegion == null ? null : new CodeGeneratorBodyRegion (lastRegion.StartOffset - trimStart, lastRegion.EndOffset - trimStart); - + pDict = pDict.Add ("InsertionText", sb.ToString ()); - pDict = pDict.Add ("DescriptionMarkup", "- <span foreground=\"darkgray\" size='small'>" + GettextCatalog.GetString("Implement protocol member") + "</span>"); + pDict = pDict.Add ("DescriptionMarkup", "- <span foreground=\"darkgray\" size='small'>" + GettextCatalog.GetString ("Implement protocol member") + "</span>"); + pDict = pDict.Add ("Description", await GenerateQuickInfo (semanticModel, position, m, cancellationToken)); + var tags = ImmutableArray<string>.Empty.Add ("NewMethod"); var completionData = CompletionItem.Create (m.Name, properties: pDict, rules: ProtocolCompletionRules, tags: tags); context.AddItem (completionData); } } + static async Task<string> GenerateQuickInfo (SemanticModel semanticModel, int position, ISymbol m, CancellationToken cancellationToken) + { + var ws = IdeApp.Workbench.ActiveDocument.RoslynWorkspace; + + var displayService = ws.Services.GetLanguageServices (LanguageNames.CSharp).GetService<ISymbolDisplayService> (); + + var sections = await displayService.ToDescriptionGroupsAsync (ws, semanticModel, position, new [] { m }.AsImmutable (), default (CancellationToken)).ConfigureAwait (false); + ImmutableArray<TaggedText> parts; + + var description = new StringBuilder (); + description.Append ("Text|"); + description.AppendLine (GettextCatalog.GetString ("Creates an implementation for:")); + description.AppendLine (); + foreach (var sect in sections) { + if (sections.TryGetValue (SymbolDescriptionGroups.MainDescription, out parts)) { + foreach (var part in parts) { + description.Append ("|"); + description.Append (part.Tag); + description.Append ("|"); + description.Append (part.Text); + } + } + } + var formatter = ws.Services.GetLanguageServices (LanguageNames.CSharp).GetService<IDocumentationCommentFormattingService> (); + + var documentation = m.GetDocumentationParts (semanticModel, position, formatter, cancellationToken); + + if (documentation != null && documentation.Any ()) { + description.Append ("|LineBreak|\n|LineBreak|\n"); + + foreach (var part in documentation) { + description.Append ("|"); + description.Append (part.Tag); + description.Append ("|"); + description.Append (part.Text); + } + } + return description.ToString (); + } + static CompletionItemRules ProtocolCompletionRules = CompletionItemRules.Create (formatOnCommit: true); - protected override Task<TextChange?> GetTextChangeAsync (CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) + public override async Task<CompletionChange> GetChangeAsync (Document document, CompletionItem item, char? commitKey, CancellationToken cancellationToken) + { + var change = (await GetTextChangeAsync (document, item, commitKey, cancellationToken).ConfigureAwait (false)) + ?? new TextChange (item.Span, item.DisplayText); + return CompletionChange.Create (change); + } + + public virtual Task<TextChange?> GetTextChangeAsync (Document document, CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) + { + return GetTextChangeAsync (selectedItem, ch, cancellationToken); + } + + Task<TextChange?> GetTextChangeAsync (CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) { var text = Microsoft.CodeAnalysis.Completion.Providers.SymbolCompletionItem.GetInsertionText (selectedItem); return Task.FromResult<TextChange?> (new TextChange (new TextSpan (selectedItem.Span.Start, selectedItem.Span.Length), text)); diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/ImportSymbolCompletionData.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/ImportSymbolCompletionData.cs index ce82f2dfdf..14b08c7024 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/ImportSymbolCompletionData.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/ImportSymbolCompletionData.cs @@ -140,13 +140,13 @@ namespace MonoDevelop.CSharp.Completion int offset = SearchUsingInsertionPoint (unit.SyntaxTree.GetRoot ()); - var text = new StringBuilder (); + var text = StringBuilderCache.Allocate (); text.Append ("using "); text.Append (nsName); text.Append (";"); text.Append (editor.EolMarker); - editor.InsertText (offset, text.ToString ()); + editor.InsertText (offset, StringBuilderCache.ReturnAndFree (text)); } static int SearchUsingInsertionPoint (SyntaxNode parent) diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs index c25ac90c4d..62369290bf 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/GotoDefinition/GotoDefinitionService.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.CSharp; +using MonoDevelop.CSharp.Navigation; namespace ICSharpCode.NRefactory6.CSharp.Features.GotoDefinition { @@ -78,6 +79,21 @@ namespace ICSharpCode.NRefactory6.CSharp.Features.GotoDefinition public static bool TryGoToDefinition (Document document, int position, CancellationToken cancellationToken) { + var metadata = Counters.CreateNavigateToMetadata ("Declaration"); + + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + try { + bool result = TryGoToDefinitionInternal (document, position, cancellationToken); + Counters.UpdateNavigateResult (metadata, result); + return result; + } finally { + Counters.UpdateUserCancellation (metadata, cancellationToken); + } + } + } + + static bool TryGoToDefinitionInternal (Document document, int position, CancellationToken cancellationToken) + { var symbol = FindSymbolAsync (document, position, cancellationToken).Result; if (symbol != null) { diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/TextPasteIndentEngine.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/TextPasteIndentEngine.cs index 15f6cca8ce..0a2adfe10c 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/TextPasteIndentEngine.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Features/IndentEngine/TextPasteIndentEngine.cs @@ -35,6 +35,7 @@ using Microsoft.CodeAnalysis.CSharp.Formatting; using Microsoft.CodeAnalysis; using MonoDevelop.Core.Text; using MonoDevelop.Ide.Editor; +using MonoDevelop.Core; namespace ICSharpCode.NRefactory6.CSharp { @@ -132,8 +133,8 @@ namespace ICSharpCode.NRefactory6.CSharp var line = sourceText.Lines.GetLineFromPosition (offset); var pasteAtLineStart = line.Start == offset; - var indentedText = new StringBuilder (); - var curLine = new StringBuilder (); + var indentedText = StringBuilderCache.Allocate (); + var curLine = StringBuilderCache.Allocate (); var clonedEngine = engine.Clone (); bool isNewLine = false, gotNewLine = false; for (int i = 0; i < text.Length; i++) { @@ -189,7 +190,8 @@ namespace ICSharpCode.NRefactory6.CSharp if (curLine.Length > 0) { indentedText.Append (curLine); } - return indentedText.ToString (); + StringBuilderCache.Free (curLine); + return StringBuilderCache.ReturnAndFree (indentedText); } /// <inheritdoc /> @@ -489,7 +491,7 @@ namespace ICSharpCode.NRefactory6.CSharp /// </summary> public static string ConvertString (string str) { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (char ch in str) { if (ch == '"') { sb.Append ("\\\""); @@ -497,13 +499,13 @@ namespace ICSharpCode.NRefactory6.CSharp sb.Append (ConvertChar (ch)); } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } /// <inheritdoc /> public string Decode (string text) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); bool isEscaped = false; for (int i = 0; i < text.Length; i++) { @@ -577,7 +579,7 @@ namespace ICSharpCode.NRefactory6.CSharp } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } static bool TryGetHex (string text, int count, ref int idx, out char r) diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs index e85128bbe7..d6a1c32e34 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Formatting/CSharpTextEditorIndentation.cs @@ -235,7 +235,7 @@ namespace MonoDevelop.CSharp.Formatting internal static string ConvertToStringLiteral (string text) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); foreach (var ch in text) { switch (ch) { case '\t': @@ -258,7 +258,7 @@ namespace MonoDevelop.CSharp.Formatting break; } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } static void ConvertNormalToVerbatimString (ITextDocument textEditorData, int offset) diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs new file mode 100644 index 0000000000..51dccb3cf8 --- /dev/null +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/Counters.cs @@ -0,0 +1,69 @@ +// +// Counters.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2018 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.Collections.Generic; +using System.Threading; +using MonoDevelop.Core.Instrumentation; + +namespace MonoDevelop.CSharp.Navigation +{ + internal static class Counters + { + public static TimerCounter NavigateTo = InstrumentationService.CreateTimerCounter ("Navigate to", "Code Navigation", id: "CodeNavigation.NavigateTo"); + + public static IDictionary<string, string> CreateNavigateToMetadata (string navigationType) + { + var metadata = new Dictionary<string, string> (); + metadata ["Type"] = navigationType; + metadata ["Result"] = "Failure"; // Will be updated when navigation finishes. + return metadata; + } + + public static void UpdateUserCancellation (IDictionary<string, string> metadata, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) { + metadata ["Result"] = "UserCancel"; + } + } + + public static void UpdateNavigateResult (IDictionary<string, string> metadata, bool result) + { + metadata ["Result"] = result ? "Success" : "Failure"; + } + + /// <summary> + /// Distinguish between IDE errors and a failure due to the user selecting an invalid item. + /// Some navigation menus are enabled even if they are not valid. For example, right clicking + /// on a method and selecting Navigate - Extension Methods will find no class at the caret and + /// will not attempt to find any extension methods. Note that the CommandInfo is disabled for + /// the menu but this does not seem to have any affect presumably because the method is async. + /// </summary> + public static void UpdateUserFault (IDictionary<string, string> metadata) + { + metadata ["Result"] = "UserFault"; + } + } +} diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs index d732aca199..7dceb59c9b 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindBaseSymbolsHandler.cs @@ -25,28 +25,31 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; using MonoDevelop.Ide.TypeSystem; using MonoDevelop.Core; +using MonoDevelop.Core.ProgressMonitoring; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Shared.Extensions; using MonoDevelop.Components.Commands; using MonoDevelop.Refactoring; -using System.Threading.Tasks; -using System.Collections.Generic; using ICSharpCode.NRefactory6.CSharp; namespace MonoDevelop.CSharp.Navigation { class FindBaseSymbolsHandler : CommandHandler { - void FindSymbols (ISymbol sym) + Task FindSymbols (ISymbol sym, CancellationTokenSource cancellationTokenSource) { if (sym == null) - return; - Task.Run (delegate { - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + return Task.FromResult (0); + return Task.Run (delegate { + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { var foundSymbol = sym.OverriddenMember (); while (foundSymbol != null) { foreach (var loc in foundSymbol.Locations) { @@ -56,7 +59,7 @@ namespace MonoDevelop.CSharp.Navigation if (loc.SourceTree == null) continue; - monitor.ReportResult (new MemberReference (foundSymbol, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (foundSymbol, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } foundSymbol = foundSymbol.OverriddenMember (); } @@ -81,11 +84,24 @@ namespace MonoDevelop.CSharp.Navigation protected override async void Run () { - var sym = await GetSymbolAtCaret (IdeApp.Workbench.ActiveDocument); - if (sym != null) - FindSymbols (sym); - } + var metadata = Counters.CreateNavigateToMetadata ("BaseSymbols"); + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + var sym = await GetSymbolAtCaret (IdeApp.Workbench.ActiveDocument); + if (sym == null) { + Counters.UpdateUserFault (metadata); + return; + } + using (var source = new CancellationTokenSource ()) { + try { + await FindSymbols (sym, source); + Counters.UpdateNavigateResult (metadata, true); + } finally { + Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } + } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindDerivedSymbolsHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindDerivedSymbolsHandler.cs index 60dfe50938..ce81fb731d 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindDerivedSymbolsHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindDerivedSymbolsHandler.cs @@ -36,6 +36,7 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions; using MonoDevelop.Components.Commands; using MonoDevelop.Core; +using MonoDevelop.Core.ProgressMonitoring; using MonoDevelop.CSharp.Navigation; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; @@ -60,13 +61,14 @@ namespace MonoDevelop.CSharp.Refactoring return symbol.IsVirtual || symbol.IsAbstract || symbol.IsOverride; } - public static void FindDerivedSymbols (ISymbol symbol) + static Task FindDerivedSymbols (ISymbol symbol, CancellationTokenSource cancellationTokenSource) { if (symbol == null) - return; + return Task.FromResult (0); var solution = IdeApp.Workbench.ActiveDocument?.AnalysisDocument?.Project?.Solution ?? TypeSystemService.Workspace.CurrentSolution; - Task.Run (async delegate { - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + return Task.Run (async delegate { + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { IEnumerable<ISymbol> result; try { if (symbol.ContainingType != null && symbol.ContainingType.TypeKind == TypeKind.Interface) { @@ -87,7 +89,7 @@ namespace MonoDevelop.CSharp.Refactoring foreach (var foundSymbol in result) { foreach (var loc in foundSymbol.Locations) { monitor.CancellationToken.ThrowIfCancellationRequested (); - monitor.ReportResult (new MemberReference (foundSymbol, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (foundSymbol, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } } catch (OperationCanceledException) { @@ -120,9 +122,23 @@ namespace MonoDevelop.CSharp.Refactoring protected override async void Run (object dataItem) { - var sym = await FindBaseSymbolsHandler.GetSymbolAtCaret (IdeApp.Workbench.ActiveDocument); - if (sym != null) - FindDerivedSymbols (sym); + var metadata = Navigation.Counters.CreateNavigateToMetadata ("DerivedSymbols"); + using (var timer = Navigation.Counters.NavigateTo.BeginTiming (metadata)) { + var sym = await FindBaseSymbolsHandler.GetSymbolAtCaret (IdeApp.Workbench.ActiveDocument); + if (sym == null) { + Navigation.Counters.UpdateUserFault (metadata); + return; + } + + using (var source = new CancellationTokenSource ()) { + try { + await FindDerivedSymbols (sym, source); + Navigation.Counters.UpdateNavigateResult (metadata, true); + } finally { + Navigation.Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindExtensionMethodsHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindExtensionMethodsHandler.cs index c7a5f0c5a9..99a0153fc3 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindExtensionMethodsHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindExtensionMethodsHandler.cs @@ -24,6 +24,8 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +using System.Linq; +using System.Threading; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; using Microsoft.CodeAnalysis; @@ -31,7 +33,6 @@ using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions; using MonoDevelop.Components.Commands; using MonoDevelop.Refactoring; -using System.Linq; using ICSharpCode.NRefactory6.CSharp; namespace MonoDevelop.CSharp.Navigation @@ -47,10 +48,24 @@ namespace MonoDevelop.CSharp.Navigation protected async override void Run () { - var doc = IdeApp.Workbench.ActiveDocument; - var sym = await GetNamedTypeAtCaret (doc); - if (sym != null) - FindExtensionMethods (await doc.GetCompilationAsync (), sym); + var metadata = Counters.CreateNavigateToMetadata ("ExtensionMethods"); + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + var doc = IdeApp.Workbench.ActiveDocument; + var sym = await GetNamedTypeAtCaret (doc); + if (sym == null) { + Counters.UpdateUserFault (metadata); + return; + } + + using (var source = new CancellationTokenSource ()) { + try { + FindExtensionMethods (await doc.GetCompilationAsync (), sym, source); + Counters.UpdateNavigateResult (metadata, true); + } finally { + Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } } internal static async System.Threading.Tasks.Task<INamedTypeSymbol> GetNamedTypeAtCaret (Ide.Gui.Document doc) @@ -62,12 +77,14 @@ namespace MonoDevelop.CSharp.Navigation return sym as INamedTypeSymbol; } - void FindExtensionMethods (Compilation compilation, ISymbol sym) + void FindExtensionMethods (Compilation compilation, ISymbol sym, CancellationTokenSource cancellationTokenSource) { var symType = sym as ITypeSymbol; if (symType == null) return; - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { foreach (var type in compilation.Assembly.GlobalNamespace.GetAllTypes (monitor.CancellationToken)) { if (!type.MightContainExtensionMethods) continue; @@ -79,7 +96,7 @@ namespace MonoDevelop.CSharp.Navigation var reducedMethod = extMethod.ReduceExtensionMethod (symType); if (reducedMethod != null) { var loc = extMethod.Locations.First (); - monitor.ReportResult (new MemberReference (extMethod, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (extMethod, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs index c37c5a4362..9bc100b784 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindImplementingMembersHandler.cs @@ -24,15 +24,16 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; using Microsoft.CodeAnalysis; using MonoDevelop.Components.Commands; using MonoDevelop.Refactoring; -using System.Linq; using ICSharpCode.NRefactory6.CSharp; using Microsoft.CodeAnalysis.CSharp; -using System.Threading.Tasks; namespace MonoDevelop.CSharp.Navigation { @@ -47,10 +48,24 @@ namespace MonoDevelop.CSharp.Navigation protected async override void Run () { - var doc = IdeApp.Workbench.ActiveDocument; - var sym = await GetNamedTypeAtCaret (doc); - if (sym != null) - FindImplementingSymbols (await doc.GetCompilationAsync (), sym); + var metadata = Counters.CreateNavigateToMetadata ("ImplementingMembers"); + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + var doc = IdeApp.Workbench.ActiveDocument; + var sym = await GetNamedTypeAtCaret (doc); + if (sym == null) { + Counters.UpdateUserFault (metadata); + return; + } + + using (var source = new CancellationTokenSource ()) { + try { + await FindImplementingSymbols (await doc.GetCompilationAsync (), sym, source); + Counters.UpdateNavigateResult (metadata, true); + } finally { + Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } } static async Task<RefactoringSymbolInfo> GetNamedTypeAtCaret (Ide.Gui.Document doc) @@ -65,14 +80,15 @@ namespace MonoDevelop.CSharp.Navigation return info; } - void FindImplementingSymbols (Compilation compilation, RefactoringSymbolInfo info) + Task FindImplementingSymbols (Compilation compilation, RefactoringSymbolInfo info, CancellationTokenSource cancellationTokenSource) { var interfaceType = info.Symbol as ITypeSymbol; if (interfaceType == null) - return; + return Task.FromResult (0); - Task.Run (delegate { - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + return Task.Run (delegate { + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { var parentTypeNode = info.Node?.Parent?.Parent?.Parent; if (parentTypeNode == null) return; @@ -86,7 +102,7 @@ namespace MonoDevelop.CSharp.Navigation if (impl == null) continue; var loc = impl.Locations.First (); - monitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } foreach (var iFace in interfaceType.AllInterfaces) { @@ -98,7 +114,7 @@ namespace MonoDevelop.CSharp.Navigation if (impl == null) continue; var loc = impl.Locations.First (); - monitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (impl, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindMemberOverloadsHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindMemberOverloadsHandler.cs index f88698e842..d02a2bc077 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindMemberOverloadsHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Navigation/FindMemberOverloadsHandler.cs @@ -25,6 +25,7 @@ // THE SOFTWARE. using System.Linq; +using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -53,9 +54,10 @@ namespace MonoDevelop.CSharp.Navigation } } - public static void FindOverloads (ISymbol symbol) + public static void FindOverloads (ISymbol symbol, CancellationTokenSource cancellationTokenSource) { - using (var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true)) { + var searchMonitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); + using (var monitor = searchMonitor.WithCancellationSource (cancellationTokenSource)) { switch (symbol.Kind) { case SymbolKind.Method: foreach (var method in symbol.ContainingType.GetMembers (symbol.Name).OfType<IMethodSymbol> ()) { @@ -63,7 +65,7 @@ namespace MonoDevelop.CSharp.Navigation if (monitor.CancellationToken.IsCancellationRequested) return; - monitor.ReportResult (new MemberReference (method, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (method, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } break; @@ -73,7 +75,7 @@ namespace MonoDevelop.CSharp.Navigation if (monitor.CancellationToken.IsCancellationRequested) return; - monitor.ReportResult (new MemberReference (property, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); + searchMonitor.ReportResult (new MemberReference (property, loc.SourceTree.FilePath, loc.SourceSpan.Start, loc.SourceSpan.Length)); } } break; @@ -100,10 +102,26 @@ namespace MonoDevelop.CSharp.Navigation var doc = IdeApp.Workbench.ActiveDocument; if (doc == null || doc.FileName == FilePath.Null) return; - var info = await RefactoringSymbolInfo.GetSymbolInfoAsync (doc, doc.Editor); - var sym = info.Symbol ?? info.DeclaredSymbol; - if (sym != null) - FindOverloads (sym); + + var metadata = Counters.CreateNavigateToMetadata ("MemberOverloads"); + using (var timer = Counters.NavigateTo.BeginTiming (metadata)) { + + var info = await RefactoringSymbolInfo.GetSymbolInfoAsync (doc, doc.Editor); + var sym = info.Symbol ?? info.DeclaredSymbol; + if (sym == null) { + Counters.UpdateUserFault (metadata); + return; + } + + using (var source = new CancellationTokenSource ()) { + try { + FindOverloads (sym, source); + Counters.UpdateNavigateResult (metadata, true); + } finally { + Counters.UpdateUserCancellation (metadata, source.Token); + } + } + } } } }
\ No newline at end of file diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpLanguageVersionHelper.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpLanguageVersionHelper.cs new file mode 100644 index 0000000000..6e75d09d6f --- /dev/null +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpLanguageVersionHelper.cs @@ -0,0 +1,50 @@ +// +// CSharpLanguageVersionHelper.cs +// +// Author: +// Marius Ungureanu <maungu@microsoft.com> +// +// Copyright (c) 2018 +// +// 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 Microsoft.CodeAnalysis.CSharp; +using MonoDevelop.Core; + +namespace MonoDevelop.CSharp.Project +{ + class CSharpLanguageVersionHelper + { + public static IEnumerable<(string localized, LanguageVersion version)> GetKnownLanguageVersions () + { + yield return (GettextCatalog.GetString ("Default"), LanguageVersion.Default); + yield return ("ISO-1", LanguageVersion.CSharp1); + yield return ("ISO-2", LanguageVersion.CSharp2); + yield return (GettextCatalog.GetString ("Version 3"), LanguageVersion.CSharp3); + yield return (GettextCatalog.GetString ("Version 4"), LanguageVersion.CSharp4); + yield return (GettextCatalog.GetString ("Version 5"), LanguageVersion.CSharp5); + yield return (GettextCatalog.GetString ("Version 6"), LanguageVersion.CSharp6); + yield return (GettextCatalog.GetString ("Version 7"), LanguageVersion.CSharp7); + yield return (GettextCatalog.GetString ("Version 7.1"), LanguageVersion.CSharp7_1); + yield return (GettextCatalog.GetString ("Version 7.2"), LanguageVersion.CSharp7_2); + yield return (GettextCatalog.GetString ("Latest"), LanguageVersion.Latest); + } + } +} diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpResourceIdBuilder.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpResourceIdBuilder.cs index 8a956915ee..2d4e4daf1b 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpResourceIdBuilder.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CSharpResourceIdBuilder.cs @@ -103,89 +103,92 @@ namespace MonoDevelop.CSharp.Project * Won't handle #if false etc kinda blocks*/ static string GetNextToken (StreamReader sr) { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); + try { + while (true) { + int c = sr.Peek (); + if (c == -1) + return null; - while (true) { - int c = sr.Peek (); - if (c == -1) - return null; + if (c == '\r' || c == '\n') { + sr.ReadLine (); + if (sb.Length > 0) + break; - if (c == '\r' || c == '\n') { - sr.ReadLine (); - if (sb.Length > 0) - break; + continue; + } - continue; - } + if (c == '/') { + sr.Read (); - if (c == '/') { - sr.Read (); + if (sr.Peek () == '*') { + /* multi-line comment */ + sr.Read (); - if (sr.Peek () == '*') { - /* multi-line comment */ + while (true) { + int n = sr.Read (); + if (n == -1) + break; + if (n != '*') + continue; + + if (sr.Peek () == '/') { + /* End of multi-line comment */ + if (sb.Length > 0) { + sr.Read (); + return sb.ToString (); + } + break; + } + } + } else if (sr.Peek () == '/') { + //Single line comment, skip the rest of the line + sr.ReadLine (); + continue; + } + } else if (c == '"') { + /* String "foo" */ sr.Read (); - while (true) { - int n = sr.Read (); + int n = sr.Peek (); if (n == -1) - break; - if (n != '*') - continue; + throw new Exception ("String literal not closed"); - if (sr.Peek () == '/') { - /* End of multi-line comment */ + if (n == '"') { + /* end of string */ if (sb.Length > 0) { sr.Read (); return sb.ToString (); } + break; } + sr.Read (); } - } else if (sr.Peek () == '/') { - //Single line comment, skip the rest of the line + } else if (c == '#') { + //skip rest of the line sr.ReadLine (); - continue; - } - } else if (c == '"') { - /* String "foo" */ - sr.Read (); - while (true) { - int n = sr.Peek (); - if (n == -1) - throw new Exception ("String literal not closed"); - - if (n == '"') { - /* end of string */ - if (sb.Length > 0) { + } else { + if (Char.IsLetterOrDigit ((char)c) || c == '_' || c == '.') { + sb.Append ((char)c); + } else { + if (sb.Length > 0) + break; + + if (c != ' ' && c != '\t') { sr.Read (); - return sb.ToString (); + return ((char)c).ToString (); } - - break; } - sr.Read (); } - } else if (c == '#') { - //skip rest of the line - sr.ReadLine (); - } else { - if (Char.IsLetterOrDigit ((char) c) || c == '_' || c == '.') { - sb.Append ((char) c); - } else { - if (sb.Length > 0) - break; - if (c != ' ' && c != '\t') { - sr.Read (); - return ((char) c).ToString (); - } - } + sr.Read (); } - sr.Read (); + return sb.ToString (); + } finally { + StringBuilderCache.Free (sb); } - - return sb.ToString (); } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CompilerOptionsPanelWidget.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CompilerOptionsPanelWidget.cs index e7c6da4235..7e67f515c0 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CompilerOptionsPanelWidget.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Project/CompilerOptionsPanelWidget.cs @@ -94,16 +94,9 @@ namespace MonoDevelop.CSharp.Project noStdLibCheckButton.Active = compilerParameters.NoStdLib; var langVerStore = new ListStore (typeof (string), typeof(LanguageVersion)); - langVerStore.AppendValues (GettextCatalog.GetString ("Default"), LanguageVersion.Default); - langVerStore.AppendValues ("ISO-1", LanguageVersion.CSharp1); - langVerStore.AppendValues ("ISO-2", LanguageVersion.CSharp2); - langVerStore.AppendValues (GettextCatalog.GetString ("Version 3"), LanguageVersion.CSharp3); - langVerStore.AppendValues (GettextCatalog.GetString ("Version 4"), LanguageVersion.CSharp4); - langVerStore.AppendValues (GettextCatalog.GetString ("Version 5"), LanguageVersion.CSharp5); - langVerStore.AppendValues (GettextCatalog.GetString ("Version 6"), LanguageVersion.CSharp6); - langVerStore.AppendValues (GettextCatalog.GetString ("Version 7"), LanguageVersion.CSharp7); - langVerStore.AppendValues (GettextCatalog.GetString ("Version 7.1"), LanguageVersion.CSharp7_1); - langVerStore.AppendValues (GettextCatalog.GetString ("Latest"), LanguageVersion.Latest); + foreach (var (text, version) in CSharpLanguageVersionHelper.GetKnownLanguageVersions ()) { + langVerStore.AppendValues (text, version); + } langVerCombo.Model = langVerStore; TreeIter iter; diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CSharpCodeGenerator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CSharpCodeGenerator.cs index 1ed9bd2aa5..bad8dc5dc4 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CSharpCodeGenerator.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CSharpCodeGenerator.cs @@ -125,7 +125,7 @@ namespace MonoDevelop.CSharp.Refactoring public override string WrapInRegions (string regionName, string text) { - StringBuilder result = new StringBuilder (); + StringBuilder result = Core.StringBuilderCache.Allocate (); AppendIndent (result); result.Append ("#region "); result.Append (regionName); @@ -134,7 +134,7 @@ namespace MonoDevelop.CSharp.Refactoring AppendLine (result); AppendIndent (result); result.Append ("#endregion"); - return result.ToString (); + return Core.StringBuilderCache.ReturnAndFree (result); } static void AppendObsoleteAttribute(StringBuilder result, CodeGenerationOptions options, ISymbol entity) @@ -378,7 +378,7 @@ namespace MonoDevelop.CSharp.Refactoring // static CodeGeneratorMemberResult GenerateCode (IFieldSymbol field, CodeGenerationOptions options) { - StringBuilder result = new StringBuilder (); + StringBuilder result = Core.StringBuilderCache.Allocate (); AppendIndent (result); AppendModifiers (result, options, field); result.Append (" "); @@ -386,7 +386,7 @@ namespace MonoDevelop.CSharp.Refactoring result.Append (" "); result.Append (CSharpAmbience.FilterName (field.Name)); result.Append (";"); - return new CodeGeneratorMemberResult (result.ToString (), -1, -1); + return new CodeGeneratorMemberResult (Core.StringBuilderCache.ReturnAndFree (result), -1, -1); } static void AppendIndent (StringBuilder result) @@ -396,7 +396,7 @@ namespace MonoDevelop.CSharp.Refactoring static CodeGeneratorMemberResult GenerateCode (IEventSymbol evt, CodeGenerationOptions options) { - StringBuilder result = new StringBuilder (); + StringBuilder result = Core.StringBuilderCache.Allocate (); AppendObsoleteAttribute (result, options, evt); AppendModifiers (result, options, evt); @@ -427,7 +427,7 @@ namespace MonoDevelop.CSharp.Refactoring } else { result.Append (";"); } - return new CodeGeneratorMemberResult (result.ToString ()); + return new CodeGeneratorMemberResult (Core.StringBuilderCache.ReturnAndFree (result)); } static void AppendNotImplementedException (StringBuilder result, CodeGenerationOptions options, out int bodyStartOffset, out int bodyEndOffset) @@ -465,7 +465,7 @@ namespace MonoDevelop.CSharp.Refactoring static CodeGeneratorMemberResult GenerateCode(IMethodSymbol method, CodeGenerationOptions options) { int bodyStartOffset = -1, bodyEndOffset = -1; - var result = new StringBuilder(); + var result = Core.StringBuilderCache.Allocate (); AppendObsoleteAttribute (result, options, method); AppendModifiers (result, options, method); // if (method.IsPartial) @@ -647,14 +647,14 @@ namespace MonoDevelop.CSharp.Refactoring } result.Append ("}"); } - return new CodeGeneratorMemberResult(result.ToString (), bodyStartOffset, bodyEndOffset); + return new CodeGeneratorMemberResult(Core.StringBuilderCache.ReturnAndFree (result), bodyStartOffset, bodyEndOffset); } static CodeGeneratorMemberResult GeneratePartialCode(IMethodSymbol method, CodeGenerationOptions options) { int bodyStartOffset = -1, bodyEndOffset = -1; - var result = new StringBuilder(); + var result = Core.StringBuilderCache.Allocate (); AppendObsoleteAttribute (result, options, method); result.Append("partial "); AppendReturnType (result, options, method.ReturnType); @@ -685,7 +685,7 @@ namespace MonoDevelop.CSharp.Refactoring AppendLine (result); bodyEndOffset = result.Length; result.AppendLine("}"); - return new CodeGeneratorMemberResult(result.ToString(), bodyStartOffset, bodyEndOffset); + return new CodeGeneratorMemberResult(Core.StringBuilderCache.ReturnAndFree (result), bodyStartOffset, bodyEndOffset); } // class ThrowsExceptionVisitor : DepthFirstAstVisitor @@ -755,7 +755,7 @@ namespace MonoDevelop.CSharp.Refactoring static string GetModifiers (ITypeSymbol implementingType, Location implementingPart, ISymbol member) { - StringBuilder result = new StringBuilder (); + var result = Core.StringBuilderCache.Allocate (); if (member.DeclaredAccessibility == Accessibility.Public || (member.ContainingType != null && member.ContainingType.TypeKind == TypeKind.Interface)) { result.Append ("public "); @@ -774,7 +774,7 @@ namespace MonoDevelop.CSharp.Refactoring if (member.IsStatic) result.Append ("static "); - return result.ToString (); + return Core.StringBuilderCache.ReturnAndFree (result); } static void AppendModifiers (StringBuilder result, CodeGenerationOptions options, ISymbol member) @@ -812,7 +812,7 @@ namespace MonoDevelop.CSharp.Refactoring static CodeGeneratorMemberResult GenerateCode (IPropertySymbol property, CodeGenerationOptions options) { var regions = new List<CodeGeneratorBodyRegion> (); - var result = new StringBuilder (); + var result = Core.StringBuilderCache.Allocate (); AppendObsoleteAttribute (result, options, property); AppendModifiers (result, options, property); AppendReturnType (result, options, property.Type); @@ -897,7 +897,7 @@ namespace MonoDevelop.CSharp.Refactoring } } result.Append ("}"); - return new CodeGeneratorMemberResult (result.ToString (), regions); + return new CodeGeneratorMemberResult (Core.StringBuilderCache.ReturnAndFree (result), regions); } internal static bool IsMonoTouchModelMember (ISymbol member) @@ -1016,7 +1016,7 @@ namespace MonoDevelop.CSharp.Refactoring static CodeGeneratorMemberResult GenerateProtocolCode(IMethodSymbol method, CodeGenerationOptions options) { int bodyStartOffset = -1, bodyEndOffset = -1; - var result = new StringBuilder(); + var result = Core.StringBuilderCache.Allocate (); var exportAttribute = method.GetAttributes ().FirstOrDefault (attr => attr.AttributeClass.Name == "ExportAttribute"); if (exportAttribute != null) { result.Append ("[Export(\""); @@ -1057,7 +1057,7 @@ namespace MonoDevelop.CSharp.Refactoring bodyEndOffset = result.Length; AppendLine (result); result.Append ("}"); - return new CodeGeneratorMemberResult(result.ToString (), bodyStartOffset, bodyEndOffset); + return new CodeGeneratorMemberResult(Core.StringBuilderCache.ReturnAndFree (result), bodyStartOffset, bodyEndOffset); } public override void AddGlobalNamespaceImport (TextEditor editor, DocumentContext context, string nsName) diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CodeGenerationService.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CodeGenerationService.cs index 667764b0de..125ba00813 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CodeGenerationService.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/CodeGenerationService.cs @@ -248,7 +248,7 @@ namespace MonoDevelop.Refactoring var buffer = TextFileProvider.Instance.GetTextEditorData (fileName, out isOpen); - var code = new StringBuilder (); + var code = StringBuilderCache.Allocate (); int pos = cls.Locations.First ().SourceSpan.Start; var line = buffer.GetLineByOffset (pos); code.Append (buffer.GetLineIndent (line)); @@ -266,7 +266,7 @@ namespace MonoDevelop.Refactoring code.Append ("]"); code.AppendLine (); - buffer.InsertText (line.Offset, code.ToString ()); + buffer.InsertText (line.Offset, StringBuilderCache.ReturnAndFree (code)); if (!isOpen) { File.WriteAllText (fileName, buffer.Text); diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/FindReferencesHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/FindReferencesHandler.cs index 7d8044f33c..d8bb848bbd 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/FindReferencesHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/FindReferencesHandler.cs @@ -32,6 +32,7 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.Shared.Extensions; using MonoDevelop.Components.Commands; using MonoDevelop.Core; +using MonoDevelop.Core.Instrumentation; using MonoDevelop.CSharp.Highlighting; using MonoDevelop.Ide; using MonoDevelop.Ide.FindInFiles; @@ -49,7 +50,12 @@ namespace MonoDevelop.CSharp.Refactoring if (workspace == null) return; Task.Run (async delegate { + ITimeTracker timer = null; + var metadata = MonoDevelop.Refactoring.Counters.CreateFindReferencesMetadata (); + try { + timer = MonoDevelop.Refactoring.Counters.FindReferences.BeginTiming (metadata); + var antiDuplicatesSet = new HashSet<SearchResult> (new SearchResultComparer ()); foreach (var loc in symbol.Locations) { if (monitor.CancellationToken.IsCancellationRequested) @@ -97,6 +103,7 @@ namespace MonoDevelop.CSharp.Refactoring } } catch (OperationCanceledException) { } catch (Exception ex) { + MonoDevelop.Refactoring.Counters.SetFailure (metadata); if (monitor != null) monitor.ReportError ("Error finding references", ex); else @@ -104,6 +111,10 @@ namespace MonoDevelop.CSharp.Refactoring } finally { if (monitor != null) monitor.Dispose (); + if (monitor.CancellationToken.IsCancellationRequested) + MonoDevelop.Refactoring.Counters.SetUserCancel (metadata); + if (timer != null) + timer.Dispose (); } }); } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/GotoBaseDeclarationHandler.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/GotoBaseDeclarationHandler.cs index fef916d350..c8f6651e22 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/GotoBaseDeclarationHandler.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/GotoBaseDeclarationHandler.cs @@ -24,10 +24,11 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. using System; -using Microsoft.CodeAnalysis; -using MonoDevelop.Ide; using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using MonoDevelop.Core; +using MonoDevelop.Ide; using MonoDevelop.Refactoring; namespace MonoDevelop.CSharp.Refactoring @@ -74,37 +75,46 @@ namespace MonoDevelop.CSharp.Refactoring return false; } - public static void GotoBase (MonoDevelop.Ide.Gui.Document doc, ISymbol symbol) + public static async Task GotoBase (MonoDevelop.Ide.Gui.Document doc, ISymbol symbol) { if (doc == null) throw new ArgumentNullException ("doc"); if (symbol == null) throw new ArgumentNullException ("symbol"); + + var metadata = Navigation.Counters.CreateNavigateToMetadata ("Base"); + using (var timer = Navigation.Counters.NavigateTo.BeginTiming (metadata)) { + await GotoBaseInternal (doc, symbol); + Navigation.Counters.UpdateNavigateResult (metadata, true); + } + } + + static Task GotoBaseInternal (MonoDevelop.Ide.Gui.Document doc, ISymbol symbol) + { switch (symbol.Kind) { case SymbolKind.NamedType: - RefactoringService.RoslynJumpToDeclaration (((ITypeSymbol)symbol).BaseType, doc.Project); - break; + return RefactoringService.RoslynJumpToDeclaration (((ITypeSymbol)symbol).BaseType, doc.Project); case SymbolKind.Property: var property = (IPropertySymbol)symbol; if (property.OverriddenProperty != null) - RefactoringService.RoslynJumpToDeclaration (property.OverriddenProperty, doc.Project); + return RefactoringService.RoslynJumpToDeclaration (property.OverriddenProperty, doc.Project); else - RefactoringService.RoslynJumpToDeclaration (property.ExplicitInterfaceImplementations.First (), doc.Project); - break; + return RefactoringService.RoslynJumpToDeclaration (property.ExplicitInterfaceImplementations.First (), doc.Project); case SymbolKind.Event: var evt = (IEventSymbol)symbol; if (evt.OverriddenEvent != null) - RefactoringService.RoslynJumpToDeclaration (evt.OverriddenEvent, doc.Project); + return RefactoringService.RoslynJumpToDeclaration (evt.OverriddenEvent, doc.Project); else - RefactoringService.RoslynJumpToDeclaration (evt.ExplicitInterfaceImplementations.First (), doc.Project); - break; + return RefactoringService.RoslynJumpToDeclaration (evt.ExplicitInterfaceImplementations.First (), doc.Project); case SymbolKind.Method: var method = (IMethodSymbol)symbol; if (method.OverriddenMethod != null) - RefactoringService.RoslynJumpToDeclaration (method.OverriddenMethod, doc.Project); + return RefactoringService.RoslynJumpToDeclaration (method.OverriddenMethod, doc.Project); else - RefactoringService.RoslynJumpToDeclaration (method.ExplicitInterfaceImplementations.First (), doc.Project); - break; + return RefactoringService.RoslynJumpToDeclaration (method.ExplicitInterfaceImplementations.First (), doc.Project); + default: + // CanGotoBase should prevent this from happening. + throw new ArgumentException (string.Format ("Invalid symbol.Kind {0}", symbol.Kind)); } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs index dade41b30a..29136372b0 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Refactoring/RefactoryCommands.cs @@ -111,7 +111,7 @@ namespace MonoDevelop.CSharp.Refactoring if (info.DeclaredSymbol != null && GotoBaseDeclarationHandler.CanGotoBase (info.DeclaredSymbol)) { - ainfo.Add (GotoBaseDeclarationHandler.GetDescription (info.DeclaredSymbol), new Action (() => GotoBaseDeclarationHandler.GotoBase (doc, info.DeclaredSymbol))); + ainfo.Add (GotoBaseDeclarationHandler.GetDescription (info.DeclaredSymbol), new Action (() => GotoBaseDeclarationHandler.GotoBase (doc, info.DeclaredSymbol).Ignore ())); added = true; } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Tooltips/LanguageItemTooltipProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Tooltips/LanguageItemTooltipProvider.cs index 3ca3b13a5b..762fe05369 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Tooltips/LanguageItemTooltipProvider.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Tooltips/LanguageItemTooltipProvider.cs @@ -60,7 +60,7 @@ namespace MonoDevelop.SourceEditor return null; int caretOffset = editor.CaretOffset; - EditorTheme theme = editor.Options.GetEditorTheme (); + EditorTheme theme = SyntaxHighlightingService.GetIdeFittingTheme (editor.Options.GetEditorTheme ()); return await Task.Run (async () => { var root = unit.SyntaxTree.GetRoot (token); SyntaxToken syntaxToken; diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Tooltips/QuickInfoProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Tooltips/QuickInfoProvider.cs index 3e65f4547d..510646ecc1 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Tooltips/QuickInfoProvider.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Tooltips/QuickInfoProvider.cs @@ -71,7 +71,7 @@ namespace MonoDevelop.SourceEditor ImmutableArray<TaggedText> parts; - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); if (sections.TryGetValue (SymbolDescriptionGroups.MainDescription, out parts)) { TaggedTextUtil.AppendTaggedText (sb, theme, parts); @@ -115,7 +115,7 @@ namespace MonoDevelop.SourceEditor } sb.Append ("</span>"); - tooltipInfo.SignatureMarkup = sb.ToString (); + tooltipInfo.SignatureMarkup = StringBuilderCache.ReturnAndFree (sb); return tooltipInfo; } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.UnitTests/UnitTestTextEditorExtension.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.UnitTests/UnitTestTextEditorExtension.cs index c3290f4fde..90378a4a28 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.UnitTests/UnitTestTextEditorExtension.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.UnitTests/UnitTestTextEditorExtension.cs @@ -136,7 +136,7 @@ namespace MonoDevelop.CSharp static string BuildArguments (AttributeData attr) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); ImmutableArray<TypedConstant> args; if (attr.ConstructorArguments.Length == 1 && attr.ConstructorArguments [0].Kind == TypedConstantKind.Array) args = attr.ConstructorArguments [0].Values; @@ -150,7 +150,7 @@ namespace MonoDevelop.CSharp AddArgument (args [i], sb); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } static void AddArgument(TypedConstant arg, StringBuilder sb) diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/AstAmbience.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/AstAmbience.cs index 7162cd0abd..526a179cb0 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/AstAmbience.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/AstAmbience.cs @@ -119,13 +119,13 @@ namespace MonoDevelop.CSharp string StripTrivia (string str) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); foreach (char ch in str) { if (char.IsWhiteSpace (ch)) continue; result.Append (ch); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } static void AppendEscaped (StringBuilder result, string text) @@ -158,7 +158,7 @@ namespace MonoDevelop.CSharp public string GetEntityMarkup (SyntaxNode e) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); if (e is NamespaceDeclarationSyntax) { return ((NamespaceDeclarationSyntax)e).Name.ToString (); } @@ -292,7 +292,7 @@ namespace MonoDevelop.CSharp sb.Append ("</s>");
sb.Insert (0, "<s>"); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs index 7dff40c4c8..42af703adb 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/CSharpBindingCompilerManager.cs @@ -99,7 +99,7 @@ namespace MonoDevelop.CSharp return null; } - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); HashSet<string> alreadyAddedReference = new HashSet<string> (); @@ -332,7 +332,7 @@ namespace MonoDevelop.CSharp workingDir = configuration.ParentItem.BaseDirectory; LoggingService.LogInfo (compilerName + " " + sb); - + StringBuilderCache.Free (sb); ExecutionEnvironment envVars = runtime.GetToolsExecutionEnvironment (project.TargetFramework); string cargs = "/noconfig @\"" + responseFileName + "\""; @@ -375,7 +375,7 @@ namespace MonoDevelop.CSharp { BuildResult result = new BuildResult (); - StringBuilder compilerOutput = new StringBuilder (); + StringBuilder compilerOutput = StringBuilderCache.Allocate (); bool typeLoadException = false; foreach (string s in new string[] { stdout, stderr }) { StreamReader sr = File.OpenText (s); @@ -414,7 +414,7 @@ namespace MonoDevelop.CSharp else result.AddError ("", 0, 0, "", "Error: A dependency of a referenced assembly may be missing, or you may be referencing an assembly created with a newer CLR version. See the compilation output for more details."); } - result.CompilerOutput = compilerOutput.ToString (); + result.CompilerOutput = StringBuilderCache.ReturnAndFree (compilerOutput); return result; } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/PathedDocumentTextEditorExtension.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/PathedDocumentTextEditorExtension.cs index 0a65c53a4d..b05703972e 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/PathedDocumentTextEditorExtension.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/PathedDocumentTextEditorExtension.cs @@ -454,13 +454,13 @@ namespace MonoDevelop.CSharp if (tag is SyntaxTree) { var type = node; if (type != null) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); sb.Append (ext.GetEntityMarkup (type)); while (type.Parent is BaseTypeDeclarationSyntax) { sb.Insert (0, ext.GetEntityMarkup (type.Parent) + "."); type = type.Parent; } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } var accessor = node as AccessorDeclarationSyntax; @@ -506,13 +506,13 @@ namespace MonoDevelop.CSharp if (tag is SyntaxTree) { var type = node; if (type != null) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); sb.Append (ext.GetEntityMarkup (type)); while (type.Parent is BaseTypeDeclarationSyntax) { sb.Insert (0, ext.GetEntityMarkup (type.Parent) + "."); type = type.Parent; } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } return ext.GetEntityMarkup (node); diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/SignatureMarkupCreator.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/SignatureMarkupCreator.cs index 4076a5e127..7e1fe63c05 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp/SignatureMarkupCreator.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp/SignatureMarkupCreator.cs @@ -79,14 +79,7 @@ namespace MonoDevelop.CSharp public SignatureMarkupCreator (DocumentContext ctx, int offset) { this.offset = offset; - try { - this.colorStyle = SyntaxHighlightingService.GetEditorTheme (Ide.IdeApp.Preferences.ColorScheme); - if (!this.colorStyle.FitsIdeTheme (Ide.IdeApp.Preferences.UserInterfaceTheme)) - this.colorStyle = SyntaxHighlightingService.GetDefaultColorStyle (Ide.IdeApp.Preferences.UserInterfaceTheme); - } catch (Exception e) { - LoggingService.LogError ("Error while getting the color style : " + Ide.IdeApp.Preferences.ColorScheme + " in ide theme : " + Ide.IdeApp.Preferences.UserInterfaceTheme, e); - this.colorStyle = SyntaxHighlightingService.DefaultColorStyle; - } + this.colorStyle = SyntaxHighlightingService.GetIdeFittingTheme (); this.ctx = ctx; if (ctx != null) { this.options = ctx.GetOptionSet (); @@ -119,7 +112,7 @@ namespace MonoDevelop.CSharp if (type.TypeKind == TypeKind.Pointer) return GetTypeReferenceString (((IPointerTypeSymbol)type).PointedAtType, highlight) + "*"; if (type.IsTupleType ()) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); sb.Append ("("); foreach (var member in type.GetMembers ().OfType<IFieldSymbol> ()) { if (member.CorrespondingTupleField == null || @@ -132,7 +125,7 @@ namespace MonoDevelop.CSharp sb.Append (Ambience.EscapeText (member.Name)); } sb.Append (")"); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } string displayString; if (ctx != null) { @@ -245,11 +238,11 @@ namespace MonoDevelop.CSharp string GetNamespaceMarkup (INamespaceSymbol ns) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); result.Append (Highlight ("namespace ", GetThemeColor (keywordOther))); result.Append (ns.Name); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } const string modifierColor = "storage.modifier.source.cs"; @@ -398,7 +391,7 @@ namespace MonoDevelop.CSharp { if (t == null) throw new ArgumentNullException ("t"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); var highlightedTypeName = Highlight (FilterEntityName (t.Name), GetThemeColor (userTypes)); result.Append (highlightedTypeName); @@ -443,14 +436,12 @@ namespace MonoDevelop.CSharp } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } string GetNullableMarkup (ITypeSymbol t) { - var result = new StringBuilder (); - result.Append (GetTypeReferenceString (t)); - return result.ToString (); + return GetTypeReferenceString (t); } void AppendTypeParameterList (StringBuilder result, INamedTypeSymbol def) @@ -471,7 +462,7 @@ namespace MonoDevelop.CSharp string GetTypeNameWithParameters (ITypeSymbol t) { - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); result.Append (Highlight (FilterEntityName (t.Name), GetThemeColor (userTypes))); var namedTypeSymbol = t as INamedTypeSymbol; if (namedTypeSymbol != null) { @@ -481,7 +472,7 @@ namespace MonoDevelop.CSharp AppendTypeArgumentList (result, namedTypeSymbol); } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public static bool IsNullableType (ITypeSymbol type) @@ -505,7 +496,7 @@ namespace MonoDevelop.CSharp return GetTypeReferenceString (t); if (t.IsNullable ()) return GetNullableMarkup (t); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); if (IsNullableType (t)) AppendModifiers (result, t); @@ -538,7 +529,7 @@ namespace MonoDevelop.CSharp } if (t.TypeKind == TypeKind.Array) - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); bool first = true; int maxLength = GetMarkupLength (result.ToString ()); @@ -581,7 +572,7 @@ namespace MonoDevelop.CSharp } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } void AppendTypeParameters (StringBuilder result, ImmutableArray<ITypeParameterSymbol> typeParameters) @@ -640,7 +631,7 @@ namespace MonoDevelop.CSharp throw new ArgumentNullException ("type"); var t = type; - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); var method = t.GetDelegateInvokeMethod (); result.Append (GetTypeReferenceString (method.ReturnType)); @@ -668,12 +659,12 @@ namespace MonoDevelop.CSharp false ); result.Append (')'); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } string GetDelegateMarkup (INamedTypeSymbol delegateType) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); var type = delegateType.IsUnboundGenericType ? delegateType.OriginalDefinition : delegateType; var method = type.GetDelegateInvokeMethod (); @@ -706,7 +697,7 @@ namespace MonoDevelop.CSharp false /* formattingOptions.SpaceBeforeDelegateDeclarationParameterComma */, false /* formattingOptions.SpaceAfterDelegateDeclarationParameterComma */); result.Append (')'); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } string GetLocalVariableMarkup (ILocalSymbol local) @@ -714,7 +705,7 @@ namespace MonoDevelop.CSharp if (local == null) throw new ArgumentNullException ("local"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); if (local.IsConst) result.Append (Highlight ("const ", GetThemeColor (modifierColor))); @@ -737,7 +728,7 @@ namespace MonoDevelop.CSharp AppendConstant (result, local.Type, local.ConstantValue); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } string GetParameterVariableMarkup (IParameterSymbol parameter) @@ -745,7 +736,7 @@ namespace MonoDevelop.CSharp if (parameter == null) throw new ArgumentNullException ("parameter"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); AppendParameter (result, parameter); if (parameter.HasExplicitDefaultValue) { @@ -757,7 +748,7 @@ namespace MonoDevelop.CSharp AppendConstant (result, parameter.Type, parameter.ExplicitDefaultValue); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } @@ -766,7 +757,7 @@ namespace MonoDevelop.CSharp if (field == null) throw new ArgumentNullException ("field"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); bool isEnum = field.ContainingType.TypeKind == TypeKind.Enum; if (!isEnum) { AppendModifiers (result, field); @@ -808,7 +799,7 @@ namespace MonoDevelop.CSharp AppendConstant (result, field.Type, field.ConstantValue, isEnum); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } string GetMethodMarkup (IMethodSymbol method) @@ -816,7 +807,7 @@ namespace MonoDevelop.CSharp if (method == null) throw new ArgumentNullException ("method"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); AppendModifiers (result, method); result.Append (GetTypeReferenceString (method.ReturnType)); if (BreakLineAfterReturnType) { @@ -854,7 +845,7 @@ namespace MonoDevelop.CSharp false /* formattingOptions.SpaceBeforeMethodDeclarationParameterComma*/, false /* formattingOptions.SpaceAfterMethodDeclarationParameterComma*/); result.Append (')'); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } string GetConstructorMarkup (IMethodSymbol method) @@ -863,7 +854,7 @@ namespace MonoDevelop.CSharp throw new ArgumentNullException ("method"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); AppendModifiers (result, method); result.Append (FilterEntityName (method.ContainingType.Name)); @@ -884,7 +875,7 @@ namespace MonoDevelop.CSharp false /* formattingOptions.SpaceAfterConstructorDeclarationParameterComma */); } result.Append (')'); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } string GetDestructorMarkup (IMethodSymbol method) @@ -892,7 +883,7 @@ namespace MonoDevelop.CSharp if (method == null) throw new ArgumentNullException ("method"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); AppendModifiers (result, method); if (BreakLineAfterReturnType) { result.AppendLine (); @@ -911,7 +902,7 @@ namespace MonoDevelop.CSharp false /* formattingOptions.SpaceBeforeConstructorDeclarationParameterComma */, false /* formattingOptions.SpaceAfterConstructorDeclarationParameterComma */); result.Append (')'); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } bool IsAccessibleOrHasSourceCode (ISymbol entity) @@ -929,7 +920,7 @@ namespace MonoDevelop.CSharp { if (property == null) throw new ArgumentNullException ("property"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); AppendModifiers (result, property); result.Append (GetTypeReferenceString (property.Type)); if (BreakLineAfterReturnType) { @@ -975,7 +966,7 @@ namespace MonoDevelop.CSharp } result.Append (" }"); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } @@ -1554,7 +1545,7 @@ namespace MonoDevelop.CSharp { if (evt == null) throw new ArgumentNullException ("evt"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); AppendModifiers (result, evt); result.Append (Highlight ("event ", GetThemeColor (modifierColor))); result.Append (GetTypeReferenceString (evt.Type)); @@ -1566,7 +1557,7 @@ namespace MonoDevelop.CSharp AppendExplicitInterfaces (result, evt.ExplicitInterfaceImplementations.Cast<ISymbol> ()); result.Append (HighlightSemantically (FilterEntityName (evt.Name), GetThemeColor ("entity.name.event.source.cs"))); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } bool grayOut; @@ -1785,7 +1776,7 @@ namespace MonoDevelop.CSharp static string ConvertString (string str) { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (char ch in str) { if (ch == '"') { sb.Append ("\\\""); @@ -1793,7 +1784,7 @@ namespace MonoDevelop.CSharp sb.Append (ConvertChar (ch)); } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } HslColor GetThemeColor (string scope) @@ -1833,7 +1824,7 @@ namespace MonoDevelop.CSharp { if (arrayType == null) throw new ArgumentNullException ("arrayType"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); result.Append (GetTypeReferenceString (arrayType.ElementType)); if (BreakLineAfterReturnType) { result.AppendLine (); @@ -1864,7 +1855,7 @@ namespace MonoDevelop.CSharp result.Append (Highlight (" set", GetThemeColor (keywordOther))).Append (";"); result.Append (" }"); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } diff --git a/main/src/addins/MacPlatform/MacPlatform.cs b/main/src/addins/MacPlatform/MacPlatform.cs index f1a790da4c..6d521ef2c3 100644 --- a/main/src/addins/MacPlatform/MacPlatform.cs +++ b/main/src/addins/MacPlatform/MacPlatform.cs @@ -144,7 +144,8 @@ namespace MonoDevelop.MacIntegration var dir = Path.GetDirectoryName (typeof(MacPlatformService).Assembly.Location); - ObjCRuntime.Dlfcn.dlopen (Path.Combine (dir, "libxammac.dylib"), 0); + if (ObjCRuntime.Dlfcn.dlopen (Path.Combine (dir, "libxammac.dylib"), 0) == IntPtr.Zero) + LoggingService.LogFatalError ("Unable to load libxammac"); mimemap = new Lazy<Dictionary<string, string>> (LoadMimeMapAsync); diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs index 326f85209b..ecbfd73d9b 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VSCodeDebuggerSession.cs @@ -344,6 +344,12 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol break; } break; + case "module": + var moduleEvent = (ModuleEvent)obj.Body; + if (moduleEvent.Reason == ModuleEvent.ReasonValue.New) { + OnAssemblyLoaded (moduleEvent.Module.Path); + } + break; } }); } diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs index cde6228e62..d89f406cea 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeBacktrace.cs @@ -44,9 +44,12 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol List<ObjectValue> results = new List<ObjectValue> (); var scopeBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new ScopesRequest (frames [frameIndex].Id)); foreach (var variablesGroup in scopeBody.Scopes) { - var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference)); - foreach (var variable in varibles.Variables) { - results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, variable.Name, variable.EvaluateName, variable.Type, variable.Value, variable.VariablesReference, variablesGroup.VariablesReference, frames [frameIndex].Id)); + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference)); + foreach (var variable in varibles.Variables) { + results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, variable.Name, variable.EvaluateName, variable.Type, variable.Value, variable.VariablesReference, variablesGroup.VariablesReference, frames [frameIndex].Id)); + } + timer.Success = true; } } return results.ToArray (); @@ -66,10 +69,13 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol { var results = new List<ObjectValue> (); foreach (var expr in expressions) { - var responseBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest ( - expr, - frames [frameIndex].Id)); - results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, expr, expr, responseBody.Type, responseBody.Result, responseBody.VariablesReference, 0, frames [frameIndex].Id)); + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + var responseBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest ( + expr, + frames [frameIndex].Id)); + results.Add (VsCodeVariableToObjectValue (vsCodeDebuggerSession, expr, expr, responseBody.Type, responseBody.Result, responseBody.VariablesReference, 0, frames [frameIndex].Id)); + timer.Success = true; + } } return results.ToArray (); } @@ -84,9 +90,12 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol List<ObjectValue> results = new List<ObjectValue> (); var scopeBody = vsCodeDebuggerSession.protocolClient.SendRequestSync (new ScopesRequest (frames [frameIndex].Id)); foreach (var variablesGroup in scopeBody.Scopes) { - var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference)); - foreach (var variable in varibles.Variables) { - results.Add (ObjectValue.CreatePrimitive (null, new ObjectPath (variable.Name), variable.Type ?? "<unknown>", new EvaluationResult (variable.Value), ObjectValueFlags.None)); + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + var varibles = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest (variablesGroup.VariablesReference)); + foreach (var variable in varibles.Variables) { + results.Add (ObjectValue.CreatePrimitive (null, new ObjectPath (variable.Name), variable.Type ?? "<unknown>", new EvaluationResult (variable.Value), ObjectValueFlags.None)); + } + timer.Success = true; } } return results.ToArray (); diff --git a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs index b6cfc55f76..02fd9fc481 100644 --- a/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs +++ b/main/src/addins/MonoDevelop.Debugger.VSCodeDebugProtocol/MonoDevelop.Debugger.VsCodeDebugProtocol/VsCodeObjectSource.cs @@ -41,10 +41,13 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol if (variablesReference <= 0) { objValChildren = new ObjectValue [0]; } else { - var children = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest ( - variablesReference - )).Variables; - objValChildren = children.Select (c => VSCodeDebuggerBacktrace.VsCodeVariableToObjectValue (vsCodeDebuggerSession, c.Name, c.EvaluateName, c.Type, c.Value, c.VariablesReference, variablesReference, frameId)).ToArray (); + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + var children = vsCodeDebuggerSession.protocolClient.SendRequestSync (new VariablesRequest ( + variablesReference + )).Variables; + objValChildren = children.Select (c => VSCodeDebuggerBacktrace.VsCodeVariableToObjectValue (vsCodeDebuggerSession, c.Name, c.EvaluateName, c.Type, c.Value, c.VariablesReference, variablesReference, frameId)).ToArray (); + timer.Success = true; + } } } return objValChildren; @@ -79,7 +82,11 @@ namespace MonoDevelop.Debugger.VsCodeDebugProtocol public object GetRawValue (ObjectPath path, EvaluationOptions options) { - var val = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest (evalName, frameId)).Result; + string val = null; + using (var timer = vsCodeDebuggerSession.EvaluationStats.StartTimer ()) { + val = vsCodeDebuggerSession.protocolClient.SendRequestSync (new EvaluateRequest (evalName, frameId)).Result; + timer.Success = true; + } if (val.StartsWith ("\"", StringComparison.Ordinal)) if (options.ChunkRawStrings) return new RawValueString (new RawString (val)); diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj index 2db55351da..4a6c516637 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger.csproj @@ -211,6 +211,7 @@ <Compile Include="MonoDevelop.Debugger\NoSourceView.cs" /> <Compile Include="MonoDevelop.Debugger\DebugSourceFilesOptionsPanel.cs" /> <Compile Include="MonoDevelop.Debugger\SolutionHasDebugSourceFilesCondition.cs" /> + <Compile Include="MonoDevelop.Debugger\Counters.cs" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="MonoDevelop.Debugger.addin.xml"> diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Counters.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Counters.cs new file mode 100644 index 0000000000..83c1330cc0 --- /dev/null +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/Counters.cs @@ -0,0 +1,36 @@ +// +// Counters.cs +// +// Author: +// Matt Ward <matt.ward@microsoft.com> +// +// Copyright (c) 2018 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 MonoDevelop.Core.Instrumentation; + +namespace MonoDevelop.Debugger +{ + static class Counters + { + public static Counter DebugSession = InstrumentationService.CreateCounter ("Debug Session", "Debugger", id: "Debugger.DebugSession"); + public static Counter EvaluationStats = InstrumentationService.CreateCounter ("Evaluation Statistics", "Debugger", id: "Debugger.EvaluationStatistics"); + } +} diff --git a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs index 1d3692426e..17f5b10fb9 100644 --- a/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs +++ b/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebuggingService.cs @@ -593,7 +593,6 @@ namespace MonoDevelop.Debugger var session = debugger.CreateSession (); var monitor = IdeApp.Workbench.ProgressMonitors.GetRunProgressMonitor (proc.Name); var sessionManager = new SessionManager (session, monitor.Console, debugger); - session.ExceptionHandler = ExceptionHandler; SetupSession (sessionManager); session.TargetExited += delegate { monitor.Dispose (); @@ -676,7 +675,6 @@ namespace MonoDevelop.Debugger startInfo.CloseExternalConsoleOnExit = ((ExternalConsole)c).CloseOnDispose; var session = factory.CreateSession (); - session.ExceptionHandler = ExceptionHandler; SessionManager sessionManager; // When using an external console, create a new internal console which will be used @@ -690,8 +688,10 @@ namespace MonoDevelop.Debugger SetDebugLayout (); try { + sessionManager.PrepareForRun (); session.Run (startInfo, GetUserOptions ()); } catch { + sessionManager.SessionError = true; Cleanup (sessionManager); throw; } @@ -713,6 +713,8 @@ namespace MonoDevelop.Debugger { OperationConsole console; IDisposable cancelRegistration; + System.Diagnostics.Stopwatch firstAssemblyLoadTimer; + public readonly DebuggerSession Session; public readonly DebugAsyncOperation debugOperation; public readonly DebuggerEngine Engine; @@ -721,6 +723,8 @@ namespace MonoDevelop.Debugger { Engine = engine; Session = session; + session.ExceptionHandler = ExceptionHandler; + session.AssemblyLoaded += OnAssemblyLoaded; this.console = console; cancelRegistration = console.CancellationToken.Register (Cancel); debugOperation = new DebugAsyncOperation (session); @@ -761,13 +765,80 @@ namespace MonoDevelop.Debugger public void Dispose () { + UpdateDebugSessionCounter (); + UpdateEvaluationStatsCounter (); + console?.Dispose (); console = null; + Session.AssemblyLoaded -= OnAssemblyLoaded; Session.Dispose (); debugOperation.Cleanup (); cancelRegistration?.Dispose (); cancelRegistration = null; } + + /// <summary> + /// Indicates whether the debug session failed to an exception or any debugger + /// operation failed and was reported to the user. + /// </summary> + public bool SessionError { get; set; } + + void UpdateDebugSessionCounter () + { + var metadata = new Dictionary<string, string> (); + metadata ["Success"] = (!SessionError).ToString (); + metadata ["DebuggerType"] = Engine.Id; + + if (firstAssemblyLoadTimer != null) { + if (firstAssemblyLoadTimer.IsRunning) { + // No first assembly load event. + firstAssemblyLoadTimer.Stop (); + } else { + metadata ["AssemblyFirstLoadDuration"] = firstAssemblyLoadTimer.ElapsedMilliseconds.ToString (); + } + } + + Counters.DebugSession.Inc (metadata); + } + + void UpdateEvaluationStatsCounter () + { + if (Session.EvaluationStats.TimingsCount == 0 && Session.EvaluationStats.FailureCount == 0) { + // No timings or failures recorded. + return; + } + + var metadata = new Dictionary<string, string> (); + metadata ["DebuggerType"] = Engine.Id; + metadata ["AverageDuration"] = Session.EvaluationStats.AverageTime.ToString (); + metadata ["MaximumDuration"] = Session.EvaluationStats.MaxTime.ToString (); + metadata ["MinimumDuration"] = Session.EvaluationStats.MinTime.ToString (); + metadata ["FailureCount"] = Session.EvaluationStats.FailureCount.ToString (); + metadata ["SuccessCount"] = Session.EvaluationStats.TimingsCount.ToString (); + + Counters.EvaluationStats.Inc (metadata); + } + + bool ExceptionHandler (Exception ex) + { + SessionError = true; + return DebuggingService.ExceptionHandler (ex); + } + + /// <summary> + /// Called just before DebugSession.Run is called. + /// </summary> + public void PrepareForRun () + { + firstAssemblyLoadTimer = new System.Diagnostics.Stopwatch (); + firstAssemblyLoadTimer.Start (); + } + + void OnAssemblyLoaded (object sender, AssemblyEventArgs e) + { + DebuggerSession.AssemblyLoaded -= OnAssemblyLoaded; + firstAssemblyLoadTimer?.Stop (); + } } static async void OnBusyStateChanged (object s, BusyStateEventArgs args) diff --git a/main/src/addins/MonoDevelop.DocFood/MonoDevelop.DocFood/Commands.cs b/main/src/addins/MonoDevelop.DocFood/MonoDevelop.DocFood/Commands.cs index e83619fdf9..6d641dfacf 100644 --- a/main/src/addins/MonoDevelop.DocFood/MonoDevelop.DocFood/Commands.cs +++ b/main/src/addins/MonoDevelop.DocFood/MonoDevelop.DocFood/Commands.cs @@ -31,6 +31,7 @@ using System.Text; using Microsoft.CodeAnalysis; using System.Linq; using MonoDevelop.Ide.Editor; +using MonoDevelop.Core; namespace MonoDevelop.DocFood { @@ -130,7 +131,7 @@ namespace MonoDevelop.DocFood internal static string GenerateDocumentation (IReadonlyTextDocument data, ISymbol member, string indent, string prefix) { - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); var generator = new DocGenerator (data); generator.GenerateDoc (member); @@ -168,7 +169,7 @@ namespace MonoDevelop.DocFood } bool inTag = false; int column = indent.Length + prefix.Length; - StringBuilder curWord = new StringBuilder (); + StringBuilder curWord = StringBuilderCache.Allocate (); foreach (char ch in section.Documentation) { if (ch == '<') inTag = true; @@ -213,14 +214,15 @@ namespace MonoDevelop.DocFood result.Append ("</"); result.Append (section.Name); result.Append (">"); + StringBuilderCache.ReturnAndFree (curWord); } result.AppendLine (); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } internal static string GenerateEmptyDocumentation (IReadonlyTextDocument data, ISymbol member, string indent) { - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); DocGenerator generator = new DocGenerator (data); generator.GenerateDoc (member); @@ -266,7 +268,7 @@ namespace MonoDevelop.DocFood result.Append (">"); } result.AppendLine (); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } } diff --git a/main/src/addins/MonoDevelop.DocFood/MonoDevelop.DocFood/DocGenerator.cs b/main/src/addins/MonoDevelop.DocFood/MonoDevelop.DocFood/DocGenerator.cs index 77f36c6617..88d441805d 100644 --- a/main/src/addins/MonoDevelop.DocFood/MonoDevelop.DocFood/DocGenerator.cs +++ b/main/src/addins/MonoDevelop.DocFood/MonoDevelop.DocFood/DocGenerator.cs @@ -88,7 +88,7 @@ namespace MonoDevelop.DocFood { if (string.IsNullOrEmpty (xmlDoc)) return; - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); sb.Append ("<root>"); bool wasWs = false; foreach (char ch in xmlDoc) { @@ -105,7 +105,7 @@ namespace MonoDevelop.DocFood } sb.Append ("</root>"); try { - using (var reader = XmlTextReader.Create (new System.IO.StringReader (sb.ToString ()))) { + using (var reader = XmlTextReader.Create (new System.IO.StringReader (StringBuilderCache.ReturnAndFree (sb)))) { while (reader.Read ()) { if (reader.NodeType != XmlNodeType.Element) continue; @@ -447,7 +447,7 @@ namespace MonoDevelop.DocFood var parameters = property != null? property.Parameters : method.Parameters; var parameterNames = new List<string> (from p in parameters select p.Name); tags ["ParameterSentence"] = string.Join (" ", parameterNames.ToArray ()); - StringBuilder paramList = new StringBuilder (); + StringBuilder paramList = StringBuilderCache.Allocate (); for (int i = 0; i < parameterNames.Count; i++) { if (i > 0) { if (i == parameterNames.Count - 1) { @@ -458,7 +458,7 @@ namespace MonoDevelop.DocFood } paramList.Append (parameterNames [i]); } - tags ["ParameterList"] = paramList.ToString (); + tags ["ParameterList"] = StringBuilderCache.ReturnAndFree (paramList); for (int i = 0; i < parameters.Length; i++) { tags ["Parameter" + i + ".Type"] = parameters [i].Type != null ? "<see cref=\"" + parameters [i].Type + "\"/>" : ""; tags ["Parameter" + i + ".Name"] = "<c>" + parameters [i].Name + "</c>"; @@ -940,7 +940,7 @@ namespace MonoDevelop.DocFood static string SeparateWords (string name) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); bool wasUnderscore = false; for (int i = 0; i < name.Length; i++) { char ch = name [i]; @@ -975,7 +975,7 @@ namespace MonoDevelop.DocFood result.Append (char.ToLower (ch)); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } static ImmutableArray<IParameterSymbol> GetParameters (ISymbol symbol) diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PickMembersService/PickMembersDialog.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PickMembersService/PickMembersDialog.cs new file mode 100644 index 0000000000..358b9aeae0 --- /dev/null +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PickMembersService/PickMembersDialog.cs @@ -0,0 +1,174 @@ +// +// PickMembersDialog.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Notification; +using Microsoft.CodeAnalysis.LanguageServices; +using System.Collections.Generic; +using Xwt; +using MonoDevelop.Ide; +using MonoDevelop.Components; +using System.Linq; +using MonoDevelop.Core; +using Xwt.Drawing; +using MonoDevelop.Components.AtkCocoaHelper; +using Microsoft.CodeAnalysis.PickMembers; +using System.Collections.Immutable; + +namespace MonoDevelop.Refactoring.PickMembersService +{ + class PickMembersDialog : Xwt.Dialog + { + DataField<bool> symbolIncludedField = new DataField<bool> (); + DataField<string> symbolTextField = new DataField<string> (); + DataField<Image> symbolIconField = new DataField<Image> (); + + DataField<ISymbol> symbolField = new DataField<ISymbol> (); + + ListStore treeStore; + + public IEnumerable<ISymbol> IncludedMembers { + get { + for (int i = 0; i < treeStore.RowCount; i++) { + if (treeStore.GetValue (i, symbolIncludedField)) + yield return treeStore.GetValue (i, symbolField); + } + } + } + + public ImmutableArray<PickMembersOption> Options { get; set; } + + ListBox listBoxPublicMembers = new ListBox (); + + public PickMembersDialog () + { + this.Build (); + this.buttonSelectAll.Clicked += delegate { + for (int i = 0; i < treeStore.RowCount; i++) { + treeStore.SetValue (i, symbolIncludedField, true); + } + UpdateOkButton (); + }; + + this.buttonDeselectAll.Clicked += delegate { + for (int i = 0; i < treeStore.RowCount; i++) { + treeStore.SetValue (i, symbolIncludedField, false); + } + UpdateOkButton (); + }; + + listBoxPublicMembers.DataSource = treeStore; + var checkBoxCellView = new CheckBoxCellView (symbolIncludedField); + checkBoxCellView.Editable = true; + checkBoxCellView.Toggled += delegate { UpdateOkButton (); }; + listBoxPublicMembers.Views.Add (checkBoxCellView); + listBoxPublicMembers.Views.Add (new ImageCellView (symbolIconField)); + listBoxPublicMembers.Views.Add (new TextCellView (symbolTextField)); + } + + void Build () + { + this.TransientFor = MessageDialog.RootWindow; + this.Title = GettextCatalog.GetString ("Pick members"); + + treeStore = new ListStore (symbolIncludedField, symbolField, symbolTextField, symbolIconField); + var box = new VBox { + // Margin = 6, + // Spacing = 6 + }; + +/* box.PackStart (new Label { + Markup = "<b>" + GettextCatalog.GetString ("Select public members for the interface:") + "</b>" + });*/ + + var hbox = new HBox { + // Spacing = 6 + }; + hbox.PackStart (listBoxPublicMembers, true); + listBoxPublicMembers.Accessible.Description = GettextCatalog.GetString ("Pick members"); + + var vbox = new VBox { + // Spacing = 6 + }; + buttonSelectAll = new Button (GettextCatalog.GetString ("Select All")); + buttonSelectAll.Clicked += delegate { + UpdateOkButton (); + }; + vbox.PackStart (buttonSelectAll); + + buttonDeselectAll = new Button (GettextCatalog.GetString ("Clear")); + buttonDeselectAll.Clicked += delegate { + UpdateOkButton (); + }; + vbox.PackStart (buttonDeselectAll); + + hbox.PackStart (vbox); + + box.PackStart (hbox, true); + + Content = box; + Buttons.Add (new DialogButton (Command.Cancel)); + Buttons.Add (okButton = new DialogButton (Command.Ok)); + this.DefaultCommand = okButton.Command; + + this.Width = 400; + this.Height = 321; + this.Resizable = false; + } + + static SymbolDisplayFormat memberDisplayFormat = new SymbolDisplayFormat ( + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, + memberOptions: SymbolDisplayMemberOptions.IncludeParameters, + parameterOptions: SymbolDisplayParameterOptions.IncludeType | SymbolDisplayParameterOptions.IncludeParamsRefOut | SymbolDisplayParameterOptions.IncludeOptionalBrackets, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.UseSpecialTypes); + + Button buttonSelectAll; + Button buttonDeselectAll; + DialogButton okButton; + + + internal void Init (string title, ImmutableArray<ISymbol> members, ImmutableArray<PickMembersOption> options) + { + this.Title = title; + listBoxPublicMembers.Accessible.Description = title; + + this.Options = options; + treeStore.Clear (); + foreach (var member in members) { + var row = treeStore.AddRow (); + treeStore.SetValue (row, symbolIncludedField, false); + treeStore.SetValue (row, symbolField, member); + treeStore.SetValue (row, symbolTextField, member.ToDisplayString (memberDisplayFormat)); + treeStore.SetValue (row, symbolIconField, ImageService.GetIcon (MonoDevelop.Ide.TypeSystem.Stock.GetStockIcon (member))); + } + } + + void UpdateOkButton () + { + okButton.Sensitive = true; + } + } +} diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PickMembersService/PickMembersService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PickMembersService/PickMembersService.cs new file mode 100644 index 0000000000..ce139a758b --- /dev/null +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.PickMembersService/PickMembersService.cs @@ -0,0 +1,62 @@ +// +// PickMembersService.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// 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.Immutable;
+using System.Composition; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PickMembers; +using MonoDevelop.Core; + +namespace MonoDevelop.Refactoring.PickMembersService +{ + [ExportWorkspaceService(typeof(IPickMembersService)), Shared] + class PickMembersService : IPickMembersService + { + PickMembersResult IPickMembersService.PickMembers(string title, ImmutableArray<ISymbol> members, ImmutableArray<PickMembersOption> options) + { + PickMembersResult result = null; + Xwt.Toolkit.NativeEngine.Invoke (delegate { + var dialog = new PickMembersDialog (); + try { + dialog.Init (title, members, options); + bool performChange = dialog.Run () == Xwt.Command.Ok; + if (!performChange) { + result = PickMembersResult.Canceled; + } else { + result = new PickMembersResult (dialog.IncludedMembers.ToImmutableArray (), dialog.Options); + } + } catch (Exception ex) { + LoggingService.LogError ("Error while signature changing.", ex); + result = PickMembersResult.Canceled; + } finally { + dialog.Dispose (); + } + }); + return result; + } + } +}
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.Rename/RenameItemDialog.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.Rename/RenameItemDialog.cs index 9d826f5273..b824b6dd0d 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.Rename/RenameItemDialog.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.Rename/RenameItemDialog.cs @@ -37,6 +37,7 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis; using System.Threading.Tasks; using RefactoringEssentials; +using System.IO; namespace MonoDevelop.Refactoring.Rename { @@ -44,6 +45,8 @@ namespace MonoDevelop.Refactoring.Rename { Func<RenameRefactoring.RenameProperties, Task<IList<Change>>> rename; + internal List<string> ChangedDocuments { get; set; } + public RenameItemDialog (string title, string currentName, Func<RenameRefactoring.RenameProperties, Task<IList<Change>>> renameOperation) { this.Build (); @@ -195,13 +198,47 @@ namespace MonoDevelop.Refactoring.Rename }; } } - + async void OnOKClicked (object sender, EventArgs e) { var properties = Properties; ((Widget)this).Destroy (); var changes = await this.rename (properties); ProgressMonitor monitor = IdeApp.Workbench.ProgressMonitors.GetBackgroundProgressMonitor (Title, null); + + + if (ChangedDocuments != null) { + AlertButton result = null; + var msg = new QuestionMessage (); + msg.Buttons.Add (AlertButton.MakeWriteable); + msg.Buttons.Add (AlertButton.Cancel); + msg.AllowApplyToAll = true; + + foreach (var path in ChangedDocuments) { + try { + var attr = File.GetAttributes (path); + if (attr.HasFlag (FileAttributes.ReadOnly)) { + msg.Text = GettextCatalog.GetString ("File {0} is read-only", path); + msg.SecondaryText = GettextCatalog.GetString ("Would you like to make the file writable?"); + result = MessageService.AskQuestion (msg); + + if (result == AlertButton.Cancel) { + return; + } else if (result == AlertButton.MakeWriteable) { + try { + File.SetAttributes (path, attr & ~FileAttributes.ReadOnly); + } catch (Exception ex) { + MessageService.ShowError (ex.Message); + return; + } + } + } + } catch (Exception ex) { + MessageService.ShowError (ex.Message); + return; + } + } + } RefactoringService.AcceptChanges (monitor, changes); } diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.Rename/RenameRefactoring.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.Rename/RenameRefactoring.cs index 2c71792386..ece56c1b54 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.Rename/RenameRefactoring.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.Rename/RenameRefactoring.cs @@ -87,7 +87,7 @@ namespace MonoDevelop.Refactoring.Rename var cts = new CancellationTokenSource (); var newSolution = await MessageService.ExecuteTaskAndShowWaitDialog (Task.Run (() => Renamer.RenameSymbolAsync (currentSolution, symbol, "_" + symbol.Name + "_", ws.Options, cts.Token)), GettextCatalog.GetString ("Looking for all references"), cts); var projectChanges = currentSolution.GetChanges (newSolution).GetProjectChanges ().ToList (); - var changedDocuments = new HashSet<string> (); + var changedDocuments = new List<string> (); foreach (var change in projectChanges) { foreach (var changedDoc in change.GetChangedDocuments ()) { changedDocuments.Add (ws.CurrentSolution.GetDocument (changedDoc).FilePath); @@ -95,8 +95,10 @@ namespace MonoDevelop.Refactoring.Rename } if (changedDocuments.Count > 1) { - using (var dlg = new RenameItemDialog (symbol, this)) + using (var dlg = new RenameItemDialog (symbol, this)) { + dlg.ChangedDocuments = changedDocuments; MessageService.ShowCustomDialog (dlg); + } return; } diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj index d2b8064860..565f8e0bb4 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring.csproj @@ -197,6 +197,8 @@ <Compile Include="MonoDevelop.Refactoring.ExtractInterface\ExtractInterfaceDialog.cs" /> <Compile Include="MonoDevelop.AnalysisCore\MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.cs" /> <Compile Include="MonoDevelop.AnalysisCore\MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.AnalyzerAssemblyLoader.cs" /> + <Compile Include="MonoDevelop.Refactoring.PickMembersService\PickMembersService.cs" /> + <Compile Include="MonoDevelop.Refactoring.PickMembersService\PickMembersDialog.cs" /> <Compile Include="MonoDevelop.AnalysisCore\MonoDevelopWorkspaceDiagnosticAnalyzerProviderService.OptionsTable.cs" /> </ItemGroup> <ItemGroup> @@ -210,6 +212,7 @@ <Folder Include="MonoDevelop.Refactoring.GenerateType\" /> <Folder Include="MonoDevelop.Refactoring.PackageInstaller\" /> <Folder Include="MonoDevelop.Refactoring.ExtractInterface\" /> + <Folder Include="MonoDevelop.Refactoring.PickMembersService\" /> </ItemGroup> <ItemGroup> <EmbeddedResource Include="MonoDevelop.Refactoring.addin.xml"> diff --git a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringService.cs b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringService.cs index ae9f1ec7c8..348d67093a 100644 --- a/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringService.cs +++ b/main/src/addins/MonoDevelop.Refactoring/MonoDevelop.Refactoring/RefactoringService.cs @@ -242,16 +242,21 @@ namespace MonoDevelop.Refactoring { if (hintProject == null) hintProject = IdeApp.Workbench.ActiveDocument?.Project; + ITimeTracker timer = null; var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); try { + var metadata = Counters.CreateFindReferencesMetadata (); + timer = Counters.FindReferences.BeginTiming (metadata); foreach (var provider in findReferencesProvider) { try { foreach (var result in await provider.FindReferences (documentIdString, hintProject, monitor.CancellationToken)) { monitor.ReportResult (result); } } catch (OperationCanceledException) { + Counters.SetUserCancel (metadata); return; } catch (Exception ex) { + Counters.SetFailure (metadata); if (monitor != null) monitor.ReportError ("Error finding references", ex); LoggingService.LogError ("Error finding references", ex); @@ -261,6 +266,8 @@ namespace MonoDevelop.Refactoring } finally { if (monitor != null) monitor.Dispose (); + if (timer != null) + timer.Dispose (); } } @@ -268,16 +275,21 @@ namespace MonoDevelop.Refactoring { if (hintProject == null) hintProject = IdeApp.Workbench.ActiveDocument?.Project; + ITimeTracker timer = null; var monitor = IdeApp.Workbench.ProgressMonitors.GetSearchProgressMonitor (true, true); try { + var metadata = Counters.CreateFindReferencesMetadata (); + timer = Counters.FindReferences.BeginTiming (metadata); foreach (var provider in findReferencesProvider) { try { foreach (var result in await provider.FindAllReferences (documentIdString, hintProject, monitor.CancellationToken)) { monitor.ReportResult (result); } } catch (OperationCanceledException) { + Counters.SetUserCancel (metadata); return; } catch (Exception ex) { + Counters.SetFailure (metadata); if (monitor != null) monitor.ReportError ("Error finding references", ex); LoggingService.LogError ("Error finding references", ex); @@ -287,6 +299,8 @@ namespace MonoDevelop.Refactoring } finally { if (monitor != null) monitor.Dispose (); + if (timer != null) + timer.Dispose (); } } @@ -308,4 +322,26 @@ namespace MonoDevelop.Refactoring return false; } } + + internal static class Counters + { + public static TimerCounter FindReferences = InstrumentationService.CreateTimerCounter ("Find references", "Code Navigation", id: "CodeNavigation.FindReferences"); + + public static IDictionary<string, string> CreateFindReferencesMetadata () + { + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; + return metadata; + } + + public static void SetFailure (IDictionary<string, string> metadata) + { + metadata ["Result"] = "Failure"; + } + + public static void SetUserCancel (IDictionary<string, string> metadata) + { + metadata ["Result"] = "UserCancel"; + } + } } diff --git a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs index ee64a09d76..9e3615fa92 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/Mono.TextEditor/Gui/TextViewMargin.cs @@ -1053,7 +1053,7 @@ namespace Mono.TextEditor wrapper.Layout.Wrap = Pango.WrapMode.WordChar; wrapper.Layout.Width = (int)((textEditor.Allocation.Width - XOffset - TextStartPosition) * Pango.Scale.PangoScale); } - StringBuilder textBuilder = new StringBuilder (); + StringBuilder textBuilder = StringBuilderCache.Allocate (); var cachedChunks = GetCachedChunks (Document, line, offset, length); var lineOffset = line.Offset; var chunks = new List<ColoredSegment> (cachedChunks.Item1.Select (c => new ColoredSegment (c.Offset + lineOffset, c.Length, c.ScopeStack)));; @@ -1073,7 +1073,7 @@ namespace Mono.TextEditor Console.WriteLine (chunk); } } - string lineText = textBuilder.ToString (); + string lineText = StringBuilderCache.ReturnAndFree (textBuilder); uint preeditLength = 0; if (containsPreedit) { @@ -2484,7 +2484,7 @@ namespace Mono.TextEditor codeSegmentEditorWindow.Resize (w, h); int indentLength = -1; - StringBuilder textBuilder = new StringBuilder (); + StringBuilder textBuilder = StringBuilderCache.Allocate (); int curOffset = previewSegment.Offset; while (curOffset >= 0 && curOffset < previewSegment.EndOffset && curOffset < Document.Length) { DocumentLine line = Document.GetLineByOffset (curOffset); @@ -2502,7 +2502,7 @@ namespace Mono.TextEditor curOffset = line.EndOffsetIncludingDelimiter; } - codeSegmentEditorWindow.Text = textBuilder.ToString (); + codeSegmentEditorWindow.Text = StringBuilderCache.ReturnAndFree (textBuilder); HideCodeSegmentPreviewWindow (); codeSegmentEditorWindow.ShowAll (); diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj index 97402825ad..32883ae442 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor.csproj @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0"> <Import Project="..\..\..\MonoDevelop.props" /> <Import Project="$(ReferencesVSEditor)" /> @@ -301,6 +301,11 @@ <Compile Include="VSEditor\TextSelection.cs" /> <Compile Include="VSEditor\TextViewRoleSet.cs" /> <Compile Include="VSEditor\VacuousTextDataModel.cs" /> + <None Include="packages.config" /> + <Compile Include="MonoDevelop.SourceEditor\TextMarker\LineSeparatorMarker.cs" /> + <Compile Include="MonoDevelop.SourceEditor.QuickTasks\QuickTaskOverviewMode.IndicatorDrawingCache.cs" /> + <Compile Include="MonoDevelop.SourceEditor.QuickTasks\QuickTaskOverviewMode.IdleUpdater.cs" /> + <Compile Include="MonoDevelop.SourceEditor\TextEditorKeyPressTimings.cs" /> <Compile Include="VSEditor\FakeWpf\Geometry.cs" /> <Compile Include="VSEditor\FakeWpf\Mouse.cs" /> <Compile Include="VSEditor\FakeWpf\SimulatingExtensions.cs" /> @@ -610,4 +615,4 @@ <Import Project="..\..\core\Mono.TextEditor.Shared\Mono.TextEditor.Shared.projitems" Label="Shared" Condition="Exists('..\..\core\Mono.TextEditor.Shared\Mono.TextEditor.Shared.projitems')" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup /> -</Project> +</Project>
\ No newline at end of file diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/Counters.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/Counters.cs index e55ef141ff..50d6f44d4b 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/Counters.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/Counters.cs @@ -34,5 +34,6 @@ namespace MonoDevelop.SourceEditor public static Counter EditorsInMemory = InstrumentationService.CreateCounter ("Editors in Memory", "Text Editor"); public static Counter SourceViewsInMemory = InstrumentationService.CreateCounter ("Source Views in Memory", "Text Editor"); public static Counter LoadedEditors = InstrumentationService.CreateCounter ("Loaded Editors", "Text Editor"); + public static Counter Typing = InstrumentationService.CreateCounter ("Typing", "Text Editor", id: "TextEditor.Typing"); } } diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ErrorText.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ErrorText.cs index 53219debb7..e1c9c188af 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ErrorText.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ErrorText.cs @@ -43,12 +43,14 @@ namespace MonoDevelop.SourceEditor public TaskListEntry Task { get; set; } public bool IsError { get; set; } public string ErrorMessage { get; set; } + public string FullErrorMessage { get; set; } - public ErrorText (TaskListEntry task, bool isError, string errorMessage) + public ErrorText (TaskListEntry task, bool isError, string errorMessage, string fullErrorMessage) { this.Task = task; this.IsError = isError; this.ErrorMessage = errorMessage; + this.FullErrorMessage = fullErrorMessage; } public override string ToString () diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ExtensibleTextEditor.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ExtensibleTextEditor.cs index b7c8198f08..784666f7cf 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ExtensibleTextEditor.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/ExtensibleTextEditor.cs @@ -56,6 +56,7 @@ namespace MonoDevelop.SourceEditor class ExtensibleTextEditor : Mono.TextEditor.MonoTextEditor { internal object MemoryProbe = Counters.EditorsInMemory.CreateMemoryProbe (); + TextEditorKeyPressTimings keyPressTimings = new TextEditorKeyPressTimings (); SourceEditorView view; Adjustment cachedHAdjustment, cachedVAdjustment; @@ -209,6 +210,12 @@ namespace MonoDevelop.SourceEditor { IsDestroyed = true; UnregisterAdjustments (); + + if (keyPressTimings != null) { + keyPressTimings.ReportTimings (view); + keyPressTimings = null; + } + view = null;
Document.SyntaxMode = null; base.OnDestroyed (); @@ -334,9 +341,18 @@ namespace MonoDevelop.SourceEditor } } - protected internal override bool OnIMProcessedKeyPressEvent (Gdk.Key key, uint ch, Gdk.ModifierType state) { + try { + keyPressTimings.StartTimer (); + return OnIMProcessedKeyPressEventInternal (key, ch, state); + } finally { + keyPressTimings.EndTimer (); + } + } + + bool OnIMProcessedKeyPressEventInternal (Gdk.Key key, uint ch, Gdk.ModifierType state) + { bool result = true; if (key == Gdk.Key.Escape) { bool b = EditorExtension != null ? ExtensionKeyPress (key, ch, state) : base.OnIMProcessedKeyPressEvent (key, ch, state); @@ -423,7 +439,7 @@ namespace MonoDevelop.SourceEditor if (sb != null)
sb.AppendLine();
else
- sb = new StringBuilder();
+ sb = StringBuilderCache.Allocate();
if (error.Error.ErrorType == MonoDevelop.Ide.TypeSystem.ErrorType.Warning) sb.Append(GettextCatalog.GetString("<b>Warning</b>: {0}", @@ -433,7 +449,7 @@ namespace MonoDevelop.SourceEditor GLib.Markup.EscapeText(error.Error.Message))); } - return sb?.ToString(); + return sb != null ? StringBuilderCache.ReturnAndFree (sb) : null; }
public MonoDevelop.Projects.Project Project { diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/MessageBubbleCache.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/MessageBubbleCache.cs index 1d0a72bfac..38959fc431 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/MessageBubbleCache.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/MessageBubbleCache.cs @@ -121,7 +121,7 @@ namespace MonoDevelop.SourceEditor foreach (var msg in marker.Errors) { if (marker.Layouts.Count == 1) drawingLayout.Width = maxTextWidth; - drawingLayout.SetText (GetFirstLine (msg)); + drawingLayout.SetText (msg.FullErrorMessage); int w; int h; drawingLayout.GetPixelSize (out w, out h); @@ -161,7 +161,7 @@ namespace MonoDevelop.SourceEditor if (!showBulletedList) drawingLayout.Width = maxTextWidth; - drawingLayout.SetText (GetFirstLine (msg)); + drawingLayout.SetText (msg.FullErrorMessage); drawingLayout.GetPixelSize (out w, out h); if (showBulletedList) { @@ -277,10 +277,9 @@ namespace MonoDevelop.SourceEditor } } - static string GetFirstLine (ErrorText errorText) + static string GetFirstLine (string firstLine) { - string firstLine = errorText.ErrorMessage ?? ""; - int idx = firstLine.IndexOfAny (new [] {'\n', '\r'}); + int idx = firstLine.IndexOfAny (new [] { '\n', '\r' }); if (idx > 0) firstLine = firstLine.Substring (0, idx); return firstLine; @@ -292,7 +291,7 @@ namespace MonoDevelop.SourceEditor if (!textWidthDictionary.TryGetValue (errorText.ErrorMessage, out result)) { Pango.Layout layout = new Pango.Layout (editor.PangoContext); layout.FontDescription = fontDescription; - layout.SetText (GetFirstLine (errorText)); + layout.SetText (GetFirstLine (errorText.ErrorMessage)); int w, h; layout.GetPixelSize (out w, out h); textWidthDictionary[errorText.ErrorMessage] = result = new LayoutDescriptor (layout, w, h); diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/MessageBubbleTextMarker.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/MessageBubbleTextMarker.cs index 768f0fc367..d948737d3f 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/MessageBubbleTextMarker.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/MessageBubbleTextMarker.cs @@ -182,9 +182,10 @@ namespace MonoDevelop.SourceEditor this.task = task; } var match = mcsErrorFormat.Match (errorMessage); + string trimmedMessage = errorMessage; if (match.Success) - errorMessage = match.Groups [1].Value; - errors.Add (new ErrorText (task, isError, errorMessage)); + trimmedMessage = match.Groups [1].Value; + errors.Add (new ErrorText (task, isError, trimmedMessage, errorMessage)); DisposeLayout (); } diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorPrintOperation.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorPrintOperation.cs index 63887c963d..0c5d36c64d 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorPrintOperation.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorPrintOperation.cs @@ -206,12 +206,12 @@ namespace MonoDevelop.SourceEditor string Subst (string text, int page) { - var sb = new StringBuilder (text); + var sb = StringBuilderCache.Allocate (text); sb.Replace ("%N", (page + 1).ToString ()); sb.Replace ("%Q", totalPages.ToString ()); //FIXME: use font width for ellipsizing better sb.Replace ("%F", SourceEditorWidget.EllipsizeMiddle (filename, 60)); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } void PrintFooter (Cairo.Context cr, PrintContext context, int page, ref double xPos, ref double yPos) diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorWidget.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorWidget.cs index 85e86f8a32..2c4af18636 100644 --- a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorWidget.cs +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/SourceEditorWidget.cs @@ -822,7 +822,7 @@ namespace MonoDevelop.SourceEditor internal void ConvertLineEndings () { string correctEol = TextEditor.Options.DefaultEolMarker; - var newText = new StringBuilder (); + var newText = StringBuilderCache.Allocate (); int offset = 0; foreach (var line in Document.Lines) { newText.Append (TextEditor.GetTextAt (offset, line.Length)); @@ -831,7 +831,7 @@ namespace MonoDevelop.SourceEditor newText.Append (correctEol); } view.StoreSettings (); - view.ReplaceContent (Document.FileName, newText.ToString (), view.SourceEncoding); + view.ReplaceContent (Document.FileName, StringBuilderCache.ReturnAndFree (newText), view.SourceEncoding); Document.HasLineEndingMismatchOnTextSet = false; view.LoadSettings (); } diff --git a/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextEditorKeyPressTimings.cs b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextEditorKeyPressTimings.cs new file mode 100644 index 0000000000..8dc879a2d4 --- /dev/null +++ b/main/src/addins/MonoDevelop.SourceEditor2/MonoDevelop.SourceEditor/TextEditorKeyPressTimings.cs @@ -0,0 +1,96 @@ +// +// TextEditorKeyPressTimings.cs +// +// Author: +// Matt Ward <matt.ward@xamarin.com> +// +// Copyright (c) 2017 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 +// 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.Diagnostics; +using System.Collections.Generic; + +namespace MonoDevelop.SourceEditor +{ + class TextEditorKeyPressTimings + { + Stopwatch stopwatch = new Stopwatch (); + + TimeSpan maxTime; + TimeSpan totalTime; + TimeSpan? firstTime; + int count; + + public void StartTimer () + { + stopwatch.Start (); + } + + /// <summary> + /// Overhead to a key press of using StartTimer and EndTimer is normally less + /// than 0.001ms. + /// + /// Note that the first ever key press in the text editor this can add up to + /// ~0.1ms but this is small compared with the text editor key press itself + /// which can take ~800ms for the first ever key press in the text editor for + /// the current IDE session. + /// </summary> + public void EndTimer () + { + stopwatch.Stop (); + + var duration = stopwatch.Elapsed; + if (duration > maxTime) { + maxTime = duration; + } + + if (!firstTime.HasValue) { + firstTime = duration; + } + + totalTime += duration; + count++; + + stopwatch.Reset (); + } + + public void ReportTimings (SourceEditorView sourceEditorView) + { + if (count == 0) { + // No timings recorded. + return; + } + + string extension = sourceEditorView.Document.FileName.Extension; + + var metadata = new Dictionary<string, string> (); + if (!string.IsNullOrEmpty (extension)) + metadata ["Extension"] = extension; + + var average = totalTime.TotalMilliseconds / count; + metadata ["Average"] = average.ToString (); + metadata ["First"] = firstTime.Value.TotalMilliseconds.ToString (); + metadata ["Maximum"] = maxTime.TotalMilliseconds.ToString (); + + Counters.Typing.Inc (metadata); + } + } +} diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Subversion.Unix/MonoDevelop.VersionControl.Subversion.Unix/SvnClient.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Subversion.Unix/MonoDevelop.VersionControl.Subversion.Unix/SvnClient.cs index 925c35f8e6..36818c23de 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl.Subversion.Unix/MonoDevelop.VersionControl.Subversion.Unix/SvnClient.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl.Subversion.Unix/MonoDevelop.VersionControl.Subversion.Unix/SvnClient.cs @@ -1304,7 +1304,7 @@ namespace MonoDevelop.VersionControl.Subversion.Unix progressData.LogTimer.Elapsed += delegate { progressData.Seconds += 1; Runtime.RunInMainThread (() => { - updatemonitor.Log.WriteLine (GettextCatalog.GetString ("Transferred {0} in {1} seconds.", BytesToSize (progressData.KBytes), progressData.Seconds)); + updatemonitor?.Log.WriteLine (GettextCatalog.GetString ("Transferred {0} in {1} seconds.", BytesToSize (progressData.KBytes), progressData.Seconds)); }); }; progressData.LogTimer.Start (); @@ -1480,9 +1480,9 @@ namespace MonoDevelop.VersionControl.Subversion.Unix if (updatemonitor != null && !string.IsNullOrEmpty (actiondesc)) { Runtime.RunInMainThread (() => { if (skipEol) { - updatemonitor.Log.Write (actiondesc); + updatemonitor?.Log.Write (actiondesc); } else { - updatemonitor.Log.WriteLine (actiondesc); + updatemonitor?.Log.WriteLine (actiondesc); } }); } diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameView.cs b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameView.cs index 28969ad588..a49e076cce 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameView.cs +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/MonoDevelop.VersionControl.Views/BlameView.cs @@ -47,7 +47,7 @@ namespace MonoDevelop.VersionControl.Views } } - public BlameView (VersionControlDocumentInfo info) : base (GettextCatalog.GetString ("Blame"), GettextCatalog.GetString ("Shows the blame for the current file")) + public BlameView (VersionControlDocumentInfo info) : base (GettextCatalog.GetString ("Authors"), GettextCatalog.GetString ("Shows the authors of the current file")) { this.info = info; } diff --git a/main/src/addins/VersionControl/MonoDevelop.VersionControl/VersionControl.addin.xml b/main/src/addins/VersionControl/MonoDevelop.VersionControl/VersionControl.addin.xml index 11f861310e..57f38c8d4d 100644 --- a/main/src/addins/VersionControl/MonoDevelop.VersionControl/VersionControl.addin.xml +++ b/main/src/addins/VersionControl/MonoDevelop.VersionControl/VersionControl.addin.xml @@ -20,8 +20,8 @@ <ExtensionNode name="SeparatorItem" type="MonoDevelop.Components.Commands.ExtensionNodes.SeparatorItemCodon" /> </ExtensionPoint> - <ExtensionPoint path = "/MonoDevelop/VersionControl/BlameView/ContextMenu" name = "Blame view context menu"> - <Description>Context menu for the blame view.</Description> + <ExtensionPoint path = "/MonoDevelop/VersionControl/BlameView/ContextMenu" name = "Author view context menu"> + <Description>Context menu for the Authors view.</Description> <ExtensionNodeSet id="MonoDevelop.Components.Commands.ItemSet" /> </ExtensionPoint> @@ -34,8 +34,8 @@ <Handler class = "MonoDevelop.VersionControl.DefaultDiffViewHandler" /> </Extension> - <ExtensionPoint path = "/MonoDevelop/VersionControl/BlameViewHandler" name = "Blame view handler"> - <Description>Handler used to create the a blame view which can display the current file.</Description> + <ExtensionPoint path = "/MonoDevelop/VersionControl/BlameViewHandler" name = "Author view handler"> + <Description>Handler used to create an authors view which can display the current file.</Description> <ExtensionNode name="Handler" objectType="MonoDevelop.VersionControl.IVersionControlViewHandler" /> </ExtensionPoint> @@ -167,7 +167,7 @@ defaultHandler = "MonoDevelop.VersionControl.UnlockCommandHandler" description = "Unlock files in the repository, so that other user can change them."/> <Command id = "MonoDevelop.VersionControl.Commands.Annotate" - _label = "Blame" + _label = "Authors" defaultHandler = "MonoDevelop.VersionControl.CurrentFileBlameHandler" description = "Show the origin of each line in a file."/> <Command id = "MonoDevelop.VersionControl.Commands.CreatePatch" @@ -193,7 +193,7 @@ <Command id = "MonoDevelop.VersionControl.Views.BlameCommands.ShowLog" _label = "S_how Log"/> <Command id = "MonoDevelop.VersionControl.Views.BlameCommands.ShowBlameBefore" - _label = "Show _Blame Before"/> + _label = "Show _Authors Before"/> </Category> </Extension> diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/CompressingTreeList.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/CompressingTreeList.cs index 068763df76..4c86cc3fdf 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/CompressingTreeList.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/CompressingTreeList.cs @@ -311,7 +311,8 @@ namespace Mono.TextEditor.Utils return GetNode (ref index).value; } set { - RemoveAt (index); + if (index < Count) + RemoveAt (index); Insert (index, value); } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/Diff.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/Diff.cs index 452719762c..9b532cca71 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/Diff.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/Diff.cs @@ -96,6 +96,7 @@ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; +using MonoDevelop.Core; namespace Mono.TextEditor.Utils { @@ -486,7 +487,7 @@ namespace Mono.TextEditor.Utils if (diff == null) return ""; - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); IEnumerator<Hunk> he = diff.GetEnumerator (); he.MoveNext (); @@ -533,7 +534,7 @@ namespace Mono.TextEditor.Utils sb.Append ("@@ -").Append (remStart).Append (",").Append (remEnd - remStart).Append (" +").Append (insStart).Append (",").Append (insEnd - insStart).AppendLine (" @@"); WriteHunks (qh, baseDocument, changedDocument, sb); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/HtmlWriter.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/HtmlWriter.cs index 0b767362dd..ec69683c7a 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/HtmlWriter.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/HtmlWriter.cs @@ -49,7 +49,7 @@ namespace Mono.TextEditor.Utils public static string GenerateHtml (List<List<ClipboardColoredText>> chunks, EditorTheme style, ITextEditorOptions options, bool includeBoilerplate = true) { - var htmlText = new StringBuilder (); + var htmlText = StringBuilderCache.Allocate (); if (includeBoilerplate) { htmlText.AppendLine (@"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.0 Transitional//EN"">"); htmlText.AppendLine ("<HTML>"); @@ -100,7 +100,7 @@ namespace Mono.TextEditor.Utils if (Platform.IsWindows) return GenerateCFHtml (htmlText.ToString ()); - return htmlText.ToString (); + return StringBuilderCache.ReturnAndFree (htmlText); } static readonly string emptyCFHtmlHeader = GenerateCFHtmlHeader (0, 0, 0, 0); diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RedBlackTree.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RedBlackTree.cs index f75e298da7..0ac5ecbe76 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RedBlackTree.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RedBlackTree.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; +using MonoDevelop.Core; namespace Mono.TextEditor.Utils { @@ -510,9 +511,9 @@ namespace Mono.TextEditor.Utils { if (Root == null) return "<null>"; - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); AppendNode (result, Root, 0); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RtfWriter.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RtfWriter.cs index 6a62622f19..4a5e01b847 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RtfWriter.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Utils/RtfWriter.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using Mono.TextEditor.Highlighting; using MonoDevelop.Core.Text; using MonoDevelop.Ide; +using MonoDevelop.Core; namespace Mono.TextEditor.Utils { @@ -36,7 +37,7 @@ namespace Mono.TextEditor.Utils { static string CreateColorTable (List<Cairo.Color> colorList) { - var colorTable = new StringBuilder (); + var colorTable = StringBuilderCache.Allocate (); colorTable.Append (@"{\colortbl ;"); for (int i = 0; i < colorList.Count; i++) { var color = colorList [i]; @@ -49,7 +50,7 @@ namespace Mono.TextEditor.Utils colorTable.Append (";"); } colorTable.Append ("}"); - return colorTable.ToString (); + return StringBuilderCache.ReturnAndFree (colorTable); } public static string GenerateRtf (TextEditorData data) @@ -97,7 +98,7 @@ namespace Mono.TextEditor.Utils internal static string GenerateRtf (List<List<ClipboardColoredText>> chunks, MonoDevelop.Ide.Editor.Highlighting.EditorTheme style, ITextEditorOptions options) { - var rtfText = new StringBuilder (); + var rtfText = StringBuilderCache.Allocate (); var colorList = new List<Cairo.Color> (); bool isItalic = false; @@ -131,7 +132,7 @@ namespace Mono.TextEditor.Utils rtfText.AppendLine (@"\line"); } - var rtf = new StringBuilder(); + var rtf = StringBuilderCache.Allocate (); rtf.AppendLine (@"{\rtf1\ansi\deff0\adeflang1025"); rtf.AppendLine (@"{\fonttbl"); @@ -147,9 +148,9 @@ namespace Mono.TextEditor.Utils rtf.Append (fontSize); } catch (Exception) {}; rtf.AppendLine (@"\cf1"); - rtf.Append (rtfText.ToString ()); + rtf.Append (StringBuilderCache.ReturnAndFree (rtfText)); rtf.Append("}"); - return rtf.ToString (); + return StringBuilderCache.ReturnAndFree (rtf); } } } diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Vi/ViActions.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Vi/ViActions.cs index 9b8c9d1df0..17eb2a7457 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Vi/ViActions.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor.Vi/ViActions.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Text; +using MonoDevelop.Core; using MonoDevelop.Core.Text; using MonoDevelop.Ide.Editor; @@ -121,7 +122,7 @@ namespace Mono.TextEditor.Vi DocumentLine seg = data.Document.GetLine (startLine); startOffset = seg.Offset; - StringBuilder sb = new StringBuilder (data.Document.GetTextAt (seg).TrimEnd ()); + StringBuilder sb = StringBuilderCache.Allocate (data.Document.GetTextAt (seg).TrimEnd ()); //lastSpaceOffset = startOffset + sb.Length; for (int i = startLine + 1; i <= endLine; i++) { @@ -132,7 +133,7 @@ namespace Mono.TextEditor.Vi } length = (seg.Offset - startOffset) + seg.Length; // TODO: handle conversion issues ? - data.Replace (startOffset, length, sb.ToString ()); + data.Replace (startOffset, length, StringBuilderCache.ReturnAndFree (sb)); } public static void ToggleCase (TextEditorData data) @@ -141,7 +142,7 @@ namespace Mono.TextEditor.Vi if (!data.CanEditSelection) return; - StringBuilder sb = new StringBuilder (data.SelectedText); + StringBuilder sb = StringBuilderCache.Allocate (data.SelectedText); for (int i = 0; i < sb.Length; i++) { char ch = sb [i]; if (Char.IsLower (ch)) @@ -149,7 +150,7 @@ namespace Mono.TextEditor.Vi else if (Char.IsUpper (ch)) sb [i] = Char.ToLower (ch); } - data.Replace (data.SelectionRange.Offset, data.SelectionRange.Length, sb.ToString ()); + data.Replace (data.SelectionRange.Offset, data.SelectionRange.Length, StringBuilderCache.ReturnAndFree (sb)); } else if (data.CanEdit (data.Caret.Line)) { char ch = data.Document.GetCharAt (data.Caret.Offset); if (Char.IsLower (ch)) diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs index 017424b72b..c0894e600b 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Actions/ClipboardActions.cs @@ -86,7 +86,7 @@ namespace Mono.TextEditor string GetCopiedPlainText (string eol = "\n") { - var plainText = new StringBuilder (); + var plainText = StringBuilderCache.Allocate (); bool first = true; foreach (var line in copiedColoredChunks) { if (!first) { @@ -99,7 +99,7 @@ namespace Mono.TextEditor plainText.Append (chunk.Text); } } - return plainText.ToString (); + return StringBuilderCache.ReturnAndFree (plainText); } public void SetData (SelectionData selection_data, uint info) diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentLine.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentLine.cs index d05fb34a1f..2ee0d26d59 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentLine.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/Document/DocumentLine.cs @@ -32,6 +32,7 @@ using Mono.TextEditor.Highlighting; using System.Linq; using MonoDevelop.Ide.Editor; using MonoDevelop.Core.Text; +using MonoDevelop.Core; namespace Mono.TextEditor { @@ -175,7 +176,7 @@ namespace Mono.TextEditor /// </returns> public string GetIndentation (TextDocument doc) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); int offset = Offset; int max = System.Math.Min (offset + LengthIncludingDelimiter, doc.Length); for (int i = offset; i < max; i++) { @@ -184,7 +185,7 @@ namespace Mono.TextEditor break; result.Append (ch); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public int GetLogicalColumn (TextEditorData editor, int visualColumn) diff --git a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs index 2db352345f..1609832a40 100644 --- a/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs +++ b/main/src/core/Mono.TextEditor.Shared/Mono.TextEditor/TextEditorData.cs @@ -39,6 +39,7 @@ using System.Threading; using MonoDevelop.Ide; using System.Linq; using System.Threading.Tasks; +using MonoDevelop.Core; namespace Mono.TextEditor { @@ -353,7 +354,7 @@ namespace Mono.TextEditor { if (str == null) throw new ArgumentNullException ("str"); - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); foreach (char ch in str) { switch (ch) { case '&': @@ -377,7 +378,7 @@ namespace Mono.TextEditor break; } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } internal static int CalcIndentLength (string indent) @@ -425,7 +426,7 @@ namespace Mono.TextEditor int indentLength = -1; int curOffset = offset; - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); while (curOffset < offset + length && curOffset < Document.Length) { DocumentLine line = Document.GetLineByOffset (curOffset); int toOffset = System.Math.Min (line.Offset + line.Length, offset + length); @@ -482,7 +483,7 @@ namespace Mono.TextEditor if (result.Length > 0 && curOffset < offset + length) result.AppendLine (); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } internal async Task<IEnumerable<MonoDevelop.Ide.Editor.Highlighting.ColoredSegment>> GetChunks (DocumentLine line, int offset, int length) @@ -519,7 +520,7 @@ namespace Mono.TextEditor { if (string.IsNullOrEmpty (str)) return ""; - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); bool convertTabs = TabsToSpaces; var tabSize = Options.TabSize; for (int i = 0; i < str.Length; i++) { @@ -550,7 +551,7 @@ namespace Mono.TextEditor break; } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public string FormatString (int offset, string str) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/PcFileCache.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/PcFileCache.cs index 167d48924e..3b7e7a3b88 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/PcFileCache.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/PcFileCache.cs @@ -29,6 +29,7 @@ using System.Text; using System.Xml; using System.IO; using System.Collections.Generic; +using MonoDevelop.Core; // IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT // This code is shared with xbuild, which has to build with .NET 2.0, @@ -535,7 +536,7 @@ namespace Mono.PkgConfig if (i == -1) return value; - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); int last = 0; while (i != -1 && i < value.Length) { sb.Append (value, last, i - last); @@ -546,6 +547,7 @@ namespace Mono.PkgConfig if (n == -1 || n == i) { // Closing bracket not found or empty name HasErrors = true; + StringBuilderCache.Free (sb); return value; } string rname = value.Substring (i, n - i); @@ -554,6 +556,7 @@ namespace Mono.PkgConfig sb.Append (rval); else { HasErrors = true; + StringBuilderCache.Free (sb); return value; } i = n + 1; @@ -565,7 +568,7 @@ namespace Mono.PkgConfig i = value.IndexOf ("${", i); } sb.Append (value, last, value.Length - last); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/LocalConsole.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/LocalConsole.cs index 17b496bf80..cadd1fd113 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/LocalConsole.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/LocalConsole.cs @@ -221,36 +221,36 @@ namespace MonoDevelop.Core.Execution public override string ReadLine () { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); while (LoadCurrent (true)) { for (int i=idx; i < current.Length; i++) { if (current[i] == '\n') { idx = i + 1; sb.Append (current, 0, i); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } if (current[i] == '\r') { idx = i + 1; sb.Append (current, 0, i); if (LoadCurrent (true) && current [idx] == '\n') idx++; - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } sb.Append (current, idx, current.Length - idx); current = null; } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public override string ReadToEnd () { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); while (LoadCurrent (true)) { sb.Append (current, idx, current.Length - idx); current = null; } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessArgumentBuilder.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessArgumentBuilder.cs index f43d1d72a5..f875d79a2d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessArgumentBuilder.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Execution/ProcessArgumentBuilder.cs @@ -115,11 +115,11 @@ namespace MonoDevelop.Core.Execution /// arguments, only quoted arguments with escaped quotes.</remarks> public static string Quote (string s) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); sb.Append ('"'); AppendEscaped (sb, escapeDoubleQuoteCharsStr, s); sb.Append ('"'); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public override string ToString () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs index 9cf02b44e3..3778df5720 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Logging/AssertLoggingTraceListener.cs @@ -45,7 +45,7 @@ namespace MonoDevelop.Core.Logging if (callerFrame == frames.Length - 1) callerFrame = 0; - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate(); if (IsRealMessage (message)) { if (!string.IsNullOrEmpty (detailMessage)) { sb.AppendFormat ("Failed assertion: {0} - {1}", message, detailMessage); @@ -61,7 +61,7 @@ namespace MonoDevelop.Core.Logging sb.Append ("\n"); FormatStackTrace (sb, frames, callerFrame); - LoggingService.LogError (sb.ToString ()); + LoggingService.LogError (StringBuilderCache.ReturnAndFree(sb)); } static bool IsRealMessage (string message) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs index 31e5958e46..2f312311ac 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Text/TextChangeEventArgs.cs @@ -222,7 +222,7 @@ namespace MonoDevelop.Core.Text { if (text == null) return null; - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); foreach (var ch in text) { switch (ch) { case '\r': @@ -239,7 +239,7 @@ namespace MonoDevelop.Core.Text break; } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj index 9fdd2d303a..6285a560d1 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.csproj @@ -756,6 +756,9 @@ <Compile Include="MonoDevelop.FSW\OSX\SR.cs" /> <Compile Include="MonoDevelop.FSW\FileSystemWatcher.cs" /> <Compile Include="MonoDevelop.FSW\Mono\FileSystemWatcher.cs" /> + <Compile Include="MonoDevelop.Core\StringBuilderCache.cs" /> + <Compile Include="MonoDevelop.Core\ObjectPool.cs" /> + <Compile Include="MonoDevelop.Core\SharedPools.cs" /> </ItemGroup> <ItemGroup> <None Include="Makefile.am" /> @@ -800,7 +803,7 @@ <InternalsVisibleTo Include="MonoDevelop.Ide.Tests" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> - <Target Name="BeforeBuild" Inputs="BuildVariables.cs.in; $(MSBuildProjectDirectory)\..\..\..\..\version.config" Outputs="BuildVariables.cs" Condition="Exists('$(MSBuildProjectDirectory)\..\..\..\..\version.config')" > + <Target Name="BeforeBuild" Inputs="BuildVariables.cs.in; $(MSBuildProjectDirectory)\..\..\..\..\version.config" Outputs="BuildVariables.cs" Condition="Exists('$(MSBuildProjectDirectory)\..\..\..\..\version.config')"> <MakeDir Directories="$(FullBuildInfo)" /> <Csc Sources="$(ConfigureScript)" OutputAssembly="$(ConfigureScriptExe)" ToolExe="$(CscToolExe)" ToolPath="$(CscToolPath)" Condition="!Exists('$(ConfigureScriptExe)')" /> <Exec Command="$(MonoLauncher)$(ConfigureScriptExe) gen-buildinfo $(FullBuildInfo)" WorkingDirectory="$(MSBuildProjectDirectory)" /> diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ErrorHelper.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ErrorHelper.cs index 73e1f56ec5..610d7cd346 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ErrorHelper.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ErrorHelper.cs @@ -51,13 +51,13 @@ namespace MonoDevelop.Core var ae = (AggregateException)ex; if (ae.InnerExceptions.Count == 1) return GetErrorMessage (ae.InnerException); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (var e in ae.InnerExceptions) { if (sb.Length > 0 && sb [sb.Length - 1] != '.') sb.Append (". "); sb.Append (GetErrorMessage (ex).Trim ()); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } else if (ex is UserException) { var ue = (UserException)ex; if (!string.IsNullOrEmpty (ue.Details)) { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs index af14d65bad..f226217773 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/LoggingService.cs @@ -37,6 +37,7 @@ using MonoDevelop.Core.LogReporting; using MonoDevelop.Core.Logging; using Mono.Unix.Native; using System.Text; +using System.Collections.Immutable; namespace MonoDevelop.Core { @@ -46,7 +47,8 @@ namespace MonoDevelop.Core const string ReportCrashesKey = "MonoDevelop.LogAgent.ReportCrashes"; const string ReportUsageKey = "MonoDevelop.LogAgent.ReportUsage"; - static List<ILogger> loggers = new List<ILogger> (); + static object serviceLock = new object (); + static ImmutableList<ILogger> loggers = ImmutableList<ILogger>.Empty; static RemoteLogger remoteLogger; static DateTime timestamp; static int logFileSuffix; @@ -65,8 +67,8 @@ namespace MonoDevelop.Core static LoggingService () { var consoleLogger = new ConsoleLogger (); - loggers.Add (consoleLogger); - loggers.Add (new InstrumentationLogger ()); + loggers = loggers.Add (consoleLogger); + loggers = loggers.Add (new InstrumentationLogger ()); string consoleLogLevelEnv = Environment.GetEnvironmentVariable ("MONODEVELOP_CONSOLE_LOG_LEVEL"); if (!string.IsNullOrEmpty (consoleLogLevelEnv)) { @@ -88,7 +90,7 @@ namespace MonoDevelop.Core if (!string.IsNullOrEmpty (logFileEnv)) { try { var fileLogger = new FileLogger (logFileEnv); - loggers.Add (fileLogger); + loggers = loggers.Add (fileLogger); string logFileLevelEnv = Environment.GetEnvironmentVariable ("MONODEVELOP_FILE_LOG_LEVEL"); fileLogger.EnabledLevel = (EnabledLoggingLevel) Enum.Parse (typeof (EnabledLoggingLevel), logFileLevelEnv, true); } catch (Exception e) { @@ -412,17 +414,21 @@ namespace MonoDevelop.Core public static void AddLogger (ILogger logger) { - if (GetLogger (logger.Name) != null) - throw new Exception ("There is already a logger with the name '" + logger.Name + "'"); - loggers.Add (logger); + lock (serviceLock) { + if (GetLogger (logger.Name) != null) + throw new Exception ("There is already a logger with the name '" + logger.Name + "'"); + loggers = loggers.Add (logger); + } } public static void RemoveLogger (string name) { - ILogger logger = GetLogger (name); - if (logger == null) - throw new Exception ("There is no logger registered with the name '" + name + "'"); - loggers.Remove (logger); + lock (serviceLock) { + ILogger logger = GetLogger (name); + if (logger == null) + throw new Exception ("There is no logger registered with the name '" + name + "'"); + loggers = loggers.Remove (logger); + } } #endregion diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ObjectPool.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ObjectPool.cs new file mode 100644 index 0000000000..93b3464ff2 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/ObjectPool.cs @@ -0,0 +1,269 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +// define TRACE_LEAKS to get additional diagnostics that can lead to the leak sources. note: it will +// make everything about 2-3x slower +// +// #define TRACE_LEAKS + +// define DETECT_LEAKS to detect possible leaks +// #if DEBUG +// #define DETECT_LEAKS //for now always enable DETECT_LEAKS in debug. +// #endif + +using System; +using System.Diagnostics; +using System.Threading; + +#if DETECT_LEAKS +using System.Runtime.CompilerServices; + +#endif + +namespace MonoDevelop.Core +{ + /// <summary> + /// Generic implementation of object pooling pattern with predefined pool size limit. The main + /// purpose is that limited number of frequently used objects can be kept in the pool for + /// further recycling. + /// + /// Notes: + /// 1) it is not the goal to keep all returned objects. Pool is not meant for storage. If there + /// is no space in the pool, extra returned objects will be dropped. + /// + /// 2) it is implied that if object was obtained from a pool, the caller will return it back in + /// a relatively short time. Keeping checked out objects for long durations is ok, but + /// reduces usefulness of pooling. Just new up your own. + /// + /// Not returning objects to the pool in not detrimental to the pool's work, but is a bad practice. + /// Rationale: + /// If there is no intent for reusing the object, do not use pool - just use "new". + /// </summary> + internal class ObjectPool<T> where T : class + { + [DebuggerDisplay ("{Value,nq}")] + private struct Element + { + internal T Value; + } + + /// <remarks> + /// Not using System.Func{T} because this file is linked into the (debugger) Formatter, + /// which does not have that type (since it compiles against .NET 2.0). + /// </remarks> + internal delegate T Factory (); + + // Storage for the pool objects. The first item is stored in a dedicated field because we + // expect to be able to satisfy most requests from it. + private T _firstItem; + private readonly Element [] _items; + + // factory is stored for the lifetime of the pool. We will call this only when pool needs to + // expand. compared to "new T()", Func gives more flexibility to implementers and faster + // than "new T()". + private readonly Factory _factory; + +#if DETECT_LEAKS + private static readonly ConditionalWeakTable<T, LeakTracker> leakTrackers = new ConditionalWeakTable<T, LeakTracker>(); + + private class LeakTracker : IDisposable + { + private volatile bool disposed; + +#if TRACE_LEAKS + internal volatile object Trace = null; +#endif + + public void Dispose() + { + disposed = true; + GC.SuppressFinalize(this); + } + + private string GetTrace() + { +#if TRACE_LEAKS + return Trace == null ? "" : Trace.ToString(); +#else + return "Leak tracing information is disabled. Define TRACE_LEAKS on ObjectPool`1.cs to get more info \n"; +#endif + } + + ~LeakTracker() + { + if (!this.disposed && !Environment.HasShutdownStarted) + { + var trace = GetTrace(); + + // If you are seeing this message it means that object has been allocated from the pool + // and has not been returned back. This is not critical, but turns pool into rather + // inefficient kind of "new". + Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nPool detected potential leaking of {typeof(T)}. \n Location of the leak: \n {GetTrace()} TRACEOBJECTPOOLLEAKS_END"); + } + } + } +#endif + + internal ObjectPool (Factory factory) + : this (factory, Environment.ProcessorCount * 2) + { } + + internal ObjectPool (Factory factory, int size) + { + Debug.Assert (size >= 1); + _factory = factory; + _items = new Element [size - 1]; + } + + private T CreateInstance () + { + var inst = _factory (); + return inst; + } + + /// <summary> + /// Produces an instance. + /// </summary> + /// <remarks> + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search. + /// </remarks> + internal T Allocate () + { + // PERF: Examine the first element. If that fails, AllocateSlow will look at the remaining elements. + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + T inst = _firstItem; + if (inst == null || inst != Interlocked.CompareExchange (ref _firstItem, null, inst)) { + inst = AllocateSlow (); + } + +#if DETECT_LEAKS + var tracker = new LeakTracker(); + leakTrackers.Add(inst, tracker); + +#if TRACE_LEAKS + var frame = CaptureStackTrace(); + tracker.Trace = frame; +#endif +#endif + return inst; + } + + private T AllocateSlow () + { + var items = _items; + + for (int i = 0; i < items.Length; i++) { + // Note that the initial read is optimistically not synchronized. That is intentional. + // We will interlock only when we have a candidate. in a worst case we may miss some + // recently returned objects. Not a big deal. + T inst = items [i].Value; + if (inst != null) { + if (inst == Interlocked.CompareExchange (ref items [i].Value, null, inst)) { + return inst; + } + } + } + + return CreateInstance (); + } + + /// <summary> + /// Returns objects to the pool. + /// </summary> + /// <remarks> + /// Search strategy is a simple linear probing which is chosen for it cache-friendliness. + /// Note that Free will try to store recycled objects close to the start thus statistically + /// reducing how far we will typically search in Allocate. + /// </remarks> + internal void Free (T obj) + { + Validate (obj); + ForgetTrackedObject (obj); + + if (_firstItem == null) { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + _firstItem = obj; + } else { + FreeSlow (obj); + } + } + + private void FreeSlow (T obj) + { + var items = _items; + for (int i = 0; i < items.Length; i++) { + if (items [i].Value == null) { + // Intentionally not using interlocked here. + // In a worst case scenario two objects may be stored into same slot. + // It is very unlikely to happen and will only mean that one of the objects will get collected. + items [i].Value = obj; + break; + } + } + } + + /// <summary> + /// Removes an object from leak tracking. + /// + /// This is called when an object is returned to the pool. It may also be explicitly + /// called if an object allocated from the pool is intentionally not being returned + /// to the pool. This can be of use with pooled arrays if the consumer wants to + /// return a larger array to the pool than was originally allocated. + /// </summary> + [Conditional ("DEBUG")] + internal void ForgetTrackedObject (T old, T replacement = null) + { +#if DETECT_LEAKS + LeakTracker tracker; + if (leakTrackers.TryGetValue(old, out tracker)) + { + tracker.Dispose(); + leakTrackers.Remove(old); + } + else + { + var trace = CaptureStackTrace(); + Debug.WriteLine($"TRACEOBJECTPOOLLEAKS_BEGIN\nObject of type {typeof(T)} was freed, but was not from pool. \n Callstack: \n {trace} TRACEOBJECTPOOLLEAKS_END"); + } + + if (replacement != null) + { + tracker = new LeakTracker(); + leakTrackers.Add(replacement, tracker); + } +#endif + } + +#if DETECT_LEAKS + private static Lazy<Type> _stackTraceType = new Lazy<Type>(() => Type.GetType("System.Diagnostics.StackTrace")); + + private static object CaptureStackTrace() + { + return Activator.CreateInstance(_stackTraceType.Value); + } +#endif + + [Conditional ("DEBUG")] + private void Validate (object obj) + { + Debug.Assert (obj != null, "freeing null?"); + + Debug.Assert (_firstItem != obj, "freeing twice?"); + + var items = _items; + for (int i = 0; i < items.Length; i++) { + var value = items [i].Value; + if (value == null) { + } + return; + + Debug.Assert (value != obj, "freeing twice?"); + } + } + } +} diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs index 1f33234b3d..8b18107d45 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/Properties.cs @@ -344,7 +344,7 @@ namespace MonoDevelop.Core public override string ToString () { - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); result.Append ("[Properties:"); foreach (KeyValuePair<string, object> property in this.properties) { result.Append (property.Key); @@ -353,7 +353,7 @@ namespace MonoDevelop.Core result.Append (","); } result.Append ("]"); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public Properties Clone () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyBag.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyBag.cs index 6f306b490d..5af30dff22 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyBag.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/PropertyBag.cs @@ -246,7 +246,7 @@ namespace MonoDevelop.Core string EscapeName (string str) { - StringBuilder sb = new StringBuilder (str.Length); + StringBuilder sb = StringBuilderCache.Allocate (); for (int n=0; n<str.Length; n++) { char c = str [n]; if (c == '_') @@ -259,24 +259,24 @@ namespace MonoDevelop.Core else sb.Append (c); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } string UnescapeName (string str) { - StringBuilder sb = new StringBuilder (str.Length); + StringBuilder sb = StringBuilderCache.Allocate (); for (int n=0; n<str.Length; n++) { char c = str [n]; if (c == '_') { if (n + 1 >= str.Length) - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); if (str [n + 1] == '_') { sb.Append (c); n++; } else { int len = int.Parse (str.Substring (n+1,1)); if (n + 2 + len - 1 >= str.Length) - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); int ic; if (int.TryParse (str.Substring (n + 2, len), NumberStyles.HexNumber, null, out ic)) sb.Append ((char)ic); @@ -285,7 +285,7 @@ namespace MonoDevelop.Core } else sb.Append (c); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SharedPools.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SharedPools.cs new file mode 100644 index 0000000000..b74dd4a279 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/SharedPools.cs @@ -0,0 +1,58 @@ +// +// StringBuilderCache.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// 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. + + +namespace MonoDevelop.Core +{ + static class SharedPools + { + /// <summary> + /// pool that uses default constructor with 100 elements pooled + /// </summary> + public static ObjectPool<T> BigDefault<T> () where T : class, new() + { + return DefaultBigPool<T>.Instance; + } + + /// <summary> + /// pool that uses default constructor with 20 elements pooled + /// </summary> + public static ObjectPool<T> Default<T> () where T : class, new() + { + return DefaultNormalPool<T>.Instance; + } + + static class DefaultBigPool<T> where T : class, new() + { + public static readonly ObjectPool<T> Instance = new ObjectPool<T> (() => new T (), 100); + } + + static class DefaultNormalPool<T> where T : class, new() + { + public static readonly ObjectPool<T> Instance = new ObjectPool<T> (() => new T (), 20); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs new file mode 100644 index 0000000000..8c01bb2310 --- /dev/null +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringBuilderCache.cs @@ -0,0 +1,59 @@ +// +// StringBuilderCache.cs +// +// Author: +// Mike Krüger <mikkrg@microsoft.com> +// +// Copyright (c) 2018 Microsoft Corporation. All rights reserved. +// +// 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.Text; + +namespace MonoDevelop.Core +{ + /// <summary> + /// This is a pool for storing StringBuilder objects. + /// </summary> + public static class StringBuilderCache + { + public static StringBuilder Allocate () + { + return SharedPools.Default<StringBuilder> ().Allocate (); + } + + public static StringBuilder Allocate (string text) + { + return SharedPools.Default<StringBuilder> ().Allocate ().Append (text); + } + + public static void Free (StringBuilder sb) + { + sb.Clear (); + SharedPools.Default<StringBuilder> ().Free (sb); + } + + public static string ReturnAndFree (StringBuilder sb) + { + var result = sb.ToString (); + Free (sb); + return result; + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringParserService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringParserService.cs index 77ebc71804..d78ff2f29d 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringParserService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core/StringParserService.cs @@ -196,7 +196,7 @@ namespace MonoDevelop.Core public static string Parse (string input, IStringTagModel customTags) { - StringBuilder result = new StringBuilder (input.Length); + StringBuilder result = StringBuilderCache.Allocate (); int brace; int i = 0; @@ -225,7 +225,7 @@ namespace MonoDevelop.Core } i++; } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public static IEnumerable<IStringTagProvider> GetProviders () diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild.Conditions/ConditionParser.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild.Conditions/ConditionParser.cs index 65cc6cbfa4..98f58747ae 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild.Conditions/ConditionParser.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild.Conditions/ConditionParser.cs @@ -257,7 +257,7 @@ namespace MonoDevelop.Projects.MSBuild.Conditions { if (tokenizer.IsEOF ()) throw new ExpressionParseException ("Missing closing parenthesis in condition " + conditionStr); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = Core.StringBuilderCache.Allocate (); sb.AppendFormat ("{0}({1}", prefix, tokenizer.Token.Value); tokenizer.GetNextToken (); @@ -280,7 +280,7 @@ namespace MonoDevelop.Projects.MSBuild.Conditions { sb.Append (")"); //FIXME: HACKY! - return new ConditionFactorExpression (new Token (sb.ToString (), TokenType.String, token_pos)); + return new ConditionFactorExpression (new Token (Core.StringBuilderCache.ReturnAndFree (sb), TokenType.String, token_pos)); } void ThrowParseException(TokenType type, string error_fmt, params object[] args) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs index e5e9a05e5c..d9873559ec 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/DefaultMSBuildEngine.cs @@ -645,18 +645,18 @@ namespace MonoDevelop.Projects.MSBuild items = result; return true; } else if (ExecuteTransformItemListFunction (ref transformItems, itemFunction, itemFunctionArgs, out ignoreMetadata)) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); for (int n = 0; n < transformItems.Length; n++) { if (n > 0) sb.Append (';'); sb.Append (transformItems[n].Include); } - items = sb.ToString (); + items = StringBuilderCache.ReturnAndFree (sb); return true; } } - var sbi = new StringBuilder (); + var sbi = StringBuilderCache.Allocate (); int count = 0; foreach (var eit in transformItems) { @@ -678,7 +678,7 @@ namespace MonoDevelop.Projects.MSBuild context.ClearItemContext (); } } - items = sbi.ToString (); + items = Core.StringBuilderCache.ReturnAndFree (sbi); return true; } @@ -943,7 +943,7 @@ namespace MonoDevelop.Projects.MSBuild static string ExcludeToRegex (string exclude, bool excludeDirectoriesOnly = false) { exclude = exclude.Replace ('/', '\\').Replace (@"\\", @"\"); - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); foreach (var ep in exclude.Split (new char [] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { var ex = ep.Trim (); if (excludeDirectoriesOnly) { @@ -977,7 +977,7 @@ namespace MonoDevelop.Projects.MSBuild } sb.Append ('$'); } - return sb.ToString (); + return Core.StringBuilderCache.ReturnAndFree (sb); } static char [] regexEscapeChars = { '\\', '^', '$', '{', '}', '[', ']', '(', ')', '.', '*', '+', '?', '|', '<', '>', '-', '&' }; diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/EscapingUtilities.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/EscapingUtilities.cs index 94a180f36d..803ff6df8a 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/EscapingUtilities.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/EscapingUtilities.cs @@ -5,7 +5,8 @@ using System; using System.Collections.Generic;
using System.Globalization;
using System.Text;
-
+using MonoDevelop.Core; + namespace Microsoft.Build.Shared
{
/// <summary>
@@ -70,7 +71,7 @@ namespace Microsoft.Build.Shared }
// This is where we're going to build up the final string to return to the caller.
- StringBuilder unescapedString = new StringBuilder (escapedString.Length);
+ StringBuilder unescapedString = StringBuilderCache.Allocate ();
int currentPosition = 0;
@@ -112,7 +113,7 @@ namespace Microsoft.Build.Shared // characters into the destination.
unescapedString.Append(escapedString, currentPosition, escapedString.Length - currentPosition);
- return unescapedString.ToString ();
+ return StringBuilderCache.ReturnAndFree (unescapedString);
}
@@ -172,16 +173,16 @@ namespace Microsoft.Build.Shared }
// This is where we're going to build up the final string to return to the caller.
- StringBuilder escapedStringBuilder = new StringBuilder (unescapedString.Length * 2);
+ StringBuilder escapedStringBuilder = StringBuilderCache.Allocate ();
AppendEscapedString(escapedStringBuilder, unescapedString);
if (!cache)
{
- return escapedStringBuilder.ToString ();
+ return StringBuilderCache.ReturnAndFree (escapedStringBuilder);
}
- string escapedString = escapedStringBuilder.ToString ();
+ string escapedString = StringBuilderCache.ReturnAndFree (escapedStringBuilder);
lock (s_unescapedToEscapedStrings)
{
diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/IMSBuildPropertySet.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/IMSBuildPropertySet.cs index 76e96caa7b..73990668a0 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/IMSBuildPropertySet.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/IMSBuildPropertySet.cs @@ -228,7 +228,7 @@ namespace MonoDevelop.Projects.MSBuild int i = val.IndexOfAny (new char[] {'\n','\r','\t'}); if (i != -1 || val [0] == '@') { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); if (i != -1) { int fi = val.IndexOf ('\\'); if (fi != -1 && fi < i) i = fi; @@ -248,7 +248,7 @@ namespace MonoDevelop.Projects.MSBuild else sb.Append (c); } - val = "@" + sb.ToString (); + val = "@" + StringBuilderCache.ReturnAndFree (sb); } char fc = val [0]; char lc = val [val.Length - 1]; @@ -283,7 +283,7 @@ namespace MonoDevelop.Projects.MSBuild end--; } if (val [start] == '@') { - StringBuilder sb = new StringBuilder (val.Length); + StringBuilder sb = StringBuilderCache.Allocate (); for (int n = start + 1; n < end; n++) { char c = val [n]; if (c == '\\') { @@ -294,7 +294,7 @@ namespace MonoDevelop.Projects.MSBuild } sb.Append (c); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } else return val.Substring (start, end - start); diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProject.cs index 72c36e7b45..395321393c 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.MSBuild/MSBuildProject.cs @@ -1152,7 +1152,7 @@ namespace MonoDevelop.Projects.MSBuild if (elem == null) return ""; var node = elem.PreviousSibling; - StringBuilder res = new StringBuilder (); + StringBuilder res = StringBuilderCache.Allocate (); while (node != null) { var ws = node as XmlWhitespace; @@ -1163,13 +1163,13 @@ namespace MonoDevelop.Projects.MSBuild res.Append (t); } else { res.Append (t, i + 1, t.Length - i - 1); - return res.ToString (); + return StringBuilderCache.ReturnAndFree (res); } } else res.Clear (); node = node.PreviousSibling; } - return res.ToString (); + return StringBuilderCache.ReturnAndFree (res); } public static void Indent (TextFormatInfo format, XmlElement elem, bool closeInNewLine) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyService.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyService.cs index 72e2c2df70..57e57f7ada 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyService.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Policies/PolicyService.cs @@ -530,7 +530,7 @@ namespace MonoDevelop.Projects.Policies } } - StringBuilder removed = new StringBuilder (); + StringBuilder removed = StringBuilderCache.Allocate (); for (int n=0; n<baseline.ItemData.Count; n++) { DataNode node = baseline.ItemData [n]; if (!extracted.Contains (node)) { @@ -545,6 +545,7 @@ namespace MonoDevelop.Projects.Policies if (removed.Length > 0) newItem.ItemData.Add (new DataValue ("__removed", removed.ToString ()) {StoreAsAttribute = true}); + StringBuilderCache.Free (removed); return newItem; } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFormatter.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFormatter.cs index cc1f54ac19..95af55600a 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFormatter.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects.Text/TextFormatter.cs @@ -26,6 +26,7 @@ using System; using System.Text; +using MonoDevelop.Core; namespace MonoDevelop.Projects.Text { @@ -343,10 +344,10 @@ namespace MonoDevelop.Projects.Text void CreateIndentString () { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); indentColumnWidth = AddIndentString (sb, indentString); - - paragFormattedIndentString = sb.ToString () + new string (' ', paragraphStartMargin); + sb.Append (new string (' ', paragraphStartMargin)); + paragFormattedIndentString = StringBuilderCache.ReturnAndFree (sb); paragIndentColumnWidth = indentColumnWidth + paragraphStartMargin; if (LeftMargin > 0) { diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs index 9dee0e5bc1..91b3ee2145 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/DotNetProject.cs @@ -1571,17 +1571,17 @@ namespace MonoDevelop.Projects static string GetHierarchicalNamespace (string relativePath) { - StringBuilder sb = new StringBuilder (relativePath); + StringBuilder sb = StringBuilderCache.Allocate (relativePath); for (int i = 0; i < sb.Length; i++) { if (sb[i] == Path.DirectorySeparatorChar) sb[i] = '.'; } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } static string SanitisePotentialNamespace (string potential) { - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (char c in potential) { if (char.IsLetter (c) || c == '_' || (sb.Length > 0 && (char.IsLetterOrDigit (sb[sb.Length - 1]) || sb[sb.Length - 1] == '_') && (c == '.' || char.IsNumber (c)))) { sb.Append (c); @@ -1591,9 +1591,10 @@ namespace MonoDevelop.Projects if (sb[sb.Length - 1] == '.') sb.Remove (sb.Length - 1, 1); - return sb.ToString (); - } else - return null; + return StringBuilderCache.ReturnAndFree (sb); + } + StringBuilderCache.Free (sb); + return null; } void RuntimeSystemAssemblyServiceDefaultRuntimeChanged (object sender, EventArgs e) diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MonoExecutionParameters.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MonoExecutionParameters.cs index b37e4343d6..f172182f44 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MonoExecutionParameters.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Projects/MonoExecutionParameters.cs @@ -269,7 +269,7 @@ namespace MonoDevelop.Projects public string GenerateDescription () { - StringBuilder ops = new StringBuilder (); + StringBuilder ops = StringBuilderCache.Allocate (); foreach (var kvp in itemPropertyAttributes) { var prop = kvp.Key; @@ -286,7 +286,7 @@ namespace MonoDevelop.Projects ops.Append (": ").Append (GetValue (pval)); } } - return ops.ToString (); + return StringBuilderCache.ReturnAndFree (ops); } public MonoExecutionParameters Clone () { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs index 0140114a63..721faff88e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/MainToolbarController.cs @@ -1092,7 +1092,7 @@ namespace MonoDevelop.Components.MainToolbar int i = s.IndexOf ('_'); if (i == -1) return s; - var sb = new StringBuilder (i); + var sb = StringBuilderCache.Allocate (); sb.Append (s, 0, i); for (; i < s.Length; i++) { if (s [i] == '_') { @@ -1102,7 +1102,7 @@ namespace MonoDevelop.Components.MainToolbar } sb.Append (s [i]); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs index c44cd39d56..c6e6c03918 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components.MainToolbar/SearchResult.cs @@ -106,7 +106,7 @@ namespace MonoDevelop.Components.MainToolbar { var lane = StringMatcher.GetMatcher (toMatch, true).GetMatch (text);
var matchHexColor = selected ? selectedResultMatchTextColor : resultMatchTextColor; - StringBuilder result = new StringBuilder (text.Length + matchHexColor.Length + 46); + var result = StringBuilderCache.Allocate (); if (lane != null) { int lastPos = 0; for (int n=0; n < lane.Length; n++) { @@ -125,7 +125,7 @@ namespace MonoDevelop.Components.MainToolbar } else { MarkupUtilities.AppendEscapedString (result, text, 0, text.Length); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public virtual bool CanActivate { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs index 30706dedee..34f8345a9c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Components/ContextMenuExtensionsMac.cs @@ -157,7 +157,8 @@ namespace MonoDevelop.Components public NSContextMenuItem (string label, ContextMenuItem item) : base (label) { contextMenu = new WeakReference<ContextMenuItem> (item); - this.Activated += OnActivated; + if (item.SubMenu == null || item.SubMenu.Items.Count == 0) + this.Activated += OnActivated; } static void OnActivated (object sender, EventArgs args) @@ -195,6 +196,7 @@ namespace MonoDevelop.Components public NSLocationAwareMenu (ContextMenu menu, Action closeHandler, NSLocationAwareMenu parent) { WeakDelegate = new ContextMenuDelegate (menu) { CloseHandler = closeHandler }; + Parent = parent != null ? new WeakReference<NSLocationAwareMenu> (parent) : null; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs index 095c4b3625..22e8d381c6 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/RoslynCompletionData.cs @@ -92,12 +92,12 @@ namespace MonoDevelop.Ide.CodeCompletion var type = GetItemType (); var hash = modifier | type << 16; if (!IconIdCache.ContainsKey (hash)) - IconIdCache [hash] = "md-" + modifierType[modifier] + completionType[type]; + IconIdCache [hash] = "md-" + modifierType [modifier] + completionType [type]; return IconIdCache [hash]; } } - static Dictionary<int, string> IconIdCache = new Dictionary<int, string>(); + static Dictionary<int, string> IconIdCache = new Dictionary<int, string> (); public RoslynCompletionData (Microsoft.CodeAnalysis.Document document, ITextSnapshot triggerSnapshot, CompletionService completionService, CompletionItem completionItem) { @@ -198,7 +198,7 @@ namespace MonoDevelop.Ide.CodeCompletion return 1; } - string[] modifierType = { + string [] modifierType = { "", "private-", "ProtectedOrInternal-", @@ -257,7 +257,7 @@ namespace MonoDevelop.Ide.CodeCompletion editor.ReplaceText (mappedSpan.Start + 1, mappedSpan.Length - 1, completionChange.TextChange.NewText); } else editor.ReplaceText (mappedSpan.Start, mappedSpan.Length, completionChange.TextChange.NewText); - + if (completionChange.NewPosition.HasValue) editor.CaretOffset = completionChange.NewPosition.Value; @@ -273,9 +273,14 @@ namespace MonoDevelop.Ide.CodeCompletion public override async Task<TooltipInformation> CreateTooltipInformation (bool smartWrap, CancellationToken cancelToken) { - var description = await Task.Run (() => completionService.GetDescriptionAsync (doc, CompletionItem)).ConfigureAwait (false); - var markup = new StringBuilder (); - var theme = DefaultSourceEditorOptions.Instance.GetEditorTheme (); + CompletionDescription description; + if (CommonCompletionItem.HasDescription (CompletionItem)) { + description = CommonCompletionItem.GetDescription (CompletionItem); + } else { + description = await Task.Run (() => completionService.GetDescriptionAsync (doc, CompletionItem)).ConfigureAwait (false); + } + var markup = StringBuilderCache.Allocate (); + var theme = SyntaxHighlightingService.GetIdeFittingTheme (DefaultSourceEditorOptions.Instance.GetEditorTheme ()); var taggedParts = description.TaggedParts; int i = 0; while (i < taggedParts.Length) { @@ -294,9 +299,27 @@ namespace MonoDevelop.Ide.CodeCompletion markup.Append ("</span>"); } return new TooltipInformation { - SignatureMarkup = markup.ToString () + SignatureMarkup = StringBuilderCache.ReturnAndFree (markup) }; } - } -} + public override bool IsCommitCharacter (char keyChar, string partialWord) + { + foreach (var rule in CompletionItem.Rules.CommitCharacterRules) { + switch (rule.Kind) { + case CharacterSetModificationKind.Add: + if (rule.Characters.Contains (keyChar)) + return true; + continue; + case CharacterSetModificationKind.Remove: + if (rule.Characters.Contains (keyChar)) + return false; + continue; + case CharacterSetModificationKind.Replace: + return rule.Characters.Contains (keyChar); + } + } + return base.IsCommitCharacter (keyChar, partialWord); + } + } +}
\ No newline at end of file diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/SignatureHelpParameterHintingData.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/SignatureHelpParameterHintingData.cs index aa8c2ba9cc..7779056362 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/SignatureHelpParameterHintingData.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeCompletion/SignatureHelpParameterHintingData.cs @@ -74,8 +74,9 @@ namespace MonoDevelop.Ide.CodeCompletion public override Task<TooltipInformation> CreateTooltipInformation (TextEditor editor, DocumentContext ctx, int currentParameter, bool smartWrap, CancellationToken cancelToken)
{
var tt = new TooltipInformation ();
- var markup = new StringBuilder ();
- var theme = DefaultSourceEditorOptions.Instance.GetEditorTheme (); + var markup = new StringBuilder (); + var theme = SyntaxHighlightingService.GetIdeFittingTheme (DefaultSourceEditorOptions.Instance.GetEditorTheme ()); + markup.AppendTaggedText (theme, Item.PrefixDisplayParts);
var prefixLength = Item.PrefixDisplayParts.GetFullText ().Length;
var prefixSpacer = new string (' ', prefixLength);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs index 02874129d6..1dc585dcf8 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplate.cs @@ -244,7 +244,7 @@ namespace MonoDevelop.Ide.CodeTemplates { var expansion = CodeTemplateService.GetExpansionObject (this); var result = new TemplateResult (); - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); int lastOffset = 0; string code = context.Editor.FormatString (context.InsertPosition, context.TemplateCode); result.TextLinks = new List<TextLink> (); @@ -339,7 +339,7 @@ namespace MonoDevelop.Ide.CodeTemplates // format & indent template code var data = TextEditorFactory.CreateNewDocument (); - data.Text = sb.ToString (); + data.Text = StringBuilderCache.ReturnAndFree (sb); data.TextChanged += delegate(object sender, MonoDevelop.Core.Text.TextChangeEventArgs e) {
for (int i = 0; i < e.TextChanges.Count; ++i) {
var change = e.TextChanges[i]; @@ -369,7 +369,7 @@ namespace MonoDevelop.Ide.CodeTemplates public string IndentCode (string code, string eol, string indent) { - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); for (int i = 0; i < code.Length; i++) { switch (code[i]) { case '\r': @@ -385,7 +385,7 @@ namespace MonoDevelop.Ide.CodeTemplates break; } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } static void IndentCode (ITextDocument data, string lineIndent) @@ -404,38 +404,38 @@ namespace MonoDevelop.Ide.CodeTemplates while (i >= 0 && !Char.IsWhiteSpace (str[i])) { i--; } - var indent = new StringBuilder (); + var indent = StringBuilderCache.Allocate (); while (i >= 0 && (str[i] == ' ' || str[i] == '\t')) { indent.Append (str[i]); i--; } - return indent.ToString (); + return StringBuilderCache.ReturnAndFree (indent); } string RemoveIndent (string text, string indent) { var doc = TextEditorFactory.CreateNewDocument (); doc.Text = text; - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); foreach (var line in doc.GetLines ()) { string curLineIndent = line.GetIndentation (doc); int offset = Math.Min (curLineIndent.Length, indent.Length); result.Append (doc.GetTextBetween (line.Offset + offset, line.EndOffsetIncludingDelimiter)); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } string Reindent (string text, string indent) { var doc = TextEditorFactory.CreateNewDocument (); doc.Text = text; - var result = new StringBuilder (); + var result = StringBuilderCache.Allocate (); foreach (var line in doc.GetLines ()) { if (result.Length > 0) result.Append (indent); result.Append (doc.GetTextAt (line.SegmentIncludingDelimiter)); } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public void Insert (MonoDevelop.Ide.Gui.Document document) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateService.cs index f8556dbc51..043c670ebe 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.CodeTemplates/CodeTemplateService.cs @@ -122,8 +122,8 @@ namespace MonoDevelop.Ide.CodeTemplates { var result = new CodeTemplate (); result.Shortcut = setting.TabTrigger; - var sb = new StringBuilder (); - var nameBuilder = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); + var nameBuilder = StringBuilderCache.Allocate (); bool readDollar = false; bool inBracketExpression = false; bool inExpressionContent = false; @@ -194,8 +194,8 @@ namespace MonoDevelop.Ide.CodeTemplates nameBuilder.Length = 0; inVariable = false; } - - result.Code = sb.ToString (); + StringBuilderCache.Free (nameBuilder); + result.Code = StringBuilderCache.ReturnAndFree (sb); result.CodeTemplateContext = CodeTemplateContext.Standard; result.CodeTemplateType = CodeTemplateType.Expansion; result.Description = setting.Name; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs index 82a578537b..76d02a6bfa 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Extension/CompletionTextEditorExtension.cs @@ -25,8 +25,8 @@ // // - using System; +using System.Collections.Generic; using MonoDevelop.Projects; using MonoDevelop.Ide.CodeCompletion; using MonoDevelop.Components.Commands; @@ -191,8 +191,11 @@ namespace MonoDevelop.Ide.Editor.Extension completionTokenSrc = new CancellationTokenSource (); var caretOffset = Editor.CaretOffset; var token = completionTokenSrc.Token; + + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (); + Counters.ProcessCodeCompletion.BeginTiming (metadata); var task = DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (CompletionTriggerReason.CharTyped, descriptor.KeyChar), token); if (task != null) { // Show the completion window in two steps. The call to PrepareShowWindow creates the window but @@ -225,6 +228,9 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = null; } } finally { + if (token.IsCancellationRequested) { + metadata ["Result"] = "UserCancel"; + } Counters.ProcessCodeCompletion.EndTiming (); } }, Runtime.MainTaskScheduler); @@ -233,10 +239,13 @@ namespace MonoDevelop.Ide.Editor.Extension Counters.ProcessCodeCompletion.EndTiming (); } } catch (TaskCanceledException) { + metadata ["Result"] = "UserCancel"; Counters.ProcessCodeCompletion.EndTiming (); } catch (AggregateException) { + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); } catch { + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); throw; } @@ -260,8 +269,11 @@ namespace MonoDevelop.Ide.Editor.Extension completionTokenSrc = new CancellationTokenSource (); var caretOffset = Editor.CaretOffset; var token = completionTokenSrc.Token; + + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (); + Counters.ProcessCodeCompletion.BeginTiming (metadata); var task = DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (CompletionTriggerReason.BackspaceOrDeleteCommand, deleteOrBackspaceTriggerChar), token); if (task != null) { // Show the completion window in two steps. The call to PrepareShowWindow creates the window but @@ -299,6 +311,9 @@ namespace MonoDevelop.Ide.Editor.Extension CurrentCompletionContext = null; } } finally { + if (token.IsCancellationRequested) { + metadata ["Result"] = "UserCancel"; + } Counters.ProcessCodeCompletion.EndTiming (); } }, Runtime.MainTaskScheduler); @@ -307,11 +322,14 @@ namespace MonoDevelop.Ide.Editor.Extension } } catch (TaskCanceledException) { CurrentCompletionContext = null; + metadata ["Result"] = "UserCancel"; Counters.ProcessCodeCompletion.EndTiming (); } catch (AggregateException) { CurrentCompletionContext = null; + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); } catch { + metadata ["Result"] = "Failure"; Counters.ProcessCodeCompletion.EndTiming (); throw; } @@ -422,11 +440,19 @@ namespace MonoDevelop.Ide.Editor.Extension cpos = Editor.CaretOffset; wlen = 0; } + + completionTokenSrc.Cancel (); + completionTokenSrc = new CancellationTokenSource (); + var token = completionTokenSrc.Token; + CurrentCompletionContext = CompletionWidget.CreateCodeCompletionContext (cpos); CurrentCompletionContext.TriggerWordLength = wlen; + + var metadata = new Dictionary<string, string> (); + metadata ["Result"] = "Success"; try { - Counters.ProcessCodeCompletion.BeginTiming (); - completionList = await DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (reason)); + Counters.ProcessCodeCompletion.BeginTiming (metadata); + completionList = await DoHandleCodeCompletionAsync (CurrentCompletionContext, new CompletionTriggerInfo (reason), token); if (completionList != null && completionList.TriggerWordStart >= 0) { CurrentCompletionContext.TriggerOffset = completionList.TriggerWordStart; CurrentCompletionContext.TriggerWordLength = completionList.TriggerWordLength; @@ -434,6 +460,9 @@ namespace MonoDevelop.Ide.Editor.Extension if (completionList == null || !CompletionWindowManager.ShowWindow (this, (char)0, completionList, CompletionWidget, CurrentCompletionContext)) { CurrentCompletionContext = null; } + } catch (Exception) { + metadata ["Result"] = "Failure"; + throw; } finally { Counters.ProcessCodeCompletion.EndTiming (); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/Formats/OldFormat.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/Formats/OldFormat.cs index 09675a6a12..6c1a94e066 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/Formats/OldFormat.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/Formats/OldFormat.cs @@ -33,6 +33,7 @@ using System.Xml.XPath; using System.Reflection; using System.Text; using System.Xml; +using MonoDevelop.Core; using MonoDevelop.Components; using MonoDevelop.Core.Text; using System.Collections.Immutable; @@ -77,7 +78,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting }; if (style.FontStyle != Xwt.Drawing.FontStyle.Normal || style.FontWeight != Xwt.Drawing.FontWeight.Normal) { - var fontStyle = new StringBuilder (); + var fontStyle = StringBuilderCache.Allocate (); if (style.FontStyle != Xwt.Drawing.FontStyle.Normal) { fontStyle.Append (style.FontStyle.ToString ().ToLower ()); fontStyle.Append (" "); @@ -85,7 +86,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting if (style.FontWeight != Xwt.Drawing.FontWeight.Normal) { fontStyle.Append (style.FontWeight.ToString ().ToLower ()); } - result ["fontStyle"] = fontStyle.ToString (); + result ["fontStyle"] = StringBuilderCache.ReturnAndFree (fontStyle); } return result; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/PObject.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/PObject.cs index 865191b39d..b4616c511c 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/PObject.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/PObject.cs @@ -887,13 +887,13 @@ namespace MonoDevelop.Ide.Editor.Highlighting public string ToStringList () { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); foreach (PString str in list.OfType<PString> ()) { if (sb.Length > 0) sb.Append (", "); sb.Append (str); } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public IEnumerator<PObject> GetEnumerator () diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/StackMatchExpression.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/StackMatchExpression.cs index 1e5ce98dfc..6113e45a1d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/StackMatchExpression.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/StackMatchExpression.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Text; using Roslyn.Utilities; +using MonoDevelop.Core; namespace MonoDevelop.Ide.Editor.Highlighting { @@ -38,7 +39,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting public static StackMatchExpression Parse (string expression) { - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); var stackStack = new Stack<Stack<StackMatchExpression>> (); var exprStack = new Stack<StackMatchExpression> (); @@ -88,6 +89,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting } if (sb.Length > 0) exprStack.Push (CreateMatchExpression (sb)); + StringBuilderCache.Free (sb); ShrinkStack (exprStack); if (exprStack.IsEmpty ()) return new StringMatchExpression (""); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlightingService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlightingService.cs index b5fdc7517a..3e8da3a46f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlightingService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.Highlighting/SyntaxHighlightingService.cs @@ -98,6 +98,7 @@ namespace MonoDevelop.Ide.Editor.Highlighting return GetEditorTheme (EditorTheme.DefaultDarkThemeName); } } + public static EditorTheme GetDefaultColorStyle (this Theme theme) { return GetEditorTheme (GetDefaultColorStyleName (theme)); @@ -135,6 +136,17 @@ namespace MonoDevelop.Ide.Editor.Highlighting return (bgColor.L > 0.5); } + public static EditorTheme GetIdeFittingTheme (EditorTheme userTheme = null) + { + try { + if (userTheme == null || !userTheme.FitsIdeTheme (Ide.IdeApp.Preferences.UserInterfaceTheme)) + return GetDefaultColorStyle (Ide.IdeApp.Preferences.UserInterfaceTheme); + } catch (Exception e) { + LoggingService.LogError ("Error while getting the color style : " + Ide.IdeApp.Preferences.ColorScheme + " in ide theme : " + Ide.IdeApp.Preferences.UserInterfaceTheme, e); + } + return userTheme; + } + internal static IEnumerable<TmSetting> GetSettings (ScopeStack scope) { foreach (var bundle in languageBundles) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.TextMate/TextMateCompletionTextEditorExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.TextMate/TextMateCompletionTextEditorExtension.cs index fb5cca77ca..cd7c7da16d 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.TextMate/TextMateCompletionTextEditorExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor.TextMate/TextMateCompletionTextEditorExtension.cs @@ -33,6 +33,7 @@ using System.Collections.Generic; using System.Text; using MonoDevelop.Core.Text; using MonoDevelop.Ide.TypeSystem; +using MonoDevelop.Core; namespace MonoDevelop.Ide.Editor.TextMate { @@ -85,7 +86,7 @@ namespace MonoDevelop.Ide.Editor.TextMate { return Task.Run (delegate { var result = new HashSet<string> (); - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); int i = 0; while (i < source.Length) { char ch = source[i]; @@ -110,6 +111,7 @@ namespace MonoDevelop.Ide.Editor.TextMate i++; } + StringBuilderCache.Free (sb); return (IEnumerable<string>)result; }); } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs index ca8fba7763..5d5adc3915 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Editor/AutoSave.cs @@ -70,11 +70,11 @@ namespace MonoDevelop.Ide.Editor static MD5 md5 = MD5.Create (); static string GetMD5 (string data) { - var result = new StringBuilder(); + var result = StringBuilderCache.Allocate(); foreach (var b in md5.ComputeHash (Encoding.ASCII.GetBytes (data))) { result.Append(b.ToString("X2")); } - return result.ToString(); + return StringBuilderCache.ReturnAndFree (result); } /// <summary> diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Execution/MonoExecutionParametersPreview.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Execution/MonoExecutionParametersPreview.cs index ba33b9cea5..cc7ef21975 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Execution/MonoExecutionParametersPreview.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Execution/MonoExecutionParametersPreview.cs @@ -44,7 +44,7 @@ namespace MonoDevelop.Ide.Execution Dictionary<string,string> vars = new Dictionary<string, string> (); options.GenerateOptions (vars, out cmd); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); if (cmd.Length == 0 && vars.Count == 0) { sb.AppendLine (GLib.Markup.EscapeText (GettextCatalog.GetString ("No options have been specified."))); @@ -68,7 +68,7 @@ namespace MonoDevelop.Ide.Execution sb.AppendLine (svar); } - labelOps.Markup = sb.ToString (); + labelOps.Markup = StringBuilderCache.ReturnAndFree (sb); } } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/MimeTypeNode.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/MimeTypeNode.cs index 441ddf81d6..71b486b1eb 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/MimeTypeNode.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Extensions/MimeTypeNode.cs @@ -103,7 +103,7 @@ namespace MonoDevelop.Ide.Extensions Regex CreateRegex (MimeTypeNode node) { - var globalPattern = new StringBuilder (); + var globalPattern = StringBuilderCache.Allocate (); foreach (MimeTypeFileNode file in node.ChildNodes) { string pattern = Regex.Escape (file.Pattern); @@ -115,7 +115,7 @@ namespace MonoDevelop.Ide.Extensions globalPattern.Append ('|'); globalPattern.Append (pattern); } - return new Regex (globalPattern.ToString (), RegexOptions.IgnoreCase); + return new Regex (StringBuilderCache.ReturnAndFree (globalPattern), RegexOptions.IgnoreCase); } public bool SupportsFile (string fileName) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs index 3b2d881171..58f710ad14 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/FindInFilesDialog.cs @@ -500,20 +500,7 @@ namespace MonoDevelop.Ide.FindInFiles Visible = true, Ready = true, }; - - var checkMenuItem = searchentryFileMask.AddFilterOption (0, GettextCatalog.GetString ("Include binary files")); - checkMenuItem.DrawAsRadio = false; - checkMenuItem.Active = properties.Get ("IncludeBinaryFiles", false); - checkMenuItem.Toggled += delegate { - properties.Set ("IncludeBinaryFiles", checkMenuItem.Active); - }; - - var checkMenuItem1 = searchentryFileMask.AddFilterOption (1, GettextCatalog.GetString ("Include hidden files and directories")); - checkMenuItem1.DrawAsRadio = false; - checkMenuItem1.Active = properties.Get ("IncludeHiddenFiles", false); - checkMenuItem1.Toggled += delegate { - properties.Set ("IncludeHiddenFiles", checkMenuItem1.Active); - }; + searchentryFileMask.Query = properties.Get ("MonoDevelop.FindReplaceDialogs.FileMask", ""); @@ -793,15 +780,12 @@ namespace MonoDevelop.Ide.FindInFiles return null; } - scope = new DirectoryScope (comboboxentryPath.Entry.Text, checkbuttonRecursively.Active) { - IncludeHiddenFiles = properties.Get ("IncludeHiddenFiles", false) - }; + scope = new DirectoryScope (comboboxentryPath.Entry.Text, checkbuttonRecursively.Active); break; default: throw new ApplicationException ("Unknown scope:" + comboboxScope.Active); } - scope.IncludeBinaryFiles = properties.Get ("IncludeBinaryFiles", false); return scope; } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs index 9da97c4c0b..19a08d442f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.FindInFiles/Scope.cs @@ -39,6 +39,7 @@ namespace MonoDevelop.Ide.FindInFiles { public abstract class Scope { + [Obsolete ("Unused - will be removed")] public bool IncludeBinaryFiles { get; set; @@ -145,7 +146,7 @@ namespace MonoDevelop.Ide.FindInFiles () => new List<FileProvider> (), (folder, loop, providers) => { foreach (var file in folder.Files.Where (f => filterOptions.NameMatches (f.FileName) && File.Exists (f.FullPath))) { - if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (file.FullPath)) + if (!DesktopService.GetFileIsText (file.FullPath)) continue; lock (alreadyVisited) { if (alreadyVisited.Contains (file.FullPath)) @@ -171,7 +172,7 @@ namespace MonoDevelop.Ide.FindInFiles foreach (ProjectFile file in project.GetSourceFilesAsync (conf).Result.Where (f => filterOptions.NameMatches (f.Name) && File.Exists (f.Name))) { if ((file.Flags & ProjectItemFlags.Hidden) == ProjectItemFlags.Hidden) continue; - if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (file.FilePath)) + if (!DesktopService.GetFileIsText (file.FilePath)) continue; lock (alreadyVisited) { @@ -227,7 +228,7 @@ namespace MonoDevelop.Ide.FindInFiles foreach (ProjectFile file in project.GetSourceFilesAsync (conf).Result.Where (f => filterOptions.NameMatches (f.Name) && File.Exists (f.Name))) { if ((file.Flags & ProjectItemFlags.Hidden) == ProjectItemFlags.Hidden) continue; - if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (file.Name)) + if (!DesktopService.GetFileIsText (file.Name)) continue; if (alreadyVisited.Contains (file.FilePath.FullPath)) continue; @@ -280,6 +281,7 @@ namespace MonoDevelop.Ide.FindInFiles get { return PathMode.Absolute; } } + [Obsolete ("Unused - will be removed")] public bool IncludeHiddenFiles { get; set; @@ -315,33 +317,29 @@ namespace MonoDevelop.Ide.FindInFiles } foreach (string fileName in Directory.EnumerateFiles (curPath, "*")) { - if (!IncludeHiddenFiles) { - if (Platform.IsWindows) { - var attr = File.GetAttributes (fileName); - if (attr.HasFlag (FileAttributes.Hidden)) - continue; - } - if (Path.GetFileName (fileName).StartsWith (".", StringComparison.Ordinal)) + if (Platform.IsWindows) { + var attr = File.GetAttributes (fileName); + if (attr.HasFlag (FileAttributes.Hidden)) continue; } + if (Path.GetFileName (fileName).StartsWith (".", StringComparison.Ordinal)) + continue; if (!filterOptions.NameMatches (fileName)) continue; - if (!IncludeBinaryFiles && !DesktopService.GetFileIsText (fileName)) + if (!DesktopService.GetFileIsText (fileName)) continue; yield return fileName; } if (recurse) { foreach (string directoryName in Directory.EnumerateDirectories (curPath)) { - if (!IncludeHiddenFiles) { - if (Platform.IsWindows) { - var attr = File.GetAttributes (directoryName); - if (attr.HasFlag (FileAttributes.Hidden)) - continue; - } - if (Path.GetFileName (directoryName).StartsWith (".", StringComparison.Ordinal)) + if (Platform.IsWindows) { + var attr = File.GetAttributes (directoryName); + if (attr.HasFlag (FileAttributes.Hidden)) continue; } + if (Path.GetFileName (directoryName).StartsWith (".", StringComparison.Ordinal)) + continue; directoryStack.Push (directoryName); } } 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 876fd4dc31..c301f2cbbf 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/ApplyPolicyDialog.cs @@ -146,7 +146,7 @@ namespace MonoDevelop.Ide.Projects var sorted = content.ToList (); sorted.Sort ((x, y) => x.Key.CompareTo(y.Key)); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (var pol in sorted) { if (sb.Length > 0) @@ -168,7 +168,7 @@ namespace MonoDevelop.Ide.Projects sb.Append (")"); } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } protected void OnCombPoliciesChanged (object sender, System.EventArgs e) @@ -256,7 +256,7 @@ namespace MonoDevelop.Ide.Projects store.Clear (); - var sb = new StringBuilder (); + var sb = StringBuilderCache.Allocate (); foreach (var pol in sorted) { sb.Append (pol.Key); if (pol.Value.Count != 1 || pol.Value[0].Length != 0) { @@ -277,7 +277,7 @@ namespace MonoDevelop.Ide.Projects store.AppendValues (sb.ToString ()); sb.Length = 0; } - + StringBuilderCache.Free (sb); HasPolicies = sorted.Count > 0; } } diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs index a87031db12..a1b91bf4ca 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/AssemblyReferencePanel.cs @@ -285,7 +285,7 @@ namespace MonoDevelop.Ide.Projects internal static string GetMatchMarkup (Gtk.Widget widget, string text, int[] matches, int startIndex) { - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); int lastPos = 0; var color = HslColor.GenerateHighlightColors (widget.Style.Base (StateType.Normal), widget.Style.Text (StateType.Normal), 3)[2]; @@ -304,7 +304,7 @@ namespace MonoDevelop.Ide.Projects } if (lastPos < text.Length) result.Append (GLib.Markup.EscapeText (text.Substring (lastPos, text.Length - lastPos))); - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public void AddReference (object sender, Gtk.ToggledArgs e) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/IdeFileSystemExtensionExtension.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/IdeFileSystemExtensionExtension.cs index 1472f82945..52ef22a80f 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/IdeFileSystemExtensionExtension.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.Projects/IdeFileSystemExtensionExtension.cs @@ -63,8 +63,7 @@ namespace MonoDevelop.Ide.Projects string error = GettextCatalog.GetString ("File {0} is read-only", file.FileName); - var btn = new AlertButton (GettextCatalog.GetString ("Make Writable")); - var res = MessageService.AskQuestion (error, GettextCatalog.GetString ("Would you like {0} to attempt to make the file writable and try again?", BrandingService.ApplicationName), btn, AlertButton.Cancel); + var res = MessageService.AskQuestion (error, GettextCatalog.GetString ("Would you like {0} to attempt to make the file writable and try again?", BrandingService.ApplicationName), AlertButton.MakeWriteable, AlertButton.Cancel); if (res == AlertButton.Cancel) throw new UserException (error) { AlreadyReportedToUser = true }; diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.StandardHeader/StandardHeaderService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.StandardHeader/StandardHeaderService.cs index f3692a002d..4f78ec6d34 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.StandardHeader/StandardHeaderService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.StandardHeader/StandardHeaderService.cs @@ -70,7 +70,7 @@ namespace MonoDevelop.Ide.StandardHeader if (!char.IsWhiteSpace (cmt[cmt.Length - 1])) cmt = cmt + " "; - StringBuilder sb = new StringBuilder (policy.Text.Length); + StringBuilder sb = StringBuilderCache.Allocate (); string[] lines = policy.Text.Split ('\n'); foreach (string line in lines) { if (string.IsNullOrWhiteSpace (line)) { @@ -82,7 +82,7 @@ namespace MonoDevelop.Ide.StandardHeader sb.Append (line); sb.Append (eolMarker); } - result = sb.ToString (); + result = StringBuilderCache.ReturnAndFree (sb); } else { //multiline comment result = String.Concat (comment[0], "\n", policy.Text, "\n", comment[1], "\n"); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs index 9b25d238c1..808edbab28 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/Ambience.cs @@ -106,9 +106,9 @@ namespace MonoDevelop.Ide.TypeSystem if (String.IsNullOrEmpty (str)) return string.Empty; - StringBuilder sb = new StringBuilder (str.Length); + var sb = StringBuilderCache.Allocate (); MarkupUtilities.AppendEscapedString (sb, str, 0, str.Length); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree(sb); } #region Documentation @@ -266,7 +266,7 @@ namespace MonoDevelop.Ide.TypeSystem { if (maxLineLength <= 0) return text; - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); int lineLength = 0; bool inTag = false; bool inAmp = false; @@ -300,14 +300,14 @@ namespace MonoDevelop.Ide.TypeSystem lineLength = 0; } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree(result); } public static string EscapeText (string text) { if (text == null) return null; - StringBuilder result = new StringBuilder (text.Length); + StringBuilder result = StringBuilderCache.Allocate (); foreach (char ch in text) { switch (ch) { case '<': @@ -335,12 +335,12 @@ namespace MonoDevelop.Ide.TypeSystem break; } } - return result.ToString (); + return StringBuilderCache.ReturnAndFree (result); } public static string UnescapeText (string text) { - var sb = new StringBuilder (text.Length); + var sb = StringBuilderCache.Allocate (); for (int i = 0; i < text.Length; i++) { char ch = text[i]; if (ch == '&') { @@ -370,7 +370,7 @@ namespace MonoDevelop.Ide.TypeSystem sb.Append (ch); } } - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } @@ -381,7 +381,7 @@ namespace MonoDevelop.Ide.TypeSystem static string ParseBody (ISymbol member, XmlTextReader xml, string endTagName, DocumentationFormatOptions options) { - StringBuilder result = new StringBuilder (); + StringBuilder result = StringBuilderCache.Allocate (); bool wasWhiteSpace = true; bool appendSpace = false; string listType = "bullet"; @@ -498,7 +498,7 @@ namespace MonoDevelop.Ide.TypeSystem } } end: - return result.ToString ().Trim (); + return StringBuilderCache.ReturnAndFree (result).Trim (); } static string FormatCref (string cref) @@ -514,9 +514,9 @@ namespace MonoDevelop.Ide.TypeSystem return null; System.IO.StringReader reader = new System.IO.StringReader ("<docroot>" + doc + "</docroot>"); XmlTextReader xml = new XmlTextReader (reader); - StringBuilder ret = new StringBuilder (70); - StringBuilder parameterBuilder = new StringBuilder (); - StringBuilder exceptions = new StringBuilder (); + StringBuilder ret = StringBuilderCache.Allocate (); + StringBuilder parameterBuilder = StringBuilderCache.Allocate (); + StringBuilder exceptions = StringBuilderCache.Allocate (); exceptions.AppendLine (options.FormatHeading (GettextCatalog.GetString ("Exceptions:"))); // ret.Append ("<small>"); int paramCount = 0, exceptionCount = 0, summaryEnd = -1; @@ -565,7 +565,7 @@ namespace MonoDevelop.Ide.TypeSystem exceptions.Append ("</b>"); if (options.SmallText) exceptions.Append ("</small>"); - + exceptions.AppendLine (options.FormatBody (ParseBody (member, xml, xml.Name, options))); break; case "returns": @@ -578,7 +578,7 @@ namespace MonoDevelop.Ide.TypeSystem break; case "param": string paramName = xml.GetAttribute ("name") != null ? xml ["name"].Trim () : ""; - + var body = options.FormatBody (ParseBody (member, xml, xml.Name, options)); if (!IsEmptyDocumentation (body)) { paramCount++; @@ -613,34 +613,37 @@ namespace MonoDevelop.Ide.TypeSystem } } } while (xml.Read ()); - - } catch (Exception ex) { - MonoDevelop.Core.LoggingService.LogError (ex.ToString ()); - return EscapeText (doc); - } - if (IsEmptyDocumentation (ret.ToString ()) && IsEmptyDocumentation (parameterBuilder.ToString ())) - return EscapeText (doc); - if (string.IsNullOrEmpty (options.HighlightParameter) && exceptionCount > 0) - ret.Append (exceptions.ToString ()); - - string result = ret.ToString (); - if (summaryEnd < 0) - summaryEnd = result.Length; - if (paramCount > 0) { - var paramSb = new StringBuilder (); - if (result.Length > 0) - paramSb.AppendLine ();/* + if (IsEmptyDocumentation (ret.ToString ()) && IsEmptyDocumentation (parameterBuilder.ToString ())) + return EscapeText (doc); + if (string.IsNullOrEmpty (options.HighlightParameter) && exceptionCount > 0) + ret.Append (exceptions.ToString ()); + + string result = ret.ToString (); + if (summaryEnd < 0) + summaryEnd = result.Length; + if (paramCount > 0) { + var paramSb = StringBuilderCache.Allocate (); + if (result.Length > 0) + paramSb.AppendLine ();/* paramSb.Append ("<small>"); paramSb.AppendLine (options.FormatHeading (GettextCatalog.GetPluralString ("Parameter:", "Parameters:", paramCount))); paramSb.Append ("</small>");*/ - paramSb.Append (parameterBuilder.ToString ()); - result = result.Insert (summaryEnd, paramSb.ToString ()); + paramSb.Append (parameterBuilder.ToString ()); + result = result.Insert (summaryEnd, StringBuilderCache.ReturnAndFree(paramSb)); + } + result = result.Trim (); + if (result.EndsWith (Environment.NewLine + "</small>")) + result = result.Substring (0, result.Length - (Environment.NewLine + "</small>").Length) + "</small>"; + return result; + } catch (Exception ex) { + MonoDevelop.Core.LoggingService.LogError (ex.ToString ()); + return EscapeText (doc); + } finally { + StringBuilderCache.Free (ret); + StringBuilderCache.Free (parameterBuilder); + StringBuilderCache.Free (exceptions); } - result = result.Trim (); - if (result.EndsWith (Environment.NewLine + "</small>")) - result = result.Substring (0, result.Length - (Environment.NewLine + "</small>").Length) + "</small>"; - return result; } #endregion diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs index 00fee4d6f3..2cc64ee033 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MarkupUtilities.cs @@ -28,6 +28,7 @@ using System; using System.Text; +using MonoDevelop.Core; namespace MonoDevelop.Ide.TypeSystem { 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 f3074a2e58..1a604658fc 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/MonoDevelopWorkspace.cs @@ -1323,6 +1323,22 @@ namespace MonoDevelop.Ide.TypeSystem var path = GetMetadataPath (metadataReference);
if (mdProject == null || path == null)
return;
+ foreach (var r in mdProject.References) {
+ if (r.ReferenceType == MonoDevelop.Projects.ReferenceType.Assembly && r.Reference == path) {
+ LoggingService.LogWarning ("Warning duplicate reference is added " + path);
+ return;
+ }
+
+ if (r.ReferenceType == MonoDevelop.Projects.ReferenceType.Project) {
+ foreach (var fn in r.GetReferencedFileNames (MonoDevelop.Projects.ConfigurationSelector.Default)) {
+ if (fn == path) {
+ LoggingService.LogWarning ("Warning duplicate reference is added " + path + " for project " + r.Reference);
+ return;
+ }
+ }
+ }
+ }
+
mdProject.AddReference (path);
tryApplyState_changedProjects.Add (mdProject);
this.OnMetadataReferenceAdded (projectId, metadataReference);
diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NR5CompatibiltyExtensions.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NR5CompatibiltyExtensions.cs index de720d862a..e0d7f7f003 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NR5CompatibiltyExtensions.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/NR5CompatibiltyExtensions.cs @@ -60,7 +60,7 @@ namespace MonoDevelop.Ide.TypeSystem /// <param name="symbol">Symbol.</param> public static string GetFullMetadataName (this INamedTypeSymbol symbol) { - var fullName = new StringBuilder (symbol.MetadataName); + var fullName = StringBuilderCache.Allocate (symbol.MetadataName); var parentType = symbol.ContainingType; while (parentType != null) { fullName.Insert (0, '+'); @@ -73,7 +73,7 @@ namespace MonoDevelop.Ide.TypeSystem fullName.Insert (0, ns.MetadataName); ns = ns.ContainingNamespace; } - return fullName.ToString (); + return StringBuilderCache.ReturnAndFree (fullName); } /// <summary> 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 e337d273b1..378afdf996 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs @@ -314,7 +314,7 @@ namespace MonoDevelop.Ide.TypeSystem { var derivedDataPath = UserProfile.Current.CacheDir.Combine ("DerivedData"); - var name = new StringBuilder (); + var name = StringBuilderCache.Allocate (); foreach (var ch in framework.Name) { if (char.IsLetterOrDigit (ch)) { name.Append (ch); @@ -323,7 +323,7 @@ namespace MonoDevelop.Ide.TypeSystem } } - string result = derivedDataPath.Combine (name.ToString ()); + string result = derivedDataPath.Combine (StringBuilderCache.ReturnAndFree (name)); try { if (!Directory.Exists (result)) Directory.CreateDirectory (result); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs index 2b7a3da97f..82c899249e 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ImageService.cs @@ -822,10 +822,10 @@ namespace MonoDevelop.Ide { var md5 = System.Security.Cryptography.MD5.Create (); byte[] hash = md5.ComputeHash (Encoding.UTF8.GetBytes (email.Trim ().ToLower ())); - StringBuilder sb = new StringBuilder (); + StringBuilder sb = StringBuilderCache.Allocate (); foreach (byte b in hash) sb.Append (b.ToString ("x2")); - return sb.ToString (); + return StringBuilderCache.ReturnAndFree (sb); } public static void LoadUserIcon (this Gtk.Image image, string email, int size) diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs index 0dedbf9f98..1dba998a37 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/MessageService.cs @@ -91,7 +91,8 @@ namespace MonoDevelop.Ide public static AlertButton OverwriteFile = new AlertButton (GettextCatalog.GetString ("_Overwrite file")); public static AlertButton AddExistingFile = new AlertButton (GettextCatalog.GetString ("Add existing file")); - + public static AlertButton MakeWriteable = new AlertButton (GettextCatalog.GetString ("Make Writeable")); + public string Label { get; set; } public string Icon { get; set; } |