Example #1
0
        private static async Task <ClassifiedSpan[]> GetClassifiedSpansForDocumentAsync(
            Document document,
            TextSpan textSpan,
            ClassificationOptions options,
            bool includeSyntacticClassifications,
            CancellationToken cancellationToken)
        {
            var classificationService = document.GetRequiredLanguageService <IClassificationService>();

            using var _ = ArrayBuilder <ClassifiedSpan> .GetInstance(out var classifiedSpans);

            // Case 1 - Generated Razor documents:
            //     In Razor, the C# syntax classifier does not run on the client. This means we need to return both
            //     syntactic and semantic classifications.
            // Case 2 - C# and VB documents:
            //     In C#/VB, the syntax classifier runs on the client. This means we only need to return semantic
            //     classifications.
            //
            // Ideally, Razor will eventually run the classifier on their end so we can get rid of this special
            // casing: https://github.com/dotnet/razor-tooling/issues/5850
            if (includeSyntacticClassifications)
            {
                // `removeAdditiveSpans` will remove token modifiers such as 'static', which we want to include in LSP.
                // `fillInClassifiedSpanGaps` includes whitespace in the results, which we don't care about in LSP.
                // Therefore, we set both optional parameters to false.
                var spans = await ClassifierHelper.GetClassifiedSpansAsync(
                    document, textSpan, options, cancellationToken, removeAdditiveSpans : false, fillInClassifiedSpanGaps : false).ConfigureAwait(false);

                // The spans returned to us may include some empty spans, which we don't care about.
                var nonEmptySpans = spans.Where(s => !s.TextSpan.IsEmpty);
                classifiedSpans.AddRange(nonEmptySpans);
            }
            else
            {
                await classificationService.AddSemanticClassificationsAsync(
                    document, textSpan, options, classifiedSpans, cancellationToken).ConfigureAwait(false);

                await classificationService.AddEmbeddedLanguageClassificationsAsync(
                    document, textSpan, options, classifiedSpans, cancellationToken).ConfigureAwait(false);
            }

            // Classified spans are not guaranteed to be returned in a certain order so we sort them to be safe.
            classifiedSpans.Sort(ClassifiedSpanComparer.Instance);
            return(classifiedSpans.ToArray());
        }
        internal static async Task <IntellisenseQuickInfoItem> BuildItemAsync(ITrackingSpan trackingSpan,
                                                                              CodeAnalysisQuickInfoItem quickInfoItem,
                                                                              ITextSnapshot snapshot,
                                                                              Document document,
                                                                              Lazy <IStreamingFindUsagesPresenter> streamingPresenter,
                                                                              CancellationToken cancellationToken)
        {
            // Build the first line of QuickInfo item, the images and the Description section should be on the first line with Wrapped style
            var glyphs            = quickInfoItem.Tags.GetGlyphs();
            var symbolGlyph       = glyphs.FirstOrDefault(g => g != Glyph.CompletionWarning);
            var warningGlyph      = glyphs.FirstOrDefault(g => g == Glyph.CompletionWarning);
            var firstLineElements = new List <object>();

            if (symbolGlyph != Glyph.None)
            {
                firstLineElements.Add(new ImageElement(symbolGlyph.GetImageId()));
            }

            if (warningGlyph != Glyph.None)
            {
                firstLineElements.Add(new ImageElement(warningGlyph.GetImageId()));
            }

            var elements    = new List <object>();
            var descSection = quickInfoItem.Sections.FirstOrDefault(s => s.Kind == QuickInfoSectionKinds.Description);

            if (descSection != null)
            {
                var isFirstElement = true;
                foreach (var element in Helpers.BuildInteractiveTextElements(descSection.TaggedParts, document, streamingPresenter))
                {
                    if (isFirstElement)
                    {
                        isFirstElement = false;
                        firstLineElements.Add(element);
                    }
                    else
                    {
                        // If the description section contains multiple paragraphs, the second and additional paragraphs
                        // are not wrapped in firstLineElements (they are normal paragraphs).
                        elements.Add(element);
                    }
                }
            }

            elements.Insert(0, new ContainerElement(ContainerElementStyle.Wrapped, firstLineElements));

            var documentationCommentSection = quickInfoItem.Sections.FirstOrDefault(s => s.Kind == QuickInfoSectionKinds.DocumentationComments);

            if (documentationCommentSection != null)
            {
                var isFirstElement = true;
                foreach (var element in Helpers.BuildInteractiveTextElements(documentationCommentSection.TaggedParts, document, streamingPresenter))
                {
                    if (isFirstElement)
                    {
                        isFirstElement = false;

                        // Stack the first paragraph of the documentation comments with the last line of the description
                        // to avoid vertical padding between the two.
                        var lastElement = elements[elements.Count - 1];
                        elements[elements.Count - 1] = new ContainerElement(
                            ContainerElementStyle.Stacked,
                            lastElement,
                            element);
                    }
                    else
                    {
                        elements.Add(element);
                    }
                }
            }

            // Add the remaining sections as Stacked style
            elements.AddRange(
                quickInfoItem.Sections.Where(s => s.Kind != QuickInfoSectionKinds.Description && s.Kind != QuickInfoSectionKinds.DocumentationComments)
                .SelectMany(s => Helpers.BuildInteractiveTextElements(s.TaggedParts, document, streamingPresenter)));

            // build text for RelatedSpan
            if (quickInfoItem.RelatedSpans.Any())
            {
                var classifiedSpanList = new List <ClassifiedSpan>();
                foreach (var span in quickInfoItem.RelatedSpans)
                {
                    var classifiedSpans = await ClassifierHelper.GetClassifiedSpansAsync(document, span, cancellationToken).ConfigureAwait(false);

                    classifiedSpanList.AddRange(classifiedSpans);
                }

                var tabSize = document.Project.Solution.Workspace.Options.GetOption(FormattingOptions.TabSize, document.Project.Language);
                var text    = await document.GetTextAsync().ConfigureAwait(false);

                var spans    = IndentationHelper.GetSpansWithAlignedIndentation(text, classifiedSpanList.ToImmutableArray(), tabSize);
                var textRuns = spans.Select(s => new ClassifiedTextRun(s.ClassificationType, snapshot.GetText(s.TextSpan.ToSpan()), ClassifiedTextRunStyle.UseClassificationFont));

                if (textRuns.Any())
                {
                    elements.Add(new ClassifiedTextElement(textRuns));
                }
            }

            var content = new ContainerElement(
                ContainerElementStyle.Stacked | ContainerElementStyle.VerticalPadding,
                elements);

            return(new IntellisenseQuickInfoItem(trackingSpan, content));
        }
Example #3
0
        private static async Task <ContainerElement> BuildInteractiveContentAsync(CodeAnalysisQuickInfoItem quickInfoItem,
                                                                                  IntellisenseQuickInfoBuilderContext?context,
                                                                                  CancellationToken cancellationToken)
        {
            // Build the first line of QuickInfo item, the images and the Description section should be on the first line with Wrapped style
            var glyphs            = quickInfoItem.Tags.GetGlyphs();
            var symbolGlyph       = glyphs.FirstOrDefault(g => g != Glyph.CompletionWarning);
            var warningGlyph      = glyphs.FirstOrDefault(g => g == Glyph.CompletionWarning);
            var firstLineElements = new List <object>();

            if (symbolGlyph != Glyph.None)
            {
                firstLineElements.Add(new ImageElement(symbolGlyph.GetImageId()));
            }

            if (warningGlyph != Glyph.None)
            {
                firstLineElements.Add(new ImageElement(warningGlyph.GetImageId()));
            }

            var elements    = new List <object>();
            var descSection = quickInfoItem.Sections.FirstOrDefault(s => s.Kind == QuickInfoSectionKinds.Description);

            if (descSection != null)
            {
                var isFirstElement = true;
                foreach (var element in Helpers.BuildInteractiveTextElements(descSection.TaggedParts, context))
                {
                    if (isFirstElement)
                    {
                        isFirstElement = false;
                        firstLineElements.Add(element);
                    }
                    else
                    {
                        // If the description section contains multiple paragraphs, the second and additional paragraphs
                        // are not wrapped in firstLineElements (they are normal paragraphs).
                        elements.Add(element);
                    }
                }
            }

            elements.Insert(0, new ContainerElement(ContainerElementStyle.Wrapped, firstLineElements));

            var documentationCommentSection = quickInfoItem.Sections.FirstOrDefault(s => s.Kind == QuickInfoSectionKinds.DocumentationComments);

            if (documentationCommentSection != null)
            {
                var isFirstElement = true;
                foreach (var element in Helpers.BuildInteractiveTextElements(documentationCommentSection.TaggedParts, context))
                {
                    if (isFirstElement)
                    {
                        isFirstElement = false;

                        // Stack the first paragraph of the documentation comments with the last line of the description
                        // to avoid vertical padding between the two.
                        var lastElement = elements[elements.Count - 1];
                        elements[elements.Count - 1] = new ContainerElement(
                            ContainerElementStyle.Stacked,
                            lastElement,
                            element);
                    }
                    else
                    {
                        elements.Add(element);
                    }
                }
            }

            // Add the remaining sections as Stacked style
            elements.AddRange(
                quickInfoItem.Sections.Where(s => s.Kind is not QuickInfoSectionKinds.Description and not QuickInfoSectionKinds.DocumentationComments)
                .SelectMany(s => Helpers.BuildInteractiveTextElements(s.TaggedParts, context)));

            // build text for RelatedSpan
            if (quickInfoItem.RelatedSpans.Any() && context?.Document is Document document)
            {
                var textRuns = new List <ClassifiedTextRun>();
                var spanSeparatorNeededBefore = false;
                foreach (var span in quickInfoItem.RelatedSpans)
                {
                    var classifiedSpans = await ClassifierHelper.GetClassifiedSpansAsync(document, span, cancellationToken).ConfigureAwait(false);

                    var tabSize = document.Project.Solution.Options.GetOption(FormattingOptions.TabSize, document.Project.Language);
                    var text    = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

                    var spans          = IndentationHelper.GetSpansWithAlignedIndentation(text, classifiedSpans.ToImmutableArray(), tabSize);
                    var textRunsOfSpan = spans.Select(s => new ClassifiedTextRun(s.ClassificationType, text.GetSubText(s.TextSpan).ToString(), ClassifiedTextRunStyle.UseClassificationFont)).ToList();
                    if (textRunsOfSpan.Count > 0)
                    {
                        if (spanSeparatorNeededBefore)
                        {
                            textRuns.Add(new ClassifiedTextRun(ClassificationTypeNames.WhiteSpace, "\r\n", ClassifiedTextRunStyle.UseClassificationFont));
                        }

                        textRuns.AddRange(textRunsOfSpan);
                        spanSeparatorNeededBefore = true;
                    }
                }

                if (textRuns.Any())
                {
                    elements.Add(new ClassifiedTextElement(textRuns));
                }
            }

            return(new ContainerElement(
                       ContainerElementStyle.Stacked | ContainerElementStyle.VerticalPadding,
                       elements));
        }