Skip to content

textDocumentSync coming back as 0/None #162

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
tom-bowles opened this issue Aug 7, 2019 · 8 comments
Closed

textDocumentSync coming back as 0/None #162

tom-bowles opened this issue Aug 7, 2019 · 8 comments

Comments

@tom-bowles
Copy link

When using OmniSharp LSP from emacs lsp-mode, the initialize handshake is as shown at the bottom. The server response contains "textDocumentSync": 0, so lsp-mode sends no textDocument/didChange notifications.

The relevant code in LanguageServer.cs is also shown below. The check for ccp.HasStaticHandler(textDocumentCapabilities.Synchronization) returns false because although there is a TextDocumentSyncHandler registered (specifically OmnisharpTextDocumentSyncHandler), TextDocumentSyncHandler doesn't provide IWillSaveTextDocumentHandler or IWillSaveWaitUntilTextDocumentHandler, both of which are linked to SynchronizationCapability.

Removing the check causes the server to return a textDocumentSync options block, but the change item is set to 1 (full) rather than 2 (incremental). This appears to be because there are two OmnisharpTextDocumentSyncHandlers registered, one for cake and one for cs/csx, and the cake one doesn't handle incremental. Not sure if that should be a separate issue for omnisharp-roslyn, but it means you can't send incremental changes for cs/csx files because the Min() call in the code below means it picks up kind 1.

            if (ccp.HasStaticHandler(textDocumentCapabilities.Synchronization))
            {
                var textDocumentSyncKind = _collection.ContainsHandler(typeof(IDidChangeTextDocumentHandler))
                    ? _collection
                        .Select(x => x.Handler)
                        .OfType<IDidChangeTextDocumentHandler>()
                        .Where(x => x.GetRegistrationOptions()?.SyncKind != TextDocumentSyncKind.None)
                        .Min(z => z.GetRegistrationOptions()?.SyncKind)
                    : TextDocumentSyncKind.None;

                if (_clientVersion == ClientVersion.Lsp2)
                {
                    serverCapabilities.TextDocumentSync = textDocumentSyncKind;
                }
                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))
                    };
                }
            }

[Trace - 12:19:49 PM] Sending request 'initialize - (55)'.
Params: {
  "processId": 33868,
  "rootPath": "p:/experiments/LspTest/",
  "rootUri": "file:///p:/experiments/LspTest/",
  "capabilities": {
    "workspace": {
      "workspaceEdit": {
        "documentChanges": true,
        "resourceOperations": [
          "create",
          "rename",
          "delete"
        ]
      },
      "applyEdit": true,
      "symbol": {
        "symbolKind": {
          "valueSet": [
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10,
            11,
            12,
            13,
            14,
            15,
            16,
            17,
            18,
            19,
            20,
            21,
            22,
            23,
            24,
            25,
            26
          ]
        }
      },
      "executeCommand": {
        "dynamicRegistration": false
      },
      "didChangeWatchedFiles": {
        "dynamicRegistration": true
      },
      "workspaceFolders": true,
      "configuration": true
    },
    "textDocument": {
      "declaration": {
        "linkSupport": true
      },
      "definition": {
        "linkSupport": true
      },
      "implementation": {
        "linkSupport": true
      },
      "typeDefinition": {
        "linkSupport": true
      },
      "synchronization": {
        "didSave": true,
        "willSaveWaitUntil": true
      },
      "documentSymbol": {
        "symbolKind": {
          "valueSet": [
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10,
            11,
            12,
            13,
            14,
            15,
            16,
            17,
            18,
            19,
            20,
            21,
            22,
            23,
            24,
            25,
            26
          ]
        },
        "hierarchicalDocumentSymbolSupport": true
      },
      "formatting": {
        "dynamicRegistration": true
      },
      "codeAction": {
        "dynamicRegistration": true,
        "codeActionLiteralSupport": {
          "codeActionKind": {
            "valueSet": [
              "",
              "quickfix",
              "refactor",
              "refactor.extract",
              "refactor.inline",
              "refactor.rewrite",
              "source",
              "source.organizeImports"
            ]
          }
        }
      },
      "completion": {
        "completionItem": {
          "snippetSupport": true,
          "documentationFormat": [
            "markdown"
          ]
        },
        "contextSupport": true
      },
      "signatureHelp": {
        "signatureInformation": {
          "parameterInformation": {
            "labelOffsetSupport": true
          }
        }
      },
      "documentLink": {
        "dynamicRegistration": true
      },
      "hover": {
        "contentFormat": [
          "markdown",
          "plaintext"
        ]
      },
      "foldingRange": {
        "dynamicRegistration": true,
        "rangeLimit": null,
        "lineFoldingOnly": false
      }
    }
  },
  "initializationOptions": null
}


[Trace - 12:20:01 PM] Received response 'initialize - (55)' in 753ms.
Result: {
  "capabilities": {
    "experimental": {
    },
    "renameProvider": {
      "prepareProvider": null
    },
    "documentOnTypeFormattingProvider": {
      "moreTriggerCharacter": [
        "}",
        ")"
      ],
      "firstTriggerCharacter": ";"
    },
    "documentRangeFormattingProvider": true,
    "documentFormattingProvider": null,
    "codeLensProvider": {
      "resolveProvider": true
    },
    "workspaceSymbolProvider": null,
    "documentSymbolProvider": true,
    "documentHighlightProvider": null,
    "referencesProvider": true,
    "definitionProvider": true,
    "signatureHelpProvider": {
      "triggerCharacters": [
        ".",
        "?",
        "["
      ]
    },
    "completionProvider": {
      "triggerCharacters": [
        "."
      ],
      "resolveProvider": null
    },
    "hoverProvider": true,
    "textDocumentSync": 0
  }
}
@david-driscoll
Copy link
Member

david-driscoll commented Aug 7, 2019

@tom-bowles sounds like there are two issues.

Issue 1

ccp.HasStaticHandler(textDocumentCapabilities.Synchronization) returns false if IWillSaveTextDocumentHandler or IWillSaveWaitUntilTextDocumentHandler are not implemented. That sounds a bug and I'll take a look at what might be causing it. If dynamic registration is supported for Synchronization then this will always return false (that's intended). Looking at the code it seems like this is a bug, that we'll look to fix.

Issue 2

Currently cake doesn't support incremental changes, so we only have one choice we can make, which is to force full text changes, this is because the InitializeResult does not define the ability to define more than one text change strategy. You have to pick one, and only one, so we pick the least specific one. It's easier to support full text change than it is incremental for all handlers.

Now the complication comes in when the client supports Dynamic Registration. If dynamic registration is supported, then we return TextDocumentSyncKind.None specifically because after initialize we turn around an register the handler specifically with it's defined TextDocumentSyncKind. When dynamic registration is not defined we again have to pick the least common denominator, so if there are any providers that take in TextDocumentSyncKind.Full we must fulfill that specification.

@david-driscoll
Copy link
Member

For the first issue, I've simply changed the implementation to look for Any vs All, this fixes this return value issue.

For the second issue with the first fixed, if synchronization is not dynamic, it should fall into the correct code path and return the proper values.

see #163

@tom-bowles
Copy link
Author

Wow, awesomely quick - thanks! I'll give this a try in the morning.

@david-driscoll
Copy link
Member

Well keep in mind we still have to update OmniSharp roslyn and stuff, unless you have a fork locally for your testing.

@tom-bowles
Copy link
Author

I do! However, I get:

System.InvalidOperationException: Sequence contains no elements
   at System.Linq.Enumerable.Min[TSource](IEnumerable`1 source)
   at OmniSharp.Extensions.LanguageServer.Protocol.Server.Capabilities.TextDocumentSyncOptions.Of(IEnumerable`1 options)
   at OmniSharp.Extensions.LanguageServer.Server.LanguageServer.<OmniSharp-Extensions-Embedded-MediatR-IRequestHandler<OmniSharp-Extensions-LanguageServer-Protocol-Models-InitializeParams\,OmniSharp-Extensions-LanguageServer-Protocol-Models-InitializeResult>-Handle>d__59.MoveNext() in D:\dev\csharp-language-server-protocol\src\Server\LanguageServer.cs:line 413
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at OmniSharp.Extensions.LanguageServer.Server.Pipelines.ResolveCommandPipeline`2.<Handle>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at OmniSharp.Extensions.JsonRpc.RequestRouterBase`1.<RouteRequest>d__6.MoveNext()

@david-driscoll
Copy link
Member

I think we did finally get this merged, let us know if it's still an issue.

@rwols
Copy link

rwols commented Apr 26, 2020

OmniSharp still sends "textDocumentSync": 0 as capability with v1.35.0 https://github.com/OmniSharp/omnisharp-roslyn/releases/tag/v1.35.0

For reference, here's a log from the SublimeText LSP plugin:

:: --> omnisharp initialize(1): {'initializationOptions': {}, 'processId': 6572, 'clientInfo': {'name': 'Sublime Text LSP'}, 'workspaceFolders': [{'name': 'python-language-server', 'uri': 'file:///D:/Development/python-language-server'}], 'rootPath': 'D:\\Development\\python-language-server', 'rootUri': 'file:///D:/Development/python-language-server', 'capabilities': {'textDocument': {'synchronization': {'willSave': True, 'didSave': True, 'willSaveWaitUntil': True}, 'references': {}, 'colorProvider': {}, 'publishDiagnostics': {'relatedInformation': True}, 'rangeFormatting': {}, 'declaration': {'linkSupport': True}, 'implementation': {'linkSupport': True}, 'completion': {'completionItemKind': {'valueSet': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]}, 'completionItem': {'snippetSupport': True, 'deprecatedSupport': True}}, 'codeAction': {'codeActionLiteralSupport': {'codeActionKind': {'valueSet': []}}}, 'signatureHelp': {'signatureInformation': {'documentationFormat': ['markdown', 'plaintext'], 'parameterInformation': {'labelOffsetSupport': True}}}, 'typeDefinition': {'linkSupport': True}, 'documentHighlight': {}, 'hover': {'contentFormat': ['markdown', 'plaintext']}, 'definition': {'linkSupport': True}, 'formatting': {}, 'documentSymbol': {'symbolKind': {'valueSet': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]}}, 'rename': {}}, 'workspace': {'configuration': True, 'executeCommand': {}, 'symbol': {'symbolKind': {'valueSet': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]}}, 'applyEdit': True, 'workspaceFolders': True, 'didChangeConfiguration': {}}, 'experimental': {}}}
:: <<< omnisharp 1: {'capabilities': {'completionProvider': {'triggerCharacters': ['.'], 'resolveProvider': False}, 'documentSymbolProvider': True, 'documentRangeFormattingProvider': True, 'hoverProvider': True, 'textDocumentSync': 0, 'codeLensProvider': {'resolveProvider': True}, 'documentHighlightProvider': False, 'experimental': {}, 'documentOnTypeFormattingProvider': {'firstTriggerCharacter': ';', 'moreTriggerCharacter': ['}', ')']}, 'workspaceSymbolProvider': False, 'definitionProvider': True, 'documentFormattingProvider': True, 'signatureHelpProvider': {'triggerCharacters': ['.', '?', '[']}, 'renameProvider': {'prepareProvider': False}, 'referencesProvider': True}}

I'm starting OmniSharp in a straightforward way. This is SublimeText-specific, but for reference here's my setup:

        "omnisharp":
        {
            "enabled": true,
            "command":
            [
                "C:/Users/Raoul/Downloads/OmniSharp/OmniSharp.exe",
                "--languageserver"
            ],
            "scopes": ["source.cs"],
            "syntaxes": ["Packages/C#/C#.sublime-syntax"],
            "languageId": "csharp"
        },

@razzmatazz
Copy link
Contributor

I believe omnisharp-roslyn was not updated to the latest csharp-language-server-protocol itself. I tried to do a PR to fix that, but some things are clearly not working for now (CI fails).

Maybe @david-driscoll could take a look..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants