Files
sourcekit-lsp/Sources/BuildServerProtocol/Messages/TaskStartNotification.swift
Alex Hoppen 06f58db5c8 Use build/taskStart, build/taskProgress and build/taskFinish to communicate progress from a BSP server to the client
Instead of defining BSP extensions for `window/workDoneProgress/create` and `$/progress`, we should be able to use the standard `build/taskStart`, `build/taskProgress` and `build/taskFinish` messages to the same effect, as suggested by https://forums.swift.org/t/extending-functionality-of-build-server-protocol-with-sourcekit-lsp/74400/9.

Fixes #1783
rdar://138653131
2024-11-06 09:39:09 -08:00

164 lines
6.0 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if compiler(>=6)
public import LanguageServerProtocol
public import Foundation
#else
import LanguageServerProtocol
import Foundation
#endif
/// The BSP server can inform the client on the execution state of any task in the build tool.
/// The execution of some tasks, such as compilation or tests, must always be reported by the server.
///
/// The server may also send additional task notifications for actions not covered by the protocol, such as resolution
/// or packaging. BSP clients can then display this information to their users at their discretion.
///
/// When beginning a task, the server may send `build/taskStart`, intermediate updates may be sent in
/// `build/taskProgress`.
///
/// If a `build/taskStart` notification has been sent, the server must send build/taskFinish on completion of the same
/// task.
///
/// `build/taskStart`, `build/taskProgress` and `build/taskFinish` notifications for the same task must use the same
/// `taskId`.
///
/// Tasks that are spawned by another task should reference the originating task's `taskId` in their own `taskId`'s
/// `parent` field. Tasks spawned directly by a request should reference the request's `originId` parent.
public struct TaskStartNotification: NotificationType {
public static let method: String = "build/taskStart"
/// Unique id of the task with optional reference to parent task id
public var taskId: TaskId
/// A unique identifier generated by the client to identify this request.
public var originId: String?
/// Timestamp of when the event started in milliseconds since Epoch.
@CustomCodable<MillisecondsSince1970Date?>
public var eventTime: Date?
/// Message describing the task.
public var message: String?
/** Kind of data to expect in the `data` field. If this field is not set, the kind of data is not specified. */
public var dataKind: TaskStartDataKind?
/// Optional metadata about the task.
// Objects for specific tasks like compile, test, etc are specified in the protocol.
public var data: LSPAny?
public init(
taskId: TaskId,
originId: String? = nil,
eventTime: Date? = nil,
message: String? = nil,
dataKind: TaskStartDataKind? = nil,
data: LSPAny? = nil
) {
self.taskId = taskId
self.originId = originId
self.eventTime = eventTime
self.message = message
self.dataKind = dataKind
self.data = data
}
}
/// Task start notifications may contain an arbitrary interface in their `data` field.
///
/// The kind of interface that is contained in a notification must be specified in the `dataKind` field.
public struct TaskStartDataKind: RawRepresentable, Codable, Hashable, Sendable {
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
/// `data` field must contain a `CompileTaskData` object.
public static let compileTask = TaskStartDataKind(rawValue: "compile-task")
/// `data` field must contain a `TestStart` object.
public static let testStart = TaskStartDataKind(rawValue: "test-start")
/// `data` field must contain a `TestTask` object.
public static let testTask = TaskStartDataKind(rawValue: "test-task")
}
/// This structure is embedded in the `TaskStartNotification.data` field, when the `dataKind` field contains
/// "compile-task".
///
/// The beginning of a compilation unit may be signalled to the client with a `build/taskStart` notification.
///
/// When the compilation unit is a build target, the notification's `dataKind` field must be "compile-task" and the
/// `data` field must include a `CompileTaskData` object
public struct CompileTaskData: Codable, Hashable, Sendable {
public var target: BuildTargetIdentifier
public init(target: BuildTargetIdentifier) {
self.target = target
}
}
/// This structure is embedded in the `TaskStartNotification.data` field, when the `dataKind` field contains
/// "test-start".
public struct TestStartData: Codable, Hashable, Sendable {
/// Name or description of the test.
public var displayName: String
/// Source location of the test, as LSP location.
public var location: Location?
public init(displayName: String, location: Location? = nil) {
self.displayName = displayName
self.location = location
}
}
/// This structure is embedded in the `TaskStartNotification.data` field, when the `dataKind` field contains
/// "test-task".
///
/// The beginning of a testing unit may be signalled to the client with a `build/taskStart`` notification.
/// When the testing unit is a build target, the notification's `dataKind` field must be `test-task` and the `data`
/// field must include a `TestTaskData` object.
public struct TestTaskData: Codable, Hashable, Sendable {
public var target: BuildTargetIdentifier
public init(target: BuildTargetIdentifier) {
self.target = target
}
}
/// 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.
public struct WorkDoneProgressTask: LSPAnyCodable {
/// The title with which the work done progress should be created in the client.
public let title: String
public init(title: String) {
self.title = title
}
public init?(fromLSPDictionary dictionary: [String: LanguageServerProtocol.LSPAny]) {
guard case .string(let title) = dictionary["workDoneProgressTitle"] else {
return nil
}
self.title = title
}
public func encodeToLSPAny() -> LanguageServerProtocol.LSPAny {
return .dictionary([
"workDoneProgressTitle": .string(title)
])
}
}