88import Foundation
99
1010/// A model to manage Git ignore patterns for a file, including loading, saving, and monitoring changes.
11+ @MainActor
1112class IgnorePatternModel : ObservableObject {
13+ /// Indicates whether patterns are currently being loaded from the Git ignore file.
1214 @Published var loadingPatterns : Bool = false
15+
16+ /// A collection of Git ignore patterns being managed by this model.
1317 @Published var patterns : [ GlobPattern ] = [ ] {
1418 didSet {
1519 if !loadingPatterns {
@@ -19,11 +23,19 @@ class IgnorePatternModel: ObservableObject {
1923 }
2024 }
2125 }
26+
27+ /// Tracks the selected patterns by their unique identifiers (UUIDs).
2228 @Published var selection : Set < UUID > = [ ]
2329
30+ /// A client for interacting with the Git configuration.
2431 private let gitConfig = GitConfigClient ( shellClient: currentWorld. shellClient)
32+
33+ /// A file system monitor for detecting changes to the Git ignore file.
2534 private var fileMonitor : DispatchSourceFileSystemObject ?
2635
36+ /// Task tracking the current save operation
37+ private var savingTask : Task < Void , Never > ?
38+
2739 init ( ) {
2840 Task {
2941 try ? await startFileMonitor ( )
@@ -32,7 +44,9 @@ class IgnorePatternModel: ObservableObject {
3244 }
3345
3446 deinit {
35- stopFileMonitor ( )
47+ Task { @MainActor [ weak self] in
48+ self ? . stopFileMonitor ( )
49+ }
3650 }
3751
3852 /// Resolves the URL for the Git ignore file.
@@ -69,9 +83,7 @@ class IgnorePatternModel: ObservableObject {
6983 )
7084
7185 source. setEventHandler {
72- Task {
73- await self . loadPatterns ( )
74- }
86+ Task { await self . loadPatterns ( ) }
7587 }
7688
7789 source. setCancelHandler {
@@ -91,40 +103,30 @@ class IgnorePatternModel: ObservableObject {
91103
92104 /// Loads patterns from the Git ignore file into the `patterns` property.
93105 func loadPatterns( ) async {
94- await MainActor . run { loadingPatterns = true } // Ensure `loadingPatterns` is updated on the main thread
106+ loadingPatterns = true
95107
96108 do {
97109 let fileURL = try await gitIgnoreURL ( )
98110 guard FileManager . default. fileExists ( atPath: fileURL. path) else {
99- await MainActor . run {
100- patterns = [ ]
101- loadingPatterns = false // Update on the main thread
102- }
111+ patterns = [ ]
112+ loadingPatterns = false
103113 return
104114 }
105115
106116 if let content = try ? String ( contentsOf: fileURL) {
107- let parsedPatterns = content. split ( separator: " \n " )
117+ patterns = content. split ( separator: " \n " )
108118 . map { $0. trimmingCharacters ( in: . whitespacesAndNewlines) }
109119 . filter { !$0. isEmpty && !$0. starts ( with: " # " ) }
110120 . map { GlobPattern ( value: String ( $0) ) }
111-
112- await MainActor . run {
113- patterns = parsedPatterns // Update `patterns` on the main thread
114- loadingPatterns = false // Ensure `loadingPatterns` is updated on the main thread
115- }
121+ loadingPatterns = false
116122 } else {
117- await MainActor . run {
118- patterns = [ ]
119- loadingPatterns = false
120- }
121- }
122- } catch {
123- print ( " Error loading patterns: \( error) " )
124- await MainActor . run {
125123 patterns = [ ]
126124 loadingPatterns = false
127125 }
126+ } catch {
127+ print ( " Error loading patterns: \( error) " )
128+ patterns = [ ]
129+ loadingPatterns = false
128130 }
129131 }
130132
@@ -137,9 +139,16 @@ class IgnorePatternModel: ObservableObject {
137139
138140 /// Saves the current patterns back to the Git ignore file.
139141 func savePatterns( ) {
140- Task {
142+ // Cancel the existing task if it exists
143+ savingTask? . cancel ( )
144+
145+ // Start a new task for saving patterns
146+ savingTask = Task {
141147 stopFileMonitor ( )
142- defer { Task { try ? await startFileMonitor ( ) } }
148+ defer {
149+ savingTask = nil // Clear the task when done
150+ Task { try ? await startFileMonitor ( ) }
151+ }
143152
144153 do {
145154 let fileURL = try await gitIgnoreURL ( )
@@ -283,13 +292,12 @@ class IgnorePatternModel: ObservableObject {
283292 }
284293
285294 /// Adds a new, empty pattern to the list of patterns.
286- @MainActor
287295 func addPattern( ) {
288296 patterns. append ( GlobPattern ( value: " " ) )
289297 }
298+
290299 /// Removes the specified patterns from the list of patterns.
291300 /// - Parameter selection: The set of UUIDs for the patterns to remove. If `nil`, no patterns are removed.
292- @MainActor
293301 func removePatterns( _ selection: Set < UUID > ? = nil ) {
294302 let patternsToRemove = selection? . compactMap { getPattern ( for: $0) } ?? [ ]
295303 patterns. removeAll { patternsToRemove. contains ( $0) }
0 commit comments