mason.nvim icon indicating copy to clipboard operation
mason.nvim copied to clipboard

Detect preinstalled packages in environment

Open nhat-vo opened this issue 2 years ago • 9 comments

I've searched open issues for similar requests

Yes

Is your feature request related to a problem? Please describe.

It seems that preinstalled packages are not recognized by Mason.

For example, I am using conda to manage my virtual environments, and Mason's pylint does not recognized the installed package. I found a solution by installing pylint in my own virtual environment and change Mason's config PATH to append instead of prepend. However, I noticed that in this case, if I remove Mason's pylint but keep the one in conda, Mason does not recognize the latter.

Describe the solution you'd like

It would be quite convenient to have Mason auto detect the preinstalled package, so that extensions such as mason-null-ls knows not to reinstall them.

Describe potential alternatives you've considered

No response

Additional context

No response

nhat-vo avatar Nov 15 '22 18:11 nhat-vo

Hello!

[...] and Mason's pylint does not recognized the installed package.

However, I noticed that in this case, if I remove Mason's pylint but keep the one in conda, Mason does not recognize the latter.

I'm not sure I'm following this part. What do you mean by "recognize" here?

It would be quite convenient to have Mason auto detect the preinstalled package, so that extensions such as mason-null-ls knows not to reinstall them.

This sounds more like a feature request to mason-null-ls, as Mason itself doesn't auto-install packages (yet).

williamboman avatar Nov 16 '22 14:11 williamboman

Hi! For example, I have an environment with mypy installed

$ conda list | grep mypy
mypy                      0.981           py310h06a4308_0
mypy_extensions           0.4.3           py310h06a4308_0

but in the :Mason dialogue, it does not recognize this as installed.

 ✗ mypy

I just think that it would be more convenient to be able to detect this, perhaps by checking that the binary exists in PATH.

nhat-vo avatar Nov 18 '22 21:11 nhat-vo

This is my temporary solution, this is more related to null-ls.nvim than to mason.nvim:

require("null-ls").setup({
    sources = {
        null_ls.builtins.diagnostics.pylint.with({
            -- command has to be the path of the pylint executable you installed in the virtual environment
            command = vim.fn.system({ "which", "pylint" }):gsub("[\n]", ""),
        }),
    },
})

I had to remove pylint from mason.nvim because the command which pylint would return that first, second it returns the pylint path from the virtual environment and last it returns the pylint path installed globally, now pylint won't give me warnings like "unable to import".

I am using linux and venv for the virtual environment.

gmr458 avatar Nov 18 '22 22:11 gmr458

but in the :Mason dialogue, it does not recognize this as installed.

 ✗ mypy

I just think that it would be more convenient to be able to detect this, perhaps by checking that the binary exists in PATH.

Ah now I understand. So yeah Mason won't detect installations outside of Mason. It's also something it will never do, for various reasons. However, detecting existing installations of a package and present these in the :Mason UI might be an interesting feature, if it helps people. Perhaps something like this:

Screenshot 2022-11-19 at 11 49 54

Although to be honest, this would be pretty far down the backlog (and currently not entirely feasible).

williamboman avatar Nov 19 '22 10:11 williamboman

I had to remove pylint from mason.nvim because the command which pylint would return that first, second it returns the pylint path from the virtual environment and last it returns the pylint path installed globally, now pylint won't give me warnings like "unable to import".

You can change the PATH setting of mason to be append:

require("mason").setup {
  PATH = "append"
}

This will put executables installed by Mason last in PATH, giving precedence to your "normal PATH".

By the way you should not have to shell out to locate the executable via which pylint. libuv (which null-ls uses for spawning commands) traverses PATH to find the command, yielding the exact same result.

williamboman avatar Nov 19 '22 10:11 williamboman

Ah now I understand. So yeah Mason won't detect installations outside of Mason. It's also something it will never do, for various reasons. However, detecting existing installations of a package and present these in the :Mason UI might be an interesting feature, if it helps people. Perhaps something like this:

Screenshot 2022-11-19 at 11 49 54

Although to be honest, this would be pretty far down the backlog (and currently not entirely feasible).

Yes, indeed, that is what I meant. Being able to detect installations outside of Mason would be quite useful, especially for linting tools like mypy or pylint, which often need to be installed inside the working environment. Giving the path to that installation could also be useful in debugging the lsp.

I could try and have a look into implementing the feature then send a PR, if you find the feature suitable.

nhat-vo avatar Nov 19 '22 11:11 nhat-vo

I could try and have a look into implementing the feature then send a PR, if you find the feature suitable.

I'm afraid this would be pretty difficult to achieve currently. It's practically impossible to programmatically introspect which executables a package is associated with as this information is not stored in an accessible way atm. Take rust-analyzer for example - you'd have to actually execute the installation to know which executable it ends up linking without keeping a manual record somewhere else (not an option). The same applies to all packages, although some would make this possible with little intervention (npm-based ones for example).

I am working on transforming package definitions to a serializable format (yaml), this format will in turn expose associated executables which would enable a feature like this.

williamboman avatar Nov 19 '22 11:11 williamboman

Ah I see. We could implement it later once that is done then

nhat-vo avatar Nov 19 '22 18:11 nhat-vo

Hi there!

I really consider this a must-have feature in the future, because lack of it makes Mason undesirable for me, unfortunately.

For example, installing clang package on Arch Linux provides you with:

  • clang - C++ compiler
  • clangd - C++ LSP server
  • clang-tidy - C++ linter
  • clang-format - C++ formatter

Of course, when I'm working on C++ files, I want clangd LSP server to be installed. At the moment, if I set

require("mason-lspconfig").setup({ ensure_installed = { "clangd" } })

it will download and install another copy in Mason's directory. This is very undesirable for following reasons:

  • Unnecessary copies of the same thing on the system.
  • Versions of clangd might actually not be the same and it may behave differently if ran from nvim vs system. (Adjusting global PATH to get consistent behavior also seems unsafe and error-prone)

If I exclude it with

require("mason-lspconfig").setup({ automatic_installation = { exclude = { "clangd" } } })

then Mason will never install it, neither on my current system (where I have it installed through system package), nor on another system where I might not have it, thus it defeats the purpose of me using Mason altogether.

Proposed Issue title rename (I find current one less intuitive)

Make Mason aware of packages installed on the system

Describe the solution you'd like

To tell which executable is installed and will be used, Mason needs to know two things:

  • Map of all of packages that Mason offers with the executables that they bring when installed.
  • Full PATH value in order to check for system installed executables.

After user sets up the Mason configuration, i.e. install_root_dir and PATH options, full PATH variable can be computed. With that, :Mason can report all executables by calling/simulating which -a [executable] in the order in which they are found.

At first glance, this will immediately tell the user:

  • If there are multiple versions of the executable installed on the system
  • Which executable is being used, one from the system or Mason

This general Mason behavior should also extend to its ecosystem. For example:

  • require("mason-lspconfig").setup({ ensure_installed = { "clangd" } })
    
    should either (further discussion on this might be needed):
    • Not install anything if:
      • PATH == "append" AND
      • clangd is installed on the system
    • Install clangd for Mason, if:
      • No clangd executable was found OR
        • PATH == "prepend" AND
        • There is no Mason's installation of clangd
  • require("mason-lspconfig").get_installed_servers() -- or some new function
    
    should also include LSP servers installed on the system, not just by Mason.

Iskustvo avatar Jan 01 '23 15:01 Iskustvo

This is my temporary solution, this is more related to null-ls.nvim than to mason.nvim:

require("null-ls").setup({
    sources = {
        null_ls.builtins.diagnostics.pylint.with({
            -- command has to be the path of the pylint executable you installed in the virtual environment
            command = vim.fn.system({ "which", "pylint" }):gsub("[\n]", ""),
        }),
    },
})

I had to remove pylint from mason.nvim because the command which pylint would return that first, second it returns the pylint path from the virtual environment and last it returns the pylint path installed globally, now pylint won't give me warnings like "unable to import".

I am using linux and venv for the virtual environment.

even that is not working

caamittiwari avatar Apr 26 '23 02:04 caamittiwari

I had to remove pylint from mason.nvim because the command which pylint would return that first, second it returns the pylint path from the virtual environment and last it returns the pylint path installed globally, now pylint won't give me warnings like "unable to import".

You can change the PATH setting of mason to be append:

require("mason").setup {
  PATH = "append"
}

This will put executables installed by Mason last in PATH, giving precedence to your "normal PATH".

By the way you should not have to shell out to locate the executable via which pylint. libuv (which null-ls uses for spawning commands) traverses PATH to find the command, yielding the exact same result.

even that is not doing any thing so u cant use pylint its always giving import error

caamittiwari avatar Apr 26 '23 02:04 caamittiwari

You need to be more specific on how it doesn't work for you. These are workarounds that are not optimal, but at least it seems to work for the previous people. So perhaps a more detailed description than "even that did not work" would be helpful to see what went wrong.

My suggestion to troubleshoot is to open a shell inside neovim, then run which -a pylint to detect which installation of pylint is visible and is used by null-ls (the first output). If you do not see the installation, then it's some problem with your PATH (for virtual environments, you need to install pylint yourself inside those environments to be able to detect the packages). The PATH = append only makes installations by Mason not preceed the pre-installed ones.

nhat-vo avatar Apr 26 '23 05:04 nhat-vo

You need to be more specific on how it doesn't work for you. These are workarounds that are not optimal, but at least it seems to work for the previous people. So perhaps a more detailed description than "even that did not work" would be helpful to see what went wrong.

My suggestion to troubleshoot is to open a shell inside neovim, then run which -a pylint to detect which installation of pylint is visible and is used by null-ls (the first output). If you do not see the installation, then it's some problem with your PATH (for virtual environments, you need to install pylint yourself inside those environments to be able to detect the packages). The PATH = append only makes installations by Mason not preceed the pre-installed ones. thanks for support path is working well its like as follows Γ¥» which -a pylint /home/amit/py_test/.venv/bin/pylint /home/amit/.local/bin/pylint /home/amit/.local/share/nvim/mason/bin/pylint /home/amit/py_test/.venv/bin/pylint /home/amit/.local/bin/pylint /home/amit/.local/bin/pylint /home/amit/anaconda3/bin/pylint but its not working i dont what is going wrong

caamittiwari avatar Apr 26 '23 07:04 caamittiwari

So "managing" or in any way integrating externally installed packages is out of scope for mason.nvim. If you don't want to install a package if it's already installed on your system you can for example utilize vim.fn.executable() in order to conditionally populate the ensure_installed or automatic_installation = { exclude = {} } lists.

williamboman avatar Apr 28 '23 19:04 williamboman

So "managing" or in any way integrating externally installed packages is out of scope for mason.nvim. If you don't want to install a package if it's already installed on your system you can for example utilize vim.fn.executable() in order to conditionally populate the ensure_installed or automatic_installation = { exclude = {} } lists.

thanks very thing working fine except import error highlighted by plyint as its unable to find site packages , beside this when ever i use path option pylint doesnt work at all ( all three option )

caamittiwari avatar Apr 29 '23 09:04 caamittiwari