Skip to content

Commit 405ca3a

Browse files
committed
Fix that suggestions comments may be inserted to a wrong line
1 parent 865d850 commit 405ca3a

2 files changed

Lines changed: 111 additions & 49 deletions

File tree

Core/Sources/SuggestionInjector/SuggestionInjector.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ public struct SuggestionInjector {
8282
let commonPrefix = longestCommonPrefix(of: lines[1], and: existedLine ?? "")
8383

8484
if !commonPrefix.isEmpty {
85+
let replacingText = {
86+
switch (commonPrefix.hasSuffix("\n"), commonPrefix.count) {
87+
case (false, let count):
88+
return String(repeating: " ", count: count - 1) + "^"
89+
case (true, let count) where count > 1:
90+
return String(repeating: " ", count: count - 2) + "^\n"
91+
case (true, _):
92+
return "\n"
93+
}
94+
}()
95+
8596
lines[1].replaceSubrange(
8697
lines[1].startIndex..<(
8798
lines[1].index(
@@ -90,18 +101,20 @@ public struct SuggestionInjector {
90101
limitedBy: lines[1].endIndex
91102
) ?? lines[1].endIndex
92103
),
93-
with: String(repeating: " ", count: commonPrefix.count - 1) + "^"
104+
with: replacingText
94105
)
95106
}
96-
107+
97108
// if the suggestion is only appeding new lines and spaces, return without modification
98-
if completion.text.dropFirst(commonPrefix.count).allSatisfy({ $0.isWhitespace || $0.isNewline }) { return }
109+
if completion.text.dropFirst(commonPrefix.count)
110+
.allSatisfy({ $0.isWhitespace || $0.isNewline }) { return }
99111

112+
// determin if it's inserted to the current line or the next line
100113
let lineIndex = start.line + {
101114
guard let existedLine else { return 0 }
102115
if existedLine.isEmptyOrNewLine { return 1 }
103-
if !commonPrefix.isEmpty, commonPrefix.count <= existedLine.count - 1 { return 1 }
104-
return 0
116+
if commonPrefix.isEmpty { return 0 }
117+
return 1
105118
}()
106119
if content.endIndex < lineIndex {
107120
extraInfo.didChangeContent = true
@@ -164,6 +177,7 @@ public struct SuggestionInjector {
164177
}
165178

166179
extension String {
180+
/// Break a string into lines.
167181
func breakLines(appendLineBreakToLastLine: Bool = false) -> [String] {
168182
let lines = split(separator: "\n", omittingEmptySubsequences: false)
169183
var all = [String]()

Core/Tests/SuggestionInjectorTests/ProposeSuggestionTests.swift

Lines changed: 92 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,19 @@ final class ProposeSuggestionTests: XCTestCase {
3737
XCTAssertFalse(extraInfo.didChangeCursorPosition)
3838
XCTAssertEqual(extraInfo.suggestionRange, 2...5)
3939
XCTAssertEqual(lines, content.breakLines().applying(extraInfo.modifications))
40-
XCTAssertEqual(lines.joined(separator: ""), """
41-
struct Cat {
40+
XCTAssertEqual(
41+
lines.joined(separator: ""),
42+
"""
43+
struct Cat {
4244
43-
/*========== Copilot Suggestion 1/10
44-
var name: String
45-
var age: String
46-
*///======== End of Copilot Suggestion
47-
}
48-
""")
45+
/*========== Copilot Suggestion 1/10
46+
var name: String
47+
var age: String
48+
*///======== End of Copilot Suggestion
49+
}
50+
""",
51+
"The user may want to keep typing on the empty line, so suggestion is addded to the next line"
52+
)
4953
}
5054

5155
func test_propose_suggestion_no_overlap_start_from_previous_line() async throws {
@@ -134,19 +138,15 @@ final class ProposeSuggestionTests: XCTestCase {
134138
""")
135139
}
136140

137-
// swiftformat:disable indent trailingSpace
138-
func test_propose_suggestion_overlap_pure_spaces() async throws {
141+
func test_propose_suggestion_overlap_first_line_is_empty() async throws {
139142
let content = """
140-
func quickSort() {
141-
143+
struct Cat {
144+
var name: String
142145
}
143146
"""
144147
let text = """
145-
var array = [1, 3, 2, 4, 5, 6, 7, 8, 9, 10]
146-
var left = 0
147-
var right = array.count - 1
148-
quickSort(&array, left, right)
149-
print(array)
148+
var name: String
149+
var age: String
150150
"""
151151
let suggestion = CopilotCompletion(
152152
text: text,
@@ -169,40 +169,40 @@ final class ProposeSuggestionTests: XCTestCase {
169169
)
170170
XCTAssertTrue(extraInfo.didChangeContent)
171171
XCTAssertFalse(extraInfo.didChangeCursorPosition)
172-
XCTAssertEqual(extraInfo.suggestionRange, 2...8)
172+
XCTAssertEqual(extraInfo.suggestionRange, 2...5)
173173
XCTAssertEqual(lines, content.breakLines().applying(extraInfo.modifications))
174174
XCTAssertEqual(lines.joined(separator: ""), """
175-
func quickSort() {
176-
175+
struct Cat {
176+
var name: String
177177
/*========== Copilot Suggestion 1/10
178-
^var array = [1, 3, 2, 4, 5, 6, 7, 8, 9, 10]
179-
var left = 0
180-
var right = array.count - 1
181-
quickSort(&array, left, right)
182-
print(array)
178+
^
179+
var age: String
183180
*///======== End of Copilot Suggestion
184181
}
185182
""")
186183
}
187184

188-
// swiftformat:enable all
189-
190-
func test_propose_suggestion_overlap_one_line_adding_only_spaces() async throws {
185+
// swiftformat:disable indent trailingSpace
186+
func test_propose_suggestion_overlap_pure_spaces() async throws {
191187
let content = """
192-
if true {
193-
print("hello")
194-
} else {
195-
print("world")
188+
func quickSort() {
189+
196190
}
191+
""" // Yes the second line has 4 spaces!
192+
let text = """
193+
var array = [1, 3, 2, 4, 5, 6, 7, 8, 9, 10]
194+
var left = 0
195+
var right = array.count - 1
196+
quickSort(&array, left, right)
197+
print(array)
197198
"""
198-
let text = "} else {\n"
199199
let suggestion = CopilotCompletion(
200200
text: text,
201-
position: .init(line: 2, character: 0),
201+
position: .init(line: 1, character: 0),
202202
uuid: "",
203203
range: .init(
204-
start: .init(line: 2, character: 0),
205-
end: .init(line: 2, character: 8)
204+
start: .init(line: 1, character: 0),
205+
end: .init(line: 2, character: 18)
206206
),
207207
displayText: ""
208208
)
@@ -215,19 +215,26 @@ final class ProposeSuggestionTests: XCTestCase {
215215
count: 10,
216216
extraInfo: &extraInfo
217217
)
218-
XCTAssertFalse(extraInfo.didChangeContent)
218+
XCTAssertTrue(extraInfo.didChangeContent)
219219
XCTAssertFalse(extraInfo.didChangeCursorPosition)
220-
XCTAssertNil(extraInfo.suggestionRange)
220+
XCTAssertEqual(extraInfo.suggestionRange, 2...8)
221221
XCTAssertEqual(lines, content.breakLines().applying(extraInfo.modifications))
222222
XCTAssertEqual(lines.joined(separator: ""), """
223-
if true {
224-
print("hello")
225-
} else {
226-
print("world")
223+
func quickSort() {
224+
225+
/*========== Copilot Suggestion 1/10
226+
^var array = [1, 3, 2, 4, 5, 6, 7, 8, 9, 10]
227+
var left = 0
228+
var right = array.count - 1
229+
quickSort(&array, left, right)
230+
print(array)
231+
*///======== End of Copilot Suggestion
227232
}
228-
""")
233+
""") // Yes the second line still has 4 spaces!
229234
}
230-
235+
236+
// swiftformat:enable all
237+
231238
func test_propose_suggestion_partial_overlap() async throws {
232239
let content = "func quickSort() {}}\n"
233240
let text = """
@@ -276,4 +283,45 @@ final class ProposeSuggestionTests: XCTestCase {
276283
277284
""")
278285
}
286+
287+
func test_propose_suggestion_overlap_one_line_adding_only_spaces() async throws {
288+
let content = """
289+
if true {
290+
print("hello")
291+
} else {
292+
print("world")
293+
}
294+
"""
295+
let text = "} else {\n"
296+
let suggestion = CopilotCompletion(
297+
text: text,
298+
position: .init(line: 2, character: 0),
299+
uuid: "",
300+
range: .init(
301+
start: .init(line: 2, character: 0),
302+
end: .init(line: 2, character: 8)
303+
),
304+
displayText: ""
305+
)
306+
var extraInfo = SuggestionInjector.ExtraInfo()
307+
var lines = content.breakLines()
308+
SuggestionInjector().proposeSuggestion(
309+
intoContentWithoutSuggestion: &lines,
310+
completion: suggestion,
311+
index: 0,
312+
count: 10,
313+
extraInfo: &extraInfo
314+
)
315+
XCTAssertFalse(extraInfo.didChangeContent)
316+
XCTAssertFalse(extraInfo.didChangeCursorPosition)
317+
XCTAssertNil(extraInfo.suggestionRange)
318+
XCTAssertEqual(lines, content.breakLines().applying(extraInfo.modifications))
319+
XCTAssertEqual(lines.joined(separator: ""), """
320+
if true {
321+
print("hello")
322+
} else {
323+
print("world")
324+
}
325+
""")
326+
}
279327
}

0 commit comments

Comments
 (0)