perl-language-server
perl-language-server copied to clipboard
Add Emacs lsp-mode installation instructions
@c-alpha Thanks for submitting this. I just have a couple of questions.
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 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!
@c-alpha I updated the settings - they now start with
pls.
(notperl.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).
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?
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+?
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 aworkspace/configuration
request.
Ok so we have three options of passing the parameters:
-
I can send them as initializationOptions in the initialize message;
-
I can send them as settings in a later workspace/didChangeConfiguration message when the workspace is opened;
-
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
andpls
- I'm surprised you're only seeingperl
. Did you upgrade to 0.900+?
Ah, stupid me
I don't think initializationOptions is the correct thing to use here.
/**
* User provided initialization options.
*/
initializationOptions?: LSPAny
The way it should work is:
- The server sends a workspace/configuration request on startup, the client sends the configuration as a result
- 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?
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.
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:
The server sends a workspace/configuration request on startup, the client sends the configuration as a result
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"
}
Okay, looks good.
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.
[...] I'll submit the PR to lsp-mode in due course then.
Done. https://github.com/emacs-lsp/lsp-mode/pull/3715
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.
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
Looks good, thanks!
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'.
Great, thanks!
@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?
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!