Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What is going on with this article?
@funzin

Xcode Source Editor Extensionでテキストを複数選択するときに気づきにくい挙動

More than 1 year has passed since last update.

Xcode Source Editor Extensionでテキストの複数選択を取得する場合、
XCSourceTextRangeを使用することで取得可能ですが、実際の使用感とは異なるケースに遭遇したため、以下にまとめました。

環境

  • Xcode10.3
  • Swift5

サンプルコード

SourceEditorCommand.swiftは以下のコードを使用することとします。

import Foundation
import XcodeKit

class SourceEditorCommand: NSObject, XCSourceEditorCommand {

    func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void ) -> Void {
        let textBuffer = invocation.buffer
        let lines = textBuffer.lines
        let selections = textBuffer.selections

        guard let selection = selections.firstObject as? XCSourceTextRange,
            let _lines = Array(lines) as? [String] else {
            completionHandler(NSError(domain: "Sample", code: 401, userInfo: ["reason": "text is not selected"]))
            return
        }

        let startLine = selection.start.line
        let endLine = selection.end.line

        print("selection: \(selection)")
        print("startLine: \(startLine)")
        print("endLine: \(endLine)")

        let selectedLines = Array(_lines[startLine...endLine])
        print("selectedLines: \(selectedLines)")
        completionHandler(nil)
    }
}

検証

以下の画像のようにSampleからTestの末尾までの2行を選択してSourceEditorCommandを実行します。
Screen Shot 2019-09-06 at 23.19.59.png

すると以下のように出力されます。

selection: <XCSourceTextRange: 0x7fd7a4702600 {{line: 0, column: 0}, {line: 1, column: 4}}>
startLine: 0
endLine: 1
selectedLines: ["Sample\n", "Test\n"]

XCSourceTextRangeの出力では、lineが行番号、columnがその行の何番目を示しているので正しい出力のように見えます。

では次に選択範囲の終端を文字ではなく、行末に変更するとどうなるでしょうか
Screen Shot 2019-09-06 at 23.22.07.png

結果は、selectedLinesArray index is out of rangeのためcrashします

selection: <XCSourceTextRange: 0x7fd2ef813f80 {{line: 0, column: 0}, {line: 2, column: 0}}>
startLine: 0
endLine: 2
Fatal error: Array index is out of range
2019-09-06 23:26:30.600303+0900 Extension[3649:87042] Fatal error: Array index is out of range

原因

終端が文字ではなく、行末の状態で複数行選択しているとselection.end.line実際に選択している次の行の先頭を示します。サンプルの場合だと3行目の0番目を指していることになります。しかし実際に存在するのは["Sample", "Test"]の2行のみのためArray index is out of rangeとなります。

完成コード

そのため、選択範囲を正しく抽出したい場合、以下のように書き換える必要があります。

import Foundation
import XcodeKit

class SourceEditorCommand: NSObject, XCSourceEditorCommand {

    func perform(with invocation: XCSourceEditorCommandInvocation, completionHandler: @escaping (Error?) -> Void ) -> Void {
        let textBuffer = invocation.buffer
        let lines = textBuffer.lines
        let selections = textBuffer.selections

        guard let selection = selections.firstObject as? XCSourceTextRange,
            let _lines = Array(lines) as? [String] else {
            completionHandler(NSError(domain: "Sample", code: 401, userInfo: ["reason": "text is not selected"]))
            return
        }

        let startLine = selection.start.line

        // 複数行選択かつ、selection.end.columnが0番目を指している場合
        let endLine = (selection.end.column == 0 && (selection.start.line != selection.end.line))
            ? selection.end.line - 1
            : selection.end.line


        print("selection: \(selection)")
        print("startLine: \(startLine)")
        print("endLine: \(endLine)")

        let selectedLines = Array(_lines[startLine...endLine])
        print("selectedLines: \(selectedLines)")
        completionHandler(nil)
    }
}

すると以下のようにendLineが正しく出力されます。

selection: <XCSourceTextRange: 0x7f8589e0abc0 {{line: 0, column: 0}, {line: 2, column: 0}}>
startLine: 0
endLine: 1
selectedLines: ["Sample\n", "Test\n"]

まとめ

上記の対応をすることで、選択範囲の終端が文字or行であっても、対応可能となります。
複数行のテキスト選択を用いたXcode Source Editor Extensionを開発する方は是非参考にしてみてください。

1
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
funzin

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
1
Help us understand the problem. What is going on with this article?