diff --git a/docs/specs/lsp.yml b/docs/specs/lsp.yml index 26e00af0160..64aa0503083 100644 --- a/docs/specs/lsp.yml +++ b/docs/specs/lsp.yml @@ -152,4 +152,4 @@ languageServer: "text": "Invalid change" } } - - expectNoNotificationAfterBuildTime: 1 + - expectNoNotification: true diff --git a/src/docfx/config/TestQuirks.cs b/src/docfx/config/TestQuirks.cs index 5cf4202d41e..7555526330a 100644 --- a/src/docfx/config/TestQuirks.cs +++ b/src/docfx/config/TestQuirks.cs @@ -15,6 +15,6 @@ internal static class TestQuirks public static bool? Verbose { get; set; } - public static Action? FinishedBuildCountIncrease { get; set; } + public static Action? HandledEventCountIncrease { get; set; } } } diff --git a/src/docfx/serve/LanguageServerBuilder.cs b/src/docfx/serve/LanguageServerBuilder.cs index f36bf0ac4de..83065517c74 100644 --- a/src/docfx/serve/LanguageServerBuilder.cs +++ b/src/docfx/serve/LanguageServerBuilder.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Channels; using System.Threading.Tasks; using OmniSharp.Extensions.LanguageServer.Protocol.Models; @@ -13,7 +14,7 @@ internal class LanguageServerBuilder { private readonly Builder _builder; private readonly ErrorList _errorList; - private readonly SemaphoreSlim _buildSemaphore; + private readonly Channel _buildChannel; private readonly DiagnosticPublisher _diagnosticPublisher; private readonly LanguageServerPackage _languageServerPackage; private readonly PathString _workingDirectory; @@ -30,28 +31,47 @@ public LanguageServerBuilder( _errorList = new(); _languageServerPackage = languageServerPackage; _builder = new(_errorList, languageServerPackage.BasePath, options, _languageServerPackage); - _buildSemaphore = new(0); + _buildChannel = Channel.CreateUnbounded(); _ = StartAsync(); } public void QueueBuild() { - _buildSemaphore.Release(); + _buildChannel.Writer.TryWrite(true); } private async Task StartAsync() { while (true) { - await _buildSemaphore.WaitAsync(); + await WaitToTriggerBuild(); var filesToBuild = _languageServerPackage.GetAllFilesInMemory(); _builder.Build(filesToBuild.Select(f => f.Value).ToArray()); PublishDiagnosticsParams(filesToBuild); - TestQuirks.FinishedBuildCountIncrease?.Invoke(); + TestQuirks.HandledEventCountIncrease?.Invoke(); } } + private async Task WaitToTriggerBuild() + { + await _buildChannel.Reader.ReadAsync(); + + try + { + while (true) + { + using var cts = new CancellationTokenSource(1000); + await _buildChannel.Reader.ReadAsync(cts.Token); + TestQuirks.HandledEventCountIncrease?.Invoke(); + } + } + catch (System.OperationCanceledException) + { + } + return; + } + private void PublishDiagnosticsParams(IEnumerable files) { foreach (var file in files) diff --git a/src/docfx/serve/lsp/TextDocumentHandler.cs b/src/docfx/serve/lsp/TextDocumentHandler.cs index 3683f828674..5ebbc8cf696 100644 --- a/src/docfx/serve/lsp/TextDocumentHandler.cs +++ b/src/docfx/serve/lsp/TextDocumentHandler.cs @@ -103,6 +103,7 @@ private bool TryUpdatePackage(DocumentUri file, string content) var filePath = new PathString(file.GetFileSystemPath()); if (!filePath.StartsWithPath(_package.BasePath, out _)) { + TestQuirks.HandledEventCountIncrease?.Invoke(); return false; } diff --git a/test/docfx.Test/DocfxTest.cs b/test/docfx.Test/DocfxTest.cs index 7f5838cab8f..a542da6f3bd 100644 --- a/test/docfx.Test/DocfxTest.cs +++ b/test/docfx.Test/DocfxTest.cs @@ -26,7 +26,8 @@ public static class DocfxTest private static readonly AsyncLocal> t_repos = new(); private static readonly AsyncLocal> t_remoteFiles = new(); private static readonly AsyncLocal t_appDataPath = new(); - private static readonly AsyncLocal> t_finishedBuildCount = new(); + private static readonly AsyncLocal> t_handledEventCount = new(); + private static readonly AsyncLocal> t_sentEventCount = new(); static DocfxTest() { @@ -52,9 +53,9 @@ static DocfxTest() return null; }; - TestQuirks.FinishedBuildCountIncrease = () => + TestQuirks.HandledEventCountIncrease = () => { - t_finishedBuildCount.Value.Value++; + t_handledEventCount.Value.Value++; }; } @@ -86,7 +87,8 @@ public static void Run(TestData test, DocfxTestSpec spec) t_repos.Value = repos; t_remoteFiles.Value = spec.Http; t_appDataPath.Value = appDataPath; - t_finishedBuildCount.Value = new StrongBox(); + t_handledEventCount.Value = new StrongBox(); + t_sentEventCount.Value = new StrongBox(); RunCore(docsetPath, outputPath, test, spec, variables, package); } catch (Exception exception) @@ -102,7 +104,8 @@ public static void Run(TestData test, DocfxTestSpec spec) t_repos.Value = null; t_remoteFiles.Value = null; t_appDataPath.Value = null; - t_finishedBuildCount.Value = null; + t_handledEventCount.Value = null; + t_sentEventCount.Value = null; } } } @@ -208,6 +211,7 @@ private static async Task RunLanguageServer(string docsetPath, DocfxTestSpec spe if (!string.IsNullOrEmpty(lspSpec.Notification)) { await lspTestHost.SendNotification(new LanguageServerNotification(lspSpec.Notification, lspSpec.Params)); + t_sentEventCount.Value.Value++; } else if (!string.IsNullOrEmpty(lspSpec.ExpectNotification)) { @@ -239,9 +243,9 @@ private static async Task RunLanguageServer(string docsetPath, DocfxTestSpec spe s_languageServerJsonDiff.Verify(expectedNotifications, actualNotifications); } - else if (lspSpec.ExpectNoNotificationAfterBuildTime != null) + else if (lspSpec.ExpectNoNotification) { - while (lspSpec.ExpectNoNotificationAfterBuildTime > t_finishedBuildCount.Value.Value) + while (t_sentEventCount.Value.Value > t_handledEventCount.Value.Value) { await Task.Delay(1000); } diff --git a/test/docfx.Test/lsp/LanguageServerTestSpec.cs b/test/docfx.Test/lsp/LanguageServerTestSpec.cs index ec7b7881181..6ee2c13bd64 100644 --- a/test/docfx.Test/lsp/LanguageServerTestSpec.cs +++ b/test/docfx.Test/lsp/LanguageServerTestSpec.cs @@ -11,7 +11,7 @@ public class LanguageServerTestSpec public string ExpectNotification { get; set; } = string.Empty; - public int? ExpectNoNotificationAfterBuildTime { get; set; } + public bool ExpectNoNotification { get; set; } public JToken Params { get; set; } }