com.intellij.debugger.ui.DebuggerEditorImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.debugger.ui.DebuggerEditorImpl.java

Source

/*
 * Copyright 2000-2013 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.intellij.debugger.ui;

import java.awt.BorderLayout;
import java.awt.event.MouseEvent;
import java.lang.ref.WeakReference;
import java.util.List;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.engine.evaluation.CodeFragmentFactory;
import com.intellij.debugger.engine.evaluation.CodeFragmentFactoryContextWrapper;
import com.intellij.debugger.engine.evaluation.DefaultCodeFragmentFactory;
import com.intellij.debugger.engine.evaluation.TextWithImports;
import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.impl.PositionUtil;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDocumentManagerBase;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.reference.SoftReference;
import com.intellij.ui.ClickListener;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xdebugger.impl.XDebuggerHistoryManager;

/**
 * @author lex
 */
public abstract class DebuggerEditorImpl extends CompletionEditor {
    private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.ui.DebuggerEditorImpl");

    public static final char SEPARATOR = 13;

    private final Project myProject;
    private PsiElement myContext;
    private PsiType myThisType;

    private final String myRecentsId;

    private final List<DocumentListener> myDocumentListeners = ContainerUtil.createLockFreeCopyOnWriteList();
    private Document myCurrentDocument;
    private final JLabel myChooseFactory = new JLabel();
    private WeakReference<ListPopup> myPopup;

    private final PsiTreeChangeListener myPsiListener = new PsiTreeChangeAdapter() {
        @Override
        public void childRemoved(@NotNull PsiTreeChangeEvent event) {
            checkContext();
        }

        @Override
        public void childReplaced(@NotNull PsiTreeChangeEvent event) {
            checkContext();
        }

        @Override
        public void childMoved(@NotNull PsiTreeChangeEvent event) {
            checkContext();
        }

        private void checkContext() {
            final PsiElement contextElement = getContext();
            if (contextElement == null || !contextElement.isValid()) {
                final DebuggerManagerEx manager = DebuggerManagerEx.getInstanceEx(myProject);
                if (manager == null) {
                    LOG.error("Cannot obtain debugger manager for project " + myProject);
                }
                final DebuggerContextImpl context = manager.getContextManager().getContext();
                final PsiElement newContextElement = PositionUtil.getContextElement(context);
                setContext(newContextElement != null && newContextElement.isValid() ? newContextElement : null);
            }
        }
    };
    private CodeFragmentFactory myFactory;
    protected boolean myInitialFactory;

    public DebuggerEditorImpl(Project project, PsiElement context, String recentsId,
            final CodeFragmentFactory factory) {
        myProject = project;
        myContext = context;
        myRecentsId = recentsId;
        PsiManager.getInstance(project).addPsiTreeChangeListener(myPsiListener);
        setFactory(factory);
        myInitialFactory = true;

        setFocusable(false);

        myChooseFactory.setToolTipText("Click to change the language");
        myChooseFactory.setBorder(new EmptyBorder(0, 3, 0, 3));
        new ClickListener() {
            @Override
            public boolean onClick(@NotNull MouseEvent e, int clickCount) {
                ListPopup oldPopup = SoftReference.dereference(myPopup);
                if (oldPopup != null && !oldPopup.isDisposed()) {
                    oldPopup.cancel();
                    myPopup = null;
                    return true;
                }

                if (myContext == null) {
                    return true;
                }

                ListPopup popup = createLanguagePopup();
                popup.showUnderneathOf(myChooseFactory);
                myPopup = new WeakReference<ListPopup>(popup);
                return true;
            }
        }.installOn(myChooseFactory);
    }

    private ListPopup createLanguagePopup() {
        DefaultActionGroup actions = new DefaultActionGroup();
        for (final CodeFragmentFactory fragmentFactory : DebuggerUtilsEx.getCodeFragmentFactories(myContext)) {
            actions.add(new AnAction(fragmentFactory.getFileType().getLanguage().getDisplayName(), null,
                    fragmentFactory.getFileType().getIcon()) {
                @Override
                public void actionPerformed(AnActionEvent e) {
                    setFactory(fragmentFactory);
                    setText(getText());
                    IdeFocusManager.getInstance(getProject()).requestFocus(DebuggerEditorImpl.this, true);
                }
            });
        }

        DataContext dataContext = DataManager.getInstance().getDataContext(this);
        return JBPopupFactory.getInstance().createActionGroupPopup("Choose language", actions, dataContext,
                JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, false);
    }

    @Override
    public void setEnabled(boolean enabled) {
        myChooseFactory.setEnabled(enabled);
        super.setEnabled(enabled);
    }

    protected TextWithImports createItem(Document document, Project project) {
        if (document != null) {
            PsiDocumentManager.getInstance(project).commitDocument(document);
            PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(document);
            if (psiFile != null) {
                return createText(psiFile.getText(), ((JavaCodeFragment) psiFile).importsToString());
            }
        }

        return createText("");
    }

    protected TextWithImports createText(String text) {
        return createText(text, "");
    }

    protected abstract TextWithImports createText(String text, String importsString);

    public abstract JComponent getPreferredFocusedComponent();

    @Override
    public void setContext(@Nullable PsiElement context) {
        myContext = context;

        List<CodeFragmentFactory> factories = DebuggerUtilsEx.getCodeFragmentFactories(context);
        boolean many = factories.size() > 1;
        if (myInitialFactory) {
            myInitialFactory = false;
            setFactory(factories.get(0));
            myChooseFactory.setVisible(many);
        }
        myChooseFactory.setVisible(myChooseFactory.isVisible() || many);
        myChooseFactory.setEnabled(many && factories.contains(myFactory));

        updateEditorUi();
    }

    @Override
    public void setText(TextWithImports text) {
        doSetText(text);
        updateEditorUi();
    }

    protected abstract void doSetText(TextWithImports text);

    protected abstract void updateEditorUi();

    @Override
    public PsiElement getContext() {
        return myContext;
    }

    protected Project getProject() {
        return myProject;
    }

    @Override
    public void requestFocus() {
        getPreferredFocusedComponent().requestFocus();
    }

    public void setThisType(PsiType thisType) {
        myThisType = thisType;
    }

    @Nullable
    protected Document createDocument(TextWithImports item) {
        LOG.assertTrue(myContext == null || myContext.isValid());

        if (item == null) {
            item = createText("");
        }
        JavaCodeFragment codeFragment = getCurrentFactory().createPresentationCodeFragment(item, myContext,
                getProject());
        codeFragment.forceResolveScope(GlobalSearchScope.allScope(myProject));
        if (myThisType != null) {
            codeFragment.setThisType(myThisType);
        } else if (myContext != null) {
            final PsiClass contextClass = PsiTreeUtil.getNonStrictParentOfType(myContext, PsiClass.class);
            if (contextClass != null) {
                final PsiClassType contextType = JavaPsiFacade.getInstance(codeFragment.getProject())
                        .getElementFactory().createType(contextClass);
                codeFragment.setThisType(contextType);
            }
        }

        if (myCurrentDocument != null) {
            for (DocumentListener documentListener : myDocumentListeners) {
                myCurrentDocument.removeDocumentListener(documentListener);
            }
        }

        myCurrentDocument = PsiDocumentManager.getInstance(getProject()).getDocument(codeFragment);

        if (myCurrentDocument != null) {
            PsiDocumentManagerBase.cachePsi(myCurrentDocument, codeFragment);
            for (DocumentListener documentListener : myDocumentListeners) {
                myCurrentDocument.addDocumentListener(documentListener);
            }
        }

        return myCurrentDocument;
    }

    @Override
    public String getRecentsId() {
        return myRecentsId;
    }

    public void addRecent(TextWithImports text) {
        if (getRecentsId() != null && text != null && !text.isEmpty()) {
            XDebuggerHistoryManager.getInstance(getProject()).addRecentExpression(getRecentsId(),
                    TextWithImportsImpl.toXExpression(text));
        }
    }

    public void addDocumentListener(DocumentListener listener) {
        myDocumentListeners.add(listener);
        if (myCurrentDocument != null) {
            myCurrentDocument.addDocumentListener(listener);
        }
    }

    public void removeDocumentListener(DocumentListener listener) {
        myDocumentListeners.remove(listener);
        if (myCurrentDocument != null) {
            myCurrentDocument.removeDocumentListener(listener);
        }
    }

    @Override
    public void dispose() {
        PsiManager.getInstance(myProject).removePsiTreeChangeListener(myPsiListener);
        myCurrentDocument = null;
    }

    @NotNull
    public static CodeFragmentFactory findAppropriateFactory(@NotNull TextWithImports text,
            @NotNull PsiElement context) {
        for (CodeFragmentFactory factory : DebuggerUtilsEx.getCodeFragmentFactories(context)) {
            if (factory.getFileType().equals(text.getFileType())) {
                return factory;
            }
        }
        return DefaultCodeFragmentFactory.getInstance();
    }

    protected void restoreFactory(TextWithImports text) {
        FileType fileType = text.getFileType();
        if (fileType == null) {
            return;
        }
        if (myContext == null) {
            return;
        }

        setFactory(findAppropriateFactory(text, myContext));
    }

    private void setFactory(@NotNull final CodeFragmentFactory factory) {
        myFactory = factory;
        Icon icon = getCurrentFactory().getFileType().getIcon();
        myChooseFactory.setIcon(icon);
        myChooseFactory.setDisabledIcon(IconLoader.getDisabledIcon(icon));
    }

    protected CodeFragmentFactory getCurrentFactory() {
        return myFactory instanceof CodeFragmentFactoryContextWrapper ? myFactory
                : new CodeFragmentFactoryContextWrapper(myFactory);
    }

    protected JPanel addChooseFactoryLabel(JComponent component, boolean top) {
        JPanel panel = new JPanel(new BorderLayout());
        panel.add(component, BorderLayout.CENTER);

        JPanel factoryPanel = new JPanel(new BorderLayout());
        factoryPanel.add(myChooseFactory, top ? BorderLayout.NORTH : BorderLayout.CENTER);
        panel.add(factoryPanel, BorderLayout.WEST);
        return panel;
    }
}