@@ -6,15 +6,19 @@ import XPCShared
66
77@ServiceActor
88final class Filespace {
9+ struct Snapshot : Equatable {
10+ var linesHash : Int
11+ var cursorPosition : CursorPosition
12+ }
13+
914 let fileURL : URL
1015 var suggestions : [ CopilotCompletion ] = [ ] {
1116 didSet { lastSuggestionUpdateTime = Environment . now ( ) }
1217 }
1318
1419 var suggestionIndex : Int = 0
15- var latestContentHash : Int = 0
16- var latestCursorPosition : CursorPosition = . init( line: - 1 , character: - 1 )
1720 var currentSuggestionLineRange : ClosedRange < Int > ?
21+ var suggestionSourceSnapshot : Snapshot = . init( linesHash: - 1 , cursorPosition: . outOfScope)
1822
1923 private( set) var lastSuggestionUpdateTime : Date = Environment . now ( )
2024 var isExpired : Bool {
@@ -50,26 +54,18 @@ final class Workspace {
5054 self . projectRootURL = projectRootURL
5155 }
5256
53- /// Trigger only when
54- /// 1. There is no pending suggestion
55- /// 2. There are pending suggestions, but either content or cursor is changed, and cursor is not inside of suggestion.
5657 func canAutoTriggerGetSuggestions(
5758 forFileAt fileURL: URL ,
58- content : String ,
59+ lines : [ String ] ,
5960 cursorPosition: CursorPosition
6061 ) -> Bool {
6162 guard isRealtimeSuggestionEnabled else { return false }
6263 guard let filespace = filespaces [ fileURL] else { return true }
63- if filespace. suggestions. isEmpty { return true }
64- if content. hashValue != filespace. latestContentHash { return true }
65- if cursorPosition != filespace. latestCursorPosition {
66- if let range = filespace. currentSuggestionLineRange,
67- range. contains ( cursorPosition. line)
68- {
69- return false
70- }
71- return true
72- }
64+ if let range = filespace. currentSuggestionLineRange,
65+ range. contains ( cursorPosition. line)
66+ { return false }
67+ if lines. hashValue != filespace. suggestionSourceSnapshot. linesHash { return true }
68+ if cursorPosition != filespace. suggestionSourceSnapshot. cursorPosition { return true }
7369 return false
7470 }
7571
@@ -91,15 +87,19 @@ final class Workspace {
9187 if filespaces [ fileURL] == nil {
9288 filespaces [ fileURL] = filespace
9389 }
94- filespace. latestContentHash = content. hashValue
95- filespace. latestCursorPosition = cursorPosition
9690 var extraInfo = SuggestionInjector . ExtraInfo ( )
9791
9892 injector. rejectCurrentSuggestions (
9993 from: & lines,
10094 cursorPosition: & cursorPosition,
10195 extraInfo: & extraInfo
10296 )
97+
98+ filespace. suggestionSourceSnapshot = . init(
99+ linesHash: lines. hashValue,
100+ cursorPosition: cursorPosition
101+ )
102+
103103 let completions = try await service. getCompletions (
104104 fileURL: fileURL,
105105 content: lines. joined ( separator: " " ) ,
@@ -124,6 +124,9 @@ final class Workspace {
124124 count: completions. count,
125125 extraInfo: & extraInfo
126126 )
127+
128+ filespace. currentSuggestionLineRange = extraInfo. suggestionRange
129+
127130 return . init(
128131 content: String ( lines. joined ( separator: " " ) ) ,
129132 newCursor: cursorPosition,
@@ -138,16 +141,16 @@ final class Workspace {
138141 cursorPosition: CursorPosition
139142 ) -> UpdatedContent {
140143 lastTriggerDate = Environment . now ( )
141- guard let fileSuggestion = filespaces [ fileURL] ,
142- fileSuggestion . suggestions. count > 1
144+ guard let filespace = filespaces [ fileURL] ,
145+ filespace . suggestions. count > 1
143146 else { return . init( content: content, modifications: [ ] ) }
144147 var cursorPosition = cursorPosition
145- fileSuggestion . suggestionIndex += 1
146- if fileSuggestion . suggestionIndex >= fileSuggestion . suggestions. endIndex {
147- fileSuggestion . suggestionIndex = 0
148+ filespace . suggestionIndex += 1
149+ if filespace . suggestionIndex >= filespace . suggestions. endIndex {
150+ filespace . suggestionIndex = 0
148151 }
149152
150- let suggestion = fileSuggestion . suggestions [ fileSuggestion . suggestionIndex]
153+ let suggestion = filespace . suggestions [ filespace . suggestionIndex]
151154 let injector = SuggestionInjector ( )
152155 var extraInfo = SuggestionInjector . ExtraInfo ( )
153156 var lines = lines
@@ -159,10 +162,13 @@ final class Workspace {
159162 injector. proposeSuggestion (
160163 intoContentWithoutSuggestion: & lines,
161164 completion: suggestion,
162- index: fileSuggestion . suggestionIndex,
163- count: fileSuggestion . suggestions. count,
165+ index: filespace . suggestionIndex,
166+ count: filespace . suggestions. count,
164167 extraInfo: & extraInfo
165168 )
169+
170+ filespace. currentSuggestionLineRange = extraInfo. suggestionRange
171+
166172 return . init(
167173 content: String ( lines. joined ( separator: " " ) ) ,
168174 newCursor: cursorPosition,
@@ -177,16 +183,16 @@ final class Workspace {
177183 cursorPosition: CursorPosition
178184 ) -> UpdatedContent {
179185 lastTriggerDate = Environment . now ( )
180- guard let fileSuggestion = filespaces [ fileURL] ,
181- fileSuggestion . suggestions. count > 1
186+ guard let filespace = filespaces [ fileURL] ,
187+ filespace . suggestions. count > 1
182188 else { return . init( content: content, modifications: [ ] ) }
183189 var cursorPosition = cursorPosition
184- fileSuggestion . suggestionIndex -= 1
185- if fileSuggestion . suggestionIndex < 0 {
186- fileSuggestion . suggestionIndex = fileSuggestion . suggestions. endIndex - 1
190+ filespace . suggestionIndex -= 1
191+ if filespace . suggestionIndex < 0 {
192+ filespace . suggestionIndex = filespace . suggestions. endIndex - 1
187193 }
188194 var extraInfo = SuggestionInjector . ExtraInfo ( )
189- let suggestion = fileSuggestion . suggestions [ fileSuggestion . suggestionIndex]
195+ let suggestion = filespace . suggestions [ filespace . suggestionIndex]
190196 let injector = SuggestionInjector ( )
191197 var lines = lines
192198 injector. rejectCurrentSuggestions (
@@ -197,10 +203,13 @@ final class Workspace {
197203 injector. proposeSuggestion (
198204 intoContentWithoutSuggestion: & lines,
199205 completion: suggestion,
200- index: fileSuggestion . suggestionIndex,
201- count: fileSuggestion . suggestions. count,
206+ index: filespace . suggestionIndex,
207+ count: filespace . suggestions. count,
202208 extraInfo: & extraInfo
203209 )
210+
211+ filespace. currentSuggestionLineRange = extraInfo. suggestionRange
212+
204213 return . init(
205214 content: String ( lines. joined ( separator: " " ) ) ,
206215 newCursor: cursorPosition,
@@ -215,16 +224,16 @@ final class Workspace {
215224 cursorPosition: CursorPosition
216225 ) -> UpdatedContent {
217226 lastTriggerDate = Environment . now ( )
218- guard let fileSuggestion = filespaces [ fileURL] ,
219- !fileSuggestion . suggestions. isEmpty,
220- fileSuggestion . suggestionIndex >= 0 ,
221- fileSuggestion . suggestionIndex < fileSuggestion . suggestions. endIndex
227+ guard let filespace = filespaces [ fileURL] ,
228+ !filespace . suggestions. isEmpty,
229+ filespace . suggestionIndex >= 0 ,
230+ filespace . suggestionIndex < filespace . suggestions. endIndex
222231 else { return . init( content: content, modifications: [ ] ) }
223232
224233 var cursorPosition = cursorPosition
225234 var extraInfo = SuggestionInjector . ExtraInfo ( )
226- var allSuggestions = fileSuggestion . suggestions
227- let suggestion = allSuggestions. remove ( at: fileSuggestion . suggestionIndex)
235+ var allSuggestions = filespace . suggestions
236+ let suggestion = allSuggestions. remove ( at: filespace . suggestionIndex)
228237 let injector = SuggestionInjector ( )
229238 var lines = lines
230239 injector. rejectCurrentSuggestions (
0 commit comments