@@ -28,7 +28,7 @@ public protocol GitHubCopilotSuggestionServiceType {
2828 func notifyAccepted( _ completion: CodeSuggestion ) async
2929 func notifyRejected( _ completions: [ CodeSuggestion ] ) async
3030 func notifyOpenTextDocument( fileURL: URL , content: String ) async throws
31- func notifyChangeTextDocument( fileURL: URL , content: String ) async throws
31+ func notifyChangeTextDocument( fileURL: URL , content: String , version : Int ) async throws
3232 func notifyCloseTextDocument( fileURL: URL ) async throws
3333 func notifySaveTextDocument( fileURL: URL ) async throws
3434 func cancelRequest( ) async
@@ -364,54 +364,21 @@ public final class GitHubCopilotService: GitHubCopilotBaseService,
364364 indentSize: Int ,
365365 usesTabsForIndentation: Bool
366366 ) async throws -> [ CodeSuggestion ] {
367- let languageId = languageIdentifierFromFileURL ( fileURL)
368-
369- let relativePath = {
370- let filePath = fileURL. path
371- let rootPath = projectRootURL. path
372- if let range = filePath. range ( of: rootPath) ,
373- range. lowerBound == filePath. startIndex
374- {
375- let relativePath = filePath. replacingCharacters (
376- in: filePath. startIndex..< range. upperBound,
377- with: " "
378- )
379- return relativePath
380- }
381- return filePath
382- } ( )
383-
384367 ongoingTasks. forEach { $0. cancel ( ) }
385368 ongoingTasks. removeAll ( )
386369 await localProcessServer? . cancelOngoingTasks ( )
387370
388- let task = Task {
389- // since when the language server is no longer using the passed in content to generate
390- // suggestions, therefore, we will need to update the content to the file before we
391- // do any request.
392- //
393- // And sometimes the language server's content was not up to date and may generate
394- // weird result when the cursor position exceeds the line.
395- try ? await notifyChangeTextDocument ( fileURL: fileURL, content: content)
396- defer {
397- // recover the content.
398- Task {
399- try ? await notifyChangeTextDocument ( fileURL: fileURL, content: originalContent)
400- }
401- }
402- try Task . checkCancellation ( )
371+ func sendRequest( maxTry: Int = 5 ) async throws -> [ CodeSuggestion ] {
403372 do {
404373 let completions = try await server
405374 . sendRequest ( GitHubCopilotRequest . InlineCompletion ( doc: . init(
406- source: content,
407- tabSize: tabSize,
408- indentSize: indentSize,
409- insertSpaces: !usesTabsForIndentation,
410- path: fileURL. path,
411- uri: fileURL. path,
412- relativePath: relativePath,
413- languageId: languageId,
414- position: cursorPosition
375+ textDocument: . init( uri: fileURL. path, version: 1 ) ,
376+ position: cursorPosition,
377+ formattingOptions: . init(
378+ tabSize: tabSize,
379+ insertSpaces: !usesTabsForIndentation
380+ ) ,
381+ context: . init( triggerKind: . invoked)
415382 ) ) )
416383 . items
417384 . compactMap { ( item: _ ) -> CodeSuggestion ? in
@@ -427,11 +394,56 @@ public final class GitHubCopilotService: GitHubCopilotBaseService,
427394 try Task . checkCancellation ( )
428395 return completions
429396 } catch let error as ServerError {
397+ switch error {
398+ case . serverError:
399+ if maxTry <= 0 { break }
400+ Logger . gitHubCopilot. error (
401+ " Try getting suggestions again: \( GitHubCopilotError . languageServerError ( error) . localizedDescription) "
402+ )
403+ try await Task . sleep ( nanoseconds: 400_000_000 )
404+ return try await sendRequest ( maxTry: maxTry - 1 )
405+ default :
406+ break
407+ }
430408 throw GitHubCopilotError . languageServerError ( error)
431409 } catch {
432410 throw error
433411 }
434412 }
413+
414+ func recoverContent( ) async {
415+ try ? await notifyChangeTextDocument (
416+ fileURL: fileURL,
417+ content: originalContent,
418+ version: 0
419+ )
420+ }
421+
422+ // since when the language server is no longer using the passed in content to generate
423+ // suggestions, we will need to update the content to the file before we do any request.
424+ //
425+ // And sometimes the language server's content was not up to date and may generate
426+ // weird result when the cursor position exceeds the line.
427+ let task = Task { @GitHubCopilotSuggestionActor in
428+ try ? await notifyChangeTextDocument (
429+ fileURL: fileURL,
430+ content: content,
431+ version: 1
432+ )
433+
434+ do {
435+ try Task . checkCancellation ( )
436+ return try await sendRequest ( )
437+ } catch let error as CancellationError {
438+ if ongoingTasks. isEmpty {
439+ await recoverContent ( )
440+ }
441+ throw error
442+ } catch {
443+ await recoverContent ( )
444+ throw error
445+ }
446+ }
435447
436448 ongoingTasks. insert ( task)
437449
@@ -440,6 +452,8 @@ public final class GitHubCopilotService: GitHubCopilotBaseService,
440452
441453 @GitHubCopilotSuggestionActor
442454 public func cancelRequest( ) async {
455+ ongoingTasks. forEach { $0. cancel ( ) }
456+ ongoingTasks. removeAll ( )
443457 await localProcessServer? . cancelOngoingTasks ( )
444458 }
445459
@@ -480,14 +494,18 @@ public final class GitHubCopilotService: GitHubCopilotBaseService,
480494 }
481495
482496 @GitHubCopilotSuggestionActor
483- public func notifyChangeTextDocument( fileURL: URL , content: String ) async throws {
497+ public func notifyChangeTextDocument(
498+ fileURL: URL ,
499+ content: String ,
500+ version: Int
501+ ) async throws {
484502 let uri = " file:// \( fileURL. path) "
485503// Logger.service.debug("Change \(uri), \(content.count)")
486504 try await server. sendNotification (
487505 . didChangeTextDocument(
488506 DidChangeTextDocumentParams (
489507 uri: uri,
490- version: 0 ,
508+ version: version ,
491509 contentChange: . init(
492510 range: nil ,
493511 rangeLength: nil ,
0 commit comments