Skip to content

Commit 59e888a

Browse files
committed
Make language server compilation asynchronous; fix failing unit tests
1 parent 05d4cde commit 59e888a

File tree

14 files changed

+151
-70
lines changed

14 files changed

+151
-70
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2020
- Language Server: enum values are now included in code completions.
2121
- Language Server: `<<set>>` statements now offer variable names in their code completions.
2222
- Language Server: hovering smart variables now shows their definition.
23+
- Language Server: Make compilation asynchronous
2324

2425
### Removed
2526

YarnSpinner.LanguageServer.Tests/ActionDeclarationTests.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Text.RegularExpressions;
77
using System.Threading;
8+
using System.Threading.Tasks;
89
using Xunit;
910
using Yarn;
1011

@@ -34,18 +35,18 @@ public void CSharpData_DocumentationCommentsAreExtracted()
3435
}
3536

3637
[Fact]
37-
public void ActionDeclaration_AllYarnFunctions_AreCalled()
38+
public async Task ActionDeclaration_AllYarnFunctions_AreCalled()
3839
{
3940
var workspace = new Workspace();
4041
workspace.Root = WorkspacePath;
41-
workspace.Initialize();
42+
await workspace.InitializeAsync();
4243

4344
var diagnostics = workspace.GetDiagnostics().SelectMany(d => d.Value);
4445

4546
diagnostics.Should().NotContain(d => d.Severity == DiagnosticSeverity.Warning);
4647
diagnostics.Should().NotContain(d => d.Severity == DiagnosticSeverity.Error);
4748

48-
var compiledOutput = workspace.Projects.Single().CompileProject(false, Yarn.Compiler.CompilationJob.Type.FullCompilation, CancellationToken.None);
49+
var compiledOutput = await workspace.Projects.Single().CompileProjectAsync(false, Yarn.Compiler.CompilationJob.Type.FullCompilation, CancellationToken.None);
4950
var compiledProgram = compiledOutput.Program;
5051

5152
compiledProgram.Should().NotBeNull();
@@ -64,10 +65,10 @@ public void ActionDeclaration_AllYarnFunctions_AreCalled()
6465
}
6566

6667
[Fact]
67-
public void ActionDeclarations_AreAllPresentInLibrary()
68+
public async Task ActionDeclarations_AreAllPresentInLibrary()
6869
{
6970
var workspace = new Workspace();
70-
workspace.Initialize();
71+
await workspace.InitializeAsync();
7172

7273
var functions = workspace.Projects.Single().Functions;
7374

@@ -93,10 +94,10 @@ public void ActionDeclarations_AreAllPresentInLibrary()
9394
}
9495

9596
[Fact]
96-
public void LibraryMethods_AreAllDeclared()
97+
public async Task LibraryMethods_AreAllDeclared()
9798
{
9899
var workspace = new Workspace();
99-
workspace.Initialize();
100+
await workspace.InitializeAsync();
100101

101102
var functions = workspace.Projects.Single().Functions;
102103

@@ -145,15 +146,15 @@ private static void CheckImplementationMatchesDeclaration(System.Delegate impl,
145146
}
146147

147148
[Fact]
148-
public void ActionsFoundInCSharpFile_AreIdentified()
149+
public async Task ActionsFoundInCSharpFile_AreIdentified()
149150
{
150151
// Given
151152
var path = Path.Combine(TestUtility.PathToTestData, "TestWorkspace", "Project1", "ActionDeclarationUsage.yarn");
152153

153154
var workspace = new Workspace();
154155
workspace.Root = Path.Combine(TestUtility.PathToTestData, "TestWorkspace", "Project1");
155156
workspace.Configuration.CSharpLookup = true;
156-
workspace.Initialize();
157+
await workspace.InitializeAsync();
157158

158159
// When
159160
var diagnostics = workspace

YarnSpinner.LanguageServer.Tests/CompletionTests.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,16 @@ public async Task Server_OnCompletingStartOfCommand_ReturnsValidCompletions()
3434
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
3535
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");
3636

37+
var lineContainingCommand = File
38+
.ReadAllLines(filePath)
39+
.Index()
40+
.Single(l => l.Item == "<<static_command_no_docs>>")
41+
.Index;
42+
3743
var startOfCommand = new Position
3844
{
3945
Character = 2,
40-
Line = 22
46+
Line = lineContainingCommand
4147
};
4248

4349
// When
@@ -67,10 +73,16 @@ public async Task Server_OnCompletingPartialCommand_ReturnsValidCompletionRange(
6773
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
6874
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");
6975

76+
var lineContainingCommand = File
77+
.ReadAllLines(filePath)
78+
.Index()
79+
.Single(l => l.Item == "<<static_command_no_docs>>")
80+
.Index;
81+
7082
var startOfCommand = new Position
7183
{
7284
Character = 2,
73-
Line = 22
85+
Line = lineContainingCommand
7486
};
7587
var middleOfCommand = startOfCommand with
7688
{

YarnSpinner.LanguageServer.Tests/HoverTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ public HoverTests(ITestOutputHelper outputHelper) : base(outputHelper)
2121
{
2222
}
2323

24-
2524
[Fact]
2625
public async Task Server_OnHoverVariable_ShouldReceiveHoverInfo()
2726
{
@@ -48,7 +47,7 @@ public async Task Server_OnHoverVariable_ShouldReceiveHoverInfo()
4847
// is expected to be returned
4948
var validHoverPosition = new Position
5049
{
51-
Line = 18,
50+
Line = 24,
5251
Character = 14,
5352
};
5453

@@ -73,7 +72,7 @@ public async Task Server_OnHoverCommands_GivesInfo()
7372
// When
7473
var hoverResult = await client.RequestHover(new HoverParams
7574
{
76-
Position = new Position { Line = 23, Character = 10 },
75+
Position = new Position { Line = 29, Character = 10 },
7776
TextDocument = new TextDocumentIdentifier { Uri = filePath },
7877
});
7978

YarnSpinner.LanguageServer.Tests/LanguageServerTests.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@ public async Task Server_OnChangingDocument_SendsNodesChangedNotification()
121121
var nodesChanged = GetNodesChangedNotificationAsync((nodesResult) =>
122122
nodesResult.Uri.AbsolutePath.Contains(filePath)
123123
);
124-
ChangeTextInDocument(client, filePath, new Position(20, 0), "title: Node3\n---\n===\n");
124+
// Insert a new node at the top of the file
125+
ChangeTextInDocument(client, filePath, new Position(0, 0), "title: Node3\n---\nLine Content\n===\n");
125126
nodeInfo = await nodesChanged;
126127

127128
nodeInfo.Nodes.Should().HaveCount(nodeCount + 1, "because we added a new node");
@@ -219,16 +220,16 @@ public async Task Server_OnJumpCommand_ShouldReceiveNodeNameCompletions()
219220
completions.Should().Contain(c => c.Label == "Start",
220221
"because 'Start' is a node we could jump to");
221222

222-
completions.Should().NotContain(c => c.Label == "Node2",
223-
"because while 'Node2' is a node we could jump to, we're currently in a syntax error");
223+
completions.Should().Contain(c => c.Label == "Node2",
224+
"because 'Node2' is a node we could jump to, even though it's after a syntax error");
224225
}
225226
}
226227

227228
[Fact(Timeout = 2000)]
228229
public void Workspace_BuiltInFunctions_MatchesDefaultLibrary()
229230
{
230231
// Given
231-
var builtInActionDecls = Workspace.GetPredefinedActions().Where(a => a.Type == ActionType.Function).Select(f => f.Declaration).ToDictionary(d => d.Name);
232+
var builtInActionDecls = Workspace.GetPredefinedActions().Where(a => a.Type == ActionType.Function).Select(f => f.Declaration).ToDictionary(d => d!.Name);
232233

233234
var storage = new Yarn.MemoryVariableStore();
234235
var dialogue = new Yarn.Dialogue(storage);
@@ -270,7 +271,7 @@ public void Workspace_BuiltInFunctions_MatchesDefaultLibrary()
270271

271272
libraryDecl.Should().BeEquivalentTo(actionDecl, (config) =>
272273
{
273-
return config.Excluding(info => info.Description);
274+
return config.Excluding(info => info!.Description);
274275
});
275276
}
276277
}

YarnSpinner.LanguageServer.Tests/LanguageServerTestsBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,13 @@ protected void ConfigureClient(LanguageClientOptions options)
137137
options.WithLink("keepalive", "ka");
138138
options.WithLink("throw", "t");
139139
options.OnRequest(
140+
#pragma warning disable CS1998 // async method lacks 'await' operators
140141
"throw", async ct =>
141142
{
142143
throw new NotSupportedException();
143144
return Task.CompletedTask;
144145
}
146+
#pragma warning restore
145147
);
146148

147149
options.OnPublishDiagnostics((diagnosticsParams) =>

YarnSpinner.LanguageServer.Tests/WorkspaceTests.cs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.IO;
44
using System.Linq;
55
using System.Threading;
6+
using System.Threading.Tasks;
67
using Xunit;
78
using YarnLanguageServer.Diagnostics;
89

@@ -17,13 +18,13 @@ public class WorkspaceTests
1718
private static string JumpsAndDetoursPath = Path.Combine(TestUtility.PathToTestWorkspace, "JumpsAndDetours");
1819

1920
[Fact]
20-
public void Projects_CanOpen()
21+
public async Task Projects_CanOpen()
2122
{
2223
// Given
2324
var project = new Project(Project1Path);
2425

2526
// When
26-
project.ReloadProjectFromDisk(false, CancellationToken.None);
27+
await project.ReloadProjectFromDiskAsync(false, CancellationToken.None);
2728

2829
// Then
2930
project.Files.Should().NotBeEmpty();
@@ -37,11 +38,11 @@ public void Projects_CanOpen()
3738
}
3839

3940
[Fact]
40-
public void Workspaces_CanOpen()
41+
public async Task Workspaces_CanOpen()
4142
{
4243
var workspace = new Workspace();
4344
workspace.Root = TestUtility.PathToTestWorkspace;
44-
workspace.Initialize();
45+
await workspace.InitializeAsync();
4546

4647
var diagnostics = workspace.GetDiagnostics();
4748

@@ -67,12 +68,12 @@ public void Workspaces_CanOpen()
6768
}
6869

6970
[Fact]
70-
public void Workspaces_WithNoProjects_HaveImplicitProject()
71+
public async Task Workspaces_WithNoProjects_HaveImplicitProject()
7172
{
7273
// Given
7374
var workspace = new Workspace();
7475
workspace.Root = NoProjectPath;
75-
workspace.Initialize();
76+
await workspace.InitializeAsync();
7677

7778
// Then
7879
var project = workspace.Projects.Should().ContainSingle().Subject;
@@ -97,14 +98,14 @@ public void ActionsDefFile_ParsesCorrectly()
9798
}
9899

99100
[Fact]
100-
public void Workspaces_WithDefsJsonAndNoProject_FindsCommands()
101+
public async Task Workspaces_WithDefsJsonAndNoProject_FindsCommands()
101102
{
102103
// Given
103104
var workspace = new Workspace();
104105
workspace.Root = NoProjectPath;
105106

106107
// When
107-
workspace.Initialize();
108+
await workspace.InitializeAsync();
108109

109110
// Then
110111
var project = workspace.Projects.Should().ContainSingle().Subject;
@@ -116,12 +117,12 @@ public void Workspaces_WithDefsJsonAndNoProject_FindsCommands()
116117
}
117118

118119
[Fact]
119-
public void Workspaces_WithDefinitionsFile_UseDefinitions()
120+
public async Task Workspaces_WithDefinitionsFile_UseDefinitions()
120121
{
121122
// Given
122123
var workspace = new Workspace();
123124
workspace.Root = Path.GetDirectoryName(Project2Path);
124-
workspace.Initialize();
125+
await workspace.InitializeAsync();
125126

126127
// Then
127128
var project = workspace.Projects.Should().ContainSingle().Subject;
@@ -130,22 +131,22 @@ public void Workspaces_WithDefinitionsFile_UseDefinitions()
130131
}
131132

132133
[Fact]
133-
public void Workspace_WithNullRoot_OpensSuccessfully()
134+
public async Task Workspace_WithNullRoot_OpensSuccessfully()
134135
{
135136
// Given
136137
var workspace = new Workspace();
137138
workspace.Root = null;
138139

139-
workspace.Initialize();
140+
await workspace.InitializeAsync();
140141
}
141142

142143
[Fact]
143-
public void Workspace_WithMultipleDefinitionsFiles_UsesMultipleFiles()
144+
public async Task Workspace_WithMultipleDefinitionsFiles_UsesMultipleFiles()
144145
{
145146
// Given
146147
var workspace = new Workspace();
147148
workspace.Root = MultipleDefsPath;
148-
workspace.Initialize();
149+
await workspace.InitializeAsync();
149150

150151
// When
151152
var projects = workspace.Projects;
@@ -166,11 +167,11 @@ public void Workspace_WithMultipleDefinitionsFiles_UsesMultipleFiles()
166167
}
167168

168169
[Fact]
169-
public void Workspace_WithJumpsBetweenFiles_IdentifiesJumpsToOtherFiles()
170+
public async Task Workspace_WithJumpsBetweenFiles_IdentifiesJumpsToOtherFiles()
170171
{
171172
var workspace = new Workspace();
172173
workspace.Root = JumpsAndDetoursPath;
173-
workspace.Initialize();
174+
await workspace.InitializeAsync();
174175

175176
var project = workspace.Projects.Single();
176177
var file = project.Files.Single(f => f.Uri.AbsolutePath.EndsWith("JumpsAndDetours.yarn"));

YarnSpinner.LanguageServer.Tests/YarnLanguageServer.Tests.csproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@
55
<Nullable>enable</Nullable>
66

77
<IsPackable>false</IsPackable>
8+
<!-- CS4014 Async function is not awaited and will run
9+
synchronously -->
10+
<!-- VSTHRD110 Observe result of async calls -->
11+
<!-- CS1998 Async method lacks 'await' operators -->
12+
<WarningsAsErrors>CS4014;VSTHRD110;CS1998</WarningsAsErrors>
13+
14+
<!-- VSTHRD200 Async methods should have 'Async' suffix -->
15+
<!-- (Some tests are async, and are not called directly, so no need to add
16+
'Async' suffix for caller clarity -->
17+
<NoWarn>VSTHRD200</NoWarn>
818
</PropertyGroup>
919

1020
<ItemGroup>

YarnSpinner.LanguageServer/YarnLanguageServer.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
<GenerateDocumentationFile>False</GenerateDocumentationFile>
1111
<UseAppHost>False</UseAppHost>
1212
<Nullable>enable</Nullable>
13+
<!-- CS4014 Async function is not awaited and will run synchronously -->
14+
<!-- VSTHRD110 Observe result of async calls -->
15+
<WarningsAsErrors>CS4014;VSTHRD110</WarningsAsErrors>
16+
1317
</PropertyGroup>
1418

1519
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

YarnSpinner.LanguageServer/src/Server/Handlers/FileOperationsHandler.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public FileOperationsHandler(Workspace workspace)
1717
this.workspace = workspace;
1818
}
1919

20-
public Task<Unit> Handle(DidChangeWatchedFilesParams request, CancellationToken cancellationToken)
20+
public async Task<Unit> Handle(DidChangeWatchedFilesParams request, CancellationToken cancellationToken)
2121
{
2222
var yarnChanges = request.Changes.Where(c => c.Uri.Path.EndsWith(".yarn"));
2323
var csChanges = request.Changes.Where(c => c.Uri.Path.EndsWith(".cs"));
@@ -78,10 +78,15 @@ public Task<Unit> Handle(DidChangeWatchedFilesParams request, CancellationToken
7878

7979
if (needsWorkspaceReload)
8080
{
81-
workspace.ReloadWorkspace(cancellationToken);
81+
try
82+
{
83+
await workspace.ReloadWorkspaceAsync(cancellationToken);
84+
85+
}
86+
catch (System.OperationCanceledException) { }
8287
}
8388

84-
return Unit.Task;
89+
return Unit.Value;
8590
}
8691

8792
public DidChangeWatchedFilesRegistrationOptions GetRegistrationOptions(DidChangeWatchedFilesCapability capability, ClientCapabilities clientCapabilities)

0 commit comments

Comments
 (0)