blob: 7400e982b5a0e13fa0ae219e5156a7957d409d96 [file] [log] [blame]
/*
* Copyright 2000-2014 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.openapi.fileEditor.impl.text;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.ide.IdeView;
import com.intellij.ide.util.EditorHelper;
import com.intellij.injected.editor.EditorWindow;
import com.intellij.injected.editor.InjectedCaret;
import com.intellij.lang.Language;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.fileEditor.EditorDataProvider;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashSet;
import static com.intellij.openapi.actionSystem.AnActionEvent.injectedId;
import static com.intellij.openapi.actionSystem.LangDataKeys.*;
import static com.intellij.util.containers.ContainerUtil.addIfNotNull;
public class TextEditorPsiDataProvider implements EditorDataProvider {
@Override
@Nullable
public Object getData(@NotNull final String dataId, @NotNull final Editor e, @NotNull final Caret caret) {
if (!(e instanceof EditorEx)) {
return null;
}
VirtualFile file = ((EditorEx)e).getVirtualFile();
if (file == null || !file.isValid()) return null;
Project project = e.getProject();
if (dataId.equals(injectedId(EDITOR.getName()))) {
if (project == null || PsiDocumentManager.getInstance(project).isUncommited(e.getDocument())) {
return e;
}
else {
return InjectedLanguageUtil.getEditorForInjectedLanguageNoCommit(e, caret, getPsiFile(e, file));
}
}
if (HOST_EDITOR.is(dataId)) {
return e instanceof EditorWindow ? ((EditorWindow)e).getDelegate() : e;
}
if (CARET.is(dataId)) {
return caret;
}
if (dataId.equals(injectedId(CARET.getName()))) {
Editor editor = (Editor)getData(injectedId(EDITOR.getName()), e, caret);
assert editor != null;
return getInjectedCaret(editor, caret);
}
if (dataId.equals(injectedId(PSI_ELEMENT.getName()))) {
Editor editor = (Editor)getData(injectedId(EDITOR.getName()), e, caret);
assert editor != null;
Caret injectedCaret = getInjectedCaret(editor, caret);
return getPsiElementIn(editor, injectedCaret, file);
}
if (PSI_ELEMENT.is(dataId)){
return getPsiElementIn(e, caret, file);
}
if (dataId.equals(injectedId(LANGUAGE.getName()))) {
PsiFile psiFile = (PsiFile)getData(injectedId(PSI_FILE.getName()), e, caret);
Editor editor = (Editor)getData(injectedId(EDITOR.getName()), e, caret);
if (psiFile == null || editor == null) return null;
Caret injectedCaret = getInjectedCaret(editor, caret);
return getLanguageAtCurrentPositionInEditor(injectedCaret, psiFile);
}
if (LANGUAGE.is(dataId)) {
final PsiFile psiFile = getPsiFile(e, file);
if (psiFile == null) return null;
return getLanguageAtCurrentPositionInEditor(caret, psiFile);
}
if (dataId.equals(injectedId(VIRTUAL_FILE.getName()))) {
PsiFile psiFile = (PsiFile)getData(injectedId(PSI_FILE.getName()), e, caret);
if (psiFile == null) return null;
return psiFile.getVirtualFile();
}
if (dataId.equals(injectedId(PSI_FILE.getName()))) {
Editor editor = (Editor)getData(injectedId(EDITOR.getName()), e, caret);
if (editor == null) {
return null;
}
if (project == null) {
return null;
}
return PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
}
if (PSI_FILE.is(dataId)) {
return getPsiFile(e, file);
}
if (IDE_VIEW.is(dataId)) {
final PsiFile psiFile = project == null ? null : PsiManager.getInstance(project).findFile(file);
final PsiDirectory psiDirectory = psiFile != null ? psiFile.getParent() : null;
if (psiDirectory != null && psiDirectory.isPhysical()) {
return new IdeView() {
@Override
public void selectElement(final PsiElement element) {
Editor editor = EditorHelper.openInEditor(element);
if (editor != null) {
ToolWindowManager.getInstance(element.getProject()).activateEditorComponent();
}
}
@NotNull
@Override
public PsiDirectory[] getDirectories() {
return new PsiDirectory[]{psiDirectory};
}
@Override
public PsiDirectory getOrChooseDirectory() {
return psiDirectory;
}
};
}
}
if (CONTEXT_LANGUAGES.is(dataId)) {
return computeLanguages(e, caret);
}
return null;
}
@NotNull
private static Caret getInjectedCaret(@NotNull Editor editor, @NotNull Caret hostCaret) {
if (!(editor instanceof EditorWindow) || hostCaret instanceof InjectedCaret) {
return hostCaret;
}
for (Caret caret : editor.getCaretModel().getAllCarets()) {
if (((InjectedCaret)caret).getDelegate() == hostCaret) {
return caret;
}
}
throw new IllegalArgumentException("Cannot find injected caret corresponding to " + hostCaret);
}
private static Language getLanguageAtCurrentPositionInEditor(Caret caret, final PsiFile psiFile) {
int caretOffset = caret.getOffset();
int mostProbablyCorrectLanguageOffset = caretOffset == caret.getSelectionStart() ||
caretOffset == caret.getSelectionEnd()
? caret.getSelectionStart()
: caretOffset;
if (caret.hasSelection()) {
return getLanguageAtOffset(psiFile, mostProbablyCorrectLanguageOffset, caret.getSelectionEnd());
}
return PsiUtilCore.getLanguageAtOffset(psiFile, mostProbablyCorrectLanguageOffset);
}
private static Language getLanguageAtOffset(PsiFile psiFile, int mostProbablyCorrectLanguageOffset, int end) {
final PsiElement elt = psiFile.findElementAt(mostProbablyCorrectLanguageOffset);
if (elt == null) return psiFile.getLanguage();
if (elt instanceof PsiWhiteSpace) {
final int incremented = elt.getTextRange().getEndOffset() + 1;
if (incremented <= end) {
return getLanguageAtOffset(psiFile, incremented, end);
}
}
return PsiUtilCore.findLanguageFromElement(elt);
}
@Nullable
private static PsiElement getPsiElementIn(@NotNull Editor editor, @NotNull Caret caret, @NotNull VirtualFile file) {
final PsiFile psiFile = getPsiFile(editor, file);
if (psiFile == null) return null;
try {
TargetElementUtilBase util = TargetElementUtilBase.getInstance();
return util.findTargetElement(editor, util.getReferenceSearchFlags(), caret.getOffset());
}
catch (IndexNotReadyException e) {
return null;
}
}
@Nullable
private static PsiFile getPsiFile(@NotNull Editor e, @NotNull VirtualFile file) {
if (!file.isValid()) {
return null; // fix for SCR 40329
}
final Project project = e.getProject();
if (project == null) {
return null;
}
PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
return psiFile != null && psiFile.isValid() ? psiFile : null;
}
private Language[] computeLanguages(@NotNull Editor editor, @NotNull Caret caret) {
LinkedHashSet<Language> set = new LinkedHashSet<Language>(4);
Language injectedLanguage = (Language)getData(injectedId(LANGUAGE.getName()), editor, caret);
addIfNotNull(injectedLanguage, set);
Language language = (Language)getData(LANGUAGE.getName(), editor, caret);
addIfNotNull(language, set);
PsiFile psiFile = (PsiFile)getData(PSI_FILE.getName(), editor, caret);
if (psiFile != null) {
addIfNotNull(psiFile.getViewProvider().getBaseLanguage(), set);
}
return set.toArray(new Language[set.size()]);
}
}