Пример #1
0
        public void AsyncAwaitClassification()
        {
            var code = @"
await f
await + f
async with f: pass
async for x in f: pass

async def f():
    await f
    async with f: pass
    async for x in f: pass

class F:
    async def f(self): [x async for x in y]

@F
async def f():
    pass
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V36)) {
                helper.CheckAstClassifierSpans("ii i+i iki:k ikiki:k iki(): ii iki:k ikiki:k ki: " +
                                               "iki(i): (iikiki) @iiki():k");
                // "await f" does not highlight "f", but "await + f" does
                // also note that only async and await are keywords here - not 'for'
                helper.CheckAnalysisClassifierSpans("fff k<async>f k<await>f k<async>f k<async>f c<F> " +
                                                    "k<async>fp<self>p<x>k<async>p c<F>k<async>f");
            }
        }
Пример #2
0
        public void KeywordClassification27()
        {
            var code = string.Join(Environment.NewLine, PythonKeywords.All(PythonLanguageVersion.V27));

            code += "\r\nTrue\r\nFalse";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                foreach (var span in helper.AstClassifierSpans)
                {
                    var text = span.Span.GetText();
                    if (string.IsNullOrWhiteSpace(text))
                    {
                        continue;
                    }

                    // None, True and False are special
                    if (text == "None" || text == "True" || text == "False")
                    {
                        Assert.AreEqual("Python builtin", span.ClassificationType.Classification, text);
                        continue;
                    }

                    Assert.AreEqual("keyword", span.ClassificationType.Classification, text);
                }
            }
        }
Пример #3
0
        public void AsyncAwaitClassification()
        {
            var code = @"
await f
await + f
async with f: pass
async for x in f: pass

async def f():
    await f
    async with f: pass
    async for x in f: pass

class F:
    async def f(self): pass

@F
async def f():
    pass
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V35)) {
                helper.CheckAstClassifierSpans("ii i+i iki:k ikiki:k iki(): ii iki:k ikiki:k ki: iki(i): k @iiki():k");
                // "await f" does not highlight "f", but "await + f" does
                helper.CheckAnalysisClassifierSpans("fff k<async>f k<await>f k<async>f k<async>f c<F> k<async>fp c<F>k<async>f");
            }
        }
Пример #4
0
        public void ReturnAnnotationClassifications()
        {
            var code = @"
def f() -> int:
    pass
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V35)) {
                helper.CheckAnalysisClassifierSpans("f<f>c<int>");
            }
        }
Пример #5
0
        public void MultilineStringClassification()
        {
            var code = @"t = '''a

b ''' + c + 'x' + '''d

e'''";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("i=sss+i+s+sss");
            }
        }
Пример #6
0
        public void TrueFalseClassification()
        {
            var code = "True False";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("b<True> b<False>");
            }

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V33)) {
                helper.CheckAstClassifierSpans("k<True> k<False>");
            }
        }
Пример #7
0
        public void DocStringClassifications()
        {
            var code = @"def f():
    '''doc string'''
    '''not a doc string'''
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V35)) {
                helper.CheckAstClassifierSpans("ki():ss");
                helper.CheckAnalysisClassifierSpans("f<f>d<'''doc string'''>");
            }
        }
Пример #8
0
        public void ParameterAnnotationClassification()
        {
            var code = @"class A: pass
class B: pass

def f(a = A, b : B):
    pass
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("ki:k ki:k ki(i=i,i:i): k");
                helper.CheckAnalysisClassifierSpans("c<A>c<B>f<f>pc<A>pc<B>");
            }
        }
Пример #9
0
        public void ImportClassifications()
        {
            var code = @"import abc as x
from os import fdopen

abc
x
os
fdopen
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("kiki kiki i i i i");
                helper.CheckAnalysisClassifierSpans("m<abc>m<x>m<os>m<x>");
            }
        }
Пример #10
0
        public void TypeClassification()
        {
            var code = @"class MyClass(object):
    pass

mc = MyClass()
MyClassAlias = MyClass
mca = MyClassAlias()
MyClassType = type(mc)
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("ki(i): k i=i() i=i i=i() i=i(i)");
                helper.CheckAnalysisClassifierSpans("c<MyClass>c<object>cc<MyClassAlias>ccc<type>");
            }
        }
Пример #11
0
        public void ModuleClassification()
        {
            var code = @"import abc
import os
import ntpath

os.path = ntpath
abc = 123
abc = True
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("ki ki ki i.i=i i=n i=b");
                helper.CheckAnalysisClassifierSpans("m<abc>m<os>m<ntpath>m<os>m<ntpath>m<abc>m<abc>");
            }
        }
Пример #12
0
        public void ParameterClassification()
        {
            var code = @"def f(a, b, c):
    a = b
    b = c
    return a

f(a, b, c)
a = b
b = c
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("ki(i,i,i): i=i i=i ki i(i,i,i) i=i i=i");
                helper.CheckAnalysisClassifierSpans("f<f>ppppppppf<f>");
            }
        }
Пример #13
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());
        }
Пример #14
0
        public void ImportClassifications()
        {
            // We import a name that is not in os, because otherwise
            // its classification may depend on the current DB for the
            // module.
            var code = @"import abc as x
from os import name_not_in_os

abc
x
os
name_not_in_os
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("kiki kiki i i i i");
                helper.CheckAnalysisClassifierSpans("m<abc>m<x>m<os>m<x>");
            }
        }
Пример #15
0
        public void KeywordClassification33() {
            var code = string.Join(Environment.NewLine, PythonKeywords.All(PythonLanguageVersion.V33));

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V33)) {
                foreach (var span in helper.AstClassifierSpans) {
                    var text = span.Span.GetText();
                    if (string.IsNullOrWhiteSpace(text)) {
                        continue;
                    }

                    // None is special
                    if (text == "None") {
                        Assert.AreEqual("Python builtin", span.ClassificationType.Classification, text);
                        continue;
                    }

                    Assert.AreEqual("keyword", span.ClassificationType.Classification, text);
                }
            }
        }
Пример #16
0
        public void RegexClassifications()
        {
            var code = @"import re as R
R.compile('pattern', 'str')
R.escape('pattern', 'str')
R.findall('pattern', 'str')
R.finditer('pattern', 'str')
R.fullmatch('pattern', 'str')
R.match('pattern', 'str')
R.search('pattern', 'str')
R.split('pattern', 'str')
R.sub('pattern', 'str')
R.subn('pattern', 'str')
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V35)) {
                helper.CheckAstClassifierSpans("kiki " + string.Join(" ", Enumerable.Repeat("i.i(s,s)", 10)));
                helper.CheckAnalysisClassifierSpans("m<re>m<R> " + string.Join(" ", Enumerable.Repeat("m<R>fr", 10)));
            }
        }
        public void KeywordClassification33()
        {
            var code = string.Join(Environment.NewLine, PythonKeywords.All(PythonLanguageVersion.V33));

            using (var helper = new ClassifierHelper(MockTextBuffer(code), PythonLanguageVersion.V33)) {
                foreach (var span in helper.AstClassifierSpans)
                {
                    var text = span.Span.GetText();
                    if (string.IsNullOrWhiteSpace(text))
                    {
                        continue;
                    }

                    // None is special
                    if (text == "None")
                    {
                        Assert.AreEqual("Python builtin", span.ClassificationType.Classification, text);
                        continue;
                    }

                    Assert.AreEqual("keyword", span.ClassificationType.Classification, text);
                }
            }
        }
Пример #18
0
        public void ParameterAnnotationClassification() {
            var code = @"class A: pass
class B: pass

def f(a = A, b : B):
    pass
";
            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("ki:k ki:k ki(i=i,i:i): k");
                helper.CheckAnalysisClassifierSpans("c<A>c<B>f<f>pc<A>pc<B>");
            }
        }
Пример #19
0
        public void ImportClassifications() {
            var code = @"import abc as x
from os import fdopen

abc
x
os
fdopen
";
            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("kiki kiki i i i i");
                helper.CheckAnalysisClassifierSpans("m<abc>m<x>m<os>m<x>");
            }
        }
Пример #20
0
        public void AsyncAwaitClassification() {
            var code = @"
await f
await + f
async with f: pass
async for x in f: pass

async def f():
    await f
    async with f: pass
    async for x in f: pass

class F:
    async def f(self): pass

";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V35)) {
                helper.CheckAstClassifierSpans("ii i+i iki:k ikiki:k iki(): ii iki:k ikiki:k ki: iki(i): k");
                // "await f" does not highlight "f", but "await + f" does
                helper.CheckAnalysisClassifierSpans("fff k<async>f k<await>f k<async>f k<async>f c<F> k<async>fp");
            }
        }
Пример #21
0
        public void ModuleClassification() {
            var code = @"import abc
import os
import ntpath

os.path = ntpath
abc = 123
abc = True
";
            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("ki ki ki i.i=i i=n i=b");
                helper.CheckAnalysisClassifierSpans("m<abc>m<os>m<ntpath>m<os>m<ntpath>m<abc>m<abc>");
            }
        }
Пример #22
0
        public void ParameterClassification() {
            var code = @"def f(a, b, c):
    a = b
    b = c
    return a

f(a, b, c)
a = b
b = c
";
            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("ki(i,i,i): i=i i=i ki i(i,i,i) i=i i=i");
                helper.CheckAnalysisClassifierSpans("f<f>ppppppppf<f>");
            }
        }
Пример #23
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));
        }
Пример #24
0
        public void TypeClassification() {
            var code = @"class MyClass(object):
    pass

mc = MyClass()
MyClassAlias = MyClass
mca = MyClassAlias()
MyClassType = type(mc)
";
            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("ki(i): k i=i() i=i i=i() i=i(i)");
                helper.CheckAnalysisClassifierSpans("c<MyClass>c<object>cc<MyClassAlias>ccc<type>");
            }
        }
Пример #25
0
        public void TrueFalseClassification() {
            var code = "True False";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V27)) {
                helper.CheckAstClassifierSpans("b<True> b<False>");
            }

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V33)) {
                helper.CheckAstClassifierSpans("k<True> k<False>");
            }
        }
        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));
        }
Пример #27
0
        public void ReturnAnnotationClassifications() {
            var code = @"
def f() -> int:
    pass
";

            using (var helper = new ClassifierHelper(code, PythonLanguageVersion.V35)) {
                helper.CheckAnalysisClassifierSpans("f<f>c<int>");
            }
        }