Skip to content

Commit f29c0c5

Browse files
TylerLeonhardtbjorkstromm
authored andcommitted
more tests and more models using the converter
1 parent bea830b commit f29c0c5

15 files changed

+214
-14
lines changed

src/Protocol/Models/ConfigurationItem.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
using System;
2+
using Newtonsoft.Json;
23
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
4+
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters;
35

46
namespace OmniSharp.Extensions.LanguageServer.Protocol.Models
57
{
68
public class ConfigurationItem
79
{
810
[Optional]
11+
[JsonConverter(typeof(AbsoluteUriConverter))]
912
public Uri ScopeUri { get; set; }
1013
[Optional]
1114
public string Section { get; set; }

src/Protocol/Models/DocumentLink.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
using MediatR;
33
using Newtonsoft.Json;
44
using Newtonsoft.Json.Linq;
5-
using Newtonsoft.Json.Serialization;
65
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
6+
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters;
77

88
namespace OmniSharp.Extensions.LanguageServer.Protocol.Models
99
{
@@ -23,6 +23,7 @@ public class DocumentLink : ICanBeResolved, IRequest<DocumentLink>
2323
/// The uri this link points to. If missing a resolve request is sent later.
2424
/// </summary>
2525
[Optional]
26+
[JsonConverter(typeof(AbsoluteUriConverter))]
2627
public Uri Target { get; set; }
2728

2829
/// </summary>

src/Protocol/Models/InitializeParams.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Newtonsoft.Json.Serialization;
55
using OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities;
66
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
7+
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters;
78

89
namespace OmniSharp.Extensions.LanguageServer.Protocol.Models
910
{
@@ -34,6 +35,7 @@ public string RootPath
3435
/// folder is open. If both `rootPath` and `rootUri` are set
3536
/// `rootUri` wins.
3637
/// </summary>
38+
[JsonConverter(typeof(AbsoluteUriConverter))]
3739
public Uri RootUri { get; set; }
3840

3941
/// <summary>

src/Protocol/Models/LocationLink.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
2+
using Newtonsoft.Json;
23
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
4+
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters;
35

46
namespace OmniSharp.Extensions.LanguageServer.Protocol.Models
57
{
@@ -17,6 +19,7 @@ public class LocationLink
1719
/// <summary>
1820
/// The target resource identifier of this link.
1921
/// </summary>
22+
[JsonConverter(typeof(AbsoluteUriConverter))]
2023
public Uri TargetUri { get; set; }
2124

2225
/// <summary>
@@ -32,4 +35,4 @@ public class LocationLink
3235
/// </summary>
3336
public Range TargetSelectionRange { get; set; }
3437
}
35-
}
38+
}

src/Protocol/Models/WorkspaceEdit.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
using System;
22
using System.Collections.Generic;
3+
using Newtonsoft.Json;
34
using Newtonsoft.Json.Serialization;
45
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization;
6+
using OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters;
57

68
namespace OmniSharp.Extensions.LanguageServer.Protocol.Models
79
{
@@ -11,6 +13,7 @@ public class WorkspaceEdit
1113
/// Holds changes to existing resources.
1214
/// </summary>
1315
[Optional]
16+
[JsonConverter(typeof(DictionaryUriConverter<Uri, IEnumerable<TextEdit>>))]
1417
public IDictionary<Uri, IEnumerable<TextEdit>> Changes { get; set; }
1518
/// <summary>
1619
/// An array of `TextDocumentEdit`s to express changes to n different text documents

src/Protocol/Serialization/Converters/AbsoluteUriConverter.cs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// 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.
33
// #see https://github.com/NuGet/NuGet.Server
44
using System;
5+
using System.Text;
56
using Newtonsoft.Json;
67

78
namespace OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters
@@ -46,26 +47,41 @@ public override void WriteJson(JsonWriter writer, Uri value, JsonSerializer seri
4647
throw new JsonSerializationException("The value must be a URI.");
4748
}
4849

49-
if (!uriValue.IsAbsoluteUri)
50+
writer.WriteValue(Convert(uriValue));
51+
}
52+
53+
public static string Convert(Uri uri)
54+
{
55+
if (!uri.IsAbsoluteUri)
5056
{
5157
throw new JsonSerializationException("The URI value must be an absolute Uri. Relative URI instances are not allowed.");
5258
}
5359

54-
if (uriValue.IsFile)
60+
if (uri.IsFile)
5561
{
56-
// Regular file paths
57-
if (uriValue.HostNameType == UriHostNameType.Basic)
62+
// First add the file scheme and ://
63+
var builder = new StringBuilder(uri.Scheme)
64+
.Append("://");
65+
66+
// UNC file paths use the Host
67+
if (uri.HostNameType != UriHostNameType.Basic)
5868
{
59-
writer.WriteValue($"{uriValue.Scheme}://{uriValue.PathAndQuery}");
60-
return;
69+
builder.Append(uri.Host);
6170
}
6271

63-
// UNC file paths
64-
writer.WriteValue($"{uriValue.Scheme}://{uriValue.Host}{uriValue.PathAndQuery}");
65-
return;
72+
// Paths that start with a drive letter don't have a slash in the PathAndQuery
73+
// but they need it in the final result.
74+
if (uri.PathAndQuery[0] != '/')
75+
{
76+
builder.Append('/');
77+
}
78+
79+
// Lastly add the remaining parts of the URL
80+
builder.Append(uri.PathAndQuery);
81+
return builder.ToString();
6682
}
6783

68-
writer.WriteValue(uriValue.AbsoluteUri);
84+
return uri.AbsoluteUri;
6985
}
7086
}
7187
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Newtonsoft.Json;
4+
5+
namespace OmniSharp.Extensions.LanguageServer.Protocol.Serialization.Converters
6+
{
7+
/// <summary>
8+
/// This is necessary because Newtonsoft.Json creates <see cref="Uri"/> instances with
9+
/// <see cref="UriKind.RelativeOrAbsolute"/> which treats UNC paths as relative. NuGet.Core uses
10+
/// <see cref="UriKind.Absolute"/> which treats UNC paths as absolute. For more details, see:
11+
/// https://github.com/JamesNK/Newtonsoft.Json/issues/2128
12+
/// </summary><
13+
class DictionaryUriConverter<TKey, TValue> :
14+
JsonConverter<Dictionary<TKey, TValue>> where TKey : Uri
15+
{
16+
private readonly AbsoluteUriConverter _uriConverter = new AbsoluteUriConverter();
17+
18+
public override Dictionary<TKey, TValue> ReadJson(
19+
JsonReader reader,
20+
Type objectType,
21+
Dictionary<TKey, TValue> existingValue,
22+
bool hasExistingValue,
23+
JsonSerializer serializer)
24+
{
25+
if (reader.TokenType != JsonToken.StartObject)
26+
{
27+
throw new JsonException();
28+
}
29+
30+
Dictionary<TKey, TValue> value = new Dictionary<TKey, TValue>();
31+
32+
while (reader.Read())
33+
{
34+
if (reader.TokenType == JsonToken.EndObject)
35+
{
36+
return value;
37+
}
38+
39+
// Get the key.
40+
if (reader.TokenType != JsonToken.PropertyName)
41+
{
42+
throw new JsonException();
43+
}
44+
45+
// Get the stringified Uri.
46+
string propertyName = (string) reader.Value;
47+
reader.Read();
48+
49+
Uri key = new Uri(propertyName, UriKind.Absolute);
50+
51+
// Get the value.
52+
TValue v = serializer.Deserialize<TValue>(reader);
53+
54+
// Add to dictionary.
55+
value.Add((TKey) key, v);
56+
}
57+
58+
throw new JsonException();
59+
}
60+
61+
public override void WriteJson(
62+
JsonWriter writer,
63+
Dictionary<TKey, TValue> value,
64+
JsonSerializer serializer)
65+
{
66+
writer.WriteStartObject();
67+
68+
foreach (KeyValuePair<TKey, TValue> kvp in value)
69+
{
70+
writer.WritePropertyName(AbsoluteUriConverter.Convert(kvp.Key));
71+
serializer.Serialize(writer, kvp.Value);
72+
}
73+
74+
writer.WriteEndObject();
75+
}
76+
}
77+
}

test/Lsp.Tests/Models/ApplyWorkspaceEditParamsTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ public void NonStandardCharactersTest(string expected)
5252
{
5353
Changes = new Dictionary<Uri, IEnumerable<TextEdit>>() {
5454
{
55-
new Uri("/abc/123/Mörkö.cs"), new [] {
55+
// Mörkö
56+
new Uri("file:///abc/123/M%C3%B6rk%C3%B6.cs"), new [] {
5657
new TextEdit() {
5758
NewText = "new text",
5859
Range = new Range(new Position(1, 1), new Position(2,2))

test/Lsp.Tests/Models/CodeActionParamsTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ public void NonStandardCharactersTest(string expected)
5454
},
5555
Range = new Range(new Position(1, 1), new Position(2, 2)),
5656
TextDocument = new TextDocumentIdentifier() {
57-
Uri = new Uri("/test/123/树.cs")
57+
// 树 - Chinese for tree
58+
Uri = new Uri("file:///test/123/%E6%A0%91.cs")
5859
}
5960
};
6061
var result = Fixture.SerializeObject(model);

test/Lsp.Tests/Models/CodeLensParamsTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,20 @@ public void SimpleTest(string expected)
2424
var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject<CodeLensParams>(expected);
2525
deresult.Should().BeEquivalentTo(model);
2626
}
27+
28+
[Theory, JsonFixture]
29+
public void NonStandardCharactersTest(string expected)
30+
{
31+
var model = new CodeLensParams() {
32+
// UNC path with Chinese character for tree.
33+
TextDocument = new TextDocumentIdentifier(new Uri("\\\\abc\\123\\树.cs")),
34+
};
35+
var result = Fixture.SerializeObject(model);
36+
37+
result.Should().Be(expected);
38+
39+
var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject<CodeLensParams>(expected);
40+
deresult.Should().BeEquivalentTo(model);
41+
}
2742
}
2843
}

0 commit comments

Comments
 (0)