/* * Copyright 2000-2011 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.editor.highlighter; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.HighlighterColors; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.util.TextRange; import com.intellij.psi.tree.IElementType; import java.util.List; import java.util.Map; import java.util.TreeMap; /** * Created by IntelliJ IDEA. * User: Irina.Chernushina * Date: 9/8/11 * Time: 12:52 PM */ public class FragmentedEditorHighlighter implements EditorHighlighter { private final TreeMap<Integer, Element> myPieces; private final Document myDocument; private final int myAdditionalOffset; private TextAttributes myUsualAttributes; private final boolean myMergeByTextAttributes; public FragmentedEditorHighlighter(HighlighterIterator sourceIterator, List<TextRange> ranges) { this(sourceIterator, ranges, 0, false); } public FragmentedEditorHighlighter(HighlighterIterator sourceIterator, List<TextRange> ranges, final int additionalOffset, boolean mergeByTextAttributes) { myMergeByTextAttributes = mergeByTextAttributes; myDocument = sourceIterator.getDocument(); myPieces = new TreeMap<Integer, Element>(); myAdditionalOffset = additionalOffset; translate(sourceIterator, ranges); } private void translate(HighlighterIterator iterator, List<TextRange> ranges) { if (iterator.atEnd()) return; int offset = 0; for (TextRange range : ranges) { while (range.getStartOffset() > iterator.getStart()) { iterator.advance(); if (iterator.atEnd()) return; } while (range.getEndOffset() >= iterator.getEnd()) { int relativeStart = iterator.getStart() - range.getStartOffset(); boolean merged = false; if (myMergeByTextAttributes && ! myPieces.isEmpty()) { final Integer first = myPieces.descendingKeySet().first(); final Element element = myPieces.get(first); if (element.getEnd() >= offset + relativeStart && myPieces.get(first).getAttributes().equals(iterator.getTextAttributes())) { // merge merged = true; myPieces.put(element.getStart(), new Element(element.getStart(), offset + (iterator.getEnd() - range.getStartOffset()), iterator.getTokenType(), iterator.getTextAttributes())); } } if (! merged) { myPieces.put(offset + relativeStart, new Element(offset + relativeStart, offset + (iterator.getEnd() - range.getStartOffset()), iterator.getTokenType(), iterator.getTextAttributes())); } iterator.advance(); if (iterator.atEnd()) return; } offset += range.getLength() + 1 + myAdditionalOffset; // myAdditionalOffset because of extra line - for shoene separators } } @Override public HighlighterIterator createIterator(int startOffset) { Map.Entry<Integer, Element> entry = myPieces.ceilingEntry(startOffset); return new ProxyIterator(myDocument, entry == null ? -1 : entry.getKey()); } @Override public void setText(CharSequence text) { } @Override public void setEditor(HighlighterClient editor) { } @Override public void setColorScheme(EditorColorsScheme scheme) { } @Override public void beforeDocumentChange(DocumentEvent event) { } @Override public void documentChanged(DocumentEvent event) { } private class ProxyIterator implements HighlighterIterator { private final Document myDocument; private int myIdx; private ProxyIterator(Document document, int idx) { myDocument = document; myIdx = idx; } @Override public TextAttributes getTextAttributes() { return myPieces.get(myIdx).getAttributes(); } @Override public int getStart() { return myPieces.get(myIdx).getStart(); } @Override public int getEnd() { return myPieces.get(myIdx).getEnd(); } @Override public IElementType getTokenType() { return myPieces.get(myIdx).myElementType; } @Override public void advance() { if (myIdx == myPieces.lastKey() || myIdx == -1) { myIdx = -1; return; } Map.Entry<Integer, Element> entry = myPieces.tailMap(myIdx, false).firstEntry(); myIdx = entry.getKey(); } @Override public void retreat() { if (myIdx == myPieces.firstKey() || myIdx == -1) { myIdx = -1; return; } Map.Entry<Integer, Element> entry = myPieces.headMap(myIdx, false).lastEntry(); myIdx = entry.getKey(); } @Override public boolean atEnd() { return myIdx < 0; } @Override public Document getDocument() { return myDocument; } } private boolean isUsualAttributes(final TextAttributes ta) { if (myUsualAttributes == null) { final EditorColorsManager manager = EditorColorsManager.getInstance(); final EditorColorsScheme[] schemes = manager.getAllSchemes(); EditorColorsScheme defaultScheme = schemes[0]; for (EditorColorsScheme scheme : schemes) { if (manager.isDefaultScheme(scheme)) { defaultScheme = scheme; break; } } myUsualAttributes = defaultScheme.getAttributes(HighlighterColors.TEXT); } return myUsualAttributes.equals(ta); } private static class Element { private final int myStart; private final int myEnd; private final IElementType myElementType; private final TextAttributes myAttributes; private Element(int start, int end, IElementType elementType, TextAttributes attributes) { myStart = start; myEnd = end; myElementType = elementType; myAttributes = attributes; } public int getStart() { return myStart; } public int getEnd() { return myEnd; } public IElementType getElementType() { return myElementType; } public TextAttributes getAttributes() { return myAttributes; } } }