示例#1
0
        public SarifLocationTextMarkerTagger(
            ITextView textView,
            ITextBuffer textBuffer,
            IPersistentSpanFactory persistentSpanFactory,
            ITextViewCaretListenerService <ITextMarkerTag> textViewCaretListenerService,
            ISarifErrorListEventSelectionService sarifErrorListEventSelectionService)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            if (!SdkUIUtilities.TryGetFileNameFromTextBuffer(textBuffer, out this.filePath))
            {
                throw new ArgumentException("Always expect to be able to get file name from text buffer.", nameof(textBuffer));
            }

            this.TextBuffer            = textBuffer;
            this.persistentSpanFactory = persistentSpanFactory;
            this.sarifErrorListEventSelectionService = sarifErrorListEventSelectionService;

            // Subscribe to the SARIF error item being selected from the error list
            // so we can properly filter the tags being shown in the editor
            // to the currently selected item.
            this.sarifErrorListEventSelectionService.SelectedItemChanged += this.SelectedSarifItemChanged;

            // Subscribe to the caret position so we can send enter and exit notifications
            // to the tags so they can decide potentially change their colors.
            textViewCaretListenerService.CreateListener(textView, this);
        }
示例#2
0
        /// <summary>
        /// Initializes a new instance of the <see cref="FixSuggestedActionsSource"/> class.
        /// </summary>
        /// <param name="textView">
        /// The <see cref="ITextView"/> for which this source will offer fix suggestions.
        /// </param>
        /// <param name="textBuffer">
        /// The <see cref="ITextBuffer"/> associated with the <see cref="ITextView"/> for which this
        /// source will offer fix suggestions.
        /// </param>
        /// <param name="persistentSpanFactory">
        /// A factory for creating the persistent spans that specify the error locations and the
        /// replacement locations (which are not necessarily the same).
        /// </param>
        /// <param name="previewProvider">
        /// Creates the XAML UIControl that displays the preview.
        /// </param>
        public FixSuggestedActionsSource(
            ITextView textView,
            ITextBuffer textBuffer,
            IPersistentSpanFactory persistentSpanFactory,
            IPreviewProvider previewProvider)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            this.textView              = textView;
            this.textBuffer            = textBuffer;
            this.persistentSpanFactory = persistentSpanFactory;
            this.previewProvider       = previewProvider;

            // when text changed and sarif errors item changes, need to refresh errorInFile
            IErrorList errorList = ServiceProvider.GlobalProvider.GetService(typeof(SVsErrorList)) as IErrorList;

            this.errorListTableControl = errorList?.TableControl;
            this.errorListTableControl.EntriesChanged += this.ErrorListTableControl_EntriesChanged;
            this.RefreshPersistentSpans();

            // Keep track of which error is associated with each suggested action, so that when
            // the action is invoked, the associated error can be marked as fixed. When we mark
            // an error as fixed, we tell VS to recompute the list of suggested actions, so that
            // it doesn't suggest actions for errors that are already fixed.
            this.fixToErrorDictionary = new Dictionary <FixSuggestedAction, SarifErrorListItem>();
        }
        public SarifLocationErrorTagger(ITextBuffer textBuffer, IPersistentSpanFactory persistentSpanFactory, ISarifErrorListEventSelectionService sarifErrorListEventSelectionService)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            if (!SdkUIUtilities.TryGetFileNameFromTextBuffer(textBuffer, out this.filePath))
            {
                throw new ArgumentException("Always expect to be able to get file name from text buffer.", nameof(textBuffer));
            }

            this.TextBuffer = textBuffer;

            this.persistentSpanFactory = persistentSpanFactory;
            this.sarifErrorListEventSelectionService = sarifErrorListEventSelectionService;
            this.sarifErrorListEventSelectionService.SelectedItemChanged += this.SarifErrorListEventSelectionService_SelectedItemChanged;
        }
        private bool TryCreatePersistentSpan(ITextBuffer textBuffer, IPersistentSpanFactory persistentSpanFactory)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            if (this.persistentSpan != null)
            {
                return(true);
            }

            if (!this.TryToFullyPopulateRegionAndFilePath())
            {
                return(false);
            }

            return(SpanHelper.TryCreatePersistentSpan(this.fullyPopulatedRegion, textBuffer, persistentSpanFactory, out this.persistentSpan));
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="FixSuggestedActionsSource"/> class.
        /// </summary>
        /// <param name="textView">
        /// The <see cref="ITextView"/> for which this source will offer fix suggestions.
        /// </param>
        /// <param name="textBuffer">
        /// The <see cref="ITextBuffer"/> associated with the <see cref="ITextView"/> for which this
        /// source will offer fix suggestions.
        /// </param>
        /// <param name="persistentSpanFactory">
        /// A factory for creating the persistent spans that specify the error locations and the
        /// replacement locations (which are not necessarily the same).
        /// </param>
        /// <param name="previewProvider">
        /// Creates the XAML UIControl that displays the preview.
        /// </param>
        public FixSuggestedActionsSource(
            ITextView textView,
            ITextBuffer textBuffer,
            IPersistentSpanFactory persistentSpanFactory,
            IPreviewProvider previewProvider)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            this.textView              = textView;
            this.textBuffer            = textBuffer;
            this.persistentSpanFactory = persistentSpanFactory;
            this.previewProvider       = previewProvider;

            if (this.previewProvider is EditActionPreviewProvider editActionPreviewProvider)
            {
                editActionPreviewProvider.ApplyFixesInDocument += this.PreviewProdiver_ApplyFixesInDocument;
            }

            // when text changed and sarif errors item changes, need to refresh errorInFile
            var errorList = ServiceProvider.GlobalProvider.GetService(typeof(SVsErrorList)) as IErrorList;

            this.errorListTableControl = errorList?.TableControl;
            if (this.errorListTableControl != null)
            {
                this.errorListTableControl.EntriesChanged += this.ErrorListTableControl_EntriesChanged;
            }

            var component = ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel)) as IComponentModel;

            this.sarifErrorListEventSelectionService = component?.GetService <ISarifErrorListEventSelectionService>();
            if (this.sarifErrorListEventSelectionService != null)
            {
                this.sarifErrorListEventSelectionService.NavigatedItemChanged += this.SarifListErrorItemNavigated;
            }

            this.RefreshPersistentSpans();

            // Keep track of which error is associated with each suggested action, so that when
            // the action is invoked, the associated error can be marked as fixed. When we mark
            // an error as fixed, we tell VS to recompute the list of suggested actions, so that
            // it doesn't suggest actions for errors that are already fixed.
            this.fixToErrorDictionary = new Dictionary <FixSuggestedAction, SarifErrorListItem>();
        }
        /// <summary>
        /// Returns the tags represented by this result marker.
        /// </summary>
        /// <remarks>
        /// This is only called by either <see cref="SarifLocationErrorTagger"/> or <see cref="SarifLocationTextMarkerTagger"/>.</remarks>
        /// <typeparam name="T">Specifies which tag type the tagger is asking for.</typeparam>
        /// <param name="textBuffer">The text buffer that the tags are being requested.</param>
        /// <param name="persistentSpanFactory">The persistent span factory that can be used to create the persistent spans for the tags.</param>
        /// <returns>List of tags returned by result marker.</returns>
        public IEnumerable <ISarifLocationTag> GetTags <T>(ITextBuffer textBuffer, IPersistentSpanFactory persistentSpanFactory)
            where T : ITag
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            var tags = new List <ISarifLocationTag>();

            if (!this.TryCreatePersistentSpan(textBuffer, persistentSpanFactory))
            {
                return(tags);
            }

            if (typeof(T) == typeof(IErrorTag) && this.ToolTipContent != null && this.ErrorType != null)
            {
                tags.Add(new SarifLocationErrorTag(
                             this.persistentSpan,
                             runIndex: this.RunIndex,
                             resultId: this.ResultId,
                             errorType: this.ErrorType,
                             toolTipContent: this.ToolTipContent,
                             toolTipXamlString: this.ToolTipXamlString,
                             context: this.Context));
            }

            if (typeof(T) == typeof(ITextMarkerTag))
            {
                tags.Add(new SarifLocationTextMarkerTag(
                             this.persistentSpan,
                             runIndex: this.RunIndex,
                             resultId: this.ResultId,
                             nonHighlightedTextMarkerTagType: this.NonHighlightedColor,
                             highlightedTextMarkerTagType: this.HighlightedColor,
                             context: this.Context));
            }

            return(tags);
        }
示例#7
0
        internal static bool TryCreatePersistentSpan(Region fullyPopulatedRegion, ITextBuffer textBuffer, IPersistentSpanFactory persistentSpanFactory, out IPersistentSpan persistentSpan)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            persistentSpan = null;

            if (!TryCreateTextSpanWithinDocumentFromSourceRegion(fullyPopulatedRegion, textBuffer, out TextSpan documentSpan))
            {
                return(false);
            }

            if (!persistentSpanFactory.CanCreate(textBuffer))
            {
                return(false);
            }

            persistentSpan = persistentSpanFactory.Create(
                textBuffer.CurrentSnapshot,
                startLine: documentSpan.iStartLine,
                startIndex: documentSpan.iStartIndex,
                endLine: documentSpan.iEndLine,
                endIndex: documentSpan.iEndIndex,
                trackingMode: SpanTrackingMode.EdgeInclusive);

            return(true);
        }
        /// <summary>
        /// Attempts to navigate a VS editor to the text marker.
        /// </summary>
        /// <param name="usePreviewPane">Indicates whether to use VS's preview pane.</param>
        /// <param name="moveFocusToCaretLocation">Indicates whether to move focus to the caret location.</param>
        /// <returns>Returns true if a VS editor was opened.</returns>
        /// <remarks>
        /// The <paramref name="usePreviewPane"/> indicates whether Visual Studio opens the document as a preview (tab to the right)
        /// rather than as an "open code editor" (tab attached to other open documents on the left).
        /// </remarks>
        public bool NavigateTo(bool usePreviewPane, bool moveFocusToCaretLocation)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            bool documentWasOpened = false;

            // Make sure to fully populate region.
            if (!this.TryToFullyPopulateRegionAndFilePath())
            {
                return(false);
            }

            // If the tag doesn't have a persistent span, or its associated document isn't open,
            // then this indicates that we need to attempt to open the document and cause it to
            // be tagged.
            if (!this.PersistentSpanValid())
            {
                // Now, we need to make sure the document gets tagged before the next section of code
                // in this method attempts to navigate to it.
                // So the flow looks like this. Get Visual Studio to open the document for us.
                // That will cause Visual Studio to create a text view for it.
                // Now, just because a text view is created does not mean that
                // a request for "tags" has occurred. Tagging (and the display of those tags)
                // is highly asynchronous. Taggers are created on demand and disposed when they are no longer
                // needed. It is quite common to have multiple taggers active for the same text view and text buffers
                // at the same time. (An easy example is a split-window scenario).
                // This class relies on a persistent span (this.persistentSpan) being non-null and valid.
                // This class "creates" the persistent span when it is asked for its tags in the GetTags
                // method.
                // To facilitate that, we will:
                // 1) Open the document
                // 2) Get the text view from the document.
                // 2a) That alone may be enough to make the persistent span valid if it was already created.
                // 3) If the persistent span still isn't valid (likely because we have crated the persistent span yet), ask ourselves for the tags which will
                //    cause the span to be created.
                IVsWindowFrame vsWindowFrame = SdkUIUtilities.OpenDocument(ServiceProvider.GlobalProvider, this.resolvedFullFilePath, usePreviewPane);
                if (vsWindowFrame == null)
                {
                    return(false);
                }

                vsWindowFrame.Show();

                documentWasOpened = true;

                // At this point, the persistent span may have "become valid" due to the document open.
                // If not, then ask ourselves for the tags which will create the persistent span.
                if (!this.PersistentSpanValid())
                {
                    if (!SdkUIUtilities.TryGetTextViewFromFrame(vsWindowFrame, out ITextView textView))
                    {
                        return(false);
                    }

                    var componentModel = (IComponentModel)Package.GetGlobalService(typeof(SComponentModel));
                    if (componentModel == null)
                    {
                        return(false);
                    }

                    IPersistentSpanFactory persistentSpanFactory = componentModel.GetService <IPersistentSpanFactory>();
                    if (persistentSpanFactory == null)
                    {
                        return(false);
                    }

                    if (!this.TryCreatePersistentSpan(textView.TextBuffer, persistentSpanFactory))
                    {
                        return(false);
                    }
                }
            }

            if (!this.PersistentSpanValid())
            {
                return(false);
            }

            // Now, if the span IS valid it doesn't mean that the editor is visible, so make sure we open the document
            // for the user if needed.
            // But before we try to call "open document" let's see if we can find an active view because calling
            // "open document" is super slow (which causes keyboard navigation from items in the SARIF explorer to be slow
            // IF "open document" is called every time.
            if (!documentWasOpened ||
                !SdkUIUtilities.TryGetActiveViewForTextBuffer(this.persistentSpan.Span.TextBuffer, out IWpfTextView wpfTextView))
            {
                IVsWindowFrame vsWindowFrame = SdkUIUtilities.OpenDocument(ServiceProvider.GlobalProvider, this.resolvedFullFilePath, usePreviewPane);
                if (vsWindowFrame == null)
                {
                    return(false);
                }

                vsWindowFrame.Show();

                if (!SdkUIUtilities.TryGetActiveViewForTextBuffer(this.persistentSpan.Span.TextBuffer, out wpfTextView))
                {
                    return(false);
                }
            }

            ITextSnapshot currentSnapshot = this.persistentSpan.Span.TextBuffer.CurrentSnapshot;

            // Note that "GetSpan" is not really a great name. What is actually happening
            // is the "Span" that "GetSpan" is called on is "mapped" onto the passed in
            // text snapshot. In essence what this means is take the "persistent span"
            // that we have and "replay" any edits that have occurred and return a new
            // span. So, if the span is no longer relevant (lets say the text has been deleted)
            // then you'll get back an empty span.
            SnapshotSpan trackingSpanSnapshot = this.persistentSpan.Span.GetSpan(currentSnapshot);

            // If the caret is already in the text within the marker, don't re-select it
            // otherwise users cannot move the caret in the region.
            // If the caret isn't in the marker, move it there.
            if (!trackingSpanSnapshot.Contains(wpfTextView.Caret.Position.BufferPosition) &&
                !trackingSpanSnapshot.IsEmpty)
            {
                wpfTextView.Selection.Select(trackingSpanSnapshot, isReversed: false);
                wpfTextView.Caret.MoveTo(trackingSpanSnapshot.End);
                wpfTextView.Caret.EnsureVisible();

                if (moveFocusToCaretLocation)
                {
                    wpfTextView.VisualElement.Focus();
                }
            }

            return(true);
        }