Skip to content

Commit 1cc34e2

Browse files
committed
Expose Roslyn LSP types to XAML and add readme
1 parent d9c433e commit 1cc34e2

File tree

5 files changed

+113
-103
lines changed

5 files changed

+113
-103
lines changed

src/LanguageServer/Protocol/Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.Razor" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
4141
<RestrictedInternalsVisibleTo Include="Microsoft.VisualStudio.LanguageServices.Razor" Namespace="Roslyn.LanguageServer.Protocol" Partner="Razor" Key="$(RazorKey)" />
4242
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.ExternalAccess.Xaml" />
43+
<!-- Restricted IVT is direct for protocol types only -->
44+
<RestrictedInternalsVisibleTo Include="Microsoft.VisualStudio.DesignTools.CodeAnalysis" Namespace="Roslyn.LanguageServer.Protocol" Partner="Xaml" Key="$(VisualStudioKey)" />
45+
<RestrictedInternalsVisibleTo Include="DesignTools.Tests.Component.CodeAnalysis" Namespace="Roslyn.LanguageServer.Protocol" Partner="Xaml" Key="$(VisualStudioKey)" />
4346
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.CSharp.EditorFeatures" />
4447
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.CSharp.Features.UnitTests" />
4548
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests" />

src/LanguageServer/Protocol/Protocol/LanguageServer.Protocol.csproj

Lines changed: 0 additions & 59 deletions
This file was deleted.

src/LanguageServer/Protocol/Protocol/LanguageServer.Protocol.ruleset

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
## Summary
2+
The files in this folder defines C# types for [LSP protocol definitions](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/) and custom VS LSP protocol definitions.
3+
4+
These types are shared via restricted IVTs to Razor and XAML, as they run inside the C# Roslyn LSP server.
5+
6+
## Breaking Changes
7+
Ensuring that these types are not binary breaking on changes is important, as this dll is shared between Roslyn, Razor, and XAML in both VSCode and VS. They export handlers that are used in our Roslyn LSP server using protocol types.
8+
9+
In general, the LSP specification itself generally does not make JSON protocol breaking changes. New additions are controlled by capabilities, properties are only added, etc. Most of the time these kinds of changes are not binary breaking for our type definitions either - it's totally fine to add new properties, methods, etc. to our type definitions
10+
11+
However, some protocol changes can result in binary breaking changes. The main scenario for this is when the protocol changes a property to a union or adds another definition to a union type. For example, if initially the server capabilities type defined a `hoverProvider`:
12+
```json
13+
hoverProvider?: boolean;
14+
```
15+
In our C# type definitions this would be defined as
16+
```csharp
17+
[JsonPropertyName("hoverProvider")]
18+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
19+
public bool? HoverProvider
20+
{
21+
get;
22+
set;
23+
}
24+
```
25+
26+
It is totally legal (and not breaking) in the LSP protocol to modify this type definition into a union type, controlling what is defined based on a capability.
27+
```json
28+
hoverProvider?: boolean | HoverOptions;
29+
```
30+
and in C# this would be defined as:
31+
```csharp
32+
[JsonPropertyName("hoverProvider")]
33+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
34+
public SumType<bool, HoverOptions>? HoverProvider
35+
{
36+
get;
37+
set;
38+
}
39+
```
40+
41+
which is now a breaking change due to the property type changing from `bool?` to `SumType<bool, HoverOptions>?`. And this same logic applies to adding a new value to a union type as going from `SumType<T, U>` to `SumType<T, U, V>` is also a breaking change.
42+
43+
### Handling breaking changes
44+
45+
Generally, changes that cause binary breaking changes are relatively rare (adding new types to a union). Additionally, we only need to be careful about binary breaking changes if our partners actually use the API being changed. If no one uses it, we can just update the property with a breaking change.
46+
47+
However, if a partner is using the type, we need to handle breaking changes to it carefully. We can support this by adding a new intermediate property for the new union type definition, obsoleting the old one, switching our partners to the new property, then move everything back:
48+
49+
### 1. Add new intermediate property.
50+
First, we add a new property representing the union version of the type and move the serialization attributes to it. The old property is then implemented by accessing the correct value of the new union type. This is safe as the new `HoverOptions` is not provided unless explicitly opted in via capabilities.
51+
52+
```csharp
53+
[JsonIgnore]
54+
[Obsolete("Use HoverProviderUnion instead")]
55+
public bool? HoverProvider
56+
{
57+
get => HoverProviderUnion?.First;
58+
set => HoverProviderUnion = value;
59+
}
60+
61+
[JsonPropertyName("hoverProvider")]
62+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
63+
public SumType<bool, HoverOptions>? HoverProviderUnion
64+
{
65+
get;
66+
set;
67+
}
68+
```
69+
70+
### 2. Update partners to new version.
71+
Update Razor / XAML to consume the new union type property.
72+
73+
### 3. Switch original property to use union type, obsolete intermediate property.
74+
After partners have switched, we can now change the type of the original property and obsolete the intermediate one:
75+
```csharp
76+
[JsonPropertyName("hoverProvider")]
77+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
78+
public SumType<bool, HoverOptions>? HoverProvider
79+
{
80+
get;
81+
set;
82+
}
83+
84+
[JsonIgnore]
85+
[Obsolete("Use HoverProvider instead")]
86+
public SumType<bool, HoverOptions>? HoverProviderUnion
87+
{
88+
get => HoverProvider?.First;
89+
set => HoverProvider = value;
90+
}
91+
```
92+
93+
### 4. Delete the intermediate property.
94+
After partners have switched again, we can delete the intermediate property.
95+
```csharp
96+
[JsonPropertyName("hoverProvider")]
97+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
98+
public SumType<bool, HoverOptions>? HoverProvider
99+
{
100+
get;
101+
set;
102+
}
103+
```

src/LanguageServer/Protocol/Protocol/ServerCapabilities.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ public SumType<bool, HoverOptions>? HoverProvider
5757
set;
5858
}
5959

60+
[JsonIgnore]
61+
public bool? HoverProvider2
62+
{
63+
get => HoverProvider?.First;
64+
set => HoverProvider = value;
65+
}
66+
6067
/// <summary>
6168
/// Gets or sets the value which indicates if signature help is supported.
6269
/// </summary>

0 commit comments

Comments
 (0)