Skip to content

Commit e6b0796

Browse files
authored
Additions to the LSP module for workspace/didChangeWatchedFiles (#376)
* Additions to the LSP module for `workspace/didChangeWatchedFiles` We can then use this functionality to allow a `BuildSystem` to watch files (e.g. via the client or even in-process file watching), which would allow us to do things like detect new files and provide accurate build settings for them. * Improve #file and #line for LSP test errors Also regenerate Linux main * Add `LSPAnyCodable` protocol requirement to RegistrationOptions * Don't encode an optional documentSelector to null * Skip running `testSourcekitdCrashRecovery` when not on macOS
1 parent 78e69f7 commit e6b0796

16 files changed

+477
-25
lines changed

Sources/LanguageServerProtocol/Messages.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public let builtinNotifications: [NotificationType.Type] = [
5353
CancelRequestNotification.self,
5454
LogMessageNotification.self,
5555
DidChangeConfigurationNotification.self,
56+
DidChangeWatchedFilesNotification.self,
5657
DidChangeWorkspaceFoldersNotification.self,
5758
DidOpenTextDocumentNotification.self,
5859
DidCloseTextDocumentNotification.self,
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 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+
/// Notification from the client when changes to watched files are detected.
14+
///
15+
/// - Parameter changes: The set of file changes.
16+
public struct DidChangeWatchedFilesNotification: NotificationType {
17+
public static let method: String = "workspace/didChangeWatchedFiles"
18+
19+
/// The file changes.
20+
public var changes: [FileEvent]
21+
22+
public init(changes: [FileEvent]) {
23+
self.changes = changes
24+
}
25+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 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 Foundation
14+
15+
/// Request sent from the server to the client to dynamically register for a new capability on the
16+
/// client side.
17+
///
18+
/// Note that not all clients support dynamic registration and clients may provide dynamic
19+
/// registration support for some capabilities but not others.
20+
///
21+
/// Servers must not register the same capability both statically through the initialization result
22+
/// and dynamically. Servers that want to support both should check the client capabilities and only
23+
/// register the capability statically if the client doesn't support dynamic registration for that
24+
/// capability.
25+
public struct RegisterCapabilityRequest: RequestType, Hashable {
26+
public static let method: String = "client/registerCapability"
27+
public typealias Response = VoidResponse
28+
29+
/// Capability registrations.
30+
public var registrations: [Registration]
31+
32+
public init(registrations: [Registration]) {
33+
self.registrations = registrations
34+
}
35+
}
36+
37+
/// General parameters to register a capability.
38+
public struct Registration: Codable, Hashable {
39+
/// The id used to register the capability which may be used to unregister support.
40+
public var id: String
41+
42+
/// The method/capability to register for.
43+
public var method: String
44+
45+
/// Options necessary for this registration.
46+
public var registerOptions: LSPAny?
47+
48+
public init(id: String, method: String, registerOptions: LSPAny?) {
49+
self.id = id
50+
self.method = method
51+
self.registerOptions = registerOptions
52+
}
53+
54+
/// Create a new `Registration` with a randomly generated id. Save the generated
55+
/// id if you wish to unregister the given registration.
56+
public init(method: String, registerOptions: LSPAny?) {
57+
self.id = UUID().uuidString
58+
self.method = method
59+
self.registerOptions = registerOptions
60+
}
61+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 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+
/// Request sent from the server to the client to unregister a previously registered
14+
/// capability.
15+
public struct UnregisterCapabilityRequest: RequestType, Hashable {
16+
public static let method: String = "client/unregisterCapability"
17+
public typealias Response = VoidResponse
18+
19+
/// Capabilities to unregister.
20+
public var unregistrations: [Unregistration]
21+
22+
public init(unregistrations: [Unregistration]) {
23+
self.unregistrations = unregistrations
24+
}
25+
}
26+
27+
extension UnregisterCapabilityRequest: Codable {
28+
private enum CodingKeys: String, CodingKey {
29+
/// This should correctly be named `unregistrations`. However changing this
30+
/// is a breaking change and needs to wait until the 4.x LSP spec update.
31+
case unregistrations = "unregisterations"
32+
}
33+
}
34+
35+
/// General parameters to unregister a capability.
36+
public struct Unregistration: Codable, Hashable {
37+
/// The id used to unregister the capability, usually provided through the
38+
/// register request.
39+
public var id: String
40+
41+
/// The method/capability to unregister for.
42+
public var method: String
43+
44+
public init(id: String, method: String) {
45+
self.id = id
46+
self.method = method
47+
}
48+
}

Sources/LanguageServerProtocol/SupportTypes/ClientCapabilities.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ public struct WorkspaceClientCapabilities: Hashable, Codable {
8989

9090
public var didChangeConfiguration: DynamicRegistrationCapability? = nil
9191

92+
/// Whether the clients supports file watching - note that the protocol currently doesn't
93+
/// support static registration for file changes.
9294
public var didChangeWatchedFiles: DynamicRegistrationCapability? = nil
9395

9496
public var symbol: Symbol? = nil
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 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+
/// An event describing a file change.
14+
public struct FileEvent: Codable, Hashable {
15+
public var uri: DocumentURI
16+
public var type: FileChangeType
17+
18+
public init(uri: DocumentURI, type: FileChangeType) {
19+
self.uri = uri
20+
self.type = type
21+
}
22+
}
23+
/// The type of file event.
24+
///
25+
/// In LSP, this is an integer, so we don't use a closed set.
26+
public struct FileChangeType: RawRepresentable, Codable, Hashable {
27+
public var rawValue: Int
28+
29+
public init(rawValue: Int) {
30+
self.rawValue = rawValue
31+
}
32+
33+
/// The file was created.
34+
public static let created: FileChangeType = FileChangeType(rawValue: 1)
35+
/// The file was changed.
36+
public static let changed: FileChangeType = FileChangeType(rawValue: 2)
37+
/// The file was deleted.
38+
public static let deleted: FileChangeType = FileChangeType(rawValue: 3)
39+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 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+
/// Defines a watcher interested in specific file system change events.
14+
public struct FileSystemWatcher: Codable, Hashable {
15+
/// The glob pattern to watch.
16+
public var globPattern: String
17+
18+
/// The kind of events of interest. If omitted it defaults to
19+
/// WatchKind.create | WatchKind.change | WatchKind.delete.
20+
public var kind: WatchKind?
21+
22+
public init(globPattern: String, kind: WatchKind? = nil) {
23+
self.globPattern = globPattern
24+
self.kind = kind
25+
}
26+
}
27+
28+
extension FileSystemWatcher: LSPAnyCodable {
29+
public init?(fromLSPDictionary dictionary: [String : LSPAny]) {
30+
guard let globPatternAny = dictionary[CodingKeys.globPattern.stringValue] else { return nil }
31+
guard case .string(let globPattern) = globPatternAny else { return nil }
32+
self.globPattern = globPattern
33+
34+
guard let kindValue = dictionary[CodingKeys.kind.stringValue] else {
35+
self.kind = nil
36+
return
37+
}
38+
39+
switch kindValue {
40+
case .null: self.kind = nil
41+
case .int(let value): self.kind = WatchKind(rawValue: value)
42+
default: return nil
43+
}
44+
}
45+
46+
public func encodeToLSPAny() -> LSPAny {
47+
var encoded = [CodingKeys.globPattern.stringValue: LSPAny.string(globPattern)]
48+
if let kind = kind {
49+
encoded[CodingKeys.kind.stringValue] = LSPAny.int(kind.rawValue)
50+
}
51+
return .dictionary(encoded)
52+
}
53+
}
54+
55+
/// The type of file event a watcher is interested in.
56+
///
57+
/// In LSP, this is an integer, so we don't use a closed set.
58+
public struct WatchKind: OptionSet, Codable, Hashable {
59+
public var rawValue: Int
60+
61+
public init(rawValue: Int) {
62+
self.rawValue = rawValue
63+
}
64+
65+
public static let create: WatchKind = WatchKind(rawValue: 1)
66+
public static let change: WatchKind = WatchKind(rawValue: 2)
67+
public static let delete: WatchKind = WatchKind(rawValue: 4)
68+
}

Sources/LanguageServerProtocol/SupportTypes/LSPAny.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,52 @@ public protocol LSPAnyCodable {
121121
init?(fromLSPDictionary dictionary: [String: LSPAny])
122122
func encodeToLSPAny() -> LSPAny
123123
}
124+
125+
extension Optional: LSPAnyCodable where Wrapped: LSPAnyCodable {
126+
public init?(fromLSPAny value: LSPAny) {
127+
if case .null = value {
128+
self = .none
129+
return
130+
}
131+
guard case .dictionary(let dict) = value else {
132+
return nil
133+
}
134+
guard let wrapped = Wrapped.init(fromLSPDictionary: dict) else {
135+
return nil
136+
}
137+
self = .some(wrapped)
138+
}
139+
140+
public init?(fromLSPDictionary dictionary: [String : LSPAny]) {
141+
return nil
142+
}
143+
144+
public func encodeToLSPAny() -> LSPAny {
145+
guard let wrapped = self else { return .null }
146+
return wrapped.encodeToLSPAny()
147+
}
148+
}
149+
150+
extension Array: LSPAnyCodable where Element: LSPAnyCodable {
151+
public init?(fromLSPArray array: LSPAny) {
152+
guard case .array(let array) = array else {
153+
return nil
154+
}
155+
var result = [Element]()
156+
for case .dictionary(let editDict) in array {
157+
guard let element = Element.init(fromLSPDictionary: editDict) else {
158+
return nil
159+
}
160+
result.append(element)
161+
}
162+
self = result
163+
}
164+
165+
public init?(fromLSPDictionary dictionary: [String : LSPAny]) {
166+
return nil
167+
}
168+
169+
public func encodeToLSPAny() -> LSPAny {
170+
return .array(map { $0.encodeToLSPAny() })
171+
}
172+
}

0 commit comments

Comments
 (0)