## 事象
```html
<template>
<div
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
```
カーソルは `<div` のあとにあり補完候補が出ている状態で決定すると以下のようになる。
```html
<template>
<divr>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
```
期待結果は
```html
<template>
<div>
<header>
<img alt="Vue logo" class="logo" src="./assets/logo.svg" width="125" height="125" />
```
### 再現手順
```console
bun create vue@latest vue-sandbox
```
`TypeScript` だけ有効にする。
```console
cd vue-sandbox
bun i
bun dev
```
`./src/App.vue` を開いて事象のように入力する。
#### 補足
- [[VSCode]]では再現しない
- [[HTML]]では再現しない
### 環境
```
+ @tsconfig/
[email protected]
+ @types/
[email protected]
+ @vitejs/
[email protected]
+ @vue/
[email protected]
+
[email protected]
+
[email protected]
+
[email protected]
+
[email protected]
+
[email protected]
+
[email protected]
```
| 対象 | バージョン |
| --------------------------------- | ----------- |
| [[Ubuntu]] | 24.04.1 LTS |
| [[Neovim]] | 0.11.0 |
| [[blink.cmp (Neovim)\|blink.cmp]] | cb5e346 |
| [[nvim-lspconfig]] | b70b900 |
| [[vue-language-server]] | 2.2.8 |
### LspInfo
```
==============================================================================
vim.lsp: require("vim.lsp.health").check()
- LSP log level : WARN
- Log path: /home/tadashi-aikawa/.local/state/nvim/lsp.log
- Log size: 0 KB
vim.lsp: Active Clients ~
- volar (id: 1)
- Version: ? (no serverInfo.version response)
- Root directory: ~/tmp/vue-sandbox
- Command: { "/home/tadashi-aikawa/.local/share/mise/installs/npm-vue-language-server/2.2.8/bin/vue-language-server", "--stdio" }
- Settings: vim.empty_dict()
- Attached buffers: 12
- copilot (id: 2)
- Version: 1.304.0
- Root directory: ~/tmp/vue-sandbox
- Command: { "node", "/home/tadashi-aikawa/.local/share/nvim/lazy/copilot.lua/copilot/js/language-server.js", "--stdio" }
- Settings: {
telemetry = {
telemetryLevel = "all"
}
}
- Attached buffers: 12
vim.lsp: Enabled Configurations ~
vim.lsp: File Watcher ~
- file watching "(workspace/didChangeWatchedFiles)" disabled on all clients
vim.lsp: Position Encodings ~
- No buffers contain mixed position encodings
```
## 原因
不明
## 解決方法
[[Volar]]の再インストール。
```
mise rm npm:@vue/language-server
mise use -g npm:@vue/language-server
```
## 調査記録
### blink.cmpのバージョンを変更する
`cb5e346` は v1.1.1 なので #2025/04/04 時点のもの。これをmasterにしてみる。
だが変わらず。以前からこうだったのかは自信がない。[[blink.cmp (Neovim)|blink.cmp]]に変更したのは #2025/04/05 であり、それ以降はWebの開発をあまりやっていないので、[[nvim-cmp]]から移行した影響の可能性もある。たが、それにしては[[Vue]]だけというのも...。
### [[Vue]]のバージョンについて
3.5.13が #2024/11/15 とかなり前であり、その頃はWeb開発バリバリやっていたのでそれが原因とは考えにくい。
### [[nvim-lspconfig]]のバージョンを変更する
#2025/04/05 時点の `2aacdb48` まで下げてみる。
変わらず。
### blink.cmpの `completion.list.selection` 設定を変更する
`preselect` と `auto_insert` を共にfalseにしたが効果はない。
```lua
completion = {
list = {
selection = {
preselect = false,
auto_insert = false,
},
},
```
### 他のプラグインを無効化してみる
怪しそうなものを一通り。
- [[conform.nvim]]
- [[copilot.lua]]
- [[Lspsaga]]
- [[ns-textobject.nvim]]
- [[nvim-autopairs]]
- [[nvim-surround]]
- [[nvim-ts-autotag]]
- [[obsidian.nvim]]
- [[timber.nvim]]
- [[tiny-inline-diagnostic.nvim (Neovim)|tiny-inline-diagnostic.nvim]]
変わらず。。まじか。。
### [[nvim-lspconfig]]で[[Volar]]の[[Hybrid Mode]]を有効にする
```lua
lspconfig.volar.setup({
capabilities = capabilities,
filetypes = { "typescript", "javascript", "javascriptreact", "typescriptreact", "vue" },
init_options = {
vue = {
-- デフォルト有効だけど明示的にfalseからtrueへ
hybridMode = true,
},
```
変わらず。
### LSPのログを確認する
以下のようなログが出力されたので、[[LSP]]の結果がそもそもおかしい気がする。少なくとも、この補完で `line` が複数行にまたがることはあり得ないので。
```lua
textEdit = {
newText = "div",
range = {
start = { character = 3, line = 6 },
["end"] = { character = 5, line = 7 }
}
}
```
となるとこちらで対応するのは厳しそう。
### nvim-cmpに戻してみる
問題が改善した... というよりは[[HTML]]タグ周りの補完が出なくなったので、結果的にそれを踏むことがなくなっただけな気がしている。
### Issueの作成
必要な情報を記載してIssueを出してみる。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
When completing HTML tags within a template tag in Neovim, text gets overwritten in unexpected areas. · Issue #5331 · vuejs/language-tools
</div>
<div class="link-card-v2-content">
Vue - Official extension or vue-tsc version 2.2.8 VSCode version 1.99.3 Vue version 3.5.13 TypeScript version 5. ...
</div>
<img class="link-card-v2-image" src="https://opengraph.githubassets.com/228fa0535ab4814cb2dd1a84e443cf22d6266b3c17f53e0f39d9bf51810f4044/vuejs/language-tools/issues/5331" />
<a href="https://github.com/vuejs/language-tools/issues/5331"></a>
</div>
#### blink.cmp x nvim-lspconfig の再現構成
再現しないので最小構成が欲しいと回答をもらった。
`repro.lua`
```lua
local root = vim.fn.fnamemodify("./.repro", ":p")
for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end
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)
local function get_typescript_server_path(root_dir)
local project_root = vim.fs.dirname(vim.fs.find("node_modules", { path = root_dir, upward = true })[1])
return project_root and vim.fs.joinpath(project_root, "node_modules", "typescript", "lib") or ""
end
---@type vim.lsp.Config
vim.lsp.config.volar = {
cmd = { "vue-language-server", "--stdio" },
filetypes = { "vue" },
root_markers = { "package.json" },
init_options = { typescript = { tsdk = "" } },
before_init = function(_, config)
if config.init_options and config.init_options.typescript and config.init_options.typescript.tsdk == "" then
config.init_options.typescript.tsdk = get_typescript_server_path(config.root_dir)
end
end,
}
vim.lsp.enable({ "volar" })
local plugins = {
{
"saghen/blink.cmp",
version = "*",
opts = {
completion = {
documentation = { auto_show = true },
},
},
},
}
require("lazy").setup(plugins, {
root = root .. "/plugins",
})
```
この設定にして編集すると再現する。
```console
nvim -u repro.lua src/App.vue
```
#### blink.cmp のみの再現構成
[[nvim-lspconfig]] `volar` 設定を直接書いただけ。
```lua
local root = vim.fn.fnamemodify("./.repro", ":p")
for _, name in ipairs({ "config", "data", "state", "cache" }) do
vim.env[("XDG_%s_HOME"):format(name:upper())] = root .. "/" .. name
end
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)
local function get_typescript_server_path(root_dir)
local project_root = vim.fs.dirname(vim.fs.find("node_modules", { path = root_dir, upward = true })[1])
return project_root and vim.fs.joinpath(project_root, "node_modules", "typescript", "lib") or ""
end
---@type vim.lsp.Config
vim.lsp.config.volar = {
cmd = { "vue-language-server", "--stdio" },
filetypes = { "vue" },
root_markers = { "package.json" },
init_options = { typescript = { tsdk = "" } },
before_init = function(_, config)
if config.init_options and config.init_options.typescript and config.init_options.typescript.tsdk == "" then
config.init_options.typescript.tsdk = get_typescript_server_path(config.root_dir)
end
end,
}
vim.lsp.enable({ "volar" })
local plugins = {
{
"saghen/blink.cmp",
version = "*",
opts = {},
},
}
require("lazy").setup(plugins, {
root = root .. "/plugins",
})
```
#### 職場環境だと再現しない
```console
- ts_ls (id: 1)
- Version: ? (no serverInfo.version response)
- Root directory: ~/work/vue-sandbox
- Command: { "/home/tadashi-aikawa/.local/share/mise/installs/npm-typescript-language-server/4.3.3/bin/typescript-language-server", "--stdio" }
- Settings: vim.empty_dict()
- Attached buffers: 10
- volar (id: 2)
- Version: ? (no serverInfo.version response)
- Root directory: ~/work/vue-sandbox
- Command: { "/home/tadashi-aikawa/.local/share/mise/installs/npm-vue-language-server/2.1.10/bin/vue-language-server", "--stdio" }
- Settings: vim.empty_dict()
- Attached buffers: 10
```
```console
npm:@vue/language-server 2.2.10 ~/.config/mise/config.toml latest
npm:@vue/typescript-plugin 2.2.10 ~/.config/mise/config.toml latest
npm-typescript 5.6.2
npm-typescript-language-server 4.3.3
```
2.2.10 -> 2.2.8 にあわせても再現しない。
#### 家の環境ふたたび
```console
npm:@vue/language-server 2.2.8 ~/.config/mise/config.toml latest
npm:@vue/typescript-plugin 2.2.8 ~/.config/mise/config.toml latest
npm:typescript 5.8.2 ~/.config/mise/config.toml latest
npm:typescript-language-server 4.3.4 ~/.config/mise/config.toml latest
```
[[TypeScript]]バージョンは利用されていないのでさておき、[[TypeScript Language Server]]のマイナーバージョン違うのは気になる。。。ただ、変更内容的には関係なさそうに見える。
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
Release v4.3.4 · typescript-language-server/typescript-language-server
</div>
<div class="link-card-v2-content">
4.3.4 (2025-02-26)Bug Fixespreviewer: mark @example and @default code blocks as TS (#918) (184c60d)
</div>
<img class="link-card-v2-image" src="https://opengraph.githubassets.com/72baa6c986f33c44bbd4a29ca772dd9e5f757a19a823b2ccefc22ac4e8416254/typescript-language-server/typescript-language-server/releases/tag/v4.3.4" />
<a href="https://github.com/typescript-language-server/typescript-language-server/releases/tag/v4.3.4"></a>
</div>
##### [[TypeScript Language Server]]バージョンを下げてみる
```console
mise use -g npm:
[email protected]
```
関係なかった。
##### [[TypeScript]]バージョンを下げてみる
```console
$ mise use -g npm:
[email protected]
Reshimming mise 22.8.0...
mise npm:
[email protected] ✓ installed mise ~/.config/mise/config.toml tools: npm:
[email protected]
```
関係なかった。5.6.2や5.8.3も試したけどダメ。
##### [[Volar]]を再インストールしてみる
```
mise rm npm:@vue/language-server
```
```console
$ mise use -g npm:@vue/language-server
mise use -g npm:@vue/language-server
Reshimming mise 22.8.0...
mise npm:@vue/
[email protected] ✓ installed mise ~/.config/mise/config.toml tools: npm:@vue/
[email protected]
```
2.2.10とバージョンは上がっているが、職場ではどちらも再現しなかったので問題ないはず。**なおった!**
#### 解消した旨を連絡
<div class="link-card-v2">
<div class="link-card-v2-site">
<img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" />
<span class="link-card-v2-site-name">GitHub</span>
</div>
<div class="link-card-v2-title">
When completing HTML tags within a template tag in Neovim, text gets overwritten in unexpected areas. · Issue #5331 · vuejs/language-tools
</div>
<div class="link-card-v2-content">
Vue - Official extension or vue-tsc version 2.2.8 VSCode version 1.99.3 Vue version 3.5.13 TypeScript version 5. ...
</div>
<img class="link-card-v2-image" src="https://opengraph.githubassets.com/61ecab88cf3c5e46b5cfaf87fd3ba01f8092578f9d81b5afc69c7e502389ccb1/vuejs/language-tools/issues/5331" />
<a href="https://github.com/vuejs/language-tools/issues/5331#issuecomment-2824109773"></a>
</div>