/*=============================================================================#
# Copyright (c) 2008-2016 Stephan Wahlbrink (WalWare.de) and others.
# 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:
# Stephan Wahlbrink - initial API and implementation
#=============================================================================*/
package de.walware.statet.r.internal.ui.editors;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Map;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.expressions.IEvaluationContext;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentRewriteSessionType;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.ui.ISources;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.commands.IElementUpdater;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.menus.UIElement;
import de.walware.ecommons.ltk.IModelManager;
import de.walware.ecommons.ltk.SourceDocumentRunnable;
import de.walware.ecommons.ltk.core.model.ISourceStructElement;
import de.walware.ecommons.ltk.core.model.ISourceUnit;
import de.walware.ecommons.ltk.core.model.ISourceUnitModelInfo;
import de.walware.ecommons.ltk.ui.sourceediting.ISourceEditor;
import de.walware.ecommons.ltk.ui.sourceediting.ISourceEditorAssociated;
import de.walware.ecommons.ltk.ui.templates.TemplatesUtil;
import de.walware.ecommons.ltk.ui.templates.TemplatesUtil.EvaluatedTemplate;
import de.walware.ecommons.ltk.ui.util.LTKSelectionUtil;
import de.walware.ecommons.ltk.ui.util.LTKWorkbenchUIUtil;
import de.walware.ecommons.text.IndentUtil;
import de.walware.ecommons.workbench.ui.WorkbenchUIUtil;
import de.walware.statet.r.codegeneration.CodeGeneration;
import de.walware.statet.r.core.model.IRClass;
import de.walware.statet.r.core.model.IRElement;
import de.walware.statet.r.core.model.IRMethod;
import de.walware.statet.r.core.model.IRSourceUnit;
import de.walware.statet.r.core.model.RModel;
import de.walware.statet.r.core.refactoring.RRefactoring;
import de.walware.statet.r.internal.ui.RUIMessages;
public class GenerateRoxygenElementComment extends AbstractHandler implements IElementUpdater {
public GenerateRoxygenElementComment() {
}
@Override
public void updateElement(final UIElement element, final Map parameters) {
element.setText(RUIMessages.GenerateRoxygenElementComment_label);
}
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
if (!(event.getApplicationContext() instanceof IEvaluationContext)) {
return null;
}
final IProgressMonitor monitor = new NullProgressMonitor();
final SubMonitor progress = SubMonitor.convert(monitor, 2);
ISourceUnit su = null;
ISourceStructElement[] elements = null;
ISourceUnitModelInfo info = null;
final IEvaluationContext context = (IEvaluationContext) event.getApplicationContext();
final ISelection selection = WorkbenchUIUtil.getCurrentSelection(context);
final IWorkbenchWindow workbenchWindow = HandlerUtil.getActiveWorkbenchWindow(event);
final IWorkbenchPart part = (IWorkbenchPart) context.getVariable(ISources.ACTIVE_PART_NAME);
final ISourceEditor editor;
if (selection instanceof IStructuredSelection) {
elements = LTKSelectionUtil.getSelectedSourceStructElements((IStructuredSelection) selection);
if (elements != null && elements.length > 0) {
su = elements[0].getSourceUnit();
for (int i = 0; i < elements.length; i++) {
if (elements[i].getSourceUnit() != su) {
// should be filtered by enablement
return null;
}
}
info = su.getModelInfo(RModel.TYPE_ID, IModelManager.MODEL_FILE, progress.newChild(1));
// update elements if necessary
}
final ISourceEditorAssociated associated = (ISourceEditorAssociated) part.getAdapter(ISourceEditorAssociated.class);
editor = (associated != null) ? associated.getSourceEditor() : null;
}
else if (selection instanceof ITextSelection) {
editor = (ISourceEditor) part.getAdapter(ISourceEditor.class);
if (editor != null) {
su = editor.getSourceUnit();
if (su != null) {
info = su.getModelInfo(RModel.TYPE_ID, IModelManager.MODEL_FILE, progress.newChild(1));
elements = LTKSelectionUtil.getSelectedSourceStructElement(info, (ITextSelection) selection);
}
}
}
else {
editor = null;
}
if (su == null || elements == null || elements.length == 0 || !(su instanceof IRSourceUnit)) {
return null;
}
if (!su.checkState(true, new NullProgressMonitor())) {
return false;
}
final IRSourceUnit rsu = (IRSourceUnit) su;
try {
final AbstractDocument doc = su.getDocument(null);
final EvaluatedTemplate[] templates = new EvaluatedTemplate[elements.length];
final String lineDelimiter = doc.getDefaultLineDelimiter();
Arrays.sort(elements, RRefactoring.getFactory().createAdapter(elements).getModelElementComparator());
ITER_ELEMENTS: for (int i = 0; i < elements.length; i++) {
switch (elements[i].getElementType() & IRElement.MASK_C1) {
case IRElement.C1_CLASS:
templates[i] = CodeGeneration.getClassRoxygenComment((IRClass) elements[i], lineDelimiter);
continue ITER_ELEMENTS;
case IRElement.C1_METHOD:
switch (elements[i].getElementType() & IRElement.MASK_C2) {
case IRElement.R_S4METHOD:
templates[i] = CodeGeneration.getMethodRoxygenComment((IRMethod) elements[i], lineDelimiter);
continue ITER_ELEMENTS;
default:
templates[i] = CodeGeneration.getCommonFunctionRoxygenComment((IRMethod) elements[i], lineDelimiter);
continue ITER_ELEMENTS;
}
}
}
final IndentUtil indentUtil = new IndentUtil(doc, rsu.getRCoreAccess().getRCodeStyle());
final MultiTextEdit multi = new MultiTextEdit();
int selectionStart = 0;
int selectionLength = 0;
boolean first = true;
for (int i = 0; i < elements.length; i++) {
if (templates[i] == null) {
continue;
}
final int line = doc.getLineOfOffset(elements[i].getSourceRange().getOffset());
final String lineIndent = indentUtil.copyLineIndent(line);
final AbstractDocument templateDoc = templates[i].startPostEdit();
TemplatesUtil.indentTemplateDocument(templateDoc, lineIndent);
templates[i].finishPostEdit();
final int lineOffset = doc.getLineOffset(line);
multi.addChild(new InsertEdit(lineOffset, templates[i].getContent()));
// select offset for first comment
if (first) {
first = false;
selectionStart = doc.getLineOffset(line);
final IRegion templateSelection = templates[i].getRegionToSelect();
if (templateSelection != null) {
selectionStart += templateSelection.getOffset();
selectionLength = templateSelection.getLength();
}
else {
selectionStart += lineIndent.length();
}
}
}
if (first) {
return null;
}
final IFile resource = (IFile) su.getResource();
final IRegion initialSelection = new Region(selectionStart, selectionLength);
su.syncExec(new SourceDocumentRunnable(doc, info.getStamp().getSourceStamp(),
DocumentRewriteSessionType.SEQUENTIAL ) {
@Override
public void run() throws InvocationTargetException {
try {
multi.apply(getDocument(), TextEdit.NONE);
if (editor != null) {
editor.getViewer().setSelectedRange(initialSelection.getOffset(), initialSelection.getLength());
}
else if (resource != null) {
LTKWorkbenchUIUtil.openEditor(workbenchWindow.getActivePage(), resource, initialSelection);
}
}
catch (final MalformedTreeException e) {
throw new InvocationTargetException(e);
}
catch (final BadLocationException e) {
throw new InvocationTargetException(e);
}
}
});
}
catch (final InvocationTargetException e) {
throw new ExecutionException(RUIMessages.GenerateRoxygenElementComment_error_message, e.getCause());
}
catch (final BadLocationException e) {
throw new ExecutionException(RUIMessages.GenerateRoxygenElementComment_error_message, e);
}
catch (final CoreException e) {
throw new ExecutionException(RUIMessages.GenerateRoxygenElementComment_error_message, e);
}
return null;
}
}