easy-18in/src/main/java/de/marhali/easyi18n/assistance/intention/AbstractTranslationIntention.java
2022-04-21 20:06:28 +02:00

139 lines
5.2 KiB
Java

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();
}
}