Skip to content

Commit

Permalink
feat: Add visibility reference contributor (#5650)
Browse files Browse the repository at this point in the history
Complete visibility refernce in build files with a custom logic to
present pseudo visibility like __pkg__ and __subpackages__ as well as
package groups, but not general targets.
  • Loading branch information
stevebarrau authored Nov 9, 2023
1 parent ea225b6 commit 79d55e7
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 8 deletions.
1 change: 0 additions & 1 deletion WORKSPACE
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ http_archive(
url = IC_233_URL,
)


# The plugin api for IntelliJ UE 2021.2. This is required to run UE-specific
# integration tests.
http_archive(
Expand Down
2 changes: 2 additions & 0 deletions base/src/META-INF/blaze-base.xml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@
<projectService serviceImplementation="com.google.idea.blaze.base.sync.BlazeSyncManager"/>
<applicationConfigurable provider="com.google.idea.blaze.base.qsync.settings.QuerySyncConfigurableProvider" id ="query.sync" displayName="Query Sync"/>
<applicationService serviceImplementation="com.google.idea.blaze.base.qsync.settings.QuerySyncSettings" id="QuerySyncSettings"/>
<psi.referenceContributor language="BUILD" implementation="com.google.idea.blaze.base.lang.buildfile.references.VisibilityReferenceContributor"/>
<projectService serviceImplementation="com.google.idea.blaze.base.qsync.QuerySyncManager"/>
<project.converterProvider implementation="com.google.idea.blaze.base.qsync.ProjectConverterProvider" id="query.sync.project.converter"/>
<iw.actionProvider implementation="com.google.idea.blaze.base.qsync.QuerySyncInspectionWidgetActionProvider"/>
Expand Down Expand Up @@ -571,6 +572,7 @@
<BuildResultHelperProvider order="last" implementation="com.google.idea.blaze.base.command.buildresult.BuildResultHelperBep$Provider"/>
<BlazeCommandRunConfigurationHandlerProvider implementation="com.google.idea.blaze.base.run.confighandler.PendingTargetRunConfigurationHandlerProvider"/>
<BlazeCommandRunConfigurationHandlerProvider implementation="com.google.idea.blaze.base.run.confighandler.BlazeCommandGenericRunConfigurationHandlerProvider" order="last"/>
<AttributeSpecificStringLiteralReferenceProvider implementation="com.google.idea.blaze.base.lang.buildfile.references.VisibilityReferenceProvider"/>
<TestTargetHeuristic implementation="com.google.idea.blaze.base.run.TargetNameHeuristic" id="TargetNameHeuristic"/>
<TestTargetHeuristic implementation="com.google.idea.blaze.base.run.TestTargetSourcesHeuristic" order="after TargetNameHeuristic"/>
<TestTargetHeuristic implementation="com.google.idea.blaze.base.run.BlazePackageHeuristic" order="last, before TestSizeHeuristic"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ public String getLookupString() {
@Nullable
public abstract Icon getIcon();

public String getLabel() {
return baseName;
}

protected String getItemText() {
return baseName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,18 @@
public class FilePathLookupElement extends BuildLookupElement {

private final String itemText;
private final Boolean isDirectory;
private final NullableLazyValue<Icon> icon;

public FilePathLookupElement(
String fullLabel, String itemText, QuoteType quoteWrapping, NullableLazyValue<Icon> icon) {
String fullLabel,
String itemText,
QuoteType quoteWrapping,
Boolean isDirectory,
NullableLazyValue<Icon> icon) {
super(fullLabel, quoteWrapping);
this.itemText = itemText;
this.isDirectory = isDirectory;
this.icon = icon;
}

Expand All @@ -38,6 +44,10 @@ protected String getItemText() {
return itemText;
}

public Boolean getIsDirectory() {
return isDirectory;
}

@Nullable
@Override
public Icon getIcon() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,8 @@ protected String getTypeText() {
protected String getItemText() {
return targetName;
}

public String getRuleType() {
return ruleType;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ protected Icon compute() {
String fullLabel =
workspacePath != null ? getFullLabel(workspacePath.relativePath()) : file.getPath();
String itemText = workspacePath != null ? getItemText(workspacePath.relativePath()) : fullLabel;
return new FilePathLookupElement(fullLabel, itemText, quoteType, icon);
return new FilePathLookupElement(fullLabel, itemText, quoteType, file.isDirectory(), icon);
}

private String getFullLabel(String relativePath) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.google.idea.blaze.base.lang.buildfile.references;

import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.base.lang.buildfile.completion.FilePathLookupElement;
import com.google.idea.blaze.base.lang.buildfile.completion.LabelRuleLookupElement;
import com.google.idea.blaze.base.lang.buildfile.psi.StringLiteral;
import com.google.idea.blaze.base.model.primitives.Label;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.icons.AllIcons;
import java.util.ArrayList;
import java.util.List;

public class VisibilityReference extends LabelReference {

private static final ImmutableSet<String> PSEUDO_VISIBILITIES =
ImmutableSet.of("__pkg__", "__subpackages__");

public VisibilityReference(StringLiteral element, boolean soft) {
super(element, soft);
}

@Override
public Object[] getVariants() {
Object[] variants = super.getVariants();
ArrayList<LookupElement> results = new ArrayList<>();
for (Object v : variants) {
if (v instanceof FilePathLookupElement) {
FilePathLookupElement le = ((FilePathLookupElement) v);
if (le.getIsDirectory()) {
results.add(
LookupElementBuilder.create("\"" + le.getLabel() + "/")
.withPresentableText(le.getLabel())
.withIcon(AllIcons.Nodes.Variable));
results.addAll(createPseudoVisibilitiesForPackage(le.getLabel()));
}

} else if (v instanceof LabelRuleLookupElement) {
LabelRuleLookupElement le = ((LabelRuleLookupElement) v);
if (le.getRuleType().equals("package_group")) {
results.add(((LookupElement) le));
Label lbl = Label.createIfValid(le.getLabel());
if (lbl != null) {
String pkg = "//" + lbl.blazePackage();
results.addAll(createPseudoVisibilitiesForPackage(pkg));
}
}
}
}

ArrayList<String> globalVisibilities = new ArrayList<>();
globalVisibilities.add("//visibility:public");
globalVisibilities.add("//visibility:private");

for (String v : globalVisibilities) {
results.add(
LookupElementBuilder.create("\"" + v)
.withIcon(AllIcons.Nodes.Variable)
.withPresentableText(v));
results.add(
LookupElementBuilder.create("'" + v)
.withIcon(AllIcons.Nodes.Variable)
.withPresentableText(v));
}

return results.toArray();
}

private List<LookupElement> createPseudoVisibilitiesForPackage(String pkg) {
List<LookupElement> result = new ArrayList<>(PSEUDO_VISIBILITIES.size() * 2);
for (String pv : PSEUDO_VISIBILITIES) {
result.add(
LookupElementBuilder.create("\"" + pkg + ":" + pv)
.withPresentableText(pkg + ":" + pv)
.withIcon(AllIcons.Nodes.Variable));
result.add(
LookupElementBuilder.create("'" + pkg + ":" + pv)
.withPresentableText(pkg + ":" + pv)
.withIcon(AllIcons.Nodes.Variable));
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.google.idea.blaze.base.lang.buildfile.references;

import com.intellij.psi.PsiReferenceContributor;
import com.intellij.psi.PsiReferenceRegistrar;
import org.jetbrains.annotations.NotNull;

public class VisibilityReferenceContributor extends PsiReferenceContributor {
@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(
VisibilityReferenceProvider.PATTERN, new VisibilityReferenceProvider());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.google.idea.blaze.base.lang.buildfile.references;

import static com.intellij.patterns.PlatformPatterns.psiElement;

import com.google.common.collect.ImmutableSet;
import com.google.idea.blaze.base.lang.buildfile.language.BuildFileLanguage;
import com.google.idea.blaze.base.lang.buildfile.psi.Argument;
import com.google.idea.blaze.base.lang.buildfile.psi.StringLiteral;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PatternCondition;
import com.intellij.patterns.PatternConditionPlus;
import com.intellij.patterns.StandardPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceProvider;
import com.intellij.psi.impl.PsiElementBase;
import com.intellij.util.PairProcessor;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;

public class VisibilityReferenceProvider extends PsiReferenceProvider
implements AttributeSpecificStringLiteralReferenceProvider {

private static final ImmutableSet<String> VISIBILITY_STRING_TYPES = ImmutableSet.of("visibility");

public static final ElementPattern<StringLiteral> PATTERN =
psiElement(StringLiteral.class)
.withLanguage(BuildFileLanguage.INSTANCE)
.inside(
psiElement(Argument.Keyword.class)
.with(nameCondition(StandardPatterns.string().oneOf(VISIBILITY_STRING_TYPES))));

private static PatternCondition<PsiElementBase> nameCondition(final ElementPattern<?> pattern) {
return new PatternConditionPlus<PsiElementBase, String>("_withPsiName", pattern) {
@Override
public boolean processValues(
PsiElementBase t,
ProcessingContext context,
PairProcessor<? super String, ? super ProcessingContext> processor) {
return processor.process(t.getName(), context);
}
};
}

@Override
public PsiReference[] getReferences(String attributeName, StringLiteral literal) {
if (!VISIBILITY_STRING_TYPES.contains(attributeName)) {
return PsiReference.EMPTY_ARRAY;
}
return new PsiReference[] {new VisibilityReference(literal, true)};
}

@Override
public PsiReference[] getReferencesByElement(
@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) {
return new PsiReference[] {psiElement.getReference()};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2023 The Bazel Authors. All rights reserved.
*
* 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.google.idea.blaze.base.lang.buildfile.completion;

import static com.google.common.truth.Truth.assertThat;

import com.google.idea.blaze.base.lang.buildfile.BuildFileIntegrationTestCase;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.intellij.openapi.editor.Editor;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

@RunWith(JUnit4.class)
public class VisibilityCompletionTest extends BuildFileIntegrationTestCase {

@Test
public void testVisibilityReferenceWithDoubleQuote() throws Throwable {
BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "sh_binary(visibility = [\"/\"])");

Editor editor = editorTest.openFileInEditor(file);
editorTest.setCaretPosition(editor, 0, "sh_binary(visibility = [\"/".length());

assertThat(editorTest.getCompletionItemsAsStrings())
.asList()
.containsExactly("\"//visibility:private", "\"//visibility:public");
}

@Test
public void testVisibilityReferenceWithSimpleQuote() throws Throwable {
BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "sh_binary(visibility = ['/'])");

Editor editor = editorTest.openFileInEditor(file);
editorTest.setCaretPosition(editor, 0, "sh_binary(visibility = ['/".length());

assertThat(editorTest.getCompletionItemsAsStrings())
.asList()
.containsExactly("'//visibility:private", "'//visibility:public");
}

@Test
public void testVisibilityReferenceWithOtherPackages() throws Throwable {
createBuildFile(new WorkspacePath("pkg/foo/BUILD"), "");
BuildFile file = createBuildFile(new WorkspacePath("BUILD"), "sh_binary(visibility = ['/'])");

Editor editor = editorTest.openFileInEditor(file);
editorTest.setCaretPosition(editor, 0, "sh_binary(visibility = ['/".length());

assertThat(editorTest.getCompletionItemsAsStrings())
.asList()
.containsExactly(
"'//visibility:private",
"'//visibility:public",
"'//pkg/foo:__pkg__",
"'//pkg/foo:__subpackages__");
}

@Test
public void testVisibilityReferenceWithPackageGroup() throws Throwable {
createBuildFile(new WorkspacePath("pkg/foo/BUILD"), "package_group(name = 'bob')");
BuildFile file =
createBuildFile(new WorkspacePath("BUILD"), "sh_binary(visibility = ['//pkg/foo:'])");

Editor editor = editorTest.openFileInEditor(file);
editorTest.setCaretPosition(editor, 0, "sh_binary(visibility = ['//pkg/foo:".length());

assertThat(editorTest.getCompletionItemsAsStrings())
.asList()
.containsExactly(
"'//pkg/foo:bob'",
"'//pkg/foo:__pkg__",
"'//pkg/foo:__subpackages__");
}
}
2 changes: 1 addition & 1 deletion cpp/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ optional_plugin_xml(

intellij_plugin_library(
name = "plugin_library",
plugin_xmls = [":non_optional_cidr"],
optional_plugin_xmls = [":optional_cidr"],
plugin_xmls = [":non_optional_cidr"],
visibility = PLUGIN_PACKAGES_VISIBILITY,
deps = [":cpp"],
)
Expand Down
4 changes: 2 additions & 2 deletions sdkcompat/v233/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ java_library(
]) + ["com/google/idea/sdkcompat/refactoring/safedelete/JavaSafeDeleteProcessorCompat.java"],
clion = glob([
"com/google/idea/sdkcompat/cpp/**",
"com/google/idea/sdkcompat/clion/**", # For classes incompatible with Android Studio
"com/google/idea/sdkcompat/javascript/**", #api223
"com/google/idea/sdkcompat/clion/**", # For classes incompatible with Android Studio
"com/google/idea/sdkcompat/javascript/**", #api223
]),
intellij = glob([
"com/google/idea/sdkcompat/java/**",
Expand Down
2 changes: 1 addition & 1 deletion third_party/go/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ java_library(
"intellij-2023.2": ["@go_2023_2//:go"],
"intellij-ue-2023.2": ["@go_2023_2//:go"],
"intellij-2023.3": ["@go_2023_3//:go"],
"intellij-ue-2023.3": ["@go_2023_3//:go"],
"intellij-ue-2023.3": ["@go_2023_3//:go"],
"default": [],
}),
)
Expand Down
2 changes: 1 addition & 1 deletion third_party/python/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ java_library(
"intellij-2023.2": ["@python_2023_2//:python"],
"intellij-ue-2023.2": ["@python_2023_2//:python"],
"intellij-2023.3": ["@python_2023_3//:python"],
"intellij-ue-2023.3": ["@python_2023_3//:python"],
"intellij-ue-2023.3": ["@python_2023_3//:python"],
"clion-2021.3": ["@clion_2021_3//:python"],
"clion-2022.1": ["@clion_2022_1//:python"],
"clion-2022.2": ["@clion_2022_2//:python"],
Expand Down

0 comments on commit 79d55e7

Please sign in to comment.