/* * Copyright 2000-2015 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.lang.dart.ide.refactoring.introduce; import com.google.common.collect.Lists; import com.intellij.CommonBundle; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.SelectionModel; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pass; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.IntroduceTargetChooser; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.introduce.inplace.OccurrencesChooser; import com.intellij.refactoring.ui.NameSuggestionsField; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.ui.JBUI; import com.jetbrains.lang.dart.assists.AssistUtils; import com.jetbrains.lang.dart.assists.DartSourceEditException; import com.jetbrains.lang.dart.ide.refactoring.ServerExtractLocalVariableRefactoring; import com.jetbrains.lang.dart.ide.refactoring.ServerRefactoringDialog; import com.jetbrains.lang.dart.ide.refactoring.status.RefactoringStatus; import com.jetbrains.lang.dart.psi.DartExpression; import com.jetbrains.lang.dart.sdk.DartSdk; import org.dartlang.analysis.server.protocol.SourceChange; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.util.List; public class DartServerExtractLocalVariableHandler implements RefactoringActionHandler { @Override public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { } @Override public void invoke(final @NotNull Project project, final Editor editor, PsiFile file, DataContext dataContext) { final DartSdk sdk = DartSdk.getDartSdk(project); if (sdk == null || StringUtil.compareVersionNumbers(sdk.getVersion(), "1.14") < 0) { new DartIntroduceVariableHandler().invoke(project, editor, file, dataContext); return; } if (editor == null || file == null) { return; } if (!CommonRefactoringUtil.checkReadOnlyStatus(file)) { return; } new ExtractLocalVariableProcessor(project, editor, file).perform(); } } class ExtractLocalVariableProcessor { final @NotNull Project project; final @NotNull Editor editor; final @NotNull PsiFile file; ServerExtractLocalVariableRefactoring refactoring; ExtractLocalVariableProcessor(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { this.project = project; this.editor = editor; this.file = file; } public void perform() { final SelectionModel selectionModel = editor.getSelectionModel(); final int offset = selectionModel.getSelectionStart(); final int length = selectionModel.getSelectionEnd() - offset; // create refactoring createRefactoring(offset, length); if (refactoring == null) { return; } // prepare expressions final java.util.List<DartExpression> expressions; { final int[] offsets = refactoring.getCoveringExpressionOffsets(); final int[] lengths = refactoring.getCoveringExpressionLengths(); expressions = getDartExpressions(offsets, lengths); if (expressions == null) { return; } } // select the expression to extract if (expressions.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { performOnExpression(expressions.get(0)); } else if (expressions.size() > 1) { IntroduceTargetChooser.showChooser(editor, expressions, new Pass<DartExpression>() { @Override public void pass(DartExpression expression) { performOnExpression(expression); } }, PsiElement::getText); } } private void createRefactoring(int offset, int length) { refactoring = new ServerExtractLocalVariableRefactoring(project, file.getVirtualFile(), offset, length); final RefactoringStatus initialStatus = refactoring.checkInitialConditions(); if (showMessageIfError(initialStatus)) { refactoring = null; } } @Nullable private DartExpression findExpressionWithRange(int offset, int length) { return PsiTreeUtil.findElementOfClassAtRange(file, offset, offset + length, DartExpression.class); } @Nullable private List<DartExpression> getDartExpressions(int[] offsets, int[] lengths) { final List<DartExpression> expressions = Lists.newArrayList(); for (int i = 0; i < offsets.length; i++) { final DartExpression expression = findExpressionWithRange(offsets[i], lengths[i]); if (expression == null) { return null; } expressions.add(expression); } return expressions; } private void performInPlace() { final String[] names = refactoring.getNames(); if (names.length != 0) { refactoring.setName(names[0]); } // validate final status { final RefactoringStatus finalConditions = refactoring.checkFinalConditions(); if (showMessageIfError(finalConditions)) { return; } } // Apply the change. ApplicationManager.getApplication().runWriteAction(() -> { final SourceChange change = refactoring.getChange(); assert change != null; try { AssistUtils.applySourceChange(project, change, true); } catch (DartSourceEditException e) { CommonRefactoringUtil.showErrorHint(project, editor, e.getMessage(), CommonBundle.getErrorTitle(), null); } }); } private void performOnElementOccurrences() { if (editor.getSettings().isVariableInplaceRenameEnabled()) { performInPlace(); } else { new DartServerExtractLocalVariableDialog(project, editor, refactoring).showAndGet(); } } private void performOnExpression(DartExpression expression) { final int offset = expression.getTextOffset(); final int length = expression.getTextLength(); createRefactoring(offset, length); if (refactoring == null) { return; } // prepare occurrences final java.util.List<DartExpression> occurrences; { final int[] occurrencesOffsets = refactoring.getOccurrencesOffsets(); final int[] occurrencesLengths = refactoring.getOccurrencesLengths(); occurrences = getDartExpressions(occurrencesOffsets, occurrencesLengths); if (occurrences == null) { return; } } // handle occurrences OccurrencesChooser.<DartExpression>simpleChooser(editor) .showChooser(expression, occurrences, new Pass<OccurrencesChooser.ReplaceChoice>() { @Override public void pass(OccurrencesChooser.ReplaceChoice replaceChoice) { refactoring.setExtractAll(replaceChoice == OccurrencesChooser.ReplaceChoice.ALL); performOnElementOccurrences(); } }); } private boolean showMessageIfError(@Nullable final RefactoringStatus status) { if (status == null) { return true; } if (status.hasError()) { final String message = status.getMessage(); assert message != null; CommonRefactoringUtil.showErrorHint(project, editor, message, CommonBundle.getErrorTitle(), null); return true; } return false; } } class DartServerExtractLocalVariableDialog extends ServerRefactoringDialog<ServerExtractLocalVariableRefactoring> { @NotNull final ServerExtractLocalVariableRefactoring myRefactoring; private final NameSuggestionsField myVariableNameField; public DartServerExtractLocalVariableDialog(@NotNull Project project, @NotNull Editor editor, @NotNull ServerExtractLocalVariableRefactoring refactoring) { super(project, editor, refactoring); myRefactoring = refactoring; final String[] names = refactoring.getNames(); myVariableNameField = new NameSuggestionsField(names, project); setTitle("Extract Local Variable"); init(); final String name = StringUtil.notNullize(ArrayUtil.getFirstElement(names), "name"); myRefactoring.setName(name); myVariableNameField.addDataChangedListener(() -> { final String name1 = myVariableNameField.getEnteredName(); myRefactoring.setName(name1); }); } @Override protected JComponent createCenterPanel() { return null; } @Override protected JComponent createNorthPanel() { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbConstraints = new GridBagConstraints(); gbConstraints.insets = JBUI.insetsBottom(4); gbConstraints.gridx = 0; gbConstraints.gridy = 0; gbConstraints.gridwidth = 1; gbConstraints.weightx = 0; gbConstraints.weighty = 0; gbConstraints.fill = GridBagConstraints.NONE; gbConstraints.anchor = GridBagConstraints.WEST; JLabel nameLabel = new JLabel(); panel.add(nameLabel, gbConstraints); nameLabel.setText("Name:"); gbConstraints.insets = JBUI.insets(0, 4, 4, 0); gbConstraints.gridx = 1; gbConstraints.gridy = 0; gbConstraints.gridwidth = GridBagConstraints.REMAINDER; gbConstraints.weightx = 1; gbConstraints.weighty = 0; gbConstraints.fill = GridBagConstraints.BOTH; gbConstraints.anchor = GridBagConstraints.WEST; panel.add(myVariableNameField, gbConstraints); myVariableNameField.setPreferredSize(new Dimension(300, myVariableNameField.getPreferredSize().height)); return panel; } @Nullable @Override public JComponent getPreferredFocusedComponent() { return myVariableNameField; } }