org.ballerinalang.plugins.idea.completion.AutoImportInsertHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.plugins.idea.completion.AutoImportInsertHandler.java

Source

/*
 *  Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 *  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 org.ballerinalang.plugins.idea.completion;

import com.intellij.codeInsight.AutoPopupController;
import com.intellij.codeInsight.completion.InsertHandler;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorModificationUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.ballerinalang.plugins.idea.psi.impl.BallerinaElementFactory;
import org.ballerinalang.plugins.idea.psi.impl.BallerinaPsiImplUtil;
import org.ballerinalang.plugins.idea.util.BallerinaUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * Provides auto import support.
 */
public class AutoImportInsertHandler implements InsertHandler<LookupElement> {

    public static final InsertHandler<LookupElement> INSTANCE = new AutoImportInsertHandler(false);
    public static final InsertHandler<LookupElement> INSTANCE_WITH_AUTO_POPUP = new AutoImportInsertHandler(true);
    public static final InsertHandler<LookupElement> INSTANCE_WITH_ALIAS = new AutoImportInsertHandler(false, true);
    public static final InsertHandler<LookupElement> INSTANCE_WITH_ALIAS_WITH_POPUP = new AutoImportInsertHandler(
            true, true);

    private final boolean suggestAlias;
    private final boolean myTriggerAutoPopup;
    private String alias = null;

    private AutoImportInsertHandler(boolean triggerAutoPopup) {
        this(triggerAutoPopup, false);
    }

    private AutoImportInsertHandler(boolean triggerAutoPopup, boolean suggestAlias) {
        this.suggestAlias = suggestAlias;
        myTriggerAutoPopup = triggerAutoPopup;
    }

    @Override
    public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement item) {
        ApplicationManager.getApplication().invokeLater(() -> {
            CommandProcessor.getInstance().runUndoTransparentAction(() -> {
                PsiElement element = item.getPsiElement();
                if (element == null || !(element instanceof PsiDirectory)) {
                    return;
                }
                Editor editor = context.getEditor();
                Project project = editor.getProject();

                if (suggestAlias) {
                    alias = Messages.showInputDialog(project,
                            "Package '" + ((PsiDirectory) element).getName()
                                    + "' already imported. Please enter an alias:",
                            "Enter Alias", Messages.getInformationIcon());
                    if (alias == null || alias.isEmpty()) {
                        Messages.showErrorDialog("Alias cannot be null or empty.", "Error");
                        return;
                    }
                }
                // Import the package.
                autoImport(context, element, alias);
                if (project == null) {
                    return;
                }
                if (!isCompletionCharAtSpace(editor)) {
                    if (suggestAlias) {
                        // InsertHandler inserts the old package name. So we need to change it to the new alias.
                        PsiFile file = context.getFile();
                        PsiElement currentPackageName = file.findElementAt(context.getStartOffset());
                        if (currentPackageName != null) {
                            if (alias == null || alias.isEmpty()) {
                                return;
                            }
                            ApplicationManager.getApplication().runWriteAction(() -> {
                                // Add a new identifier node.
                                PsiElement identifier = BallerinaElementFactory.createIdentifier(project, alias);
                                currentPackageName.getParent().addBefore(identifier, currentPackageName);
                                // Delete the current identifier node.
                                currentPackageName.delete();
                            });
                        }
                    }
                    if (myTriggerAutoPopup) {
                        ApplicationManager.getApplication().runWriteAction(() -> {
                            PsiDocumentManager.getInstance(project)
                                    .doPostponedOperationsAndUnblockDocument(editor.getDocument());
                            EditorModificationUtil.insertStringAtCaret(editor, ":");
                            PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());
                        });
                    }
                } else {
                    editor.getCaretModel().moveToOffset(editor.getCaretModel().getOffset() + 1);
                }
                // Invoke the popup.
                if (myTriggerAutoPopup) {
                    // We need to invoke the popup with a delay. Otherwise it might not show.
                    ApplicationManager.getApplication().invokeLater(
                            () -> AutoPopupController.getInstance(project).autoPopupMemberLookup(editor, null));
                }
            });
        });
    }

    @Nullable
    private void autoImport(@NotNull InsertionContext context, @NotNull PsiElement element,
            @Nullable String alias) {
        PsiFile file = context.getFile();
        if (!(element instanceof PsiDirectory)) {
            return;
        }
        String importPath = BallerinaUtil.suggestPackageNameForDirectory(((PsiDirectory) element));
        if (StringUtil.isEmpty(importPath)) {
            return;
        }
        ApplicationManager.getApplication().runWriteAction(() -> {
            BallerinaPsiImplUtil.addImport(file, importPath, alias);
        });
    }

    private static boolean isCompletionCharAtSpace(Editor editor) {
        final int startOffset = editor.getCaretModel().getOffset();
        final Document document = editor.getDocument();
        return document.getTextLength() > startOffset && document.getCharsSequence().charAt(startOffset) == ':';
    }
}