11use crate :: model:: { FsEvent , FsEventKind } ;
2+ use crate :: sdk:: workspace_manager:: FileState ;
23use anyhow:: Result ;
4+ use dashmap:: DashMap ;
35use globset:: { Glob , GlobSetBuilder } ;
46use ignore:: gitignore:: { Gitignore , GitignoreBuilder } ;
57use notify:: { Event , RecommendedWatcher , RecursiveMode , Watcher } ;
68
79use std:: path:: { Path , PathBuf } ;
10+ use std:: sync:: Arc ;
811use std:: time:: Instant ;
912use tokio:: sync:: mpsc;
1013use url:: Url ;
@@ -247,16 +250,23 @@ pub(crate) struct EventProcessor {
247250 event_rx : mpsc:: UnboundedReceiver < FsEvent > ,
248251 workspace_manager : * mut crate :: sdk:: WorkspaceManager ,
249252 workspace_root : PathBuf ,
253+ opened_files : Arc < DashMap < PathBuf , FileState > > , // Thread-safe shared state
250254}
251255
252256unsafe impl Send for EventProcessor { }
253257
254258impl EventProcessor {
255- pub fn new ( event_rx : mpsc:: UnboundedReceiver < FsEvent > , workspace_manager : * mut crate :: sdk:: WorkspaceManager , workspace_root : PathBuf ) -> Self {
259+ pub fn new (
260+ event_rx : mpsc:: UnboundedReceiver < FsEvent > ,
261+ workspace_manager : * mut crate :: sdk:: WorkspaceManager ,
262+ workspace_root : PathBuf ,
263+ opened_files : Arc < DashMap < PathBuf , FileState > >
264+ ) -> Self {
256265 Self {
257266 event_rx,
258267 workspace_manager,
259268 workspace_root,
269+ opened_files,
260270 }
261271 }
262272
@@ -290,9 +300,11 @@ impl EventProcessor {
290300 match event. kind {
291301 FsEventKind :: Created => {
292302 // Send workspace/didChangeWatchedFiles for new files (only if not already open)
293- unsafe {
294- let workspace_manager = & mut * self . workspace_manager ;
295- if !workspace_manager. is_file_opened ( & absolute_path) {
303+ let is_file_open = self . opened_files . get ( & absolute_path) . map_or ( false , |state| state. is_open ) ;
304+
305+ if !is_file_open {
306+ unsafe {
307+ let workspace_manager = & mut * self . workspace_manager ;
296308 if let Ok ( Some ( client) ) = workspace_manager. get_client_for_file ( & absolute_path) . await {
297309 // 1. Send didChangeWatchedFiles notification
298310 let params = DidChangeWatchedFilesParams {
@@ -305,17 +317,19 @@ impl EventProcessor {
305317 tracing:: info!( "📄 File created, sending didChangeWatchedFiles: {:?}" , absolute_path) ;
306318 let _ = client. did_change_watched_files ( params) . await ;
307319 }
308- } else {
309- tracing:: info!( "📄 File created but already open, skipping notification: {:?}" , absolute_path) ;
310320 }
321+ } else {
322+ tracing:: info!( "📄 File created but already open, skipping notification: {:?}" , absolute_path) ;
311323 }
312324 }
313325
314326 FsEventKind :: Deleted => {
327+ let is_file_open = self . opened_files . get ( & absolute_path) . map_or ( false , |state| state. is_open ) ;
328+
315329 unsafe {
316330 let workspace_manager = & mut * self . workspace_manager ;
317331
318- if workspace_manager . is_file_opened ( & absolute_path ) {
332+ if is_file_open {
319333 // File was opened - send didClose AND didChangeWatchedFiles
320334 if let Ok ( Some ( client) ) = workspace_manager. get_client_for_file ( & absolute_path) . await {
321335 // 1. Close the opened file
@@ -337,7 +351,12 @@ impl EventProcessor {
337351 tracing:: info!( "🗑️ Sending didChangeWatchedFiles for deleted file: {:?}" , absolute_path) ;
338352 let _ = client. did_change_watched_files ( watch_params) . await ;
339353 }
340- workspace_manager. mark_file_closed ( & absolute_path) ;
354+
355+ // Mark file as closed in shared state
356+ if let Some ( mut state) = self . opened_files . get_mut ( & absolute_path) {
357+ state. is_open = false ;
358+ state. version = 0 ;
359+ }
341360 } else {
342361 // File was closed - just send didChangeWatchedFiles
343362 if let Ok ( Some ( client) ) = workspace_manager. get_client_for_file ( & absolute_path) . await {
@@ -355,13 +374,27 @@ impl EventProcessor {
355374 }
356375
357376 FsEventKind :: Modified => {
358- // SAFETY: We know workspace_manager is valid during EventProcessor lifetime
377+ let is_file_open = self . opened_files . get ( & absolute_path) . map_or ( false , |state| state. is_open ) ;
378+
359379 unsafe {
360380 let workspace_manager = & mut * self . workspace_manager ;
361381
362- if workspace_manager. is_file_opened ( & absolute_path) {
363- // Send didChange for opened files
364- let version = workspace_manager. get_next_version ( & absolute_path) ;
382+ if is_file_open {
383+ // Send didChange for opened files - increment version in shared state
384+ let version = match self . opened_files . get_mut ( & absolute_path) {
385+ Some ( mut state) => {
386+ state. version += 1 ;
387+ state. version
388+ }
389+ None => {
390+ // File not tracked, start at version 1
391+ self . opened_files . insert ( absolute_path. clone ( ) , FileState {
392+ version : 1 ,
393+ is_open : true ,
394+ } ) ;
395+ 1
396+ }
397+ } ;
365398
366399 if let Ok ( Some ( client) ) = workspace_manager. get_client_for_file ( & absolute_path) . await {
367400 if let Ok ( content) = std:: fs:: read_to_string ( & absolute_path) {
@@ -403,11 +436,13 @@ impl EventProcessor {
403436 let from_absolute = self . workspace_root . join ( & from_path) ;
404437 tracing:: info!( "📋 File renamed: {:?} -> {:?}" , from_absolute, absolute_path) ;
405438
439+ let was_from_open = self . opened_files . get ( & from_absolute) . map_or ( false , |state| state. is_open ) ;
440+
406441 unsafe {
407442 let workspace_manager = & mut * self . workspace_manager ;
408443
409444 // Handle as Delete(old) + Create(new)
410- if workspace_manager . is_file_opened ( & from_absolute ) {
445+ if was_from_open {
411446 // Old file was opened - send didClose
412447 if let Ok ( Some ( client) ) = workspace_manager. get_client_for_file ( & from_absolute) . await {
413448 if let Ok ( from_uri) = Url :: from_file_path ( & from_absolute) {
@@ -420,7 +455,12 @@ impl EventProcessor {
420455 let _ = client. did_close ( params) . await ;
421456 }
422457 }
423- workspace_manager. mark_file_closed ( & from_absolute) ;
458+
459+ // Mark old file as closed in shared state
460+ if let Some ( mut state) = self . opened_files . get_mut ( & from_absolute) {
461+ state. is_open = false ;
462+ state. version = 0 ;
463+ }
424464 }
425465
426466 // Send didChangeWatchedFiles for new file
0 commit comments