Skip to content

Commit e71aa5d

Browse files
authored
Merge pull request #1214 from ahoppen/background-index-preparation
Miscellaneous commits in preparation for background indexing
2 parents ccea54f + 28d0dd3 commit e71aa5d

33 files changed

+451
-191
lines changed

Package.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,18 @@ let package = Package(
168168
]
169169
),
170170

171+
// MARK: SemanticIndex
172+
173+
.target(
174+
name: "SemanticIndex",
175+
dependencies: [
176+
"LSPLogging",
177+
"SKCore",
178+
.product(name: "IndexStoreDB", package: "indexstore-db"),
179+
],
180+
exclude: ["CMakeLists.txt"]
181+
),
182+
171183
// MARK: SKCore
172184
// Data structures and algorithms useful across the project, but not necessarily
173185
// suitable for use in other packages.
@@ -300,9 +312,11 @@ let package = Package(
300312
name: "SourceKitLSP",
301313
dependencies: [
302314
"BuildServerProtocol",
315+
"CAtomics",
303316
"LanguageServerProtocol",
304317
"LanguageServerProtocolJSONRPC",
305318
"LSPLogging",
319+
"SemanticIndex",
306320
"SKCore",
307321
"SKSupport",
308322
"SKSwiftPMWorkspace",

Sources/CAtomics/include/CAtomics.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,32 @@ static inline void atomic_uint8_set(AtomicUInt8 *atomic, uint8_t newValue) {
6363
atomic->value = newValue;
6464
}
6565

66+
// MARK: AtomicInt
67+
68+
typedef struct {
69+
_Atomic(int) value;
70+
} AtomicUInt32;
71+
72+
__attribute__((swift_name("AtomicUInt32.init(initialValue:)")))
73+
static inline AtomicUInt32 atomic_int_create(uint8_t initialValue) {
74+
AtomicUInt32 atomic;
75+
atomic.value = initialValue;
76+
return atomic;
77+
}
78+
79+
__attribute__((swift_name("getter:AtomicUInt32.value(self:)")))
80+
static inline uint32_t atomic_int_get(AtomicUInt32 *atomic) {
81+
return atomic->value;
82+
}
83+
84+
__attribute__((swift_name("setter:AtomicUInt32.value(self:_:)")))
85+
static inline void atomic_uint32_set(AtomicUInt32 *atomic, uint32_t newValue) {
86+
atomic->value = newValue;
87+
}
88+
89+
__attribute__((swift_name("AtomicUInt32.fetchAndIncrement(self:)")))
90+
static inline uint32_t atomic_uint32_fetch_and_increment(AtomicUInt32 *atomic) {
91+
return atomic->value++;
92+
}
93+
6694
#endif // SOURCEKITLSP_CATOMICS_H

Sources/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_subdirectory(Diagnose)
55
add_subdirectory(LanguageServerProtocol)
66
add_subdirectory(LanguageServerProtocolJSONRPC)
77
add_subdirectory(LSPLogging)
8+
add_subdirectory(SemanticIndex)
89
add_subdirectory(SKCore)
910
add_subdirectory(SKSupport)
1011
add_subdirectory(SKSwiftPMWorkspace)

Sources/LSPLogging/Logging.swift

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,6 @@
2121

2222
import Foundation
2323

24-
/// The subsystem that should be used for any logging by default.
25-
public let subsystem = "org.swift.sourcekit-lsp"
26-
2724
#if canImport(os) && !SOURCEKITLSP_FORCE_NON_DARWIN_LOGGER
2825
import os // os_log
2926

@@ -44,5 +41,5 @@ public typealias Signposter = NonDarwinSignposter
4441

4542
/// The logger that is used to log any messages.
4643
public var logger: Logger {
47-
Logger(subsystem: subsystem, category: LoggingScope.scope)
44+
Logger(subsystem: LoggingScope.subsystem, category: LoggingScope.scope)
4845
}

Sources/LSPLogging/LoggingScope.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,50 @@
1313
import Foundation
1414

1515
public final class LoggingScope {
16+
/// The name of the current logging subsystem or `nil` if no logging scope is set.
17+
@TaskLocal fileprivate static var _subsystem: String?
18+
1619
/// The name of the current logging scope or `nil` if no logging scope is set.
1720
@TaskLocal fileprivate static var _scope: String?
1821

22+
/// The name of the current logging subsystem.
23+
public static var subsystem: String {
24+
return _subsystem ?? "org.swift.sourcekit-lsp"
25+
}
26+
1927
/// The name of the current logging scope.
2028
public static var scope: String {
2129
return _scope ?? "default"
2230
}
2331
}
2432

33+
/// Logs all messages created from the operation to the given subsystem.
34+
///
35+
/// This overrides the current logging subsystem.
36+
///
37+
/// - Note: Since this stores the logging subsystem in a task-local value, it only works when run inside a task.
38+
/// Outside a task, this is a no-op.
39+
public func withLoggingSubsystemAndScope<Result>(
40+
subsystem: String,
41+
scope: String?,
42+
_ operation: () throws -> Result
43+
) rethrows -> Result {
44+
return try LoggingScope.$_subsystem.withValue(subsystem) {
45+
return try LoggingScope.$_scope.withValue(scope, operation: operation)
46+
}
47+
}
48+
49+
/// Same as `withLoggingSubsystemAndScope` but allows the operation to be `async`.
50+
public func withLoggingSubsystemAndScope<Result>(
51+
subsystem: String,
52+
scope: String?,
53+
_ operation: () async throws -> Result
54+
) async rethrows -> Result {
55+
return try await LoggingScope.$_subsystem.withValue(subsystem) {
56+
return try await LoggingScope.$_scope.withValue(scope, operation: operation)
57+
}
58+
}
59+
2560
/// Create a new logging scope, which will be used as the category in any log messages created from the operation.
2661
///
2762
/// This overrides the current logging scope.

Sources/SKCore/BuildServerBuildSystem.swift

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,14 @@ extension BuildServerBuildSystem: BuildSystem {
263263
///
264264
/// Returns `nil` if no build settings have been received from the build
265265
/// server yet or if no build settings are available for this file.
266-
public func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings? {
266+
public func buildSettings(for document: DocumentURI, language: Language) async -> FileBuildSettings? {
267267
return buildSettings[document]
268268
}
269269

270+
public func defaultLanguage(for document: DocumentURI) async -> Language? {
271+
return nil
272+
}
273+
270274
public func registerForChangeNotifications(for uri: DocumentURI) {
271275
let request = RegisterForChanges(uri: uri, action: .register)
272276
_ = self.buildServer?.send(request) { result in
@@ -317,14 +321,14 @@ extension BuildServerBuildSystem: BuildSystem {
317321
return .unhandled
318322
}
319323

320-
public func testFiles() async -> [DocumentURI] {
321-
// BuildServerBuildSystem does not support syntactic test discovery
324+
public func sourceFiles() async -> [SourceFileInfo] {
325+
// BuildServerBuildSystem does not support syntactic test discovery or background indexing.
322326
// (https://github.com/apple/sourcekit-lsp/issues/1173).
323327
return []
324328
}
325329

326-
public func addTestFilesDidChangeCallback(_ callback: @escaping () async -> Void) {
327-
// BuildServerBuildSystem does not support syntactic test discovery
330+
public func addSourceFilesDidChangeCallback(_ callback: @escaping () async -> Void) {
331+
// BuildServerBuildSystem does not support syntactic test discovery or background indexing.
328332
// (https://github.com/apple/sourcekit-lsp/issues/1173).
329333
}
330334
}

Sources/SKCore/BuildSystem.swift

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@ public enum FileHandlingCapability: Comparable, Sendable {
2727
case handled
2828
}
2929

30+
public struct SourceFileInfo: Sendable {
31+
/// The URI of the source file.
32+
public let uri: DocumentURI
33+
34+
/// Whether the file might contain test cases. This property is an over-approximation. It might be true for files
35+
/// from non-test targets or files that don't actually contain any tests. Keeping this list of files with
36+
/// `mayContainTets` minimal as possible helps reduce the amount of work that the syntactic test indexer needs to
37+
/// perform.
38+
public let mayContainTests: Bool
39+
40+
public init(uri: DocumentURI, mayContainTests: Bool) {
41+
self.uri = uri
42+
self.mayContainTests = mayContainTests
43+
}
44+
}
45+
3046
/// Provider of FileBuildSettings and other build-related information.
3147
///
3248
/// The primary role of the build system is to answer queries for
@@ -71,6 +87,13 @@ public protocol BuildSystem: AnyObject, Sendable {
7187
/// file or if it hasn't computed build settings for the file yet.
7288
func buildSettings(for document: DocumentURI, language: Language) async throws -> FileBuildSettings?
7389

90+
/// If the build system has knowledge about the language that this document should be compiled in, return it.
91+
///
92+
/// This is used to determine the language in which a source file should be background indexed.
93+
///
94+
/// If `nil` is returned, the language based on the file's extension.
95+
func defaultLanguage(for document: DocumentURI) async -> Language?
96+
7497
/// Register the given file for build-system level change notifications, such
7598
/// as command line flag changes, dependency changes, etc.
7699
///
@@ -88,18 +111,13 @@ public protocol BuildSystem: AnyObject, Sendable {
88111

89112
func fileHandlingCapability(for uri: DocumentURI) async -> FileHandlingCapability
90113

91-
/// Returns the list of files that might contain test cases.
92-
///
93-
/// The returned file list is an over-approximation. It might contain tests from non-test targets or files that don't
94-
/// actually contain any tests. Keeping this list as minimal as possible helps reduce the amount of work that the
95-
/// syntactic test indexer needs to perform.
96-
func testFiles() async -> [DocumentURI]
114+
/// Returns the list of source files in the project.
115+
func sourceFiles() async -> [SourceFileInfo]
97116

98-
/// Adds a callback that should be called when the value returned by `testFiles()` changes.
117+
/// Adds a callback that should be called when the value returned by `sourceFiles()` changes.
99118
///
100-
/// The callback might also be called without an actual change to `testFiles`.
101-
func addTestFilesDidChangeCallback(_ callback: @Sendable @escaping () async -> Void) async
119+
/// The callback might also be called without an actual change to `sourceFiles`.
120+
func addSourceFilesDidChangeCallback(_ callback: @Sendable @escaping () async -> Void) async
102121
}
103122

104-
public let buildTargetsNotSupported =
105-
ResponseError.methodNotFound(BuildTargets.method)
123+
public let buildTargetsNotSupported = ResponseError.methodNotFound(BuildTargets.method)

Sources/SKCore/BuildSystemManager.swift

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public actor BuildSystemManager {
5050
/// Build system delegate that will receive notifications about setting changes, etc.
5151
var delegate: BuildSystemDelegate?
5252

53+
/// The list of toolchains that are available.
54+
///
55+
/// Used to determine which toolchain to use for a given document.
56+
private let toolchainRegistry: ToolchainRegistry
57+
5358
/// The root of the project that this build system manages. For example, for SwiftPM packages, this is the folder
5459
/// containing Package.swift. For compilation databases it is the root folder based on which the compilation database
5560
/// was found.
@@ -65,13 +70,15 @@ public actor BuildSystemManager {
6570
buildSystem: BuildSystem?,
6671
fallbackBuildSystem: FallbackBuildSystem?,
6772
mainFilesProvider: MainFilesProvider?,
73+
toolchainRegistry: ToolchainRegistry,
6874
fallbackSettingsTimeout: DispatchTimeInterval = .seconds(3)
6975
) async {
7076
let buildSystemHasDelegate = await buildSystem?.delegate != nil
7177
precondition(!buildSystemHasDelegate)
7278
self.buildSystem = buildSystem
7379
self.fallbackBuildSystem = fallbackBuildSystem
7480
self.mainFilesProvider = mainFilesProvider
81+
self.toolchainRegistry = toolchainRegistry
7582
self.fallbackSettingsTimeout = fallbackSettingsTimeout
7683
await self.buildSystem?.setDelegate(self)
7784
}
@@ -87,11 +94,34 @@ extension BuildSystemManager {
8794
self.delegate = delegate
8895
}
8996

97+
/// Returns the toolchain that should be used to process the given document.
98+
public func toolchain(for uri: DocumentURI, _ language: Language) async -> Toolchain? {
99+
// To support multiple toolchains within a single workspace, we need to ask the build system which toolchain to use
100+
// for this document.
101+
return await toolchainRegistry.defaultToolchain(for: language)
102+
}
103+
90104
/// - Note: Needed so we can set the delegate from a different isolation context.
91105
public func setMainFilesProvider(_ mainFilesProvider: MainFilesProvider?) {
92106
self.mainFilesProvider = mainFilesProvider
93107
}
94108

109+
/// Returns the language that a document should be interpreted in for background tasks where the editor doesn't
110+
/// specify the document's language.
111+
public func defaultLanguage(for document: DocumentURI) async -> Language? {
112+
if let defaultLanguage = await buildSystem?.defaultLanguage(for: document) {
113+
return defaultLanguage
114+
}
115+
switch document.fileURL?.pathExtension {
116+
case "c": return .c
117+
case "cpp", "cc", "cxx": return .cpp
118+
case "m": return .objective_c
119+
case "mm", "h": return .objective_cpp
120+
case "swift": return .swift
121+
default: return nil
122+
}
123+
}
124+
95125
private func buildSettings(
96126
for document: DocumentURI,
97127
language: Language
@@ -177,8 +207,17 @@ extension BuildSystemManager {
177207
)
178208
}
179209

210+
public func sourceFiles() async -> [SourceFileInfo] {
211+
return await buildSystem?.sourceFiles() ?? []
212+
}
213+
180214
public func testFiles() async -> [DocumentURI] {
181-
return await buildSystem?.testFiles() ?? []
215+
return await sourceFiles().compactMap { (info: SourceFileInfo) -> DocumentURI? in
216+
guard info.mayContainTests else {
217+
return nil
218+
}
219+
return info.uri
220+
}
182221
}
183222
}
184223

Sources/SKCore/CompilationDatabaseBuildSystem.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
114114
)
115115
}
116116

117+
public func defaultLanguage(for document: DocumentURI) async -> Language? {
118+
return nil
119+
}
120+
117121
public func registerForChangeNotifications(for uri: DocumentURI) async {
118122
self.watchedFiles.insert(uri)
119123
}
@@ -192,11 +196,16 @@ extension CompilationDatabaseBuildSystem: BuildSystem {
192196
}
193197
}
194198

195-
public func testFiles() async -> [DocumentURI] {
196-
return compdb?.allCommands.map { DocumentURI($0.url) } ?? []
199+
public func sourceFiles() async -> [SourceFileInfo] {
200+
guard let compdb else {
201+
return []
202+
}
203+
return compdb.allCommands.map {
204+
SourceFileInfo(uri: DocumentURI($0.url), mayContainTests: true)
205+
}
197206
}
198207

199-
public func addTestFilesDidChangeCallback(_ callback: @escaping () async -> Void) async {
208+
public func addSourceFilesDidChangeCallback(_ callback: @escaping () async -> Void) async {
200209
testFilesDidChangeCallbacks.append(callback)
201210
}
202211
}

0 commit comments

Comments
 (0)