IDEA-64131 support for links in comments

This commit is contained in:
Maxim.Mossienko
2014-12-22 13:09:46 +01:00
parent 477204696e
commit 2f73716a0e
6 changed files with 158 additions and 7 deletions

View File

@@ -15,8 +15,11 @@
*/
package com.intellij.psi.impl.source.javadoc;
import com.intellij.psi.JavaDocTokenType;
import com.intellij.psi.JavaElementVisitor;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiReference;
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.psi.tree.IElementType;
@@ -42,6 +45,15 @@ public class PsiDocTokenImpl extends LeafPsiElement implements PsiDocToken{
}
}
@NotNull
@Override
public PsiReference[] getReferences() {
if (getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) {
return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiDocToken.class);
}
return super.getReferences();
}
public String toString(){
return "PsiDocToken:" + getTokenType().toString();
}

View File

@@ -0,0 +1,8 @@
/**
* <a href="http://www.unicode.org/unicode/standard/standard.html">
* <i>The Unicode Standard</i></a>
*/
class LinksInJavaDoc {
// Since Java 7 classloading is parallel on parallel capable classloader (http://docs.oracle.com/javase/7/docs/technotes/guides/lang/cl-mt.html)
// Parallel classloading avoids deadlocks like https://youtrack.jetbrains.com/issue/IDEA-131621,
}

View File

@@ -4,10 +4,17 @@ import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.javaDoc.JavaDocLocalInspection;
import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection;
import com.intellij.openapi.paths.WebReference;
import com.intellij.openapi.roots.LanguageLevelProjectExtension;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import com.intellij.psi.PsiReference;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class JavadocHighlightingTest extends LightDaemonAnalyzerTestCase {
private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/javaDoc";
@@ -107,6 +114,33 @@ public class JavadocHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testLinksInJavaDoc() throws Exception {
configureByFile(BASE_PATH + "/" + getTestName(false) + ".java");
final List<WebReference> refs = new ArrayList<WebReference>();
myFile.accept(new PsiRecursiveElementWalkingVisitor() {
@Override
public void visitElement(PsiElement element) {
for(PsiReference ref:element.getReferences()) {
if (ref instanceof WebReference) refs.add((WebReference)ref);
}
super.visitElement(element);
}
});
String[] targets = {"http://www.unicode.org/unicode/standard/standard.html",
"http://docs.oracle.com/javase/7/docs/technotes/guides/lang/cl-mt.html",
"https://youtrack.jetbrains.com/issue/IDEA-131621"};
assertTrue(refs.size() == targets.length);
int i = 0;
for(WebReference ref:refs) {
assertEquals(ref.getCanonicalText(), targets[i++]);
assertTrue(ref.isSoft());
assertNotNull(ref.resolve());
}
}
protected void doTest() throws Exception {
super.doTest(BASE_PATH + "/" + getTestName(false) + ".java", true, false);
}

View File

@@ -0,0 +1,83 @@
/*
* 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.codeInsight.documentation;
import com.intellij.openapi.paths.GlobalPathReferenceProvider;
import com.intellij.openapi.paths.PathReferenceManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.UserDataCache;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceProvider;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.util.ProcessingContext;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by Maxim.Mossienko on 12/19/2014.
*/
public class ArbitraryPlaceUrlReferenceProvider extends PsiReferenceProvider {
public static final ArbitraryPlaceUrlReferenceProvider INSTANCE = new ArbitraryPlaceUrlReferenceProvider();
private static final Pattern urlPattern = Pattern.compile("((mailto\\:|(news|(ht|f)tp(s?))\\://){1}[^\\s\\),\"']+)");
private static final UserDataCache<CachedValue<PsiReference[]>, PsiElement, Object> ourRefsCache = new UserDataCache<CachedValue<PsiReference[]>, PsiElement, Object>("psielement.url.refs") {
private final AtomicReference<GlobalPathReferenceProvider> myReferenceProvider = new AtomicReference<GlobalPathReferenceProvider>();
@Override
protected CachedValue<PsiReference[]> compute(final PsiElement element, Object p) {
return CachedValuesManager
.getManager(element.getProject()).createCachedValue(new CachedValueProvider<PsiReference[]>() {
public Result<PsiReference[]> compute() {
Matcher matcher = urlPattern.matcher(element.getText());
List<PsiReference> refs = null;
GlobalPathReferenceProvider provider = myReferenceProvider.get();
while (matcher.find()) {
final int start = matcher.start();
final int end = matcher.end();
if (refs == null) refs = new SmartList<PsiReference>();
if (provider == null) {
provider = (GlobalPathReferenceProvider)PathReferenceManager.getInstance().getGlobalWebPathReferenceProvider();
myReferenceProvider.lazySet(provider);
}
provider.createUrlReference(element, matcher.group(0), new TextRange(start, end), refs);
}
return new Result<PsiReference[]>(refs != null ? refs.toArray(new PsiReference[refs.size()]) : PsiReference.EMPTY_ARRAY,
element);
}
}, false);
}
};
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull final PsiElement element, @NotNull ProcessingContext context) {
return ourRefsCache.get(element, null).getValue();
}
}

View File

@@ -49,14 +49,23 @@ public class GlobalPathReferenceProvider implements PathReferenceProvider {
if (manipulator == null) {
return false;
}
final TextRange range = manipulator.getRangeInElement(psiElement);
final String s = range.substring(psiElement.getText());
if (isWebReferenceUrl(s)) {
references.add(new WebReference(psiElement, range));
return createUrlReference(
psiElement,
manipulator.getRangeInElement(psiElement).substring(psiElement.getText()),
manipulator.getRangeInElement(psiElement),
references
);
}
public boolean createUrlReference(@NotNull PsiElement psiElement,
String url,
TextRange rangeInElement, @NotNull List<PsiReference> references) {
if (isWebReferenceUrl(url)) {
references.add(new WebReference(psiElement, rangeInElement));
return true;
}
else if (s.contains("://") || s.startsWith("//") || startsWithAllowedPrefix(s)) {
final PsiReference reference = PsiReferenceBase.createSelfReference(psiElement, psiElement);
references.add(reference);
else if (url.contains("://") || url.startsWith("//") || startsWithAllowedPrefix(url)) {
references.add(PsiReferenceBase.createSelfReference(psiElement, psiElement));
return true;
}
return false;

View File

@@ -16,11 +16,13 @@
package com.intellij.psi.impl.source.resolve.reference.impl.providers;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.documentation.ArbitraryPlaceUrlReferenceProvider;
import com.intellij.codeInspection.i18n.JavaI18nUtil;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.position.FilterPattern;
import com.intellij.psi.javadoc.PsiDocToken;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
@@ -52,5 +54,8 @@ public class JavaReferenceContributor extends PsiReferenceContributor{
return true;
}
})), filePathReferenceProvider);
registrar.registerReferenceProvider(PlatformPatterns.psiElement(PsiDocToken.class), ArbitraryPlaceUrlReferenceProvider.INSTANCE);
// todo register for all ?
registrar.registerReferenceProvider(PlatformPatterns.psiElement(PsiComment.class), ArbitraryPlaceUrlReferenceProvider.INSTANCE);
}
}