private static async Task <SignatureHelp?> RequestSignatureHelp(ILanguageClient client, Position position, DocumentUri uri, SignatureHelpContext?context = null) => await client.RequestSignatureHelp(new SignatureHelpParams { Position = position, TextDocument = new TextDocumentIdentifier(uri), Context = context ?? new SignatureHelpContext { TriggerKind = SignatureHelpTriggerKind.Invoked, IsRetrigger = false } });
public CompilationContext?GetCompilation(DocumentUri uri) { this.activeContexts.TryGetValue(uri, out var context); return(context); }
public async Task Should_Fallback_To_Original_Configuration() { var(_, server, configuration) = await InitializeWithConfiguration(ConfigureClient, ConfigureServer); var scopedConfiguration = await server.Configuration.GetScopedConfiguration(DocumentUri.From("/my/file.cs"), CancellationToken); configuration.Update("mysection", new Dictionary <string, string> { ["key"] = "value" }); configuration.Update("othersection", new Dictionary <string, string> { ["value"] = "key" }); await server.Configuration.WaitForChange(CancellationToken); await SettleNext(); configuration.Update("mysection", DocumentUri.From("/my/file.cs"), new Dictionary <string, string> { ["key"] = "scopedvalue" }); configuration.Update("othersection", DocumentUri.From("/my/file.cs"), new Dictionary <string, string> { ["value"] = "scopedkey" }); await server.Configuration.WaitForChange(CancellationToken); await SettleNext(); server.Configuration["mysection:key"].Should().Be("value"); scopedConfiguration["mysection:key"].Should().Be("scopedvalue"); server.Configuration["othersection:value"].Should().Be("key"); scopedConfiguration["othersection:value"].Should().Be("scopedkey"); configuration.Update("mysection", DocumentUri.From("/my/file.cs"), new Dictionary <string, string>()); configuration.Update("othersection", DocumentUri.From("/my/file.cs"), new Dictionary <string, string>()); await scopedConfiguration.WaitForChange(CancellationToken); await SettleNext(); await TestHelper.DelayUntil(() => scopedConfiguration["mysection:key"] == "value", CancellationToken); scopedConfiguration["mysection:key"].Should().Be("value"); scopedConfiguration["othersection:value"].Should().Be("key"); }
public async Task Diagnostics_Success() { await Connect(); var documentPath = AbsoluteDocumentPath; var expectedDocumentUri = DocumentUri.FromFileSystemPath(documentPath); var expectedDiagnostics = new List <Diagnostic> { new Diagnostic { Source = "Test", Code = new DiagnosticCode(1234), Message = "This is a diagnostic message.", Range = new Range { Start = new Position { Line = 2, Character = 5 }, End = new Position { Line = 3, Character = 7 } }, Severity = DiagnosticSeverity.Warning } }; var receivedDiagnosticsNotification = new TaskCompletionSource <object>(); Uri actualDocumentUri = null; List <Diagnostic> actualDiagnostics = null; LanguageClient.TextDocument.OnPublishDiagnostics((documentUri, diagnostics) => { actualDocumentUri = documentUri; actualDiagnostics = diagnostics; receivedDiagnosticsNotification.SetResult(null); }); ServerConnection.SendNotification(DocumentNames.PublishDiagnostics, new PublishDiagnosticsParams { Uri = DocumentUri.FromFileSystemPath(documentPath), Diagnostics = expectedDiagnostics }); // Timeout. var winner = await Task.WhenAny( receivedDiagnosticsNotification.Task, Task.Delay( TimeSpan.FromSeconds(2) ) ); Assert.Same(receivedDiagnosticsNotification.Task, winner); Assert.NotNull(actualDocumentUri); Assert.Equal(expectedDocumentUri, actualDocumentUri); Assert.NotNull(actualDiagnostics); Assert.Equal(1, actualDiagnostics.Count); var expectedDiagnostic = expectedDiagnostics[0]; var actualDiagnostic = actualDiagnostics[0]; Assert.Equal(expectedDiagnostic.Code, actualDiagnostic.Code); Assert.Equal(expectedDiagnostic.Message, actualDiagnostic.Message); Assert.Equal(expectedDiagnostic.Range.Start.Line, actualDiagnostic.Range.Start.Line); Assert.Equal(expectedDiagnostic.Range.Start.Character, actualDiagnostic.Range.Start.Character); Assert.Equal(expectedDiagnostic.Range.End.Line, actualDiagnostic.Range.End.Line); Assert.Equal(expectedDiagnostic.Range.End.Character, actualDiagnostic.Range.End.Character); Assert.Equal(expectedDiagnostic.Severity, actualDiagnostic.Severity); Assert.Equal(expectedDiagnostic.Source, actualDiagnostic.Source); }
private (ImmutableArray <SyntaxTree> added, ImmutableArray <SyntaxTree> removed) UpdateCompilationInternal(DocumentUri documentUri, int?version) { try { var context = this.provider.Create(workspace, documentUri); var output = workspace.UpsertSyntaxTrees(context.Compilation.SyntaxTreeGrouping.SyntaxTrees); // there shouldn't be concurrent upsert requests (famous last words...), so a simple overwrite should be sufficient this.activeContexts[documentUri] = context; // convert all the diagnostics to LSP diagnostics var diagnostics = GetDiagnosticsFromContext(context).ToDiagnostics(context.LineStarts); // publish all the diagnostics this.PublishDocumentDiagnostics(documentUri, version, diagnostics); return(output); } catch (Exception exception) { // this is a fatal error likely due to a code defect // publish a single fatal error diagnostic to tell the user something horrible has occurred // TODO: Tell user how to create an issue on GitHub. var fatalError = new OmniSharp.Extensions.LanguageServer.Protocol.Models.Diagnostic { Range = new Range { Start = new Position(0, 0), End = new Position(1, 0), }, Severity = DiagnosticSeverity.Error, Message = exception.Message, Code = new DiagnosticCode("Fatal") }; // the file is no longer in a state that can be parsed // clear all info to prevent cascading failures elsewhere var closedTrees = CloseCompilationInternal(documentUri, version, fatalError.AsEnumerable()); return(ImmutableArray <SyntaxTree> .Empty, closedTrees); } }
private record CompletionNotification(ICompilationManager CompilationManager, DocumentUri Uri);
public async Task Completions_Success() { await Connect(); const int line = 5; const int column = 5; var expectedDocumentPath = AbsoluteDocumentPath; var expectedDocumentUri = DocumentUri.FromFileSystemPath(expectedDocumentPath); var expectedCompletionItems = new CompletionItem[] { new CompletionItem { Kind = CompletionItemKind.Class, Label = "Class1", TextEdit = new TextEdit { Range = new Range { Start = new Position { Line = line, Character = column }, End = new Position { Line = line, Character = column } }, NewText = "Class1", } } }; ServerDispatcher.HandleRequest <TextDocumentPositionParams, CompletionList>(DocumentNames.Completion, (request, cancellationToken) => { Assert.NotNull(request.TextDocument); Assert.Equal(expectedDocumentUri, request.TextDocument.Uri); Assert.Equal(line, request.Position.Line); Assert.Equal(column, request.Position.Character); return(Task.FromResult(new CompletionList( expectedCompletionItems, isIncomplete: true ))); }); var actualCompletions = await LanguageClient.TextDocument.Completions(AbsoluteDocumentPath, line, column); Assert.True(actualCompletions.IsIncomplete, "completions.IsIncomplete"); Assert.NotNull(actualCompletions.Items); var actualCompletionItems = actualCompletions.Items.ToArray(); Assert.Collection(actualCompletionItems, actualCompletionItem => { var expectedCompletionItem = expectedCompletionItems[0]; Assert.Equal(expectedCompletionItem.Kind, actualCompletionItem.Kind); Assert.Equal(expectedCompletionItem.Label, actualCompletionItem.Label); Assert.NotNull(actualCompletionItem.TextEdit); Assert.Equal(expectedCompletionItem.TextEdit.NewText, actualCompletionItem.TextEdit.NewText); Assert.NotNull(actualCompletionItem.TextEdit.Range); Assert.NotNull(actualCompletionItem.TextEdit.Range.Start); Assert.NotNull(actualCompletionItem.TextEdit.Range.End); Assert.Equal(expectedCompletionItem.TextEdit.Range.Start.Line, actualCompletionItem.TextEdit.Range.Start.Line); Assert.Equal(expectedCompletionItem.TextEdit.Range.Start.Character, actualCompletionItem.TextEdit.Range.Start.Character); Assert.Equal(expectedCompletionItem.TextEdit.Range.End.Line, actualCompletionItem.TextEdit.Range.End.Line); Assert.Equal(expectedCompletionItem.TextEdit.Range.End.Character, actualCompletionItem.TextEdit.Range.End.Character); }); }
public void Should_Normalize_Unix_Uris(Uri uri, string expected) { _testOutputHelper.WriteLine($"Given: {uri}"); _testOutputHelper.WriteLine($"Expected: {expected}"); DocumentUri.GetFileSystemPath(uri).Should().Be(expected); }
public void Should_Handle_Unix_File_System_Paths(string uri, DocumentUri expected) { _testOutputHelper.WriteLine($"Given: {uri}"); _testOutputHelper.WriteLine($"Expected: {expected}"); new DocumentUri(uri).Should().Be(expected); }
public void Should_Handle_Unix_Uris(Uri uri, DocumentUri expected) { _testOutputHelper.WriteLine($"Given: {uri}"); _testOutputHelper.WriteLine($"Expected: {expected}"); DocumentUri.From(uri).Should().Be(expected); }
public void Should_Normalize_Unix_FileSystem_Paths(string uri, DocumentUri expected) { _testOutputHelper.WriteLine($"Given: {uri}"); _testOutputHelper.WriteLine($"Expected: {expected}"); DocumentUri.FromFileSystemPath(uri).Should().Be(expected); }
public void Should_Handle_Windows_Alt_String_Uris(string uri, DocumentUri expected) { _testOutputHelper.WriteLine($"Given: {uri}"); _testOutputHelper.WriteLine($"Expected: {expected}"); new DocumentUri(uri).Should().Be(expected); }
public SymbolLocation(DocumentUri uri, Range name, Range declaration) { Uri = uri; Name = name; Declaration = declaration; }
public async Task NonExistentUriShouldProvideNoSignatureHelp() { using var client = await IntegrationTestHelper.StartServerWithTextAsync(this.TestContext, string.Empty, DocumentUri.From("/fake.bicep")); var signatureHelp = await RequestSignatureHelp(client, new Position(0, 0), DocumentUri.From("/fake2.bicep")); signatureHelp.Should().BeNull(); }
public void HandleBicepConfigCloseEvent(DocumentUri documentUri) { activeBicepConfigCache.TryRemove(documentUri, out _); }
public async Task DidOpenTextDocument_should_trigger_PublishDiagnostics() { var documentUri = DocumentUri.From("/template.bicep"); var diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); using var helper = await LanguageServerHelper.StartServerWithClientConnectionAsync(this.TestContext, options => { options.OnPublishDiagnostics(diags => { diagsReceived.SetResult(diags); }); }); var client = helper.Client; // open document client.TextDocument.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, @" param myParam string = 2 resource myRes 'invalidFormat' = { } randomToken ", 1)); var response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task); response.Diagnostics.Should().SatisfyRespectively( d => { d.Range.Should().HaveRange((1, 6), (1, 13)); // note documentation pretty printing moves Uri to code for output d.Should().HaveCodeAndSeverity(new NoUnusedParametersRule().Uri !.AbsoluteUri, DiagnosticSeverity.Warning); }, d => { d.Range.Should().HaveRange((1, 23), (1, 24)); d.Should().HaveCodeAndSeverity("BCP027", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((2, 15), (2, 30)); d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((5, 0), (5, 11)); d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error); } ); // change document diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); client.TextDocument.DidChangeTextDocument(TextDocumentParamHelper.CreateDidChangeTextDocumentParams(documentUri, @" param myParam string = 'fixed!' resource myRes 'invalidFormat' = { } randomToken ", 2)); response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task); response.Diagnostics.Should().SatisfyRespectively( d => { d.Range.Should().HaveRange((1, 6), (1, 13)); // documentation provided with linter sets code to uri for pretty link print outs d.Should().HaveCodeAndSeverity(new NoUnusedParametersRule().Uri !.AbsoluteUri, DiagnosticSeverity.Warning); }, d => { d.Range.Should().HaveRange((2, 15), (2, 30)); d.Should().HaveCodeAndSeverity("BCP029", DiagnosticSeverity.Error); }, d => { d.Range.Should().HaveRange((5, 0), (5, 11)); d.Should().HaveCodeAndSeverity("BCP007", DiagnosticSeverity.Error); } ); // close document diagsReceived = new TaskCompletionSource <PublishDiagnosticsParams>(); client.TextDocument.DidCloseTextDocument(TextDocumentParamHelper.CreateDidCloseTextDocumentParams(documentUri, 3)); response = await IntegrationTestHelper.WithTimeoutAsync(diagsReceived.Task); response.Diagnostics.Should().BeEmpty(); }
private record QueueItem(ICompilationManager CompilationManager, DocumentUri Uri, ImmutableArray <ModuleReference> ModuleReferences, RootConfiguration Configuration);
private List <TextDocumentAttributes> GetTextDocumentAttributes(DocumentUri uri) => _textDocumentIdentifiers .Select(x => x.GetTextDocumentAttributes(uri)) .Where(x => x != null) .ToList();
/// <summary> /// Starts a language client/server pair that will load the specified Bicep text and wait for the diagnostics to be published. /// No further file opening is possible. /// </summary> /// <param name="testContext">The test context</param> /// <param name="text">The bicep text</param> /// <param name="documentUri">The document URI of the Bicep text</param> /// <param name="onClientOptions">The additional client options</param> /// <param name="creationOptions">The server creation options</param> public static async Task <LanguageServerHelper> StartServerWithTextAsync(TestContext testContext, string text, DocumentUri documentUri, Action <LanguageClientOptions>?onClientOptions = null, Server.CreationOptions?creationOptions = null) { var diagnosticsPublished = new TaskCompletionSource <PublishDiagnosticsParams>(); creationOptions ??= new Server.CreationOptions(); creationOptions = creationOptions with { FileResolver = creationOptions.FileResolver ?? new InMemoryFileResolver(new Dictionary <Uri, string> { [documentUri.ToUri()] = text, }), ModuleRestoreScheduler = creationOptions.ModuleRestoreScheduler ?? BicepTestConstants.ModuleRestoreScheduler }; var helper = await LanguageServerHelper.StartServerWithClientConnectionAsync( testContext, options => { onClientOptions?.Invoke(options); options.OnPublishDiagnostics(p => { testContext.WriteLine($"Received {p.Diagnostics.Count()} diagnostic(s)."); diagnosticsPublished.SetResult(p); }); }, creationOptions); // send open document notification helper.Client.DidOpenTextDocument(TextDocumentParamHelper.CreateDidOpenDocumentParams(documentUri, text, 0)); testContext.WriteLine($"Opened file {documentUri}."); // notifications don't produce responses, // but our server should send us diagnostics when it receives the notification await IntegrationTestHelper.WithTimeoutAsync(diagnosticsPublished.Task); return(helper); }
public static LanguageClientOptions WithRootUri(this LanguageClientOptions options, DocumentUri rootUri) { options.RootUri = rootUri; return(options); }
public async Task SignatureHelp_Success() { await Connect(); const int line = 5; const int column = 5; var expectedDocumentPath = AbsoluteDocumentPath; var expectedDocumentUri = DocumentUri.FromFileSystemPath(expectedDocumentPath); var expectedSignatureHelp = new SignatureHelp { ActiveParameter = 0, ActiveSignature = 0, Signatures = new[] { new SignatureInformation { Documentation = new StringOrMarkupContent("test documentation"), Label = "TestSignature", Parameters = new[] { new ParameterInformation { Documentation = "test parameter documentation", Label = "parameter label" } } } } }; ServerDispatcher.HandleRequest <TextDocumentPositionParams, SignatureHelp>(DocumentNames.SignatureHelp, (request, cancellationToken) => { Assert.NotNull(request.TextDocument); Assert.Equal(expectedDocumentUri, request.TextDocument.Uri); Assert.Equal(line, request.Position.Line); Assert.Equal(column, request.Position.Character); return(Task.FromResult(expectedSignatureHelp)); }); var actualSignatureHelp = await LanguageClient.TextDocument.SignatureHelp(AbsoluteDocumentPath, line, column); Assert.Equal(expectedSignatureHelp.ActiveParameter, actualSignatureHelp.ActiveParameter); Assert.Equal(expectedSignatureHelp.ActiveSignature, actualSignatureHelp.ActiveSignature); var actualSignatures = actualSignatureHelp.Signatures.ToArray(); Assert.Collection(actualSignatures, actualSignature => { var expectedSignature = expectedSignatureHelp.Signatures.ToArray()[0]; Assert.True(actualSignature.Documentation.HasString); Assert.Equal(expectedSignature.Documentation.String, actualSignature.Documentation.String); Assert.Equal(expectedSignature.Label, actualSignature.Label); var expectedParameters = expectedSignature.Parameters.ToArray(); var actualParameters = actualSignature.Parameters.ToArray(); Assert.Collection(actualParameters, actualParameter => { var expectedParameter = expectedParameters[0]; Assert.True(actualParameter.Documentation.HasString); Assert.Equal(expectedParameter.Documentation.String, actualParameter.Documentation.String); Assert.Equal(expectedParameter.Label, actualParameter.Label); }); }); }
public static LanguageClientOptions WithWorkspaceFolder(this LanguageClientOptions options, DocumentUri documentUri, string name) { options.Services.AddSingleton(new WorkspaceFolder { Name = name, Uri = documentUri }); return(options); }
public TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri) { return(new TextDocumentAttributes(uri, "ostw")); }
public TextDocumentIdentifier(DocumentUri uri) { Uri = uri; }
public void CloseCompilation(DocumentUri documentUri) { // close and clear diagnostics for the file // if upsert failed to create a compilation due to a fatal error, we still need to clean up the diagnostics CloseCompilationInternal(documentUri, 0, Enumerable.Empty <OmniSharp.Extensions.LanguageServer.Protocol.Models.Diagnostic>()); }
private string GenerateCompiledFileAndReturnBuildOutputMessage(string bicepFilePath, DocumentUri documentUri) { string compiledFilePath = PathHelper.GetDefaultBuildOutputPath(bicepFilePath); string compiledFile = Path.GetFileName(compiledFilePath); // If the template exists and contains bicep generator metadata, we can go ahead and replace the file. // If not, we'll fail the build. if (File.Exists(compiledFilePath) && !TemplateContainsBicepGeneratorMetadata(File.ReadAllText(compiledFilePath))) { return("Build failed. The file \"" + compiledFile + "\" already exists and was not generated by Bicep. If overwriting the file is intended, delete it manually and retry the Build command."); } var fileUri = documentUri.ToUri(); RootConfiguration?configuration = null; try { configuration = this.configurationManager.GetConfiguration(fileUri); } catch (ConfigurationException exception) { // Fail the build if there's configuration errors. return(exception.Message); } CompilationContext?context = compilationManager.GetCompilation(fileUri); Compilation compilation; if (context is null) { SourceFileGrouping sourceFileGrouping = SourceFileGroupingBuilder.Build(this.fileResolver, this.moduleDispatcher, new Workspace(), fileUri, configuration); compilation = new Compilation(namespaceProvider, sourceFileGrouping, configuration, new LinterAnalyzer(configuration)); } else { compilation = context.Compilation; } KeyValuePair <BicepFile, IEnumerable <IDiagnostic> > diagnosticsByFile = compilation.GetAllDiagnosticsByBicepFile() .FirstOrDefault(x => x.Key.FileUri == fileUri); if (diagnosticsByFile.Value.Any(x => x.Level == DiagnosticLevel.Error)) { return(GetDiagnosticsMessage(diagnosticsByFile)); } using var fileStream = new FileStream(compiledFilePath, FileMode.Create, FileAccess.ReadWrite); var emitter = new TemplateEmitter(compilation.GetEntrypointSemanticModel(), emitterSettings); EmitResult result = emitter.Emit(fileStream); return("Build succeeded. Created file " + compiledFile); }
public override TextDocumentAttributes GetTextDocumentAttributes(DocumentUri uri) { return(new TextDocumentAttributes(uri, LanguageConstants.LanguageId)); }
public void HandleBicepConfigChangeEvent(DocumentUri documentUri) { HandleBicepConfigOpenOrChangeEvent(documentUri); }
internal static RazorCodeAction CreateAddUsingCodeAction(string fullyQualifiedName, DocumentUri uri) { var @namespace = GetNamespaceFromFQN(fullyQualifiedName); if (string.IsNullOrEmpty(@namespace)) { return(null); } var actionParams = new AddUsingsCodeActionParams { Uri = uri, Namespace = @namespace }; var resolutionParams = new RazorCodeActionResolutionParams { Action = LanguageServerConstants.CodeActions.AddUsing, Language = LanguageServerConstants.CodeActions.Languages.Razor, Data = actionParams, }; return(new RazorCodeAction() { Title = $"@using {@namespace}", Data = JToken.FromObject(resolutionParams) }); }
/// <summary> /// Request completions at the specified document position. /// </summary> /// <param name="documentUri"> /// The document URI. /// </param> /// <param name="line"> /// The target line (0-based). /// </param> /// <param name="column"> /// The target column (0-based). /// </param> /// <param name="cancellationToken"> /// An optional <see cref="CancellationToken"/> that can be used to cancel the request. /// </param> /// <returns> /// A <see cref="Task{TResult}"/> that resolves to the completions or <c>null</c> if no completions are available at the specified position. /// </returns> public Task <CompletionList> Completions(DocumentUri documentUri, int line, int column, CancellationToken cancellationToken = default(CancellationToken)) { return(PositionalRequest <CompletionList>(DocumentNames.Completion, documentUri, line, column, cancellationToken)); }