/*******************************************************************************
* Copyright (c) 2014 Bruno Medeiros and other Contributors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Bruno Medeiros - initial API and implementation
*******************************************************************************/
package melnorme.lang.ide.ui.utils.operations;
import static melnorme.utilbox.core.Assert.AssertNamespace.assertNotNull;
import static melnorme.utilbox.core.CoreUtil.areEqual;
import org.eclipse.core.resources.IProject;
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.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.source.SourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.texteditor.ITextEditor;
import melnorme.lang.ide.ui.editor.AbstractLangEditor;
import melnorme.lang.ide.ui.editor.EditorSourceBuffer;
import melnorme.lang.ide.ui.editor.EditorUtils;
import melnorme.lang.tooling.ast.SourceRange;
import melnorme.lang.tooling.toolchain.ops.SourceOpContext;
import melnorme.utilbox.core.CommonException;
import melnorme.utilbox.misc.Location;
public abstract class AbstractEditorOperation2<RESULT> extends CalculateValueUIOperation<RESULT> {
protected final ITextEditor editor;
protected final IWorkbenchWindow window;
protected final EditorSourceBuffer sourceBuffer;
protected final IDocument doc;
protected final IProject project;
private final SourceOpContext sourceOpContext;
public AbstractEditorOperation2(String operationName, ITextEditor editor) {
this(operationName, editor, new SourceRange(0, 0));
}
public AbstractEditorOperation2(String operationName, ITextEditor editor, SourceRange range) {
super(operationName);
this.editor = assertNotNull(editor);
this.window = editor.getSite().getWorkbenchWindow();
this.doc = assertNotNull(editor.getDocumentProvider().getDocument(editor.getEditorInput()));
this.project = EditorUtils.getAssociatedProject(editor.getEditorInput());
this.sourceBuffer = new EditorSourceBuffer(editor);
this.sourceOpContext = sourceBuffer.getSourceOpContext(range);
}
public SourceOpContext getSourceOpContext() {
return sourceOpContext;
}
public String getSource() {
return sourceOpContext.getSource();
}
public SourceRange getOperationRange() {
return sourceOpContext.getOperationRange();
}
public int getOperationOffset() {
return sourceOpContext.getOffset();
}
public Location getInputLocation() throws CommonException {
return sourceOpContext.getFileLocation();
}
@Override
public void prepareOperation() throws CommonException {
if(!sourceOpContext.getOptionalFileLocation().isPresent()) {
throw new CommonException("No file available for editor contents.");
}
}
/* ----------------- ----------------- */
@Override
protected abstract void handleComputationResult(RESULT result) throws CommonException;
protected void handleStatusErrorMessage(String statusErrorMessage) {
EditorUtils.setStatusLineErrorMessage(editor, statusErrorMessage, null);
Display.getCurrent().beep();
}
/* ----------------- ----------------- */
protected SourceViewer getEditorSourceViewer() throws CommonException {
if(editor instanceof AbstractLangEditor) {
AbstractLangEditor langEditor = (AbstractLangEditor) editor;
return langEditor.getSourceViewer_();
} else {
throw new CommonException("Possible internal error: SourceViewer not available.");
}
}
protected void setEditorTextPreservingCarret(String newContents) throws CommonException {
if(areEqual(newContents, doc.get())) {
return;
}
SourceViewer sourceViewer = getEditorSourceViewer();
ISelection sel = sourceViewer.getSelectionProvider().getSelection();
int line = -1;
int col = -1;
if(sel instanceof ITextSelection) {
ITextSelection textSelection = (ITextSelection) sel;
try {
line = doc.getLineOfOffset(textSelection.getOffset());
col = textSelection.getOffset() - doc.getLineOffset(line);
} catch(BadLocationException e) {
// Ignore
}
}
IDocument document = sourceViewer.getDocument();
if(document instanceof IDocumentExtension4) {
IDocumentExtension4 doc_4 = (IDocumentExtension4) document;
// We use the DocumentRewriteSession to prevent the caret from jumping around
DocumentRewriteSession rewriteSession = doc_4.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL);
document.set(newContents);
doc_4.stopRewriteSession(rewriteSession);
} else {
int topIndex = sourceViewer.getTopIndex();
document.set(newContents);
sourceViewer.setTopIndex(topIndex);
}
int newOffset = -1;
if(line != -1 && col != -1) {
try {
newOffset = getOffsetFor(line, col);
} catch(BadLocationException e) {
// ignore
}
}
if(newOffset != -1) {
sourceViewer.getSelectionProvider().setSelection(new TextSelection(newOffset, 0));
} else {
sourceViewer.getSelectionProvider().setSelection(sel);
}
}
protected int getOffsetFor(int line, int col) throws BadLocationException {
IRegion lineRegion = doc.getLineInformation(line);
int offset = lineRegion.getOffset();
return offset + Math.min(col, lineRegion.getLength());
}
}