blob: 43d84b8f6790fd572cd5bd3a83c6db377c9089a6 [file] [log] [blame]
/*
* Copyright 2016 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.references;
import com.google.common.collect.Lists;
import com.google.idea.blaze.base.lang.buildfile.psi.BuildFile;
import com.google.idea.blaze.base.lang.buildfile.search.BlazePackage;
import com.google.idea.blaze.base.model.primitives.Label;
import com.google.idea.blaze.base.model.primitives.RuleName;
import com.google.idea.blaze.base.model.primitives.WorkspacePath;
import com.intellij.codeInsight.completion.CompletionUtilCore;
import com.intellij.util.PathUtil;
import javax.annotation.Nullable;
import java.util.List;
/**
* Utility methods for working with blaze labels.
*/
public class LabelUtils {
/**
* Label referring to the given file, or null if it cannot be determined.
*/
@Nullable
public static Label createLabelForFile(BlazePackage blazePackage, @Nullable String filePath) {
if (blazePackage == null || filePath == null) {
return null;
}
String relativeFilePath = blazePackage.getPackageRelativePath(filePath);
if (relativeFilePath == null) {
return null;
}
return createLabelFromRuleName(blazePackage, relativeFilePath);
}
/**
* Returns null if this is not a valid Label (if either the package path or rule name are invalid)
*/
@Nullable
public static Label createLabelFromRuleName(@Nullable BlazePackage blazePackage, @Nullable String ruleName) {
if (blazePackage == null || ruleName == null) {
return null;
}
WorkspacePath packagePath = blazePackage.buildFile.getPackageWorkspacePath();
RuleName name = RuleName.createIfValid(ruleName);
if (packagePath == null || name == null) {
return null;
}
// TODO: Is Label too inefficient?
// (validation done twice,creating List during constructor, re-parsing to extract the package/rule each time)
return new Label(packagePath, name);
}
/**
* Canonicalizes the label (to the form //packagePath:packageRelativeTarget).
* Returns null if the string does not represent a valid label.
*/
@Nullable
public static Label createLabelFromString(@Nullable BuildFile file, @Nullable String labelString) {
if (labelString == null) {
return null;
}
int colonIndex = labelString.indexOf(':');
if (labelString.startsWith("//")) {
if (colonIndex == -1) {
// add the implicit rule name
labelString += ":" + PathUtil.getFileName(labelString);
}
return Label.createIfValid(labelString);
}
WorkspacePath packagePath = file != null ? file.getPackageWorkspacePath() : null;
if (packagePath == null) {
return null;
}
String localPath = colonIndex == -1 ? labelString : labelString.substring(1);
return Label.createIfValid("//" + packagePath.relativePath() + ":" + localPath);
}
/**
* The blaze file referenced by the label.
*/
@Nullable
public static BuildFile getReferencedBuildFile(@Nullable BuildFile containingFile, String packagePathComponent) {
if (containingFile == null) {
return null;
}
if (!packagePathComponent.startsWith("//")) {
return containingFile;
}
return BuildReferenceManager.getInstance(containingFile.getProject()).resolveBlazePackage(packagePathComponent);
}
public static String getRuleComponent(String labelString) {
if (labelString.startsWith("/")) {
int colonIndex = labelString.indexOf(':');
return colonIndex == -1 ? "" : labelString.substring(colonIndex + 1);
}
return labelString.startsWith(":") ? labelString.substring(1) : labelString;
}
public static String getPackagePathComponent(String labelString) {
if (!labelString.startsWith("//")) {
return "";
}
int colonIndex = labelString.indexOf(':');
return colonIndex == -1 ? labelString : labelString.substring(0, colonIndex);
}
/**
* 'load' reference. Of the form [path][/ or :][extra_path/]file_name.bzl
*/
@Nullable
public static String getNiceSkylarkFileName(@Nullable String path) {
if (path == null) {
return null;
}
int colonIndex = path.lastIndexOf(":");
if (colonIndex != -1) {
path = path.substring(colonIndex + 1);
}
int lastSlash = path.lastIndexOf("/");
if (lastSlash == -1) {
return path;
}
return path.substring(lastSlash + 1);
}
/**
* All the possible strings which could resolve to the given target.
* @param includePackageLocalLabels if true, include strings omitting the package path
*/
public static List<String> getAllValidLabelStrings(Label label, boolean includePackageLocalLabels) {
List<String> strings = Lists.newArrayList();
strings.add(label.toString());
String packagePath = label.blazePackage().relativePath();
if (packagePath.isEmpty()) {
return strings;
}
String ruleName = label.ruleName().toString();
if (PathUtil.getFileName(packagePath).equals(ruleName)) {
strings.add("//" + packagePath); // implicit rule name equal to package name
}
if (includePackageLocalLabels) {
strings.add(":" + ruleName);
strings.add(ruleName);
}
return strings;
}
/**
* IntelliJ inserts an identifier string at the caret position during code completion.<br>
* We're only interested in the portion of the string before the caret, so trim the rest.
*/
public static String trimToDummyIdentifier(String string) {
int dummyIdentifierIndex = string.indexOf(CompletionUtilCore.DUMMY_IDENTIFIER);
if (dummyIdentifierIndex == -1) {
dummyIdentifierIndex = string.indexOf(CompletionUtilCore.DUMMY_IDENTIFIER_TRIMMED);
}
return dummyIdentifierIndex == -1 ? string : string.substring(0, dummyIdentifierIndex);
}
}