/******************************************************************************* * Copyright (c) 2000, 2008 IBM Corporation 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.internal.ui.text.template.contentassist; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.IRewriteTarget; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.ITextViewerExtension; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.templates.GlobalTemplateVariables; import org.eclipse.jface.text.templates.Template; import org.eclipse.jface.text.templates.TemplateBuffer; import org.eclipse.jface.text.templates.TemplateException; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Shell; import org.eclipse.text.edits.MalformedTreeException; import org.eclipse.wst.jsdt.core.IJavaScriptUnit; import org.eclipse.wst.jsdt.core.IJavaScriptProject; import org.eclipse.wst.jsdt.core.dom.AST; import org.eclipse.wst.jsdt.core.dom.ASTNode; import org.eclipse.wst.jsdt.core.dom.ASTParser; import org.eclipse.wst.jsdt.core.dom.Block; import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; import org.eclipse.wst.jsdt.core.dom.Statement; import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes; import org.eclipse.wst.jsdt.internal.corext.dom.GenericVisitor; import org.eclipse.wst.jsdt.internal.corext.template.java.CompilationUnitContext; import org.eclipse.wst.jsdt.internal.corext.template.java.CompilationUnitContextType; import org.eclipse.wst.jsdt.internal.corext.template.java.JavaContextType; import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin; import org.eclipse.wst.jsdt.internal.ui.text.correction.AssistContext; import org.eclipse.wst.jsdt.internal.ui.text.correction.SurroundWith; import org.eclipse.wst.jsdt.ui.text.java.IInvocationContext; public class SurroundWithTemplateProposal extends TemplateProposal { private static class SurroundWithTemplate extends SurroundWith { private static final String $_LINE_SELECTION= "${" + GlobalTemplateVariables.LineSelection.NAME + "}"; //$NON-NLS-1$ //$NON-NLS-2$ private final Template fTemplate; private final IJavaScriptProject fCurrentProject; private ASTNode fTemplateNode; public SurroundWithTemplate(IInvocationContext context, Statement[] selectedNodes, Template template) { super(context.getASTRoot(), selectedNodes); fTemplate= template; fCurrentProject= context.getCompilationUnit().getJavaScriptProject(); } protected List getVariableDeclarationReadsInside(Statement[] selectedNodes, int maxVariableId) { if (isNewContext()) return super.getVariableDeclarationReadsInside(selectedNodes, maxVariableId); return new ArrayList(); } protected boolean isNewContext() { final String templateVariableRegEx= "\\$\\{[^\\}]*\\}"; //$NON-NLS-1$ String template= fTemplate.getPattern(); int currentPosition= template.indexOf($_LINE_SELECTION); int insertionPosition= -1; while (currentPosition != -1) { insertionPosition= currentPosition; template= template.replaceFirst(templateVariableRegEx, ""); //$NON-NLS-1$ currentPosition= template.indexOf($_LINE_SELECTION); } template= template.replaceAll(templateVariableRegEx, ""); //$NON-NLS-1$ AST ast= getAst(); ASTParser parser= ASTParser.newParser(ast.apiLevel()); parser.setSource(template.toCharArray()); parser.setProject(fCurrentProject); parser.setKind(ASTParser.K_STATEMENTS); ASTNode root= parser.createAST(null); if (((Block)root).statements().isEmpty()) { parser= ASTParser.newParser(ast.apiLevel()); parser.setSource(template.toCharArray()); parser.setProject(fCurrentProject); parser.setKind(ASTParser.K_EXPRESSION); root= parser.createAST(null); } final int lineSelectionPosition= insertionPosition; root.accept(new GenericVisitor() { public void endVisit(Block node) { super.endVisit(node); if (fTemplateNode == null && node.getStartPosition() <= lineSelectionPosition && node.getLength() + node.getStartPosition() >= lineSelectionPosition) { fTemplateNode= node; } } }); if (fTemplateNode != null && ASTNodes.getParent(fTemplateNode, FunctionDeclaration.class) != null) { return true; } return false; } } private final IRegion fRegion; private final IJavaScriptUnit fCompilationUnit; private final CompilationUnitContext fContext; private final Template fTemplate; private final Statement[] fSelectedStatements; private TemplateProposal fProposal; private IRegion fSelectedRegion; public SurroundWithTemplateProposal(IJavaScriptUnit compilationUnit, Template template, CompilationUnitContext context, IRegion region, Image image, Statement[] selectedStatements) { super(template, context, region, image); fCompilationUnit= compilationUnit; fTemplate= template; fContext= context; fRegion= region; fSelectedStatements= selectedStatements; } /* (non-Javadoc) * @see org.eclipse.wst.jsdt.internal.ui.text.template.contentassist.TemplateProposal#getAdditionalProposalInfo() */ public String getPreviewContent() { try { IDocument document= new Document(fCompilationUnit.getBuffer().getContents()); CompilationUnitContext context= createNewContext(document); int offset= context.getCompletionOffset(); int start= context.getStart(); int end= context.getEnd(); IRegion region= new Region(start, end - start); context.setReadOnly(false); TemplateBuffer templateBuffer; try { templateBuffer= context.evaluate(fTemplate); } catch (TemplateException e1) { JavaScriptPlugin.log(e1); return null; } start= region.getOffset(); end= region.getOffset() + region.getLength(); end= Math.max(end, offset); String templateString= templateBuffer.getString(); document.replace(start, end - start, templateString); return document.get(); } catch (MalformedTreeException e) { JavaScriptPlugin.log(e); } catch (IllegalArgumentException e) { JavaScriptPlugin.log(e); } catch (BadLocationException e) { JavaScriptPlugin.log(e); } catch (CoreException e) { JavaScriptPlugin.log(e); } return null; } /* (non-Javadoc) * @see org.eclipse.wst.jsdt.internal.ui.text.template.contentassist.TemplateProposal#apply(org.eclipse.jface.text.ITextViewer, char, int, int) */ public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) { try { setRedraw(viewer, false); IDocument document= viewer.getDocument(); CompilationUnitContext context= createNewContext(document); int start= context.getStart(); int end= context.getEnd(); IRegion region= new Region(start, end - start); //Evaluate the template within the new context fProposal= new TemplateProposal(fTemplate, context, region, null); fProposal.apply(viewer, trigger, stateMask, context.getCompletionOffset()); } catch (MalformedTreeException e) { handleException(viewer, e, fRegion); } catch (IllegalArgumentException e) { handleException(viewer, e, fRegion); } catch (BadLocationException e) { handleException(viewer, e, fRegion); } catch (CoreException e) { handleException(viewer, e, fRegion); } finally { setRedraw(viewer, true); } } private void setRedraw(ITextViewer viewer, boolean redraw) { if (viewer instanceof ITextViewerExtension) { ITextViewerExtension extension= (ITextViewerExtension) viewer; IRewriteTarget target= extension.getRewriteTarget(); target.setRedraw(redraw); } } public Point getSelection(IDocument document) { if (fSelectedRegion != null) { return new Point(fSelectedRegion.getOffset(), fSelectedRegion.getLength()); } else if (fProposal != null) { return fProposal.getSelection(document); } else { return null; } } private CompilationUnitContext createNewContext(IDocument document) throws CoreException, BadLocationException { AssistContext invocationContext= new AssistContext(fCompilationUnit, fContext.getStart(), fContext.getEnd() - fContext.getStart()); SurroundWithTemplate surroundWith= new SurroundWithTemplate(invocationContext, fSelectedStatements, fTemplate); Map options= fCompilationUnit.getJavaScriptProject().getOptions(true); surroundWith.getRewrite().rewriteAST(document, options).apply(document); int offset= surroundWith.getBodyStart(); int length= surroundWith.getBodyLength(); String newSelection= document.get(offset, length); //Create the new context CompilationUnitContextType contextType= (CompilationUnitContextType) JavaScriptPlugin.getDefault().getTemplateContextRegistry().getContextType(JavaContextType.NAME); CompilationUnitContext context= contextType.createContext(document, offset, newSelection.length(), fCompilationUnit); context.setVariable("selection", newSelection); //$NON-NLS-1$ context.setForceEvaluation(true); return context; } private void handleException(ITextViewer viewer, Exception e, IRegion region) { JavaScriptPlugin.log(e); openErrorDialog(viewer.getTextWidget().getShell(), e); fSelectedRegion= region; } private void openErrorDialog(Shell shell, Exception e) { MessageDialog.openError(shell, TemplateContentAssistMessages.TemplateEvaluator_error_title, e.getMessage()); } /** * {@inheritDoc} */ public boolean validate(IDocument document, int offset, DocumentEvent event) { return false; } }