@@ -16,32 +16,56 @@ import SKSupport
16
16
import sourcekitd
17
17
18
18
extension CodeAction {
19
- init ? ( fixit: SKResponseDictionary , in snapshot: DocumentSnapshot ) {
20
- let keys = fixit. sourcekitd. keys
21
19
22
- guard let utf8Offset: Int = fixit [ keys. offset] ,
23
- let length: Int = fixit [ keys. length] ,
24
- let replacement: String = fixit [ keys. sourcetext] ,
25
- let position = snapshot. positionOf ( utf8Offset: utf8Offset) ,
26
- let endPosition = snapshot. positionOf ( utf8Offset: utf8Offset + length) ,
27
- let startIndex = snapshot. indexOf ( utf8Offset: utf8Offset) ,
28
- let endIndex = snapshot. indexOf ( utf8Offset: utf8Offset + length) ,
29
- length > 0 || !replacement. isEmpty
30
- else {
20
+ /// Creates a CodeAction from a list for sourcekit fixits.
21
+ ///
22
+ /// If this is from a note, the note's description should be passed as `fromNote`.
23
+ init ? ( fixits: SKResponseArray , in snapshot: DocumentSnapshot , fromNote: String ? ) {
24
+ var edits : [ TextEdit ] = [ ]
25
+ let editsMapped = fixits. forEach { ( _, skfixit) -> Bool in
26
+ if let edit = TextEdit ( fixit: skfixit, in: snapshot) {
27
+ edits. append ( edit)
28
+ return true
29
+ }
30
+ return false
31
+ }
32
+
33
+ if !editsMapped {
34
+ log ( " failed to construct TextEdits from response \( fixits) " , level: . warning)
31
35
return nil
32
36
}
33
37
34
- let range = position..< endPosition
35
- let original = String ( snapshot. text [ startIndex..< endIndex] )
36
- let title = Self . fixitTitle ( replace: original, with: replacement)
37
- let workspaceEdit = WorkspaceEdit (
38
- changes: [ snapshot. document. uri: [ TextEdit ( range: range, newText: replacement) ] ] )
38
+ if edits. isEmpty {
39
+ return nil
40
+ }
41
+
42
+ let title : String
43
+ if let fromNote = fromNote {
44
+ title = fromNote
45
+ } else {
46
+ guard let startIndex = snapshot. index ( of: edits [ 0 ] . range. lowerBound) ,
47
+ let endIndex = snapshot. index ( of: edits [ 0 ] . range. upperBound) ,
48
+ startIndex <= endIndex,
49
+ snapshot. text. indices. contains ( startIndex) ,
50
+ endIndex <= snapshot. text. endIndex
51
+ else {
52
+ logAssertionFailure ( " position mapped, but indices failed for edit range \( edits [ 0 ] ) " )
53
+ return nil
54
+ }
55
+ let oldText = String ( snapshot. text [ startIndex..< endIndex] )
56
+ let description = Self . fixitTitle ( replace: oldText, with: edits [ 0 ] . newText)
57
+ if edits. count == 1 {
58
+ title = description
59
+ } else {
60
+ title = description + " ... "
61
+ }
62
+ }
39
63
40
64
self . init (
41
65
title: title,
42
66
kind: . quickFix,
43
67
diagnostics: nil ,
44
- edit: workspaceEdit )
68
+ edit: WorkspaceEdit ( changes : [ snapshot . document . uri : edits ] ) )
45
69
}
46
70
47
71
/// Describe a fixit's edit briefly.
@@ -61,6 +85,25 @@ extension CodeAction {
61
85
}
62
86
}
63
87
88
+ extension TextEdit {
89
+
90
+ /// Creates a TextEdit from a sourcekitd fixit response dictionary.
91
+ init ? ( fixit: SKResponseDictionary , in snapshot: DocumentSnapshot ) {
92
+ let keys = fixit. sourcekitd. keys
93
+ if let utf8Offset: Int = fixit [ keys. offset] ,
94
+ let length: Int = fixit [ keys. length] ,
95
+ let replacement: String = fixit [ keys. sourcetext] ,
96
+ let position = snapshot. positionOf ( utf8Offset: utf8Offset) ,
97
+ let endPosition = snapshot. positionOf ( utf8Offset: utf8Offset + length) ,
98
+ length > 0 || !replacement. isEmpty
99
+ {
100
+ self . init ( range: position..< endPosition, newText: replacement)
101
+ } else {
102
+ return nil
103
+ }
104
+ }
105
+ }
106
+
64
107
extension Diagnostic {
65
108
66
109
/// Creates a diagnostic from a sourcekitd response dictionary.
@@ -98,26 +141,18 @@ extension Diagnostic {
98
141
}
99
142
}
100
143
101
- var fixits : [ CodeAction ] ? = nil
102
- if let skfixits: SKResponseArray = diag [ keys. fixits] {
103
- fixits = [ ]
104
- skfixits. forEach { ( _, skfixit) -> Bool in
105
- if let codeAction = CodeAction ( fixit: skfixit, in: snapshot) {
106
- fixits? . append ( codeAction)
107
- }
108
- return true
109
- }
144
+ var actions : [ CodeAction ] ? = nil
145
+ if let skfixits: SKResponseArray = diag [ keys. fixits] ,
146
+ let action = CodeAction ( fixits: skfixits, in: snapshot, fromNote: nil ) {
147
+ actions = [ action]
110
148
}
111
149
112
150
var notes : [ DiagnosticRelatedInformation ] ? = nil
113
151
if let sknotes: SKResponseArray = diag [ keys. diagnostics] {
114
152
notes = [ ]
115
153
sknotes. forEach { ( _, sknote) -> Bool in
116
- guard let note = Diagnostic ( sknote, in: snapshot) else { return true }
117
- notes? . append ( DiagnosticRelatedInformation (
118
- location: Location ( uri: snapshot. document. uri, range: note. range) ,
119
- message: note. message
120
- ) )
154
+ guard let note = DiagnosticRelatedInformation ( sknote, in: snapshot) else { return true }
155
+ notes? . append ( note)
121
156
return true
122
157
}
123
158
}
@@ -129,7 +164,42 @@ extension Diagnostic {
129
164
source: " sourcekitd " ,
130
165
message: message,
131
166
relatedInformation: notes,
132
- codeActions: fixits)
167
+ codeActions: actions)
168
+ }
169
+ }
170
+
171
+ extension DiagnosticRelatedInformation {
172
+
173
+ /// Creates related information from a sourcekitd note response dictionary.
174
+ init ? ( _ diag: SKResponseDictionary , in snapshot: DocumentSnapshot ) {
175
+ let keys = diag. sourcekitd. keys
176
+
177
+ var position : Position ? = nil
178
+ if let line: Int = diag [ keys. line] ,
179
+ let utf8Column: Int = diag [ keys. column] ,
180
+ line > 0 , utf8Column > 0
181
+ {
182
+ position = snapshot. positionOf ( zeroBasedLine: line - 1 , utf8Column: utf8Column - 1 )
183
+ } else if let utf8Offset: Int = diag [ keys. offset] {
184
+ position = snapshot. positionOf ( utf8Offset: utf8Offset)
185
+ }
186
+
187
+ if position == nil {
188
+ return nil
189
+ }
190
+
191
+ guard let message: String = diag [ keys. description] else { return nil }
192
+
193
+ var actions : [ CodeAction ] ? = nil
194
+ if let skfixits: SKResponseArray = diag [ keys. fixits] ,
195
+ let action = CodeAction ( fixits: skfixits, in: snapshot, fromNote: message) {
196
+ actions = [ action]
197
+ }
198
+
199
+ self . init (
200
+ location: Location ( uri: snapshot. document. uri, range: Range ( position!) ) ,
201
+ message: message,
202
+ codeActions: actions)
133
203
}
134
204
}
135
205
0 commit comments