Using JETLS in neovim with dynamic registration

Since JETLS assumes textDocuments/completions/dynamicRegistration = true (LSP specification) in capabilities (for “lazy completion”) while cmp-nvim-lsp doesn’t support dynamic registration (source), it seems that the completion candidates are not updated correctly.

I also checked nvim’s built-in completion, mini.completion, and blink.cmp, but none of them supports this features according to the client capabilities definitions on the source codes and :=vim.lsp.protocol.make_client_capabilities()['textDocument']['completion']['dynamicRegistration'].
nvim-cmp-kit seems to support this feature but it looks experimental.

Does anyone have a configuration for JETLS with properly working completion?

I found that client not supporting textDocuments/completions/dynamicRegistration is not the cause of the problem.
Even though client doesn’t allow resolving completion item kind later (in client capabilities, textDocuments/completion/completionItem/resolveSupport (definition in LSP specification doesn’t include kind (for cmp-nvim-lsp, defined here)) while JETLS updates kind during completionItem/resolve.

Below are dumped JSON RPC when completing

V # here's the cursor
JSON RPC log

First textDocument/completion request:

{
  "jsonrpc": "2.0",
  "method": "textDocument/completion",
  "id": 11,
  "params": {
    "textDocument": {
      "uri": "file:///tmp/example.jl"
    },
    "position": {
      "line": 0,
      "character": 1
    },
    "context": {
      "triggerKind": 1
    }
  }
}

part of the response:

{
  "label": "Val",
  "labelDetails": {
    "description": "global"
  },
  "sortText": "100000",
  "insertTextFormat": 1,
  "textEdit": {
    "range": {
      "start": {
        "line": 0,
        "character": 0
      },
      "end": {
        "line": 0,
        "character": 1
      }
    },
    "newText": "Val"
  },
  "data": {
    "resolver_id": "##GlobalCompletionResolverInfo_resovler_id#286",
    "name": "Val"
  }
}

then, completionItem/resolve request:

{
  "jsonrpc": "2.0",
  "method": "completionItem/resolve",
  "id": 21,
  "params": {
    "sortText": "100000",
    "textEdit": {
      "newText": "Val",
      "range": {
        "end": {
          "line": 0,
          "character": 1
        },
        "start": {
          "line": 0,
          "character": 0
        }
      }
    },
    "data": {
      "resolver_id": "##GlobalCompletionResolverInfo_resovler_id#286",
      "name": "Val"
    },
    "insertTextFormat": 1,
    "label": "Val",
    "labelDetails": {
      "description": "global"
    }
  }
}

and its response:

{
  "jsonrpc": "2.0",
  "id": 21,
  "result": {
    "label": "Val",
    "labelDetails": {
      "description": "global [type]"
    },
    "kind": 22,
    "detail": "[type]",
    "documentation": {
      "kind": "markdown",
      "value": "```julia\nVal(c)\n```\n\nReturn `Val{c}()`, which contains no run-time data. Types like this can be used to pass the information between functions through the value `c`, which must be an `isbits` value or a `Symbol`. The intent of this construct is to be able to dispatch on constants directly (at compile time) without having to test the value of the constant at run time.\n\n# Examples\n\n```julia\njulia> f(::Val{true}) = \"Good\"\nf (generic function with 1 method)\n\njulia> f(::Val{false}) = \"Bad\"\nf (generic function with 2 methods)\n\njulia> f(Val(true))\n\"Good\"\n```\n"
    },
    "sortText": "100000",
    "insertTextFormat": 1,
    "textEdit": {
      "range": {
        "start": {
          "line": 0,
          "character": 0
        },
        "end": {
          "line": 0,
          "character": 1
        }
      },
      "newText": "Val"
    },
    "data": {
      "resolver_id": "##GlobalCompletionResolverInfo_resovler_id#286",
      "name": "Val"
    }
  }
}

result.kind is updated later (22 = Struct)

Since this is the problem of JETLS, I’ll open an issue on JETLS repo.