#Obsidian
## 経緯
[[Code Editor Shortcuts]]のマルチカーソルは便利だが、表題の問題がある。`Select all occurrences`では日本語も動作するので実は不具合なのかもと思い調べてみることにした。
## 対象コード
以下2つの違いを見る。
- [selectWordOrNextOccurrence](https://github.com/timhor/obsidian-editor-shortcuts/blob/master/src/actions.ts#L158)
- [selectAllOccurrences](https://github.com/timhor/obsidian-editor-shortcuts/blob/master/src/actions.ts#L200)
## 動作の確認
### if文の判定
まずは[if文の処理](https://github.com/timhor/obsidian-editor-shortcuts/blob/master/src/actions.ts#L167)をデバッグするとこから。
```ts
console.log(searchText)
console.log(singleSearchText)
if (searchText.length > 0 && singleSearchText) {
```
`aa aa aa`で`aa`に対して実行した場合の出力。
```
true
aa
true
aa
true
```
`ああ ああ ああ`で`ああ`に対して実行した場合の出力。
```
true
ああ
true
ああ
true
```
- どちらもif文までは適切な判定をとっている
- だが日本語の場合はカーソルが増えない
- 内部状態は正しいが、カーソルを増やす処理がうまくいってなさそう
### カーソルを増やす処理
```ts
if (searchText.length > 0 && singleSearchText) {
const { from: latestMatchPos } = getSelectionBoundaries(
allSelections[allSelections.length - 1],
);
console.log(latestMatchPos)
const nextMatch = findNextMatchPosition({
editor,
latestMatchPos,
searchText,
searchWithinWords: isManualSelection,
documentContent: editor.getValue(),
});
console.log(nextMatch)
const newSelections = nextMatch
? allSelections.concat(nextMatch)
: allSelections;
console.log(newSelections)
editor.setSelections(newSelections);
const lastSelection = newSelections[newSelections.length - 1];
editor.scrollIntoView(getSelectionBoundaries(lastSelection));
} else {
```
`aa aa aa`で`aa`に対して実行した場合の出力。
```
{line: 49, ch: 0}
{
"anchor": {
"line": 49,
"ch": 3
},
"head": {
"line": 49,
"ch": 5
}
}
[
{
"anchor": {
"line": 49,
"ch": 0
},
"head": {
"line": 49,
"ch": 2
}
},
{
"anchor": {
"line": 49,
"ch": 3
},
"head": {
"line": 49,
"ch": 5
}
}
]
```
`ああ ああ ああ`で`ああ`に対して実行した場合の出力。
```
{line: 49, ch: 0}
null
[
{
"anchor": {
"line": 49,
"ch": 0
},
"head": {
"line": 49,
"ch": 2
}
}
]
```
`nextMatch`の判定がうまくいってなさそう。
### マッチポジションを探す処理
```ts
export const findNextMatchPosition = ({
editor,
latestMatchPos,
searchText,
searchWithinWords,
documentContent,
}: {
editor: Editor;
latestMatchPos: EditorPosition;
searchText: string;
searchWithinWords: boolean;
documentContent: string;
}) => {
const latestMatchOffset = editor.posToOffset(latestMatchPos);
const matches = findAllMatches({
searchText,
searchWithinWords,
documentContent,
});
console.log(matches)
```
日本語の場合、`matches`が既に空。英語の場合は出現箇所のArrayが返却されている。
```ts
export const findAllMatches = ({
searchText,
searchWithinWords,
documentContent,
}: {
searchText: string;
searchWithinWords: boolean;
documentContent: string;
}) => {
console.log(searchText, searchWithinWords)
const escapedSearchText = escapeRegExp(searchText);
const searchExpression = new RegExp(
searchWithinWords ? escapedSearchText : `\\b${escapedSearchText}\\b`,
'g',
);
return Array.from(documentContent.matchAll(searchExpression));
};
```
`searchWithinWords`が`false`になっている。`selectAllOccurrences`でも同じ関数が呼ばれているが、そのときは`searchWithinWords`は`true`になっている。
`searchWithinWords`が`true`の場合、検索文字列の前後に`\\b`が挿入される。これは単語の完全一致を表す正規表現。これは単語の部分文字列をマッチ対象から外すためと思われる。
たとえば以下のケース。
```
play aplay playb play aplayb
```
`play`にカーソルがある状態で`Select word or next occurrence of selection`を実行すると、`aplay`、`playb`、`aplayb`はマルチカーソル対象から除外される。一方、`Select all occurrences`の場合はすべての`play`部分が対象に加わる。
### なぜ`searchWithinWords`は`false`なのか
`selectAllOccurrences`の場合は該当箇所に必ず`true`が入る。一方、`selectWordOrNextOccurrence`の場合は`isManualSelection`の値が反映される。
```ts
const nextMatch = findNextMatchPosition({
editor,
latestMatchPos,
searchText,
searchWithinWords: isManualSelection,
documentContent: editor.getValue(),
});
```
つまり、**`isManualSelection`が`false`のとき**に今回の期待しない挙動となる。これ以上深追いするとコスパが悪くなりそうなため、自力調査はここまでとする。
## 問題を解消するには
[actions.tsの175行目](https://github.com/timhor/obsidian-editor-shortcuts/blob/master/src/actions.ts#L175) を以下のように変更する。
```diff
const nextMatch = findNextMatchPosition({
editor,
latestMatchPos,
searchText,
- searchWithinWords: isManualSelection,
+ searchWithinWords: true,
documentContent: editor.getValue(),
});
```
少なくとも自分の利用用途ではこれで問題ない。せっかくなので[[GitHub]]にIssueを立てておく。
<div class="link-card">
<div class="link-card-header">
<img src="https://github.githubassets.com/favicons/favicon.svg" class="link-card-site-icon"/>
<span class="link-card-site-name">GitHub</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<div>
<p class="link-card-title">Doesn't increase cursors when we use not English (Select word or next occurrence of selection) · Issue #37 · timhor/obsidian-editor-shortcuts</p>
</div>
<div class="link-card-description">
Thank you for the great plugin! Especially, Select word or next occurrence of selection is very exci...
</div>
</div>
<img src="https://opengraph.githubassets.com/38bd00581f1ca4cae446845f2642f62761513a61758035c2e83aaf1c6832bb0b/timhor/obsidian-editor-shortcuts/issues/37" class="link-card-image" />
</div>
<a href="https://github.com/timhor/obsidian-editor-shortcuts/issues/37"></a>
</div>