blob: ee6325271b7f31abc633d6badcb351de26ce6de8 [file] [log] [blame]
/*
* Copyright 2000-2012 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.
*/
/**
* @author Yura Cangea
*/
package com.intellij.application.options.colors.highlighting;
import com.intellij.openapi.editor.colors.TextAttributesKey;
import com.intellij.openapi.util.TextRange;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HighlightsExtractor {
private final Map<String,TextAttributesKey> myTags;
private int myStartOffset;
private int myEndOffset;
private int mySkippedLen;
private int myIndex;
private boolean myIsOpeningTag;
private List<TextRange> mySkipped = new ArrayList<TextRange>();
public HighlightsExtractor(@Nullable Map<String, TextAttributesKey> tags) {
myTags = tags;
}
public String extractHighlights(String text, List<HighlightData> highlights) {
mySkipped.clear();
if (myTags == null || myTags.isEmpty()) return text;
resetIndices();
Stack<HighlightData> highlightsStack = new Stack<HighlightData>();
while (true) {
String tagName = findTagName(text);
if (tagName == null || myIndex < 0) break;
if (myTags.containsKey(tagName)) {
if (myIsOpeningTag) {
mySkippedLen += tagName.length() + 2;
HighlightData highlightData = new HighlightData(myStartOffset - mySkippedLen, myTags.get(tagName));
highlightsStack.push(highlightData);
} else {
HighlightData highlightData = highlightsStack.pop();
highlightData.setEndOffset(myEndOffset - mySkippedLen);
mySkippedLen += tagName.length() + 3;
highlights.add(highlightData);
}
}
}
return cutDefinedTags(text);
}
private String findTagName(String text) {
myIsOpeningTag = true;
int openTag = text.indexOf('<', myIndex);
if (openTag == -1) {
return null;
}
while (text.charAt(openTag + 1) == '<') {
openTag++;
}
if (text.charAt(openTag + 1) == '/') {
myIsOpeningTag = false;
openTag++;
}
if (!isValidTagFirstChar(text.charAt(openTag + 1))) {
myIndex = openTag + 1;
return "";
}
int closeTag = text.indexOf('>', openTag + 1);
if (closeTag == -1) return null;
int i = text.indexOf('<', openTag + 1);
if (i != -1 && i < closeTag) {
myIndex = i;
return "";
}
final String tagName = text.substring(openTag + 1, closeTag);
if (myIsOpeningTag) {
myStartOffset = openTag + tagName.length() + 2;
if (myTags.containsKey(tagName)) {
mySkipped.add(TextRange.from(openTag, tagName.length() + 2));
}
} else {
myEndOffset = openTag - 1;
if (myTags.containsKey(tagName)) {
mySkipped.add(TextRange.from(openTag - 1, tagName.length() + 3));
}
}
myIndex = Math.max(myStartOffset, myEndOffset + 1);
return tagName;
}
private static boolean isValidTagFirstChar(char c) {
return Character.isLetter(c) || c == '_';
}
private String cutDefinedTags(String text) {
StringBuilder builder = new StringBuilder(text);
for (int i = mySkipped.size() - 1; i >= 0; i--) {
TextRange range = mySkipped.get(i);
builder.delete(range.getStartOffset(), range.getEndOffset());
}
return builder.toString();
}
private void resetIndices() {
myIndex = 0;
myStartOffset = 0;
myEndOffset = 0;
mySkippedLen = 0;
}
}