/** * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ /* * Created on Apr 12, 2005 * * @author Fabio Zadrozny */ package org.python.pydev.editor.correctionassist.heuristics; import java.io.File; import java.util.ArrayList; import java.util.List; import org.eclipse.core.runtime.CoreException; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.contentassist.CompletionProposal; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.templates.Template; import org.eclipse.jface.text.templates.TemplateContext; import org.eclipse.jface.text.templates.TemplateProposal; import org.python.pydev.core.IPythonNature; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.editor.PyEdit; import org.python.pydev.editor.actions.PyAction; import org.python.pydev.editor.autoedit.DefaultIndentPrefs; import org.python.pydev.editor.codecompletion.AbstractTemplateCodeCompletion; import org.python.pydev.editor.codecompletion.CompletionRequest; import org.python.pydev.shared_core.string.FastStringBuffer; import org.python.pydev.shared_core.string.StringUtils; import org.python.pydev.shared_ui.ImageCache; import org.python.pydev.shared_ui.UIConstants; /** * @author Fabio Zadrozny */ public class AssistSurroundWith extends AbstractTemplateCodeCompletion implements IAssistProps { /** * @throws BadLocationException * @see org.python.pydev.editor.correctionassist.heuristics.IAssistProps#getProps(org.python.pydev.core.docutils.PySelection, org.python.pydev.shared_ui.ImageCache) */ @Override public List<ICompletionProposal> getProps(PySelection ps, ImageCache imageCache, File f, IPythonNature nature, PyEdit edit, int offset) throws BadLocationException { ArrayList<ICompletionProposal> l = new ArrayList<ICompletionProposal>(); String indentation = edit != null ? edit.getIndentPrefs().getIndentationString() : DefaultIndentPrefs.get( nature).getIndentationString(); ps.selectCompleteLine(); String selectedText = ps.getSelectedText(); List<String> splitInLines = StringUtils.splitInLines(selectedText); int firstCharPosition = -1; int firstCommentCharPosition = -1; for (String string : splitInLines) { String trimmed = string.trim(); if (trimmed.startsWith("#")) { int localFirst = PySelection.getFirstCharPosition(string); if (firstCommentCharPosition == -1) { firstCommentCharPosition = localFirst; } else if (localFirst < firstCommentCharPosition) { firstCommentCharPosition = localFirst; } continue; } if (trimmed.length() > 0) { int localFirst = PySelection.getFirstCharPosition(string); if (firstCharPosition == -1) { firstCharPosition = localFirst; } else if (localFirst < firstCharPosition) { firstCharPosition = localFirst; } } } if (firstCharPosition == -1) { if (firstCommentCharPosition != -1) { firstCharPosition = firstCommentCharPosition; } else { // Haven't found any non-empty line. return l; } } //delimiter to use String delimiter = PyAction.getDelimiter(ps.getDoc()); //get the 1st char (determines indent) FastStringBuffer startIndentBuffer = new FastStringBuffer(firstCharPosition + 1); startIndentBuffer.appendN(' ', firstCharPosition); final String startIndent = startIndentBuffer.toString(); //code to be surrounded String surroundedCode = selectedText; surroundedCode = indentation + surroundedCode.replaceAll(delimiter, delimiter + indentation); //region IRegion region = ps.getRegion(); TemplateContext context = null; if (edit != null) { context = createContext(edit.getPySourceViewer(), region, ps.getDoc()); } //not static because we need the actual code. String[] replace0to3 = new String[] { startIndent, delimiter, surroundedCode, delimiter, startIndent, delimiter, startIndent, indentation, indentation }; String[] replace4toEnd = new String[] { startIndent, delimiter, surroundedCode, delimiter, startIndent, indentation }; //actually create the template for (int iComp = 0, iRep = 0; iComp < SURROUND_WITH_COMPLETIONS.length; iComp += 2, iRep++) { String comp = SURROUND_WITH_COMPLETIONS[iComp]; if (iRep < 4) { comp = StringUtils.format(comp, (Object[]) replace0to3); } else { comp = StringUtils.format(comp, (Object[]) replace4toEnd); } l.add(createProposal(ps, imageCache, edit, startIndent, region, iComp, comp, context)); } return l; } private ICompletionProposal createProposal(PySelection ps, ImageCache imageCache, PyEdit edit, final String startIndent, IRegion region, int iComp, String comp, TemplateContext context) { Template t = new Template("Surround with", SURROUND_WITH_COMPLETIONS[iComp + 1], "", comp, false); if (context != null) { TemplateProposal proposal = new TemplateProposal(t, context, region, imageCache.get(UIConstants.COMPLETION_TEMPLATE), 5) { @Override public String getAdditionalProposalInfo() { return startIndent + super.getAdditionalProposalInfo(); } }; return proposal; } else { //In tests return new CompletionProposal(comp, region.getOffset(), region.getLength(), 0); } } /** * Template completions available for surround with... They %s will be replaced later for the actual code/indentation. * * Could be refactored so that we don't have to put the actual indent here (creating a subclass of PyDocumentTemplateContext) * Also, if that refactoring was done, we could give an interface for the user to configure those templates better. * * Another nice thing may be analyzing the current context for local variables so that * for item in collection could have 'good' choices for the collection variable based on the local variables. */ public static final String[] SURROUND_WITH_COMPLETIONS = new String[] { "%stry:%s%s%s%sexcept${cursor}:%s%s%sraise", "try..except", "%stry:%s%s%s%sexcept (${RuntimeError}, ), e:%s%s%s${raise}${cursor}", "try..except (RuntimeError, ), e", "%stry:%s%s%s%sfinally:%s%s%s${pass}", "try..finally", "%sif ${True}:%s%s%s%selse:%s%s%s${pass}", "if..else", "%swhile ${True}:%s%s%s%s%s", "while", "%sfor ${item} in ${collection}:%s%s%s%s%s${cursor}", "for", "%sif ${True}:%s%s%s%s%s${cursor}", "if", "%swith ${var}:%s%s%s%s%s${cursor}", "with", }; /** * @see org.python.pydev.editor.correctionassist.heuristics.IAssistProps#isValid(org.python.pydev.core.docutils.PySelection) */ @Override public boolean isValid(PySelection ps, String sel, PyEdit edit, int offset) { return ps.getTextSelection().getLength() > 0; } @Override public List<Object> getCodeCompletionProposals(ITextViewer viewer, CompletionRequest request) throws CoreException, BadLocationException { throw new RuntimeException("Not implemented: completions should be gotten from the IAssistProps interface."); } }