/* * Copyright 2013-2017 consulo.io * * 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 consulo.csharp.ide.codeInsight.actions; import java.util.Set; import javax.swing.Icon; import org.jetbrains.annotations.NotNull; import com.intellij.codeInsight.actions.OptimizeImportsProcessor; import com.intellij.codeInsight.hint.QuestionAction; import com.intellij.icons.AllIcons; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModifiableRootModel; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.openapi.ui.popup.PopupStep; import com.intellij.openapi.ui.popup.util.BaseListPopupStep; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiParserFacade; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import consulo.annotations.RequiredDispatchThread; import consulo.annotations.RequiredReadAction; import consulo.csharp.ide.codeInsight.CSharpCodeInsightSettings; import consulo.csharp.lang.psi.CSharpCodeFragment; import consulo.csharp.lang.psi.CSharpFile; import consulo.csharp.lang.psi.CSharpFileFactory; import consulo.csharp.lang.psi.CSharpReferenceExpression; import consulo.csharp.lang.psi.CSharpUsingListChild; import consulo.csharp.lang.psi.CSharpUsingNamespaceStatement; import consulo.dotnet.DotNetBundle; import consulo.dotnet.libraryAnalyzer.NamespaceReference; import consulo.dotnet.psi.DotNetQualifiedElement; import consulo.dotnet.roots.orderEntry.DotNetLibraryOrderEntryImpl; import consulo.roots.impl.ModuleRootLayerImpl; /** * @author VISTALL * @since 30.12.13. */ public class AddUsingAction implements QuestionAction { private static final Logger LOGGER = Logger.getInstance(AddUsingAction.class); private final Editor myEditor; private final Project myProject; private final PsiFile myFile; private final Set<NamespaceReference> myElements; public AddUsingAction(Editor editor, PsiFile file, Set<NamespaceReference> references) { myEditor = editor; myFile = file; myProject = file.getProject(); myElements = references; } public AddUsingAction(Editor editor, CSharpReferenceExpression ref, Set<NamespaceReference> references) { myEditor = editor; myFile = ref.getContainingFile(); myProject = ref.getProject(); myElements = references; } @NotNull @RequiredReadAction private PsiElement getElementForBeforeAdd() { if(myFile instanceof CSharpFile) { CSharpUsingListChild[] usingStatements = ((CSharpFile) myFile).getUsingStatements(); if(usingStatements.length > 0) { return ArrayUtil.getLastElement(usingStatements); } } return myFile; } @Override @RequiredDispatchThread public boolean execute() { PsiDocumentManager.getInstance(myProject).commitAllDocuments(); NamespaceReference firstCount = myElements.size() == 1 ? ContainerUtil.getFirstItem(myElements) : null; if(firstCount != null) { execute0(ContainerUtil.getFirstItem(myElements)); } else { BaseListPopupStep<NamespaceReference> step = new BaseListPopupStep<NamespaceReference>(DotNetBundle.message("add.using"), myElements.toArray(new NamespaceReference[myElements.size()])) { @Override public Icon getIconFor(NamespaceReference aValue) { return AllIcons.Nodes.Package; } @NotNull @Override public String getTextFor(NamespaceReference value) { return formatMessage(value); } @Override@RequiredDispatchThread public PopupStep onChosen(final NamespaceReference selectedValue, boolean finalChoice) { execute0(selectedValue); return FINAL_CHOICE; } }; JBPopupFactory.getInstance().createListPopup(step).showInBestPositionFor(myEditor); } return true; } @NotNull public static String formatMessage(@NotNull NamespaceReference couple) { String libraryName = couple.getLibraryName(); String namespace = couple.getNamespace(); if(libraryName == null) { return namespace; } return namespace + " from '" + libraryName + "'"; } @RequiredDispatchThread private void execute0(final NamespaceReference namespaceReference) { PsiDocumentManager.getInstance(myProject).commitAllDocuments(); new WriteCommandAction<Object>(myProject, myFile) { @Override @RequiredReadAction protected void run(Result<Object> objectResult) throws Throwable { addUsing(namespaceReference.getNamespace()); String libraryName = namespaceReference.getLibraryName(); if(libraryName != null) { Module moduleForFile = ModuleUtilCore.findModuleForPsiElement(myFile); if(moduleForFile != null) { ModuleRootManager moduleRootManager = ModuleRootManager.getInstance(moduleForFile); final ModifiableRootModel modifiableModel = moduleRootManager.getModifiableModel(); modifiableModel.addOrderEntry(new DotNetLibraryOrderEntryImpl((ModuleRootLayerImpl) moduleRootManager.getCurrentLayer(), libraryName)); new WriteCommandAction<Object>(moduleForFile.getProject()) { @Override protected void run(Result<Object> objectResult) throws Throwable { modifiableModel.commit(); } }.execute(); } } } }.execute(); } @RequiredReadAction private void addUsing(String qName) { PsiElement elementForBeforeAdd = getElementForBeforeAdd(); CSharpUsingNamespaceStatement newStatement = CSharpFileFactory.createUsingNamespaceStatement(myProject, qName); if(myFile instanceof CSharpCodeFragment) { ((CSharpCodeFragment) myFile).addUsingChild(newStatement); } else if(elementForBeforeAdd instanceof CSharpUsingListChild) { addUsingStatementAfter(elementForBeforeAdd, newStatement); } else if(elementForBeforeAdd instanceof CSharpFile) { DotNetQualifiedElement[] members = ((CSharpFile) elementForBeforeAdd).getMembers(); PsiElement firstChild = members.length > 0 ? members[0] : elementForBeforeAdd.getFirstChild(); assert firstChild != null; PsiElement usingStatementNew = elementForBeforeAdd.addBefore(newStatement, firstChild); PsiElement whiteSpaceFromText = PsiParserFacade.SERVICE.getInstance(myProject).createWhiteSpaceFromText("\n\n"); elementForBeforeAdd.addAfter(whiteSpaceFromText, usingStatementNew); } int caretOffset = myEditor.getCaretModel().getOffset(); RangeMarker caretMarker = myEditor.getDocument().createRangeMarker(caretOffset, caretOffset); int colByOffset = myEditor.offsetToLogicalPosition(caretOffset).column; int col = myEditor.getCaretModel().getLogicalPosition().column; int virtualSpace = col == colByOffset ? 0 : col - colByOffset; int line = myEditor.getCaretModel().getLogicalPosition().line; LogicalPosition pos = new LogicalPosition(line, 0); myEditor.getCaretModel().moveToLogicalPosition(pos); try { if(CSharpCodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) { Document document = myEditor.getDocument(); PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document); new OptimizeImportsProcessor(myProject, psiFile).runWithoutProgress(); } } catch(IncorrectOperationException e) { AddUsingAction.LOGGER.error(e); } line = myEditor.getCaretModel().getLogicalPosition().line; LogicalPosition pos1 = new LogicalPosition(line, col); myEditor.getCaretModel().moveToLogicalPosition(pos1); if(caretMarker.isValid()) { LogicalPosition pos2 = myEditor.offsetToLogicalPosition(caretMarker.getStartOffset()); int newCol = pos2.column + virtualSpace; myEditor.getCaretModel().moveToLogicalPosition(new LogicalPosition(pos2.line, newCol)); myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); } } @RequiredReadAction private static void addUsingStatementAfter(@NotNull PsiElement afterElement, @NotNull CSharpUsingNamespaceStatement newStatement) { Project project = afterElement.getProject(); PsiElement parent = afterElement.getParent(); PsiElement whiteSpaceFromText = PsiParserFacade.SERVICE.getInstance(project).createWhiteSpaceFromText("\n"); parent.addAfter(whiteSpaceFromText, afterElement); parent.addAfter(newStatement, afterElement.getNode().getTreeNext().getPsi()); } }