perl-language-server icon indicating copy to clipboard operation
perl-language-server copied to clipboard

Add Emacs lsp-mode installation instructions

Open c-alpha opened this issue 2 years ago • 18 comments

c-alpha avatar Aug 04 '22 17:08 c-alpha

@c-alpha Thanks for submitting this. I just have a couple of questions.

FractalBoy avatar Aug 04 '22 18:08 FractalBoy

LSP Mode has a couple of options, which are described well (IMO) in its documentation. FWIW, I've added a link to the advanced configuration docs.

c-alpha avatar Aug 05 '22 12:08 c-alpha

@c-alpha I updated the settings - they now start with pls. (not perl.pls). Ready for you to make the PR on the emacs side of things. Thanks!

FractalBoy avatar Aug 30 '22 03:08 FractalBoy

@c-alpha I updated the settings - they now start with pls. (not perl.pls). Ready for you to make the PR on the emacs side of things. Thanks!

Thanks for the swift action! I've got the LSP Mode modifications basically working.

One thing that caught my eye was the parameters. As I have things now, it does not send them in the initialize message, but a as a settings element in a later workspace/didChangeConfiguration message. I guess it's to enable different settings per project. Could that be that an issue on the server side?

Anecdotally, it also sends parameters for another server, which is succinctly called gopls 😉. "Close miss, but no cigar", as they say...

[Trace - 06:57:13 PM] Sending notification 'workspace/didChangeConfiguration'.
Params: {
  "settings": {
    "pls": {
      "cmd": "pls",
      "perlcritic": {
        "enabled": true
      },
      "syntax": {
        "enabled": true
      }
    },
    "gopls": {
      "usePlaceholders": true,
      "hoverKind": "SynopsisDocumentation",
      "buildFlags": [],
      "linkTarget": "pkg.go.dev",
      "codelenses": {
        "gc_details": false,
        "generate": true,
        "regenerate_cgo": true,
        "tidy": true,
        "upgrade_dependency": true,
        "test": true,
        "vendor": true
      },
      "linksInHover": true,
      "gofumpt": false,
      "local": "",
      "directoryFilters": [],
      "importShortcut": "Both",
      "symbolMatcher": "Fuzzy",
      "symbolStyle": "Dynamic"
    }
  }
}

What the trace also shows is that it sends all parameters for which it has a value. Thus you see the two boolean ones (can't have those without a value), and the cmd one. All other ones are strings and have an elisp value of nil, which means "unset" (and is different from an empty string).

c-alpha avatar Sep 08 '22 17:09 c-alpha

One more thing:

[Trace - 06:57:13 PM] Received request 'workspace/configuration - (1).
Params: {
  "items": [
    {
      "section": "perl"
    }
  ]
}


[Trace - 06:57:13 PM] Sending response 'workspace/configuration - (1)'. Processing request took 0ms
Params: {
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "showLocalVars": false
    }
  ]
}

Shouldn't the request from PLS instead of "perl" ask for"pls" now?

c-alpha avatar Sep 08 '22 17:09 c-alpha

That's how configuration is sent. It's not included in initialize, you need to request the config from the server with a workspace/configuration request.

We should be requesting both perl and pls - I'm surprised you're only seeing perl. Did you upgrade to 0.900+?

FractalBoy avatar Sep 08 '22 19:09 FractalBoy

Marc Reisner @.***> writes:

That's how configuration is sent. It's not included in initialize, you need to request the config from the server with a workspace/configuration request.

Ok so we have three options of passing the parameters:

  1. I can send them as initializationOptions in the initialize message;

  2. I can send them as settings in a later workspace/didChangeConfiguration message when the workspace is opened;

  3. I can wait for the PLS server to send a workspace/configuration and provide them in the response.

Wen can do any one, any two, or all of them. Which option do you prefer?

We should be requesting both perl and pls - I'm surprised you're only seeing perl. Did you upgrade to 0.900+?

Ah, stupid me ! Of course I had forgotten to. The new version 0.902 (which I just installed) does request both indeed.

c-alpha avatar Sep 09 '22 21:09 c-alpha

I don't think initializationOptions is the correct thing to use here.

/**
* User provided initialization options.
*/
initializationOptions?: LSPAny

The way it should work is:

  1. The server sends a workspace/configuration request on startup, the client sends the configuration as a result
  2. Any time the configuration changes, the client sends a workspace/didChangeConfiguration notification, and the server responds (if they wish) with a workspace/configuration request, and the client sends the configuration as a result.

Are you seeing other servers accept initializationOptions for configuration?

FractalBoy avatar Sep 09 '22 21:09 FractalBoy

To be 100% clear - yes, workspace/didChangeConfiguration sends a settings parameter. However, in practice, the client is not required to actually include any settings in it. VSCode just sends a null value for "settings". Because of that, I wrote PLS to ignore whatever is actually passed as params for workspace/didChangeConfiguration, and just re-request the entire configuration.

FractalBoy avatar Sep 09 '22 21:09 FractalBoy

Marc Reisner @.***> writes:

I don't think initializationOptions is the correct thing to use here.

I wasn't trying to advertise using it. I just mentioned it for completeness, so we don't overlook anything.

[...] The way it should work is:

  1. The server sends a workspace/configuration request on startup, the client sends the configuration as a result

  2. Any time the configuration changes, the client sends a workspace/didChangeConfiguration notification, and the server responds (if they wish) with a workspace/configuration request, and the client sends the configuration as a result. [...]

The way I have it now, sends the parameters as the payload of the workspace/didChangeConfiguration notification already, which happens right after the initialize request/response.

Later, the PLS server sends a workspace/configuration request anyway (regardless of whether I sent the workspace/didChangeConfiguration notification before or not), and the client sends a response with the parameters.

The trace below shows the complete communication between the client in Emacs, and the PLS server when opening a Perl file. Search for lines starting with "[Trace" to jump from message to message.

[Trace - 12:39:58 PM] Sending request 'initialize - (8)'.
Params: {
  "processId": null,
  "rootPath": "/Users/path-redacted/Downloads",
  "clientInfo": {
    "name": "emacs",
    "version": "GNU Emacs 29.0.50 (build 1, x86_64-apple-darwin20.6.0, NS appkit-2202.70 Version 11.6.8 (Build 20G730))\n of 2022-09-08"
  },
  "rootUri": "file:///Users/path-redacted/Downloads",
  "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,
      "semanticTokens": {
        "refreshSupport": false
      },
      "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
      },
      "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
      },
      "semanticTokens": {
        "dynamicRegistration": true,
        "requests": {
          "range": true,
          "full": true
        },
        "tokenModifiers": [
          "declaration",
          "definition",
          "implementation",
          "readonly",
          "static",
          "deprecated",
          "abstract",
          "async",
          "modification",
          "documentation",
          "defaultLibrary"
        ],
        "tokenTypes": [
          "comment",
          "keyword",
          "string",
          "number",
          "regexp",
          "operator",
          "namespace",
          "type",
          "struct",
          "class",
          "interface",
          "enum",
          "typeParameter",
          "function",
          "method",
          "member",
          "property",
          "event",
          "macro",
          "variable",
          "parameter",
          "label",
          "enumConstant",
          "enumMember",
          "dependent",
          "concept"
        ],
        "formats": [
          "relative"
        ]
      },
      "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": false,
          "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
      },
      "linkedEditingRange": {
        "dynamicRegistration": true
      }
    },
    "window": {
      "workDoneProgress": true,
      "showDocument": {
        "support": true
      }
    }
  },
  "initializationOptions": null,
  "workDoneToken": "1"
}


[Trace - 12:40:00 PM] Received response 'initialize - (8)' in 899ms.
Result: {
  "capabilities": {
    "completionItem": {
      "labelDetailsSupport": true
    },
    "completionProvider": {
      "resolveProvider": true,
      "triggerCharacters": [
        ">",
        ":",
        "$",
        "@",
        "%",
        " ",
        "-"
      ]
    },
    "hoverProvider": true,
    "workspaceSymbolProvider": true,
    "signatureHelpProvider": {
      "triggerCharacters": [
        "(",
        ","
      ]
    },
    "documentRangeFormattingProvider": true,
    "definitionProvider": true,
    "documentSymbolProvider": true,
    "workspace": {
      "workspaceFolders": {
        "supported": true,
        "changeNotifications": true
      }
    },
    "executeCommandProvider": {
      "commands": [
        "pls.sortImports"
      ]
    },
    "documentFormattingProvider": true,
    "textDocumentSync": {
      "save": true,
      "openClose": true,
      "change": 2
    }
  }
}


[Trace - 12:40:00 PM] Sending notification 'initialized'.
Params: {}


[Trace - 12:40:00 PM] Sending notification 'workspace/didChangeConfiguration'.
Params: {
  "settings": {
    "pls": {
      "cmd": "pls",
      "perlcritic": {
        "enabled": true
      },
      "syntax": {
        "enabled": true
      }
    },
    "gopls": {
      "usePlaceholders": true,
      "hoverKind": "SynopsisDocumentation",
      "buildFlags": [],
      "linkTarget": "pkg.go.dev",
      "codelenses": {
        "gc_details": false,
        "generate": true,
        "regenerate_cgo": true,
        "tidy": true,
        "upgrade_dependency": true,
        "test": true,
        "vendor": true
      },
      "linksInHover": true,
      "gofumpt": false,
      "local": "",
      "directoryFilters": [],
      "importShortcut": "Both",
      "symbolMatcher": "Fuzzy",
      "symbolStyle": "Dynamic"
    }
  }
}


[Trace - 12:40:00 PM] Sending notification 'textDocument/didOpen'.
Params: {
  "textDocument": {
    "uri": "file:///Users/path-redacted/Downloads/ctest.pl",
    "languageId": "perl",
    "version": 0,
    "text": "#!/Users/path-redacted/Library/Perl/perl5/perls/perl-5.36.0/bin/perl\n\nuse strict;\nuse warnings;\nuse feature qw(say);\nuse Locale::Codes ':constants';\n\nuse Data::Printer;\nsub Dumper { np @_, colored => 1, array_max => 0 }\n\nmy $obj = new Locale::Codes 'country';\n\nsub canonicalize_country {\n  my ($candidate) = @_;\n  my $code = $obj->name2code($candidate);\n  return ( $obj->code2name($code) );\n}\n\nmy $country = canonicalize_country( $ARGV[0] );\nif ($country) {\n  say $country;\n}\n"
  }
}


[Trace - 12:40:00 PM] Sending request 'textDocument/documentSymbol - (9)'.
Params: {
  "textDocument": {
    "uri": "file:///Users/path-redacted/Downloads/ctest.pl"
  }
}


[Trace - 12:40:00 PM] Sending notification '$/cancelRequest'.
Params: {
  "id": 9
}


[Trace - 12:40:00 PM] Sending request 'textDocument/documentSymbol - (10)'.
Params: {
  "textDocument": {
    "uri": "file:///Users/path-redacted/Downloads/ctest.pl"
  }
}


[Trace - 12:40:00 PM] Received request 'workspace/configuration - (1).
Params: {
  "items": [
    {
      "section": "perl"
    },
    {
      "section": "pls"
    }
  ]
}


[Trace - 12:40:00 PM] Sending response 'workspace/configuration - (1)'. Processing request took 2ms
Params: {
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "showLocalVars": false
    },
    {
      "cmd": "pls",
      "perlcritic": {
        "enabled": true
      },
      "syntax": {
        "enabled": true
      }
    }
  ]
}


[Trace - 12:40:01 PM] Received response 'textDocument/documentSymbol - (10)' in 315ms.
Result: [
  {
    "name": "main",
    "range": {
      "end": {
        "line": 22,
        "character": 1
      },
      "start": {
        "line": 0,
        "character": 0
      }
    },
    "kind": 4,
    "selectionRange": {
      "end": {
        "line": 22,
        "character": 1
      },
      "start": {
        "line": 0,
        "character": 0
      }
    },
    "children": [
      {
        "range": {
          "end": {
            "character": 50,
            "line": 8
          },
          "start": {
            "line": 8,
            "character": 0
          }
        },
        "kind": 12,
        "name": "Dumper",
        "children": [],
        "selectionRange": {
          "end": {
            "character": 50,
            "line": 8
          },
          "start": {
            "line": 8,
            "character": 0
          }
        }
      },
      {
        "kind": 13,
        "range": {
          "end": {
            "character": 7,
            "line": 10
          },
          "start": {
            "line": 10,
            "character": 3
          }
        },
        "name": "$obj",
        "selectionRange": {
          "end": {
            "character": 7,
            "line": 10
          },
          "start": {
            "line": 10,
            "character": 3
          }
        }
      },
      {
        "name": "canonicalize_country",
        "range": {
          "start": {
            "line": 12,
            "character": 0
          },
          "end": {
            "character": 131,
            "line": 16
          }
        },
        "kind": 12,
        "selectionRange": {
          "start": {
            "line": 12,
            "character": 0
          },
          "end": {
            "character": 131,
            "line": 16
          }
        },
        "children": [
          {
            "range": {
              "start": {
                "character": 6,
                "line": 13
              },
              "end": {
                "character": 16,
                "line": 13
              }
            },
            "kind": 13,
            "selectionRange": {
              "start": {
                "character": 6,
                "line": 13
              },
              "end": {
                "character": 16,
                "line": 13
              }
            },
            "name": "$candidate"
          },
          {
            "selectionRange": {
              "end": {
                "line": 14,
                "character": 10
              },
              "start": {
                "line": 14,
                "character": 5
              }
            },
            "name": "$code",
            "range": {
              "end": {
                "line": 14,
                "character": 10
              },
              "start": {
                "line": 14,
                "character": 5
              }
            },
            "kind": 13
          }
        ]
      },
      {
        "range": {
          "end": {
            "character": 11,
            "line": 18
          },
          "start": {
            "character": 3,
            "line": 18
          }
        },
        "kind": 13,
        "name": "$country",
        "selectionRange": {
          "end": {
            "character": 11,
            "line": 18
          },
          "start": {
            "character": 3,
            "line": 18
          }
        }
      }
    ]
  }
]


[Trace - 12:40:01 PM] Received notification 'textDocument/publishDiagnostics'.
Params: {
  "diagnostics": [],
  "uri": "file:///Users/path-redacted/Downloads/ctest.pl",
  "version": 0
}


[Trace - 12:40:01 PM] Received notification '$/progress'.
Params: {
  "token": "D8AD2DCC",
  "value": {
    "kind": "report",
    "percentage": 25,
    "message": "Indexed ctest.pl (1/4)"
  }
}


[Trace - 12:40:02 PM] Received notification '$/progress'.
Params: {
  "token": "D8AD2DCC",
  "value": {
    "message": "Indexed ctest2.pl (2/4)",
    "percentage": 50,
    "kind": "report"
  }
}


[Trace - 12:40:02 PM] Received notification '$/progress'.
Params: {
  "token": "D8AD2DCC",
  "value": {
    "percentage": 75,
    "kind": "report",
    "message": "Indexed fstest.pl (3/4)"
  }
}


[Trace - 12:40:02 PM] Received notification '$/progress'.
Params: {
  "value": {
    "kind": "report",
    "percentage": 100,
    "message": "Indexed profanity.pl (4/4)"
  },
  "token": "D8AD2DCC"
}


[Trace - 12:40:02 PM] Received notification '$/progress'.
Params: {
  "token": "D8AD2DCC",
  "value": {
    "message": "Finished indexing all files",
    "kind": "end"
  }
}


[Trace - 12:40:03 PM] Received request 'client/registerCapability - (2).
Params: {
  "registrations": [
    {
      "method": "workspace/didChangeConfiguration",
      "id": "did-change-configuration"
    },
    {
      "id": "did-change-watched-files",
      "registerOptions": {
        "watchers": [
          {
            "globPattern": "**/*"
          }
        ]
      },
      "method": "workspace/didChangeWatchedFiles"
    }
  ]
}


[Trace - 12:40:03 PM] Sending response 'client/registerCapability - (2)'. Processing request took 48ms
Params: {
  "jsonrpc": "2.0",
  "id": 2,
  "result": null
}


[Trace - 12:40:03 PM] Received request 'window/workDoneProgress/create - (3).
Params: {
  "token": "D8AD2DCC"
}


[Trace - 12:40:03 PM] Sending response 'window/workDoneProgress/create - (3)'. Processing request took 0ms
Params: {
  "jsonrpc": "2.0",
  "id": 3,
  "result": null
}


[Trace - 12:40:03 PM] Received notification '$/progress'.
Params: {
  "value": {
    "title": "Indexing",
    "percentage": 0,
    "kind": "begin",
    "cancellable": null
  },
  "token": "D8AD2DCC"
}


[Trace - 12:40:03 PM] Received request 'workspace/configuration - (4).
Params: {
  "items": [
    {
      "section": "perl"
    },
    {
      "section": "pls"
    }
  ]
}


[Trace - 12:40:03 PM] Sending response 'workspace/configuration - (4)'. Processing request took 1ms
Params: {
  "jsonrpc": "2.0",
  "id": 4,
  "result": [
    {
      "showLocalVars": false
    },
    {
      "cmd": "pls",
      "perlcritic": {
        "enabled": true
      },
      "syntax": {
        "enabled": true
      }
    }
  ]
}


[Trace - 12:40:03 PM] Received notification 'textDocument/publishDiagnostics'.
Params: {
  "diagnostics": [],
  "uri": "file:///Users/path-redacted/Downloads/ctest.pl",
  "version": 0
}


[Trace - 12:40:04 PM] Received notification 'textDocument/publishDiagnostics'.
Params: {
  "diagnostics": [],
  "version": 0,
  "uri": "file:///Users/path-redacted/Downloads/ctest.pl"
}

c-alpha avatar Sep 09 '22 23:09 c-alpha

Okay, looks good.

FractalBoy avatar Sep 09 '22 23:09 FractalBoy

Marc Reisner @.***> writes:

To be 100% clear - yes, workspace/didChangeConfiguration sends a settings parameter. However, in practice, the client is not required to actually include any settings in it. VSCode just sends a null value for "settings". Because of that, I wrote PLS to ignore whatever is actually passed as params for workspace/didChangeConfiguration, and just re-request the entire configuration.

Thanks for the clarification. So I can leave stuff as it is now. I'll submit the PR to lsp-mode in due course then.

c-alpha avatar Sep 10 '22 13:09 c-alpha

[...] I'll submit the PR to lsp-mode in due course then.

Done. https://github.com/emacs-lsp/lsp-mode/pull/3715

c-alpha avatar Sep 10 '22 13:09 c-alpha

I noticed that pls.syntax.args was not included in your PR. It's a newer option, not likely to be used, but we should probably still include it.

FractalBoy avatar Sep 11 '22 15:09 FractalBoy

Marc Reisner @.***> writes:

I noticed that perl.syntax.args was not included in your PR. It's a newer option, not likely to be used, but we should probably still include it.

Well spotted; thanks for double-checking!

There was also one other where yo had chosen a more speaking name.

PR is updated: https://github.com/emacs-lsp/lsp-mode/pull/3715/commits/9e65f95afd40643ed28f568394d342033c20532b

c-alpha avatar Sep 13 '22 16:09 c-alpha

Looks good, thanks!

FractalBoy avatar Sep 14 '22 13:09 FractalBoy

Marc Reisner @.***> writes:

Looks good, thanks!

My PR against lsp-mode has just been approved and merged. Hooray! Now we'll have to wait for lsp-mode 8.0.1 to be released.

I'll make updates to the PR here in PLS, so that the README.md points to 'lsp-mode'.

c-alpha avatar Sep 14 '22 15:09 c-alpha

Great, thanks!

FractalBoy avatar Sep 14 '22 15:09 FractalBoy

@c-alpha This is pretty old, but picking it back up again. Are there any outstanding changes to the README needed before this is merged?

FractalBoy avatar Jun 24 '24 03:06 FractalBoy

Thanks for getting back on this! I have just checked the lsp-mode release notes, and it turns out that the feature was released there with revision 9.0.0. I have just updated the readme to point to that official revision number.

Nothing else to do from my side.

Thanks in advance!

c-alpha avatar Jun 24 '24 11:06 c-alpha