use of org.eclipse.n4js.xtext.ide.server.ResourceChangeSet in project n4js by eclipse.
the class XWorkspaceBuilder method doIncrementalWorkspaceUpdateAndBuild.
/**
* Based on the raw, "reported changes" accumulated in {@link #newDirtyFiles} / {@link #newDeletedFiles}, do the
* following:
* <ol>
* <li>perform an update of the workspace configuration, if necessary, which may lead to additional "discovered
* changes" (e.g. resources in newly added source folders),
* <li><em>move</em> the "reported changes" together with the "discovered changes" to {@link #dirtyFiles} /
* {@link #deletedFiles} / {@link #affectedByDeletedProjects},
* <li>then trigger an incremental build.
* </ol>
*/
protected IResourceDescription.Event doIncrementalWorkspaceUpdateAndBuild(CancelIndicator cancelIndicator) {
// in case many incremental build tasks pile up in the queue (e.g. while a non-cancelable initial build is
// running), we don't want to repeatedly invoke IWorkspaceManager#update() in each of those tasks but only in
// the last one; therefore, we here check for a cancellation:
operationCanceledManager.checkCanceled(cancelIndicator);
Set<URI> newDirtyFiles = new LinkedHashSet<>(this.newDirtyFiles);
Set<URI> newDeletedFiles = new LinkedHashSet<>(this.newDeletedFiles);
boolean newRefreshRequest = this.newRefreshRequest;
this.newDirtyFiles.clear();
this.newDeletedFiles.clear();
this.newRefreshRequest = false;
Stopwatch stopwatch = Stopwatch.createStarted();
if (newRefreshRequest) {
lspLogger.log("Refreshing ...");
}
UpdateResult updateResult = workspaceManager.update(newDirtyFiles, newDeletedFiles, newRefreshRequest);
WorkspaceChanges changes = updateResult.changes;
List<URI> actualDirtyFiles = UtilN4.concat(changes.getAddedURIs(), changes.getChangedURIs());
List<URI> actualDeletedFiles = new ArrayList<>(changes.getRemovedURIs());
if (newRefreshRequest) {
// scan all source folders of all projects for source file additions, changes, and deletions
// - including source files of added projects,
// - including source files of added source folders of existing projects,
// - including source files of removed source folders of existing projects,
// - *not* including source files of removed projects.
actualDirtyFiles = new ArrayList<>();
actualDeletedFiles = new ArrayList<>();
for (ProjectBuilder projectBuilder : workspaceManager.getProjectBuilders()) {
ResourceChangeSet sourceFileChanges = projectBuilder.scanForSourceFileChanges();
actualDirtyFiles.addAll(sourceFileChanges.getDirty());
actualDeletedFiles.addAll(sourceFileChanges.getDeleted());
}
} else {
// scan only the added source folders (including those of added projects) for source files
actualDirtyFiles.addAll(scanAddedSourceFoldersForNewSourceFiles(changes, scanner));
// collect URIs from removed source folders (*not* including those of removed projects)
actualDeletedFiles.addAll(getURIsFromRemovedSourceFolders(changes));
}
queue(this.dirtyFiles, actualDeletedFiles, actualDirtyFiles);
queue(this.deletedFiles, actualDirtyFiles, actualDeletedFiles);
// take care of removed projects
Set<ProjectConfigSnapshot> deletedProjects = new HashSet<>();
for (ProjectConfigSnapshot prjConfig : changes.getRemovedProjects()) {
deletedProjects.add(prjConfig);
}
for (ProjectConfigSnapshot prjConfig : Iterables.concat(changes.getAddedProjects(), changes.getChangedProjects())) {
deletedProjects.remove(prjConfig);
}
for (ProjectConfigSnapshot delPrj : deletedProjects) {
ImmutableSet<? extends ProjectConfigSnapshot> affected = updateResult.oldWorkspaceConfigSnapshot.getProjectsDependingOn(delPrj.getName());
this.affectedByDeletedProjects.addAll(affected);
}
handleContentsOfRemovedProjects(updateResult.removedProjectsContents);
if (newRefreshRequest) {
lspLogger.log("... refresh done (" + stopwatch.toString() + "; " + "projects added/removed: " + changes.getAddedProjects().size() + "/" + changes.getRemovedProjects().size() + "; " + "files dirty/deleted: " + dirtyFiles.size() + "/" + deletedFiles.size() + ").");
}
for (String cyclicProject : updateResult.cyclicProjectChanges) {
ProjectConfigSnapshot projectConfig = workspaceManager.getWorkspaceConfig().findProjectByID(cyclicProject);
Collection<URI> projectDescriptionUris = projectConfig.getProjectDescriptionUris();
dirtyFiles.addAll(projectDescriptionUris);
}
if (dirtyFiles.isEmpty() && deletedFiles.isEmpty() && affectedByDeletedProjects.isEmpty()) {
return new ResourceDescriptionChangeEvent(Collections.emptyList());
}
return doIncrementalBuild(cancelIndicator);
}
use of org.eclipse.n4js.xtext.ide.server.ResourceChangeSet in project n4js by eclipse.
the class ProjectBuilder method readProjectState.
/**
* Reads the persisted project state from disk
*
* @return set of all source URIs with modified contents
*/
private ResourceChangeSet readProjectState() {
if (persisterConfig.isDeleteState(projectConfig)) {
deletePersistenceFile();
}
ResourceChangeSet result = new ResourceChangeSet();
// sets this#projectStateSnapshot to an empty project state
doClearWithoutNotification();
boolean fullBuildRequired = false;
ImmutableProjectState projectState = projectStatePersister.readProjectState(projectConfig);
if (projectState != null) {
fullBuildRequired |= handleProjectAdditionRemovalSinceProjectStateWasComputed(result, projectState);
setProjectIndex(projectState.internalGetResourceDescriptions());
this.projectStateSnapshot.set(projectState);
}
if (fullBuildRequired) {
result.getDirty().addAll(scanForSourceFiles());
} else {
result.addAll(scanForSourceFileChanges());
}
return result;
}
use of org.eclipse.n4js.xtext.ide.server.ResourceChangeSet in project n4js by eclipse.
the class ProjectBuilder method scanForSourceFileChanges.
/**
* Scans the files system and returns URIs of source files that were added, changed, removed since this builder's
* current {@link #projectStateSnapshot project state} was created. Content changes in source files are detected by
* way of hash comparison.
*/
public ResourceChangeSet scanForSourceFileChanges() {
ResourceChangeSet result = new ResourceChangeSet();
ImmutableProjectState oldProjectState = this.projectStateSnapshot.get();
Map<URI, HashedFileContent> oldHashes = oldProjectState.getFileHashes();
Set<URI> oldSourceFilesURIs = oldProjectState.internalGetResourceDescriptions().getAllURIs();
Set<URI> existingSourceFileURIs = scanForSourceFiles();
for (URI currURI : Sets.union(oldSourceFilesURIs, existingSourceFileURIs)) {
boolean isOld = oldSourceFilesURIs.contains(currURI);
boolean isNew = existingSourceFileURIs.contains(currURI);
if (!isOld && isNew) {
// added
result.getDirty().add(currURI);
} else if (isOld && !isNew) {
// removed
result.getDeleted().add(currURI);
} else if (isOld && isNew) {
// compare hash ...
HashedFileContent hfc = oldHashes.get(currURI);
if (hfc != null) {
switch(getSourceChangeKind(hfc, oldProjectState)) {
case UNCHANGED:
{
break;
}
case CHANGED:
{
result.getDirty().add(currURI);
break;
}
case DELETED:
{
result.getDeleted().add(currURI);
break;
}
}
} else {
LOG.warn("inconsistency in project state: URI is indexed but not hashed: " + currURI);
result.getDirty().add(currURI);
}
}
}
return result;
}
use of org.eclipse.n4js.xtext.ide.server.ResourceChangeSet in project n4js by eclipse.
the class ProjectBuilder method handleProjectAdditionRemovalSinceProjectStateWasComputed.
/**
* @return <code>true</code> iff full build of this builder's project is required due to project changes.
*/
protected boolean handleProjectAdditionRemovalSinceProjectStateWasComputed(ResourceChangeSet result, ImmutableProjectState projectState) {
Set<String> oldExistingDeps = FluentIterable.from(projectState.getDependencies().entrySet()).filter(// value tells whether project did exist when project state was computed
Entry::getValue).transform(Entry::getKey).toSet();
Set<String> newExistingDeps = FluentIterable.from(projectConfig.getDependencies()).filter(depName -> workspaceIndex.getProjectConfig(depName) != null).toSet();
if (!Sets.difference(oldExistingDeps, newExistingDeps).isEmpty()) {
// -> therefore we simply perform a full build of this builder's project
return true;
}
for (String depName : Sets.difference(newExistingDeps, oldExistingDeps)) {
// project 'depName' was added since 'projectState' was persisted
// -> treat all resources that exist now in that added project as newly created, by creating additional
// external deltas for them
ResourceDescriptionsData addedDepIndex = workspaceIndex.getProjectIndex(depName);
if (addedDepIndex != null) {
FluentIterable.from(addedDepIndex.getAllResourceDescriptions()).transform(desc -> new DefaultResourceDescriptionDelta(null, desc)).copyInto(result.getAdditionalExternalDeltas());
}
}
return false;
}
use of org.eclipse.n4js.xtext.ide.server.ResourceChangeSet in project n4js by eclipse.
the class ProjectBuilder method doInitialBuild.
/**
* Initial build reads the project state and resolves changes. Generate output files.
* <p>
* This method assumes that it is only invoked in situations when the client does not have any diagnostics stored,
* e.g. directly after invoking {@link #doClean(XBuildRequest, CancelIndicator)}. Therefore no 'publishDiagnostics'
* events with an empty list of diagnostics need to be sent to the client in order to remove obsolete diagnostics.
* <p>
* NOTE: this is not only invoked shortly after server startup but also at various other occasions, for example
* <ul>
* <li>when the client executes the {@code N4JSCommandService#rebuild(ILanguageServerAccess, CancelIndicator)
* rebuild command},
* <li>when the workspace folder is changed in VS Code.
* </ul>
*/
public XBuildResult doInitialBuild(IBuildRequestFactory buildRequestFactory, List<Delta> externalDeltas) {
ResourceChangeSet changeSet = readProjectState();
XBuildResult result = doBuild(createInitialBuildRequestFactory(buildRequestFactory), changeSet.getDirty(), changeSet.getDeleted(), UtilN4.concat(externalDeltas, changeSet.getAdditionalExternalDeltas()), CancelIndicator.NullImpl);
// clear the resource set to release memory
clearResourceSet();
LOG.info("Project built: " + this.projectConfig.getName());
return result;
}
Aggregations