Skip to content

Commit

Permalink
feat: Add support for links in tree (#69)
Browse files Browse the repository at this point in the history
* feat: Add support for links in tree

Fixes #67

Signed-off-by: Jeff MAURY <jmaury@redhat.com>
  • Loading branch information
jeffmaury authored Apr 27, 2021
1 parent 98f74c7 commit 14f42e7
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 2 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ intellij {
plugins 'terminal', 'yaml'
}

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

java {
withSourcesJar()
withJavadocJar()
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ideaVersion = IC-2018.3
projectVersion=1.0.1-SNAPSHOT
projectVersion=1.1.0-SNAPSHOT
nexusUser=invalid
nexusPassword=invalid
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,22 @@
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.ide.util.treeView.PresentableNodeDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.ui.JBColor;
import com.intellij.ui.SimpleTextAttributes;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.Icon;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LabelAndIconDescriptor<T> extends PresentableNodeDescriptor<T> {

public static final Pattern HREF_PATTERN = Pattern.compile("<a(?:\\s+href\\s*=\\s*[\"']([^\"']*)[\"'])?\\s*>([^<]*)</a>");

public static final SimpleTextAttributes LINK_ATTRIBUTES = new SimpleTextAttributes(SimpleTextAttributes.STYLE_UNDERLINE | SimpleTextAttributes.STYLE_CLICKABLE, JBColor.blue);

private final T element;
private final Supplier<String> label;
private final Supplier<String> location;
Expand Down Expand Up @@ -53,7 +61,7 @@ public LabelAndIconDescriptor(Project project, T element, Supplier<String> label

@Override
protected void update(@NotNull PresentationData presentation) {
presentation.setPresentableText(label.get());
processLabel(presentation);
if (location != null && location.get() != null) {
presentation.setLocationString(location.get());
}
Expand All @@ -62,6 +70,29 @@ protected void update(@NotNull PresentationData presentation) {
}
}

private void processLabel(@NotNull PresentationData presentation) {
String text = label.get();
Matcher matcher = HREF_PATTERN.matcher(text);
if (matcher.find()) {
int prev = 0;
do {
if (matcher.start() != prev) {
presentation.addText(text.substring(prev, matcher.start()), SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
presentation.addText(matcher.group(2), LINK_ATTRIBUTES);
prev = matcher.end();
}
while (matcher.find());

if (prev < text.length()) {
presentation.addText(text.substring(prev), SimpleTextAttributes.REGULAR_ATTRIBUTES);
}

} else {
presentation.setPresentableText(label.get());
}
}

@Override
public T getElement() {
return element;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*******************************************************************************
* Copyright (c) 2021 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.intellij.common.tree;

/**
* Interface to be implemented by tree elements
* to support links.
*/
public interface LinkElement {
void execute();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2021 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.intellij.common.tree;

import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.ide.util.treeView.PresentableNodeDescriptor;
import com.intellij.util.ui.tree.TreeUtil;

import javax.swing.JTree;
import javax.swing.tree.TreePath;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class TreeHelper {
public static TreePath getPathForLocation(JTree tree, int x, int y) {
TreePath path = tree.getClosestPathForLocation(x, y);
Rectangle bounds = tree.getPathBounds(path);
return bounds != null && bounds.y <= y && y < bounds.y + bounds.height ? path : null;
}

public static void addLinkSupport(final JTree tree) {
tree.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
TreePath path = getPathForLocation(tree, e.getX(), e.getY());
Object object = TreeUtil.getLastUserObject(path);
if (!(object instanceof LinkElement) && object instanceof NodeDescriptor) {
object = ((NodeDescriptor<?>) object).getElement();
}
if (object instanceof LinkElement) {
((LinkElement)object).execute();
}
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*******************************************************************************
* Copyright (c) 2021 Red Hat, Inc.
* Distributed under license by Red Hat, Inc. All rights reserved.
* This program is made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Red Hat, Inc. - initial API and implementation
******************************************************************************/
package com.redhat.devtools.intellij.common.tree;

import com.intellij.ide.projectView.PresentationData;
import com.intellij.ide.util.treeView.PresentableNodeDescriptor;
import com.intellij.ui.SimpleTextAttributes;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class LabelAndIconDescriptorTest {

@Test
public void checkLinkWithoutHref() {
assertTrue(LabelAndIconDescriptor.HREF_PATTERN.matcher("Click <a>here</a>.").find());
}

@Test
public void checkLinkWithHref() {
assertTrue(LabelAndIconDescriptor.HREF_PATTERN.matcher("Click <a href=\"xxxx\">here</a>.").find());
}

@Test
public void checkTextWithoutHyperLink() {
LabelAndIconDescriptor descriptor = new LabelAndIconDescriptor(null, null, "mytext", null, null);
PresentationData data = new PresentationData();
descriptor.update(data);
assertEquals("mytext", data.getPresentableText());

}

private void testTextWithOneHyperlink(String text) {
LabelAndIconDescriptor descriptor = new LabelAndIconDescriptor(null, null, text, null, null);
PresentationData data = new PresentationData();
descriptor.update(data);
assertNull(data.getPresentableText());
List<PresentableNodeDescriptor.ColoredFragment> fragments = data.getColoredText();
assertNotNull(fragments);
assertEquals(3, fragments.size());
assertEquals("Click ", fragments.get(0).getText());
assertEquals(SimpleTextAttributes.REGULAR_ATTRIBUTES, fragments.get(0).getAttributes());
assertEquals("here", fragments.get(1).getText());
assertEquals(LabelAndIconDescriptor.LINK_ATTRIBUTES, fragments.get(1).getAttributes());
assertEquals(".", fragments.get(2).getText());
assertEquals(SimpleTextAttributes.REGULAR_ATTRIBUTES, fragments.get(2).getAttributes());
}

@Test
public void checkTextWithOneHyperLinkAndNoHref() {
testTextWithOneHyperlink("Click <a>here</a>.");
}

@Test
public void checkTextWithOneHyperLinkAndHref() {
testTextWithOneHyperlink("Click <a href=\"xxx\">here</a>.");
}

private void testTextWithTwoHyperlinks(String text) {
LabelAndIconDescriptor descriptor = new LabelAndIconDescriptor(null, null, text, null, null);
PresentationData data = new PresentationData();
descriptor.update(data);
assertNull(data.getPresentableText());
List<PresentableNodeDescriptor.ColoredFragment> fragments = data.getColoredText();
assertNotNull(fragments);
assertEquals(5, fragments.size());
assertEquals("Click ", fragments.get(0).getText());
assertEquals(SimpleTextAttributes.REGULAR_ATTRIBUTES, fragments.get(0).getAttributes());
assertEquals("here", fragments.get(1).getText());
assertEquals(LabelAndIconDescriptor.LINK_ATTRIBUTES, fragments.get(1).getAttributes());
assertEquals(" or ", fragments.get(2).getText());
assertEquals(SimpleTextAttributes.REGULAR_ATTRIBUTES, fragments.get(2).getAttributes());
assertEquals("there", fragments.get(3).getText());
assertEquals(LabelAndIconDescriptor.LINK_ATTRIBUTES, fragments.get(3).getAttributes());
assertEquals(".", fragments.get(4).getText());
assertEquals(SimpleTextAttributes.REGULAR_ATTRIBUTES, fragments.get(4).getAttributes());
}

@Test
public void checkTextWithTwoHyperLinkAndNoHref() {
testTextWithTwoHyperlinks("Click <a>here</a> or <a>there</a>.");
}

@Test
public void checkTextWithTwoHyperLinkAndHref() {
testTextWithTwoHyperlinks("Click <a href=\"xxx\">here</a> or <a>there</a>.");
}
}

0 comments on commit 14f42e7

Please sign in to comment.