表題の件だけでなく、ファイルを開く順序がたまにバラバラになる問題も解消したかも。 ## ログ > [!right-bubble] ![[minerva-face-right.webp]] > Obsidianでファイル(エディタ)を開いていない状態で、Custom Searchを起動しpreview表示したあとに複数のファイルを選択して決定で開くと、1つ目に選択したファイルだけが開いた直後に閉じてし > まう問題を調査して。 > [!left-bubble] ![[chappy.webp]] > 1. 問題の主因は「preview 後の復元処理」と「複数選択オープン処理」の競合です。 > preview() は same-tab + keepOpen で開き、usedPreview と stateToRestore を設定します。src/ui/AnotherQuickSwitcherModal.ts:410 src/ui/AnotherQuickSwitcherModal.ts:419 src/ui/ > AnotherQuickSwitcherModal.ts:880 > 2. ファイル未オープン状態だと initialLeaf は null でモーダル生成されます。 > src/commands.ts:37 src/commands.ts:52 > 3. この状態で stateToRestore.restore() が走ると、対象 leaf は detach()(=タブを閉じる)されます。 > src/app-helper.ts:492 src/app-helper.ts:493 > 4. 複数選択時の "open" 系ハンドラは close() の直後にファイルを開き始めますが、onClose 側の復元を待っていません。 > src/ui/AnotherQuickSwitcherModal.ts:1117 src/ui/AnotherQuickSwitcherModal.ts:1119 > 一方 onClose は stateToRestore があれば復元を必ずキューします。src/ui/AnotherQuickSwitcherModal.ts:324 > 5. そのため、先頭で開いたファイルが復元処理の detach() に巻き込まれ、直後に閉じる挙動になります。 > 特に preventDuplicateTabs 有効時は、new-tab でも既存 leaf を再利用するため再現しやすいです。src/app-helper.ts:683 src/app-helper.ts:696 > 6. 追加で、AnotherQuickSwitcherModal の複数選択オープンだけ openFile(...) の await が抜けており、レースを悪化させています。 > src/ui/AnotherQuickSwitcherModal.ts:1119 src/ui/AnotherQuickSwitcherModal.ts:1143 src/ui/AnotherQuickSwitcherModal.ts:1158 src/ui/AnotherQuickSwitcherModal.ts:1173 > 比較として BacklinkModal/GrepModal は await しています。src/ui/BacklinkModal.ts:560 src/ui/GrepModal.ts:1099 > [!right-bubble] ![[minerva-face-right.webp]] > > 6. 追加で、AnotherQuickSwitcherModal の複数選択オープンだけ openFile(...) の await が抜けており、レースを悪化させています。 > src/ui/AnotherQuickSwitcherModal.ts:1119 src/ui/AnotherQuickSwitcherModal.ts:1143 src/ui/AnotherQuickSwitcherModal.ts:1158 src/ui/AnotherQuickSwitcherModal.ts:1173 > 比較として BacklinkModal/GrepModal は await しています。src/ui/BacklinkModal.ts:560 src/ui/GrepModal.ts:1099 > > これは `await` をつけるとなぜか『複数選択してもすべて閉じてしまう』ので無い方が良さそうですね。理由は分かっていませんが。。 > [!left-bubble] ![[chappy.webp]] > • その観察は筋が通っています。await の有無は「原因」ではなく「レースの出方」を変えています。 > > - close() 後、onClose が stateToRestore.restore() をキューします(usedPreview 時)。src/ui/AnotherQuickSwitcherModal.ts:324 > - エディタ未オープン開始だと initialLeaf === null なので、restore() は newLeaf.detach()(タブを閉じる)になります。src/app-helper.ts:492 > - 複数選択ルートでは skipRestoreOnClose を立てていないため、この detach() が必ず走ります。 > - ここで await を入れるとイベントループ上で onClose 側の復元が先に進みやすくなり、再利用された leaf が閉じられて「全部閉じる」側に振れます。 > 逆に await なしだと競合が弱まって「先頭だけ閉じる」に見える、という差です。 > > なので結論は、await を外す判断自体は妥当ですが、根本は > 「複数選択確定時に preview 復元(restore)を止めていないこと」です。 > await はその不具合を増幅しただけです。 > > 実装するなら、まず複数選択分岐で close() 前に usedPreview 時の skipRestoreOnClose/skipRecentHistoryRestoreOnClose を設定するのが本筋です。 > [!right-bubble] ![[minerva-face-right.webp]] > 実装するなら、まず複数選択分岐で close() 前に usedPreview 時の skipRestoreOnClose/skipRecentHistoryRestoreOnClose を設定するのが本筋です。 > > これを具体的に知りたいです。それらを設定する・しないでどう変わるのか? > [!left-bubble] ![[chappy.webp]] > • ポイントは「onClose で何を実行させるか」を2つのフラグで制御していることです。 > > onClose の実質ロジックはこうです。src/ui/AnotherQuickSwitcherModal.ts:324 > > if (this.stateToRestore && !this.skipRestoreOnClose) { > this.navigate(() => this.stateToRestore!.restore()); > } > if (this.usedPreview && !this.skipRecentHistoryRestoreOnClose) { > this.navigate(() => this.scheduleRecentHistoryRestore(this.recentHistorySnapshot)); > } > > それぞれの意味は次です。 > > 1. skipRestoreOnClose > > - false(設定しない): stateToRestore.restore() が走る > - true(設定する): restore() を止める > - 影響: ここがタブが閉じる/閉じないに直結します。 > 特に初期ファイルなしだと restore() 内で detach() が走るため、開いた leaf が閉じられます。src/app-helper.ts:492 > > 2. skipRecentHistoryRestoreOnClose > > - false(設定しない): preview前の recent 履歴に戻す > - true(設定する): recent 履歴の巻き戻しを止める > - 影響: これは「最近開いたファイル履歴」だけの話で、タブ閉鎖の直接原因ではありません。 > > 組み合わせで言うとこうなります。 > > 1. 両方 false(現状の複数選択) > > - restore() 実行 + recent巻き戻し実行 > - タブ閉じ問題が起きる余地あり > > 2. skipRestoreOnClose=true, skipRecent...=false > > - タブ閉じは止まる > - recent は巻き戻る > > 3. skipRestoreOnClose=false, skipRecent...=true > > - recent は維持される > - タブ閉じ問題は残る > > 4. 両方 true > > - タブ閉じも recent巻き戻しも止まる > - 複数選択確定時に最も意図に近いことが多い > > 補足として、このフラグは onClose 内で毎回 false に戻されるため(src/ui/AnotherQuickSwitcherModal.ts:327 src/ui/AnotherQuickSwitcherModal.ts:334)、close() の前に都度セットする必要が > あります。 > [!right-bubble] ![[minerva-face-right.webp]] > > 4. 両方 true > > > > - タブ閉じも recent巻き戻しも止まる > > - 複数選択確定時に最も意図に近いことが多い > > とありますが、複数選択の有無にかかわらず **プレビューやnavigateでエディタや履歴の状態が変わっているなら、それは戻さないとまずいのでは** ないでしょうか? > > 論点は、**エディタの状態を巻き戻すとき** に > > 1. エディタが閉じる > 2. 複数選択1つ目が開く > 3. 複数選択2つ目が開く > > のような順序を期待していたのが > > 1. 複数選択1つ目が開く > 2. エディタが閉じる > 3. 複数選択2つ目が開く > > のように変わってしまっているのが問題だと思います。 > > エディタが閉じるまで、複数選択の対象を開くのを待機させることはできますか? > [!left-bubble] ![[chappy.webp]] > • はい、できます。 > そして今回の論点なら、skipRestoreOnClose を立てるより「close 完了を待つ」方が正しいです。 > > AnotherQuickSwitcherModal は単一選択ではすでに close() -> isClosed待機 -> open で順序制御しています(src/ui/AnotherQuickSwitcherModal.ts:876 以降)。 > 複数選択でも同じにすれば、期待どおり次の順になります。 > > 1. onClose で preview 巻き戻し(restore / recent復元) > 2. 1件目を開く > 3. 2件目を開く > > 実装イメージはこれです。 > > this.close(); > await this.isClosed; // または await this.safeClose() > > for (const x of items) { > await this.appHelper.openFile(await this.toFileToOpened(x), {...}); > await sleep(0); > } > > この方針なら「巻き戻しは維持しつつ、順序だけ直す」ができます。 > skipRestoreOnClose / skipRecentHistoryRestoreOnClose は「巻き戻し自体を無効化したい」場合だけ使うのが適切です。 > [!left-bubble] ![[chappy.webp]] > • 実装方針を3点で進めます。 > > 1. AnotherQuickSwitcherModal の複数選択 (open / open in new tab / open in new pane) で、close() 後に必ず await this.isClosed を挟み、onClose の巻き戻し完了を待ってから開く。 > 2. その上で openFile(...) は逐次 await して順序を保証する。 > 3. 既存の単一選択・APIモード分岐は変更しない。 > [!left-bubble] ![[chappy.webp]] > • 実装を反映しました。 > 複数選択時は「close() 完了(onClose の巻き戻し完了)を待ってから開く」順序に変更しています。 > > - 変更: src/ui/AnotherQuickSwitcherModal.ts:1117 > - 変更: src/ui/AnotherQuickSwitcherModal.ts:1142 > - 変更: src/ui/AnotherQuickSwitcherModal.ts:1158 > - 変更: src/ui/AnotherQuickSwitcherModal.ts:1174 > > 各分岐で以下を実施しました。 > > 1. this.close(); > 2. await this.isClosed; > 3. await this.appHelper.openFile(...) を順次実行 > > これで、期待していた順序(巻き戻し完了 → 1件目オープン → 2件目オープン...)になります。 > > 確認結果: > > - bun run test 実行済み > - 8 suites / 315 tests すべて pass > > 必要なら次に、実機で再現手順(エディタ未オープン + preview + 複数選択)で挙動確認するためのチェック観点も整理します。