package org.intellij.sonar.util;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.markup.MarkupModel;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
public class Finders {
public static Optional<PsiElement> findFirstElementAtLine(@NotNull final PsiFile file,Integer line) {
if (line == null) return Optional.empty();
int ijLine = line-1;
final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
Optional<PsiElement> element = getFirstSiblingFrom(file,ijLine,document);
while (element.isPresent() && element.get().getTextLength() == 0) {
element = Optional.ofNullable(element.get().getNextSibling());
}
if (document != null && element.isPresent()
&& document.getLineNumber(element.get().getTextOffset()) != ijLine) {
element = Optional.empty();
}
return element;
}
private static Optional<PsiElement> getFirstSiblingFrom(PsiFile file,int ijLine,Document document) {
if (document == null) return Optional.empty();
Optional<PsiElement> element = Optional.empty();
try {
final int offset = document.getLineStartOffset(ijLine);
element = Optional.ofNullable(file.getViewProvider().findElementAt(offset));
if (element.isPresent() && document.getLineNumber(element.get().getTextOffset()) != ijLine) {
element = Optional.ofNullable(element.get().getNextSibling());
}
} catch (@NotNull final IndexOutOfBoundsException ignore) { //NOSONAR
// element keeps to be absent
}
return element;
}
public static Optional<Document> findDocumentFromPsiFile(PsiFile psiFile) {
final Optional<Project> project = Optional.ofNullable(psiFile.getProject());
if (!project.isPresent()) return Optional.empty();
return Optional.ofNullable(PsiDocumentManager.getInstance(project.get()).getDocument(psiFile));
}
@NotNull
public static List<Editor> findEditorsFrom(@NotNull Document document) {
return Lists.newArrayList(EditorFactory.getInstance().getEditors(document));
}
@NotNull
public static Optional<RangeHighlighter> findRangeHighlighterAtLine(final Editor editor,final Integer line) {
if (line == null) return Optional.empty();
final MarkupModel markupModel = editor.getMarkupModel();
final RangeHighlighter[] highlighters = markupModel.getAllHighlighters();
for (RangeHighlighter highlighter : highlighters) {
final LogicalPosition logicalPosition = editor.offsetToLogicalPosition(highlighter.getStartOffset());
final int lineOfHighlighter = logicalPosition.line;
if (lineOfHighlighter == line-1) {
return Optional.of(highlighter);
}
}
return Optional.empty();
}
@NotNull
public static Set<RangeHighlighter> findAllRangeHighlightersFrom(@NotNull Document document) {
final Set<RangeHighlighter> highlighters = Sets.newHashSet();
for (final Editor editor : findEditorsFrom(document)) {
addHighlightersFromEditor(highlighters, editor);
}
return highlighters;
}
private static void addHighlightersFromEditor(final Set<RangeHighlighter> highlighters, final Editor editor) {
ApplicationManager.getApplication().invokeAndWait(() -> {
final RangeHighlighter[] highlightersFromCurrentEditor = editor.getMarkupModel().getAllHighlighters();
highlighters.addAll(Sets.newHashSet(highlightersFromCurrentEditor));
}, ModalityState.any());
}
public static int findLineOfRangeHighlighter(@NotNull RangeHighlighter highlighter,@NotNull Editor editor) {
final LogicalPosition logicalPosition = editor.offsetToLogicalPosition(highlighter.getStartOffset());
return logicalPosition.line;
}
@NotNull
public static TextRange getLineRange(@NotNull PsiFile psiFile,Integer line) {
if (line == null) return TextRange.EMPTY_RANGE;
Project project = psiFile.getProject();
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
Document document = documentManager.getDocument(psiFile.getContainingFile());
if (document == null) {
return TextRange.EMPTY_RANGE;
}
int ijLine = line > 0
? line-1
: 0;
return getTextRangeForLine(document,ijLine);
}
private static TextRange getTextRangeForLine(Document document,int line) {
try {
int lineStartOffset = document.getLineStartOffset(line);
int lineEndOffset = document.getLineEndOffset(line);
return new TextRange(lineStartOffset,lineEndOffset);
} catch (IndexOutOfBoundsException ignore) { //NOSONAR
// Local file should be different than remote
return TextRange.EMPTY_RANGE;
}
}
public static TextRange getLineRange(@NotNull PsiElement psiElement) {
Project project = psiElement.getProject();
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
Document document = documentManager.getDocument(psiElement.getContainingFile().getContainingFile());
if (document == null) {
return TextRange.EMPTY_RANGE;
}
int line = document.getLineNumber(psiElement.getTextOffset());
int lineEndOffset = document.getLineEndOffset(line);
return new TextRange(psiElement.getTextOffset(),lineEndOffset);
}
}