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

What is needed to use this with other lsp clients? #43

Closed
Lenbok opened this issue Oct 30, 2024 · 18 comments
Closed

What is needed to use this with other lsp clients? #43

Lenbok opened this issue Oct 30, 2024 · 18 comments

Comments

@Lenbok
Copy link

Lenbok commented Oct 30, 2024

I tried to connect this language server up in emacs, using lsp-mode, and communication is established between the client and server fine. However when editing one of the examples in the nextflow-patterns repo, my lsp client sends various messages to the server but nothing of interest comes back.

What configuration settings does the language server expect?

e.g., when I trace the server initialization, I get:

[Trace - 06:00:34 PM] Sending request 'initialize - (1)'.
Params: {
  "processId": 81477,
  "rootPath": "/home/len/dev/nextflow-patterns",
  "clientInfo": {
    "name": "emacs",
    "version": "GNU Emacs 29.4.50 (build 2, x86_64-pc-linux-gnu, GTK+ Version 2.24.33, cairo version 1.18.0)\n of 2024-08-02"
  },
  "rootUri": "file:///home/len/dev/nextflow-patterns",
  "capabilities": {
    "general": {
      "positionEncodings": [
        "utf-32",
        "utf-16"
      ]
    },
    "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,
      "codeLens": {
        "refreshSupport": true
      },
      "fileOperations": {
        "didCreate": false,
        "willCreate": false,
        "didRename": true,
        "willRename": true,
        "didDelete": false,
        "willDelete": false
      }
    },
    "textDocument": {
      "declaration": {
        "dynamicRegistration": true,
        "linkSupport": true
      },
      "definition": {
        "dynamicRegistration": true,
        "linkSupport": true
      },
      "references": {
        "dynamicRegistration": true
      },
      "implementation": {
        "dynamicRegistration": true,
        "linkSupport": true
      },
      "typeDefinition": {
        "dynamicRegistration": true,
        "linkSupport": true
      },
      "synchronization": {
        "willSave": true,
        "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
      },
      "rangeFormatting": {
        "dynamicRegistration": true
      },
      "onTypeFormatting": {
        "dynamicRegistration": true
      },
      "rename": {
        "dynamicRegistration": true,
        "prepareSupport": true
      },
      "codeAction": {
        "dynamicRegistration": true,
        "isPreferredSupport": true,
        "codeActionLiteralSupport": {
          "codeActionKind": {
            "valueSet": [
              "",
              "quickfix",
              "refactor",
              "refactor.extract",
              "refactor.inline",
              "refactor.rewrite",
              "source",
              "source.organizeImports"
            ]
          }
        },
        "resolveSupport": {
          "properties": [
            "edit",
            "command"
          ]
        },
        "dataSupport": true
      },
      "completion": {
        "completionItem": {
          "snippetSupport": true,
          "documentationFormat": [
            "markdown",
            "plaintext"
          ],
          "resolveAdditionalTextEditsSupport": true,
          "insertReplaceSupport": true,
          "deprecatedSupport": true,
          "resolveSupport": {
            "properties": [
              "documentation",
              "detail",
              "additionalTextEdits",
              "command"
            ]
          },
          "insertTextModeSupport": {
            "valueSet": [
              1,
              2
            ]
          }
        },
        "contextSupport": true,
        "dynamicRegistration": true
      },
      "signatureHelp": {
        "signatureInformation": {
          "parameterInformation": {
            "labelOffsetSupport": true
          }
        },
        "dynamicRegistration": true
      },
      "documentLink": {
        "dynamicRegistration": true,
        "tooltipSupport": true
      },
      "hover": {
        "contentFormat": [
          "markdown",
          "plaintext"
        ],
        "dynamicRegistration": true
      },
      "foldingRange": {
        "dynamicRegistration": true
      },
      "selectionRange": {
        "dynamicRegistration": true
      },
      "callHierarchy": {
        "dynamicRegistration": false
      },
      "typeHierarchy": {
        "dynamicRegistration": true
      },
      "publishDiagnostics": {
        "relatedInformation": true,
        "tagSupport": {
          "valueSet": [
            1,
            2
          ]
        },
        "versionSupport": true
      },
      "diagnostic": {
        "dynamicRegistration": false,
        "relatedDocumentSupport": false
      },
      "linkedEditingRange": {
        "dynamicRegistration": true
      }
    },
    "window": {
      "workDoneProgress": true,
      "showDocument": {
        "support": true
      }
    }
  },
  "initializationOptions": null,
  "workDoneToken": "1",
  "workspaceFolders": [
    {
      "uri": "file:///home/len/dev/nextflow-patterns",
      "name": "nextflow-patterns"
    }
  ]
}


[Trace - 06:00:35 PM] Received response 'initialize - (1)' in 1007ms.
Result: {
  "capabilities": {
    "textDocumentSync": 2,
    "hoverProvider": true,
    "completionProvider": {
      "resolveProvider": null,
      "triggerCharacters": [
        "."
      ]
    },
    "definitionProvider": true,
    "referencesProvider": true,
    "documentSymbolProvider": true,
    "workspaceSymbolProvider": true,
    "codeLensProvider": {
      "resolveProvider": null
    },
    "documentFormattingProvider": true,
    "renameProvider": true,
    "documentLinkProvider": {
      "resolveProvider": null
    },
    "executeCommandProvider": {
      "commands": [
        "nextflow.server.previewDag"
      ]
    },
    "workspace": {
      "workspaceFolders": {
        "supported": true,
        "changeNotifications": true
      }
    },
    "callHierarchyProvider": true
  }
}


[Trace - 06:00:35 PM] Sending notification 'initialized'.
Params: {}


[Trace - 06:00:35 PM] Sending notification 'textDocument/didOpen'.
Params: {
  "textDocument": {
    "uri": "file:///home/len/dev/nextflow-patterns/process-when-empty.nf",
    "languageId": "nextflow",
    "version": 0,
    "text": "#!/usr/bin/env nextflow\n\n/*\n * Copyright (c) 2018, Centre for Genomic Regulation (CRG).\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n * \n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n /*\n  * author Paolo Di Tommaso <[email protected]> \n  */\n\nparams.inputs = ''\n\nprocess foo {\n  debug true  \n  input:\n  val x\n  when:\n  x == 'EMPTY'\n\n  script:\n  '''\n  echo hello\n  ''' \n}\n\nworkflow {\n  reads_ch = params.inputs\n    ? Channel.fromPath(params.inputs, checkIfExists:true)\n    : Channel.empty()\n\n  reads_ch \\\n    | ifEmpty { 'EMPTY' } \\\n    | foo\n}\n"
  }
}

but e.g. jump to definition requests come back empty:

[Trace - 06:19:30 PM] Sending request 'textDocument/definition - (148)'.
Params: {
  "textDocument": {
    "uri": "file:///home/len/dev/nextflow-patterns/process-when-empty.nf"
  },
  "position": {
    "line": 48,
    "character": 8
  }
}


[Trace - 06:19:30 PM] Received response 'textDocument/definition - (148)' in 1ms.
Result: []
[Trace - 06:20:51 PM] Sending request 'textDocument/definition - (152)'.
Params: {
  "textDocument": {
    "uri": "file:///home/len/dev/nextflow-patterns/process-when-empty.nf"
  },
  "position": {
    "line": 50,
    "character": 7
  }
}


[Trace - 06:20:51 PM] Received response 'textDocument/definition - (152)' in 1ms.
Result: []
@Lenbok
Copy link
Author

Lenbok commented Oct 30, 2024

If anyone wants to play along, here's my emacs nextflow use-package declaration that attempts to set up this server under both eglot and lsp-mode language server clients. (They call via a bash wrapper script nextflow-language-server that just calls java -jar on the language server jarfile I downloaded from this repo).

(use-package nextflow-mode
  :ensure (nextflow-mode :host github :repo "edmundmiller/nextflow-mode" :branch "master")
  :config
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 '(nextflow-mode . ("nextflow-language-server"))))

  (with-eval-after-load 'lsp-mode
    (defvar lsp-nextflow-server-command '("nextflow-language-server"))
    (lsp-register-client
     (make-lsp-client
      :new-connection (lsp-stdio-connection (lambda () lsp-nextflow-server-command))
      :major-modes '(nextflow-mode)
      :multi-root t
      :server-id 'nextflow-lsp))
    (add-to-list 'lsp-language-id-configuration '(nextflow-mode . "nextflow"))
    (lsp-consistency-check lsp-nextflow)))

@bentsherman
Copy link
Member

Tagging @edmundmiller since he is also trying to set up the language client for emacs

@edmundmiller
Copy link

Ooo!

I've got the lsp-mode download working with the vsix from the other repo. I think we can combine these and make a PR

@edmundmiller
Copy link

We could probably use this repo, but it would need CI set up and no guarantee the release numbers stay in sync.

Whatever @bentsherman thinks we should rely on.

@bentsherman
Copy link
Member

We can set up a repo in the nextflow-io org for you to collaborate

@Lenbok
Copy link
Author

Lenbok commented Oct 30, 2024

In terms of lsp-mode (which is the "batteries included" emacs language client), I see @edmundmiller has a fork where he's working on adding that (emacs-lsp/lsp-mode@master...edmundmiller:lsp-mode:master). He should be able to open a PR directly against upstream lsp-mode. (I think it should be able to download the language server jar from this repo's releases rather than the vscode vsix).

I think rather than needing another repo, what is needed is documentation in this repo on what configuration is required/accepted when using other clients, plus testing/debugging against other language clients (and either fixing issues here or in the repos of those other clients).

@edmundmiller
Copy link

edmundmiller commented Oct 31, 2024

@bentsherman
Copy link
Member

Are you saying there is one emacs language client that everyone uses to connect to all language servers?

@mehalter
Copy link

Yeah, isn't that how all of the editors do it? They have an implementation of a LSP client: for emacs there is a plugin that implements it, vim.lsp.Client is the native LSP client in Neovim, etc. These clients define how to connect to a server that implements the LSP spec then you tell it where to connect to the server either one that's already running or how to start it up.

I also was trying to get the language server jar up and running in the Neovim LSP client and got the server running and connected successfully but it didn't provide anything. Formatting/completion/etc. didn't seem to get provided.

@bentsherman
Copy link
Member

In vscode there is a typescript API that you use to connect vscode to the language server from a vscode extension. So there is no central repository for all vscode <-> language server integrations

@Lenbok
Copy link
Author

Lenbok commented Oct 31, 2024

In emacs there is one language client (eglot) that is now part of emacs, and there are a couple of alternatives available as external packages (lsp-mode, lsp-bridge), but as @mehalter notes, generally you choose a client and use it for pretty much all language servers, and it's usually a small amount of configuration to connect the client to the server for a particular language.

I haven't used the groovy language server that this one is derived from, but it might be worth trying in order to see whether it also has similar difficulties with other clients.

@edmundmiller
Copy link

@Lenbok
Copy link
Author

Lenbok commented Nov 5, 2024

@edmundmiller are you saying you have it actually working and jumping to definitions etc?

@edmundmiller
Copy link

edmundmiller commented Nov 5, 2024

Yep! I was mainly testing for formatting myself 😬

Give it a shot and let me know if anything isn't working!

Screenshots

Search for symbol
image

Look for references
image

Highlighting of imports
image

Jump to definition works.

Might need to clean up the imenu attempts I had in Nextflow-mode before.

Completions
image

Errors list
image

@Lenbok
Copy link
Author

Lenbok commented Nov 6, 2024

@edmundmiller Thanks, I was able to get the server working using your lsp-mode PR, and it was also the help I needed to get it working under eglot. It does seem the nextflow language server does not have a working "default" configuration, and so the language client has to send minimal configuration. From my experimentation, it must contain a "nextflow.files.exclude" section, so for eglot a minimal project config (i.e. .dir-locals.el) is:

;;; Directory Local Variables
;;; For more information see (info "(emacs) Directory Variables")
((nextflow-mode
  (eglot-workspace-configuration
   . (:nextflow (:files (:exclude [".git" ".nf-test" "work"]))))))

@bentsherman Would you be able to document the configuration options the server knows about and what the defaults are? (Possibly even give the server a default value for the above files directive).

@edmundmiller
Copy link

@Lenbok I think it's listed out in the schema on the vscode repo. I ran

(lsp-generate-settings "~/src/nf-core/vscode-language-nextflow/package.json" 'lsp-nextflow)

And in the neovim setup https://github.com/mason-org/mason-registry/blob/841ceaba936a1f18a927cb40ff303a3e8efd42ac/packages/nextflow-language-server/package.yaml#L20

@Lenbok
Copy link
Author

Lenbok commented Nov 6, 2024

@edmundmiller Neat, I didn't know about that ability to reverse engineer the server settings from the vscode package.json. That could come handy in future. Nevertheless, it feels like there should be some documentation as part of this repo that says what those settings are and do (and if there are any others that don't happen to be mentioned there), since in principle language servers are independent of any particular client such as vscode.

@bentsherman
Copy link
Member

Closing in favor of #56

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