astral-sh/ruff-lsp

How to use ruff-lsp along with pyright in Neovim

pratyushmittal opened this issue ยท 14 comments

Ruff-LSP handles most of the things. I want to use pyright only for things which ruff-lsp doesn't do. I am unable to disable pyright for things which ruff-lsp already does.

The README and documentation mentions about using ruff-lsp with another lsp:

Note that if you're using Ruff alongside another LSP (like Pyright), you may want to defer to that LSP for certain capabilities, like textDocument/hover

How do we disable pyright for linting and formatting?

I read through all existing issues but couldn't find a complete configuration of using pyright along with ruff.

I am using this configuration, but it does linting using both pyright and ruff.

local lspconfig = require('lspconfig')
local on_attach = function(client, bufnr)
    -- Disable hover in favor of Pyright
    client.server_capabilities.hoverProvider = false
end
lspconfig.ruff_lsp.setup {
    on_attach = on_attach,
    init_options = {
        settings = {
            -- Any extra CLI arguments for `ruff` go here.
            args = {},
        }
    }
}
lspconfig.pyright.setup {
    settings = {
        python = {
            analysis = {
                -- warnings in factory boy for meta class overide
                typeCheckingMode = "basic"
            }
        }
    }
}

Please share your config if anyone has been able to use pyright along with ruff-lsp without the overlaps.

Hey, thanks for using Ruff!

How do we disable pyright for linting and formatting?

Well, I just figured out how to disable diagnostics in Pyright, thanks! You can put the following in your config which disables linting, import sorting and type checking from Pyright and thus only provides completions, goto(s), etc.

  pyright = {
    settings = {
      pyright = {
        disableOrganizeImports = true, -- Using Ruff
      },
      python = {
        analysis = {
          ignore = { '*' }, -- Using Ruff
          typeCheckingMode = 'off', -- Using mypy
        },
      },
    },
  }

I'll document this in the README.

Thanks a lot @dhruvmanila. It works!

@dhruvmanila Thank you!!!! Related to: neovim/nvim-lspconfig#726

Hey, thanks for using Ruff!

How do we disable pyright for linting and formatting?

Well, I just figured out how to disable diagnostics in Pyright, thanks! You can put the following in your config which disables linting, import sorting and type checking from Pyright and thus only provides completions, goto(s), etc.

  pyright = {
    settings = {
      pyright = {
        disableOrganizeImports = true, -- Using Ruff
      },
      python = {
        analysis = {
          ignore = { '*' }, -- Using Ruff
          typeCheckingMode = 'off', -- Using mypy
        },
      },
    },
  }

I'll document this in the README.

For me it seems that


python = {
        analysis = {
          ignore = { '*' }, -- Using Ruff
        },
      },

also disables pyright's typing, how would one let ruff manage linting etc but let pyright provide typing errors ?
Using typeCheckingMode = 'off' doesn't work.

Hi, as mentioned above by @StitiFatah is it possible to preserve pyright's typing?

Unfortunately, I don't think it's possible to just enable type checking for Pyright and disable other diagnostics. One would need to manually go through the list of diagnostics supported by Pyright (https://github.com/microsoft/pyright/blob/main/docs/configuration.md#type-check-diagnostics-settings) and disable the ones which overlaps with Ruff's rules.

After using mypy for a bit instead of pyright I feel that mypy misses a lot of things pyright doesn't. I've thus removed mypy, removed that :

            analysis = {
              -- Ignore all files for analysis to exclusively use Ruff for linting
              ignore = { "*" },
            },

and also added this (which i found here) to my lspconfig configuration

        pyright = {
          capabilities = (function()
            local capabilities = vim.lsp.protocol.make_client_capabilities()
            capabilities.textDocument.publishDiagnostics.tagSupport.valueSet = { 2 }
            return capabilities
          end)(),

And it sseems to do the trick.

It fixes duplicates like subprocess imported but not used from Ruff and "subprocess" is not accessed by removing Pyright's ones while still benefiting from its type diagnostics.

We'll see if it removes enough duplicates.

Thanks for the suggestion!

My lspconfig actually includes:

function M.common_capabilities()
  local status_ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
  if status_ok then
    return cmp_nvim_lsp.default_capabilities()
  end

  local capabilities = vim.lsp.protocol.make_client_capabilities()
  capabilities.textDocument.completion.completionItem.snippetSupport = true
  capabilities.textDocument.completion.completionItem.resolveSupport = {
    properties = {
      "documentation",
      "detail",
      "additionalTextEdits",
    },
  }
  capabilities.textDocument.foldingRange = {
    dynamicRegistration = false,
    lineFoldingOnly = true,
  }

  return capabilities
end

How could I include this change?

Well it seems that there are still duplicates, from my current understanding the settings above only remove what they call "hints" microsoft/pyright#3929 (reply in thread)

Errors like those for undefined variables are still emitted by both Pyright and Ruff. (as pointed out by a message above which seems to have been deleted)

Those pyright errors are documented here but I couldn't figure out where to place them in my nvimlsconfig.lua, I've tried under each table but nothing worked, maybe it's only possible to use them in pyproject.toml or pyrightconfig.json, the latter works for me but it'd be cumbersome to define those for each project and would be problematic for standalone scripts too, and there is apparently no way to set those in a global way.

So for now I'm settling with disabling those duplicates from the Ruff side. For undefined variables for example one would do :

ruff_lsp={
        init_options = {
          settings = {
            args = {
              "--ignore=F821",
            },
          },
        },
}

It'd be helpful if we could document duplicates somewhere since pyright + ruff seems like a popular combo.

Edit : btw the snippet from my previous message can be abbreviated as this in a cleaner way :

        capabilities = {
          textDocument = {
            publishDiagnostics = {
              tagSupport = {
                valueSet = { 2 },
              },
            },
          },
        },

My lspconfig actually includes:

function M.common_capabilities()
  local status_ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
  if status_ok then
    return cmp_nvim_lsp.default_capabilities()
  end

  local capabilities = vim.lsp.protocol.make_client_capabilities()
  capabilities.textDocument.completion.completionItem.snippetSupport = true
  capabilities.textDocument.completion.completionItem.resolveSupport = {
    properties = {
      "documentation",
      "detail",
      "additionalTextEdits",
    },
  }
  capabilities.textDocument.foldingRange = {
    dynamicRegistration = false,
    lineFoldingOnly = true,
  }

  return capabilities
end

How could I include this change?

@steakhutzeee
If you're using the lazy.nvim package manager you could have a look at my config.
Bear in mind that I'm also using LazyVim (the distro), so things are getting merged with the default config which is here

My lspconfig actually includes:

function M.common_capabilities()
  local status_ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
  if status_ok then
    return cmp_nvim_lsp.default_capabilities()
  end

  local capabilities = vim.lsp.protocol.make_client_capabilities()
  capabilities.textDocument.completion.completionItem.snippetSupport = true
  capabilities.textDocument.completion.completionItem.resolveSupport = {
    properties = {
      "documentation",
      "detail",
      "additionalTextEdits",
    },
  }
  capabilities.textDocument.foldingRange = {
    dynamicRegistration = false,
    lineFoldingOnly = true,
  }

  return capabilities
end

How could I include this change?

@steakhutzeee If you're using the lazy.nvim package manager you could have a look at my config. Bear in mind that I'm also using LazyVim (the distro), so things are getting merged with the default config which is here

I'm using this configuration basically https://github.com/ChristianChiarulli/nvim/blob/master/lua/user/lspconfig.lua

As you can see the capabilities are already defined there. And he configured the servers in a separate dir https://github.com/ChristianChiarulli/nvim/tree/master/lua/user/lspsettings

So you think there is a way to add the settings in the lsp config or in a dedicated file separately? I'm going to try but have no idea if it's a problem to set capabilites in different locations.

EDIT: Ok so i left the lspconfig as is and edited pyright and ruff_lsp configs separately in their files. Looks it works but not sure if something is wrong with the capabilities set both in lspconfig and (maybe overwritten?) in the pyright config.

I cant guarantee it but my guess is that it'd overwrite the main neovim/nvim-lspconfig configuration. But you could easily add local opts = { on_attach = M.on_attach, capabilities = M.common_capabilities(), } to your own individual configs and merge your own options to it including capabilities.

Or you could let them in this file, and do like he did with the lua lsp with a if server == "pyright" and merge your own options to the opts here

you can do this to use pyright with ruff_lsp and ignore pyright's diagnostics to avoid duplicates (but you have to disable them one by one) while still keeping pyright's type checking:

settings = {
    pyright = {
      disableOrganizeImports = true,
      disableTaggedHints = true,
    },
    python = {
      analysis = {
        diagnosticSeverityOverrides = {
          -- https://github.com/microsoft/pyright/blob/main/docs/configuration.md#type-check-diagnostics-settings
          reportUndefinedVariable = "none",
        },
      },
    },
  },