package org.rascalmpl.eclipse.terms;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentRewriteSession;
import org.eclipse.jface.text.DocumentRewriteSessionType;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.UIJob;
import org.rascalmpl.eclipse.Activator;
import org.rascalmpl.interpreter.result.ICallableValue;
import org.rascalmpl.interpreter.types.RascalTypeFactory;
import io.usethesource.vallang.IConstructor;
import io.usethesource.vallang.IList;
import io.usethesource.vallang.ISourceLocation;
import io.usethesource.vallang.IString;
import io.usethesource.vallang.ITuple;
import io.usethesource.vallang.IValue;
import io.usethesource.vallang.type.Type;
import io.usethesource.impulse.editor.UniversalEditor;
import io.usethesource.impulse.parser.IParseController;
import io.usethesource.impulse.services.IEditorService;
/*
* NB: this might end up in an infinite loop when the function used to compute patches
* does not reach a fixpoint.
*/
public class EditorUpdater implements IEditorService {
public EditorUpdater() {
}
class Job extends UIJob {
private IParseController parseController;
public Job(IParseController parseController) {
super("updating editor");
this.parseController = parseController;
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
IDocument doc = parseController.getDocument();
IWorkbenchWindow activeWindow = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (activeWindow != null) {
IWorkbenchPage activePage = activeWindow.getActivePage();
if (activePage != null) {
IEditorPart activeEditor = activePage.getActiveEditor();
if (activeEditor != null && activeEditor instanceof UniversalEditor) {
ICallableValue func = TermLanguageRegistry.getInstance().getLiveUpdater(parseController.getLanguage());
if (func == null) {
return Status.CANCEL_STATUS;
}
IConstructor pt = (IConstructor) parseController.getCurrentAst();
if (pt == null) {
return Status.CANCEL_STATUS;
}
Type type = RascalTypeFactory.getInstance().nonTerminalType(pt);
IList patch = null;
synchronized (func.getEval()) {
func.getEval().__setInterrupt(false);
patch = (IList) func.call(new Type[] {type}, new IValue[] {pt}, null).getValue();
}
if (patch.isEmpty()) {
return Status.OK_STATUS;
}
DocumentRewriteSession session = ((IDocumentExtension4)doc).startRewriteSession(DocumentRewriteSessionType.UNRESTRICTED_SMALL);
try {
int offset = 0;
for (IValue v: patch) {
ITuple subst = (ITuple)v;
ISourceLocation loc = (ISourceLocation) subst.get(0);
IString txt = (IString) subst.get(1);
doc.replace(loc.getOffset() + offset, loc.getLength(), txt.getValue());
offset += txt.length() - loc.getLength();
}
} catch (UnsupportedOperationException e) {
e.printStackTrace();
return Status.CANCEL_STATUS;
} catch (BadLocationException e) {
e.printStackTrace();
return Status.CANCEL_STATUS;
}
finally {
((IDocumentExtension4)doc).stopRewriteSession(session);
}
}
}
}
return Status.OK_STATUS;
}
}
@Override
public void update(IParseController parseController, IProgressMonitor monitor) {
try {
Job job = new Job(parseController);
job.schedule();
job.join();
} catch (InterruptedException e) {
Activator.getInstance().logException("live updater interrupted", e);
}
}
@Override
public String getName() {
// TODO Auto-generated method stub
return null;
}
@Override
public void setEditor(UniversalEditor arg0) {
// TODO Auto-generated method stub
}
}