1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use std::collections::HashMap;
/// Trait for the editor to provide services to the plugin runtime
/// without the runtime depending directly on UI or complex system logic.
pub trait PluginServiceBridge: Send + Sync + 'static {
/// Support downcasting for tests
fn as_any(&self) -> &dyn std::any::Any;
/// Translate a string for a plugin
fn translate(&self, plugin_name: &str, key: &str, args: &HashMap<String, String>) -> String;
/// Get the current locale
fn current_locale(&self) -> String;
/// Update the current JavaScript execution state (for debugging/signal handlers)
fn set_js_execution_state(&self, state: String);
/// Clear the JavaScript execution state
fn clear_js_execution_state(&self);
/// Get the JSON schema for themes
fn get_theme_schema(&self) -> serde_json::Value;
/// Get a list of builtin theme names
fn get_builtin_themes(&self) -> serde_json::Value;
/// Full theme registry (builtins + user + packages + bundles) as a JSON
/// object keyed by canonical registry key. Each value is the parsed theme
/// with `_key` / `_pack` metadata fields (see `ThemeRegistry::to_json_map`).
fn get_all_themes(&self) -> serde_json::Value;
/// Register custom i18n strings for a plugin
fn register_plugin_strings(
&self,
_plugin_name: &str,
_strings: HashMap<String, HashMap<String, String>>,
) {
}
/// Unregister custom i18n strings for a plugin
fn unregister_plugin_strings(&self, _plugin_name: &str) {}
/// Register a plugin command
fn register_command(&self, command: crate::command::Command);
/// Unregister a command by name
fn unregister_command(&self, name: &str);
/// Unregister all commands with a given prefix
fn unregister_commands_by_prefix(&self, prefix: &str);
/// Unregister all commands registered by a specific plugin
fn unregister_commands_by_plugin(&self, plugin_name: &str);
/// Get the plugins directory path
fn plugins_dir(&self) -> std::path::PathBuf;
/// Get the config directory path
fn config_dir(&self) -> std::path::PathBuf;
/// Get the persistent data directory path (DirectoryContext::data_dir).
/// Used for long-lived plugin state such as review-diff comment history.
fn data_dir(&self) -> std::path::PathBuf;
/// Directory holding terminal scrollback backing files for the given
/// working directory (project root / worktree). Each root gets its own
/// subdir, so Universal Search's terminal scope can stay scoped to the
/// active project. Default falls back to the shared `terminals` root
/// (covers all roots); the editor bridge overrides with the per-root
/// subdir (`DirectoryContext::terminal_dir_for`).
fn terminal_dir(&self, working_dir: &std::path::Path) -> std::path::PathBuf {
let _ = working_dir;
self.data_dir().join("terminals")
}
/// Per-working-directory data root for plugin state that should be scoped
/// to a single project root / worktree (e.g. `<data_dir>/workdirs/
/// <encoded-cwd>/`). Default falls back to the shared parent; the editor
/// bridge overrides with the per-root subdir
/// (`DirectoryContext::working_data_dir_for`).
fn working_data_dir(&self, working_dir: &std::path::Path) -> std::path::PathBuf {
let _ = working_dir;
self.data_dir().join("workdirs")
}
/// Get theme data (JSON) by name from the in-memory cache.
fn get_theme_data(&self, _name: &str) -> Option<serde_json::Value> {
None
}
/// Save a theme file to the user themes directory.
/// Returns the path where the file was written.
fn save_theme_file(&self, _name: &str, _content: &str) -> Result<String, String> {
Err("not implemented".to_string())
}
/// Check if a user theme file exists (for overwrite confirmation).
fn theme_file_exists(&self, _name: &str) -> bool {
false
}
}
/// A no-op implementation of the service bridge for testing
pub struct NoopServiceBridge;
impl PluginServiceBridge for NoopServiceBridge {
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn translate(&self, _plugin_name: &str, key: &str, _args: &HashMap<String, String>) -> String {
key.to_string()
}
fn current_locale(&self) -> String {
"en".to_string()
}
fn set_js_execution_state(&self, _state: String) {}
fn clear_js_execution_state(&self) {}
fn get_theme_schema(&self) -> serde_json::Value {
serde_json::Value::Null
}
fn get_builtin_themes(&self) -> serde_json::Value {
serde_json::Value::Null
}
fn get_all_themes(&self) -> serde_json::Value {
serde_json::Value::Null
}
fn register_plugin_strings(
&self,
_plugin_name: &str,
_strings: HashMap<String, HashMap<String, String>>,
) {
}
fn unregister_plugin_strings(&self, _plugin_name: &str) {}
fn register_command(&self, _command: crate::command::Command) {}
fn unregister_command(&self, _name: &str) {}
fn unregister_commands_by_prefix(&self, _prefix: &str) {}
fn unregister_commands_by_plugin(&self, _plugin_name: &str) {}
fn plugins_dir(&self) -> std::path::PathBuf {
std::path::PathBuf::from("/tmp/plugins")
}
fn config_dir(&self) -> std::path::PathBuf {
std::path::PathBuf::from("/tmp/config")
}
fn data_dir(&self) -> std::path::PathBuf {
std::path::PathBuf::from("/tmp/data")
}
}