All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jetbrains.python.codeInsight.intentions.ImportToggleAliasIntention Maven / Gradle / Ivy

Go to download

A packaging of the IntelliJ Community Edition python-community library. This is release number 1 of trunk branch 142.

The newest version!
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jetbrains.python.codeInsight.intentions;

import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.lang.ASTNode;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.InputValidator;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiLanguageInjectionHost;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static com.jetbrains.python.codeInsight.intentions.DeclarationConflictChecker.findDefinitions;
import static com.jetbrains.python.codeInsight.intentions.DeclarationConflictChecker.showConflicts;
import static com.jetbrains.python.psi.PyUtil.sure;

/**
 * Adds an alias to "import foo" or "from foo import bar" import elements, or removes it if it's already present. 
 * User: dcheryasov
 * Date: Oct 9, 2009 6:07:19 PM
 */
public class ImportToggleAliasIntention implements IntentionAction {
  private static class IntentionState {
    private PyImportElement myImportElement;
    private PyFromImportStatement myFromImportStatement;
    private PyImportStatement myImportStatement;
    private String myAlias;

    private static IntentionState fromContext(Editor editor, PsiFile file) {
      IntentionState state = new IntentionState();
      state.myImportElement  = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyImportElement.class);
      if (state.myImportElement != null && state.myImportElement.isValid()) {
        PyTargetExpression target = state.myImportElement.getAsNameElement();
        if (target != null && target.isValid()) state.myAlias = target.getName();
        else state.myAlias = null;
        state.myFromImportStatement = PsiTreeUtil.getParentOfType(state.myImportElement, PyFromImportStatement.class);
        state.myImportStatement = PsiTreeUtil.getParentOfType(state.myImportElement, PyImportStatement.class);
      }
      return state;
    }

    public boolean isAvailable() {
      if (myFromImportStatement != null) {
        if (!myFromImportStatement.isValid() || myFromImportStatement.isFromFuture()) {
          return false;
        }
      }
      else {
        if (myImportStatement == null || !myImportStatement.isValid()) {
          return false;
        }
      }
      final PyReferenceExpression referenceExpression = myImportElement.getImportReferenceExpression();
      if (referenceExpression == null || referenceExpression.getReference().resolve() == null) {
        return false;
      }
      return true;
    }

    public String getText() {
      String add_name = "Add alias";
      if (myImportElement != null) {
        PyReferenceExpression refex = myImportElement.getImportReferenceExpression();
        if (refex != null) {
          add_name = PyBundle.message("INTN.add.alias.for.import.$0", refex.getText());
        }
      }
      return myAlias == null? add_name : PyBundle.message("INTN.remove.alias.for.import.$0", myAlias);
    }
  }

  private String myLastText;


  @NotNull
  public String getText() {
    return myLastText;
  }

  @NotNull
  public String getFamilyName() {
    return PyBundle.message("INTN.Family.toggle.import.alias");
  }

  public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
    if (!(file instanceof PyFile)) {
      return false;
    }

    IntentionState state = IntentionState.fromContext(editor, file);
    myLastText = state.getText();
    return state.isAvailable();
  }

  public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
    // sanity check: isAvailable must have set it.
    final IntentionState state = IntentionState.fromContext(editor, file);
    //
    final String target_name; // we set in in the source
    final String remove_name; // we replace it in the source
    PyReferenceExpression reference = sure(state.myImportElement.getImportReferenceExpression());
    // search for references to us with the right name
    try {
      String imported_name = PyPsiUtils.toPath(reference);
      if (state.myAlias != null) {
        // have to remove alias, rename everything to original
        target_name = imported_name;
        remove_name = state.myAlias;
      }
      else {
        // ask for and add alias
        Application application = ApplicationManager.getApplication();
        if (application != null && !application.isUnitTestMode()) {
          String alias = Messages.showInputDialog(project, PyBundle.message("INTN.alias.for.$0.dialog.title", imported_name),
                                                  "Add Alias", Messages.getQuestionIcon(), "", new InputValidator() {
            @Override
            public boolean checkInput(String inputString) {
              return PyNames.isIdentifier(inputString);
            }

            @Override
            public boolean canClose(String inputString) {
              return PyNames.isIdentifier(inputString);
            }
          });
          if (alias == null) {
            return;
          }
          target_name = alias;
        }
        else { // test mode
          target_name = "alias";
        }
        remove_name = imported_name;
      }
      final PsiElement referee = reference.getReference().resolve();
      if (referee != null && imported_name != null) {
        final Collection references = new ArrayList();
        final ScopeOwner scope = PsiTreeUtil.getParentOfType(state.myImportElement, ScopeOwner.class);
        PsiTreeUtil.processElements(scope, new PsiElementProcessor() {
          public boolean execute(@NotNull PsiElement element) {
            getReferences(element);
            if (element instanceof PyStringLiteralExpression) {
              final PsiLanguageInjectionHost host = (PsiLanguageInjectionHost)element;
              final List> files =
                InjectedLanguageManager.getInstance(project).getInjectedPsiFiles(host);
              if (files != null) {
                for (Pair pair : files) {
                  final PsiElement first = pair.getFirst();
                  if (first instanceof ScopeOwner) {
                    final ScopeOwner scopeOwner = (ScopeOwner)first;
                    PsiTreeUtil.processElements(scopeOwner, new PsiElementProcessor() {
                      public boolean execute(@NotNull PsiElement element) {
                        getReferences(element);
                        return true;
                      }
                    });
                  }
                }
              }
            }
            return true;
          }

          private void getReferences(PsiElement element) {
            if (element instanceof PyReferenceExpression && PsiTreeUtil.getParentOfType(element,
                                                                                        PyImportElement.class) == null) {
              PyReferenceExpression ref = (PyReferenceExpression)element;
               if (remove_name.equals(PyPsiUtils.toPath(ref))) {  // filter out other names that might resolve to our target
                PsiElement resolved = ref.getReference().resolve();
                if (resolved == referee) references.add(ref.getReference());
              }
            }
          }
        });
        // no references here is OK by us.
        if (showConflicts(project, findDefinitions(target_name, references, null), target_name, null)) {
          return; // got conflicts
        }

        // alter the import element
        PyElementGenerator generator = PyElementGenerator.getInstance(project);
        final LanguageLevel languageLevel = LanguageLevel.forElement(state.myImportElement);
        if (state.myAlias != null) {
          // remove alias
          ASTNode node = sure(state.myImportElement.getNode());
          ASTNode parent = sure(node.getTreeParent());
          node = sure(node.getFirstChildNode()); // this is the reference
          node = sure(node.getTreeNext()); // things past the reference: space, 'as', and alias
          parent.removeRange(node, null);
        }
        else {
          // add alias
          ASTNode my_ielt_node = sure(state.myImportElement.getNode());
          PyImportElement fountain = generator.createFromText(languageLevel, PyImportElement.class, "import foo as "+target_name, new int[]{0,2});
          ASTNode graft_node = sure(fountain.getNode()); // at import elt
          graft_node = sure(graft_node.getFirstChildNode()); // at ref
          graft_node = sure(graft_node.getTreeNext()); // space
          my_ielt_node.addChild((ASTNode)graft_node.clone());
          graft_node = sure(graft_node.getTreeNext()); // 'as'
          my_ielt_node.addChild((ASTNode)graft_node.clone());
          graft_node = sure(graft_node.getTreeNext()); // space
          my_ielt_node.addChild((ASTNode)graft_node.clone());
          graft_node = sure(graft_node.getTreeNext()); // alias
          my_ielt_node.addChild((ASTNode)graft_node.clone());
        }
        // alter references
        for (PsiReference ref : references) {
          ASTNode ref_name_node = sure(sure(ref.getElement()).getNode());
          ASTNode parent = sure(ref_name_node.getTreeParent());
          ASTNode new_name_node = generator.createExpressionFromText(languageLevel, target_name).getNode();
          assert new_name_node != null;
          parent.replaceChild(ref_name_node, new_name_node);
        }
      }
    }
    catch (IncorrectOperationException ignored) {
      PyUtil.showBalloon(project, PyBundle.message("QFIX.action.failed"), MessageType.WARNING);
    }
  }

  public boolean startInWriteAction() {
    return true;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy