Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/SourceKitLSP/Swift/SwiftLanguageService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,7 @@ extension SwiftLanguageService {
inFlightPublishDiagnosticsTasks[notification.textDocument.uri] = nil
await diagnosticReportManager.removeItemsFromCache(with: notification.textDocument.uri)
buildSettingsForOpenFiles[notification.textDocument.uri] = nil
await syntaxTreeManager.clearSyntaxTrees(for: notification.textDocument.uri)
switch try? ReferenceDocumentURL(from: notification.textDocument.uri) {
case .macroExpansion:
break
Expand Down
6 changes: 6 additions & 0 deletions Sources/SourceKitLSP/Swift/SyntaxTreeManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//

import LanguageServerProtocol
import SKUtilities
import SwiftParser
import SwiftSyntax
Expand Down Expand Up @@ -94,4 +95,9 @@ actor SyntaxTreeManager {
}
self.setComputation(for: postEditSnapshot.id, computation: incrementalParseComputation)
}

/// Remove all cached syntax trees for the given document, eg. when the document is closed.
func clearSyntaxTrees(for uri: DocumentURI) {
syntaxTreeComputations.removeAll(where: { $0.uri == uri })
}
}
49 changes: 49 additions & 0 deletions Tests/SourceKitLSPTests/SemanticTokensTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,55 @@ final class SemanticTokensTests: XCTestCase {
)
}

func testCloseAndReopenDocumentWithSameDocumentVersion() async throws {
// When neovim detects a change of the document on-disk (eg. caused by git operations). It closes the document and
// re-opens it with the same document version but different contents. Check that we don't re-use the syntax tree of
// the previously opened document.
let testClient = try await TestSourceKitLSPClient()
let uri = DocumentURI(for: .swift)
let initialPositions = testClient.openDocument(
"""
1️⃣import 2️⃣Foo
3️⃣func 4️⃣bar() {}
""",
uri: uri
)
let initialTokens = try await testClient.send(
DocumentSemanticTokensRequest(textDocument: TextDocumentIdentifier(uri))
)

XCTAssertEqual(
SyntaxHighlightingTokens(lspEncodedTokens: try unwrap(initialTokens).data).tokens,
[
Token(start: initialPositions["1️⃣"], utf16length: 6, kind: .keyword),
Token(start: initialPositions["2️⃣"], utf16length: 3, kind: .identifier),
Token(start: initialPositions["3️⃣"], utf16length: 4, kind: .keyword),
Token(start: initialPositions["4️⃣"], utf16length: 3, kind: .identifier),
]
)

testClient.send(DidCloseTextDocumentNotification(textDocument: TextDocumentIdentifier(uri)))

let reopenedPositions = testClient.openDocument(
"""
1️⃣func 2️⃣bar() {}
""",
uri: uri
)

let reopenedTokens = try await testClient.send(
DocumentSemanticTokensRequest(textDocument: TextDocumentIdentifier(uri))
)

XCTAssertEqual(
SyntaxHighlightingTokens(lspEncodedTokens: try unwrap(reopenedTokens).data).tokens,
[
Token(start: reopenedPositions["1️⃣"], utf16length: 4, kind: .keyword),
Token(start: reopenedPositions["2️⃣"], utf16length: 3, kind: .identifier),
]
)
}

func testClang() async throws {
try await assertSemanticTokens(
markedContents: """
Expand Down