10

I have neovim 0.8.0 installed. I have neovim/nvim-lspconfig plugin installed. I have installed pyright language server on my system (pip install pyright) I have configured neovim to use the pyright language server.

So now when I open a python file I get some warnings produced by pyright. :LspInfo command confirms that "1 client(s) attached to this buffer" and this client is pyright.

So far so good. Now I start editing some code. For example I write the following function call:

x = get_value_of_x("aaaaaaaaaa", "bbbbbbb", "ccccccc",
        "dddddddd")

As you can see this code is not properly formatted. According to pep8 rules it should be formatted like this:

x = get_value_of_x("aaaaaaaaaa", "bbbbbbb", "ccccccc",
                   "dddddddd")

I expected that neovim integrated with language server will automatically indent the code, but somehow it does not happen.

I tried to trigger format explicitly by executing the following command:

:lua vim.lsp.buf.format()

but get following error message: [LSP] Format request failed, no matching language servers.

I tried to install different language server ("jedi-language-server") instead of "pyright" - and the result is approximately the same: both servers function properly (for example "go to definition" functionality works fine) but not auto-formatting is available.

No log messages generated during these formatting attempts in ~/.cache/nvim/lsp.log

How can I configure nvim / lsp servers to properly indent python code?

1
  • 1
    pyright does not offer formatting. Instead, install another tool to use formatting (I use null-ls + black + an autocmd to format on save) . Here's my config, which I use on MacOS and has links in comments to different wikis
    – Ben
    Aug 2, 2022 at 17:34

6 Answers 6

5

Here is an example of how to make this work with lspconfig

~/.config/nvim/init.lua

require("_plugins")

~/.config/nvim/lua/_plugins.lua

local fn = vim.fn
local install_path = fn.stdpath("data") .. "/site/pack/packer/start/packer.nvim"
if fn.empty(fn.glob(install_path)) > 0 then
  Packer_Bootstrap = fn.system({
    "git",
    "clone",
    "--depth",
    "1",
    "https://github.com/wbthomason/packer.nvim",
    install_path,
  })
end

local packer_ok, packer = pcall(require, "packer")
if not packer_ok then
  return
end

return packer.startup(function()
  use({
    "wbthomason/packer.nvim",
  })
  use({
    "neovim/nvim-lspconfig",
    requires = {
      "williamboman/mason.nvim",
      "williamboman/mason-lspconfig.nvim",
    },
    config = function()
      require("_lsp").setup()
    end,
  })
    use { "jose-elias-alvarez/null-ls.nvim",
    requires = { "nvim-lua/plenary.nvim" },
    config = function()
      require("_null-ls")
    end
  }
  if Packer_Bootstrap then
    require("packer").sync()
  end
end)

~/.config/nvim/lua/_lsp.lua

local lspconfig_ok, lspconfig = pcall(require, "lspconfig")
if not lspconfig_ok then
  return
end

local mason_ok, mason = pcall(require, "mason")
if not mason_ok then
  return
end

local mason_lspconfig_ok, mason_lspconfig = pcall(require, "mason-lspconfig")
if not mason_lspconfig_ok then
  return
end

local _lsp = {}
function _lsp.setup()
  mason.setup({})
  mason_lspconfig.setup({})

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

return _lsp

~/.config/lua/_null-ls

local null_ls_ok, null_ls = pcall(require, "null-ls")
if not null_ls_ok then
  return
end

local sources = {
  -- python
  null_ls.builtins.formatting.black.with({
    extra_args = { "--line-length=120" }
  }),
  null_ls.builtins.formatting.isort,
}

null_ls.setup({ sources = sources })

Install plugins

:PackerSync

Install LSPs

  • Install from the list, use i to install
:MasonInstall black
:MasonInstall pyright
:MasonInstall isort

verify

Open a python file and run

:LspInfo

This should report that the servers are setup and running.

Then however you choose to wire up the formatting command.

2

It took some time to figure out what is happening.

Short answer: looks like LSP servers do not affect nvim's indentation when you press "Enter" button while editing text. I had to install and properly configure 'nvim-treesitter' plugin to make things work.

More detailed answer. To find out what happens when you proceed to next line (push "Enter" button in insert mode or "o" button in normal mode) check value of the indentexpr setting:

:set indentexpr?
  indentexpr=python#GetIndent(v:lnum)

In my case it was a vimscript method, I found it in /usr/local/share/nvim/runtime/autoload/python.vim. (To find the location of the function run the following command: verbose function python#GetIndent, if it compains that the function is not found do edit you file and try again). You can check what this function does to calculate the indent level. It was interesting for me to look into this function and play with it a little. Bottomline: it does not try to use lsp.

Installation of different language servers did not affect indentexpr.

It turned out that for a long time I used not standard indentation implemented in my .vimrc/indent/python.vim - I really do not remember where have I got this file from. It was created in 2004 by some guy David Bustos - thanks a lot to him. Autoindentation implemented in this file is much better than standard, but still with some glitches. Lots changed since 2004, so there should be a better way.

Looks like a better way is 'nvim-treesitter' plugin. Very good instructions on plugin configuration can be found in this short tutorial: https://www.youtube.com/watch?v=hkxPa5w3bZ0&list=PLhoH5vyxr6Qq41NFL4GvhFp-WLd5xzIzZ

Example of configuration (via lua script):

local treesitter_ok, treesitter = pcall(require, 'nvim-treesitter.configs')
if treesitter_ok then
  treesitter.setup{
    ensure_installed = { 'c', 'lua', 'vim', 'help', 'python', 'go' },
    sync_install = false,                                                                                                 
    auto_install = false,                                                                                                       
    highlight = {                                                                                                 
      enable = false,
    },
    indent = {
      enable = true,
    },
  }
end

Make sure to run :TSUpdate after or during installation (depends on plugin manager you use).

Make sure that treesitter is enabled for python: :TSInstallInfo

If everything is ok than treesitter is used for indentation:

:set indentexpr?
  indentexpr=nvim_treesitter#indent()

On-the-fly indentation is indeed better now.

UPDATE: Indentation is still not good. In some cases it's better, but still not good.

1
  • Thanks, perfect solution, works like a charm! Jun 11, 2023 at 12:54
1

For range formatting you may have a look at black-macchiato.

Install black-macchiato by running the shell command:

pip install black-macchiato

To format a range of select lines run the vim command:

:'<,'>!python -m macchiato

Or install the pluging smbl64/vim-black-macchiato: vim-black-macchiato

1
  • 3
    If you're going for this, maybe it is more comfortable to run it as part of formatprg: :set formatprg=python3\ -m\ macchiato and then type gqip to format that paragraph.
    – r_31415
    Jun 3, 2022 at 20:26
0

You need the efm language server, and the python black formatter. You also need to set up efm like this:

nvim_lsp.efm.setup {               
    on_attach = on_attach,    
    flags = {                                                                                                                                                                                 
      debounce_text_changes = 150,
    },
    init_options = {documentFormatting = true},
    filetypes = {"python"},
    settings = {
        rootMarkers = {".git/"},
        languages = {
            python = {
                {formatCommand = "black --quiet -", formatStdin = true}
            }
        }
    }
}

Efm is difficult to install. You need Go. I figured it out on my system using these commands:

GO111MODULE=on go get github.com/mattn/efm-langserver@latest    
echo 'export PATH=$PATH:~/go/bin' >> ~/.zshrc 
0

I would suggest trying null-ls, the configuration is relatively simple and you don't need to change your lspconfig settings at most of the time.

My conf is just a copy/paste from null-ls's wiki(formatting fires automatically when you save the buffer):

https://github.com/dreamerlzl/nvim-setup/blob/lua/lua/lsp/null.lua

local augroup = vim.api.nvim_create_augroup("LspFormatting", {})
local null_ls = require("null-ls")

null_ls.setup({
    on_attach = function(client, bufnr)
        if client.supports_method("textDocument/formatting") then
            vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
            vim.api.nvim_create_autocmd("BufWritePre", {
                group = augroup,
                buffer = bufnr,
                callback = function()
                    -- on 0.8, you should use vim.lsp.buf.format({ bufnr = bufnr }) instead
                    vim.lsp.buf.format({ bufnr = bufnr })
                end,
            })
        end
    end,
    sources = {
        null_ls.builtins.formatting.stylua,
        null_ls.builtins.formatting.black,
    },
})

You could find the original wiki here on null-ls's wiki

The following link is only for demonstrating that I do nothing special about the pyright configuration:

https://github.com/dreamerlzl/nvim-setup/blob/lua/lua/lsp/lspconfig.lua#LL126C29-L126C36

6
  • 1
    We would like to have as much as possible the full answer within the answer and limit at maximum the exterior link. Could you simplify your configuration, include it into the answer and explain it? Mar 26, 2023 at 5:39
  • Thank you for reply. Actually I also use null-ls. null-ls is configured to use black source for formatting. But that doesn't affect nvim's behavior when I push "Enter" button. Could you check what happens when you open a new python file and manually print the text from my original question?
    – lesnik
    Mar 27, 2023 at 13:16
  • I've included some code directly from one link; I'm not sure how the other link is relevant to null-ls, so I left it in but didn't include the code.
    – D. Ben Knoble
    Mar 29, 2023 at 13:43
  • 1
    @lesnik Oh sorry I don't see you mention pressing "Enter" in the question. In my configuration the formatting fires when you save the buffer. When I open a new python file and manually print the text from your original question, the code is formatted as one line. Mar 29, 2023 at 14:17
  • 1
    @VivianDeSmedt Thank you for your suggestion. I have added some more explanations. But since the code is from wiki and not mine, I think it's better to give a reference. Mar 29, 2023 at 14:20
-1

There are many ways.

Here is one using Coc.Nvim (neoclide/coc.nvim)

Follow the instructions to install Coc.Nvim

Basically (if you are using Plug)

  1. Add the following line to your .vimrc
Plug 'neoclide/coc.nvim' , {'branch': 'release'}
  1. Resource your .vimrc using the vim command:

:so %

  1. Run the vim command:

:PlugInstall

  1. Restart Neovim

When installed install Pyright support by entering the vim command:

:CocInstall coc-pyright

Add the following line to your .vimrc file to add Format support:

  " Add `:Format` command to format current buffer.
  command! -nargs=0 Format :call CocAction('format')

  " Use CTRL-S for selections ranges.
  " Requires 'textDocument/selectionRange' support of language server.
  nmap <silent> <C-s> <Plug>(coc-range-select)
  xmap <silent> <C-s> <Plug>(coc-range-select)

The :Format command format the entire file.

For that to work you'll need to have Black installed. This can be achieved using the following shell command:

pip install black

If Coc.Nvim fails to find Black you can configure it explicitly by typing the vim command:

:CocConfig

The coc-settings.json file will open.

Make sure the following lines (On a Windows machine) are part of the config.

Remark: Coc.Nvim support also range formatting if the corresponding language server support it. It seems that PyRight is not supporting range formatting yet.

{
    "python.formatting.provider": "black",
    "python.formatting.blackPath": "C:\\Python36_x64\\Scripts\\black.exe",
}
6
  • 1
    Thanks for the answer! I have installed Coc.Nvim and coc-pyright extension. How can I use :Format command now? I have even installed black - but still the code is not properly indented.
    – lesnik
    May 3, 2022 at 19:51
  • The vim command :Format should reformat your code but maybe you have a provlem with the installation of black. Can you tell us what is your OS? To verify black is in the path can you run the shell command black? Could you also try to save your file and run the vim command :!black % that should reformat it. May 4, 2022 at 3:25
  • 1
    Operating system is ubuntu linux. Vim command :!black % works - it does re-format the whole file. But :Format does not: "E492: Not an editor command: Format". And frankly speaking the re-formatting of the whole file with black is not what I need. There could be several valid ways to format a function call and black re-formats even portions of code which are already correctly formatted :(
    – lesnik
    May 4, 2022 at 7:38
  • 1
    Is the answer essentially "pyright and other python LSPs do not include formatting; use black for that"?
    – D. Ben Knoble
    May 4, 2022 at 14:01
  • Coc.Nvim bring support for file formatting (using black) it also bring range formatting if the corresponding LSP is supporting it. It seems that PyRight doesn't support it yet. May 4, 2022 at 17:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.