- Introduced WSL editor launch modes in SettingsPage with options for internal, remote, and UNC path launches. - Enhanced settings saving to include the new WSL launch mode. - Added an update section to check for application updates, download, and install them with progress tracking. - Updated Toast component to handle error messages with a copy button for convenience. - Expanded commands to include project tags and templates management, along with WSL proxy synchronization features.
162 lines
6.0 KiB
Rust
162 lines
6.0 KiB
Rust
use crate::db::pool;
|
||
use rusqlite::params;
|
||
use std::path::Path;
|
||
use std::process::Command;
|
||
|
||
/// 将 UNC WSL 路径转换为 Linux 路径
|
||
/// \\wsl.localhost\Ubuntu\home\user\proj → /home/user/proj
|
||
/// \\wsl$\Ubuntu\home\user\proj → /home/user/proj
|
||
/// /home/user/proj → /home/user/proj(直接返回)
|
||
fn unc_to_linux(path: &str) -> String {
|
||
for prefix in &["\\\\wsl.localhost\\", "\\\\wsl$\\"] {
|
||
if path.to_lowercase().starts_with(&prefix.to_lowercase()) {
|
||
let rest = &path[prefix.len()..];
|
||
// 去掉 distro 名(第一段),剩余部分转 /
|
||
let after_distro = rest.splitn(2, '\\').nth(1).unwrap_or("");
|
||
return format!("/{}", after_distro.replace('\\', "/"));
|
||
}
|
||
}
|
||
// 已经是 Linux 路径,直接返回
|
||
path.to_string()
|
||
}
|
||
|
||
fn get_setting(key: &str) -> Option<String> {
|
||
let conn = pool().get().ok()?;
|
||
// 先查全局 settings 表
|
||
if let Ok(val) = conn.query_row(
|
||
"SELECT value FROM settings WHERE key = ?1",
|
||
params![key],
|
||
|row| row.get::<_, String>(0),
|
||
) {
|
||
if !val.is_empty() {
|
||
return Some(val);
|
||
}
|
||
}
|
||
// 兼容旧数据:回退查 space_settings(任意空间取第一条)
|
||
conn.query_row(
|
||
"SELECT value FROM space_settings WHERE key = ?1 LIMIT 1",
|
||
params![key],
|
||
|row| row.get::<_, String>(0),
|
||
).ok()
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub fn open_editor(project_id: String, env: String, editor_path: String) -> Result<String, String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
let (wsl_path, win_path): (Option<String>, Option<String>) = conn
|
||
.query_row(
|
||
"SELECT wsl_path, win_path FROM project_workspaces WHERE id = ?1",
|
||
params![project_id],
|
||
|row| Ok((row.get(0)?, row.get(1)?)),
|
||
)
|
||
.map_err(|_| format!("Project not found: {project_id}"))?;
|
||
|
||
let wsl_distro = get_setting("wsl_distro").unwrap_or_else(|| "Ubuntu".into());
|
||
|
||
// WSL 环境:按 wsl_open_mode 配置自适应启动
|
||
// wsl_internal (默认): wsl -d <distro> -- <editor> <linux_path>
|
||
// remote_flag : <editor> --remote wsl+<distro> <linux_path>
|
||
// unc_path : <editor> <unc_path>(直接用 UNC 路径,不走 Remote)
|
||
if env == "wsl" {
|
||
let path = wsl_path.ok_or("WSL path not configured")?;
|
||
let open_mode = get_setting("wsl_open_mode")
|
||
.unwrap_or_else(|| "wsl_internal".into());
|
||
|
||
if open_mode == "wsl_internal" {
|
||
// 直接调用 wsl 进程,用参数数组传路径,避免 cmd /c 的引号转义问题
|
||
let linux_path = unc_to_linux(&path);
|
||
let cmd_name = std::path::Path::new(&editor_path)
|
||
.file_stem()
|
||
.and_then(|s| s.to_str())
|
||
.map(|s| s.to_lowercase())
|
||
.unwrap_or_else(|| editor_path.clone());
|
||
Command::new("wsl")
|
||
.args(["-d", &wsl_distro, "--", &cmd_name, &linux_path])
|
||
.spawn()
|
||
.map_err(|e| format!("无法启动编辑器: {e}"))?;
|
||
return Ok(format!("wsl -d {} -- {} {}", wsl_distro, cmd_name, linux_path));
|
||
}
|
||
|
||
let cmd_str = match open_mode.as_str() {
|
||
"remote_flag" => {
|
||
let linux_path = unc_to_linux(&path);
|
||
format!("\"{}\" --remote wsl+{} \"{}\"", editor_path, wsl_distro, linux_path)
|
||
}
|
||
_ => {
|
||
// unc_path:直接用原始路径,不走 Remote
|
||
format!("\"{}\" \"{}\"", editor_path, path)
|
||
}
|
||
};
|
||
|
||
Command::new("cmd")
|
||
.args(["/c", &cmd_str])
|
||
.spawn()
|
||
.map_err(|e| format!("无法启动编辑器: {e}"))?;
|
||
return Ok(cmd_str);
|
||
}
|
||
|
||
let args: Vec<String> = match env.as_str() {
|
||
"win" => {
|
||
let path = win_path.ok_or("Windows path not configured")?;
|
||
vec![path]
|
||
}
|
||
_ => return Err(format!("Unknown env: {env}")),
|
||
};
|
||
|
||
// Windows 上编辑器可能是 .cmd 脚本,通过 cmd /c 让 shell 解析
|
||
#[cfg(target_os = "windows")]
|
||
let spawn_result = Command::new("cmd")
|
||
.args(["/c", &editor_path])
|
||
.args(&args)
|
||
.spawn();
|
||
|
||
#[cfg(not(target_os = "windows"))]
|
||
let spawn_result = Command::new(&editor_path).args(&args).spawn();
|
||
|
||
spawn_result.map_err(|e| {
|
||
format!("无法启动编辑器 \"{editor_path}\": {e}\n请在设置中检查编辑器路径是否正确")
|
||
})?;
|
||
|
||
Ok(format!("{editor_path} {}", args.join(" ")))
|
||
}
|
||
|
||
#[tauri::command]
|
||
pub fn open_terminal(project_id: String, env: String) -> Result<String, String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
let (wsl_path, win_path): (Option<String>, Option<String>) = conn
|
||
.query_row(
|
||
"SELECT wsl_path, win_path FROM project_workspaces WHERE id = ?1",
|
||
params![project_id],
|
||
|row| Ok((row.get(0)?, row.get(1)?)),
|
||
)
|
||
.map_err(|_| format!("Project not found: {project_id}"))?;
|
||
|
||
let wsl_distro = get_setting("wsl_distro").unwrap_or_else(|| "Ubuntu".into());
|
||
|
||
let args: Vec<String> = match env.as_str() {
|
||
"wsl" => {
|
||
let path = wsl_path.ok_or("WSL path not configured")?;
|
||
vec!["-p".into(), wsl_distro, "--".into(), "wsl".into(), "--cd".into(), path]
|
||
}
|
||
"win" => {
|
||
let path = win_path.ok_or("Windows path not configured")?;
|
||
if Path::new(&path).exists() {
|
||
vec!["-p".into(), "PowerShell".into(), "-d".into(), path]
|
||
} else {
|
||
vec!["-p".into(), "PowerShell".into()]
|
||
}
|
||
}
|
||
_ => return Err(format!("Unknown env: {env}")),
|
||
};
|
||
|
||
#[cfg(target_os = "windows")]
|
||
let term_result = Command::new("cmd").args(["/c", "wt"]).args(&args).spawn();
|
||
|
||
#[cfg(not(target_os = "windows"))]
|
||
let term_result = Command::new("wt").args(&args).spawn();
|
||
|
||
term_result.map_err(|e| format!("无法启动终端: {e}"))?;
|
||
|
||
Ok(format!("wt {}", args.join(" ")))
|
||
}
|