/** * Copyright (c) 2005-2011 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. */ package com.python.pydev.codecompletion.simpleassist; import java.util.HashSet; import java.util.Set; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.ITextViewer; import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2; import org.eclipse.jface.text.contentassist.IContextInformation; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.editor.autoedit.PyAutoIndentStrategy; import org.python.pydev.editor.codecompletion.PyCompletionProposal; import com.aptana.shared_core.structure.Tuple; import com.aptana.shared_core.utils.DocCmd; import com.python.pydev.codecompletion.ui.CodeCompletionPreferencesPage; /** * by using this assist (with the extension), we are able to just validate it (without recomputing all completions each time). * * They are only recomputed on backspace... * * @author Fabio */ public class SimpleAssistProposal extends PyCompletionProposal implements ICompletionProposalExtension2 { private static final Set<String> ADD_SPACE_AND_COLOR_AFTER = new HashSet<String>(); static { ADD_SPACE_AND_COLOR_AFTER.add("if"); ADD_SPACE_AND_COLOR_AFTER.add("class"); ADD_SPACE_AND_COLOR_AFTER.add("for"); ADD_SPACE_AND_COLOR_AFTER.add("while"); ADD_SPACE_AND_COLOR_AFTER.add("with"); } private static final Set<String> ADD_SPACE_AFTER = new HashSet<String>(); static { ADD_SPACE_AFTER.add("and"); ADD_SPACE_AFTER.add("assert"); ADD_SPACE_AFTER.add("del"); ADD_SPACE_AFTER.add("def"); ADD_SPACE_AFTER.add("from"); ADD_SPACE_AFTER.add("global"); ADD_SPACE_AFTER.add("import"); ADD_SPACE_AFTER.add("lambda"); ADD_SPACE_AFTER.add("not"); ADD_SPACE_AFTER.add("raise"); ADD_SPACE_AFTER.add("yield"); ADD_SPACE_AFTER.add("print"); //Py3K will be print() and won't be affected //not there by default but covered for ADD_SPACE_AFTER.add("or"); ADD_SPACE_AFTER.add("as"); ADD_SPACE_AFTER.add("in"); ADD_SPACE_AFTER.add("is"); } public SimpleAssistProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, int priority) { super(replacementString, replacementOffset, replacementLength, cursorPosition, priority); } public SimpleAssistProposal(String replacementString, int replacementOffset, int replacementLength, int cursorPosition, Image image, String displayString, IContextInformation contextInformation, String additionalProposalInfo, int priority) { super(replacementString, replacementOffset, replacementLength, cursorPosition, image, displayString, contextInformation, additionalProposalInfo, priority); } private int changeInCursorPos = 0; public Point getSelection(IDocument document) { return new Point(fReplacementOffset + fCursorPosition + changeInCursorPos, 0); } public void apply(ITextViewer viewer, char trigger, int stateMask, int offset) { try { IDocument doc = viewer.getDocument(); int dif = offset - fReplacementOffset; if (fReplacementString.equals("elif")) { doc.replace(offset, 0, fReplacementString.substring(dif)); //check if we should dedent PyAutoIndentStrategy strategy = new PyAutoIndentStrategy(); DocCmd cmd = new DocCmd(offset + fReplacementString.length() - dif, 0, " "); Tuple<String, Integer> dedented = PyAutoIndentStrategy.autoDedentElif(doc, cmd, strategy.getIndentPrefs()); doc.replace(cmd.offset, 0, " :"); //make up for the ' :' (right before ':') if (dedented != null) { changeInCursorPos = -dedented.o2 + 1; } return; } else if (fReplacementString.endsWith(":")) { //else:, finally:, except: ... //make the replacement for the 'else' String replacementString = fReplacementString.substring(0, fReplacementString.length() - 1); doc.replace(offset, 0, replacementString.substring(dif)); //dedent if needed PyAutoIndentStrategy strategy = new PyAutoIndentStrategy(); DocCmd cmd = new DocCmd(offset + replacementString.length() - dif, 0, ":"); Tuple<String, Integer> dedented = PyAutoIndentStrategy.autoDedentAfterColon(doc, cmd, strategy.getIndentPrefs()); doc.replace(cmd.offset, 0, ":"); //make up for the ':' if (dedented != null) { changeInCursorPos = -dedented.o2; } return; } else if (ADD_SPACE_AFTER.contains(fReplacementString) && CodeCompletionPreferencesPage.addSpaceWhenNeeded()) { doc.replace(offset, 0, fReplacementString.substring(dif)); doc.replace(offset + fReplacementString.length() - dif, 0, " "); //make up for the '' changeInCursorPos = 1; return; } else if (ADD_SPACE_AND_COLOR_AFTER.contains(fReplacementString) && CodeCompletionPreferencesPage.addSpaceAndColonWhenNeeded()) { //it's something as 'class', 'for', etc, which will start a new block. //create it as "class space colon" (if the colon is still not there) doc.replace(offset, 0, fReplacementString.substring(dif)); doc.replace(offset + fReplacementString.length() - dif, 0, " :"); //should we add a ':' here (basically changing ':<ENTER>' for '<SHIFT+ENTER> changeInCursorPos = 1; //make up for the ' ' return; } if (fReplacementString.equals("print()")) { changeInCursorPos = -1; } //execute default if it still hasn't returned. doc.replace(offset, 0, fReplacementString.substring(dif)); } catch (BadLocationException x) { // ignore } } public void selected(ITextViewer viewer, boolean smartToggle) { } public void unselected(ITextViewer viewer) { } public boolean validate(IDocument document, int offset, DocumentEvent event) { String[] strs = PySelection.getActivationTokenAndQual(document, offset, false); String activationToken = strs[0]; String qualifier = strs[1]; if (activationToken.equals("") && qualifier.equals("") == false) { if (fReplacementString.startsWith(qualifier) && !fReplacementString.equals(qualifier)) { return true; } } return false; } }