@@ -4,154 +4,156 @@ import Highlightr
44import SuggestionModel
55import 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