mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 00:07:50 +00:00
* perf(vfs): list directories off the main thread and reuse the repository lookup Opening a folder on a network share (\\wsl.localhost\<distro>\...) is slow because every listing ran on the main thread and walked up to the repository root and ran a repository discovery again. - fs_read_dir runs on the blocking pool. - GitignoreStack::cached keeps the repository root and the repo-level rules for a few seconds. An edit of .git/info/exclude is still seen at once (its mtime and size are checked) and every .gitignore / .ignore is read fresh. Refs #916 * feat(remote): open WSL projects through a WSL transport A folder of a WSL distro (\\wsl.localhost\<distro>\... or \\wsl$\...) now opens as a remote project whose openidea-server runs inside the distro, so the terminal, SDK discovery, run, language servers and git are native Linux. - RemoteTarget (ssh | wsl) replaces the SSH configuration id in the remote launch, connection, session and recents; old recents are read as SSH and a WSL target never inherits the legacy trust migration. - remote_wsl: probe the distro, stream the server into ~/.openidea/server and start it with `wsl.exe -d <distro> --exec <bin> --stdio`. Distro names are validated and every command goes through --exec with positional arguments. - wsl_distros / wsl_resolve commands; File > Open, the CLI and recents route WSL folders to the distro and focus the window that already has them. - Remote Development dialog: SSH/WSL choice with the detected distros; the status bar shows `WSL: <distro>` / `SSH: <host>`. - ide-jsonrpc: skip the NUL left at the start of a line by UTF-16 output of wsl.exe, which made the first message of the server disappear. Not run on Windows yet: only a stand-in for wsl.exe is tested. Refs #925, #327, #721 * fix(remote): satisfy clippy in WSL output decoding * style(run): format run types * fix: regenerate bindings and drop untranslated WSL catalog entry * test(i18n): list WSL as an untranslated product name * fix(run): pick build wrappers and shell quoting by the execution host A remote window runs commands on its Linux or macOS server, so a Windows desktop no longer selects gradlew.bat, mvnw.cmd or Windows quoting for it. * fix(welcome): open legacy WSL UNC recents and sessions through the distro Recents and the restored session called the local openProject for a \\wsl.localhost folder saved before WSL projects, which fails with OS error 123. They now resolve it like the other entry points. * fix(windows): keep WSL files out of remote project launches path_launch tested the WSL share before is_dir, so a first-instance CLI launch with a WSL file opened a remote project instead of LightEdit. * fix(remote): run-command OS comes from the remote host, not an assumption The server's OS was dropped after connect, so a restored session had none and isWindows() assumed every remote host is Unix. Keep it on the connection and the session, and resolve it through hostOs(). --------- Co-authored-by: NguyenBang <nguyenbang@NTBANG.local> Co-authored-by: NguyenBang <nguyenbang@Mac.lan> Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
90 lines
3.0 KiB
TypeScript
90 lines
3.0 KiB
TypeScript
/** Remote ports forwarded to this machine, remembered per host and folder and forwarded again on reconnect. */
|
|
import { create } from "zustand";
|
|
import type { ForwardedPort } from "@/bindings";
|
|
import { tx } from "@/platform/i18n";
|
|
import { call, commands } from "@/platform/ipc";
|
|
import { useRemote } from "@/platform/remote";
|
|
import { notifications } from "@/workbench/contributions";
|
|
|
|
const SAVED_KEY = "openidea.remote.ports:";
|
|
|
|
function savedKey(): string | null {
|
|
const session = useRemote.getState().session;
|
|
return session?.target.kind === "ssh" ? `${SAVED_KEY}${session.target.config}:${session.root}` : null;
|
|
}
|
|
|
|
export function savedPorts(): number[] {
|
|
const key = savedKey();
|
|
if (!key) return [];
|
|
try {
|
|
const raw = JSON.parse(localStorage.getItem(key) ?? "[]") as unknown;
|
|
return Array.isArray(raw) ? raw.filter((p): p is number => Number.isInteger(p) && p > 0 && p < 65536) : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function save(ports: readonly ForwardedPort[]) {
|
|
const key = savedKey();
|
|
if (!key) return;
|
|
try {
|
|
if (ports.length) localStorage.setItem(key, JSON.stringify(ports.map((p) => p.remotePort)));
|
|
else localStorage.removeItem(key);
|
|
} catch {
|
|
/* storage unavailable */
|
|
}
|
|
}
|
|
|
|
export const usePorts = create<{ ports: ForwardedPort[] }>(() => ({ ports: [] }));
|
|
|
|
/** Reads the forwarded ports; `remember` keeps them for the next connection (after the user changed them). */
|
|
export async function refreshPorts(remember = false): Promise<void> {
|
|
const ports = await commands.remoteForwardedPorts().catch(() => []);
|
|
usePorts.setState({ ports });
|
|
if (remember) save(ports);
|
|
}
|
|
|
|
/** A port number typed by the user, or null. */
|
|
export function parsePort(text: string): number | null {
|
|
const t = text.trim();
|
|
if (!/^\d+$/.test(t)) return null;
|
|
const n = Number(t);
|
|
return n >= 1 && n <= 65535 ? n : null;
|
|
}
|
|
|
|
export async function forwardPort(port: number): Promise<ForwardedPort | null> {
|
|
if (useRemote.getState().session?.target.kind === "wsl") {
|
|
notifications().notify({
|
|
group: "remote",
|
|
level: "error",
|
|
title: tx("Forwarding ports is not supported in a WSL project yet"),
|
|
});
|
|
return null;
|
|
}
|
|
try {
|
|
const forwarded = await call(commands.remoteForwardPort(port));
|
|
await refreshPorts(true);
|
|
return forwarded;
|
|
} catch (e) {
|
|
notifications().notify({
|
|
group: "remote",
|
|
level: "error",
|
|
title: tx`Could not forward port ${port}`,
|
|
body: e instanceof Error ? e.message : String(e),
|
|
});
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function stopForwarding(port: number): Promise<void> {
|
|
await call(commands.remoteStopForwarding(port)).catch(() => {});
|
|
await refreshPorts(true);
|
|
}
|
|
|
|
/** After (re)connecting: forwards the ports this host and folder had forwarded before. */
|
|
export async function restorePorts(): Promise<void> {
|
|
const saved = savedPorts();
|
|
for (const port of saved) await call(commands.remoteForwardPort(port)).catch(() => null);
|
|
usePorts.setState({ ports: await commands.remoteForwardedPorts().catch(() => []) });
|
|
}
|