internal Task <CodeActionContainer> GetCurrentFixesAsync(CancellationToken cancellationToken) { var loc = Editor.CaretOffset; var ad = DocumentContext.AnalysisDocument; if (ad == null) { return(Task.FromResult(CodeActionContainer.Empty)); } TextSpan span; if (Editor.IsSomethingSelected) { var selectionRange = Editor.SelectionRange; span = selectionRange.Offset >= 0 ? TextSpan.FromBounds(selectionRange.Offset, selectionRange.EndOffset) : TextSpan.FromBounds(loc, loc); } else { span = TextSpan.FromBounds(loc, loc); } return(Task.Run(async delegate { try { var fixes = await codeFixService.GetFixesAsync(ad, span, true, cancellationToken); var refactorings = await codeRefactoringService.GetRefactoringsAsync(ad, span, cancellationToken); var codeActionContainer = new CodeActionContainer(fixes, refactorings); Application.Invoke((o, args) => { if (cancellationToken.IsCancellationRequested) { return; } if (codeActionContainer.IsEmpty) { RemoveWidget(); return; } CreateSmartTag(codeActionContainer, loc); }); return codeActionContainer; } catch (AggregateException ae) { ae.Flatten().Handle(aex => aex is OperationCanceledException); return CodeActionContainer.Empty; } catch (OperationCanceledException) { return CodeActionContainer.Empty; } catch (TargetInvocationException ex) { if (ex.InnerException is OperationCanceledException) { return CodeActionContainer.Empty; } throw; } }, cancellationToken)); }
void CreateSmartTag(CodeActionContainer fixes, int offset) { if (!AnalysisOptions.EnableFancyFeatures || fixes.IsEmpty) { RemoveWidget(); return; } var editor = Editor; if (editor == null) { RemoveWidget(); return; } if (DocumentContext.ParsedDocument == null || DocumentContext.ParsedDocument.IsInvalid) { RemoveWidget(); return; } bool first = true; var smartTagLocBegin = offset; foreach (var fix in fixes.CodeFixActions.Concat(fixes.CodeRefactoringActions)) { var textSpan = fix.ValidSegment; if (textSpan.IsEmpty) { continue; } if (first || offset < textSpan.Start) { smartTagLocBegin = textSpan.Start; } first = false; } if (currentSmartTag != null && currentSmartTagBegin == smartTagLocBegin) { return; } RemoveWidget(); currentSmartTagBegin = smartTagLocBegin; var realLoc = Editor.OffsetToLocation(smartTagLocBegin); currentSmartTag = TextMarkerFactory.CreateSmartTagMarker(Editor, smartTagLocBegin, realLoc); currentSmartTag.CancelPopup += CurrentSmartTag_CancelPopup; currentSmartTag.ShowPopup += CurrentSmartTag_ShowPopup; currentSmartTag.Tag = fixes; currentSmartTag.IsVisible = fixes.CodeFixActions.Count > 0; editor.AddMarker(currentSmartTag); }
internal void PopupQuickFixMenu(Gdk.EventButton evt, CodeActionContainer fixes, Action <CodeFixMenu> menuAction, Xwt.Point?point = null) { var token = quickFixCancellationTokenSource.Token; if (token.IsCancellationRequested) { return; } var menu = CodeFixMenuService.CreateFixMenu(Editor, fixes, token); if (token.IsCancellationRequested) { return; } if (menu.Items.Count == 0) { return; } if (menuAction != null) { menuAction(menu); } Gdk.Rectangle rect; Widget widget = Editor; if (!point.HasValue) { var p = Editor.LocationToPoint(Editor.CaretLocation); rect = new Gdk.Rectangle( (int)p.X + widget.Allocation.X, (int)p.Y + widget.Allocation.Y, 0, 0); } else { rect = new Gdk.Rectangle((int)point.Value.X, (int)point.Value.Y, 0, 0); } ShowFixesMenu(widget, rect, menu); }
void CreateSmartTag(CodeActionContainer fixes, int offset) { if (!AnalysisOptions.EnableFancyFeatures || fixes.IsEmpty) { RemoveWidget(); return; } var editor = Editor; if (editor == null) { RemoveWidget(); return; } if (DocumentContext.ParsedDocument == null || DocumentContext.ParsedDocument.IsInvalid) { RemoveWidget(); return; } var severity = fixes.GetSmartTagSeverity(); if (smartTagMarginMarker?.Line?.LineNumber != editor.CaretLine) { RemoveWidget(); smartTagMarginMarker = new SourceEditor.SmartTagMarginMarker() { SmartTagSeverity = severity }; smartTagMarginMarker.ShowPopup += SmartTagMarginMarker_ShowPopup; editor.AddMarker(editor.GetLine(editor.CaretLine), smartTagMarginMarker); } else { smartTagMarginMarker.SmartTagSeverity = severity; var view = editor.GetContent <SourceEditorView> (); view.TextEditor.RedrawMarginLine(view.TextEditor.TextArea.QuickFixMargin, editor.CaretLine); } }
public static async Task <CodeFixMenu> CreateFixMenu(TextEditor editor, CodeActionContainer fixes, CancellationToken cancellationToken = default(CancellationToken)) { var menu = new CodeFixMenu(); if (editor.DocumentContext.AnalysisDocument == null) { return(menu); } int mnemonic = 1; foreach (var fix in fixes.CodeFixActions.OrderByDescending(i => GetUsage(i.CodeAction.EquivalenceKey))) { AddFixMenuItem(editor, menu, ref mnemonic, fix.CodeAction); } bool first = true; foreach (var fix in fixes.CodeRefactoringActions) { if (first) { if (menu.Items.Count > 0) { menu.Add(CodeFixMenuEntry.Separator); } first = false; } AddFixMenuItem(editor, menu, ref mnemonic, fix.CodeAction); } var warningsAtCaret = (await editor.DocumentContext.AnalysisDocument.GetSemanticModelAsync(cancellationToken)) .GetDiagnostics(new TextSpan(editor.CaretOffset, 0)) .Where(diag => diag.Severity == DiagnosticSeverity.Warning).ToList(); var caretSpan = new TextSpan(editor.CaretOffset, 0); first = true; foreach (var warning in warningsAtCaret) { if (string.IsNullOrWhiteSpace(warning.Descriptor.Title.ToString())) { continue; } var label = GettextCatalog.GetString("_Options for \u2018{0}\u2019", warning.Descriptor.Title); var subMenu = new CodeFixMenu(label); await AddSuppressionMenuItems(subMenu, editor, warning, caretSpan); if (subMenu.Items.Count > 0) { if (first) { menu.Add(CodeFixMenuEntry.Separator); first = false; } menu.Add(subMenu); } } first = true; foreach (var diag in fixes.DiagnosticsAtCaret) { if (string.IsNullOrWhiteSpace(diag.Descriptor.Title.ToString())) { continue; } var notConfigurable = DescriptorHasTag(diag.Descriptor, WellKnownDiagnosticTags.NotConfigurable); var label = GettextCatalog.GetString("_Options for \u2018{0}\u2019", diag.Descriptor.Title); var subMenu = new CodeFixMenu(label); if (first) { menu.Add(CodeFixMenuEntry.Separator); first = false; } await AddSuppressionMenuItems(subMenu, editor, diag, caretSpan); var descriptor = BuiltInCodeDiagnosticProvider.GetCodeDiagnosticDescriptor(diag.Id); if (descriptor != null && IsConfigurable(diag.Descriptor)) { var optionsMenuItem = new CodeFixMenuEntry(GettextCatalog.GetString("_Configure Rule"), delegate { IdeApp.Workbench.ShowGlobalPreferencesDialog(null, "C#", dialog => { var panel = dialog.GetPanel <CodeIssuePanel> ("C#"); if (panel == null) { return; } panel.Widget.SelectCodeIssue(diag.Descriptor.Id); }); }); subMenu.Add(optionsMenuItem); } foreach (var fix in fixes.CodeFixActions.OrderByDescending(i => GetUsage(i.CodeAction.EquivalenceKey))) { if (cancellationToken.IsCancellationRequested) { return(null); } var provider = fix.Diagnostic.GetCodeFixProvider().GetFixAllProvider(); if (provider == null) { continue; } if (!provider.GetSupportedFixAllScopes().Contains(FixAllScope.Document)) { continue; } var language = editor.DocumentContext.AnalysisDocument.Project.Language; var diagnosticdDescriptor = fix.Diagnostic?.GetCodeDiagnosticDescriptor(language); if (diagnosticdDescriptor == null) { continue; } var subMenu2 = new CodeFixMenu(GettextCatalog.GetString("Fix all")); var diagnosticAnalyzer = diagnosticdDescriptor.GetProvider(); if (!diagnosticAnalyzer.SupportedDiagnostics.Contains(diag.Descriptor)) { continue; } var menuItem = new CodeFixMenuEntry( GettextCatalog.GetString("In _Document"), async delegate { await FixAll(editor, fix, provider, diagnosticAnalyzer); } ); subMenu2.Add(menuItem); subMenu.Add(CodeFixMenuEntry.Separator); subMenu.Add(subMenu2); } menu.Add(subMenu); } return(menu); }
public static CodeFixMenu CreateFixMenu(Ide.Editor.TextEditor editor, CodeActionContainer fixes, CancellationToken cancellationToken = default(CancellationToken)) { var menu = new CodeFixMenu(); if (editor.DocumentContext.AnalysisDocument == null) { return(menu); } var options = ((MonoDevelopWorkspaceDiagnosticAnalyzerProviderService)Ide.Composition.CompositionManager.Instance.GetExportedValue <IWorkspaceDiagnosticAnalyzerProviderService> ()).GetOptionsAsync().Result; int mnemonic = 1; var suppressLabel = GettextCatalog.GetString("_Suppress"); var suppressMenu = new CodeFixMenu(suppressLabel); var fixAllLabel = GettextCatalog.GetString("_Fix all"); var fixAllMenu = new CodeFixMenu(fixAllLabel); var configureLabel = GettextCatalog.GetString("_Options"); var configureMenu = new CodeFixMenu(configureLabel); var fixAllTasks = new List <Task <CodeAction> > (); foreach (var cfa in fixes.CodeFixActions) { var scopes = cfa.SupportedScopes; // FIXME: No global undo yet to support fixall in project/solution var state = scopes.Contains(FixAllScope.Document) ? cfa.FixAllState : null; foreach (var fix in cfa.Fixes) { var diag = fix.PrimaryDiagnostic; if (options.TryGetDiagnosticDescriptor(diag.Id, out var descriptor) && !diag.Descriptor.IsEnabledByDefault) { continue; } bool isSuppress = fix.Action is TopLevelSuppressionCodeAction; CodeFixMenu fixMenu; FixAllState fixState; if (isSuppress) { fixMenu = suppressMenu; fixState = null; } else { fixMenu = menu; fixState = state; } AddFixMenuItem(editor, fixMenu, fixAllMenu, ref mnemonic, fix.Action, fixState, cancellationToken); } } bool first = true; foreach (var refactoring in fixes.CodeRefactoringActions) { if (options.TryGetRefactoringDescriptor(refactoring.GetType(), out var descriptor) && !descriptor.IsEnabled) { continue; } if (first) { if (menu.Items.Count > 0) { menu.Add(CodeFixMenuEntry.Separator); } first = false; } foreach (var codeAction in refactoring.CodeActions) { AddFixMenuItem(editor, menu, null, ref mnemonic, codeAction.action, null, cancellationToken); } } first = true; AddMenuWithSeparatorIfNeeded(fixAllMenu, menu, ref first); AddMenuWithSeparatorIfNeeded(suppressMenu, menu, ref first); AddMenuWithSeparatorIfNeeded(configureMenu, menu, ref first); if (!AnalysisOptions.AnalysisEnabled) { if (first) { menu.Add(CodeFixMenuEntry.Separator); } var enableLabel = GettextCatalog.GetString("Enable Source Analysis"); menu.Add(new CodeFixMenuEntry(enableLabel, enableAction)); first = false; } return(menu); void enableAction() => AnalysisOptions.AnalysisEnabled.Value = true; }
void CreateSmartTag(CodeActionContainer fixes, int offset) { if (!AnalysisOptions.EnableFancyFeatures || fixes.IsEmpty) { RemoveWidget(); return; } var editor = Editor; if (editor == null) { RemoveWidget(); return; } if (DocumentContext.ParsedDocument == null || DocumentContext.ParsedDocument.IsInvalid) { RemoveWidget(); return; } // var container = editor.Parent; // if (container == null) { // RemoveWidget (); // return; // } bool first = true; var smartTagLocBegin = offset; foreach (var fix in fixes.CodeFixActions.Concat(fixes.CodeRefactoringActions)) { var textSpan = fix.ValidSegment; if (textSpan.IsEmpty) { continue; } if (first || offset < textSpan.Start) { smartTagLocBegin = textSpan.Start; } first = false; } // if (smartTagLocBegin.Line != loc.Line) // smartTagLocBegin = new DocumentLocation (loc.Line, 1); // got no fix location -> try to search word start // if (first) { // int offset = document.Editor.LocationToOffset (smartTagLocBegin); // while (offset > 0) { // char ch = document.Editor.GetCharAt (offset - 1); // if (!char.IsLetterOrDigit (ch) && ch != '_') // break; // offset--; // } // smartTagLocBegin = document.Editor.OffsetToLocation (offset); // } if (currentSmartTag != null && currentSmartTagBegin == smartTagLocBegin) { return; } RemoveWidget(); currentSmartTagBegin = smartTagLocBegin; var realLoc = Editor.OffsetToLocation(smartTagLocBegin); currentSmartTag = TextMarkerFactory.CreateSmartTagMarker(Editor, smartTagLocBegin, realLoc); currentSmartTag.CancelPopup += CurrentSmartTag_CancelPopup; currentSmartTag.ShowPopup += CurrentSmartTag_ShowPopup; currentSmartTag.Tag = fixes; currentSmartTag.IsVisible = fixes.CodeFixActions.Count > 0; editor.AddMarker(currentSmartTag); }
void HandleCaretPositionChanged(object sender, EventArgs e) { if (Editor.IsInAtomicUndo) { return; } CancelQuickFixTimer(); if (AnalysisOptions.EnableFancyFeatures && DocumentContext.ParsedDocument != null) { var token = quickFixCancellationTokenSource.Token; var curOffset = Editor.CaretOffset; if (HasCurrentFixes) { foreach (var fix in GetCurrentFixes().AllValidCodeActions) { if (!fix.ValidSegment.Contains(curOffset)) { RemoveWidget(); break; } } } var loc = Editor.CaretOffset; var ad = DocumentContext.AnalysisDocument; if (ad == null) { return; } TextSpan span; if (Editor.IsSomethingSelected) { var selectionRange = Editor.SelectionRange; span = selectionRange.Offset >= 0 ? TextSpan.FromBounds(selectionRange.Offset, selectionRange.EndOffset) : TextSpan.FromBounds(loc, loc); } else { span = TextSpan.FromBounds(loc, loc); } var diagnosticsAtCaret = Editor.GetTextSegmentMarkersAt(Editor.CaretOffset) .OfType <IGenericTextSegmentMarker> () .Select(rm => rm.Tag) .OfType <DiagnosticResult> () .Select(dr => dr.Diagnostic) .ToList(); var errorList = Editor .GetTextSegmentMarkersAt(Editor.CaretOffset) .OfType <IErrorMarker> () .Where(rm => !string.IsNullOrEmpty(rm.Error.Id)).ToList(); int editorLength = Editor.Length; smartTagTask = Task.Run(async delegate { try { var codeIssueFixes = new List <ValidCodeDiagnosticAction> (); var diagnosticIds = diagnosticsAtCaret.Select(diagnostic => diagnostic.Id).Concat(errorList.Select(rm => rm.Error.Id)).ToList(); if (codeFixes == null) { codeFixes = (await CodeRefactoringService.GetCodeFixesAsync(DocumentContext, CodeRefactoringService.MimeTypeToLanguage(Editor.MimeType), token).ConfigureAwait(false)).ToList(); } foreach (var cfp in codeFixes) { if (token.IsCancellationRequested) { return(CodeActionContainer.Empty); } var provider = cfp.GetCodeFixProvider(); if (!provider.FixableDiagnosticIds.Any(diagnosticIds.Contains)) { continue; } try { var groupedDiagnostics = diagnosticsAtCaret .Concat(errorList.Select(em => em.Error.Tag) .OfType <Diagnostic> ()) .GroupBy(d => d.Location.SourceSpan); foreach (var g in groupedDiagnostics) { if (token.IsCancellationRequested) { return(CodeActionContainer.Empty); } var diagnosticSpan = g.Key; var validDiagnostics = g.Where(d => provider.FixableDiagnosticIds.Contains(d.Id)).ToImmutableArray(); if (validDiagnostics.Length == 0) { continue; } await provider.RegisterCodeFixesAsync(new CodeFixContext(ad, diagnosticSpan, validDiagnostics, (ca, d) => codeIssueFixes.Add(new ValidCodeDiagnosticAction(cfp, ca, validDiagnostics, diagnosticSpan)), token)); // TODO: Is that right ? Currently it doesn't really make sense to run one code fix provider on several overlapping diagnostics at the same location // However the generate constructor one has that case and if I run it twice the same code action is generated twice. So there is a dupe check problem there. // Work around for now is to only take the first diagnostic batch. break; } } catch (OperationCanceledException) { return(CodeActionContainer.Empty); } catch (AggregateException ae) { ae.Flatten().Handle(aex => aex is OperationCanceledException); return(CodeActionContainer.Empty); } catch (Exception ex) { LoggingService.LogError("Error while getting refactorings from code fix provider " + cfp.Name, ex); continue; } } var codeActions = new List <ValidCodeAction> (); foreach (var action in await CodeRefactoringService.GetValidActionsAsync(Editor, DocumentContext, span, token).ConfigureAwait(false)) { codeActions.Add(action); } var codeActionContainer = new CodeActionContainer(codeIssueFixes, codeActions, diagnosticsAtCaret); Application.Invoke(delegate { if (token.IsCancellationRequested) { return; } if (codeActionContainer.IsEmpty) { RemoveWidget(); return; } CreateSmartTag(codeActionContainer, loc); }); return(codeActionContainer); } catch (AggregateException ae) { ae.Flatten().Handle(aex => aex is OperationCanceledException); return(CodeActionContainer.Empty); } catch (OperationCanceledException) { return(CodeActionContainer.Empty); } catch (TargetInvocationException ex) { if (ex.InnerException is OperationCanceledException) { return(CodeActionContainer.Empty); } throw; } }, token); } else { RemoveWidget(); } }
internal Task <CodeActionContainer> GetCurrentFixesAsync(CancellationToken cancellationToken) { var loc = Editor.CaretOffset; var ad = DocumentContext.AnalysisDocument; var workspace = ad?.Project?.Solution?.Workspace; var line = Editor.GetLine(Editor.CaretLine); if (ad == null || workspace == null) { return(Task.FromResult(CodeActionContainer.Empty)); } TextSpan span; if (Editor.IsSomethingSelected) { var selectionRange = Editor.SelectionRange; span = selectionRange.Offset >= 0 ? TextSpan.FromBounds(selectionRange.Offset, selectionRange.EndOffset) : TextSpan.FromBounds(loc, loc); } else { span = TextSpan.FromBounds(loc, loc); } return(Task.Run(async delegate { try { var root = await ad.GetSyntaxRootAsync(cancellationToken); if (root == null) { // WebEditorRoslynWorkspace adds .json, .css, .html etc. files to the workspace // but they don't support syntax trees or semantic model return CodeActionContainer.Empty; } if (root.Span.End < span.End) { LoggingService.LogError($"Error in GetCurrentFixesAsync span {span.Start}/{span.Length} not inside syntax root {root.Span.End} document length {Editor.Length}."); return CodeActionContainer.Empty; } var lineSpan = new TextSpan(line.Offset, line.Length); var fixes = await codeFixService.GetFixesAsync(ad, lineSpan, true, cancellationToken); fixes = await Runtime.RunInMainThread(() => FilterOnUIThread(fixes, workspace)); var refactorings = await codeRefactoringService.GetRefactoringsAsync(ad, span, cancellationToken); var codeActionContainer = new CodeActionContainer(fixes, refactorings); Application.Invoke((o, args) => { if (cancellationToken.IsCancellationRequested) { return; } if (codeActionContainer.IsEmpty) { RemoveWidget(); return; } CreateSmartTag(codeActionContainer, loc); }); return codeActionContainer; } catch (AggregateException ae) { ae.Flatten().Handle(aex => aex is OperationCanceledException); return CodeActionContainer.Empty; } catch (OperationCanceledException) { return CodeActionContainer.Empty; } catch (TargetInvocationException ex) { if (ex.InnerException is OperationCanceledException) { return CodeActionContainer.Empty; } throw; } }, cancellationToken)); }
public static async Task <CodeFixMenu> CreateFixMenu(TextEditor editor, CodeActionContainer fixes, CancellationToken cancellationToken = default(CancellationToken)) { var menu = new CodeFixMenu(); if (editor.DocumentContext.AnalysisDocument == null) { return(menu); } int mnemonic = 1; var suppressLabel = GettextCatalog.GetString("_Suppress"); var suppressMenu = new CodeFixMenu(suppressLabel); var fixAllLabel = GettextCatalog.GetString("_Fix all"); var fixAllMenu = new CodeFixMenu(fixAllLabel); var configureLabel = GettextCatalog.GetString("_Options"); var configureMenu = new CodeFixMenu(configureLabel); var fixAllTasks = new List <Task <CodeAction> > (); foreach (var cfa in fixes.CodeFixActions) { var state = cfa.FixAllState; var scopes = cfa.SupportedScopes; foreach (var fix in cfa.Fixes) { var diag = fix.PrimaryDiagnostic; if (options.TryGetDiagnosticDescriptor(diag.Id, out var descriptor) && !descriptor.GetIsEnabled(diag.Descriptor)) { continue; } bool isSuppress = fix.Action is TopLevelSuppressionCodeAction; if (isSuppress) { AddFixMenuItem(editor, suppressMenu, ref mnemonic, fix.Action); continue; } AddFixMenuItem(editor, menu, ref mnemonic, fix.Action); var configurable = !DescriptorHasTag(diag.Descriptor, WellKnownDiagnosticTags.NotConfigurable); if (descriptor != null && configurable) { var optionsMenuItem = new CodeFixMenuEntry(GettextCatalog.GetString("_Configure Rule \u2018{0}\u2019", diag.Descriptor.Title), delegate { IdeApp.Workbench.ShowGlobalPreferencesDialog(null, "C#", dialog => { var panel = dialog.GetPanel <CodeIssuePanel> ("C#"); if (panel == null) { return; } panel.Widget.SelectCodeIssue(fix.PrimaryDiagnostic.Descriptor.Id); }); }); configureMenu.Add(optionsMenuItem); } if (!scopes.Contains(FixAllScope.Document)) { continue; } // FIXME: No global undo yet to support fixall in project/solution var fixState = state.WithScopeAndEquivalenceKey(FixAllScope.Document, fix.Action.EquivalenceKey); var provider = state.FixAllProvider; if (provider == null) { continue; } // FIXME: Use a real progress tracker. var fixAll = Task.Run(() => provider.GetFixAsync(fixState.CreateFixAllContext(new RoslynProgressTracker(), cancellationToken))); fixAllTasks.Add(fixAll); } } var fixAllActions = await Task.WhenAll(fixAllTasks); foreach (var fixAllAction in fixAllActions) { AddFixMenuItem(editor, fixAllMenu, ref mnemonic, fixAllAction); } bool first = true; foreach (var refactoring in fixes.CodeRefactoringActions) { if (options.TryGetRefactoringDescriptor(refactoring.GetType(), out var descriptor) && !descriptor.IsEnabled) { continue; } if (first) { if (menu.Items.Count > 0) { menu.Add(CodeFixMenuEntry.Separator); } first = false; } foreach (var action in refactoring.Actions) { AddFixMenuItem(editor, menu, ref mnemonic, action); } } first = true; if (fixAllMenu.Items.Count != 0) { if (first) { menu.Add(CodeFixMenuEntry.Separator); } menu.Add(fixAllMenu); first = false; } if (suppressMenu.Items.Count != 0) { if (first) { menu.Add(CodeFixMenuEntry.Separator); } menu.Add(suppressMenu); first = false; } if (configureMenu.Items.Count != 0) { if (first) { menu.Add(CodeFixMenuEntry.Separator); } menu.Add(configureMenu); first = false; } return(menu); }
internal Task <CodeActionContainer> GetCurrentFixesAsync(CancellationToken cancellationToken) { var loc = Editor.CaretOffset; var ad = DocumentContext.AnalysisDocument; if (ad == null) { return(Task.FromResult(CodeActionContainer.Empty)); } TextSpan span; if (Editor.IsSomethingSelected) { var selectionRange = Editor.SelectionRange; span = selectionRange.Offset >= 0 ? TextSpan.FromBounds(selectionRange.Offset, selectionRange.EndOffset) : TextSpan.FromBounds(loc, loc); } else { span = TextSpan.FromBounds(loc, loc); } var rExt = Editor.GetContent <ResultsEditorExtension> (); var errorList = Editor .GetTextSegmentMarkersAt(Editor.CaretOffset) .OfType <IErrorMarker> () .Where(rm => !string.IsNullOrEmpty(rm.Error.Id)).ToList(); return(Task.Run(async delegate { try { var result = await CodeDiagnosticRunner.Check(new AnalysisDocument(Editor, DocumentContext), cancellationToken).ConfigureAwait(false); var diagnosticsAtCaret = result.OfType <DiagnosticResult> ().Where(d => d.Region.Contains(loc)).Select(d => d.Diagnostic).ToList(); var codeIssueFixes = new List <ValidCodeDiagnosticAction> (); var diagnosticIds = diagnosticsAtCaret.Select(diagnostic => diagnostic.Id).Concat(errorList.Select(rm => rm.Error.Id)).ToList(); if (codeFixes == null) { codeFixes = (await CodeRefactoringService.GetCodeFixesAsync(DocumentContext, CodeRefactoringService.MimeTypeToLanguage(Editor.MimeType), cancellationToken).ConfigureAwait(false)).ToList(); } var root = await ad.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); foreach (var cfp in codeFixes) { if (cancellationToken.IsCancellationRequested) { return CodeActionContainer.Empty; } var provider = cfp.GetCodeFixProvider(); if (!provider.FixableDiagnosticIds.Any(diagnosticIds.Contains)) { continue; } // These two delegates were factored out, as using them as lambdas in the inner loop creates more captures than declaring them here. Func <Diagnostic, bool> providerIdsContain = d => provider.FixableDiagnosticIds.Contains(d.Id); Action <Microsoft.CodeAnalysis.CodeActions.CodeAction, ImmutableArray <Diagnostic> > codeFixRegistration = (ca, d) => codeIssueFixes.Add(new ValidCodeDiagnosticAction(cfp, ca, d, d [0].Location.SourceSpan)); try { var groupedDiagnostics = diagnosticsAtCaret .Concat(errorList.Select(em => em.Error.Tag) .OfType <Diagnostic> ()) .GroupBy(d => d.Location.SourceSpan); foreach (var g in groupedDiagnostics) { if (cancellationToken.IsCancellationRequested) { return CodeActionContainer.Empty; } var diagnosticSpan = g.Key; var validDiagnostics = g.Where(providerIdsContain).ToImmutableArray(); if (validDiagnostics.Length == 0) { continue; } if (diagnosticSpan.Start < 0 || diagnosticSpan.End > root.Span.End) { continue; } await provider.RegisterCodeFixesAsync(new CodeFixContext(ad, diagnosticSpan, validDiagnostics, codeFixRegistration, cancellationToken)).ConfigureAwait(false); // TODO: Is that right ? Currently it doesn't really make sense to run one code fix provider on several overlapping diagnostics at the same location // However the generate constructor one has that case and if I run it twice the same code action is generated twice. So there is a dupe check problem there. // Work around for now is to only take the first diagnostic batch. break; } } catch (OperationCanceledException) { return CodeActionContainer.Empty; } catch (AggregateException ae) { ae.Flatten().Handle(aex => aex is OperationCanceledException); return CodeActionContainer.Empty; } catch (Exception ex) { LoggingService.LogError("Error while getting refactorings from code fix provider " + cfp.Name, ex); continue; } } var codeActions = new List <ValidCodeAction> (); foreach (var action in await CodeRefactoringService.GetValidActionsAsync(Editor, DocumentContext, span, cancellationToken).ConfigureAwait(false)) { codeActions.Add(action); } if (cancellationToken.IsCancellationRequested) { return CodeActionContainer.Empty; } var codeActionContainer = new CodeActionContainer(codeIssueFixes, codeActions, diagnosticsAtCaret); Application.Invoke((o, args) => { if (cancellationToken.IsCancellationRequested) { return; } if (codeActionContainer.IsEmpty) { RemoveWidget(); return; } CreateSmartTag(codeActionContainer, loc); }); return codeActionContainer; } catch (AggregateException ae) { ae.Flatten().Handle(aex => aex is OperationCanceledException); return CodeActionContainer.Empty; } catch (OperationCanceledException) { return CodeActionContainer.Empty; } catch (TargetInvocationException ex) { if (ex.InnerException is OperationCanceledException) { return CodeActionContainer.Empty; } throw; } }, cancellationToken)); }
public static CodeFixMenu CreateFixMenu(TextEditor editor, CodeActionContainer fixes, CancellationToken cancellationToken = default(CancellationToken)) { var menu = new CodeFixMenu(); if (editor.DocumentContext.AnalysisDocument == null) { return(menu); } int mnemonic = 1; var suppressLabel = GettextCatalog.GetString("_Suppress"); var suppressMenu = new CodeFixMenu(suppressLabel); var fixAllLabel = GettextCatalog.GetString("_Fix all"); var fixAllMenu = new CodeFixMenu(fixAllLabel); var configureLabel = GettextCatalog.GetString("_Options"); var configureMenu = new CodeFixMenu(configureLabel); var fixAllTasks = new List <Task <CodeAction> > (); foreach (var cfa in fixes.CodeFixActions) { var scopes = cfa.SupportedScopes; // FIXME: No global undo yet to support fixall in project/solution var state = scopes.Contains(FixAllScope.Document) ? cfa.FixAllState : null; foreach (var fix in cfa.Fixes) { var diag = fix.PrimaryDiagnostic; if (options.TryGetDiagnosticDescriptor(diag.Id, out var descriptor) && !descriptor.GetIsEnabled(diag.Descriptor)) { continue; } bool isSuppress = fix.Action is TopLevelSuppressionCodeAction; CodeFixMenu fixMenu; FixAllState fixState; if (isSuppress) { fixMenu = suppressMenu; fixState = null; } else { fixMenu = menu; fixState = state; AddConfigurationMenuEntry(diag, descriptor, fix, configureMenu); } AddFixMenuItem(editor, fixMenu, fixAllMenu, ref mnemonic, fix.Action, fixState, cancellationToken); } } bool first = true; foreach (var refactoring in fixes.CodeRefactoringActions) { if (options.TryGetRefactoringDescriptor(refactoring.GetType(), out var descriptor) && !descriptor.IsEnabled) { continue; } if (first) { if (menu.Items.Count > 0) { menu.Add(CodeFixMenuEntry.Separator); } first = false; } foreach (var action in refactoring.Actions) { AddFixMenuItem(editor, menu, null, ref mnemonic, action, null, cancellationToken); } } first = true; AddMenuWithSeparatorIfNeeded(fixAllMenu, menu, ref first); AddMenuWithSeparatorIfNeeded(suppressMenu, menu, ref first); AddMenuWithSeparatorIfNeeded(configureMenu, menu, ref first); return(menu); }