package com.redhat.ceylon.eclipse.code.html;
import static com.redhat.ceylon.eclipse.code.html.HTMLPrinter.convertToHTMLContent;
import static com.redhat.ceylon.eclipse.code.html.HTMLPrinter.toHex;
import static com.redhat.ceylon.eclipse.util.Highlights.CHARS;
import static com.redhat.ceylon.eclipse.util.Highlights.COMMENTS;
import static com.redhat.ceylon.eclipse.util.Highlights.IDENTIFIERS;
import static com.redhat.ceylon.eclipse.util.Highlights.KEYWORDS;
import static com.redhat.ceylon.eclipse.util.Highlights.NUMBERS;
import static com.redhat.ceylon.eclipse.util.Highlights.PACKAGES;
import static com.redhat.ceylon.eclipse.util.Highlights.STRINGS;
import static com.redhat.ceylon.eclipse.util.Highlights.TYPES;
import static com.redhat.ceylon.eclipse.util.Highlights.getCurrentThemeColor;
import static org.antlr.runtime.Token.HIDDEN_CHANNEL;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import org.antlr.runtime.Token;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.jface.util.Util;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.osgi.framework.Bundle;
import com.redhat.ceylon.compiler.typechecker.parser.CeylonLexer;
import com.redhat.ceylon.compiler.typechecker.util.NewlineFixingStringStream;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;
import com.redhat.ceylon.eclipse.util.EditorUtil;
import com.redhat.ceylon.ide.common.util.escaping_;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Module;
import com.redhat.ceylon.model.typechecker.model.Package;
import com.redhat.ceylon.model.typechecker.model.Referenceable;
import com.redhat.ceylon.model.typechecker.model.Scope;
public class HTML {
/**
* The style sheet (css).
*/
private static String fgStyleSheet;
public static URL fileUrl(String icon) {
try {
Bundle bundle =
CeylonPlugin.getInstance().getBundle();
return FileLocator.toFileURL(FileLocator.find(bundle,
new Path("icons/").append(icon), null));
}
catch (IOException e) {
e.printStackTrace();
return null;
}
}
/**
* Returns the Javadoc hover style sheet with the current
* Javadoc font from the preferences.
* @return the updated style sheet
* @since 3.4
*/
public static String getStyleSheet() {
if (fgStyleSheet == null) {
fgStyleSheet = loadStyleSheet();
}
final StringBuffer monospaceSize = new StringBuffer();
final Font editorFont = CeylonPlugin.getEditorFont();
final Font hoverFont = CeylonPlugin.getHoverFont();
final FontData monospaceFontData =
editorFont.getFontData()[0];
final FontData textFontData =
hoverFont.getFontData()[0];
final Display display = Display.getDefault();
display.syncExec(new Runnable() {
@Override
public void run() {
Shell activeShell = display.getActiveShell();
//TODO: how can we make sure this is never called
// without a Shell at startup time
if (activeShell!=null) {
GC gc = new GC(activeShell);
Font font = gc.getFont();
gc.setFont(hoverFont);
int hoverFontHeight =
gc.getFontMetrics()
.getAscent();
gc.setFont(editorFont);
int monospaceFontHeight =
gc.getFontMetrics()
.getAscent();
gc.setFont(font);
int ratio = 100 *
monospaceFontData.getHeight() *
hoverFontHeight / monospaceFontHeight /
textFontData.getHeight();
monospaceSize.append(ratio).append("%");
}
}
});
Color linkColor =
EditorUtil.createColor(
JFacePreferences.getPreferenceStore(),
JFacePreferences.HYPERLINK_COLOR);
if (linkColor == null) {
linkColor = JFaceColors.getHyperlinkText(display);
}
Color activeLinkColor =
EditorUtil.createColor(
JFacePreferences.getPreferenceStore(),
JFacePreferences.ACTIVE_HYPERLINK_COLOR);
if (activeLinkColor == null) {
activeLinkColor = JFaceColors.getActiveHyperlinkText(display);
}
boolean bold = (textFontData.getStyle() & SWT.BOLD) != 0;
boolean italic = (textFontData.getStyle() & SWT.ITALIC) != 0;
String size = Integer.toString(textFontData.getHeight()) + UNIT;
String family = textFontData.getName();
return fgStyleSheet
.replaceFirst("%fontFamily", family)
.replaceFirst("%fontSize", size)
.replaceFirst("%fontStyle", italic ? "italic" : "normal")
.replaceFirst("%fontWeight", bold ? "bold" : "normal")
.replaceFirst("%monospaceFontFamily", monospaceFontData.getName())
.replaceFirst("%monospaceFontSize", monospaceSize.toString())
.replaceFirst("%linkColor", toHex(linkColor))
.replaceFirst("%activeLinkColor", toHex(activeLinkColor));
}
private static final String UNIT = Util.isMac() ? "px" : "pt";
public static String loadStyleSheet() {
Bundle bundle = Platform.getBundle(CeylonPlugin.PLUGIN_ID);
URL styleSheetURL = bundle.getEntry("/css/hover.css");
if (styleSheetURL != null) {
BufferedReader reader= null;
try {
reader= new BufferedReader(new InputStreamReader(styleSheetURL.openStream()));
StringBuilder buffer= new StringBuilder(1500);
String line= reader.readLine();
while (line != null) {
buffer.append(line);
buffer.append('\n');
line= reader.readLine();
}
return buffer.toString();
} catch (IOException ex) {
JavaPlugin.log(ex);
return "";
} finally {
try {
if (reader != null)
reader.close();
} catch (IOException e) {
}
}
}
return null;
}
public static void addImageAndLabel(StringBuilder buf, Referenceable model, String imageSrcPath,
int imageWidth, int imageHeight, String label, int labelLeft, int labelTop) {
buf.append("<div style='word-wrap: break-word; position: relative; ");
if (imageSrcPath != null) {
buf.append("margin-left: ").append(labelLeft).append("px; ");
buf.append("padding-top: ").append(labelTop).append("px; ");
}
buf.append("'>");
if (imageSrcPath != null) {
if (model!=null) {
buf.append("<a ").append(HTML.link(model)).append(">");
}
addImage(buf, imageSrcPath, imageWidth, imageHeight,
labelLeft);
if (model!=null) {
buf.append("</a>");
}
}
buf.append(label);
buf.append("</div>");
}
public static void addImage(StringBuilder buf, String imageSrcPath,
int imageWidth, int imageHeight, int labelLeft) {
StringBuilder imageStyle= new StringBuilder("border:none; position: absolute; ");
imageStyle.append("width: ").append(imageWidth).append("px; ");
imageStyle.append("height: ").append(imageHeight).append("px; ");
imageStyle.append("left: ").append(- labelLeft - 1).append("px; ");
// hack for broken transparent PNG support in IE 6, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=223900 :
buf.append("<!--[if lte IE 6]><![if gte IE 5.5]>\n");
//String tooltip= element == null ? "" : "alt='" + "Open Declaration" + "' ";
buf.append("<span ").append("style=\"").append(imageStyle)
.append("filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='")
.append(imageSrcPath).append("')\"></span>\n");
buf.append("<![endif]><![endif]-->\n");
buf.append("<!--[if !IE]>-->\n");
buf.append("<img ").append("style='").append(imageStyle).append("' src='")
.append(imageSrcPath).append("'/>\n");
buf.append("<!--<![endif]-->\n");
buf.append("<!--[if gte IE 7]>\n");
buf.append("<img ").append("style='").append(imageStyle).append("' src='")
.append(imageSrcPath).append("'/>\n");
buf.append("<![endif]-->\n");
}
public static String getAddress(Referenceable model) {
if (model==null) return null;
return "dec:" + declink(model);
}
public static String link(Referenceable model) {
return "href='doc:" + declink(model) + "'";
}
public static String declink(Referenceable model) {
if (model instanceof Package) {
Package p = (Package) model;
return declink(p.getModule()) + ":" + p.getNameAsString();
}
if (model instanceof Module) {
Module module = (Module) model;
return module.getNameAsString() + "/" +
module.getVersion();
}
else if (model instanceof Declaration) {
Declaration declaration = (Declaration) model;
String result = ":" + declaration.getName();
Scope container = declaration.getContainer();
if (container instanceof Referenceable) {
return declink((Referenceable) container)
+ result;
}
else {
return result;
}
}
else {
return "";
}
}
public static String keyword(String kw) {
String kwc = toHex(getCurrentThemeColor(KEYWORDS));
return "<span style='color:"+kwc+"'>"+ kw + "</span>";
}
public static String highlightLine(String line) {
String kwc = toHex(getCurrentThemeColor(KEYWORDS));
String tc = toHex(getCurrentThemeColor(TYPES));
String ic = toHex(getCurrentThemeColor(IDENTIFIERS));
String sc = toHex(getCurrentThemeColor(STRINGS));
String nc = toHex(getCurrentThemeColor(NUMBERS));
String cc = toHex(getCurrentThemeColor(CHARS));
String pc = toHex(getCurrentThemeColor(PACKAGES));
String lcc = toHex(getCurrentThemeColor(COMMENTS));
CeylonLexer lexer = new CeylonLexer(new NewlineFixingStringStream(line));
Token token;
boolean inPackageName = false;
StringBuilder result = new StringBuilder();
while ((token=lexer.nextToken()).getType()!=CeylonLexer.EOF) {
String s = convertToHTMLContent(token.getText());
int type = token.getType();
if (type!=CeylonLexer.LIDENTIFIER &&
type!=CeylonLexer.MEMBER_OP &&
token.getChannel()!=HIDDEN_CHANNEL) {
inPackageName = false;
}
else if (inPackageName) {
result.append("<span style='color:"+pc+"'>").append(s).append("</span>");
continue;
}
switch (type) {
case CeylonLexer.FLOAT_LITERAL:
case CeylonLexer.NATURAL_LITERAL:
result.append("<span style='color:"+nc+"'>").append(s).append("</span>");
break;
case CeylonLexer.CHAR_LITERAL:
result.append("<span style='color:"+cc+"'>").append(s).append("</span>");
break;
case CeylonLexer.STRING_LITERAL:
case CeylonLexer.STRING_START:
case CeylonLexer.STRING_END:
case CeylonLexer.STRING_MID:
case CeylonLexer.VERBATIM_STRING:
result.append("<span style='color:"+sc+"'>").append(s).append("</span>");
break;
case CeylonLexer.UIDENTIFIER:
result.append("<span style='color:"+tc+"'>").append(s).append("</span>");
break;
case CeylonLexer.LIDENTIFIER:
result.append("<span style='color:"+ic+"'>").append(s).append("</span>");
break;
case CeylonLexer.MULTI_COMMENT:
case CeylonLexer.LINE_COMMENT:
result.append("<span style='color:"+lcc+"'>").append(s).append("</span>");
break;
case CeylonLexer.IMPORT:
case CeylonLexer.PACKAGE:
case CeylonLexer.MODULE:
inPackageName = true; //then fall through!
default:
if (escaping_.get_().isKeyword(s)) {
result.append("<span style='color:"+kwc+"'>").append(s).append("</span>");
}
else {
result.append(s);
}
}
}
return result.toString();
}
}