Skip to content

Commit b8de1f1

Browse files
committed
Add new playground types
Needed to get playground support into SourceKit-LSP: swiftlang/sourcekit-lsp#2340
1 parent 7bf2acf commit b8de1f1

File tree

3 files changed

+159
-1
lines changed

3 files changed

+159
-1
lines changed

Sources/LanguageServerProtocol/SupportTypes/Location.swift

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
/// Range within a particular document.
1414
///
1515
/// For a location where the document is implied, use `Position` or `Range<Position>`.
16-
public struct Location: ResponseType, Hashable, Codable, CustomDebugStringConvertible, Comparable, Sendable {
16+
public struct Location: ResponseType, Hashable, Codable, CustomDebugStringConvertible, Comparable, Sendable,
17+
LSPAnyCodable
18+
{
1719
public static func < (lhs: Location, rhs: Location) -> Bool {
1820
if lhs.uri != rhs.uri {
1921
return lhs.uri.stringValue < rhs.uri.stringValue
@@ -34,7 +36,27 @@ public struct Location: ResponseType, Hashable, Codable, CustomDebugStringConver
3436
self._range = CustomCodable<PositionRange>(wrappedValue: range)
3537
}
3638

39+
public init?(fromLSPDictionary dictionary: [String: LSPAny]) {
40+
guard
41+
case .string(let uriString) = dictionary["uri"],
42+
case .dictionary(let rangeDict) = dictionary["range"],
43+
let uri = try? DocumentURI(string: uriString),
44+
let range = Range<Position>(fromLSPDictionary: rangeDict)
45+
else {
46+
return nil
47+
}
48+
self.uri = uri
49+
self._range = CustomCodable<PositionRange>(wrappedValue: range)
50+
}
51+
3752
public var debugDescription: String {
3853
return "\(uri):\(range.lowerBound)-\(range.upperBound)"
3954
}
55+
56+
public func encodeToLSPAny() -> LSPAny {
57+
return .dictionary([
58+
"uri": .string(uri.stringValue),
59+
"range": range.encodeToLSPAny(),
60+
])
61+
}
4062
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 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+
/// A `Playground` represents a usage of the #Playground macro, providing the editor with the
14+
/// location of the playground and identifiers to allow executing the playground through a "swift play" command.
15+
///
16+
/// **(LSP Extension)**
17+
public struct Playground: ResponseType, Equatable, LSPAnyCodable {
18+
/// Unique identifier for the `Playground`. Client can run the playground by executing `swift play <id>`.
19+
///
20+
/// This property is always present whether the `Playground` has a `label` or not.
21+
///
22+
/// Follows the format output by `swift play --list`.
23+
public var id: String
24+
25+
/// The label that can be used as a display name for the playground. This optional property is only available
26+
/// for named playgrounds. For example: `#Playground("hello") { print("Hello!) }` would have a `label` of `"hello"`.
27+
public var label: String?
28+
29+
/// The location of where the #Playground macro was used in the source code.
30+
public var location: Location
31+
32+
public init(
33+
id: String,
34+
label: String?,
35+
location: Location,
36+
) {
37+
self.id = id
38+
self.label = label
39+
self.location = location
40+
}
41+
42+
public init?(fromLSPDictionary dictionary: [String: LSPAny]) {
43+
guard
44+
case .string(let id) = dictionary["id"],
45+
case .dictionary(let locationDict) = dictionary["location"],
46+
let location = Location(fromLSPDictionary: locationDict)
47+
else {
48+
return nil
49+
}
50+
self.id = id
51+
self.location = location
52+
if case .string(let label) = dictionary["label"] {
53+
self.label = label
54+
}
55+
}
56+
57+
public func encodeToLSPAny() -> LSPAny {
58+
var dict: [String: LSPAny] = [
59+
"id": .string(id),
60+
"location": location.encodeToLSPAny(),
61+
]
62+
63+
if let label {
64+
dict["label"] = .string(label)
65+
}
66+
67+
return .dictionary(dict)
68+
}
69+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2025 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+
/// A `TextDocumentPlayground` item can be used to identify playground and identify it
14+
/// to allow executing the playground through a "swift play" command. Differs from `Playground`
15+
/// by only including the `range` instead of full `location` with the expectation being that
16+
/// it is only returned as part of a textDocument/* request such as textDocument/codelens
17+
public struct TextDocumentPlayground: ResponseType, Equatable, LSPAnyCodable {
18+
/// Unique identifier for the `Playground`. Client can run the playground by executing `swift play <id>`.
19+
///
20+
/// This property is always present whether the `Playground` has a `label` or not.
21+
///
22+
/// Follows the format output by `swift play --list`.
23+
public var id: String
24+
25+
/// The label that can be used as a display name for the playground. This optional property is only available
26+
/// for named playgrounds. For example: `#Playground("hello") { print("Hello!) }` would have a `label` of `"hello"`.
27+
public var label: String?
28+
29+
/// The full range of the #Playground macro body in the given file.
30+
public var range: Range<Position>
31+
32+
public init(
33+
id: String,
34+
label: String?,
35+
range: Range<Position>
36+
) {
37+
self.id = id
38+
self.label = label
39+
self.range = range
40+
}
41+
42+
public init?(fromLSPDictionary dictionary: [String: LSPAny]) {
43+
guard
44+
case .string(let id) = dictionary["id"],
45+
case .dictionary(let rangeDict) = dictionary["range"],
46+
let range = Range<Position>(fromLSPDictionary: rangeDict)
47+
else {
48+
return nil
49+
}
50+
self.id = id
51+
self.range = range
52+
if case .string(let label) = dictionary["label"] {
53+
self.label = label
54+
}
55+
}
56+
57+
public func encodeToLSPAny() -> LSPAny {
58+
var dict: [String: LSPAny] = [
59+
"id": .string(id),
60+
"range": range.encodeToLSPAny(),
61+
]
62+
if let label {
63+
dict["label"] = .string(label)
64+
}
65+
return .dictionary(dict)
66+
}
67+
}

0 commit comments

Comments
 (0)