blob: 9afb380bcbaf508e122846849b8c8c6af347831f [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.psi;
import com.google.idea.blaze.base.lang.buildfile.references.LabelReference;
import com.google.idea.blaze.base.lang.buildfile.references.LoadedSymbolReference;
import com.google.idea.blaze.base.lang.buildfile.references.PackageReferenceFragment;
import com.google.idea.blaze.base.lang.buildfile.references.QuoteType;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import javax.annotation.Nullable;
/** PSI node for string literal expressions */
public class StringLiteral extends BuildElementImpl implements LiteralExpression {
/**
* Removes the leading and trailing quotes. Naive implementation intended for resolving references
* (in which case escaped characters, raw strings, etc. are unlikely).
*/
public static String stripQuotes(String string) {
// TODO: Handle escaped characters, etc. here?
// (extract logic from BuildLexerBase.addStringLiteral)
if (string.startsWith("\"\"\"")) {
return string.length() <= 3 ? "" : string.substring(3, endTrimIndex(string, '"', 3));
}
if (string.startsWith("'''")) {
return string.length() <= 3 ? "" : string.substring(3, endTrimIndex(string, '\'', 3));
}
if (string.startsWith("\"")) {
return string.substring(1, endTrimIndex(string, '"', 1));
}
if (string.startsWith("'")) {
return string.substring(1, endTrimIndex(string, '\'', 1));
}
return string;
}
private static int endTrimIndex(String string, char quoteChar, int numberQuoteChars) {
int trimIndex = string.length();
for (int i = 1;
i <= Math.min(numberQuoteChars, string.length() - numberQuoteChars);
i++, trimIndex--) {
if (string.charAt(string.length() - i) != quoteChar) {
break;
}
}
return trimIndex;
}
public static QuoteType getQuoteType(@Nullable String rawText) {
if (rawText == null) {
return QuoteType.NoQuotes;
}
if (rawText.startsWith("\"\"\"")) {
return QuoteType.TripleDouble;
}
if (rawText.startsWith("'''")) {
return QuoteType.TripleSingle;
}
if (rawText.startsWith("'")) {
return QuoteType.Single;
}
if (rawText.startsWith("\"")) {
return QuoteType.Double;
}
return QuoteType.NoQuotes;
}
public StringLiteral(ASTNode astNode) {
super(astNode);
}
@Override
protected void acceptVisitor(BuildElementVisitor visitor) {
visitor.visitStringLiteral(this);
}
/** Removes the leading and trailing quotes */
public String getStringContents() {
return stripQuotes(getText());
}
public QuoteType getQuoteType() {
return getQuoteType(getText());
}
/**
* Labels are taken to reference: - the actual target they reference - the BUILD package specified
* before the colon (only if explicitly present)
*/
@Override
public PsiReference[] getReferences() {
PsiReference primaryReference = getReference();
if (primaryReference instanceof LabelReference) {
return new PsiReference[] {
primaryReference, new PackageReferenceFragment((LabelReference) primaryReference)
};
}
return primaryReference != null
? new PsiReference[] {primaryReference}
: PsiReference.EMPTY_ARRAY;
}
/** The primary reference -- this is the target referenced by the full label */
@Nullable
@Override
public PsiReference getReference() {
PsiElement parent = getParent();
if (parent instanceof LoadStatement) {
LoadStatement load = (LoadStatement) parent;
StringLiteral importNode = load.getImportPsiElement();
if (importNode == null) {
return null;
}
LabelReference importReference = new LabelReference(importNode, false);
if (this.equals(importNode)) {
return importReference;
}
return new LoadedSymbolReference(this, importReference);
}
return new LabelReference(this, true);
}
public boolean insideLoadStatement() {
return getParentType() == BuildElementTypes.LOAD_STATEMENT;
}
@Override
public String getPresentableText() {
return getText();
}
}