Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ tauri-build = { version = "2", features = [] }
chrono = { version = "0.4.41", features = ["serde"] }

[dependencies]
tauri = { version = "2.9", features = ["devtools"] }
tauri = { version = "2.9", features = ["devtools", "protocol-asset"] }
tauri-plugin-opener = "2.5"
tauri-plugin-shell = "2.0"
tauri-plugin-dialog = "2.4"
Expand Down
8 changes: 8 additions & 0 deletions src-tauri/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub struct AppConfig {
pub keep_log_days: Option<u32>,
pub theme: Option<String>,
pub plugins: Option<Vec<PluginConfig>>,
pub custom_plugins: Option<Vec<PluginConfig>>,
pub editor: Option<EditorConfig>,
pub environment_mirror: Option<EnvironmentMirrorConfig>,
pub github: Option<GithubConfig>,
Expand All @@ -54,6 +55,7 @@ impl Default for AppConfig {
keep_log_days: Some(30),
theme: Some("system".to_string()),
plugins: Some(vec![]),
custom_plugins: Some(vec![]),
editor: Some(EditorConfig {
indent_with_tab: Some(true),
tab_size: Some(2),
Expand Down Expand Up @@ -238,6 +240,7 @@ impl ConfigManager {
keep_log_days: Some(30),
theme: Some("system".to_string()),
plugins: Self::get_default_plugins_config(app_handle),
custom_plugins: Some(vec![]),
editor: Some(EditorConfig {
indent_with_tab: Some(true),
tab_size: Some(2),
Expand Down Expand Up @@ -279,6 +282,11 @@ impl ConfigManager {
self.config.log_directory = path;
self.save_config()
}

pub fn update_config(&mut self, new_config: AppConfig) -> Result<(), String> {
self.config = new_config;
self.save_config()
}
}

// 初始化配置
Expand Down
142 changes: 142 additions & 0 deletions src-tauri/src/custom_plugin_commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use crate::config::{get_app_config_internal, get_config_manager};
use crate::execution::PluginManagerState;
use crate::plugins::PluginConfig;
use crate::plugins::custom::CustomPlugin;
use log::info;
use std::fs;
use tauri::{AppHandle, Emitter, State, command};

#[command]
pub async fn add_custom_plugin(
config: PluginConfig,
app_handle: AppHandle,
plugin_manager: State<'_, PluginManagerState>,
) -> Result<(), String> {
{
let mut guard = get_config_manager()?;
if let Some(config_manager) = guard.as_mut() {
let mut app_config = config_manager.get_config().clone();

let mut custom_plugins = app_config.custom_plugins.unwrap_or_default();

if custom_plugins.iter().any(|p| p.language == config.language) {
return Err(format!("自定义插件 {} 已存在", config.language));
}

custom_plugins.push(config.clone());
app_config.custom_plugins = Some(custom_plugins);

config_manager.update_config(app_config)?;
} else {
return Err("配置管理器未初始化".to_string());
}
}

let mut manager = plugin_manager.lock().await;
let custom_plugin = CustomPlugin::new(config.clone());
manager.register_plugin(config.language.clone(), Box::new(custom_plugin));

app_handle.emit("config-updated", ()).ok();
info!("自定义插件 {} 已添加", config.language);

Ok(())
}

#[command]
pub async fn update_custom_plugin(
config: PluginConfig,
app_handle: AppHandle,
plugin_manager: State<'_, PluginManagerState>,
) -> Result<(), String> {
{
let mut guard = get_config_manager()?;
if let Some(config_manager) = guard.as_mut() {
let mut app_config = config_manager.get_config().clone();

let mut custom_plugins = app_config.custom_plugins.unwrap_or_default();

if let Some(index) = custom_plugins
.iter()
.position(|p| p.language == config.language)
{
custom_plugins[index] = config.clone();
} else {
return Err(format!("自定义插件 {} 不存在", config.language));
}

app_config.custom_plugins = Some(custom_plugins);

config_manager.update_config(app_config)?;
} else {
return Err("配置管理器未初始化".to_string());
}
}

let mut manager = plugin_manager.lock().await;
manager.unregister_plugin(&config.language);
let custom_plugin = CustomPlugin::new(config.clone());
manager.register_plugin(config.language.clone(), Box::new(custom_plugin));

app_handle.emit("config-updated", ()).ok();
info!("自定义插件 {} 已更新", config.language);

Ok(())
}

#[command]
pub async fn remove_custom_plugin(
language: String,
app_handle: AppHandle,
plugin_manager: State<'_, PluginManagerState>,
) -> Result<(), String> {
{
let mut guard = get_config_manager()?;
if let Some(config_manager) = guard.as_mut() {
let mut app_config = config_manager.get_config().clone();

let mut custom_plugins = app_config.custom_plugins.unwrap_or_default();

custom_plugins.retain(|p| p.language != language);

app_config.custom_plugins = Some(custom_plugins);

config_manager.update_config(app_config)?;
} else {
return Err("配置管理器未初始化".to_string());
}
}

let mut manager = plugin_manager.lock().await;
manager.unregister_plugin(&language);

app_handle.emit("config-updated", ()).ok();
info!("自定义插件 {} 已移除", language);

Ok(())
}

#[command]
pub async fn get_custom_plugins() -> Result<Vec<PluginConfig>, String> {
let app_config = get_app_config_internal()?;
Ok(app_config.custom_plugins.unwrap_or_default())
}

#[command]
pub async fn save_custom_icon(
language: String,
icon_data: Vec<u8>,
file_extension: String,
) -> Result<String, String> {
let home_dir = dirs::home_dir().ok_or("无法获取用户主目录")?;
let icons_dir = home_dir.join(".codeforge").join("custom-icons");

fs::create_dir_all(&icons_dir).map_err(|e| format!("创建图标目录失败: {}", e))?;

let icon_path = icons_dir.join(format!("{}.{}", language, file_extension));

fs::write(&icon_path, icon_data).map_err(|e| format!("保存图标文件失败: {}", e))?;

info!("自定义图标已保存: {:?}", icon_path);

Ok(icon_path.to_string_lossy().to_string())
}
26 changes: 25 additions & 1 deletion src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

mod cache;
mod config;
mod custom_plugin_commands;
mod env_commands;
mod env_manager;
mod env_providers;
Expand All @@ -19,6 +20,10 @@ mod update;
mod utils;

use crate::cache::{clear_all_cache, clear_plugins_cache, get_cache_info};
use crate::custom_plugin_commands::{
add_custom_plugin, get_custom_plugins, remove_custom_plugin, save_custom_icon,
update_custom_plugin,
};
use crate::env_commands::{
EnvironmentManagerState, download_and_install_version, get_environment_info,
get_supported_environment_languages, switch_environment_version, uninstall_environment_version,
Expand Down Expand Up @@ -70,7 +75,20 @@ fn main() {
eprintln!("Failed to initialize config: {}", e);
}

// 第二步:初始化日志系统
// 第二步:加载自定义插件
use tauri::Manager;
if let Some(plugin_manager_state) = app.try_state::<ExecutionPluginManagerState>() {
if let Ok(mut manager) = plugin_manager_state.try_lock() {
if let Ok(app_config) = config::get_app_config_internal() {
if let Some(custom_plugins) = app_config.custom_plugins {
manager.load_custom_plugins(custom_plugins);
info!("初始化 -> 已加载自定义插件");
}
}
}
}

// 第三步:初始化日志系统
if let Err(e) = logger::setup_logger(app.handle()) {
eprintln!("Failed to setup logger: {}", e);
}
Expand Down Expand Up @@ -115,6 +133,12 @@ fn main() {
get_app_config,
update_app_config,
get_config_path,
// 自定义插件相关命令
add_custom_plugin,
update_custom_plugin,
remove_custom_plugin,
get_custom_plugins,
save_custom_icon,
// 缓存相关命令
get_cache_info,
clear_plugins_cache,
Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/plugins/applescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ impl LanguagePlugin for AppleScriptPlugin {
template: Some(String::from("-- 在这里输入 AppleScript 代码")),
timeout: Some(45),
console_type: Some(String::from("console")),
icon_path: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/plugins/c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl LanguagePlugin for CPlugin {
template: Some(String::from("// 在这里输入 C 代码")),
timeout: Some(30),
console_type: Some(String::from("console")),
icon_path: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/plugins/cangjie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl LanguagePlugin for CangjiePlugin {
)),
timeout: Some(30),
console_type: Some(String::from("console")),
icon_path: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/plugins/clojure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ impl LanguagePlugin for ClojurePlugin {
template: Some(String::from(";; 在这里输入 Clojure 代码")),
timeout: Some(45),
console_type: Some(String::from("console")),
icon_path: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/plugins/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl LanguagePlugin for CppPlugin {
template: Some(String::from("// 在这里输入 C++ 代码")),
timeout: Some(30),
console_type: Some(String::from("console")),
icon_path: None,
}
}

Expand Down
1 change: 1 addition & 0 deletions src-tauri/src/plugins/css.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ impl LanguagePlugin for CssPlugin {
template: Some(String::from("// 在这里输入 CSS 代码")),
timeout: Some(30),
console_type: Some(String::from("web")),
icon_path: None,
}
}

Expand Down
77 changes: 77 additions & 0 deletions src-tauri/src/plugins/custom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use super::{LanguagePlugin, PluginConfig};

pub struct CustomPlugin {
config: PluginConfig,
}

impl CustomPlugin {
pub fn new(config: PluginConfig) -> Self {
Self { config }
}
}

impl LanguagePlugin for CustomPlugin {
fn get_language_name(&self) -> &'static str {
Box::leak(self.config.language.clone().into_boxed_str())
}

fn get_language_key(&self) -> &'static str {
Box::leak(self.config.language.clone().into_boxed_str())
}

fn get_file_extension(&self) -> String {
self.config.extension.clone()
}

fn get_version_args(&self) -> Vec<&'static str> {
vec!["--version"]
}

fn get_path_command(&self) -> String {
// 从 run_command 中提取第一个命令(如 "bash $filename" -> "bash")
self.config
.run_command
.as_ref()
.and_then(|cmd| cmd.split_whitespace().next())
.map(|s| format!("which {}", s))
.unwrap_or_else(|| "echo unknown".to_string())
}

fn get_command(
&self,
file_path: Option<&str>,
is_version: bool,
_file_name: Option<String>,
) -> String {
// 当获取版本信息时或没有file_path时,返回 run_command 的第一个命令
if is_version || file_path.is_none() {
return self
.config
.run_command
.as_ref()
.and_then(|cmd| cmd.split_whitespace().next())
.map(|s| s.to_string())
.unwrap_or_else(|| self.config.language.clone());
}

// 执行代码时,返回完整的命令
if let Some(run_cmd) = &self.config.run_command {
if let Some(path) = file_path {
return run_cmd.replace("$filename", path);
}
}

self.get_default_command()
}

fn get_default_config(&self) -> PluginConfig {
self.config.clone()
}

fn get_default_command(&self) -> String {
self.config
.run_command
.clone()
.unwrap_or_else(|| self.config.language.clone())
}
}
1 change: 1 addition & 0 deletions src-tauri/src/plugins/go.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ impl LanguagePlugin for GoPlugin {
template: Some(String::from("// 在这里输入 Go 代码")),
timeout: Some(30),
console_type: Some(String::from("console")),
icon_path: None,
}
}

Expand Down
Loading
Loading