-
Notifications
You must be signed in to change notification settings - Fork 4
Add new playground types #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,72 @@ | ||||||||
| //===----------------------------------------------------------------------===// | ||||||||
| // | ||||||||
| // This source file is part of the Swift.org open source project | ||||||||
| // | ||||||||
| // Copyright (c) 2014 - 2025 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 | ||||||||
| // | ||||||||
| //===----------------------------------------------------------------------===// | ||||||||
|
|
||||||||
| /// A `Playground` represents a usage of the #Playground macro, providing the editor with the | ||||||||
| /// location of the playground and identifiers to allow executing the playground through a "swift play" command. | ||||||||
| /// | ||||||||
| /// **(LSP Extension)** | ||||||||
| public struct Playground: ResponseType, Equatable, LSPAnyCodable { | ||||||||
| /// Unique identifier for the `Playground` with the format `<target>/<filename>:<line>:[column]` where `target` | ||||||||
| /// corresponds to the Swift package's target where the playground is defined, `filename` is the basename of the file | ||||||||
| /// (not entire relative path), and `column` is optional only required if multiple playgrounds are defined on the same | ||||||||
| /// line. Client can run the playground by executing `swift play <id>`. | ||||||||
| /// | ||||||||
| /// This property is always present whether the `Playground` has a `label` or not. | ||||||||
| /// | ||||||||
| /// Follows the format output by `swift play --list`. | ||||||||
| public var id: String | ||||||||
|
|
||||||||
| /// The label that can be used as a display name for the playground. This optional property is only available | ||||||||
| /// for named playgrounds. For example: `#Playground("hello") { print("Hello!) }` would have a `label` of `"hello"`. | ||||||||
| public var label: String? | ||||||||
|
|
||||||||
| /// The location of where the #Playground macro was used in the source code. | ||||||||
| public var location: Location | ||||||||
|
|
||||||||
| public init( | ||||||||
| id: String, | ||||||||
| label: String?, | ||||||||
| location: Location, | ||||||||
| ) { | ||||||||
| self.id = id | ||||||||
| self.label = label | ||||||||
| self.location = location | ||||||||
| } | ||||||||
|
|
||||||||
| public init?(fromLSPDictionary dictionary: [String: LSPAny]) { | ||||||||
| guard | ||||||||
| case .string(let id) = dictionary["id"], | ||||||||
| case .dictionary(let locationDict) = dictionary["location"], | ||||||||
| let location = Location(fromLSPDictionary: locationDict) | ||||||||
|
Comment on lines
+48
to
+49
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be shortened slightly.
Suggested change
|
||||||||
| else { | ||||||||
| return nil | ||||||||
| } | ||||||||
| self.id = id | ||||||||
| self.location = location | ||||||||
| if case .string(let label) = dictionary["label"] { | ||||||||
| self.label = label | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| public func encodeToLSPAny() -> LSPAny { | ||||||||
| var dict: [String: LSPAny] = [ | ||||||||
| "id": .string(id), | ||||||||
| "location": location.encodeToLSPAny(), | ||||||||
| ] | ||||||||
|
|
||||||||
| if let label { | ||||||||
| dict["label"] = .string(label) | ||||||||
| } | ||||||||
|
|
||||||||
| return .dictionary(dict) | ||||||||
| } | ||||||||
| } | ||||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,70 @@ | ||||||||
| //===----------------------------------------------------------------------===// | ||||||||
| // | ||||||||
| // This source file is part of the Swift.org open source project | ||||||||
| // | ||||||||
| // Copyright (c) 2014 - 2025 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 | ||||||||
| // | ||||||||
| //===----------------------------------------------------------------------===// | ||||||||
|
|
||||||||
| /// A `TextDocumentPlayground` item can be used to identify playground and identify it | ||||||||
| /// to allow executing the playground through a "swift play" command. Differs from `Playground` | ||||||||
| /// by only including the `range` instead of full `location` with the expectation being that | ||||||||
| /// it is only returned as part of a textDocument/* request such as textDocument/codelens | ||||||||
| public struct TextDocumentPlayground: ResponseType, Equatable, LSPAnyCodable { | ||||||||
| /// Unique identifier for the `Playground` with the format `<target>/<filename>:<line>:[column]` where `target` | ||||||||
| /// corresponds to the Swift package's target where the playground is defined, `filename` is the basename of the file | ||||||||
| /// (not entire relative path), and `column` is optional only required if multiple playgrounds are defined on the same | ||||||||
| /// line. Client can run the playground by executing `swift play <id>`. | ||||||||
| /// | ||||||||
| /// This property is always present whether the `Playground` has a `label` or not. | ||||||||
| /// | ||||||||
| /// Follows the format output by `swift play --list`. | ||||||||
| public var id: String | ||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this needs to capture either the corresponding product or target as well so that the playground can be executed in the appropriate context
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @owenv the LSP code that parses these does include the target https://github.com/swiftlang/sourcekit-lsp/pull/2340/files#diff-d76b1dc09dd52af2a88684043d44d1e5261f013c09f6137dffd4e23aebce6e56R91 but do you want the comment code to reflect the expected format?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a better explanation about
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, so it seems like the playgrounds implementation is building a dylib composed of every target in the package and linking the runner against that. This will work in small examples, but in general it's not safe to assume all the targets in a package can be safely linked into a single image. e.g. they may have different platform requirements, conflicting static initializers, multiple copies of a binary dependency built from the same sources, etc. I think sourcekit-lsp will need to pick a single specific appropriate library product containing the playground's code, thread that through to the play command, and adjust the build of the runner accordingly |
||||||||
|
|
||||||||
| /// The label that can be used as a display name for the playground. This optional property is only available | ||||||||
| /// for named playgrounds. For example: `#Playground("hello") { print("Hello!) }` would have a `label` of `"hello"`. | ||||||||
| public var label: String? | ||||||||
|
|
||||||||
| /// The full range of the #Playground macro body in the given file. | ||||||||
| public var range: Range<Position> | ||||||||
|
|
||||||||
| public init( | ||||||||
| id: String, | ||||||||
| label: String?, | ||||||||
| range: Range<Position> | ||||||||
| ) { | ||||||||
| self.id = id | ||||||||
| self.label = label | ||||||||
| self.range = range | ||||||||
| } | ||||||||
|
|
||||||||
| public init?(fromLSPDictionary dictionary: [String: LSPAny]) { | ||||||||
| guard | ||||||||
| case .string(let id) = dictionary["id"], | ||||||||
| case .dictionary(let rangeDict) = dictionary["range"], | ||||||||
| let range = Range<Position>(fromLSPDictionary: rangeDict) | ||||||||
|
Comment on lines
+48
to
+49
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, we can use the
Suggested change
|
||||||||
| else { | ||||||||
| return nil | ||||||||
| } | ||||||||
| self.id = id | ||||||||
| self.range = range | ||||||||
| if case .string(let label) = dictionary["label"] { | ||||||||
| self.label = label | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| public func encodeToLSPAny() -> LSPAny { | ||||||||
| var dict: [String: LSPAny] = [ | ||||||||
| "id": .string(id), | ||||||||
| "range": range.encodeToLSPAny(), | ||||||||
| ] | ||||||||
| if let label { | ||||||||
| dict["label"] = .string(label) | ||||||||
| } | ||||||||
| return .dictionary(dict) | ||||||||
| } | ||||||||
| } | ||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn’t used anywhere yet, right? Do you want to introduce the
workspace/playgroundsrequest in this PR already or in a follow-up PR? I’m fine with either.