package com.redhat.ceylon.eclipse.code.hover;
import static com.redhat.ceylon.eclipse.code.editor.Navigation.gotoDeclaration;
import static com.redhat.ceylon.eclipse.code.hover.DocumentationHover.getModel;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.hoverJ2C;
import static com.redhat.ceylon.eclipse.ui.CeylonPlugin.getPreferences;
import static com.redhat.ceylon.eclipse.ui.CeylonResources.GOTO;
import static com.redhat.ceylon.eclipse.util.EditorUtil.getCurrentEditor;
import static com.redhat.ceylon.eclipse.util.Nodes.findNode;
import static java.lang.Integer.parseInt;
import static org.eclipse.ui.ISharedImages.IMG_TOOL_BACK;
import static org.eclipse.ui.ISharedImages.IMG_TOOL_BACK_DISABLED;
import static org.eclipse.ui.ISharedImages.IMG_TOOL_FORWARD;
import static org.eclipse.ui.ISharedImages.IMG_TOOL_FORWARD_DISABLED;
import static org.eclipse.ui.PlatformUI.getWorkbench;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.LocationEvent;
import org.eclipse.swt.browser.LocationListener;
import org.eclipse.swt.browser.VisibilityWindowListener;
import org.eclipse.swt.browser.WindowEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.part.ViewPart;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.eclipse.code.browser.BrowserInput;
import com.redhat.ceylon.eclipse.code.correct.correctJ2C;
import com.redhat.ceylon.eclipse.code.editor.CeylonEditor;
import com.redhat.ceylon.eclipse.code.html.HTML;
import com.redhat.ceylon.eclipse.code.html.HTMLPrinter;
import com.redhat.ceylon.eclipse.code.parse.CeylonParseController;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;
import com.redhat.ceylon.eclipse.ui.CeylonResources;
import com.redhat.ceylon.eclipse.util.DocBrowser;
import com.redhat.ceylon.ide.common.correct.specifyTypeQuickFix_;
import com.redhat.ceylon.ide.common.doc.DocGenerator;
import com.redhat.ceylon.model.typechecker.model.Referenceable;
public class DocumentationView extends ViewPart {
private DocGenerator gen
= hoverJ2C().getDocGenerator();
private static final Image GOTO_IMAGE =
CeylonPlugin.imageRegistry().get(GOTO);
private static DocumentationView instance;
public static DocumentationView getInstance() {
return instance;
}
public DocumentationView() {
instance = this;
}
private DocBrowser control;
private CeylonEditor editor;
private CeylonBrowserInput info;
private BackAction back;
private ForwardAction forward;
private OpenDeclarationAction openDeclarationAction;
private IPropertyChangeListener propertyChangeListener;
@Override
public void createPartControl(Composite parent) {
IToolBarManager tbm =
getViewSite()
.getActionBars()
.getToolBarManager();
back = new BackAction();
back.setEnabled(false);
tbm.add(back);
forward = new ForwardAction();
forward.setEnabled(false);
tbm.add(forward);
openDeclarationAction = new OpenDeclarationAction();
tbm.add(openDeclarationAction);
openDeclarationAction.setEnabled(false);
control = new DocBrowser(parent, SWT.NONE);
control.addLocationListener(new LocationListener() {
@Override
public void changing(LocationEvent event) {
String location = event.location;
//necessary for windows environment (fix for blank page)
//somehow related to this: https://bugs.eclipse.org/bugs/show_bug.cgi?id=129236
if (!"about:blank".equals(location) &&
!location.startsWith("http:")) {
event.doit = false;
handleLink(location);
}
}
@Override
public void changed(LocationEvent event) {}
});
// Replace browser's built-in context menu with none
Menu menu = new Menu(getSite().getShell(), SWT.NONE);
MenuItem menuItem = new MenuItem(menu, SWT.NONE);
menuItem.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
openDeclarationAction.run();
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {}
});
menuItem.setImage(GOTO_IMAGE);
menuItem.setText("Open Declaration");
control.setMenu(menu);
updateWithCurrentEditor();
control.addVisibilityWindowListener(
new VisibilityWindowListener() {
@Override
public void show(WindowEvent event) {
updateWithCurrentEditor();
}
@Override
public void hide(WindowEvent event) {}
});
propertyChangeListener =
new IPropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent event) {
updateWithCurrentEditor();
}
};
getPreferences()
.addPropertyChangeListener(propertyChangeListener);
getWorkbench()
.getThemeManager()
.addPropertyChangeListener(propertyChangeListener);
}
private void updateWithCurrentEditor() {
IEditorPart part = getCurrentEditor();
if (part instanceof CeylonEditor) {
CeylonEditor editor = (CeylonEditor) part;
IRegion selection = editor.getSelection();
editor.getSelectionText();
update(editor,
selection.getOffset(),
selection.getLength());
}
}
//TODO: big copy/paste from CeylonLocationListener.handleLink
private void handleLink(String location) {
CeylonParseController controller =
editor.getParseController();
if (location.startsWith("dec:")) {
Referenceable target =
gen.getLinkedModel(
new ceylon.language.String(location),
controller);
if (target!=null) {
gotoDeclaration(target);
}
}
else if (location.startsWith("doc:")) {
Referenceable target =
gen.getLinkedModel(
new ceylon.language.String(location),
controller);
if (target!=null) {
String html =
gen.getDocumentationText(target,
null,
controller.getLastCompilationUnit(),
controller).toString();
if (html!=null) {
control.setText(html);
info = new CeylonBrowserInput(info, target, html);
}
back.update();
forward.update();
}
}
/*else if (location.startsWith("ref:")) {
Referenceable target =
getLinkedModel(location, editor);
new FindReferencesAction(editor,
(Declaration) target).run();
}
else if (location.startsWith("sub:")) {
Referenceable target =
getLinkedModel(location, editor);
new FindSubtypesAction(editor,
(Declaration) target).run();
}
else if (location.startsWith("act:")) {
Referenceable target =
getLinkedModel(location, editor);
new FindRefinementsAction(editor,
(Declaration) target).run();
}
else if (location.startsWith("ass:")) {
Referenceable target =
getLinkedModel(location, editor);
new FindAssignmentsAction(editor,
(Declaration) target).run();
}*/
else if (location.startsWith("stp:")) {
CeylonParseController parseController =
controller;
Tree.CompilationUnit rootNode =
parseController.getLastCompilationUnit();
int offset = parseInt(location.substring(4));
Node node = findNode(rootNode, offset);
if (node instanceof Tree.Type) {
Tree.Type type = (Tree.Type) node;
specifyTypeQuickFix_.get_().specifyType(
rootNode,
new correctJ2C().newDocument(parseController.getDocument()),
type,
true,
type.getTypeModel()
);
}
}
/*else if (location.startsWith("exv:")) {
new ExtractValueProposal(editor).apply(editor.getParseController().getDocument());
}
else if (location.startsWith("exf:")) {
new ExtractFunctionProposal(editor).apply(editor.getParseController().getDocument());
}*/
}
@Override
public void setFocus() {}
public void update(CeylonEditor editor,
int offset, int length) {
if (editor==null) {
clear();
}
else {
Region hoverRegion = new Region(offset, length);
CeylonParseController controller =
editor.getParseController();
Tree.CompilationUnit rootNode =
controller.getLastCompilationUnit();
ceylon.language.String html;
if (rootNode==null) {
html = null;
}
else {
html = gen.getDocumentation(rootNode,
hoverRegion.getOffset(),
controller);
}
if (html!=null) {
String text = html.toString();
if (info==null ||
!info.getHtml().equals(text)) {
control.setText(text);
info = new CeylonBrowserInput(info,
getModel(editor, hoverRegion),
text);
back.update();
forward.update();
openDeclarationAction.setEnabled(true);
}
}
else if (this.editor!=editor) {
clear();
}
}
this.editor = editor;
}
private void clear() {
StringBuilder buffer = new StringBuilder();
HTMLPrinter.insertPageProlog(buffer, 0,
HTML.getStyleSheet());
HTML.addImageAndLabel(buffer, null,
HTML.fileUrl("information.gif")
.toExternalForm(),
16, 16,
"<i>Nothing selected in Ceylon editor.</i>",
20, 2);
// buffer.append("<p>Nothing selected.</p>");
HTMLPrinter.addPageProlog(buffer);
control.setText(buffer.toString());
info=null;
back.update();
forward.update();
openDeclarationAction.setEnabled(false);
}
@Override
public void dispose() {
instance = null;
CeylonPlugin.getPreferences()
.removePropertyChangeListener(propertyChangeListener);
getWorkbench().getThemeManager()
.removePropertyChangeListener(propertyChangeListener);
super.dispose();
}
class BackAction extends Action {
public BackAction() {
setText("Back");
ISharedImages images =
getWorkbench().getSharedImages();
setImageDescriptor(images.getImageDescriptor(IMG_TOOL_BACK));
setDisabledImageDescriptor(images.getImageDescriptor(IMG_TOOL_BACK_DISABLED));
update();
}
@Override
public void run() {
BrowserInput previous = info.getPrevious();
if (previous != null) {
control.setText(previous.getHtml());
info = (CeylonBrowserInput) previous;
update();
forward.update();
}
}
public void update() {
if (info != null && info.getPrevious() != null) {
BrowserInput previous = info.getPrevious();
setToolTipText("Back to " + previous.getInputName());
setEnabled(true);
}
else {
setToolTipText("Back");
setEnabled(false);
}
}
}
class ForwardAction extends Action {
public ForwardAction() {
setText("Forward");
ISharedImages images =
getWorkbench().getSharedImages();
setImageDescriptor(images.getImageDescriptor(IMG_TOOL_FORWARD));
setDisabledImageDescriptor(images.getImageDescriptor(IMG_TOOL_FORWARD_DISABLED));
update();
}
@Override
public void run() {
BrowserInput next = info.getNext();
if (next != null) {
control.setText(next.getHtml());
info = (CeylonBrowserInput) next;
update();
forward.update();
}
}
public void update() {
if (info != null && info.getNext() != null) {
BrowserInput next = info.getNext();
setToolTipText("Forward to " + next.getInputName());
setEnabled(true);
}
else {
setToolTipText("Forward");
setEnabled(false);
}
}
}
final class OpenDeclarationAction extends Action {
public OpenDeclarationAction() {
setText("Open Declaration");
ImageDescriptor descriptor =
CeylonPlugin.imageRegistry()
.getDescriptor(CeylonResources.GOTO);
this.setImageDescriptor(descriptor);
}
@Override
public void run() {
Referenceable model = gen.getLinkedModel(
new ceylon.language.String(info.getAddress()),
editor.getParseController());
gotoDeclaration(model);
}
}
}