Skip to content

Commit

Permalink
feat: Starlark Debugger Gutter Icons (#6999)
Browse files Browse the repository at this point in the history
* feat: Starlark Debugger Gutter Icons
  • Loading branch information
tpasternak authored Nov 19, 2024
1 parent 373eb7a commit fda4bb2
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 69 deletions.
4 changes: 4 additions & 0 deletions base/src/META-INF/blaze-base.xml
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,9 @@
<runConfigurationProducer
implementation="com.google.idea.blaze.base.run.producers.BinaryContextRunConfigurationProducer"
order="first"/>
<runConfigurationProducer
implementation="com.google.idea.blaze.base.run.producers.BlazeBuildTargetRunConfigurationProducer"
order="first"/>
<stepsBeforeRunProvider implementation="com.google.idea.blaze.base.run.BlazeBeforeRunTaskProvider"/>
<applicationService serviceInterface="com.google.idea.blaze.base.help.BlazeHelpHandler"
serviceImplementation="com.google.idea.blaze.base.help.BlazeHelpHandlerImpl"/>
Expand Down Expand Up @@ -623,6 +626,7 @@
<BuildSystemProvider implementation="com.google.idea.blaze.base.bazel.BazelBuildSystemProvider" id="BazelBuildSystemProvider" order="last"/>
<BuildifierBinaryProvider implementation="com.google.idea.blaze.base.buildmodifier.DefaultBuildifierBinaryProvider"/>
<BlazeCommandRunConfigurationHandlerProvider implementation="com.google.idea.blaze.base.run.confighandler.PendingTargetRunConfigurationHandlerProvider"/>
<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"/>
Expand Down
5 changes: 5 additions & 0 deletions base/src/com/google/idea/blaze/base/run/ExecutorType.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public enum ExecutorType {
DEBUG,
FAST_BUILD_DEBUG,
COVERAGE,
DEBUG_STARLARK,
UNKNOWN;

public static ExecutorType fromExecutor(Executor executor) {
Expand All @@ -52,6 +53,10 @@ public static ExecutorType fromExecutorId(String executorId) {
if (executorId.equals("Coverage")) {
return COVERAGE;
}

if (executorId.equals("SkylarkDebugExecutor")) {
return DEBUG_STARLARK;
}
return UNKNOWN;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,35 +36,13 @@
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nullable;

/** Creates run configurations from a BUILD file targets. */
public class BlazeBuildFileRunConfigurationProducer
extends BlazeRunConfigurationProducer<BlazeCommandRunConfiguration> {

static class BuildTarget {

final FuncallExpression rule;
final RuleType ruleType;
final Label label;

BuildTarget(FuncallExpression rule, RuleType ruleType, Label label) {
this.rule = rule;
this.ruleType = ruleType;
this.label = label;
}

@Nullable
TargetInfo guessTargetInfo() {
String ruleName = rule.getFunctionName();
if (ruleName == null) {
return null;
}
Kind kind = Kind.fromRuleName(ruleName);
return kind != null ? TargetInfo.builder(label, kind.getKindString()).build() : null;
}
}

public BlazeBuildFileRunConfigurationProducer() {
super(BlazeCommandRunConfigurationType.getInstance());
}
Expand All @@ -85,9 +63,9 @@ protected boolean doSetupConfigFromContext(
if (target == null) {
return false;
}
sourceElement.set(target.rule);
sourceElement.set(target.rule());
setupConfiguration(configuration.getProject(), blazeProjectData, configuration, target);
return true;
return configuration.getHandler().getCommandName() != null;
}

@Override
Expand All @@ -97,7 +75,7 @@ protected boolean doIsConfigFromContext(
if (target == null) {
return false;
}
if (!Objects.equals(configuration.getTargets(), ImmutableList.of(target.label))) {
if (!Objects.equals(configuration.getTargets(), ImmutableList.of(target.label()))) {
return false;
}
// We don't know any details about how the various factories set up configurations from here.
Expand Down Expand Up @@ -162,9 +140,9 @@ private static void setupConfiguration(
// First see if a BlazeRunConfigurationFactory can give us a specialized setup.
for (BlazeRunConfigurationFactory configurationFactory :
BlazeRunConfigurationFactory.EP_NAME.getExtensions()) {
if (configurationFactory.handlesTarget(project, blazeProjectData, target.label)
if (configurationFactory.handlesTarget(project, blazeProjectData, target.label())
&& configurationFactory.handlesConfiguration(configuration)) {
configurationFactory.setupConfiguration(configuration, target.label);
configurationFactory.setupConfiguration(configuration, target.label());
return;
}
}
Expand All @@ -179,24 +157,27 @@ private static void setupBuildFileConfiguration(
if (info != null) {
config.setTargetInfo(info);
} else {
config.setTarget(target.label);
config.setTarget(target.label());
}
BlazeCommandRunConfigurationCommonState state =
config.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
if (state != null) {
state.getCommandState().setCommand(commandForRuleType(target.ruleType));
Optional<BlazeCommandName> blazeCommandName = commandForRuleType(target.ruleType());
blazeCommandName.ifPresent(command ->
state.getCommandState().setCommand(command)
);
}
config.setGeneratedName();
}

static BlazeCommandName commandForRuleType(RuleType ruleType) {
static Optional<BlazeCommandName> commandForRuleType(RuleType ruleType) {
switch (ruleType) {
case BINARY:
return BlazeCommandName.RUN;
return Optional.of(BlazeCommandName.RUN);
case TEST:
return BlazeCommandName.TEST;
return Optional.of(BlazeCommandName.TEST);
default:
return BlazeCommandName.BUILD;
return Optional.empty();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* Copyright 2024 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.run.producers;

import com.google.common.collect.ImmutableList;
import com.google.idea.blaze.base.command.BlazeCommandName;
import com.google.idea.blaze.base.dependencies.TargetInfo;
import com.google.idea.blaze.base.lang.buildfile.psi.FuncallExpression;
import com.google.idea.blaze.base.model.BlazeProjectData;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.run.BlazeCommandRunConfiguration;
import com.google.idea.blaze.base.run.BlazeCommandRunConfigurationType;
import com.google.idea.blaze.base.run.state.BlazeCommandRunConfigurationCommonState;
import com.google.idea.blaze.base.settings.Blaze;
import com.google.idea.blaze.base.settings.BlazeImportSettings.ProjectType;
import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager;
import com.intellij.execution.actions.ConfigurationContext;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import java.util.Objects;
import javax.annotation.Nullable;

/** Creates run configurations from a BUILD file targets.
* Based on BlazeBuildFileRunConfigurationProducer.java
* */
public class BlazeBuildTargetRunConfigurationProducer
extends BlazeRunConfigurationProducer<BlazeCommandRunConfiguration> {

public BlazeBuildTargetRunConfigurationProducer() {
super(BlazeCommandRunConfigurationType.getInstance());
}

@Override
protected boolean doSetupConfigFromContext(
BlazeCommandRunConfiguration configuration,
ConfigurationContext context,
Ref<PsiElement> sourceElement) {
Project project = configuration.getProject();
BlazeProjectData blazeProjectData =
BlazeProjectDataManager.getInstance(project).getBlazeProjectData();
// With query sync we don't need a sync to run a configuration
if (blazeProjectData == null && Blaze.getProjectType(project) != ProjectType.QUERY_SYNC) {
return false;
}
BuildTarget target = getBuildTarget(context);
if (target == null) {
return false;
}
sourceElement.set(target.rule());
setupConfiguration(configuration.getProject(), blazeProjectData, configuration, target);
return true;
}

@Override
protected boolean doIsConfigFromContext(
BlazeCommandRunConfiguration configuration, ConfigurationContext context) {
BuildTarget target = getBuildTarget(context);
if (target == null) {
return false;
}
return Objects.equals(configuration.getTargets(), ImmutableList.of(target.label()));
}

@Nullable
private static BuildTarget getBuildTarget(ConfigurationContext context) {
return getBuildTarget(
PsiTreeUtil.getNonStrictParentOfType(context.getPsiLocation(), FuncallExpression.class));
}

@Nullable
static BuildTarget getBuildTarget(@Nullable FuncallExpression rule) {
if (rule == null) {
return null;
}
String ruleType = rule.getFunctionName();
Label label = rule.resolveBuildLabel();
if (ruleType == null || label == null) {
return null;
}
return new BuildTarget(rule, Kind.guessRuleType(ruleType), label);
}

private static void setupConfiguration(
Project ignoredProject,
BlazeProjectData ignoredBlazeProjectData,
BlazeCommandRunConfiguration configuration,
BuildTarget target) {
TargetInfo info = target.guessTargetInfo();
if (info != null) {
configuration.setTargetInfo(info);
} else {
configuration.setTarget(target.label());
}
BlazeCommandRunConfigurationCommonState state =
configuration.getHandlerStateIfType(BlazeCommandRunConfigurationCommonState.class);
if (state != null) {
state.getCommandState().setCommand(BlazeCommandName.BUILD);
}
configuration.setGeneratedName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,12 @@
*/
package com.google.idea.blaze.base.run.producers;

import static java.util.concurrent.TimeUnit.SECONDS;

import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.idea.blaze.base.dependencies.TargetInfo;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile.BlazeFileType;
import com.google.idea.blaze.base.lang.buildfile.psi.FuncallExpression;
import com.google.idea.blaze.base.lang.buildfile.psi.ReferenceExpression;
import com.google.idea.blaze.base.model.primitives.RuleType;
import com.google.idea.blaze.base.run.producers.BlazeBuildFileRunConfigurationProducer.BuildTarget;
import com.google.idea.blaze.base.run.targetfinder.TargetFinder;
import com.google.idea.common.experiments.BoolExperiment;
import com.intellij.execution.lineMarker.ExecutorAction;
import com.intellij.execution.lineMarker.RunLineMarkerContributor;
Expand All @@ -39,8 +33,7 @@
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.source.tree.LeafElement;
import com.intellij.util.containers.ContainerUtil;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;

import javax.annotation.Nullable;

/** Generates run/debug gutter icons for BUILD files. */
Expand Down Expand Up @@ -77,28 +70,10 @@ private static boolean isRunContext(PsiElement element) {
return false;
}
BuildTarget data = BlazeBuildFileRunConfigurationProducer.getBuildTarget(rule);
if (data == null || data.ruleType == RuleType.LIBRARY) {
if (data == null) {
return false;
}
if (HANDLED_RULE_TYPES.contains(data.ruleType)) {
return true;
}
// finally, run a slower check for the underlying target type (useful for macros)
// TODO(brendandouglas): do this asynchronously? Hard to do with RunLineMarkerProvider. Ideas:
// - custom LineMarkerFactory delegating to LineMarkerPass
// - dirty file status somehow?
// - override RunLineMarkerProvider, supporting collectSlowLineMarkers
ListenableFuture<TargetInfo> future =
TargetFinder.findTargetInfoFuture(element.getProject(), data.label);
try {
TargetInfo target = future.get(2, SECONDS);
return target != null && HANDLED_RULE_TYPES.contains(target.getRuleType());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (TimeoutException | ExecutionException e) {
// ignore
}
return false;
return true; // We want to put a gutter icon next to each target to provide a starlark debugger action
}

private static FuncallExpression getRuleFuncallExpression(PsiElement element) {
Expand Down
22 changes: 22 additions & 0 deletions base/src/com/google/idea/blaze/base/run/producers/BuildTarget.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.google.idea.blaze.base.run.producers;

import com.google.idea.blaze.base.dependencies.TargetInfo;
import com.google.idea.blaze.base.lang.buildfile.psi.FuncallExpression;
import com.google.idea.blaze.base.model.primitives.Kind;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.RuleType;

import javax.annotation.Nullable;

record BuildTarget(FuncallExpression rule, RuleType ruleType, Label label) {

@Nullable
TargetInfo guessTargetInfo() {
String ruleName = rule.getFunctionName();
if (ruleName == null) {
return null;
}
Kind kind = Kind.fromRuleName(ruleName);
return kind != null ? TargetInfo.builder(label, kind.getKindString()).build() : null;
}
}
Loading

0 comments on commit fda4bb2

Please sign in to comment.