Java tutorial
/* * 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; } }