diff --git a/README.md b/README.md
index c7a6368..391a3f2 100644
--- a/README.md
+++ b/README.md
@@ -30,6 +30,7 @@ CodeForge 是一款轻量级、高性能的桌面代码执行器,专为开发
+
diff --git a/package.json b/package.json
index c888947..bcd126a 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "codeforge",
"private": true,
- "version": "25.0.3",
+ "version": "25.0.4",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/public/icons/haskell.svg b/public/icons/haskell.svg
new file mode 100644
index 0000000..af16d5f
--- /dev/null
+++ b/public/icons/haskell.svg
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 3c2974f..42589f1 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -4,7 +4,7 @@ version = 4
[[package]]
name = "CodeForge"
-version = "25.0.3"
+version = "25.0.4"
dependencies = [
"chrono",
"dirs",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 1659971..6d65b0b 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "CodeForge"
-version = "25.0.3"
+version = "25.0.4"
description = "CodeForge 是一款轻量级、高性能的桌面代码执行器,专为开发者、学生和编程爱好者设计。"
authors = ["devlive-community"]
edition = "2024"
diff --git a/src-tauri/src/examples/haskell.hs b/src-tauri/src/examples/haskell.hs
new file mode 100644
index 0000000..c8b7914
--- /dev/null
+++ b/src-tauri/src/examples/haskell.hs
@@ -0,0 +1,80 @@
+-- Haskell示例代码 - CodeForge 代码执行环境
+
+import Data.List (intercalate)
+
+main :: IO ()
+main = do
+ putStrLn "🎉 欢迎使用 CodeForge!"
+ putStrLn "Welcome to CodeForge!"
+ putStrLn ""
+
+ putStrLn "========================================="
+ putStrLn " CodeForge Haskell "
+ putStrLn "========================================="
+ putStrLn ""
+
+ -- 基本输出示例
+ putStrLn "✅ Haskell运行成功! (Haskell is working!)"
+ putStrLn "⚡ 这是Haskell程序 (This is Haskell program)"
+ putStrLn ""
+
+ -- 变量操作
+ let name = "CodeForge"
+ version = "Haskell"
+ number1 = 10
+ number2 = 20
+ result = number1 + number2
+
+ putStrLn "🔢 简单计算 (Simple calculation):"
+ putStrLn $ show number1 ++ " + " ++ show number2 ++ " = " ++ show result
+ putStrLn ""
+
+ -- 字符串操作
+ putStrLn "📝 字符串操作 (String operations):"
+ putStrLn $ "平台名称 (Platform): " ++ name
+ putStrLn $ "语言版本 (Language): " ++ version
+ putStrLn $ "完整信息 (Full info): " ++ name ++ " - " ++ version
+ putStrLn ""
+
+ -- 列表操作
+ putStrLn "🍎 列表示例 (List example):"
+ let fruits = ["苹果", "香蕉", "橙子", "葡萄"]
+ mapM_ (\(i, fruit) -> putStrLn $ show (i + 1) ++ ". " ++ fruit) (zip [0..] fruits)
+ putStrLn ""
+
+ -- 条件判断
+ let score = 85
+ putStrLn "📊 成绩评估 (Score evaluation):"
+ putStrLn $ evaluateScore score
+ putStrLn ""
+
+ -- 循环示例
+ putStrLn "🔄 循环输出 (Loop output):"
+ mapM_ (\i -> putStrLn $ "第 " ++ show i ++ " 次输出 (Output #" ++ show i ++ "): Hello from CodeForge!") [1..5]
+ putStrLn ""
+
+ -- 递归示例 (模拟while循环)
+ putStrLn "🔁 递归示例 (Recursion example):"
+ countDown 3
+ putStrLn ""
+
+ putStrLn "🎯 CodeForge Haskell代码执行完成!"
+ putStrLn "🎯 CodeForge Haskell execution completed!"
+ putStrLn ""
+ putStrLn "感谢使用 CodeForge 代码执行环境! 🚀"
+ putStrLn "Thank you for using CodeForge! 🚀"
+
+-- 评估分数的函数
+evaluateScore :: Int -> String
+evaluateScore score
+ | score >= 90 = "优秀! (Excellent!)"
+ | score >= 80 = "良好! (Good!)"
+ | score >= 60 = "及格 (Pass)"
+ | otherwise = "需要努力 (Need improvement)"
+
+-- 递归倒计数函数 (模拟while循环)
+countDown :: Int -> IO ()
+countDown 0 = return ()
+countDown n = do
+ putStrLn $ "递归循环: 第 " ++ show (4 - n) ++ " 次"
+ countDown (n - 1)
\ No newline at end of file
diff --git a/src-tauri/src/plugins/haskell.rs b/src-tauri/src/plugins/haskell.rs
new file mode 100644
index 0000000..561a5b5
--- /dev/null
+++ b/src-tauri/src/plugins/haskell.rs
@@ -0,0 +1,99 @@
+use super::{LanguagePlugin, PluginConfig};
+use std::vec;
+
+pub struct HaskellPlugin;
+
+impl LanguagePlugin for HaskellPlugin {
+ fn get_order(&self) -> i32 {
+ 25
+ }
+
+ fn get_language_name(&self) -> &'static str {
+ "Haskell"
+ }
+
+ fn get_language_key(&self) -> &'static str {
+ "haskell"
+ }
+
+ fn get_file_extension(&self) -> String {
+ self.get_config()
+ .map(|config| config.extension.clone())
+ .unwrap_or_else(|| "hs".to_string())
+ }
+
+ fn get_version_args(&self) -> Vec<&'static str> {
+ vec!["--version"]
+ }
+
+ fn get_path_command(&self) -> String {
+ "which ghc".to_string()
+ }
+
+ fn get_command(
+ &self,
+ _file_path: Option<&str>,
+ _is_version: bool,
+ _file_name: Option,
+ ) -> String {
+ if _is_version {
+ let ghc_command = if self.get_execute_home().is_some() {
+ "./ghc"
+ } else {
+ "ghc"
+ };
+
+ return ghc_command.to_string();
+ }
+
+ // 执行代码时
+ if let Some(config) = self.get_config() {
+ if let Some(run_cmd) = &config.run_command {
+ return if let Some(file_name) = _file_name {
+ run_cmd.replace("$filename", &file_name)
+ } else {
+ // 执行代码但没有文件名时,返回原始命令让框架处理 $filename 替换
+ run_cmd.clone()
+ };
+ }
+ }
+ self.get_default_command()
+ }
+
+ fn get_execute_args(&self, file_path: &str) -> Vec {
+ let ghc_command = if self.get_execute_home().is_some() {
+ "./ghc"
+ } else {
+ "ghc"
+ };
+
+ // 对于 Haskell,通常先编译再运行
+ // 这里假设编译后的可执行文件名为 main
+ let cmd = format!("{} {} -o main && ./main", ghc_command, file_path);
+
+ vec!["-c".to_string(), cmd]
+ }
+
+ fn get_default_config(&self) -> PluginConfig {
+ PluginConfig {
+ enabled: true,
+ language: String::from("haskell"),
+ before_compile: Some(String::from("ghc $filename -o /tmp/main")),
+ extension: String::from("hs"),
+ execute_home: None,
+ run_command: Some(String::from("/tmp/main")),
+ after_compile: Some(String::from("rm -f /tmp/main /tmp/main.hi /tmp/main.o")),
+ template: Some(String::from(
+ "-- 在这里输入 Haskell 代码\n-- Haskell - 纯函数式编程语言\n",
+ )),
+ timeout: Some(30),
+ console_type: Some(String::from("console")),
+ }
+ }
+
+ fn get_default_command(&self) -> String {
+ self.get_config()
+ .and_then(|config| config.run_command.clone())
+ .unwrap_or_else(|| "./main".to_string())
+ }
+}
diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs
index 6344962..64271a4 100644
--- a/src-tauri/src/plugins/manager.rs
+++ b/src-tauri/src/plugins/manager.rs
@@ -7,6 +7,7 @@ use crate::plugins::cpp::CppPlugin;
use crate::plugins::css::CssPlugin;
use crate::plugins::go::GoPlugin;
use crate::plugins::groovy::GroovyPlugin;
+use crate::plugins::haskell::HaskellPlugin;
use crate::plugins::html::HtmlPlugin;
use crate::plugins::java::JavaPlugin;
use crate::plugins::javascript_browser::JavaScriptBrowserPlugin;
@@ -60,6 +61,7 @@ impl PluginManager {
plugins.insert("php".to_string(), Box::new(PHPPlugin));
plugins.insert("r".to_string(), Box::new(RPlugin));
plugins.insert("cangjie".to_string(), Box::new(CangjiePlugin));
+ plugins.insert("haskell".to_string(), Box::new(HaskellPlugin));
plugins.insert(
"javascript-nodejs".to_string(),
Box::new(JavaScriptNodeJsPlugin),
diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs
index a845ae6..8253379 100644
--- a/src-tauri/src/plugins/mod.rs
+++ b/src-tauri/src/plugins/mod.rs
@@ -377,6 +377,7 @@ pub mod cpp;
pub mod css;
pub mod go;
pub mod groovy;
+pub mod haskell;
pub mod html;
pub mod java;
pub mod javascript_browser;
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index e2b3416..ded4215 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "CodeForge",
- "version": "25.0.3",
+ "version": "25.0.4",
"identifier": "org.devlive.codeforge",
"build": {
"beforeDevCommand": "pnpm dev",
diff --git a/src/composables/useCodeMirrorEditor.ts b/src/composables/useCodeMirrorEditor.ts
index 3cfb0c0..78e6ced 100644
--- a/src/composables/useCodeMirrorEditor.ts
+++ b/src/composables/useCodeMirrorEditor.ts
@@ -16,6 +16,7 @@ import {clojure} from '@codemirror/legacy-modes/mode/clojure'
import {ruby} from '@codemirror/legacy-modes/mode/ruby'
import {groovy} from '@codemirror/legacy-modes/mode/groovy'
import {r} from "@codemirror/legacy-modes/mode/r"
+import {haskell} from "@codemirror/legacy-modes/mode/haskell"
import {
abcdef,
abyss,
@@ -213,6 +214,8 @@ export function useCodeMirrorEditor(props: Props)
return php()
case 'r':
return StreamLanguage.define(r)
+ case 'haskell':
+ return StreamLanguage.define(haskell)
default:
return null
}