Refactor EditorCounter

This commit is contained in:
1024jp 2024-06-28 00:27:07 +09:00
parent 38bec5d9f2
commit 9ce8f7dffb
3 changed files with 32 additions and 25 deletions

View File

@ -35,6 +35,13 @@ import FileEncoding
import FilePermissions
import Syntax
extension Document: EditorSource {
var string: String? { self.textView?.string }
var selectedRanges: [NSRange] { self.textView?.selectedRanges.map(\.rangeValue) ?? [] }
}
@Observable final class Document: NSDocument, AdditionalDocumentPreparing, EncodingChanging {
// MARK: Enums
@ -126,7 +133,7 @@ import Syntax
super.init()
self.lineEndingScanner.observe(lineEnding: self.$lineEnding)
self.counter.document = self
self.counter.source = self
// auto-link URLs in the content
if UserDefaults.standard[.autoLinkDetection] {

View File

@ -23,16 +23,15 @@
// limitations under the License.
//
import AppKit
import Foundation
import Observation
protocol TextViewProvider: AnyObject {
@MainActor protocol EditorSource: AnyObject {
@MainActor var textView: NSTextView? { get }
var string: String? { get }
var selectedRanges: [NSRange] { get }
}
extension Document: TextViewProvider { }
struct EditorCount: Equatable {
@ -94,7 +93,7 @@ struct EditorCount: Equatable {
let result: Result = .init()
weak var document: (any TextViewProvider)? // weak to avoid cycle retain
weak var source: (any EditorSource)? // weak to avoid cycle retain
var updatesAll = false { didSet { self.updateTypes() } }
var statusBarRequirements: Types = [] { didSet { self.updateTypes() } }
@ -128,7 +127,7 @@ struct EditorCount: Equatable {
self.contentTask = Task {
try await Task.sleep(for: .milliseconds(20), tolerance: .milliseconds(20)) // debounce
guard let string = self.document?.textView?.string.immutable else { return }
guard let string = self.source?.string?.immutable else { return }
if self.types.contains(.characters) {
try Task.checkCancellation()
@ -158,10 +157,10 @@ struct EditorCount: Equatable {
self.selectionTask = Task {
try await Task.sleep(for: .milliseconds(200), tolerance: .milliseconds(40)) // debounce
guard let textView = self.document?.textView else { return }
let string = textView.string.immutable
let selectedRanges = textView.selectedRanges.compactMap { Range($0.rangeValue, in: string) }
guard
let string = self.source?.string?.immutable,
let selectedRanges = self.source?.selectedRanges.compactMap({ Range($0, in: string) })
else { return }
let selectedStrings = selectedRanges.map { string[$0] }
let location = selectedRanges.first?.lowerBound ?? string.startIndex

View File

@ -23,21 +23,22 @@
// limitations under the License.
//
import AppKit
import Foundation
import Testing
@testable import CotEditor
@MainActor final class EditorCounterTests {
@MainActor final class Provider: TextViewProvider {
@MainActor final class Source: EditorSource {
var textView: NSTextView? = NSTextView()
var string: String?
var selectedRanges: [NSRange]
init(string: String, selectedRange: NSRange) {
self.textView?.string = string
self.textView?.selectedRange = selectedRange
self.string = string
self.selectedRanges = [selectedRange]
}
}
@ -50,10 +51,10 @@ import Testing
@Test func noRequiredInfo() throws {
let provider = Provider(string: self.testString, selectedRange: NSRange(0..<3))
let source = Source(string: self.testString, selectedRange: NSRange(0..<3))
let counter = EditorCounter()
counter.document = provider
counter.source = source
counter.invalidateContent()
counter.invalidateSelection()
@ -68,10 +69,10 @@ import Testing
@Test func allRequiredInfo() throws {
let provider = Provider(string: self.testString, selectedRange: NSRange(11..<21))
let source = Source(string: self.testString, selectedRange: NSRange(11..<21))
let counter = EditorCounter()
counter.document = provider
counter.source = source
counter.updatesAll = true
counter.invalidateContent()
counter.invalidateSelection()
@ -94,10 +95,10 @@ import Testing
@Test func skipWholeText() throws {
let provider = Provider(string: self.testString, selectedRange: NSRange(11..<21))
let source = Source(string: self.testString, selectedRange: NSRange(11..<21))
let counter = EditorCounter()
counter.document = provider
counter.source = source
counter.updatesAll = true
counter.invalidateSelection()
@ -119,10 +120,10 @@ import Testing
@Test func crlf() throws {
let provider = Provider(string: "a\r\nb", selectedRange: NSRange(1..<4))
let source = Source(string: "a\r\nb", selectedRange: NSRange(1..<4))
let counter = EditorCounter()
counter.document = provider
counter.source = source
counter.updatesAll = true
counter.invalidateContent()
counter.invalidateSelection()