コード例 #1
0
ファイル: OutputHelper.cs プロジェクト: husains/bicep
        public static string AddDiagsToSourceText <T>(string bicepOutput, string newlineSequence, IEnumerable <T> items, Func <T, TextSpan> getSpanFunc, Func <T, string> diagsFunc)
        {
            var lineStarts = TextCoordinateConverter.GetLineStarts(bicepOutput);

            var itemsByLine = items
                              .Select(item => {
                var(line, character) = TextCoordinateConverter.GetPosition(lineStarts, getSpanFunc(item).Position);
                return(line, character, item);
            })
                              .ToLookup(t => t.line);

            var sourceTextLines = bicepOutput.Split(newlineSequence);
            var stringBuilder   = new StringBuilder();

            for (var i = 0; i < sourceTextLines.Length; i++)
            {
                stringBuilder.Append(sourceTextLines[i]);
                stringBuilder.Append(newlineSequence);
                foreach (var(line, character, item) in itemsByLine[i])
                {
                    var escapedDiagsText = EscapeWhitespace(diagsFunc(item));
                    stringBuilder.Append($"//@[{character}:{character + getSpanFunc(item).Length}) {escapedDiagsText}");
                    stringBuilder.Append(newlineSequence);
                }
            }

            return(stringBuilder.ToString());
        }
コード例 #2
0
        public async Task Completions_are_not_offered_inside_comments()
        {
            var(file, cursors) = ParserHelper.GetFileWithCursors(@"
var test = /|/ comment here|
var test2 = /|* block c|omment *|/
");

            var syntaxTree = SyntaxTree.Create(new Uri("file:///main.bicep"), file);

            using var client = await IntegrationTestHelper.StartServerWithTextAsync(file, syntaxTree.FileUri, resourceTypeProvider : TypeProvider);

            foreach (var cursor in cursors)
            {
                using (new AssertionScope().WithVisualCursor(syntaxTree, new TextSpan(cursor, 0)))
                {
                    var completions = await client.RequestCompletion(new CompletionParams
                    {
                        TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri),
                        Position     = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, cursor),
                    });

                    completions.Should().BeEmpty();
                }
            }
        }
コード例 #3
0
ファイル: SemanticModel.cs プロジェクト: stan-sz/bicep
        private IReadOnlyList <IDiagnostic> AssembleDiagnostics()
        {
            var diagnostics = GetParseDiagnostics()
                              .Concat(GetSemanticDiagnostics())
                              .Concat(GetAnalyzerDiagnostics())
                              .OrderBy(diag => diag.Span.Position);
            var filteredDiagnostics = new List <IDiagnostic>();

            var disabledDiagnosticsCache = SourceFile.DisabledDiagnosticsCache;

            foreach (IDiagnostic diagnostic in diagnostics)
            {
                (int diagnosticLine, _) = TextCoordinateConverter.GetPosition(SourceFile.LineStarts, diagnostic.Span.Position);

                if (diagnosticLine == 0 || !diagnostic.CanBeSuppressed())
                {
                    filteredDiagnostics.Add(diagnostic);
                    continue;
                }

                if (disabledDiagnosticsCache.TryGetDisabledNextLineDirective(diagnosticLine - 1) is { } disableNextLineDirectiveEndPositionAndCodes&&
                    disableNextLineDirectiveEndPositionAndCodes.diagnosticCodes.Contains(diagnostic.Code))
                {
                    continue;
                }

                filteredDiagnostics.Add(diagnostic);
            }

            return(filteredDiagnostics);
        }
コード例 #4
0
        private async Task <string> RequestSnippetCompletion(string bicepFileName, CompletionData completionData, string placeholderFile)
        {
            var documentUri = DocumentUri.FromFileSystemPath(bicepFileName);
            var syntaxTree  = SyntaxTree.Create(documentUri.ToUri(), placeholderFile);

            var client = await IntegrationTestHelper.StartServerWithTextAsync(
                placeholderFile,
                documentUri,
                null,
                TypeProvider);

            var cursor      = placeholderFile.IndexOf("// Insert snippet here");
            var completions = await client.RequestCompletion(new CompletionParams
            {
                TextDocument = documentUri,
                Position     = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, cursor),
            });

            var matchingSnippets = completions.Where(x => x.Kind == CompletionItemKind.Snippet && x.Label == completionData.Prefix);

            matchingSnippets.Should().HaveCount(1);
            var completion = matchingSnippets.First();

            completion.TextEdit.Should().NotBeNull();
            completion.TextEdit !.Range.Should().Be(new TextSpan(cursor, 0).ToRange(syntaxTree.LineStarts));
            completion.TextEdit.NewText.Should().NotBeNullOrWhiteSpace();

            return(completion.TextEdit.NewText);
        }
コード例 #5
0
        public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers(DataSet dataSet)
        {
            var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out _, out var fileUri);
            var uri         = DocumentUri.From(fileUri);
            var client      = await IntegrationTestHelper.StartServerWithTextAsync(this.TestContext, dataSet.Bicep, uri, resourceTypeProvider : AzResourceTypeProvider.CreateWithAzTypes(), fileResolver : BicepTestConstants.FileResolver);

            var symbolTable = compilation.ReconstructSymbolTable();
            var lineStarts  = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts;

            var symbolReferences = SyntaxAggregator.Aggregate(
                compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax,
                new List <SyntaxBase>(),
                (accumulated, node) =>
            {
                if (node is ISymbolReference || node is ITopLevelNamedDeclarationSyntax)
                {
                    accumulated.Add(node);
                }

                return(accumulated);
            },
                accumulated => accumulated);

            foreach (var symbolReference in symbolReferences)
            {
                // by default, request a hover on the first character of the syntax, but for certain syntaxes, this doesn't make sense.
                // for example on an instance function call 'az.resourceGroup()', it only makes sense to request a hover on the 3rd character.
                var nodeForHover = symbolReference switch
                {
                    ITopLevelDeclarationSyntax d => d.Keyword,
                    ResourceAccessSyntax r => r.ResourceName,
                    FunctionCallSyntaxBase f => f.Name,
                         _ => symbolReference,
                };

                var hover = await client.RequestHover(new HoverParams
                {
                    TextDocument = new TextDocumentIdentifier(uri),
                    Position     = TextCoordinateConverter.GetPosition(lineStarts, nodeForHover.Span.Position)
                });

                // fancy method to give us some annotated source code to look at if any assertions fail :)
                using (new AssertionScope().WithVisualCursor(compilation.SyntaxTreeGrouping.EntryPoint, nodeForHover.Span.ToZeroLengthSpan()))
                {
                    if (!symbolTable.TryGetValue(symbolReference, out var symbol))
                    {
                        if (symbolReference is InstanceFunctionCallSyntax &&
                            compilation.GetEntrypointSemanticModel().GetSymbolInfo(symbolReference) is FunctionSymbol ifcSymbol)
                        {
                            ValidateHover(hover, ifcSymbol);
                            break;
                        }

                        // symbol ref not bound to a symbol
                        hover.Should().BeNull();
                        continue;
                    }

                    switch (symbol !.Kind)
                    {
コード例 #6
0
ファイル: BicepCodeActionHandler.cs プロジェクト: miqm/bicep
        private static CommandOrCodeAction?DisableDiagnostic(DocumentUri documentUri,
                                                             DiagnosticCode diagnosticCode,
                                                             BicepFile bicepFile,
                                                             TextSpan span,
                                                             ImmutableArray <int> lineStarts)
        {
            if (diagnosticCode.String is null)
            {
                return(null);
            }

            var disabledDiagnosticsCache = bicepFile.DisabledDiagnosticsCache;

            (int diagnosticLine, _) = TextCoordinateConverter.GetPosition(bicepFile.LineStarts, span.Position);

            TextEdit?textEdit;
            int      previousLine = diagnosticLine - 1;

            if (disabledDiagnosticsCache.TryGetDisabledNextLineDirective(previousLine) is { } disableNextLineDirectiveEndPositionAndCodes)
            {
                textEdit = new TextEdit
                {
                    Range   = new Range(previousLine, disableNextLineDirectiveEndPositionAndCodes.endPosition, previousLine, disableNextLineDirectiveEndPositionAndCodes.endPosition),
                    NewText = ' ' + diagnosticCode.String
                };
            }
コード例 #7
0
ファイル: SemanticModelTests.cs プロジェクト: visnema/bicep
        public void ProgramsShouldProduceExpectedUserDeclaredSymbols(DataSet dataSet)
        {
            var compilation = new Compilation(TestResourceTypeProvider.CreateRegistrar(), SyntaxFactory.CreateFromText(dataSet.Bicep));
            var model       = compilation.GetSemanticModel();

            var symbols = SymbolCollector
                          .CollectSymbols(model)
                          .OfType <DeclaredSymbol>();

            var lineStarts = TextCoordinateConverter.GetLineStarts(dataSet.Bicep);

            string getLoggingString(DeclaredSymbol symbol)
            {
                (_, var startChar) = TextCoordinateConverter.GetPosition(lineStarts, symbol.DeclaringSyntax.Span.Position);

                return($"{symbol.Kind} {symbol.Name}. Type: {symbol.Type}. Declaration start char: {startChar}, length: {symbol.DeclaringSyntax.Span.Length}");
            }

            var sourceTextWithDiags = OutputHelper.AddDiagsToSourceText(dataSet, symbols, symb => symb.NameSyntax.Span, getLoggingString);
            var resultsFile         = FileHelper.SaveResultFile(this.TestContext !, Path.Combine(dataSet.Name, DataSet.TestFileMainSymbols), sourceTextWithDiags);

            sourceTextWithDiags.Should().EqualWithLineByLineDiffOutput(
                dataSet.Symbols,
                expectedLocation: OutputHelper.GetBaselineUpdatePath(dataSet, DataSet.TestFileMainSymbols),
                actualLocation: resultsFile);
        }
コード例 #8
0
ファイル: SemanticModelTests.cs プロジェクト: wpouseele/bicep
        public void ProgramsShouldProduceExpectedUserDeclaredSymbols(DataSet dataSet)
        {
            var compilation = dataSet.CopyFilesAndCreateCompilation(TestContext, out var outputDirectory);
            var model       = compilation.GetEntrypointSemanticModel();

            var symbols = SymbolCollector
                          .CollectSymbols(model)
                          .OfType <DeclaredSymbol>();

            var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts;

            string getLoggingString(DeclaredSymbol symbol)
            {
                (_, var startChar) = TextCoordinateConverter.GetPosition(lineStarts, symbol.DeclaringSyntax.Span.Position);

                return($"{symbol.Kind} {symbol.Name}. Type: {symbol.Type}. Declaration start char: {startChar}, length: {symbol.DeclaringSyntax.Span.Length}");
            }

            var sourceTextWithDiags = DataSet.AddDiagsToSourceText(dataSet, symbols, symb => symb.NameSyntax.Span, getLoggingString);
            var resultsFile         = Path.Combine(outputDirectory, DataSet.TestFileMainDiagnostics);

            File.WriteAllText(resultsFile, sourceTextWithDiags);

            sourceTextWithDiags.Should().EqualWithLineByLineDiffOutput(
                TestContext,
                dataSet.Symbols,
                expectedLocation: DataSet.GetBaselineUpdatePath(dataSet, DataSet.TestFileMainSymbols),
                actualLocation: resultsFile);
        }
コード例 #9
0
        public async Task String_segments_do_not_return_completions()
        {
            var(file, cursors) = ParserHelper.GetFileWithCursors(@"
var completeString = |'he|llo'|
var interpolatedString = |'abc${|true}|de|f${|false}|gh|i'|
var multilineString = |'''|
hel|lo
'''|
");

            var syntaxTree = SyntaxTree.Create(new Uri("file:///main.bicep"), file);

            using var client = await IntegrationTestHelper.StartServerWithTextAsync(file, syntaxTree.FileUri, resourceTypeProvider : TypeProvider);

            foreach (var cursor in cursors)
            {
                using (new AssertionScope().WithVisualCursor(syntaxTree, new TextSpan(cursor, 0)))
                {
                    var completions = await client.RequestCompletion(new CompletionParams
                    {
                        TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri),
                        Position     = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, cursor),
                    });

                    completions.Should().BeEmpty();
                }
            }
        }
コード例 #10
0
        public void GetPosition_FirstElementOfLineStartsIsNotZero_ThrowsArgumentException()
        {
            Action sut = () => TextCoordinateConverter.GetPosition(new List <int> {
                42
            }, 10);

            sut.Should().Throw <ArgumentException>().WithMessage("*must be 0, but got 42*");
        }
コード例 #11
0
ファイル: PrintHelper.cs プロジェクト: vancosuna/bicep
        public static string PrintWithAnnotations(SyntaxTree syntaxTree, IEnumerable <Annotation> annotations, int context, bool includeLineNumbers)
        {
            var output       = new StringBuilder();
            var programLines = GetProgramTextLines(syntaxTree);

            var annotationPositions = annotations.ToDictionary(
                x => x,
                x => TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, x.Span.Position));

            var annotationsByLine = annotationPositions.ToLookup(x => x.Value.line, x => x.Key);

            var minLine = annotationPositions.Values.Aggregate(int.MaxValue, (min, curr) => Math.Min(curr.line, min));
            var maxLine = annotationPositions.Values.Aggregate(0, (max, curr) => Math.Max(curr.line, max)) + 1;

            minLine = Math.Max(0, minLine - context);
            maxLine = Math.Min(syntaxTree.LineStarts.Length, maxLine + context);
            var digits = maxLine.ToString().Length;

            for (var i = minLine; i < maxLine; i++)
            {
                var gutterOffset = 0;
                if (includeLineNumbers)
                {
                    var lineNumber = i + 1; // to match VSCode's line numbering (starting at 1)
                    output.Append(lineNumber.ToString().PadLeft(digits, '0'));
                    output.Append("| ");

                    gutterOffset = digits + 2;
                }
                output.Append(programLines[i]);
                output.Append('\n');

                var annotationsToDisplay = annotationsByLine[i].OrderBy(x => annotationPositions[x].character);
                foreach (var annotation in annotationsToDisplay)
                {
                    var position = annotationPositions[annotation];
                    output.Append(new String(' ', gutterOffset + position.character));

                    switch (annotation.Span.Length)
                    {
                    case 0:
                        output.Append("^");
                        break;

                    case int x:
                        // TODO handle annotation spanning multiple lines
                        output.Append(new String('~', x));
                        break;
                    }

                    output.Append(" ");
                    output.Append(annotation.Message);
                    output.Append('\n');
                }
            }

            return(output.ToString());
        }
コード例 #12
0
        public void GetPosition_NegtiveOffset_ThrowsArgumentOutOfRangeException()
        {
            IReadOnlyList <int> lineStarts = new List <int> {
                0, 24
            }.AsReadOnly();
            Action sut = () => TextCoordinateConverter.GetPosition(lineStarts, -9);

            sut.Should().Throw <ArgumentException>().WithMessage("*must not be a negative number*");
        }
コード例 #13
0
        public void LogDiagnostic(Uri fileUri, Diagnostic diagnostic, ImmutableArray <int> lineStarts)
        {
            (int line, int character) = TextCoordinateConverter.GetPosition(lineStarts, diagnostic.Span.Position);
            string message = $"{fileUri.LocalPath}({line + 1},{character + 1}) : {diagnostic.Level} {diagnostic.Code}: {diagnostic.Message}";

            this.logger.Log(ToLogLevel(diagnostic.Level), message);

            this.HasLoggedErrors |= diagnostic.Level == DiagnosticLevel.Error;
        }
コード例 #14
0
 public override void VisitSyntaxTrivia(SyntaxTrivia syntaxTrivia)
 {
     if (syntaxTrivia is DisableNextLineDiagnosticsSyntaxTrivia disableNextLineDiagnosticsSyntaxTrivia)
     {
         var codes = disableNextLineDiagnosticsSyntaxTrivia.DiagnosticCodes.Select(x => x.Text).ToImmutableArray();
         (int line, _) = TextCoordinateConverter.GetPosition(lineStarts, syntaxTrivia.Span.Position);
         DisableNextLineDirectiveEndPositionAndCodes disableNextLineDirectiveEndPosAndCodes = new(syntaxTrivia.Span.GetEndPosition(), codes);
         disableNextLineDiagnosticDirectivesCacheBuilder.Add(line, disableNextLineDirectiveEndPosAndCodes);
     }
 }
コード例 #15
0
        public void LogDiagnostic(string filePath, Diagnostic diagnostic, ImmutableArray <int> lineStarts)
        {
            (int line, int character) = TextCoordinateConverter.GetPosition(lineStarts, diagnostic.Span.Position);
            string message = $"{filePath}({line + 1},{character + 1}) : {diagnostic.Level} {diagnostic.Code}: {diagnostic.Message}";

            this.logger.Log(ToLogLevel(diagnostic.Level), message);

            // TODO: Fix this when we have diagnostic severity
            this.HasLoggedErrors = true;
        }
コード例 #16
0
        public void LogDiagnostic(Uri fileUri, IDiagnostic diagnostic, ImmutableArray <int> lineStarts)
        {
            (int line, int character) = TextCoordinateConverter.GetPosition(lineStarts, diagnostic.Span.Position);

            // build a a code description link if the Uri is assigned
            var codeDescription = diagnostic.Uri == null ? string.Empty : $" [{diagnostic.Uri.AbsoluteUri}]";

            var message = $"{fileUri.LocalPath}({line + 1},{character + 1}) : {diagnostic.Level} {diagnostic.Code}: {diagnostic.Message}{codeDescription}";

            this.logger.Log(ToLogLevel(diagnostic.Level), message);

            this.HasLoggedErrors |= diagnostic.Level == DiagnosticLevel.Error;
        }
コード例 #17
0
ファイル: ProgramTests.cs プロジェクト: steeleprice/bicep
        private IEnumerable <string> GetAllDiagnostics(string text, string bicepFilePath)
        {
            var compilation = new Compilation(SyntaxFactory.CreateFromText(text));
            var lineStarts  = TextCoordinateConverter.GetLineStarts(text);

            return(compilation.GetSemanticModel()
                   .GetAllDiagnostics()
                   .Select(d =>
            {
                var(line, character) = TextCoordinateConverter.GetPosition(lineStarts, d.Span.Position);
                return $"{bicepFilePath}({line + 1},{character + 1}) : {d.Level} {d.Code}: {d.Message}";
            }));
        }
コード例 #18
0
ファイル: Interop.cs プロジェクト: husains/bicep
        private static object ToMonacoDiagnostic(IDiagnostic diagnostic, IReadOnlyList <int> lineStarts)
        {
            var(startLine, startChar) = TextCoordinateConverter.GetPosition(lineStarts, diagnostic.Span.Position);
            var(endLine, endChar)     = TextCoordinateConverter.GetPosition(lineStarts, diagnostic.GetEndPosition());

            return(new {
                code = diagnostic.Code,
                message = diagnostic.Message,
                severity = ToMonacoSeverity(diagnostic.Level),
                startLineNumber = startLine + 1,
                startColumn = startChar + 1,
                endLineNumber = endLine + 1,
                endColumn = endChar + 1,
            });
        }
コード例 #19
0
        private static async Task <List <LocationOrLocationLinks> > RequestDefinitions(ILanguageClient client, BicepFile bicepFile, IEnumerable <int> cursors)
        {
            var results = new List <LocationOrLocationLinks>();

            foreach (var cursor in cursors)
            {
                var result = await client.RequestDefinition(new DefinitionParams()
                {
                    TextDocument = new TextDocumentIdentifier(bicepFile.FileUri),
                    Position     = TextCoordinateConverter.GetPosition(bicepFile.LineStarts, cursor)
                });

                results.Add(result);
            }

            return(results);
        }
コード例 #20
0
        public static string GetDiagnosticsMessage(KeyValuePair <BicepFile, IEnumerable <IDiagnostic> > diagnosticsByFile)
        {
            StringBuilder       sb         = new StringBuilder();
            IReadOnlyList <int> lineStarts = diagnosticsByFile.Key.LineStarts;

            foreach (IDiagnostic diagnostic in diagnosticsByFile.Value)
            {
                (int line, int character) = TextCoordinateConverter.GetPosition(lineStarts, diagnostic.Span.Position);

                // Build a code description link if the Uri is assigned
                var codeDescription = diagnostic.Uri == null ? string.Empty : $" [{diagnostic.Uri.AbsoluteUri}]";

                sb.AppendLine($"{diagnosticsByFile.Key.FileUri.LocalPath}({line + 1},{character + 1}) : {diagnostic.Level} {diagnostic.Code}: {diagnostic.Message}{codeDescription}");
            }

            return(sb.ToString());
        }
コード例 #21
0
ファイル: ProgramTests.cs プロジェクト: Princetimber/bicep-1
        private static IEnumerable <string> GetAllDiagnostics(string bicepFilePath)
        {
            var syntaxTreeGrouping = SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath));
            var compilation        = new Compilation(TestTypeHelper.CreateEmptyProvider(), syntaxTreeGrouping);

            var output = new List <string>();

            foreach (var(syntaxTree, diagnostics) in compilation.GetAllDiagnosticsBySyntaxTree())
            {
                foreach (var diagnostic in diagnostics)
                {
                    var(line, character) = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, diagnostic.Span.Position);
                    output.Add($"{syntaxTree.FileUri.LocalPath}({line + 1},{character + 1}) : {diagnostic.Level} {diagnostic.Code}: {diagnostic.Message}");
                }
            }

            return(output);
        }
コード例 #22
0
        public async Task VerifyResourceBodyCompletionWithoutExistingKeywordIncludesCustomSnippet()
        {
            string text = @"resource aksCluster 'Microsoft.ContainerService/managedClusters@2021-03-01' = ";

            var syntaxTree = SyntaxTree.Create(new Uri("file:///main.bicep"), text);

            using var client = await IntegrationTestHelper.StartServerWithTextAsync(text, syntaxTree.FileUri, resourceTypeProvider : TypeProvider);

            var completions = await client.RequestCompletion(new CompletionParams
            {
                TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri),
                Position     = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, text.Length),
            });

            completions.Should().SatisfyRespectively(
                c =>
            {
                c.Label.Should().Be("{}");
            },
                c =>
            {
                c.Label.Should().Be("snippet");
            },
                c =>
            {
                c.Label.Should().Be("required-properties");
            },
                c =>
            {
                c.Label.Should().Be("if");
            },
                c =>
            {
                c.Label.Should().Be("for");
            },
                c =>
            {
                c.Label.Should().Be("for-indexed");
            },
                c =>
            {
                c.Label.Should().Be("for-filtered");
            });
        }
コード例 #23
0
ファイル: OutputHelper.cs プロジェクト: Azure/bicep
        public static string AddDiagsToSourceText <T>(string bicepOutput, string newlineSequence, IEnumerable <T> items, Func <T, TextSpan> getSpanFunc, Func <T, string> diagsFunc)
        {
            var lineStarts = TextCoordinateConverter.GetLineStarts(bicepOutput);

            var diagsByLine = items
                              .Select(item =>
            {
                var span             = getSpanFunc(item);
                var(line, startChar) = TextCoordinateConverter.GetPosition(lineStarts, span.Position);
                var endChar          = startChar + span.Length;

                var escapedText = EscapeWhitespace(diagsFunc(item));

                return(line, startChar, endChar, escapedText);
            })
                              .ToLookup(t => t.line);

            var diags = diagsByLine.SelectMany(x => x);

            var startCharPadding = diags.Any() ? CountDigits(diags.Max(x => x.startChar)) : 0;
            var endCharPadding   = diags.Any() ? CountDigits(diags.Max(x => x.endChar)) : 0;

            var sourceTextLines = bicepOutput.Split(newlineSequence);
            var stringBuilder   = new StringBuilder();

            for (var i = 0; i < sourceTextLines.Length; i++)
            {
                stringBuilder.Append(sourceTextLines[i]);
                stringBuilder.Append(newlineSequence);
                foreach (var diag in diagsByLine[i])
                {
                    var startCharPadded = diag.startChar.ToString().PadLeft(startCharPadding, '0');
                    var endCharPadded   = diag.endChar.ToString().PadLeft(endCharPadding, '0');

                    // Pad the start & end char with zeros to ensure that the escaped text always starts at the same place
                    // This makes it easier to compare lines visually
                    stringBuilder.Append($"//@[{startCharPadded}:{endCharPadded}) {diag.escapedText}");
                    stringBuilder.Append(newlineSequence);
                }
            }

            return(stringBuilder.ToString());
        }
コード例 #24
0
        public async Task String_segments_do_not_return_completions()
        {
            var fileWithCursors = @"
var completeString = |'he|llo'|
var interpolatedString = |'abc${|true}|de|f${|false}|gh|i'|
var multilineString = |'''|
hel|lo
'''|
";
            var bicepFile       = fileWithCursors.Replace("|", "");
            var syntaxTree      = SyntaxTree.Create(new Uri("file:///main.bicep"), bicepFile);

            var cursors = new List <int>();

            for (var i = 0; i < fileWithCursors.Length; i++)
            {
                if (fileWithCursors[i] == '|')
                {
                    cursors.Add(i - cursors.Count);
                }
            }

            using var client = await IntegrationTestHelper.StartServerWithTextAsync(bicepFile, syntaxTree.FileUri, resourceTypeProvider : TypeProvider);

            foreach (var cursor in cursors)
            {
                using var assertionScope = new AssertionScope();
                assertionScope.AddReportable(
                    "completion context",
                    PrintHelper.PrintWithAnnotations(syntaxTree, new [] {
                    new PrintHelper.Annotation(new TextSpan(cursor, 0), "cursor position"),
                }, 1, true));

                var completions = await client.RequestCompletion(new CompletionParams
                {
                    TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri),
                    Position     = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, cursor),
                });

                completions.Should().BeEmpty();
            }
        }
コード例 #25
0
        protected static IEnumerable <string> GetAllDiagnostics(string bicepFilePath)
        {
            var dispatcher         = new ModuleDispatcher(new DefaultModuleRegistryProvider(BicepTestConstants.FileResolver));
            var sourceFileGrouping = SourceFileGroupingBuilder.Build(BicepTestConstants.FileResolver, dispatcher, new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath));
            var compilation        = new Compilation(TestTypeHelper.CreateEmptyProvider(), sourceFileGrouping);

            var output = new List <string>();

            foreach (var(bicepFile, diagnostics) in compilation.GetAllDiagnosticsByBicepFile())
            {
                foreach (var diagnostic in diagnostics)
                {
                    var(line, character) = TextCoordinateConverter.GetPosition(bicepFile.LineStarts, diagnostic.Span.Position);
                    var codeDescription = diagnostic.Uri == null ? string.Empty : $" [{diagnostic.Uri.AbsoluteUri}]";
                    output.Add($"{bicepFile.FileUri.LocalPath}({line + 1},{character + 1}) : {diagnostic.Level} {diagnostic.Code}: {diagnostic.Message}{codeDescription}");
                }
            }

            return(output);
        }
コード例 #26
0
        public void GetPosition_EmptyLineStarts_ThrowsArgumentException()
        {
            Action sut = () => TextCoordinateConverter.GetPosition(new List <int>().AsReadOnly(), 10);

            sut.Should().Throw <ArgumentException>().WithMessage("*must not be empty*");
        }
コード例 #27
0
        public async Task VerifyResourceBodyCompletionWithDiscriminatedObjectTypeContainsRequiredPropertiesSnippet()
        {
            string text       = @"resource deploymentScripts 'Microsoft.Resources/deploymentScripts@2020-10-01'=";
            var    syntaxTree = SyntaxTree.Create(new Uri("file:///main.bicep"), text);

            using var client = await IntegrationTestHelper.StartServerWithTextAsync(text, syntaxTree.FileUri, resourceTypeProvider : TypeProvider);

            var completions = await client.RequestCompletion(new CompletionParams
            {
                TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri),
                Position     = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, text.Length),
            });

            completions.Should().SatisfyRespectively(
                c =>
            {
                c.Label.Should().Be("{}");
            },
                c =>
            {
                c.InsertTextFormat.Should().Be(InsertTextFormat.Snippet);
                c.Label.Should().Be("required-properties-AzureCLI");
                c.Detail.Should().Be("Required properties");
                c.TextEdit?.NewText?.Should().BeEquivalentToIgnoringNewlines(@"{
	name: $1
	location: $2
	kind: 'AzureCLI'
	properties: {
		azCliVersion: $3
		retentionInterval: $4
	}
	$0
}");
            },
                c =>
            {
                c.Label.Should().Be("required-properties-AzurePowerShell");
                c.Detail.Should().Be("Required properties");
                c.TextEdit?.NewText?.Should().BeEquivalentToIgnoringNewlines(@"{
	name: $1
	location: $2
	kind: 'AzurePowerShell'
	properties: {
		azPowerShellVersion: $3
		retentionInterval: $4
	}
	$0
}");
            },
                c =>
            {
                c.Label.Should().Be("if");
            },
                c =>
            {
                c.Label.Should().Be("for");
            },
                c =>
            {
                c.Label.Should().Be("for-indexed");
            },
                c =>
            {
                c.Label.Should().Be("for-filtered");
            });
        }