Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public synchronized void start(Duration d) {
}, d.toMillis(), TimeUnit.MILLISECONDS);
}

/** marks timer as ready immediately and triggers action */
public synchronized void triggerNow() {
stop();
isReady = true;
action.run();
}




Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public void setRootPath(WFile rootPath) {

private final Object lock = new Object();
private ModelManager.Changes changesToReconcile = ModelManager.Changes.empty();
private boolean reconcileNowRequested = false;
private final DebouncingTimer packagesToReconcileTimer = new DebouncingTimer(() -> {
synchronized (lock) {
lock.notify();
Expand Down Expand Up @@ -112,6 +113,17 @@ public String toString() {
}
}

class FileSystemUpdated extends PendingChange {
public FileSystemUpdated(WFile filename) {
super(filename);
}

@Override
public String toString() {
return "FileSystemUpdated(" + getFilename() + ")";
}
}

class FileDeleted extends PendingChange {

public FileDeleted(WFile filename) {
Expand Down Expand Up @@ -182,6 +194,12 @@ private Workitem getNextWorkItem() {
// cannot do anything useful at the moment
WLogger.info("LanguageWorker is waiting for init ... ");
}
} else if (reconcileNowRequested && !changesToReconcile.isEmpty() && changes.isEmpty()) {
packagesToReconcileTimer.stop();
ModelManager.Changes changes = changesToReconcile;
changesToReconcile = ModelManager.Changes.empty();
reconcileNowRequested = false;
return new Workitem("reconcile files (save)", () -> modelManager.reconcile(changes));
} else if (!userRequests.isEmpty()) {
UserRequest<?> req = userRequests.remove();
return new Workitem(req.toString(), () -> req.run(modelManager));
Expand All @@ -191,11 +209,25 @@ private Workitem getNextWorkItem() {
return new Workitem(change.toString(), () -> {
ModelManager.Changes affected = null;
if (isWurstDependencyFile(change)) {
if (!(change instanceof FileReconcile)) {
modelManager.clean();
if (change instanceof FileReconcile) {
FileReconcile fr = (FileReconcile) change;
affected = modelManager.syncCompilationUnitContent(fr.getFilename(), fr.getContents());
} else if (change instanceof FileSystemUpdated || change instanceof FileDeleted) {
// Dependency roots may have changed (e.g. grill install), refresh and sync incrementally.
modelManager.refreshDependencies();
if (change instanceof FileDeleted) {
affected = modelManager.removeCompilationUnit(change.getFilename());
} else {
affected = modelManager.syncCompilationUnit(change.getFilename());
}
} else {
// Editor-triggered updates (save/close) use the normal incremental path.
affected = modelManager.syncCompilationUnit(change.getFilename());
}
} else if (change instanceof FileDeleted) {
affected = modelManager.removeCompilationUnit(change.getFilename());
} else if (change instanceof FileSystemUpdated) {
affected = modelManager.syncCompilationUnit(change.getFilename());
} else if (change instanceof FileUpdated) {
affected = modelManager.syncCompilationUnit(change.getFilename());
} else if (change instanceof FileReconcile) {
Expand All @@ -214,6 +246,7 @@ private Workitem getNextWorkItem() {
packagesToReconcileTimer.stop();
ModelManager.Changes changes = changesToReconcile;
changesToReconcile = ModelManager.Changes.empty();
reconcileNowRequested = false;

return new Workitem("reconcile files", () -> {
modelManager.reconcile(changes);
Expand All @@ -223,7 +256,11 @@ private Workitem getNextWorkItem() {
}

private boolean isWurstDependencyFile(PendingChange change) {
String uri = change.getFilename().getUriString().replace('\\', '/');
return isWurstDependencyFile(change.getFilename());
}

private boolean isWurstDependencyFile(WFile file) {
String uri = file.getUriString().replace('\\', '/');
return uri.contains("/_build/dependencies/");
}

Expand Down Expand Up @@ -262,13 +299,18 @@ private void log(String s) {
public void handleFileChanged(DidChangeWatchedFilesParams params) {
synchronized (lock) {
for (FileEvent fileEvent : params.getChanges()) {
bufferManager.handleFileChange(fileEvent);

WFile file = WFile.create(fileEvent.getUri());
boolean isOpenInEditor = bufferManager.getTextDocumentVersion(file) >= 0;
// For open documents incremental didChange is authoritative.
// Ignore watcher changed/created events to avoid clobbering in-memory state.
if (isOpenInEditor && fileEvent.getType() != FileChangeType.Deleted) {
continue;
}
bufferManager.handleFileChange(fileEvent);
if (fileEvent.getType() == FileChangeType.Deleted) {
changes.put(file, new FileDeleted(file));
} else {
changes.put(file, new FileUpdated(file));
changes.put(file, new FileSystemUpdated(file));
}
}
lock.notifyAll();
Expand Down Expand Up @@ -306,7 +348,10 @@ public void handleClose(DidCloseTextDocumentParams params) {
public void handleSave(DidSaveTextDocumentParams params) {
synchronized (lock) {
WFile file = WFile.create(params.getTextDocument().getUri());
reconcileNowRequested = true;
changes.put(file, new FileUpdated(file));
// Save should flush diagnostics quickly instead of waiting for debounce.
packagesToReconcileTimer.triggerNow();
lock.notifyAll();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ public interface ModelManager {

void buildProject();

/**
* refresh discovered dependency roots (e.g. _build/dependencies after grill install)
*/
void refreshDependencies();

Changes syncCompilationUnit(WFile changedFilePath);

Changes syncCompilationUnitContent(WFile filename, String contents);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ private void readDependencies() {
WurstCompilerJassImpl.addDependenciesFromFolder(projectPath, dependencies);
}

@Override
public void refreshDependencies() {
readDependencies();
}

private String getCanonicalPath(File f) {
try {
return f.getCanonicalPath();
Expand Down Expand Up @@ -477,6 +482,13 @@ private void replaceCompilationUnit(WFile filename) {
@Override
public Changes syncCompilationUnitContent(WFile filename, String contents) {
WLogger.debug("sync contents for " + filename);
int newHash = contentHash(contents);
Integer oldHash = fileHashcodes.get(filename);
CompilationUnit existing = getCompilationUnit(filename);
if (oldHash != null && oldHash == newHash && existing != null) {
// No content change and CU still present -> skip expensive reconcile/typecheck work.
return Changes.empty();
}
Set<String> oldPackages = declaredPackages(filename);
replaceCompilationUnit(filename, contents, false);
return new Changes(io.vavr.collection.HashSet.of(filename), oldPackages);
Expand Down Expand Up @@ -509,8 +521,28 @@ public CompilationUnit replaceCompilationUnitContent(WFile filename, String cont
@Override
public Changes syncCompilationUnit(WFile f) {
WLogger.debug("syncCompilationUnit File " + f);
String contents;
try {
File file = f.getFile();
if (!file.exists()) {
removeCompilationUnit(f);
return Changes.empty();
}
contents = Files.toString(file, Charsets.UTF_8);
bufferManager.updateFile(WFile.create(file), contents);
} catch (IOException e) {
WLogger.severe(e);
throw new ModelManagerException(e);
}
int newHash = contentHash(contents);
Integer oldHash = fileHashcodes.get(f);
CompilationUnit existing = getCompilationUnit(f);
if (oldHash != null && oldHash == newHash && existing != null) {
WLogger.trace(() -> "syncCompilationUnit no-op for " + f);
return Changes.empty();
}
Set<String> oldPackages = declaredPackages(f);
replaceCompilationUnit(f);
replaceCompilationUnit(f, contents, true);
WLogger.debug("replaced file " + f);
WurstGui gui = new WurstGuiLogger();
doTypeCheckPartial(gui, ImmutableList.of(f), oldPackages);
Expand All @@ -521,9 +553,10 @@ private CompilationUnit replaceCompilationUnit(WFile filename, String contents,
if (!isInWurstFolder(filename) && !isAlreadyLoaded(filename)) {
return null;
}
int newHash = contentHash(contents);
if (fileHashcodes.containsKey(filename)) {
int oldHash = fileHashcodes.get(filename);
if (oldHash == contents.hashCode()) {
if (oldHash == newHash) {
CompilationUnit existing = getCompilationUnit(filename);
if (existing != null) {
// no change
Expand All @@ -533,7 +566,7 @@ private CompilationUnit replaceCompilationUnit(WFile filename, String contents,
// Stale hash cache after remove/move; CU is gone, so reparse.
WLogger.debug("CU hash unchanged but model entry missing for " + filename + ", reparsing.");
} else {
WLogger.debug("CU changed. oldHash = " + oldHash + " == " + contents.hashCode());
WLogger.debug("CU changed. oldHash = " + oldHash + " == " + newHash);
}
}

Expand All @@ -543,7 +576,7 @@ private CompilationUnit replaceCompilationUnit(WFile filename, String contents,
CompilationUnit cu = c.parse(filename.toString(), new StringReader(contents));
cu.getCuInfo().setFile(filename.toString());
updateModel(cu, gui);
fileHashcodes.put(filename, contents.hashCode());
fileHashcodes.put(filename, newHash);
if (reportErrors) {
if (gui.getErrorCount() > 0) {
WLogger.debug("found " + gui.getErrorCount() + " errors in file " + filename);
Expand Down Expand Up @@ -832,4 +865,10 @@ private boolean isAlreadyLoaded(WFile file) {
public File getProjectPath() {
return projectPath;
}

private int contentHash(String s) {
// Normalize line endings to avoid false "changed" signals between editor buffers and disk text.
String normalized = s.replace("\r\n", "\n").replace('\r', '\n');
return normalized.hashCode();
}
}
Loading
Loading