问题
Background:
I have a java project that uses lesscss.
I am using grunt
with grunt-contrib-watch
and grunt-contrib-less to compile my
.lessfiles to
.css`.
It all works nicely.
The issue is that to get the eclipse tomcat server to start serving the updated .css
files I need to refresh the project in eclipse.
I was wandering if there is a way to force eclipse to refresh as part of the watch
cycle in grunt ?
or actually, is there a way to cause the currently open eclipse project ( given that I know its path if it helps ) to refresh using grunt.
Connecting it to the watch cycle is not hard and can be probably done by changing my Gruntfile.js
from:
watch: {
styles: {
// Which files to watch (all .less files recursively in the less directory)
files: ['../WebContent/less/**/*.less'],
tasks: ['less'],
options: {
nospawn: true
}
},
to:
watch: {
styles: {
// Which files to watch (all .less files recursively in the less directory)
files: ['../WebContent/less/**/*.less'],
tasks: ['less','updateEclipseTask'],
options: {
nospawn: true
}
},
回答1:
Try enabling Preferences > General > Workspace > Refresh using native hooks or polling
. Depending on what OS you're using it will refresh more or less quickly. It's not exactly what you want but it might solve the problem.
Another option that I haven't tried myself but that looks like it could be made to work is to use eclipse-remote-control to trigger an external command you have set up in Eclipse (Run > External Tools > External Tools Configurations...
), if you create a new "Program" configuration there you can in the "Refresh" tab set Eclipse to refresh the workspace when that external tool config is executed.
To run eclipse-remote-control from grunt you could use grunt-shell.
回答2:
I recently became fed up with waiting for Eclipse to refresh files and folders that were changing due to Grunt tasks running outside of Eclipse.
I ended up writing my own plugin for Eclipse that would speed up the refresh interval so that external filesystem changes would be detected and the Tomcat instances running in Eclipse would serve those changes almost instantaneously.
While it is true that you can enable the setting 'Refresh using native hooks and polling', I found that file changes still took too long to appear in Eclipse and newly created files still required manual refreshing.
Here are the details and code, so that you can do something like this for yourself.
Overview
- First, create a new Eclipse plugin project within Eclipse. I named mine RapidFileRefresher.
- Modify or create all of the files below.
- Customize the files below to track the projects you want (by adding your project names to
PROJECT_NAMES
inRapidRefreshProvider
) and the folders your want to monitor by adding the folder names toFOLDERS
inProjectMonitorJob
. - Double click Manifest.MF, open the overview tab, and click "Export Wizard". Click "Install into host repository" and select a folder to server as the host repository. I don't think the folder matters.
- Click Finish. Eclipse should automatically install the local plugin.
- Make sure that your have the "Refresh using native hooks or polling" setting enabled under Settings > General > Workspace
To make changes to the plugin, uninstall it by going to Help > About Eclipse, click "Installation details", search for the plugin under "Installed Software", uninstall it, make the code changes, and then re-export it using the steps above.
Essentially what the code below does is:
- Uses the RefreshProvider extension point to register a new refresh provider (RapidRefreshProvider) for all of the desired projects in the workspace
- Create a new RefreshMonitor for each desired project
- Create a Job that runs every 350ms (this value can be changed) to loop through all of the resources in the specified folders, until a resource that is out of sync is discovered. Said resource is then refreshed.
I created this plugin for myself for a very specific use-case, so please keep that in mind.
Update
I threw all of the code below into a Github repo: https://github.com/peterjkirby/RapidFileRefreshPlugin
Results
After grunt tasks run and modify my filesystem (whether it be creating, updating, or deleting files), the changes are reflected in Eclipse within in under a second. The updates resources are served by the Tomcat instance I have running through Eclipse usually in under a second.
Files
Manifest.MF
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: RapidFileRefresher
Bundle-SymbolicName: rfr.RapidFileRefresher;singleton:=true
Bundle-Version: 1.0.2
Bundle-Activator: rfr.core.Activator
Require-Bundle: org.eclipse.core.runtime
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ActivationPolicy: lazy
Import-Package: org.eclipse.core.internal.refresh,
org.eclipse.core.internal.resources.mapping,
org.eclipse.core.resources,
org.eclipse.core.resources.refresh
Bundle-Vendor: PeterKirby
Export-Package: rfr.core
plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
id="RapidFileRefresher"
point="org.eclipse.core.resources.refreshProviders">
<refreshProvider
class="rfr.core.RapidFileRefresher"
name="RapidFileRefresher">
</refreshProvider> </extension>
</plugin>
Activator.java
public class Activator extends Plugin {
private static Activator INSTANCE;
public static final String PLUGIN_ID = "RapidFileRefresher";
public Activator() {
super();
INSTANCE = this;
}
public static Activator getInstance() {
return INSTANCE;
}
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
}
@Override
public void stop(BundleContext context) throws Exception {
super.stop(context);
}
}
RapidRefreshProvider.java
public class RapidRefreshProvider extends RefreshProvider {
private static final String[] PROJECTS_NAMES = {
"YOUR_PROJECT_NAMES"
};
private Set<String> projects = new HashSet<>();
public RapidRefreshProvider() {
projects.addAll(Arrays.asList(PROJECTS_NAMES));
}
@Override
public IRefreshMonitor installMonitor(IResource resource, IRefreshResult result) {
// only monitor resources that are projects
if (resource.getType() != IResource.PROJECT) return null;
IProject project = (IProject) resource;
// only monitor the projects in PROJECT_NAMES
if (!projects.contains(project.getName())) return null;
RapidRefreshMonitor monitor = new RapidRefreshMonitor(resource, result);
monitor.start();
return monitor;
}
}
RapidRefreshMonitor.java
public class RapidRefreshMonitor implements IRefreshMonitor {
// wait INITIAL_DELAY seconds before starting to track changes
// in order to allow eclipse to startup faster.
private static final int INITIAL_DELAY = 1000 * 20;
private ProjectMonitorJob job;
private IResource resource;
private IRefreshResult result;
public RapidRefreshMonitor(IResource resource, IRefreshResult result) {
this.resource = resource;
this.result = result;
}
public void start() {
job = new ProjectMonitorJob(resource, result);
job.schedule(INITIAL_DELAY);
}
@Override
public void unmonitor(IResource resource) {
job.stop();
}
}
ProjectMonitorJob.java
public class ProjectMonitorJob extends Job {
private static final String JOB_NAME = "ProjectMonitorJob";
private static final String[] FOLDERS = {
"src/main/webapp/resources"
};
// fires about 3 times per second.
private static final int DELAY = 350;
private boolean RUNNING = true;
private IResource resource;
private IRefreshResult result;
public ProjectMonitorJob(IResource resource, IRefreshResult result) {
super(Activator.PLUGIN_ID + " - " + JOB_NAME);
this.resource = resource;
this.result = result;
}
@Override
protected IStatus run(IProgressMonitor monitor) {
if (RUNNING) {
IProject project = (IProject) resource;
for (String folderPath : FOLDERS) {
monitor(project, folderPath);
}
// schedule the next run
schedule(DELAY);
}
return Status.OK_STATUS;
}
private void monitor(IProject project, String folderPath) {
IFolder folder = project.getFolder(new Path(folderPath));
if (folder.exists()) {
ResourceTraverser traverser = new ResourceTraverser(new ResourceChangeEvaluator());
try {
traverser.traverse(folder, result);
} catch (CoreException e) {}
}
}
public void stop() {
RUNNING = false;
}
}
ResourceChangeEvaluator.java
public class ResourceChangeEvaluator {
public ResourceChangeEvaluator() {}
public boolean changed(IFolder folder) {
if (folder == null) return false;
return !folder.isSynchronized(IResource.DEPTH_ONE);
}
public boolean changed(IFile file) throws CoreException {
if (file == null) return false;
if (!file.exists()) return false;
if (file.isSynchronized(IResource.DEPTH_ZERO)) return false;
return true;
}
}
ResourceTraverser.java
public class ResourceTraverser {
private ResourceChangeEvaluator resourceChangeEvaluator;
public ResourceTraverser(ResourceChangeEvaluator resourceChangeEvaluator) {
this.resourceChangeEvaluator = resourceChangeEvaluator;
}
public void traverse(IFolder folder, IRefreshResult refreshResult) throws CoreException {
IResource[] contents = folder.members();
Activator activator = Activator.getInstance();
if (resourceChangeEvaluator.changed(folder)) {
refreshResult.refresh(folder);
}
for (IResource resource : contents) {
if (resource.getType() == IResource.FILE) {
IFile file = (IFile) resource;
if (resourceChangeEvaluator.changed(file)) {
refreshResult.refresh(file);
activator.getLog().log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "File change detected. Refreshing " + file.getName()));
}
} else if (resource.getType() == IResource.FOLDER) {
IFolder subfolder = (IFolder) resource;
// only continue traversing if a folder has not changed. If a folder has changed
// the refresh event at that folder should be enough to force synchronization
// Plus, other changes at a depth > 1 will be detected on the next pass.
// This is more a performance optimization than anything else.
if (resourceChangeEvaluator.changed(subfolder)) {
refreshResult.refresh(subfolder);
activator.getLog().log(new Status(IStatus.INFO, Activator.PLUGIN_ID, "Folder change detected. Refreshing " + subfolder.getName()));
} else {
traverse((IFolder) resource, refreshResult);
}
}
}
}
}
来源:https://stackoverflow.com/questions/22129029/grunt-plugin-to-refresh-eclipse-java-project