/* * Copyright 2013-2016 consulo.io * * 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 consulo.web.gwt.client.ui; import com.google.gwt.dom.client.Style; import com.google.gwt.user.client.ui.InlineHTML; import consulo.web.gwt.client.util.BitUtil; import consulo.web.gwt.client.util.GwtStyleUtil; import consulo.web.gwt.shared.transport.GwtColor; import consulo.web.gwt.shared.transport.GwtHighlightInfo; import consulo.web.gwt.shared.transport.GwtTextAttributes; import consulo.web.gwt.shared.transport.GwtTextRange; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author VISTALL * @since 19-May-16 */ public class EditorSegmentBuilder { public static class CharSpan extends InlineHTML { public static class StyleInfo { private String key; private String value; private String tooltip; private int flag; public StyleInfo(String key, String value, String tooltip,int flag) { this.key = key; this.flag = flag; this.value = value; this.tooltip = tooltip; } } public GwtTextRange range; public boolean lineWrap; private int highlightFlags; @Nullable private List<StyleInfo> myStyles; @Nullable private Map<String, Integer> mySeverityMap; public CharSpan(String html) { super(html); } public String getToolTip() { if(myStyles == null) { return null; } for (StyleInfo style : myStyles) { if(style.flag == Editor.ourLexerFlag) { continue; } String tooltip = style.tooltip; if(tooltip != null) { return tooltip; } } return null; } public void add(String key, String value, String tooltip, int severity, int flag) { highlightFlags = BitUtil.set(highlightFlags, flag, true); Integer oldSeverity = mySeverityMap == null ? null : mySeverityMap.get(key); if (oldSeverity != null && severity <= oldSeverity) { return; } if (severity != 0) { if (mySeverityMap == null) { mySeverityMap = new HashMap<String, Integer>(); } mySeverityMap.put(key, severity); } if (myStyles == null) { myStyles = new ArrayList<StyleInfo>(); } myStyles.add(new StyleInfo(key, value, tooltip, flag)); Style style = getElement().getStyle(); if (key.equals("textDecoration")) { String oldValue = style.getProperty(key); if (oldValue != null) { style.setProperty(key, oldValue + " " + value); return; } } style.setProperty(key, value); } public void removeByFlag(int flag) { if (BitUtil.isSet(highlightFlags, flag)) { highlightFlags = BitUtil.set(highlightFlags, flag, false); if (myStyles == null) { return; } StyleInfo[] styleInfos = myStyles.toArray(new StyleInfo[myStyles.size()]); for (StyleInfo styleInfo : styleInfos) { if (mySeverityMap != null) { mySeverityMap.remove(styleInfo.key); } if (styleInfo.flag == flag) { myStyles.remove(styleInfo); Style style = getElement().getStyle(); if (styleInfo.key.equals("textDecoration")) { String oldValue = style.getProperty(styleInfo.key); if (oldValue == null) { continue; } // it mixin - need removed only our value if (oldValue.contains(" ")) { oldValue = oldValue.replace(" " + styleInfo.value, ""); style.setProperty(styleInfo.key, oldValue); continue; } } style.setProperty(styleInfo.key, null); } } } } } private CharSpan[] myFragments; private int myLineCount; public EditorSegmentBuilder(String text) { myFragments = new CharSpan[text.length()]; for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); String labelText = mapChar(c); final int startOffset = i; if (c == ' ' || c == '\t') { for (int k = startOffset + 1; k < text.length(); k++) { char nextChar = text.charAt(k); if (nextChar == c) { labelText += mapChar(nextChar); i++; } else { break; } } } int endOffset = i + 1; CharSpan charSpan = new CharSpan(labelText.isEmpty() ? "‍" : labelText); charSpan.range = new GwtTextRange(startOffset, endOffset); charSpan.setStyleName(null); charSpan.getElement().setPropertyObject("range", charSpan.range); charSpan.getElement().setPropertyObject("widget", charSpan); charSpan.lineWrap = labelText.isEmpty(); if (charSpan.lineWrap) { myLineCount++; } for (int k = startOffset; k < endOffset; k++) { myFragments[k] = charSpan; } } } public void addHighlights(List<GwtHighlightInfo> result, int flag) { for (CharSpan fragment : myFragments) { fragment.removeByFlag(flag); } for (GwtHighlightInfo highlightInfo : result) { GwtTextRange textRange = highlightInfo.getTextRange(); for (int i = textRange.getStartOffset(); i < textRange.getEndOffset(); i++) { CharSpan fragment = myFragments[i]; GwtTextAttributes textAttributes = highlightInfo.getTextAttributes(); if(textAttributes != null) { add(fragment, textAttributes, highlightInfo.getTooltip(), highlightInfo.getSeverity(), flag); } } } } public void removeHighlightByRange(GwtTextRange textRange, int flag) { for (int i = textRange.getStartOffset(); i < textRange.getEndOffset(); i++) { CharSpan fragment = myFragments[i]; fragment.removeByFlag(flag); } } private void add(CharSpan fragment, GwtTextAttributes textAttributes, String tooltip, int severity, int flag) { GwtColor foreground = textAttributes.getForeground(); if (foreground != null) { fragment.add("color", GwtStyleUtil.toString(foreground), tooltip, severity, flag); } GwtColor background = textAttributes.getBackground(); if (background != null) { fragment.add("backgroundColor", GwtStyleUtil.toString(background), tooltip, severity, flag); } if (BitUtil.isSet(textAttributes.getFlags(), GwtTextAttributes.BOLD)) { fragment.add("fontWeight", "bold", tooltip, severity, flag); } if (BitUtil.isSet(textAttributes.getFlags(), GwtTextAttributes.ITALIC)) { fragment.add("fontStyle", "italic", tooltip, severity, flag); } if (BitUtil.isSet(textAttributes.getFlags(), GwtTextAttributes.UNDERLINE)) { fragment.add("textDecoration", "underline", tooltip, severity, flag); } if (BitUtil.isSet(textAttributes.getFlags(), GwtTextAttributes.LINE_THROUGH)) { fragment.add("textDecoration", "line-through", tooltip, severity, flag); } } public int getLineCount() { return myLineCount; } public CharSpan[] getFragments() { return myFragments; } private static String mapChar(char c) { switch (c) { case '&': return "&"; case '<': return "<"; case '>': return ">"; case '\"': return """; case '\t': return " "; case ' ': return " "; case '\n': return ""; // hack } return String.valueOf(c); } }