Skip to content

fix(macos): bind Find Action to Command-Shift-A - #1406

Open
mvanhorn wants to merge 3 commits into
1lck:previewfrom
mvanhorn:fix/1177-macos-find-action-shortcut
Open

mvanhorn wants to merge 3 commits into
1lck:previewfrom
mvanhorn:fix/1177-macos-find-action-shortcut

Conversation

@mvanhorn

Copy link
Copy Markdown

Problem

Issue #1177 lists macOS shortcuts that do nothing in Lithe, and Find Action (Shift-Command-A in IntelliJ) is one of the P0 entries. Search Everywhere already has a commands-only mode when the query starts with /, but nothing opened the popup in that mode. This PR adds that one shortcut. The rest of the issue (smart completion, Generate Code, refactoring, F4 and so on) needs separate design work, so this refs the issue rather than closing it.

Fix

A new find-action command in the shared keymap catalog defaults to Shift-Command-A on the macOS preset. IDEA Classic and Eclipse leave it unbound, because there is no upstream keymap XML in the tree to confirm their mapping and the command should not quietly inherit the macOS default.

It is registered in LitheActionRegistry, follows the workspace and search-module availability rules, and has a "Find Action…" menu item that uses the effective binding, so remapping, disabling and restoring in Settings > Keymap apply to the menu as well. No new key event monitor is added, and double-Shift and Shift-Command-O still open ordinary Search Everywhere.

openFindAction() opens Search Everywhere seeded with /. If the popup is already open, it switches the query back to / and refocuses the input instead of closing. Activation failure goes through dismissSearchEverywhere(), and dismissing clears a leftover command-mode query so reopening starts clean, while ordinary search drafts are kept.

In the view, a small AppKit helper behind the input makes it first responder again each time the popup is shown or reseeded. SwiftUI focus state alone did not bring focus back after AppKit had cleared the responder. The popup panel selects the restored text when it takes focus, so when the text is exactly / the helper puts the caret after the slash. Typing run then gives /run instead of replacing the slash and falling back to workspace search.

The menu strings have English and Simplified Chinese translations. The keymap agent note and the keymap-customization capability record describe the new entry point and its manual checks, and verificationStatus stays pending.

Testing

All of this was run on top of current preview.

  • The test-stability harness passes for KeyboardShortcutTests, SearchEverywhereQueryModeTests and AppLocalizationTests. The new native popup test runs the real command through AppModel and takes about 1.35 s.
  • scripts/test-macos.sh --no-parallel ran all 3013 tests. The only failures were gitLogQueryMatchesInclusiveAfterAndExclusiveBeforeDates and the two Plugin UI icon tests, which fail the same way on unmodified preview on this machine. One Search Everywhere appearance test failed once in that run and then passed in three later runs of every Search Everywhere suite.
  • The agent-notes, service-boundary and platform feature matrix scripts (including the change check against preview) pass.

I have not done the manual run in the app yet (Shift-Command-A from the editor, the sidebar and a second project window), so I'm opening this as a draft, as the repo's PR guidelines ask.

Refs #1177

@ghfind-review ghfind-review Bot added the review: no-score ghfind author score missing (not zero) label Oct 21, 2026

@xiaoyumuxi xiaoyumuxi left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

跨 PR 审查(head 9632f520)

结论:没有发现可以从本次差异中确定的阻塞缺陷;当前仍为 Draft,且 mergeable=false。

新增的 Find Action 命令进入命令目录、快捷键映射和 Search Everywhere 的 / 命令搜索模式;已有弹窗重新唤起时显式重置查询与聚焦,关闭时清理模式前缀。静态追踪显示没有另建一套搜索引擎,继续复用既有 Search Everywhere 的数据与窗口。

合并前建议核对真实 AppKit/SwiftUI 键盘交互:⌘⇧A 在工作台打开、在已打开的 Search Everywhere 上切换到命令模式并把光标放在 / 后、Esc 退出后普通搜索不残留前缀;输入法组合输入与其他自定义 keymap 不被抢焦点。还应在项目切换及 module 激活迟到场景检查窗口不会复活到错误项目。新增测试已覆盖部分模式状态,但本轮没有实际运行 GUI。

先同步最新 preview、重跑相关 Search Everywhere 原生点击/焦点测试后再转 Ready。

@mvanhorn

Copy link
Copy Markdown
Author

同步进展说明。我在本地把分支变基到最新 preview(a9264ca2),有三处冲突:

  • Localizable.strings 现在由 shared/i18n 生成,所以 Find Action 的三个词条改为加到 shared/i18n/messages.json 和 en-US / zh-CN 语言文件,再用 scripts/generate-i18n.mjs 重新生成。
  • SearchEverywhereView 的输入框改成了 LitheSearchField。聚焦辅助视图改为挂在新的搜索栏上,并在辅助视图范围内查找可编辑文本框,而不是只看中心点,因为这一行现在还包含图标和右侧提示。

本地已运行并通过:node scripts/generate-i18n.mjs --check,scripts/test-i18n.mjs(35 项;第一次运行有一项失败,之后连续三次全部通过),以及 verify-agent-notes、verify-service-boundaries、verify-platform-feature-matrix 和相对 preview 的 matrix change 检查。

还没有完成的部分:这台机器磁盘空间不足,Swift 编译中途失败,所以 KeyboardShortcutTests、SearchEverywhereQueryModeTests 和 Search Everywhere 原生点击与焦点测试没有在变基后的代码上重新运行,变基结果也还没有推送。快捷键唤起、已打开弹窗切换到命令模式、光标位于 / 之后、Esc 后的普通搜索、输入法组合输入、自定义 keymap、项目切换和 module 延迟激活这些真实界面场景也没有手动验证。PR 继续保持 Draft。

Add a find-action command to the shared keymap catalog with the macOS
default Shift-Command-A. It opens Search Everywhere in slash command
mode, or switches an already open popup to command mode and refocuses
the input. IDEA Classic and Eclipse leave it unbound.

Refs 1lck#1177
@mvanhorn
mvanhorn force-pushed the fix/1177-macos-find-action-shortcut branch from 9632f52 to 4e5fe61 Compare October 25, 2026 15:37
@mvanhorn

Copy link
Copy Markdown
Author

Synced to the latest preview (80101d8). New head is 4e5fe61. The only conflict was in the two Localizable.strings files next to the new Find Branch entries from #1496; both sets of keys are kept.

Tests that passed on the new head:

  • ./scripts/test-macos.sh --filter 'KeyboardShortcutTests|SearchEverywhereQueryModeTests|AppLocalizationTests': 53 tests in 6 suites passed
  • node scripts/generate-i18n.mjs --check: en-US and zh-CN complete
  • node scripts/verify-app-language-localization.mjs: passed
  • node scripts/test-i18n.mjs: 0 failures

Native check, on a local debug build of this head:

  • With a project open and nothing focused, Command-Shift-A opens Search Everywhere with / in the field, the caret after it, and the command list showing. Typing straight away filters commands, so the field has keyboard focus.
  • With Search Everywhere already open and a file query typed, Command-Shift-A replaces the query with / and keeps focus. Typing filters commands.
  • After switching to a second project in the same window, Command-Shift-A works the same way. After switching back to the first project, it still opens in command mode with focus.

Something I saw that this PR does not cause: Escape did not close the Search Everywhere popup in my run. An unpatched build of preview 80101d8 does the same thing, so it is not from this change. It may only happen with synthetic key events.

@mvanhorn
mvanhorn marked this pull request as ready for review October 25, 2026 15:54
@mvanhorn
mvanhorn requested review from 1lck and Mucheen as code owners October 25, 2026 15:54
@lithe-code-review

lithe-code-review Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Lithe Review

结论: ⚠️ 有建议
范围: preview ← fix/1177-macos-find-action-shortcut · head: 4e5fe613

Findings

  1. [P2] 搜索模块激活被项目切换取消后会留下无法恢复的可见状态
    macos/Sources/Lithe/Models/AppModel/AppModel+SearchModule.swift:36

    • 实际影响:切换项目期间激活返回 nil 时,isSearchEverywhereVisible 保持为 true,但搜索 capability 已被释放,弹窗可能消失或显示旧状态;之后再次按 ⌘⇧A 会因 wasVisible 为 true 跳过激活,Find Action 无法恢复,直到关闭项目或重置会话。
    • 触发条件:首次打开 Search Everywhere 时模块仍在激活,随后立即切换项目,使 activateSearchModule() 在模块运行时关闭过程中被取消并返回 nil。
    • 复现步骤:在项目 A 冷启动后立即打开 Search Everywhere,激活尚未完成时切换到项目 B;确认弹窗状态仍为 visible,再按 ⌘⇧A,调用会直接进入 guard !wasVisible 分支而不会重新激活搜索模块。
    • 来源:本次引入。base 在激活失败时无条件清除可见状态;当前改动只在 workspace 仍等于旧 root 时调用 dismissSearchEverywhere()。

Scope Check

  • 关联 Issue 或需求是否完成:[Bug] 项目大部分快捷键无效 #1177 的 Find Action(⌘⇧A)入口已实现;Issue 中其余快捷键仍明确不在本 PR 范围内。
  • 已完成范围:命令目录、macOS 默认与预设绑定、菜单入口、Search Everywhere / 命令模式、重新聚焦与命令模式查询清理、i18n、Agent Note 和平台能力记录均已更新。
  • 未完成范围:未实现 [Bug] 项目大部分快捷键无效 #1177 列出的其他快捷键;平台矩阵仍为 verificationStatus: pending。
  • 是否复用了已有上游能力,或对自研边界给出充分证据:复用现有 Search Everywhere、LitheActionRegistry 和统一 keymap 分发,没有新增搜索引擎或并行项目模型。
  • 每个 Finding 是否由本次变更引入或加剧:是。
  • 是否属于本次变更职责范围:是,问题位于新增的 Search Everywhere 激活与重入生命周期路径。

Suggestions

  1. 为打开请求绑定 workspace/session generation;激活失败或旧 workspace 失效时清理该请求的可见状态,并让后续 ⌘⇧A 能重新激活当前项目。补充“激活中切换项目/模块关闭”的回归测试。
  2. 该 PR 修改了菜单、弹窗和焦点行为,但提供的描述与评论中没有截图。请补充受影响界面的截图(新菜单项、首次打开及已打开弹窗重置查询后的状态);当前视觉效果未独立验证。
  3. head 检查列表明确记录 Verify Agent Notes 失败,而评论只提供了本地通过结果;转 Ready 前应确认该 head 上的检查已重新通过并提供失败原因或重跑结果。

Summary

命令注册、预设覆盖、菜单快捷键和本地化资源保持一致,/ 命令模式也正确复用了现有 Search Everywhere。未发现需要阻塞合并的问题;项目切换期间的激活取消路径仍需修正或补充保护。

Verification

  • 实际检查的文件、提交和调用链:检查 80101d86...4e5fe613 的全部 16 个变更文件;追踪 LitheCommandCatalog → WorkbenchModuleUIComposition/菜单 → AppModel.openFindAction() → SearchModuleCoordinator.activate() → SearchEverywhereView 焦点与查询状态,并读取了 base 对应实现和 keymap Agent Note。
  • 已提供的测试或 CI 结果:PR 上下文提供了 KeyboardShortcutTests、SearchEverywhereQueryModeTests、AppLocalizationTests 共 53 项通过;i18n 生成检查、应用语言检查和 i18n 测试通过;评论还提供了本地原生焦点与重入检查通过的结果。
  • 本次未运行的测试:本次远程只读审查未运行应用、Swift 测试、验证脚本或 CI。
  • 仍存在的非阻塞风险或平台验证缺口:缺少 UI 截图;项目切换与激活取消场景没有测试证据;Verify Agent Notes 的 head 检查状态与本地通过声明不一致。

Opening a project in the same window kept isSearchEverywhereVisible
from the previous project. If that project's search module activation
was still running, the runtime shutdown cancelled it and the stale task
no longer dismissed the popup, because the workspace had changed. The
flag stayed true, so Command-Shift-A in the new project skipped
activation and Find Action could not recover.

prepareForWorkspaceOpen now dismisses Search Everywhere, the way closing
a project already resets the search session. A test switches projects
before the first activation runs and checks that Find Action opens again
in the second project.
@mvanhorn

Copy link
Copy Markdown
Author

The P2 finding is real. When a project opens in the same window, prepareForWorkspaceOpen shuts down the module runtime but never cleared isSearchEverywhereVisible. The old project's activation task then got nil with a different workspaceURL, skipped the dismiss, and left the flag set. After that, Command-Shift-A in the new project took the wasVisible path and never activated. Closing a project didn't have this problem, because finishWorkspaceClose already calls searchSessionFeature.reset().

4b7dda2 makes prepareForWorkspaceOpen call dismissSearchEverywhere(), so the popup closes along with the project it belongs to. I kept the workspaceURL == root check in the activation task, so a stale task from the old project can't close a popup the new project just opened. SearchEverywhereQueryModeTests has a new test: it opens Find Action in project A, opens project B before the activation task runs, then checks the popup is closed and that Find Action opens again in B with /. Without the fix it fails at the "closed" check. With the fix, ./scripts/test-macos.sh --filter 'SearchEverywhereQueryModeTests|KeyboardShortcutTests|AppLocalizationTests' passes 54 tests in 6 suites.

On the Verify Agent Notes failure: verify-agent-notes on preview 80101d8 fails the same way. .agents/notes/implemented/feature/2026-10-22-html-preview-embedded-browser.md still lists MonacoDiffEditor.swift, which #1498 removed. This PR doesn't touch that note.

…ind-action-shortcut

# Conflicts:
#	.agents/notes/implemented/feature/2026-08-15-macos-keymap-customization.md
@mvanhorn

Copy link
Copy Markdown
Author

Merged the latest preview into this branch (new head e096b2f). The only conflict was in the keymap customization agent note, where preview moved the scripts into subfolders. I kept the new SearchEverywhereQueryModeTests.swift line and used the new ./scripts/agent-notes/verify-agent-notes.sh path.

Checks on the new head:

  • ./scripts/macos/test/test-macos.sh --filter 'SearchEverywhereQueryModeTests|KeyboardShortcutTests|AppLocalizationTests': 54 tests in 6 suites passed
  • node scripts/agent-notes/verify-agent-notes.mjs: 168 notes verified
  • node scripts/i18n/generate-i18n.mjs --check, verify-app-language-localization.mjs and test-i18n.mjs: passed

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review: no-score ghfind author score missing (not zero)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants