Skip to content

Commit 045024d

Browse files
authored
Merge pull request #1385 from ahoppen/improve-line-folding
Improve folding ranges if editor only supports line folding
2 parents 936b5b2 + 2da648e commit 045024d

File tree

2 files changed

+78
-8
lines changed

2 files changed

+78
-8
lines changed

Sources/SourceKitLSP/Swift/FoldingRange.swift

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import LSPLogging
1414
import LanguageServerProtocol
15+
import SKSupport
1516
import SwiftSyntax
1617

1718
fileprivate final class FoldingRangeFinder: SyntaxAnyVisitor {
@@ -210,9 +211,21 @@ fileprivate final class FoldingRangeFinder: SyntaxAnyVisitor {
210211
let end = snapshot.positionOf(utf8Offset: end.utf8Offset)
211212
let range: FoldingRange
212213
if lineFoldingOnly {
214+
// If the folding range doesn't end at the end of the last line, exclude that line from the folding range since
215+
// the end line gets folded away. This means if we reported `end.line`, we would eg. fold away the `}` that
216+
// matches a `{`, which looks surprising.
217+
// If the folding range does end at the end of the line we are in cases that don't have a closing indicator (like
218+
// comments), so we can fold the last line as well.
219+
let endLine: Int
220+
if snapshot.lineTable.isAtEndOfLine(end) {
221+
endLine = end.line
222+
} else {
223+
endLine = end.line - 1
224+
}
225+
213226
// Since the client cannot fold less than a single line, if the
214227
// fold would span 1 line there's no point in reporting it.
215-
guard end.line > start.line else {
228+
guard endLine > start.line else {
216229
return .visitChildren
217230
}
218231

@@ -221,7 +234,7 @@ fileprivate final class FoldingRangeFinder: SyntaxAnyVisitor {
221234
range = FoldingRange(
222235
startLine: start.line,
223236
startUTF16Index: nil,
224-
endLine: end.line,
237+
endLine: endLine,
225238
endUTF16Index: nil,
226239
kind: kind
227240
)
@@ -264,3 +277,14 @@ extension SwiftLanguageService {
264277
return ranges.sorted()
265278
}
266279
}
280+
281+
fileprivate extension LineTable {
282+
func isAtEndOfLine(_ position: Position) -> Bool {
283+
guard position.line >= 0, position.line < self.count else {
284+
return false
285+
}
286+
let line = self[position.line]
287+
let suffixAfterPositionColumn = line[line.utf16.index(line.startIndex, offsetBy: position.utf16index)...]
288+
return suffixAfterPositionColumn.allSatisfy(\.isNewline)
289+
}
290+
}

Tests/SourceKitLSPTests/FoldingRangeTests.swift

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,63 @@ final class FoldingRangeTests: XCTestCase {
9696
try await assertFoldingRanges(
9797
markedSource: """
9898
1️⃣func foo() {
99-
100-
2️⃣}
101-
"""
102-
,
99+
2️⃣
100+
}
101+
""",
103102
expectedRanges: [
104103
FoldingRangeSpec(from: "1️⃣", to: "2️⃣")
105104
],
106105
lineFoldingOnly: true
107106
)
108107
}
109108

109+
func testLineFoldingOfFunctionWithMultiLineParameters() async throws {
110+
try await assertFoldingRanges(
111+
markedSource: """
112+
1️⃣func foo(
113+
2️⃣ param: Int
114+
3️⃣) {
115+
print(param)
116+
4️⃣
117+
}
118+
""",
119+
expectedRanges: [
120+
FoldingRangeSpec(from: "1️⃣", to: "2️⃣"),
121+
FoldingRangeSpec(from: "3️⃣", to: "4️⃣"),
122+
],
123+
lineFoldingOnly: true
124+
)
125+
}
126+
127+
func testLineFoldingOfComment() async throws {
128+
try await assertFoldingRanges(
129+
markedSource: """
130+
1️⃣// abc
131+
// def
132+
2️⃣// ghi
133+
134+
""",
135+
expectedRanges: [
136+
FoldingRangeSpec(from: "1️⃣", to: "2️⃣", kind: .comment)
137+
],
138+
lineFoldingOnly: true
139+
)
140+
}
141+
142+
func testLineFoldingOfCommentAtEndOfFile() async throws {
143+
try await assertFoldingRanges(
144+
markedSource: """
145+
1️⃣// abc
146+
// def
147+
2️⃣// ghi
148+
""",
149+
expectedRanges: [
150+
FoldingRangeSpec(from: "1️⃣", to: "2️⃣", kind: .comment)
151+
],
152+
lineFoldingOnly: true
153+
)
154+
}
155+
110156
func testLineFoldingDoesntReportSingleLine() async throws {
111157
try await assertFoldingRanges(
112158
markedSource: """
@@ -272,8 +318,8 @@ final class FoldingRangeTests: XCTestCase {
272318
try await assertFoldingRanges(
273319
markedSource: """
274320
let x = [1️⃣
275-
1: "one",
276-
2: "two",
321+
1: "one",
322+
2: "two",
277323
3: "three"
278324
2️⃣]
279325
""",

0 commit comments

Comments
 (0)