Skip to content
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

Code completion on override method doesn't apply correctly #76141

Open
drzbida opened this issue Nov 28, 2024 · 25 comments
Open

Code completion on override method doesn't apply correctly #76141

drzbida opened this issue Nov 28, 2024 · 25 comments
Assignees
Milestone

Comments

@drzbida
Copy link

drzbida commented Nov 28, 2024

Version Used:
4.13.0-2.24558.12

Steps to Reproduce:

  1. Use the override keyword in a class
  2. Select one of the suggested method
  3. Apply the completion

Expected Behavior:
The completion should apply with the appropriate selected method.

Actual Behavior:
The suggested methods are correct, but the completion is not. Instead of the selected method, a single character appears, or paranthesis, or none at all. This happens in:

I expect that it was fixed client-side in vscode for normal C# as it somehow seems to work in that scenario.

Attached one more example:

2024-11-29_00-05-55.mp4

From what I see relevant in the logs, the sent workspace/executeCommand is correct, but the server responds with a few exceptions:

[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          ["end"] = {            character = 14,            line = 3          },          start = {            character = 14,            line = 3          }        },        rangeLength = 0,        text = "p"      }, {        range = {          ["end"] = {            character = 15,            line = 3          },          start = {            character = 15,            line = 3          }        },        rangeLength = 0,        text = "e"      }, {        range = {          ["end"] = {            character = 16,            line = 3          },          start = {            character = 16,            line = 3          }        },        rangeLength = 0,        text = "a"      }, {        range = {          ["end"] = {            character = 17,            line = 3          },          start = {            character = 17,            line = 3          }        },        rangeLength = 0,        text = "k"      }, {        range = {          ["end"] = {            character = 18,            line = 3          },          start = {            character = 18,            line = 3          }        },        rangeLength = 0,        text = "("      }, {        range = {          ["end"] = {            character = 19,            line = 3          },          start = {            character = 19,            line = 3          }        },        rangeLength = 0,        text = ")"      }, {        range = {          ["end"] = {            character = 20,            line = 3          },          start = {            character = 13,            line = 3          }        },        rangeLength = 7,        text = ""      }, {        range = {          ["end"] = {            character = 13,            line = 3          },          start = {            character = 13,            line = 3          }        },        rangeLength = 0,        text = ""      } },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs",      version = 29    }  }}
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          ["end"] = {            character = 14,            line = 3          },          start = {            character = 14,            line = 3          }        },        rangeLength = 0,        text = "p"      }, {        range = {          ["end"] = {            character = 15,            line = 3          },          start = {            character = 15,            line = 3          }        },        rangeLength = 0,        text = "e"      }, {        range = {          ["end"] = {            character = 16,            line = 3          },          start = {            character = 16,            line = 3          }        },        rangeLength = 0,        text = "a"      }, {        range = {          ["end"] = {            character = 17,            line = 3          },          start = {            character = 17,            line = 3          }        },        rangeLength = 0,        text = "k"      }, {        range = {          ["end"] = {            character = 18,            line = 3          },          start = {            character = 18,            line = 3          }        },        rangeLength = 0,        text = "("      }, {        range = {          ["end"] = {            character = 19,            line = 3          },          start = {            character = 19,            line = 3          }        },        rangeLength = 0,        text = ")"      }, {        range = {          ["end"] = {            character = 20,            line = 3          },          start = {            character = 13,            line = 3          }        },        rangeLength = 7,        text = ""      }, {        range = {          ["end"] = {            character = 13,            line = 3          },          start = {            character = 13,            line = 3          }        },        rangeLength = 0,        text = ""      } },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs",      version = 29    }  }}
[DEBUG][2024-11-28 19:49:56] ...m/lsp/client.lua:676	"LSP[roslyn]"	"client.request"	1	"workspace/executeCommand"	{  arguments = { {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"    }, {      newText = "\r\n    public override void Speak()\r\n    {\r\n        base.Speak();\r\n    }",      range = {        ["end"] = {          character = 13,          line = 3        },        start = {          character = 49,          line = 2        }      }    }, false, 130 },  command = "roslyn.client.completionComplexEdit",  title = "CompleteComplexEditCommand"}	<function 1>	3
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 31,  jsonrpc = "2.0",  method = "workspace/executeCommand",  params = {    arguments = { {        uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"      }, {        newText = "\r\n    public override void Speak()\r\n    {\r\n        base.Speak();\r\n    }",        range = {          ["end"] = {            character = 13,            line = 3          },          start = {            character = 49,            line = 2          }        }      }, false, 130 },    command = "roslyn.client.completionComplexEdit",    title = "CompleteComplexEditCommand"  }}
[DEBUG][2024-11-28 19:49:56] ...m/lsp/client.lua:676	"LSP[roslyn]"	"client.request"	1	"textDocument/diagnostic"	{  range = {    ["end"] = {      character = 0,      line = 5    },    start = {      character = 0,      line = 1    }  },  textDocument = {    uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"  }}	<function 1>	3
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 32,  jsonrpc = "2.0",  method = "textDocument/diagnostic",  params = {    range = {      ["end"] = {        character = 0,        line = 5      },      start = {        character = 0,        line = 1      }    },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"    }  }}
[DEBUG][2024-11-28 19:49:56] ...m/lsp/client.lua:676	"LSP[roslyn]"	"client.request"	1	"textDocument/diagnostic"	{  range = {    ["end"] = {      character = 0,      line = 5    },    start = {      character = 0,      line = 1    }  },  textDocument = {    uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"  }}	<function 1>	3
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 33,  jsonrpc = "2.0",  method = "textDocument/diagnostic",  params = {    range = {      ["end"] = {        character = 0,        line = 5      },      start = {        character = 0,        line = 1      }    },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"    }  }}
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  jsonrpc = "2.0",  method = "window/logMessage",  params = {    message = "[LanguageServerHost] System.InvalidOperationException: This cannot be done after listening has started.\r\n   at Microsoft.Verify.FailOperation(String message)\r\n   at StreamJsonRpc.JsonRpc.ThrowIfConfigurationLocked()\r\n   at StreamJsonRpc.JsonRpc.AddLocalRpcMethod(MethodInfo handler, Object target, JsonRpcMethodAttribute methodRpcSettings)\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.SetupRequestDispatcher(AbstractHandlerProvider handlerProvider) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 136\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.get_HandlerProvider() in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 94\r\n   at Microsoft.CodeAnalysis.LanguageServer.BaseService.LazyService`1.GetInstance(ILspServices lspServices) in /_/src/LanguageServer/Protocol/LspServices/BaseService.cs:line 46\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService(String typeName) in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 143\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 97\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetRequiredService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 90\r\n   at Microsoft.CodeAnalysis.LanguageServer.Handler.ExecuteWorkspaceCommandHandler.HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) in /_/src/LanguageServer/Protocol/Handler/WorkspaceCommand/ExecuteWorkspaceCommandHandler.cs:line 32\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.QueueItem`1.StartRequestAsync[TRequest,TResponse](TRequest request, TRequestContext context, IMethodHandler handler, String language, CancellationToken cancellationToken) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/QueueItem.cs:line 191",    type = 1  }}
[TRACE][2024-11-28 19:49:56] ...m/lsp/client.lua:1001	"notification"	"window/logMessage"	{  message = "[LanguageServerHost] System.InvalidOperationException: This cannot be done after listening has started.\r\n   at Microsoft.Verify.FailOperation(String message)\r\n   at StreamJsonRpc.JsonRpc.ThrowIfConfigurationLocked()\r\n   at StreamJsonRpc.JsonRpc.AddLocalRpcMethod(MethodInfo handler, Object target, JsonRpcMethodAttribute methodRpcSettings)\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.SetupRequestDispatcher(AbstractHandlerProvider handlerProvider) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 136\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.get_HandlerProvider() in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 94\r\n   at Microsoft.CodeAnalysis.LanguageServer.BaseService.LazyService`1.GetInstance(ILspServices lspServices) in /_/src/LanguageServer/Protocol/LspServices/BaseService.cs:line 46\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService(String typeName) in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 143\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 97\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetRequiredService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 90\r\n   at Microsoft.CodeAnalysis.LanguageServer.Handler.ExecuteWorkspaceCommandHandler.HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) in /_/src/LanguageServer/Protocol/Handler/WorkspaceCommand/ExecuteWorkspaceCommandHandler.cs:line 32\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.QueueItem`1.StartRequestAsync[TRequest,TResponse](TRequest request, TRequestContext context, IMethodHandler handler, String language, CancellationToken cancellationToken) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/QueueItem.cs:line 191",  type = 1}
[TRACE][2024-11-28 19:49:56] ...lsp/handlers.lua:711	"default_handler"	"window/logMessage"	{  ctx = '{\n  client_id = 1,\n  method = "window/logMessage"\n}',  result = {    message = "[LanguageServerHost] System.InvalidOperationException: This cannot be done after listening has started.\r\n   at Microsoft.Verify.FailOperation(String message)\r\n   at StreamJsonRpc.JsonRpc.ThrowIfConfigurationLocked()\r\n   at StreamJsonRpc.JsonRpc.AddLocalRpcMethod(MethodInfo handler, Object target, JsonRpcMethodAttribute methodRpcSettings)\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.SetupRequestDispatcher(AbstractHandlerProvider handlerProvider) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 136\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.get_HandlerProvider() in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 94\r\n   at Microsoft.CodeAnalysis.LanguageServer.BaseService.LazyService`1.GetInstance(ILspServices lspServices) in /_/src/LanguageServer/Protocol/LspServices/BaseService.cs:line 46\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService(String typeName) in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 143\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 97\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetRequiredService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 90\r\n   at Microsoft.CodeAnalysis.LanguageServer.Handler.ExecuteWorkspaceCommandHandler.HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) in /_/src/LanguageServer/Protocol/Handler/WorkspaceCommand/ExecuteWorkspaceCommandHandler.cs:line 32\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.QueueItem`1.StartRequestAsync[TRequest,TResponse](TRequest request, TRequestContext context, IMethodHandler handler, String language, CancellationToken cancellationToken) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/QueueItem.cs:line 191",    type = 1  }}
[ERROR][2024-11-28 19:49:56] ...lsp/handlers.lua:623	"[LanguageServerHost] System.InvalidOperationException: This cannot be done after listening has started.\r\n   at Microsoft.Verify.FailOperation(String message)\r\n   at StreamJsonRpc.JsonRpc.ThrowIfConfigurationLocked()\r\n   at StreamJsonRpc.JsonRpc.AddLocalRpcMethod(MethodInfo handler, Object target, JsonRpcMethodAttribute methodRpcSettings)\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.SetupRequestDispatcher(AbstractHandlerProvider handlerProvider) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 136\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.get_HandlerProvider() in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 94\r\n   at Microsoft.CodeAnalysis.LanguageServer.BaseService.LazyService`1.GetInstance(ILspServices lspServices) in /_/src/LanguageServer/Protocol/LspServices/BaseService.cs:line 46\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService(String typeName) in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 143\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 97\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetRequiredService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 90\r\n   at Microsoft.CodeAnalysis.LanguageServer.Handler.ExecuteWorkspaceCommandHandler.HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) in /_/src/LanguageServer/Protocol/Handler/WorkspaceCommand/ExecuteWorkspaceCommandHandler.cs:line 32\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.QueueItem`1.StartRequestAsync[TRequest,TResponse](TRequest request, TRequestContext context, IMethodHandler handler, String language, CancellationToken cancellationToken) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/QueueItem.cs:line 191"
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  error = {    code = -32000,    data = {      code = -2146233079,      message = "This cannot be done after listening has started.",      stack = "   at Microsoft.Verify.FailOperation(String message)\r\n   at StreamJsonRpc.JsonRpc.ThrowIfConfigurationLocked()\r\n   at StreamJsonRpc.JsonRpc.AddLocalRpcMethod(MethodInfo handler, Object target, JsonRpcMethodAttribute methodRpcSettings)\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.SetupRequestDispatcher(AbstractHandlerProvider handlerProvider) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 136\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.get_HandlerProvider() in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 94\r\n   at Microsoft.CodeAnalysis.LanguageServer.BaseService.LazyService`1.GetInstance(ILspServices lspServices) in /_/src/LanguageServer/Protocol/LspServices/BaseService.cs:line 46\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService(String typeName) in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 143\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 97\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetRequiredService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 90\r\n   at Microsoft.CodeAnalysis.LanguageServer.Handler.ExecuteWorkspaceCommandHandler.HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) in /_/src/LanguageServer/Protocol/Handler/WorkspaceCommand/ExecuteWorkspaceCommandHandler.cs:line 32\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.QueueItem`1.StartRequestAsync[TRequest,TResponse](TRequest request, TRequestContext context, IMethodHandler handler, String language, CancellationToken cancellationToken) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/QueueItem.cs:line 191\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.DelegatingEntryPoint.InvokeAsync(IRequestExecutionQueue`1 queue, Object requestObject, ILspServices lspServices, CancellationToken cancellationToken) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 204\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.SystemTextJsonLanguageServer`1.SystemTextJsonDelegatingEntryPoint.ExecuteRequestAsync(Nullable`1 request, CancellationToken cancellationToken) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/SystemTextJsonLanguageServer.cs:line 88",      type = "System.InvalidOperationException"    },    message = "This cannot be done after listening has started."  },  id = 31,  jsonrpc = "2.0"}
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          ["end"] = {            character = 13,            line = 3          },          start = {            character = 13,            line = 3          }        },        rangeLength = 0,        text = "("      }, {        range = {          ["end"] = {            character = 14,            line = 3          },          start = {            character = 14,            line = 3          }        },        rangeLength = 0,        text = ")"      } },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs",      version = 31    }  }}
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  jsonrpc = "2.0",  method = "textDocument/didChange",  params = {    contentChanges = { {        range = {          ["end"] = {            character = 13,            line = 3          },          start = {            character = 13,            line = 3          }        },        rangeLength = 0,        text = "("      }, {        range = {          ["end"] = {            character = 14,            line = 3          },          start = {            character = 14,            line = 3          }        },        rangeLength = 0,        text = ")"      } },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs",      version = 31    }  }}
[DEBUG][2024-11-28 19:49:56] ...m/lsp/client.lua:676	"LSP[roslyn]"	"client.request"	1	"textDocument/completion"	{  context = {    triggerCharacter = "(",    triggerKind = 2  },  position = {    character = 14,    line = 3  },  textDocument = {    uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"  }}	<function 1>	3
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 34,  jsonrpc = "2.0",  method = "textDocument/completion",  params = {    context = {      triggerCharacter = "(",      triggerKind = 2    },    position = {      character = 14,      line = 3    },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"    }  }}
[DEBUG][2024-11-28 19:49:56] ...m/lsp/client.lua:676	"LSP[roslyn]"	"client.request"	1	"textDocument/signatureHelp"	{  position = {    character = 14,    line = 3  },  textDocument = {    uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"  }}	<function 1>	3
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 35,  jsonrpc = "2.0",  method = "textDocument/signatureHelp",  params = {    position = {      character = 14,      line = 3    },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"    }  }}
[DEBUG][2024-11-28 19:49:56] ...m/lsp/client.lua:676	"LSP[roslyn]"	"client.request"	1	"textDocument/diagnostic"	{  range = {    ["end"] = {      character = 0,      line = 5    },    start = {      character = 0,      line = 1    }  },  textDocument = {    uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"  }}	<function 1>	3
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 36,  jsonrpc = "2.0",  method = "textDocument/diagnostic",  params = {    range = {      ["end"] = {        character = 0,        line = 5      },      start = {        character = 0,        line = 1      }    },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"    }  }}
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 34,  jsonrpc = "2.0"}
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 35,  jsonrpc = "2.0"}
[DEBUG][2024-11-28 19:49:56] ...m/lsp/client.lua:676	"LSP[roslyn]"	"client.request"	1	"textDocument/signatureHelp"	{  position = {    character = 14,    line = 3  },  textDocument = {    uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"  }}	<function 1>	3
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:286	"rpc.send"	{  id = 37,  jsonrpc = "2.0",  method = "textDocument/signatureHelp",  params = {    position = {      character = 14,      line = 3    },    textDocument = {      uri = "file:///C:/Users/bida/Desktop/testtraits/Dog.cs"    }  }}
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:408	"rpc.receive"	{  id = 37,  jsonrpc = "2.0"}
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Nov 28, 2024
@arunchndr arunchndr added VSCode and removed untriaged Issues and PRs which have not yet been triaged by a lead labels Dec 2, 2024
@arunchndr arunchndr added this to the 17.14 P1 milestone Dec 2, 2024
@genlu genlu added the Bug label Jan 13, 2025
@a-usr
Copy link

a-usr commented Feb 7, 2025

While I myself am not seeing roslyn send any exceptions, I still have the same issue as you. This also extends to attempting to perform completion in xml comments. (Nevermind that, I wasnt seeing any exceptions because blink.cmp wasnt calling the lsp command via workspace/executeCommand)

Excuse me for saying this, but while Im no Expert when it comes to the Language Server Protocol, it seems like Roslyn is once again making things unnecessarily difficult for external editors such as neovim.

I have compared the messages for completion for roslyn and lua_ls:

Roslyn output
{  
  id = 1840,  
  jsonrpc = "2.0",
  result = {
    command = {  
      arguments = {
        {
          uri = "file:///...."  -- Valid File URI
        },
        {
          newText = "<!---->",
          range = {
            ["end"] = { 
              character = 4,
              line = 11
            },
            start = {
              character = 4,
              line = 11
            }
          }
        },
        false,
        287 
      },
      command = "roslyn.client.completionComplexEdit",
      title = "CompleteComplexEditCommand"
    },
    commitCharacters = {},
    data = {
      ResultId = 44,
      TextDocument = {
        uri = "file:///...." -- Valid File URI
      }
    },
    documentation = {
      kind = "markdown",
      value = ""
    },
    insertTextFormat = 1,
    kind = 14,
    label = "!--",    
    textEdit = {
      newText = "",
      range = {       
        ["end"] = {
          character = 4,
          line = 11        
        },        
        start = {
          character = 4,
          line = 11        
        }
      }
    },    
    textEditText = ""
  }
}
lua_ls output
{
  id = 748,
  jsonrpc = "2.0",
  result = { 
    detail = "C:\\Users\\[myUsername]\\AppData\\Local\\nvim-data\\mason\\packages\\lua-language-server\\meta\\Lua 5.4 en-us utf8\\coroutine.lua",
    documentation = {
      kind = "markdown",      
      value = "* [[meta]](file:///c%3A/Users/[myUsername]/AppData/Local/nvim-data/mason/packages/lua-language-server/meta/Lua%205.4%20en-us%20utf8/coroutine.lua)"    
    },
    insertTextFormat = 2,
    kind = 17,
    label = "coroutine", 
    score = 0,
    sortText = "0001",
    textEdit = {
      newText = "coroutine",
      range = {
        ["end"] = {
          character = 9,
          line = 0 
        },
        start = {
          character = 9,
          line = 0
        }
      } 
    }
  }
}

The most notable difference here, is that lua_ls puts the completion data into the textEdit field of the result object, while roslyn relies on a command identified by the string roslyn.client.completionComplexEdit, which fails when the editor tries to make the lsp execute it, seemingly due to some handlers being initialized lazily after they are allowed to be initialized.

@CyrusNajmabadi
Copy link
Member

it seems like Roslyn is once again making things unnecessarily difficult

This is a complex edit, requiring a command, because not only do we have to insert the method body, we might also be adding imports at the top of the file. On top of that, we have to place the caret properly after inserting the full method body. This isn't possible with the simple textual API provided for completion.

The most notable difference here, is that lua_ls puts the completion data into the textEdit field of the result object,

Roslyn does this for most completions as well. However, as mentioned above, override completion is a complex case that goes beyond a simple text edit.

@a-usr
Copy link

a-usr commented Feb 10, 2025

This is a complex edit, requiring a command, because not only do we have to insert the method body, we might also be adding imports at the top of the file. On top of that, we have to place the caret properly after inserting the full method body. This isn't possible with the simple textual API provided for completion.

At least partially incorrect.
The CompletionItem Interface supports the optional member additionalTextEdits, which is an array of Text Edits. This makes it possible to add both method body, and possible imports. Additionally, as long as the new target location for the cursor is inside the method body, I am confident that it is very much possible to manipulate the cursor to move to its target location by splitting the body into two halves, and putting the second half into the aforementioned additionalTextEdits field. However, as I dont have any experience regarding the developing side of the LS Protocol, I could also be wrong

Roslyn does this for most completions as well. However, as mentioned above, override completion is a complex case that goes beyond a simple text edit.

While that is statistically correct, seeing that both XML-comment- and override-completion use the complexEdit command, at least some people working on the roslyn language server seem to be aware of the additionalTextEdits field, as regular completion works just fine, and even adds imports if needed.

@a-usr
Copy link

a-usr commented Feb 10, 2025

Furthermore, I am really curious to know why it is that the same functionality thats broken in neovim (and likely also other editors), works just fine in vscode.

@CyrusNajmabadi
Copy link
Member

However, as I dont have any experience regarding the developing side of the LS Protocol, I could also be wrong

The above does not work here. Lsp requires things like the additional edits to be returned with the results of completion. They cannot be returned through a callback when completion is invoked.

But this doesn't work for our complex edits because they are too computationally complex to compute up front (not hypothetical, we've measured this). Imagine a type with 100 complex overrides. Having to actually compute all the changes they all perform, and also see where they move the caret, and also then convert that to additional edits is not feasible within the time budget allowed.

Furthermore, I am really curious to know why it is that the same functionality thats broken in neovim (and likely also other editors), works just fine in vscode.

You'd have to check with neovim.

@CyrusNajmabadi
Copy link
Member

While that is statistically correct,

Override completion is a complex case. So we break it into the fast computation of what goes into the completion list, and the change that is made when completion is invoked. It is much more expensive than the other cases where we do use normal additional edits.

@CyrusNajmabadi
Copy link
Member

it seems like Roslyn is once again making things unnecessarily difficult for external editors such as neovim.

Roslyn's responsibility is to follow the lsp spec. If we are doing that, then the bug lies in the consumer of it. If we are not following the lsp spec, then that's something we can fix.

If you can examine the neovim side to see what's up, we can determine if it's an issue on their end our end.

@drzbida
Copy link
Author

drzbida commented Feb 10, 2025

I've linked in the original post a video with the same behavior happening in Razor VSCode, so being a neovim specific issue is highly unlikely in my opinion. As for why it is not happening for normal C# code in VSCode, my best guess is that is was somehow "patched" client side in that scenario as a lot of things unfortunately are.

At the very least on the specified Roslyn version I get the aforementioned exception from the server and I don't see anything wrong with the client request, at least to my limited understanding of how it should work

If there is any specific additional info, testing, or something else required please feel free to ask.

@CyrusNajmabadi
Copy link
Member

Wdym "patched client side"?

@CyrusNajmabadi
Copy link
Member

@dibarbet do you know what would cause:

LanguageServerHost] System.InvalidOperationException: This cannot be done after listening has started.\r\n   at Microsoft.Verify.FailOperation(String message)\r\n   at StreamJsonRpc.JsonRpc.ThrowIfConfigurationLocked()\r\n   at StreamJsonRpc.JsonRpc.AddLocalRpcMethod(MethodInfo handler, Object target, JsonRpcMethodAttribute methodRpcSettings)\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.SetupRequestDispatcher(AbstractHandlerProvider handlerProvider) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 136\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.AbstractLanguageServer`1.get_HandlerProvider() in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/AbstractLanguageServer.cs:line 94\r\n   at Microsoft.CodeAnalysis.LanguageServer.BaseService.LazyService`1.GetInstance(ILspServices lspServices) in /_/src/LanguageServer/Protocol/LspServices/BaseService.cs:line 46\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService(String typeName) in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 143\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 97\r\n   at Microsoft.CodeAnalysis.LanguageServer.LspServices.GetRequiredService[T]() in /_/src/LanguageServer/Protocol/LspServices/LspServices.cs:line 90\r\n   at Microsoft.CodeAnalysis.LanguageServer.Handler.ExecuteWorkspaceCommandHandler.HandleRequestAsync(ExecuteCommandParams request, RequestContext context, CancellationToken cancellationToken) in /_/src/LanguageServer/Protocol/Handler/WorkspaceCommand/ExecuteWorkspaceCommandHandler.cs:line 32\r\n   at Microsoft.CommonLanguageServerProtocol.Framework.QueueItem`1.StartRequestAsync[TRequest,TResponse](TRequest request, TRequestContext context, IMethodHandler handler, String language, CancellationToken cancellationToken) in /_/src/LanguageServer/Microsoft.CommonLanguageServerProtocol.Framework/QueueItem.cs:line 191"
[DEBUG][2024-11-28 19:49:56] .../vim/lsp/rpc.lua:408	

@CyrusNajmabadi
Copy link
Member

with the same behavior happening in Razor VSCode

Razor is not this repo. If they are mishandling things here, that would be GitHub.com/dotnet/razor :-)

@CyrusNajmabadi
Copy link
Member

If there is any specific additional info, testing, or something else required please feel free to ask.

Well, it would be good to know why neovim borks on the lsp responses containing complex edits.

@a-usr
Copy link

a-usr commented Feb 10, 2025

But this doesn't work for our complex edits because they are too computationally complex to compute up front (not hypothetical, we've measured this).

That is a very good point, which I failed to consider. Sorry for the trouble that I may have caused. One last question on this topic though: how do XML comments fit into this? At least some XML completions, like <!---->or <description> (?) could be inserted using regular completion semantics, no?

@a-usr
Copy link

a-usr commented Feb 10, 2025

Well, it would be good to know why neovim borks on the lsp responses containing complex edits.

As far as I could tell when I dug into Roslyns code (and from the call stack on the exception) it seemed like Roslyn was trying to initialize some lsp handler(s) (I believe?), which was/where left uninitialized in a Lazy<T> for whatever reason

@CyrusNajmabadi
Copy link
Member

could be inserted using regular completion semantics, no?

Not trivial to say. The specification explicitly says:

Additional text edits should be used to change text unrelated to the current cursor position (for example adding an import statement at the top of the file if the completion item will insert an unqualified type).

XML completion very much manipulates the cursor. So it seems like it would be inappropriate to try to use additional text edits here.

@CyrusNajmabadi
Copy link
Member

As far as I could tell when I dug into Roslyns code (and from the call stack on the exception) it seemed like Roslyn was trying to initialize some lsp handler(s) (I believe?), which was/where left uninitialized in a Lazy for whatever reason

We will look into that. But why is that impacting neovim. If it did get a normal lsp competing completions result back, it should be fine. Unless it is explicitly the "completion item resolve" call back into Roslyn that is failing.

@a-usr
Copy link

a-usr commented Feb 10, 2025

XML completion very much manipulates the cursor. So it seems like it would be inappropriate to try to use additional text edits here.

Well, one part does anyway. Let me re-explain my previously mentioned idea with the XML comment as an example:
What we want to archieve is the following: <!--|--> (| represents the Cursor here).
The part of the TextEdit, which would manipulate the Cursor here, is <!--. So, we use this part as the main edit, and append --> to the aditionalTextEdits (with the correct positioning of course). Wouldn't that work?

@a-usr
Copy link

a-usr commented Feb 10, 2025

We will look into that. But why is that impacting neovim. If it did get a normal lsp competing completions result back, it should be fine. Unless it is explicitly the "completion item resolve" call back into Roslyn that is failing.

As mentioned previously, its not the completion request, nor the completion resolve request, but the execute command request that is failing and throwing the exception

@CyrusNajmabadi
Copy link
Member

We will look into that. But why is that impacting neovim. If it did get a normal lsp competing completions result back, it should be fine. Unless it is explicitly the "completion item resolve" call back into Roslyn that is failing.

As mentioned previously, its not the completion request, nor the completion resolve request, but the execute command request that is failing and throwing the exception

Ok. We can look into that.

@CyrusNajmabadi
Copy link
Member

The part of the TextEdit, which would manipulate the Cursor here, is to the aditionalTextEdits (with the correct positioning of course). Wouldn't that work?.

Tbh, I don't believe so. For the reason above. It's Ill defined what a client would/should do here. For example, it could end up with the caret after the -->

Furthermore, this just simply isn't conducive to how Roslyn is built. This information isn't in our completion item. Our own design is that when the item is selected, it going through a resolution step that performs all the computation and does things like set the caret. We'd have to move to a different model for all of these cases. Our model works, and is explicitly supported by lsp. It means we can have the same core base code for lsp and noon lsp hosts.

Fundamentally, the rules are simple here. If we are not following the lsp spec, it's in us to fix. If we are, it's in the client. :-)

@dibarbet
Copy link
Member

dibarbet commented Feb 10, 2025

As noted above, the cost of computing the full edits upfront is expensive - too expensive to provide upfront for all completion items in the list. Adding on to the problem, VSCode does not support lazily resolving the main textEdit field via completionItem/resolve - so we cannot provide the textEdit lazily via resolve either.

Instead, we fill in the command field during completionItem/resolve with the appropriate text edit to make. This is the roslyn.client.completionComplexEdit command. Because we already know the edit we need to make (returned in the command args), this does not need to go back to the server to execute the command. Instead, the command's edit is applied client side. You can see this implementation here - https://github.com/dotnet/vscode-csharp/blob/3afe7b55d26bd7dadf957f1e6a37c7fe00654fb9/src/lsptoolshost/server/serverCommands.ts#L77

This of course has its own issues (for example fast typing can cancel the application of the override item). But this is what we came up with that worked within the limitations of VSCode.

I'm not 100% sure why this System.InvalidOperationException: This cannot be done after listening has started. is the exception being thrown, but executing the command on the server (as shown in the logs) will not work - the server has no implementation for it. It likely needs a client side implementation similar to what we do in VSCode.

I am open to making things easier here for other clients. For example, if a client indicated in the completion capabilities that resolveSupport.properties included the textEdit field - we could potentially have the server fill in the normal completionItem.textEdit field instead of using a custom command.

@a-usr
Copy link

a-usr commented Feb 10, 2025

You can see this implementation here - https://github.com/dotnet/vscode-csharp/blob/3afe7b55d26bd7dadf957f1e6a37c7fe00654fb9/src/lsptoolshost/server/serverCommands.ts#L77

I already saw that, however I was unsure wether that actually got called as a result of the command in the answer to the resolve request. Thanks for the explanation!

I am open to making things easier here for other clients. For example, if a client indicated in the completion capabilities that resolveSupport.properties included the textEdit field - we could potentially have the server fill in the normal completionItem.textEdit field instead of using a custom command.

That would be awesome, and is also pretty much what I was about to ask before I read that part of your comment. However, seeing as this isn't already implemented, is there more to this solution? (E.g. structure of the codebase making it difficult). Because I'd be willing to help

@dibarbet
Copy link
Member

dibarbet commented Feb 10, 2025

That would be awesome, and is also pretty much what I was about to ask before I read that part of your comment. However, seeing as this isn't already implemented, is there more to this solution? (E.g. structure of the codebase making it difficult). Because I'd be willing to help

I haven't dug into this bit of code for a bit, but it may be as simple as modifying this part of the code to check if the client supports modifying the textEdit field and updating the field instead of creating a command - https://github.com/dotnet/roslyn/blob/main/src/LanguageServer/Protocol/Handler/Completion/CompletionResultFactory.cs#L611

Note that of course whatever client is being used has to allow textEdit resolution on resolve and advertise the capability.

If that's mainly all we need I think we'd be willing to accept that contribution.

@drzbida
Copy link
Author

drzbida commented Feb 10, 2025

Instead, we fill in the command field during completionItem/resolve with the appropriate text edit to make. This is the roslyn.client.completionComplexEdit command. Because we already know the edit we need to make (returned in the command args), this does not need to go back to the server to execute the command. Instead, the command's edit is applied client side. You can see this implementation here - https://github.com/dotnet/vscode-csharp/blob/3afe7b55d26bd7dadf957f1e6a37c7fe00654fb9/src/lsptoolshost/server/serverCommands.ts#L77

Ah, I see. This can in theory be replicated in neovim as this is already done in roslyn.nvim for code actions https://github.com/seblyng/roslyn.nvim/blob/main/lua/roslyn/lsp_commands.lua

I've translated the vscode implementation you've linked in the plugin as a custom command here and verified that the command was indeed registered, but for some reason it never gets called. Well that is outside the scope of this issue, but my best guess is that it's somehow related to the exception which still happens.

I'm not 100% sure why this System.InvalidOperationException: This cannot be done after listening has started. is the exception being thrown

Do you have any hints or ideas as to why that exception might be thrown?

And as a sidenote, is there any list or friendlier ways to find out what functionalities the server expects to be done client side? Is such a search a reliable way to find out, or are there more? https://github.com/search?q=repo%3Adotnet%2Froslyn+roslyn.client&type=code

@dibarbet
Copy link
Member

Do you have any hints or ideas as to why that exception might be thrown?

Looks like the only test using this got disabled, and we don't really use server side workspace commands in VSCode today. So a bug slipped in. Tentative fix here - #77152. Do note that you'll still hit an exception if you try and execute the complex edit command on the server (just a different one). That command still needs to be executed client side (or pursue the path discussed above if the client supports text edit modification on resolve).

And as a sidenote, is there any list or friendlier ways to find out what functionalities the server expects to be done client side? Is such a search a reliable way to find out, or are there more? https://github.com/search?q=repo%3Adotnet%2Froslyn+roslyn.client&type=code

I think what you have there is the best way. We don't really have a centralized set of client side only commands, but they should be mostly prefixed with 'roslyn.client'.

dibarbet added a commit that referenced this issue Feb 11, 2025
Discovered in #76141

The other test for this was disabled (feature not currently used in
vscode), so this regression slipped in.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants