Skip to content

Commit 3257f5f

Browse files
committed
Put syntax highlighting code into a enum
1 parent cbb0221 commit 3257f5f

4 files changed

Lines changed: 139 additions & 137 deletions

File tree

Core/Sources/ChatGPTChatTab/CodeBlockHighlighter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ struct AsyncCodeBlockView: View {
4545
highlightTask = Task {
4646
let string = await withUnsafeContinuation { continuation in
4747
Self.queue.async {
48-
let content = highlightedCodeBlock(
48+
let content = CodeHighlighting.highlightedCodeBlock(
4949
code: content,
5050
language: language,
5151
scenario: "chat",

Pro

Submodule Pro updated from 9fc4c31 to b915eb2

Tool/Sources/SharedUIComponents/Experiment/NewCodeBlock.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ struct _CodeBlock: View {
7777
font: NSFont,
7878
droppingLeadingSpaces: Bool
7979
) -> (code: AttributedString, commonLeadingSpaceCount: Int) {
80-
let (lines, commonLeadingSpaceCount) = highlighted(
80+
let (lines, commonLeadingSpaceCount) = CodeHighlighting.highlighted(
8181
code: code,
8282
language: language,
8383
scenario: scenario,

Tool/Sources/SharedUIComponents/SyntaxHighlighting.swift

Lines changed: 136 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -4,154 +4,156 @@ import Highlightr
44
import SuggestionModel
55
import SwiftUI
66

7-
public func highlightedCodeBlock(
8-
code: String,
9-
language: String,
10-
scenario: String,
11-
brightMode: Bool,
12-
font: NSFont
13-
) -> NSAttributedString {
14-
var language = language
15-
// Workaround: Highlightr uses a different identifier for Objective-C.
16-
if language.lowercased().hasPrefix("objective"), language.lowercased().hasSuffix("c") {
17-
language = "objectivec"
18-
}
19-
func unhighlightedCode() -> NSAttributedString {
20-
return NSAttributedString(
21-
string: code,
22-
attributes: [
23-
.foregroundColor: brightMode ? NSColor.black : NSColor.white,
24-
.font: font,
25-
]
26-
)
27-
}
28-
guard let highlighter = Highlightr() else {
29-
return unhighlightedCode()
30-
}
31-
highlighter.setTheme(to: {
32-
let mode = brightMode ? "light" : "dark"
33-
if scenario.isEmpty {
34-
return mode
7+
public enum CodeHighlighting {
8+
public static func highlightedCodeBlock(
9+
code: String,
10+
language: String,
11+
scenario: String,
12+
brightMode: Bool,
13+
font: NSFont
14+
) -> NSAttributedString {
15+
var language = language
16+
// Workaround: Highlightr uses a different identifier for Objective-C.
17+
if language.lowercased().hasPrefix("objective"), language.lowercased().hasSuffix("c") {
18+
language = "objectivec"
3519
}
36-
return "\(scenario)-\(mode)"
37-
}())
38-
highlighter.theme.setCodeFont(font)
39-
guard let formatted = highlighter.highlight(code, as: language) else {
40-
return unhighlightedCode()
41-
}
42-
if formatted.string == "undefined" {
43-
return unhighlightedCode()
20+
func unhighlightedCode() -> NSAttributedString {
21+
return NSAttributedString(
22+
string: code,
23+
attributes: [
24+
.foregroundColor: brightMode ? NSColor.black : NSColor.white,
25+
.font: font,
26+
]
27+
)
28+
}
29+
guard let highlighter = Highlightr() else {
30+
return unhighlightedCode()
31+
}
32+
highlighter.setTheme(to: {
33+
let mode = brightMode ? "light" : "dark"
34+
if scenario.isEmpty {
35+
return mode
36+
}
37+
return "\(scenario)-\(mode)"
38+
}())
39+
highlighter.theme.setCodeFont(font)
40+
guard let formatted = highlighter.highlight(code, as: language) else {
41+
return unhighlightedCode()
42+
}
43+
if formatted.string == "undefined" {
44+
return unhighlightedCode()
45+
}
46+
return formatted
4447
}
45-
return formatted
46-
}
4748

48-
public func highlighted(
49-
code: String,
50-
language: String,
51-
scenario: String,
52-
brightMode: Bool,
53-
droppingLeadingSpaces: Bool,
54-
font: NSFont,
55-
replaceSpacesWithMiddleDots: Bool = true
56-
) -> (code: [NSAttributedString], commonLeadingSpaceCount: Int) {
57-
let formatted = highlightedCodeBlock(
58-
code: code,
59-
language: language,
60-
scenario: scenario,
61-
brightMode: brightMode,
62-
font: font
63-
)
64-
let middleDotColor = brightMode
65-
? NSColor.black.withAlphaComponent(0.1)
66-
: NSColor.white.withAlphaComponent(0.1)
67-
return convertToCodeLines(
68-
formatted,
69-
middleDotColor: middleDotColor,
70-
droppingLeadingSpaces: droppingLeadingSpaces,
71-
replaceSpacesWithMiddleDots: replaceSpacesWithMiddleDots
72-
)
73-
}
74-
75-
func convertToCodeLines(
76-
_ formattedCode: NSAttributedString,
77-
middleDotColor: NSColor,
78-
droppingLeadingSpaces: Bool,
79-
replaceSpacesWithMiddleDots: Bool = true
80-
) -> (code: [NSAttributedString], commonLeadingSpaceCount: Int) {
81-
let input = formattedCode.string
82-
func isEmptyLine(_ line: String) -> Bool {
83-
if line.isEmpty { return true }
84-
guard let regex = try? NSRegularExpression(pattern: #"^\s*\n?$"#) else { return false }
85-
if regex.firstMatch(
86-
in: line,
87-
options: [],
88-
range: NSMakeRange(0, line.utf16.count)
89-
) != nil {
90-
return true
91-
}
92-
return false
49+
public static func highlighted(
50+
code: String,
51+
language: String,
52+
scenario: String,
53+
brightMode: Bool,
54+
droppingLeadingSpaces: Bool,
55+
font: NSFont,
56+
replaceSpacesWithMiddleDots: Bool = true
57+
) -> (code: [NSAttributedString], commonLeadingSpaceCount: Int) {
58+
let formatted = highlightedCodeBlock(
59+
code: code,
60+
language: language,
61+
scenario: scenario,
62+
brightMode: brightMode,
63+
font: font
64+
)
65+
let middleDotColor = brightMode
66+
? NSColor.black.withAlphaComponent(0.1)
67+
: NSColor.white.withAlphaComponent(0.1)
68+
return convertToCodeLines(
69+
formatted,
70+
middleDotColor: middleDotColor,
71+
droppingLeadingSpaces: droppingLeadingSpaces,
72+
replaceSpacesWithMiddleDots: replaceSpacesWithMiddleDots
73+
)
9374
}
9475

95-
let separatedInput = input.splitByNewLine(omittingEmptySubsequences: false)
96-
.map { String($0) }
97-
let commonLeadingSpaceCount = {
98-
if !droppingLeadingSpaces { return 0 }
99-
let split = separatedInput
100-
var result = 0
101-
outerLoop: for i in stride(from: 40, through: 4, by: -4) {
102-
for line in split {
103-
if isEmptyLine(line) { continue }
104-
if i >= line.count { continue outerLoop }
105-
if !line.hasPrefix(.init(repeating: " ", count: i)) { continue outerLoop }
76+
public static func convertToCodeLines(
77+
_ formattedCode: NSAttributedString,
78+
middleDotColor: NSColor,
79+
droppingLeadingSpaces: Bool,
80+
replaceSpacesWithMiddleDots: Bool = true
81+
) -> (code: [NSAttributedString], commonLeadingSpaceCount: Int) {
82+
let input = formattedCode.string
83+
func isEmptyLine(_ line: String) -> Bool {
84+
if line.isEmpty { return true }
85+
guard let regex = try? NSRegularExpression(pattern: #"^\s*\n?$"#) else { return false }
86+
if regex.firstMatch(
87+
in: line,
88+
options: [],
89+
range: NSMakeRange(0, line.utf16.count)
90+
) != nil {
91+
return true
10692
}
107-
result = i
108-
break
93+
return false
10994
}
110-
return result
111-
}()
112-
var output = [NSAttributedString]()
113-
var start = 0
114-
for sub in separatedInput {
115-
let range = NSMakeRange(start, sub.utf16.count)
116-
let attributedString = formattedCode.attributedSubstring(from: range)
117-
let mutable = NSMutableAttributedString(attributedString: attributedString)
11895

119-
// remove leading spaces
120-
if commonLeadingSpaceCount > 0 {
121-
let leadingSpaces = String(repeating: " ", count: commonLeadingSpaceCount)
122-
if mutable.string.hasPrefix(leadingSpaces) {
123-
mutable.replaceCharacters(
124-
in: NSRange(location: 0, length: commonLeadingSpaceCount),
125-
with: ""
126-
)
127-
} else if isEmptyLine(mutable.string) {
128-
mutable.mutableString.setString("")
96+
let separatedInput = input.splitByNewLine(omittingEmptySubsequences: false)
97+
.map { String($0) }
98+
let commonLeadingSpaceCount = {
99+
if !droppingLeadingSpaces { return 0 }
100+
let split = separatedInput
101+
var result = 0
102+
outerLoop: for i in stride(from: 40, through: 4, by: -4) {
103+
for line in split {
104+
if isEmptyLine(line) { continue }
105+
if i >= line.count { continue outerLoop }
106+
if !line.hasPrefix(.init(repeating: " ", count: i)) { continue outerLoop }
107+
}
108+
result = i
109+
break
129110
}
130-
}
111+
return result
112+
}()
113+
var output = [NSAttributedString]()
114+
var start = 0
115+
for sub in separatedInput {
116+
let range = NSMakeRange(start, sub.utf16.count)
117+
let attributedString = formattedCode.attributedSubstring(from: range)
118+
let mutable = NSMutableAttributedString(attributedString: attributedString)
131119

132-
if replaceSpacesWithMiddleDots {
133-
// use regex to replace all spaces to a middle dot
134-
do {
135-
let regex = try NSRegularExpression(pattern: "[ ]*", options: [])
136-
let result = regex.matches(
137-
in: mutable.string,
138-
range: NSRange(location: 0, length: mutable.mutableString.length)
139-
)
140-
for r in result {
141-
let range = r.range
120+
// remove leading spaces
121+
if commonLeadingSpaceCount > 0 {
122+
let leadingSpaces = String(repeating: " ", count: commonLeadingSpaceCount)
123+
if mutable.string.hasPrefix(leadingSpaces) {
142124
mutable.replaceCharacters(
143-
in: range,
144-
with: String(repeating: "·", count: range.length)
125+
in: NSRange(location: 0, length: commonLeadingSpaceCount),
126+
with: ""
145127
)
146-
mutable.addAttributes([
147-
.foregroundColor: middleDotColor,
148-
], range: range)
128+
} else if isEmptyLine(mutable.string) {
129+
mutable.mutableString.setString("")
149130
}
150-
} catch {}
131+
}
132+
133+
if replaceSpacesWithMiddleDots {
134+
// use regex to replace all spaces to a middle dot
135+
do {
136+
let regex = try NSRegularExpression(pattern: "[ ]*", options: [])
137+
let result = regex.matches(
138+
in: mutable.string,
139+
range: NSRange(location: 0, length: mutable.mutableString.length)
140+
)
141+
for r in result {
142+
let range = r.range
143+
mutable.replaceCharacters(
144+
in: range,
145+
with: String(repeating: "·", count: range.length)
146+
)
147+
mutable.addAttributes([
148+
.foregroundColor: middleDotColor,
149+
], range: range)
150+
}
151+
} catch {}
152+
}
153+
output.append(mutable)
154+
start += range.length + 1
151155
}
152-
output.append(mutable)
153-
start += range.length + 1
156+
return (output, commonLeadingSpaceCount)
154157
}
155-
return (output, commonLeadingSpaceCount)
156158
}
157159

0 commit comments

Comments
 (0)