fresh/services/plugins/
bridge.rs1use crate::config_io::DirectoryContext;
2use crate::i18n;
3use crate::input::command_registry::CommandRegistry;
4use crate::services::signal_handler;
5use crate::view::theme;
6use fresh_core::services::PluginServiceBridge;
7use std::any::Any;
8use std::collections::HashMap;
9use std::path::PathBuf;
10use std::sync::{Arc, RwLock};
11
12pub struct EditorServiceBridge {
13 pub command_registry: Arc<RwLock<CommandRegistry>>,
14 pub dir_context: DirectoryContext,
15 pub theme_cache: Arc<RwLock<std::collections::HashMap<String, serde_json::Value>>>,
16}
17
18impl PluginServiceBridge for EditorServiceBridge {
19 fn as_any(&self) -> &dyn Any {
20 self
21 }
22
23 fn translate(&self, plugin_name: &str, key: &str, args: &HashMap<String, String>) -> String {
24 i18n::translate_plugin_string(plugin_name, key, args)
25 }
26
27 fn current_locale(&self) -> String {
28 i18n::current_locale()
29 }
30
31 fn set_js_execution_state(&self, state: String) {
32 signal_handler::set_js_execution_state(state);
33 }
34
35 fn clear_js_execution_state(&self) {
36 signal_handler::clear_js_execution_state();
37 }
38
39 fn get_theme_schema(&self) -> serde_json::Value {
40 theme::get_theme_schema()
41 }
42
43 fn get_builtin_themes(&self) -> serde_json::Value {
44 theme::get_builtin_themes()
45 }
46
47 fn get_all_themes(&self) -> serde_json::Value {
48 let cache = self.theme_cache.read().unwrap();
49 let map: serde_json::Map<String, serde_json::Value> =
50 cache.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
51 serde_json::Value::Object(map)
52 }
53
54 fn register_plugin_strings(
55 &self,
56 plugin_name: &str,
57 strings: HashMap<String, HashMap<String, String>>,
58 ) {
59 i18n::register_plugin_strings(plugin_name, strings);
60 }
61
62 fn unregister_plugin_strings(&self, plugin_name: &str) {
63 i18n::unregister_plugin_strings(plugin_name);
64 }
65
66 fn register_command(&self, command: fresh_core::command::Command) {
67 use crate::input::commands::{Command as EditorCommand, CommandSource};
69 use crate::input::keybindings::{Action, KeyContext};
70
71 let editor_command = EditorCommand {
72 name: command.name,
73 description: command.description,
74 action: Action::PluginAction(command.action_name),
75 contexts: vec![KeyContext::Global],
76 custom_contexts: command.custom_contexts,
77 source: CommandSource::Plugin(command.plugin_name),
78 terminal_bypass: command.terminal_bypass,
79 };
80 self.command_registry
81 .read()
82 .unwrap()
83 .register(editor_command);
84 }
85
86 fn unregister_command(&self, name: &str) {
87 self.command_registry.read().unwrap().unregister(name);
88 }
89
90 fn unregister_commands_by_prefix(&self, prefix: &str) {
91 self.command_registry
92 .read()
93 .unwrap()
94 .unregister_by_prefix(prefix);
95 }
96
97 fn unregister_commands_by_plugin(&self, plugin_name: &str) {
98 self.command_registry
99 .read()
100 .unwrap()
101 .unregister_by_plugin(plugin_name);
102 }
103
104 fn plugins_dir(&self) -> PathBuf {
105 self.dir_context.plugins_dir()
106 }
107
108 fn config_dir(&self) -> PathBuf {
109 self.dir_context.config_dir.clone()
110 }
111
112 fn data_dir(&self) -> PathBuf {
113 self.dir_context.data_dir.clone()
114 }
115
116 fn terminal_dir(&self, working_dir: &std::path::Path) -> PathBuf {
117 self.dir_context.terminal_dir_for(working_dir)
118 }
119
120 fn working_data_dir(&self, working_dir: &std::path::Path) -> PathBuf {
121 self.dir_context.working_data_dir_for(working_dir)
122 }
123
124 fn get_theme_data(&self, key_or_name: &str) -> Option<serde_json::Value> {
125 let cache = self.theme_cache.read().unwrap();
126 if let Some(v) = cache.get(key_or_name) {
128 return Some(v.clone());
129 }
130 let normalized = key_or_name.to_lowercase().replace(['_', ' '], "-");
132 cache
133 .values()
134 .find(|v| {
135 v.get("name")
136 .and_then(|n| n.as_str())
137 .is_some_and(|n| n.to_lowercase().replace(['_', ' '], "-") == normalized)
138 })
139 .cloned()
140 }
141
142 fn save_theme_file(&self, name: &str, content: &str) -> Result<String, String> {
143 let themes_dir = self.dir_context.themes_dir();
144 if !themes_dir.exists() {
145 std::fs::create_dir_all(&themes_dir).map_err(|e| e.to_string())?;
146 }
147 let path = themes_dir.join(format!("{}.json", name));
148 std::fs::write(&path, content).map_err(|e| e.to_string())?;
149 Ok(path.to_string_lossy().to_string())
150 }
151
152 fn theme_file_exists(&self, name: &str) -> bool {
153 let themes_dir = self.dir_context.themes_dir();
154 themes_dir.join(format!("{}.json", name)).exists()
155 }
156}