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.
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.
- **`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).
When the client opts in to `workspace/tests/refresh` or
`workspace/playgrounds/refresh` via experimental client capabilities,
SourceKit-LSP now maintains a proactive cache of the current test and
playground lists and sends the corresponding `workspace/.../refresh`
notification whenever the cache changes. `workspaceTests()` /
`workspacePlaygrounds()` then serve subsequent fetch requests directly
from the cache.
Add `EntryPointManager`: runs background scans, stores the results,
fires callbacks on changes:
- Start scanning when build targets are updated including initial
updates, any watched files are changed, and index is updated.
- Send '/refresh' server initiated requests when the cache has changed.
- Coalesces rapid invalidations by cancelling any in-flight refresh task.
Also:
- Simplify `SourceKitIndexDelegate` from an `actor` with `AtomicInt32`
to a plain `class`, since it is only called from IndexStoreDB's
internal serial dispatch queue.
Previously, test discovery used the semantic index as the primary
source and fell back to the syntactic index only for files where the
semantic index was out-of-date. This meant test locations came from the
semantic index, which only records a point position rather than the
full symbol range.
Flip the priority: use syntactic scan results as the primary source
(which have correct location ranges) and supplement with semantic index
results. The semantic results are range-fixed via
'textDocument/documentSymbol' before being returned.
This logic is unified into a single 'combineTests' helper shared by
both 'workspaceTests' and 'documentTests'.
Also fix 'SyntacticSwiftXCTestScanner' to emit extensions as proper
'AnnotatedTestItem' nodes (with 'isExtension: true') rather than a flat
list of methods, so extension test methods are correctly merged into
their class via 'mergingTestsInExtensions'. The class and extension
visitors are unified through a shared 'handleClassOrExtension' helper.
Shut down language services when a workspace is closed
- Added allLanguageServices property to Workspace to get all services it references
- Added shutdownOrphanedLanguageServices to clean up services no longer in use
- When workspace folders are removed, we now shut down their associated language services
- This properly terminates clangd and other language server processes when workspaces close
Closes#2209
- Only call `setLanguageServices` from `openDocument` to avoid race conditions
- Remove language services when documents are closed via `removeLanguageServices`
- `SourceKitLSPServer.languageServices` now just returns services without storing them
- Fixed a small typo (serveer -> server) while I was in there
This way the languageServices dictionary only tracks documents that are actually open,
and we avoid race conditions since openDocument is a blocking request for that document.
Apply the following changes:
- Check for the presence of `#Playgrounds` textually before getting the module name in `SwiftPlaygroundsScanner`. This is important because getting the module name requires us to get build settings for the file, which can be expensive. Do the cheaper check first
- Make `syntacticTests` and `syntacticPlaygrounds` closures capture the workspace instead of passing the workspace from the `SwiftSyntacticIndex` back out. I like this better because now we can’t accidentally pass the wrong workspace to a `SwiftSyntacticIndex`, eg. to `buildTargetsChanges`.
- Capture the initialize result in `TestSourceKitLSPClient` instead of using `postInitialization` to capture the result
- Minor cleanup of unnecessary abstractions, likely artifacts of earlier iterations
- Restructure tests so that every test has its own list of source files, allowing for easier local reasoning – turns out some of these tests didn’t even need to open a workspace, just to check the initialize response
We previously waited for the initialization response from the build server during the creation of a `Workspace` so that we could create a `SemanticIndexManager` with the index store path etc. that was returned by the `build/initialize` response. This caused all functionality (including syntactic) of SourceKit-LSP to be blocked until the build server was initialized.
Change the computation of the `SemanticIndexManager` and related types to happen in the background so that we can provide functionality that doesn’t rely on the build server immediately.
Fixes#2304
I suspect that we don’t wait for `TestSourceKitLSPClient` to finish deinitializing (and thus waiting for the shutdown response) when we destroy it in `tearDown` based on the logs in https://ci.swift.org/job/oss-swift-incremental-RA-macos-apple-silicon/9004 (rdar://160344405).
Since I generally dislike the `setUp` and `tearDown` methods and we have `CustomBuildServerTestProject` now to model a setup of a SourceKitLSP server with a custom build server, use that instead of manually hooking up the build server through a workspace.
When I added the log structure to `build/logMessage` in #2022 I must have assumed that the entire BSP notifciation was an extension defined by SourceKit-LSP and didn’t realized that this was actually a change that made the notification non-compliant with BSP. Change it up a little bit to make it compliant again.
If a build server copies files (eg. header) to the build directory during preparation and those copied files are referenced for semantic functionality, we would currently jump to the file in the build directory. Teach SourceKit-LSP about files that are copied during preparation and if we detect that we are jumping to such a file, jump to the original file instead.
So far only the definition request checks the copied file paths. Adding support for copied file paths in the other requests will be a follow-up change.
This allows us to implement all of `doccDocumentation` in `DocumentationLanguageService`. `DocumentationLanguageService` will be a secondary language service for Swift files and can also provide the docc documentation support that’s currently in `SwiftLangaugeService`.
The term *build system* predated our wide-spread adoption of BSP for communicating between SourceKit-LSP to the build system and was never really the correct term anyway – ie. a `JSONCompilationDatabaseBuildSystem` never really sounded right. We now have a correct term for the communication layer between SourceKit-LSP: A build server. Rename most occurrences of *build system* to *build server* to reflect this. There are unfortunately a couple lingering instances of *build system* that we can’t change, most notably: `fallbackBuildSystem` in the config file, the `workspace/waitForBuildSystemUpdates` BSP extension request and the `synchronize-for-build-system-updates` experimental feature.
Since we re-index source files if the build server sends us a `buildTarget/didChange` notification, we no longer need to wait for an up-to-date build graph when a file is modified. This resolves an issue that causes a `Scheduling tasks` progress to appear in the status bar whenever a file in the project is changed. Before https://github.com/swiftlang/sourcekit-lsp/pull/1973, this happened fairly frequently during the initial indexing when a header file was written into the build directory. After that PR the `Scheduling Indexing` status appears a lot more frequently, namely every time the index’s database is modified, which happens quite all the time during indexing...
If a source file is part of multiple targets, we should index it in the context of all of those targets because the different targets may produce different USRs since they might use different build settings to interpret the file.
Technically, the watched files notification can change the response of any other request (eg. because a target needs to be re-prepared). But treating it as a `globalConfiguration` inserts a lot of barriers in request handling and significantly prevents parallelism. Since many editors batch file change notifications already, they might have delayed the file change notification even more, which is equivalent to handling the notification a little later inside SourceKit-LSP. Thus, treating it as `freestanding` should be acceptable.
Also, simplify the logic needed in tests to write modified files to disk because we now need to run a barrier request in tests to ensure that the watched file notification has been handled.