Lsp: Custom environment variables for each language-server.

Created on 25 Sep 2017  路  8Comments  路  Source: sublimelsp/LSP

I use LSP mainly with python and python-language-server.

The server uses sys.path of my local python 3.6 installation to look for modules to provide completions or tooltips for. I would like to extend the lookup paths, so python-language-server can provide completions for _sublime.py_ and _sublime_plugin.py_ (and others), too.

There are 2 options to do that:

  1. Extend the workspace/didChangeConfiguration API to push custom environment variables to python-language-server. This requires both LSP and server changes.
  2. Extend the os.environ passed to POpen by PYTHONPATH=C:\Apps\Sublime environment variable.

While PYTHONPATH could be added as global environment variable in Windows, it might be useful to be able to set different variables/values per project and/or language-server.

The following proof of concept just adds the ST install dir to the os.environ and passes the result to subprocess.POpen.

        env = os.environ
        env["PYTHONPATH"] = os.path.dirname(sublime.executable_path())
        process = subprocess.Popen(
            server_binary_args,
            stdin=subprocess.PIPE,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            cwd=working_dir,
            env=env,
            startupinfo=si)

Tada: LSP provides completions for _sublime.py_ and _sublime_plugin.py_.

The question/request now is:

Could you/we add an "environment" key to the global/project specific client configuration, which contains a list of environment variables to add to os.environ?

Example:

      "pyls":
      {
        "command": ["pyls"],
        "environment": {
          "PYTHONPATH": "C:\\Apps\\Sublime;C:\\projects\\libs",
        },
        "scopes": ["source.python"],
        "syntaxes": ["Packages/Python/Python.sublime-syntax"],
        "languageId": "python"
      },

All 8 comments

I think these kinds of extra vars should ideally be handled through experimental fields in the protocol. But it requires both the server (pyls) as well as the client (we) to set it up. There's an experimental field in the ClientCapabilities structure as well as in the ServerCapabilities structure. I'm imagining a dict for the experimental field with a key named extra_search_paths or something and whose value is a list of paths. But the environment variables method works too for now.

Using the protocol would be the cleanest solution - no question.

This looks immediately useful.

Sublime already makes an updated PATH available to launch subprocesses, is this not true for other environment variables?

If I launch

PYTHONPATH=/path/to/sublimepkg sublime

would the env inherited by the subprocess also have PYTHONPATH?

Either way, it seems reasonable to me that a parallel to the build system's env variable be exposed. If this means hardcoding the path to sublime.executable_path() in the project file that's probably ok?

You are correct. A globally created PYTHONPATH would be loaded and passed to POpen. The issue is, I do not want to generally publish ST's python3.3 libraries to my python 3.6 interpreter. It might cause conflicts and avoidable trouble. Therefore I need a way to locally send custom variables to pyls, so only Jedi knows about these paths - just to know where to look for completions. Maybe someone else who is writing comprehensive C programs, needs completions for files not within a global path variable. They may be required for single projects only.

But as LSP is not for python only, this issue tries to ask for a very general way to pass custom variables/configuration to a language-server. I don't know what other language-servers need.

The common question is: Is there a way to let the (each) language-server know about certain search paths to use for library lookups, which are not part of the current workspace.

My first idea therefore was the didChangeConfiguration API, but I do know too little about the protocol details to judge which solution is the best or is intended to be used. I just found the env thing working with little changes needed.

I even have a hacky little implementation, which adds the "environement" key to the client config, but this might interfere with your work on the configuration manager.

Anything you add under "settings" under a client key is sent to the server immediately after startup, see https://github.com/tomv564/LSP/blob/bb469648766ce5ae3606747811b643e666e3274d/main.py#L995-L999
You would have to agree with the python language server on the setting name and how it is to be used.

As you might imagine, LSP does not dictate how to override library lookups for all language servers

I never intended a "python" specific but a standard way of passing configurations. 馃槈

If I understand correct, the pyls would get the configParams object with one settings child? All settings would be located in configParams["settings"]?

_Looks very similar to Microsoft's example server._

https://code.visualstudio.com/docs/extensions/example-language-server

// The settings interface describe the server relevant settings part
interface Settings {
    lspSample: ExampleSettings;
}

// These are the example settings we defined in the client's package.json
// file
interface ExampleSettings {
    maxNumberOfProblems: number;
}

// hold the maxNumberOfProblems setting
let maxNumberOfProblems: number;
// The settings have changed. Is send on server activation
// as well.
connection.onDidChangeConfiguration((change) => {
    let settings = <Settings>change.settings;
    maxNumberOfProblems = settings.lspSample.maxNumberOfProblems || 100;
    // Revalidate any open text documents
    documents.all().forEach(validateTextDocument);
});

Sounds good.

@deathaxe , I like the idea of adding an env dict to the client settings for extending / overriding the current env. Would that solve your problem? Want to do up a PR?

I'll have a look on it. Should not be too complicated.

Btw.: I'd suggest to add all settings keys such as "settings", ... as empty objects to the default config so user's can quickly see available elements to work with.

Was this page helpful?
0 / 5 - 0 ratings