@@ -22,6 +22,9 @@ internal enum FileTextLoader {
2222
2323 static func load( _ url: URL ) -> LoadedText ? {
2424 let modifiedAt = modificationDate ( of: url)
25+ if startsWithByteOrderMark ( url) {
26+ return loadByteOrderMarked ( url, modifiedAt: modifiedAt)
27+ }
2528 var detected : String . Encoding = . utf8
2629 if let content = try ? String ( contentsOf: url, usedEncoding: & detected) {
2730 return LoadedText ( content: content, encoding: detected, modifiedAt: modifiedAt)
@@ -36,20 +39,8 @@ internal enum FileTextLoader {
3639 }
3740
3841 static func decode( _ data: Data ) -> String ? {
39- let utf8ByteOrderMark : [ UInt8 ] = [ 0xEF , 0xBB , 0xBF ]
40- if data. starts ( with: utf8ByteOrderMark) {
41- return String ( data: data. dropFirst ( utf8ByteOrderMark. count) , encoding: . utf8)
42- }
43- if data. starts ( with: [ 0xFF , 0xFE , 0x00 , 0x00 ] ) || data. starts ( with: [ 0x00 , 0x00 , 0xFE , 0xFF ] ) {
44- return String ( data: data, encoding: . utf32)
45- }
46- if data. starts ( with: [ 0xFF , 0xFE ] ) || data. starts ( with: [ 0xFE , 0xFF ] ) {
47- return String ( data: data, encoding: . utf16)
48- }
49- if let content = String ( data: data, encoding: . utf8) {
50- return content
51- }
52- return String ( data: data, encoding: . isoLatin1)
42+ guard !data. isEmpty else { return " " }
43+ return TextPrefixDecoder . decode ( data, prefixLength: data. count) ? . content
5344 }
5445
5546 static func modificationDate( of url: URL ) -> Date ? {
@@ -59,16 +50,23 @@ internal enum FileTextLoader {
5950 static func loadHeader( _ url: URL , maxBytes: Int = 4_096 ) -> LoadedText ? {
6051 guard let handle = try ? FileHandle ( forReadingFrom: url) else { return nil }
6152 defer { try ? handle. close ( ) }
62- guard let data = try ? handle. read ( upToCount: maxBytes) , !data. isEmpty else { return nil }
53+ guard let bytes = try ? handle. read ( upToCount: maxBytes + TextPrefixDecoder. lookaheadLength) ,
54+ let decoded = TextPrefixDecoder . decode ( bytes, prefixLength: maxBytes) else { return nil }
6355 let modifiedAt = modificationDate ( of: url)
56+ return LoadedText ( content: decoded. content, encoding: decoded. encoding, modifiedAt: modifiedAt)
57+ }
6458
65- if let content = String ( data: data, encoding: . utf8) {
66- return LoadedText ( content: content, encoding: . utf8, modifiedAt: modifiedAt)
67- }
68- if let content = String ( data: data, encoding: . isoLatin1) {
69- return LoadedText ( content: content, encoding: . isoLatin1, modifiedAt: modifiedAt)
70- }
71- return nil
59+ private static func startsWithByteOrderMark( _ url: URL ) -> Bool {
60+ guard let handle = try ? FileHandle ( forReadingFrom: url) else { return false }
61+ defer { try ? handle. close ( ) }
62+ guard let bytes = try ? handle. read ( upToCount: ByteOrderMark . longestLength) else { return false }
63+ return ByteOrderMark . leading ( bytes) != nil
64+ }
65+
66+ private static func loadByteOrderMarked( _ url: URL , modifiedAt: Date ? ) -> LoadedText ? {
67+ guard let bytes = try ? Data ( contentsOf: url) ,
68+ let decoded = TextPrefixDecoder . decode ( bytes, prefixLength: bytes. count) else { return nil }
69+ return LoadedText ( content: decoded. content, encoding: decoded. encoding, modifiedAt: modifiedAt)
7270 }
7371}
7472
0 commit comments