From e66783b8a6b2960ab380c7ab9534502aedd287f4 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 7 Aug 2019 14:17:02 -0400 Subject: [PATCH 1/5] Changes for #162 correct how text document sync settings are configured --- .../Capabilities/SynchronizationCapability.cs | 2 +- .../Capabilities/TextDocumentSyncOptions.cs | 33 +++++--- src/Server/LanguageServer.cs | 54 ++++++++----- .../ClientCapabilityProviderTests.cs | 75 +++++++++++++++++-- .../Lsp.Tests/Models/InitializeResultTests.cs | 59 ++++++++++----- 5 files changed, 166 insertions(+), 57 deletions(-) diff --git a/src/Protocol/Client/Capabilities/SynchronizationCapability.cs b/src/Protocol/Client/Capabilities/SynchronizationCapability.cs index 9f1cb988e..8d22a23e6 100644 --- a/src/Protocol/Client/Capabilities/SynchronizationCapability.cs +++ b/src/Protocol/Client/Capabilities/SynchronizationCapability.cs @@ -5,7 +5,7 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities { - public class SynchronizationCapability : DynamicCapability, ConnectedCapability, ConnectedCapability, ConnectedCapability, ConnectedCapability, ConnectedCapability, ConnectedCapability + public class SynchronizationCapability : DynamicCapability, ConnectedCapability { /// /// The client supports sending will save notifications. diff --git a/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs b/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs index 3bd76486d..044fcc9f4 100644 --- a/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs +++ b/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; @@ -36,19 +37,29 @@ public class TextDocumentSyncOptions : ITextDocumentSyncOptions [Optional] public SaveOptions Save { get; set; } - public static TextDocumentSyncOptions Of(IEnumerable options) + public static Func, TextDocumentSyncOptions> Of(TextDocumentSyncOptions @default) { - return new TextDocumentSyncOptions() { - OpenClose = options.Any(z => z.OpenClose), - Change = options - .Where(x => x.Change != TextDocumentSyncKind.None) - .Min(z => z.Change), - WillSave = options.Any(z => z.WillSave), - WillSaveWaitUntil = options.Any(z => z.WillSaveWaitUntil), - Save = new SaveOptions() { - IncludeText = options.Any(z => z.Save?.IncludeText == true) + return options => + { + var change = @default.Change; + if (@default.Change == TextDocumentSyncKind.None && options.Any()) + { + change = @default.Change > 0 ? @default.Change : options + .Where(x => x.Change != TextDocumentSyncKind.None) + .Min(z => z.Change); } - }; + return new TextDocumentSyncOptions() + { + OpenClose = @default.OpenClose || options.Any(z => z.OpenClose), + Change = change, + WillSave = @default.WillSave || options.Any(z => z.WillSave), + WillSaveWaitUntil = @default.WillSaveWaitUntil || options.Any(z => z.WillSaveWaitUntil), + Save = new SaveOptions() + { + IncludeText = @default.Save?.IncludeText ?? options.Any(z => z.Save?.IncludeText == true) + } + }; + }; } } } diff --git a/src/Server/LanguageServer.cs b/src/Server/LanguageServer.cs index 975ce12b1..d5fb6d3d4 100644 --- a/src/Server/LanguageServer.cs +++ b/src/Server/LanguageServer.cs @@ -329,7 +329,8 @@ private IDisposable RegisterHandlers(LspHandlerDescriptorDisposable handlerDispo { var registrations = handlerDisposable.Descriptors .Where(d => d.AllowsDynamicRegistration) - .Select(d => new Registration() { + .Select(d => new Registration() + { Id = d.Id.ToString(), Method = d.Method, RegisterOptions = d.RegistrationOptions @@ -409,6 +410,17 @@ async Task IRequestHandler var ccp = new ClientCapabilityProvider(_collection); + var defaultTextDocumentSyncOptions = new TextDocumentSyncOptions() + { + Change = TextDocumentSyncKind.None, + OpenClose = _collection.ContainsHandler(typeof(IDidOpenTextDocumentHandler)) || _collection.ContainsHandler(typeof(IDidCloseTextDocumentHandler)), + Save = _collection.ContainsHandler(typeof(IDidSaveTextDocumentHandler)) ? + new SaveOptions() { IncludeText = true /* TODO: Make configurable */ } : + null, + WillSave = _collection.ContainsHandler(typeof(IWillSaveTextDocumentHandler)), + WillSaveWaitUntil = _collection.ContainsHandler(typeof(IWillSaveWaitUntilTextDocumentHandler)) + }; + var serverCapabilities = new ServerCapabilities() { CodeActionProvider = ccp.GetStaticOptions(textDocumentCapabilities.CodeAction).Get(CodeActionOptions.Of), @@ -422,7 +434,7 @@ async Task IRequestHandler DocumentRangeFormattingProvider = ccp.HasStaticHandler(textDocumentCapabilities.RangeFormatting), DocumentSymbolProvider = ccp.HasStaticHandler(textDocumentCapabilities.DocumentSymbol), ExecuteCommandProvider = ccp.GetStaticOptions(workspaceCapabilities.ExecuteCommand).Reduce(ExecuteCommandOptions.Of), - TextDocumentSync = ccp.GetStaticOptions(textDocumentCapabilities.Synchronization).Reduce(TextDocumentSyncOptions.Of), + TextDocumentSync = ccp.GetStaticOptions(textDocumentCapabilities.Synchronization).Reduce(TextDocumentSyncOptions.Of(defaultTextDocumentSyncOptions)), HoverProvider = ccp.HasStaticHandler(textDocumentCapabilities.Hover), ReferencesProvider = ccp.HasStaticHandler(textDocumentCapabilities.References), RenameProvider = ccp.GetStaticOptions(textDocumentCapabilities.Rename).Get(RenameOptions.Of), @@ -434,6 +446,10 @@ async Task IRequestHandler FoldingRangeProvider = ccp.GetStaticOptions(textDocumentCapabilities.FoldingRange).Get(FoldingRangeOptions.Of), DeclarationProvider = ccp.GetStaticOptions(textDocumentCapabilities.Declaration).Get(DeclarationOptions.Of), }; + if (serverCapabilities.TextDocumentSync.HasOptions) + { + defaultTextDocumentSyncOptions = serverCapabilities.TextDocumentSync.Options; + } if (_collection.ContainsHandler(typeof(IDidChangeWorkspaceFoldersHandler))) { @@ -449,13 +465,20 @@ async Task IRequestHandler if (ccp.HasStaticHandler(textDocumentCapabilities.Synchronization)) { - var textDocumentSyncKind = _collection.ContainsHandler(typeof(IDidChangeTextDocumentHandler)) - ? _collection + var textDocumentSyncKind = TextDocumentSyncKind.None; + if (_collection.ContainsHandler(typeof(IDidChangeTextDocumentHandler))) + { + var kinds = _collection .Select(x => x.Handler) .OfType() - .Where(x => x.GetRegistrationOptions()?.SyncKind != TextDocumentSyncKind.None) - .Min(z => z.GetRegistrationOptions()?.SyncKind) - : TextDocumentSyncKind.None; + .Select(x => x.GetRegistrationOptions()?.SyncKind ?? TextDocumentSyncKind.None) + .Where(x => x != TextDocumentSyncKind.None) + .ToArray(); + if (kinds.Any()) + { + textDocumentSyncKind = kinds.Min(z => z); + } + } if (_clientVersion == ClientVersion.Lsp2) { @@ -463,18 +486,15 @@ async Task IRequestHandler } else { - serverCapabilities.TextDocumentSync = new TextDocumentSyncOptions() - { - Change = textDocumentSyncKind ?? TextDocumentSyncKind.None, - OpenClose = _collection.ContainsHandler(typeof(IDidOpenTextDocumentHandler)) || _collection.ContainsHandler(typeof(IDidCloseTextDocumentHandler)), - Save = _collection.ContainsHandler(typeof(IDidSaveTextDocumentHandler)) ? - new SaveOptions() { IncludeText = true /* TODO: Make configurable */ } : - null, - WillSave = _collection.ContainsHandler(typeof(IWillSaveTextDocumentHandler)), - WillSaveWaitUntil = _collection.ContainsHandler(typeof(IWillSaveWaitUntilTextDocumentHandler)) - }; + defaultTextDocumentSyncOptions.Change = textDocumentSyncKind; + serverCapabilities.TextDocumentSync = defaultTextDocumentSyncOptions; } } + else + { + defaultTextDocumentSyncOptions.Change = TextDocumentSyncKind.None; + serverCapabilities.TextDocumentSync = defaultTextDocumentSyncOptions; + } // TODO: Need a call back here // serverCapabilities.Experimental; diff --git a/test/Lsp.Tests/ClientCapabilityProviderTests.cs b/test/Lsp.Tests/ClientCapabilityProviderTests.cs index bf02d87f3..dedb54a67 100644 --- a/test/Lsp.Tests/ClientCapabilityProviderTests.cs +++ b/test/Lsp.Tests/ClientCapabilityProviderTests.cs @@ -36,7 +36,8 @@ public void Should_AllowSupportedCapabilities(IJsonRpcHandler handler, object in public static IEnumerable AllowSupportedCapabilities() { - return GetItems(Capabilities, type => { + return GetItems(Capabilities, type => + { var handlerTypes = GetHandlerTypes(type); var handler = Substitute.For(handlerTypes.ToArray(), new object[0]); return new[] { handler, Activator.CreateInstance(typeof(Supports<>).MakeGenericType(type), true, Activator.CreateInstance(type)) }; @@ -56,7 +57,8 @@ public void Should_AllowUnsupportedCapabilities(IJsonRpcHandler handler, object public static IEnumerable AllowUnsupportedCapabilities() { - return GetItems(Capabilities, type => { + return GetItems(Capabilities, type => + { var handlerTypes = GetHandlerTypes(type); var handler = Substitute.For(handlerTypes.ToArray(), new object[0]); return new[] { handler, Activator.CreateInstance(typeof(Supports<>).MakeGenericType(type), false) }; @@ -104,7 +106,8 @@ public void Should_AllowNullSupportedCapabilities(IJsonRpcHandler handler, objec public static IEnumerable AllowNullSupportsCapabilities() { - return GetItems(Capabilities, type => { + return GetItems(Capabilities, type => + { var handlerTypes = GetHandlerTypes(type); var handler = Substitute.For(handlerTypes.ToArray(), new object[0]); return new[] { handler, Activator.CreateInstance(typeof(Supports<>).MakeGenericType(type), true) }; @@ -125,7 +128,8 @@ public void Should_DisallowDynamicSupportedCapabilities(IJsonRpcHandler handler, public static IEnumerable DisallowDynamicSupportsCapabilities() { - return GetItems(Capabilities, type => { + return GetItems(Capabilities, type => + { var handlerTypes = GetHandlerTypes(type); var handler = Substitute.For(handlerTypes.ToArray(), new object[0]); var capability = Activator.CreateInstance(type); @@ -145,12 +149,16 @@ public void Should_Handle_Mixed_Capabilities() var collection = new HandlerCollection(SupportedCapabilitiesFixture.AlwaysTrue, new TextDocumentIdentifiers()) { textDocumentSyncHandler, codeActionHandler, definitionHandler, typeDefinitionHandler }; var provider = new ClientCapabilityProvider(collection); - var capabilities = new ClientCapabilities() { - TextDocument = new TextDocumentClientCapabilities() { - CodeAction = new Supports(true, new CodeActionCapability() { + var capabilities = new ClientCapabilities() + { + TextDocument = new TextDocumentClientCapabilities() + { + CodeAction = new Supports(true, new CodeActionCapability() + { DynamicRegistration = false, }), - TypeDefinition = new Supports(true, new TypeDefinitionCapability() { + TypeDefinition = new Supports(true, new TypeDefinitionCapability() + { DynamicRegistration = true, }) } @@ -161,6 +169,57 @@ public void Should_Handle_Mixed_Capabilities() provider.HasStaticHandler(capabilities.TextDocument.TypeDefinition).Should().BeFalse(); } + [Fact] + public void GH162_TextDocumentSync_Should_Work_Without_WillSave_Or_WillSaveWaitUntil() + { + var textDocumentSyncHandler = TextDocumentSyncHandlerExtensions.With(DocumentSelector.ForPattern("**/*.cs"), "csharp"); + + var collection = new HandlerCollection(SupportedCapabilitiesFixture.AlwaysTrue, new TextDocumentIdentifiers()) { textDocumentSyncHandler }; + var provider = new ClientCapabilityProvider(collection); + var capabilities = new ClientCapabilities() + { + TextDocument = new TextDocumentClientCapabilities() + { + Synchronization = new SynchronizationCapability() + { + DidSave = true, + DynamicRegistration = false, + WillSave = true, + WillSaveWaitUntil = true + }, + } + }; + + provider.HasStaticHandler(capabilities.TextDocument.Synchronization).Should().BeTrue(); + } + + [Fact] + public void GH162_TextDocumentSync_Should_Work_With_WillSave_Or_WillSaveWaitUntil() + { + var textDocumentSyncHandler = TextDocumentSyncHandlerExtensions.With(DocumentSelector.ForPattern("**/*.cs"), "csharp"); + var willSaveTextDocumentHandler = Substitute.For(); + var willSaveWaitUntilTextDocumentHandler = Substitute.For(); + var didSaveTextDocumentHandler = Substitute.For(); + + var collection = new HandlerCollection(SupportedCapabilitiesFixture.AlwaysTrue, new TextDocumentIdentifiers()) { textDocumentSyncHandler, willSaveTextDocumentHandler, willSaveWaitUntilTextDocumentHandler, didSaveTextDocumentHandler }; + var provider = new ClientCapabilityProvider(collection); + var capabilities = new ClientCapabilities() + { + TextDocument = new TextDocumentClientCapabilities() + { + Synchronization = new SynchronizationCapability() + { + DidSave = true, + DynamicRegistration = false, + WillSave = true, + WillSaveWaitUntil = true + }, + } + }; + + provider.HasStaticHandler(capabilities.TextDocument.Synchronization).Should().BeTrue(); + } + private static bool HasHandler(ClientCapabilityProvider provider, object instance) { return (bool)typeof(ClientCapabilityProviderTests).GetTypeInfo() diff --git a/test/Lsp.Tests/Models/InitializeResultTests.cs b/test/Lsp.Tests/Models/InitializeResultTests.cs index c2731eac1..b2dc94f54 100644 --- a/test/Lsp.Tests/Models/InitializeResultTests.cs +++ b/test/Lsp.Tests/Models/InitializeResultTests.cs @@ -17,29 +17,36 @@ public class InitializeResultTests [Theory, JsonFixture] public void SimpleTest(string expected) { - var model = new InitializeResult() { - Capabilities = new ServerCapabilities() { + var model = new InitializeResult() + { + Capabilities = new ServerCapabilities() + { CodeActionProvider = true, - CodeLensProvider = new CodeLensOptions() { + CodeLensProvider = new CodeLensOptions() + { ResolveProvider = true, }, - CompletionProvider = new CompletionOptions() { + CompletionProvider = new CompletionOptions() + { ResolveProvider = true, TriggerCharacters = new[] { "a", "b", "c" } }, DefinitionProvider = true, DocumentFormattingProvider = true, DocumentHighlightProvider = true, - DocumentLinkProvider = new DocumentLinkOptions() { + DocumentLinkProvider = new DocumentLinkOptions() + { ResolveProvider = true }, - DocumentOnTypeFormattingProvider = new DocumentOnTypeFormattingOptions() { + DocumentOnTypeFormattingProvider = new DocumentOnTypeFormattingOptions() + { FirstTriggerCharacter = ".", MoreTriggerCharacter = new[] { ";", " " } }, DocumentRangeFormattingProvider = true, DocumentSymbolProvider = true, - ExecuteCommandProvider = new ExecuteCommandOptions() { + ExecuteCommandProvider = new ExecuteCommandOptions() + { Commands = new string[] { "command1", "command2" } }, Experimental = new Dictionary() { @@ -48,13 +55,16 @@ public void SimpleTest(string expected) HoverProvider = true, ReferencesProvider = true, RenameProvider = true, - SignatureHelpProvider = new SignatureHelpOptions() { + SignatureHelpProvider = new SignatureHelpOptions() + { TriggerCharacters = new[] { ";", " " } }, - TextDocumentSync = new TextDocumentSync(new TextDocumentSyncOptions() { + TextDocumentSync = new TextDocumentSync(new TextDocumentSyncOptions() + { Change = TextDocumentSyncKind.Full, OpenClose = true, - Save = new SaveOptions() { + Save = new SaveOptions() + { IncludeText = true }, WillSave = true, @@ -74,33 +84,42 @@ public void SimpleTest(string expected) [Theory, JsonFixture] public void BooleanOrTest(string expected) { - var model = new InitializeResult() { - Capabilities = new ServerCapabilities { - CodeActionProvider = new CodeActionOptions { - CodeActionKinds = new [] { + var model = new InitializeResult() + { + Capabilities = new ServerCapabilities + { + CodeActionProvider = new CodeActionOptions + { + CodeActionKinds = new[] { CodeActionKind.QuickFix } }, - ColorProvider = new ColorOptions { + ColorProvider = new ColorOptions + { DocumentSelector = DocumentSelector.ForPattern("**/*.foo"), Id = "foo" }, - DeclarationProvider = new DeclarationOptions { + DeclarationProvider = new DeclarationOptions + { DocumentSelector = DocumentSelector.ForPattern("**/*.foo"), Id = "foo" }, - FoldingRangeProvider = new FoldingRangeOptions { + FoldingRangeProvider = new FoldingRangeOptions + { DocumentSelector = DocumentSelector.ForPattern("**/*.foo"), Id = "foo" }, - ImplementationProvider = new ImplementationOptions { + ImplementationProvider = new ImplementationOptions + { DocumentSelector = DocumentSelector.ForPattern("**/*.foo"), Id = "foo" }, - RenameProvider = new RenameOptions { + RenameProvider = new RenameOptions + { PrepareProvider = true }, - TypeDefinitionProvider = new TypeDefinitionOptions { + TypeDefinitionProvider = new TypeDefinitionOptions + { DocumentSelector = DocumentSelector.ForPattern("**/*.foo"), Id = "foo" } From 52a13f00fc166d966670cc7595faca51820accb7 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 7 Aug 2019 14:21:51 -0400 Subject: [PATCH 2/5] Slight change --- src/Server/LanguageServer.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Server/LanguageServer.cs b/src/Server/LanguageServer.cs index d5fb6d3d4..fa3c635e0 100644 --- a/src/Server/LanguageServer.cs +++ b/src/Server/LanguageServer.cs @@ -487,14 +487,8 @@ async Task IRequestHandler else { defaultTextDocumentSyncOptions.Change = textDocumentSyncKind; - serverCapabilities.TextDocumentSync = defaultTextDocumentSyncOptions; } } - else - { - defaultTextDocumentSyncOptions.Change = TextDocumentSyncKind.None; - serverCapabilities.TextDocumentSync = defaultTextDocumentSyncOptions; - } // TODO: Need a call back here // serverCapabilities.Experimental; From b5ec461e297ddd72b4335b68057093100d48cc2c Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 7 Aug 2019 14:28:50 -0400 Subject: [PATCH 3/5] simplify the settings --- .../Capabilities/SynchronizationCapability.cs | 8 ++++- .../Capabilities/TextDocumentSyncOptions.cs | 33 +++++++++---------- src/Server/ClientCapabilityProvider.cs | 2 +- src/Server/LanguageServer.cs | 28 +++++++--------- 4 files changed, 34 insertions(+), 37 deletions(-) diff --git a/src/Protocol/Client/Capabilities/SynchronizationCapability.cs b/src/Protocol/Client/Capabilities/SynchronizationCapability.cs index 8d22a23e6..d84f0f3bb 100644 --- a/src/Protocol/Client/Capabilities/SynchronizationCapability.cs +++ b/src/Protocol/Client/Capabilities/SynchronizationCapability.cs @@ -5,7 +5,13 @@ namespace OmniSharp.Extensions.LanguageServer.Protocol.Client.Capabilities { - public class SynchronizationCapability : DynamicCapability, ConnectedCapability + public class SynchronizationCapability : DynamicCapability, + ConnectedCapability, + ConnectedCapability, + ConnectedCapability, + ConnectedCapability, + ConnectedCapability, + ConnectedCapability { /// /// The client supports sending will save notifications. diff --git a/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs b/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs index 044fcc9f4..0ec3395fd 100644 --- a/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs +++ b/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs @@ -37,28 +37,25 @@ public class TextDocumentSyncOptions : ITextDocumentSyncOptions [Optional] public SaveOptions Save { get; set; } - public static Func, TextDocumentSyncOptions> Of(TextDocumentSyncOptions @default) + public static TextDocumentSyncOptions Of(IEnumerable options) { - return options => + var change = TextDocumentSyncKind.None; + if (options.Any()) { - var change = @default.Change; - if (@default.Change == TextDocumentSyncKind.None && options.Any()) + change = options + .Where(x => x.Change != TextDocumentSyncKind.None) + .Min(z => z.Change); + } + return new TextDocumentSyncOptions() + { + OpenClose = options.Any(z => z.OpenClose), + Change = change, + WillSave = options.Any(z => z.WillSave), + WillSaveWaitUntil = options.Any(z => z.WillSaveWaitUntil), + Save = new SaveOptions() { - change = @default.Change > 0 ? @default.Change : options - .Where(x => x.Change != TextDocumentSyncKind.None) - .Min(z => z.Change); + IncludeText = options.Any(z => z.Save?.IncludeText == true) } - return new TextDocumentSyncOptions() - { - OpenClose = @default.OpenClose || options.Any(z => z.OpenClose), - Change = change, - WillSave = @default.WillSave || options.Any(z => z.WillSave), - WillSaveWaitUntil = @default.WillSaveWaitUntil || options.Any(z => z.WillSaveWaitUntil), - Save = new SaveOptions() - { - IncludeText = @default.Save?.IncludeText ?? options.Any(z => z.Save?.IncludeText == true) - } - }; }; } } diff --git a/src/Server/ClientCapabilityProvider.cs b/src/Server/ClientCapabilityProvider.cs index 59fd1b233..b4a8aebcc 100644 --- a/src/Server/ClientCapabilityProvider.cs +++ b/src/Server/ClientCapabilityProvider.cs @@ -30,7 +30,7 @@ public bool HasStaticHandler(Supports capability) .Where(x => x.GetTypeInfo().IsGenericType && x.GetTypeInfo().GetGenericTypeDefinition() == typeof(ConnectedCapability<>)) .Select(x => x.GetTypeInfo().GetGenericArguments()[0].GetTypeInfo()); - return handlerTypes.All(_collection.ContainsHandler); + return handlerTypes.Any(_collection.ContainsHandler); } public IOptionsGetter GetStaticOptions(Supports capability) diff --git a/src/Server/LanguageServer.cs b/src/Server/LanguageServer.cs index fa3c635e0..66b9de5ce 100644 --- a/src/Server/LanguageServer.cs +++ b/src/Server/LanguageServer.cs @@ -410,17 +410,6 @@ async Task IRequestHandler var ccp = new ClientCapabilityProvider(_collection); - var defaultTextDocumentSyncOptions = new TextDocumentSyncOptions() - { - Change = TextDocumentSyncKind.None, - OpenClose = _collection.ContainsHandler(typeof(IDidOpenTextDocumentHandler)) || _collection.ContainsHandler(typeof(IDidCloseTextDocumentHandler)), - Save = _collection.ContainsHandler(typeof(IDidSaveTextDocumentHandler)) ? - new SaveOptions() { IncludeText = true /* TODO: Make configurable */ } : - null, - WillSave = _collection.ContainsHandler(typeof(IWillSaveTextDocumentHandler)), - WillSaveWaitUntil = _collection.ContainsHandler(typeof(IWillSaveWaitUntilTextDocumentHandler)) - }; - var serverCapabilities = new ServerCapabilities() { CodeActionProvider = ccp.GetStaticOptions(textDocumentCapabilities.CodeAction).Get(CodeActionOptions.Of), @@ -434,7 +423,7 @@ async Task IRequestHandler DocumentRangeFormattingProvider = ccp.HasStaticHandler(textDocumentCapabilities.RangeFormatting), DocumentSymbolProvider = ccp.HasStaticHandler(textDocumentCapabilities.DocumentSymbol), ExecuteCommandProvider = ccp.GetStaticOptions(workspaceCapabilities.ExecuteCommand).Reduce(ExecuteCommandOptions.Of), - TextDocumentSync = ccp.GetStaticOptions(textDocumentCapabilities.Synchronization).Reduce(TextDocumentSyncOptions.Of(defaultTextDocumentSyncOptions)), + TextDocumentSync = ccp.GetStaticOptions(textDocumentCapabilities.Synchronization).Reduce(TextDocumentSyncOptions.Of), HoverProvider = ccp.HasStaticHandler(textDocumentCapabilities.Hover), ReferencesProvider = ccp.HasStaticHandler(textDocumentCapabilities.References), RenameProvider = ccp.GetStaticOptions(textDocumentCapabilities.Rename).Get(RenameOptions.Of), @@ -446,10 +435,6 @@ async Task IRequestHandler FoldingRangeProvider = ccp.GetStaticOptions(textDocumentCapabilities.FoldingRange).Get(FoldingRangeOptions.Of), DeclarationProvider = ccp.GetStaticOptions(textDocumentCapabilities.Declaration).Get(DeclarationOptions.Of), }; - if (serverCapabilities.TextDocumentSync.HasOptions) - { - defaultTextDocumentSyncOptions = serverCapabilities.TextDocumentSync.Options; - } if (_collection.ContainsHandler(typeof(IDidChangeWorkspaceFoldersHandler))) { @@ -486,7 +471,16 @@ async Task IRequestHandler } else { - defaultTextDocumentSyncOptions.Change = textDocumentSyncKind; + serverCapabilities.TextDocumentSync = new TextDocumentSyncOptions() + { + Change = TextDocumentSyncKind.None, + OpenClose = (_collection.ContainsHandler(typeof(IDidOpenTextDocumentHandler)) || _collection.ContainsHandler(typeof(IDidCloseTextDocumentHandler))), + Save = _collection.ContainsHandler(typeof(IDidSaveTextDocumentHandler)) ? + new SaveOptions() { IncludeText = true /* TODO: Make configurable */ } : + null, + WillSave = _collection.ContainsHandler(typeof(IWillSaveTextDocumentHandler)), + WillSaveWaitUntil = _collection.ContainsHandler(typeof(IWillSaveWaitUntilTextDocumentHandler)) + }; } } From 648581d24e97e85d41ce3d1adfc400dd42231f04 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Wed, 7 Aug 2019 14:29:46 -0400 Subject: [PATCH 4/5] remove extra braces --- src/Server/LanguageServer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server/LanguageServer.cs b/src/Server/LanguageServer.cs index 66b9de5ce..31535e8a4 100644 --- a/src/Server/LanguageServer.cs +++ b/src/Server/LanguageServer.cs @@ -474,7 +474,7 @@ async Task IRequestHandler serverCapabilities.TextDocumentSync = new TextDocumentSyncOptions() { Change = TextDocumentSyncKind.None, - OpenClose = (_collection.ContainsHandler(typeof(IDidOpenTextDocumentHandler)) || _collection.ContainsHandler(typeof(IDidCloseTextDocumentHandler))), + OpenClose = _collection.ContainsHandler(typeof(IDidOpenTextDocumentHandler)) || _collection.ContainsHandler(typeof(IDidCloseTextDocumentHandler)), Save = _collection.ContainsHandler(typeof(IDidSaveTextDocumentHandler)) ? new SaveOptions() { IncludeText = true /* TODO: Make configurable */ } : null, From 155948f7a0ba9f9fbe6cab7f1d2d17c3d8c6dce1 Mon Sep 17 00:00:00 2001 From: David Driscoll Date: Sat, 12 Oct 2019 14:11:49 -0400 Subject: [PATCH 5/5] ensure that one of the changes includes text document sync --- src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs b/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs index 0ec3395fd..8860c3619 100644 --- a/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs +++ b/src/Protocol/Server/Capabilities/TextDocumentSyncOptions.cs @@ -40,7 +40,7 @@ public class TextDocumentSyncOptions : ITextDocumentSyncOptions public static TextDocumentSyncOptions Of(IEnumerable options) { var change = TextDocumentSyncKind.None; - if (options.Any()) + if (options.Any(x => x.Change != TextDocumentSyncKind.None)) { change = options .Where(x => x.Change != TextDocumentSyncKind.None)