Slide 1

Slide 1 text

題: 場所: 日付: 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ NBD04OBUJWF4ZNQPTJVN ೥݄೔ KQ 発表者:

Slide 2

Slide 2 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp ࣗݾ঺հ CotEditor Gapplin Qli application works plain-text editor SVG viewer movie player macOS meet-up icon works @1024jp hobby macOS developer/designer

Slide 3

Slide 3 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp $PU&EJUPS CotEditor plain-text editor

Slide 4

Slide 4 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp ࠓ೔͢Δ࿩ 2022೥6݄ 2018೥12݄ 5FYU,JUͰγϯλοΫεϋΠϥΠτΛ͢Δʹ͋ͨͬͯͷߴ଎ԽϙΠϯτ

Slide 5

Slide 5 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp γϯλοΫεϋΠϥΠτ CotEditor -Text Editor for macOS CotEditor -Text Editor for macOS

Slide 6

Slide 6 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp ʢ$PU&EJUPSͰʣٻΊΒΕ͍ͯΔ͜ͱ ✔ 5FYU,JUΛ࢖͏ ! NBD04ͷػೳΛ࠷େݶڗड͢Δ ! ଟ༷ͳݴޠʹରԠ͢ΔʢFHॎॻ͖ CJEJςΩετ ϓϩϙʔγϣφϧϑΥϯτ 7PJDF0WFS FUDʣ 5FYU,JU͸େมʹϦον͕ͩɺ ͦͷ෼΍͍ͬͯΔ͜ͱ͕ଟ͍ TextKit 2には期待してるよ… = 一億字 ॲཧͷϘτϧωοΫ͕ଘࡏ͢Δ ✔ ͲΜͳॻྨ͕։͔Εͯ΋ͦΕͳΓʹॲཧ͕Ͱ͖Δ ! ྫ͑͹.# ࣈ ͷ9.-ϑΝΠϧ͕։͔Εͨͱ͖Ͳ͏ͳΔͷ͔ ! "

Slide 7

Slide 7 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp γϯλοΫεϋΠϥΠτΛߴ଎Խ͢ΔͨΊͷςΫχοΫ ᶃ ඞཁͳՕॴ͚ͩύʔε ᶄ όοΫάϥ΢ϯυεϨουͰύʔεΛ͢Δ ᶈ EJTQMBZͷWBMJEBUJPOΛ஗Ԇ͢Δ ᶉ MBZPVUNBOBHFSʹۃྗSBOHFܭࢉΛͤ͞ͳ͍ ᶅ UFNQPSBSZBUUSJCVUFTΛ࢖͏ ᶆ ฒྻʹॲཧ͢Δ ᶇ όοΫάϥ΢υʹ͍Δؒʹద༻಺༰Λ࠷খԽ͢Δ ϋʔυίΞ ॳ ڃ ج ຊ Y. Niwa: iOS ͷΩʔϘʔυͱจࣈೖྗͷ͢΂ͯ, iOSDC Japan 2020, 2020-09

Slide 8

Slide 8 text

©2022 1024jp macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ᶃ ඞཁͳՕॴ͚ͩύʔε ᶄ όοΫάϥ΢ϯυεϨουͰύʔεΛ͢Δ ج ຊ

Slide 9

Slide 9 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp ύʔεൣғ CotEditor -Text Editor for macOS switch (navigator.language.substr(0,2)) { case 'ja': window.location = 'index.ja'; break; case 'tr': window.location = 'index.tr'; break; } [appIcon]

CotEditor

The Plain-Text Editor for macOS

It's free!

Download on the Mac App Store
[screenshot] [screenshot: Dark Mode] [screenshot: tools] [screenshot: vertical orientation] [screenshot: preferences window]
[appIcon]

CotEditor

The Plain-Text Editor for macOS

It's free!

Download on the Mac App Store edited in window document edited characters Download on the Mac App Store appx. 1,000 จࣈ

Slide 10

Slide 10 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp γϯλοΫεϋΠϥΠτͷྲྀΕ ϋ Π ϥ Π τ ׬ ྃ ς Ω ε τ ม ߋ จ ࣈ ྻ ύ ồ ε ϋ Π ϥ Π τ ద ༻ start end ύ ồ ε ൣ ғ ܾ ఆ

Slide 11

Slide 11 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp γϯλοΫεϋΠϥΠτͷྲྀΕ Main Thread ϋ Π ϥ Π τ ׬ ྃ ς Ω ε τ ม ߋ จ ࣈ ྻ ύ ồ ε ϋ Π ϥ Π τ ద ༻ start end ύ ồ ε ൣ ғ ܾ ఆ Highlighting… ! Background Threads

Slide 12

Slide 12 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp let string = NSString(string: textStorage.string) as String Task.detached { let highlights = parser.parse(string, in: range) await MainActor.run { textStorage.apply(highlights) } } όοΫάϥ΢ϯυεϨουʹTUSJOHΛ౉͢ let string = textStorage.string Task.detached { let highlights = parser.parse(string, in: range) await MainActor.run { textStorage.apply(highlights) } } NSBigMutableString crash !! NSTaggedPointerString or __NSCFString type(of: string) // Swift.String NSTextStorage @property(readonly, copy) NSString *string; let textStorage.apply(highlights) ! (string as AnyObject).className // NSBigMutableString let highlights = parser.parse(textStorage.string, in: range)

Slide 13

Slide 13 text

©2022 1024jp macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ᶅ UFNQPSBSZBUUSJCVUFTΛ࢖͏ ᶆ ฒྻʹॲཧ͢Δ ᶇ όοΫάϥ΢υʹ͍Δؒʹద༻಺༰Λ࠷খԽ͢Δ ॳ ڃ

Slide 14

Slide 14 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp 5FNQPSBSZBUUSJCVUFT func addTemporaryAttributes(_: forCharacterRange: Discussion ... Currently the only temporary attributes recognized are those that do not affect layout (colors, underlines, and so on). func addAttributes(_:range:) I am dogco w. I am dogcow. NSTextLayoutManagerͷrendering attributesʹ૬౰ Temporary attributes NSLayoutManager Attributes NSTextStorage

Slide 15

Slide 15 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp 5FNQPSBSZBUUSJCVUFT for range in ranges { layoutManager.addTemporaryAttributes(attributes, forCharacterRange: range) } for range in ranges { textStorage.addAttribute(attributes, range: range) } ? s 3.35 s textStorage.beginEditing() for range in ranges { textStorage.addAttribute(attributes, range: range) } textStorage.endEditing() 4.46 s Temporary attributes NSLayoutManager Attributes NSTextStorage

Slide 16

Slide 16 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp γϯλοΫεϋΠϥΠτͷྲྀΕ Main Thread ϋ Π ϥ Π τ ׬ ྃ ς Ω ε τ ม ߋ จ ࣈ ྻ ύ ồ ε ϋ Π ϥ Π τ ద ༻ start end ύ ồ ε ൣ ғ ܾ ఆ Highlighting… ! Background Threads 3.35 s 0.00 s 16.23 s

Slide 17

Slide 17 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp γϯλοΫεϋΠϥΠτͷύʔε Text Highlighted moof moof

Slide 18

Slide 18 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp moof moof γϯλοΫεϋΠϥΠτͷύʔε Elements Elements Attributes Numbers Strings Characters

Slide 19

Slide 19 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp moof γϯλοΫεϋΠϥΠτͷύʔε Attributes Elements Attributes Numbers Strings Characters

Slide 20

Slide 20 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp moof γϯλοΫεϋΠϥΠτͷύʔε Numbers Elements Attributes Numbers Strings Characters

Slide 21

Slide 21 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp moof γϯλοΫεϋΠϥΠτͷύʔε Strings Elements Attributes Numbers Strings Characters

Slide 22

Slide 22 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp moof γϯλοΫεϋΠϥΠτͷύʔε Elements Attributes Numbers Strings Characters Characters

Slide 23

Slide 23 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp moof γϯλοΫεϋΠϥΠτͷύʔε Elements Attributes Numbers Strings Characters Highlighted

Slide 24

Slide 24 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp var highlights: [SyntaxType: [NSRange]] = [.elements: [NSRange(), NSRange()], .attributes: [NSRange(), NSRange(), ...], .strings: [NSRange(), NSRange(), ...], .characters: [NSRange()]] ύʔε಺༰Λద༻͢Δ Text Background Threads Highlighted Main Thread apply NSLayoutManager SyntaxParser parse Highlights moof moof

Slide 25

Slide 25 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp moof γϯλοΫεϋΠϥΠτͷύʔε Elements Attributes Numbers Strings Characters Highlighted

Slide 26

Slide 26 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp moof γϯλοΫεϋΠϥΠτͷύʔε Elements Attributes Numbers Strings Characters Highlighted

Slide 27

Slide 27 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp γϯλοΫεϋΠϥΠτͷྲྀΕ Main Thread ϋ Π ϥ Π τ ׬ ྃ ς Ω ε τ ม ߋ จ ࣈ ྻ ύ ồ ε ϋ Π ϥ Π τ ద ༻ start end ύ ồ ε ൣ ғ ܾ ఆ Highlighting… ! Background Threads 3.35 s 6.44 s 0.00 s 16.23 s

Slide 28

Slide 28 text

©2022 1024jp macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ᶈ EJTQMBZͷWBMJEBUJPOΛ஗Ԇ͢Δ ᶉ MBZPVUNBOBHFSʹۃྗSBOHFܭࢉΛͤ͞ͳ͍ ϋʔυίΞ

Slide 29

Slide 29 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp ϋΠϥΠτ಺༰Λ/4-BZPVU.BOBHFSʹద༻͢Δ func apply(highlights: [SyntaxType: [NSRange]], range highlightRange: NSRange, theme: Theme) { layoutManager.removeTemporaryAttribute(.foregroundColor, forCharacterRange: highlightRange) for type in SyntaxType.allCases { guard let ranges = highlights[type]?.compactMap({ $0.intersection(highlightRange) }), !ranges.isEmpty else { continue } let color = theme.style(for: type).color for range in ranges { layoutManager.addTemporaryAttribute(.foregroundColor, value: color, forCharacterRange: range) } } } 3.33 s 3.35 s – NSLayoutManager.invalidateDisplay(forCharacterRange:) – [NSMutableRLEArray replaceObjectsInRange:withObject:length:] – [__NSDictonaryM mutableCopyWithZone:] 2.58 s 0.73 s 0.005 s

Slide 30

Slide 30 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp JOWBMJEBUF%JTQMBZ GPS$IBSBDUFS3BOHF ͱ͸ func invalidateDisplay(forCharacterRange charRange: NSRange) Summary Invalidates display for the specified character range. Discussion Parts of the range that are not laid out are remembered and redisplayed later when the layout is available. Does not actually cause layout. fi fi Temporary attributes NSLayoutManager Attributes NSTextStorage fi fi

Slide 31

Slide 31 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp %JTQMBZΛຖճJOWBMJEBUF͠ͳ͍Α͏ʹ͢Δ class LayoutManager: NSLayoutManager { private var ignoresDisplayValidation = false func groupTemporaryAttributesUpdate(in range: NSRange, work: () throws -> Void) rethrows { self.ignoresDisplayValidation = true defer { self.ignoresDisplayValidation = false self.invalidateDisplay(forCharacterRange: range) } try work() } override func invalidateDisplay(forCharacterRange charRange: NSRange) { if self.ignoresDisplayValidation { return } super.invalidateDisplay(forCharacterRange: charRange) } }

Slide 32

Slide 32 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp %JTQMBZΛຖճJOWBMJEBUF͠ͳ͍Α͏ʹ͢Δ func apply(highlights: [SyntaxType: [NSRange]], range highlightRange: NSRange, theme: Theme) { layoutManager.removeTemporaryAttribute(.foregroundColor, forCharacterRange: highlightRange) for type in SyntaxType.allCases { guard let ranges = highlights[type]?.compactMap({ $0.intersection(highlightRange) }), !ranges.isEmpty else { continue } let color = theme.style(for: type).color for range in ranges { layoutManager.addTemporaryAttribute(.foregroundColor, value: color, forCharacterRange: range) } } } 3.33 s 3.35 s – NSLayoutManager.invalidateDisplay(forCharacterRange:) – [NSMutableRLEArray replaceObjectsInRange:withObject:length:] – [__NSDictonaryM mutableCopyWithZone:] 2.58 s 0.73 s 0.005 s

Slide 33

Slide 33 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp %JTQMBZΛຖճJOWBMJEBUF͠ͳ͍Α͏ʹ͢Δ func apply(highlights: [SyntaxType: [NSRange]], range highlightRange: NSRange, theme: Theme) { layoutManager.groupTemporaryAttributesUpdate(in: highlightRange) { layoutManager.removeTemporaryAttribute(.foregroundColor, forCharacterRange: highlightRange) for type in SyntaxType.allCases { guard let ranges = highlights[type]?.compactMap({ $0.intersection(highlightRange) }), !ranges.isEmpty else { continue } let color = theme.style(for: type).color for range in ranges { layoutManager.addTemporaryAttribute(.foregroundColor, value: color, forCharacterRange: range) } } } } 0.736 s 0.76 s – NSLayoutManager.invalidateDisplay(forCharacterRange:) – [NSMutableRLEArray replaceObjectsInRange:withObject:length:] – [__NSDictonaryM mutableCopyWithZone:] 2.58 s 0.73 s 0.005 s

Slide 34

Slide 34 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp %JTQMBZΛຖճJOWBMJEBUF͠ͳ͍Α͏ʹ͢Δ func apply(highlights: [SyntaxType: [NSRange]], range highlightRange: NSRange, theme: Theme) { layoutManager.groupTemporaryAttributesUpdate(in: highlightRange) { layoutManager.removeTemporaryAttribute(.foregroundColor, forCharacterRange: highlightRange) for type in SyntaxType.allCases { guard let ranges = highlights[type]?.compactMap({ $0.intersection(highlightRange) }), !ranges.isEmpty else { continue } let color = theme.style(for: type).color for range in ranges { layoutManager.addTemporaryAttribute(.foregroundColor, value: color, forCharacterRange: range) } } } } 0.736 s 0.76 s – [NSMutableRLEArray replaceObjectsInRange:withObject:length:] – [__NSDictonaryM mutableCopyWithZone:] 0.73 s 0.005 s

Slide 35

Slide 35 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp BEE"UUSJCVUFͰԿ͕ى͍ͬͯ͜Δͷ͔ ᶅ 4XJGUˠ0CKFDUJWF$ʹίετֻ͕͔͍ͬͯΔ ˠ͚ͩ͜͜શ෦0CKFDUJWF$Ͱॻ͍ͯΈΔ ᶃ ࣮͸·ͩඳըपΓͷॲཧΛ͍ͯ͠Δ ద༻ճ਺ ࣌ؒ (ms) , , , , , , , , O(n²) ͍ΘΏΔ つらい ܏޲Λ௫Ή ᶄ /4$PMPS͕ͳΜ͔ແବʹҠಈͯ͠Δ ˠVOEFSMJOF4UZMFʹม͑ͯΈΔʢWBMVF͕*OUͳͷͰʣ ˠ/4.VUBCMF3-&"SSBZ͔ͩΒҧ͏ – [NSMutableRLEArray replaceObjectsInRange:withObject:length:] Ծઆ 0.73 s ≒ 4 min. –_platform_memmove

Slide 36

Slide 36 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp ςετ༻ͷ࠷খίʔυ let layoutManager = LayoutManager() // prepare string let length = 7_000_000 let string = String(repeating: "a", count: length) let textStorage = NSTextStorage(string: string) textStorage.addLayoutManager(layoutManager) // prepare temporary attributes let ranges = (0..<100_000) .map { _ in Int.random(in: (0..

Slide 37

Slide 37 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp moof ϋΠϥΠτͷద༻ॱং Elements Attributes Numbers Strings Characters Highlighted ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩

Slide 38

Slide 38 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp var highlights: [SyntaxType: [NSRange]] = [.elements: [NSRange(), NSRange()], .attributes: [NSRange(), NSRange(), ...], .strings: [NSRange(), NSRange(), ...], .characters: [NSRange()]] ύʔε಺༰Λద༻͢Δ Text Background Threads Highlighted Main Thread apply NSLayoutManager SyntaxParser parse Highlights moof moof

Slide 39

Slide 39 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp struct Highlight { var range: NSRange var type: SyntaxType } ύʔε಺༰Λద༻͢Δ Text Background Threads Highlighted Main Thread apply NSLayoutManager SyntaxParser parse Highlights moof moof var highlights: [Highlight]

Slide 40

Slide 40 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp ιʔτࡁΈͷϋΠϥΠτΛద༻͢Δ func apply(highlights: [SyntaxType: [NSRange]], range highlightRange: NSRange, theme: Theme) { layoutManager.groupTemporaryAttributesUpdate(in: highlightRange) { layoutManager.removeTemporaryAttribute(.foregroundColor, forCharacterRange: highlightRange) for type in SyntaxType.allCases { guard let ranges = highlights[type]?.compactMap({ $0.intersection(highlightRange) }), !ranges.isEmpty else { continue } let color = theme.style(for: type).color for range in ranges { layoutManager.addTemporaryAttribute(.foregroundColor, value: color, forCharacterRange: range) } } } } 0.736 s 0.76 s – [NSMutableRLEArray replaceObjectsInRange:withObject:length:] – [__NSDictonaryM mutableCopyWithZone:] 0.73 s 0.005 s

Slide 41

Slide 41 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp ιʔτࡁΈͷϋΠϥΠτΛద༻͢Δ func apply(highlights: [Highlight], range highlightRange: NSRange, theme: Theme) { layoutManager.groupTemporaryAttributesUpdate(in: highlightRange) { layoutManager.removeTemporaryAttribute(.foregroundColor, forCharacterRange: highlightRange) for highlight in highlights { let color = theme.style(for: highlight.type).color layoutManager.addTemporaryAttribute(.foregroundColor, value: color, forCharacterRange: highlight.range) } } } 0.044 s 0.049 s 0.000 s – [NSMutableRLEArray replaceObjectsInRange:withObject:length:] – [__NSDictonaryM mutableCopyWithZone:] 0.005 s

Slide 42

Slide 42 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp γϯλοΫεϋΠϥΠτͷྲྀΕ Main Thread ϋ Π ϥ Π τ ׬ ྃ ς Ω ε τ ม ߋ จ ࣈ ྻ ύ ồ ε ϋ Π ϥ Π τ ద ༻ start end ύ ồ ε ൣ ғ ܾ ఆ Highlighting… ! Background Threads 0.05 s 3.35 s 6.44 s 0.00 s 16.23 s

Slide 43

Slide 43 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp γϯλοΫεϋΠϥΠτΛߴ଎Խ͢ΔͨΊͷςΫχοΫ ᶃ ඞཁͳՕॴ͚ͩύʔε ᶄ όοΫάϥ΢ϯυεϨουͰύʔεΛ͢Δ ᶈ EJTQMBZͷWBMJEBUJPOΛ஗Ԇ͢Δ ᶉ MBZPVUNBOBHFSʹۃྗSBOHFܭࢉΛͤ͞ͳ͍ ᶅ UFNQPSBSZBUUSJCVUFTΛ࢖͏ ᶆ ฒྻʹॲཧ͢Δ ᶇ όοΫάϥ΢υʹ͍Δؒʹద༻಺༰Λ࠷খԽ͢Δ ϋʔυίΞ ॳ ڃ ج ຊ # "

Slide 44

Slide 44 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp 5FYU,JU͸ʁ func apply(highlights: [Highlight], range highlightRange: NSRange, theme: Theme) { layoutManager.groupTemporaryAttributesUpdate(in: highlightRange) { layoutManager.removeTemporaryAttribute(.foregroundColor, forCharacterRange: highlightRange) for highlight in highlights { let color = theme.style(for: highlight.type).color layoutManager.addTemporaryAttribute(.foregroundColor, value: color, forCharacterRange: highlight.range) } } } 0.05 s TextKit 1 NSLayoutManager

Slide 45

Slide 45 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp 5FYU,JU͸ʁ func apply(highlights: [Highlight], range highlightRange: NSRange, theme: Theme) { if let location = layoutManager.location(layoutManager.documentRange.location, offsetBy: highlightRange.location), let end = layoutManager.location(location, offsetBy: highlightRange.length), let textRange = NSTextRange(location: location, end: end) { layoutManager.removeRenderingAttribute(.foregroundColor, for: textRange) } for highlight in highlights { guard let location = layoutManager.location(layoutManager.documentRange.location, offsetBy: highlight.range.location), let end = layoutManager.location(location, offsetBy: highlight.range.length), let textRange = NSTextRange(location: location, end: end) else { continue } let color = theme.style(for: highlight.type).color layoutManager.addRenderingAttribute(.foregroundColor, value: highlight.color, for: textRange) } } 0.39 s TextKit 2 NSTextLayoutManager

Slide 46

Slide 46 text

macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ©2022 1024jp 5FYU,JU͸ʁ ᶃ ඞཁͳՕॴ͚ͩύʔε ᶄ όοΫάϥ΢ϯυεϨουͰύʔεΛ͢Δ ᶈ EJTQMBZͷWBMJEBUJPOΛ஗Ԇ͢Δ ᶉ MBZPVUNBOBHFSʹۃྗSBOHFܭࢉΛͤ͞ͳ͍ ᶅ UFNQPSBSZBUUSJCVUFTΛ࢖͏ ᶆ ฒྻʹॲཧ͢Δ ᶇ όοΫάϥ΢υʹ͍Δؒʹద༻಺༰Λ࠷খԽ͢Δ ϋʔυίΞ ॳ ڃ ج ຊ # "

Slide 47

Slide 47 text

©2022 1024jp macOS native 5FYU,JUͰͷγϯλοΫεϋΠϥΠτߴ଎Խ ͓ΘΓ