C. Bess Wonders

XcodeKit: XCSourceTextRange multi-line selection replace

Saturday, June 4, 2022
visibility 229

XcodeKit: XCSourceTextRange multi-line selection replace

The code below will allow you to remove the selected text range, by getting the range that is selected within the buffer.

This will allow you to replace selected text for a single mult-line selection range.

import XcodeKit

let newLineRegex = try! NSRegularExpression(pattern: "\\n", options: [])

/// Returns the range in the Editor `completeBuffer` of the specified range
func stringRange(inTextRange range: XCSourceTextRange) -> Range<String.Index> {
    if isSameLine(range) {
        return lineStringRange(forTextRange: range)
    }

    let buffer = invocation.buffer.completeBuffer
    var startIndex = buffer.startIndex
    var endIndex = buffer.startIndex
    var lineIndex = 0

    // get the range for the start and end line
    newLineRegex.enumerateMatches(in: buffer, range: NSRange(..<buffer.endIndex, in: buffer)) { result, _, stop in
        // -1, to account for the target range being before the \n char
        if lineIndex == range.start.line - 1 {
            startIndex = Range(result!.range, in: buffer)!.lowerBound
        }

        if lineIndex == range.end.line - 1 {
            endIndex = Range(result!.range, in: buffer)!.lowerBound
            stop.pointee = true
        }

        lineIndex += 1
    }

    // +1, for the \n char
    let start = range.start.column + 1
    let end = range.end.column + 1
    let range = buffer.index(startIndex, offsetBy: start)..<buffer.index(endIndex, offsetBy: end)
    return range
}

Usage

let selectedBufferRange = stringRange(inTextRange: selectedSourceTextRange)
let selectedText = String(invocation.buffer.completeBuffer[selectedBufferRange])

// replace selected text with some other text
invocation.buffer.completeBuffer.replaceSubrange(selectedRange, with: someOtherText)

Other refs: #1 - #2 - #3

Soli Deo gloria