finalize editor assistance
This commit is contained in:
parent
6e9d0671ca
commit
fc107aa839
@ -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 <kbd>.idea</kbd> folder
|
||||
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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<Pair<String, PsiElement>> 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<FoldingDescriptor> descriptors = new ArrayList<>();
|
||||
|
||||
TranslationData data = InstanceManager.get(root.getProject()).store().getData();
|
||||
KeyPathConverter converter = new KeyPathConverter(root.getProject());
|
||||
|
||||
for(Pair<String, PsiElement> 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));
|
||||
|
@ -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<FoldingDescriptor> descriptors = new ArrayList<>();
|
||||
|
||||
if(!isAssistance(root.getProject())) {
|
||||
return FoldingDescriptor.EMPTY;
|
||||
}
|
||||
|
||||
Collection<PsiLiteralExpression> 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<Pair<String, PsiElement>> 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());
|
||||
}
|
||||
}
|
||||
|
@ -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<Pair<String, PsiElement>> 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();
|
||||
}
|
||||
}
|
@ -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<FoldingDescriptor> descriptors = new ArrayList<>();
|
||||
@NotNull List<Pair<String, PsiElement>> extractRegions(@NotNull PsiElement root) {
|
||||
List<Pair<String, PsiElement>> regions = new ArrayList<>();
|
||||
|
||||
if(!isAssistance(root.getProject())) {
|
||||
return FoldingDescriptor.EMPTY;
|
||||
for (KtStringTemplateExpression templateExpression : PsiTreeUtil.findChildrenOfType(root, KtStringTemplateExpression.class)) {
|
||||
for (KtStringTemplateEntry entry : templateExpression.getEntries()) {
|
||||
regions.add(Pair.pair(entry.getText(), templateExpression));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Collection<KtStringTemplateEntry> 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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<Pair<String, PsiElement>> 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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<intentionAction>
|
||||
<className>de.marhali.easyi18n.assistance.intention.JavaExtractIntention</className>
|
||||
<className>de.marhali.easyi18n.assistance.intention.JavaTranslationIntention</className>
|
||||
</intentionAction>
|
||||
|
||||
<psi.referenceContributor
|
||||
|
@ -10,8 +10,36 @@
|
||||
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||
/>
|
||||
|
||||
<!-- JavaScript plugin also includes TypeScript -->
|
||||
<lang.documentationProvider
|
||||
language="TypeScript"
|
||||
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||
/>
|
||||
|
||||
<!-- JavaScript plugin also includes TypeScript -->
|
||||
<lang.foldingBuilder
|
||||
language="TypeScript"
|
||||
implementationClass="de.marhali.easyi18n.assistance.folding.JsFoldingBuilder"
|
||||
/>
|
||||
|
||||
<!-- JavaScript plugin also includes TypeScript JSX -->
|
||||
<lang.foldingBuilder
|
||||
language="TypeScript JSX"
|
||||
implementationClass="de.marhali.easyi18n.assistance.folding.JsFoldingBuilder"
|
||||
/>
|
||||
|
||||
<lang.foldingBuilder
|
||||
language="JavaScript"
|
||||
implementationClass="de.marhali.easyi18n.assistance.folding.JsFoldingBuilder"
|
||||
/>
|
||||
|
||||
<intentionAction>
|
||||
<className>de.marhali.easyi18n.assistance.intention.JsExtractIntention</className>
|
||||
<className>de.marhali.easyi18n.assistance.intention.JsTranslationIntention</className>
|
||||
</intentionAction>
|
||||
|
||||
<completion.contributor
|
||||
language="JavaScript"
|
||||
implementationClass="de.marhali.easyi18n.assistance.completion.JsCompletionContributor"
|
||||
/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
@ -1,7 +1,7 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<intentionAction>
|
||||
<className>de.marhali.easyi18n.assistance.intention.KtExtractIntention</className>
|
||||
<className>de.marhali.easyi18n.assistance.intention.KtTranslationIntention</className>
|
||||
</intentionAction>
|
||||
|
||||
<psi.referenceContributor
|
||||
|
27
src/main/resources/META-INF/de.marhali.easyi18n-php.xml
Normal file
27
src/main/resources/META-INF/de.marhali.easyi18n-php.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<intentionAction>
|
||||
<className>de.marhali.easyi18n.assistance.intention.PhpTranslationIntention</className>
|
||||
</intentionAction>
|
||||
|
||||
<psi.referenceContributor
|
||||
language="PHP"
|
||||
implementation="de.marhali.easyi18n.assistance.reference.PhpKeyReferenceContributor"
|
||||
/>
|
||||
|
||||
<lang.foldingBuilder
|
||||
language="PHP"
|
||||
implementationClass="de.marhali.easyi18n.assistance.folding.PhpFoldingBuilder"
|
||||
/>
|
||||
|
||||
<lang.documentationProvider
|
||||
language="PHP"
|
||||
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||
/>
|
||||
|
||||
<completion.contributor
|
||||
language="PHP"
|
||||
implementationClass="de.marhali.easyi18n.assistance.completion.PhpCompletionContributor"
|
||||
/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
@ -1,6 +1,15 @@
|
||||
<idea-plugin>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<lang.foldingBuilder language="HTML"
|
||||
implementationClass="de.marhali.easyi18n.editor.generic.GenericFoldingBuilder" />
|
||||
<lang.foldingBuilder language="VueJS" implementationClass="de.marhali.easyi18n.assistance.folding.JsFoldingBuilder" />
|
||||
|
||||
<lang.documentationProvider
|
||||
language="VueJS"
|
||||
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||
/>
|
||||
|
||||
<lang.documentationProvider
|
||||
language="Vue"
|
||||
implementationClass="de.marhali.easyi18n.assistance.documentation.CommonDocumentationProvider"
|
||||
/>
|
||||
</extensions>
|
||||
</idea-plugin>
|
@ -13,6 +13,7 @@
|
||||
<depends optional="true" config-file="de.marhali.easyi18n-javascript.xml">JavaScript</depends>
|
||||
<depends optional="true" config-file="de.marhali.easyi18n-java.xml">com.intellij.java</depends>
|
||||
<depends optional="true" config-file="de.marhali.easyi18n-vue.xml">org.jetbrains.plugins.vue</depends>
|
||||
<depends optional="true" config-file="de.marhali.easyi18n-php.xml">com.jetbrains.php</depends>
|
||||
|
||||
<actions>
|
||||
<action
|
||||
|
Loading…
x
Reference in New Issue
Block a user