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

How to execute LSP command only from the Ruff server? #504

Closed
Drachenfels opened this issue Oct 10, 2024 · 1 comment
Closed

How to execute LSP command only from the Ruff server? #504

Drachenfels opened this issue Oct 10, 2024 · 1 comment

Comments

@Drachenfels
Copy link

Drachenfels commented Oct 10, 2024

It's probably not a bug with ruff but more on the intersection of lsp-server, copilot, and ruff (maybe?). I would appreciate any pointers on what might be wrong.

I am attempting to follow the tip that will add an extra command to my neovim RuffAutoFormat (#119 (comment))

require('lspconfig').ruff.setup {
  commands = {
    RuffAutoFormat = {
      function()
        vim.lsp.buf.execute_command {
          command = 'ruff.applyFormat',
          arguments = {
            {
                uri = vim.uri_from_bufnr(0),
                version = 1,
            },
          },
        }
      end,
      description = 'Ruff: Fix all formatting errors',
    },
  }
}

It worked (well with quirks) till I installed copilot plugin. Right now whenever I run :RuffAutoFormat I get error message: GitHub Copilot: -32603: Request workspace/executeCommand failed with message: Unknown command: ruff.applyFormat.

No idea what to think about it.

Extra question in the past I had this on autocmd:

vim.api.nvim_create_autocmd("BufWritePre", {
	callback = function()
		if vim.bo.ft == "python" then
			vim.lsp.buf.code_action {
				apply = true,
                context = { only = { "source.fixAll.ruff" } },
			}

            vim.cmd(":RuffAutoFormat")

            -- need to apply sleep otherwise organizeImports will mess up code
            -- if fixAll triggers in the same call, 125ms seems to be enough to
            -- allow buffer reload or something
            vim.cmd(':sleep 125ms')

	    vim.lsp.buf.code_action {
		context = { only = { "source.organizeImports.ruff" } },
		apply = true,
	    }
        end
    end
})

Sometimes when ruff had to fixAll and organizeImports and run AutoFormat it was removing some lines (looked like a race condition to me). Is there a way to run AutoFormat in sequence or as a callback to callback? Sleep works for 90% of cases but not always. It's again probably a question showing the limits of my knowledge about nvim more than ruff, but again I would appreciate any pointers.

For the record my init.vim:

call plug#begin()

Plug 'williamboman/mason.nvim'
Plug 'williamboman/mason-lspconfig.nvim'

Plug 'neovim/nvim-lspconfig', {'branch': 'master'}

" We recommend updating the parsers on update
Plug 'nvim-treesitter/nvim-treesitter', {'do': ':TSUpdate'}

Plug 'github/copilot.vim'

Plug 'hrsh7th/nvim-cmp'

Plug 'hrsh7th/cmp-vsnip'
Plug 'hrsh7th/vim-vsnip'

" Enables better search with autocompletion
Plug 'hrsh7th/cmp-buffer'

" loads collection of vscode snippets
Plug 'rafamadriz/friendly-snippets'

call plug#end()

lua require('init')

lua << EOF
local has_words_before = function()
  unpack = unpack or table.unpack
  local line, col = unpack(vim.api.nvim_win_get_cursor(0))
  return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
end

local feedkey = function(key, mode)
  vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(key, true, true, true), mode, true)
end

local cmp = require('cmp')

cmp.setup {
  mapping = {
    ["<Tab>"] = cmp.mapping(function(fallback)
      if cmp.visible() then
        cmp.select_next_item()
      elseif vim.fn["vsnip#available"](1) == 1 then
        feedkey("<Plug>(vsnip-expand-or-jump)", "")
      elseif has_words_before() then
        cmp.complete()
      else
        fallback() -- The fallback function sends a already mapped key. In this case, it's probably `<Tab>`.
      end
    end, { "i", "s" }),

    ["<S-Tab>"] = cmp.mapping(function()
      if cmp.visible() then
        cmp.select_prev_item()
      elseif vim.fn["vsnip#jumpable"](-1) == 1 then
        feedkey("<Plug>(vsnip-jump-prev)", "")
      end
    end, { "i", "s" }),
  }
}

-- Use buffer source for `/` and `?` (if you enabled `native_menu`, this won't work anymore).
cmp.setup.cmdline({ '/', '?' }, {
  mapping = cmp.mapping.preset.cmdline(),
  sources = {
    { name = 'buffer' }
  }
})
EOF

" accept copilot's suggestion whole with <C-l>
imap <silent><script><expr> <C-l> copilot#Accept("\<CR>")

" accept copilot's suggested line with <C-k>
imap <silent> <C-k> <Plug>(copilot-accept-line)

" accept copilot's suggested work with <C-j>
imap <silent> <C-j> <Plug>(copilot-accept-word)

" cycle next copilot's suggestion with <C-n
imap <silent> <C-n> <Plug>(copilot-next)

" cycle prev copilot's suggestion with <C-n
imap <silent> <C-p> <Plug>(copilot-previous)

let g:copilot_no_tab_map = v:true

And init.lua:

require("mason").setup()
require("mason-lspconfig").setup()

require("mason-lspconfig").setup_handlers {
    function (server_name) -- default handler (optional)
        require("lspconfig")[server_name].setup {}
    end,
}

require('lspconfig').ruff.setup {
  commands = {
    RuffAutoFormat = {
      function()
        vim.lsp.buf.execute_command {
          command = 'ruff.applyFormat',
          arguments = {
            {
                uri = vim.uri_from_bufnr(0),
                version = 1,
            },
          },
        }
      end,
      description = 'Ruff: Fix all formatting errors',
    },
  }
}

vim.api.nvim_create_autocmd("BufWritePre", {
	callback = function()
		if vim.bo.ft == "python" then
			vim.lsp.buf.code_action {
				apply = true,
                context = { only = { "source.fixAll.ruff" } },
    	}

            vim.cmd(":RuffAutoFormat")

            -- need to apply sleep otherwise organizeImports will mess up code
            -- if fixAll triggers in the same call, 125ms seems to be enough to
            -- allow buffer reload or something
            vim.cmd(':sleep 125ms')

			vim.lsp.buf.code_action {
				context = { only = { "source.organizeImports.ruff" } },
				apply = true,
			}
        end
    end
})
@dhruvmanila
Copy link
Member

It worked (well with quirks) till I installed copilot plugin. Right now whenever I run :RuffAutoFormat I get error message: GitHub Copilot: -32603: Request workspace/executeCommand failed with message: Unknown command: ruff.applyFormat.

This is because the client will send the command request to all the servers attached to the buffer which includes the copilot client as well. This is copilot failed with an unknown command error message.

Instead, you should send the request using the specific client:

local client = vim.lsp.get_clients({ bufnr = vim.api.nvim_get_current_buf(), name = 'ruff' })[1]

if client then
  client.request(vim.lsp.protocol.Methods.workspace_executeCommand, {
    command = 'ruff.applyFormat',
    arguments = {
      { uri = vim.uri_from_bufnr(0), version = 0 },
    },
  })
end

@dhruvmanila dhruvmanila changed the title Strange interaction of ruff with nvim and copilot How to execute LSP command only from the Ruff server? Oct 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants