Skip to content

Commit a60b35a

Browse files
committed
Refactors willEditDocument in favor of SourceEdits
Removes willEditDocument completion block and moves that code into the DocumentManager using SourceEdit
1 parent 6087ce5 commit a60b35a

File tree

2 files changed

+63
-42
lines changed

2 files changed

+63
-42
lines changed

Sources/SourceKitLSP/DocumentManager.swift

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Dispatch
1414
import LSPLogging
1515
import LanguageServerProtocol
1616
import SKSupport
17+
import SwiftSyntax
1718

1819
/// An immutable snapshot of a document at a given time.
1920
///
@@ -140,26 +141,27 @@ public final class DocumentManager {
140141
/// - newVersion: The new version of the document. Must be greater than the
141142
/// latest version of the document.
142143
/// - edits: The edits to apply to the document
143-
/// - willEditDocument: Optional closure to call before each edit. Will be
144-
/// called multiple times if there are multiple edits.
144+
/// - requiresUpcomingEdits: Flag to return upcoming editst. Will be
145+
/// calling ``willEditDocument`` for each edit made
145146
/// - Returns: The snapshot of the document before the edit and the snapshot
146-
/// of the document after the edit.
147+
/// of the document after the edit, including all edits that were made.
147148
@discardableResult
148149
public func edit(
149150
_ uri: DocumentURI,
150151
newVersion: Int,
151152
edits: [TextDocumentContentChangeEvent],
152-
willEditDocument: ((_ before: LineTable, TextDocumentContentChangeEvent) -> Void)? = nil
153-
) throws -> (preEditSnapshot: DocumentSnapshot, postEditSnapshot: DocumentSnapshot) {
153+
requiresUpcomingEdits: Bool = false
154+
) throws -> (preEditSnapshot: DocumentSnapshot, postEditSnapshot: DocumentSnapshot, edits: [SourceEdit]) {
154155
return try queue.sync {
155156
guard let document = documents[uri] else {
156157
throw Error.missingDocument(uri)
157158
}
158159
let preEditSnapshot = document.latestSnapshot
159160

161+
var upcomingEdits: [SourceEdit] = []
160162
for edit in edits {
161-
if let willEditDocument {
162-
willEditDocument(document.latestLineTable, edit)
163+
if requiresUpcomingEdits {
164+
upcomingEdits.append(willEditDocument(document.latestLineTable, edit: edit))
163165
}
164166

165167
if let range = edit.range {
@@ -180,7 +182,7 @@ public final class DocumentManager {
180182
logger.error("Document version did not increase on edit from \(document.latestVersion) to \(newVersion)")
181183
}
182184
document.latestVersion = newVersion
183-
return (preEditSnapshot, document.latestSnapshot)
185+
return (preEditSnapshot, document.latestSnapshot, upcomingEdits)
184186
}
185187
}
186188

@@ -192,6 +194,35 @@ public final class DocumentManager {
192194
return document.latestSnapshot
193195
}
194196
}
197+
198+
private func willEditDocument(_ before: LineTable, edit: TextDocumentContentChangeEvent) -> SourceEdit {
199+
guard let range = edit.range else {
200+
return SourceEdit(
201+
range: Range(
202+
uncheckedBounds: (
203+
AbsolutePosition(utf8Offset: 0),
204+
upper: AbsolutePosition(utf8Offset: before.content.utf8.count)
205+
)
206+
),
207+
replacement: edit.text
208+
)
209+
}
210+
guard let offset = before.utf8OffsetOf(line: range.lowerBound.line, utf16Column: range.lowerBound.utf16index),
211+
let end = before.utf8OffsetOf(line: range.upperBound.line, utf16Column: range.upperBound.utf16index)
212+
else {
213+
fatalError("invalid edit \(range)")
214+
}
215+
return SourceEdit(
216+
range: Range(
217+
uncheckedBounds: (
218+
AbsolutePosition(utf8Offset: offset),
219+
upper: AbsolutePosition(utf8Offset: end)
220+
)
221+
),
222+
replacement: edit.text
223+
)
224+
}
225+
195226
}
196227

197228
extension DocumentManager {
@@ -214,19 +245,19 @@ extension DocumentManager {
214245
}
215246
}
216247

217-
/// Convenience wrapper for `edit(_:newVersion:edits:willEditDocument:updateDocumentTokens:)`
248+
/// Convenience wrapper for `edit(_:newVersion:edits:requiresUpcomingEdits:updateDocumentTokens:)`
218249
/// that logs on failure.
219250
@discardableResult
220251
func edit(
221252
_ note: DidChangeTextDocumentNotification,
222-
willEditDocument: ((_ before: LineTable, TextDocumentContentChangeEvent) -> Void)? = nil
223-
) -> (preEditSnapshot: DocumentSnapshot, postEditSnapshot: DocumentSnapshot)? {
253+
requiresUpcomingEdits: Bool = false
254+
) -> (preEditSnapshot: DocumentSnapshot, postEditSnapshot: DocumentSnapshot, edits: [SourceEdit])? {
224255
return orLog("failed to edit document", level: .error) {
225256
return try edit(
226257
note.textDocument.uri,
227258
newVersion: note.textDocument.version,
228259
edits: note.contentChanges,
229-
willEditDocument: willEditDocument
260+
requiresUpcomingEdits: requiresUpcomingEdits
230261
)
231262
}
232263
}

Sources/SourceKitLSP/Swift/SwiftLanguageServer.swift

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -497,33 +497,12 @@ extension SwiftLanguageServer {
497497
let replacement: String
498498
}
499499

500-
var edits: [Edit] = []
500+
let editResult = self.documentManager.edit(
501+
note,
502+
requiresUpcomingEdits: true
503+
)
501504

502-
let editResult = self.documentManager.edit(note) {
503-
(before: LineTable, edit: TextDocumentContentChangeEvent) in
504-
if let range = edit.range {
505-
guard let offset = before.utf8OffsetOf(line: range.lowerBound.line, utf16Column: range.lowerBound.utf16index),
506-
let end = before.utf8OffsetOf(line: range.upperBound.line, utf16Column: range.upperBound.utf16index)
507-
else {
508-
fatalError("invalid edit \(range)")
509-
}
510-
edits.append(
511-
Edit(
512-
offset: offset,
513-
length: end - offset,
514-
replacement: edit.text
515-
)
516-
)
517-
} else {
518-
edits.append(
519-
Edit(
520-
offset: 0,
521-
length: before.content.utf8.count,
522-
replacement: edit.text
523-
)
524-
)
525-
}
526-
}
505+
let edits = editResult?.edits ?? []
527506
for edit in edits {
528507
let req = sourcekitd.dictionary([
529508
keys.request: self.requests.editorReplaceText,
@@ -532,8 +511,8 @@ extension SwiftLanguageServer {
532511
keys.enableStructure: 0,
533512
keys.enableDiagnostics: 0,
534513
keys.syntacticOnly: 1,
535-
keys.offset: edit.offset,
536-
keys.length: edit.length,
514+
keys.offset: edit.range.lowerBound.utf8Offset,
515+
keys.length: edit.positiveLength,
537516
keys.sourceText: edit.replacement,
538517
])
539518
do {
@@ -543,12 +522,16 @@ extension SwiftLanguageServer {
543522
}
544523
}
545524

546-
guard let (preEditSnapshot, postEditSnapshot) = editResult else {
525+
guard let (preEditSnapshot, postEditSnapshot, _) = editResult else {
547526
return
548527
}
549528
let concurrentEdits = ConcurrentEdits(
550529
fromSequential: edits.map {
551-
IncrementalEdit(offset: $0.offset, length: $0.length, replacementLength: $0.replacement.utf8.count)
530+
IncrementalEdit(
531+
offset: $0.range.lowerBound.utf8Offset,
532+
length: $0.positiveLength,
533+
replacementLength: $0.replacement.utf8.count
534+
)
552535
}
553536
)
554537
await syntaxTreeManager.registerEdit(
@@ -1129,3 +1112,10 @@ extension sourcekitd_api_uid_t {
11291112
}
11301113
}
11311114
}
1115+
1116+
extension SourceEdit {
1117+
var positiveLength: Int {
1118+
// Required since SourceEdit returns a negative length
1119+
-length.utf8Length
1120+
}
1121+
}

0 commit comments

Comments
 (0)