дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 19:25:54 +02:00
feat(app): an openidea shell command that opens files and folders in the running app (macOS)
The bundle ships Contents/Resources/bin/openidea, a script that runs the app executable in CLI mode. It sends its targets (--line/--column, or path:line:column) to the running app over a per-identifier Unix socket, or starts the app and waits for it. Install/Uninstall 'openidea' Command in the app menu links it onto PATH. Restoring a project's editor session no longer counts as the user picking a tab, so a file opened with the project keeps its caret and activation. Closes #1286 Co-Authored-By: Claude Opus 5.5 <[email protected]>
Цей коміт міститься в:
1 джерело
90cca59462
коміт
be0a6b4359
22 files changed
+916
-8
No files matched your search
@@ -952,6 +952,10 @@ export const commands = {
|
||||
*/
|
||||
menuSetDisabled: (ids: string[]) => __TAURI_INVOKE<void>("menu_set_disabled", { ids }),
|
||||
appLogLocation: () => typedError<LogLocation, IdeError>(__TAURI_INVOKE("app_log_location")),
|
||||
/** Links the `openidea` command onto `PATH` (macOS app bundle only); returns the link. */
|
||||
appCliInstall: () => typedError<IpcPath, IdeError>(__TAURI_INVOKE("app_cli_install")),
|
||||
/** Removes the installed `openidea` command; returns the links removed. */
|
||||
appCliUninstall: () => typedError<IpcPath[], IdeError>(__TAURI_INVOKE("app_cli_uninstall")),
|
||||
appBuildInfo: () => __TAURI_INVOKE<BuildInfo>("app_build_info"),
|
||||
appCheckUpdate: (source: UpdateSource) => typedError<UpdateCheck, IdeError>(__TAURI_INVOKE("app_check_update", { source })),
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { registerFileEditorProvider } from "@/workbench/contributions";
|
||||
import { EditorController, openFilePaths, useTabs } from "./controller";
|
||||
import { createMemoryBackend } from "./backend";
|
||||
import { storeSession } from "./model/session";
|
||||
import { initialTabsState } from "./model/tabs";
|
||||
import { useProject } from "@/platform/project";
|
||||
|
||||
@@ -63,6 +64,25 @@ function tabFor(path: string) {
|
||||
}
|
||||
|
||||
describe("file open order", () => {
|
||||
it("keeps the caret and activation of a file opened while the project's session restores", async () => {
|
||||
const before = setup();
|
||||
before.restoreSession("/p");
|
||||
await before.openFile("/p/b.ts", { focus: false });
|
||||
before.stop();
|
||||
useTabs.setState(initialTabsState(), true);
|
||||
try {
|
||||
const controller = setup();
|
||||
const open = controller.openFile("/p/a.ts", { selection: { line: 1, col: 2 }, focus: false });
|
||||
controller.restoreSession("/p");
|
||||
await open;
|
||||
expect(openFilePaths(useTabs.getState()).sort()).toEqual(["/p/a.ts", "/p/b.ts"]);
|
||||
expect(activePath()).toBe("/p/a.ts");
|
||||
expect(controller.slotFor(tabFor("/p/a.ts")!.id)?.caret()).toMatchObject({ line: 1, col: 2 });
|
||||
} finally {
|
||||
storeSession("/p", null);
|
||||
}
|
||||
});
|
||||
|
||||
it.each([1, 30])("keeps the newer file active after an earlier probe (tab limit: %s)", async (tabLimit) => {
|
||||
useProject.setState({ settings: { editor: { tabs: { tabLimit } } } as never });
|
||||
const { started, response } = delayProvider();
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/** Install / Uninstall the `openidea` shell command (macOS app bundle only). */
|
||||
import type { Action } from "@/platform/actions";
|
||||
import { t } from "@/platform/i18n";
|
||||
import { call, commands, isCancelled } from "@/platform/ipc";
|
||||
import { notifications } from "@/workbench/contributions";
|
||||
|
||||
const KEY = "app.cli";
|
||||
|
||||
function errorText(e: unknown) {
|
||||
return e instanceof Error ? e.message : String(e);
|
||||
}
|
||||
|
||||
async function installCli(): Promise<void> {
|
||||
try {
|
||||
const link = await call(commands.appCliInstall());
|
||||
notifications().notify({ level: "info", title: t("app.cli.installed"), body: link, key: KEY });
|
||||
} catch (e) {
|
||||
if (!isCancelled(e))
|
||||
notifications().notify({ level: "error", title: t("app.cli.installFailed"), body: errorText(e), key: KEY });
|
||||
}
|
||||
}
|
||||
|
||||
async function uninstallCli(): Promise<void> {
|
||||
try {
|
||||
const links = await call(commands.appCliUninstall());
|
||||
notifications().notify({
|
||||
level: "info",
|
||||
title: t(links.length ? "app.cli.uninstalled" : "app.cli.notInstalled"),
|
||||
body: links.join("\n") || undefined,
|
||||
key: KEY,
|
||||
});
|
||||
} catch (e) {
|
||||
if (!isCancelled(e))
|
||||
notifications().notify({ level: "error", title: t("app.cli.uninstallFailed"), body: errorText(e), key: KEY });
|
||||
}
|
||||
}
|
||||
|
||||
export function cliActions(): Action[] {
|
||||
return [
|
||||
{
|
||||
id: "app.installCli",
|
||||
title: t("app.action.installCli"),
|
||||
category: t("app.category.tools"),
|
||||
keywords: ["cli", "shell", "terminal", "path", "command line", "launcher"],
|
||||
run: installCli,
|
||||
},
|
||||
{
|
||||
id: "app.uninstallCli",
|
||||
title: t("app.action.uninstallCli"),
|
||||
category: t("app.category.tools"),
|
||||
keywords: ["cli", "shell", "terminal", "path", "command line", "launcher"],
|
||||
run: uninstallCli,
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -6,10 +6,11 @@ import { registerOverlay, registerStatusItem } from "@/workbench/contributions";
|
||||
import { AboutOverlay } from "./AboutDialog";
|
||||
import { appActions, updateActions } from "./appActions";
|
||||
import { checkForUpdatesOnStartup } from "./updates";
|
||||
import { cliActions } from "./cliCommand";
|
||||
import { installCloseGuard } from "./closeGuard";
|
||||
import { TerminationOverlay } from "./termination";
|
||||
import { InvalidateCachesOverlay, registerInvalidateCachesActions } from "./InvalidateCaches";
|
||||
import { buildInfo } from "./host";
|
||||
import { buildInfo, guessOs } from "./host";
|
||||
import { KeymapReferenceOverlay } from "./KeymapReference";
|
||||
import { LightEditStatus } from "./LightEditStatus";
|
||||
import { installLocaleSync, registerLocalizedActions } from "./locale";
|
||||
@@ -31,6 +32,7 @@ export function activate(): () => void {
|
||||
const disposers = [
|
||||
installLocaleSync(),
|
||||
registerLocalizedActions(() => [...appActions(), ...windowActions()]),
|
||||
registerLocalizedActions(() => (guessOs() === "macos" ? cliActions() : [])),
|
||||
registerOverlay({ id: "app.about", component: AboutOverlay }),
|
||||
registerOverlay({ id: "app.keymapReference", component: KeymapReferenceOverlay }),
|
||||
registerOverlay({ id: "app.invalidateCaches", component: InvalidateCachesOverlay }),
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"app.action.checkForUpdates": "Check for Updates…",
|
||||
"app.action.copy": "Copy",
|
||||
"app.action.cut": "Cut",
|
||||
"app.action.installCli": "Install 'openidea' Command",
|
||||
"app.action.keymapReference": "Keymap Reference",
|
||||
"app.action.lightEditOpenProject": "Open Containing Folder as Project",
|
||||
"app.action.newWindow": "New Window",
|
||||
@@ -32,11 +33,18 @@
|
||||
"app.action.showLog": "{os, select, macos {Show Log in Finder} windows {Show Log in Explorer} other {Show Log in Files}}",
|
||||
"app.action.toggleFullScreen": "Toggle Full Screen",
|
||||
"app.action.undo": "Undo",
|
||||
"app.action.uninstallCli": "Uninstall 'openidea' Command",
|
||||
"app.category.edit": "Edit",
|
||||
"app.category.file": "File",
|
||||
"app.category.help": "Help",
|
||||
"app.category.tools": "Tools",
|
||||
"app.category.view": "View",
|
||||
"app.category.window": "Window",
|
||||
"app.cli.installFailed": "Cannot install the openidea command",
|
||||
"app.cli.installed": "The openidea command is installed",
|
||||
"app.cli.notInstalled": "The openidea command is not installed",
|
||||
"app.cli.uninstallFailed": "Cannot uninstall the openidea command",
|
||||
"app.cli.uninstalled": "The openidea command is uninstalled",
|
||||
"app.exit.body": "Are you sure you want to exit OpenIDEA?",
|
||||
"app.exit.confirm": "Exit",
|
||||
"app.exit.dontAsk": "Don't ask again",
|
||||
@@ -100,7 +108,9 @@
|
||||
"menu.app": "OpenIDEA",
|
||||
"menu.app.about": "About OpenIDEA",
|
||||
"menu.app.checkForUpdates": "Check for Updates…",
|
||||
"menu.app.installCli": "Install 'openidea' Command",
|
||||
"menu.app.quit": "{os, select, macos {Quit OpenIDEA} other {Exit}}",
|
||||
"menu.app.uninstallCli": "Uninstall 'openidea' Command",
|
||||
"menu.appearance": "Appearance",
|
||||
"menu.code": "Code",
|
||||
"menu.code.commentBlock": "Comment with Block Comment",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"app.action.checkForUpdates": "检查更新…",
|
||||
"app.action.copy": "复制",
|
||||
"app.action.cut": "剪切",
|
||||
"app.action.installCli": "安装 'openidea' 命令",
|
||||
"app.action.keymapReference": "快捷键参考",
|
||||
"app.action.lightEditOpenProject": "将所在文件夹作为项目打开",
|
||||
"app.action.newWindow": "新建窗口",
|
||||
@@ -32,11 +33,18 @@
|
||||
"app.action.showLog": "{os, select, macos {在访达中显示日志} windows {在资源管理器中显示日志} other {在文件管理器中显示日志}}",
|
||||
"app.action.toggleFullScreen": "切换全屏",
|
||||
"app.action.undo": "撤销",
|
||||
"app.action.uninstallCli": "卸载 'openidea' 命令",
|
||||
"app.category.edit": "编辑",
|
||||
"app.category.file": "文件",
|
||||
"app.category.help": "帮助",
|
||||
"app.category.tools": "工具",
|
||||
"app.category.view": "视图",
|
||||
"app.category.window": "窗口",
|
||||
"app.cli.installFailed": "无法安装 openidea 命令",
|
||||
"app.cli.installed": "openidea 命令已安装",
|
||||
"app.cli.notInstalled": "openidea 命令未安装",
|
||||
"app.cli.uninstallFailed": "无法卸载 openidea 命令",
|
||||
"app.cli.uninstalled": "openidea 命令已卸载",
|
||||
"app.exit.body": "确定要退出 OpenIDEA 吗?",
|
||||
"app.exit.confirm": "退出",
|
||||
"app.exit.dontAsk": "不再询问",
|
||||
@@ -100,7 +108,9 @@
|
||||
"menu.app": "OpenIDEA",
|
||||
"menu.app.about": "关于 OpenIDEA",
|
||||
"menu.app.checkForUpdates": "检查更新…",
|
||||
"menu.app.installCli": "安装 'openidea' 命令",
|
||||
"menu.app.quit": "{os, select, macos {退出 OpenIDEA} other {退出}}",
|
||||
"menu.app.uninstallCli": "卸载 'openidea' 命令",
|
||||
"menu.appearance": "外观",
|
||||
"menu.code": "代码",
|
||||
"menu.code.commentBlock": "使用块注释",
|
||||
|
||||
Reference in new issue
Заблокувати користувача