package net.sf.eclipsefp.haskell.ui.internal.backend;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Stack;
import net.sf.eclipsefp.haskell.browser.BrowserPlugin;
import net.sf.eclipsefp.haskell.browser.Database;
import net.sf.eclipsefp.haskell.browser.items.HaskellPackage;
import net.sf.eclipsefp.haskell.browser.items.PackageIdentifier;
import net.sf.eclipsefp.haskell.browser.views.packages.PackagesContentProvider;
import net.sf.eclipsefp.haskell.buildwrapper.BWFacade;
import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
import net.sf.eclipsefp.haskell.buildwrapper.JobFacade;
import net.sf.eclipsefp.haskell.buildwrapper.types.BuildOptions;
import net.sf.eclipsefp.haskell.buildwrapper.types.CabalImplDetails;
import net.sf.eclipsefp.haskell.buildwrapper.types.CabalImplDetails.SandboxType;
import net.sf.eclipsefp.haskell.core.HaskellCorePlugin;
import net.sf.eclipsefp.haskell.core.builder.HaskellBuilder;
import net.sf.eclipsefp.haskell.core.cabal.CabalImplementation;
import net.sf.eclipsefp.haskell.core.cabal.CabalImplementationManager;
import net.sf.eclipsefp.haskell.core.cabal.CabalPackageVersion;
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.cabalmodel.PackageDescriptionStanza;
import net.sf.eclipsefp.haskell.core.cabalmodel.RealValuePosition;
import net.sf.eclipsefp.haskell.core.code.ModuleCreationInfo;
import net.sf.eclipsefp.haskell.core.compiler.CompilerManager;
import net.sf.eclipsefp.haskell.core.hlint.HLintBuilder;
import net.sf.eclipsefp.haskell.core.partitioned.runner.AlexRunner;
import net.sf.eclipsefp.haskell.core.partitioned.runner.HappyRunner;
import net.sf.eclipsefp.haskell.core.partitioned.runner.UuagcRunner;
import net.sf.eclipsefp.haskell.core.util.ResourceUtil;
import net.sf.eclipsefp.haskell.debug.core.internal.HaskellDebugCore;
import net.sf.eclipsefp.haskell.hlint.HLintPlugin;
import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin;
import net.sf.eclipsefp.haskell.ui.console.HaskellConsole;
import net.sf.eclipsefp.haskell.ui.internal.preferences.IPreferenceConstants;
import net.sf.eclipsefp.haskell.ui.internal.util.UITexts;
import net.sf.eclipsefp.haskell.ui.util.CabalFileChangeListener;
import net.sf.eclipsefp.haskell.util.FileUtil;
import net.sf.eclipsefp.haskell.util.ProcessRunner;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IWorkspace;
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.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IPageService;
import org.eclipse.ui.IPerspectiveDescriptor;
import org.eclipse.ui.IPerspectiveListener;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.editors.text.TextFileDocumentProvider;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.osgi.framework.Version;
/**
* Manages helper executables
*
* This class manages buildwrapper, scion-browser, etc. It gets the path from the preferences and does the initialization
*
* This works by listening for resource changes.
*/
public class BackendManager implements IResourceChangeListener {
/** Current executable path string */
private IPath buildWrapperExecutablePath;
/** Current browser executable path string */
private IPath browserExecutablePath;
/** Haskell console low water mark */
private int hConLowWater=-1;
/** Haskell console high water mark */
private int hConHighWater=-1;
private boolean browserStarted=false;
private final static String MINIMUM_BUILDWRAPPER="0.9.0";
private final static String MINIMUM_SCIONBROWSER="0.4.3";
public BackendManager() {
// The interesting stuff is done in the start() method
buildWrapperExecutablePath = null;
browserExecutablePath = null;
// hConLowWater = HaskellConsole.HASKELL_CONSOLE_LOW_WATER_MARK;
// hConHighWater = HaskellConsole.HASKELL_CONSOLE_HIGH_WATER_MARK;
}
public static String getExecutablePath(final String preference,final String exeName,final boolean strict){
IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
String exe = preferenceStore.getString( preference );
if (exe!=null && exe.length()>0){
File f=new File(exe);
if(f.exists()){
return f.getAbsolutePath();
}
}
File exeF=FileUtil.findExecutableInPath( exeName, getToolSandboxBin());
if (exeF!=null){
HaskellUIPlugin.getDefault().getPreferenceStore().setValue(preference,exeF.getAbsolutePath());
return exeF.getAbsolutePath();
}
return strict?null:exeName;
}
private void setConsoleMax(final int max){
hConHighWater=max;
//hConLowWater = preferenceStore.getInt( IPreferenceConstants.HASKELL_CONSOLE_LOW_WATER_MARK );
//if (hConLowWater == 0) {
if (hConHighWater>-1){
hConLowWater = hConHighWater/4;
} else {
hConLowWater=-1;
}
//}
}
public void start() {
IWorkspace workSpace = ResourcesPlugin.getWorkspace();
IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
// Capture preferences as currently stored:
setConsoleMax(preferenceStore.getInt( IPreferenceConstants.HASKELL_CONSOLE_HIGH_WATER_MARK ));
/*final String serverExecutable = preferenceStore.getString( IPreferenceConstants.BUILDWRAPPER_EXECUTABLE );
if (serverExecutable.length() > 0) {
buildWrapperExecutablePath = new Path(serverExecutable);
if (!buildWrapperExecutablePath.toFile().exists()){
buildWrapperExecutablePath=null;
}
}
// look in path
if (buildWrapperExecutablePath==null){
File f=FileUtil.findExecutableInPath( "buildwrapper" );
if (f!=null){
buildWrapperExecutablePath=new Path(f.getAbsolutePath());
// set preference
HaskellUIPlugin.getDefault().getPreferenceStore().setValue(IPreferenceConstants.BUILDWRAPPER_EXECUTABLE,f.getAbsolutePath());
}
}*/
String serverExecutable =getExecutablePath( IPreferenceConstants.BUILDWRAPPER_EXECUTABLE, "buildwrapper",true );
if (serverExecutable!=null){
buildWrapperExecutablePath = new Path(serverExecutable);
}
// final String browserExecutable = preferenceStore.getString( IPreferenceConstants.SCION_BROWSER_SERVER_EXECUTABLE );
// if (browserExecutable.length() > 0) {
// browserExecutablePath = new Path(browserExecutable);
// if (!browserExecutablePath.toFile().exists()){
// browserExecutablePath=null;
// }
//
// }
// // look in path
// if (browserExecutablePath==null){
// File f=FileUtil.findExecutableInPath( "scion-browser" );
// if (f!=null){
// browserExecutablePath=new Path(f.getAbsolutePath());
// // set preference
// HaskellUIPlugin.getDefault().getPreferenceStore().setValue(IPreferenceConstants.SCION_BROWSER_SERVER_EXECUTABLE,f.getAbsolutePath());
// }
// }
serverExecutable =getExecutablePath( IPreferenceConstants.SCION_BROWSER_SERVER_EXECUTABLE, "scion-browser",true );
if (serverExecutable!=null){
browserExecutablePath = new Path(serverExecutable);
}
CabalImplementation impl=CabalImplementationManager.getInstance().getDefaultCabalImplementation();
boolean sandboxed=impl!=null && impl.allowsSandbox();
boolean hasInstalledAll=false;
// can't install if no cabal executable
if ((buildWrapperExecutablePath==null || browserExecutablePath==null) && impl!=null){
boolean ignore=HaskellUIPlugin.getDefault().getPreferenceStore().getBoolean( IPreferenceConstants.IGNORE_MISSING_EXECUTABLE );
if (!ignore){
if (sandboxed){
installAll(true,true);
hasInstalledAll=true;
} else {
final Display display = HaskellUIPlugin.getStandardDisplay();
display.asyncExec( new Runnable() {
@Override
public void run() {
Shell parent = display.getActiveShell();
InstallExecutableDialog ied=new InstallExecutableDialog(parent , buildWrapperExecutablePath==null, MINIMUM_BUILDWRAPPER, browserExecutablePath==null, MINIMUM_SCIONBROWSER );
ied.open();
}
});
}
}
}
boolean doBuildWrapperSetup=true;
boolean doBrowserSetup=true;
boolean ignore=HaskellUIPlugin.getDefault().getPreferenceStore().getBoolean( IPreferenceConstants.IGNORE_TOOOLD_EXECUTABLE );
if (!hasInstalledAll && !ignore && impl!=null){
final String buildwrapperVersion=buildWrapperExecutablePath!=null?getVersion( buildWrapperExecutablePath, true ):null;
final String browserVersion=browserExecutablePath!=null?getVersion( browserExecutablePath, false ):null;
final boolean buildwrapperVersionOK=checkVersion( buildwrapperVersion, MINIMUM_BUILDWRAPPER );
final boolean browserVersionOK=checkVersion( browserVersion, MINIMUM_SCIONBROWSER );
doBuildWrapperSetup=buildwrapperVersionOK; // do not launch if too old
doBrowserSetup=browserVersionOK;// do not launch if too old
if (!buildwrapperVersionOK || !browserVersionOK){
if (sandboxed){
installAll(!buildwrapperVersionOK,!browserVersionOK);
} else {
final Display display = HaskellUIPlugin.getStandardDisplay();
display.asyncExec( new Runnable() {
@Override
public void run() {
Shell parent = display.getActiveShell();
InstallOutdatedExecutableDialog ied =
new InstallOutdatedExecutableDialog(parent,
!buildwrapperVersionOK, MINIMUM_BUILDWRAPPER, buildwrapperVersion, buildWrapperExecutablePath.toOSString(),
!browserVersionOK, MINIMUM_SCIONBROWSER, browserVersion, browserExecutablePath.toOSString() );
ied.open();
}
});
}
}
}
if (doBuildWrapperSetup){
buildWrapperFactorySetup();
}
// Set up the output logging console for the shared Browser
HaskellConsole cBrowser = new HaskellConsole( UITexts.sharedBrowserInstance_console );
BrowserPlugin.setSharedLogStream( cBrowser.createOutputWriter() );
cBrowser.setWaterMarks( hConLowWater, hConHighWater );
if (doBrowserSetup){
// ensure class is loaded
BrowserLocalDatabaseRebuildJob.class.toString();
BrowserLocalDatabaseRebuildJobListener.class.toString();
browserSetup();
}
HLintPlugin.setHlintPath( preferenceStore.getString( IPreferenceConstants.HLINT_EXECUTABLE ));
HLintBuilder.setAlwaysFull( preferenceStore.getBoolean( IPreferenceConstants.HLINT_ALWAYS_SHOW_FULL_TEXT ) );
AlexRunner.setFullPath( preferenceStore.getString( IPreferenceConstants.ALEX_EXECUTABLE ) );
HappyRunner.setFullPath( preferenceStore.getString( IPreferenceConstants.HAPPY_EXECUTABLE ) );
UuagcRunner.setFullPath( preferenceStore.getString( IPreferenceConstants.UUAGC_EXECUTABLE ) );
CabalImplDetails d=getCabalImplDetails();
BrowserPlugin.setSandboxPath(d.isSandboxed() && d.isUniqueSandbox()?d.getSandboxPath():null );
File sandbox=getToolSandbox();
if (sandbox!=null){
BrowserPlugin.setToolSandboxPath(sandbox.getAbsolutePath());
}
// Sit and listen to the preference store changes
preferenceStore.addPropertyChangeListener( new ExecutablesPropertiesListener() );
try {
workSpace.getRoot().accept( new UpdateResourceVisitor() );
} catch( CoreException ex ) {
HaskellUIPlugin.log( UITexts.scion_delta_error, ex );
}
workSpace.addResourceChangeListener( this, IResourceChangeEvent.POST_CHANGE );
workSpace.addResourceChangeListener( new FileDeletionListener(), IResourceChangeEvent.PRE_BUILD );
// POST_BUILD is similar to POST_CHANGE but the workspace tree is not locked, which is useful for some listeners
workSpace.addResourceChangeListener( new CabalFileResourceChangeListener(), IResourceChangeEvent.POST_BUILD );
workSpace.addResourceChangeListener( new ProjectDeletionListener(), IResourceChangeEvent.PRE_DELETE);
}
/**
*
*/
private void installAll(final boolean buildWrapper,final boolean scionbrowser) {
final InstallExecutableRunnable j=new InstallExecutableRunnable();
if (buildWrapper){
j.getPackages().add( new InstallExecutableRunnable.Package( "buildwrapper", IPreferenceConstants.BUILDWRAPPER_EXECUTABLE) );
}
if (scionbrowser){
j.getPackages().add( new InstallExecutableRunnable.Package( "scion-browser", IPreferenceConstants.SCION_BROWSER_SERVER_EXECUTABLE) );
}
j.getPackages().addAll (InstallExecutableDialog.getExtras());
j.setCabalUpdate( true );
j.setGlobal( false );
new Thread(j).start();
}
/**
* Handle ScionPP preference changes.
*/
public void handlePreferenceChanges() {
handlePreferenceChangesServer( );
handlePreferenceChangesBrowser( );
}
public void handlePreferenceChangesServer() {
IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
final String newServerExecutable = preferenceStore.getString( IPreferenceConstants.BUILDWRAPPER_EXECUTABLE );
IPath newServerExecutablePath = new Path(newServerExecutable);
buildWrapperExecutablePath = newServerExecutablePath;
buildWrapperFactorySetup();
}
public void handlePreferenceChangesBrowser() {
IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
//boolean newUseBuiltIn = preferenceStore.getBoolean( IPreferenceConstants.SCION_BROWSER_SERVER_BUILTIN );
final String newServerExecutable = preferenceStore.getString( IPreferenceConstants.SCION_BROWSER_SERVER_EXECUTABLE );
IPath newServerExecutablePath = new Path(newServerExecutable);
// we may not have started the browser if we're not in the perspective, in this case do nothing
if (!browserStarted){
return;
}
// Did something change?
if (newServerExecutablePath.equals( browserExecutablePath )) {
return;
}
// Yup, something changed, so shut down the instances...
BrowserPlugin.getSharedInstance().stop();
// Switch over to the null instance factory
BrowserPlugin.useNullSharedInstance();
// And update...
browserExecutablePath = newServerExecutablePath;
browserSetup();
}
/**
* setup buildwrapper
*/
private synchronized void buildWrapperFactorySetup(){
IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
boolean verbose = preferenceStore.getBoolean( IPreferenceConstants.VERBOSE_INTERACTION );
BuildWrapperPlugin.logAnswers=verbose;
int maxConfigureFailures=preferenceStore.getInt( IPreferenceConstants.MAX_CONFIGURE_FAILURES );
BuildWrapperPlugin.setMaxConfigureFailures( maxConfigureFailures );
int maxEvalTime=preferenceStore.getInt( IPreferenceConstants.MAX_EVAL_TIME );
BuildWrapperPlugin.setMaxConfigureFailures( maxEvalTime );
if ( buildWrapperExecutablePath != null && buildWrapperExecutablePath.toFile().exists() ) {
try {
/** we get the dreaded message about mismatch cabal versions if the buildwrapper cabal library is not the same as the cabal library used to build the cabal executable **/
List<String> ls=ProcessRunner.getExecutableAndCabalVersion( buildWrapperExecutablePath.toOSString(),true);
if (ls!=null && ls.size()>1){
String bwVersion=ls.get( 0 );
// in buildwrapper 0.8 and above, we use dynamic-cabal
if (CabalPackageVersion.compare( bwVersion, "0.8.0" )<0){
String cabalVersion=ls.get( 1 );
if (CabalImplementationManager.getCabalLibraryVersion()!=null && !CabalImplementationManager.getCabalLibraryVersion().toString().equals( cabalVersion )){
String msg=NLS.bind( UITexts.buildWrapperCabalVersionMismatch, new Object[]{CabalImplementationManager.getCabalLibraryVersion().toString(),cabalVersion,CabalImplementationManager.getCabalExecutable()} );
HaskellUIPlugin.log( msg, IStatus.ERROR );
}
}
}
} catch (IOException ioe){
HaskellUIPlugin.log(UITexts.error_getVersion, ioe);
}
BuildWrapperPlugin.setBwPath( buildWrapperExecutablePath.toOSString() );
} else {
BuildWrapperPlugin.setBwPath(null);
buildWrapperExecutablePath = null;
}
}
/**
* get version from the executable
* @param path the executable path
* @param wait wait for the executable to return?
* @return
*/
public static String getVersion(final IPath path,final boolean wait){
if (path!=null){
try {
return ProcessRunner.getExecutableVersion(path.toOSString(),wait);
} catch (IOException ioe){
HaskellUIPlugin.log(UITexts.error_getVersion, ioe);
}
}
return null;
}
/**
* check executable version meets minimal version
* @param path the path of the executable
* @param minimal the minimal version
* @param wait wait for the executable to return?
* @return
*/
public static boolean checkVersion(final IPath path,final String minimal,final boolean wait){
String currentVersion=getVersion(path, wait);
return checkVersion( currentVersion, minimal );
}
/**
* check executable version meets minimal version
* @param currentVersion the current version
* @param minimal the minimal version
* @return
*/
private static boolean checkVersion(final String currentVersion,final String minimal){
if (currentVersion==null) {
return false;
} else {
return CabalPackageVersion.compare( currentVersion, minimal )>=0;
}
}
private synchronized void startBrowser(){
browserStarted=true;
if ( browserExecutablePath != null && browserExecutablePath.toFile().exists() ) {
IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
boolean verbose = preferenceStore.getBoolean( IPreferenceConstants.BROWSER_VERBOSE_INTERACTION );
BrowserPlugin.changeSharedInstance( browserExecutablePath ,verbose );
final Display display = Display.getDefault();
display.asyncExec( new Runnable() {
@Override
public void run() {
rebuildBrowser();
}
} );
} else {
browserExecutablePath = null;
BrowserPlugin.useNullSharedInstance();
}
}
public void rebuildBrowser(){
Job builder = new BrowserLocalDatabaseRebuildJob(UITexts.scionBrowserRebuildingDatabase);
//builder.setRule( ResourcesPlugin.getWorkspace().getRoot() );
builder.setPriority( Job.DECORATE );
builder.schedule();
}
private boolean registerPerspectiveListener(final IWorkbenchWindow w){
if (w==null){
return false;
}
final IPageService svc=(IPageService)w.getService( IPageService.class );
if (svc==null){
return false;
}
if (svc.getActivePage()!=null && svc.getActivePage().getPerspective()!=null && svc.getActivePage().getPerspective().getId().contains( "haskell" )){
startBrowser();
return true;
}
// register the listener who's going to start browser when a Haskell perspective opens
svc.addPerspectiveListener( new IPerspectiveListener() {
@Override
public void perspectiveChanged( final IWorkbenchPage page,
final IPerspectiveDescriptor perspective, final String changeId ) {
// NOOP
}
@Override
public void perspectiveActivated( final IWorkbenchPage page,
final IPerspectiveDescriptor perspective ) {
String pid=perspective.getId();
if (pid.contains( "haskell" )){
startBrowser();
// work done, let's get out!
svc.removePerspectiveListener( this );
}
}
} );
return true;
}
private synchronized void browserSetup() {
boolean onPerspective=HaskellUIPlugin.getDefault().getPreferenceStore().getBoolean( IPreferenceConstants.BROWSER_START_ONLY_PERSPECTIVE );
if (onPerspective){
IWorkbenchWindow w=PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (!registerPerspectiveListener( w )){
PlatformUI.getWorkbench().addWindowListener( new IWindowListener() {
@Override
public void windowOpened( final IWorkbenchWindow window ) {
// NOOP
}
@Override
public void windowDeactivated( final IWorkbenchWindow window ) {
// NOOP
}
@Override
public void windowClosed( final IWorkbenchWindow window ) {
// NOOP
}
@Override
public void windowActivated( final IWorkbenchWindow window ) {
if (registerPerspectiveListener( window )){
PlatformUI.getWorkbench().removeWindowListener( this );
}
}
} );
}
} else {
startBrowser();
}
}
void loadHackageDatabase() {
final Display display = Display.getDefault();
final IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
boolean questionWasAnswered = preferenceStore.getBoolean( IPreferenceConstants.SCION_BROWSER_HACKAGE_QUESTION_ANSWERED );
if( !questionWasAnswered ) {
display.asyncExec( new Runnable() {
@Override
public void run() {
// needs ui thread
final Shell parentShell = display.getActiveShell();
boolean result = MessageDialog.openQuestion( parentShell,
UITexts.scionBrowserUseHackage_QuestionNew_title,
UITexts.scionBrowserUseHackage_QuestionNew_label );
preferenceStore.setValue( IPreferenceConstants.SCION_BROWSER_HACKAGE_QUESTION_ANSWERED, true );
preferenceStore.setValue( IPreferenceConstants.SCION_BROWSER_USE_HACKAGE, result );
}
} );
}
if (preferenceStore.getBoolean( IPreferenceConstants.SCION_BROWSER_USE_HACKAGE )) {
boolean rebuild = !questionWasAnswered || !BrowserPlugin.getHackageDatabasePath().toFile().exists();
if (!rebuild) {
/* Check time of the Hackage database */
long timeDiff = BrowserPlugin.getHackageDatabasePath().toFile().lastModified() - (new Date()).getTime();
/* We ask to rebuild if more than one week passed since last update */
boolean askRebuild = timeDiff > 7 /* days */ * 24 /* h/day */ * 3600 /* s/h */ * 1000 /* ms/s */;
if (askRebuild) {
final Stack<Boolean> response = new Stack<>();
display.asyncExec( new Runnable() {
@Override
public void run() {
// needs ui thread
final Shell parentShell = display.getActiveShell();
boolean result = MessageDialog.openQuestion( parentShell,
UITexts.scionBrowserUseHackage_QuestionUpdate_title,
UITexts.scionBrowserUseHackage_QuestionUpdate_label );
response.push(result);
}
} );
rebuild = response.pop();
}
}
/* Execute build job */
final boolean doRebuild = rebuild;
display.asyncExec( new Runnable() {
@Override
public void run() {
Job builder = new BrowserHackageDatabaseRebuildJob(
UITexts.scionBrowserRebuildingDatabase, doRebuild );
// builder.setRule( ResourcesPlugin.getWorkspace().getRoot() );
builder.setPriority( Job.DECORATE );
builder.schedule();
}
} );
} else {
preloadPrelude();
checkHoogleDataIsPresent();
}
}
void preloadPrelude() {
try {
BrowserPlugin.getSharedInstance().getDeclarations(Database.ALL, "Prelude" );
} catch (Exception e) {
// Do nothing
}
}
void checkHoogleDataIsPresent() {
final IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
String extraHooglePath = preferenceStore.getString( IPreferenceConstants.SCION_BROWSER_EXTRA_HOOGLE_PATH );
checkHoogleDataIsPresent(extraHooglePath);
}
void checkHoogleDataIsPresent(final String extraHooglePath) {
// boolean rebuild = false;
try {
// Set extra paths if needed
if (extraHooglePath != null && extraHooglePath.length() > 0) {
BrowserPlugin.getSharedInstance().setExtraHooglePath( extraHooglePath );
}
// rebuild = HoogleStatus.ERROR.equals(BrowserPlugin.getSharedInstance().checkHoogle());
} catch( Exception e ) {
// ignore
}
// if( rebuild ) {
// // There is no "fmap", we don't have a database
// final Display display = Display.getDefault();
//
// display.asyncExec( new Runnable() {
//
// @Override
// public void run() {
// // needs ui thread
// Shell parentShell = display.getActiveShell();
// if( MessageDialog
// .openQuestion(
// parentShell,
// UITexts.hoogle_dataNotPresent_title,
// UITexts.hoogle_dataNotPresent_message ) ) {
// display.asyncExec( new Runnable() {
//
// @Override
// public void run() {
// Job builder = new HoogleDownloadDataJob(
// UITexts.hoogle_downloadingData );
// // no need to stop all other operations
// //builder.setRule( ResourcesPlugin.getWorkspace().getRoot() );
// builder.setPriority( Job.DECORATE );
// builder.schedule();
// }
// } );
// }
// }
// } );
// }
Job builder = new HoogleDownloadDataJob(
UITexts.hoogle_downloadingData );
// no need to stop all other operations
//builder.setRule( ResourcesPlugin.getWorkspace().getRoot() );
builder.setPriority( Job.DECORATE );
builder.schedule();
}
/**
* Detects when a file is deleted and updates the Cabal file accordingly (remove the module).
* If the removed file is the cabal file, stop the underlying scion-server.
*
* @author JP Moresmau
*/
private class FileDeletionListener implements IResourceChangeListener {
@Override
public void resourceChanged( final IResourceChangeEvent event ) {
try {
event.getDelta().accept( new IResourceDeltaVisitor() {
@Override
public boolean visit( final IResourceDelta delta )
throws CoreException {
if( delta.getKind() == IResourceDelta.REMOVED ) {
if( delta.getResource() instanceof IFile){
IFile f = ( IFile )delta.getResource();
if (!f.getProject().isOpen()){
return false;
}
IFile cabalF = BuildWrapperPlugin.getCabalFile( f.getProject() );
if(FileUtil.hasHaskellExtension( f ) && f.getProject().isOpen()) {
// System.out.println(delta.getFullPath());
PackageDescription pd = PackageDescriptionLoader.load( cabalF );
ModuleCreationInfo info = new ModuleCreationInfo( f );
if (info.getSourceContainer()!=null){
List<PackageDescriptionStanza> lpds = pd.getStanzasBySourceDir().get( info.getSourceContainer().getProjectRelativePath().toOSString() );
if (lpds!=null && lpds.size()>0){
String qn = info.getQualifiedModuleName();
IDocumentProvider prov = new TextFileDocumentProvider();
prov.connect( cabalF );
try {
IDocument doc = prov.getDocument( cabalF );
for( PackageDescriptionStanza pds: lpds ) {
pds=pd.getSameStanza(pds);
RealValuePosition rvp = pds.removeFromPropertyList( CabalSyntax.FIELD_EXPOSED_MODULES, qn );
if (rvp!=null){
rvp.updateDocument( doc );
pd=PackageDescriptionLoader.load( doc.get() );
pds=pd.getSameStanza(pds);
}
rvp = pds.removeFromPropertyList( CabalSyntax.FIELD_OTHER_MODULES, qn );
if (rvp!=null){
rvp.updateDocument( doc );
pd=PackageDescriptionLoader.load( doc.get() );
}
}
prov.saveDocument( null, cabalF, doc, true );
} finally {
prov.disconnect( cabalF );
}
}
}
/* final ScionInstance si = ScionPlugin.getScionInstance( f );
if (si != null) {
BuildOptions buildOptions=new BuildOptions().setOutput(false).setRecompile(true);
si.buildProject( buildOptions);
}*/
JobFacade fa=BuildWrapperPlugin.getJobFacade( f.getProject() );
if (fa!=null){
fa.build( new BuildOptions().setOutput(false) );
}
return false;
} else if (f.equals( cabalF )){
stopInstance( f );
return false;
}
}
}
return true;
}
} );
} catch( CoreException ex ) {
HaskellUIPlugin.log( UITexts.scion_delta_error, ex );
}
}
}
/**
* <p>detects when a haskell project is deleted and stops the corresponding scion server</p>
*
* @author JP Moresmau
*/
public class ProjectDeletionListener implements IResourceChangeListener{
@Override
public void resourceChanged( final IResourceChangeEvent event ) {
if (event.getResource() instanceof IProject){
stopInstance( event.getResource() );
try {
List<ILaunchConfiguration> confs=HaskellDebugCore.getDefault().listHaskellLaunchConfigurations( (IProject )event.getResource());
for (ILaunchConfiguration c:confs){
c.delete();
}
} catch (CoreException ce){
HaskellUIPlugin.log( ce );
}
}
}
}
/** */
public class ExecutablesPropertiesListener implements IPropertyChangeListener {
@Override
public void propertyChange( final PropertyChangeEvent event ) {
if( event.getProperty().equals( IPreferenceConstants.BUILDWRAPPER_EXECUTABLE ) ) {
if( event.getNewValue() instanceof String ) {
buildWrapperExecutablePath=new Path((String)event.getNewValue());
buildWrapperFactorySetup();
}
} else if (event.getProperty().equals( IPreferenceConstants.SCION_BROWSER_SERVER_EXECUTABLE)) {
if (event.getNewValue() instanceof String) {
browserExecutablePath = new Path((String)event.getNewValue());
browserSetup();
}
} else if (event.getProperty().equals(IPreferenceConstants.BROWSER_VERBOSE_INTERACTION)){
if (event.getNewValue() instanceof Boolean){
BrowserPlugin.setSharedLogError((Boolean )event.getNewValue() );
}
} else if (event.getProperty().equals( IPreferenceConstants.SCION_BROWSER_EXTRA_HOOGLE_PATH)) {
if (event.getNewValue() instanceof String) {
checkHoogleDataIsPresent((String)event.getNewValue());
}
} else if (event.getProperty().equals(IPreferenceConstants.HLINT_EXECUTABLE)){
if (event.getNewValue() instanceof String || event.getNewValue()==null){
HLintPlugin.setHlintPath( (String)event.getNewValue() );
}
} else if (event.getProperty().equals(IPreferenceConstants.HLINT_ALWAYS_SHOW_FULL_TEXT)){
if (event.getNewValue() instanceof Boolean || event.getNewValue()==null){
HLintBuilder.setAlwaysFull( (Boolean )event.getNewValue() );
}
} else if (event.getProperty().equals(IPreferenceConstants.CABALDEV_EXECUTABLE)){
if (event.getNewValue() instanceof String || event.getNewValue()==null){
BuildWrapperPlugin.setCabalImplDetails( getCabalImplDetails() );
}
} else if (event.getProperty().equals(IPreferenceConstants.UNIQUE_SANDBOX)){
BuildWrapperPlugin.setCabalImplDetails( getCabalImplDetails() );
} else if (event.getProperty().equals(IPreferenceConstants.UNIQUE_SANDBOX_PATH)){
CabalImplDetails d= getCabalImplDetails();
BuildWrapperPlugin.setCabalImplDetails(d );
BrowserPlugin.setSandboxPath(d.isSandboxed() && d.isUniqueSandbox()? d.getSandboxPath():null);
// for (final IProject p:ResourceUtil.listHaskellProjects()){
// new Job("") {
//
// @Override
// protected IStatus run( IProgressMonitor monitor ) {
// p.build( IncrementalProjectBuilder.FULL_BUILD, monitor);
// return Status.OK_STATUS;
// }
// }.schedule();;
//
// }
} else if (event.getProperty().equals(IPreferenceConstants.MANAGE_DEPENDENCIES)){
BuildWrapperPlugin.setCabalImplDetails( getCabalImplDetails() );
} else if (event.getProperty().equals(IPreferenceConstants.ALEX_EXECUTABLE)){
if (event.getNewValue() instanceof String || event.getNewValue()==null){
AlexRunner.setFullPath( (String)event.getNewValue() );
}
} else if (event.getProperty().equals(IPreferenceConstants.HAPPY_EXECUTABLE)){
if (event.getNewValue() instanceof String || event.getNewValue()==null){
HappyRunner.setFullPath( (String)event.getNewValue() );
}
} else if (event.getProperty().equals(IPreferenceConstants.UUAGC_EXECUTABLE)){
if (event.getNewValue() instanceof String || event.getNewValue()==null){
UuagcRunner.setFullPath( (String)event.getNewValue() );
}
} else if (event.getProperty().equals(IPreferenceConstants.VERBOSE_INTERACTION)){
boolean verbose = ((Boolean)event.getNewValue()).booleanValue();
BuildWrapperPlugin.logAnswers=verbose;
} else if (event.getProperty().equals(IPreferenceConstants.MAX_CONFIGURE_FAILURES)){
int max = ((Integer)event.getNewValue()).intValue();
BuildWrapperPlugin.setMaxConfigureFailures( max );
} else if (event.getProperty().equals(IPreferenceConstants.MAX_EVAL_TIME)){
int max = ((Integer)event.getNewValue()).intValue();
BuildWrapperPlugin.setMaxEvalTime( max );
} else if (event.getProperty().equals( IPreferenceConstants.HASKELL_CONSOLE_HIGH_WATER_MARK )){
setConsoleMax(((Integer)event.getNewValue()).intValue());
// update all existing consoles
for (IConsole c:ConsolePlugin.getDefault().getConsoleManager().getConsoles()){
if (c instanceof HaskellConsole){
( ( HaskellConsole )c ).setWaterMarks( hConLowWater, hConHighWater );
}
}
} else if (event.getProperty().equals( IPreferenceConstants.HASKELL_CONSOLE_ACTIVATE_ON_WRITE )){
boolean activate=((Boolean)event.getNewValue()).booleanValue();
// update all existing consoles
for (IConsole c:ConsolePlugin.getDefault().getConsoleManager().getConsoles()){
if (c instanceof HaskellConsole){
( ( HaskellConsole )c ).setActivate( activate );
}
}
} else if (event.getProperty().equals( IPreferenceConstants.BROWSER_START_ONLY_PERSPECTIVE )){
// we haven't started
if (!browserStarted){
boolean startPerspective=((Boolean)event.getNewValue()).booleanValue();
// but now we want to start whatever the perspective
if (!startPerspective){
// so start explicitly
startBrowser();
}
}
}
}
}
/** */
public class UpdateResourceVisitor implements IResourceVisitor {
@Override
public boolean visit( final IResource resource ) {
return updateForResource( resource );
}
}
/** */
public class CabalFileResourceChangeListener implements IResourceChangeListener {
@Override
public void resourceChanged( final IResourceChangeEvent event ) {
try {
event.getDelta().accept( new IResourceDeltaVisitor() {
@Override
public boolean visit( final IResourceDelta delta ) {
if( delta.getKind() == IResourceDelta.CHANGED && (delta.getFlags() & IResourceDelta.CONTENT)>0) {
if( delta.getResource() instanceof IFile ) {
IFile f = ( IFile )delta.getResource();
IProject prj=f.getProject();
IFile cabalF = BuildWrapperPlugin.getCabalFile( prj );
if( f.equals( cabalF ) ) {
BWFacade bwf=BuildWrapperPlugin.getFacade( prj );
if (bwf!=null){
bwf.cabalFileChanged();
}
for (CabalFileChangeListener l:CabalFileChangeListenerManager.getListeners()){
l.cabalFileChanged( f );
}
if (!ResourcesPlugin.getWorkspace().isAutoBuilding()){
HaskellBuilder.addProjectDependencies( bwf, prj );
}
}
return false;
}
}
return true;
}
} );
} catch( CoreException ex ) {
HaskellUIPlugin.log( UITexts.scion_delta_error, ex );
}
}
}
/** Preference change listener when EclipseFP core preferences change */
// public class CorePreferencesChangeListener implements IPreferenceChangeListener {
// public void preferenceChange( final PreferenceChangeEvent event ) {
// String key = event.getKey();
// //if( ICorePreferenceNames.HS_IMPLEMENTATIONS.equals( key )
// // || ICorePreferenceNames.SELECTED_HS_IMPLEMENTATION.equals( key ) ) {
// if (CabalImplementationManager.DEFAULT_CABAL_IMPLEMENTATION.equals( key ) || ICorePreferenceNames.SELECTED_HS_IMPLEMENTATION.equals( key )){
// //if (useBuiltIn){
// handlePreferenceChanges();
// //}
// } /*else {
// HaskellUIPlugin.log("Core preference changed: ".concat( key ), IStatus.INFO);
// }*/
// }
// }
public void stop() {
ResourcesPlugin.getWorkspace().removeResourceChangeListener( this );
for (IProject p:ResourceUtil.listHaskellProjects()){
stopInstance( p );
}
// ScionPlugin.stopAllInstances();
}
/**
* Called after a resource in the workspace was changed. It finds all projects
* that were opened/closed, and starts/stops Scion instances accordingly.
*/
@Override
public void resourceChanged( final IResourceChangeEvent event ) {
try {
event.getDelta().accept( new IResourceDeltaVisitor() {
@Override
public boolean visit( final IResourceDelta delta ) {
return updateForResource( delta.getResource() );
}
} );
} catch( CoreException ex ) {
HaskellUIPlugin.log( UITexts.scion_delta_error, ex );
}
}
private boolean updateForResource( final IResource resource ) {
if( resource instanceof IProject ) {
IProject project = ( IProject ) resource;
if( ResourceUtil.hasHaskellNature( project )) {
startInstance( project );
}
if( !project.isOpen() ) {
// we cannot check the nature of closed projects, but if it's in
// instances, stop it
stopInstance( project );
}
return true; // projects can't be children of other projects, can they?
}
return true;
}
/**
* Creates a new BuildWrapper Facade instance for the given project.
*/
private synchronized void startInstance( final IProject project ) {
if (BuildWrapperPlugin.getFacade( project )==null && CabalImplementationManager.getCabalExecutable()!=null){
HaskellConsole cbw=getBWHaskellConsole( project );
Writer outStreamBw = cbw.createOutputWriter();
CabalImplDetails cid=getCabalImplDetails();
BuildWrapperPlugin.createFacade(project,cid, outStreamBw );
}
}
public List<String> getInstalledHcPkgOptions(){
CabalImplDetails det=getCabalImplDetails();
if (det.getType().equals( SandboxType.CABAL ) && det.isUniqueSandbox()){
List<String> ls=new ArrayList<>();
ls.add( det.getExecutable() );
ls.add("sandbox");
ls.add("hc-pkg");
ls.addAll(det.getInitOptions());
}
return null;
}
public static CabalImplDetails getCabalImplDetails(){
CabalImplDetails details=new CabalImplDetails();
String cabal=CabalImplementationManager.getCabalExecutable();
IPreferenceStore preferenceStore = HaskellUIPlugin.getDefault().getPreferenceStore();
String cabalDev = preferenceStore.getString( IPreferenceConstants.CABALDEV_EXECUTABLE );
details.setManageProjectDependencies( preferenceStore.getBoolean( IPreferenceConstants.MANAGE_DEPENDENCIES ) );
//String cabalDev=ScionManager.getExecutablePath( IPreferenceConstants.CABALDEV_EXECUTABLE, "cabal-dev", false );
if (cabalDev!=null && cabalDev.length()>0 && new File(cabalDev).exists()){
details.setExecutable( cabalDev);
details.setUniqueSandbox( preferenceStore.getBoolean( IPreferenceConstants.UNIQUE_SANDBOX ) );
String sd=details.isUniqueSandbox()?
preferenceStore.getString( IPreferenceConstants.UNIQUE_SANDBOX_PATH )
:BWFacade.DIST_FOLDER_CABALDEV;
details.setSandboxPath( sd );
details.getOptions().add("--sandbox="+sd);
details.getOptions().add("--with-cabal-install="+cabal);
details.setType( SandboxType.CABAL_DEV );
} else { // standard cabal
details.setExecutable(cabal);
boolean cabalSandbox=preferenceStore.getBoolean( IPreferenceConstants.CABAL_SANDBOX );
if (cabalSandbox){
details.setUniqueSandbox( preferenceStore.getBoolean( IPreferenceConstants.UNIQUE_SANDBOX ) );
String sd=details.isUniqueSandbox()?
preferenceStore.getString( IPreferenceConstants.UNIQUE_SANDBOX_PATH )
:BWFacade.DIST_FOLDER_SANDBOX;
details.setSandboxPath( sd );
if (details.isUniqueSandbox()){
details.getInitOptions().add("--sandbox");
details.getInitOptions().add(sd);
}
details.setType( SandboxType.CABAL );
} else {
details.setType( SandboxType.NONE );
}
}
addCabalInstallOptions( details.getInstallOptions() );
details.getOptions().add( "--with-ghc="+CompilerManager.getCompilerExecutable() );
//HaskellUIPlugin.getDefault().getPreferenceStore().getString( IPreferenceConstants.CABALDEV_EXECUTABLE );
return details;
}
/**
* get the home of EclipseFP, for files common to all workspaces
* @return
*/
public static File getEclipseFPHome(){
try {
File folder=new File(System.getProperty( "user.home" ),".eclipsefp");
folder.mkdirs();
return folder;
} catch (Exception e){
HaskellUIPlugin.log( e );
}
return null;
}
/**
* get the location of the sandbox for tools, or null if our Cabal doesn't support it
* @return
*/
public static File getToolSandbox(){
if (CabalImplementationManager.getInstance().getDefaultCabalImplementation()!=null && CabalImplementationManager.getInstance().getDefaultCabalImplementation().allowsSandbox()){
File home=getEclipseFPHome();
if (home!=null){
File folder=new File(home, "sandbox" );
folder.mkdirs();
return folder;
} else {
File folder=new File(HaskellUIPlugin.getDefault().getStateLocation().append( "sandbox" ).toOSString());
folder.mkdirs();
return folder;
}
}
return null;
}
public static File getToolSandboxBin(){
File f=getToolSandbox();
if (f!=null){
return new File(f,"bin");
}
return null;
}
/**
* add options for cabal install
* @param options
*/
public static void addCabalInstallOptions(final List<String> options){
//https://github.com/JPMoresmau/eclipsefp/issues/107
if (CabalImplementationManager.getCabalLibraryVersion()!=null && CabalImplementationManager.getCabalLibraryVersion().compareTo( new Version(1,16,0) )>=0){
options.add( "-j" );
}
options.add( "--with-ghc="+CompilerManager.getCompilerExecutable() );
}
/**
* Stops the Scion instance for the given project. Does not remove the
* instance from the instances map.
*/
private void stopInstance( final IProject project ) {
if( project != null) {
BuildWrapperPlugin.removeFacade( project );
//if ( ScionPlugin.terminateScionInstance( project ) ) {
IConsoleManager mgr = ConsolePlugin.getDefault().getConsoleManager();
String name = bwconsoleName( project);
for( IConsole c: mgr.getConsoles() ) {
if( c.getName().equals( name ) ) {
mgr.removeConsoles( new IConsole[] { c } );
break;
}
}
//}
}
}
private void stopInstance(final IResource res){
if (res != null && res.getProject() != null) {
stopInstance( res.getProject() );
}
}
private final String bwconsoleName ( final IProject project ) {
String projectName = project != null ? project.getName() : UITexts.noproject;
return NLS.bind( UITexts.bw_console_title, projectName );
}
private HaskellConsole getBWHaskellConsole(final IProject project) {
final String consoleName = bwconsoleName(project);
final IConsoleManager mgr = ConsolePlugin.getDefault().getConsoleManager();
for( IConsole c: mgr.getConsoles() ) {
if( c.getName().equals( consoleName ) ) {
return (HaskellConsole) c;
}
}
HaskellConsole hCon = new HaskellConsole( consoleName );
hCon.setWaterMarks( hConLowWater, hConHighWater );
return hCon;
}
private class BrowserLocalDatabaseRebuildJobListener extends JobChangeAdapter{
@Override
public void done( final IJobChangeEvent event ) {
if (event.getResult().isOK()) {
loadHackageDatabase();
} else {
// done by handling the job status
// Display.getDefault().syncExec( new Runnable() {
// @Override
// public void run() {
// MessageDialog.openError( Display.getDefault().getActiveShell(),
// UITexts.scionBrowserRebuildingDatabaseError_title,
// UITexts.scionBrowserRebuildingDatabaseError_message );
// }
// } );
}
super.done( event );
}
}
/** Specialized Job class that manages rebuilding the Browser database.
* Based in the work of B. Scott Michel.
*
* @author B. Alejandro Serrano
*/
public class BrowserLocalDatabaseRebuildJob extends Job {
IStatus status;
public BrowserLocalDatabaseRebuildJob(final String jobTitle) {
super(jobTitle);
// If the build failed, there will be some indication of why it failed.
addJobChangeListener( new BrowserLocalDatabaseRebuildJobListener());
}
@Override
protected IStatus run( final IProgressMonitor monitor ) {
monitor.beginTask( UITexts.scionBrowserRebuildingDatabase, IProgressMonitor.UNKNOWN );
status = BrowserPlugin.loadLocalDatabase( true );
monitor.done();
PackagesContentProvider.clearCache();
return status;
}
}
/** Specialized Job class that manages loading the Hackage part of Browser database.
* Based in the work of B. Scott Michel.
*
* @author B. Alejandro Serrano
*/
public class BrowserHackageDatabaseRebuildJob extends Job {
IStatus status;
boolean rebuild;
public BrowserHackageDatabaseRebuildJob(final String jobTitle, final boolean rebuild) {
super(jobTitle);
this.rebuild = rebuild;
// If the build failed, there will be some indication of why it failed.
addJobChangeListener( new JobChangeAdapter() {
@Override
public void done( final IJobChangeEvent event ) {
if (event.getResult().isOK()) {
preloadPrelude();
checkHoogleDataIsPresent();
} else {
// done by handling the job status
/*Display.getDefault().syncExec( new Runnable() {
@Override
public void run() {
MessageDialog.openError( Display.getDefault().getActiveShell(),
UITexts.scionBrowserRebuildingDatabaseError_title,
UITexts.scionBrowserRebuildingDatabaseError_message );
}
} );*/
}
super.done( event );
}
});
}
@Override
protected IStatus run( final IProgressMonitor monitor ) {
monitor.beginTask( UITexts.scionBrowserRebuildingDatabase, IProgressMonitor.UNKNOWN );
status = BrowserPlugin.loadHackageDatabase( rebuild );
monitor.done();
return status;
}
}
/** Specialized Job class that manages downloading Hoogle data.
* Based in the work of B. Scott Michel.
*
* @author B. Alejandro Serrano
*/
public class HoogleDownloadDataJob extends Job {
public HoogleDownloadDataJob( final String jobTitle ) {
super( jobTitle );
}
@Override
protected IStatus run( final IProgressMonitor monitor ) {
monitor.beginTask( UITexts.hoogle_downloadingData, IProgressMonitor.UNKNOWN );
try {
BrowserPlugin.initHoogle(false);
} catch( Exception e ) {
// Do nothing if fails
}
monitor.done();
return Status.OK_STATUS;
}
}
/**
* list haskell projects that have a library as packages
* @return the list of HaskellPackages corresponding to library projects
*/
public static List<HaskellPackage> listProjectPackages(){
List<IProject> prjs=ResourceUtil.listHaskellProjects();
List<HaskellPackage> hps=new ArrayList<>(prjs.size());
for (IProject p:prjs){
IFile f=BuildWrapperPlugin.getCabalFile( p );
try {
PackageDescription pd=PackageDescriptionLoader.load(f);
PackageDescriptionStanza pds=pd.getPackageStanza();
if (pds!=null && pd.getLibraryStanza()!=null){
String version=pds.getProperties().get( CabalSyntax.FIELD_VERSION );
String doc=pds.getProperties().get( CabalSyntax.FIELD_SYNOPSIS );
HaskellPackage hp=new HaskellPackage( doc, new PackageIdentifier( pds.getName(), version ) );
hps.add(hp);
}
} catch( CoreException ex ) {
HaskellCorePlugin.log( "listProjectPackages:", ex ); //$NON-NLS-1$
}
}
return hps;
}
}