package com.redhat.ceylon.eclipse.code.editor;
import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.utilJ2C;
import static com.redhat.ceylon.eclipse.ui.CeylonPlugin.EDITOR_ID;
import static com.redhat.ceylon.eclipse.util.EditorUtil.getActivePage;
import static com.redhat.ceylon.eclipse.util.EditorUtil.getEditorInput;
import static com.redhat.ceylon.eclipse.util.InteropUtils.toCeylonString;
import static com.redhat.ceylon.eclipse.util.InteropUtils.toJavaString;
import static com.redhat.ceylon.eclipse.util.JavaSearch.toCeylonDeclaration;
import static com.redhat.ceylon.eclipse.util.Nodes.getIdentifyingNode;
import static com.redhat.ceylon.eclipse.util.Nodes.getReferencedNode;
import static org.eclipse.jdt.internal.ui.javaeditor.EditorUtility.revealInEditor;
import static org.eclipse.ui.PlatformUI.getWorkbench;
import static org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds.TOGGLE_SHOW_SELECTED_ELEMENT_ONLY;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.action.IAction;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.TextEditorAction;
import com.redhat.ceylon.common.Backends;
import com.redhat.ceylon.compiler.typechecker.io.VirtualFile;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.eclipse.core.builder.CeylonBuilder;
import com.redhat.ceylon.eclipse.ui.CeylonPlugin;
import com.redhat.ceylon.eclipse.util.EditorUtil;
import com.redhat.ceylon.ide.common.model.CeylonBinaryUnit;
import com.redhat.ceylon.ide.common.model.CeylonUnit;
import com.redhat.ceylon.ide.common.model.ExternalSourceFile;
import com.redhat.ceylon.ide.common.model.IJavaModelAware;
import com.redhat.ceylon.ide.common.model.IResourceAware;
import com.redhat.ceylon.ide.common.model.JavaUnit;
import com.redhat.ceylon.ide.common.typechecker.ExternalPhasedUnit;
import com.redhat.ceylon.ide.common.typechecker.IdePhasedUnit;
import com.redhat.ceylon.model.typechecker.model.Declaration;
import com.redhat.ceylon.model.typechecker.model.Function;
import com.redhat.ceylon.model.typechecker.model.ModelUtil;
import com.redhat.ceylon.model.typechecker.model.Referenceable;
import com.redhat.ceylon.model.typechecker.model.Unit;
public class Navigation {
public static ITextEditor gotoDeclaration(Referenceable model) {
if (model==null) {
return null;
}
else {
Unit unit = model.getUnit();
if (unit instanceof CeylonUnit) {
CeylonUnit ceylonUnit = (CeylonUnit) unit;
Node node =
getReferencedNode(model,
ceylonUnit.getCompilationUnit());
if (node != null) {
return gotoNode(node, null);
}
else if (ceylonUnit instanceof CeylonBinaryUnit) {
//special case for Java source in ceylon.language!
@SuppressWarnings("rawtypes")
CeylonBinaryUnit binaryUnit =
(CeylonBinaryUnit) ceylonUnit;
String path = toJavaString(binaryUnit.getSourceRelativePath());
if (JavaCore.isJavaLikeFileName(path) &&
model instanceof Declaration) {
return gotoJavaNode((Declaration) model);
}
else {
return null;
}
}
else {
return null;
}
}
else if (unit instanceof JavaUnit) {
return gotoJavaNode((Declaration) model);
}
else {
return null;
}
}
}
public static ITextEditor gotoCeylonDeclarationFromJava(IProject project,
IJavaElement javaElement) {
Declaration declaration =
toCeylonDeclaration(project, javaElement);
// if (declaration != null) {
// Unit u = declaration.getUnit();
// if (u instanceof CeylonUnit) {
// PhasedUnit pu = ((CeylonUnit) u).getPhasedUnit();
// if (pu != null) {
// gotoDeclaration(pu, declaration);
// return;
// }
// }
return gotoDeclaration(declaration);
// }
}
// private static void gotoDeclaration(PhasedUnit pu, Declaration declaration) {
// IEditorInput editorInput =
// getEditorInput(pu.getUnit());
// Node node = getReferencedNode(declaration,
// pu.getCompilationUnit());
// try {
// CeylonEditor editor = (CeylonEditor)
// getActivePage().openEditor(editorInput, EDITOR_ID);
// editor.selectAndReveal(getIdentifyingNode(node).getStartIndex(),
// declaration.getName().length());
// }
// catch (PartInitException e) {
// e.printStackTrace();
// }
// }
public static ITextEditor gotoNode(Node node, CeylonEditor editor) {
Unit unit = node.getUnit();
Node identifyingNode = getIdentifyingNode(node);
int length = identifyingNode.getDistance();
int startOffset = identifyingNode.getStartIndex();
Tree.CompilationUnit rootNode =
editor==null ? null :
editor.getParseController()
.getLastCompilationUnit();
if (rootNode!=null && unit.equals(rootNode.getUnit())) {
editor.selectAndReveal(startOffset, length);
return editor;
}
else {
if (unit instanceof IResourceAware) {
@SuppressWarnings({ "unchecked", "rawtypes" })
IResourceAware<IProject,IFolder,IFile>
ra = (IResourceAware) unit;
IFile file = ra.getResourceFile();
if (file != null) {
return gotoFile(file, startOffset, length);
}
}
return gotoLocation(getNodePath(node), startOffset, length);
}
}
public static ITextEditor gotoLocation(Unit unit,
int startOffset, int length) {
if (unit instanceof IResourceAware) {
@SuppressWarnings({ "unchecked", "rawtypes" })
IResourceAware<IProject,IFolder,IFile>
ra = (IResourceAware) unit;
IFile file = ra.getResourceFile();
if (file != null) {
return gotoFile(file, startOffset, length);
}
}
return gotoLocation(getUnitPath(unit), startOffset, length);
}
public static ITextEditor gotoLocation(IPath path, int offset) {
return gotoLocation(path, offset, 0);
}
public static ITextEditor gotoLocation(IPath path, int offset, int length) {
if (path==null || path.isEmpty()) {
return null;
}
IEditorInput editorInput;
try {
editorInput = getEditorInput(path);
}
catch (IllegalArgumentException iae) {
//this happens for source files that are not in a Ceylon source folder
return null;
}
try {
ITextEditor editor =
(ITextEditor)
getActivePage()
.openEditor(editorInput,
EDITOR_ID);
editor.selectAndReveal(offset, length);
return editor;
}
catch (PartInitException pie) {
pie.printStackTrace();
return null;
}
}
public static ITextEditor gotoFile(IFile file, int offset, int length) {
IWorkbenchPage page = getActivePage();
IEditorInput input = new FileEditorInput(file);
IEditorPart part = page.findEditor(input);
ITextEditor editor = null;
if (part instanceof ITextEditor) {
editor = (ITextEditor) part;
}
else {
try {
editor = (ITextEditor)
page.openEditor(input,
EDITOR_ID);
}
catch (PartInitException e) {
e.printStackTrace();
return null;
}
}
if (offset>=0) {
editor.selectAndReveal(offset, length);
}
page.activate(editor);
return editor;
}
public static IPath getNodePath(Node node) {
return getUnitPath(node.getUnit());
}
public static IPath getUnitPath(Unit unit) {
if (unit instanceof IResourceAware) {
@SuppressWarnings({ "rawtypes", "unchecked" })
IResourceAware<IProject,IFolder,IFile>
ra = (IResourceAware) unit;
IFile fileResource = ra.getResourceFile();
return fileResource!=null ?
fileResource.getLocation() :
new Path(unit.getFullPath());
}
if ((unit instanceof ExternalSourceFile) ||
(unit instanceof CeylonBinaryUnit)) {
CeylonUnit ceylonUnit = (CeylonUnit) unit;
IdePhasedUnit externalPhasedUnit =
ceylonUnit.getPhasedUnit();
VirtualFile file =
externalPhasedUnit.getUnitFile();
return new Path(file.getPath());
}
return null;
}
private static IEditorPart openInEditor(IFile file,
boolean activate)
throws PartInitException {
if (file!=null) {
IWorkbenchPage page =
getWorkbench()
.getActiveWorkbenchWindow()
.getActivePage();
if (page!=null) {
IEditorPart editorPart =
IDE.openEditor(page, file, activate);
Navigation.initializeHighlightRange(editorPart);
return editorPart;
}
}
return null;
}
private static IEditorPart openInEditor(IEditorInput input,
String editorID, boolean activate)
throws PartInitException {
if (input!=null) {
IWorkbenchPage page =
getWorkbench()
.getActiveWorkbenchWindow()
.getActivePage();
if (page!=null) {
IEditorPart editorPart =
page.openEditor(input, editorID, activate);
Navigation.initializeHighlightRange(editorPart);
return editorPart;
}
}
return null;
}
/**
* Opens an editor suitable for a model element, IFile, IStorage...
*
* @return the IEditorPart or null if wrong element type or opening failed
*/
public static IEditorPart openInEditor(Object inputElement,
boolean activate)
throws PartInitException {
if (inputElement instanceof IFile) {
IFile file = (IFile) inputElement;
return openInEditor(file, activate);
}
IEditorInput input = EditorUtil.getEditorInput(inputElement);
if (input!=null) {
String id = EditorUtil.getEditorID(input, inputElement);
return openInEditor(input, id, activate);
}
return null;
}
/**
* Opens an editor suitable for a model element, <code>IFile</code>, or <code>IStorage</code>.
* The editor is activated by default.
*
* @return the IEditorPart or null if wrong element type or opening failed
*/
public static IEditorPart openInEditor(Object inputElement)
throws PartInitException {
return openInEditor(inputElement, true);
}
public static void gotoLocation(final IResource file, final int offset, int length) {
Map<String, Object> map = new HashMap<String, Object>();
map.put(IMarker.CHAR_START, offset);
map.put(IMarker.CHAR_END, offset+length);
if (file instanceof IFile &&
CeylonBuilder.isCeylon((IFile) file)) {
map.put(IDE.EDITOR_ID_ATTR, CeylonPlugin.EDITOR_ID);
}
try {
IMarker marker = file.createMarker(IMarker.TEXT);
marker.setAttributes(map);
IDE.openEditor(EditorUtil.getActivePage(), marker);
marker.delete();
}
catch (CoreException ce) {} //deliberately swallow it
/*try {
IEditorPart editor = EditorUtility.isOpenInEditor(path);
if (editor == null) {
editor = EditorUtility.openInEditor(path);
}
EditorUtility.revealInEditor(editor, targetOffset, 0);
}
catch (PartInitException e) {
e.printStackTrace();
}*/
}
public static void gotoLocation(final IResource file, final int offset) {
gotoLocation(file, offset, 0);
}
private static void initializeHighlightRange(IEditorPart editorPart) {
if (editorPart instanceof ITextEditor) {
IAction toggleAction =
editorPart.getEditorSite()
.getActionBars()
.getGlobalActionHandler(
TOGGLE_SHOW_SELECTED_ELEMENT_ONLY);
boolean enable = toggleAction!=null;
if (enable && editorPart instanceof CeylonEditor) {
// TODO Maybe support show segments?
enable = false; // EditorUtil.getPreferences().getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS);
}
else {
enable = enable &&
toggleAction.isEnabled() &&
toggleAction.isChecked();
}
if (enable) {
if (toggleAction instanceof TextEditorAction) {
TextEditorAction textEditorAction =
(TextEditorAction) toggleAction;
ITextEditor editor = (ITextEditor) editorPart;
// Reset the action
textEditorAction.setEditor(null);
// Restore the action
textEditorAction.setEditor(editor);
}
else {
// Un-check
toggleAction.run();
// Check
toggleAction.run();
}
}
}
}
public static ITextEditor gotoJavaNode(Declaration declaration) {
try {
IJavaElement element = getJavaElement(declaration);
if (element==null) {
return null;
}
else {
IEditorPart part = openInEditor(element, true);
if (part!=null) {
revealInEditor(part, element);
}
return (ITextEditor) part;
}
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static IJavaElement getJavaElement(Declaration declaration)
throws JavaModelException {
return getJavaElement(declaration, null);
}
public static IJavaElement getJavaElement(Declaration declaration, IProgressMonitor monitor)
throws JavaModelException {
if (declaration instanceof Function && declaration.isAnnotation()) {
Function fun = (Function) declaration;
declaration = fun.getTypeDeclaration();
}
Unit unit = declaration.getUnit();
if (unit instanceof IJavaModelAware) {
@SuppressWarnings({ "rawtypes", "unchecked" })
IJavaModelAware<IProject, ITypeRoot, IJavaElement>
javaModelAware = (IJavaModelAware) unit;
return javaModelAware.toJavaElement(declaration,
utilJ2C().wrapProgressMonitor(monitor));
}
return null;
}
public static Referenceable resolveNative(
Declaration dec, Backends backends) {
if (backends.none()) {
return null;
}
Unit unit = dec.getUnit();
if (unit instanceof CeylonBinaryUnit) {
@SuppressWarnings("rawtypes")
CeylonBinaryUnit binaryUnit =
(CeylonBinaryUnit) unit;
ExternalPhasedUnit phasedUnit =
binaryUnit.getPhasedUnit();
if (phasedUnit != null) {
Unit sourceFile = phasedUnit.getUnit();
if (sourceFile != null) {
String sourceRelativePath =
toJavaString(binaryUnit.getCeylonModule()
.toSourceUnitRelativePath(
toCeylonString(
unit.getRelativePath())));
boolean isCeylon = sourceRelativePath!=null &&
sourceRelativePath.endsWith(".ceylon");
for (Declaration sourceDecl:
sourceFile.getDeclarations()) {
boolean thisOne = isCeylon ?
sourceDecl.equals(dec) :
sourceDecl.getQualifiedNameString()
.equals(dec.getQualifiedNameString());
if (thisOne) {
return ModelUtil.getNativeDeclaration(sourceDecl, backends);
}
}
}
}
}
return ModelUtil.getNativeDeclaration(dec, backends);
}
}