diff --git a/src/Client/Clients/TextDocumentClient.Definition.cs b/src/Client/Clients/TextDocumentClient.Definition.cs new file mode 100644 index 000000000..d113763d3 --- /dev/null +++ b/src/Client/Clients/TextDocumentClient.Definition.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using OmniSharp.Extensions.LanguageServer.Client.Utilities; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; + +namespace OmniSharp.Extensions.LanguageServer.Client.Clients +{ + /// + /// Client for the LSP Text Document API. + /// + public partial class TextDocumentClient + { + /// + /// Request definition at the specified document position. + /// + /// + /// The full file-system path of the text document. + /// + /// + /// The target line (0-based). + /// + /// + /// The target column (0-based). + /// + /// + /// An optional that can be used to cancel the request. + /// + /// + /// A that resolves to the completions or null if no definitions are available at the specified position. + /// + public Task Definition(string filePath, int line, int column, CancellationToken cancellationToken = default(CancellationToken)) + { + if (string.IsNullOrWhiteSpace(filePath)) + throw new ArgumentException($"Argument cannot be null, empty, or entirely composed of whitespace: {nameof(filePath)}.", nameof(filePath)); + + var documentUri = DocumentUri.FromFileSystemPath(filePath); + + return Definition(documentUri, line, column, cancellationToken); + } + + /// + /// Request definition at the specified document position. + /// + /// + /// The document URI. + /// + /// + /// The target line (0-based). + /// + /// + /// The target column (0-based). + /// + /// + /// An optional that can be used to cancel the request. + /// + /// + /// A that resolves to the completions or null if no definitions are available at the specified position. + /// + public Task Definition(Uri documentUri, int line, int column, CancellationToken cancellationToken = default(CancellationToken)) + { + return PositionalRequest(DocumentNames.Definition, documentUri, line, column, cancellationToken); + } + } +} diff --git a/src/Client/Clients/TextDocumentClient.DocumentHighlights.cs b/src/Client/Clients/TextDocumentClient.DocumentHighlights.cs new file mode 100644 index 000000000..0b2ff9b45 --- /dev/null +++ b/src/Client/Clients/TextDocumentClient.DocumentHighlights.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using OmniSharp.Extensions.LanguageServer.Client.Utilities; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; + +namespace OmniSharp.Extensions.LanguageServer.Client.Clients +{ + /// + /// Client for the LSP Text Document API. + /// + public partial class TextDocumentClient + { + /// + /// Request document highlights at the specified document position. + /// + /// + /// The full file-system path of the text document. + /// + /// + /// The target line (0-based). + /// + /// + /// The target column (0-based). + /// + /// + /// An optional that can be used to cancel the request. + /// + /// + /// A that resolves to the completions or null if no document highlights are available at the specified position. + /// + public Task DocumentHighlights(string filePath, int line, int column, CancellationToken cancellationToken = default(CancellationToken)) + { + if (string.IsNullOrWhiteSpace(filePath)) + throw new ArgumentException($"Argument cannot be null, empty, or entirely composed of whitespace: {nameof(filePath)}.", nameof(filePath)); + + var documentUri = DocumentUri.FromFileSystemPath(filePath); + + return DocumentHighlights(documentUri, line, column, cancellationToken); + } + + /// + /// Request document highlights at the specified document position. + /// + /// + /// The document URI. + /// + /// + /// The target line (0-based). + /// + /// + /// The target column (0-based). + /// + /// + /// An optional that can be used to cancel the request. + /// + /// + /// A that resolves to the completions or null if no document highlights are available at the specified position. + /// + public Task DocumentHighlights(Uri documentUri, int line, int column, CancellationToken cancellationToken = default(CancellationToken)) + { + return PositionalRequest(DocumentNames.DocumentHighlight, documentUri, line, column, cancellationToken); + } + } +} diff --git a/src/Client/Clients/TextDocumentClient.FoldingRanges.cs b/src/Client/Clients/TextDocumentClient.FoldingRanges.cs new file mode 100644 index 000000000..01433477c --- /dev/null +++ b/src/Client/Clients/TextDocumentClient.FoldingRanges.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using OmniSharp.Extensions.LanguageServer.Client.Utilities; +using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; + +namespace OmniSharp.Extensions.LanguageServer.Client.Clients +{ + /// + /// Client for the LSP Text Document API. + /// + public partial class TextDocumentClient + { + /// + /// Request document folding ranges. + /// + /// + /// The full file-system path of the text document. + /// + /// + /// An optional that can be used to cancel the request. + /// + /// + /// A that resolves to the completions or null if no document highlights are available at the specified position. + /// + public Task> FoldingRanges(string filePath, CancellationToken cancellationToken = default(CancellationToken)) + { + if (string.IsNullOrWhiteSpace(filePath)) + throw new ArgumentException($"Argument cannot be null, empty, or entirely composed of whitespace: {nameof(filePath)}.", nameof(filePath)); + + var documentUri = DocumentUri.FromFileSystemPath(filePath); + + return FoldingRanges(documentUri, cancellationToken); + } + + /// + /// Request document highlights at the specified document position. + /// + /// + /// The document URI. + /// + /// + /// The target line (0-based). + /// + /// + /// The target column (0-based). + /// + /// + /// An optional that can be used to cancel the request. + /// + /// + /// A that resolves to the completions or null if no document highlights are available at the specified position. + /// + public async Task> FoldingRanges(Uri documentUri, CancellationToken cancellationToken = default(CancellationToken)) + { + var request = new FoldingRangeRequestParam { + TextDocument = new TextDocumentItem { + Uri = documentUri + } + }; + + return await Client.SendRequest>(DocumentNames.FoldingRange, request, cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/test/Client.Tests/ClientTests.cs b/test/Client.Tests/ClientTests.cs index d14ea297d..4f2eb6afd 100644 --- a/test/Client.Tests/ClientTests.cs +++ b/test/Client.Tests/ClientTests.cs @@ -258,6 +258,162 @@ public async Task SignatureHelp_Success() }); } + /// + /// Ensure that the language client can successfully request Definition. + /// + [Fact(DisplayName = "Language client can successfully request definition", Skip = "Periodic failures")] + public async Task Definition_Success() + { + await Connect(); + + const int line = 5; + const int column = 5; + var expectedDocumentPath = AbsoluteDocumentPath; + var expectedDocumentUri = DocumentUri.FromFileSystemPath(expectedDocumentPath); + + var expectedDefinitions = new LocationOrLocationLinks( + new LocationOrLocationLink(new Location { + Uri = expectedDocumentUri, + Range = new Range { + Start = new Position { + Line = line, + Character = column + }, + End = new Position { + Line = line, + Character = column + } + }, + })); + + ServerDispatcher.HandleRequest(DocumentNames.Definition, (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(expectedDefinitions); + }); + + var definitions = await LanguageClient.TextDocument.Definition(AbsoluteDocumentPath, line, column); + + var actualDefinitions = definitions.ToArray(); + Assert.Collection(actualDefinitions, actualDefinition => { + var expectedDefinition = expectedDefinitions.Single(); + + Assert.NotNull(actualDefinition.Location); + Assert.Equal(expectedDefinition.Location.Uri, actualDefinition.Location.Uri); + + Assert.NotNull(actualDefinition.Location.Range); + Assert.NotNull(actualDefinition.Location.Range.Start); + Assert.NotNull(actualDefinition.Location.Range.End); + Assert.Equal(expectedDefinition.Location.Range.Start.Line, actualDefinition.Location.Range.Start.Line); + Assert.Equal(expectedDefinition.Location.Range.Start.Character, actualDefinition.Location.Range.Start.Character); + Assert.Equal(expectedDefinition.Location.Range.End.Line, actualDefinition.Location.Range.End.Line); + Assert.Equal(expectedDefinition.Location.Range.End.Character, actualDefinition.Location.Range.End.Character); + }); + } + + /// + /// Ensure that the language client can successfully request DocumentHighlight. + /// + [Fact(DisplayName = "Language client can successfully request document highlights", Skip = "Periodic failures")] + public async Task DocumentHighlights_Success() + { + await Connect(); + + const int line = 5; + const int column = 5; + var expectedDocumentPath = AbsoluteDocumentPath; + var expectedDocumentUri = DocumentUri.FromFileSystemPath(expectedDocumentPath); + + var expectedHighlights = new DocumentHighlightContainer( + new DocumentHighlight { + Kind = DocumentHighlightKind.Write, + Range = new Range { + Start = new Position { + Line = line, + Character = column + }, + End = new Position { + Line = line, + Character = column + } + }, + }); + + ServerDispatcher.HandleRequest(DocumentNames.DocumentHighlight, (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(expectedHighlights); + }); + + var definitions = await LanguageClient.TextDocument.DocumentHighlights(AbsoluteDocumentPath, line, column); + + var actualDefinitions = definitions.ToArray(); + Assert.Collection(actualDefinitions, actualHighlight => { + var expectedHighlight = expectedHighlights.Single(); + + Assert.Equal(DocumentHighlightKind.Write, expectedHighlight.Kind); + + Assert.NotNull(actualHighlight.Range); + Assert.NotNull(actualHighlight.Range.Start); + Assert.NotNull(actualHighlight.Range.End); + Assert.Equal(expectedHighlight.Range.Start.Line, actualHighlight.Range.Start.Line); + Assert.Equal(expectedHighlight.Range.Start.Character, actualHighlight.Range.Start.Character); + Assert.Equal(expectedHighlight.Range.End.Line, actualHighlight.Range.End.Line); + Assert.Equal(expectedHighlight.Range.End.Character, actualHighlight.Range.End.Character); + }); + } + + /// + /// Ensure that the language client can successfully request FoldingRanges. + /// + [Fact(DisplayName = "Language client can successfully request document folding ranges", Skip = "Periodic failures")] + public async Task FoldingRanges_Success() + { + await Connect(); + + var expectedDocumentPath = AbsoluteDocumentPath; + var expectedDocumentUri = DocumentUri.FromFileSystemPath(expectedDocumentPath); + + var expectedFoldingRanges = new Container( + new FoldingRange { + Kind = FoldingRangeKind.Region, + StartLine = 5, + StartCharacter = 1, + EndLine = 7, + EndCharacter = 2, + }); + + ServerDispatcher.HandleRequest>(DocumentNames.FoldingRange, (request, cancellationToken) => { + Assert.NotNull(request.TextDocument); + Assert.Equal(expectedDocumentUri, request.TextDocument.Uri); + return Task.FromResult(expectedFoldingRanges); + }); + + var foldingRanges = await LanguageClient.TextDocument.FoldingRanges(AbsoluteDocumentPath); + + var actualFoldingRanges = foldingRanges.ToArray(); + Assert.Collection(actualFoldingRanges, actualFoldingRange => { + var expectedFoldingRange = expectedFoldingRanges.Single(); + + Assert.Equal(FoldingRangeKind.Region, expectedFoldingRange.Kind); + + Assert.Equal(expectedFoldingRange.StartLine, actualFoldingRange.StartLine); + Assert.Equal(expectedFoldingRange.StartCharacter, actualFoldingRange.StartCharacter); + Assert.Equal(expectedFoldingRange.EndLine, actualFoldingRange.EndLine); + Assert.Equal(expectedFoldingRange.EndCharacter, actualFoldingRange.EndCharacter); + }); + } + /// /// Ensure that the language client can successfully receive Diagnostics from the server. ///