mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 11:53:49 +07:00
[java, property] remove folded substitutions for property key values IDEA-369920
GitOrigin-RevId: f49686dd5053e4f1f4b90a2a19145aff0daa0a04
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d9e548bb55
commit
2096dae2b9
@@ -37,8 +37,6 @@
|
||||
|
||||
<lang.foldingBuilder language="UAST" implementationClass="com.intellij.codeInspection.i18n.folding.PropertyFoldingBuilder"
|
||||
order="FIRST"/>
|
||||
<lang.foldingBuilder language="Properties" implementationClass="com.intellij.lang.properties.ResourceBundleContextFoldingBuilder"/>
|
||||
|
||||
<gotoDeclarationHandler implementation="com.intellij.codeInspection.i18n.folding.I18nMessageGotoDeclarationHandler" order="FIRST"/>
|
||||
<inlineActionHandler implementation="com.intellij.refactoring.inline.InlinePropertyHandler"/>
|
||||
|
||||
@@ -98,9 +96,6 @@
|
||||
|
||||
<completion.confidence language="UAST" implementationClass="com.intellij.lang.properties.PropertyKeyLiteralConfidence" id="propertyKeys" order="before javaSkipAutopopupInStrings"/>
|
||||
|
||||
<applicationService serviceImplementation="com.intellij.lang.properties.PropertiesFoldingSettings"/>
|
||||
<codeFoldingOptionsProvider instance="com.intellij.lang.properties.PropertiesFoldingOptionsProvider"/>
|
||||
|
||||
<fileIconProvider implementation="com.intellij.java.i18n.MessagesFileIconProvider"/>
|
||||
</extensions>
|
||||
|
||||
|
||||
@@ -97,8 +97,6 @@ capitalization.kind.sentence=sentence
|
||||
value.column.name=Value
|
||||
key.column.name=Key
|
||||
command.name.edit.property.value=Edit Property Value
|
||||
# suppress inspection "DevKitPropertiesQuotesValidation"
|
||||
checkbox.fold.to.context=Fold '{0}', '{1}', ... placeholders to corresponding context expressions from Java/Kotlin code
|
||||
inspection.dialog.title.capitalization.display.name=Incorrect string capitalization
|
||||
inspection.suspicious.locales.languages.display.name=Suspicious resource bundle locale languages
|
||||
inspection.non.basic.latin.character.display.name=Non-Basic Latin character
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.lang.properties;
|
||||
|
||||
import com.intellij.application.options.editor.CodeFoldingOptionsProvider;
|
||||
import com.intellij.java.i18n.JavaI18nBundle;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.options.BeanConfigurable;
|
||||
|
||||
@State(name = "PropertiesFoldingSettings", storages = @Storage("editor.xml"))
|
||||
public final class PropertiesFoldingOptionsProvider extends BeanConfigurable<PropertiesFoldingSettings> implements CodeFoldingOptionsProvider {
|
||||
|
||||
public PropertiesFoldingOptionsProvider() {
|
||||
super(PropertiesFoldingSettings.getInstance(), PropertiesFileType.INSTANCE.getDescription());
|
||||
PropertiesFoldingSettings settings = getInstance();
|
||||
|
||||
checkBox(JavaI18nBundle.message("checkbox.fold.to.context"), settings::isFoldPlaceholdersToContext, settings::setFoldPlaceholdersToContext);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.lang.properties;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.SettingsCategory;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@State(name = "PropertiesFoldingSettings", storages = @Storage("editor.xml"), category = SettingsCategory.CODE)
|
||||
public class PropertiesFoldingSettings implements PersistentStateComponent<PropertiesFoldingSettings> {
|
||||
private boolean myFoldPlaceholdersToContext;
|
||||
|
||||
public static PropertiesFoldingSettings getInstance() {
|
||||
return ApplicationManager.getApplication().getService(PropertiesFoldingSettings.class);
|
||||
}
|
||||
|
||||
public void setFoldPlaceholdersToContext(boolean fold) {
|
||||
myFoldPlaceholdersToContext = fold;
|
||||
}
|
||||
|
||||
public boolean isFoldPlaceholdersToContext() {
|
||||
return myFoldPlaceholdersToContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PropertiesFoldingSettings getState() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadState(final @NotNull PropertiesFoldingSettings state) {
|
||||
XmlSerializerUtil.copyBean(state, this);
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.lang.properties;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.folding.FoldingBuilderEx;
|
||||
import com.intellij.lang.folding.FoldingDescriptor;
|
||||
import com.intellij.lang.properties.psi.PropertiesFile;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.progress.EmptyProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressIndicatorProvider;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.LeafPsiElement;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Folds '{@code {n}}' placeholders in .properties file to corresponding arguments from Java code.
|
||||
*
|
||||
* For example, given two files:
|
||||
*
|
||||
* <pre>
|
||||
* <b>myBundle.properties</b>:
|
||||
* {@code extract.name=Extract {0} name to {1} {2}}
|
||||
* <b>A.java</b>:
|
||||
* {@code MyBundle.message("extract.name", "method", "interface", "I.java")}
|
||||
* </pre>
|
||||
* This builder will produce these foldings:
|
||||
* <pre>
|
||||
* <b>myBundle.properties</b>:
|
||||
* {@code extract.name=Extract +method+ name to +interface+ +I.java+}</pre>
|
||||
*/
|
||||
public final class ResourceBundleContextFoldingBuilder extends FoldingBuilderEx {
|
||||
@Override
|
||||
public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
|
||||
if (!PropertiesFoldingSettings.getInstance().isFoldPlaceholdersToContext()) {
|
||||
return FoldingDescriptor.EMPTY_ARRAY;
|
||||
}
|
||||
List<FoldingDescriptor> result = new ArrayList<>();
|
||||
for (IProperty property : ((PropertiesFile)root).getProperties()) {
|
||||
String value = property.getValue();
|
||||
if (value != null && value.contains("{0}")) {
|
||||
fold(property, result);
|
||||
}
|
||||
}
|
||||
return result.toArray(FoldingDescriptor.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
private static void fold(@NotNull IProperty property, @NotNull List<? super FoldingDescriptor> result) {
|
||||
ProgressManager.getInstance().executeProcessUnderProgress(() ->
|
||||
ReferencesSearch.search(property.getPsiElement()).forEach((PsiReference reference) -> !tryToFoldReference(reference, property, result)),
|
||||
getOrCreateIndicator());
|
||||
}
|
||||
private static @NotNull ProgressIndicator getOrCreateIndicator() {
|
||||
ProgressIndicator progress = ProgressIndicatorProvider.getGlobalProgressIndicator();
|
||||
if (progress == null) {
|
||||
progress = new EmptyProgressIndicator();
|
||||
progress.start();
|
||||
}
|
||||
progress.setIndeterminate(false);
|
||||
return progress;
|
||||
}
|
||||
|
||||
|
||||
// return true if folded successfully
|
||||
private static boolean tryToFoldReference(@NotNull PsiReference reference,
|
||||
@NotNull IProperty property,
|
||||
@NotNull List<? super FoldingDescriptor> result) {
|
||||
int before = result.size();
|
||||
PsiElement referenceElement = reference.getElement();
|
||||
// all arguments[i+1..] are considered template arguments
|
||||
String key = property.getUnescapedKey();
|
||||
if (key == null) return false;
|
||||
PsiElement psiElement = property.getPsiElement();
|
||||
String text = psiElement.getText();
|
||||
PsiElement[] arguments = referenceElement.getLanguage().getID().equals("kotlin") ?
|
||||
getKotlinArguments(referenceElement) : getJavaArguments(referenceElement);
|
||||
if (arguments == null) return false;
|
||||
for (int i=0; i<arguments.length; i++) {
|
||||
PsiElement argument = arguments[i];
|
||||
String templateText = "{" + i + "}";
|
||||
int offset = text.indexOf(templateText, key.length());
|
||||
if (offset != -1) {
|
||||
int start = psiElement.getTextRange().getStartOffset();
|
||||
result.add(new FoldingDescriptor(psiElement, start+ offset, start+offset+templateText.length(), null, (argument.getText())));
|
||||
}
|
||||
}
|
||||
return result.size() != before;
|
||||
}
|
||||
|
||||
/**
|
||||
* return arguments of the method referencing this property.
|
||||
* E.g. for {@code MyBundle.message("prop", expr1, argX)} return (expr1, argX)
|
||||
*/
|
||||
private static PsiElement[] getJavaArguments(@NotNull PsiElement referenceElement) {
|
||||
PsiExpression expression = PsiTreeUtil.getParentOfType(referenceElement, PsiExpression.class, false);
|
||||
if (expression == null) return null;
|
||||
PsiElement parent = expression.getParent();
|
||||
if (!(parent instanceof PsiExpressionList)) return null;
|
||||
PsiExpression[] arguments = ((PsiExpressionList)parent).getExpressions();
|
||||
int i = ArrayUtil.indexOf(arguments, expression);
|
||||
if (i == -1) return null;
|
||||
return Arrays.copyOfRange(arguments, i + 1, arguments.length, PsiElement[].class);
|
||||
}
|
||||
|
||||
// since kotlin and java method calls have different psi structure and no common UAST,
|
||||
// we have to have two methods for finding Java/Kotlin code referencing this property
|
||||
private static PsiElement[] getKotlinArguments(@NotNull PsiElement referenceElement) {
|
||||
PsiElement expression = referenceElement.getParent();
|
||||
if (expression == null) return null;
|
||||
List<PsiElement> arguments = new ArrayList<>();
|
||||
for (PsiElement e = expression.getNextSibling(); e != null; e = e.getNextSibling()) {
|
||||
if (e instanceof PsiComment || e instanceof LeafPsiElement) continue;
|
||||
arguments.add(e);
|
||||
}
|
||||
if (arguments.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return arguments.toArray(PsiElement.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCollapsedByDefault(@NotNull ASTNode node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getPlaceholderText(@NotNull ASTNode node) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class MyClass {
|
||||
private final static String BUNDLE_NAME = "i18n";
|
||||
private final static ResourceBundle BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
|
||||
|
||||
public static String getMessage(@PropertyKey(resourceBundle = BUNDLE_NAME) String key, Object... args) {
|
||||
return java.text.MessageFormat.format(BUNDLE.getString(key), args);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
getMessage("com.example.welcomeMessage2", "our App", BUNDLE_NAME);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class MyClass {
|
||||
private final static String BUNDLE_NAME = "i18n";
|
||||
private final static ResourceBundle BUNDLE = ResourceBundle.getBundle(BUNDLE_NAME);
|
||||
|
||||
public static String getMessage(@org.jetbrains.annotations.PropertyKey(resourceBundle = BUNDLE_NAME) String key, Object... args) {
|
||||
return java.text.MessageFormat.format(BUNDLE.getString(key), args);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int i = 0;
|
||||
i++;
|
||||
System.out.println(i);
|
||||
<selection>MyClass.getMessage("com.example.welcomeMessage3", "our App", BUNDLE_NAME)</selection>;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
com.example.welcomeMessage2=Welcome to <fold text='"our App"' expand='false'>{0}</fold> by <fold text='BUNDLE_NAME' expand='false'>{1}</fold>
|
||||
com.example.welcomeMessage3=Welcome
|
||||
@@ -1,2 +0,0 @@
|
||||
com.example.welcomeMessage2=Welcome to {0} by {1}
|
||||
com.example.welcomeMessage3=Welcome
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.folding.impl;
|
||||
|
||||
import com.intellij.codeInsight.folding.JavaCodeFoldingSettings;
|
||||
import com.intellij.lang.properties.PropertiesFoldingSettings;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.testFramework.PlatformTestUtil;
|
||||
import com.intellij.testFramework.fixtures.BasePlatformTestCase;
|
||||
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class PropertiesContextFoldingTest extends BasePlatformTestCase {
|
||||
private boolean old;
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return PlatformTestUtil.getCommunityPath() + "/plugins/java-i18n/testData/contextFolding";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
old = PropertiesFoldingSettings.getInstance().isFoldPlaceholdersToContext();
|
||||
PropertiesFoldingSettings.getInstance().setFoldPlaceholdersToContext(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
try {
|
||||
PropertiesFoldingSettings.getInstance().setFoldPlaceholdersToContext(old);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
addSuppressedException(e);
|
||||
}
|
||||
finally {
|
||||
super.tearDown();
|
||||
}
|
||||
}
|
||||
|
||||
public void testPlaceholdersInPropertiesAreFolded() throws IOException {
|
||||
myFixture.configureByFile("MyClass.java");
|
||||
myFixture.configureByFile("i18n.properties");
|
||||
|
||||
String actual = StringUtil.convertLineSeparators(((CodeInsightTestFixtureImpl)myFixture).getFoldingDescription(true));
|
||||
String expected = StringUtil.convertLineSeparators(FileUtil.loadFile(new File(myFixture.getTestDataPath(), "i18n.after")));
|
||||
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
public void testRangesInJava() {
|
||||
JavaCodeFoldingSettings foldingSettings = JavaCodeFoldingSettings.getInstance();
|
||||
boolean collapseMethods = foldingSettings.isCollapseMethods();
|
||||
boolean i18nMessages = foldingSettings.isCollapseI18nMessages();
|
||||
try {
|
||||
foldingSettings.setCollapseMethods(false);
|
||||
foldingSettings.setCollapseI18nMessages(true);
|
||||
myFixture.copyFileToProject("i18n.properties");
|
||||
PsiFile file = myFixture.configureByFile("MyClass1.java");
|
||||
|
||||
List<FoldingUpdate.RegionInfo> foldings = FoldingUpdate.getFoldingsFor(file, false);
|
||||
assertFalse(foldings.stream().map(info -> info.descriptor).anyMatch(descriptor -> descriptor.getPlaceholderText().equals("\"Welcome\"")));
|
||||
}
|
||||
finally {
|
||||
foldingSettings.setCollapseMethods(collapseMethods);
|
||||
foldingSettings.setCollapseI18nMessages(i18nMessages);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user