@@ -316,8 +316,7 @@ private ICodeMining createMining(IDocument doc, UnifiedDiff diff, int offset, in
316316 throws BadLocationException {
317317 int end = doc .getLength ();
318318 if (offset >= end && !startsLine (doc , end )) {
319- return new UnifiedDiffFooterCodeMining (doc , this , null , diff , tabWidth , this .deletionBackgroundColor ,
320- this .detailedDiffColor , tv );
319+ return new UnifiedDiffFooterCodeMining (doc , this , diff , tabWidth , this .deletionBackgroundColor , tv );
321320 }
322321 // a position must not reach beyond the document, otherwise the annotation model
323322 // silently drops it
@@ -540,10 +539,10 @@ public static class UnifiedDiffFooterCodeMining extends DocumentFooterCodeMining
540539 private List <StyleRange > styleRanges ;
541540 private final HashMap <Font , Map <Integer , Font >> styledFonts = new HashMap <>();
542541 private Rectangle lastRectangle ;
542+ private Font cachedFont ;
543543
544544 public UnifiedDiffFooterCodeMining (IDocument document , ICodeMiningProvider provider ,
545- Consumer <MouseEvent > action , UnifiedDiff diff , int tabWidth , Color deletionBackgroundColor ,
546- Color detailedDiffColor , ITextViewer viewer ) {
545+ UnifiedDiff diff , int tabWidth , Color deletionBackgroundColor , ITextViewer viewer ) {
547546 super (document , provider , new MouseClickConsumer (viewer ));
548547 this .deletionBackgroundColor = deletionBackgroundColor ;
549548 this .viewer = viewer ;
@@ -585,9 +584,14 @@ public String getLabel() {
585584 public void dispose () {
586585 styleRanges = null ;
587586 lastRectangle = null ;
587+ cachedFont = null ;
588+ clearStyledFonts ();
589+ super .dispose ();
590+ }
591+
592+ private void clearStyledFonts () {
588593 styledFonts .forEach ((font , map ) -> map .forEach ((style , f ) -> f .dispose ()));
589594 styledFonts .clear ();
590- super .dispose ();
591595 }
592596
593597 private List <StyleRange > styleRanges (String label ) {
@@ -597,17 +601,19 @@ private List<StyleRange> styleRanges(String label) {
597601 return styleRanges ;
598602 }
599603
600- private StyleRange transformFontStyleToFont (Font baseFont , StyleRange styleRange ) {
601- return UnifiedDiffCodeMiningProvider .transformFontStyleToFont (styledFonts , baseFont , styleRange );
602- }
603-
604604 @ Override
605605 public Point draw (GC gc , StyledText textWidget , Color color , int x , int y ) {
606606 gc .setBackground (this .deletionBackgroundColor );
607607 Color c = textWidget .getForeground ();
608608 gc .setForeground (c );
609609 Font font = textWidget .getFont ();
610610 gc .setFont (font );
611+ if (cachedFont != null && (cachedFont .isDisposed () || !cachedFont .equals (font ))) {
612+ // font might have been changed in the meantime - drop the derived fonts
613+ // keyed on the old base font so their native handles are not leaked
614+ clearStyledFonts ();
615+ }
616+ cachedFont = font ;
611617 // first run to get width and height for label
612618 // change from https://github.com/eclipse-platform/eclipse.platform.ui/pull/3651
613619 // is required so that background correctly drawn with line spacing > 0
@@ -627,81 +633,132 @@ public Point draw(GC gc, StyledText textWidget, Color color, int x, int y) {
627633 }
628634
629635 gc .setFont (font );
630- int textWidgetLineHeight = textWidget .getLineHeight ();
631- int cx = x ;
632- int cy = y ;
633- for (StyleRange range : ranges ) {
634- String sub = label .substring (range .start , range .start + range .length );
635- if (sub .trim ().length () > 0 ) {
636- if (range .background != null ) {
637- gc .setBackground (range .background );
638- }
639- if (range .foreground != null ) {
640- gc .setForeground (range .foreground );
641- }
642- Font currentFont = gc .getFont ();
643- var rangeWithFont = transformFontStyleToFont (currentFont , range );
644- if (rangeWithFont .font != null ) {
645- gc .setFont (rangeWithFont .font );
646- }
647- String [] lines = sub .split ("\n " ); //$NON-NLS-1$
648- if (lines .length > 1 ) {
649- for (int i = 0 ; i < lines .length ; i ++) {
650- String line = lines [i ].replace ("\r " , "" ); //$NON-NLS-1$ //$NON-NLS-2$
651- gc .drawString (line , cx , cy , true );
652- Point p = gc .stringExtent (line );
653- if (i < lines .length - 1 ) {
654- cy += textWidgetLineHeight + textWidget .getLineSpacing ();
655- cx = x ;
656- } else {
657- if (sub .endsWith ("\n " )) { //$NON-NLS-1$
658- cy += textWidgetLineHeight + textWidget .getLineSpacing ();
659- cx = x ;
660- } else {
661- cx += p .x ;
662- }
663- }
636+ drawStyleRanges (gc , textWidget , ranges , label , styledFonts , x , y , null );
637+ return result ;
638+ }
639+ }
640+
641+ static final class ForegroundInfo {
642+
643+ final int x ;
644+ final int y ;
645+ final String str ;
646+ final Font font ;
647+ final Color background ;
648+ final Color foreground ;
649+
650+ ForegroundInfo (int x , int y , String str , Font font , Color background , Color foreground ) {
651+ this .x = x ;
652+ this .y = y ;
653+ this .str = str ;
654+ this .font = font ;
655+ this .background = background ;
656+ this .foreground = foreground ;
657+ }
658+ }
659+
660+ /**
661+ * Draws the syntax-colored label using the given style ranges, advancing the
662+ * cursor position range by range. The {@code onForeground} consumer is called
663+ * for each drawn segment and may be {@code null}; the header mining uses it to
664+ * populate its foreground cache so subsequent repaints skip this path.
665+ */
666+ static void drawStyleRanges (GC gc , StyledText textWidget , List <StyleRange > ranges , String label ,
667+ HashMap <Font , Map <Integer , Font >> styledFonts , int x , int y ,
668+ Consumer <ForegroundInfo > onForeground ) {
669+ Font font = gc .getFont ();
670+ int textWidgetLineHeight = textWidget .getLineHeight ();
671+ int cx = x ;
672+ int cy = y ;
673+ for (StyleRange range : ranges ) {
674+ String sub = label .substring (range .start , range .start + range .length );
675+ if (sub .trim ().length () > 0 ) {
676+ if (range .background != null ) {
677+ gc .setBackground (range .background );
678+ }
679+ if (range .foreground != null ) {
680+ gc .setForeground (range .foreground );
681+ }
682+ Font currentFont = gc .getFont ();
683+ var rangeWithFont = transformFontStyleToFont (styledFonts , currentFont , range );
684+ if (rangeWithFont .font != null ) {
685+ gc .setFont (rangeWithFont .font );
686+ }
687+ String [] lines = sub .split ("\n " ); //$NON-NLS-1$
688+ if (lines .length > 1 ) {
689+ for (int i = 0 ; i < lines .length ; i ++) {
690+ String line = lines [i ].replace ("\r " , "" ); //$NON-NLS-1$ //$NON-NLS-2$
691+ gc .drawString (line , cx , cy , true );
692+ if (onForeground != null ) {
693+ onForeground .accept (new ForegroundInfo (cx - x , cy - y , line , gc .getFont (),
694+ gc .getBackground (), gc .getForeground ()));
664695 }
665- } else {
666- gc .drawString (sub , cx , cy , true );
667- Point p = gc .stringExtent (sub );
668- if (sub .endsWith ("\n " )) { //$NON-NLS-1$
696+ Point p = gc .stringExtent (line );
697+ if (i < lines .length - 1 ) {
669698 cy += textWidgetLineHeight + textWidget .getLineSpacing ();
670699 cx = x ;
671700 } else {
672- cx += p .x ;
701+ if (sub .endsWith ("\n " )) { //$NON-NLS-1$
702+ cy += textWidgetLineHeight + textWidget .getLineSpacing ();
703+ cx = x ;
704+ } else {
705+ cx += p .x ;
706+ }
673707 }
674708 }
675- gc .setFont (currentFont );
676709 } else {
677- int lfCount = 0 ;
678- if (sub . contains ( " \n " )) { //$NON-NLS-1$
679- lfCount = sub . split ( " \n " , - 1 ). length - 1 ; //$NON-NLS-1$
680- sub = sub . substring ( sub . lastIndexOf ( " \n " ) + 1 ); //$NON-NLS-1$
710+ gc . drawString ( sub , cx , cy , true ) ;
711+ if (onForeground != null ) {
712+ onForeground . accept ( new ForegroundInfo ( cx - x , cy - y , sub , gc . getFont (),
713+ gc . getBackground (), gc . getForeground ()));
681714 }
682715 Point p = gc .stringExtent (sub );
683- if (lfCount > 0 ) {
684- cy += lfCount * ( textWidgetLineHeight + textWidget .getLineSpacing () );
716+ if (sub . endsWith ( " \n " )) { //$NON-NLS-1$
717+ cy += textWidgetLineHeight + textWidget .getLineSpacing ();
685718 cx = x ;
719+ } else {
720+ cx += p .x ;
686721 }
687- cx += p .x ;
688722 }
723+ gc .setFont (currentFont );
724+ } else {
725+ int lfCount = 0 ;
726+ if (sub .contains ("\n " )) { //$NON-NLS-1$
727+ lfCount = sub .split ("\n " , -1 ).length - 1 ; //$NON-NLS-1$
728+ sub = sub .substring (sub .lastIndexOf ("\n " ) + 1 ); //$NON-NLS-1$
729+ }
730+ Point p = gc .stringExtent (sub );
731+ if (lfCount > 0 ) {
732+ cy += lfCount * (textWidgetLineHeight + textWidget .getLineSpacing ());
733+ cx = x ;
734+ }
735+ cx += p .x ;
689736 }
690- return result ;
691737 }
738+ gc .setFont (font );
692739 }
693740
694741 private static List <StyleRange > computeStyleRanges (ITextViewer v , int offset , String source ) {
695742 List <StyleRange > result = new ArrayList <>();
696743 if (!(v instanceof SourceViewer sv )) {
697744 return result ;
698745 }
746+ IDocument originalDocument = sv .getDocument ();
747+ if (originalDocument == null ) {
748+ return result ;
749+ }
699750 try {
700- IDocument originalDocument = sv .getDocument ();
701751 String prefix = originalDocument .get (0 , offset /* diff.leftStart */ );
702752 IDocument document = new Document (prefix + source );
703753 IRegion damage = new Region (prefix .length (), source .length ());
704- result = sv .computeStyleRanges (document , damage );
754+ try {
755+ result = sv .computeStyleRanges (document , damage );
756+ } catch (NullPointerException e ) {
757+ // SourceViewer.computeStyleRanges dereferences its presentation
758+ // reconciler without a null check; a viewer configured without one
759+ // (e.g. a plain projection viewer) has no syntax coloring to offer
760+ return result ;
761+ }
705762 int startOffset = prefix .length ();
706763 for (StyleRange next : result ) {
707764 if (next .start < startOffset ) {
@@ -788,26 +845,6 @@ private static List<DetailedDiffRange> computeDetailedDiffRanges(UnifiedDiff dif
788845 return result ;
789846 }
790847
791- private static final class ForegroundInfo {
792-
793- final int x ;
794- final int y ;
795- final String str ;
796- final Font font ;
797- final Color background ;
798- final Color foreground ;
799-
800- public ForegroundInfo (int x , int y , String str , Font font , Color background , Color foreground ) {
801- this .x = x ;
802- this .y = y ;
803- this .str = str ;
804- this .font = font ;
805- this .background = background ;
806- this .foreground = foreground ;
807- }
808-
809- }
810-
811848 @ Override
812849 public String getLabel () {
813850 return this .unifiedDiffLabel ;
@@ -1092,70 +1129,7 @@ public Point draw(GC gc, StyledText textWidget, Color color, int x, int y) {
10921129 }
10931130 foregrounds = new ArrayList <>();
10941131 gc .setFont (cachedFont );
1095- int textWidgetLineHeight = textWidget .getLineHeight ();
1096- int cx = x ;
1097- int cy = y ;
1098- for (StyleRange range : ranges ) {
1099- String sub = label .substring (range .start , range .start + range .length );
1100- if (sub .trim ().length () > 0 ) {
1101- if (range .background != null ) {
1102- gc .setBackground (range .background );
1103- }
1104- if (range .foreground != null ) {
1105- gc .setForeground (range .foreground );
1106- }
1107- Font currentFont = gc .getFont ();
1108- var rangeWithFont = transformFontStyleToFont (currentFont , range );
1109- if (rangeWithFont .font != null ) {
1110- gc .setFont (rangeWithFont .font );
1111- }
1112- String [] lines = sub .split ("\n " ); //$NON-NLS-1$
1113- if (lines .length > 1 ) {
1114- for (int i = 0 ; i < lines .length ; i ++) {
1115- String line = lines [i ].replace ("\r " , "" ); //$NON-NLS-1$ //$NON-NLS-2$
1116- gc .drawString (line , cx , cy , true );
1117- foregrounds .add (new ForegroundInfo (cx - x , cy - y , line , gc .getFont (), gc .getBackground (),
1118- gc .getForeground ()));
1119- Point p = gc .stringExtent (line );
1120- if (i < lines .length - 1 ) {
1121- cy += textWidgetLineHeight + textWidget .getLineSpacing ();
1122- cx = x ;
1123- } else {
1124- if (sub .endsWith ("\n " )) { //$NON-NLS-1$
1125- cy += textWidgetLineHeight + textWidget .getLineSpacing ();
1126- cx = x ;
1127- } else {
1128- cx += p .x ;
1129- }
1130- }
1131- }
1132- } else {
1133- gc .drawString (sub , cx , cy , true );
1134- foregrounds .add (new ForegroundInfo (cx - x , cy - y , sub , gc .getFont (), gc .getBackground (),
1135- gc .getForeground ()));
1136- Point p = gc .stringExtent (sub );
1137- if (sub .endsWith ("\n " )) { //$NON-NLS-1$
1138- cy += textWidgetLineHeight + textWidget .getLineSpacing ();
1139- cx = x ;
1140- } else {
1141- cx += p .x ;
1142- }
1143- }
1144- gc .setFont (currentFont );
1145- } else {
1146- int lfCount = 0 ;
1147- if (sub .contains ("\n " )) { //$NON-NLS-1$
1148- lfCount = sub .split ("\n " , -1 ).length - 1 ; //$NON-NLS-1$
1149- sub = sub .substring (sub .lastIndexOf ("\n " ) + 1 ); //$NON-NLS-1$
1150- }
1151- Point p = gc .stringExtent (sub );
1152- if (lfCount > 0 ) {
1153- cy += lfCount * (textWidgetLineHeight + textWidget .getLineSpacing ());
1154- cx = x ;
1155- }
1156- cx += p .x ;
1157- }
1158- }
1132+ drawStyleRanges (gc , textWidget , ranges , label , styledFonts , x , y , foregrounds ::add );
11591133 return result ;
11601134 }
11611135
0 commit comments