/** * 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 org.python.pydev.editor.actions; import java.util.Map; import org.eclipse.jface.action.IAction; import org.eclipse.jface.text.BadLocationException; 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.Region; import org.eclipse.jface.text.TextSelection; import org.eclipse.ui.texteditor.ITextEditor; import org.python.pydev.core.docutils.PySelection; import org.python.pydev.core.docutils.PythonPairMatcher; import org.python.pydev.core.docutils.StringUtils; import org.python.pydev.core.log.Log; import org.python.pydev.core.structure.FastStack; import org.python.pydev.editor.PyEdit; import org.python.pydev.parser.fastparser.ScopesParser; import org.python.pydev.parser.fastparser.ScopesParser.Scopes; import com.aptana.shared_core.structure.Tuple; /** * @author fabioz * */ public class PyScopeSelection extends PyAction { public static final String SELECTION_SCOPE_CACHE = "_SELECTION_SCOPE_CACHE_"; public void run(IAction action) { try { ITextEditor textEditor = getTextEditor(); IDocument doc = textEditor.getDocumentProvider().getDocument(textEditor.getEditorInput()); ITextSelection selection = (ITextSelection) textEditor.getSelectionProvider().getSelection(); perform(doc, selection); } catch (Exception e) { Log.log(e); } } public void perform(IDocument doc, ITextSelection selection) { PyEdit pyEdit = getPyEdit(); FastStack<IRegion> cache = getCache(pyEdit); Region initialRegion = new Region(selection.getOffset(), selection.getLength()); if (cache.size() > 0) { IRegion peek = cache.peek(); if (!peek.equals(initialRegion)) { cache.clear(); } } if (cache.size() == 0) { cache.push(initialRegion); } ITextSelection newSelection = getNewSelection(doc, selection); if (initialRegion.equals(new Region(newSelection.getOffset(), newSelection.getLength()))) { return; } pyEdit.setSelection(newSelection.getOffset(), newSelection.getLength()); cache.push(new Region(newSelection.getOffset(), newSelection.getLength())); } public ITextSelection getNewSelection(IDocument doc, ITextSelection selection) { try { PySelection ps = new PySelection(doc, selection); String selectedText = ps.getSelectedText(); if (selectedText.length() == 0) { //Select the current word Tuple<String, Integer> currToken = ps.getCurrToken(); if (currToken.o1.length() > 0) { return new TextSelection(currToken.o2, currToken.o1.length()); } else { char c = '\0'; try { c = ps.getCharAtCurrentOffset(); } catch (BadLocationException e) { //Ignore (end of document is selected). } if (StringUtils.isClosingPeer(c)) { PythonPairMatcher pairMatcher = new PythonPairMatcher(); int openingOffset = pairMatcher.searchForOpeningPeer(ps.getAbsoluteCursorOffset(), StringUtils.getPeer(c), c, doc); if (openingOffset >= 0) { return new TextSelection(openingOffset, ps.getAbsoluteCursorOffset() - openingOffset + 1); } } } } else { //There's already some text selected boolean tryMatchWithQualifier = true; boolean hasDotSelected = false; //value only valid if tryMatchWithQualifier == true! for (int i = 0; i < selectedText.length(); i++) { char c = selectedText.charAt(i); if (c == '.') { hasDotSelected = true; continue; } if (!Character.isJavaIdentifierPart(c)) { tryMatchWithQualifier = false; break; } } if (tryMatchWithQualifier) { Tuple<String, Integer> currToken = ps.getCurrToken(); if (!hasDotSelected && !currToken.o1.equals(selectedText)) { return new TextSelection(currToken.o2, currToken.o1.length()); } else { //The selected text is not equal to the current token, let's see if we have to select a full dotted word Tuple<String, Integer> currDottedToken = ps.getCurrDottedStatement(); if (!currDottedToken.o1.equals(selectedText)) { return new TextSelection(currDottedToken.o2, currDottedToken.o1.length()); } } } } Scopes scopes = ScopesParser.createScopes(doc); // System.out.println(scopes.debugString(doc)); IRegion scope = scopes.getScopeForSelection(selection.getOffset(), selection.getLength()); if (scope != null) { return new TextSelection(scope.getOffset(), scope.getLength()); } } catch (BadLocationException e) { Log.log(e); } return selection; } private static String getCurrentSelectionCacheKey(PyEdit pyEdit) { IDocument doc = pyEdit.getDocument(); int length = doc.getLength(); String key = Integer.toString(length); if (doc instanceof IDocumentExtension4) { IDocumentExtension4 document = (IDocumentExtension4) doc; long modificationStamp = document.getModificationStamp(); key += " - " + modificationStamp; } return key; } @SuppressWarnings("unchecked") public static FastStack<IRegion> getCache(PyEdit pyEdit) { Map<String, Object> cache = pyEdit.getCache(); String key = getCurrentSelectionCacheKey(pyEdit); try { Tuple<String, FastStack<IRegion>> object = (Tuple<String, FastStack<IRegion>>) cache .get(PyScopeSelection.SELECTION_SCOPE_CACHE); if (object != null) { if (key.equals(object.o1)) { return object.o2; } } } catch (Exception e) { Log.log(e); } FastStack<IRegion> stack = new FastStack<IRegion>(20); cache.put(PyScopeSelection.SELECTION_SCOPE_CACHE, new Tuple<String, FastStack<IRegion>>(key, stack)); return stack; } }