Commits

sk  committed f1746b5

Added new project wizard.

  • Participants
  • Parent commits 8bb848f

Comments (0)

Files changed (9)

       </perspective>
    </extension>
    <extension
-         id="LispNature"
+         id="org.lispdev.lispNature"
          name="Lisp Nature"
          point="org.eclipse.core.resources.natures">
       <runtime>
             id="org.lispdev.wizards.newFileWizard"
             name="Lisp File"
             preferredPerspectives="org.lispdev.main.LispPerspective">
+         <selection
+               class="org.eclipse.core.resources.IResource">
+         </selection>
+      </wizard>
+      <wizard
+            category="org.lispdev.wizards"
+            class="org.lispdev.main.NewProjectWizard"
+            finalPerspective="org.lispdev.main.LispPerspective"
+            icon="icons/lisp-file.gif"
+            id="org.lispdev.wizards.newProjectWizard"
+            name="Lisp Project"
+            preferredPerspectives="org.lispdev.main.LispPerspective"
+            project="true">
+         <selection
+               class="org.eclipse.core.resources.IResource">
+         </selection>
       </wizard>
    </extension>
 

File src/org/lispdev/main/NewProjectWizPage1.java

+package org.lispdev.main;
+
+import java.io.File;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.DirectoryDialog;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+public class NewProjectWizPage1 extends WizardPage
+{
+  private Text projectName;
+  private Text customProjectPath;
+  public boolean useUnitTests = true;
+  public boolean makeExampleFunction = true;
+
+  /**
+   * Constructor.
+   *
+   * @param selection
+   *          The selection this wizard was called on.
+   */
+  public NewProjectWizPage1(ISelection selection)
+  {
+    super("wizardPage");
+    setTitle("New Lisp Project");
+    setDescription("This wizard creates a new Lisp project");
+    // this.selection = selection;
+  }
+
+  public void createControl(final Composite parent)
+  {
+    Composite container = new Composite(parent, SWT.NULL);
+    GridLayout layout = new GridLayout(3, false);
+    layout.verticalSpacing = 2;
+    container.setLayout(layout);
+    GridData gd;
+
+    Label label = new Label(container, SWT.NULL);
+    label.setText("Project name:");
+
+    projectName = new Text(container, SWT.BORDER | SWT.SINGLE);
+    projectName.addModifyListener(new ValidationListener());
+    projectName.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+    gd.horizontalSpan = 2;
+
+    // GridData.horizontalSpan = 3 isn't cooperating; messy, but effective
+    new Label(container, SWT.NULL);
+    new Label(container, SWT.NULL);
+    new Label(container, SWT.NULL);
+
+    final Button useDefaultLocation = new Button(container, SWT.CHECK);
+    new Label(container, SWT.NULL);
+    new Label(container, SWT.NULL);
+
+    label = new Label(container, SWT.NULL);
+    label.setText("Location:");
+
+    customProjectPath = new Text(container, SWT.BORDER | SWT.SINGLE);
+    customProjectPath.setEditable(false);
+    customProjectPath.addModifyListener(new ValidationListener());
+    customProjectPath
+      .setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
+
+    final Button chooseLocation = new Button(container, SWT.PUSH);
+    chooseLocation.setEnabled(false);
+    customProjectPath.setEnabled(false);
+
+    final Button useTests = new Button(container, SWT.CHECK);
+    new Label(container, SWT.NULL);
+    new Label(container, SWT.NULL);
+
+    final Button makeExample = new Button(container, SWT.CHECK);
+    new Label(container, SWT.NULL);
+    new Label(container, SWT.NULL);
+
+    useDefaultLocation.setSelection(true);
+    useDefaultLocation.addSelectionListener(new SelectionListener(){
+      public void widgetDefaultSelected(SelectionEvent e)
+      {
+        widgetSelected(e);
+      }
+
+      public void widgetSelected(SelectionEvent e)
+      {
+        boolean useDefaultPath = useDefaultLocation.getSelection();
+        chooseLocation.setEnabled(!useDefaultPath);
+        customProjectPath.setEnabled(!useDefaultPath);
+      }
+    });
+    useDefaultLocation.setText("Use default location");
+
+    useTests.setSelection(true);
+    useTests.addSelectionListener(new SelectionListener(){
+      public void widgetDefaultSelected(SelectionEvent e)
+      {
+        widgetSelected(e);
+      }
+
+      public void widgetSelected(SelectionEvent e)
+      {
+        useUnitTests = useTests.getSelection();
+      }
+    });
+    useTests.setText("Use lisp-unit testing framework");
+
+    makeExample.setSelection(true);
+    makeExample.addSelectionListener(new SelectionListener(){
+      public void widgetDefaultSelected(SelectionEvent e)
+      {
+        widgetSelected(e);
+      }
+
+      public void widgetSelected(SelectionEvent e)
+      {
+        makeExampleFunction = makeExample.getSelection();
+      }
+    });
+    makeExample.setText("Generate code for an example function.");
+
+    chooseLocation.setText("Browse...");
+    chooseLocation.addMouseListener(new MouseAdapter(){
+      @Override
+      public void mouseDown(MouseEvent e)
+      {
+        DirectoryDialog fd = new DirectoryDialog(parent.getShell(), SWT.OPEN);
+        fd.setMessage("Choose Location for New Lisp Project");
+        String path = fd.open();
+        if( path != null ) customProjectPath.setText(path);
+      }
+    });
+
+    initialize();
+    setControl(container);
+  }
+
+  /**
+   * Initializes the project name to one that is available.
+   */
+  private void initialize()
+  {
+    String name;
+    IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+    IProject project;
+    int projectNum = 1;
+
+    // Find an available project name to use as the default
+    do
+    {
+      name = "new-lisp" + projectNum;
+
+      project = root.getProject(name);
+
+      projectNum++;
+    }
+    while( project.exists() && projectNum <= 99 );
+
+    projectName.setText(name);
+    projectName.setFocus();
+  } // void initialize()
+
+  private class ValidationListener implements ModifyListener
+  {
+    public void modifyText(ModifyEvent e)
+    {
+      String projectName = getProjectName();
+      // Characters which cannot be used in a resource name:
+      char[] invalids = {'*', '\\', '/', '"', ':', '<', '>', '|', '?'};
+
+      if( projectName.length() == 0 )
+      {
+        updateStatus("Project name must be specified");
+        return;
+      } // if
+
+      for(int i = 0; i < invalids.length; i++)
+      {
+        if( projectName.indexOf(invalids[i]) != -1 )
+        {
+          updateStatus(invalids[i]
+              + " is an invalid character in the project name " + projectName);
+          return;
+        } // if
+      } // for i
+
+      if( projectName.charAt(projectName.length() - 1) == '.' )
+      {
+        updateStatus("Resource name cannot end in a period.");
+        return;
+      } // if
+
+      // Make sure the project doesn't already exist
+      IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+      IProject newProject = root.getProject(getProjectName());
+      if( newProject.exists() )
+      {
+        updateStatus("A project with that name already exists.");
+        return;
+      } // if
+
+      if( customProjectPath.isEnabled() )
+      {
+        if( new File(getCustomProjectPath(), getProjectName()).exists() )
+        {
+          updateStatus("A directory with that project name already exists in that location.");
+          return;
+        }
+      }
+
+      updateStatus(null);
+    } // void dialogChanged()
+  }
+
+  /**
+   * Updates the wizard status message.
+   *
+   * @param message
+   *          The new status message.
+   */
+  private void updateStatus(String message)
+  {
+    setErrorMessage(message);
+    setPageComplete(message == null);
+  }
+
+  /**
+   * Gets the name to be used for this project.
+   *
+   * @return The new project name.
+   */
+  public String getProjectName()
+  {
+    return projectName.getText();
+  }
+
+  public String getCustomProjectPath()
+  {
+    return customProjectPath.isEnabled() ? customProjectPath.getText() : null;
+  }
+}

File src/org/lispdev/main/NewProjectWizard.java

+package org.lispdev.main;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+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.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.ui.INewWizard;
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.WorkbenchException;
+import org.eclipse.ui.ide.IDE;
+
+public class NewProjectWizard extends Wizard implements INewWizard
+{
+  public static final String ID = "org.lispdev.wizards.newProjectWizard";
+
+  private NewProjectWizPage1 page;
+  private ISelection selection;
+  private IWorkbench workbench;
+
+  public NewProjectWizard()
+  {
+    super();
+    setNeedsProgressMonitor(true);
+  }
+
+  @Override
+  public void addPages()
+  {
+    page = new NewProjectWizPage1(selection);
+    addPage(page);
+  }
+
+  @Override
+  public void init(IWorkbench workbench, IStructuredSelection selection)
+  {
+    this.workbench = workbench;
+    this.selection = selection;
+  }
+
+  @Override
+  public boolean performFinish()
+  {
+    final String projectName = page.getProjectName();
+    final String customProjectPath = page.getCustomProjectPath();
+    final boolean useLispUnit = page.useUnitTests;
+    final boolean makeExampleFunction = page.makeExampleFunction;
+    IRunnableWithProgress op = new IRunnableWithProgress(){
+      public void run(IProgressMonitor monitor)
+          throws InvocationTargetException
+      {
+        try
+        {
+          doFinish(projectName, customProjectPath, useLispUnit,
+              makeExampleFunction, monitor);
+        }
+        catch(CoreException e)
+        {
+          e.printStackTrace();
+          throw new InvocationTargetException(e);
+        }
+        finally
+        {
+          monitor.done();
+        }
+      }
+    };
+    try
+    {
+      getContainer().run(true, false, op);
+      workbench.showPerspective(LispPerspectiveFactory.ID,
+          workbench.getWorkbenchWindows()[0]);
+    }
+    catch(InterruptedException e)
+    {
+      return false;
+    }
+    catch(InvocationTargetException e)
+    {
+      Throwable realException = e.getTargetException();
+      MessageDialog.openError(getShell(), "Error", realException.getMessage());
+      return false;
+    }
+    catch(WorkbenchException e)
+    {
+      MessageDialog.openError(getShell(), "Error", e.getMessage());
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * The worker method. It will create a new project, then
+   * create the appropriate Soar heirarchy and files.
+   */
+  private void doFinish(String projectName, String customProjectPath,
+      final boolean useLispUnit, boolean makeExample, IProgressMonitor monitor)
+      throws CoreException
+  {
+    monitor.beginTask("Creating " + projectName, 10);
+
+    IWorkspace workspace = ResourcesPlugin.getWorkspace();
+    IWorkspaceRoot root = workspace.getRoot();
+    IProject newProject = root.getProject(projectName);
+
+    // creation of the project
+    if( newProject.exists() )
+    {
+      throwCoreException("Project \"" + projectName + "\" already exists");
+    }
+    else
+    {
+      IProjectDescription pdesc =
+          workspace.newProjectDescription(newProject.getName());
+      pdesc.setLocation(customProjectPath != null ? new Path(new File(
+          customProjectPath, projectName).getAbsolutePath()) : null);
+
+      newProject.create(pdesc, monitor);
+      newProject.open(monitor);
+
+      try
+      {
+        String[] natures = pdesc.getNatureIds();
+        String[] newNatures = new String[natures.length + 1];
+        System.arraycopy(natures, 0, newNatures, 0, natures.length);
+        newNatures[natures.length] = LispProjectNature.ID;
+        pdesc.setNatureIds(newNatures);
+
+        newProject.setDescription(pdesc, IResource.FORCE, monitor);
+      }
+      catch(CoreException e)
+      {
+        e.printStackTrace();
+      } // catch
+
+    } // else
+
+    monitor.worked(2);
+
+    // Create the contents of the project's root directory
+    final String pkg = makePackageName(projectName);
+
+    InputStream contents =
+        Templater.getTemplate("main.lisp", pkg, useLispUnit, makeExample);
+    final IFile main = newProject.getFile("main.lisp");
+    if( !main.exists() )
+    {
+      main.create(contents, true, monitor);
+    } // if
+    try
+    {
+      contents.close();
+    }
+    catch(IOException e)
+    {}
+    monitor.worked(1);
+
+    // Make the asd file
+    contents = Templater.getTemplate("asd.asd", pkg, useLispUnit, makeExample);
+    final IFile asd = newProject.getFile(pkg + ".asd");
+    if( !asd.exists() )
+    {
+      asd.create(contents, true, monitor);
+    }
+    try
+    {
+      contents.close();
+    }
+    catch(IOException e)
+    {}
+    monitor.worked(1);
+
+    // Make the defpackage file
+    contents =
+        Templater.getTemplate("defpackage.lisp", pkg, useLispUnit, makeExample);
+    final IFile defpackage = newProject.getFile("defpackage.lisp");
+    if( !defpackage.exists() )
+    {
+      defpackage.create(contents, true, monitor);
+    }
+    try
+    {
+      contents.close();
+    }
+    catch(IOException e)
+    {}
+    monitor.worked(1);
+
+    // Make the tests file
+    final IFile tests = newProject.getFile("tests.lisp");
+    if( useLispUnit )
+    {
+      contents =
+          Templater.getTemplate("tests.lisp", pkg, useLispUnit, makeExample);
+      if( !tests.exists() )
+      {
+        tests.create(contents, true, monitor);
+      }
+      try
+      {
+        contents.close();
+      }
+      catch(IOException e)
+      {}
+      monitor.worked(1);
+    }
+
+    newProject.close(monitor);
+    newProject.open(monitor);
+
+    //Plugin.get().getSwank().compileAndLoadAsd(asd, true);
+
+    monitor.setTaskName("Opening files for editing...");
+    getShell().getDisplay().asyncExec(new Runnable(){
+      public void run()
+      {
+        IWorkbenchPage page =
+            PlatformUI.getWorkbench().getActiveWorkbenchWindow()
+              .getActivePage();
+        try
+        {
+          IDE.openEditor(page, asd, true);
+          IDE.openEditor(page, defpackage, true);
+          if( useLispUnit )
+          {
+            IDE.openEditor(page, tests, true);
+          }
+          IDE.openEditor(page, main, true);
+
+        }
+        catch(PartInitException e)
+        {}
+        catch(Exception e)
+        {
+          e.printStackTrace();
+        }
+      }
+    });
+
+    monitor.worked(2);
+    monitor.done();
+  } // void doFinish(...)
+
+  private String makePackageName(String projectName)
+  {
+    String ret = projectName.toLowerCase().replace(' ', '-');
+    return ret;
+  }
+
+  private void throwCoreException(String message) throws CoreException
+  {
+    IStatus status =
+        new Status(IStatus.ERROR, Plugin.ID, IStatus.OK,
+            message, null);
+    throw new CoreException(status);
+  }
+
+}

File src/org/lispdev/main/Plugin.java

    *
    * @return the shared instance
    */
-  public static Plugin getDefault()
+  public static Plugin get()
   {
     return plugin;
   }

File src/org/lispdev/main/Templater.java

+package org.lispdev.main;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Calendar;
+import java.util.Random;
+import java.util.TimeZone;
+
+import org.eclipse.core.runtime.Platform;
+
+public class Templater
+{
+  private static String[] inspirations =
+      new String[]{
+          "This is your lisp file. May it serve you well.",
+          "This is your lisp file. There are many like it, but this one is yours.",
+          "My lisp code, without me, is useless. Without my lisp code, I am useless. I must code my lisp true.",
+          "Behold, the power of lisp!"};
+
+  public static InputStream getTemplate(String fileName, String pkg,
+      boolean withTests, boolean withExample)
+  {
+    try
+    {
+      URL installURL = Platform.getBundle(Plugin.ID).getEntry("/");
+      URL url = new URL(installURL, "templates/" + fileName);
+      BufferedReader template =
+          new BufferedReader(new InputStreamReader(url.openStream()));
+
+      StringBuilder sb = new StringBuilder();
+      String line = template.readLine();
+
+      while( line != null )
+      {
+        sb.append(line);
+        sb.append('\n');
+        line = template.readLine();
+      }
+
+      String contents = sb.toString();
+
+      contents = contents.replace("${inspiration}", getInspiration());
+      contents = contents.replace("${time}", getTime());
+      contents = contents.replace("${package}", pkg);
+      if( withTests )
+      {
+        contents =
+            contents.replace("${with-tests}",
+                "(:file \"tests\" :depends-on (\"defpackage\" \"main\"))");
+        contents = contents.replace("${lisp-unit}", ":lisp-unit");
+      }
+      else
+      {
+        contents = contents.replace("${with-tests}", "");
+        contents = contents.replace("${lisp-unit}", "");
+      }
+      if( withExample )
+      {
+        if( withTests )
+        {
+          contents =
+              contents.replace("${example-test}", "(define-test main-test\n"
+                  + "  (assert-equal 0 (main)) ;should pass\n"
+                  + "  (assert-equal 1 (main)) ;should fail\n" + ")");
+        }
+        else
+        {
+          contents = contents.replace("${example-test}", "");
+        }
+        contents = contents.replace("${example-export}", "#:main");
+
+        if( false /*Plugin.get().getSwank().implementation.lispType()
+          .toLowerCase().contains("sbcl")*/ )
+        {
+          contents =
+              contents
+                .replace(
+                    "${example-source}",
+                    "(defun main ()\n"
+                        + "  \"This function greets and returns 0.\n"
+                        + "If this function is used as top level in executable,\n"
+                        + "Prints 'Hello, World!' if no command line arguments are supplied\n"
+                        + "and 'Hello, User!' if the first command line argument is 'User'.\"\n"
+                        + "  (format t \"Hello, ~A!~%\"\n"
+                        + "      (if (second sb-ext:*posix-argv*)\n"
+                        + "          (second sb-ext:*posix-argv*)\n"
+                        + "          \"World\"))\n" + "  0)");
+        }
+        else
+        {
+          contents =
+              contents.replace("${example-source}", "(defun main ()\n"
+                  + "  \"This function greets and returns 0.\"\n"
+                  + "  (format t \"Hello, ~A!~%\" \"World\")\n" + "  0)");
+        }
+
+      }
+      else
+      {
+        contents = contents.replace("${example-test}", "");
+        contents = contents.replace("${example-export}", "");
+        contents = contents.replace("${example-source}", "");
+      }
+
+      return new ByteArrayInputStream(contents.getBytes());
+    }
+    catch(MalformedURLException e)
+    {
+      e.printStackTrace();
+    }
+    catch(IOException e)
+    {
+      e.printStackTrace();
+    }
+
+    return new ByteArrayInputStream("".getBytes());
+  }
+
+  private static String getTime()
+  {
+    Calendar cal = Calendar.getInstance(TimeZone.getDefault());
+    String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+    java.text.SimpleDateFormat sdf =
+        new java.text.SimpleDateFormat(DATE_FORMAT);
+    sdf.setTimeZone(TimeZone.getDefault());
+
+    return sdf.format(cal.getTime());
+  }
+
+  private static String getInspiration()
+  {
+    Random rand = new Random();
+    return inspirations[rand.nextInt(inspirations.length)];
+    // InputStream contents = new ByteArrayInputStream(header.getBytes());
+  }
+}

File templates/asd.asd

+;;;; ${time}
+;;;;
+;;;; Think of this as your project file.
+;;;; Keep it up to date, and you can reload your project easily
+;;;;  by right-clicking on it and selecting "Load Project"
+
+(defpackage #:${package}-asd
+  (:use :cl :asdf))
+
+(in-package :${package}-asd)
+
+(defsystem ${package}
+  :name "${package}"
+  :version "0.1"
+  :serial t
+  :components ((:file "defpackage")
+               (:file "main" :depends-on ("defpackage"))
+               ${with-tests}
+               
+               ; As you add files to your project,
+               ; make sure to add them here as well
+               
+               )
+  :depends-on ())

File templates/defpackage.lisp

+;;;; ${time}
+
+
+(in-package :common-lisp-user)
+
+(defpackage :${package}
+  (:nicknames :${package})
+  (:use :cl ${lisp-unit}
+   ;; Packages you want to import go here
+   )
+  (:export
+  
+   ;; Exported symbols go here
+   ${example-export}
+  
+   ))
+

File templates/main.lisp

+;;;; ${time}
+;;;; ${inspiration}
+
+(in-package :${package})
+
+${example-source}

File templates/tests.lisp

+;;;; ${time}
+;;;; tests for your lisp code
+
+(in-package :${package})
+
+${example-test}