Skip to content

Commit c52c154

Browse files
committed
LSP Protocol: fix FormattingOptions.OtherOptions serialization
1 parent e68ff75 commit c52c154

17 files changed

+387
-0
lines changed

src/LanguageServer/Protocol/LanguageServerProtocolResources.Designer.cs

Lines changed: 33 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LanguageServer/Protocol/LanguageServerProtocolResources.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,15 @@
101101
<data name="DocumentUriSerializationError" xml:space="preserve">
102102
<value>Unable to deserialize Uri. Unexpected value encountered: {0}</value>
103103
</data>
104+
<data name="FormattingOptionsEncounteredInvalidToken" xml:space="preserve">
105+
<value>Unable to deserialize FormattingOptions. Invalid token: {0}</value>
106+
</data>
107+
<data name="FormattingOptionsEndedUnexpectedly" xml:space="preserve">
108+
<value>Unable to deserialize FormattingOptions as it ended unexpectedly</value>
109+
</data>
110+
<data name="FormattingOptionsMissingRequiredProperty" xml:space="preserve">
111+
<value>Unable to deserialize FormattingOptions. Missing required property: {0}</value>
112+
</data>
104113
<data name="MarkupContentSerializationError" xml:space="preserve">
105114
<value>Unable to deserialize MarkupContent. Unexpected value encountered: {0}</value>
106115
</data>

src/LanguageServer/Protocol/Protocol/FormattingOptions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ namespace Roslyn.LanguageServer.Protocol
1313
/// See the <see href="https://microsoft.github.io/language-server-protocol/specifications/specification-current/#formattingOptions">Language Server Protocol specification</see> for additional information.
1414
/// </para>
1515
/// </summary>
16+
// NOTE: The FormattingOptionsConverter enables the FormattingOptions.OtherOptions JsonExtensionData to be strongly typed.
17+
// The Json* attributes on the members are for reference only - the converter is fully custom and ignores them.
18+
[JsonConverter(typeof(FormattingOptionsConverter))]
1619
internal class FormattingOptions
1720
{
1821
/// <summary>
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Diagnostics;
8+
using System.Diagnostics.CodeAnalysis;
9+
using System.Globalization;
10+
using System.Text.Json;
11+
using System.Text.Json.Serialization;
12+
using Microsoft.CodeAnalysis.LanguageServer;
13+
14+
namespace Roslyn.LanguageServer.Protocol;
15+
16+
/// <summary>Enables the FormattingOptions.OtherOptions JsonExtensionData to be strongly typed</summary>
17+
internal class FormattingOptionsConverter : JsonConverter<FormattingOptions>
18+
{
19+
public override FormattingOptions? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
20+
{
21+
var result = new FormattingOptions();
22+
23+
Debug.Assert(reader.TokenType == JsonTokenType.StartObject);
24+
25+
static void ReadSkippingComments(ref Utf8JsonReader reader)
26+
{
27+
do
28+
{
29+
if (!reader.Read())
30+
{
31+
throw new JsonException(LanguageServerProtocolResources.FormattingOptionsEndedUnexpectedly);
32+
}
33+
}
34+
while (reader.TokenType == JsonTokenType.Comment);
35+
}
36+
37+
[DoesNotReturn]
38+
static T ThrowMissingRequiredProperty<T>(string propertyName)
39+
{
40+
throw new JsonException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.FormattingOptionsMissingRequiredProperty, propertyName));
41+
}
42+
43+
int? tabSize = null;
44+
bool? insertSpaces = null;
45+
bool trimTrailingWhitespace = false;
46+
bool insertFinalNewline = false;
47+
bool trimFinalNewlines = false;
48+
Dictionary<string, SumType<bool, int, string>>? otherOptions = null;
49+
50+
while (true)
51+
{
52+
ReadSkippingComments(ref reader);
53+
54+
if (reader.TokenType == JsonTokenType.EndObject)
55+
{
56+
return new FormattingOptions
57+
{
58+
TabSize = tabSize ?? ThrowMissingRequiredProperty<int>(nameof(tabSize)),
59+
InsertSpaces = insertSpaces ?? ThrowMissingRequiredProperty<bool>(nameof(insertSpaces)),
60+
TrimTrailingWhitespace = trimTrailingWhitespace,
61+
InsertFinalNewline = insertFinalNewline,
62+
TrimFinalNewlines = trimFinalNewlines,
63+
OtherOptions = otherOptions
64+
};
65+
}
66+
67+
if (reader.TokenType == JsonTokenType.Comment)
68+
{
69+
continue;
70+
}
71+
72+
if (reader.TokenType != JsonTokenType.PropertyName)
73+
{
74+
throw new JsonException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.FormattingOptionsEncounteredInvalidToken, reader.TokenType));
75+
}
76+
77+
var propertyName = reader.GetString();
78+
79+
ReadSkippingComments(ref reader);
80+
81+
switch (propertyName)
82+
{
83+
case nameof(tabSize):
84+
tabSize = reader.GetInt32();
85+
continue;
86+
case nameof(insertSpaces):
87+
insertSpaces = reader.GetBoolean();
88+
continue;
89+
case nameof(trimTrailingWhitespace):
90+
trimTrailingWhitespace = reader.GetBoolean();
91+
continue;
92+
case nameof(insertFinalNewline):
93+
insertFinalNewline = reader.GetBoolean();
94+
continue;
95+
case nameof(trimFinalNewlines):
96+
trimFinalNewlines = reader.GetBoolean();
97+
continue;
98+
default:
99+
break;
100+
}
101+
102+
SumType<bool, int, string> value = reader.TokenType switch
103+
{
104+
JsonTokenType.Number => reader.GetInt32(),
105+
JsonTokenType.String => reader.GetString(),
106+
JsonTokenType.True => reader.GetBoolean(),
107+
JsonTokenType.False => reader.GetBoolean(),
108+
_ => throw new JsonException(string.Format(CultureInfo.InvariantCulture, LanguageServerProtocolResources.FormattingOptionsEncounteredInvalidToken, reader.TokenType))
109+
};
110+
111+
(otherOptions ??= []).Add(propertyName, value);
112+
}
113+
}
114+
115+
public override void Write(Utf8JsonWriter writer, FormattingOptions value, JsonSerializerOptions options)
116+
{
117+
writer.WriteStartObject();
118+
writer.WriteNumber("tabSize", value.TabSize);
119+
writer.WriteBoolean("insertSpaces", value.InsertSpaces);
120+
121+
if (value.TrimTrailingWhitespace != default)
122+
{
123+
writer.WriteBoolean("trimTrailingWhitespace", value.TrimTrailingWhitespace);
124+
}
125+
126+
if (value.InsertFinalNewline != default)
127+
{
128+
writer.WriteBoolean("insertFinalNewline", value.InsertFinalNewline);
129+
}
130+
131+
if (value.TrimFinalNewlines != default)
132+
{
133+
writer.WriteBoolean("trimFinalNewlines", value.TrimFinalNewlines);
134+
}
135+
136+
if (value.OtherOptions is not null)
137+
{
138+
foreach (var item in value.OtherOptions)
139+
{
140+
writer.WritePropertyName(item.Key);
141+
JsonSerializer.Serialize(writer, item.Value, options);
142+
}
143+
}
144+
145+
writer.WriteEndObject();
146+
}
147+
}

src/LanguageServer/Protocol/xlf/LanguageServerProtocolResources.cs.xlf

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LanguageServer/Protocol/xlf/LanguageServerProtocolResources.de.xlf

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LanguageServer/Protocol/xlf/LanguageServerProtocolResources.es.xlf

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LanguageServer/Protocol/xlf/LanguageServerProtocolResources.fr.xlf

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LanguageServer/Protocol/xlf/LanguageServerProtocolResources.it.xlf

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/LanguageServer/Protocol/xlf/LanguageServerProtocolResources.ja.xlf

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)