2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.diff.tools.fragmented;
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.diagnostic.Logger;
20 import com.intellij.openapi.editor.Document;
21 import com.intellij.openapi.editor.colors.EditorColorsScheme;
22 import com.intellij.openapi.editor.event.DocumentEvent;
23 import com.intellij.openapi.editor.highlighter.EditorHighlighter;
24 import com.intellij.openapi.editor.highlighter.HighlighterClient;
25 import com.intellij.openapi.editor.highlighter.HighlighterIterator;
26 import com.intellij.openapi.editor.markup.TextAttributes;
27 import com.intellij.openapi.util.Comparing;
28 import com.intellij.openapi.util.TextRange;
29 import com.intellij.psi.tree.IElementType;
30 import org.jetbrains.annotations.NotNull;
32 import java.util.ArrayList;
33 import java.util.Collections;
34 import java.util.Comparator;
35 import java.util.List;
37 class UnifiedEditorHighlighter implements EditorHighlighter {
38 public static final Logger LOG = UnifiedDiffViewer.LOG;
40 @NotNull private final Document myDocument;
41 @NotNull private final List<Element> myPieces;
43 public UnifiedEditorHighlighter(@NotNull Document document,
44 @NotNull EditorHighlighter highlighter1,
45 @NotNull EditorHighlighter highlighter2,
46 @NotNull List<HighlightRange> ranges,
48 myDocument = document;
49 myPieces = new ArrayList<Element>();
50 init(highlighter1.createIterator(0), highlighter2.createIterator(0), ranges, textLength);
53 private void init(@NotNull HighlighterIterator it1,
54 @NotNull HighlighterIterator it2,
55 @NotNull List<HighlightRange> ranges,
57 ApplicationManager.getApplication().assertReadAccessAllowed();
61 for (HighlightRange range : ranges) {
62 TextRange base = range.getBase();
63 TextRange changed = range.getChanged();
65 if (base.isEmpty()) continue;
67 if (base.getStartOffset() > offset) {
68 addElement(new Element(offset, base.getStartOffset(), null, TextAttributes.ERASE_MARKER));
69 offset = base.getStartOffset();
72 HighlighterIterator it = range.getSide().select(it1, it2);
73 while (!it.atEnd() && changed.getStartOffset() >= it.getEnd()) {
78 LOG.error("Unexpected end of highlighter");
82 if (changed.getEndOffset() <= it.getStart()) {
87 int relativeStart = Math.max(it.getStart() - changed.getStartOffset(), 0);
88 int relativeEnd = Math.min(it.getEnd() - changed.getStartOffset(), changed.getLength() + 1);
90 addElement(new Element(offset + relativeStart,
93 it.getTextAttributes()));
95 if (changed.getEndOffset() <= it.getEnd()) {
96 offset += changed.getLength();
102 LOG.error("Unexpected end of highlighter");
108 if (offset < textLength) {
109 addElement(new Element(offset, textLength, null, TextAttributes.ERASE_MARKER));
113 private void addElement(@NotNull Element element) {
114 boolean merged = false;
115 if (!myPieces.isEmpty()) {
116 Element oldElement = myPieces.get(myPieces.size() - 1);
117 if (oldElement.getEnd() >= element.getStart() &&
118 Comparing.equal(oldElement.getAttributes(), element.getAttributes()) &&
119 Comparing.equal(oldElement.getElementType(), element.getElementType())) {
121 myPieces.remove(myPieces.size() - 1);
122 myPieces.add(new Element(oldElement.getStart(),
124 element.getElementType(),
125 element.getAttributes()));
129 myPieces.add(element);
135 public HighlighterIterator createIterator(int startOffset) {
136 int index = Collections.binarySearch(myPieces, new Element(startOffset, 0, null, null), new Comparator<Element>() {
138 public int compare(Element o1, Element o2) {
139 return o1.getStart() - o2.getStart();
142 // index: (-insertion point - 1), where insertionPoint is the index of the first element greater than the key
143 // and we need index of the first element that is less or equal (floorElement)
144 if (index < 0) index = Math.max(-index - 2, 0);
145 return new ProxyIterator(myDocument, index, myPieces);
149 public void setColorScheme(@NotNull EditorColorsScheme scheme) {
153 public void setEditor(@NotNull HighlighterClient editor) {
157 public void setText(@NotNull CharSequence text) {
161 public void beforeDocumentChange(DocumentEvent event) {
165 public void documentChanged(DocumentEvent event) {
168 private static class ProxyIterator implements HighlighterIterator {
169 private final Document myDocument;
171 private final List<Element> myPieces;
173 private ProxyIterator(@NotNull Document document, int idx, @NotNull List<Element> pieces) {
174 myDocument = document;
180 public TextAttributes getTextAttributes() {
181 return myPieces.get(myIdx).getAttributes();
185 public int getStart() {
186 return myPieces.get(myIdx).getStart();
190 public int getEnd() {
191 return myPieces.get(myIdx).getEnd();
195 public IElementType getTokenType() {
196 return myPieces.get(myIdx).myElementType;
200 public void advance() {
201 if (myIdx < myPieces.size()) {
207 public void retreat() {
214 public boolean atEnd() {
215 return myIdx < 0 || myIdx >= myPieces.size();
219 public Document getDocument() {
224 private static class Element {
225 private final int myStart;
226 private final int myEnd;
227 private final IElementType myElementType;
228 private final TextAttributes myAttributes;
230 private Element(int start, int end, IElementType elementType, TextAttributes attributes) {
233 myElementType = elementType;
234 myAttributes = attributes;
237 public int getStart() {
241 public int getEnd() {
245 public IElementType getElementType() {
246 return myElementType;
249 public TextAttributes getAttributes() {