From e3d108f8db24cbfa7cee58c8e238a10ce03caba1 Mon Sep 17 00:00:00 2001 From: Yevgeni Zolotko Date: Mon, 14 Oct 2019 15:27:30 +0300 Subject: [PATCH 1/3] Added Definition support to TextDocumentClient. --- .../Clients/TextDocumentClient.Definition.cs | 66 +++++++++++++++++++ test/Client.Tests/ClientTests.cs | 58 ++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/Client/Clients/TextDocumentClient.Definition.cs diff --git a/src/Client/Clients/TextDocumentClient.Definition.cs b/src/Client/Clients/TextDocumentClient.Definition.cs new file mode 100644 index 000000000..ceadd80a4 --- /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 completions 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 completions 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/test/Client.Tests/ClientTests.cs b/test/Client.Tests/ClientTests.cs index d14ea297d..df4e4d24e 100644 --- a/test/Client.Tests/ClientTests.cs +++ b/test/Client.Tests/ClientTests.cs @@ -258,6 +258,64 @@ 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.First(); + + 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 receive Diagnostics from the server. /// From e7fdd684318c24deeb1b72dadf1a4fc9744ae7ed Mon Sep 17 00:00:00 2001 From: Yevgeni Zolotko Date: Mon, 14 Oct 2019 17:51:29 +0300 Subject: [PATCH 2/3] Added DocumentHighlight support to TextDocumentClient. --- .../Clients/TextDocumentClient.Definition.cs | 4 +- .../TextDocumentClient.DocumentHighlight.cs | 66 +++++++++++++++++++ test/Client.Tests/ClientTests.cs | 59 ++++++++++++++++- 3 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 src/Client/Clients/TextDocumentClient.DocumentHighlight.cs diff --git a/src/Client/Clients/TextDocumentClient.Definition.cs b/src/Client/Clients/TextDocumentClient.Definition.cs index ceadd80a4..d113763d3 100644 --- a/src/Client/Clients/TextDocumentClient.Definition.cs +++ b/src/Client/Clients/TextDocumentClient.Definition.cs @@ -28,7 +28,7 @@ public partial class TextDocumentClient /// An optional that can be used to cancel the request. /// /// - /// A that resolves to the completions or null if no completions are available at the specified position. + /// 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)) { @@ -56,7 +56,7 @@ public partial class TextDocumentClient /// An optional that can be used to cancel the request. /// /// - /// A that resolves to the completions or null if no completions are available at the specified position. + /// 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)) { diff --git a/src/Client/Clients/TextDocumentClient.DocumentHighlight.cs b/src/Client/Clients/TextDocumentClient.DocumentHighlight.cs new file mode 100644 index 000000000..243f792cc --- /dev/null +++ b/src/Client/Clients/TextDocumentClient.DocumentHighlight.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 DocumentHighlight(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 DocumentHighlight(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 DocumentHighlight(Uri documentUri, int line, int column, CancellationToken cancellationToken = default(CancellationToken)) + { + return PositionalRequest(DocumentNames.DocumentHighlight, documentUri, line, column, cancellationToken); + } + } +} diff --git a/test/Client.Tests/ClientTests.cs b/test/Client.Tests/ClientTests.cs index df4e4d24e..b519e3745 100644 --- a/test/Client.Tests/ClientTests.cs +++ b/test/Client.Tests/ClientTests.cs @@ -301,7 +301,7 @@ public async Task Definition_Success() var actualDefinitions = definitions.ToArray(); Assert.Collection(actualDefinitions, actualDefinition => { - var expectedDefinition = expectedDefinitions.First(); + var expectedDefinition = expectedDefinitions.Single(); Assert.NotNull(actualDefinition.Location); Assert.Equal(expectedDefinition.Location.Uri, actualDefinition.Location.Uri); @@ -316,6 +316,63 @@ public async Task Definition_Success() }); } + /// + /// Ensure that the language client can successfully request DocumentHighlight. + /// + [Fact(DisplayName = "Language client can successfully request document highlights", Skip = "Periodic failures")] + public async Task DocumentHighlight_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.DocumentHighlight(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 receive Diagnostics from the server. /// From cebad41331db4cddc3ae867bd0e2521e9719958b Mon Sep 17 00:00:00 2001 From: Yevgeni Zolotko Date: Tue, 15 Oct 2019 19:32:34 +0300 Subject: [PATCH 3/3] Added FoldingRanges support to TextDocumentClient. --- ... TextDocumentClient.DocumentHighlights.cs} | 6 +- .../TextDocumentClient.FoldingRanges.cs | 66 +++++++++++++++++++ test/Client.Tests/ClientTests.cs | 45 ++++++++++++- 3 files changed, 112 insertions(+), 5 deletions(-) rename src/Client/Clients/{TextDocumentClient.DocumentHighlight.cs => TextDocumentClient.DocumentHighlights.cs} (89%) create mode 100644 src/Client/Clients/TextDocumentClient.FoldingRanges.cs diff --git a/src/Client/Clients/TextDocumentClient.DocumentHighlight.cs b/src/Client/Clients/TextDocumentClient.DocumentHighlights.cs similarity index 89% rename from src/Client/Clients/TextDocumentClient.DocumentHighlight.cs rename to src/Client/Clients/TextDocumentClient.DocumentHighlights.cs index 243f792cc..0b2ff9b45 100644 --- a/src/Client/Clients/TextDocumentClient.DocumentHighlight.cs +++ b/src/Client/Clients/TextDocumentClient.DocumentHighlights.cs @@ -30,14 +30,14 @@ public partial class TextDocumentClient /// /// A that resolves to the completions or null if no document highlights are available at the specified position. /// - public Task DocumentHighlight(string filePath, int line, int column, CancellationToken cancellationToken = default(CancellationToken)) + 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 DocumentHighlight(documentUri, line, column, cancellationToken); + return DocumentHighlights(documentUri, line, column, cancellationToken); } /// @@ -58,7 +58,7 @@ public partial class TextDocumentClient /// /// A that resolves to the completions or null if no document highlights are available at the specified position. /// - public Task DocumentHighlight(Uri documentUri, int line, int column, CancellationToken cancellationToken = default(CancellationToken)) + 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 b519e3745..4f2eb6afd 100644 --- a/test/Client.Tests/ClientTests.cs +++ b/test/Client.Tests/ClientTests.cs @@ -320,7 +320,7 @@ public async Task Definition_Success() /// Ensure that the language client can successfully request DocumentHighlight. /// [Fact(DisplayName = "Language client can successfully request document highlights", Skip = "Periodic failures")] - public async Task DocumentHighlight_Success() + public async Task DocumentHighlights_Success() { await Connect(); @@ -355,7 +355,7 @@ public async Task DocumentHighlight_Success() return Task.FromResult(expectedHighlights); }); - var definitions = await LanguageClient.TextDocument.DocumentHighlight(AbsoluteDocumentPath, line, column); + var definitions = await LanguageClient.TextDocument.DocumentHighlights(AbsoluteDocumentPath, line, column); var actualDefinitions = definitions.ToArray(); Assert.Collection(actualDefinitions, actualHighlight => { @@ -373,6 +373,47 @@ public async Task DocumentHighlight_Success() }); } + /// + /// 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. ///