Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supporting Virtualenv in NeoVim #71

Closed
IceS2 opened this issue Feb 17, 2023 · 16 comments
Closed

Supporting Virtualenv in NeoVim #71

IceS2 opened this issue Feb 17, 2023 · 16 comments
Labels
question Further information is requested vim Related to the Neo(Vim) editor

Comments

@IceS2
Copy link

IceS2 commented Feb 17, 2023

Hello folks, is there anyway to select a virtualenv depending on the folder for neovim?
I'm really looking forward to testing ruff-lsp but I couldn't configure it properly...

I'm used to pyright with the following setup:

    require("lspconfig").pyright.setup {
      on_init = function(client)
        client.config.settings.python.pythonPath = get_python_path(client.config.root_dir)
        client.config.settings.venvPath = path.join(vim.env.PYENV_ROOT, 'versions')
        client.config.settings.venv = get_venv(client.config.root_dir)
      end
    }
  end,

Then the get_python_path and get_venv are lua functions defined to get the right value for each different folder where I'm in (using pyenv virtualenv and pyenv local version to setup the virtualenvs)

For ruff I tried something similar but didn't work.

Thanks a lot (=

@charliermarsh
Copy link
Member

I'm wondering if this was improved by #52. Would you be able to try again with the latest version of ruff-lsp?

@charliermarsh charliermarsh added the question Further information is requested label Mar 4, 2023
@IceS2
Copy link
Author

IceS2 commented Mar 7, 2023

Hey @charliermarsh, thanks for letting me know about that possible fix. I'm currently travelling but as soon as I get back home I'll try it out and let you know!

@IceS2
Copy link
Author

IceS2 commented Apr 11, 2023

It didn't seem to work... I'm using Mason to handle my LSP settings and have tried with and without the following without success...

  ["ruff_lsp"] = function()
    require("lspconfig")["ruff_lsp"].setup {
      on_attach = function(client, bufnr)
        client.server_capabilities.hoverProvider = false
      end,
      on_init = function(client)
        client.config.interpreter = get_python_path(client.config.root_dir)
      end
    }
  end,

Every time I check the :LspInfo it says that it's running in single file, so it's actually not detecting anything from the environment.

@bryanforbes
Copy link

@IceS2 I'm also using mason-lspconig and I essentially have something similar to you (I've simplified it a lot here):

local server_configs = {
  ruff_lsp = {
    return {
      init_options = {
        settings = {
          interpreter = { '.venv/bin/python' }
        }
      }
    }
  }
}

require('mason-lspconfig').setup_handlers({
  function(server_name)
    local server_config = server_configs[server_name] or {}
    require('lspconfig')[server_name].setup(server_config)
  end
})

It looks like you don't have the right structure for the config and you need to wrap the interpreter string in a list.

@charliermarsh It still feels a little odd that I need to set interpreter if I have a virtual environment activated. Shouldn't it check if there's a ruff executable for the current python interpreter before falling back to the built-in executable?

@IceS2
Copy link
Author

IceS2 commented Apr 11, 2023

Hey @bryanforbes , I got the structure from the mason.nvim repository

It could however be outdated and I'd love to know if there is any better way of doing it currently! I got it to configure different options for different LS. This is actually my complete setup_handlers

require("mason-lspconfig").setup_handlers {

  function(server_name)
    require("lspconfig")[server_name].setup {}
  end,

  ["pyright"] = function()
    require("lspconfig").pyright.setup {
      on_init = function(client)
        client.config.settings.python.pythonPath = get_python_path(client.config.root_dir)
        client.config.settings.venvPath = path.join(vim.env.PYENV_ROOT, 'versions')
        client.config.settings.venv = get_venv(client.config.root_dir)
      end
    }
  end,

  ["ruff_lsp"] = function()
    require("lspconfig")["ruff_lsp"].setup {
      on_attach = function(client, bufnr)
        client.server_capabilities.hoverProvider = false
      end,
      on_init = function(client)
        client.config.interpreter = get_python_path(client.config.root_dir)
      end
    }
  end,

  ["rust_analyzer"] = function()
    require("rust-tools").setup {}
  end
}

@bryanforbes
Copy link

@IceS2 what you have looks mostly fine (I think). I like to keep configuration options in one place, so I use a module-level server_configs table and look up the settings in the setup_handlers default handler.

From what I'm seeing in the source of ruff-lsp, you have to set the settings with init_options or in the before_init callback since ruff-lsp doesn't respond to workspace/didChangeConfiguration. You could try something like this:

  ["ruff_lsp"] = function()
    require("lspconfig")["ruff_lsp"].setup {
      on_attach = function(client, bufnr)
        client.server_capabilities.hoverProvider = false
      end,
      before_init = function(initialize_parameters, config)
        initialize_parameters.initializationOptions = {
          settings = {
            interpreter = { get_python_path(config.root_dir) }
          }
        }
      end
    }
  end,

You'll need to check :LspLog with the log level set to debug for a message that starts with Using interpreter executable: to ensure it's picking up the right interpreter.

@dhruvmanila
Copy link
Member

Can you tell me what's the use case of the virtual environment w.r.t. ruff-lsp? Ruff can be installed globally (aka pipx) and doesn't require you to install it in a local environment. Atleast for now, it doesn't need context of the project local environment unlike other tools such as mypy or pyright.

@esron
Copy link

esron commented Mar 13, 2024

Hello, @dhruvmanila
I have a use case for that. My team uses ruff in CI to check for code standards and we have the version pinned in pyproject.toml

@bellini666
Copy link

For anyone that needs a workaround for that, you can copy my soltion from my dotfiles: https://github.com/bellini666/dotfiles/blob/master/vim/lua/config/lsp.lua#L138

I have a function called find_python_cmd that will try to find a command (in this case, ruff) inside the bin directory of a virtualenv. It will try the VIRTUAL_ENV environment variable if defined, then try poetry, falling back to the system's installed one if no other option was found

@dhruvmanila
Copy link
Member

@esron Can you try enforcing that using the required-version setting (https://docs.astral.sh/ruff/settings/#required-version)? For example:

[tool.ruff]
required-version = "==0.3.2"

Output:

$ pipx run "ruff==0.3.1" .                           
ruff failed
  Cause: Required version `==0.3.2` does not match the running version `0.3.1`

@esron
Copy link

esron commented Mar 18, 2024

@dhruvmanila That is what we do. So when I have ruff in my machine in version 0.3.2 and in virtual env is 0.3.1, enforced by required-version nvim keeps showing an error:

LSP[ruff_lsp] Ruff: Lint failed (ruff failed
  Cause: Required version `==0.3.1` does not match the running version `0.3.2`
)

The solution for me at this point was to keep ruff in venv up to date.

@dhruvmanila
Copy link
Member

Yeah, the use case is valid and that's what the VS Code extension does as well.

@IceS2 you'll need to notify the server that you've changed the configuration with the following:

on_init = function(client)
  client.config.settings.interpreter = { vim.fn.exepath 'python3' }
  client.notify(M.workspace_didChangeConfiguration, { settings = client.config.settings })
  return true
end

But the problem here is that ruff-lsp doesn't support workspace/didChangeConfiguration (#42) but that's going to change with the new ruff server where we do plan on supporting it. (/cc @snowsignal)

For the time being, @esron I think the following solution would be sufficient as per my understanding:

init_options = {
  settings = {
    path = { vim.fn.exepath 'ruff' },
  },
}

This will pickup the executable installed in your virtual environment if there is any by given that the virtual environment is activated before running Neovim. This basically is like doing which ruff and as the virtual environment directory will be first in PATH, it will be picked up.

@IceS2
Copy link
Author

IceS2 commented Mar 25, 2024

Thanks a lot for the new info!

Feel free to close the thread if you think the workarounds and the upcoming ruff server are enough.
I'm more than happy!

@dhruvmanila dhruvmanila added the vim Related to the Neo(Vim) editor label Mar 25, 2024
@snowsignal
Copy link
Contributor

@IceS2 As previously stated, this should (hopefully) work with the new Ruff language server. We just added it to neovim-lspconfig under the name ruff. It's still in an experimental state with several missing features, but we're getting it stabilized fast, so stay tuned!

(just in case you wanted to know where to find it in the future)

@leiteg
Copy link

leiteg commented Apr 12, 2024

@snowsignal that's great news! Will ruff-lsp be deprecated in the future in favor of ruff server?

@snowsignal
Copy link
Contributor

@leiteg Yes, that's the eventual plan 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested vim Related to the Neo(Vim) editor
Projects
None yet
Development

No branches or pull requests

8 participants