Skip to content

Commit 25a53c1

Browse files
authored
Merge pull request #1324 from ahoppen/6.0/merge-main-2024-05-21
Merge `main` into `release/6.0`
2 parents 1b0d1e4 + 9a06737 commit 25a53c1

File tree

61 files changed

+1874
-669
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1874
-669
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ default.profraw
33
Package.resolved
44
/.build
55
/.index-build
6+
/.linux-build
67
/Packages
78
/*.xcodeproj
89
/*.sublime-project

Documentation/Files_To_Reindex.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Which files to re-index when a file is modified
2+
3+
## `.swift`
4+
5+
Obviously affects the file itself, which we do re-index.
6+
7+
If an internal declaration was changed, all files within the module might be also affected. If a public declaration was changed, all modules that depend on it might be affected. The effect can only really be in three ways:
8+
1. It might change overload resolution in another file, which is fairly unlikely
9+
2. A new declaration is introduced in this file that is already referenced by another file
10+
3. A declaration is removed in this file that was referenced from other files. In those cases the other files now have an invalid reference.
11+
12+
We decided to not re-index any files other than the file itself because naively re-indexing all files that might depend on the modified file requires too much processing power that will likely have no or very little effect on the index – we are trading accuracy for CPU time here.
13+
We mark the targets of the changed file as well as any dependent targets as out-of-date. The assumption is that most likely the user will go back to any of the affected files shortly afterwards and modify them again. When that happens, the affected file will get re-indexed and bring the index back up to date.
14+
15+
Alternatives would be:
16+
- We could we check the file’s interface hash and re-index other files based on whether it has changed. But at that point we are somewhat implementing a build system. And even if a new public method was introduced it’s very likely that the user hasn’t actually used it anywhere yet, which means that re-indexing all dependent modules would still be doing unnecessary work.
17+
- To cover case (2) from above, we could re-index only dependencies that previously indexed with errors. This is an alternative that hasn’t been fully explored.
18+
19+
## `.h`
20+
21+
All files that include the header (including via other headers) might be affected by the change, similar to how all `.swift` files that import a module might be affected. Similar to modules, we choose to not re-index all files that include the header because of the same considerations mentioned above.
22+
23+
To re-index the header, we pick one main file that includes the header and re-index that, which will effectively update the index for the header. For existing headers, we know which files import a header from the existing index. For new header files, we assume that it hasn’t been included in any file yet and thus don't index it. If the user wrote an include to the new header before creating the header itself, we don't know about that include from the existing index. But it’s likely that the user will modify the file that includes the new header file shortly after, which will index the header and establish the header to main file connection.
24+
25+
## `.c` / `.cpp` / `.m`
26+
27+
This is the easy case since only the file itself is affected.
28+
29+
## Compiler settings (`compile_commands.json` / `Package.swift`)
30+
31+
Ideally, we would like to consider a file as changed when its compile commands have changed, if they changed in a meaningful way (ie. in a way that would also trigger re-compilation in an incremental build). Non-meaningful changes would be:
32+
- If compiler arguments that aren't order dependent are shuffled around. We could have a really quick check for compiler arguments equality by comparing them unordered. Any real compiler argument change will most likely do more than rearranging the arguments.
33+
- The addition of a new Swift file to a target is equivalent to that file being modified and shouldn’t trigger a re-index of the entire target.
34+
35+
At the moment, unit files don’t include information about the compiler arguments with which they were created, so it’s impossible to know whether the compiler arguments have changed when a project is opened. Thus, for now, we don’t re-index any files on compiler settings changing.

Editors/README.md

Lines changed: 1 addition & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -22,58 +22,7 @@ The [Swift for Visual Studio Code extension](https://marketplace.visualstudio.co
2222

2323
## Sublime Text
2424

25-
Before using SourceKit-LSP with Sublime Text, you will need to install the LSP package from Package Control. To configure SourceKit-LSP, open the LSP package's settings. The following snippet should be enough to get started with Swift.
26-
27-
You will need the path to the `sourcekit-lsp` executable for the "command" section.
28-
29-
```json
30-
{
31-
"clients":
32-
{
33-
"SourceKit-LSP":
34-
{
35-
"enabled": true,
36-
"command": [
37-
"<sourcekit-lsp command>"
38-
],
39-
"env": {
40-
// To override the toolchain, uncomment the following:
41-
// "SOURCEKIT_TOOLCHAIN_PATH": "<path to toolchain>",
42-
},
43-
"languages": [
44-
{
45-
"scopes": ["source.swift"],
46-
"syntaxes": [
47-
"Packages/Swift/Syntaxes/Swift.tmLanguage",
48-
"Packages/Decent Swift Syntax/Swift.sublime-syntax",
49-
],
50-
"languageId": "swift"
51-
},
52-
{
53-
"scopes": ["source.c"],
54-
"syntaxes": ["Packages/C++/C.sublime-syntax"],
55-
"languageId": "c"
56-
},
57-
{
58-
"scopes": ["source.c++"],
59-
"syntaxes": ["Packages/C++/C++.sublime-syntax"],
60-
"languageId": "cpp"
61-
},
62-
{
63-
"scopes": ["source.objc"],
64-
"syntaxes": ["Packages/Objective-C/Objective-C.sublime-syntax"],
65-
"languageId": "objective-c"
66-
},
67-
{
68-
"scopes": ["source.objc++"],
69-
"syntaxes": ["Packages/Objective-C/Objective-C++.sublime-syntax"],
70-
"languageId": "objective-cpp"
71-
},
72-
]
73-
}
74-
}
75-
}
76-
```
25+
Before using SourceKit-LSP with Sublime Text, you will need to install the [LSP](https://packagecontrol.io/packages/LSP), [LSP-SourceKit](https://github.com/sublimelsp/LSP-SourceKit) and [Swift-Next](https://github.com/Swift-Next/Swift-Next) packages from Package Control. Then toggle the server on by typing in command palette `LSP: Enable Language Server Globally` or `LSP: Enable Language Server in Project`.
7726

7827
## Emacs
7928

Package.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ let package = Package(
182182
exclude: ["CMakeLists.txt"]
183183
),
184184

185+
.testTarget(
186+
name: "SemanticIndexTests",
187+
dependencies: [
188+
"SemanticIndex"
189+
]
190+
),
191+
185192
// MARK: SKCore
186193
// Data structures and algorithms useful across the project, but not necessarily
187194
// suitable for use in other packages.

Sources/LSPLogging/NonDarwinLogging.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,17 @@ public struct NonDarwinLogInterpolation: StringInterpolationProtocol, Sendable {
195195
append(description: message.description, redactedDescription: message.redactedDescription, privacy: privacy)
196196
}
197197

198+
public mutating func appendInterpolation(
199+
_ message: (some CustomLogStringConvertibleWrapper & Sendable)?,
200+
privacy: NonDarwinLogPrivacy = .private
201+
) {
202+
if let message {
203+
self.appendInterpolation(message, privacy: privacy)
204+
} else {
205+
self.appendLiteral("<nil>")
206+
}
207+
}
208+
198209
public mutating func appendInterpolation(_ type: Any.Type, privacy: NonDarwinLogPrivacy = .public) {
199210
append(description: String(reflecting: type), redactedDescription: "<private>", privacy: privacy)
200211
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Dispatch
14+
import LSPLogging
15+
import LanguageServerProtocol
16+
17+
/// A connection between two message handlers in the same process.
18+
///
19+
/// You must call `start(handler:)` before sending any messages, and must call `close()` when finished to avoid a memory leak.
20+
///
21+
/// ```
22+
/// let client: MessageHandler = ...
23+
/// let server: MessageHandler = ...
24+
/// let conn = LocalConnection()
25+
/// conn.start(handler: server)
26+
/// conn.send(...) // handled by server
27+
/// conn.close()
28+
/// ```
29+
///
30+
/// - Note: Unchecked sendable conformance because shared state is guarded by `queue`.
31+
public final class LocalConnection: Connection, @unchecked Sendable {
32+
enum State {
33+
case ready, started, closed
34+
}
35+
36+
/// A name of the endpoint for this connection, used for logging, e.g. `clangd`.
37+
private let name: String
38+
39+
/// The queue guarding `_nextRequestID`.
40+
let queue: DispatchQueue = DispatchQueue(label: "local-connection-queue")
41+
42+
var _nextRequestID: Int = 0
43+
44+
var state: State = .ready
45+
46+
var handler: MessageHandler? = nil
47+
48+
public init(name: String) {
49+
self.name = name
50+
}
51+
52+
deinit {
53+
if state != .closed {
54+
close()
55+
}
56+
}
57+
58+
public func start(handler: MessageHandler) {
59+
precondition(state == .ready)
60+
state = .started
61+
self.handler = handler
62+
}
63+
64+
public func close() {
65+
precondition(state != .closed)
66+
handler = nil
67+
state = .closed
68+
}
69+
70+
func nextRequestID() -> RequestID {
71+
return queue.sync {
72+
_nextRequestID += 1
73+
return .number(_nextRequestID)
74+
}
75+
}
76+
77+
public func send<Notification: NotificationType>(_ notification: Notification) {
78+
logger.info(
79+
"""
80+
Sending notification to \(self.name, privacy: .public)
81+
\(notification.forLogging)
82+
"""
83+
)
84+
self.handler?.handle(notification)
85+
}
86+
87+
public func send<Request: RequestType>(
88+
_ request: Request,
89+
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
90+
) -> RequestID {
91+
let id = nextRequestID()
92+
93+
logger.info(
94+
"""
95+
Sending request to \(self.name, privacy: .public) (id: \(id, privacy: .public)):
96+
\(request.forLogging)
97+
"""
98+
)
99+
100+
guard let handler = self.handler else {
101+
logger.info(
102+
"""
103+
Replying to request \(id, privacy: .public) with .serverCancelled because no handler is specified in \(self.name, privacy: .public)
104+
"""
105+
)
106+
reply(.failure(.serverCancelled))
107+
return id
108+
}
109+
110+
precondition(self.state == .started)
111+
handler.handle(request, id: id) { result in
112+
switch result {
113+
case .success(let response):
114+
logger.info(
115+
"""
116+
Received reply for request \(id, privacy: .public) from \(self.name, privacy: .public)
117+
\(response.forLogging)
118+
"""
119+
)
120+
case .failure(let error):
121+
logger.error(
122+
"""
123+
Received error for request \(id, privacy: .public) from \(self.name, privacy: .public)
124+
\(error.forLogging)
125+
"""
126+
)
127+
}
128+
reply(result)
129+
}
130+
131+
return id
132+
}
133+
}

Sources/LSPTestSupport/TestJSONRPCConnection.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import XCTest
1717

1818
import class Foundation.Pipe
1919

20-
public final class TestJSONRPCConnection {
20+
public final class TestJSONRPCConnection: Sendable {
2121
public let clientToServer: Pipe = Pipe()
2222
public let serverToClient: Pipe = Pipe()
2323

@@ -76,9 +76,9 @@ public final class TestJSONRPCConnection {
7676

7777
public struct TestLocalConnection {
7878
public let client: TestClient
79-
public let clientConnection: LocalConnection = LocalConnection()
79+
public let clientConnection: LocalConnection = LocalConnection(name: "Test")
8080
public let server: TestServer
81-
public let serverConnection: LocalConnection = LocalConnection()
81+
public let serverConnection: LocalConnection = LocalConnection(name: "Test")
8282

8383
public init(allowUnexpectedNotification: Bool = true) {
8484
client = TestClient(connectionToServer: serverConnection, allowUnexpectedNotification: allowUnexpectedNotification)
@@ -151,7 +151,7 @@ public actor TestClient: MessageHandler {
151151
/// Send a request to the LSP server and (asynchronously) receive a reply.
152152
public nonisolated func send<Request: RequestType>(
153153
_ request: Request,
154-
reply: @escaping (LSPResult<Request.Response>) -> Void
154+
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
155155
) -> RequestID {
156156
return connectionToServer.send(request, reply: reply)
157157
}

Sources/LanguageServerProtocol/Connection.swift

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

13-
import Dispatch
14-
1513
/// An abstract connection, allow messages to be sent to a (potentially remote) `MessageHandler`.
1614
public protocol Connection: AnyObject, Sendable {
1715

@@ -47,83 +45,3 @@ public protocol MessageHandler: AnyObject, Sendable {
4745
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
4846
)
4947
}
50-
51-
/// A connection between two message handlers in the same process.
52-
///
53-
/// You must call `start(handler:)` before sending any messages, and must call `close()` when finished to avoid a memory leak.
54-
///
55-
/// ```
56-
/// let client: MessageHandler = ...
57-
/// let server: MessageHandler = ...
58-
/// let conn = LocalConnection()
59-
/// conn.start(handler: server)
60-
/// conn.send(...) // handled by server
61-
/// conn.close()
62-
/// ```
63-
///
64-
/// - Note: Unchecked sendable conformance because shared state is guarded by `queue`.
65-
public final class LocalConnection: Connection, @unchecked Sendable {
66-
67-
enum State {
68-
case ready, started, closed
69-
}
70-
71-
/// The queue guarding `_nextRequestID`.
72-
let queue: DispatchQueue = DispatchQueue(label: "local-connection-queue")
73-
74-
var _nextRequestID: Int = 0
75-
76-
var state: State = .ready
77-
78-
var handler: MessageHandler? = nil
79-
80-
public init() {}
81-
82-
deinit {
83-
if state != .closed {
84-
close()
85-
}
86-
}
87-
88-
public func start(handler: MessageHandler) {
89-
precondition(state == .ready)
90-
state = .started
91-
self.handler = handler
92-
}
93-
94-
public func close() {
95-
precondition(state != .closed)
96-
handler = nil
97-
state = .closed
98-
}
99-
100-
func nextRequestID() -> RequestID {
101-
return queue.sync {
102-
_nextRequestID += 1
103-
return .number(_nextRequestID)
104-
}
105-
}
106-
107-
public func send<Notification>(_ notification: Notification) where Notification: NotificationType {
108-
self.handler?.handle(notification)
109-
}
110-
111-
public func send<Request: RequestType>(
112-
_ request: Request,
113-
reply: @Sendable @escaping (LSPResult<Request.Response>) -> Void
114-
) -> RequestID {
115-
let id = nextRequestID()
116-
117-
guard let handler = self.handler else {
118-
reply(.failure(.serverCancelled))
119-
return id
120-
}
121-
122-
precondition(self.state == .started)
123-
handler.handle(request, id: id) { result in
124-
reply(result)
125-
}
126-
127-
return id
128-
}
129-
}

0 commit comments

Comments
 (0)