diff --git a/src/Protocol/Models/FileEvent.cs b/src/Protocol/Models/FileEvent.cs index 8cc2f5b34..4fd0cc3d7 100644 --- a/src/Protocol/Models/FileEvent.cs +++ b/src/Protocol/Models/FileEvent.cs @@ -1,6 +1,7 @@ using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -12,7 +13,9 @@ public class FileEvent /// /// The file's URI. /// + [JsonConverter(typeof(AbsoluteUriConverter))] public Uri Uri { get; set; } + /// /// The change type. /// diff --git a/src/Protocol/Models/Location.cs b/src/Protocol/Models/Location.cs index fc864a04f..defc56498 100644 --- a/src/Protocol/Models/Location.cs +++ b/src/Protocol/Models/Location.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -10,6 +11,7 @@ public class Location : IEquatable /// /// The uri of the document /// + [JsonConverter(typeof(AbsoluteUriConverter))] public Uri Uri { get; set; } /// diff --git a/src/Protocol/Models/PublishDiagnosticsParams.cs b/src/Protocol/Models/PublishDiagnosticsParams.cs index 04624f448..b089c7188 100644 --- a/src/Protocol/Models/PublishDiagnosticsParams.cs +++ b/src/Protocol/Models/PublishDiagnosticsParams.cs @@ -2,6 +2,7 @@ using OmniSharp.Extensions.Embedded.MediatR; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -10,6 +11,7 @@ public class PublishDiagnosticsParams : IRequest /// /// The URI for which diagnostic information is reported. /// + [JsonConverter(typeof(AbsoluteUriConverter))] public Uri Uri { get; set; } /// diff --git a/src/Protocol/Models/TextDocumentIdentifier.cs b/src/Protocol/Models/TextDocumentIdentifier.cs index 702099099..18f99d855 100644 --- a/src/Protocol/Models/TextDocumentIdentifier.cs +++ b/src/Protocol/Models/TextDocumentIdentifier.cs @@ -1,6 +1,7 @@ using System; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -15,9 +16,11 @@ public TextDocumentIdentifier(Uri uri) { Uri = uri; } + /// /// The text document's URI. /// + [JsonConverter(typeof(AbsoluteUriConverter))] public Uri Uri { get; set; } } } diff --git a/src/Protocol/Models/WorkspaceFolder.cs b/src/Protocol/Models/WorkspaceFolder.cs index 50a6f38fb..b83e7433d 100644 --- a/src/Protocol/Models/WorkspaceFolder.cs +++ b/src/Protocol/Models/WorkspaceFolder.cs @@ -1,4 +1,6 @@ using System; +using Newtonsoft.Json; +using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters; namespace OmniSharp.Extensions.LanguageServer.Protocol.Models { @@ -7,6 +9,7 @@ public class WorkspaceFolder /// /// The associated URI for this workspace folder. /// + [JsonConverter(typeof(AbsoluteUriConverter))] public Uri Uri { get; set; } /// diff --git a/src/Protocol/Serialization/Converters/AbsoluteUriConverter.cs b/src/Protocol/Serialization/Converters/AbsoluteUriConverter.cs new file mode 100644 index 000000000..b9e9abc87 --- /dev/null +++ b/src/Protocol/Serialization/Converters/AbsoluteUriConverter.cs @@ -0,0 +1,57 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// #see https://github.com/NuGet/NuGet.Server +using System; +using Newtonsoft.Json; + +namespace OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters +{ + /// + /// This is necessary because Newtonsoft.Json creates instances with + /// which treats UNC paths as relative. NuGet.Core uses + /// which treats UNC paths as absolute. For more details, see: + /// https://github.com/JamesNK/Newtonsoft.Json/issues/2128 + /// + class AbsoluteUriConverter : JsonConverter + { + public override Uri ReadJson(JsonReader reader, Type objectType, Uri existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + { + return null; + } + else if (reader.TokenType == JsonToken.String) + { + var uri = new Uri((string)reader.Value, UriKind.RelativeOrAbsolute); + if (!uri.IsAbsoluteUri) + { + throw new JsonSerializationException($"The Uri must be absolute. Given: {reader.Value}"); + } + return uri; + } + + throw new JsonSerializationException("The JSON value must be a string."); + } + + public override void WriteJson(JsonWriter writer, Uri value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + if (!(value is Uri uriValue)) + { + throw new JsonSerializationException("The value must be a URI."); + } + + if (!uriValue.IsAbsoluteUri) + { + throw new JsonSerializationException("The URI value must be an absolute Uri. Relative URI instances are not allowed."); + } + + writer.WriteValue(uriValue.ToString()); + } + } +} diff --git a/test/Lsp.Tests/Models/TextDocumentIdentifierTests.cs b/test/Lsp.Tests/Models/TextDocumentIdentifierTests.cs index 8a2fe60bf..074a0d57f 100644 --- a/test/Lsp.Tests/Models/TextDocumentIdentifierTests.cs +++ b/test/Lsp.Tests/Models/TextDocumentIdentifierTests.cs @@ -22,5 +22,41 @@ public void SimpleTest(string expected) var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject(expected); deresult.Should().BeEquivalentTo(model); } + + [Fact] + public void Should_Fail_To_Serialize_When_Given_A_Non_Relative_Uri() + { + var serializer = new Serializer(ClientVersion.Lsp3); + var model = new TextDocumentIdentifier() + { + Uri = new Uri("./abc23.cs", UriKind.Relative), + }; + + Action a = () => serializer.SerializeObject(model); + a.Should().Throw(); + } + + [Fact] + public void Should_Fail_To_Deserialize_When_Given_A_Non_Relative_Uri() + { + var serializer = new Serializer(ClientVersion.Lsp3); + var json = @"{ + ""uri"":""./0b0jnxg2.kgh.ps1"" + }"; + + Action a = () => serializer.DeserializeObject(json); + a.Should().Throw(); + } + + [Fact] + public void Should_Deserialize_For_Example_Value() + { + var serializer = new Serializer(ClientVersion.Lsp3); + var result = serializer.DeserializeObject(@"{ + ""uri"":""file:///Users/tyler/Code/PowerShell/vscode/PowerShellEditorServices/test/PowerShellEditorServices.Test.E2E/bin/Debug/netcoreapp2.1/0b0jnxg2.kgh.ps1"" + }"); + + result.Uri.Should().Be(new Uri("file:///Users/tyler/Code/PowerShell/vscode/PowerShellEditorServices/test/PowerShellEditorServices.Test.E2E/bin/Debug/netcoreapp2.1/0b0jnxg2.kgh.ps1", UriKind.Absolute)); + } } } diff --git a/test/Lsp.Tests/Models/VersionedTextDocumentIdentifierTests.cs b/test/Lsp.Tests/Models/VersionedTextDocumentIdentifierTests.cs index 42b617b62..0ca9c66aa 100644 --- a/test/Lsp.Tests/Models/VersionedTextDocumentIdentifierTests.cs +++ b/test/Lsp.Tests/Models/VersionedTextDocumentIdentifierTests.cs @@ -14,7 +14,8 @@ public class VersionedTextDocumentIdentifierTests [Theory, JsonFixture] public void SimpleTest(string expected) { - var model = new VersionedTextDocumentIdentifier() { + var model = new VersionedTextDocumentIdentifier() + { Uri = new Uri("file:///abc/123.cs"), Version = 12 };