1.12 ★ External Comment for COBOL etc | 外部注释用于 COBOL 等
This commit is contained in:
@@ -13,7 +13,9 @@ import io.github.linwancen.plugin.show.json.JsonRef;
|
||||
import io.github.linwancen.plugin.show.json.JsonUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class JsonJump extends PsiReferenceContributor {
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import io.github.linwancen.plugin.show.doc.DocTextUtils;
|
||||
import io.github.linwancen.plugin.show.line.LineDocLeftToRightUtils;
|
||||
import io.github.linwancen.plugin.show.line.LineDocRightToLeftUtils;
|
||||
import io.github.linwancen.plugin.show.ext.LineExtUtils;
|
||||
import io.github.linwancen.plugin.show.settings.AppSettingsState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -33,20 +34,23 @@ public class LineEnd extends EditorLinePainter {
|
||||
if (DumbService.isDumb(project)) {
|
||||
return null;
|
||||
}
|
||||
FileViewProvider viewProvider = PsiManager.getInstance(project).findViewProvider(file);
|
||||
PsiDocComment docComment = doc(project, viewProvider, lineNumber);
|
||||
String comment = DocTextUtils.text(docComment);
|
||||
if (comment == null) {
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
String doc = doc(project, file, lineNumber);
|
||||
if (doc == null) {
|
||||
return null;
|
||||
}
|
||||
TextAttributes textAttr = file.getFileType().equals(JsonFileType.INSTANCE)
|
||||
? settings.lineEndJsonTextAttr
|
||||
: settings.lineEndTextAttr;
|
||||
LineExtensionInfo info = new LineExtensionInfo(settings.lineEndPrefix + comment, textAttr);
|
||||
LineExtensionInfo info = new LineExtensionInfo(settings.lineEndPrefix + doc, textAttr);
|
||||
return Collections.singletonList(info);
|
||||
}
|
||||
|
||||
private static @Nullable PsiDocComment doc(Project project, FileViewProvider viewProvider, int lineNumber) {
|
||||
private static @Nullable String doc(@NotNull Project project,
|
||||
@NotNull VirtualFile file, int lineNumber) {
|
||||
FileViewProvider viewProvider = PsiManager.getInstance(project).findViewProvider(file);
|
||||
if (viewProvider == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -63,9 +67,13 @@ public class LineEnd extends EditorLinePainter {
|
||||
if (startOffset == endOffset) {
|
||||
return null;
|
||||
}
|
||||
if (AppSettingsState.getInstance().findElementRightToLeft) {
|
||||
return LineDocRightToLeftUtils.rightDoc(viewProvider, startOffset, endOffset);
|
||||
String ext = LineExtUtils.extDoc(project, file, document, startOffset, endOffset);
|
||||
if (ext != null) {
|
||||
return ext;
|
||||
}
|
||||
return LineDocLeftToRightUtils.leftDoc(viewProvider, document, startOffset, endOffset);
|
||||
PsiDocComment docComment = AppSettingsState.getInstance().findElementRightToLeft
|
||||
? LineDocRightToLeftUtils.rightDoc(viewProvider, startOffset, endOffset)
|
||||
: LineDocLeftToRightUtils.leftDoc(viewProvider, document, startOffset, endOffset);
|
||||
return DocTextUtils.text(docComment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.ide.projectView.ProjectViewNode;
|
||||
import com.intellij.ide.projectView.ProjectViewNodeDecorator;
|
||||
import com.intellij.ide.projectView.impl.nodes.*;
|
||||
import com.intellij.ide.util.treeView.PresentableNodeDescriptor.ColoredFragment;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -15,6 +16,7 @@ import com.intellij.ui.ColoredTreeCellRenderer;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import io.github.linwancen.plugin.show.doc.DocTextUtils;
|
||||
import io.github.linwancen.plugin.show.doc.DocUtils;
|
||||
import io.github.linwancen.plugin.show.ext.TreeExtUtils;
|
||||
import io.github.linwancen.plugin.show.settings.AppSettingsState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -34,25 +36,43 @@ public class Tree implements ProjectViewNodeDecorator {
|
||||
if (DumbService.isDumb(project)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PsiDocComment docComment = nodeDoc(node, project);
|
||||
if (docComment == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String commentText = DocTextUtils.text(docComment);
|
||||
if (commentText == null) {
|
||||
return;
|
||||
}
|
||||
List<ColoredFragment> coloredText = data.getColoredText();
|
||||
if (coloredText.isEmpty()) {
|
||||
data.addText(data.getPresentableText(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
|
||||
}
|
||||
data.addText(commentText, SimpleTextAttributes.GRAY_ATTRIBUTES);
|
||||
ApplicationManager.getApplication().runReadAction(() -> {
|
||||
String doc = doc(node, project);
|
||||
if (doc == null) {
|
||||
return;
|
||||
}
|
||||
List<ColoredFragment> coloredText = data.getColoredText();
|
||||
if (coloredText.isEmpty()) {
|
||||
data.addText(data.getPresentableText(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
|
||||
}
|
||||
data.addText(doc, SimpleTextAttributes.GRAY_ATTRIBUTES);
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiDocComment nodeDoc(ProjectViewNode<?> node, Project project) {
|
||||
private String doc(ProjectViewNode<?> node, Project project) {
|
||||
String extDoc = extDoc(node, project);
|
||||
if (extDoc != null) {
|
||||
return extDoc;
|
||||
}
|
||||
PsiDocComment docComment = nodeDoc(node, project);
|
||||
if (docComment == null) {
|
||||
return null;
|
||||
}
|
||||
return DocTextUtils.text(docComment);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String extDoc(ProjectViewNode<?> node, Project project) {
|
||||
VirtualFile file = node.getVirtualFile();
|
||||
if (file == null) {
|
||||
return null;
|
||||
}
|
||||
return TreeExtUtils.extDoc(project, file);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiDocComment nodeDoc(ProjectViewNode<?> node, Project project) {
|
||||
if (node instanceof PsiFileNode) {
|
||||
PsiFile psiFile = ((PsiFileNode) node).getValue();
|
||||
return DocUtils.fileDoc(psiFile);
|
||||
@@ -97,7 +117,7 @@ public class Tree implements ProjectViewNodeDecorator {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiDocComment dirDoc(PsiDirectory child) {
|
||||
private static PsiDocComment dirDoc(PsiDirectory child) {
|
||||
while (true) {
|
||||
PsiDocComment docComment = DocUtils.dirDoc(child);
|
||||
if (docComment != null) {
|
||||
@@ -115,7 +135,7 @@ public class Tree implements ProjectViewNodeDecorator {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiDocComment packageDoc(PsiPackage child) {
|
||||
private static PsiDocComment packageDoc(PsiPackage child) {
|
||||
while (true) {
|
||||
PsiDocComment docComment = DocUtils.packageDoc(child);
|
||||
if (docComment != null) {
|
||||
|
||||
@@ -7,9 +7,6 @@ import com.intellij.psi.javadoc.PsiInlineDocTag;
|
||||
import io.github.linwancen.plugin.show.settings.AppSettingsState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DocTextUtils {
|
||||
|
||||
private DocTextUtils() {}
|
||||
@@ -20,30 +17,40 @@ public class DocTextUtils {
|
||||
return null;
|
||||
}
|
||||
AppSettingsState appSettings = AppSettingsState.getInstance();
|
||||
List<String> comments = new ArrayList<>();
|
||||
int lineCount = 0;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
PsiElement[] elements = psiDocComment.getDescriptionElements();
|
||||
for (PsiElement element : elements) {
|
||||
if (element instanceof PsiDocToken) {
|
||||
PsiDocToken psiDocToken = (PsiDocToken) element;
|
||||
comments.add(psiDocToken.getText());
|
||||
} else if (element instanceof PsiInlineDocTag) {
|
||||
PsiInlineDocTag psiInlineDocTag = (PsiInlineDocTag) element;
|
||||
PsiElement[] children = psiInlineDocTag.getChildren();
|
||||
if (children.length > 2) {
|
||||
comments.add(children[2].getText());
|
||||
}
|
||||
if (appendElementText(sb, element)) {
|
||||
lineCount++;
|
||||
}
|
||||
if (appSettings.lineEndCount > 0 && comments.size() >= appSettings.lineEndCount) {
|
||||
if (appSettings.lineEndCount > 0
|
||||
&& lineCount >= appSettings.lineEndCount
|
||||
|| appSettings.lineEndLen > 0
|
||||
&& sb.length() >= appSettings.lineEndLen) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (comments.isEmpty()) {
|
||||
if (sb.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(" ");
|
||||
for (String s : comments) {
|
||||
sb.append(s.trim().replace("<br>", "")).append(" ");
|
||||
}
|
||||
sb.insert(0, " ");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean appendElementText(StringBuilder sb, PsiElement element) {
|
||||
if (element instanceof PsiDocToken) {
|
||||
PsiDocToken psiDocToken = (PsiDocToken) element;
|
||||
sb.append(psiDocToken.getText().trim().replace("<br>", "")).append(" ");
|
||||
return true;
|
||||
}
|
||||
if (element instanceof PsiInlineDocTag) {
|
||||
PsiInlineDocTag psiInlineDocTag = (PsiInlineDocTag) element;
|
||||
PsiElement[] children = psiInlineDocTag.getChildren();
|
||||
if (children.length > 2) {
|
||||
sb.append(children[2].getText().trim().replace("<br>", "")).append(" ");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.github.linwancen.plugin.show.ext;
|
||||
|
||||
import io.github.linwancen.plugin.show.settings.ProjectSettingsState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
class DocMapUtils {
|
||||
|
||||
private DocMapUtils() {}
|
||||
|
||||
@Nullable
|
||||
static String get(@NotNull Map<String, Map<String, List<String>>> docMap,
|
||||
@NotNull ProjectSettingsState set, @NotNull String... words) {
|
||||
List<String> keywordDoc = list(docMap, words);
|
||||
if (keywordDoc.size() >= set.extDocColumn) {
|
||||
return keywordDoc.get(set.extDocColumn - 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<String> list(@NotNull Map<String, Map<String, List<String>>> docMap, @NotNull String... words) {
|
||||
for (Map.Entry<String, Map<String, List<String>>> entry : docMap.entrySet()) {
|
||||
Map<String, List<String>> map = entry.getValue();
|
||||
for (String word : words) {
|
||||
List<String> wordDoc = map.get(word);
|
||||
if (wordDoc != null) {
|
||||
return wordDoc;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package io.github.linwancen.plugin.show.ext;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import io.github.linwancen.plugin.show.ext.conf.ConfCache;
|
||||
import io.github.linwancen.plugin.show.settings.ProjectSettingsState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class LineExtUtils {
|
||||
|
||||
private LineExtUtils() {}
|
||||
|
||||
public static String extDoc(@NotNull Project project, @NotNull VirtualFile file,
|
||||
@NotNull Document document, int startOffset, int endOffset) {
|
||||
Map<String, Map<String, List<String>>> keyMap = ConfCache.keyMap(project, file);
|
||||
if (keyMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Pattern pattern = ConfCache.pattern(project, file, keyMap);
|
||||
if (pattern == null || pattern.pattern().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Map<String, List<String>>> docMap = ConfCache.docMap(project, file);
|
||||
if (docMap.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Map<String, Map<String, List<String>>> treeMap = ConfCache.treeMap(project, file);
|
||||
String text = document.getText(new TextRange(startOffset, endOffset));
|
||||
String[] words = pattern.split(text);
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
return extDoc(keyMap, matcher, docMap, words, treeMap, project);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String extDoc(@NotNull Map<String, Map<String, List<String>>> keyMap, @NotNull Matcher matcher,
|
||||
@NotNull Map<String, Map<String, List<String>>> docMap, @NotNull String[] words,
|
||||
@NotNull Map<String, Map<String, List<String>>> treeMap, @NotNull Project project) {
|
||||
ProjectSettingsState set = ProjectSettingsState.getInstance(project);
|
||||
Pattern extReplaceToSpace = set.extReplaceToSpace;
|
||||
boolean isReplaceToSpace = extReplaceToSpace.pattern().length() != 0;
|
||||
boolean haveDoc = false;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : words) {
|
||||
if (appendDoc(set, sb, docMap, treeMap, extReplaceToSpace, isReplaceToSpace, s)) {
|
||||
haveDoc = true;
|
||||
}
|
||||
appendKeyDoc(set, sb, keyMap, matcher);
|
||||
}
|
||||
if (!haveDoc) {
|
||||
return null;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static boolean appendDoc(ProjectSettingsState set, StringBuilder sb,
|
||||
@NotNull Map<String, Map<String, List<String>>> docMap,
|
||||
@NotNull Map<String, Map<String, List<String>>> treeMap,
|
||||
Pattern extReplaceToSpace, boolean isReplaceToSpace, String word) {
|
||||
word = word.trim();
|
||||
if (isReplaceToSpace) {
|
||||
word = extReplaceToSpace.matcher(word).replaceAll(" ");
|
||||
}
|
||||
if (word.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
String wordDoc = DocMapUtils.get(docMap, set, word);
|
||||
if (wordDoc != null) {
|
||||
sb.append(wordDoc);
|
||||
return true;
|
||||
}
|
||||
String treeDoc = DocMapUtils.get(treeMap, set, word);
|
||||
if (treeDoc != null) {
|
||||
sb.append(treeDoc);
|
||||
return true;
|
||||
}
|
||||
sb.append(word);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void appendKeyDoc(@NotNull ProjectSettingsState set, @NotNull StringBuilder sb,
|
||||
@NotNull Map<String, Map<String, List<String>>> keyMap,
|
||||
@NotNull Matcher matcher) {
|
||||
if (!matcher.find()) {
|
||||
return;
|
||||
}
|
||||
String keyword = matcher.group();
|
||||
// "" if no doc
|
||||
String keyDoc = DocMapUtils.get(keyMap, set, keyword);
|
||||
if (keyDoc != null) {
|
||||
sb.append(" ").append(keyDoc);
|
||||
}
|
||||
// no one space
|
||||
if (sb.length() != 1) {
|
||||
sb.append(" ");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.github.linwancen.plugin.show.ext;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import io.github.linwancen.plugin.show.ext.conf.ConfCache;
|
||||
import io.github.linwancen.plugin.show.settings.ProjectSettingsState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TreeExtUtils {
|
||||
|
||||
private TreeExtUtils() {}
|
||||
|
||||
public static String extDoc(@NotNull Project project, @NotNull VirtualFile file) {
|
||||
Map<String, Map<String, List<String>>> docMap = ConfCache.treeMap(project, file);
|
||||
ProjectSettingsState set = ProjectSettingsState.getInstance(project);
|
||||
String[] words = {
|
||||
file.getName(),
|
||||
file.getNameWithoutExtension(),
|
||||
};
|
||||
String extDoc = DocMapUtils.get(docMap, set, words);
|
||||
if (extDoc == null) {
|
||||
return null;
|
||||
}
|
||||
return " " + extDoc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package io.github.linwancen.plugin.show.ext.conf;
|
||||
|
||||
import com.intellij.notification.NotificationDisplayType;
|
||||
import com.intellij.notification.NotificationGroup;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.DumbService;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* call ConfFactory, ConfCacheGetUtils
|
||||
*/
|
||||
public class ConfCache {
|
||||
|
||||
private static final Pattern LINE_PATTERN = Pattern.compile("[\\r\\n]++");
|
||||
|
||||
static final String EXT = "tsv";
|
||||
static final String KEY_MID_EXT = ".key";
|
||||
static final String DOC_MID_EXT = ".doc";
|
||||
static final String TREE_MID_EXT = ".tree";
|
||||
|
||||
private static final Map<VirtualFile, Map<String, List<String>>> KEY_CACHE = new ConcurrentHashMap<>();
|
||||
private static final Map<VirtualFile, Map<String, List<String>>> DOC_CACHE = new ConcurrentHashMap<>();
|
||||
private static final Map<VirtualFile, Map<String, List<String>>> TREE_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
private ConfCache() {}
|
||||
|
||||
@Nullable
|
||||
public static Pattern pattern(@Nullable Project project, @NotNull VirtualFile file,
|
||||
@NotNull Map<String, Map<String, List<String>>> keyMap) {
|
||||
return ConfFactory.buildPattern(project, file.getPath(), keyMap);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<String, Map<String, List<String>>> keyMap(@Nullable Project project, @NotNull VirtualFile file) {
|
||||
return ConfCacheGetUtils.get(file, KEY_MID_EXT, KEY_CACHE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<String, Map<String, List<String>>> docMap(@Nullable Project project, @NotNull VirtualFile file) {
|
||||
return ConfCacheGetUtils.get(file, DOC_MID_EXT, DOC_CACHE);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<String, Map<String, List<String>>> treeMap(@Nullable Project project, @NotNull VirtualFile file) {
|
||||
return ConfCacheGetUtils.get(file, TREE_MID_EXT, TREE_CACHE);
|
||||
}
|
||||
|
||||
static void remove(@NotNull VirtualFile file, String name) {
|
||||
if (name != null) {
|
||||
int i = name.lastIndexOf('.');
|
||||
name = name.substring(0, i);
|
||||
} else {
|
||||
name = file.getNameWithoutExtension();
|
||||
}
|
||||
if (name.endsWith(KEY_MID_EXT)) {
|
||||
KEY_CACHE.remove(file);
|
||||
} else if (name.endsWith(DOC_MID_EXT)) {
|
||||
DOC_CACHE.remove(file);
|
||||
} else if (name.endsWith(TREE_MID_EXT)) {
|
||||
TREE_CACHE.remove(file);
|
||||
}
|
||||
}
|
||||
|
||||
static void copy(@NotNull VirtualFile file, @NotNull VirtualFile newFile) {
|
||||
String name = file.getNameWithoutExtension();
|
||||
if (name.endsWith(KEY_MID_EXT)) {
|
||||
copyCache(file, newFile, KEY_CACHE);
|
||||
} else if (name.endsWith(DOC_MID_EXT)) {
|
||||
copyCache(file, newFile, DOC_CACHE);
|
||||
} else if (name.endsWith(TREE_MID_EXT)) {
|
||||
copyCache(file, newFile, TREE_CACHE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyCache(@Nullable VirtualFile file, @NotNull VirtualFile newFile,
|
||||
@NotNull Map<VirtualFile, Map<String, List<String>>> cache) {
|
||||
Map<String, List<String>> map = cache.get(file);
|
||||
if (map != null) {
|
||||
cache.put(newFile, new LinkedHashMap<>(map));
|
||||
}
|
||||
}
|
||||
|
||||
private static final NotificationGroup LOAD_ALL_LOG =
|
||||
new NotificationGroup("Ext Doc Conf Load All", NotificationDisplayType.BALLOON, true);
|
||||
|
||||
static void loadAll(@NotNull Project project) {
|
||||
ApplicationManager.getApplication().runReadAction(() ->
|
||||
DumbService.getInstance(project).runReadActionInSmartMode(() -> {
|
||||
Collection<VirtualFile> files = FilenameIndex.getAllFilesByExt(project, EXT);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
KEY_CACHE.clear();
|
||||
DOC_CACHE.clear();
|
||||
TREE_CACHE.clear();
|
||||
for (VirtualFile file : files) {
|
||||
load(project, file);
|
||||
sb.append(file.getPath()).append("\n");
|
||||
}
|
||||
if (files.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
LOAD_ALL_LOG.createNotification("Ext doc conf load all complete", files.size() + " files",
|
||||
sb.toString(), NotificationType.INFORMATION).notify(project);
|
||||
}));
|
||||
}
|
||||
|
||||
static void loadFile(@Nullable Project project, @NotNull VirtualFile file) {
|
||||
ApplicationManager.getApplication().runReadAction(() -> ConfCache.load(project, file));
|
||||
}
|
||||
|
||||
private static void load(@Nullable Project project, @NotNull VirtualFile file) {
|
||||
if (!ConfCache.EXT.equals(file.getExtension())) {
|
||||
return;
|
||||
}
|
||||
Document document = FileDocumentManager.getInstance().getDocument(file);
|
||||
if (document == null) {
|
||||
return;
|
||||
}
|
||||
String text = document.getText();
|
||||
String name = file.getNameWithoutExtension();
|
||||
// this pattern would skip empty line
|
||||
String[] lines = LINE_PATTERN.split(text);
|
||||
if (name.endsWith(KEY_MID_EXT)) {
|
||||
KEY_CACHE.put(file, ConfFactory.buildMap(project, file.getPath(), lines, true));
|
||||
} else if (name.endsWith(DOC_MID_EXT)) {
|
||||
DOC_CACHE.put(file, ConfFactory.buildMap(project, file.getPath(), lines, false));
|
||||
} else if (name.endsWith(TREE_MID_EXT)) {
|
||||
TREE_CACHE.put(file, ConfFactory.buildMap(project, file.getPath(), lines, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package io.github.linwancen.plugin.show.ext.conf;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
class ConfCacheGetUtils {
|
||||
|
||||
private static final String SKIP_PATH = "doc";
|
||||
private static final String MATCH_STR = "%";
|
||||
|
||||
private ConfCacheGetUtils() {}
|
||||
|
||||
/**
|
||||
* <br>file:
|
||||
* <br>a/b/c.ext
|
||||
* <br>
|
||||
* <br>configure file priority:
|
||||
* <br>a/b/c.ext.key.tsv
|
||||
* <br>a/b/x.c.ext.key.tsv
|
||||
* <br>a/b/ext.key.tsv
|
||||
* <br>a/b/x.ext.key.tsv
|
||||
* <br>a/c.ext.key.tsv
|
||||
*
|
||||
* @return {@code <sortKey, T>}
|
||||
*/
|
||||
@NotNull
|
||||
static <T> TreeMap<String, T> get(@NotNull VirtualFile file,
|
||||
@NotNull String confMidExt,
|
||||
@NotNull Map<VirtualFile, T> cache) {
|
||||
String path = file.getPath();
|
||||
String name = file.getName();
|
||||
String ext = file.getExtension();
|
||||
if (ext == null) {
|
||||
ext = "";
|
||||
}
|
||||
TreeMap<String, T> map = new TreeMap<>();
|
||||
int max = path.length();
|
||||
int length = String.valueOf(max).length();
|
||||
for (Map.Entry<VirtualFile, T> entry : cache.entrySet()) {
|
||||
VirtualFile confFile = entry.getKey();
|
||||
String confName = confFile.getNameWithoutExtension();
|
||||
String confPath = confFile.getPath();
|
||||
int level = level(path, confPath);
|
||||
if (level == 0) {
|
||||
continue;
|
||||
}
|
||||
String levelStr = StringUtils.leftPad(String.valueOf(max - level), length, '0');
|
||||
if ((name + confMidExt).equals(confName)) {
|
||||
map.put(levelStr + "\b" + confPath, entry.getValue());
|
||||
} else if (confName.endsWith((name + confMidExt))) {
|
||||
map.put(levelStr + "\t" + confPath, entry.getValue());
|
||||
} else if ((ext + confMidExt).equals(confName)) {
|
||||
map.put(levelStr + "\n" + confPath, entry.getValue());
|
||||
} else if (confName.endsWith((ext + confMidExt))) {
|
||||
map.put(levelStr + "\f" + confPath, entry.getValue());
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static int level(String path, String confPath) {
|
||||
path = srcPath(path);
|
||||
confPath = srcPath(confPath);
|
||||
String[] paths = StringUtils.split(path, '/');
|
||||
String[] confPaths = StringUtils.split(confPath, '/');
|
||||
int length = confPaths.length;
|
||||
if (length > paths.length) {
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!match(paths[i], confPaths[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static String srcPath(String path) {
|
||||
int i = path.indexOf('!');
|
||||
if (i != -1) {
|
||||
return path.substring(i + 1);
|
||||
}
|
||||
i = path.indexOf("/resources");
|
||||
if (i != -1) {
|
||||
return path.substring(i + "/resources".length());
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private static boolean match(String path, String confPath) {
|
||||
if (confPath.equals(path) || SKIP_PATH.equals(confPath)) {
|
||||
return true;
|
||||
}
|
||||
boolean end = false;
|
||||
boolean start = false;
|
||||
if (confPath.startsWith(MATCH_STR)) {
|
||||
confPath = confPath.substring(1);
|
||||
end = true;
|
||||
}
|
||||
if (confPath.endsWith(MATCH_STR)) {
|
||||
confPath = confPath.substring(0, confPath.length() - 1);
|
||||
start = true;
|
||||
}
|
||||
return end && path.endsWith(confPath) || start && path.startsWith(confPath);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package io.github.linwancen.plugin.show.ext.conf;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.intellij.notification.NotificationDisplayType;
|
||||
import com.intellij.notification.NotificationGroup;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.twelvemonkeys.util.LinkedSet;
|
||||
import groovy.json.StringEscapeUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
class ConfFactory {
|
||||
|
||||
private static final Pattern EMPTY_PATTERN = Pattern.compile("");
|
||||
private static final Map<String, Pattern> PATTERN_CACHE = new ConcurrentHashMap<>();
|
||||
private static final NotificationGroup REGEXP_LOG =
|
||||
new NotificationGroup("Ext Doc Keyword Regexp Compile", NotificationDisplayType.TOOL_WINDOW, true);
|
||||
private static final NotificationGroup DATA_LOG =
|
||||
new NotificationGroup("Ext Doc Data", NotificationDisplayType.BALLOON, true);
|
||||
|
||||
private ConfFactory() {}
|
||||
|
||||
@Nullable
|
||||
static Pattern buildPattern(@Nullable Project project, @NotNull String path,
|
||||
@NotNull Map<String, Map<String, List<String>>> map) {
|
||||
Set<String> exclude = new LinkedSet<>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map<String, List<String>> keyMap : map.values()) {
|
||||
// key() is escape
|
||||
for (List<String> list : keyMap.values()) {
|
||||
String key = list.get(0);
|
||||
if (key.startsWith("?")) {
|
||||
exclude.add(key.substring(1));
|
||||
} else if (key.length() > 0 && !exclude.contains(key)) {
|
||||
sb.append(key).append("|");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sb.length() > 0) {
|
||||
sb.delete(sb.length() - 1, sb.length());
|
||||
}
|
||||
String regex = sb.toString();
|
||||
Pattern pattern = PATTERN_CACHE.get(regex);
|
||||
if (pattern != null) {
|
||||
return pattern;
|
||||
}
|
||||
sb.insert(0, "\n");
|
||||
sb.insert(0, path);
|
||||
sb.insert(0, "\n");
|
||||
try {
|
||||
Pattern compile = Pattern.compile(regex);
|
||||
PATTERN_CACHE.put(regex, compile);
|
||||
REGEXP_LOG.createNotification("Ext doc keyword regexp compile success", regex.length() + " chars",
|
||||
sb.toString(), NotificationType.INFORMATION).notify(project);
|
||||
return compile;
|
||||
} catch (Exception e) {
|
||||
sb.insert(0, "\n");
|
||||
sb.insert(0, e.getLocalizedMessage());
|
||||
PATTERN_CACHE.put(regex, EMPTY_PATTERN);
|
||||
REGEXP_LOG.createNotification("Ext doc keyword regexp compile fail", regex.length() + " chars",
|
||||
sb.toString(), NotificationType.ERROR).notify(project);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static final Pattern DEL_PATTERN = Pattern.compile("\\(\\?[^)]++\\)");
|
||||
|
||||
@NotNull
|
||||
static Map<String, List<String>> buildMap(@Nullable Project project, @NotNull String path,
|
||||
@NotNull String[] lines, boolean isKey) {
|
||||
Map<String, List<String>> map = new LinkedHashMap<>();
|
||||
for (String line : lines) {
|
||||
List<String> words = Splitter.on('\t').splitToList(line);
|
||||
if (!words.isEmpty()) {
|
||||
String key = words.get(0);
|
||||
if (key.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (isKey) {
|
||||
key = StringEscapeUtils.unescapeJava(key);
|
||||
String del = DEL_PATTERN.matcher(key).replaceAll("");
|
||||
if (del.length() != 0) {
|
||||
key = del;
|
||||
}
|
||||
}
|
||||
map.put(key, words);
|
||||
}
|
||||
}
|
||||
DATA_LOG.createNotification("Ext doc file load complete", map.size() + " lines",
|
||||
"\n" + path, NotificationType.INFORMATION).notify(project);
|
||||
return map;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.github.linwancen.plugin.show.ext.conf;
|
||||
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerEvent;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManagerListener;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* call ConfCache.loadFile
|
||||
*/
|
||||
public class ConfFileChangeListener implements FileEditorManagerListener {
|
||||
|
||||
@Override
|
||||
public void selectionChanged(@NotNull FileEditorManagerEvent event) {
|
||||
VirtualFile file = event.getOldFile();
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
if (file.exists()) {
|
||||
ConfCache.loadFile(event.getManager().getProject(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.github.linwancen.plugin.show.ext.conf;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManagerListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* call ConfCache.loadAll
|
||||
*/
|
||||
public class ConfFileInitListener implements ProjectManagerListener {
|
||||
|
||||
@Override
|
||||
public void projectOpened(@NotNull Project project) {
|
||||
ConfCache.loadAll(project);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package io.github.linwancen.plugin.show.ext.conf;
|
||||
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
|
||||
import com.intellij.openapi.vfs.newvfs.events.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* call ConfCache.loadFile, copy, remove
|
||||
*/
|
||||
public class ConfFileListener implements BulkFileListener {
|
||||
|
||||
@Override
|
||||
public void after(@NotNull List<? extends VFileEvent> events) {
|
||||
for (VFileEvent event : events) {
|
||||
forEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private static void forEvent(VFileEvent event) {
|
||||
VirtualFile file = event.getFile();
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
if (event instanceof VFilePropertyChangeEvent) {
|
||||
VFilePropertyChangeEvent changeEvent = (VFilePropertyChangeEvent) event;
|
||||
if ("name".equals(changeEvent.getPropertyName())) {
|
||||
String oldName = changeEvent.getOldValue().toString();
|
||||
if (oldName.endsWith(ConfCache.EXT)) {
|
||||
// change cache too complicated so remove
|
||||
ConfCache.remove(file, oldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ConfCache.EXT.equals(file.getExtension())) {
|
||||
return;
|
||||
}
|
||||
if (event instanceof VFileMoveEvent) {
|
||||
return;
|
||||
}
|
||||
if (event instanceof VFileDeleteEvent) {
|
||||
ConfCache.remove(file, null);
|
||||
return;
|
||||
}
|
||||
if (event instanceof VFileCopyEvent) {
|
||||
VFileCopyEvent copyEvent = (VFileCopyEvent) event;
|
||||
VirtualFile newFile = copyEvent.findCreatedFile();
|
||||
if (newFile == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
ConfCache.copy(file, newFile);
|
||||
} catch (Exception ignored) {
|
||||
// ignore
|
||||
}
|
||||
return;
|
||||
}
|
||||
// VFileCreateEvent
|
||||
// VFileContentChangeEvent
|
||||
ConfCache.loadFile(null, file);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.github.linwancen.plugin.show.ext.conf;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* call ConfCache.loadAll
|
||||
*/
|
||||
public class ReloadExtDocAction extends AnAction {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
Project project = e.getProject();
|
||||
if (project == null) {
|
||||
return;
|
||||
}
|
||||
ConfCache.loadAll(project);
|
||||
}
|
||||
}
|
||||
@@ -19,24 +19,18 @@ public class LineDocRightToLeftUtils {
|
||||
// End is always white, can not -1 because @see class.name need it
|
||||
PsiElement element = viewProvider.findElementAt(endOffset);
|
||||
if (element == null) {
|
||||
// one line end
|
||||
return null;
|
||||
}
|
||||
AppSettingsState instance = AppSettingsState.getInstance();
|
||||
PsiElement identifier;
|
||||
PsiDocComment psiDocComment;
|
||||
AppSettingsState setting = AppSettingsState.getInstance();
|
||||
while (true) {
|
||||
identifier = PsiTreeUtil.prevVisibleLeaf(element);
|
||||
PsiElement identifier = PsiTreeUtil.prevVisibleLeaf(element);
|
||||
if (identifier != null && identifier.getTextRange().getStartOffset() < startOffset) {
|
||||
identifier = null;
|
||||
}
|
||||
if (identifier == null || identifier instanceof PsiIdentifier) {
|
||||
if (instance.inJson && element.getLanguage().is(JsonLanguage.INSTANCE)) {
|
||||
return JsonDocUtils.jsonDoc(element, startOffset, endOffset);
|
||||
}
|
||||
psiDocComment = LineDocUtils.elementDoc(element, identifier, startOffset, endOffset);
|
||||
if (psiDocComment != null) {
|
||||
return psiDocComment;
|
||||
}
|
||||
PsiDocComment docComment = psiDoc(setting, identifier, element, startOffset, endOffset);
|
||||
if (docComment != null) {
|
||||
return docComment;
|
||||
}
|
||||
if (identifier == null) {
|
||||
return null;
|
||||
@@ -44,4 +38,16 @@ public class LineDocRightToLeftUtils {
|
||||
element = identifier;
|
||||
}
|
||||
}
|
||||
|
||||
public static PsiDocComment psiDoc(AppSettingsState setting,
|
||||
PsiElement identifier, PsiElement element,
|
||||
int startOffset, int endOffset) {
|
||||
if (identifier != null && !(identifier instanceof PsiIdentifier)) {
|
||||
return null;
|
||||
}
|
||||
if (setting.inJson && element.getLanguage().is(JsonLanguage.INSTANCE)) {
|
||||
return JsonDocUtils.jsonDoc(element, startOffset, endOffset);
|
||||
}
|
||||
return LineDocUtils.elementDoc(element, identifier, startOffset, endOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ public class AppSettingsState implements PersistentStateComponent<AppSettingsSta
|
||||
public boolean showTreeComment = true;
|
||||
public boolean showLineEndComment = true;
|
||||
|
||||
@SuppressWarnings("all")
|
||||
@SuppressWarnings("UseJBColor")
|
||||
public Color lineEndColorBright = new Color(98, 151, 85);
|
||||
@SuppressWarnings("all")
|
||||
@SuppressWarnings("UseJBColor")
|
||||
public Color lineEndColorDark = new Color(98, 151, 85);
|
||||
public final TextAttributes lineEndTextAttr = new TextAttributes(new JBColor(lineEndColorBright, lineEndColorDark),
|
||||
null, null, null, Font.ITALIC);
|
||||
@@ -34,6 +34,7 @@ public class AppSettingsState implements PersistentStateComponent<AppSettingsSta
|
||||
public boolean findElementRightToLeft = true;
|
||||
public String lineEndPrefix = " //";
|
||||
public int lineEndCount = 2;
|
||||
public int lineEndLen = 0;
|
||||
public boolean fromCall = true;
|
||||
public boolean fromNew = true;
|
||||
public boolean fromRef = true;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package io.github.linwancen.plugin.show.settings;
|
||||
|
||||
import com.intellij.openapi.components.PersistentStateComponent;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.openapi.components.Storage;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -9,6 +8,8 @@ import com.intellij.util.xmlb.XmlSerializerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@State(
|
||||
name = "io.github.linwancen.plugin.comment.settings.ProjectSettingsState",
|
||||
storages = @Storage("ShowCommentProject.xml")
|
||||
@@ -21,9 +22,11 @@ public class ProjectSettingsState implements PersistentStateComponent<ProjectSet
|
||||
public String lineEndExclude = "";
|
||||
public String[] lineEndIncludeArray = {};
|
||||
public String[] lineEndExcludeArray = {};
|
||||
public Pattern extReplaceToSpace = Pattern.compile("");
|
||||
public int extDocColumn = 2;
|
||||
|
||||
public static ProjectSettingsState getInstance(Project project) {
|
||||
return ServiceManager.getService(project, ProjectSettingsState.class);
|
||||
return project.getService(ProjectSettingsState.class);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
Reference in New Issue
Block a user