@@ -190,7 +190,7 @@ var (
190190 codeTagSuffix = []byte (`</span>` )
191191)
192192
193- func diffToHTML (hcd * HighlightCodeDiff , diffs []diffmatchpatch.Diff , lineType DiffLineType ) DiffInline {
193+ func diffToHTML (hcd * HighlightCodeDiff , diffs []diffmatchpatch.Diff , lineType DiffLineType ) string {
194194 buf := bytes .NewBuffer (nil )
195195 if hcd != nil {
196196 for _ , tag := range hcd .lineWrapperTags {
@@ -216,7 +216,7 @@ func diffToHTML(hcd *HighlightCodeDiff, diffs []diffmatchpatch.Diff, lineType Di
216216 buf .WriteString ("</span>" )
217217 }
218218 }
219- return DiffInlineWithUnicodeEscape ( template . HTML ( buf .String ()) )
219+ return buf .String ()
220220}
221221
222222// GetLine gets a specific line by type (add or del) and file line number
@@ -289,7 +289,7 @@ func DiffInlineWithHighlightCode(fileName, language, code string) DiffInline {
289289type HighlightCodeDiff struct {
290290 placeholderBegin rune
291291 placeholderMaxCount int
292- placeholderCounter int
292+ placeholderIndex int
293293 placeholderTagMap map [rune ]string
294294 tagPlaceholderMap map [string ]rune
295295
@@ -305,12 +305,49 @@ func NewHighlightCodeDiff() *HighlightCodeDiff {
305305 }
306306}
307307
308+ // nextPlaceholder returns 0 if no more placeholder can be used
308309func (hcd * HighlightCodeDiff ) nextPlaceholder () rune {
309- // TODO: handle placeholder rune conflicts ( case 1: counter reaches placeholderMaxCount, case 2: there are placeholder runes in code )
310- r := hcd .placeholderBegin + rune (hcd .placeholderCounter % hcd .placeholderMaxCount )
311- hcd .placeholderCounter ++
312- hcd .placeholderCounter %= hcd .placeholderMaxCount
313- return r
310+ for hcd .placeholderIndex < hcd .placeholderMaxCount {
311+ r := hcd .placeholderBegin + rune (hcd .placeholderIndex )
312+ hcd .placeholderIndex ++
313+ // only use non-existing (not used by code) rune as placeholders
314+ if _ , ok := hcd .placeholderTagMap [r ]; ! ok {
315+ return r
316+ }
317+ }
318+ return 0 // no more available placeholder
319+ }
320+
321+ func (hcd * HighlightCodeDiff ) isInPlaceholderRange (r rune ) bool {
322+ return hcd .placeholderBegin <= r && r < hcd .placeholderBegin + rune (hcd .placeholderMaxCount )
323+ }
324+
325+ func (hcd * HighlightCodeDiff ) collectUsedRunes (code string ) {
326+ for _ , r := range code {
327+ if hcd .isInPlaceholderRange (r ) {
328+ // put the existing rune (used by code) in map, then this rune won't be used a placeholder anymore.
329+ hcd .placeholderTagMap [r ] = ""
330+ }
331+ }
332+ }
333+
334+ func (hcd * HighlightCodeDiff ) diffWithHighlight (filename , language , codeA , codeB string ) []diffmatchpatch.Diff {
335+ hcd .collectUsedRunes (codeA )
336+ hcd .collectUsedRunes (codeB )
337+
338+ highlightCodeA := highlight .Code (filename , language , codeA )
339+ highlightCodeB := highlight .Code (filename , language , codeB )
340+
341+ highlightCodeA = hcd .convertToPlaceholders (highlightCodeA )
342+ highlightCodeB = hcd .convertToPlaceholders (highlightCodeB )
343+
344+ diffs := diffMatchPatch .DiffMain (highlightCodeA , highlightCodeB , true )
345+ diffs = diffMatchPatch .DiffCleanupEfficiency (diffs )
346+
347+ for i := range diffs {
348+ hcd .recoverOneDiff (& diffs [i ])
349+ }
350+ return diffs
314351}
315352
316353func (hcd * HighlightCodeDiff ) convertToPlaceholders (highlightCode string ) string {
@@ -362,12 +399,19 @@ func (hcd *HighlightCodeDiff) convertToPlaceholders(highlightCode string) string
362399 placeholder , ok := hcd .tagPlaceholderMap [tagInMap ]
363400 if ! ok {
364401 placeholder = hcd .nextPlaceholder ()
365- hcd .tagPlaceholderMap [tagInMap ] = placeholder
366- hcd .placeholderTagMap [placeholder ] = tagInMap
402+ if placeholder != 0 {
403+ hcd .tagPlaceholderMap [tagInMap ] = placeholder
404+ hcd .placeholderTagMap [placeholder ] = tagInMap
405+ }
367406 }
368407
369- res .WriteRune (placeholder ) // use the placeholder to replace the tag
408+ if placeholder != 0 {
409+ res .WriteRune (placeholder ) // use the placeholder to replace the tag
410+ } else {
411+ res .WriteString (tag ) // unfortunately, all private use runes has been exhausted, no more placeholder could be used, so do not covert the tag
412+ }
370413 }
414+
371415 res .WriteString (s )
372416 return res .String ()
373417}
@@ -378,7 +422,7 @@ func (hcd *HighlightCodeDiff) recoverOneDiff(diff *diffmatchpatch.Diff) {
378422
379423 for _ , r := range diff .Text {
380424 tag , ok := hcd .placeholderTagMap [r ]
381- if ! ok {
425+ if ! ok || tag == "" {
382426 sb .WriteRune (r )
383427 continue
384428 }
@@ -413,12 +457,6 @@ func (hcd *HighlightCodeDiff) recoverOneDiff(diff *diffmatchpatch.Diff) {
413457 diff .Text = sb .String ()
414458}
415459
416- func (hcd * HighlightCodeDiff ) recoverFromPlaceholders (diffs []diffmatchpatch.Diff ) {
417- for i := range diffs {
418- hcd .recoverOneDiff (& diffs [i ])
419- }
420- }
421-
422460// GetComputedInlineDiffFor computes inline diff for the given line.
423461func (diffSection * DiffSection ) GetComputedInlineDiffFor (diffLine * DiffLine ) DiffInline {
424462 if setting .Git .DisableDiffHighlight {
@@ -461,19 +499,10 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) Dif
461499 return DiffInlineWithHighlightCode (diffSection .FileName , language , diffLine .Content )
462500 }
463501
464- highlightCodeA := highlight .Code (diffSection .FileName , language , diff1 [1 :])
465- highlightCodeB := highlight .Code (diffSection .FileName , language , diff2 [1 :])
466-
467502 hcd := NewHighlightCodeDiff ()
468- highlightCodeA = hcd .convertToPlaceholders (highlightCodeA )
469- highlightCodeB = hcd .convertToPlaceholders (highlightCodeB )
470-
471- diffRecord := diffMatchPatch .DiffMain (highlightCodeA , highlightCodeB , true )
472- diffRecord = diffMatchPatch .DiffCleanupEfficiency (diffRecord )
473-
474- hcd .recoverFromPlaceholders (diffRecord )
475-
476- return diffToHTML (hcd , diffRecord , diffLine .Type )
503+ diffRecord := hcd .diffWithHighlight (diffSection .FileName , language , diff1 [1 :], diff2 [1 :])
504+ diffHTML := diffToHTML (hcd , diffRecord , diffLine .Type )
505+ return DiffInlineWithUnicodeEscape (template .HTML (diffHTML ))
477506}
478507
479508// DiffFile represents a file diff.
0 commit comments