Exemple #1
0
        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));
        }
Exemple #2
0
        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);
            }
        }
Exemple #5
0
        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);
        }
Exemple #6
0
        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));
        }
Exemple #10
0
        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);
        }
Exemple #11
0
        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));
        }
Exemple #12
0
        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);
        }