The LanguageClient API doesn't seem to support `workspace/didChangeConfiguration`

Despite the docs stating that the LanguageClient API supports workspace/didChangeConfiguration that doesn’t seem to be the case, although maybe I’m just using the API wrong?

I’m working on the Intelephense language server extension and trying to add an option that will allow people to modify which stubs are available to the language server. That has to be done through the workspace/configuration language server API.

This is what I’m trying right now, without success:

client.start();

// Send notification 2 seconds after server starts before sending
// new configuration to make sure server is running.
setTimeout(() => {
	client.sendNotification(
		'workspace/didChangeConfiguration',
		{
			settings: {
				intelephense: {
					stubs: [...stubsDefault, 'wordpress'],
				},
				'intelephense.stubs': [
					...stubsDefault,
					'wordpress',
				],
			},
			intelephense: {
				stubs: [...stubsDefault, 'wordpress'],
			},
			'intelephense.stubs': [
				...stubsDefault,
				'wordpress',
			],
			'settings.intelephense.stubs': [
				...stubsDefault,
				'wordpress',
			],
		}
	);
}, 2000);

I’ve also tried wrapping the object parameter in an array ([ { intelephense: { stubs: [ /* ... */ ] } } ]), and that doesn’t work either.

None of the ways I’ve tried here successfully update the stubs used by Intelephense; it still can’t find any information about WordPress functions.

Am I not using the API correctly? Is this an issue in Nova? Or do I need to implement support for workspace/configuration manually?

I’m really not sure how to proceed here, and would love to get some input on what the intended use of the API is.

The workspace/didChangeConfiguration is not a message you send from your extension, but is sent by Nova itself. The data Nova sends comes from your extension’s Preferences and properties set with the Configuration API.

Here I have a user visible preferences item, defined in the extension’s config JSON (this being just a fragment of the config):

{
   "key": "gopls.ui.completion.usePlaceholders",
   "title": "usePlaceholders",
   "description": "placeholders enables placeholders for function parameters or struct  fields in completion responses.",
   "type": "boolean",
   "default": false
}

When I check or uncheck the box in the extension preferences UI, Nova sends this language server automatically:

[Trace - 20:10:03.667 PM] Sending notification 'workspace/didChangeConfiguration'.
Params: {"settings":{"gopls":{"ui.completion.usePlaceholders":true}}}

So, it is a matter of arranging the data, either with the user visible Preferences configuration, or set programmatically with the Configuration API, but then letting Nova and the language server handle the actually message exchange.

Related to this, the language server can also ask Nova for workspace configuration more or less whenever it wants. Continuing with the example, Nova gets this request from the gopls language server, which asks for configuration scoped to a “section” named gopls, where a “section” in Nova’s universe is simply the first part before the first dot (.) of extension preferences key. So for this request from gopls for the "gopls" section:

[Trace - 20:10:03.667 PM] Received request 'workspace/configuration - (2)'.
Params: {"items":[{"section":"gopls"}]}

Nova responds with all the extension preferences keys starting with gopls.:

[Trace - 20:10:03.668 PM] Sending response 'workspace/configuration - (2)' in 0ms.
Result: [{"ui.completion.usePlaceholders":true,"ui.navigation.importShortcut":"Both","build.buildFlags":[],"formatting.gofumpt":false,"ui.navigation.symbolMatcher":"Fuzzy","formatting.local":"","ui.documentation.linksInHover":true,"ui.navigation.symbolStyle":"Dynamic","build.directoryFilters":[],"ui.documentation.hoverKind":"FullDocumentation","ui.completion.matcher":"Fuzzy","ui.documentation.linkTarget":"pkg.go.dev"}]

I hope that helps!

-john

4 Likes

Ah, that makes sense, I guess. And worked too, so thank you! :smiley:

@logan y’all might want to consider adding this to the documentation, and at least refer to it in the LanguageClient API docs. I would’ve never figured this out without @jrf pointing this out :slight_smile:

Feature request @logan: The gopls language server has a nice dump of all RPC messages. It would be nifty if Nova could also do that in a language server independent fashion. Seeing the whole conversation is super helpful in understanding the boundary between the Nova-language server dialog, and supplemental extension-language server dialog used to fill in LSP bits that Nova doesn’t yet support as built in.

4 Likes

That is indeed a good suggestion! I will look into this.

Here is my “aha” moment :slight_smile:

Thanks a lot for the long explanation; after trying to bang my head against the wall several times in a frustrated attempt to figure out how to pass configuration information from Nova to the LPS, I now realise that my approach was totally wrong.

I totally subscribe to this feature request — at the very least, a bit of information on the documentation would go a long, long way towards maintaining my sanity :slight_smile: (although stopping to bang my head against the wall might also be useful…)

Note that the current documentation indeed shows that didChangeConfiguration/configuration protocol messages are supported — but sadly only links to Microsoft’s specification document, not how these are implemented within an extension’s code. It really requires a bit more information. Perhaps just add a link to this thread? :slight_smile:

2 Likes

This thread has gotten me so close to fixing an issue with my extension.

I frequently get requests like this:

Deno Language Server[10:12:03.157000] Received JSON-RPC request: number(0) workspace/configuration
{
  "id" : 0,
  "method" : "workspace\/configuration",
  "jsonrpc" : "2.0",
  "params" : {
    "items" : [
      {
        "section" : "deno",
        "scopeUri" : "file:\/\/\/Users\/gwil\/Projects\/nova-deno\/"
      }
    ]
  }
}

I had no idea how to get my extension to reply to these requests until I saw this thread.

So now I go ahead and add the following to my extension.json’s configWorkspace array:

{
      "key": "deno.enablePaths",
      "title": "Enabled paths",
      "description": "Choose paths in which to enable Deno integration. For features to be enabled invariably, leave this blank.",
      "type": "pathArray",
      "allowFolders": true,
      "relative": true
    },

The first part of the key is deno., so it should respond to the configuration requests for section deno, right? But it does not. No responses to these requests are sent (or I don’t see anything logged in the extension console — should I?)

What am I doing wrong?

I had a quick look at my old Deno extension and I saw the same thing in the LSP debug logs, it was requesting configuration but there was no response message. Weirdly though, that configuration was being set, so maybe nova isn’t logging that message?

For the extension I had an “examples” directory to test the server which my enabledPaths was pointing to and I was getting LSP stuff (hovers, completions, etc) in the examples folder but not for the extension itself, which is what I expected.

1 Like

This might save some time for others: I checked whether an extension’s base configuration (shared by all workspaces) passes config to a LanguageClient’s initializationOptions, and I can confirm that it does not.

Thank you, without your message I wouldn’t have known that the the config is actually being picked up!

Running on 11.9, I’m having a slightly different experience than The LanguageClient API doesn't seem to support `workspace/didChangeConfiguration` - #2 by jrf

The workspace/didChangeConfiguration notification does indeed seem to get sent automatically by Nova when a setting is changed via the extension Settings. For example when I check my Enable debug mode checkbox, this is sent:

Sending JSON-RPC notification: workspace/didChangeConfiguration
{
  "method" : "workspace\/didChangeConfiguration",
  "jsonrpc" : "2.0",
  "params" : {
    "settings" : {
      "gitlab.debug" : true
    }
  }
}

There are a couple problems with this:

  1. Our settings should always be prefixed with something, such as gitlab.debug rather than just debug. Nova sends gitlab.debug in the settings object, but my language server expects debug. So this automatic behavior doesn’t work well for me.

    This really is unfortunate, because that’s a great way to let Nova do the heavy lifting of notifying when that changes. It would be great if there was a way to either specify the prefix to strip off, or something in the configuration.

  2. Preferences/Configuration settings are not sandboxed and get stored to disk. This means I can’t have a setting for a personal access token because then any malicious extension could grab it. Instead I need to store it with the Credentials API.

    But that means I need to be able to send it to the language server. AFAIK tell, sending it during server initialization does not work.

So I tried what was basically asked about originally, sending the workspace/didChangeConfiguration notification. This did actually work for me:

      client.sendNotification("workspace/didChangeConfiguration", {
        settings: {
          token: 'XXXXX',
        }
      });

I know it works because the server validates the token successfully and returns a response.

It’s about 3 years after the original post, so it looks like this has been fixed.