@@ -405,6 +405,80 @@ final class SwiftSourceKitPluginTests: XCTestCase {
405405 XCTAssertEqual ( result3. items. count, 1 )
406406 }
407407
408+ func testEditBounds( ) async throws {
409+ try await SkipUnless . sourcekitdSupportsPlugin ( )
410+ let sourcekitd = try await getSourceKitD ( )
411+ let path = scratchFilePath ( )
412+ _ = try await sourcekitd. openDocument (
413+ path,
414+ contents: " " ,
415+ compilerArguments: [ path]
416+ )
417+
418+ let typeWithMethod = """
419+ struct S {
420+ static func foo() -> Int {}
421+ }
422+
423+ """
424+ var fullText = typeWithMethod
425+
426+ try await sourcekitd. editDocument ( path, fromOffset: 0 , length: 0 , newContents: typeWithMethod)
427+
428+ let completion = """
429+ S.
430+ """
431+ fullText += completion
432+
433+ try await sourcekitd. editDocument ( path, fromOffset: typeWithMethod. utf8. count, length: 0 , newContents: completion)
434+
435+ func testCompletion( file: StaticString = #filePath, line: UInt = #line) async throws {
436+ let result = try await sourcekitd. completeOpen (
437+ path: path,
438+ position: Position ( line: 3 , utf16index: 2 ) ,
439+ filter: " foo " ,
440+ flags: [ ]
441+ )
442+ XCTAssertGreaterThan ( result. unfilteredResultCount, 1 , file: file, line: line)
443+ XCTAssertEqual ( result. items. count, 1 , file: file, line: line)
444+ }
445+ try await testCompletion ( )
446+
447+ // Bogus edits are ignored (negative offsets crash SourceKit itself so we don't test them here).
448+ await assertThrowsError (
449+ try await sourcekitd. editDocument ( path, fromOffset: 0 , length: 99999 , newContents: " " )
450+ )
451+ await assertThrowsError (
452+ try await sourcekitd. editDocument ( path, fromOffset: 99999 , length: 1 , newContents: " " )
453+ )
454+ await assertThrowsError (
455+ try await sourcekitd. editDocument ( path, fromOffset: 99999 , length: 0 , newContents: " unrelated " )
456+ )
457+ // SourceKit doesn't throw an error for a no-op edit.
458+ try await sourcekitd. editDocument ( path, fromOffset: 99999 , length: 0 , newContents: " " )
459+
460+ try await sourcekitd. editDocument ( path, fromOffset: 0 , length: 0 , newContents: " " )
461+ try await sourcekitd. editDocument ( path, fromOffset: fullText. utf8. count, length: 0 , newContents: " " )
462+
463+ try await testCompletion ( )
464+
465+ let badCompletion = """
466+ X.
467+ """
468+ fullText = fullText. dropLast ( 2 ) + badCompletion
469+
470+ try await sourcekitd. editDocument ( path, fromOffset: fullText. utf8. count - 2 , length: 2 , newContents: badCompletion)
471+
472+ let result = try await sourcekitd. completeOpen (
473+ path: path,
474+ position: Position ( line: 3 , utf16index: 2 ) ,
475+ filter: " foo " ,
476+ flags: [ ]
477+ )
478+ XCTAssertEqual ( result. unfilteredResultCount, 0 )
479+ XCTAssertEqual ( result. items. count, 0 )
480+ }
481+
408482 func testDocumentation( ) async throws {
409483 try await SkipUnless . sourcekitdSupportsPlugin ( )
410484 try await SkipUnless . sourcekitdSupportsFullDocumentationInCompletion ( )
0 commit comments