66 "fmt"
77 "os"
88 "path/filepath"
9+ "sort"
910 "strconv"
10- "sync "
11+ "strings "
1112 "time"
1213
1314 "github.com/sgtdi/fswatcher"
@@ -38,17 +39,48 @@ func watch_dir(ctx context.Context, path string, debounce time.Duration, eventCh
3839 return nil
3940}
4041
41- type config_file_collection struct {
42- mutex sync.Mutex
43- config_paths []string
44- dirs_to_watch []string
42+ // returns the closest unique parent directories for a list of paths.
43+ // It excludes any directory that is a subdirectory of another directory already in the result set.
44+ func get_unique_directories (paths []string ) []string {
45+ if len (paths ) == 0 {
46+ return nil
47+ }
48+
49+ // 1. Extract parent directories and remove duplicates
50+ dirMap := utils.NewSet [string ](len (paths ))
51+ for _ , p := range paths {
52+ dirMap .Add (filepath .Dir (p ))
53+ }
54+
55+ // 2. Convert map to a sorted slice
56+ // Sorting ensures that shorter paths (potential parents) come before longer ones (potential children)
57+ uniqueDirs := dirMap .AsSlice ()
58+ sort .Strings (uniqueDirs )
59+
60+ // 3. Filter out subdirectories
61+ var result []string
62+ for _ , current := range uniqueDirs {
63+ isSubDir := false
64+ for _ , parent := range result {
65+ // Check if 'current' is a subdirectory of 'parent'
66+ // Strings.HasPrefix is safe here because paths are sorted and Cleaned by filepath.Dir
67+ if current == parent || strings .HasPrefix (current , parent + string (filepath .Separator )) {
68+ isSubDir = true
69+ break
70+ }
71+ }
72+ if ! isSubDir {
73+ result = append (result , current )
74+ }
75+ }
76+ return result
4577}
4678
47- func ( cfc * config_file_collection ) get_list_of_config_files ( ) * utils.Set [string ] {
79+ func get_set_of_config_files ( config_paths [] string ) * utils.Set [string ] {
4880 cp := config.ConfigParser {
4981 AllIncludedFiles : utils .NewSet [string ](), LineHandler : func (k , v string ) error { return nil }}
50- cp .ParseFiles (cfc . config_paths ... )
51- for _ , path := range cfc . config_paths {
82+ cp .ParseFiles (config_paths ... )
83+ for _ , path := range config_paths {
5284 path = filepath .Clean (path )
5385 cp .AllIncludedFiles .Add (path )
5486 for _ , q := range []string {"dark-theme.auto.conf" , "light-theme.auto.conf" , "no-preference-theme.auto.conf" } {
@@ -59,36 +91,24 @@ func (cfc *config_file_collection) get_list_of_config_files() *utils.Set[string]
5991 return cp .AllIncludedFiles
6092}
6193
62- func (cfc * config_file_collection ) EventIsSignificant (ev fswatcher.WatchEvent ) bool {
63- cfc .mutex .Lock ()
64- defer cfc .mutex .Unlock ()
65- conf_files := cfc .get_list_of_config_files ()
66- q := filepath .Clean (ev .Path )
67- return conf_files .Has (q )
68- }
69-
7094func watch_for_kitty_config_changes (action func () error , debounce_time time.Duration , config_paths []string ) error {
7195 ctx , cancel := context .WithCancel (context .Background ())
7296 defer cancel ()
7397 event_chan := make (chan fswatcher.WatchEvent )
74- dirs := utils.NewSet [string ](len (config_paths ))
75- for _ , path := range config_paths {
76- if parent := filepath .Dir (path ); parent != "" && parent != "." && parent != "/" {
77- dirs .Add (path )
78- }
79- }
80- if dirs .Len () == 0 {
98+ all_paths := get_set_of_config_files (config_paths )
99+ dirs_to_watch := get_unique_directories (all_paths .AsSlice ())
100+ if len (dirs_to_watch ) == 0 {
81101 return fmt .Errorf ("No directories to watch provided" )
82102 }
83- cfc := config_file_collection {config_paths : config_paths , dirs_to_watch : dirs .AsSlice ()}
84103
85104 filtered_action := func (ev fswatcher.WatchEvent ) error {
86- if cfc .EventIsSignificant (ev ) {
105+ all_paths := get_set_of_config_files (config_paths )
106+ if all_paths .Has (filepath .Clean (ev .Path )) {
87107 return action ()
88108 }
89109 return nil
90110 }
91- for _ , path := range cfc . dirs_to_watch {
111+ for _ , path := range dirs_to_watch {
92112 if err := watch_dir (ctx , path , debounce_time , event_chan ); err != nil {
93113 return err
94114 }
0 commit comments