@@ -2,7 +2,7 @@ use std::path::PathBuf;
22use std:: sync:: Arc ;
33use std:: collections:: HashMap ;
44use std:: time:: Duration ;
5- use tokio:: sync:: { Mutex , Notify } ;
5+ use tokio:: sync:: { Mutex , watch } ;
66use serde_json:: json;
77use tracing:: info;
88use anyhow:: Result ;
@@ -13,15 +13,15 @@ use crate::app_state::SocketData;
1313use crate :: code:: Code ;
1414use crate :: diff:: compute_text_edits;
1515
16- #[ derive( Clone , Debug , PartialEq ) ]
16+ #[ derive( Clone , Debug , PartialEq ) ] // Added Copy/Clone/PartialEq if needed by usage, sticking to original Clone/PartialEq + Debug
1717enum FileState {
1818 Exists ,
1919 DoesNotExist ,
2020}
2121
2222pub struct FileWatchState {
2323 pub state : FileState ,
24- pub notify : Arc < Notify > ,
24+ pub sender : watch :: Sender < ( ) > ,
2525 pub pending : bool ,
2626}
2727
@@ -109,33 +109,30 @@ pub async fn handle_watch_event(
109109 git_manager : & Arc < Mutex < crate :: git:: GitManager > > ,
110110) {
111111 let path_str = match path. to_str ( ) {
112- Some ( s) => s. to_string ( ) ,
113- None => return ,
112+ Some ( s) => s. to_string ( ) , None => return ,
114113 } ;
115114
116- let should_spawn = {
115+ let ( should_spawn, rx ) = {
117116 let mut states = file_states. lock ( ) . await ;
118- let entry = states. entry ( path_str. clone ( ) ) . or_insert_with ( || FileWatchState {
119- state : FileState :: DoesNotExist , // never seen = didn't exist for us
120- notify : Arc :: new ( Notify :: new ( ) ) ,
121- pending : false ,
117+ let entry = states. entry ( path_str. clone ( ) ) . or_insert_with ( || {
118+ let ( tx, _) = watch:: channel ( ( ) ) ;
119+ FileWatchState { state : FileState :: DoesNotExist , sender : tx, pending : false , }
122120 } ) ;
123121
124- // Signal the existing task to reset its timer
125- entry. notify . notify_one ( ) ;
122+ let _ = entry. sender . send ( ( ) ) ;
126123
127124 if entry. pending {
128- // A task is already waiting — it will pick up the new event
129- false
125+ ( false , None )
130126 } else {
131127 entry. pending = true ;
132- true
128+ let receiver = entry. sender . subscribe ( ) ;
129+ ( true , Some ( receiver) )
133130 }
134131 } ;
135132
136- if !should_spawn {
137- return ;
138- }
133+ if !should_spawn { return ; }
134+
135+ let mut rx = rx . unwrap ( ) ;
139136
140137 // Spawn a single debounce task for this file
141138 let path = path. clone ( ) ;
@@ -144,33 +141,30 @@ pub async fn handle_watch_event(
144141 let socket2data = socket2data. clone ( ) ;
145142 let file_states = file_states. clone ( ) ;
146143 let git_manager = git_manager. clone ( ) ;
147-
148- // Get the notify handle for this file
149- let file_notify = {
150- let states = file_states. lock ( ) . await ;
151- states. get ( & path_str) . unwrap ( ) . notify . clone ( )
152- } ;
144+ let path_str_key = path_str. clone ( ) ;
153145
154146 tokio:: spawn ( async move {
155147 // Wait until events stop arriving (trailing-edge debounce)
156148 loop {
157- match tokio:: time:: timeout ( DEBOUNCE , file_notify. notified ( ) ) . await {
149+ // Mark as seen so we wait for *new* changes
150+ let _ = rx. borrow_and_update ( ) ;
151+ match tokio:: time:: timeout ( DEBOUNCE , rx. changed ( ) ) . await {
158152 Ok ( _) => continue , // new event arrived — reset timer
159153 Err ( _) => break , // timeout — silence, time to process
160154 }
161155 }
162156
163157 process_watch_event (
164- & path, & path_str , & socket, & file2code, & socket2data, & file_states, & git_manager,
158+ & path, & path_str_key , & socket, & file2code, & socket2data, & file_states, & git_manager,
165159 ) . await ;
166160
167161 // Mark as not pending so future events spawn a new task
168- {
169- let mut states = file_states. lock ( ) . await ;
170- if let Some ( state) = states. get_mut ( & path_str) {
171- state. pending = false ;
172- }
162+
163+ let mut states = file_states. lock ( ) . await ;
164+ if let Some ( state) = states. get_mut ( & path_str_key) {
165+ state. pending = false ;
173166 }
167+
174168 } ) ;
175169}
176170
0 commit comments