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

+3
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

+2-1
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

+2
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

+4-1
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

+3
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

+26-10
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
}
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

+2-1
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

+2-1
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

+15
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
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"textDocument": {
3+
"uri": "file://abc/123/%E6%A0%91.cs"
4+
}
5+
}

test/Lsp.Tests/Models/DidChangeTextDocumentParamsTests.cs

+23
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,28 @@ public void SimpleTest(string expected)
3333
var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject<DidChangeTextDocumentParams>(expected);
3434
deresult.Should().BeEquivalentTo(model);
3535
}
36+
37+
[Theory, JsonFixture]
38+
public void NonStandardCharactersTest(string expected)
39+
{
40+
var model = new DidChangeTextDocumentParams() {
41+
ContentChanges = new[] {
42+
new TextDocumentContentChangeEvent() {
43+
Range = new Range(new Position(1,1), new Position(2, 2)),
44+
RangeLength = 12,
45+
Text = "abc"
46+
}
47+
},
48+
TextDocument = new VersionedTextDocumentIdentifier() {
49+
Uri = new Uri("C:\\abc\\Mörkö.cs")
50+
}
51+
};
52+
var result = Fixture.SerializeObject(model);
53+
54+
result.Should().Be(expected);
55+
56+
var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject<DidChangeTextDocumentParams>(expected);
57+
deresult.Should().BeEquivalentTo(model);
58+
}
3659
}
3760
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"textDocument": {
3+
"version": 0,
4+
"uri": "file:///C:/abc/M%C3%B6rk%C3%B6.cs"
5+
},
6+
"contentChanges": [
7+
{
8+
"range": {
9+
"start": {
10+
"line": 1,
11+
"character": 1
12+
},
13+
"end": {
14+
"line": 2,
15+
"character": 2
16+
}
17+
},
18+
"rangeLength": 12,
19+
"text": "abc"
20+
}
21+
]
22+
}

test/Lsp.Tests/Models/DidChangeWatchedFilesParamsTests.cs

+20
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,25 @@ public void SimpleTest(string expected)
2929
var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject<DidChangeWatchedFilesParams>(expected);
3030
deresult.Should().BeEquivalentTo(model);
3131
}
32+
33+
[Theory, JsonFixture]
34+
public void NonStandardCharactersTest(string expected)
35+
{
36+
var model = new DidChangeWatchedFilesParams() {
37+
Changes = new[] {
38+
new FileEvent() {
39+
Type = FileChangeType.Created,
40+
// Mörkö
41+
Uri = new Uri("file:///M%C3%B6rk%C3%B6.cs")
42+
}
43+
}
44+
};
45+
var result = Fixture.SerializeObject(model);
46+
47+
result.Should().Be(expected);
48+
49+
var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject<DidChangeWatchedFilesParams>(expected);
50+
deresult.Should().BeEquivalentTo(model);
51+
}
3252
}
3353
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"changes": [
3+
{
4+
"uri": "file:///M%C3%B6rk%C3%B6.cs",
5+
"type": 1
6+
}
7+
]
8+
}

0 commit comments

Comments
 (0)