*ฅ^•ﻌ•^ฅ* ✨✨  HWisnu's blog  ✨✨ о ฅ^•ﻌ•^ฅ

Neovim config - Ruff linter + Pyright hover info

Introduction

I got Python files with > 10-20k lines, which is too much for Pyright (the defacto Python LSP) to handle. On the other hand, Ruff is a bwazzingly fwast Python linter written in Rust --> it does not have issues handling my 20k lines of Python code.

Pyright

Is the most complete Python formatter, static type checker, linter, whatever else your grandma got in the basement. But, like your grandma, Pyright is slow..my Neovim froze when I open the 20k loc Py file. At first I thought Neovim was not up to the task but actually the culprit was Pyright.

Ruff

Comparatively, Ruff is bwazzingly fwast Python linter which eats my 20k loc Py file for breakfast everyday no problem! My Neovim loads instantly, errors got detected right away...it's awesome (thank you Rust and the Astral team!). But Ruff is not good for checking documentations..actually it's dogshit compared to the hover info documentation provided by Pyright.

The idea: combine both strengths!

By combining them, we get the best from both worlds: lightning fast linter / error checking and wide access to libraries' documentation. However Pyright is known to be atrociously difficult to disable some of the features..just like their Windows OS - fuckin Microsoft!

The code

Fuck it, at last I managed to combine Pyright+Ruff! Here's my lsp-config.lua:

return {
  {
    "williamboman/mason.nvim",
    lazy = false,
    config = function()
      require("mason").setup()
    end,
  },
  {
    "williamboman/mason-lspconfig.nvim",
    lazy = false,
    opts = {
      auto_install = true,
    },
    config = function()
      require("mason-lspconfig").setup({
        ensure_installed = {
          "pyright",
          "ruff",
        },
      })
    end,
  },
  {
    "neovim/nvim-lspconfig",
    lazy = false,
    config = function()
      local capabilities = require("cmp_nvim_lsp").default_capabilities()
      local lspconfig = require("lspconfig")
      lspconfig.lua_ls.setup({ capabilities = capabilities })
      lspconfig.clangd.setup({ capabilities = capabilities })

            local on_attach_pyright = function(client, _)
        -- Disable all capabilities except hoverProvider
        -- client.server_capabilities.completionProvider = false
        -- client.server_capabilities.definitionProvider = false
        -- client.server_capabilities.typeDefinitionProvider = false
        -- client.server_capabilities.implementationProvider = false
        -- client.server_capabilities.referencesProvider = false
        -- client.server_capabilities.documentSymbolProvider = false
        -- client.server_capabilities.workspaceSymbolProvider = false
        -- client.server_capabilities.codeActionProvider = false
        -- client.server_capabilities.documentFormattingProvider = false
        -- client.server_capabilities.documentRangeFormattingProvider = false
        -- client.server_capabilities.renameProvider = false
        -- client.server_capabilities.signatureHelpProvider = false
        -- client.server_capabilities.documentHighlightProvider = false
        -- client.server_capabilities.foldingRangeProvider = false
        -- client.server_capabilities.semanticTokensProvider = false
        -- client.server_capabilities.declarationProvider = false
        -- client.server_capabilities.callHierarchyProvider = false
        -- client.server_capabilities.diagnosticProvider = false

        -- Enable hoverProvider
        client.server_capabilities.hoverProvider = true
      end


      -- Configure pyright
      lspconfig.pyright.setup({
        on_attach = on_attach_pyright,
        capabilities = (function()
          local capabilities = vim.lsp.protocol.make_client_capabilities()
          capabilities.textDocument.publishDiagnostics.tagSupport.valueSet = { 2 }
          return capabilities
        end)(),
        settings = {
          python = {
            analysis = {
              useLibraryCodeForTypes = true,
              diagnosticSeverityOverrides = {
                reportUnusedVariable = "warning",
              },
              typeCheckingMode = "off", -- Set type-checking mode to off
              diagnosticMode = "off", -- Disable diagnostics entirely
            },
          },
        },
      })

      local on_attach_ruff = function(client, _)
        if client.name == "ruff" then
          -- disable hover in favor of pyright
          client.server_capabilities.hoverProvider = false
        end
      end

      lspconfig.ruff.setup({
        on_attach = on_attach_ruff,
        init_options = {
          settings = {
            args = {
              "--ignore",
              "F821",
              "--ignore",
              "E402",
              "--ignore",
              "E722",
              "--ignore",
              "E712",
            },
          },
        },
      })
      vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, {
        border = "rounded",
        width = 70,
        height = 15,
      })
      vim.lsp.handlers["textDocument/signatureHelp"] =
        vim.lsp.with(vim.lsp.handlers.signature_help, { border = "rounded" })

      vim.keymap.set("n", "K", vim.lsp.buf.hover, {})
      vim.keymap.set("n", "<leader>gd", vim.lsp.buf.definition, {})
      vim.keymap.set("n", "<leader>gr", vim.lsp.buf.references, {})
      vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, {})
    end,
  },
}

Note: I removed all the irrelevant LSPs and entries like the ones for C, Zig, Rust, JS/TS, Lua to avoid cluttering the code with unnecessary stuff.

That's it, this one is a short post as I only want to document this Pyright+Ruff combo I managed to get working well. Cheers!