package com.redhat.ceylon.eclipse.code.style;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.BufferedTokenStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jface.preference.PreferenceConverter;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.MarginPainter;
import org.eclipse.jface.text.WhitespaceCharacterPainter;
import org.eclipse.jface.text.formatter.FormattingContextProperties;
import org.eclipse.jface.text.formatter.IFormattingContext;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
import ceylon.formatter.format_;
import ceylon.formatter.options.FormattingOptions;
import ceylon.formatter.options.SparseFormattingOptions;
import ceylon.formatter.options.combinedOptions_;
import ceylon.language.Singleton;
import com.redhat.ceylon.compiler.typechecker.parser.CeylonLexer;
import com.redhat.ceylon.compiler.typechecker.parser.CeylonParser;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.CompilationUnit;
import com.redhat.ceylon.eclipse.code.editor.CeylonSourceViewer;
import com.redhat.ceylon.eclipse.code.editor.CeylonSourceViewerConfiguration;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;
import com.redhat.ceylon.eclipse.util.StringBuilderWriter;
public class CeylonPreview {
private final class CeylonSourcePreviewerUpdater {
final IPropertyChangeListener fontListener = new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (event.getProperty().equals(
PreferenceConstants.EDITOR_TEXT_FONT)) {
final Font font = JFaceResources
.getFont(PreferenceConstants.EDITOR_TEXT_FONT);
fSourceViewer.getTextWidget().setFont(font);
if (fMarginPainter != null) {
fMarginPainter.initialize();
}
}
}
};
final IPropertyChangeListener propertyListener = new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
if (affectsTextPresentation(event)) {
handlePropertyChangeEvent(event);
fSourceViewer.invalidateTextPresentation();
}
}
private void handlePropertyChangeEvent(PropertyChangeEvent event) {
// TODO do something more
}
private boolean affectsTextPresentation(PropertyChangeEvent event) {
return true; // Always
}
};
public CeylonSourcePreviewerUpdater() {
JFaceResources.getFontRegistry().addListener(fontListener);
EditorsUI.getPreferenceStore().addPropertyChangeListener(
propertyListener);
fSourceViewer.getTextWidget().addDisposeListener(
new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
JFaceResources.getFontRegistry().removeListener(
fontListener);
EditorsUI.getPreferenceStore()
.removePropertyChangeListener(
propertyListener);
}
});
}
}
protected final CeylonSourceViewerConfiguration viewerConfiguration;
protected final Document fPreviewDocument;
protected final SourceViewer fSourceViewer;
protected final MarginPainter fMarginPainter;
protected FormatterPreferences workingValues;
private int fTabSize = 0;
private WhitespaceCharacterPainter fWhitespaceCharacterPainter;
private String fPreviewText;
private CeylonLexer fPreviewLexer;
private CompilationUnit fPreviewCu;
public CeylonPreview(FormatterPreferences workingValues, Composite parent) {
fPreviewDocument = new Document();
this.workingValues = workingValues;
fSourceViewer = new CeylonSourceViewer(parent, null, null, false,
SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER);
fSourceViewer.setEditable(false);
Cursor arrowCursor = fSourceViewer.getTextWidget().getDisplay()
.getSystemCursor(SWT.CURSOR_ARROW);
fSourceViewer.getTextWidget().setCursor(arrowCursor);
viewerConfiguration = new CeylonSourceViewerConfiguration(null);
fSourceViewer.configure(viewerConfiguration);
fSourceViewer.getTextWidget().setFont(
JFaceResources.getFont(PreferenceConstants.EDITOR_TEXT_FONT));
fMarginPainter = new MarginPainter(fSourceViewer);
final RGB rgb = PreferenceConverter
.getColor(EditorsUI.getPreferenceStore(),
AbstractDecoratedTextEditorPreferenceConstants.EDITOR_PRINT_MARGIN_COLOR);
fMarginPainter.setMarginRulerColor(new Color(fSourceViewer
.getTextWidget().getDisplay(), rgb));
fSourceViewer.addPainter(fMarginPainter);
new CeylonSourcePreviewerUpdater();
fSourceViewer.setDocument(fPreviewDocument);
}
public Control getControl() {
return fSourceViewer.getControl();
}
public void update() {
if (workingValues == null) {
fPreviewDocument.set("");
return;
}
// update the print margin
final String value = workingValues
.get(CeylonFormatterConstants.FORMATTER_LINE_SPLIT);
final int lineWidth = getPositiveIntValue(value, 0);
fMarginPainter.setMarginRulerColumn(lineWidth);
// update the tab size
final int tabSize = getPositiveIntValue(
workingValues.get(CeylonFormatterConstants.FORMATTER_TAB_SIZE),
0);
if (tabSize != fTabSize)
fSourceViewer.getTextWidget().setTabs(tabSize);
fTabSize = tabSize;
final StyledText widget = (StyledText) fSourceViewer.getControl();
final int height = widget.getClientArea().height;
final int top0 = widget.getTopPixel();
final int totalPixels0 = getHeightOfAllLines(widget);
final int topPixelRange0 = totalPixels0 > height ? totalPixels0
- height : 0;
widget.setRedraw(false);
doFormatPreview();
fSourceViewer.setSelection(null);
final int totalPixels1 = getHeightOfAllLines(widget);
final int topPixelRange1 = totalPixels1 > height ? totalPixels1
- height : 0;
final int top1 = topPixelRange0 > 0 ? (int) (topPixelRange1 * top0 / (double) topPixelRange0)
: 0;
widget.setTopPixel(top1);
widget.setRedraw(true);
}
private int getHeightOfAllLines(StyledText styledText) {
int height = 0;
int lineCount = styledText.getLineCount();
for (int i = 0; i < lineCount; i++)
height = height
+ styledText.getLineHeight(styledText.getOffsetAtLine(i));
return height;
}
protected void doFormatPreview() {
if (fPreviewText == null) {
fPreviewDocument.set("");
return;
}
fPreviewDocument.set(fPreviewText);
if (fPreviewCu == null) {
return;
}
fSourceViewer.setRedraw(false);
final IFormattingContext context = new CeylonFormattingContext();
try {
final StringBuilder builder = new StringBuilder(
fPreviewDocument.getLength());
context.setProperty(
FormattingContextProperties.CONTEXT_PREFERENCES,
combinedOptions_.combinedOptions(
workingValues.getOptions(),
new Singleton<SparseFormattingOptions>
(SparseFormattingOptions.$TypeDescriptor$,
CeylonStyle.getEclipseWsOptions(null)))
);
context.setProperty(
FormattingContextProperties.CONTEXT_DOCUMENT,
Boolean.valueOf(true));
fPreviewLexer.reset();
format_.format(
fPreviewCu,
(FormattingOptions) context.getProperty(FormattingContextProperties.CONTEXT_PREFERENCES),
new StringBuilderWriter(builder),
new BufferedTokenStream(fPreviewLexer));
fPreviewDocument.set(builder.toString());
} catch (Throwable e) {
final IStatus status = new Status(IStatus.ERROR,
CeylonPlugin.PLUGIN_ID, 10001,
"Internal Formatter Preview Exception", e);
CeylonPlugin.getInstance().getLog().log(status);
} finally {
context.dispose();
fSourceViewer.setRedraw(true);
}
}
public void setPreviewText(String previewText) {
if (previewText == null)
throw new IllegalArgumentException();
fPreviewText = previewText;
fPreviewLexer = new CeylonLexer(new ANTLRStringStream(previewText));
try {
fPreviewCu = new CeylonParser(new CommonTokenStream(
fPreviewLexer)).compilationUnit();
} catch (RecognitionException re) {
CeylonPlugin.getInstance().getLog()
.log(new Status(IStatus.WARNING, CeylonPlugin.PLUGIN_ID,
"Error parsing preview code, should not have happened"));
fPreviewCu = null;
}
update();
}
private static int getPositiveIntValue(String string, int defaultValue) {
try {
int i = Integer.parseInt(string);
if (i >= 0) {
return i;
}
} catch (NumberFormatException e) {
}
return defaultValue;
}
public FormatterPreferences getWorkingValues() {
return workingValues;
}
public void setWorkingValues(FormatterPreferences options) {
workingValues = options;
}
public void showInvisibleCharacters(boolean enable) {
if (enable) {
if (fWhitespaceCharacterPainter == null) {
fWhitespaceCharacterPainter = new WhitespaceCharacterPainter(
fSourceViewer);
fSourceViewer.addPainter(fWhitespaceCharacterPainter);
}
} else {
fSourceViewer.removePainter(fWhitespaceCharacterPainter);
fWhitespaceCharacterPainter = null;
}
}
}