The `WorkspaceConfiguration.default` was being used to populate
the `Workspace`; there was an extra step here needed to assure that
we are propagating the trait configuration to the workspace.
The added test assures that non-default traits that are enabled are
indeed processed as enabled.
For ID counters and standalone flags (most sites), drop to .relaxed
since nothing reads other memory based on the atomic's value.
MultiEntrySemaphore.signaled is the exception: it is used as a
signal/wait primitive where waiters proceed to use state set up
before signal(). Use .releasing on store and .acquiring on load so
that pre-signal writes are visible to waiters that observe `true`.
.relaxed there would be incorrect on weakly-ordered architectures.
Replace AtomicBool/UInt8/UInt32/Int32 from
ToolsProtocolsSwiftExtensions with Synchronization.Atomic<T> where
the storage is a static, module-level let, or class stored property.
For local lets captured by @Sendable closures (where Atomic's
~Copyable nature prevents capture), use ThreadSafeBox<T> instead.
Rename languageServices(for:), primaryLanguageService(for:), and their
internal counterparts to use the `forOpenDocument` label, so the
precondition that the document must already be open is visible at call
sites.
Also make primaryLanguageService(forOpenDocument:) throw instead of
returning an optional, and switch several resolve-style handlers from
the find-or-create primaryLanguageService(for:_:) to
primaryLanguageService(forOpenDocument:), since those call sites already
have an open document in hand.
vscode provides an alternative action if the target of goto definition is in the same character range we requested.
this requires the start and end in the LSP response to be properly set, so vscode can see that the current character
is actually part of the request
Replace 'weak var' with 'weak let' for weak reference properties that are
set in init and never reassigned, using the Swift 6.3 feature introduced
by SE-0481.
Previously, language services were held in a global registry on
SourceKitLSPServer and shared across workspaces, requiring complex
lifetime tracking (isImmortal, shutdownOrphanedLanguageServices) to
decide when to tear them down. In practice, every language service
already stored workspace-specific properties (buildServerManager,
semanticIndexManagerTask), so sharing them across workspaces was never
truly safe. Giving each Workspace its own service instances simplifies
lifetime management: services are created when needed and shut down
with their workspace.
Remove LanguageService.isImmortal, the workspace parameter from
canHandle(toolchain:), and the initialize/clientInitialized protocol
requirements.
The buildSettingsFile key is an implementation detail of Workspace's
language-service dictionary, so the guard that prevents removing a
still-needed service belongs there rather than in SourceKitLSPServer.
Closing a generated interface document (sourcekit-lsp:// URI) was
removing the language service for the originating source file because
both share the same buildSettingsFile key. Guard the removal so it
only happens when no other open document shares that key.
Relates to #2209
- **`sourcekit/workspace/symbolNames`** — returns a flat, deduplicated
list of every symbol name in the workspace index (source and indexed
system modules). Clients use this to drive their search UI locally.
- **`sourcekit/workspace/symbolInfo`** — given a list of exact symbol
names, returns `WorkspaceSymbolItem` for each occurrence across all
workspaces, for display in the search result list. Source-file symbols
get `SymbolInformation` with a `file://` location. SDK/stdlib symbols
get a `WorkspaceSymbol` with `location: .uri(…)` The client must call
`workspaceSymbol/resolve` after the user selects an SDK/stdlib symbol to
obtain the concrete interface location.
- **`workspaceSymbol/resolve`** — resolves the deferred
`WorkspaceSymbol` location from `sourcekit/workspace/symbolInfo`. Parses
the `?module=` value into `moduleName`/`groupName`, finds a real source
file via `mainFiles(containing:)`, calls `openGeneratedInterface`, and
returns the symbol with `location` replaced by a full
`sourcekit-lsp://generated-swift-interface/` URI + range (or a temp
`file://` path for clients without `workspace/getReferenceDocument`
support).
- WorkspaceEdit.adjusted(for:) was using originalURI(for:) ?? uri, which
skips the file-existence check; switch to adjustedURI(for:)
- TypeHierarchyItem.adjusted(for:) was not adjusting the URI stored in
data, unlike CallHierarchyItem
`CopiedFileMap` previously held methods like
`workspaceEditAdjustedForCopiedFiles`,
`callHierarchyItemAdjustedForCopiedFiles`, etc. that encoded knowledge
of high-level LSP message types into `BuildServerIntegration`.
Replace them with `adjusted(for:)` methods on each LSP type
(`Location`, `[Location]`, `LocationsOrLocationLinksResponse`,
`WorkspaceEdit`, `CallHierarchyItem`, `TypeHierarchyItem`) in a new
`LSP+CopiedFileMap.swift` in the SourceKitLSP module. The primitive
URI remapping is extracted into `CopiedFileMap.adjustedURI(for:)`
which remains in `BuildServerIntegration`.
`CallHierarchyItem.adjusted(for:)` also drops manual `LSPAny`
dictionary construction in favour of `HierarchyItemData`.
Introduce `SourceKitDCore` as the protocol boundary between dylib
lifecycle management and the high-level `SourceKitD` API. Its single
lifecycle entry point, `initializeService(api:notificationCallback:)`,
receives the already-loaded `sourcekitd_api_functions_t` from
`SourceKitD.init(core:)`.
`SourceKitDCoreImpl` is the standard implementation: `init` opens the
dylib; `initializeService` registers any plugin paths, calls
`api.initialize()`, and wires the notification handler; `deinit` calls
`shutdown()` and closes the handle. Pre-initialized conformances
implement `initializeService` as a no-op.
Wire a `sourcekitdCoreInjector` hook through `Hooks` so an embedding
host can return a pre-initialized `SourceKitDCore` for a given toolchain
path, preventing `sourcekitd_initialize()` from being called a second
time.
Declare `SourceKitDCoreForPlugin` at its use sites so each call site
can express the exact deinit behavior it needs: `dlclose` for handles
acquired via `RTLD_NOLOAD`, and `leak` for externally-owned handles.
Previously, we always recomputed the inlay hints by calling into
SourceKit. If the document that we compute inlay hints for has recently
changed, SourceKit may need a bit of time to recompute the semantic
analysis. The inlay hints may thus need roughly 200-700ms to be
computed. This causes flickering in VSCode as it removes the old inlay
hints immediately and only displays them again when SourceKit-LSP
returned them.
With this commit we cache the inlay hints and recompute them in the
background. After the recompute is finished we use
`workspace/inlayHint/refresh` request to tell the client to refresh its
inlay hints. On each textDocument/didChange request the cached inlay
hints are shifted according to the text edits to ensure their positions
are still correct. This avoids the flickering as we can always return
the cached inlay hints immediately. The returned hints may however
temporarily show outdated type information until the background
recompute is finished.
Extract the copied-file adjustment logic from BuildServerManager into a
Sendable value type CopiedFileMap. Call sites snapshot cachedCopiedFileMap
once per request (hoisted before loops) instead of awaiting the actor on
every iteration.
Add computed properties to `SymbolLocation` that centralise the
index-to-LSP coordinate conversion:
- `uri: DocumentURI?` — returns nil when `path` is empty.
- `lspPosition: Position` — converts the 1-based line/utf8Column to a
0-based LSP Position, using UTF-8 column as a UTF-16 approximation.
- `lspLocation: Location?` — wraps `documentUri` + `lspPosition` into
an LSP Location, returning nil when `path` is empty.
Update all call sites.
SwiftPM PR #9985 introduce files in the scratch path whose contents
indicate which build system was used to build artifact for a given build
configuration. if this file exist, use it's contents to infer the build
system. Otherwise, fallback to the existing behaviour.
Fixes: #2576
Binary targets backed by remote `.artifactbundleindex` URLs cause
SwiftPM to re-extract the artifact bundle into the scratch directory
on every package load. The resulting file-change events (delete +
create) for the extracted files triggered another reload, causing an
infinite loop.
Add `isInScratchDirectory` to `fileEventShouldTriggerPackageReload`
to filter out events before the build-settings check. It covers
both the configured scratch directory and the default `.build/`
directory (which receives regular `swift build` output even when
a custom scratch path is configured).
Also add tests using local zip-based binary targets to cover both
the default and custom-scratch-path scenarios. Local zips are used
in the tests to avoid a network dependency; they exercise the same
`isInScratchDirectory` code path even though the infinite loop in
practice requires a remote `.artifactbundleindex` target (where
SwiftPM's checksum mismatch causes re-extraction on every load).
https://github.com/swiftlang/sourcekit-lsp/issues/2615