@@ -23,6 +23,8 @@ class ZLCommitInfoPatchCellData: ZMBaseTableViewCellViewModel {
2323 var isBinary : Bool = false
2424 var isImage : Bool = false
2525
26+ var cacheHtml : String ?
27+
2628 lazy var _webView : ZLReportHeightWebViewV2 = {
2729 let frame = CGRect ( x: 0 ,
2830 y: 0 ,
@@ -65,13 +67,8 @@ class ZLCommitInfoPatchCellData: ZMBaseTableViewCellViewModel {
6567
6668 override func zm_clearCache( ) {
6769 super. zm_clearCache ( )
68- _webView. evaluateJavaScript ( renderDiffContentScript ( ) ) { ( result, error) in
69- if let error = error {
70- print ( " JavaScript 执行错误: \( error) " )
71- } else if let result = result {
72- print ( " JavaScript 执行结果: \( result) " )
73- }
74- }
70+ generateHTML ( )
71+ _webView. loadHTML ( cacheHtml ?? " " , baseURL: Bundle . main. bundleURL)
7572 }
7673
7774 init ( model: ZLGithubFileModel , cellHeight: CGFloat ? ) {
@@ -81,29 +78,23 @@ class ZLCommitInfoPatchCellData: ZMBaseTableViewCellViewModel {
8178 if let cellHeight = cellHeight {
8279 self . cellHeight = cellHeight
8380 }
84- guard let url = Bundle . main. url ( forResource: " gitpatchV2 " , withExtension: " html " ) else {
85- return
86- }
87- _webView. loadFileURL ( url, allowingReadAccessTo: url. deletingLastPathComponent ( ) )
81+ generateHTML ( )
82+ _webView. loadHTML ( cacheHtml ?? " " , baseURL: Bundle . main. bundleURL)
8883 }
8984
9085 func initData( model: ZLGithubFileModel ) {
9186 if !model. patch. isEmpty {
92- var patch = model. patch
93- patch = patch. replacingOccurrences ( of: " ` " , with: " \\ ` " )
94- patch = patch. replacingOccurrences ( of: " $ " , with: " \\ $ " )
95- self . patchStr = patch
87+ let patch = model. patch
88+ self . patchStr = patch. htmlEscaped ( )
9689 self . isBinary = false
9790 self . isImage = false
9891 } else {
9992 self . isBinary = true
10093 let pathExtension = ( model. filename as NSString ) . pathExtension. lowercased ( )
10194 if [ " png " , " jpg " , " jpeg " , " gif " , " svg " , " webp " , " bmp " , " icon " ] . contains ( pathExtension) {
10295 self . isImage = true
103- var imagePath = model. blob_url
104- imagePath = imagePath. replacingOccurrences ( of: " ` " , with: " \\ ` " )
105- imagePath = imagePath. replacingOccurrences ( of: " $ " , with: " \\ $ " )
106- self . imagePath = imagePath
96+ let imagePath = model. raw_url
97+ self . imagePath = imagePath. htmlEscaped ( )
10798 }
10899 }
109100
@@ -150,3 +141,259 @@ extension ZLCommitInfoPatchCellData: ZLCommitInfoPatchCellSourceAndDelegate {
150141 }
151142}
152143
144+ extension ZLCommitInfoPatchCellData {
145+
146+ func generateHTML( ) {
147+ var gitPatchDivContent = " "
148+ if !isBinary {
149+ let tag = parsePatchAndGenerateHTML ( patchText: self . patchStr)
150+ gitPatchDivContent = tag? . toHTMLString ( ) ?? " "
151+ } else if isImage {
152+ gitPatchDivContent = " <img src= \" \( imagePath) \" class= \" img_binary \" /> "
153+ } else {
154+ if isLight {
155+ gitPatchDivContent = " <div class= \" div_binary \" >Binary File</div> "
156+ } else {
157+ gitPatchDivContent = " <div class= \" div_binary dark \" >Binary File</div> "
158+ }
159+
160+ }
161+
162+ let htmlURL : URL ? = Bundle . main. url ( forResource: " gitpatchV2 " , withExtension: " html " )
163+
164+ if let url = htmlURL {
165+
166+ do {
167+ let htmlStr = try String . init ( contentsOf: url)
168+ let newHtmlStr = NSMutableString . init ( string: htmlStr)
169+
170+ let htmlRange = ( newHtmlStr as NSString ) . range ( of: " <html> " )
171+ if htmlRange. location != NSNotFound, !isLight {
172+ newHtmlStr. insert ( " class= \" dark \" " , at: htmlRange. location + 5 )
173+ }
174+
175+ let divRange = ( newHtmlStr as NSString ) . range ( of: " </div> " )
176+ if divRange. location != NSNotFound {
177+ newHtmlStr. insert ( gitPatchDivContent, at: divRange. location)
178+ }
179+
180+ self . cacheHtml = newHtmlStr as String
181+
182+ } catch {
183+ ZLToastView . showMessage ( " load Code index html failed " )
184+ }
185+ }
186+ }
187+ }
188+
189+ // MARK: - parse and generate html
190+ extension ZLCommitInfoPatchCellData {
191+
192+ class HTMLTag {
193+ let name : String
194+ var classList : [ String ] = [ ]
195+ var children : [ HTMLTag ] = [ ]
196+ var textContent : String = " "
197+
198+ init ( name: String ) {
199+ self . name = name
200+ }
201+
202+ func toHTMLString( ) -> String {
203+ var html = " < " + name
204+
205+ // 添加class属性
206+ if !classList. isEmpty {
207+ html += " class= \" " + classList. joined ( separator: " " ) + " \" "
208+ }
209+
210+ html += " > "
211+
212+ // 添加文本内容
213+ if !textContent. isEmpty {
214+ html += textContent
215+ }
216+
217+ // 添加子元素
218+ for child in children {
219+ html += child. toHTMLString ( )
220+ }
221+
222+ html += " </ " + name + " > "
223+
224+ return html
225+ }
226+ }
227+
228+
229+ func parsePatchAndGenerateHTML( patchText: String ) -> HTMLTag ? {
230+ let patchLines = patchText. split ( separator: " \n " )
231+ guard !patchLines. isEmpty else { return nil }
232+
233+ var currentOldLineNumber = 0 ;
234+ var currentNewLineNumber = 0 ;
235+
236+ let tableTag = HTMLTag ( name: " table " )
237+ let tbody = HTMLTag ( name: " tbody " )
238+ tableTag. children = [ tbody]
239+
240+
241+ patchLines. forEach { line in
242+
243+ if (
244+ line. hasPrefix ( " diff --git " ) ||
245+ line. hasPrefix ( " index " ) ||
246+ line. hasPrefix ( " --- a " ) ||
247+ line. hasPrefix ( " +++ b " )
248+ ) {
249+ return ;
250+ }
251+
252+ if ( line. hasPrefix ( " @@ " ) ) {
253+ let ( tr, oldLineNumber, newLineNumber ) = generateFileLineTr ( line: String ( line) ) ;
254+ tbody. children. append ( tr)
255+ currentOldLineNumber = oldLineNumber;
256+ currentNewLineNumber = newLineNumber;
257+
258+ } else if ( line. hasPrefix ( " + " ) ) {
259+ let ( tr, newNum ) = generateAdditionTr (
260+ line: String ( line) ,
261+ newLineNumber: currentNewLineNumber
262+ ) ;
263+ tbody. children. append ( tr)
264+ currentNewLineNumber = newNum;
265+ } else if ( line. hasPrefix ( " - " ) ) {
266+ let ( tr, oldNum ) = generateDeletionTr (
267+ line: String ( line) ,
268+ oldLineNumber: currentOldLineNumber
269+ ) ;
270+ tbody. children. append ( tr) ;
271+ currentOldLineNumber = oldNum;
272+ } else {
273+ let ( tr, oldNum, newNum) = generateNormalTr (
274+ line: String ( line) ,
275+ oldLineNumber: currentOldLineNumber,
276+ newLineNumber: currentNewLineNumber
277+ ) ;
278+ tbody. children. append ( tr) ;
279+ currentOldLineNumber = oldNum;
280+ currentNewLineNumber = newNum;
281+ }
282+ }
283+
284+ return tableTag
285+ }
286+
287+ // @@ -138,28 +136,72 @@ extension ZLCommitInfoController
288+ func generateFileLineTr( line: String ) -> ( HTMLTag , Int , Int ) {
289+ let ( tr, td_lineNumber, td_lineContent, div_lineNumber, div_lineContent ) =
290+ generatePatchLineTr ( ) ;
291+
292+ let ( oldStart, oldLines, newStart, newLines) = parseGitChangeLine ( line) ;
293+
294+ div_lineContent. textContent = line;
295+
296+ td_lineNumber. classList. append ( " patch " ) ;
297+ td_lineContent. classList. append ( " patch " ) ;
298+
299+ return ( tr, oldStart, newStart ) ;
300+ }
301+
302+ // +// func requestDiscussionComment(isLoadNew: Bool) {
303+ func generateAdditionTr( line: String , newLineNumber: Int ) -> ( HTMLTag , Int ) {
304+ let ( tr, td_lineNumber, td_lineContent, div_lineNumber, div_lineContent ) =
305+ generatePatchLineTr ( ) ;
306+
307+ div_lineNumber. textContent = " \( newLineNumber) "
308+ div_lineContent. textContent = line;
309+ td_lineNumber. classList. append ( " add " ) ;
310+ td_lineContent. classList. append ( " add " ) ;
311+
312+ let newNum = newLineNumber + 1 ;
313+
314+ return ( tr, newNum ) ;
315+ }
316+
317+ // -// func requestCommitDiffInfo() {
318+ func generateDeletionTr( line: String , oldLineNumber: Int ) -> ( HTMLTag , Int ) {
319+ let ( tr, td_lineNumber, td_lineContent, div_lineNumber, div_lineContent) =
320+ generatePatchLineTr ( ) ;
321+
322+ div_lineNumber. textContent = " \( oldLineNumber) "
323+ div_lineContent. textContent = line;
324+ td_lineNumber. classList. append ( " delete " ) ;
325+ td_lineContent. classList. append ( " delete " ) ;
326+
327+ let oldNum = oldLineNumber + 1 ;
328+ return ( tr, oldNum ) ;
329+ }
330+
331+ func generateNormalTr( line: String , oldLineNumber: Int , newLineNumber: Int ) -> ( HTMLTag , Int , Int ) {
332+ let ( tr, td_lineNumber, td_lineContent, div_lineNumber, div_lineContent ) =
333+ generatePatchLineTr ( ) ;
334+
335+ div_lineNumber. textContent = " \( newLineNumber) " ;
336+ div_lineContent. textContent = line;
337+
338+ let oldNum = oldLineNumber + 1 ;
339+ let newNum = newLineNumber + 1 ;
340+ return ( tr, oldNum, newNum ) ;
341+ }
342+
343+
344+
345+ func parseGitChangeLine( _ str: String ) -> ( Int , Int , Int , Int ) {
346+ let pattern = #"^@@ -(\d+),(\d+) \+(\d+),(\d+) @@"#
347+
348+ guard let rangeMatch = str. range ( of: pattern, options: . regularExpression) else {
349+ return ( 0 , 0 , 0 , 0 )
350+ }
351+
352+ let numbers = str [ rangeMatch] . components ( separatedBy: CharacterSet . decimalDigits. inverted)
353+ . filter { !$0. isEmpty }
354+ . compactMap { Int ( $0) }
355+
356+ guard numbers. count == 4 else {
357+ return ( 0 , 0 , 0 , 0 )
358+ }
359+
360+ return ( numbers [ 0 ] , numbers [ 1 ] , numbers [ 2 ] , numbers [ 3 ] )
361+ }
362+
363+
364+ func generatePatchLineTr( ) -> ( HTMLTag , HTMLTag , HTMLTag , HTMLTag , HTMLTag ) {
365+ let tr = HTMLTag ( name: " tr " )
366+ let td_lineNumber = HTMLTag ( name: " td " )
367+ let div_lineNumber = HTMLTag ( name: " div " )
368+ let td_lineContent = HTMLTag ( name: " td " )
369+ let div_lineContent = HTMLTag ( name: " div " )
370+ td_lineNumber. children = [ div_lineNumber]
371+ td_lineContent. children = [ div_lineContent]
372+
373+ td_lineNumber. classList = [ " td_linenum " ]
374+ td_lineContent. classList = [ " td_lineContent " ]
375+ div_lineContent. classList = [ " div_lineContent " ]
376+
377+ if ( !isLight) {
378+ td_lineNumber. classList. append ( " dark " ) ;
379+ td_lineContent. classList. append ( " dark " ) ;
380+ }
381+
382+ tr. children. append ( td_lineNumber) ;
383+ tr. children. append ( td_lineContent) ;
384+
385+ return ( tr, td_lineNumber, td_lineContent, div_lineNumber, div_lineContent)
386+ }
387+
388+ }
389+
390+ extension String {
391+ func htmlEscaped( ) -> String {
392+ return self
393+ . replacingOccurrences ( of: " & " , with: " & " )
394+ . replacingOccurrences ( of: " < " , with: " < " )
395+ . replacingOccurrences ( of: " > " , with: " > " )
396+ . replacingOccurrences ( of: " \" " , with: " " " )
397+ . replacingOccurrences ( of: " ' " , with: " ' " )
398+ }
399+ }
0 commit comments