-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathIBeamTextViewSystem.swift
91 lines (74 loc) · 3.06 KB
/
IBeamTextViewSystem.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import AppKit
import IBeam
import Glyph
import Ligature
extension IBeam.TextGranularity {
var ligatureGranulaity: Ligature.TextGranularity {
switch self {
case .character: .character
case .word: .word
case .line: .line
}
}
}
@MainActor
public struct IBeamTextViewSystem {
let textView: NSTextView
let tokenizer: UTF16CodePointTextViewTextTokenizer
public init(textView: NSTextView) {
self.textView = textView
self.tokenizer = UTF16CodePointTextViewTextTokenizer(textView: textView)
}
private var partialSystem: MutableStringPartialSystem {
MutableStringPartialSystem(textView.textStorage ?? NSTextStorage())
}
}
extension IBeamTextViewSystem : @preconcurrency IBeam.TextSystemInterface {
public typealias TextRange = NSRange
public typealias TextPosition = Int
public func location(for position: TextPosition) -> CGFloat? {
tokenizer.location(for: position)
}
// movement calculation
public func position(from position: TextPosition, moving direction: IBeam.TextDirection, by granularity: IBeam.TextGranularity) -> TextPosition? {
let ligGranularity = granularity.ligatureGranulaity
switch direction {
case .forward:
return tokenizer.position(from: position, toBoundary: ligGranularity, inDirection: .storage(.forward))
case .backward:
return tokenizer.position(from: position, toBoundary: ligGranularity, inDirection: .storage(.backward))
case .left:
return tokenizer.position(from: position, toBoundary: ligGranularity, inDirection: .layout(.left))
case .right:
return tokenizer.position(from: position, toBoundary: ligGranularity, inDirection: .layout(.right))
case let .down(alignment):
return tokenizer.position(from: position, toBoundary: ligGranularity, inDirection: .layout(.down), alignment: alignment)
case let .up(alignment):
return tokenizer.position(from: position, toBoundary: ligGranularity, inDirection: .layout(.up), alignment: alignment)
}
}
public func position(from start: TextPosition, offset: Int) -> TextPosition? {
partialSystem.position(from: start, offset: offset)
}
public func layoutDirection(at position: TextPosition) -> IBeam.TextLayoutDirection? {
partialSystem.layoutDirection(at: position)
}
// range calculation
public var beginningOfDocument: TextPosition { partialSystem.beginningOfDocument }
public var endOfDocument: TextPosition { partialSystem.endOfDocument }
public func compare(_ position: TextPosition, to other: TextPosition) -> ComparisonResult {
partialSystem.compare(position, to: other)
}
public func positions(composing range: TextRange) -> (TextPosition, TextPosition) {
partialSystem.positions(composing: range)
}
public func textRange(from start: TextPosition, to end: TextPosition) -> TextRange? {
partialSystem.textRange(from: start, to: end)
}
// content mutation
public func beginEditing() { partialSystem.beginEditing() }
public func endEditing() { partialSystem.endEditing() }
public func applyMutation(_ range: TextRange, string: AttributedString) -> MutationOutput<TextRange>? {
partialSystem.applyMutation(range, string: string, undoManager: nil)
}
}