dx_forge/core/
editor_integration.rs1use std::path::PathBuf;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum EditorType {
11 VSCode,
13
14 Other(String),
16}
17
18#[derive(Debug, Clone)]
20pub struct EditorInfo {
21 pub editor_type: EditorType,
23
24 pub workspace_path: PathBuf,
26
27 pub extensions: Vec<String>,
29}
30
31#[derive(Debug, Clone)]
33pub enum OutputStrategy {
34 CurrentEditorDir {
36 path: PathBuf,
37 },
38
39 ProjectRoot {
41 path: PathBuf,
42 },
43
44 FileWatchOnly,
46}
47
48pub struct EditorIntegration {
50 active_editor: Option<EditorInfo>,
51 output_strategy: OutputStrategy,
52 vscode_extension_present: bool,
53}
54
55impl EditorIntegration {
56 pub fn new() -> Self {
58 Self {
59 active_editor: None,
60 output_strategy: OutputStrategy::FileWatchOnly,
61 vscode_extension_present: false,
62 }
63 }
64
65 pub fn detect_editor(&mut self) -> Option<EditorType> {
67 if std::env::var("VSCODE_PID").is_ok() || std::env::var("TERM_PROGRAM").map_or(false, |t| t == "vscode") {
69 self.vscode_extension_present = self.check_vscode_extension();
70 return Some(EditorType::VSCode);
71 }
72
73 None
74 }
75
76 fn check_vscode_extension(&self) -> bool {
78 if let Some(home) = dirs::home_dir() {
80 let extension_dirs = vec![
81 home.join(".vscode").join("extensions"),
82 home.join(".vscode-server").join("extensions"),
83 ];
84
85 for ext_dir in extension_dirs {
86 if ext_dir.exists() {
87 if let Ok(entries) = std::fs::read_dir(ext_dir) {
88 for entry in entries.flatten() {
89 let name = entry.file_name();
90 if name.to_string_lossy().contains("forge-lsp") {
91 return true;
92 }
93 }
94 }
95 }
96 }
97 }
98
99 false
100 }
101
102 pub fn get_current_editor_dir(&self) -> Option<PathBuf> {
104 None
107 }
108
109 pub fn set_active_editor(&mut self, editor_info: EditorInfo) {
111 self.active_editor = Some(editor_info);
112 }
113
114 pub fn has_vscode_extension(&self) -> bool {
116 self.vscode_extension_present
117 }
118
119 pub fn set_output_strategy(&mut self, strategy: OutputStrategy) {
121 tracing::info!("Output strategy changed to: {:?}", strategy);
122 self.output_strategy = strategy;
123 }
124
125 pub fn output_strategy(&self) -> &OutputStrategy {
127 &self.output_strategy
128 }
129
130 pub fn get_output_directory(&self) -> Option<PathBuf> {
132 match &self.output_strategy {
133 OutputStrategy::CurrentEditorDir { path } => Some(path.clone()),
134 OutputStrategy::ProjectRoot { path } => Some(path.clone()),
135 OutputStrategy::FileWatchOnly => None,
136 }
137 }
138
139 pub fn update_editor_directory(&mut self, path: PathBuf) {
141 self.output_strategy = OutputStrategy::CurrentEditorDir { path: path.clone() };
142 tracing::debug!("Updated editor directory to: {:?}", path);
143 }
144}
145
146impl Default for EditorIntegration {
147 fn default() -> Self {
148 Self::new()
149 }
150}
151
152mod dirs {
154 use std::path::PathBuf;
155
156 pub fn home_dir() -> Option<PathBuf> {
157 #[cfg(windows)]
158 {
159 std::env::var_os("USERPROFILE").map(PathBuf::from)
160 }
161
162 #[cfg(not(windows))]
163 {
164 std::env::var_os("HOME").map(PathBuf::from)
165 }
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn test_editor_integration_creation() {
175 let integration = EditorIntegration::new();
176 assert!(matches!(integration.output_strategy, OutputStrategy::FileWatchOnly));
177 }
178
179 #[test]
180 fn test_set_output_strategy() {
181 let mut integration = EditorIntegration::new();
182
183 let path = PathBuf::from("/test/path");
184 integration.set_output_strategy(OutputStrategy::CurrentEditorDir {
185 path: path.clone(),
186 });
187
188 assert_eq!(integration.get_output_directory(), Some(path));
189 }
190
191 #[test]
192 fn test_update_editor_directory() {
193 let mut integration = EditorIntegration::new();
194
195 let path = PathBuf::from("/new/dir");
196 integration.update_editor_directory(path.clone());
197
198 match integration.output_strategy() {
199 OutputStrategy::CurrentEditorDir { path: p } => {
200 assert_eq!(p, &path);
201 }
202 _ => panic!("Expected CurrentEditorDir strategy"),
203 }
204 }
205}