static void AddMenuWithSeparatorIfNeeded(CodeFixMenu toAdd, CodeFixMenu into, ref bool first) { if (toAdd.Items.Count == 0) { return; } if (first) { into.Add(CodeFixMenuEntry.Separator); } into.Add(toAdd); first = false; }
static void AddFixMenuItem(TextEditor editor, CodeFixMenu menu, CodeFixMenu fixAllMenu, ref int mnemonic, CodeAction fix, FixAllState fixState, CancellationToken token) { if (fix is CodeAction.CodeActionWithNestedActions nested) { // Inline code actions if they are, otherwise add a nested fix menu if (nested.IsInlinable) { int actionCount = nested.NestedCodeActions.Length; foreach (var nestedFix in nested.NestedCodeActions) { var nestedFixState = actionCount > 1 && nestedFix.EquivalenceKey == null ? null : fixState; AddFixMenuItem(editor, menu, fixAllMenu, ref mnemonic, nestedFix, nestedFixState, token); } return; } if (nested.NestedCodeActions.Length > 0) { AddNestedFixMenu(editor, menu, fixAllMenu, nested, fixState, token); } return; } menu.Add(CreateFixMenuEntry(editor, fix, ref mnemonic)); // TODO: Add support for more than doc when we have global undo. fixState = fixState?.WithScopeAndEquivalenceKey(FixAllScope.Document, fix.EquivalenceKey); var fixAllMenuEntry = CreateFixAllMenuEntry(editor, fixState, ref mnemonic, token); if (fixAllMenuEntry != null) { fixAllMenu.Add(fixAllMenuEntry); } }
static void AddNestedFixMenu(TextEditor editor, CodeFixMenu menu, CodeAction.CodeActionWithNestedActions fixes) { int subMnemonic = 0; var subMenu = new CodeFixMenu(fixes.Title); foreach (var fix in fixes.NestedCodeActions) { AddFixMenuItem(editor, subMenu, ref subMnemonic, fix); } menu.Add(subMenu); }
static void AddNestedFixMenu(Ide.Editor.TextEditor editor, CodeFixMenu menu, CodeFixMenu fixAllMenu, CodeAction.CodeActionWithNestedActions fixes, FixAllState fixState, CancellationToken token) { int subMnemonic = 0; var subMenu = new CodeFixMenu(fixes.Title); foreach (var fix in fixes.NestedCodeActions) { AddFixMenuItem(editor, subMenu, fixAllMenu, ref subMnemonic, fix, fixState, token); } menu.Add(subMenu); }
static void AddFixMenuItem(TextEditor editor, CodeFixMenu menu, ref int mnemonic, CodeAction fix) { var nested = fix as CodeAction.CodeActionWithNestedActions; if (nested != null) { AddNestedFixMenu(editor, menu, nested); return; } menu.Add(CreateFixMenuEntry(editor, fix, ref mnemonic)); }
static void AddConfigurationMenuEntry(Diagnostic diag, CodeDiagnosticDescriptor descriptor, Microsoft.CodeAnalysis.CodeFixes.CodeFix fix, CodeFixMenu configureMenu) { 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); } }
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; }
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); }
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); }