Skip to content
Browse files

initial code for sqlite usage: open module

  • Loading branch information...
1 parent 7f8ea76 commit b307b7142f7a2098543a19f3dc05bd0d307b9cfa @JPMoresmau committed Apr 26, 2012
Showing with 889 additions and 13 deletions.
  1. +5 −5 EclipseFP-JE.tex
  2. +1 −0 docs/BW-DB.txt
  3. +2 −2 net.sf.eclipsefp.haskell-feature/feature.xml
  4. +1 −0 net.sf.eclipsefp.haskell.buildwrapper/.classpath
  5. +4 −1 net.sf.eclipsefp.haskell.buildwrapper/META-INF/MANIFEST.MF
  6. +2 −1 net.sf.eclipsefp.haskell.buildwrapper/build.properties
  7. BIN net.sf.eclipsefp.haskell.buildwrapper/lib/sqlitejdbc-v056.jar
  8. +50 −0 net.sf.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/BWFacade.java
  9. +27 −2 ....eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/BuildWrapperPlugin.java
  10. +12 −0 net.sf.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/JobFacade.java
  11. +66 −0 net.sf.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/types/Module.java
  12. +144 −0 net.sf.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/usage/UsageAPI.java
  13. +230 −0 net.sf.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/usage/UsageDB.java
  14. +77 −0 ...f.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/usage/UsageThread.java
  15. +6 −1 net.sf.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/util/BWText.java
  16. +6 −0 ...f.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/util/bwtext.properties
  17. +1 −1 net.sf.eclipsefp.haskell.ui/META-INF/MANIFEST.MF
  18. +6 −0 net.sf.eclipsefp.haskell.ui/plugin.properties
  19. +26 −0 net.sf.eclipsefp.haskell.ui/plugin.xml
  20. +212 −0 net.sf.eclipsefp.haskell.ui/src/net/sf/eclipsefp/haskell/ui/actions/OpenModuleAction.java
  21. +6 −0 net.sf.eclipsefp.haskell.ui/src/net/sf/eclipsefp/haskell/ui/internal/util/UITexts.java
  22. +5 −0 net.sf.eclipsefp.haskell.ui/src/net/sf/eclipsefp/haskell/ui/internal/util/uitexts.properties
View
10 EclipseFP-JE.tex
@@ -1,13 +1,13 @@
% EclipseFP-JE.tex
\begin{hcarentry}{EclipseFP}
\report{JP Moresmau}%11/11
-\status{stable, maintained}
-\participants{B.\ Scott Michel, Alejandro Serrano, building on code from Thiago Arrais, Leif Frenzel, Thomas ten Cate, and others}
+\status{stable, maintained and actively developed}
+\participants{JP Moresmau, building on code from B.\ Scott Michel, Alejandro Serrano, Thiago Arrais, Leif Frenzel, Thomas ten Cate, and others}
\makeheader
EclipseFP is a set of Eclipse plugins to allow working on Haskell code projects.
-It features Cabal integration (.cabal file editor, uses Cabal settings for compilation), and GHC integration. Compilation is done via the GHC API, syntax coloring uses the GHC Lexer. Other standard Eclipse features like code outline, folding, and quick fixes for common errors are also provided. EclipseFP also allows launching GHCi sessions on any module including extensive debugging facilities. It uses Scion to bridge between the Java code for Eclipse and the Haskell APIs.
-The source code is fully open source (Eclipse License) and anyone can contribute. Current version is 2.1.0, released in September 2011 and supporting GHC 6.12 and 7.0, and more versions with additional features are planned. Feedback on what is needed is welcome! The website has information on downloading binary releases and getting a copy of the source code. Support and bug tracking is handled through Sourceforge forums.
+It features Cabal integration (.cabal file editor, uses Cabal settings for compilation, allows the user to install Cabal packages from within the IDE), and GHC integration. Compilation is done via the GHC API, syntax coloring uses the GHC Lexer. Other standard Eclipse features like code outline, folding, and quick fixes for common errors are also provided. HLint suggestions can be applied in one click. EclipseFP also allows launching GHCi sessions on any module including extensive debugging facilities. It uses BuildWrapper to bridge between the Java code for Eclipse and the Haskell APIs. It also provides a full package and module browser to navigate the Haskell packages installed on your system, integrated with Hackage.
+The source code is fully open source (Eclipse License) on github and anyone can contribute. Current version is 2.2.4, released in March 2012 and supporting GHC 7.0 and above, and more versions with additional features are planned and actively worked on. Feedback on what is needed is welcome! The website has information on downloading binary releases and getting a copy of the source code. Support and bug tracking is handled through Sourceforge forums.
%**<img width=500 src="./eclipsefp-screenshot1.jpg">
%*ignore
@@ -17,5 +17,5 @@
%*endignore
\FurtherReading
-\url{http://eclipsefp.sourceforge.net/}
+\url{http://eclipsefp.github.com/}
\end{hcarentry}
View
1 docs/BW-DB.txt
@@ -73,3 +73,4 @@ module id + name -> unique symbol id + definition (for local symbols)
symbol id -> usages (file in Eclipse format (project + path), line span)
module id -> usages (import)
+
View
4 net.sf.eclipsefp.haskell-feature/feature.xml
@@ -93,7 +93,7 @@ available at http://www.eclipse.org/legal/epl-v10.html.
id="net.sf.eclipsefp.haskell.ui"
download-size="0"
install-size="0"
- version="2.2.4"
+ version="2.2.5"
unpack="false"/>
<plugin
@@ -163,7 +163,7 @@ available at http://www.eclipse.org/legal/epl-v10.html.
id="net.sf.eclipsefp.haskell.buildwrapper"
download-size="0"
install-size="0"
- version="2.2.4"
+ version="2.2.5"
unpack="false"/>
</feature>
View
1 net.sf.eclipsefp.haskell.buildwrapper/.classpath
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
+ <classpathentry exported="true" kind="lib" path="lib/sqlitejdbc-v056.jar"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
View
5 net.sf.eclipsefp.haskell.buildwrapper/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %bundleName
Bundle-SymbolicName: net.sf.eclipsefp.haskell.buildwrapper;singleton:=true
-Bundle-Version: 2.2.4
+Bundle-Version: 2.2.5
Bundle-Activator: net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin
Bundle-Vendor: %bundleVendor
Bundle-Localization: plugin
@@ -17,4 +17,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-ActivationPolicy: lazy
Export-Package: net.sf.eclipsefp.haskell.buildwrapper,
net.sf.eclipsefp.haskell.buildwrapper.types,
+ net.sf.eclipsefp.haskell.buildwrapper.usage,
net.sf.eclipsefp.haskell.buildwrapper.util
+Bundle-ClassPath: lib/sqlitejdbc-v056.jar,
+ .
View
3 net.sf.eclipsefp.haskell.buildwrapper/build.properties
@@ -2,4 +2,5 @@ source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.,\
- plugin.properties
+ plugin.properties,\
+ lib/sqlitejdbc-v056.jar
View
BIN net.sf.eclipsefp.haskell.buildwrapper/lib/sqlitejdbc-v056.jar
Binary file not shown.
View
50 ...sf.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/BWFacade.java
@@ -277,6 +277,52 @@ public void synchronize(boolean force){
}
}
}
+ JSONArray dels=allPaths.optJSONArray(1);
+ if (dels!=null){
+ for (int a=0;a<dels.length();a++){
+ try {
+ String p=dels.getString(a);
+ BuildWrapperPlugin.getDefault().getUsageAPI().removeFile(getProject(), p);
+ } catch (JSONException je){
+ BuildWrapperPlugin.logError(BWText.process_parse_component_error, je);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void generateUsage(Component c){
+ LinkedList<String> command=new LinkedList<String>();
+ command.add("generateusage");
+ command.add("--cabalcomponent="+serializeComponent(c));
+ JSONArray arr=run(command,ARRAY);
+ if (arr!=null){
+ if(arr.length()>1){
+
+ JSONArray notes=arr.optJSONArray(1);
+ parseNotes(notes);
+ }
+ JSONArray allPaths=arr.optJSONArray(0);
+ if (allPaths!=null){
+ if (allPaths.length()>0){
+ IFolder fldr=getProject().getFolder(BWFacade.DIST_FOLDER);
+ if (fldr!=null && fldr.exists()){
+ try {
+ fldr.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor());
+ } catch (CoreException ce){
+ BuildWrapperPlugin.logError(BWText.error_refreshLocal, ce);
+ }
+ }
+ }
+ for (int a=0;a<allPaths.length();a++){
+ try {
+ String p=allPaths.getString(a);
+ BuildWrapperPlugin.getDefault().getUsageAPI().addFile(getProject(),c, p);
+ } catch (JSONException je){
+ BuildWrapperPlugin.logError(BWText.error_parsing_usage_path, je);
+ }
+ }
}
}
}
@@ -639,6 +685,10 @@ private Component parseComponent(JSONObject obj){
return null;
}
+ private String serializeComponent(Component c) {
+ return ComponentType.LIBRARY.equals(c.getType())?"":c.getName();
+ }
+
private CabalPackage parsePackage(JSONObject obj){
try {
String name=obj.getString("n");
View
29 ...fp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/BuildWrapperPlugin.java
@@ -5,6 +5,8 @@
import java.util.HashMap;
import java.util.Map;
+import net.sf.eclipsefp.haskell.buildwrapper.usage.UsageAPI;
+import net.sf.eclipsefp.haskell.buildwrapper.usage.UsageThread;
import net.sf.eclipsefp.haskell.buildwrapper.util.BWText;
import net.sf.eclipsefp.haskell.util.FileUtil;
@@ -15,7 +17,6 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
@@ -46,6 +47,9 @@
public static boolean logAnswers=false;
+ private UsageAPI usageAPI;
+ private UsageThread usageThread=new UsageThread();
+
/**
* The constructor
*/
@@ -59,7 +63,8 @@ public BuildWrapperPlugin() {
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
-
+ usageAPI=new UsageAPI();
+ usageThread.start();
}
/*
@@ -68,6 +73,12 @@ public void start(BundleContext context) throws Exception {
*/
public void stop(BundleContext context) throws Exception {
plugin = null;
+ usageThread.setShouldStop();
+ usageThread.interrupt();
+ // wait for all pending writes for 10 secs
+ usageThread.join(10000);
+ // then close api and db
+ usageAPI.close();
super.stop(context);
}
@@ -80,6 +91,20 @@ public static BuildWrapperPlugin getDefault() {
return plugin;
}
+ /**
+ * @return the usageAPI
+ */
+ public UsageAPI getUsageAPI() {
+ return usageAPI;
+ }
+
+ /**
+ * @return the usageThread
+ */
+ public UsageThread getUsageThread() {
+ return usageThread;
+ }
+
public static BWFacade createFacade(IProject p,String cabalPath,Writer outStream){
IFile cf=getCabalFile(p);
View
12 ...f.eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/JobFacade.java
@@ -136,10 +136,22 @@ protected IStatus run(IProgressMonitor monitor) {
}
return Status.OK_STATUS;
}
+
};
//
buildJob.setRule( getProject().getWorkspace().getRoot() );
buildJob.setPriority(Job.BUILD);
+ buildJob.addJobChangeListener(new JobChangeAdapter(){
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
+ */
+ @Override
+ public void done(IJobChangeEvent event) {
+ if (event.getResult().isOK()){
+ BuildWrapperPlugin.getDefault().getUsageThread().addProject(getProject());
+ }
+ }
+ });
return buildJob;
}
View
66 ...clipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/types/Module.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2012 by JP Moresmau
+ * This code is made available under the terms of the Eclipse Public License,
+ * version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html
+ */
+package net.sf.eclipsefp.haskell.buildwrapper.types;
+
+/**
+ * @author JP Moresmau
+ *
+ */
+public class Module {
+ private String moduleName;
+ private String packageName;
+ private long moduleID;
+ private Long fileID;
+
+
+ public Module(long moduleID, String packageName, String moduleName,Long fileID) {
+ super();
+ this.moduleID = moduleID;
+ this.packageName = packageName;
+ this.moduleName = moduleName;
+ this.fileID=fileID;
+ }
+
+ public String getModuleName() {
+ return moduleName;
+ }
+
+ public String getPackageName() {
+ return packageName;
+ }
+
+ public long getModuleID() {
+ return moduleID;
+ }
+ public Long getFileID() {
+ return fileID;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (int) (moduleID ^ (moduleID >>> 32));
+ return result;
+ }
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ Module other = (Module) obj;
+ if (moduleID != other.moduleID)
+ return false;
+ return true;
+ }
+
+
+
+
+}
View
144 ...ipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/usage/UsageAPI.java
@@ -0,0 +1,144 @@
+/**
+ * Copyright (c) 2012 by JP Moresmau
+ * This code is made available under the terms of the Eclipse Public License,
+ * version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html
+ */
+package net.sf.eclipsefp.haskell.buildwrapper.usage;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.eclipsefp.haskell.buildwrapper.BWFacade;
+import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
+import net.sf.eclipsefp.haskell.buildwrapper.types.Component;
+import net.sf.eclipsefp.haskell.buildwrapper.types.Module;
+import net.sf.eclipsefp.haskell.buildwrapper.util.BWText;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.IStatus;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONTokener;
+
+/**
+ * @author JP Moresmau
+ *
+ */
+public class UsageAPI {
+ private UsageDB db=new UsageDB();
+
+ public void close(){
+ db.close();
+ }
+
+ public void removeFile(IFile f){
+ removeFile(f.getProject(),f.getProjectRelativePath().toOSString());
+ }
+
+ public void removeFile(IProject p, String relPath){
+ BuildWrapperPlugin.log(IStatus.INFO, "Removing "+p.getName()+"/"+relPath, null);
+ }
+
+ public void addFile(Component c,IFile f){
+ addFile(f.getProject(),c,f.getProjectRelativePath().toOSString());
+ }
+
+ public void addFile(IProject p,Component c, String relPath){
+ IFile f=p.getFile(relPath);
+ if (f!=null && f.exists()){
+ BuildWrapperPlugin.log(IStatus.INFO, "Adding "+p.getName()+"/"+f.getProjectRelativePath().toPortableString(), null);
+ IFile uf=getUsageFile(p, relPath);
+ if (uf!=null){
+ BuildWrapperPlugin.log(IStatus.INFO, "Adding "+p.getName()+"/"+f.getProjectRelativePath().toPortableString()+": usage file found", null);
+ JSONArray arr=parseUsageFile(uf);
+ if (arr!=null){
+ try {
+ String pkg=formatPackage(arr.getString(0));
+ String module=formatModule(c, arr.getString(1));
+ long fileID=db.getFileID(f);
+ long moduleID=db.getModuleID(pkg, module, fileID);
+ db.commit();
+ } catch (SQLException sqle){
+ BuildWrapperPlugin.logError(BWText.error_db, sqle);
+ } catch (JSONException je){
+ BuildWrapperPlugin.logError(BWText.error_parsing_usage_file, je);
+ }
+ }
+ }
+ }
+ }
+
+ public IFile getFile(Long fileid){
+ try {
+ return db.getFile(fileid);
+ } catch (SQLException sqle){
+ BuildWrapperPlugin.logError(BWText.error_db, sqle);
+ }
+ return null;
+ }
+
+ public List<Module> listLocalModules() {
+ try {
+ return db.listLocalModules();
+ } catch (SQLException sqle){
+ BuildWrapperPlugin.logError(BWText.error_db, sqle);
+ }
+ return new ArrayList<Module>();
+ }
+
+ private JSONArray parseUsageFile(IFile uf){
+ try {
+ InputStream is=uf.getContents();
+ try {
+ BufferedReader br = new BufferedReader(new InputStreamReader( is,uf.getCharset() ));
+ try {
+ return new JSONArray(new JSONTokener(br));
+ } finally {
+ br.close();
+ }
+ } finally {
+ is.close();
+ }
+ } catch (Exception e){
+ BuildWrapperPlugin.logError(BWText.error_parsing_usage_file, e);
+ }
+ return null;
+ }
+
+ private IFile getUsageFile(IProject p, String relPath){
+ IFolder fldr=p.getFolder(BWFacade.DIST_FOLDER);
+ if (fldr!=null && fldr.exists()){
+ IFile f=fldr.getFile(relPath);
+ IFile f2=((IFolder)f.getParent()).getFile("."+f.getName()+".bwusage");
+ if (f2.exists()){
+ return f2;
+ }
+ }
+ return null;
+ }
+
+ private String formatPackage(String pkg){
+ String ret=pkg;
+ int ix=pkg.lastIndexOf('-');
+ if (ix>-1 && ix<pkg.length()-1){
+ if (Character.isDigit(pkg.charAt(ix+1))){
+ ret=pkg.substring(0,ix);
+ }
+ }
+ return ret;
+ }
+
+ private String formatModule(Component c,String module){
+ String ret=module;
+ if("Main".equals(module)){
+ ret=module+" "+c.getName();
+ }
+ return ret;
+ }
+}
View
230 ...lipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/usage/UsageDB.java
@@ -0,0 +1,230 @@
+/**
+ * Copyright (c) 2012 by JP Moresmau
+ * This code is made available under the terms of the Eclipse Public License,
+ * version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html
+ */
+package net.sf.eclipsefp.haskell.buildwrapper.usage;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IPath;
+
+import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
+import net.sf.eclipsefp.haskell.buildwrapper.types.Module;
+import net.sf.eclipsefp.haskell.buildwrapper.util.BWText;
+
+/**
+ * @author JP Moresmau
+ *
+ */
+public class UsageDB {
+ private Connection conn;
+
+
+ public UsageDB(){
+ IPath p=BuildWrapperPlugin.getDefault().getStateLocation().append("usage.db");
+ File f=p.toFile();
+ f.getParentFile().mkdirs();
+ try {
+ Class.forName("org.sqlite.JDBC");
+ conn =
+ DriverManager.getConnection("jdbc:sqlite:"+f.getAbsolutePath());
+ conn.setAutoCommit(false);
+
+ setup();
+ } catch (Exception e){
+ BuildWrapperPlugin.logError(BWText.error_setup_db, e);
+ }
+ }
+
+ public void close(){
+ if (conn!=null){
+ try {
+ conn.close();
+ } catch (SQLException sqle){
+ BuildWrapperPlugin.logError(BWText.error_db, sqle);
+ }
+ conn=null;
+ }
+ }
+
+ public void commit() throws SQLException{
+ checkConnection();
+ conn.commit();
+ }
+
+ public boolean isValid(){
+ return conn!=null;
+ }
+
+ protected void checkConnection() throws SQLException{
+ if (conn==null){
+ throw new SQLException(BWText.error_no_db);
+ }
+ }
+
+ protected void setup() throws SQLException{
+ checkConnection();
+ Statement s=conn.createStatement();
+ try {
+ s.execute("create table if not exists files (fileid INTEGER PRIMARY KEY ASC,project TEXT not null, name TEXT not null)");
+ s.execute("create unique index if not exists filenames on files (project, name)");
+
+ s.execute("create table if not exists modules (moduleid INTEGER PRIMARY KEY ASC,package TEXT not null, module TEXT not null,fileid INTEGER,foreign key (fileid) references files(fileid) on delete set null)");
+ s.execute("create unique index if not exists modulenames on modules (package,module)");
+
+ //s.execute("create index if not exists modulefiles on modules (fileid)");
+ } finally {
+ s.close();
+ }
+ conn.commit();
+ }
+
+ public long getFileID(IFile f) throws SQLException{
+ checkConnection();
+ PreparedStatement ps=conn.prepareStatement("select fileid from files where project=? and name=?");
+ Long fileID=null;
+ try {
+ ps.setString(1, f.getProject().getName());
+ ps.setString(2, f.getProjectRelativePath().toPortableString());
+ ResultSet rs=ps.executeQuery();
+ try {
+ if (rs.next()){
+ fileID=rs.getLong(1);
+ }
+ } finally {
+ rs.close();
+ }
+
+ } finally {
+ ps.close();
+ }
+ if (fileID==null){
+ ps=conn.prepareStatement("insert into files (project,name) values(?,?)");
+ try {
+ ps.setString(1, f.getProject().getName());
+ ps.setString(2, f.getProjectRelativePath().toPortableString());
+ ps.execute();
+ ResultSet rs=ps.getGeneratedKeys();
+ try {
+ rs.next();
+ fileID=rs.getLong(1);
+ } finally {
+ rs.close();
+ }
+ } finally {
+ ps.close();
+ }
+ }
+ return fileID;
+ }
+
+ public long getModuleID(String pkg,String module,Long fileID) throws SQLException {
+ checkConnection();
+ PreparedStatement ps=conn.prepareStatement("select moduleid from modules where package=? and module=?");
+ Long moduleID=null;
+ try {
+ ps.setString(1, pkg);
+ ps.setString(2, module);
+ ResultSet rs=ps.executeQuery();
+ try {
+ if (rs.next()){
+ moduleID=rs.getLong(1);
+ }
+ } finally {
+ rs.close();
+ }
+
+ } finally {
+ ps.close();
+ }
+ if (moduleID==null){
+ ps=conn.prepareStatement("insert into modules (package,module,fileid) values(?,?,?)");
+ try {
+ ps.setString(1, pkg);
+ ps.setString(2, module);
+ if (fileID!=null){
+ ps.setLong(3, fileID);
+ } else {
+ ps.setNull(3, Types.NUMERIC);
+ }
+ ps.execute();
+ ResultSet rs=ps.getGeneratedKeys();
+ try {
+ rs.next();
+ moduleID=rs.getLong(1);
+ } finally {
+ rs.close();
+ }
+ } finally {
+ ps.close();
+ }
+ }
+ return moduleID;
+ }
+
+ public List<Module> listLocalModules() throws SQLException {
+ checkConnection();
+ List<Module> ret=new ArrayList<Module>();
+ PreparedStatement ps=conn.prepareStatement("select moduleid,package,module,fileid from modules where fileid is not null");
+ try {
+ ResultSet rs=ps.executeQuery();
+ try {
+ while (rs.next()){
+ long moduleID=rs.getLong(1);
+ String packageName=rs.getString(2);
+ String moduleName=rs.getString(3);
+ long fileid=rs.getLong(4);
+ Module mod=new Module(moduleID,packageName,moduleName,fileid);
+ ret.add(mod);
+ }
+ } finally {
+ rs.close();
+ }
+ } finally {
+ ps.close();
+ }
+
+ return ret;
+ }
+
+ public IFile getFile(Long fileid) throws SQLException {
+ if (fileid==null){
+ return null;
+ }
+ checkConnection();
+ PreparedStatement ps=conn.prepareStatement("select project,name from files where fileid =?");
+ try {
+ ps.setLong(1, fileid);
+ ResultSet rs=ps.executeQuery();
+ try {
+ if (rs.next()){
+
+ String project=rs.getString(1);
+ String name=rs.getString(2);
+ IProject p=ResourcesPlugin.getWorkspace().getRoot().getProject(project);
+ if (p!=null){
+ return p.getFile(name);
+ }
+ }
+ } finally {
+ rs.close();
+ }
+ } finally {
+ ps.close();
+ }
+ return null;
+ }
+}
View
77 ...efp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/usage/UsageThread.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright (c) 2012 by JP Moresmau
+ * This code is made available under the terms of the Eclipse Public License,
+ * version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html
+ */
+package net.sf.eclipsefp.haskell.buildwrapper.usage;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
+import net.sf.eclipsefp.haskell.buildwrapper.types.Component;
+
+import org.eclipse.core.resources.IProject;
+
+/**
+ * @author JP Moresmau
+ *
+ */
+public class UsageThread extends Thread {
+ private boolean shouldStop=false;
+
+ private LinkedList<IProject> ps=new LinkedList<IProject>();
+
+ public UsageThread() {
+ super("UsageThread");
+ setDaemon(true);
+ }
+
+ public void run(){
+ while (!shouldStop){
+ synchronized (ps) {
+ try{
+ ps.wait();
+ } catch (InterruptedException ie){
+ // noop
+ }
+ if (!shouldStop){
+ IProject p=getNext();
+
+ while (p!=null){
+
+ List<Component> cs=BuildWrapperPlugin.getFacade(p).getComponents();
+ for (Component c:cs){
+ BuildWrapperPlugin.getFacade(p).generateUsage(c);
+ }
+ p=getNext();
+ }
+ }
+ }
+ }
+
+ }
+
+ private IProject getNext(){
+ synchronized (ps) {
+ if (!ps.isEmpty()){
+ return ps.removeFirst();
+ }
+ }
+ return null;
+ }
+
+ public void addProject(IProject p){
+ synchronized (ps) {
+ ps.addLast(p);
+ ps.notifyAll();
+ }
+ }
+
+ /**
+ * @param shouldStop the shouldStop to set
+ */
+ public void setShouldStop() {
+ this.shouldStop = true;
+ }
+}
View
7 ...eclipsefp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/util/BWText.java
@@ -34,7 +34,12 @@
public static String error_derived;
public static String error_refreshLocal;
public static String error_clean;
-
+ public static String error_parsing_usage_path;
+ public static String error_parsing_usage_file;
+ public static String error_setup_db;
+ public static String error_db;
+ public static String error_no_db;
+
public static String outline_job_name;
public static String editor_job_name;
public static String occurrences_job_name;
View
6 ...efp.haskell.buildwrapper/src/net/sf/eclipsefp/haskell/buildwrapper/util/bwtext.properties
@@ -17,6 +17,12 @@ error_noexe = No buildwrapper executable defined
error_derived = Cannot set derived status on build-wrapper folder
error_refreshLocal = Error refreshing resource
error_clean = Error cleaning project
+error_parsing_usage_path = Error parsing usage paths
+error_parsing_usage_file = Error parsing usage file
+
+error_setup_db = Error setting up SQLite DB
+error_db = SQLite DB threw an exception
+error_no_db = SQLite DB not available
job_build=Building project {0}
job_synchronize=Synchronizing project {0}
View
2 net.sf.eclipsefp.haskell.ui/META-INF/MANIFEST.MF
@@ -1,7 +1,7 @@
Manifest-Version: 1.0
Bundle-Name: %bundleName
Bundle-SymbolicName: net.sf.eclipsefp.haskell.ui;singleton:=true
-Bundle-Version: 2.2.4
+Bundle-Version: 2.2.5
Bundle-Activator: net.sf.eclipsefp.haskell.ui.HaskellUIPlugin
Bundle-Vendor: %bundleVendor
Bundle-Localization: plugin
View
6 net.sf.eclipsefp.haskell.ui/plugin.properties
@@ -207,3 +207,9 @@ contenttype_julius_name = Julius JavaScript Template
contenttype_lucius_name = Lucius CSS Template
sorter_name=Haskell Content Sorter
+
+haskellNavigateCategory_desc = Haskell navigate category
+haskellNavigateCategory_name = Haskell navigate commands
+NavigationActionSet.label=Haskell Navigation
+OpenModuleAction.label=Open Haskell Module...
+OpenModuleAction.tooltip=Open Local Haskell Module
View
26 net.sf.eclipsefp.haskell.ui/plugin.xml
@@ -432,6 +432,16 @@
categoryId="net.sf.eclipsefp.haskell.ui.category.source"
name="%HaddocDocumentBlockFollow.name"
id="net.sf.eclipsefp.haskell.ui.editor.actions.haddock_block_following"/>
+ <category
+ description="%haskellNavigateCategory_desc"
+ name="%haskellNavigateCategory_name"
+ id="net.sf.eclipsefp.haskell.ui.category.navigate"/>
+ <command
+ description="%OpenModuleAction.tooltip"
+ categoryId="net.sf.eclipsefp.haskell.ui.category.navigate"
+ name="%OpenModuleAction.label"
+ id="net.sf.eclipsefp.haskell.ui.OpenModuleActionCommand"/>
+
</extension>
<extension
point="org.eclipse.ui.menus">
@@ -517,6 +527,9 @@
<key schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
commandId="net.sf.eclipsefp.haskell.ui.editor.actions.firstChar"
sequence="Home"/>
+ <key schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"
+ commandId="net.sf.eclipsefp.haskell.ui.OpenModuleActionCommand"
+ sequence="Ctrl+Shift+M"/>
</extension>
<extension
point="org.eclipse.ui.handlers">
@@ -915,6 +928,19 @@
menubarPath="net.sf.eclipsefp.haskell.ui.sourceMenu/editGroup"
id="net.sf.eclipsefp.haskell.ui.actions.ShiftRight">
</action>
+
+ </actionSet>
+ <actionSet id="net.sf.eclipsefp.haskell.ui.navigation" label="%NavigationActionSet.label" visible="true">
+ <action
+ class="net.sf.eclipsefp.haskell.ui.actions.OpenModuleAction"
+ icon="icons/obj16/module.gif"
+ id="net.sf.eclipsefp.haskell.ui.actions.OpenModuleAction"
+ label="%OpenModuleAction.label"
+ menubarPath="navigate/open.ext3"
+ style="push"
+ toolbarPath="org.eclipse.search.searchActionSet/Search"
+ tooltip="%OpenModuleAction.tooltip"
+ definitionId="net.sf.eclipsefp.haskell.ui.OpenModuleActionCommand"/>
</actionSet>
</extension>
<extension
View
212 net.sf.eclipsefp.haskell.ui/src/net/sf/eclipsefp/haskell/ui/actions/OpenModuleAction.java
@@ -0,0 +1,212 @@
+/**
+ * Copyright (c) 2012 by JP Moresmau
+ * This code is made available under the terms of the Eclipse Public License,
+ * version 1.0 (EPL). See http://www.eclipse.org/legal/epl-v10.html
+ */
+package net.sf.eclipsefp.haskell.ui.actions;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import net.sf.eclipsefp.haskell.buildwrapper.BuildWrapperPlugin;
+import net.sf.eclipsefp.haskell.buildwrapper.types.Module;
+import net.sf.eclipsefp.haskell.ui.HaskellUIPlugin;
+import net.sf.eclipsefp.haskell.ui.internal.util.UITexts;
+import net.sf.eclipsefp.haskell.ui.util.HaskellUIImages;
+import net.sf.eclipsefp.haskell.ui.util.IImageNames;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.jface.window.Window;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Table;
+import org.eclipse.swt.widgets.TableItem;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.dialogs.ElementListSelectionDialog;
+import org.eclipse.ui.dialogs.FilteredList;
+import org.eclipse.ui.ide.IDE;
+
+
+/**
+ * @author JP Moresmau
+ *
+ */
+public class OpenModuleAction extends Action implements
+ IWorkbenchWindowActionDelegate {
+
+ private Shell shell;
+ private IWorkbenchPage page;
+ /**
+ *
+ */
+ public OpenModuleAction() {
+ super(Platform.getResourceBundle( HaskellUIPlugin.getDefault().getBundle()).getString( "OpenModuleAction.label" ));
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction)
+ */
+ @Override
+ public void run( final IAction arg0 ) {
+
+ ModuleListSelectionDialog elsd=new ModuleListSelectionDialog( shell);
+
+ //elsd.setFilter( filter )
+ List<Module> mods=BuildWrapperPlugin.getDefault().getUsageAPI().listLocalModules();
+
+ elsd.setElements( mods.toArray( new Module[mods.size()] ) );
+ int code=elsd.open();
+ if (Window.OK==code){
+ Object[] res=elsd.getResult();
+ for (Object o:res){
+ if (o instanceof Module){
+ Module mod=(Module)o;
+ Long fileid=mod.getFileID();
+ if (fileid!=null){
+ IFile efile=BuildWrapperPlugin.getDefault().getUsageAPI().getFile( fileid );
+ if (efile!=null){
+ try {
+ IDE.openEditor( page, efile);
+ } catch (PartInitException pie){
+ HaskellUIPlugin.log( pie );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection)
+ */
+ @Override
+ public void selectionChanged( final IAction arg0, final ISelection arg1 ) {
+ //NOOP
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#dispose()
+ */
+ @Override
+ public void dispose() {
+ shell=null;
+ page=null;
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.IWorkbenchWindowActionDelegate#init(org.eclipse.ui.IWorkbenchWindow)
+ */
+ @Override
+ public void init( final IWorkbenchWindow arg0 ) {
+ shell=arg0.getShell();
+ page=arg0.getActivePage();
+ }
+
+ private static class ModuleListSelectionDialog extends ElementListSelectionDialog {
+ /**
+ * table items that have the selection focus (show package name)
+ */
+ private final Map<Integer,TableItem> lastTis=new HashMap<Integer,TableItem>();
+
+ public ModuleListSelectionDialog( final Shell parent ) {
+ super( parent, new ModuleLabelProvider() );
+ setTitle( UITexts.OpenModuleAction_title );
+ setMessage( UITexts.OpenModuleAction_text );
+ setEmptyListMessage( UITexts.OpenModuleAction_nomodule );
+ setEmptySelectionMessage( UITexts.OpenModuleAction_nomodulesel );
+ setMultipleSelection( true );
+ setStatusLineAboveButtons( true );
+ setAllowDuplicates( true );
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.dialogs.AbstractElementListSelectionDialog#createFilteredList(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected FilteredList createFilteredList( final Composite parent ) {
+ FilteredList fl=super.createFilteredList( parent );
+ fl.addSelectionListener( new SelectionAdapter() {
+ @Override
+ public void widgetSelected(final SelectionEvent e) {
+ TableItem[] tis=((Table)e.widget).getItems();
+ Object[] obj=getSelectedElements();
+ int[] sels=getSelectionIndices();
+ /**
+ * new focused table items
+ */
+ Map<Integer,TableItem> newTis=new HashMap<Integer, TableItem>();
+ for (int a=0;a<sels.length;a++){
+ int idx=sels[a];
+ Module mod=(Module)obj[a];
+
+ TableItem ti=lastTis.remove( idx );
+ // change text
+ if (ti==null){
+ tis[idx].setText( mod.getModuleName() +" - "+ mod.getPackageName() );
+ }
+ newTis.put( idx, tis[idx] );
+ }
+ // reset text to unselected
+ for (Integer i:lastTis.keySet()){
+ TableItem ti=lastTis.get(i);
+ String s=ti.getText();
+ int ix=s.indexOf( " - " );
+ if (ix>-1){
+ ti.setText( s.substring( 0,ix ) );
+ }
+ }
+ lastTis.clear();
+ // new map
+ lastTis.putAll( newTis );
+
+ }
+ } );
+ return fl;
+ }
+
+ }
+
+ private static class ModuleLabelProvider extends LabelProvider{
+ private final Image img;
+ /**
+ *
+ */
+ public ModuleLabelProvider() {
+ img=HaskellUIImages.getImage( IImageNames.MODULE );
+ }
+
+ @Override
+ public String getText(final Object element) {
+ if (element instanceof Module){
+ Module mod=(Module)element;
+ return mod.getModuleName();
+ }
+ return super.getText( element );
+ }
+
+ @Override
+ public org.eclipse.swt.graphics.Image getImage(final Object element) {
+ if (element instanceof Module){
+ return img;
+ }
+ return super.getImage( element );
+
+ }
+
+
+
+ }
+}
View
6 net.sf.eclipsefp.haskell.ui/src/net/sf/eclipsefp/haskell/ui/internal/util/UITexts.java
@@ -713,6 +713,12 @@
public static String job_syntax_coloring;
+ public static String OpenModuleAction_title;
+ public static String OpenModuleAction_matching;
+ public static String OpenModuleAction_text;
+ public static String OpenModuleAction_nomodule;
+ public static String OpenModuleAction_nomodulesel;
+
private static final String BUNDLE_NAME = UITexts.class.getPackage()
.getName() + ".uitexts"; //$NON-NLS-1$
View
5 net.sf.eclipsefp.haskell.ui/src/net/sf/eclipsefp/haskell/ui/internal/util/uitexts.properties
@@ -712,3 +712,8 @@ generic_remove=Remove
job_syntax_coloring=Syntax Coloring
+OpenModuleAction_title=Open Haskell Module
+OpenModuleAction_matching=Matching modules:
+OpenModuleAction_text=Enter module component prefix
+OpenModuleAction_nomodule=No Module found!
+OpenModuleAction_nomodulesel=No Module selected

0 comments on commit b307b71

Please sign in to comment.
Something went wrong with that request. Please try again.