示例#1
0
        public async Task Handle_FilterDiagnostics_CSharpWarning()
        {
            // Arrange
            var documentPath = "C:/path/to/document.cshtml";
            var codeDocument = CreateCodeDocumentWithCSharpProjection(
                "<p>@DateTime.Now</p>",
                "var __o = DateTime.Now",
                new[] {
                new SourceMapping(
                    new SourceSpan(4, 12),
                    new SourceSpan(10, 12))
            });
            var documentResolver    = CreateDocumentResolver(documentPath, codeDocument);
            var diagnosticsEndpoint = new RazorDiagnosticsEndpoint(Dispatcher, documentResolver, DocumentVersionCache, MappingService, LoggerFactory);
            var request             = new RazorDiagnosticsParams()
            {
                Kind        = RazorLanguageKind.CSharp,
                Diagnostics = new[] {
                    new Diagnostic()
                    {
                        Range    = new Range(new Position(0, 10), new Position(0, 22)),
                        Code     = RazorDiagnosticsEndpoint.DiagnosticsToIgnore.First(),
                        Severity = DiagnosticSeverity.Warning
                    }
                },
                RazorDocumentUri = new Uri(documentPath),
            };
            var expectedRange = new Range(new Position(0, 4), new Position(0, 16));

            // Act
            var response = await Task.Run(() => diagnosticsEndpoint.Handle(request, default));

            // Assert
            Assert.Empty(response.Diagnostics);
            Assert.Equal(1337, response.HostDocumentVersion);
        }
        /// <summary>
        ///     Get comment completions.
        /// </summary>
        /// <param name="replaceRange">
        ///     The range of text to be replaced by the completions.
        /// </param>
        /// <returns>
        ///     A sequence of <see cref="CompletionItem"/>s.
        /// </returns>
        public IEnumerable <CompletionItem> GetCompletionItems(Range replaceRange)
        {
            if (replaceRange == null)
            {
                throw new ArgumentNullException(nameof(replaceRange));
            }

            LspModels.Range completionRange = replaceRange.ToLsp();

            // <!--  -->
            yield return(new CompletionItem
            {
                Label = "<!-- -->",
                Detail = "Comment",
                Documentation = "XML comment",
                SortText = Priority + "<!-- -->",
                TextEdit = new TextEdit
                {
                    NewText = "<!-- $0 -->",
                    Range = completionRange
                },
                InsertTextFormat = InsertTextFormat.Snippet
            });
        }
        public override bool TryMapToProjectedDocumentRange(RazorCodeDocument codeDocument, Range originalRange, out Range projectedRange)
        {
            if (codeDocument is null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

            if (originalRange is null)
            {
                throw new ArgumentNullException(nameof(originalRange));
            }

            projectedRange = default;

            if ((originalRange.End.Line < originalRange.Start.Line) ||
                (originalRange.End.Line == originalRange.Start.Line &&
                 originalRange.End.Character < originalRange.Start.Character))
            {
                Debug.Fail($"DefaultRazorDocumentMappingService:TryMapToProjectedDocumentRange original range end < start '{originalRange}'");
                return(false);
            }

            var sourceText = codeDocument.GetSourceText();
            var range      = originalRange;

            var startIndex = range.Start.GetAbsoluteIndex(sourceText);

            if (!TryMapToProjectedDocumentPosition(codeDocument, startIndex, out var projectedStart, out var _))
            {
                return(false);
            }

            var endIndex = range.End.GetAbsoluteIndex(sourceText);

            if (!TryMapToProjectedDocumentPosition(codeDocument, endIndex, out var projectedEnd, out var _))
            {
                return(false);
            }

            // Ensures a valid range is returned.
            // As we're doing two seperate TryMapToProjectedDocumentPosition calls,
            // it's possible the projectedStart and projectedEnd positions are in completely
            // different places in the document, including the possibility that the
            // projectedEnd position occurs before the projectedStart position.
            // We explicitly disallow such ranges where the end < start.
            if ((projectedEnd.Line < projectedStart.Line) ||
                (projectedEnd.Line == projectedStart.Line &&
                 projectedEnd.Character < projectedStart.Character))
            {
                return(false);
            }

            projectedRange = new Range(
                projectedStart,
                projectedEnd);

            return(true);
        }
        private bool TryMapFromProjectedDocumentRangeStrict(RazorCodeDocument codeDocument, Range projectedRange, out Range originalRange)
        {
            originalRange = default;

            var csharpSourceText = SourceText.From(codeDocument.GetCSharpDocument().GeneratedCode);
            var range            = projectedRange;
            var startIndex       = range.Start.GetAbsoluteIndex(csharpSourceText);

            if (!TryMapFromProjectedDocumentPosition(codeDocument, startIndex, out var hostDocumentStart, out _))
            {
                return(false);
            }

            var endIndex = range.End.GetAbsoluteIndex(csharpSourceText);

            if (!TryMapFromProjectedDocumentPosition(codeDocument, endIndex, out var hostDocumentEnd, out _))
            {
                return(false);
            }

            originalRange = new Range(
                hostDocumentStart,
                hostDocumentEnd);

            return(true);
        }
 public override bool TryMapFromProjectedDocumentRange(RazorCodeDocument codeDocument, Range projectedRange, out Range originalRange) => TryMapFromProjectedDocumentRange(codeDocument, projectedRange, MappingBehavior.Strict, out originalRange);
示例#6
0
        private string AssertSemanticTokens(string txt, IEnumerable <int> expectedData, bool isRazor, out RazorSemanticTokensInfoService outService, RazorSemanticTokensInfoService service = null, OmniSharpRange location = null)
        {
            // Arrange
            if (service is null)
            {
                service = GetDefaultRazorSemanticTokenInfoService();
            }
            outService = service;

            RazorCodeDocument codeDocument;

            if (isRazor)
            {
                codeDocument = CreateRazorDocument(txt, DefaultTagHelpers);
            }
            else
            {
                codeDocument = CreateCodeDocument(txt, DefaultTagHelpers);
            }

            // Act
            var tokens = service.GetSemanticTokens(codeDocument, location);

            // Assert

            Assert.Equal(expectedData, tokens.Data);

            return(tokens.ResultId);
        }
        public override bool TryMapFromProjectedDocumentRange(RazorCodeDocument codeDocument, Range projectedRange, out Range originalRange)
        {
            if (codeDocument is null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

            if (projectedRange is null)
            {
                throw new ArgumentNullException(nameof(projectedRange));
            }

            originalRange = default;

            var csharpSourceText = SourceText.From(codeDocument.GetCSharpDocument().GeneratedCode);
            var range            = projectedRange;
            var startIndex       = range.Start.GetAbsoluteIndex(csharpSourceText);

            if (!TryMapFromProjectedDocumentPosition(codeDocument, startIndex, out var hostDocumentStart, out var _))
            {
                return(false);
            }

            var endIndex = range.End.GetAbsoluteIndex(csharpSourceText);

            if (!TryMapFromProjectedDocumentPosition(codeDocument, endIndex, out var hostDocumentEnd, out var _))
            {
                return(false);
            }

            originalRange = new Range(
                hostDocumentStart,
                hostDocumentEnd);

            return(true);
        }
示例#8
0
        private HoverModel ElementInfoToHover(IEnumerable <TagHelperDescriptor> descriptors, RangeModel range, ClientCapabilities clientCapabilities)
        {
            var descriptionInfos = descriptors.Select(descriptor => BoundElementDescriptionInfo.From(descriptor))
                                   .ToList()
                                   .AsReadOnly();
            var elementDescriptionInfo = new AggregateBoundElementDescription(descriptionInfos);

            var isVSClient = clientCapabilities is PlatformAgnosticClientCapabilities platformAgnosticClientCapabilities &&
                             platformAgnosticClientCapabilities.SupportsVisualStudioExtensions;

            if (isVSClient && _vsLspTagHelperTooltipFactory.TryCreateTooltip(elementDescriptionInfo, out ContainerElement classifiedTextElement))
            {
                var vsHover = new OmniSharpVSHover
                {
                    Contents   = new MarkedStringsOrMarkupContent(),
                    Range      = range,
                    RawContent = classifiedTextElement,
                };

                return(vsHover);
            }
            else
            {
                var hoverContentFormat = GetHoverContentFormat(clientCapabilities);

                if (!_lspTagHelperTooltipFactory.TryCreateTooltip(elementDescriptionInfo, hoverContentFormat, out var vsMarkupContent))
                {
                    return(null);
                }

                Enum.TryParse(vsMarkupContent.Kind.Value, out MarkupKind markupKind);

                var markupContent = new MarkupContent()
                {
                    Value = vsMarkupContent.Value,
                    Kind  = markupKind,
                };

                var hover = new HoverModel
                {
                    Contents = new MarkedStringsOrMarkupContent(markupContent),
                    Range    = range
                };

                return(hover);
            }
        }
 public static void ShiftLineNumber(this Range range, int amount)
 {
     range.Start.ShiftLineNumber(amount);
     range.End.ShiftLineNumber(amount);
 }
 public static Range Offset(this Range range, int character = 0, int line = 0)
 {
     return(range.OffsetStart(character, line).OffsetEnd(character, line));
 }
 public static Range OffsetEnd(this Range range, int character = 0, int line = 0)
 {
     return(new Range(range.Start, new Position(range.End.Line + line, range.End.Character + character)));
 }
        /// <summary>
        ///     Create a <see cref="CompletionItem"/> for the specified MSBuild task element.
        /// </summary>
        /// <param name="taskName">
        ///     The MSBuild task name.
        /// </param>
        /// <param name="taskMetadata">
        ///     The MSBuild task's metadata.
        /// </param>
        /// <param name="replaceRange">
        ///     The range of text that will be replaced by the completion.
        /// </param>
        /// <returns>
        ///     The <see cref="CompletionItem"/>.
        /// </returns>
        CompletionItem TaskElementCompletionItem(string taskName, MSBuildTaskMetadata taskMetadata, LspModels.Range replaceRange)
        {
            MSBuildTaskParameterMetadata[] requiredParameters = taskMetadata.Parameters.Where(parameter => parameter.IsRequired).ToArray();
            string requiredAttributes = String.Join(" ", requiredParameters.Select(
                                                        (parameter, index) => $"{parameter.Name}=\"${index + 1}\""
                                                        ));
            string attributePadding = (requiredAttributes.Length > 0) ? " " : String.Empty;

            string restOfElement = " />$0";

            if (taskMetadata.Parameters.Any(parameter => parameter.IsOutput))
            {
                // Create Outputs sub-element if there are any output parameters.
                restOfElement = $">\n\t${requiredParameters.Length + 1}\n</{taskName}>$0";
            }

            return(new CompletionItem
            {
                Label = $"<{taskName}>",
                Detail = "Task",
                Documentation = MSBuildSchemaHelp.ForTask(taskName),
                SortText = $"{Priority:0000}<{taskName}>",
                TextEdit = new TextEdit
                {
                    NewText = $"<{taskName}{attributePadding}{requiredAttributes}{restOfElement}",
                    Range = replaceRange
                },
                InsertTextFormat = InsertTextFormat.Snippet
            });
        }
示例#13
0
 public static CompletionItem WithSnippetEdit(this CompletionItem item, Range range, string snippet)
 {
     AssertNoInsertText(item);
     SetTextEditInternal(item, range, InsertTextFormat.Snippet, snippet);
     return(item);
 }
示例#14
0
 public static CompletionItem WithPlainTextEdit(this CompletionItem item, Range range, string text)
 {
     AssertNoInsertText(item);
     SetTextEditInternal(item, range, InsertTextFormat.PlainText, text);
     return(item);
 }
示例#15
0
        public async Task CanSendPesterLegacyCodeLensRequestAsync()
        {
            // Make sure LegacyCodeLens is enabled because we'll need it in this test.
            PsesLanguageClient.Workspace.DidChangeConfiguration(
                new DidChangeConfigurationParams
            {
                Settings = JObject.Parse(@"
{
    ""powershell"": {
        ""pester"": {
            ""useLegacyCodeLens"": true
        }
    }
}
")
            });

            string filePath = NewTestFile(@"
Describe 'DescribeName' {
    Context 'ContextName' {
        It 'ItName' {
            1 | Should - Be 1
        }
    }
}
", isPester: true);

            CodeLensContainer codeLenses = await PsesLanguageClient
                                           .SendRequest <CodeLensParams>(
                "textDocument/codeLens",
                new CodeLensParams
            {
                TextDocument = new TextDocumentIdentifier
                {
                    Uri = new Uri(filePath)
                }
            })
                                           .Returning <CodeLensContainer>(CancellationToken.None).ConfigureAwait(false);

            Assert.Collection(codeLenses,
                              codeLens1 =>
            {
                Range range = codeLens1.Range;

                Assert.Equal(1, range.Start.Line);
                Assert.Equal(0, range.Start.Character);
                Assert.Equal(7, range.End.Line);
                Assert.Equal(1, range.End.Character);

                Assert.Equal("Run tests", codeLens1.Command.Title);
            },
                              codeLens2 =>
            {
                Range range = codeLens2.Range;

                Assert.Equal(1, range.Start.Line);
                Assert.Equal(0, range.Start.Character);
                Assert.Equal(7, range.End.Line);
                Assert.Equal(1, range.End.Character);

                Assert.Equal("Debug tests", codeLens2.Command.Title);
            });
        }
示例#16
0
 /// <summary>
 /// Push a token onto the builder
 /// </summary>
 /// <remarks>Avoid creating the range just to call this method</remarks>
 /// <param name="range">The range, cannot span multiple lines</param>
 /// <param name="tokenType"></param>
 /// <param name="tokenModifiers"></param>
 public void Push(Range range, SemanticTokenType?tokenType, IEnumerable <SemanticTokenModifier> tokenModifiers) => Push(
     range, _legend.GetTokenTypeIdentity(tokenType), _legend.GetTokenModifiersIdentity(tokenModifiers)
     );
示例#17
0
 /// <summary>
 /// Push a token onto the builder
 /// </summary>
 /// <remarks>Avoid creating the range just to call this method</remarks>
 /// <param name="range">The range, cannot span multiple lines</param>
 /// <param name="tokenType"></param>
 /// <param name="tokenModifiers"></param>
 public void Push(Range range, string tokenType, params string[] tokenModifiers) => Push(
     range, _legend.GetTokenTypeIdentity(tokenType), _legend.GetTokenModifiersIdentity(tokenModifiers)
     );
 public static Range Clone(this Range range)
 {
     return(From(range.Start.Line, range.Start.Character, range.End.Line, range.End.Character));
 }
示例#19
0
        /// <summary>
        ///     Get property element completions.
        /// </summary>
        /// <param name="projectDocument">
        ///     The <see cref="ProjectDocument"/> for which completions will be offered.
        /// </param>
        /// <param name="replaceRange">
        ///     The range of text to be replaced by the completions.
        /// </param>
        /// <returns>
        ///     A sequence of <see cref="CompletionItem"/>s.
        /// </returns>
        public IEnumerable <CompletionItem> GetCompletionItems(ProjectDocument projectDocument, Range replaceRange)
        {
            if (replaceRange == null)
            {
                throw new ArgumentNullException(nameof(replaceRange));
            }

            LspModels.Range replaceRangeLsp = replaceRange.ToLsp();

            HashSet <string> offeredPropertyNames = new HashSet <string>();

            // Special-case properties

            // Output type
            yield return(new CompletionItem
            {
                Label = "<OutputType>",
                Detail = "Property",
                Kind = CompletionItemKind.Property,
                Documentation = MSBuildSchemaHelp.ForProperty("OutputType"),
                SortText = Priority + "<OutputType>",
                TextEdit = new TextEdit
                {
                    NewText = "<OutputType>${1|Library,Exe|}</OutputType>",
                    Range = replaceRangeLsp
                },
                InsertTextFormat = InsertTextFormat.Snippet
            });

            offeredPropertyNames.Add("OutputType");

            // Target framework
            yield return(new CompletionItem
            {
                Label = "<TargetFramework>",
                Detail = "Property",
                Kind = CompletionItemKind.Property,
                Documentation = MSBuildSchemaHelp.ForProperty("TargetFramework"),
                SortText = Priority + "<TargetFramework>",
                TextEdit = new TextEdit
                {
                    NewText = "<TargetFramework>${1|netstandard1.0,netstandard1.1,netstandard1.2,netstandard1.3,netstandard1.4,netstandard1.5,netstandard1.6,netstandard2.0,netcoreapp1.0,netcoreapp1.1,netcoreapp2.0,net4,net451,net452,net46,net461,net462,net47|}</TargetFramework>",
                    Range = replaceRangeLsp
                },
                InsertTextFormat = InsertTextFormat.Snippet
            });

            offeredPropertyNames.Add("TargetFramework");

            // Well-known (but standard-format) properties.

            foreach (string wellKnownPropertyName in MSBuildSchemaHelp.WellKnownPropertyNames)
            {
                if (!offeredPropertyNames.Add(wellKnownPropertyName))
                {
                    continue;
                }

                var propertyDefaults = MSBuildSchemaHelp.DefaultsForProperty(wellKnownPropertyName);

                yield return(PropertyCompletionItem(wellKnownPropertyName, replaceRangeLsp,
                                                    description: MSBuildSchemaHelp.ForProperty(wellKnownPropertyName),
                                                    defaultValue: propertyDefaults.defaultValue,
                                                    defaultValues: propertyDefaults.defaultValues
                                                    ));
            }

            if (!projectDocument.HasMSBuildProject)
            {
                yield break; // Without a valid MSBuild project (even a cached one will do), we can't inspect existing MSBuild properties.
            }
            if (!projectDocument.Workspace.Configuration.Language.CompletionsFromProject.Contains(CompletionSource.Property))
            {
                yield break;
            }

            int otherPropertyPriority = Priority + 10;

            string[] otherPropertyNames =
                projectDocument.MSBuildProject.Properties
                .Select(property => property.Name)
                .Where(propertyName => !propertyName.StartsWith("_"))     // Ignore private properties.
                .ToArray();
            foreach (string propertyName in otherPropertyNames)
            {
                if (!offeredPropertyNames.Add(propertyName))
                {
                    continue;
                }

                yield return(PropertyCompletionItem(propertyName, replaceRangeLsp, otherPropertyPriority,
                                                    description: $"I don't know anything about the '{propertyName}' property, but it's defined in this project (or a project that it imports); you can override its value by specifying it here."
                                                    ));
            }
        }
        public static FormattingContext Create(
            DocumentUri uri,
            DocumentSnapshot originalSnapshot,
            RazorCodeDocument codeDocument,
            FormattingOptions options,
            Range range         = null,
            bool isFormatOnType = false)
        {
            if (uri is null)
            {
                throw new ArgumentNullException(nameof(uri));
            }

            if (originalSnapshot is null)
            {
                throw new ArgumentNullException(nameof(originalSnapshot));
            }

            if (codeDocument is null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

            if (options is null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            var text = codeDocument.GetSourceText();

            range ??= TextSpan.FromBounds(0, text.Length).AsRange(text);

            var result = new FormattingContext()
            {
                Uri = uri,
                OriginalSnapshot = originalSnapshot,
                CodeDocument     = codeDocument,
                Range            = range,
                Options          = options,
                IsFormatOnType   = isFormatOnType
            };

            var sourceText      = codeDocument.GetSourceText();
            var syntaxTree      = codeDocument.GetSyntaxTree();
            var formattingSpans = syntaxTree.GetFormattingSpans();
            var indentations    = new Dictionary <int, IndentationContext>();

            var previousIndentationLevel = 0;

            for (var i = 0; i < sourceText.Lines.Count; i++)
            {
                // Get first non-whitespace character position
                var nonWsPos              = sourceText.Lines[i].GetFirstNonWhitespacePosition();
                var existingIndentation   = (nonWsPos ?? sourceText.Lines[i].End) - sourceText.Lines[i].Start;
                var emptyOrWhitespaceLine = false;
                if (nonWsPos == null)
                {
                    emptyOrWhitespaceLine = true;
                    nonWsPos = sourceText.Lines[i].Start;
                }

                // position now contains the first non-whitespace character or 0. Get the corresponding FormattingSpan.
                if (TryGetFormattingSpan(nonWsPos.Value, formattingSpans, out var span))
                {
                    indentations[i] = new IndentationContext
                    {
                        Line = i,
                        RazorIndentationLevel    = span.RazorIndentationLevel,
                        HtmlIndentationLevel     = span.HtmlIndentationLevel,
                        RelativeIndentationLevel = span.IndentationLevel - previousIndentationLevel,
                        ExistingIndentation      = existingIndentation,
                        FirstSpan             = span,
                        EmptyOrWhitespaceLine = emptyOrWhitespaceLine,
                    };
                    previousIndentationLevel = span.IndentationLevel;
                }
                else
                {
                    // Couldn't find a corresponding FormattingSpan. Happens if it is a 0 length line.
                    // Let's create a 0 length span to represent this and default it to HTML.
                    var placeholderSpan = new FormattingSpan(
                        new Language.Syntax.TextSpan(nonWsPos.Value, 0),
                        new Language.Syntax.TextSpan(nonWsPos.Value, 0),
                        FormattingSpanKind.Markup,
                        FormattingBlockKind.Markup,
                        razorIndentationLevel: 0,
                        htmlIndentationLevel: 0,
                        isInClassBody: false);

                    indentations[i] = new IndentationContext
                    {
                        Line = i,
                        RazorIndentationLevel    = 0,
                        HtmlIndentationLevel     = 0,
                        RelativeIndentationLevel = previousIndentationLevel,
                        ExistingIndentation      = existingIndentation,
                        FirstSpan             = placeholderSpan,
                        EmptyOrWhitespaceLine = emptyOrWhitespaceLine,
                    };
                }
            }

            result.Indentations = indentations;

            return(result);
        }
        public override bool TryMapToProjectedDocumentRange(RazorCodeDocument codeDocument, Range originalRange, out Range projectedRange)
        {
            if (codeDocument is null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

            if (originalRange is null)
            {
                throw new ArgumentNullException(nameof(originalRange));
            }

            projectedRange = default;
            var sourceText = codeDocument.GetSourceText();
            var range      = originalRange;

            var startIndex = range.Start.GetAbsoluteIndex(sourceText);

            if (!TryMapToProjectedDocumentPosition(codeDocument, startIndex, out var projectedStart, out var _))
            {
                return(false);
            }

            var endIndex = range.End.GetAbsoluteIndex(sourceText);

            if (!TryMapToProjectedDocumentPosition(codeDocument, endIndex, out var projectedEnd, out var _))
            {
                return(false);
            }

            projectedRange = new Range(
                projectedStart,
                projectedEnd);

            return(true);
        }
示例#22
0
 // We can't get C# responses without significant amounts of extra work, so let's just shim it for now, any non-Null result is fine.
 internal override Task <IReadOnlyList <SemanticRange> > GetCSharpSemanticRangesAsync(RazorCodeDocument codeDocument, TextDocumentIdentifier textDocumentIdentifier, Range range, long?documentVersion, CancellationToken cancellationToken)
 {
     return(Task.FromResult((IReadOnlyList <SemanticRange>) new List <SemanticRange>()));
 }
示例#23
0
        private static DefaultRazorDocumentMappingService CreateDocumentMappingService(Range projectedRange = null)
        {
            projectedRange ??= new Range(new Position(5, 2), new Position(5, 2));
            var documentMappingService = Mock.Of <DefaultRazorDocumentMappingService>(
                d => d.TryMapToProjectedDocumentRange(It.IsAny <RazorCodeDocument>(), It.IsAny <Range>(), out projectedRange) == true
                );

            return(documentMappingService);
        }
        /// <summary>
        ///     Get all completion items for target names.
        /// </summary>
        /// <param name="projectDocument">
        ///     The <see cref="ProjectDocument"/> for which completions will be offered.
        /// </param>
        /// <param name="replaceRange">
        ///     The range of text to be replaced by the completions.
        /// </param>
        /// <param name="excludeTargetNames">
        ///     The names of targets (if any) to exclude from the completion list.
        /// </param>
        /// <returns>
        ///     A sequence of <see cref="CompletionItem"/>s.
        /// </returns>
        public IEnumerable <CompletionItem> GetCompletionItems(ProjectDocument projectDocument, Range replaceRange, HashSet <string> excludeTargetNames)
        {
            if (replaceRange == null)
            {
                throw new ArgumentNullException(nameof(replaceRange));
            }

            HashSet <string> offeredTargetNames = new HashSet <string>(excludeTargetNames);

            LspModels.Range replaceRangeLsp = replaceRange.ToLsp();

            // Well-known targets.
            foreach (string targetName in WellKnownTargets.Keys)
            {
                if (!offeredTargetNames.Add(targetName))
                {
                    continue;
                }

                yield return(TargetNameCompletionItem(targetName, replaceRangeLsp,
                                                      description: WellKnownTargets[targetName]
                                                      ));
            }

            // All other (public) targets defined in the project.

            if (!projectDocument.HasMSBuildProject)
            {
                yield break; // Without a valid MSBuild project (even a cached one will do), we can't inspect existing MSBuild targets.
            }
            if (!projectDocument.Workspace.Configuration.Language.CompletionsFromProject.Contains(CompletionSource.Target))
            {
                yield break;
            }

            int otherTargetPriority = Priority + 10;

            ProjectTargetInstance[] otherTargets =
                projectDocument.MSBuildProject.Targets.Values
                .Where(
                    target => !target.Name.StartsWith("_")     // Ignore private targets.
                    )
                .OrderBy(target => target.Name)
                .ToArray();
            foreach (ProjectTargetInstance otherTarget in otherTargets)
            {
                if (!offeredTargetNames.Add(otherTarget.Name))
                {
                    continue;
                }

                // We can't really tell them much else about the target if it's not one of the well-known ones.
                string targetDescription = String.Format("Originally declared in {0} (line {1}, column {2})",
                                                         Path.GetFileName(otherTarget.Location.File),
                                                         otherTarget.Location.Line,
                                                         otherTarget.Location.Column
                                                         );

                yield return(TargetNameCompletionItem(otherTarget.Name, replaceRangeLsp, otherTargetPriority, targetDescription));
            }
        }
        public override bool TryMapFromProjectedDocumentRange(RazorCodeDocument codeDocument, Range projectedRange, MappingBehavior mappingBehavior, out Range originalRange)
        {
            if (codeDocument is null)
            {
                throw new ArgumentNullException(nameof(codeDocument));
            }

            if (projectedRange is null)
            {
                throw new ArgumentNullException(nameof(projectedRange));
            }

            if (mappingBehavior == MappingBehavior.Strict)
            {
                return(TryMapFromProjectedDocumentRangeStrict(codeDocument, projectedRange, out originalRange));
            }
            else if (mappingBehavior == MappingBehavior.Inclusive)
            {
                return(TryMapFromProjectedDocumentRangeInclusive(codeDocument, projectedRange, out originalRange));
            }
            else
            {
                throw new InvalidOperationException("Unknown mapping behavior");
            }
        }
        /// <summary>
        ///     Get top-level element completions.
        /// </summary>
        /// <param name="replaceRange">
        ///     The range of text to be replaced by the completions.
        /// </param>
        /// <returns>
        ///     A sequence of <see cref="CompletionItem"/>s.
        /// </returns>
        public IEnumerable <CompletionItem> GetCompletionItems(Range replaceRange)
        {
            if (replaceRange == null)
            {
                throw new ArgumentNullException(nameof(replaceRange));
            }

            LspModels.Range completionRange = replaceRange.ToLsp();

            // <PropertyGroup>
            //     $0
            // </PropertyGroup>
            yield return(new CompletionItem
            {
                Label = "<PropertyGroup>",
                Detail = "Element",
                Documentation = MSBuildSchemaHelp.ForElement("PropertyGroup"),
                SortText = Priority + "<PropertyGroup>",
                TextEdit = new TextEdit
                {
                    NewText = "<PropertyGroup>\n\t$0\n</PropertyGroup>",
                    Range = completionRange
                },
                InsertTextFormat = InsertTextFormat.Snippet
            });

            // <ItemGroup>
            //     $0
            // </ItemGroup>
            yield return(new CompletionItem
            {
                Label = "<ItemGroup>",
                Detail = "Element",
                Documentation = MSBuildSchemaHelp.ForElement("ItemGroup"),
                SortText = Priority + "<ItemGroup>",
                TextEdit = new TextEdit
                {
                    NewText = "<ItemGroup>\n\t$0\n</ItemGroup>",
                    Range = completionRange
                },
                InsertTextFormat = InsertTextFormat.Snippet
            });

            // <Target Name="TargetName">
            //     $0
            // </Target>
            yield return(new CompletionItem
            {
                Label = "<Target>",
                Detail = "Element",
                Documentation = MSBuildSchemaHelp.ForElement("Target"),
                SortText = Priority + "<Target>",
                TextEdit = new TextEdit
                {
                    NewText = "<Target Name=\"${1:TargetName}\">\n\t$0\n</Target>",
                    Range = completionRange
                },
                InsertTextFormat = InsertTextFormat.Snippet
            });

            // <Import Project="ProjectFile" />
            yield return(new CompletionItem
            {
                Label = "<Import>",
                Detail = "Element",
                Documentation = MSBuildSchemaHelp.ForElement("Import"),
                SortText = Priority + "<Import>",
                TextEdit = new TextEdit
                {
                    NewText = "<Import Project=\"${1:ProjectFile}\" />$0",
                    Range = completionRange
                },
                InsertTextFormat = InsertTextFormat.Snippet
            });
        }
        private bool TryMapFromProjectedDocumentRangeInclusive(RazorCodeDocument codeDocument, Range projectedRange, out Range originalRange)
        {
            originalRange = default;

            var csharpDoc            = codeDocument.GetCSharpDocument();
            var csharpSourceText     = SourceText.From(csharpDoc.GeneratedCode);
            var projectedRangeAsSpan = projectedRange.AsTextSpan(csharpSourceText);
            var range               = projectedRange;
            var startIndex          = projectedRangeAsSpan.Start;
            var startMappedDirectly = TryMapFromProjectedDocumentPosition(codeDocument, startIndex, out var hostDocumentStart, out _);

            var endIndex          = projectedRangeAsSpan.End;
            var endMappedDirectly = TryMapFromProjectedDocumentPosition(codeDocument, endIndex, out var hostDocumentEnd, out _);

            if (startMappedDirectly && endMappedDirectly)
            {
                // We strictly mapped the start/end of the projected range.
                originalRange = new Range(hostDocumentStart, hostDocumentEnd);
                return(true);
            }

            List <SourceMapping> candidateMappings;

            if (startMappedDirectly)
            {
                // Start of projected range intersects with a mapping
                candidateMappings = csharpDoc.SourceMappings.Where(mapping => IntersectsWith(startIndex, mapping.GeneratedSpan)).ToList();
            }
            else if (endMappedDirectly)
            {
                // End of projected range intersects with a mapping
                candidateMappings = csharpDoc.SourceMappings.Where(mapping => IntersectsWith(endIndex, mapping.GeneratedSpan)).ToList();
            }
            else
            {
                // Our range does not intersect with any mapping; we should see if it overlaps generated locations
                candidateMappings = csharpDoc.SourceMappings.Where(mapping => Overlaps(projectedRangeAsSpan, mapping.GeneratedSpan)).ToList();
            }

            if (candidateMappings.Count == 1)
            {
                // We're intersecting or overlapping a single mapping, lets choose that.

                var mapping = candidateMappings[0];
                originalRange = ConvertMapping(codeDocument.Source, mapping);
                return(true);
            }
            else
            {
                // More then 1 or exactly 0 intersecting/overlapping mappings
                return(false);
            }

            bool Overlaps(TextSpan projectedRangeAsSpan, SourceSpan span)
            {
                var overlapStart = Math.Max(projectedRangeAsSpan.Start, span.AbsoluteIndex);
                var overlapEnd   = Math.Min(projectedRangeAsSpan.End, span.AbsoluteIndex + span.Length);

                return(overlapStart < overlapEnd);
            }

            bool IntersectsWith(int position, SourceSpan span)
            {
                return(unchecked ((uint)(position - span.AbsoluteIndex) <= (uint)span.Length));
            }
示例#28
0
 /// <summary>
 /// Push a token onto the builder
 /// </summary>
 /// <remarks>Avoid creating the range just to call this method</remarks>
 /// <param name="range">The range, cannot span multiple lines</param>
 /// <param name="tokenType"></param>
 /// <param name="tokenModifiers"></param>
 public void Push(Range range, string tokenType, IEnumerable <string> tokenModifiers) => Push(
     range, _legend.GetTokenTypeIdentity(tokenType), _legend.GetTokenModifiersIdentity(tokenModifiers)
     );
        public async Task CanSendPesterCodeLensRequest()
        {
            // Make sure Pester legacy CodeLens is disabled because we'll need it in this test.
            LanguageClient.Workspace.DidChangeConfiguration(JObject.Parse(@"
            {
                ""powershell"": {
                    ""pester"": {
                        ""useLegacyCodeLens"": false
                    }
                }
            }
            "));

            string filePath = NewTestFile(@"
Describe 'DescribeName' {
    Context 'ContextName' {
        It 'ItName' {
            1 | Should - Be 1
        }
    }
}
", isPester: true);

            CodeLensContainer codeLenses = await LanguageClient.SendRequest <CodeLensContainer>(
                "textDocument/codeLens",
                new CodeLensParams
            {
                TextDocument = new TextDocumentIdentifier
                {
                    Uri = new Uri(filePath)
                }
            });

            Assert.Collection(codeLenses,
                              codeLens =>
            {
                Range range = codeLens.Range;

                Assert.Equal(1, range.Start.Line);
                Assert.Equal(0, range.Start.Character);
                Assert.Equal(7, range.End.Line);
                Assert.Equal(1, range.End.Character);

                Assert.Equal("Run tests", codeLens.Command.Title);
            },
                              codeLens =>
            {
                Range range = codeLens.Range;

                Assert.Equal(1, range.Start.Line);
                Assert.Equal(0, range.Start.Character);
                Assert.Equal(7, range.End.Line);
                Assert.Equal(1, range.End.Character);

                Assert.Equal("Debug tests", codeLens.Command.Title);
            },
                              codeLens =>
            {
                Range range = codeLens.Range;

                Assert.Equal(2, range.Start.Line);
                Assert.Equal(4, range.Start.Character);
                Assert.Equal(6, range.End.Line);
                Assert.Equal(5, range.End.Character);

                Assert.Equal("Run tests", codeLens.Command.Title);
            },
                              codeLens =>
            {
                Range range = codeLens.Range;

                Assert.Equal(2, range.Start.Line);
                Assert.Equal(4, range.Start.Character);
                Assert.Equal(6, range.End.Line);
                Assert.Equal(5, range.End.Character);

                Assert.Equal("Debug tests", codeLens.Command.Title);
            },
                              codeLens =>
            {
                Range range = codeLens.Range;

                Assert.Equal(3, range.Start.Line);
                Assert.Equal(8, range.Start.Character);
                Assert.Equal(5, range.End.Line);
                Assert.Equal(9, range.End.Character);

                Assert.Equal("Run test", codeLens.Command.Title);
            },
                              codeLens =>
            {
                Range range = codeLens.Range;

                Assert.Equal(3, range.Start.Line);
                Assert.Equal(8, range.Start.Character);
                Assert.Equal(5, range.End.Line);
                Assert.Equal(9, range.End.Character);

                Assert.Equal("Debug test", codeLens.Command.Title);
            });
        }
示例#30
0
 /// <summary>
 /// Push a token onto the builder
 /// </summary>
 /// <remarks>Avoid creating the range just to call this method</remarks>
 /// <param name="range">The range, cannot span multiple lines</param>
 /// <param name="tokenType"></param>
 /// <param name="tokenModifiers"></param>
 public void Push(Range range, SemanticTokenType?tokenType, params SemanticTokenModifier[] tokenModifiers) => Push(
     range, _legend.GetTokenTypeIdentity(tokenType), _legend.GetTokenModifiersIdentity(tokenModifiers)
     );