diff --git a/CHANGELOG.md b/CHANGELOG.md
index 41298bc..b934305 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@
### Added
- Key delimiters (namespace / section) can be configured
- Extract translation intention
+- Full language support for Java, Kotlin, JavaScript / TypeScript, Vue and PHP
- Individual icon for tool-window and lookup items
- Dedicated configuration file (easy-i18n.xml) inside .idea folder
diff --git a/gradle.properties b/gradle.properties
index b630b41..6c5ffae 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,7 +17,7 @@ platformVersion = 2021.3
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
-platformPlugins = org.jetbrains.kotlin, JavaScriptLanguage, org.jetbrains.plugins.vue:213.5744.223
+platformPlugins = org.jetbrains.kotlin, JavaScriptLanguage, org.jetbrains.plugins.vue:213.5744.223, com.jetbrains.php:213.5744.279
# Java language level used to compile sources and to generate the files for - Java 11 is required since 2020.3
javaVersion = 11
diff --git a/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java b/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java
index 8ebc10f..3d2b705 100644
--- a/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java
+++ b/src/main/java/de/marhali/easyi18n/assistance/completion/JavaCompletionContributor.java
@@ -3,14 +3,15 @@ package de.marhali.easyi18n.assistance.completion;
import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.patterns.PlatformPatterns;
-import com.intellij.psi.PsiLiteralValue;
+import com.intellij.psi.PsiLiteralExpression;
/**
- * Java specific completion contributor
+ * Java specific completion contributor.
* @author marhali
*/
public class JavaCompletionContributor extends CompletionContributor {
public JavaCompletionContributor() {
- extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(PsiLiteralValue.class), new KeyCompletionProvider());
+ extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(PsiLiteralExpression.class),
+ new KeyCompletionProvider());
}
}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/completion/JsCompletionContributor.java b/src/main/java/de/marhali/easyi18n/assistance/completion/JsCompletionContributor.java
new file mode 100644
index 0000000..3154937
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/completion/JsCompletionContributor.java
@@ -0,0 +1,17 @@
+package de.marhali.easyi18n.assistance.completion;
+
+import com.intellij.codeInsight.completion.CompletionContributor;
+import com.intellij.codeInsight.completion.CompletionType;
+import com.intellij.lang.javascript.psi.JSLiteralExpression;
+import com.intellij.patterns.PlatformPatterns;
+
+/**
+ * JavaScript specific completion contributor.
+ * @author marhali
+ */
+public class JsCompletionContributor extends CompletionContributor {
+ public JsCompletionContributor() {
+ extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(JSLiteralExpression.class),
+ new KeyCompletionProvider());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/de/marhali/easyi18n/assistance/completion/PhpCompletionContributor.java b/src/main/java/de/marhali/easyi18n/assistance/completion/PhpCompletionContributor.java
new file mode 100644
index 0000000..f6c76b9
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/completion/PhpCompletionContributor.java
@@ -0,0 +1,17 @@
+package de.marhali.easyi18n.assistance.completion;
+
+import com.intellij.codeInsight.completion.CompletionContributor;
+import com.intellij.codeInsight.completion.CompletionType;
+import com.intellij.patterns.PlatformPatterns;
+import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
+
+/**
+ * Php specific completion contributor.
+ * @author marhali
+ */
+public class PhpCompletionContributor extends CompletionContributor {
+ public PhpCompletionContributor() {
+ extend(CompletionType.BASIC, PlatformPatterns.psiElement().inside(StringLiteralExpression.class),
+ new KeyCompletionProvider());
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java
index 5637da7..a4387bf 100644
--- a/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java
+++ b/src/main/java/de/marhali/easyi18n/assistance/folding/AbstractFoldingBuilder.java
@@ -2,12 +2,17 @@ package de.marhali.easyi18n.assistance.folding;
import com.intellij.lang.ASTNode;
import com.intellij.lang.folding.FoldingBuilderEx;
-import com.intellij.openapi.editor.FoldingGroup;
+import com.intellij.lang.folding.FoldingDescriptor;
+import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
import de.marhali.easyi18n.DataStore;
import de.marhali.easyi18n.InstanceManager;
import de.marhali.easyi18n.assistance.OptionalAssistance;
+import de.marhali.easyi18n.model.TranslationData;
import de.marhali.easyi18n.model.TranslationValue;
import de.marhali.easyi18n.settings.ProjectSettingsService;
import de.marhali.easyi18n.util.KeyPathConverter;
@@ -15,25 +20,67 @@ import de.marhali.easyi18n.util.KeyPathConverter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
- * Language specific folding of translation key with preferred locale value.
+ * Language specific translation key folding with representative locale value.
* @author marhali
*/
abstract class AbstractFoldingBuilder extends FoldingBuilderEx implements OptionalAssistance {
-
- protected static final FoldingGroup group = FoldingGroup.newGroup("EasyI18n key folding");
+ /**
+ * Extract all relevant folding regions for the desired root element.
+ * The implementation does not need to verify if the character literal is a valid translation.
+ * @param root Root element
+ * @return found regions
+ */
+ abstract @NotNull List> extractRegions(@NotNull PsiElement root);
/**
- * Constructs the folding text for the provided text.
- * @param project Opened project
- * @param text Designated translation key
- * @return Preferred locale value or null if translation does not exists
+ * Extract the text from the given node.
+ * @param node Node
+ * @return extracted text or null if not applicable
*/
- protected @Nullable String getPlaceholderText(@NotNull Project project, @Nullable String text) {
+ abstract @Nullable String extractText(@NotNull ASTNode node);
+
+ @Override
+ public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
+
+ if(quick || !isAssistance(root.getProject())) {
+ return FoldingDescriptor.EMPTY;
+ }
+
+ List descriptors = new ArrayList<>();
+
+ TranslationData data = InstanceManager.get(root.getProject()).store().getData();
+ KeyPathConverter converter = new KeyPathConverter(root.getProject());
+
+ for(Pair region : extractRegions(root)) {
+ if(data.getTranslation(converter.fromString(region.first)) == null) {
+ continue;
+ }
+
+ TextRange range = new TextRange(region.second.getTextRange().getStartOffset() + 1,
+ region.second.getTextRange().getEndOffset() - 1);
+
+ // Some language implementations like [Vue Template] does not support FoldingGroup's
+ FoldingDescriptor descriptor = new FoldingDescriptor(region.second.getNode(), range);
+
+ descriptors.add(descriptor);
+ }
+
+ return descriptors.toArray(new FoldingDescriptor[0]);
+ }
+
+ @Override
+ public @Nullable String getPlaceholderText(@NotNull ASTNode node) {
+ String text = extractText(node);
+
if(text == null) {
return null;
}
+ Project project = node.getPsi().getProject();
DataStore store = InstanceManager.get(project).store();
KeyPathConverter converter = new KeyPathConverter(project);
TranslationValue localeValues = store.getData().getTranslation(converter.fromString(text));
diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java
index cac89f3..e4904a5 100644
--- a/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java
+++ b/src/main/java/de/marhali/easyi18n/assistance/folding/JavaFoldingBuilder.java
@@ -1,66 +1,32 @@
package de.marhali.easyi18n.assistance.folding;
import com.intellij.lang.ASTNode;
-import com.intellij.lang.folding.FoldingDescriptor;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.util.PsiTreeUtil;
-import de.marhali.easyi18n.DataStore;
-import de.marhali.easyi18n.InstanceManager;
-import de.marhali.easyi18n.util.KeyPathConverter;
-
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Java specific translation key folding.
* @author marhali
*/
public class JavaFoldingBuilder extends AbstractFoldingBuilder {
-
@Override
- public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
- List descriptors = new ArrayList<>();
-
- if(!isAssistance(root.getProject())) {
- return FoldingDescriptor.EMPTY;
- }
-
- Collection literalExpressions =
- PsiTreeUtil.findChildrenOfType(root, PsiLiteralExpression.class);
-
- DataStore store = InstanceManager.get(root.getProject()).store();
- KeyPathConverter converter = new KeyPathConverter(root.getProject());
-
- for(final PsiLiteralExpression literalExpression : literalExpressions) {
- String value = literalExpression.getValue() instanceof String
- ? (String) literalExpression.getValue() : null;
-
- if(value == null || store.getData().getTranslation(converter.fromString(value)) == null) {
- continue;
- }
-
- TextRange range = literalExpression.getTextRange();
- FoldingDescriptor descriptor = new FoldingDescriptor(literalExpression.getNode(),
- new TextRange(range.getStartOffset() + 1, range.getEndOffset() - 1), group);
-
- descriptors.add(descriptor);
- }
-
- return descriptors.toArray(new FoldingDescriptor[0]);
+ @NotNull List> extractRegions(@NotNull PsiElement root) {
+ return PsiTreeUtil.findChildrenOfType(root, PsiLiteralExpression.class).stream().map(literalExpression ->
+ Pair.pair(String.valueOf(literalExpression.getValue()), (PsiElement) literalExpression))
+ .collect(Collectors.toList());
}
@Override
- public @Nullable String getPlaceholderText(@NotNull ASTNode node) {
+ @Nullable String extractText(@NotNull ASTNode node) {
PsiLiteralExpression literalExpression = node.getPsi(PsiLiteralExpression.class);
- String value = literalExpression.getValue() instanceof String ? (String) literalExpression.getValue() : null;
- return getPlaceholderText(literalExpression.getProject(), value);
+ return String.valueOf(literalExpression.getValue());
}
}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/JsFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/JsFoldingBuilder.java
new file mode 100644
index 0000000..e96b53a
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/folding/JsFoldingBuilder.java
@@ -0,0 +1,32 @@
+package de.marhali.easyi18n.assistance.folding;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.lang.javascript.psi.JSLiteralExpression;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * JavaScript specific translation key folding.
+ * @author marhali
+ */
+public class JsFoldingBuilder extends AbstractFoldingBuilder {
+ @Override
+ @NotNull List> extractRegions(@NotNull PsiElement root) {
+ return PsiTreeUtil.findChildrenOfType(root, JSLiteralExpression.class).stream().map(literalExpression ->
+ Pair.pair(literalExpression.getStringValue(), (PsiElement) literalExpression))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ @Nullable String extractText(@NotNull ASTNode node) {
+ JSLiteralExpression literalExpression = node.getPsi(JSLiteralExpression.class);
+ return literalExpression.getStringValue();
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java
index 014bfa3..bd95f16 100644
--- a/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java
+++ b/src/main/java/de/marhali/easyi18n/assistance/folding/KtFoldingBuilder.java
@@ -1,22 +1,16 @@
package de.marhali.easyi18n.assistance.folding;
import com.intellij.lang.ASTNode;
-import com.intellij.lang.folding.FoldingDescriptor;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
-import de.marhali.easyi18n.DataStore;
-import de.marhali.easyi18n.InstanceManager;
-import de.marhali.easyi18n.util.KeyPathConverter;
-
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.psi.KtStringTemplateEntry;
+import org.jetbrains.kotlin.psi.KtStringTemplateExpression;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.List;
/**
@@ -25,39 +19,27 @@ import java.util.List;
*/
public class KtFoldingBuilder extends AbstractFoldingBuilder {
@Override
- public FoldingDescriptor @NotNull [] buildFoldRegions(@NotNull PsiElement root, @NotNull Document document, boolean quick) {
- List descriptors = new ArrayList<>();
+ @NotNull List> extractRegions(@NotNull PsiElement root) {
+ List> regions = new ArrayList<>();
- if(!isAssistance(root.getProject())) {
- return FoldingDescriptor.EMPTY;
- }
-
- Collection templateEntries =
- PsiTreeUtil.findChildrenOfType(root, KtStringTemplateEntry.class);
-
- DataStore store = InstanceManager.get(root.getProject()).store();
- KeyPathConverter converter = new KeyPathConverter(root.getProject());
-
- for (KtStringTemplateEntry templateEntry : templateEntries) {
- String value = templateEntry.getText();
-
- if(value == null || store.getData().getTranslation(converter.fromString(value)) == null) {
- continue;
+ for (KtStringTemplateExpression templateExpression : PsiTreeUtil.findChildrenOfType(root, KtStringTemplateExpression.class)) {
+ for (KtStringTemplateEntry entry : templateExpression.getEntries()) {
+ regions.add(Pair.pair(entry.getText(), templateExpression));
+ break;
}
-
- TextRange range = templateEntry.getTextRange();
- FoldingDescriptor descriptor = new FoldingDescriptor(templateEntry.getNode(),
- new TextRange(range.getStartOffset(), range.getEndOffset()), group);
-
- descriptors.add(descriptor);
}
- return descriptors.toArray(new FoldingDescriptor[0]);
+ return regions;
}
@Override
- public @Nullable String getPlaceholderText(@NotNull ASTNode node) {
- KtStringTemplateEntry templateEntry = node.getPsi(KtStringTemplateEntry.class);
- return getPlaceholderText(templateEntry.getProject(), templateEntry.getText());
+ @Nullable String extractText(@NotNull ASTNode node) {
+ KtStringTemplateExpression templateExpression = node.getPsi(KtStringTemplateExpression.class);
+
+ for (KtStringTemplateEntry entry : templateExpression.getEntries()) {
+ return entry.getText();
+ }
+
+ return null;
}
}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/folding/PhpFoldingBuilder.java b/src/main/java/de/marhali/easyi18n/assistance/folding/PhpFoldingBuilder.java
new file mode 100644
index 0000000..f694b3b
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/folding/PhpFoldingBuilder.java
@@ -0,0 +1,31 @@
+package de.marhali.easyi18n.assistance.folding;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.util.Pair;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Php specific translation key folding.
+ * @author marhali
+ */
+public class PhpFoldingBuilder extends AbstractFoldingBuilder {
+ @Override
+ @NotNull List> extractRegions(@NotNull PsiElement root) {
+ return PsiTreeUtil.findChildrenOfType(root, StringLiteralExpression.class).stream().map(literalExpression ->
+ Pair.pair(literalExpression.getContents(), (PsiElement) literalExpression))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ @Nullable String extractText(@NotNull ASTNode node) {
+ StringLiteralExpression literalExpression = node.getPsi(StringLiteralExpression.class);
+ return literalExpression.getContents();
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java
deleted file mode 100644
index 2f7c2bf..0000000
--- a/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractExtractIntention.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package de.marhali.easyi18n.assistance.intention;
-
-import com.intellij.codeInsight.intention.FileModifier;
-import com.intellij.openapi.command.WriteCommandAction;
-import com.intellij.openapi.editor.Caret;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
-import com.intellij.openapi.project.Project;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
-import com.intellij.psi.util.PsiEditorUtil;
-
-import com.siyeh.ipp.base.MutablyNamedIntention;
-
-import de.marhali.easyi18n.InstanceManager;
-import de.marhali.easyi18n.assistance.OptionalAssistance;
-import de.marhali.easyi18n.dialog.AddDialog;
-import de.marhali.easyi18n.model.KeyPath;
-import de.marhali.easyi18n.model.TranslationData;
-import de.marhali.easyi18n.settings.ProjectSettings;
-import de.marhali.easyi18n.settings.ProjectSettingsService;
-import de.marhali.easyi18n.util.KeyPathConverter;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.ResourceBundle;
-
-/**
- * Intention for extracting a translation. Either by translation key or preferred locale value
- * @author marhali
- */
-abstract class AbstractExtractIntention extends MutablyNamedIntention implements OptionalAssistance {
-
- protected static final ResourceBundle bundle = ResourceBundle.getBundle("messages");
-
- @Override
- public @NotNull String getFamilyName() {
- return "EasyI18n";
- }
-
- @Override
- public boolean startInWriteAction() {
- return false;
- }
-
- protected @NotNull String getTextForElement(@NotNull Project project, @Nullable String key) {
- KeyPathConverter converter = new KeyPathConverter(project);
- TranslationData data = InstanceManager.get(project).store().getData();
-
- return key != null && data.getTranslation(converter.fromString(key)) != null
- ? bundle.getString("action.edit")
- : bundle.getString("action.extract");
- }
-
- protected void extractTranslation(@NotNull Project project, @NotNull String text, PsiElement psi) {
- ProjectSettings settings = ProjectSettingsService.get(project).getState();
- KeyPathConverter converter = new KeyPathConverter(settings);
-
- // Extract translation key
- // We assume that a text is a translation-key if it contains section delimiters and does not end with them
- if(text.contains(settings.getSectionDelimiter()) && !text.endsWith(settings.getSectionDelimiter())) {
- new AddDialog(project, converter.fromString(text), null).showAndHandle();
-
- } else { // Extract translation value (here preview locale value)
- AddDialog dialog = new AddDialog(project, new KeyPath(), text);
-
- // Replace editor caret with chosen translation key
- dialog.registerCallback(translationUpdate -> {
- Editor editor = PsiEditorUtil.findEditor(psi);
-
- if(editor != null) {
- Document doc = editor.getDocument();
- Caret caret = editor.getCaretModel().getPrimaryCaret();
- int start = psi.getTextOffset() + 1;
- int end = start + text.length();
-
- WriteCommandAction.runWriteCommandAction(project, () ->
- doc.replaceString(start, end, converter.toString(translationUpdate.getChange().getKey())));
-
- caret.removeSelection();
- }
- });
-
- dialog.showAndHandle();;
- }
- }
-
- @Override
- public @Nullable FileModifier getFileModifierForPreview(@NotNull PsiFile target) {
- return this;
- }
-}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractTranslationIntention.java
new file mode 100644
index 0000000..99be4c2
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractTranslationIntention.java
@@ -0,0 +1,138 @@
+package de.marhali.easyi18n.assistance.intention;
+
+import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction;
+import com.intellij.codeInspection.util.IntentionFamilyName;
+import com.intellij.codeInspection.util.IntentionName;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.editor.Caret;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.util.IncorrectOperationException;
+
+import de.marhali.easyi18n.InstanceManager;
+import de.marhali.easyi18n.assistance.OptionalAssistance;
+import de.marhali.easyi18n.dialog.AddDialog;
+import de.marhali.easyi18n.dialog.EditDialog;
+import de.marhali.easyi18n.model.KeyPath;
+import de.marhali.easyi18n.model.Translation;
+import de.marhali.easyi18n.model.TranslationData;
+import de.marhali.easyi18n.model.TranslationValue;
+import de.marhali.easyi18n.settings.ProjectSettings;
+import de.marhali.easyi18n.settings.ProjectSettingsService;
+import de.marhali.easyi18n.util.KeyPathConverter;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ResourceBundle;
+
+/**
+ * Intention for translation related use-cases.
+ * Can be used to extract (create) translations or to edit existing ones.
+ * @author marhali
+ */
+abstract class AbstractTranslationIntention extends BaseElementAtCaretIntentionAction implements OptionalAssistance {
+
+ protected static final ResourceBundle bundle = ResourceBundle.getBundle("messages");
+
+ private boolean existingTranslation = false;
+
+ @Override
+ public @IntentionName @NotNull String getText() {
+ return existingTranslation
+ ? bundle.getString("action.edit")
+ : bundle.getString("action.extract");
+ }
+
+ @Override
+ public @NotNull @IntentionFamilyName String getFamilyName() {
+ return "EasyI18n";
+ }
+
+ @Override
+ public boolean startInWriteAction() {
+ return false;
+ }
+
+ /**
+ * This is the only method a language-specific translation intention needs to implement.
+ * The implementation needs to verify element type and extract the relevant key literal or value.
+ * @param element Element at caret
+ * @return extract translation key (not verified!) or null if intention is not applicable for this element
+ */
+ protected abstract @Nullable String extractText(@NotNull PsiElement element);
+
+ @NotNull TextRange convertRange(@NotNull TextRange input) {
+ return new TextRange(input.getStartOffset(), input.getEndOffset());
+ }
+
+ @Override
+ public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
+ if(!isAssistance(project)) {
+ return false;
+ }
+
+ String text = extractText(element);
+
+ if(text != null) {
+ KeyPathConverter converter = new KeyPathConverter(project);
+ existingTranslation = InstanceManager.get(project).store().getData()
+ .getTranslation(converter.fromString(text)) != null;
+ }
+
+ return text != null;
+ }
+
+ @Override
+ public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element)
+ throws IncorrectOperationException {
+
+ ProjectSettings settings = ProjectSettingsService.get(project).getState();
+ KeyPathConverter converter = new KeyPathConverter(settings);
+
+ String text = extractText(element);
+
+ if(text == null) {
+ throw new IncorrectOperationException("Cannot extract translation intention at caret");
+ }
+
+ TranslationData data = InstanceManager.get(project).store().getData();
+ KeyPath path = converter.fromString(text);
+ TranslationValue existingTranslation = data.getTranslation(path);
+
+ // Existing translation - edit dialog
+ if(existingTranslation != null) {
+ new EditDialog(project, new Translation(path, existingTranslation)).showAndHandle();
+ return;
+ }
+
+ // Extract translation by key
+ // We assume that a text is a translation-key if it contains section delimiters and does not end with them
+ if(text.contains(settings.getSectionDelimiter()) && !text.endsWith(settings.getSectionDelimiter())) {
+ new AddDialog(project, path, null).showAndHandle();
+ return;
+ }
+
+ // Extract translation by preview locale value
+ AddDialog dialog = new AddDialog(project, new KeyPath(), text);
+
+ dialog.registerCallback(translationUpdate -> { // Replace text at caret with chosen translation key
+ if(editor != null) {
+ Document doc = editor.getDocument();
+ Caret caret = editor.getCaretModel().getPrimaryCaret();
+ TextRange range = convertRange(element.getTextRange());
+
+ WriteCommandAction.runWriteCommandAction(project, () ->
+ doc.replaceString(range.getStartOffset(), range.getEndOffset(),
+ converter.toString(translationUpdate.getChange().getKey())));
+
+ caret.removeSelection();
+ }
+ });
+
+ dialog.showAndHandle();
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java
deleted file mode 100644
index d074d4f..0000000
--- a/src/main/java/de/marhali/easyi18n/assistance/intention/JavaExtractIntention.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package de.marhali.easyi18n.assistance.intention;
-
-import com.intellij.codeInspection.util.IntentionName;
-import com.intellij.psi.*;
-import com.siyeh.ipp.base.PsiElementPredicate;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Java specific translation key intention.
- * @author marhali
- */
-public class JavaExtractIntention extends AbstractExtractIntention {
-
- @Override
- protected @IntentionName String getTextForElement(PsiElement element) {
- return getTextForElement(element.getProject(), (String) ((PsiLiteralExpression) element).getValue());
- }
-
- @Override
- protected void processIntention(@NotNull PsiElement element) {
- System.out.println("proci2");
- if(!(element instanceof PsiLiteralExpression)
- || !(((PsiLiteralExpression) element).getValue() instanceof String)) {
- return;
- }
-
- extractTranslation(element.getProject(), (String) ((PsiLiteralExpression) element).getValue(), element);
- }
-
- @Override
- protected @NotNull PsiElementPredicate getElementPredicate() {
- System.out.println("predi2");
- return element -> element instanceof PsiLiteralExpression;
- }
-}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JavaTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JavaTranslationIntention.java
new file mode 100644
index 0000000..aed1b98
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/intention/JavaTranslationIntention.java
@@ -0,0 +1,27 @@
+package de.marhali.easyi18n.assistance.intention;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.*;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Java specific translation intention.
+ * @author marhali
+ */
+public class JavaTranslationIntention extends AbstractTranslationIntention {
+ @Override
+ protected @Nullable String extractText(@NotNull PsiElement element) {
+ if(!(element.getParent() instanceof PsiLiteralExpression)) {
+ return null;
+ }
+
+ return String.valueOf(((PsiLiteralExpression) element.getParent()).getValue());
+ }
+
+ @Override
+ @NotNull TextRange convertRange(@NotNull TextRange input) {
+ return new TextRange(input.getStartOffset() + 1, input.getEndOffset() - 1);
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java
deleted file mode 100644
index 5496802..0000000
--- a/src/main/java/de/marhali/easyi18n/assistance/intention/JsExtractIntention.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package de.marhali.easyi18n.assistance.intention;
-
-import com.intellij.codeInspection.util.IntentionName;
-import com.intellij.lang.javascript.psi.JSLiteralExpression;
-import com.intellij.psi.PsiElement;
-import com.siyeh.ipp.base.PsiElementPredicate;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * JavaScript specific translation key intention.
- * @author marhali
- */
-// TODO: current implementation does not support other languages than Java
-public class JsExtractIntention extends AbstractExtractIntention {
-
- @Override
- public @NotNull String getFamilyName() {
- return "JavaScript";
- }
-
- @Override
- protected @IntentionName String getTextForElement(PsiElement element) {
- return "HILFE";
- //return getTextForElement(element.getProject(), ((JSLiteralExpression) element).getStringValue());
- }
-
- @Override
- protected void processIntention(@NotNull PsiElement element) {
- if(!(element instanceof JSLiteralExpression) || ((JSLiteralExpression) element).getStringValue() == null) {
- return;
- }
-
- extractTranslation(element.getProject(), ((JSLiteralExpression) element).getStringValue(), element);
- }
-
- @Override
- protected @NotNull PsiElementPredicate getElementPredicate() {
- return element -> true;
- }
-}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/JsTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/JsTranslationIntention.java
new file mode 100644
index 0000000..da526a5
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/intention/JsTranslationIntention.java
@@ -0,0 +1,28 @@
+package de.marhali.easyi18n.assistance.intention;
+
+import com.intellij.lang.javascript.psi.JSLiteralExpression;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * JavaScript specific translation key intention.
+ * @author marhali
+ */
+public class JsTranslationIntention extends AbstractTranslationIntention {
+ @Override
+ protected @Nullable String extractText(@NotNull PsiElement element) {
+ if(!(element.getParent() instanceof JSLiteralExpression)) {
+ return null;
+ }
+
+ return ((JSLiteralExpression) element.getParent()).getStringValue();
+ }
+
+ @Override
+ @NotNull TextRange convertRange(@NotNull TextRange input) {
+ return new TextRange(input.getStartOffset() + 1, input.getEndOffset() - 1);
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java
deleted file mode 100644
index bdaf628..0000000
--- a/src/main/java/de/marhali/easyi18n/assistance/intention/KtExtractIntention.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package de.marhali.easyi18n.assistance.intention;
-
-import com.intellij.codeInspection.util.IntentionName;
-import com.intellij.psi.PsiElement;
-import com.siyeh.ipp.base.PsiElementPredicate;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry;
-import org.jetbrains.kotlin.psi.KtStringTemplateExpression;
-
-/**
- * Kotlin specific translation key intention.
- * @author marhali
- */
-// TODO: kotlin impl does not work - no action
-public class KtExtractIntention extends AbstractExtractIntention {
- @Override
- protected @IntentionName String getTextForElement(PsiElement element) {
- return "hallo";
- //return getTextForElement(element.getProject(), element.getText());
- }
-
- @Override
- protected void processIntention(@NotNull PsiElement element) {
- System.out.println("Hallo");
-
- if(!(element instanceof KtStringTemplateExpression)) {
- System.out.println(element.getClass());
- return;
- }
-
- System.out.println("hallo");
-
- extractTranslation(element.getProject() , element.getText(), element);
- }
-
- @Override
- protected @NotNull PsiElementPredicate getElementPredicate() {
- System.out.println("predi");
- return element -> element instanceof KtLiteralStringTemplateEntry;
- }
-}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/KtTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/KtTranslationIntention.java
new file mode 100644
index 0000000..7ed3f31
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/intention/KtTranslationIntention.java
@@ -0,0 +1,23 @@
+package de.marhali.easyi18n.assistance.intention;
+
+import com.intellij.psi.PsiElement;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.kotlin.psi.KtLiteralStringTemplateEntry;
+
+/**
+ * Kotlin specific translation key intention.
+ * @author marhali
+ */
+public class KtTranslationIntention extends AbstractTranslationIntention {
+ @Override
+ protected @Nullable String extractText(@NotNull PsiElement element) {
+ if(!(element.getParent() instanceof KtLiteralStringTemplateEntry)) {
+ return null;
+ }
+
+ KtLiteralStringTemplateEntry expression = (KtLiteralStringTemplateEntry) element.getParent();
+ return expression.getText();
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/intention/PhpTranslationIntention.java b/src/main/java/de/marhali/easyi18n/assistance/intention/PhpTranslationIntention.java
new file mode 100644
index 0000000..76693a3
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/intention/PhpTranslationIntention.java
@@ -0,0 +1,23 @@
+package de.marhali.easyi18n.assistance.intention;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Php specific translation intention
+ * @author marhali
+ */
+public class PhpTranslationIntention extends AbstractTranslationIntention {
+ @Override
+ protected @Nullable String extractText(@NotNull PsiElement element) {
+ if(!(element.getParent() instanceof StringLiteralExpression)) {
+ return null;
+ }
+
+ return ((StringLiteralExpression) element.getParent()).getContents();
+ }
+}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java
index bff2307..7d80e66 100644
--- a/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java
+++ b/src/main/java/de/marhali/easyi18n/assistance/reference/JavaKeyReferenceContributor.java
@@ -4,6 +4,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
+
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
@@ -14,12 +15,10 @@ import org.jetbrains.annotations.NotNull;
*/
public class JavaKeyReferenceContributor extends AbstractKeyReferenceContributor {
- // TODO: why not PsiLiteralExpression?
-
@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(
- PlatformPatterns.psiElement(PsiLiteralValue.class),
+ PlatformPatterns.psiElement(PsiLiteralExpression.class),
getProvider());
}
@@ -30,8 +29,10 @@ public class JavaKeyReferenceContributor extends AbstractKeyReferenceContributor
@NotNull PsiElement element, @NotNull ProcessingContext context) {
Project project = element.getProject();
- PsiLiteralValue literalValue = (PsiLiteralValue) element;
- String value = literalValue.getValue() instanceof String ? (String) literalValue.getValue() : null;
+ PsiLiteralExpression literalExpression = (PsiLiteralExpression) element;
+ String value = literalExpression.getValue() instanceof String
+ ? (String) literalExpression.getValue()
+ : null;
return getReferences(project, element, value);
}
diff --git a/src/main/java/de/marhali/easyi18n/assistance/reference/PhpKeyReferenceContributor.java b/src/main/java/de/marhali/easyi18n/assistance/reference/PhpKeyReferenceContributor.java
new file mode 100644
index 0000000..0d11599
--- /dev/null
+++ b/src/main/java/de/marhali/easyi18n/assistance/reference/PhpKeyReferenceContributor.java
@@ -0,0 +1,37 @@
+package de.marhali.easyi18n.assistance.reference;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.patterns.PlatformPatterns;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiReferenceProvider;
+import com.intellij.psi.PsiReferenceRegistrar;
+import com.intellij.util.ProcessingContext;
+import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Php specific key reference binding
+ */
+public class PhpKeyReferenceContributor extends AbstractKeyReferenceContributor {
+ @Override
+ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
+ registrar.registerReferenceProvider(
+ PlatformPatterns.psiElement(StringLiteralExpression.class),
+ getProvider());
+ }
+
+ private PsiReferenceProvider getProvider() {
+ return new PsiReferenceProvider() {
+ @Override
+ public PsiReference @NotNull [] getReferencesByElement(
+ @NotNull PsiElement element, @NotNull ProcessingContext context) {
+
+ Project project = element.getProject();
+ StringLiteralExpression literalExpression = (StringLiteralExpression) element;
+ return getReferences(project, element, literalExpression.getContents());
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/META-INF/de.marhali.easyi18n-java.xml b/src/main/resources/META-INF/de.marhali.easyi18n-java.xml
index 43a9382..e172b2b 100644
--- a/src/main/resources/META-INF/de.marhali.easyi18n-java.xml
+++ b/src/main/resources/META-INF/de.marhali.easyi18n-java.xml
@@ -1,7 +1,7 @@
- de.marhali.easyi18n.assistance.intention.JavaExtractIntention
+ de.marhali.easyi18n.assistance.intention.JavaTranslationIntention
+
+
+
+
+
+
+
+
+
+
+
- de.marhali.easyi18n.assistance.intention.JsExtractIntention
+ de.marhali.easyi18n.assistance.intention.JsTranslationIntention
+
+
\ No newline at end of file
diff --git a/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml b/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml
index b1b898b..7e5c8c4 100644
--- a/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml
+++ b/src/main/resources/META-INF/de.marhali.easyi18n-kotlin.xml
@@ -1,7 +1,7 @@
- de.marhali.easyi18n.assistance.intention.KtExtractIntention
+ de.marhali.easyi18n.assistance.intention.KtTranslationIntention
+
+
+ de.marhali.easyi18n.assistance.intention.PhpTranslationIntention
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/META-INF/de.marhali.easyi18n-vue.xml b/src/main/resources/META-INF/de.marhali.easyi18n-vue.xml
index 68c3fa0..79ed051 100644
--- a/src/main/resources/META-INF/de.marhali.easyi18n-vue.xml
+++ b/src/main/resources/META-INF/de.marhali.easyi18n-vue.xml
@@ -1,6 +1,15 @@
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index b1274f6..4db7d2f 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -13,6 +13,7 @@
JavaScript
com.intellij.java
org.jetbrains.plugins.vue
+ com.jetbrains.php