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

feat(rust): add bacon-ls and improve debugging #3212

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

crisidev
Copy link

Changes

NOTE: the PR will be in draft until mason-org/mason-registry#5774 is not merged

Some improvements in the Rust extra

bacon-ls can be used as an alternative to rust-analyzer for diagnostics, improving rust-analyzer performances. This is configured by vim.g.lazyvim_rust_diagnostics, which can be set to rust-analyzer or bacon-lsp.

screenshot

@mirsella
Copy link
Contributor

i just discovered bacon, and it looks awesome, but i don't think lazyvim should swap the default rust LSP everyone is used to, and assume is used ?
If I understand correctly, bacon runs alone without rust analyzer, or both runs at the same time ?

maybe as a extra extra ? like rust-bacon ?

@crisidev
Copy link
Author

i just discovered bacon, and it looks awesome, but i don't think lazyvim should swap the default rust LSP everyone is used to, and assume is used ? If I understand correctly, bacon runs alone without rust analyzer, or both runs at the same time ?

maybe as a extra extra ? like rust-bacon ?

This MR does not disable rust-analyzer at all, it just disables the diagnostic if bacon and bacon-ls are used. To me this is pertinent to the Rust extra, but I'll defer to people more experienced with LazyVim.

@mirsella
Copy link
Contributor

mirsella commented Jun 14, 2024

Thanks for the explanation.
i tried your rust.lua (even merged it to latest to test), but for me, I get no diagnostic whatsoever. bacon-ls is running alongside rust-analyzer, but nothing give any diagnostics

here is a repro.lua (include the rust.lua, your version not the merged one)

-- DO NOT change the paths and don't remove the colorscheme
local root = vim.fn.fnamemodify("./.repro", ":p")

-- set stdpaths to use .repro
for _, name in ipairs({ "config", "data", "state", "cache" }) do
	vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end

-- bootstrap lazy
local lazypath = root .. "/plugins/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
	vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", lazypath })
end
vim.opt.runtimepath:prepend(lazypath)

vim.g.lazyvim_rust_diagnostics = "bacon-ls"

if lazyvim_docs then
	-- LSP Server to use for Rust.
	-- Set to "bacon-ls" to use bacon-ls instead of rust-analyzer.
	-- only for diagnostics. The rest of LSP support will still be
	-- provided by rust-analyzer.
	vim.g.lazyvim_rust_diagnostics = "rust-analyzer"
end

local diagnostics = vim.g.lazyvim_rust_diagnostics or "rust-analyzer"

-- install plugins
local plugins = {
	"folke/tokyonight.nvim",
	{ "LazyVim/LazyVim", import = "lazyvim.plugins" },
	-- add any other plugins here

	-- Extend auto completion
	{
		"hrsh7th/nvim-cmp",
		dependencies = {
			{
				"Saecki/crates.nvim",
				event = { "BufRead Cargo.toml" },
				opts = {
					completion = {
						cmp = { enabled = true },
					},
				},
			},
		},
		---@param opts cmp.ConfigSchema
		opts = function(_, opts)
			opts.sources = opts.sources or {}
			table.insert(opts.sources, { name = "crates" })
		end,
	},

	-- Add Rust & related to treesitter
	{
		"nvim-treesitter/nvim-treesitter",
		opts = function(_, opts)
			opts.ensure_installed = opts.ensure_installed or {}
			vim.list_extend(opts.ensure_installed, { "ron", "rust", "toml" })
		end,
	},

	-- Ensure Rust debugger is installed
	{
		"williamboman/mason.nvim",
		optional = true,
		opts = function(_, opts)
			opts.ensure_installed = opts.ensure_installed or {}
			vim.list_extend(opts.ensure_installed, { "codelldb" })
			if diagnostics == "bacon-ls" then
				vim.list_extend(opts.ensure_installed, { "bacon", "bacon-ls" })
			end
		end,
	},

	{
		"mrcjkb/rustaceanvim",
		version = "^4", -- Recommended
		ft = { "rust" },
		opts = {
			server = {
				on_attach = function(_, bufnr)
					vim.keymap.set("n", "<leader>cR", function()
						vim.cmd.RustLsp("codeAction")
					end, { desc = "Code Action", buffer = bufnr })
					vim.keymap.set("n", "<leader>dr", function()
						vim.cmd.RustLsp("debuggables")
					end, { desc = "Rust Debuggables", buffer = bufnr })
				end,
				default_settings = {
					-- rust-analyzer language server configuration
					["rust-analyzer"] = {
						cargo = {
							allFeatures = true,
							loadOutDirsFromCheck = true,
							buildScripts = {
								enable = true,
							},
						},
						-- Enable diagnostics if using rust-analyzer
						diagnostics = {
							enable = diagnostics == "rust-analyzer",
						},
						-- Add clippy lints for Rust.
						checkOnSave = {
							enable = diagnostics == "rust-analyzer",
							allFeatures = true,
							command = "clippy",
							extraArgs = { "--no-deps" },
						},
						procMacro = {
							enable = true,
							ignored = {
								["async-trait"] = { "async_trait" },
								["napi-derive"] = { "napi" },
								["async-recursion"] = { "async_recursion" },
							},
						},
					},
				},
			},
		},
		config = function(_, opts)
			-- Configure dap with codelldb
			local package_path = require("mason-registry").get_package("codelldb"):get_install_path()
			opts.dap = {
				adapter = require("rustaceanvim.config").get_codelldb_adapter(
					package_path .. "/codelldb",
					package_path .. "/extension/lldb/lib/liblldb.dylib"
				),
			}
			vim.g.rustaceanvim = vim.tbl_deep_extend("keep", vim.g.rustaceanvim or {}, opts or {})
		end,
	},

	-- Correctly setup lspconfig for Rust 🚀
	{
		"neovim/nvim-lspconfig",
		opts = {
			servers = {
				rust_analyzer = {
					enabled = false,
				},
				bacon_ls = {
					enabled = diagnostics == "bacon-ls",
				},
				taplo = {
					keys = {
						{
							"K",
							function()
								if vim.fn.expand("%:t") == "Cargo.toml" and require("crates").popup_available() then
									require("crates").show_popup()
								else
									vim.lsp.buf.hover()
								end
							end,
							desc = "Show Crate Documentation",
						},
					},
				},
			},
			setup = {
				rust_analyzer = function()
					return true
				end,
			},
		},
	},

	{
		"nvim-neotest/neotest",
		optional = true,
		opts = function(_, opts)
			opts.adapters = opts.adapters or {}
			vim.list_extend(opts.adapters, {
				require("rustaceanvim.neotest"),
			})
		end,
	},
}
require("lazy").setup(plugins, {
	root = root .. "/plugins",
})

vim.cmd.colorscheme("tokyonight")
-- add anything else here

and i get some rust-analyzer: -32603: Invalid offset LineCol { line: 175, col: 0 } (line index length: 5299) that i don't get with the default lazyvim (this might be only due to my repro.lua having a quirk)

@dpetka2001
Copy link
Contributor

dpetka2001 commented Jun 14, 2024

I also tried your PR and don't get any diagnostics from bacon compared to the default LazyVim implementation. This is the configuration I used for rust.lua after rebasing to latest LazyVim. I don't get the error that @mirsella mentioned.

I left out the rust-analyzer stuff in nvim-lspconfig spec (even though this PR won't install it because of rust_analyzer.enabled = false, so it's just redundant), because as already discussed in #3389 and #2755 it was decided that users would be better off installing rust-analyzer via their Rust toolchain when using rustaceanvim.

if lazyvim_docs then
  -- LSP Server to use for Rust.
  -- Set to "bacon-ls" to use bacon-ls instead of rust-analyzer.
  -- only for diagnostics. The rest of LSP support will still be
  -- provided by rust-analyzer.
  vim.g.lazyvim_rust_diagnostics = "rust-analyzer"
end
local diagnostics = vim.g.lazyvim_rust_diagnostics or "rust-analyzer"

return {
  recommended = function()
    return LazyVim.extras.wants({
      ft = "rust",
      root = { "Cargo.toml", "rust-project.json" },
    })
  end,

  -- Extend auto completion
  {
    "hrsh7th/nvim-cmp",
    dependencies = {
      {
        "Saecki/crates.nvim",
        event = { "BufRead Cargo.toml" },
        opts = {
          completion = {
            cmp = { enabled = true },
          },
        },
      },
    },
    ---@param opts cmp.ConfigSchema
    opts = function(_, opts)
      opts.sources = opts.sources or {}
      table.insert(opts.sources, { name = "crates" })
    end,
  },

  -- Add Rust & related to treesitter
  {
    "nvim-treesitter/nvim-treesitter",
    opts = { ensure_installed = { "rust", "ron" } },
  },

  -- Ensure Rust debugger is installed
  {
    "williamboman/mason.nvim",
    optional = true,
    opts = function(_, opts)
      opts.ensure_installed = opts.ensure_installed or {}
      vim.list_extend(opts.ensure_installed, { "codelldb" })
      if diagnostics == "bacon-ls" then
        vim.list_extend(opts.ensure_installed, { "bacon", "bacon-ls" })
      end
    end,
  },

  {
    "mrcjkb/rustaceanvim",
    version = "^4", -- Recommended
    ft = { "rust" },
    opts = {
      server = {
        on_attach = function(_, bufnr)
          vim.keymap.set("n", "<leader>cR", function()
            vim.cmd.RustLsp("codeAction")
          end, { desc = "Code Action", buffer = bufnr })
          vim.keymap.set("n", "<leader>dr", function()
            vim.cmd.RustLsp("debuggables")
          end, { desc = "Rust Debuggables", buffer = bufnr })
        end,
        default_settings = {
          -- rust-analyzer language server configuration
          ["rust-analyzer"] = {
            cargo = {
              allFeatures = true,
              loadOutDirsFromCheck = true,
              buildScripts = {
                enable = true,
              },
            },
            -- Enable diagnostics if using rust-analyzer
            diagnostics = {
              enable = diagnostics == "rust-analyzer",
            },
            -- Add clippy lints for Rust.
            checkOnSave = {
              enable = diagnostics == "rust-analyzer",
              allFeatures = true,
              command = "clippy",
              extraArgs = { "--no-deps" },
            },
            procMacro = {
              enable = true,
              ignored = {
                ["async-trait"] = { "async_trait" },
                ["napi-derive"] = { "napi" },
                ["async-recursion"] = { "async_recursion" },
              },
            },
          },
        },
      },
    },
    config = function(_, opts)
      -- Configure dap with codelldb
      local package_path = require("mason-registry").get_package("codelldb"):get_install_path()
      opts.dap = {
        adapter = require("rustaceanvim.config").get_codelldb_adapter(
          package_path .. "/codelldb",
          package_path .. "/extension/lldb/lib/liblldb.dylib"
        ),
      }
      vim.g.rustaceanvim = vim.tbl_deep_extend("keep", vim.g.rustaceanvim or {}, opts or {})
      if vim.fn.executable("rust-analyzer") == 0 then
        LazyVim.error(
          "**rust-analyzer** not found in PATH, please install it.\nhttps://rust-analyzer.github.io/",
          { title = "rustaceanvim" }
        )
      end
    end,
  },

  -- Correctly setup lspconfig for Rust 🚀
  {
    "neovim/nvim-lspconfig",
    opts = {
      servers = {
        bacon_ls = {
          enabled = diagnostics == "bacon-ls",
        },
        taplo = {
          keys = {
            {
              "K",
              function()
                if vim.fn.expand("%:t") == "Cargo.toml" and require("crates").popup_available() then
                  require("crates").show_popup()
                else
                  vim.lsp.buf.hover()
                end
              end,
              desc = "Show Crate Documentation",
            },
          },
        },
      },
    },
  },

  {
    "nvim-neotest/neotest",
    optional = true,
    opts = {
      adapters = {
        ["rustaceanvim.neotest"] = {},
      },
    },
  },
}

@dpetka2001
Copy link
Contributor

So, I started bacon in another terminal and started the clippy job by pressing c and it correctly shows diagnostics in bacon. However, in Neovim I don't see any diagnostics. Is there maybe some additional configuration needed on the user end?

@crisidev
Copy link
Author

Bacon requires a specific configuration to work with the language server.

Have you followed the installation instructions here by any chance here https://github.com/crisidev/bacon-ls/blob/main/README.md?

@mirsella
Copy link
Contributor

mirsella commented Jun 14, 2024

Personally no I didn't check further, sorry, as usually lsp are plug and play, especially in lazyvim.
i'm sure it's me who is having a problem, but even with the readme i can't manage to make it work.
from the repro.lua i've given, i've just added the lazyvim config from you readme, uncommented the 2 lines and everything is still the same.
of course bacon and bacon-ls are both installed with mason from the start.

i don't know where to put the

[export]
enabled = true
path = ".bacon-locations"
line_format = "{kind}:{path}:{line}:{column}:{message}"

but i guess it's not required as there is the same config in lazyvim spec.

thanks !

@crisidev
Copy link
Author

I'll try to finish the PR early next week btw, the mason.nvim inclusion for bacon and bacon-ls was merged a couple of days ago.

@dpetka2001
Copy link
Contributor

In the same directory where Cargo.toml resides, I run bacon --init and that created a default bacon.toml file. I then added at the end of that file

[export]
enabled = true
path = ".bacon-locations"
line_format = "{kind}:{path}:{line}:{column}:{message}"

I left the bacon-ls configuration in nvim-lspconfig untouched with the default values. I then started bacon in another terminal and pressed c to start the clippy job. It shows correctly diagnostics. A file is created in the same directory as Cargo.toml and bacon.toml that is named .bacon-locations just like it's supposed to. However, when I open Neovim I still can't see any diagnostics. Are there any other steps that I'm maybe missing?

@mirsella
Copy link
Contributor

mirsella commented Jun 14, 2024

Following @dpetka2001 instruction and my repro.lua i got it working

edit: it's my personal opinion, just throwing some first impression
it is indeed faster, but rust_analyzer is still very good, and i'll probably stick with it for now:

  • need to have an extra file bacon.toml
  • need to put some extra config in it that i'll have to search on GitHub every time
  • need to launch bacon
  • if i'm on quick project/another person repo which most didn't setup theses things, i'll have 0 diagnostic because vim.g.lazyvim_rust_diagnostics = "bacon-ls" would still be set, so i'll need to setup everything for every project.

It would need a few things for it to be usable for me:
if the cargo project is set up for bacon + bacon-ls, then use it, and auto launch bacon if not running (the autostart can be a flag in bacon-ls)
if the cargo project is not setup for bacon + bacon-ls, fallback to rust_analyzer

@dpetka2001
Copy link
Contributor

dpetka2001 commented Jun 14, 2024

@mirsella Seriously? How? Why is it not working for me? I'll try your repro.lua right now.

@mirsella
Copy link
Contributor

mirsella commented Jun 14, 2024

@dpetka2001 i was using mason's bacon and bacon-ls btw don't know if it can change anything 🤷

@crisidev
Copy link
Author

Can you share the content of .bacon-locations after you have run it in your repo?

I'll try to use the repro this weekend to check what's going on with your config, because "it works on my computer" 😞

@dpetka2001
Copy link
Contributor

Funnily enough I just created a new Rust project with cargo init and there it works correctly as it should. So, there's no need for further help, really appreciate it. There was another change today for the Rust Extra, reflecting some changes in rustaceanvim, so maybe you would like to take that into account. Nothing big, one line really 😛

@crisidev crisidev reopened this Jun 25, 2024
@crisidev crisidev marked this pull request as ready for review June 25, 2024 13:21
@crisidev crisidev changed the title feat(rust) add bacon-ls and improve debugging feat(rust): add bacon-ls and improve debugging Jun 25, 2024
@crisidev
Copy link
Author

This is ready for review now!

Co-authored-by: Iordanis Petkakis <12776461+dpetka2001@users.noreply.github.com>
opts.ensure_installed = opts.ensure_installed or {}
vim.list_extend(opts.ensure_installed, { "codelldb" })
if diagnostics == "bacon-ls" then
vim.list_extend(opts.ensure_installed, { "bacon", "bacon-ls" })
Copy link
Contributor

@dpetka2001 dpetka2001 Jun 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also remove bacon-ls from mason.ensure_installed, since LSP servers that are defined in nvim-lspconfig spec get installed automatically if they are enabled. This is just a suggestion and doesn't mean you absolutely have to do it. It's just how the rest of LazyVim LSP servers are defined as well throughout the codebase. Corresponding code snippet in LazyVim that takes care of it.

PS: Also keep in mind I'm just a regular user like you and everything I comment on is just a suggestion from my part. The maintainer has the final say.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice. I'll change this, it will make the code lighter..

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bacon needs to be remain if I am not mistaken, since it's not an lsp server itself..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes correct.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants