Skip to content

Commit 2effc39

Browse files
committed
Improve performance of code highlighting in chat
1 parent 586e6a8 commit 2effc39

3 files changed

Lines changed: 108 additions & 49 deletions

File tree

Core/Sources/ChatGPTChatTab/ChatPanel.swift

Lines changed: 38 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -277,39 +277,52 @@ struct ChatHistory: View {
277277
var body: some View {
278278
WithViewStore(chat, observe: \.history) { viewStore in
279279
ForEach(viewStore.state, id: \.id) { message in
280-
let text = message.text
281-
282-
switch message.role {
283-
case .user:
284-
UserMessage(id: message.id, text: text, chat: chat)
285-
.listRowInsets(EdgeInsets(
286-
top: 0,
287-
leading: -8,
288-
bottom: 0,
289-
trailing: -8
290-
))
291-
.padding(.vertical, 4)
292-
case .assistant:
293-
BotMessage(
294-
id: message.id,
295-
text: text,
296-
references: message.references,
297-
chat: chat
298-
)
280+
ChatHistoryItem(chat: chat, message: message).id(message.id)
281+
}
282+
}
283+
}
284+
}
285+
286+
struct ChatHistoryItem: View {
287+
let chat: StoreOf<Chat>
288+
let message: DisplayedChatMessage
289+
@State var height: CGFloat = 0
290+
@State var codeHighlightCacheController = CodeBlockHighlighterCacheController()
291+
292+
var body: some View {
293+
let text = message.text
294+
295+
Group {
296+
switch message.role {
297+
case .user:
298+
UserMessage(id: message.id, text: text, chat: chat)
299299
.listRowInsets(EdgeInsets(
300300
top: 0,
301301
leading: -8,
302302
bottom: 0,
303303
trailing: -8
304304
))
305305
.padding(.vertical, 4)
306-
case .tool:
307-
FunctionMessage(id: message.id, text: text)
308-
case .ignored:
309-
EmptyView()
310-
}
306+
case .assistant:
307+
BotMessage(
308+
id: message.id,
309+
text: text,
310+
references: message.references,
311+
chat: chat
312+
)
313+
.listRowInsets(EdgeInsets(
314+
top: 0,
315+
leading: -8,
316+
bottom: 0,
317+
trailing: -8
318+
))
319+
.padding(.vertical, 4)
320+
case .tool:
321+
FunctionMessage(id: message.id, text: text)
322+
case .ignored:
323+
EmptyView()
311324
}
312-
}
325+
}.environment(\.codeHighlightCacheController, codeHighlightCacheController)
313326
}
314327
}
315328

@@ -563,29 +576,6 @@ struct ChatPanel_EmptyChat_Preview: PreviewProvider {
563576
}
564577
}
565578

566-
struct ChatCodeSyntaxHighlighter: CodeSyntaxHighlighter {
567-
let brightMode: Bool
568-
let font: NSFont
569-
let colorChange: Color?
570-
571-
init(brightMode: Bool, font: NSFont, colorChange: Color?) {
572-
self.brightMode = brightMode
573-
self.font = font
574-
self.colorChange = colorChange
575-
}
576-
577-
func highlightCode(_ content: String, language: String?) -> Text {
578-
let content = highlightedCodeBlock(
579-
code: content,
580-
language: language ?? "",
581-
scenario: "chat",
582-
brightMode: brightMode,
583-
font: font
584-
)
585-
return Text(AttributedString(content))
586-
}
587-
}
588-
589579
struct ChatPanel_InputText_Preview: PreviewProvider {
590580
static var previews: some View {
591581
ChatPanel(chat: .init(
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import Foundation
2+
import MarkdownUI
3+
import SharedUIComponents
4+
import SwiftUI
5+
6+
final class CodeBlockHighlighterCacheController {
7+
private var cache: [String: AttributedString] = [:]
8+
9+
func get(_ key: String) -> AttributedString? {
10+
cache[key]
11+
}
12+
13+
func set(_ key: String, _ value: AttributedString) {
14+
cache[key] = value
15+
}
16+
}
17+
18+
struct CodeHighlightCacheEnvironmentKey: EnvironmentKey {
19+
static var defaultValue: CodeBlockHighlighterCacheController = .init()
20+
}
21+
22+
extension EnvironmentValues {
23+
var codeHighlightCacheController: CodeBlockHighlighterCacheController {
24+
get { self[CodeHighlightCacheEnvironmentKey.self] }
25+
set { self[CodeHighlightCacheEnvironmentKey.self] = newValue }
26+
}
27+
}
28+
29+
struct ChatCodeSyntaxHighlighter: CodeSyntaxHighlighter {
30+
let brightMode: Bool
31+
let font: NSFont
32+
let colorChange: Color?
33+
var cacheController: CodeBlockHighlighterCacheController
34+
35+
init(
36+
brightMode: Bool,
37+
font: NSFont,
38+
colorChange: Color?,
39+
cacheController: CodeBlockHighlighterCacheController
40+
) {
41+
self.brightMode = brightMode
42+
self.font = font
43+
self.colorChange = colorChange
44+
self.cacheController = cacheController
45+
}
46+
47+
func highlightCode(_ code: String, language: String?) -> Text {
48+
let key = "\(language ?? "unknown") - \(code)"
49+
if let text = cacheController.get(key) {
50+
return Text(text)
51+
}
52+
53+
let content = highlightedCodeBlock(
54+
code: code,
55+
language: language ?? "",
56+
scenario: "chat",
57+
brightMode: brightMode,
58+
font: font
59+
)
60+
let string = AttributedString(content)
61+
Task { @MainActor in
62+
cacheController.set(key, string)
63+
}
64+
return Text(string)
65+
}
66+
}
67+

Core/Sources/ChatGPTChatTab/Styles.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct ThemedMarkdownText: View {
8383
@AppStorage(\.chatFontSize) var chatFontSize
8484
@AppStorage(\.chatCodeFont) var chatCodeFont
8585
@Environment(\.colorScheme) var colorScheme
86+
@Environment(\.codeHighlightCacheController) var codeHighlightCacheController
8687

8788
let text: String
8889

@@ -125,7 +126,8 @@ struct ThemedMarkdownText: View {
125126
font: chatCodeFont.value.nsFont,
126127
colorChange: colorScheme == .dark
127128
? codeForegroundColorDark.value?.swiftUIColor
128-
: codeForegroundColorLight.value?.swiftUIColor
129+
: codeForegroundColorLight.value?.swiftUIColor,
130+
cacheController: codeHighlightCacheController
129131
)
130132
)
131133
}

0 commit comments

Comments
 (0)