11import AppKit
22import Foundation
33import Highlightr
4- import Splash
54import SwiftUI
65import XPCShared
76
@@ -11,76 +10,39 @@ func highlightedCodeBlock(
1110 brightMode: Bool ,
1211 fontSize: Double
1312) -> NSAttributedString {
14- switch language {
15- case " swift " :
16- let plainTextColor = brightMode
17- ? . black
18- : #colorLiteral( red: 0.6509803922 , green: 0.6980392157 , blue: 0.7529411765 , alpha: 1 )
19- let highlighter =
20- SyntaxHighlighter (
21- format: AttributedStringOutputFormat ( theme: . init(
22- font: . init( size: 14 ) ,
23- plainTextColor: plainTextColor,
24- tokenColors: brightMode
25- ? [
26- . keyword: #colorLiteral( red: 0.6078431373 , green: 0.137254902 , blue: 0.5764705882 , alpha: 1 ) ,
27- . string: #colorLiteral( red: 0.1371159852 , green: 0.3430536985 , blue: 0.362406373 , alpha: 1 ) ,
28- . type: #colorLiteral( red: 0.2456904352 , green: 0.5002114773 , blue: 0.5297455192 , alpha: 1 ) ,
29- . call: #colorLiteral( red: 0.1960784314 , green: 0.4274509804 , blue: 0.4549019608 , alpha: 1 ) ,
30- . number: #colorLiteral( red: 0.4385872483 , green: 0.4995297194 , blue: 0.5483990908 , alpha: 1 ) ,
31- . comment: #colorLiteral( red: 0.3647058824 , green: 0.4235294118 , blue: 0.4745098039 , alpha: 1 ) ,
32- . property: #colorLiteral( red: 0.1960784314 , green: 0.4274509804 , blue: 0.4549019608 , alpha: 1 ) ,
33- . dotAccess: #colorLiteral( red: 0.1960784314 , green: 0.4274509804 , blue: 0.4549019608 , alpha: 1 ) ,
34- . preprocessing: #colorLiteral( red: 0.3921568627 , green: 0.2196078431 , blue: 0.1254901961 , alpha: 1 ) ,
35- ] : [
36- . keyword: #colorLiteral( red: 0.8258609176 , green: 0.5708742738 , blue: 0.8922662139 , alpha: 1 ) ,
37- . string: #colorLiteral( red: 0.6253595352 , green: 0.7963448763 , blue: 0.5427476764 , alpha: 1 ) ,
38- . type: #colorLiteral( red: 0.9221783876 , green: 0.7978314757 , blue: 0.5575165749 , alpha: 1 ) ,
39- . call: #colorLiteral( red: 0.4466812611 , green: 0.742190659 , blue: 0.9515134692 , alpha: 1 ) ,
40- . number: #colorLiteral( red: 0.8620631099 , green: 0.6468816996 , blue: 0.4395158887 , alpha: 1 ) ,
41- . comment: #colorLiteral( red: 0.4233166873 , green: 0.4612616301 , blue: 0.5093258619 , alpha: 1 ) ,
42- . property: #colorLiteral( red: 0.906378448 , green: 0.5044228435 , blue: 0.5263597369 , alpha: 1 ) ,
43- . dotAccess: #colorLiteral( red: 0.906378448 , green: 0.5044228435 , blue: 0.5263597369 , alpha: 1 ) ,
44- . preprocessing: #colorLiteral( red: 0.3776347041 , green: 0.8792117238 , blue: 0.4709561467 , alpha: 1 ) ,
45- ]
46- ) )
47- )
48- let formatted = NSMutableAttributedString ( attributedString: highlighter. highlight ( code) )
49- formatted. addAttributes (
50- [ . font: NSFont . monospacedSystemFont ( ofSize: fontSize, weight: . regular) ] ,
51- range: NSRange ( location: 0 , length: formatted. length)
13+ var language = language
14+ if language == " objective-c " {
15+ language = " objectivec "
16+ }
17+ func unhighlightedCode( ) -> NSAttributedString {
18+ return NSAttributedString (
19+ string: code,
20+ attributes: [
21+ . foregroundColor: brightMode ? NSColor . black : NSColor . white,
22+ . font: NSFont . monospacedSystemFont ( ofSize: fontSize, weight: . regular) ,
23+ ]
5224 )
53- return formatted
54- default :
55- var language = language
56- if language == " objective-c " {
57- language = " objectivec "
58- }
59- func unhighlightedCode( ) -> NSAttributedString {
60- return NSAttributedString (
61- string: code,
62- attributes: [
63- . foregroundColor: brightMode ? NSColor . black : NSColor . white,
64- . font: NSFont . monospacedSystemFont ( ofSize: fontSize, weight: . regular) ,
65- ]
66- )
67- }
68- guard let highlighter = Highlightr ( ) else {
69- return unhighlightedCode ( )
70- }
71- highlighter. setTheme ( to: brightMode ? " xcode " : " atom-one-dark " )
72- highlighter. theme. setCodeFont ( . monospacedSystemFont( ofSize: fontSize, weight: . regular) )
73- guard let formatted = highlighter. highlight ( code, as: language) else {
74- return unhighlightedCode ( )
75- }
76- if formatted. string == " undefined " {
77- return unhighlightedCode ( )
78- }
79- return formatted
8025 }
26+ guard let highlighter = Highlightr ( ) else {
27+ return unhighlightedCode ( )
28+ }
29+ highlighter. setTheme ( to: brightMode ? " xcode " : " atom-one-dark " )
30+ highlighter. theme. setCodeFont ( . monospacedSystemFont( ofSize: fontSize, weight: . regular) )
31+ guard let formatted = highlighter. highlight ( code, as: language) else {
32+ return unhighlightedCode ( )
33+ }
34+ if formatted. string == " undefined " {
35+ return unhighlightedCode ( )
36+ }
37+ return formatted
8138}
8239
83- func highlighted( code: String , language: String , brightMode: Bool ) -> [ NSAttributedString ] {
40+ func highlighted(
41+ code: String ,
42+ language: String ,
43+ brightMode: Bool ,
44+ droppingLeadingSpaces: Bool
45+ ) -> ( code: [ NSAttributedString ] , commonLeadingSpaceCount: Int ) {
8446 let formatted = highlightedCodeBlock (
8547 code: code,
8648 language: language,
@@ -90,21 +52,67 @@ func highlighted(code: String, language: String, brightMode: Bool) -> [NSAttribu
9052 let middleDotColor = brightMode
9153 ? NSColor . black. withAlphaComponent ( 0.1 )
9254 : NSColor . white. withAlphaComponent ( 0.1 )
93- return convertToCodeLines ( formatted, middleDotColor: middleDotColor)
55+ return convertToCodeLines (
56+ formatted,
57+ middleDotColor: middleDotColor,
58+ droppingLeadingSpaces: droppingLeadingSpaces
59+ )
9460}
9561
96- private func convertToCodeLines(
62+ func convertToCodeLines(
9763 _ formattedCode: NSAttributedString ,
98- middleDotColor: NSColor
99- ) -> [ NSAttributedString ] {
64+ middleDotColor: NSColor ,
65+ droppingLeadingSpaces: Bool
66+ ) -> ( code: [ NSAttributedString ] , commonLeadingSpaceCount: Int ) {
10067 let input = formattedCode. string
68+ func isEmptyLine( _ line: String ) -> Bool {
69+ guard let regex = try ? NSRegularExpression ( pattern: #"^\s*\n?$"# ) else { return false }
70+ if regex. firstMatch (
71+ in: line,
72+ options: [ ] ,
73+ range: NSMakeRange ( 0 , line. utf16. count)
74+ ) != nil {
75+ return true
76+ }
77+ return false
78+ }
79+
10180 let separatedInput = input. components ( separatedBy: " \n " )
81+ let commonLeadingSpaceCount = {
82+ if !droppingLeadingSpaces { return 0 }
83+ let splitted = separatedInput
84+ var result = 0
85+ outerLoop: for i in [ 4 , 8 , 12 , 16 , 20 ] {
86+ for line in splitted {
87+ if isEmptyLine ( line) { continue }
88+ if i >= line. count { break outerLoop }
89+ let targetIndex = line. index ( line. startIndex, offsetBy: i - 1 )
90+ if line [ targetIndex] != " " { break outerLoop }
91+ }
92+ result = i
93+ }
94+ return result
95+ } ( )
10296 var output = [ NSAttributedString] ( )
10397 var start = 0
10498 for sub in separatedInput {
10599 let range = NSMakeRange ( start, sub. utf16. count)
106100 let attributedString = formattedCode. attributedSubstring ( from: range)
107101 let mutable = NSMutableAttributedString ( attributedString: attributedString)
102+
103+ // remove leading spaces
104+ if commonLeadingSpaceCount > 0 {
105+ let leadingSpaces = String ( repeating: " " , count: commonLeadingSpaceCount)
106+ if mutable. string. hasPrefix ( leadingSpaces) {
107+ mutable. replaceCharacters (
108+ in: NSRange ( location: 0 , length: commonLeadingSpaceCount) ,
109+ with: " "
110+ )
111+ } else if isEmptyLine ( mutable. string) {
112+ mutable. mutableString. setString ( " " )
113+ }
114+ }
115+
108116 // use regex to replace all spaces to a middle dot
109117 do {
110118 let regex = try NSRegularExpression ( pattern: " [ ]* " , options: [ ] )
@@ -126,5 +134,5 @@ private func convertToCodeLines(
126134 output. append ( mutable)
127135 start += range. length + 1
128136 }
129- return output
137+ return ( output, commonLeadingSpaceCount )
130138}
0 commit comments