Skip to content

Commit 7f376a1

Browse files
authored
Merge pull request #1809 from ahoppen/bsp-task-notifications
Use `build/taskStart`, `build/taskProgress` and `build/taskFinish` to communicate progress from a BSP server to the client
2 parents e73e8b7 + 06f58db commit 7f376a1

25 files changed

+820
-116
lines changed

Contributor Documentation/BSP Extensions.md

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ export interface SourceKitInitializeBuildResponseData {
3535
}
3636
```
3737

38+
## `build/taskStart`
39+
40+
If `data` contains a string value for the `workDoneProgressTitle` key, then the task's message will be displayed in the client as a work done progress with that title.
41+
3842
## `buildTarget/didChange`
3943

4044
`changes` can be `null` to indicate that all targets have changed.
@@ -119,12 +123,6 @@ export interface TextDocumentSourceKitOptionsResult {
119123
}
120124
```
121125
122-
## `window/workDoneProgress/create`
123-
124-
Request from the build server to SourceKit-LSP to create a work done progress.
125-
126-
Definition is the same as in LSP.
127-
128126
## `workspace/buildTargets`
129127
130128
`BuildTargetTag` can have the following additional values
@@ -172,9 +170,3 @@ This request is a no-op and doesn't have any effects.
172170
If the build system is currently updating the build graph, this request should return after those updates have finished processing.
173171
174172
- method: `workspace/waitForBuildSystemUpdates`
175-
176-
## `$/progress`
177-
178-
Notification from the build server to SourceKit-LSP to update a work done progress created using `window/workDoneProgress/create`.
179-
180-
Definition is the same as in LSP.

Contributor Documentation/Implementing a BSP server.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ If the build system does not have a notion of targets, eg. because it provides b
2727

2828
If the build system loads the entire build graph during initialization, it may immediately return from `workspace/waitForBuildSystemUpdates`.
2929

30-
3130
## Supporting background indexing
3231

3332
To support background indexing, the build system must set `data.prepareProvider: true` in the `build/initialize` response and implement the `buildTarget/prepare` method.
@@ -37,9 +36,8 @@ To support background indexing, the build system must set `data.prepareProvider:
3736
The following methods are not necessary to implement for SourceKit-LSP to work but might help with the implementation of the build server.
3837

3938
- `build/logMessage`
40-
- `window/workDoneProgress/create`
39+
- `build/taskStart`, `build/taskProgress`, and `build/taskFinish`
4140
- `workspace/didChangeWatchedFiles`
42-
- `$/progress`
4341

4442
## Build server discovery
4543

Package.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ var targets: [Target] = [
5353
swiftSettings: globalSwiftSettings
5454
),
5555

56+
.testTarget(
57+
name: "BuildServerProtocolTests",
58+
dependencies: [
59+
"BuildServerProtocol",
60+
"LanguageServerProtocol",
61+
"SKTestSupport",
62+
],
63+
swiftSettings: globalSwiftSettings
64+
),
65+
5666
// MARK: BuildSystemIntegration
5767

5868
.target(

Sources/BuildServerProtocol/CMakeLists.txt

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
add_library(BuildServerProtocol STATIC
22
Messages.swift
33

4-
Messages/TextDocumentSourceKitOptionsRequest.swift
5-
Messages/OnBuildTargetDidChangeNotification.swift
6-
Messages/InitializeBuildRequest.swift
4+
Messages/BuildShutdownRequest.swift
5+
Messages/BuildTargetPrepareRequest.swift
76
Messages/BuildTargetSourcesRequest.swift
7+
Messages/InitializeBuildRequest.swift
88
Messages/OnBuildExitNotification.swift
9-
Messages/RegisterForChangeNotifications.swift
9+
Messages/OnBuildInitializedNotification.swift
1010
Messages/OnBuildLogMessageNotification.swift
11-
Messages/WorkDoneProgress.swift
11+
Messages/OnBuildTargetDidChangeNotification.swift
1212
Messages/OnWatchedFilesDidChangeNotification.swift
13-
Messages/OnBuildInitializedNotification.swift
14-
Messages/WorkspaceWaitForBuildSystemUpdates.swift
15-
Messages/BuildShutdownRequest.swift
13+
Messages/RegisterForChangeNotifications.swift
14+
Messages/TaskFinishNotification.swift
15+
Messages/TaskProgressNotification.swift
16+
Messages/TaskStartNotification.swift
17+
Messages/TextDocumentSourceKitOptionsRequest.swift
1618
Messages/WorkspaceBuildTargetsRequest.swift
17-
Messages/BuildTargetPrepareRequest.swift
19+
Messages/WorkspaceWaitForBuildSystemUpdates.swift
1820

19-
SupportTypes/TextDocumentIdentifier.swift
20-
SupportTypes/TaskId.swift
2121
SupportTypes/BuildTarget.swift
22-
SupportTypes/MessageType.swift)
22+
SupportTypes/MessageType.swift
23+
SupportTypes/MillisecondsSince1970Date.swift
24+
SupportTypes/StatusCode.swift
25+
SupportTypes/TaskId.swift
26+
SupportTypes/TextDocumentIdentifier.swift)
2327
set_target_properties(BuildServerProtocol PROPERTIES
2428
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
2529
target_link_libraries(BuildServerProtocol PRIVATE

Sources/BuildServerProtocol/Messages.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,20 @@ fileprivate let requestTypes: [_RequestType.Type] = [
2424
InitializeBuildRequest.self,
2525
RegisterForChanges.self,
2626
TextDocumentSourceKitOptionsRequest.self,
27-
WorkspaceWaitForBuildSystemUpdatesRequest.self,
2827
WorkspaceBuildTargetsRequest.self,
28+
WorkspaceWaitForBuildSystemUpdatesRequest.self,
2929
]
3030

3131
fileprivate let notificationTypes: [NotificationType.Type] = [
32-
OnBuildExitNotification.self,
3332
FileOptionsChangedNotification.self,
33+
OnBuildExitNotification.self,
3434
OnBuildInitializedNotification.self,
3535
OnBuildLogMessageNotification.self,
3636
OnBuildTargetDidChangeNotification.self,
3737
OnWatchedFilesDidChangeNotification.self,
38+
TaskFinishNotification.self,
39+
TaskProgressNotification.self,
40+
TaskStartNotification.self,
3841
]
3942

4043
public let bspRegistry = MessageRegistry(requests: requestTypes, notifications: notificationTypes)
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2024 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+
#if compiler(>=6)
14+
public import LanguageServerProtocol
15+
public import Foundation
16+
#else
17+
import LanguageServerProtocol
18+
import Foundation
19+
#endif
20+
21+
/// A `build/taskFinish` notification must always be sent after a `build/taskStart`` with the same `taskId` was sent.
22+
public struct TaskFinishNotification: NotificationType {
23+
public static let method: String = "build/taskFinish"
24+
25+
/// Unique id of the task with optional reference to parent task id.
26+
public var taskId: TaskId
27+
28+
/// A unique identifier generated by the client to identify this request.
29+
public var originId: String?
30+
31+
/// Timestamp of when the event started in milliseconds since Epoch.
32+
@CustomCodable<MillisecondsSince1970Date?>
33+
public var eventTime: Date?
34+
35+
/// Message describing the task.
36+
public var message: String?
37+
38+
/// Task completion status.
39+
public var status: StatusCode
40+
41+
/// Kind of data to expect in the `data` field. If this field is not set, the kind of data is not specified.
42+
public var dataKind: TaskFinishDataKind?
43+
44+
/// Optional metadata about the task.
45+
///
46+
/// Objects for specific tasks like compile, test, etc are specified in the protocol.
47+
public var data: LSPAny?
48+
49+
public init(
50+
taskId: TaskId,
51+
originId: String? = nil,
52+
eventTime: Date? = nil,
53+
message: String? = nil,
54+
status: StatusCode,
55+
dataKind: TaskFinishDataKind? = nil,
56+
data: LSPAny? = nil
57+
) {
58+
self.taskId = taskId
59+
self.originId = originId
60+
self.eventTime = eventTime
61+
self.message = message
62+
self.status = status
63+
self.dataKind = dataKind
64+
self.data = data
65+
}
66+
67+
}
68+
69+
/// Task finish notifications may contain an arbitrary interface in their `data` field.
70+
///
71+
/// The kind of interface that is contained in a notification must be specified in the `dataKind` field.
72+
public struct TaskFinishDataKind: RawRepresentable, Codable, Hashable, Sendable {
73+
public let rawValue: String
74+
public init(rawValue: String) {
75+
self.rawValue = rawValue
76+
}
77+
78+
/// `data` field must contain a `CompileReport` object.
79+
public static let compileReport = TaskStartDataKind(rawValue: "compile-report")
80+
81+
/// `data` field must contain a `TestFinish` object.
82+
public static let testFinish = TaskStartDataKind(rawValue: "test-finish")
83+
84+
/// `data` field must contain a `TestReport` object.
85+
public static let testReport = TaskStartDataKind(rawValue: "test-report")
86+
}
87+
88+
/// This structure is embedded in the `TaskFinishNotification.data` field, when the `dataKind` field contains
89+
/// `"compile-report"`.
90+
///
91+
/// The completion of a compilation task should be signalled with a `build/taskFinish` notification. When the
92+
/// compilation unit is a build target, the notification's `dataKind` field must be `compile-report` and the `data`
93+
/// field must include a `CompileReportData` object.
94+
public struct CompileReportData: Codable, Hashable, Sendable {
95+
/// The build target that was compiled.
96+
public var target: BuildTargetIdentifier
97+
98+
/// An optional request id to know the origin of this report.
99+
///
100+
/// Deprecated: Use the field in TaskFinishParams instead .
101+
public var originId: String?
102+
103+
/// The total number of reported errors compiling this target.
104+
public var errors: Int
105+
106+
/// The total number of reported warnings compiling the target.
107+
public var warnings: Int
108+
109+
/// The total number of milliseconds it took to compile the target.
110+
@CustomCodable<MillisecondsSince1970Date?>
111+
public var time: Date?
112+
113+
/// The compilation was a noOp compilation.
114+
public var noOp: Bool?
115+
116+
public init(
117+
target: BuildTargetIdentifier,
118+
originId: String? = nil,
119+
errors: Int,
120+
warnings: Int,
121+
time: Date? = nil,
122+
noOp: Bool? = nil
123+
) {
124+
self.target = target
125+
self.originId = originId
126+
self.errors = errors
127+
self.warnings = warnings
128+
self.time = time
129+
self.noOp = noOp
130+
}
131+
}
132+
133+
/// This structure is embedded in the `TaskFinishNotification.data` field, when the `dataKind` field contains
134+
/// `"test-finish"`.
135+
public struct TestFinishData: Codable, Hashable, Sendable {
136+
/// Name or description of the test.
137+
public var displayName: String?
138+
139+
/// Information about completion of the test, for example an error message.
140+
public var message: String?
141+
142+
/// Completion status of the test.
143+
public var status: TestStatus
144+
145+
/// Source location of the test, as LSP location.
146+
public var location: Location?
147+
148+
/// Kind of data to expect in the `data` field. If this field is not set, the kind of data is not specified.
149+
public var dataKind: TestFinishDataKind?
150+
151+
/// Optionally, structured metadata about the test completion.
152+
/// For example: stack traces, expected/actual values.
153+
public var data: LSPAny?
154+
155+
public init(
156+
displayName: String? = nil,
157+
message: String? = nil,
158+
status: TestStatus,
159+
location: Location? = nil,
160+
dataKind: TestFinishDataKind? = nil,
161+
data: LSPAny? = nil
162+
) {
163+
self.displayName = displayName
164+
self.message = message
165+
self.status = status
166+
self.location = location
167+
self.dataKind = dataKind
168+
self.data = data
169+
}
170+
171+
}
172+
173+
public enum TestStatus: Int, Codable, Hashable, Sendable {
174+
/// The test passed successfully.
175+
case passed = 1
176+
177+
/// The test failed.
178+
case failed = 2
179+
180+
/// The test was marked as ignored.
181+
case ignored = 3
182+
183+
/// The test execution was cancelled.
184+
case cancelled = 4
185+
186+
/// The was not included in execution.
187+
case skipped = 5
188+
}
189+
190+
public struct TestFinishDataKind: RawRepresentable, Codable, Hashable, Sendable {
191+
public var rawValue: String
192+
193+
public init?(rawValue: String) {
194+
self.rawValue = rawValue
195+
}
196+
}
197+
198+
/// This structure is embedded in the `TaskFinishNotification.data` field, when the `dataKind` field contains
199+
/// `"test-report"`.
200+
public struct TestReportData: Codable, Hashable, Sendable {
201+
/// Deprecated: Use the field in TaskFinishParams instead
202+
public var originId: String?
203+
204+
/// The build target that was compiled.
205+
public var target: BuildTargetIdentifier
206+
207+
/// The total number of successful tests.
208+
public var passed: Int
209+
210+
/// The total number of failed tests.
211+
public var failed: Int
212+
213+
/// The total number of ignored tests.
214+
public var ignored: Int
215+
216+
/// The total number of cancelled tests.
217+
public var cancelled: Int
218+
219+
/// The total number of skipped tests.
220+
public var skipped: Int
221+
222+
/// The total number of milliseconds tests take to run (e.g. doesn't include compile times).
223+
public var time: Int64?
224+
225+
public init(
226+
originId: String? = nil,
227+
target: BuildTargetIdentifier,
228+
passed: Int,
229+
failed: Int,
230+
ignored: Int,
231+
cancelled: Int,
232+
skipped: Int,
233+
time: Int64? = nil
234+
) {
235+
self.originId = originId
236+
self.target = target
237+
self.passed = passed
238+
self.failed = failed
239+
self.ignored = ignored
240+
self.cancelled = cancelled
241+
self.skipped = skipped
242+
self.time = time
243+
}
244+
245+
}

0 commit comments

Comments
 (0)