nextflow-io / language-server

The Nextflow language server
Apache License 2.0
2 stars 0 forks source link

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

Open Lenbok opened 17 hours ago

Lenbok commented 17 hours ago

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 <paolo.ditommaso@gmail.com> \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 commented 17 hours ago

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 commented 13 hours ago

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

edmundmiller commented 11 hours ago

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 commented 11 hours ago

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 commented 11 hours ago

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

Lenbok commented 1 hour ago

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 (https://github.com/emacs-lsp/lsp-mode/compare/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).