All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.intellij.openapi.diff.impl.highlighting.NumberedFragmentHighlighter Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition platform-impl library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intellij.openapi.diff.impl.highlighting;

import com.intellij.codeInsight.daemon.GutterMark;
import com.intellij.openapi.diff.DiffColors;
import com.intellij.openapi.diff.impl.FragmentNumberGutterIconRenderer;
import com.intellij.openapi.diff.impl.fragments.Fragment;
import com.intellij.openapi.diff.impl.fragments.FragmentHighlighterImpl;
import com.intellij.openapi.diff.impl.fragments.LineFragment;
import com.intellij.openapi.diff.impl.util.TextDiffTypeEnum;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.editor.markup.HighlighterLayer;
import com.intellij.openapi.editor.markup.HighlighterTargetArea;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.containers.MultiMap;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;

/**
 * @author irengrig
 *         Date: 8/12/11
 *         Time: 4:01 PM
 */
public class NumberedFragmentHighlighter extends FragmentHighlighterImpl {
  private final boolean myDrawNumber;
  private final Map> myLeftPrecalculated;
  private final Map> myRightPrecalculated;
  private int myPreviousLineLeft;
  private int myPreviousLineRight;
  private NumberedFragmentHighlighter.MyPropertyChangeListener myPropertyChangeListener;

  public NumberedFragmentHighlighter(DiffMarkup appender1, DiffMarkup appender2, boolean drawNumber) {
    super(appender1, appender2);
    myDrawNumber = drawNumber;
    myLeftPrecalculated = new HashMap>();
    myRightPrecalculated = new HashMap>();
    myPreviousLineLeft = -1;
    myPreviousLineRight = -1;
  }

  private class MyPropertyChangeListener implements PropertyChangeListener {
    @Override
    public void propertyChange(PropertyChangeEvent evt) {
      if (!EditorEx.PROP_FONT_SIZE.equals(evt.getPropertyName())) return;
      if (evt.getOldValue().equals(evt.getNewValue())) return;
      RangeHighlighter[] allHighlighters = myAppender1.getEditor().getMarkupModel().getAllHighlighters();
      resetFont(allHighlighters);
      RangeHighlighter[] allHighlighters2 = myAppender2.getEditor().getMarkupModel().getAllHighlighters();
      resetFont(allHighlighters2);
    }

    private void resetFont(RangeHighlighter[] allHighlighters) {
      for (RangeHighlighter highlighter : allHighlighters) {
        GutterMark renderer = highlighter.getGutterIconRenderer();
        if (renderer instanceof FragmentNumberGutterIconRenderer) {
          ((FragmentNumberGutterIconRenderer)renderer).resetFont(myAppender1.getEditor());
        }
      }
    }
  }

  private TextAttributesKey getColorAttributesKey(final TextDiffTypeEnum textDiffTypeEnum) {
    if (TextDiffTypeEnum.CHANGED.equals(textDiffTypeEnum)) {
      return DiffColors.DIFF_MODIFIED;
    } else if (TextDiffTypeEnum.INSERT.equals(textDiffTypeEnum)) {
      return DiffColors.DIFF_INSERTED;
    } else if (TextDiffTypeEnum.DELETED.equals(textDiffTypeEnum)) {
      return DiffColors.DIFF_DELETED;
    } else if (TextDiffTypeEnum.CONFLICT.equals(textDiffTypeEnum)) {
      return DiffColors.DIFF_CONFLICT;
    } else {
      return null;
    }
  }

  @Override
  protected void highlightFragmentImpl(Fragment fragment) {
    if (! myDrawNumber || fragment.getType() == null || TextDiffTypeEnum.NONE.equals(fragment.getType())) {
      myAppender1.highlightText(fragment, null);
      myAppender2.highlightText(fragment, null);
      return;
    }
    int lineLeft = myAppender1.getDocument().getLineNumber(fragment.getRange(FragmentSide.SIDE1).getStartOffset());
    int lineRight = myAppender2.getDocument().getLineNumber(fragment.getRange(FragmentSide.SIDE2).getStartOffset());
    Pair left = myLeftPrecalculated.get(lineLeft);
    if (myPreviousLineLeft == lineLeft || left == null) {
      myAppender1.highlightText(fragment, null);
    } else {
      // draw border == true for range marker with highlighting and number be set anyway, even if range is empty
      myAppender1.highlightText(fragment, new FragmentNumberGutterIconRenderer(left.getFirst(), getColorAttributesKey(left.getSecond()), myAppender1.getEditor().getScrollPane(), myAppender1.getEditor()));
      myPreviousLineLeft = lineLeft;
    }

    Pair right = myRightPrecalculated.get(lineRight);
    if (myPreviousLineRight == lineRight || right == null) {
      myAppender2.highlightText(fragment, null);
    } else {
      // draw border == true for range marker with highlighting and number be set anyway, even if range is empty
      myAppender2.highlightText(fragment, new FragmentNumberGutterIconRenderer(right.getFirst(), getColorAttributesKey(right.getSecond()), myAppender1.getEditor().getScrollPane(),
                                                                                     myAppender1.getEditor()));
      myPreviousLineRight = lineRight;
    }
  }

  public void addRangeHighlighter(final boolean left, int start, int end, final TextAttributes attributes) {
    if (left) {
      myAppender1.getEditor().getMarkupModel().addRangeHighlighter(start, end, HighlighterLayer.SELECTION - 3, attributes,
                                                                   HighlighterTargetArea.EXACT_RANGE);
    } else {
      myAppender2.getEditor().getMarkupModel().addRangeHighlighter(start, end, HighlighterLayer.SELECTION - 3, attributes,
                                                                         HighlighterTargetArea.EXACT_RANGE);
    }
  }

  public void reset() {
    myLeftPrecalculated.clear();
    myRightPrecalculated.clear();
    myPreviousLineLeft = -1;
    myPreviousLineRight = -1;
  }

  public void precalculateNumbers(List lines) {
    if (myPropertyChangeListener == null) {
      myPropertyChangeListener = new MyPropertyChangeListener();
      myAppender1.getEditor().addPropertyChangeListener(myPropertyChangeListener);
    }
    final MultiMap> leftMap = new MultiMap>();
    final MultiMap> rightMap = new MultiMap>();
    
    int cnt = 1;
    for (LineFragment line : lines) {
      if (line.getType() == null || TextDiffTypeEnum.NONE.equals(line.getType())) continue;
      
      final Iterator iterator = line.getChildrenIterator();
      if (iterator == null) {
        TextRange left = line.getRange(FragmentSide.SIDE1);
        TextRange right = line.getRange(FragmentSide.SIDE2);

        leftMap.putValue(myAppender1.getDocument().getLineNumber(left.getStartOffset()), new Pair(cnt, line.getType()));
        rightMap.putValue(myAppender2.getDocument().getLineNumber(right.getStartOffset()), new Pair(cnt, line.getType()));
        ++ cnt;
        continue;
      }
      while (iterator.hasNext()) {
        final Fragment next = iterator.next();
        if (next.getType() == null || TextDiffTypeEnum.NONE.equals(next.getType())) continue;

        TextRange left = next.getRange(FragmentSide.SIDE1);
        TextRange right = next.getRange(FragmentSide.SIDE2);

        leftMap.putValue(myAppender1.getDocument().getLineNumber(left.getStartOffset()), new Pair(cnt, next.getType()));
        rightMap.putValue(myAppender2.getDocument().getLineNumber(right.getStartOffset()), new Pair(cnt, next.getType()));
        ++ cnt;
      }
    }
    
    // merge
    merge(leftMap, myLeftPrecalculated);
    merge(rightMap, myRightPrecalculated);
  }

  private void merge(MultiMap> leftMap,
                     final Map> whereTo) {
    for (Map.Entry>> entry : leftMap.entrySet()) {
      List> value = (List>) entry.getValue();
      if (value.size() > 1) {
        Pair pair1 = value.iterator().next();
        Pair pair2 = value.get(value.size() - 1);
        TextDiffTypeEnum type = mergeDiffType(value);
        whereTo.put(entry.getKey(), Pair.create(String.valueOf(pair1.getFirst()) + "-" +
                                                String.valueOf(pair2.getFirst()), type));
      } else {
        Pair pair = value.iterator().next();
        whereTo.put(entry.getKey(), Pair.create(String.valueOf(pair.getFirst()), pair.getSecond()));
      }
    }
  }

  private TextDiffTypeEnum mergeDiffType(List> value) {
    TextDiffTypeEnum previous = null;
    for (Pair pair : value) {
      if (previous == null) {
        previous = pair.getSecond();
        continue;
      }
      if (! previous.equals(pair.getSecond())) return TextDiffTypeEnum.CHANGED;
    }
    return previous;
  }
  
  public List getLeftLines() {
    List list = new ArrayList(myLeftPrecalculated.keySet());
    Collections.sort(list);
    return list;
  }

  public List getRightLines() {
    List list = new ArrayList(myRightPrecalculated.keySet());
    Collections.sort(list);
    return list;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy