Esempio n. 1
0
        public Task <QuickInfoItem> GetQuickInfoItemAsync(IAsyncQuickInfoSession session, CancellationToken cancellationToken)
        {
            QuickInfoItem quickInfoItem = null;

            if (session == null)
            {
                throw new ArgumentNullException("session");
            }

            TemplateAnalysis analysis     = this.analyzer.CurrentAnalysis;
            SnapshotPoint?   triggerPoint = session.GetTriggerPoint(analysis.TextSnapshot);

            if (triggerPoint != null && analysis.Template != null)
            {
                string description;
                Span   applicableTo;
                if (analysis.Template.TryGetDescription(triggerPoint.Value.Position, out description, out applicableTo))
                {
                    ITrackingSpan applicableToSpan = analysis.TextSnapshot.CreateTrackingSpan(applicableTo, SpanTrackingMode.EdgeExclusive);
                    quickInfoItem = new QuickInfoItem(applicableToSpan, description);
                }
            }

            return(Task.FromResult(quickInfoItem));
        }
            public void PresentItem(ITrackingSpan triggerSpan, QuickInfoItem item, bool trackMouse)
            {
                AssertIsForeground();

                _triggerSpan = triggerSpan;
                _item        = item;

                // It's a new list of items.  Either create the editor session if this is the first time, or ask the
                // editor session that we already have to recalculate.
                if (_editorSessionOpt == null || _editorSessionOpt.IsDismissed)
                {
                    // We're tracking the caret.  Don't have the editor do it.
                    var triggerPoint = triggerSpan.GetStartTrackingPoint(PointTrackingMode.Negative);

                    _editorSessionOpt            = _quickInfoBroker.CreateQuickInfoSession(_textView, triggerPoint, trackMouse: trackMouse);
                    _editorSessionOpt.Dismissed += (s, e) => OnEditorSessionDismissed();
                }

                // So here's the deal.  We cannot create the editor session and give it the right
                // signatures (even though we know what they are).  Instead, the session with
                // call back into the ISignatureHelpSourceProvider (which is us) to get those
                // values. It will pass itself along with the calls back into
                // ISignatureHelpSourceProvider. So, in order to make that connection work, we
                // add properties to the session so that we can call back into ourselves, get
                // the signatures and add it to the session.
                if (!_editorSessionOpt.Properties.ContainsProperty(s_augmentSessionKey))
                {
                    _editorSessionOpt.Properties.AddProperty(s_augmentSessionKey, this);
                }

                _editorSessionOpt.Recalculate();
            }
Esempio n. 3
0
        internal override void OnModelUpdated(Model modelOpt)
        {
            AssertIsForeground();
            if (modelOpt == null || modelOpt.TextVersion != this.SubjectBuffer.CurrentSnapshot.Version)
            {
                this.StopModelComputation();
            }
            else
            {
                // We want the span to actually only go up to the caret.  So get the expected span
                // and then update its end point accordingly.
                ITrackingSpan trackingSpan;
                QuickInfoItem item = null;

                // Whether or not we have an item to show, we need to start the session.
                // If the user Edit.QuickInfo's on a squiggle, they want to see the
                // error text even if there's no symbol quickinfo.
                if (modelOpt.Item != null)
                {
                    item = modelOpt.Item;
                    var triggerSpan = modelOpt.GetCurrentSpanInSnapshot(item.TextSpan, this.SubjectBuffer.CurrentSnapshot);
                    trackingSpan = triggerSpan.CreateTrackingSpan(SpanTrackingMode.EdgeInclusive);
                }
                else
                {
                    var caret = this.TextView.GetCaretPoint(this.SubjectBuffer).Value;
                    trackingSpan = caret.Snapshot.CreateTrackingSpan(caret.Position, 0, SpanTrackingMode.EdgeInclusive, TrackingFidelityMode.Forward);
                }
                sessionOpt.PresenterSession.PresentItem(trackingSpan, item, modelOpt.TrackMouse);
            }
        }
Esempio n. 4
0
            static async Task <Hover> GetHoverAsync(QuickInfoItem info, Document document, ClientCapabilities clientCapabilities, CancellationToken cancellationToken)
            {
                var supportsVSExtensions = clientCapabilities.HasVisualStudioLspCapability();

                var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

                if (supportsVSExtensions)
                {
                    return(new VSHover
                    {
                        Range = ProtocolConversions.TextSpanToRange(info.Span, text),
                        Contents = new SumType <SumType <string, MarkedString>, SumType <string, MarkedString>[], MarkupContent>(string.Empty),
                        // Build the classified text without navigation actions - they are not serializable.
                        // TODO - Switch to markup content once it supports classifications.
                        // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/918138
                        RawContent = await IntellisenseQuickInfoBuilder.BuildContentWithoutNavigationActionsAsync(info, document, cancellationToken).ConfigureAwait(false)
                    });
                }
                else
                {
                    return(new Hover
                    {
                        Range = ProtocolConversions.TextSpanToRange(info.Span, text),
                        Contents = GetContents(info, document, clientCapabilities),
                    });
                }
            }
            public void PresentItem(ITrackingSpan triggerSpan, QuickInfoItem item, bool trackMouse)
            {
                AssertIsForeground();

                _triggerSpan = triggerSpan;
                _item = item;

                // It's a new list of items.  Either create the editor session if this is the first time, or ask the
                // editor session that we already have to recalculate.
                if (_editorSessionOpt == null || _editorSessionOpt.IsDismissed)
                {
                    // We're tracking the caret.  Don't have the editor do it.
                    var triggerPoint = triggerSpan.GetStartTrackingPoint(PointTrackingMode.Negative);

                    _editorSessionOpt = _quickInfoBroker.CreateQuickInfoSession(_textView, triggerPoint, trackMouse: trackMouse);
                    _editorSessionOpt.Dismissed += (s, e) => OnEditorSessionDismissed();
                }

                // So here's the deal.  We cannot create the editor session and give it the right
                // signatures (even though we know what they are).  Instead, the session with
                // call back into the ISignatureHelpSourceProvider (which is us) to get those
                // values. It will pass itself along with the calls back into
                // ISignatureHelpSourceProvider. So, in order to make that connection work, we
                // add properties to the session so that we can call back into ourselves, get
                // the signatures and add it to the session.
                if (!_editorSessionOpt.Properties.ContainsProperty(s_augmentSessionKey))
                {
                    _editorSessionOpt.Properties.AddProperty(s_augmentSessionKey, this);
                }

                _editorSessionOpt.Recalculate();
            }
Esempio n. 6
0
 private static bool IsNullOrEmpty(QuickInfoItem info)
 {
     // Note that Sections.IsEmpty doesn't mean there is nothing
     // E.g. closing bracket `}` will have related open bracket
     // code in related spans. However this isn't supported yet.
     return(info == null || info.Sections.IsEmpty);
 }
Esempio n. 7
0
        private static QuickInfoItem?BuildQuickInfoDirectives(SyntaxToken token, CancellationToken cancellationToken)
        {
            if (token.Parent is DirectiveTriviaSyntax directiveTrivia)
            {
                if (directiveTrivia is EndRegionDirectiveTriviaSyntax)
                {
                    var regionStart = directiveTrivia.GetMatchingDirective(cancellationToken);
                    if (regionStart is not null)
                    {
                        return(QuickInfoItem.Create(token.Span, relatedSpans: ImmutableArray.Create(regionStart.Span)));
                    }
                }
                else if (directiveTrivia is ElifDirectiveTriviaSyntax or ElseDirectiveTriviaSyntax or EndIfDirectiveTriviaSyntax)
                {
                    var matchingDirectives = directiveTrivia.GetMatchingConditionalDirectives(cancellationToken);
                    var matchesBefore      = matchingDirectives
                                             .TakeWhile(d => d.SpanStart < directiveTrivia.SpanStart)
                                             .Select(d => d.Span)
                                             .ToImmutableArray();
                    if (matchesBefore.Length > 0)
                    {
                        return(QuickInfoItem.Create(token.Span, relatedSpans: matchesBefore));
                    }
                }
            }

            return(null);
        }
Esempio n. 8
0
        protected override async Task <QuickInfoItem?> BuildQuickInfoAsync(
            Document document,
            SyntaxToken token,
            CancellationToken cancellationToken
            )
        {
            if (token.Kind() != SyntaxKind.CloseBraceToken)
            {
                return(null);
            }

            // Don't show for interpolations
            if (
                token.Parent.IsKind(
                    SyntaxKind.Interpolation,
                    out InterpolationSyntax? interpolation
                    ) &&
                interpolation.CloseBraceToken == token
                )
            {
                return(null);
            }

            // Now check if we can find an open brace.
            var parent    = token.Parent !;
            var openBrace = parent
                            .ChildNodesAndTokens()
                            .FirstOrDefault(n => n.Kind() == SyntaxKind.OpenBraceToken)
                            .AsToken();

            if (openBrace.Kind() != SyntaxKind.OpenBraceToken)
            {
                return(null);
            }

            var spanStart = parent.SpanStart;
            var spanEnd   = openBrace.Span.End;

            // If the parent is a scope block, check and include nearby comments around the open brace
            // LeadingTrivia is preferred
            if (IsScopeBlock(parent))
            {
                MarkInterestedSpanNearbyScopeBlock(parent, openBrace, ref spanStart, ref spanEnd);
            }
            // If the parent is a child of a property/method declaration, object/array creation, or control flow node,
            // then walk up one higher so we can show more useful context
            else if (parent.GetFirstToken() == openBrace)
            {
                // parent.Parent must be non-null, because for GetFirstToken() to have returned something it would have had to walk up to its parent
                spanStart = parent.Parent !.SpanStart;
            }

            // encode document spans that correspond to the text to show
            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            var spans = ImmutableArray.Create(TextSpan.FromBounds(spanStart, spanEnd));

            return(QuickInfoItem.Create(token.Span, relatedSpans: spans));
        }
Esempio n. 9
0
        public async Task <QuickInfoItem> GetQuickInfoItemAsync(IAsyncQuickInfoSession session, CancellationToken cancellationToken)
        {
            await Task.Yield().ConfigureAwait(false);

            if (cancellationToken.IsCancellationRequested)
            {
                return(null);
            }

            var syntaxTreeAndSnapshot = ParserService.SyntaxTreeAndSnapshot;

            if (syntaxTreeAndSnapshot == null)
            {
                return(null);
            }

            // Map the trigger point down to our buffer.
            SnapshotPoint?triggerPoint = session.GetTriggerPoint(syntaxTreeAndSnapshot.Snapshot);

            if (triggerPoint == null)
            {
                return(null);
            }

            var triggerToken = syntaxTreeAndSnapshot.SyntaxTree.Root.FindToken(triggerPoint.Value.Position);

            if (triggerToken.IsMissing || triggerToken.Parent == null)
            {
                return(null);
            }

            var applicableToSpan = syntaxTreeAndSnapshot.Snapshot.CreateTrackingSpan(
                triggerToken.Extent.Start,
                triggerToken.Extent.Length,
                SpanTrackingMode.EdgeExclusive);

            var location  = triggerToken.GetLocation();
            var qiContent = $"{triggerToken.GetText()}\r\n{triggerToken.Kind} Ln {location?.StartLine + 1} Ch {location?.StartCharacter + 1}\r\n{triggerToken.Parent?.GetType().Name}";

            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            var modifier = ModifierKeys.Control | ModifierKeys.Shift;

            if ((Keyboard.Modifiers & modifier) != modifier)
            {
                return(null);
            }

            var controlControl = new SymbolQuickInfoControl {
                CrispImage  = { Moniker = ImageMonikers.StatusInformation },
                TextContent = { Content = qiContent }
            };

            var qiItem = new QuickInfoItem(applicableToSpan: applicableToSpan,
                                           item: controlControl
                                           );

            return(qiItem);
        }
Esempio n. 10
0
        public Model(ITextVersion textVersion, QuickInfoItem item, bool trackMouse)
        {
            Contract.ThrowIfNull(item);

            this.TextVersion = textVersion;
            this.Item        = item;
            this.TrackMouse  = trackMouse;
        }
Esempio n. 11
0
            static MarkupContent GetContents(QuickInfoItem info, Document document, ClientCapabilities clientCapabilities)
            {
                var clientSupportsMarkdown = clientCapabilities?.TextDocument?.Hover?.ContentFormat.Contains(MarkupKind.Markdown) == true;
                // Insert line breaks in between sections to ensure we get double spacing between sections.
                var tags = info.Sections
                           .SelectMany(section => section.TaggedParts.Add(new TaggedText(TextTags.LineBreak, Environment.NewLine)))
                           .ToImmutableArray();

                return(ProtocolConversions.GetDocumentationMarkupContent(tags, document, clientSupportsMarkdown));
            }
Esempio n. 12
0
 public Model(
     ITextVersion textVersion,
     QuickInfoItem item,
     IQuickInfoProvider provider,
     bool trackMouse)
 {
     this.TextVersion = textVersion;
     this.Item        = item;
     this.Provider    = provider;
     this.TrackMouse  = trackMouse;
 }
Esempio n. 13
0
        public Model(
            ITextVersion textVersion,
            QuickInfoItem item,
            IQuickInfoProvider provider,
            bool trackMouse)
        {
            Contract.ThrowIfNull(item);

            this.TextVersion = textVersion;
            this.Item = item;
            this.Provider = provider;
            this.TrackMouse = trackMouse;
        }
Esempio n. 14
0
        public async Task <QuickInfoItem> GetQuickInfoItemAsync(IAsyncQuickInfoSession session, CancellationToken cancellationToken)
        {
            await Task.Yield().ConfigureAwait(false);

            if (cancellationToken.IsCancellationRequested)
            {
                return(null);
            }

            var syntaxTreeAndSnapshot = ParserService.SyntaxTreeAndSnapshot;

            if (syntaxTreeAndSnapshot == null)
            {
                return(null);
            }

            // Map the trigger point down to our buffer.
            SnapshotPoint?triggerPoint = session.GetTriggerPoint(syntaxTreeAndSnapshot.Snapshot);

            if (triggerPoint == null)
            {
                return(null);
            }

            var qiInfo = QuickInfoProvider.GetQuickInfoDefinition(syntaxTreeAndSnapshot.SyntaxTree, triggerPoint.Value.Position);

            if (qiInfo == null)
            {
                return(null);
            }

            var applicableToSpan = syntaxTreeAndSnapshot.Snapshot.CreateTrackingSpan(
                qiInfo.ApplicableToExtent.Start,
                qiInfo.ApplicableToExtent.Length,
                SpanTrackingMode.EdgeExclusive);

            await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();

            var qiContent = _textBlockBuilderService.ToTextBlock(qiInfo.Content);

            var controlControl = new SymbolQuickInfoControl {
                CrispImage  = { Moniker = GdImageMonikers.GetMoniker(qiInfo.Glyph) },
                TextContent = { Content = qiContent }
            };

            var qiItem = new QuickInfoItem(applicableToSpan: applicableToSpan,
                                           item: controlControl
                                           );

            return(qiItem);
        }
Esempio n. 15
0
		public IEnumerable<object> Create(QuickInfoItem item) {
			if (item == null)
				throw new ArgumentNullException(nameof(item));

			switch (item.Content.Type) {
			case PredefinedQuickInfoContentTypes.Information:
				return Create((InformationQuickInfoContent)item.Content);

			case PredefinedQuickInfoContentTypes.CodeSpan:
				return Create((CodeSpanQuickInfoContent)item.Content);

			default:
				Debug.Fail($"Unknown QuickInfo content: {item.Content.Type}");
				return Array.Empty<object>();
			}
		}
Esempio n. 16
0
            // local functions
            // TODO - This should return correctly formatted markdown from quick info.
            // https://github.com/dotnet/roslyn/projects/45#card-20033878
            static string GetMarkdownString(QuickInfoItem info)
            {
                var stringBuilder = new StringBuilder();
                var description   = info.Sections.FirstOrDefault(s => QuickInfoSectionKinds.Description.Equals(s.Kind))?.Text ?? string.Empty;
                var documentation = info.Sections.FirstOrDefault(s => QuickInfoSectionKinds.DocumentationComments.Equals(s.Kind))?.Text ?? string.Empty;

                if (!string.IsNullOrEmpty(description))
                {
                    stringBuilder.Append(description);
                    if (!string.IsNullOrEmpty(documentation))
                    {
                        stringBuilder.Append("\r\n> ").Append(documentation);
                    }
                }

                return(stringBuilder.ToString());
            }
Esempio n. 17
0
        private static async Task <Hover> GetHoverAsync(
            QuickInfoItem info,
            SourceText text,
            string language,
            Document?document,
            ClassificationOptions?classificationOptions,
            ClientCapabilities?clientCapabilities,
            CancellationToken cancellationToken)
        {
            Contract.ThrowIfFalse(document is null == (classificationOptions == null));

            var supportsVSExtensions = clientCapabilities.HasVisualStudioLspCapability();

            if (supportsVSExtensions)
            {
                var context = document == null
                    ? null
                    : new IntellisenseQuickInfoBuilderContext(
                    document,
                    classificationOptions !.Value,
                    threadingContext: null,
                    operationExecutor: null,
                    asynchronousOperationListener: null,
                    streamingPresenter: null);
                return(new VSInternalHover
                {
                    Range = ProtocolConversions.TextSpanToRange(info.Span, text),
                    Contents = new SumType <SumType <string, MarkedString>, SumType <string, MarkedString>[], MarkupContent>(string.Empty),
                    // Build the classified text without navigation actions - they are not serializable.
                    // TODO - Switch to markup content once it supports classifications.
                    // https://devdiv.visualstudio.com/DevDiv/_workitems/edit/918138
                    RawContent = await IntellisenseQuickInfoBuilder.BuildContentWithoutNavigationActionsAsync(info, context, cancellationToken).ConfigureAwait(false)
                });
            }
            else
            {
                return(new Hover
                {
                    Range = ProtocolConversions.TextSpanToRange(info.Span, text),
                    Contents = GetContents(info, language, clientCapabilities),
                });
            }
Esempio n. 18
0
        public IEnumerable <object> Create(QuickInfoItem item)
        {
            if (item is null)
            {
                throw new ArgumentNullException(nameof(item));
            }

            switch (item.Content.Type)
            {
            case PredefinedQuickInfoContentTypes.Information:
                return(Create((InformationQuickInfoContent)item.Content));

            case PredefinedQuickInfoContentTypes.CodeSpan:
                return(Create((CodeSpanQuickInfoContent)item.Content));

            default:
                Debug.Fail($"Unknown QuickInfo content: {item.Content.Type}");
                return(Array.Empty <object>());
            }
        }
        private static QuickInfoItem CreateQuickInfo(
            TextSpan location,
            DiagnosticDescriptor descriptor,
            params TextSpan[] relatedSpans
            )
        {
            var description =
                descriptor.Title.ToStringOrNull()
                ?? descriptor.Description.ToStringOrNull()
                ?? descriptor.MessageFormat.ToStringOrNull()
                ?? descriptor.Id;
            var idTag = !string.IsNullOrWhiteSpace(descriptor.HelpLinkUri)
                ? new TaggedText(
                TextTags.Text,
                descriptor.Id,
                TaggedTextStyle.None,
                descriptor.HelpLinkUri,
                descriptor.HelpLinkUri
                )
                : new TaggedText(TextTags.Text, descriptor.Id);

            return(QuickInfoItem.Create(
                       location,
                       sections: new[]
            {
                QuickInfoSection.Create(
                    QuickInfoSectionKinds.Description,
                    new[]
                {
                    idTag,
                    new TaggedText(TextTags.Punctuation, ":"),
                    new TaggedText(TextTags.Space, " "),
                    new TaggedText(TextTags.Text, description)
                }.ToImmutableArray()
                    )
            }.ToImmutableArray(),
                       relatedSpans: relatedSpans.ToImmutableArray()
                       ));
        }
Esempio n. 20
0
        private Task SendInfoTipAsync(QuickInfoItem info, ICommandResultSender sender, CancellationToken cancellationToken)
        {
            var writer = sender.StartJsonMessage("infotip");

            if (IsNullOrEmpty(info))
            {
                return(sender.SendJsonMessageAsync(cancellationToken));
            }

            writer.WriteTagsProperty("kinds", info.Tags);
            writer.WritePropertyStartArray("sections");
            foreach (var section in info.Sections)
            {
                writer.WriteStartObject();
                writer.WriteProperty("kind", FastConvert.StringToLowerInvariantString(section.Kind));
                writer.WritePropertyStartArray("parts");
                writer.WriteTaggedTexts(section.TaggedParts);
                writer.WriteEndArray();
                writer.WriteEndObject();
            }
            writer.WriteEndArray();
            writer.WriteSpanProperty("span", info.Span);
            return(sender.SendJsonMessageAsync(cancellationToken));
        }
Esempio n. 21
0
 static string GetMarkdownString(QuickInfoItem info)
 => string.Join("\r\n", info.Sections.Select(section => section.Text).Where(text => !string.IsNullOrEmpty(text)));