package net.sf.eclipsefp.haskell.ui.handlers; import java.io.File; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.util.Map; import net.sf.eclipsefp.haskell.buildwrapper.BWFacade; import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin; import net.sf.eclipsefp.haskell.buildwrapper.types.CabalPackage; import net.sf.eclipsefp.haskell.buildwrapper.types.Location; import net.sf.eclipsefp.haskell.core.cabalmodel.CabalSyntax; import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescription; import net.sf.eclipsefp.haskell.core.cabalmodel.PackageDescriptionLoader; import net.sf.eclipsefp.haskell.core.compiler.CompilerManager; import net.sf.eclipsefp.haskell.core.compiler.IHsImplementation; import net.sf.eclipsefp.haskell.core.util.ResourceUtil; import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.HaskellEditor; import net.sf.eclipsefp.haskell.ui.internal.editors.haskell.imports.ImportsManager; import net.sf.eclipsefp.haskell.ui.internal.preferences.IPreferenceConstants; import net.sf.eclipsefp.haskell.ui.internal.preferences.SearchPathsPP; import net.sf.eclipsefp.haskell.ui.internal.util.UITexts; import net.sf.eclipsefp.haskell.ui.util.text.WordFinder; import net.sf.eclipsefp.haskell.ui.util.text.WordFinder.EditorThing; import net.sf.eclipsefp.haskell.util.PlatformUtil; import org.eclipse.core.commands.AbstractHandler; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.IValueVariable; import org.eclipse.core.variables.VariablesPlugin; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.browser.IWorkbenchBrowserSupport; import org.eclipse.ui.handlers.HandlerUtil; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.progress.UIJob; import org.eclipse.ui.texteditor.ITextEditor; /** * Action that opens an editor and scrolls it to the definition of the currently * selected element. * * @author Thomas ten Cate */ public class OpenDefinitionHandler extends AbstractHandler { public OpenDefinitionHandler() { // explicit default constructor } @Override public Object execute( final ExecutionEvent event ) { IEditorPart editor = HandlerUtil.getActiveEditor( event ); if( !( editor instanceof HaskellEditor ) ) { return null; } final HaskellEditor haskellEditor = ( HaskellEditor )editor; WordFinder.getEditorThing( haskellEditor, new WordFinder.EditorThingHandler() { @Override public void handle( final EditorThing thing ) { //String name = thing.getName(); //char haddockType = thing.getHaddockType(); // final ScionInstance instance=thing.getInstance(); openDefinition( haskellEditor, thing ); } } ); return null; } public static void openDefinition(final HaskellEditor haskellEditor, final EditorThing thing) { if(thing==null || thing.getThing()==null) { return; } final IFile file = thing.getFile(); String haddockType=thing.getThing().getHaddockType(); /* * Location location = instance.firstDefinitionLocation( name ); * * if( location != null ) { final Location theLocation = location; * new UIJob(UITexts.openDefinition_open_job) { * * @Override public IStatus runInUIThread( final IProgressMonitor * monitor ) { try { openInEditor( * haskellEditor.getEditorSite().getPage(), theLocation, * file.getProject() ); } catch( PartInitException ex ) { * ex.printStackTrace(); // too bad } * * return Status.OK_STATUS; } }.schedule(); } else { */ String module = null; String shortName = thing.getThing().getName(); String fullName=shortName; if ("m".equals(thing.getThing().getHaddockType())){ shortName=null; module=thing.getThing().getName(); } else { module = thing.getThing().getModule(); if (module!=null){ fullName=module+"."+shortName; } } final IProject p = file.getProject(); final BWFacade f = BuildWrapperPlugin.getFacade( p ); // we try to find an id for an object not exported, that scion // doesn't // know // but that we have in the outline // short name of course in the outline String myModule = haskellEditor.getModuleName(); if( ( module != null && module.equals( myModule ) ) || ( module == null && ( myModule == null || myModule .length() == 0 ) ) ) { Location location = haskellEditor .getOutlineLocation( shortName ); if( location != null ) { final Location theLocation = location; // Ensure that this happens in the UI thread new UIJob( UITexts.openDefinition_select_job ) { @Override public IStatus runInUIThread( final IProgressMonitor monitor ) { selectAndReveal( haskellEditor, haskellEditor .getDocument(), theLocation ); return Status.OK_STATUS; } }.schedule(); return; } } if (module==null){ ImportsManager mgr = haskellEditor.getImportsManager(); Map<String, ImportsManager.Imported> decls = mgr.getImportedDeclarations(); for ( String s : decls.keySet() ) { ImportsManager.Imported i=decls.get(s); if (i.getDocumented().getDocumented().getName().equals( fullName ) || i.getAnimport().getImportDef().getModule().equals( fullName )){ // IFile fi=i.getDocumented().getFile(); if (fi!=null){ openFile( haskellEditor .getEditorSite().getPage(), fi, shortName ); return; } module=i.getAnimport().getImportDef().getModule(); if (haddockType==null){ haddockType="t"; // assume types since not resolved } //break; } } } if( module != null ) { IFile fi = ResourceUtil.findFileFromModule( p, module ); if (fi!=null){ openFile( haskellEditor .getEditorSite().getPage(), fi, shortName ); return; } final String moduleF = module; final String shortNameF = shortName; final String haddockTypeF = haddockType; new Thread( new Runnable() { @Override public void run() { // find in outside location... outer: for( CabalPackage[] pkgs: f.getPackagesByDB() .values() ) { for( CabalPackage cp: pkgs ) { if( cp.getModules() != null && cp.getModules().contains( moduleF ) ) { final String pkg = cp.toString(); //new UIJob( UITexts.openDefinition_select_job ) { // @Override // public IStatus runInUIThread( // final IProgressMonitor monitor ) { openExternalDefinition( haskellEditor .getEditorSite().getPage(), p, pkg, moduleF, shortNameF, haddockTypeF ); // return Status.OK_STATUS; // } //}.schedule(); break outer; } } } } } ).start(); } } private static void openFile(final IWorkbenchPage page,final IFile file,final String shortName){ new UIJob( UITexts.openDefinition_select_job ) { @Override public IStatus runInUIThread(final IProgressMonitor mon){ try { IEditorPart editor = IDE.openEditor( page, file, true ); if( editor instanceof HaskellEditor ) { HaskellEditor hEditor = ( HaskellEditor )editor; if (shortName!=null){ openLocation( hEditor, shortName ); } } } catch (CoreException ce){ HaskellUIPlugin.log( ce ); } return Status.OK_STATUS; } }.schedule(); } private static void openLocation( final HaskellEditor hEditor,final String shortName){ if (!hEditor.hasOutline()){ new Thread(new Runnable() { @Override public synchronized void run() { for (int i=0;i<10 && !hEditor.hasOutline();i++){ try { wait(500); } catch (InterruptedException ie){ // ignore; } } if (shortName!=null && hEditor.hasOutline()){ new UIJob( UITexts.openDefinition_select_job ) { @Override public IStatus runInUIThread(final IProgressMonitor mon){ openLocation( hEditor, shortName ); return Status.OK_STATUS; } }.schedule(); } } }).start(); return; } Location location = hEditor.getOutlineLocation( shortName ); if( location != null ) { IDocument document = hEditor.getDocument(); selectAndReveal( hEditor, document, location ); } } public static boolean openExternalDefinition( final IWorkbenchPage page, final IProject project, final String pkg, final String module, final String shortName, final String type ) { int ix = pkg!=null?pkg.lastIndexOf( '-' ):-1; String packageName = pkg; String packageVersion = ""; if( ix > -1 && pkg!=null) { packageName = pkg.substring( 0, ix ); if( ix < pkg.length() - 2 ) { packageVersion = pkg.substring( ix + 1 ); } } try { if (project!=null && packageName!=null){ // String moduleHSFile=module.replace( '.', '/' ); for( IProject p: project.getReferencedProjects() ) { if( ResourceUtil.hasHaskellNature( p )) { IFile cf=BuildWrapperPlugin.getCabalFile( p ); if (cf!=null){ PackageDescription pd=PackageDescriptionLoader.load(cf); // check on name in cabal file, not project name, as their may be differences if (pd.getPackageStanza()!=null && packageName.equals( pd.getPackageStanza().getProperties().get( CabalSyntax.FIELD_NAME.getCabalName() ))){ IFile f = ResourceUtil.findFileFromModule( p, module ); if( f != null ) { openFile( page, f, shortName ); return true; } } } /* * IFile f=BuildWrapperPlugin.getCabalFile( project ); * PackageDescription pd=PackageDescriptionLoader.load(f); IResource * r=null; for (String src:pd.getStanzasBySourceDir().keySet()){ if * (src!=null && src.equals( "." )) { //$NON-NLS-1$ r=p.findMember( * moduleHSFile +"."+ FileUtil.EXTENSION_HS); if (r==null || * !r.exists()){ r=p.findMember( moduleHSFile +"."+ * FileUtil.EXTENSION_LHS); } } else { IFolder fldr=p.getFolder( src * ); r=fldr.findMember( moduleHSFile +"."+ FileUtil.EXTENSION_HS); * if (r==null || !r.exists()){ r=fldr.findMember( moduleHSFile * +"."+ FileUtil.EXTENSION_LHS); } } */ // } } } } } catch( CoreException ce ) { HaskellUIPlugin.log( ce ); } String moduleHTMLFile = module!=null?module.replace( '.', '-' ) + ".html":""; // String relFile=pkg+"/"+moduleHTMLFile; String anchor = shortName!=null?type + ":" + toAnchorName(shortName):""; IHsImplementation hsImpl = CompilerManager.getCurrentHsImplementation(); IStringVariableManager mgr = VariablesPlugin.getDefault() .getStringVariableManager(); String hsImplBinDir = hsImpl != null ? hsImpl.getBinDir() : ""; if( hsImplBinDir.length() > 0 ) { IPath hsIBDPath = new Path( hsImplBinDir ); // Need the extra "/" to make it a proper file URL: file:///C:/... if( PlatformUtil.runningOnWindows() && hsIBDPath.isAbsolute() ) { hsImplBinDir = "/".concat( hsIBDPath.toPortableString() ); } } IValueVariable[] vars = new IValueVariable[] { mgr.newValueVariable( "IMPL_BIN", "", true, hsImplBinDir ), mgr.newValueVariable( "PACKAGE_NAME", "", true, String.valueOf(packageName) ), mgr.newValueVariable( "PACKAGE_VERSION", "", true, packageVersion ), mgr.newValueVariable( "MODULE", "", true, module ), mgr.newValueVariable( "MODULE_HTML", "", true, moduleHTMLFile ), mgr.newValueVariable( "ANCHOR", "", true, anchor ), mgr.newValueVariable( "NAME", "", true, shortName ), }; try { mgr.addVariables( vars ); try { String s = HaskellUIPlugin.getDefault().getPreferenceStore().getString( IPreferenceConstants.HADDOCK_SEARCH_PATHS ); if( s != null && s.length() > 0 ) { String[] paths = SearchPathsPP.parseString( s ); for( String p: paths ) { if (module==null){ if (p.contains( "${MODULE}" ) || p.contains( "${MODULE_HTML}" )){ continue; } if (packageVersion.length()>0){ int ixPV=p.indexOf( "${PACKAGE_VERSION}" ); if (ixPV>-1){ p=p.substring(0,ixPV+"${PACKAGE_VERSION}".length()); } } else { int ixPV=p.indexOf( "${PACKAGE_NAME}" ); if (ixPV>-1){ p=p.substring(0,ixPV+"${PACKAGE_NAME}".length()); } } } String fullPath = mgr.performStringSubstitution( p ); try { final URL url = new URL( fullPath ); // Ensure that spaces are properly escaped inside the path portion // of the URL if( exists( url ) ) { page.getWorkbenchWindow().getShell().getDisplay().asyncExec( new Runnable() { @Override public void run() { try { PlatformUI.getWorkbench().getBrowserSupport().createBrowser( IWorkbenchBrowserSupport.AS_EDITOR | IWorkbenchBrowserSupport.LOCATION_BAR | IWorkbenchBrowserSupport.NAVIGATION_BAR | IWorkbenchBrowserSupport.STATUS, pkg + " " + module,null,null ).openURL( url ); } catch( Exception ce ) { HaskellUIPlugin.log( ce ); } } }); return true; } } catch( Exception ce ) { HaskellUIPlugin.log( ce ); } } } } finally { mgr.removeVariables( vars ); } } catch( CoreException ce ) { HaskellUIPlugin.log( ce ); } /* * IHsImplementation hsImpl = * CompilerManager.getInstance().getCurrentHsImplementation(); if * (hsImpl!=null){ File bin=new File(hsImpl.getBinDir()); File htmlDocs=new * File(bin.getParentFile(),"doc/html/libraries"); if (htmlDocs.exists()){ * File htmlFile=new File(htmlDocs,relFile); if (htmlFile.exists()){ try { * URL url=new URL(htmlFile.toURL().toString()+anchor); * PlatformUI.getWorkbench * ().getBrowserSupport().createBrowser(pkg+" "+module ).openURL( url ); * return true; } catch (Exception e){ HaskellUIPlugin.log( e ); } } } } * * * //http://hackage.haskell.org/packages/archive/websockets/0.1.2.3/doc/html/ * Network-WebSockets.html try { URL url=new * URL("http://hackage.haskell.org/packages/archive/" * +packageName+"/"+packageVersion+"/doc/html/"+moduleHTMLFile +anchor); * PlatformUI * .getWorkbench().getBrowserSupport().createBrowser(pkg+" "+module * ).openURL( url ); return true; } catch (Exception e){ * HaskellUIPlugin.log( e ); } */ return false; } private static boolean exists( final URL url ) { try { if( url != null ) { if( url.getProtocol().equalsIgnoreCase( "file" ) ) { return new File( url.getFile() ).exists(); } else if( url.getProtocol().equalsIgnoreCase( "http" ) ) { HttpURLConnection conn = ( HttpURLConnection )url.openConnection(); conn.setRequestMethod( "HEAD" ); conn.connect(); try { return ( HttpURLConnection.HTTP_OK == conn.getResponseCode() ); } finally { conn.disconnect(); } } } } catch( Exception e ) { HaskellUIPlugin.log( e ); } return false; } protected static void openInEditor( final IWorkbenchPage page, final Location location, final IProject p ) throws PartInitException { IWorkspace workspace = ResourcesPlugin.getWorkspace(); IWorkspaceRoot root = workspace.getRoot(); URI uri = new File( location.getFileName() ).toURI(); // new URI("file", "", // location.getFileName(), // null, null); IFile[] files = root.findFilesForLocationURI( uri, IResource.FILE ); if( files.length > 0 ) { IFile file = files[ 0 ]; // open only the first file; they should be the // same anyway if( !file.getProject().equals( p ) && files.length > 1 ) { for( IFile f: files ) { if( f.getProject().equals( p ) ) { file = f; break; } } } IEditorPart editor = IDE.openEditor( page, file, true ); ITextEditor textEditor = ( ITextEditor )editor; IDocument document = textEditor.getDocumentProvider().getDocument( editor.getEditorInput() ); selectAndReveal( textEditor, document, location ); } } public static void openInEditor( final Location location, final IProject p ) throws PartInitException { IFile file=location.getIFile( p ); IEditorPart editor = IDE.openEditor( HaskellUIPlugin.getActivePage(), file, true ); ITextEditor textEditor = ( ITextEditor )editor; IDocument document = textEditor.getDocumentProvider().getDocument( editor.getEditorInput() ); selectAndReveal( textEditor, document, location ); } protected static void selectAndReveal( final ITextEditor textEditor, final IDocument document, final Location location ) { if( document == null ) { return; } try { int startOffset = location.getStartOffset( document ); int length = location.getLength( document ); textEditor.selectAndReveal( startOffset, length ); } catch( BadLocationException ex ) { // ignore } } /** * see Haddock.Utils makeAnchorId * @param name * @return */ public static String toAnchorName(final String name){ if (name!=null){ StringBuilder sb=new StringBuilder(); for (int a=0;a<name.length();a++){ char c=name.charAt( a ); // ascii letter, digit, or . : _, ok if ((Character.isLetterOrDigit( c ) && c<128) || c=='.' || c==':' || c=='_'){ sb.append( c ); } else { // escape sb.append( "-" ); sb.append(Integer.toString(c) ); sb.append( "-" ); } } return sb.toString(); } return name; } }