diff options
12 files changed, 230 insertions, 60 deletions
diff --git a/main/src/addins/CSharpBinding/CSharpBinding.csproj b/main/src/addins/CSharpBinding/CSharpBinding.csproj index dcf933b611..9a555fb29c 100644 --- a/main/src/addins/CSharpBinding/CSharpBinding.csproj +++ b/main/src/addins/CSharpBinding/CSharpBinding.csproj @@ -286,6 +286,7 @@ <Compile Include="MonoDevelop.Ide.Completion.Presentation\RoslynCompletionPresenter.cs" /> <Compile Include="MonoDevelop.Ide.Completion.Presentation\RoslynCompletionPresenterSession.ICompletionPresenterSession.cs" /> <Compile Include="MonoDevelop.Ide.Completion.Presentation\ContainedDocumentPreserveFormattingRule.cs" /> + <Compile Include="MonoDevelop.Ide.Completion.Presentation\ExtensionMethods.cs" /> <Compile Include="MonoDevelop.Ide.Completion.Presentation\IMonoDevelopContainedLanguageHost.cs" /> <Compile Include="MonoDevelop.Ide.Completion.Presentation\IMyRoslynCompletionDataProvider.cs" /> <Compile Include="MonoDevelop.Ide.Completion.Presentation\MonoDevelopContainedDocument.cs" /> diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.IDebuggerCompletionProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.IDebuggerCompletionProvider.cs index c4ea793875..9db791513a 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.IDebuggerCompletionProvider.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.Completion/CSharpCompletionTextEditorExtension.IDebuggerCompletionProvider.cs @@ -37,6 +37,7 @@ using Microsoft.VisualStudio.Platform; using Microsoft.VisualStudio.Text; using MonoDevelop.Debugger; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Tags; namespace MonoDevelop.CSharp.Completion { @@ -120,7 +121,7 @@ namespace MonoDevelop.CSharp.Completion return null; var result = new Mono.Debugging.Client.CompletionData (); foreach (var roslynCompletion in roslynCompletions.Items) { - if (roslynCompletion.Tags.Contains (CompletionTags.Snippet)) + if (roslynCompletion.Tags.Contains (WellKnownTags.Snippet)) continue; result.Items.Add (new Mono.Debugging.Client.CompletionItem (roslynCompletion.DisplayText, RoslynTagsToDebuggerFlags (roslynCompletion.Tags))); } @@ -133,61 +134,61 @@ namespace MonoDevelop.CSharp.Completion var result = Mono.Debugging.Client.ObjectValueFlags.None; foreach (var tag in tags) { switch (tag) { - case CompletionTags.Public: + case WellKnownTags.Public: result |= Mono.Debugging.Client.ObjectValueFlags.Public; break; - case CompletionTags.Protected: + case WellKnownTags.Protected: result |= Mono.Debugging.Client.ObjectValueFlags.Protected; break; - case CompletionTags.Private: + case WellKnownTags.Private: result |= Mono.Debugging.Client.ObjectValueFlags.Private; break; - case CompletionTags.Internal: + case WellKnownTags.Internal: result |= Mono.Debugging.Client.ObjectValueFlags.Internal; break; - case CompletionTags.File: - case CompletionTags.Project: - case CompletionTags.Folder: - case CompletionTags.Assembly: - case CompletionTags.Intrinsic: - case CompletionTags.Keyword: - case CompletionTags.Label: - case CompletionTags.Snippet: - case CompletionTags.Error: - case CompletionTags.Warning: - case CompletionTags.Module: - case CompletionTags.Operator: + case WellKnownTags.File: + case WellKnownTags.Project: + case WellKnownTags.Folder: + case WellKnownTags.Assembly: + case WellKnownTags.Intrinsic: + case WellKnownTags.Keyword: + case WellKnownTags.Label: + case WellKnownTags.Snippet: + case WellKnownTags.Error: + case WellKnownTags.Warning: + case WellKnownTags.Module: + case WellKnownTags.Operator: break; - case CompletionTags.Local: - case CompletionTags.Constant: - case CompletionTags.RangeVariable: - case CompletionTags.Reference: + case WellKnownTags.Local: + case WellKnownTags.Constant: + case WellKnownTags.RangeVariable: + case WellKnownTags.Reference: result |= Mono.Debugging.Client.ObjectValueFlags.Variable; break; - case CompletionTags.Class: - case CompletionTags.Enum: - case CompletionTags.Delegate: - case CompletionTags.Interface: - case CompletionTags.Structure: - case CompletionTags.TypeParameter: + case WellKnownTags.Class: + case WellKnownTags.Enum: + case WellKnownTags.Delegate: + case WellKnownTags.Interface: + case WellKnownTags.Structure: + case WellKnownTags.TypeParameter: result |= Mono.Debugging.Client.ObjectValueFlags.Type; break; - case CompletionTags.EnumMember: - case CompletionTags.Event: - case CompletionTags.Field: + case WellKnownTags.EnumMember: + case WellKnownTags.Event: + case WellKnownTags.Field: result |= Mono.Debugging.Client.ObjectValueFlags.Field; break; - case CompletionTags.Namespace: + case WellKnownTags.Namespace: result |= Mono.Debugging.Client.ObjectValueFlags.Namespace; break; - case CompletionTags.ExtensionMethod: - case CompletionTags.Method: + case WellKnownTags.ExtensionMethod: + case WellKnownTags.Method: result |= Mono.Debugging.Client.ObjectValueFlags.Method; break; - case CompletionTags.Parameter: + case WellKnownTags.Parameter: result |= Mono.Debugging.Client.ObjectValueFlags.Parameter; break; - case CompletionTags.Property: + case WellKnownTags.Property: result |= Mono.Debugging.Client.ObjectValueFlags.Property; break; } diff --git a/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/ExtensionMethods.cs b/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/ExtensionMethods.cs new file mode 100644 index 0000000000..d94fa20bad --- /dev/null +++ b/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/ExtensionMethods.cs @@ -0,0 +1,31 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; + +namespace MonoDevelop.Ide.Completion.Presentation +{ + public static class ExtensionMethods + { + /// <summary> + /// Temporary workaround for https://devdiv.visualstudio.com/DevDiv/_workitems/edit/662639. + /// The first call may fail with the ArgumentException coming from Mono due to ConditionalWeakTable + /// reentrancy, however by that time the table will have already been populated. + /// Subsequent call should not hit this problem and should succeed. + /// TODO: remove when https://github.com/dotnet/roslyn/issues/28256 is fixed. + /// </summary> + public static Document GetOpenDocumentInCurrentContextWithChangesSafe (this ITextSnapshot textSnapshot) + { + Document document = null; + try { + document = textSnapshot.GetOpenDocumentInCurrentContextWithChanges (); + } catch { + try { + document = textSnapshot.GetOpenDocumentInCurrentContextWithChanges (); + } catch { + } + } + + return document; + } + } +}
\ No newline at end of file diff --git a/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/MonoDevelopContainedDocument.cs b/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/MonoDevelopContainedDocument.cs index c9e3564b27..4e3517d0ba 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/MonoDevelopContainedDocument.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/MonoDevelopContainedDocument.cs @@ -104,7 +104,7 @@ namespace MonoDevelop.Ide.Completion.Presentation private void FinishInitialization ()
{
- var document = LanguageBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges ();
+ var document = LanguageBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChangesSafe (); var project = document.Project;
_workspace = project.Solution.Workspace;
@@ -451,7 +451,7 @@ namespace MonoDevelop.Ide.Completion.Presentation private IHierarchicalDifferenceCollection DiffStrings (string leftTextWithReplacement, string rightTextWithReplacement)
{
- var document = LanguageBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges (); ;
+ var document = LanguageBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChangesSafe (); var diffService = _differenceSelectorService.GetTextDifferencingService (
_workspace.Services.GetLanguageServices (document.Project.Language).GetService<IContentTypeLanguageService> ().GetDefaultContentType ());
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/RoslynCompletionPresenterSession.View.cs b/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/RoslynCompletionPresenterSession.View.cs index f424f8403c..09ed648737 100644 --- a/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/RoslynCompletionPresenterSession.View.cs +++ b/main/src/addins/CSharpBinding/MonoDevelop.Ide.Completion.Presentation/RoslynCompletionPresenterSession.View.cs @@ -295,6 +295,8 @@ namespace MonoDevelop.Ide.Completion.Presentation protected override bool OnExposeEvent (Gdk.EventExpose args) { + bool needsRefresh = false; + using (var context = Gdk.CairoHelper.Create (args.Window)) { var scalef = GtkWorkarounds.GetScaleFactor (this); context.LineWidth = 1; @@ -446,13 +448,13 @@ namespace MonoDevelop.Ide.Completion.Presentation if (Math.Min (maxListWidth, wi + xpos + iconWidth + 2) > listWidth) { box.WidthRequest = listWidth = Math.Min (maxListWidth, wi + xpos + iconWidth + 2 + iconTextSpacing); - textView.QueueSpaceReservationStackRefresh (); + needsRefresh = true; } else { //workaround for the vscrollbar display - the calculated width needs to be the width ofthe render region. if (Allocation.Width < listWidth) { if (listWidth - Allocation.Width < 30) { box.WidthRequest = listWidth + listWidth - Allocation.Width; - textView.QueueSpaceReservationStackRefresh (); + needsRefresh = true; } } } @@ -460,10 +462,24 @@ namespace MonoDevelop.Ide.Completion.Presentation return true; }); + if (needsRefresh) { + QueueSpaceReservationStackRefresh (); + } + return false; } } + /// <summary> + /// Need to postpone this after the current painting on the UI thread is done, + /// otherwise the stack refresh will close the current completion while it's mid-painting. + /// See VSTS 662457. + /// </summary> + void QueueSpaceReservationStackRefresh () + { + Task.Run (() => textView.QueueSpaceReservationStackRefresh ()); + } + public int TextOffset { get { int iconWidth, iconHeight; @@ -632,7 +648,7 @@ namespace MonoDevelop.Ide.Completion.Presentation TooltipInformation description = null; try { - var document = _subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges (); + var document = _subjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChangesSafe (); description = await RoslynCompletionData.CreateTooltipInformation (document, completionItem, false, token); } catch { } diff --git a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFramework.cs b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFramework.cs index 9c78def14b..d62fcc7a48 100644 --- a/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFramework.cs +++ b/main/src/core/MonoDevelop.Core/MonoDevelop.Core.Assemblies/TargetFramework.cs @@ -229,13 +229,19 @@ namespace MonoDevelop.Core.Assemblies cacheKey += "_" + moniker.Profile; } - FrameworkInfo fxInfo; + FrameworkInfo fxInfo = null; var cachedListFile = fxCacheDir.Combine (cacheKey + ".xml"); var cachedListInfo = new FileInfo (cachedListFile); if (cachedListInfo.Exists && cachedListInfo.LastWriteTime == fxListInfo.LastWriteTime) { fxInfo = FrameworkInfo.Load (moniker, cachedListFile); - } else { + //if Mono was upgraded since caching, the cached location may no longer be valid + if (!Directory.Exists (fxInfo.TargetFrameworkDirectory)) { + fxInfo = null; + } + } + + if (fxInfo == null) { fxInfo = FrameworkInfo.Load (moniker, fxListFile); var supportedFrameworksDir = dir.Combine ("SupportedFrameworks"); if (Directory.Exists (supportedFrameworksDir)) { 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 cf72a47739..dd4fb52f62 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide.TypeSystem/TypeSystemService.cs @@ -52,10 +52,13 @@ namespace MonoDevelop.Ide.TypeSystem static string[] filesSkippedInParseThread = new string[0]; public static Microsoft.CodeAnalysis.SyntaxAnnotation InsertionModeAnnotation = new Microsoft.CodeAnalysis.SyntaxAnnotation(); - static IEnumerable<TypeSystemParserNode> Parsers { + internal static IEnumerable<TypeSystemParserNode> Parsers { get { return parsers; } + set { + parsers = value; + } } public static void RemoveSkippedfile (FilePath fileName) @@ -229,6 +232,9 @@ namespace MonoDevelop.Ide.TypeSystem var t = Counters.ParserService.FileParsed.BeginTiming (options.FileName); try { var result = await parser.GenerateParsedDocumentProjection (options, cancellationToken); + if (cancellationToken.IsCancellationRequested) + return null; + if (options.Project != null) { var ws = workspaces.First () ; var projectId = ws.GetProjectId (options.Project); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs index 2f5ad3d18e..d93adc5ac5 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/Ide.cs @@ -205,9 +205,7 @@ namespace MonoDevelop.Ide } } - public static void Initialize (ProgressMonitor monitor) => Initialize (monitor, showWelcomePage: true); - - internal static void Initialize (ProgressMonitor monitor, bool showWelcomePage) + public static void Initialize (ProgressMonitor monitor) { // Already done in IdeSetup, but called again since unit tests don't use IdeSetup. DispatchService.Initialize (); @@ -252,8 +250,7 @@ namespace MonoDevelop.Ide monitor.Step (1); MonoDevelop.Ide.WelcomePage.WelcomePageService.Initialize (); - if (showWelcomePage) - MonoDevelop.Ide.WelcomePage.WelcomePageService.ShowWelcomePage (); + MonoDevelop.Ide.WelcomePage.WelcomePageService.ShowWelcomePage (); monitor.Step (1); diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs index 09acf72176..0b66b61642 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/IdeStartup.cs @@ -241,8 +241,7 @@ namespace MonoDevelop.Ide // which is then replaced with a second empty Apple menu. // XBC #33699 Counters.Initialization.Trace ("Initializing IdeApp"); - bool showWelcomePage = !startupInfo.HasFiles; - IdeApp.Initialize (monitor, showWelcomePage); + IdeApp.Initialize (monitor); if (errorsList.Count > 0) { using (AddinLoadErrorDialog dlg = new AddinLoadErrorDialog (errorsList.ToArray (), false)) { diff --git a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs index fe08694bf1..442ac0a0ec 100644 --- a/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs +++ b/main/src/core/MonoDevelop.Ide/MonoDevelop.Ide/ProjectOperations.cs @@ -1344,23 +1344,56 @@ namespace MonoDevelop.Ide } finally { tt.End (); } + }
+
+ static IBuildTarget[] GetBuildTargetsForExecution (IBuildTarget executionTarget, RunConfiguration runConfiguration)
+ {
+ if (executionTarget is Solution sol) {
+ if (runConfiguration is SingleItemSolutionRunConfiguration src) {
+ return new [] { src.Item };
+ }
+ if (runConfiguration is MultiItemSolutionRunConfiguration mrc) {
+ var buildTargets = new IBuildTarget [mrc.Items.Count];
+ int i = 0;
+ foreach (var item in mrc.Items) {
+ buildTargets [i++] = item.SolutionItem;
+ }
+ return buildTargets;
+ }
+ }
+ return new [] { executionTarget };
+ } + + // given a solution RunConfiguration, determine the matching project RunConfigurations + static RunConfiguration GetProjectRunConfiguration (IRunTarget target, RunConfiguration config)
+ {
+ if (config is SingleItemSolutionRunConfiguration src) {
+ if (src.Item == target) {
+ return src.RunConfiguration;
+ }
+ }
+ else if (config is MultiItemSolutionRunConfiguration mrc) {
+ foreach (var item in mrc.Items) {
+ if (item.SolutionItem == target)
+ return item.RunConfiguration;
+ }
+ }
+ return config;
} Task<bool> CheckAndBuildForExecute (IBuildTarget executionTarget, ExecutionContext context, ConfigurationSelector configuration, RunConfiguration runConfiguration) { - var buildTarget = executionTarget; - - // When executing a solution we are actually going to execute the startup project. So we only need to build that project. - // TODO: handle multi-startup solutions. - if (buildTarget is Solution sol && sol.StartupItem != null) - buildTarget = sol.StartupItem; + // When executing a solution we are actually going to execute the startup project(s), so we only need to build it/them + IBuildTarget [] buildTargets = GetBuildTargetsForExecution (executionTarget, runConfiguration); return CheckAndBuildForExecute ( - new [] { buildTarget }, configuration, + buildTargets, configuration, IdeApp.Preferences.BuildBeforeExecuting, IdeApp.Preferences.RunWithWarnings, (target, monitor) => { - if (target is IRunTarget) - return ((IRunTarget)target).PrepareExecution (monitor, context, configuration, runConfiguration); + if (target is IRunTarget runTarget) {
+ var projectRunConfig = GetProjectRunConfiguration (runTarget, runConfiguration);
+ return runTarget.PrepareExecution (monitor, context, configuration, projectRunConfig); + } return target.PrepareExecution (monitor, context, configuration); } ); diff --git a/main/tests/Ide.Tests/MonoDevelop.Ide/TypeSystemServiceTests.cs b/main/tests/Ide.Tests/MonoDevelop.Ide/TypeSystemServiceTests.cs index 3a769c9962..89ee289d16 100644 --- a/main/tests/Ide.Tests/MonoDevelop.Ide/TypeSystemServiceTests.cs +++ b/main/tests/Ide.Tests/MonoDevelop.Ide/TypeSystemServiceTests.cs @@ -28,6 +28,7 @@ using System; using NUnit.Framework; using System.Collections.Generic; using UnitTests; +using Mono.Addins; using MonoDevelop.Ide.TypeSystem; using MonoDevelop.Projects; using MonoDevelop.Core; @@ -220,5 +221,84 @@ namespace MonoDevelop.Ide Assert.IsTrue (solutionLoaded); Assert.IsFalse (reloaded); } + + /// <summary> + /// Tests that if the parser takes a long time to return a projection it is ignored instead + /// of being used by the type system. + /// </summary> + [Test] + public async Task CancelledParsedProjectionIsIgnored () + { + string solFile = Util.GetSampleProject ("console-project", "ConsoleProject.sln"); + + var parsers = TypeSystemService.Parsers; + try { + var projectionParser = new TypeSystemParserNode (); + projectionParser.BuildActions = new [] { "Compile" }; + projectionParser.MimeType = "text/csharp"; + + var flags = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic; + var field = typeof (TypeExtensionNode).GetField ("type", flags); + field.SetValue (projectionParser, typeof (ProjectionParser)); + + var newParsers = new List<TypeSystemParserNode> (); + newParsers.Add (projectionParser); + TypeSystemService.Parsers = newParsers; + + using (var sol = (Solution)await Services.ProjectService.ReadWorkspaceItem (Util.GetMonitor (), solFile)) { + using (var ws = await TypeSystemServiceTestExtensions.LoadSolution (sol)) { + + var source1 = new CancellationTokenSource (); + var source2 = new CancellationTokenSource (); + + var options = new ParseOptions { + FileName = "first.xaml.cs" + }; + var task1 = TypeSystemService.ParseProjection (options, "text/csharp", source1.Token); + source1.Cancel (); + + options = new ParseOptions { + FileName = "second.xaml.cs" + }; + var task2 = TypeSystemService.ParseProjection (options, "text/csharp", source2.Token); + + var result1 = await task1; + var result2 = await task2; + + Assert.IsNotNull (result2); + Assert.IsNull (result1); + } + } + } finally { + TypeSystemService.Parsers = parsers; + } + } + + class ProjectionParser : TypeSystemParser + { + public override Task<ParsedDocument> Parse (ParseOptions options, CancellationToken cancellationToken = default) + { + throw new NotImplementedException (); + } + + public override bool CanGenerateProjection (string mimeType, string buildAction, string [] supportedLanguages) + { + return true; + } + + public async override Task<ParsedDocumentProjection> GenerateParsedDocumentProjection (ParseOptions options, CancellationToken cancellationToken = default) + { + string fileName = Path.GetFileName (options.FileName); + if (fileName == "first.xaml.cs") { + await Task.Delay (2000).ConfigureAwait (false); + } + var projections = new List<Editor.Projection.Projection> (); + var projection = new ParsedDocumentProjection ( + new DefaultParsedDocument (options.FileName), + projections.AsReadOnly () + ); + return projection; + } + } } } diff --git a/version-checks b/version-checks index 05fb9743d1..c32dae4087 100755 --- a/version-checks +++ b/version-checks @@ -17,7 +17,7 @@ DEP[0]=md-addins DEP_NAME[0]=MDADDINS DEP_PATH[0]=${top_srcdir}/../md-addins DEP_MODULE[0]=git@github.com:xamarin/md-addins.git -DEP_NEEDED_VERSION[0]=161ba8f418697fea09e9be5a0ce22cbd8aa03b9e +DEP_NEEDED_VERSION[0]=77af1f90b419d76c348cfa155e3a2b36fdbb061a DEP_BRANCH_AND_REMOTE[0]="master origin/master" # heap-shot |