mirror of
https://github.com/coteditor/CotEditor.git
synced 2024-09-11 19:27:30 +03:00
Refactor EditorCounter
This commit is contained in:
parent
38bec5d9f2
commit
9ce8f7dffb
@ -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] {
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user