Skip to content

Commit 45534b0

Browse files
authored
Merge pull request #2223 from ahoppen/neovim-tokens-out-of-sync
Fix issue that cause semantic tokens to be out-of-sync in neovim after a document changes on disk
2 parents 5df8f3d + 8d454e6 commit 45534b0

File tree

3 files changed

+56
-0
lines changed

3 files changed

+56
-0
lines changed

Sources/SourceKitLSP/Swift/SwiftLanguageService.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ extension SwiftLanguageService {
571571
inFlightPublishDiagnosticsTasks[notification.textDocument.uri] = nil
572572
await diagnosticReportManager.removeItemsFromCache(with: notification.textDocument.uri)
573573
buildSettingsForOpenFiles[notification.textDocument.uri] = nil
574+
await syntaxTreeManager.clearSyntaxTrees(for: notification.textDocument.uri)
574575
switch try? ReferenceDocumentURL(from: notification.textDocument.uri) {
575576
case .macroExpansion:
576577
break

Sources/SourceKitLSP/Swift/SyntaxTreeManager.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13+
import LanguageServerProtocol
1314
import SKUtilities
1415
import SwiftParser
1516
import SwiftSyntax
@@ -94,4 +95,9 @@ actor SyntaxTreeManager {
9495
}
9596
self.setComputation(for: postEditSnapshot.id, computation: incrementalParseComputation)
9697
}
98+
99+
/// Remove all cached syntax trees for the given document, eg. when the document is closed.
100+
func clearSyntaxTrees(for uri: DocumentURI) {
101+
syntaxTreeComputations.removeAll(where: { $0.uri == uri })
102+
}
97103
}

Tests/SourceKitLSPTests/SemanticTokensTests.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,55 @@ final class SemanticTokensTests: XCTestCase {
913913
)
914914
}
915915

916+
func testCloseAndReopenDocumentWithSameDocumentVersion() async throws {
917+
// When neovim detects a change of the document on-disk (eg. caused by git operations). It closes the document and
918+
// re-opens it with the same document version but different contents. Check that we don't re-use the syntax tree of
919+
// the previously opened document.
920+
let testClient = try await TestSourceKitLSPClient()
921+
let uri = DocumentURI(for: .swift)
922+
let initialPositions = testClient.openDocument(
923+
"""
924+
1️⃣import 2️⃣Foo
925+
3️⃣func 4️⃣bar() {}
926+
""",
927+
uri: uri
928+
)
929+
let initialTokens = try await testClient.send(
930+
DocumentSemanticTokensRequest(textDocument: TextDocumentIdentifier(uri))
931+
)
932+
933+
XCTAssertEqual(
934+
SyntaxHighlightingTokens(lspEncodedTokens: try unwrap(initialTokens).data).tokens,
935+
[
936+
Token(start: initialPositions["1️⃣"], utf16length: 6, kind: .keyword),
937+
Token(start: initialPositions["2️⃣"], utf16length: 3, kind: .identifier),
938+
Token(start: initialPositions["3️⃣"], utf16length: 4, kind: .keyword),
939+
Token(start: initialPositions["4️⃣"], utf16length: 3, kind: .identifier),
940+
]
941+
)
942+
943+
testClient.send(DidCloseTextDocumentNotification(textDocument: TextDocumentIdentifier(uri)))
944+
945+
let reopenedPositions = testClient.openDocument(
946+
"""
947+
1️⃣func 2️⃣bar() {}
948+
""",
949+
uri: uri
950+
)
951+
952+
let reopenedTokens = try await testClient.send(
953+
DocumentSemanticTokensRequest(textDocument: TextDocumentIdentifier(uri))
954+
)
955+
956+
XCTAssertEqual(
957+
SyntaxHighlightingTokens(lspEncodedTokens: try unwrap(reopenedTokens).data).tokens,
958+
[
959+
Token(start: reopenedPositions["1️⃣"], utf16length: 4, kind: .keyword),
960+
Token(start: reopenedPositions["2️⃣"], utf16length: 3, kind: .identifier),
961+
]
962+
)
963+
}
964+
916965
func testClang() async throws {
917966
try await assertSemanticTokens(
918967
markedContents: """

0 commit comments

Comments
 (0)