Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pub support #107

Merged
merged 19 commits into from Jul 31, 2019
Merged
Changes from 4 commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -20,6 +20,11 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ds.core.builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
@@ -25,5 +25,8 @@ Require-Bundle: org.eclipse.ui.genericeditor;bundle-version="1.0.0",
org.eclipse.tm4e.ui,
com.google.guava;bundle-version="21.0.0",
org.eclipse.lsp4j.jsonrpc
Import-Package: org.slf4j,
Import-Package: org.osgi.service.component.annotations;version="1.3.0",
org.slf4j,
org.slf4j.spi
Service-Component: OSGI-INF/org.eclipse.dartboard.ListenerService.xml
Bundle-ActivationPolicy: lazy
This conversation was marked as resolved by jonas-jonas

This comment has been minimized.

Copy link
@jonas-jonas

jonas-jonas Jul 22, 2019

Author Member

Is this correct?

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
This conversation was marked as resolved by jonas-jonas

This comment has been minimized.

Copy link
@jonas-jonas

jonas-jonas Jul 22, 2019

Author Member

This file should be committed right?

<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" activate="registerListeners" immediate="true" name="org.eclipse.dartboard.ListenerService">
<implementation class="org.eclipse.dartboard.ListenerService"/>
</scr:component>
@@ -6,6 +6,7 @@ Launch_NoProjectSelected_Body=Please select a project to launch
Launch_Project=Project:
Preference_SDKLocation=Dart SDK &Location:
Preference_SDKVersion=Dart SDK Version:
Preference_PubAutoSync=Pub dependency synchronization
Launch_SDKLocation_Message=The location the Dart SDK is installed to
Launch_MainClass=Main class:
Launch_MainClass_Message=Entry point to your Dart program
@@ -34,3 +35,7 @@ NewFile_Description=Create a new Dart file.
NewFile_Creating=Creating file {0}
NewFile_Container_Doesnot_Exist=Container {0} does not exist.
NewFile_OpeningFile=Opening file for editing...

PubSync_Job_Name=Resolving pub dependencies of {0}
PubSync_Task_ResolvingDependencies=Resolving dependencies...
PubSync_Task_PrecompilingExecutables=Precompiling executables...
@@ -16,4 +16,5 @@ output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.xml,\
assets/
assets/,\
OSGI-INF/
@@ -25,6 +25,8 @@ private Constants() {
*/
public static final String PREFERENCES_SDK_LOCATION = "sdk_location"; //$NON-NLS-1$

public static final String PREFERENCES_SYNC_PUB = "auto_pub_sync"; //$NON-NLS-1$

/**
* Plugin ID of the plugin
*/
@@ -50,8 +52,6 @@ private Constants() {
*/
public static final String LAUNCH_GROUP = "org.eclipse.dartboard.launchGroup"; //$NON-NLS-1$

/**
* The ID used to identify this project nature
*/
public static final String NATURE_ID = "org.eclipse.dartboard.dartprojectnature"; //$NON-NLS-1$
public static final String PUBSPEC = "pubspec.yaml"; //$NON-NLS-1$

}
@@ -0,0 +1,17 @@
package org.eclipse.dartboard;

import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.dartboard.pub.PubspecChangeListener;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;

@Component(immediate = true)
public class ListenerService {

@Activate
public void registerListeners() {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
workspace.addResourceChangeListener(new PubspecChangeListener());
This conversation was marked as resolved by jonas-jonas

This comment has been minimized.

Copy link
@lak-proddev

lak-proddev Jul 30, 2019

Contributor

Don't need to create an object for IWorkspace. We can call directly ResourcesPlugin.getWorkspace().addResourceChangeListener(new PubspecChangeListener());

This comment has been minimized.

Copy link
@jonas-jonas

jonas-jonas Jul 30, 2019

Author Member

Not now, but maybe later. There is no harm in having it this way.

This comment has been minimized.

Copy link
@lak-proddev

lak-proddev Jul 30, 2019

Contributor

There is no harm. except creating an unnecessary object reference :)

This comment has been minimized.

Copy link
@lak-proddev

lak-proddev Jul 30, 2019

Contributor

Don't we need to remove the listener?. I am not sure.

This comment has been minimized.

Copy link
@jonas-jonas

jonas-jonas Jul 30, 2019

Author Member

Yes, ideally we would remove the listener when the user unchecks the checkbox on the preference page. But as the ListenerService is not available in the class in the current design, the listener stays registered as is and the necessary checks are done whenever it fires.

And it should stay registered until the workspace is closed.

}
}
@@ -13,6 +13,7 @@
public static String Launch_Project;
public static String Preference_SDKLocation;
public static String Preference_SDKVersion;
public static String Preference_PubAutoSync;
public static String Launch_SDKLocation_Message;
public static String Launch_MainClass;
public static String Launch_MainClass_Message;
@@ -41,6 +42,9 @@
public static String NewFile_Creating;
public static String NewFile_Container_Doesnot_Exist;
public static String NewFile_OpeningFile;
public static String PubSync_Job_Name;
public static String PubSync_Task_ResolvingDependencies;
public static String PubSync_Task_PrecompilingExecutables;

static {
NLS.initializeMessages("assets.messages", Messages.class);
@@ -58,6 +58,7 @@ public void initializeDefaultPreferences() {
Messages.Preference_SDKNotFound_Body);
}
}
scopedPreferenceStore.setDefault(Constants.PREFERENCES_SYNC_PUB, true);
}

/**
@@ -23,6 +23,7 @@
import org.eclipse.dartboard.Constants;
import org.eclipse.dartboard.Messages;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.BooleanFieldEditor;
import org.eclipse.jface.preference.DirectoryFieldEditor;
import org.eclipse.jface.preference.FieldEditor;
import org.eclipse.jface.preference.FieldEditorPreferencePage;
@@ -54,6 +55,12 @@
*/
private DartSDKLocationFieldEditor dartSDKLocationEditor;

private BooleanFieldEditor autoPubSyncEditor;

public DartPreferencePage() {
super(GRID);
}

/**
* Initializes the {@link DartPreferencePage#getPreferenceStore()} to the
* default preference store of the plugin
@@ -67,6 +74,8 @@ public void init(IWorkbench workbench) {
public boolean performOk() {
String sdkLocation = dartSDKLocationEditor.getStringValue();
String oldValue = getPreferenceStore().getString(Constants.PREFERENCES_SDK_LOCATION);

boolean ok = super.performOk();
// Don't update the preference store if the oldValue matches the new value
if (sdkLocation.equals(oldValue)) {
return true;
@@ -81,7 +90,6 @@ public boolean performOk() {

dartSDKLocationEditor.setStringValue(path.toAbsolutePath().toString());

boolean ok = super.performOk();
boolean result = MessageDialog.openQuestion(null, Messages.Preference_RestartRequired_Title,
Messages.Preference_RestartRequired_Message);

@@ -138,6 +146,9 @@ protected void createFieldEditors() {
dartSDKLocationEditor.addModifyListener(event -> {
setValid(dartSDKLocationEditor.doCheckState());
});
autoPubSyncEditor = new BooleanFieldEditor(Constants.PREFERENCES_SYNC_PUB, Messages.Preference_PubAutoSync,
parent);
addField(autoPubSyncEditor);
}

/**
@@ -87,7 +87,7 @@ private IProject createNewProject() {
Messages.NewProject_WindowTitle);
try {
projectOperation.execute(monitor, WorkspaceUndoUtil.getUIInfoAdapter(getShell()));
IFile pubspecFile = newProjectHandle.getFile("pubspec.yaml"); //$NON-NLS-1$
IFile pubspecFile = newProjectHandle.getFile(Constants.PUBSPEC); // $NON-NLS-1$
if (!pubspecFile.exists()) {
pubspecFile.create(new NullInputStream(0), true, null);
}
@@ -5,6 +5,7 @@
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Adapters;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.dartboard.Constants;

public class IsDartProjectPropertyTester extends PropertyTester {

@@ -25,7 +26,7 @@ public boolean test(Object receiver, String property, Object[] args, Object expe

try {
for (IResource res : project.members()) {
if ("pubspec.yaml".equals(res.getName()) || "dart".equals(res.getFileExtension())) { //$NON-NLS-1$ //$NON-NLS-2$
if (Constants.PUBSPEC.equals(res.getName()) || "dart".equals(res.getFileExtension())) { //$NON-NLS-1$
This conversation was marked as resolved by jonas-jonas

This comment has been minimized.

Copy link
@lak-proddev

lak-proddev Jul 30, 2019

Contributor

Since we know the default path. We can check in the default path using #findMember(String)
If not found, we need to iterate all the members.

This comment has been minimized.

Copy link
@jonas-jonas

jonas-jonas Jul 30, 2019

Author Member

Done

return true;
}
}
@@ -0,0 +1,102 @@
package org.eclipse.dartboard.pub;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.dartboard.Messages;
import org.eclipse.dartboard.util.DartUtil;
import org.eclipse.osgi.util.NLS;

public class Pub {

private Map<IProject, Job> pubSyncJobs;
private static Pub instance;

public Pub() {
pubSyncJobs = new HashMap<>();
}

public void get(IProject project) {
// Sometimes the same project has 2 or more pubspec.yaml files (see stagehand
// project).
// This leads to the same job being executed once for every pubspec file.
// The result is a huge performance hit.
// Instead we store the active jobs for any project and cancel already running
// jobs
Job active = pubSyncJobs.get(project);
if (active != null) {
active.cancel();
pubSyncJobs.remove(project);
}

Job pubSync = Job.create(NLS.bind(Messages.PubSync_Job_Name, project.getName()), (monitor) -> {
IPath location = project.getLocation();
// if (location == null) {
// monitor.setCanceled(true);
// } TODO: Check how to handle this case?

String[] commands = { DartUtil.getExecutable("pub"), "get" }; //$NON-NLS-1$ //$NON-NLS-2$
ProcessBuilder builder = new ProcessBuilder(commands);
builder.directory(location.makeAbsolute().toFile());
Process process = null;
try {
process = builder.start();
} catch (IOException e1) {
// TODO: Check how to handle this case?
}

try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {

SubMonitor mo = SubMonitor.convert(monitor, 100);

String taskName = reader.readLine();
while (taskName != null) {
if (taskName.equalsIgnoreCase(Messages.PubSync_Task_ResolvingDependencies)
|| taskName.equalsIgnoreCase(Messages.PubSync_Task_PrecompilingExecutables)) {
mo.split(50);
mo.setTaskName(taskName);
}
if (mo.isCanceled() && process != null) {
reader.close();
process.destroy();
}
taskName = reader.readLine();
}
} catch (IOException e) {
// TODO: Check how to handle this case?
}
});
pubSync.addJobChangeListener(new JobChangeAdapter() {
@Override
public void done(IJobChangeEvent event) {
pubSyncJobs.remove(project);
try {
project.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (CoreException e) {
// Ignore here?
}
}
});
pubSync.setPriority(Job.LONG);
pubSync.schedule(1000);
pubSyncJobs.put(project, pubSync);
}

public static Pub getInstance() {
if (instance == null) {
instance = new Pub();
}
return instance;
}
}
@@ -0,0 +1,57 @@
package org.eclipse.dartboard.pub;

import org.eclipse.core.resources.IFile;
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.runtime.CoreException;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.dartboard.Constants;
import org.eclipse.ui.preferences.ScopedPreferenceStore;

public class PubspecChangeListener implements IResourceChangeListener {

private ScopedPreferenceStore preferences;

private Pub pub;

public PubspecChangeListener() {
preferences = new ScopedPreferenceStore(InstanceScope.INSTANCE, Constants.PLUGIN_ID);
This conversation was marked as resolved by jonas-jonas

This comment has been minimized.

Copy link
@lak-proddev

lak-proddev Jul 30, 2019

Contributor

It's a duplicate object for the preference store. We need to access the same object in a whole plugin

This comment has been minimized.

Copy link
@jonas-jonas

jonas-jonas Jul 30, 2019

Author Member

It's a duplicate object for the preference store. We need to access the same object in a whole plugin

That's not true. If I understand this https://www.vogella.com/tutorials/EclipsePreferences/article.html correctly, a new object should be created every time it is needed. But again, OSGi service would be nice here.

pub = Pub.getInstance();
}

@Override
public void resourceChanged(IResourceChangeEvent event) {
boolean syncPub = preferences.getBoolean(Constants.PREFERENCES_SYNC_PUB);
if (!syncPub) {
return;
}

if (event.getType() == IResourceChangeEvent.POST_CHANGE) {
try {
event.getDelta().accept(new DeltaPrinter());
} catch (CoreException e) {
e.printStackTrace();
}
}
}

private class DeltaPrinter implements IResourceDeltaVisitor {

@Override
public boolean visit(IResourceDelta delta) throws CoreException {
if (delta.getResource().getType() == IResource.FILE) {
IFile file = (IFile) delta.getResource();
if (file.getName().equalsIgnoreCase(Constants.PUBSPEC)
&& (delta.getKind() == IResourceDelta.ADDED || delta.getKind() == IResourceDelta.CHANGED)) {
pub.get(delta.getResource().getProject());
}
}
return true;
}

}

}
@@ -0,0 +1,29 @@
package org.eclipse.dartboard.util;

import java.io.File;

import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.dartboard.Constants;
import org.eclipse.ui.preferences.ScopedPreferenceStore;

public class DartUtil {

static final boolean IS_WINDOWS = Platform.OS_WIN32.equals(Platform.getOS());

static ScopedPreferenceStore preferences = new ScopedPreferenceStore(InstanceScope.INSTANCE,
Constants.PLUGIN_ID);

public static String getExecutable(String name) {
String dartSdk = preferences.getString(Constants.PREFERENCES_SDK_LOCATION);

String result = dartSdk + File.separator + "bin" + File.separator + name; //$NON-NLS-1$

if (IS_WINDOWS) {
return result + ".exe"; //$NON-NLS-1$
}
return result;

}

}
This conversation was marked as resolved by jonas-jonas

This comment has been minimized.

Copy link
@lak-proddev

lak-proddev Jul 30, 2019

Contributor

I think Dart service should provide other services, preference store, listeners, etc..
Someone, need to design this.

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.