> [!warning]
> 執筆当時の環境で起こっていたものであり、 #2025/04/06 時点の [[blink.cmp (Neovim)|blink.cmp]]、[[blink.compat (Neovim)|blink.compat]]、[[obsidian.nvim]]では発生しない。むしろこの対応のせいでバグになってしまっている。詳細は [[📜2025-04-06 obsidian.nvimをblink.cmpに対応]] を参照。
## 事象
赤枠の選択肢を挿入すると **カーソル位置によって** 結果が変わる。
![[Pasted image 20250323213309.png]]
| 挿入場所 | 結果 | OK/NG |
| ------- | ----------------------- | ----- |
| \|あいうえお | `[[AIに関する雑記]]あいうえお` | OK |
| あ\|いうえお | `あ[[[[AIに関する雑記]]えお` | NG |
| あい\|うえお | `あい[[ai[[ai]]うえお` | NG |
| あいう\|えお | `あいう[[ai[[AIに関する雑記]]えお` | NG |
| あいうえ\|お | `あいうえ[[AIに関する雑記]]お` | OK |
| あいうえお\| | `あいうえお[[AIに関する雑記]]` | OK |
なお、[[ASCII]]の場合は問題は発生しない。
### 環境
| 対象 | バージョン |
| ----------------- | ------------------ |
| [[Ubuntu]] | 24.04.1 LTS |
| [[Neovim]] | 0.10.3 |
| [[obsidian.nvim]] | `e5f4791` (fork版) |
| [[nvim-cmp]] | ??? |
## 調査
[[Claude 3.7 Sonnet]]が言うには `can_complete` の中でマルチバイト文字が考慮されていないため、insert位置が不正になっているのが問題ではないかとのこと。
`あ|い` と `a|i` では `insert_start` が 3 or 1 で変わる。これはバイト数。
```lua
local insert_start = vim.str_byteindex(before_line, cursor_char_idx - input_char_len)
```
一旦AIの結果を反映したがうまくいかない。一旦ログ仕込む。
```lua
M.can_complete = function(request)
local input, search = find_search_start(request.context.cursor_before_line)
if input == nil or search == nil then
return false
elseif string.len(search) == 0 or util.is_whitespace(search) then
return false
end
-- マルチバイト文字考慮
local cursor_col = request.context.cursor.col
local before_line = request.context.cursor_before_line
if vim.startswith(input, "[[") then
local suffix = string.sub(request.context.cursor_after_line, 1, 2)
local insert_end_offset = suffix == "]]" and 1 or -1
-- 文字インデックスでの挿入開始位置を計算
local cursor_char_idx = vim.str_utfindex(before_line, #before_line)
local input_char_len = vim.str_utfindex(input, #input)
local insert_start = vim.str_byteindex(before_line, cursor_char_idx - input_char_len)
local origin_insert_start = vim.str_utfindex(cursor_col - 1 - #input)
print(
string.format(
"🪵 L55 suffix=%s, insert_end_offset=%s, cursor_char_idx=%s, input_char_len=%s, insert_start=%s, origin_insert_start=%s",
suffix,
insert_end_offset,
cursor_char_idx,
input_char_len,
insert_start,
origin_insert_start
)
)
return true, search, insert_start, cursor_col + insert_end_offset, M.RefType.Wiki
elseif vim.startswith(input, "[") then
local suffix = string.sub(request.context.cursor_after_line, 1, 1)
local insert_end_offset = suffix == "]" and 0 or -1
-- 文字インデックスでの挿入開始位置を計算
local cursor_char_idx = vim.str_utfindex(before_line, #before_line)
local input_char_len = vim.str_utfindex(input, #input)
local insert_start = vim.str_byteindex(before_line, cursor_char_idx - input_char_len)
return true, search, insert_start, cursor_col + insert_end_offset, M.RefType.Markdown
else
return false
end
end
```
どうやら `origin_insert_start` の方が近そうなので
```lua
if vim.startswith(input, "[[") then
local suffix = string.sub(request.context.cursor_after_line, 1, 2)
local cursor_col = request.context.cursor.col
local insert_end_offset = suffix == "]]" and 1 or -1
return true, search, vim.str_utfindex(cursor_col - 1 - #input), cursor_col + insert_end_offset, M.RefType.Wiki
elseif vim.startswith(input, "[") then
local suffix = string.sub(request.context.cursor_after_line, 1, 1)
local cursor_col = request.context.cursor.col
local insert_end_offset = suffix == "]" and 0 or -1
return true, search, cursor_col - 1 - #input, cursor_col + insert_end_offset, M.RefType.Markdown
else
return false
end
```
としたらスタートは正解になったが、補完後の2文字が今度は消えてしまう。
`insert_end` を byteで返してるのまずくない?? 文字数だよねそこ??
## 最終的に
バイト数を文字数にすべて変換した上で処理をしたら動いた。
```lua
---@return boolean, string|?, integer|?, integer|?, obsidian.completion.RefType|?
M.can_complete = function(request)
local input, search = find_search_start(request.context.cursor_before_line)
if input == nil or search == nil or string.len(search) == 0 or util.is_whitespace(search) then
return false
end
local until_cursor_len = vim.str_utfindex(request.context.cursor_before_line, #request.context.cursor_before_line)
local cursor_col = vim.str_utfindex(request.context.cursor.col)
local input_len = vim.str_utfindex(input, #input)
if vim.startswith(input, "[[") then
return true, search, until_cursor_len - input_len, cursor_col, M.RefType.Wiki
end
if vim.startswith(input, "[") then
return true, search, until_cursor_len - input_len, cursor_col, M.RefType.Markdown
end
return false
end
```
## コミット
<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">
fix: Inserting completions into sentences containing multibyte charac… · tadashi-aikawa/obsidian.nvim@4812505
</div>
<div class="link-card-v2-content">
…ters causes insertion positions and ranges to shift
</div>
<img class="link-card-v2-image" src="https://opengraph.githubassets.com/734818767bb12c9dece4669241954b20674aedf60ebf5f239704f1dc7b901117/tadashi-aikawa/obsidian.nvim/commit/4812505c499179d73fbb6778a3702d23e92a7a6d" />
<a href="https://github.com/tadashi-aikawa/obsidian.nvim/commit/4812505c499179d73fbb6778a3702d23e92a7a6d"></a>
</div>