/*
* 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;
import com.intellij.CommonBundle;
import com.intellij.lang.Language;
import com.intellij.lang.refactoring.InlineActionHandler;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.inline.InlineOptionsDialog;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.jetbrains.lang.dart.DartLanguage;
import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService;
import com.jetbrains.lang.dart.analyzer.DartServerData.DartNavigationRegion;
import com.jetbrains.lang.dart.analyzer.DartServerData.DartNavigationTarget;
import com.jetbrains.lang.dart.assists.AssistUtils;
import com.jetbrains.lang.dart.assists.DartSourceEditException;
import com.jetbrains.lang.dart.ide.refactoring.status.RefactoringStatus;
import org.dartlang.analysis.server.protocol.ElementKind;
import org.dartlang.analysis.server.protocol.SourceChange;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class DartInlineHandler extends InlineActionHandler {
@Override
public boolean canInlineElement(PsiElement element) {
return false;
}
@Override
public boolean canInlineElementInEditor(PsiElement element, Editor editor) {
final InlineRefactoringContext context = findContext(editor);
if (context == null) return false;
final String kind = context.kind;
return ElementKind.LOCAL_VARIABLE.equals(kind) ||
ElementKind.METHOD.equals(kind) ||
ElementKind.FUNCTION.equals(kind) ||
ElementKind.GETTER.equals(kind) ||
ElementKind.SETTER.equals(kind);
}
@Override
public void inlineElement(@NotNull final Project project, @Nullable final Editor editor, PsiElement element) {
final InlineRefactoringContext context = findContext(editor);
if (context == null) {
return;
}
// create refactoring
final ServerRefactoring refactoring;
if (ElementKind.LOCAL_VARIABLE.equals(context.kind)) {
refactoring = new ServerInlineLocalRefactoring(project, context.virtualFile, context.offset, 0);
}
else {
refactoring = new ServerInlineMethodRefactoring(project, context.virtualFile, context.offset, 0);
}
// validate initial status
{
final RefactoringStatus initialConditions = refactoring.checkInitialConditions();
if (showMessageIfError(editor, initialConditions)) {
return;
}
}
// configure using dialog
if (refactoring instanceof ServerInlineMethodRefactoring) {
boolean dialogOK = new InlineMethodDialog(project, element, (ServerInlineMethodRefactoring)refactoring).showAndGet();
if (!dialogOK) {
return;
}
}
// validate final status
{
final RefactoringStatus finalConditions = refactoring.checkFinalConditions();
if (showMessageIfError(editor, finalConditions)) {
return;
}
}
// Apply the change.
ApplicationManager.getApplication().runWriteAction(() -> {
final SourceChange change = refactoring.getChange();
assert change != null;
try {
AssistUtils.applySourceChange(project, change, false);
}
catch (DartSourceEditException e) {
CommonRefactoringUtil.showErrorHint(project, editor, e.getMessage(), CommonBundle.getErrorTitle(), null);
}
});
}
@Override
public boolean isEnabledForLanguage(Language l) {
return l == DartLanguage.INSTANCE;
}
@Override
public boolean isEnabledOnElement(PsiElement element, @Nullable Editor editor) {
return canInlineElementInEditor(element, editor);
}
@Nullable
static private InlineRefactoringContext findContext(@Nullable Editor editor) {
if (editor == null) {
return null;
}
// prepare project
final Project project = editor.getProject();
if (project == null) {
return null;
}
// prepare files
final Document document = editor.getDocument();
final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
if (psiFile == null) {
return null;
}
final VirtualFile virtualFile = psiFile.getVirtualFile();
// prepare navigation regions
final int offset = editor.getCaretModel().getOffset();
final List<DartNavigationRegion> navigationRegions = DartAnalysisServerService.getInstance(project).getNavigation(virtualFile);
// find the navigation region
for (DartNavigationRegion region : navigationRegions) {
if (region.getOffset() <= offset && offset <= region.getOffset() + region.getLength()) {
final List<DartNavigationTarget> targets = region.getTargets();
final String kind = targets.get(0).getKind();
return new InlineRefactoringContext(virtualFile, offset, kind);
}
}
// fail
return null;
}
private static boolean showMessageIfError(@Nullable Editor editor, @Nullable final RefactoringStatus status) {
if (status == null) {
return true;
}
if (status.hasError()) {
final String message = status.getMessage();
assert message != null;
if (editor != null) {
CommonRefactoringUtil.showErrorHint(editor.getProject(), editor, message, CommonBundle.getErrorTitle(), null);
}
return true;
}
return false;
}
}
class InlineRefactoringContext {
final VirtualFile virtualFile;
final String kind;
final int offset;
InlineRefactoringContext(VirtualFile virtualFile, int offset, String kind) {
this.virtualFile = virtualFile;
this.kind = kind;
this.offset = offset;
}
}
class InlineMethodDialog extends InlineOptionsDialog {
public static final String REFACTORING_NAME = RefactoringBundle.message("inline.method.title");
private final ServerInlineMethodRefactoring refactoring;
protected InlineMethodDialog(Project project, PsiElement element, ServerInlineMethodRefactoring refactoring) {
super(project, true, element);
this.refactoring = refactoring;
setTitle(REFACTORING_NAME);
myInvokedOnReference = !refactoring.isDeclaration();
init();
}
@Override
protected void doAction() {
if (!isInlineThisOnly()) {
refactoring.setInlineAll(true);
refactoring.setDeleteSource(true);
}
close(DialogWrapper.OK_EXIT_CODE);
}
@Override
protected String getBorderTitle() {
return RefactoringBundle.message("inline.method.border.title"); // not used actually
}
@Override
protected String getInlineAllText() {
return "Inline all references and remove the method";
}
@Override
protected String getInlineThisText() {
return "Inline this reference and leave the method";
}
@Override
protected String getNameLabelText() {
return "Method " + refactoring.getFullName();
}
@Override
protected boolean hasPreviewButton() {
return false;
}
@Override
protected boolean isInlineThis() {
return !refactoring.isDeclaration();
}
}