12
12
// This file provides Swift language support by invoking SourceKit internally.
13
13
//===----------------------------------------------------------------------===//
14
14
15
+ import Foundation
16
+
15
17
enum SourceKitdError : Error , CustomStringConvertible {
16
18
case EditorOpenError( message: String )
17
19
case EditorCloseError( message: String )
@@ -26,19 +28,132 @@ enum SourceKitdError: Error, CustomStringConvertible {
26
28
}
27
29
}
28
30
29
- public class SwiftLang {
31
+ // MARK: Public APIs
32
+
33
+ public enum SwiftLang {
34
+ /// Synchronously parses Swift source code into a syntax tree.
35
+ ///
36
+ /// - Parameter url: A file URL pointing to a Swift source file.
37
+ /// - Parameter format: The format to use for the returned syntax tree.
38
+ /// - Returns: The syntax tree in the indicated format.
39
+ /// - Throws: If SourceKit responds to the request with an error. This is
40
+ /// usually a communication or configuration error, not a
41
+ /// syntax error in the code being parsed.
42
+ /// - Precondition: `url` must be a file URL.
43
+ public static func parse< Tree> (
44
+ contentsOf url: URL , into format: SyntaxTreeFormat < Tree >
45
+ ) throws -> Tree {
46
+ precondition ( url. isFileURL, " Can only parse files at file URLs " )
47
+ return try parse ( SourceFile ( path: url. path) , into: format)
48
+ }
49
+ }
50
+
51
+ extension SwiftLang . SyntaxTreeFormat {
52
+ /// Return the syntax tree serialized in JSON format. JSON is easy to inspect
53
+ /// and test with but very inefficient to deserialize. Use it for testing and
54
+ /// debugging.
55
+ public static var json : SwiftLang . SyntaxTreeFormat < Data > {
56
+ return jsonString. withTreeMapped { $0. data ( using: . utf8) ! }
57
+ }
58
+
59
+ /// Return the syntax tree serialized as ByteTree. ByteTree is fast and
60
+ /// compact but difficult to inspect or manipulate with textual tools.
61
+ /// It's recommended in production.
62
+ public static var byteTree : SwiftLang . SyntaxTreeFormat < Data > {
63
+ return . init( kind: . kind_SyntaxTreeSerializationByteTree) { dict in
64
+ dict. getData ( syntaxTreeKey)
65
+ }
66
+ }
67
+
68
+ /// Creates a new `SyntaxTreeFormat` instance which converts the tree of an
69
+ /// existing format.
70
+ ///
71
+ /// You can use this method to add new `SyntaxTreeFormat`s; simply declare a
72
+ /// new static constant in an extension of `SyntaxTreeFormat` which maps one
73
+ /// of the existing formats.
74
+ ///
75
+ /// - Parameter transform: A function which converts `self`'s Tree type to
76
+ /// the result type's Tree type.
77
+ /// - Returns: A new format which creates a tree in `self`'s format, then
78
+ /// applies `transform` to the tree to convert it.
79
+ public func withTreeMapped< NewTree> (
80
+ by transform: @escaping ( Tree ) throws -> NewTree
81
+ ) -> SwiftLang . SyntaxTreeFormat < NewTree > {
82
+ return . init( kind: kind) { [ makeTree] dict in
83
+ try transform ( makeTree ( dict) )
84
+ }
85
+ }
86
+ }
87
+
88
+ // MARK: Deprecated APIs
30
89
31
- fileprivate static func parse( content: String , name: String , isURL: Bool ) throws -> String {
90
+ extension SwiftLang {
91
+ /// Parses the Swift file at the provided URL into a `Syntax` tree in Json
92
+ /// serialization format by querying SourceKitd service. This function isn't
93
+ /// thread safe.
94
+ /// - Parameter path: The URL you wish to parse.
95
+ /// - Returns: The syntax tree in Json format string.
96
+ @available ( swift, deprecated: 5.0 , renamed: " parse(_:into:) " )
97
+ public static func parse( path: String ) throws -> String {
98
+ return try parse ( SourceFile ( path: path) , into: . jsonString)
99
+ }
100
+
101
+ /// Parses a given source buffer into a `Syntax` tree in Json serialization
102
+ /// format by querying SourceKitd service. This function isn't thread safe.
103
+ /// - Parameter source: The source buffer you wish to parse.
104
+ /// - Returns: The syntax tree in Json format string.
105
+ @available ( swift, deprecated: 5.0 , message: " use parse(_:into:) with a file instead " )
106
+ public static func parse( source: String ) throws -> String {
107
+ let content = SourceFile (
108
+ name: " foo " , contentKey: . key_SourceText, contentValue: source
109
+ )
110
+ return try parse ( content, into: . jsonString)
111
+ }
112
+ }
113
+
114
+ // MARK: Internals
115
+
116
+ extension SwiftLang {
117
+ /// The format to serialize the syntax tree in.
118
+ public struct SyntaxTreeFormat < Tree> {
119
+ /// Value for EditorOpen's key_SyntaxTreeSerializationFormat key.
120
+ fileprivate let kind : SourceKitdUID
121
+
122
+ /// Extracts the syntax tree from the response dictionary and converts it
123
+ /// into a value of type Tree.
124
+ fileprivate let makeTree : ( SourceKitdResponse . Dictionary ) throws -> Tree
125
+
126
+ /// Serialize the syntax tree into a JSON string. For backwards
127
+ /// compatibility only.
128
+ fileprivate static var jsonString : SyntaxTreeFormat < String > {
129
+ return . init( kind: . kind_SyntaxTreeSerializationJSON) { dict in
130
+ dict. getString ( syntaxTreeKey)
131
+ }
132
+ }
133
+ }
134
+ }
135
+
136
+ extension SwiftLang {
137
+ fileprivate struct SourceFile {
138
+ let name : String
139
+ let contentKey : SourceKitdUID
140
+ let contentValue : String
141
+ }
142
+
143
+ /// Parses the SourceFile in question using the provided serialization format.
144
+ fileprivate static func parse< Tree> (
145
+ _ content: SourceFile , into format: SyntaxTreeFormat < Tree >
146
+ ) throws -> Tree {
32
147
let Service = SourceKitdService ( )
33
148
let Request = SourceKitdRequest ( uid: . request_EditorOpen)
34
- if isURL {
35
- Request . addParameter ( . key_SourceFile, value: content)
36
- } else {
37
- Request . addParameter ( . key_SourceText, value: content)
38
- }
39
- Request . addParameter ( . key_Name, value: name)
149
+
150
+ Request . addParameter ( content. contentKey, value: content. contentValue)
151
+ Request . addParameter ( . key_Name, value: content. name)
152
+
40
153
Request . addParameter ( . key_SyntaxTreeTransferMode,
41
154
value: . kind_SyntaxTreeFull)
155
+ Request . addParameter ( . key_SyntaxTreeSerializationFormat,
156
+ value: format. kind)
42
157
Request . addParameter ( . key_EnableSyntaxMap, value: 0 )
43
158
Request . addParameter ( . key_EnableStructure, value: 0 )
44
159
Request . addParameter ( . key_SyntacticOnly, value: 1 )
@@ -50,28 +165,19 @@ public class SwiftLang {
50
165
}
51
166
52
167
let CloseReq = SourceKitdRequest ( uid: . request_EditorClose)
53
- CloseReq . addParameter ( . key_Name, value: name)
168
+ CloseReq . addParameter ( . key_Name, value: content . name)
54
169
let CloseResp = Service . sendSyn ( request: CloseReq)
55
170
if CloseResp . isError {
56
171
throw SourceKitdError . EditorCloseError ( message: CloseResp . description)
57
172
}
58
- return Resp . value. getString ( . key_SerializedSyntaxTree)
59
- }
60
-
61
- /// Parses the Swift file at the provided URL into a `Syntax` tree in Json
62
- /// serialization format by querying SourceKitd service. This function isn't
63
- /// thread safe.
64
- /// - Parameter url: The URL you wish to parse.
65
- /// - Returns: The syntax tree in Json format string.
66
- public static func parse( path: String ) throws -> String {
67
- return try parse ( content: path, name: path, isURL: true )
173
+ return try format. makeTree ( Resp . value)
68
174
}
175
+ }
69
176
70
- /// Parses a given source buffer into a `Syntax` tree in Json serialization
71
- /// format by querying SourceKitd service. This function isn't thread safe.
72
- /// - Parameter source: The source buffer you wish to parse.
73
- /// - Returns: The syntax tree in Json format string.
74
- public static func parse( source: String ) throws -> String {
75
- return try parse ( content: source, name: " foo " , isURL: false )
177
+ extension SwiftLang . SourceFile {
178
+ init ( path: String ) {
179
+ self . init ( name: path, contentKey: . key_SourceFile, contentValue: path)
76
180
}
77
181
}
182
+
183
+ private let syntaxTreeKey = SourceKitdUID . key_SerializedSyntaxTree
0 commit comments