nvim-cmp
nvim-cmp copied to clipboard
Astro LSP completions sometimes write over incorrect ranges
FAQ
- [X] I have checked the FAQ and it didn't resolve my problem.
Announcement
- [X] I have checked Breaking change announcement.
Minimal reproducible full config
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
-- bootstrap lazy.nvim
-- stylua: ignore
vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", "--branch=stable", lazypath })
end
vim.opt.rtp:prepend(vim.env.LAZY or lazypath)
require("lazy").setup({
spec = {
{
"williamboman/mason.nvim",
cmd = "Mason",
keys = { { "<leader>cm", "<cmd>Mason<cr>", desc = "Mason" } },
build = ":MasonUpdate",
opts = {
ensure_installed = {
"astro-language-server",
},
},
---@param opts MasonSettings | {ensure_installed: string[]}
config = function(_, opts)
require("mason").setup(opts)
local mr = require("mason-registry")
local function ensure_installed()
for _, tool in ipairs(opts.ensure_installed) do
local p = mr.get_package(tool)
if not p:is_installed() then
p:install()
end
end
end
if mr.refresh then
mr.refresh(ensure_installed)
else
ensure_installed()
end
end,
},
"hrsh7th/cmp-nvim-lsp",
{
"hrsh7th/nvim-cmp",
version = false, -- last release is way too old
event = "InsertEnter",
dependencies = {
"hrsh7th/cmp-nvim-lsp",
},
opts = function()
vim.api.nvim_set_hl(0, "CmpGhostText", { link = "Comment", default = true })
local cmp = require("cmp")
local defaults = require("cmp.config.default")()
return {
completion = {
completeopt = "menu,menuone,noinsert",
},
mapping = cmp.mapping.preset.insert({
["<C-n>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }),
["<C-p>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }),
["<C-b>"] = cmp.mapping.scroll_docs(-4),
["<C-f>"] = cmp.mapping.scroll_docs(4),
["<C-l>"] = cmp.mapping.complete(),
["<C-e>"] = cmp.mapping.abort(),
["<CR>"] = cmp.mapping.confirm({ select = true }), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
["<S-CR>"] = cmp.mapping.confirm({
behavior = cmp.ConfirmBehavior.Replace,
select = true,
}), -- Accept currently selected item. Set `select` to `false` to only confirm explicitly selected items.
}),
sources = cmp.config.sources({
{ name = "nvim_lsp" },
}),
experimental = {
ghost_text = {
hl_group = "CmpGhostText",
},
},
sorting = defaults.sorting,
}
end,
},
"williamboman/mason-lspconfig.nvim",
{
"neovim/nvim-lspconfig",
event = { "BufReadPre", "BufNewFile" },
dependencies = {
"mason.nvim",
"williamboman/mason-lspconfig.nvim",
"hrsh7th/cmp-nvim-lsp",
},
---@class PluginLspOpts
opts = {
-- LSP Server Settings
---@type lspconfig.options
servers = {
astro = {},
},
setup = {},
},
---@param opts PluginLspOpts
config = function(_, opts)
local servers = opts.servers
local has_cmp, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp")
local capabilities = vim.tbl_deep_extend(
"force",
{},
vim.lsp.protocol.make_client_capabilities(),
has_cmp and cmp_nvim_lsp.default_capabilities() or {}
)
local function setup(server)
local server_opts = vim.tbl_deep_extend("force", {
capabilities = vim.deepcopy(capabilities),
}, servers[server] or {})
if opts.setup[server] then
if opts.setup[server](server, server_opts) then
return
end
elseif opts.setup["*"] then
if opts.setup["*"](server, server_opts) then
return
end
end
require("lspconfig")[server].setup(server_opts)
end
-- get all the servers that are available through mason-lspconfig
local have_mason, mlsp = pcall(require, "mason-lspconfig")
local all_mslp_servers = {}
if have_mason then
all_mslp_servers = vim.tbl_keys(require("mason-lspconfig.mappings.server").lspconfig_to_package)
end
local ensure_installed = {} ---@type string[]
for server, server_opts in pairs(servers) do
if server_opts then
server_opts = server_opts == true and {} or server_opts
-- run manual setup if mason=false or if this is a server that cannot be installed with mason-lspconfig
if server_opts.mason == false or not vim.tbl_contains(all_mslp_servers, server) then
setup(server)
else
ensure_installed[#ensure_installed + 1] = server
end
end
end
if have_mason then
mlsp.setup({ ensure_installed = ensure_installed, handlers = { setup } })
end
end,
},
},
defaults = {
-- By default, only LazyVim plugins will be lazy-loaded. Your custom plugins will load during startup.
-- If you know what you're doing, you can set this to `true` to have all your custom plugins lazy-loaded by default.
lazy = false,
-- It's recommended to leave version=false for now, since a lot the plugin that support versioning,
-- have outdated releases, which may break your Neovim install.
version = false, -- always use the latest git commit
-- version = "*", -- try installing the latest stable version for plugins that support semver
},
performance = {
rtp = {
-- disable some rtp plugins
disabled_plugins = {
"gzip",
-- "matchit",
-- "matchparen",
-- "netrwPlugin",
"tarPlugin",
"tohtml",
"tutor",
"zipPlugin",
},
},
},
})
Description
Under some circumstances, inserting completions from the Astro LSP inside interpolations in the HTML template result in what looks like the completions being written over incorrect ranges in the buffer.
Steps to reproduce
Initialize an Astro project with npm create astro@latest
or equivalent. Then try to ciw
the test
inside the {test}
interpolations in a file like the below, type t
, and insert a completion for the test
variable.
---
const test = "TEST";
---
<!-- Case 1: Multiline tag -->
<div
id={test}
>
</div>
<!-- Case 2: Nested interpolation -->
{<div id={test}></div>}
<!-- Case 3: Space before = -->
<div id ={test}></div>
Expected behavior
The completion gets inserted and the file is as given in the reproduction example.
Actual behavior
Individually, the cases will become as below. Note that doing the replacement and completions sequentially within the same file may not produce this due to the missing brackets changing the syntax.
---
const test = "TEST";
---
<!-- Case 1: Multiline tag -->
<div
id={ttest
>
</div>
<!-- Case 2: Nested interpolation -->
{<div id={ttest
<!-- Case 3: Space before = -->
<div id =test}></div>
Additional context
Another user in the discussion linked below found no issue when using Neovim omnifunc to insert completions in similar circumstances.
Astro framework: https://astro.build/ Some prior discussion of this issue can be found here: https://github.com/LazyVim/LazyVim/discussions/1455
Hi, @Trildar
I think this problem is a bug on the language server side. it does not cause problems with VSCode, but seems to cause problems with LSP clients of other editors. The start and end ranges for the selected completion items returned by the language server are probably incorrect. (completionItem/resolve
)
You might want to report the issue to the language server side.
Hi, @Trildar
I think this problem is a bug on the language server side. it does not cause problems with VSCode, but seems to cause problems with LSP clients of other editors. The start and end ranges for the selected completion items returned by the language server are probably incorrect. (
completionItem/resolve
)You might want to report the issue to the language server side.
Yeah, looking at the language server responses, there does seem to be an issue with the ranges returned for completionItem/resolve
, so I filed an issue here: https://github.com/withastro/language-tools/issues/664
But seeing as how Neovim omnifunc apparently also works fine (mentioned in the discussion on LazyVim repo), I have to wonder if it's just a matter of that and VS Code not using ranges from completionItem/resolve
or some bigger issue with how nvim-cmp interacts with the language server.
This issue already solved in latest @astrojs/language-server