Skip to content

Commit

Permalink
add caret text overlay for types of xml container
Browse files Browse the repository at this point in the history
  • Loading branch information
Haehnchen committed Nov 3, 2015
1 parent 8551de8 commit b8f59c1
Show file tree
Hide file tree
Showing 7 changed files with 476 additions and 6 deletions.
Expand Up @@ -5,13 +5,16 @@
import com.intellij.notification.Notifications;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.event.CaretListener;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.wm.StatusBar;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.PsiElement;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.caret.overlay.CaretTextOverlayListener;
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerFile;
import fr.adrienbrault.idea.symfony2plugin.extension.ServiceContainerLoader;
import fr.adrienbrault.idea.symfony2plugin.extension.ServiceContainerLoaderParameter;
Expand All @@ -24,9 +27,7 @@
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.*;

/**
* @author Adrien Brault <adrien.brault@gmail.com>
Expand All @@ -38,11 +39,12 @@ public class Symfony2ProjectComponent implements ProjectComponent {
private static final ExtensionPointName<ServiceContainerLoader> SERVICE_CONTAINER_POINT_NAME = new ExtensionPointName<ServiceContainerLoader>("fr.adrienbrault.idea.symfony2plugin.extension.ServiceContainerLoader");

private Project project;
private Map<String, Route> routes;
private Long routesLastModified;
private final EditorFactory editorFactory;
private CaretListener overlayerCaretListener;

public Symfony2ProjectComponent(Project project) {
public Symfony2ProjectComponent(Project project, EditorFactory editorFactory) {
this.project = project;
this.editorFactory = editorFactory;
}

public void initComponent() {
Expand All @@ -59,6 +61,7 @@ public String getComponentName() {
}

public void projectOpened() {
editorFactory.getEventMulticaster().addCaretListener(this.overlayerCaretListener = new CaretTextOverlayListener());

this.checkProject();

Expand Down Expand Up @@ -86,6 +89,10 @@ public void projectOpened() {
}

public void projectClosed() {
if(overlayerCaretListener != null) {
editorFactory.getEventMulticaster().removeCaretListener(overlayerCaretListener);
}

ServiceXmlParserFactory.cleanInstance(project);

// clean routing
Expand Down
@@ -0,0 +1,16 @@
package fr.adrienbrault.idea.symfony2plugin.codeInsight.caret.overlay;

import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public interface CaretTextOverlay {

@Nullable
CaretTextOverlayElement getOverlay(@NotNull CaretTextOverlayArguments args);

boolean accepts(@NotNull VirtualFile virtualFile);
}
@@ -0,0 +1,48 @@
package fr.adrienbrault.idea.symfony2plugin.codeInsight.caret.overlay;

import com.intellij.openapi.editor.event.CaretEvent;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class CaretTextOverlayArguments {

@NotNull
private final CaretEvent caretEvent;

@NotNull
private final PsiFile psiFile;

@NotNull
private final PsiElement psiElement;

public CaretTextOverlayArguments(@NotNull CaretEvent caretEvent, @NotNull PsiFile psiFile, @NotNull PsiElement psiElement) {
this.caretEvent = caretEvent;
this.psiFile = psiFile;
this.psiElement = psiElement;
}

@NotNull
public PsiFile getPsiFile() {
return psiFile;
}

@NotNull
public PsiElement getPsiElement() {
return psiElement;
}

@NotNull
public Project getProject() {
return psiElement.getProject();
}

@NotNull
public CaretEvent getCaretEvent() {
return caretEvent;
}
}
@@ -0,0 +1,21 @@
package fr.adrienbrault.idea.symfony2plugin.codeInsight.caret.overlay;

import org.jetbrains.annotations.NotNull;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class CaretTextOverlayElement {

@NotNull
private final String text;

public CaretTextOverlayElement(@NotNull String text) {
this.text = text;
}

@NotNull
public String getText() {
return text;
}
}
@@ -0,0 +1,157 @@
package fr.adrienbrault.idea.symfony2plugin.codeInsight.caret.overlay;

import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Caret;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.event.CaretEvent;
import com.intellij.openapi.editor.event.CaretListener;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.caret.overlay.component.CaretOverlayComponent;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.caret.overlay.provider.XmlServiceContainerCaretTextOverlay;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;
import java.util.Timer;
import java.util.TimerTask;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class CaretTextOverlayListener implements CaretListener {

private static CaretTextOverlay[] CONTRIBUTOR = new CaretTextOverlay[] {
new XmlServiceContainerCaretTextOverlay()
};

private Timer timer = null;
private int startDelayMs = 250;

@Override
public void caretPositionChanged(final CaretEvent caretEvent) {

this.clear();
final Editor editor = caretEvent.getEditor();
removeOverlays(editor);

if(!(editor instanceof EditorEx)) {
return;
}

final Project project = editor.getProject();
if(project == null || DumbService.getInstance(project).isDumb()) {
return;
}

this.timer = new Timer();
this.timer.schedule(new TimerTask() {
@Override
public void run() {
ApplicationManager.getApplication().runReadAction(new MyPsiElementRunnable(project, caretEvent, editor));
timer = null;
}
}, startDelayMs);
}

@Override
public void caretAdded(CaretEvent caretEvent) {

}

@Override
public void caretRemoved(CaretEvent caretEvent) {

}

private void invokeUiComponent(final @NotNull Editor editor, final @NotNull PsiElement psiElement, final @NotNull CaretTextOverlayElement overlayElement) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
CaretOverlayComponent component = new CaretOverlayComponent(editor, overlayElement.getText(), psiElement.getTextOffset(), psiElement.getLanguage());
editor.getContentComponent().add(component);

JViewport viewpoint = ((EditorEx) editor).getScrollPane().getViewport();
component.setBounds(0, 0, viewpoint.getWidth(), viewpoint.getHeight());
}
});
}

private void removeOverlays(@NotNull Editor editor) {
for (Component component : editor.getContentComponent().getComponents()) {
if(component instanceof CaretOverlayComponent) {
editor.getContentComponent().remove(component);
}
}
}

private void clear() {
if(timer == null) {
return;
}

timer.cancel();
timer.purge();
timer = null;
}

private class MyPsiElementRunnable implements Runnable {
private final Project project;
private final CaretEvent caretEvent;
private final Editor editor;

public MyPsiElementRunnable(Project project, CaretEvent caretEvent, Editor editor) {
this.project = project;
this.caretEvent = caretEvent;
this.editor = editor;
}

@Override
public void run() {

Caret caret = caretEvent.getCaret();
if(caret == null) {
return;
}

if (DumbService.getInstance(project).isDumb()) {
return;
}

final PsiFile psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
if(psiFile == null) {
return;
}

CaretTextOverlayArguments args = null;

for (CaretTextOverlay caretTextOverlay : CONTRIBUTOR) {
if(!caretTextOverlay.accepts(psiFile.getVirtualFile())) {
continue;
}

if(args == null) {
PsiElement element = psiFile.findElementAt(caret.getOffset());
if (element == null) {
return;
}

args = new CaretTextOverlayArguments(caretEvent, psiFile, element);
}

CaretTextOverlayElement overlay = caretTextOverlay.getOverlay(args);
if(overlay == null) {
continue;
}

invokeUiComponent(editor, args.getPsiElement(), overlay);

return;
}
}
}
}
@@ -0,0 +1,61 @@
package fr.adrienbrault.idea.symfony2plugin.codeInsight.caret.overlay.component;

import com.intellij.lang.Language;
import com.intellij.lang.xml.XMLLanguage;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.XmlHighlighterColors;
import com.intellij.openapi.editor.colors.EditorFontType;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.php.lang.PhpLanguage;
import com.jetbrains.php.lang.highlighter.PhpHighlightingData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.YAMLHighlighter;
import org.jetbrains.yaml.YAMLLanguage;

import javax.swing.*;
import java.awt.*;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class CaretOverlayComponent extends JComponent {

private final Editor editor;
private final String content;
private final int offset;
private final Language language;
private int horizontalMargin = 20;

public CaretOverlayComponent(@NotNull Editor editor, @NotNull String content, int offset, @NotNull Language language) {
this.editor = editor;
this.content = content;
this.offset = offset;
this.language = language;
}

@Override
protected void paintComponent(Graphics g) {

if(language == XMLLanguage.INSTANCE) {
g.setColor(
editor.getColorsScheme().getAttributes(XmlHighlighterColors.XML_COMMENT).getForegroundColor()
);
} else if(language == YAMLLanguage.INSTANCE) {
g.setColor(
editor.getColorsScheme().getAttributes(YAMLHighlighter.COMMENT).getForegroundColor()
);
} else if(language == PhpLanguage.INSTANCE) {
g.setColor(
editor.getColorsScheme().getAttributes(PhpHighlightingData.COMMENT).getForegroundColor()
);
}

g.setFont(editor.getColorsScheme().getFont(EditorFontType.CONSOLE_ITALIC));

int verticalAlignment = editor.getLineHeight() - editor.getColorsScheme().getEditorFontSize();

int offset = editor.getDocument().getLineEndOffset(StringUtil.offsetToLineNumber(editor.getDocument().getCharsSequence(), this.offset));
Point point = editor.visualPositionToXY(editor.offsetToVisualPosition(offset));
g.drawString(content, point.x + horizontalMargin, point.y + editor.getLineHeight() - verticalAlignment);
}
}

0 comments on commit b8f59c1

Please sign in to comment.