package com.intellij.lang.javascript.linter.tslint.fix;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.intention.HighPriorityAction;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.lang.javascript.linter.tslint.TsLintBundle;
import com.intellij.lang.javascript.linter.tslint.execution.TsLinterError;
import com.intellij.lang.javascript.linter.tslint.highlight.TsLintFixInfo;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.util.text.StringUtilRt;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.Consumer;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Comparator;
public class TsLintErrorFixAction extends BaseIntentionAction implements HighPriorityAction {
@NotNull
private final TsLinterError myError;
private final long myModificationStamp;
public TsLintErrorFixAction(@NotNull TsLinterError error, @NotNull Document document) {
//noinspection DialogTitleCapitalization
setText(getFamilyName());
myError = error;
myModificationStamp = document.getModificationStamp();
}
@NotNull
@Override
public String getText() {
//noinspection DialogTitleCapitalization
return TsLintBundle.message("tslint.action.fix.problems.current.text");
}
@Nls
@NotNull
@Override
public String getFamilyName() {
return getText();
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
return editor != null && editor.getDocument().getModificationStamp() == myModificationStamp && myError.getFixInfo() != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
if (!isAvailable(project, editor, file)) {
return;
}
TsLintFixInfo info = myError.getFixInfo();
if (info == null) {
return;
}
WriteCommandAction.runWriteCommandAction(project, getText(), null, () -> {
Document document = editor.getDocument();
String separator = FileDocumentManager.getInstance().getLineSeparator(file.getViewProvider().getVirtualFile(), project);
TsLintFixInfo.TsLintFixReplacements[] replacements = info.innerReplacements;
if (replacements == null || replacements.length == 0) {
return;
}
Arrays.sort(replacements, Comparator.comparingInt(el -> -el.innerStart));
if (!applyReplacements(document, separator, replacements)) return;
PsiDocumentManager.getInstance(project).commitDocument(document);
});
DaemonCodeAnalyzer.getInstance(project).restart(file);
}
public boolean applyReplacements(@NotNull Document document,
@NotNull String separator,
@NotNull TsLintFixInfo.TsLintFixReplacements[] replacements) {
if ("\n".equals(separator)) {
if (!applyFor(document.getTextLength(), replacements,
(replacement) -> document
.replaceString(replacement.innerStart, replacement.innerStart + replacement.innerLength, StringUtil
.notNullize(replacement.innerText)))) {
return false;
}
}
else {
StringBuilder newContent = new StringBuilder(StringUtilRt.convertLineSeparators(document.getText(), separator));
if (!applyFor(newContent.length(), replacements,
(replacement) -> newContent
.replace(replacement.innerStart, replacement.innerStart + replacement.innerLength, StringUtil.notNullize(
replacement.innerText)))) {
return false;
}
document.setText(StringUtilRt.convertLineSeparators(newContent, "\n"));
}
return true;
}
public boolean applyFor(int documentLength,
@NotNull TsLintFixInfo.TsLintFixReplacements[] replacements,
@NotNull Consumer<TsLintFixInfo.TsLintFixReplacements> apply) {
for (TsLintFixInfo.TsLintFixReplacements replacement : replacements) {
int offset = replacement.innerStart;
if (offset > documentLength || (offset + replacement.innerLength) > documentLength) {
//incorrect value
return false;
}
apply.consume(replacement);
}
return true;
}
}