diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9539231..dca639d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## [1.0.3] - 2019-05-28
+- Fix multiple rescan requests and add ignoring events while rescan running
+- Fix cache invalidation in Bulk mode for files update
+- CheckBundle after uploadFiles to ensure no missingFiles left
+
+## [1.0.2] - 2019-05-25
+- Bugfixing and better support for refactoring
+
## [1.0.1] - 2019-05-22
- Updated Java-client and added support for Java 8, required for Android Studio
diff --git a/build.gradle b/build.gradle
index 65c937f..3fef38e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ plugins {
//group 'org.example'
-version '1.0.2'
+version '1.0.3'
sourceCompatibility = 1.8
repositories {
@@ -35,6 +35,8 @@ patchPluginXml {
untilBuild 202
changeNotes """
+ 1.0.3 - Bugfixing and optimization of bulk file changes
+ 1.0.2 - Bugfixing and better support for refactoring
1.0.1 - Updated Java-client and added support for Java 8, required for Android Studio
1.0.0 - First public beta release.
"""
diff --git a/src/main/java/ai/deepcode/jbplugin/annotators/DeepCodeAnnotator.java b/src/main/java/ai/deepcode/jbplugin/annotators/DeepCodeAnnotator.java
deleted file mode 100644
index 4d69588..0000000
--- a/src/main/java/ai/deepcode/jbplugin/annotators/DeepCodeAnnotator.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package ai.deepcode.jbplugin.annotators;
-
-import ai.deepcode.jbplugin.actions.DeepCodeIntentionAction;
-import ai.deepcode.jbplugin.core.*;
-import com.intellij.lang.annotation.Annotation;
-import com.intellij.lang.annotation.AnnotationHolder;
-import com.intellij.lang.annotation.Annotator;
-import com.intellij.openapi.progress.ProgressManager;
-import com.intellij.openapi.util.TextRange;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.List;
-
-public class DeepCodeAnnotator implements Annotator {
-
- @Override
- public void annotate(@NotNull PsiElement psiElement, @NotNull AnnotationHolder holder) {
- if (!(psiElement instanceof PsiFile)) return;
- PsiFile psiFile = (PsiFile) psiElement;
- if (!DeepCodeUtils.isSupportedFileFormat(psiFile)) return;
- final long annotatorId = System.currentTimeMillis();
- DCLogger.info(
- "Annotator ("
- + annotatorId
- + ") requested for file: "
- + psiFile.getName()
- + " with Holder: "
- + holder);
-
-// RunUtils.runInBackground(
-// psiElement.getProject(),
-// () -> {
- doAnnotate(psiFile, holder, annotatorId);
-// }, "DeepCodeAnnotator.annotate"
-// );
- }
-
- private static void doAnnotate(
- @NotNull PsiFile psiFile, @NotNull AnnotationHolder holder, long annotatorId) {
- DCLogger.info(
- "Annotator ("
- + annotatorId
- + ") started for file: "
- + psiFile.getName()
- + " with Holder: "
- + holder);
- AnalysisData.waitForUpdateAnalysisFinish();
- ProgressManager.checkCanceled();
-
- List suggestions = AnalysisData.getAnalysis(psiFile);
- DCLogger.info(
- "Annotator (" + annotatorId + ") suggestions gotten for file: " + psiFile.getName());
- ProgressManager.checkCanceled();
-
- for (SuggestionForFile suggestion : suggestions) {
- final String message = "DeepCode: " + suggestion.getMessage();
- Annotation annotation;
- for (TextRange range : suggestion.getRanges()) {
- switch (suggestion.getSeverity()) {
- case 1:
- annotation = holder.createWeakWarningAnnotation(range, message);
- break;
- case 2:
- annotation = holder.createWarningAnnotation(range, message);
- break;
- case 3:
- annotation = holder.createErrorAnnotation(range, message);
- break;
- default:
- annotation = holder.createInfoAnnotation(range, message);
- break;
- }
- annotation.registerFix(
- new DeepCodeIntentionAction(psiFile, range, suggestion.getId(), false));
- annotation.registerFix(
- new DeepCodeIntentionAction(psiFile, range, suggestion.getId(), true));
- /*
- holder
- .newAnnotation(severity, "DeepCode: " + suggestion.getMessage())
- .range(range)
- .create();
- */
- }
- }
- }
-}
diff --git a/src/main/java/ai/deepcode/jbplugin/core/AnalysisData.java b/src/main/java/ai/deepcode/jbplugin/core/AnalysisData.java
index 6428d3e..894d66d 100644
--- a/src/main/java/ai/deepcode/jbplugin/core/AnalysisData.java
+++ b/src/main/java/ai/deepcode/jbplugin/core/AnalysisData.java
@@ -12,7 +12,6 @@
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.NotNull;
@@ -46,8 +45,7 @@ private AnalysisData() {}
// todo: keep few latest file versions (Guava com.google.common.cache.CacheBuilder ?)
private static final Map> mapFile2Suggestions =
- // deepcode ignore ApiMigration~java.util.Hashtable: we need read and write full data lock
- new Hashtable<>(); // new ConcurrentHashMap<>();
+ new ConcurrentHashMap<>();
private static final Map mapPsiFile2Hash = new ConcurrentHashMap<>();
@@ -111,6 +109,7 @@ static void removeFilesFromCache(@NotNull Collection files) {
info("Request to remove from cache " + files.size() + " files: " + files);
// todo: do we really need mutex here?
MUTEX.lock();
+ info("MUTEX LOCK");
int removeCounter = 0;
for (PsiFile file : files) {
if (file != null && isFileInCache(file)) {
@@ -125,13 +124,12 @@ static void removeFilesFromCache(@NotNull Collection files) {
+ " files. Were not in cache: "
+ (files.size() - removeCounter));
} finally {
+ info("MUTEX RELEASED");
MUTEX.unlock();
}
}
static void removeProjectFromCache(@NotNull Project project) {
- // lets all running ProgressIndicators release MUTEX first
- RunUtils.cancelRunningIndicators(project);
if (mapProject2BundleId.remove(project) != null) {
info("Removed from cache: " + project);
}
@@ -174,6 +172,7 @@ public static void updateCachedResultsForFiles(
Collection filesToProceed = null;
try {
MUTEX.lock();
+ info("MUTEX LOCK");
updateInProgress = true;
filesToProceed =
// DeepCodeUtils.computeNonBlockingReadAction(
@@ -209,7 +208,8 @@ public static void updateCachedResultsForFiles(
updateInProgress = false;
} finally {
- if (filesToProceed != null && !filesToProceed.isEmpty()) info("MUTEX RELEASED");
+ // if (filesToProceed != null && !filesToProceed.isEmpty())
+ info("MUTEX RELEASED");
MUTEX.unlock();
}
}
@@ -266,6 +266,10 @@ private static Map> retrieveSuggestions(
ProgressManager.checkCanceled();
progress.setFraction(((double) fileCounter++) / totalFiles);
progress.setText(PREPARE_FILES_TEXT + fileCounter + " of " + totalFiles + " files done.");
+ if (!file.isValid()) {
+ DCLogger.warn("Invalid PsiFile: " + psiFiles);
+ continue;
+ }
final String path = DeepCodeUtils.getDeepCodedFilePath(file);
// info("getHash requested");
final String hash = getHash(file);
@@ -285,24 +289,58 @@ private static Map> retrieveSuggestions(
CreateBundleResponse createBundleResponse = makeNewBundle(project, mapPath2Hash, filesToRemove);
if (isNotSucceed(project, createBundleResponse, "Bad Create/Extend Bundle request: "))
return EMPTY_MAP;
+ final String bundleId = createBundleResponse.getBundleId();
+ List missingFiles = createBundleResponse.getMissingFiles();
info(
"--- Create/Extend Bundle took: "
+ (System.currentTimeMillis() - startTime)
- + " milliseconds");
-
- final String bundleId = createBundleResponse.getBundleId();
- info("bundleId: " + bundleId);
-
- final List missingFiles = createBundleResponse.getMissingFiles();
- info("missingFiles: " + missingFiles.size());
+ + " milliseconds"
+ + "\nbundleId: "
+ + bundleId
+ + "\nmissingFiles: "
+ + missingFiles.size());
// ---------------------------------------- Upload Files
startTime = System.currentTimeMillis();
progress.setText(UPLOADING_FILES_TEXT);
ProgressManager.checkCanceled();
- fileCounter = 0;
- totalFiles = missingFiles.size();
+ for (int counter = 0; counter < 10; counter++) {
+ uploadFiles(project, psiFiles, missingFiles, bundleId, progress);
+ missingFiles = checkBundle(project, bundleId, progress);
+ if (missingFiles.isEmpty()) {
+ break;
+ } else {
+ warn(
+ "Check Bundle found some missingFiles to be NOT uploaded, will try to upload "
+ + (10 - counter)
+ + " more times:\nmissingFiles = "
+ + missingFiles);
+ }
+ }
+ // mapPsiFile2Hash.clear();
+ mapPsiFile2Content.clear();
+ info("--- Upload Files took: " + (System.currentTimeMillis() - startTime) + " milliseconds");
+
+ // ---------------------------------------- Get Analysis
+ startTime = System.currentTimeMillis();
+ progress.setText(WAITING_FOR_ANALYSIS_TEXT);
+ ProgressManager.checkCanceled();
+ GetAnalysisResponse getAnalysisResponse = doRetrieveSuggestions(project, bundleId, progress);
+ result = parseGetAnalysisResponse(project, psiFiles, getAnalysisResponse, progress);
+ info("--- Get Analysis took: " + (System.currentTimeMillis() - startTime) + " milliseconds");
+ // progress.stop();
+ return result;
+ }
+
+ private static void uploadFiles(
+ @NotNull Project project,
+ @NotNull Collection psiFiles,
+ @NotNull List missingFiles,
+ @NotNull String bundleId,
+ @NotNull ProgressIndicator progress) {
+ int fileCounter = 0;
+ int totalFiles = missingFiles.size();
long fileChunkSize = 0;
int brokenMissingFilesCount = 0;
String brokenMissingFilesMessage = "";
@@ -335,7 +373,7 @@ private static Map> retrieveSuggestions(
final long fileSize = psiFile.getVirtualFile().getLength();
if (fileChunkSize + fileSize > MAX_BUNDLE_SIZE) {
info("Files-chunk size: " + fileChunkSize);
- uploadFiles(project, filesChunk, bundleId, progress);
+ doUploadFiles(project, filesChunk, bundleId, progress);
fileChunkSize = 0;
filesChunk.clear();
}
@@ -344,21 +382,18 @@ private static Map> retrieveSuggestions(
}
if (brokenMissingFilesCount > 0) warn(brokenMissingFilesCount + brokenMissingFilesMessage);
info("Last files-chunk size: " + fileChunkSize);
- uploadFiles(project, filesChunk, bundleId, progress);
-
- // mapPsiFile2Hash.clear();
- mapPsiFile2Content.clear();
- info("--- Upload Files took: " + (System.currentTimeMillis() - startTime) + " milliseconds");
+ doUploadFiles(project, filesChunk, bundleId, progress);
+ }
- // ---------------------------------------- Get Analysis
- startTime = System.currentTimeMillis();
- progress.setText(WAITING_FOR_ANALYSIS_TEXT);
- ProgressManager.checkCanceled();
- GetAnalysisResponse getAnalysisResponse = doRetrieveSuggestions(project, bundleId, progress);
- result = parseGetAnalysisResponse(project, psiFiles, getAnalysisResponse, progress);
- info("--- Get Analysis took: " + (System.currentTimeMillis() - startTime) + " milliseconds");
- // progress.stop();
- return result;
+ @NotNull
+ private static List checkBundle(
+ @NotNull Project project, @NotNull String bundleId, @NotNull ProgressIndicator progress) {
+ CreateBundleResponse checkBundleResponse =
+ DeepCodeRestApi.checkBundle(DeepCodeParams.getSessionToken(), bundleId);
+ if (isNotSucceed(project, checkBundleResponse, "Bad CheckBundle request: ")) {
+ return Collections.emptyList();
+ }
+ return checkBundleResponse.getMissingFiles();
}
private static CreateBundleResponse makeNewBundle(
@@ -479,7 +514,7 @@ private static String doGetFileContent(@NotNull PsiFile psiFile) {
*/
}
- private static void uploadFiles(
+ private static void doUploadFiles(
@NotNull Project project,
@NotNull Collection psiFiles,
@NotNull String bundleId,
@@ -609,6 +644,8 @@ private static FileContent createFileContent(PsiFile psiFile) {
public static Set getAllFilesWithSuggestions(@NotNull final Project project) {
return mapFile2Suggestions.entrySet().stream()
.filter(e -> e.getKey().getProject().equals(project))
+ // otherwise ai.deepcode.jbplugin.ui.TodoTreeBuilder.getAllFiles may fail
+ .filter(e -> e.getKey().isValid())
.filter(e -> !e.getValue().isEmpty())
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
@@ -618,6 +655,7 @@ public static boolean isFileInCache(@NotNull PsiFile psiFile) {
return mapFile2Suggestions.containsKey(psiFile);
}
+ /** Remove project from all Caches and CANCEL all background tasks for it */
public static void clearCache(@Nullable final Project project) {
info("Cache clearance requested for project: " + project);
mapPsiFile2Hash.clear();
@@ -625,6 +663,8 @@ public static void clearCache(@Nullable final Project project) {
final Set projects =
(project == null) ? getAllCachedProject() : Collections.singleton(project);
for (Project prj : projects) {
+ // lets all running ProgressIndicators release MUTEX first
+ RunUtils.cancelRunningIndicators(prj);
removeProjectFromCache(prj);
ServiceManager.getService(prj, myTodoView.class).refresh();
mapProject2analysisUrl.put(prj, "");
diff --git a/src/main/java/ai/deepcode/jbplugin/core/DeepCodeUtils.java b/src/main/java/ai/deepcode/jbplugin/core/DeepCodeUtils.java
index 66c58ae..be25b0e 100644
--- a/src/main/java/ai/deepcode/jbplugin/core/DeepCodeUtils.java
+++ b/src/main/java/ai/deepcode/jbplugin/core/DeepCodeUtils.java
@@ -3,6 +3,7 @@
import ai.deepcode.javaclient.DeepCodeRestApi;
import ai.deepcode.javaclient.responses.GetFiltersResponse;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectUtil;
import com.intellij.openapi.vcs.changes.ChangeListManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
@@ -42,8 +43,17 @@ static List getAllSupportedFilesInProject(@NotNull Project project) {
private static List allProjectFiles(@NotNull Project project) {
final PsiManager psiManager = PsiManager.getInstance(project);
- final PsiDirectory prjDirectory = psiManager.findDirectory(project.getBaseDir());
- return prjDirectory != null ? getFilesRecursively(prjDirectory) : Collections.emptyList();
+ final VirtualFile projectDir = ProjectUtil.guessProjectDir(project);
+ if (projectDir == null) {
+ DCLogger.warn("Project directory not found for: " + project);
+ return Collections.emptyList();
+ }
+ final PsiDirectory prjDirectory = psiManager.findDirectory(projectDir);
+ if (prjDirectory == null) {
+ DCLogger.warn("Project directory not found for: " + project);
+ return Collections.emptyList();
+ }
+ return getFilesRecursively(prjDirectory);
}
private static List getFilesRecursively(@NotNull PsiDirectory psiDirectory) {
diff --git a/src/main/java/ai/deepcode/jbplugin/core/LoginUtils.java b/src/main/java/ai/deepcode/jbplugin/core/LoginUtils.java
index 825b647..35c51a0 100644
--- a/src/main/java/ai/deepcode/jbplugin/core/LoginUtils.java
+++ b/src/main/java/ai/deepcode/jbplugin/core/LoginUtils.java
@@ -28,6 +28,7 @@ private LoginUtils() {}
/** network request! */
public static boolean isLogged(@Nullable Project project, boolean userActionNeeded) {
final String sessionToken = DeepCodeParams.getSessionToken();
+ ProgressManager.checkCanceled();
final EmptyResponse response = DeepCodeRestApi.checkSession(sessionToken);
boolean isLogged = response.getStatusCode() == 200;
String message = response.getStatusDescription();
diff --git a/src/main/java/ai/deepcode/jbplugin/core/MyBulkFileListener.java b/src/main/java/ai/deepcode/jbplugin/core/MyBulkFileListener.java
index cf8bb58..2d5ecc9 100644
--- a/src/main/java/ai/deepcode/jbplugin/core/MyBulkFileListener.java
+++ b/src/main/java/ai/deepcode/jbplugin/core/MyBulkFileListener.java
@@ -1,11 +1,9 @@
package ai.deepcode.jbplugin.core;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
-import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent;
-import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent;
-import com.intellij.openapi.vfs.newvfs.events.VFileDeleteEvent;
-import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
+import com.intellij.openapi.vfs.newvfs.events.*;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.PsiTreeUtil;
@@ -26,40 +24,52 @@ public class MyBulkFileListener implements BulkFileListener {
@Override
public void after(@NotNull List extends VFileEvent> events) {
// fixme debug only
- DCLogger.info("MyBulkFileListener.after begins for: " + events);
- for (Project project : AnalysisData.getAllCachedProject()) {
- RunUtils.runInBackground(
- project,
- () -> {
- Set filesChangedOrCreated =
- RunUtils.computeInReadActionInSmartMode(
- project,
- () ->
- getFilteredFilesByEventTypes(
- project,
- events,
- (psiFile ->
- DeepCodeUtils.isSupportedFileFormat(psiFile)
- // to prevent updating files already done by
- // MyPsiTreeChangeAdapter
- // fixme: doesn't work, try to use isFromSave or isFromRefresh
- && AnalysisData.isHashChanged(psiFile)),
- VFileContentChangeEvent.class,
- // fixme doen't work for copy-past file ( VFileMoveEvent ?)
- VFileCreateEvent.class));
- if (!filesChangedOrCreated.isEmpty()) {
- DCLogger.info(
- filesChangedOrCreated.size() + " files changed: " + filesChangedOrCreated);
- for (PsiFile psiFile : filesChangedOrCreated) {
- RunUtils.runInBackgroundCancellable(
- psiFile,
- () -> {
- AnalysisData.removeFilesFromCache(Collections.singleton(psiFile));
- RunUtils.asyncAnalyseAndUpdatePanel(project, Collections.singleton(psiFile));
- });
- }
- }
- });
+ DCLogger.info("MyBulkFileListener.after begins for " + events.size() + " events " + events);
+ for (Project project : ProjectManager.getInstance().getOpenProjects()) {
+ /*
+ for (Project project : AnalysisData.getAllCachedProject()) {
+ RunUtils.runInBackground(
+ project,
+ () -> {
+ */
+ Set filesChangedOrCreated =
+ /*
+ RunUtils.computeInReadActionInSmartMode(
+ project,
+ () ->
+ */
+ getFilteredFilesByEventTypes(
+ project,
+ events,
+ (psiFile -> DeepCodeUtils.isSupportedFileFormat(psiFile)
+ // to prevent updating files already done by
+ // MyPsiTreeChangeAdapter
+ // fixme: doesn't work, try to use isFromSave or isFromRefresh
+ // && AnalysisData.isHashChanged(psiFile)
+ ),
+ VFileContentChangeEvent.class,
+ VFileMoveEvent.class,
+ VFileCopyEvent.class,
+ VFileCreateEvent.class);
+ if (!filesChangedOrCreated.isEmpty()) {
+ DCLogger.info(filesChangedOrCreated.size() + " files changed: " + filesChangedOrCreated);
+ if (filesChangedOrCreated.size() > 10) {
+ // if too many files changed then it's easier to do Bulk Mode full rescan
+ RunUtils.setBulkMode(project);
+ // small delay to prevent multiple rescan Background tasks
+ RunUtils.rescanInBackgroundCancellableDelayed(project, 100, true);
+ } else {
+ for (PsiFile psiFile : filesChangedOrCreated) {
+ RunUtils.runInBackgroundCancellable(
+ psiFile,
+ () -> {
+ AnalysisData.removeFilesFromCache(Collections.singleton(psiFile));
+ RunUtils.updateCachedAnalysisResults(project, Collections.singleton(psiFile));
+ });
+ }
+ }
+ }
+ // });
Set gcignoreChangedFiles =
getFilteredFilesByEventTypes(
@@ -69,50 +79,67 @@ public void after(@NotNull List extends VFileEvent> events) {
VFileContentChangeEvent.class,
VFileCreateEvent.class);
if (!gcignoreChangedFiles.isEmpty()) {
- RunUtils.runInBackground(
- project,
- () -> {
- gcignoreChangedFiles.forEach(DeepCodeIgnoreInfoHolder::update_dcignoreFileContent);
- // small delay to prevent duplicated delete with MyPsiTreeChangeAdapter
- RunUtils.rescanProject(project, 100);
- });
+ RunUtils.setBulkMode(project);
+ for (PsiFile gcignoreFile : gcignoreChangedFiles) {
+ RunUtils.runInBackgroundCancellable(
+ gcignoreFile,
+ () -> DeepCodeIgnoreInfoHolder.update_dcignoreFileContent(gcignoreFile));
+ }
+ // small delay to prevent multiple rescan Background tasks
+ RunUtils.rescanInBackgroundCancellableDelayed(project, 100, true);
}
}
// fixme debug only
- DCLogger.info("MyBulkFileListener.after ends for: " + events);
+ DCLogger.info("MyBulkFileListener.after ends");
}
@Override
public void before(@NotNull List extends VFileEvent> events) {
- DCLogger.info("MyBulkFileListener.before begins for: " + events);
- for (Project project : AnalysisData.getAllCachedProject()) {
+ DCLogger.info("MyBulkFileListener.before begins for " + events.size() + " events " + events);
+ for (Project project : ProjectManager.getInstance().getOpenProjects()) {
+ // for (Project project : AnalysisData.getAllCachedProject()) {
if (project.isDisposed()) continue;
Set filesRemoved =
getFilteredFilesByEventTypes(
project, events, DeepCodeUtils::isSupportedFileFormat, VFileDeleteEvent.class);
if (!filesRemoved.isEmpty()) {
- RunUtils.runInBackground(
- project,
- () -> {
- AnalysisData.removeFilesFromCache(filesRemoved);
- RunUtils.asyncAnalyseAndUpdatePanel(project, Collections.emptyList(), filesRemoved);
- });
+ DCLogger.info("Found " + filesRemoved.size() + " files to remove: " + filesRemoved);
+ // if too many files removed then it's easier to do full rescan
+ if (filesRemoved.size() > 10) {
+ RunUtils.setBulkMode(project);
+ // small delay to prevent multiple rescan Background tasks
+ RunUtils.rescanInBackgroundCancellableDelayed(project, 100, true);
+ } else if (!RunUtils.isFullRescanRequested(project)) {
+ RunUtils.runInBackground(
+ project,
+ () -> {
+ AnalysisData.removeFilesFromCache(filesRemoved);
+ RunUtils.updateCachedAnalysisResults(
+ project, Collections.emptyList(), filesRemoved);
+ });
+ }
}
Set ignoreFilesToRemove =
getFilteredFilesByEventTypes(
project, events, DeepCodeIgnoreInfoHolder::is_ignoreFile, VFileDeleteEvent.class);
if (!ignoreFilesToRemove.isEmpty()) {
- RunUtils.runInBackground(
- project,
- () -> {
- ignoreFilesToRemove.forEach(DeepCodeIgnoreInfoHolder::remove_dcignoreFileContent);
- // small delay to prevent duplicated delete with MyPsiTreeChangeAdapter
- RunUtils.rescanProject(project, 100);
- });
+ RunUtils.setBulkMode(project);
+ // small delay to prevent multiple rescan Background tasks
+ RunUtils.rescanInBackgroundCancellableDelayed(project, 100, true);
+ /*
+ RunUtils.rescanInBackgroundCancellableDelayed(
+ project,
+ 100, // small delay to prevent multiple rescan
+ () -> {
+ ignoreFilesToRemove.forEach(DeepCodeIgnoreInfoHolder::remove_dcignoreFileContent);
+ RunUtils.rescanProject(project);
+ RunUtils.unsetBulkMode(project);
+ });
+ */
}
}
- DCLogger.info("MyBulkFileListener.before ends for: " + events);
+ DCLogger.info("MyBulkFileListener.before ends");
}
private Set getFilteredFilesByEventTypes(
@@ -122,6 +149,8 @@ private Set getFilteredFilesByEventTypes(
@NotNull Class>... classesOfEventsToFilter) {
PsiManager manager = PsiManager.getInstance(project);
return events.stream()
+ // to prevent updating files already done by MyPsiTreeChangeAdapter
+ .filter(VFileEvent::isFromRefresh)
.filter(event -> PsiTreeUtil.instanceOf(event, classesOfEventsToFilter))
.map(VFileEvent::getFile)
.filter(Objects::nonNull)
diff --git a/src/main/java/ai/deepcode/jbplugin/core/MyProjectManagerListener.java b/src/main/java/ai/deepcode/jbplugin/core/MyProjectManagerListener.java
index 3525549..9375a06 100644
--- a/src/main/java/ai/deepcode/jbplugin/core/MyProjectManagerListener.java
+++ b/src/main/java/ai/deepcode/jbplugin/core/MyProjectManagerListener.java
@@ -2,7 +2,6 @@
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
-import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManagerListener;
import com.intellij.openapi.vfs.VirtualFile;
@@ -32,16 +31,18 @@ public void projectOpened(@NotNull Project project) {
@Override
public void projectClosing(@NotNull Project project) {
- // lets all running ProgressIndicators release MUTEX first
- // RunUtils.cancelRunningIndicators(project);
- RunUtils.runInBackground(project, () -> AnalysisData.removeProjectFromCache(project));
+ RunUtils.runInBackground(project, () -> {
+ // lets all running ProgressIndicators release MUTEX first
+ RunUtils.cancelRunningIndicators(project);
+ AnalysisData.removeProjectFromCache(project);
+ });
}
private static class MyPsiTreeChangeAdapter extends PsiTreeChangeAdapter {
@Override
public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) {
final PsiFile psiFile = event.getFile();
- if (psiFile == null) return;
+ if (psiFile == null || RunUtils.inBulkMode(psiFile.getProject())) return;
if (AnalysisData.isFileInCache(psiFile)) {
// ?? immediate delete for visual updates in Panel, annotations, etc.
// should be done in background to wait MUTEX released in case of currently running update
@@ -68,6 +69,8 @@ public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) {
public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
final PsiFile psiFile = event.getFile();
if (psiFile == null) return;
+ final Project project = psiFile.getProject();
+ if (RunUtils.inBulkMode(project)) return;
if (DeepCodeUtils.isSupportedFileFormat(psiFile)) {
RunUtils.runInBackgroundCancellable(
@@ -79,14 +82,14 @@ public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
// but in case of update finished between beforeChildrenChange and now.
AnalysisData.removeFilesFromCache(psiFileSet);
}
- RunUtils.asyncAnalyseAndUpdatePanel(psiFile.getProject(), psiFileSet);
+ RunUtils.updateCachedAnalysisResults(project, psiFileSet);
});
}
if (DeepCodeIgnoreInfoHolder.is_dcignoreFile(psiFile)) {
DeepCodeIgnoreInfoHolder.update_dcignoreFileContent(psiFile);
// delayed to prevent unnecessary updates in case of continuous typing by user
- RunUtils.rescanProject(psiFile.getProject(), 1000);
+ RunUtils.rescanInBackgroundCancellableDelayed(project, 1000, false);
}
// .gitignore content delay to be parsed https://youtrack.jetbrains.com/issue/IDEA-239773
final VirtualFile virtualFile = psiFile.getVirtualFile();
@@ -95,7 +98,7 @@ public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
if (document != null) {
FileDocumentManager.getInstance().saveDocument(document);
// delayed to let git update it meta-info
- RunUtils.rescanProject(psiFile.getProject(), 1000);
+ RunUtils.rescanInBackgroundCancellableDelayed(project, 1000, false);
}
}
}
@@ -103,10 +106,14 @@ public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
@Override
public void beforeChildRemoval(@NotNull PsiTreeChangeEvent event) {
PsiFile psiFile = (event.getChild() instanceof PsiFile) ? (PsiFile) event.getChild() : null;
- if (psiFile != null && DeepCodeIgnoreInfoHolder.is_ignoreFile(psiFile)) {
+ if (psiFile == null) return;
+ final Project project = psiFile.getProject();
+ if (RunUtils.inBulkMode(project)) return;
+
+ if (DeepCodeIgnoreInfoHolder.is_ignoreFile(psiFile)) {
DeepCodeIgnoreInfoHolder.remove_dcignoreFileContent(psiFile);
- // small delay to prevent duplicated delete with MyBulkFileListener
- RunUtils.rescanProject(psiFile.getProject(), 100);
+ // ??? small delay to prevent duplicated delete with MyBulkFileListener
+ RunUtils.rescanInBackgroundCancellableDelayed(project, 100, false);
}
}
}
diff --git a/src/main/java/ai/deepcode/jbplugin/core/RunUtils.java b/src/main/java/ai/deepcode/jbplugin/core/RunUtils.java
index a4ebac9..ae37bd5 100644
--- a/src/main/java/ai/deepcode/jbplugin/core/RunUtils.java
+++ b/src/main/java/ai/deepcode/jbplugin/core/RunUtils.java
@@ -21,17 +21,44 @@
public class RunUtils {
private RunUtils() {}
- public static void asyncUpdateCurrentFilePanel(PsiFile psiFile) {
- /*
- ApplicationManager.getApplication()
- .invokeLater(
- () ->
- WriteCommandAction.runWriteCommandAction(
- psiFile.getProject(),
- () -> DeepCodeConsoleToolWindowFactory.updateCurrentFilePanel(psiFile)));
- */
+ private static final Map mapProject2RequestsCounter = new ConcurrentHashMap<>();
+
+ private static int getBulkRequestsCount(@NotNull Project project) {
+ return mapProject2RequestsCounter.computeIfAbsent(project, p -> 0);
+ }
+
+ /** No events for individual files should be processed */
+ public static boolean inBulkMode(@NotNull Project project) {
+ return getBulkRequestsCount(project) > 0;
+ }
+
+ public static void setBulkMode(@NotNull Project project) {
+ final int counter = getBulkRequestsCount(project) + 1;
+ if (counter == 1) {
+ // cancel all running tasks first
+ cancelRunningIndicators(project);
+ }
+ DCLogger.info("BulkMode ON with " + counter + " total requests");
+ mapProject2RequestsCounter.put(project, counter);
+ }
+
+ public static void unsetBulkMode(@NotNull Project project) {
+ final int counter = getBulkRequestsCount(project) - 1;
+ if (counter >= 0) {
+ mapProject2RequestsCounter.put(project, counter);
+ DCLogger.info("BulkMode OFF with " + counter + " total requests");
+ } else {
+ DCLogger.warn("BulkMode OFF request with already " + counter + " total requests");
+ }
+ }
+
+ public static void forceUnsetBulkMode(@NotNull Project project) {
+ mapProject2RequestsCounter.put(project, 0);
+ DCLogger.info("BulkMode OFF forced");
}
+ public static void asyncUpdateCurrentFilePanel(PsiFile psiFile) {}
+
public static T computeInReadActionInSmartMode(
@NotNull Project project, @NotNull final Computable computation) {
// DCLogger.info("computeInReadActionInSmartMode requested");
@@ -58,20 +85,6 @@ public static void delay(long millis) {
ProgressManager.checkCanceled();
}
- private static long timeOfLastRescanRequest = 0;
- // BackgroundTaskQueue ??? com.intellij.openapi.wm.ex.StatusBarEx#getBackgroundProcesses ???
- public static void rescanProject(@NotNull Project project, long delayMilliseconds) {
- runInBackground(
- project,
- () -> {
- long timeOfThisRequest = timeOfLastRescanRequest = System.currentTimeMillis();
- delay(delayMilliseconds);
- if (timeOfLastRescanRequest > timeOfThisRequest) return;
- AnalysisData.clearCache(project);
- asyncAnalyseProjectAndUpdatePanel(project);
- });
- }
-
public static void runInBackground(@NotNull Project project, @NotNull Runnable runnable) {
DCLogger.info("runInBackground requested");
final ProgressManager progressManager = ProgressManager.getInstance();
@@ -109,7 +122,8 @@ public void run(@NotNull ProgressIndicator indicator) {
private static final Map> mapProject2Indicators =
new ConcurrentHashMap<>();
- private static synchronized Set getRunningIndicators(@NotNull Project project) {
+ private static synchronized Set getRunningIndicators(
+ @NotNull Project project) {
return mapProject2Indicators.computeIfAbsent(project, p -> new HashSet<>());
}
@@ -123,8 +137,11 @@ public static void cancelRunningIndicators(@NotNull Project project) {
.map(ProgressIndicator::toString)
.collect(Collectors.joining("\n"));
DCLogger.info("Canceling ProgressIndicators:\n" + indicatorsList);
+ // in case any indicator holds Bulk mode process
+ forceUnsetBulkMode(project);
getRunningIndicators(project).forEach(ProgressIndicator::cancel);
getRunningIndicators(project).clear();
+ projectsWithFullRescanRequested.remove(project);
}
private static final Map mapFileProcessed2CancellableIndicator =
@@ -134,13 +151,20 @@ public static void cancelRunningIndicators(@NotNull Project project) {
public static void runInBackgroundCancellable(
@NotNull PsiFile psiFile, @NotNull Runnable runnable) {
- DCLogger.info("runInBackgroundCancellable requested for: " + psiFile.getName());
+ final String s = runnable.toString();
+ final String runId = s.substring(s.lastIndexOf('/'), s.length() - 1);
+ DCLogger.info(
+ "runInBackgroundCancellable requested for: "
+ + psiFile.getName()
+ + " with Runnable "
+ + runId);
final VirtualFile virtualFile = psiFile.getVirtualFile();
// To proceed multiple PSI events in a bunch (every 100 milliseconds)
Runnable prevRunnable = mapFile2Runnable.put(virtualFile, runnable);
if (prevRunnable != null) return;
- DCLogger.info("new Background task registered for: " + psiFile.getName());
+ DCLogger.info(
+ "new Background task registered for: " + psiFile.getName() + " with Runnable " + runId);
final Project project = psiFile.getProject();
ProgressManager.getInstance()
@@ -170,10 +194,13 @@ && getRunningIndicators(project).contains(prevProgressIndicator)) {
// small delay to let new consequent requests proceed and cancel current one
delay(100);
- DCLogger.info("New Process started for " + psiFile.getName());
Runnable actualRunnable = mapFile2Runnable.get(virtualFile);
- mapFile2Runnable.remove(virtualFile);
if (actualRunnable != null) {
+ final String s1 = actualRunnable.toString();
+ final String runId = s1.substring(s1.lastIndexOf('/'), s1.length() - 1);
+ DCLogger.info(
+ "New Process started for " + psiFile.getName() + " with Runnable " + runId);
+ mapFile2Runnable.remove(virtualFile);
actualRunnable.run();
} else {
DCLogger.warn("No actual Runnable found for: " + psiFile.getName());
@@ -183,34 +210,128 @@ && getRunningIndicators(project).contains(prevProgressIndicator)) {
});
}
- public static void asyncAnalyseProjectAndUpdatePanel(@Nullable Project project) {
- if (project == null) {
- for (Project prj : ProjectManager.getInstance().getOpenProjects()) {
- asyncAnalyseAndUpdatePanel(prj, null);
+ public static boolean isFullRescanRequested(@NotNull Project project) {
+ return projectsWithFullRescanRequested.contains(project);
+ }
+
+ private static final Set projectsWithFullRescanRequested =
+ ContainerUtil.newConcurrentSet();
+
+ private static final Map mapProject2CancellableIndicator =
+ new ConcurrentHashMap<>();
+ private static final Map mapProject2CancellableRequestId =
+ new ConcurrentHashMap<>();
+
+ private static final Map mapProject2RequestId = new ConcurrentHashMap<>();
+ private static final Set bulkModeRequests = ContainerUtil.newConcurrentSet();
+
+ public static void rescanInBackgroundCancellableDelayed(
+ @NotNull Project project, long delayMilliseconds, boolean inBulkMode) {
+ final long requestId = System.currentTimeMillis();
+ DCLogger.info(
+ "rescanInBackgroundCancellableDelayed requested for: "
+ + project.getName()
+ + "] with RequestId "
+ + requestId);
+ projectsWithFullRescanRequested.add(project);
+
+ // To proceed multiple events in a bunch (every )
+ Long prevRequestId = mapProject2RequestId.put(project, requestId);
+ if (inBulkMode) bulkModeRequests.add(requestId);
+ if (prevRequestId != null) {
+ if (bulkModeRequests.remove(prevRequestId)) {
+ RunUtils.unsetBulkMode(project);
}
- } else asyncAnalyseAndUpdatePanel(project, null);
+ return;
+ }
+ DCLogger.info(
+ "new Background Rescan task registered for ["
+ + project.getName()
+ + "] with RequestId "
+ + requestId);
+
+ ProgressManager.getInstance()
+ .run(
+ new Task.Backgroundable(project, "DeepCode: Analysing Files...") {
+ @Override
+ public void run(@NotNull ProgressIndicator indicator) {
+
+ // To let new event cancel the currently running one
+ ProgressIndicator prevProgressIndicator =
+ mapProject2CancellableIndicator.put(project, indicator);
+ if (prevProgressIndicator != null
+ // can't use prevProgressIndicator.isRunning() due to
+ // https://youtrack.jetbrains.com/issue/IDEA-241055
+ && getRunningIndicators(project).remove(prevProgressIndicator)) {
+ DCLogger.info(
+ "Previous Rescan cancelling for "
+ + project.getName()
+ + "\nProgressIndicator ["
+ + prevProgressIndicator.toString()
+ + "]");
+ prevProgressIndicator.cancel();
+ }
+ getRunningIndicators(project).add(indicator);
+
+ // unset BulkMode if cancelled process did run under BulkMode
+ Long prevRequestId = mapProject2CancellableRequestId.put(project, requestId);
+ if (prevRequestId != null && bulkModeRequests.remove(prevRequestId)) {
+ RunUtils.unsetBulkMode(project);
+ }
+
+ // delay to let new consequent requests proceed and cancel current one
+ // or to let Idea proceed internal events (.gitignore update)
+ delay(delayMilliseconds);
+
+ Long actualRequestId = mapProject2RequestId.get(project);
+ if (actualRequestId != null) {
+ DCLogger.info(
+ "New Rescan started for ["
+ + project.getName()
+ + "] with RequestId "
+ + actualRequestId);
+ mapProject2RequestId.remove(project);
+
+ // actual rescan
+ AnalysisData.removeProjectFromCache(project);
+ updateCachedAnalysisResults(project, null);
+
+ if (bulkModeRequests.remove(actualRequestId)) {
+ RunUtils.unsetBulkMode(project);
+ }
+ } else {
+ DCLogger.warn("No actual RequestId found for: " + project.getName());
+ }
+ projectsWithFullRescanRequested.remove(project);
+ DCLogger.info("Rescan ending for " + project.getName());
+ }
+ });
+ }
+
+ public static void asyncAnalyseProjectAndUpdatePanel(@Nullable Project project) {
+ final Project[] projects =
+ (project == null)
+ ? ProjectManager.getInstance().getOpenProjects()
+ : new Project[] {project};
+ for (Project prj : projects) {
+ // DumbService.getInstance(project).runWhenSmart(() ->
+ runInBackground(prj, () -> updateCachedAnalysisResults(prj, null));
+ }
}
- public static void asyncAnalyseAndUpdatePanel(
+ public static void updateCachedAnalysisResults(
@NotNull Project project, @Nullable Collection psiFiles) {
- asyncAnalyseAndUpdatePanel(project, psiFiles, Collections.emptyList());
+ updateCachedAnalysisResults(project, psiFiles, Collections.emptyList());
}
- public static void asyncAnalyseAndUpdatePanel(
+ public static void updateCachedAnalysisResults(
@NotNull Project project,
@Nullable Collection psiFiles,
@NotNull Collection filesToRemove) {
- // DumbService.getInstance(project)
- // .runWhenSmart(
- // () ->
- runInBackground(
+ AnalysisData.updateCachedResultsForFiles(
project,
- () -> {
- AnalysisData.updateCachedResultsForFiles(
- project,
- (psiFiles != null) ? psiFiles : DeepCodeUtils.getAllSupportedFilesInProject(project),
- filesToRemove);
- // StatusBarUtil.setStatusBarInfo(project, message);
- });
+ (psiFiles != null) ? psiFiles : DeepCodeUtils.getAllSupportedFilesInProject(project),
+ filesToRemove);
+ // StatusBarUtil.setStatusBarInfo(project, message);
}
}
diff --git a/src/main/java/ai/deepcode/jbplugin/ui/myTodoView.java b/src/main/java/ai/deepcode/jbplugin/ui/myTodoView.java
index 2f01e53..cde63d4 100644
--- a/src/main/java/ai/deepcode/jbplugin/ui/myTodoView.java
+++ b/src/main/java/ai/deepcode/jbplugin/ui/myTodoView.java
@@ -117,8 +117,7 @@ public void dispose() {
public void initToolWindow(@NotNull ToolWindow toolWindow) {
// Create panels
ContentFactory contentFactory = ContentFactory.SERVICE.getInstance();
- final String projectDisplayName =
- IdeUICustomization.getInstance().getProjectDisplayName() + " [ " + myProject.getName() + " ]";
+ final String projectDisplayName ="Project [ " + myProject.getName() + " ]";
Content allTodosContent = contentFactory.createContent(null, projectDisplayName, false);
toolWindow.setHelpId("find.todoList");
myAllTodos = new TodoPanel(myProject, state.all, false, allTodosContent) {
diff --git a/src/main/java/ai/deepcode/jbplugin/ui/nodes/ModuleToDoNode.java b/src/main/java/ai/deepcode/jbplugin/ui/nodes/ModuleToDoNode.java
index 3e97783..a8d9932 100644
--- a/src/main/java/ai/deepcode/jbplugin/ui/nodes/ModuleToDoNode.java
+++ b/src/main/java/ai/deepcode/jbplugin/ui/nodes/ModuleToDoNode.java
@@ -13,11 +13,13 @@
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootManager;
+import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.TodoItem;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
@@ -82,10 +84,17 @@ public void update(@NotNull PresentationData presentation) {
presentation.setPresentableText(newName);
}
+/*
@Override
public String getTestPresentation() {
return "Module";
}
+*/
+
+ @Override
+ public String toTestString(@Nullable Queryable.PrintInfo printInfo) {
+ return "Module";
+ }
@Override
public int getFileCount(Module module) {
diff --git a/src/main/java/ai/deepcode/jbplugin/ui/nodes/SummaryNode.java b/src/main/java/ai/deepcode/jbplugin/ui/nodes/SummaryNode.java
index 372db76..33b72e1 100644
--- a/src/main/java/ai/deepcode/jbplugin/ui/nodes/SummaryNode.java
+++ b/src/main/java/ai/deepcode/jbplugin/ui/nodes/SummaryNode.java
@@ -17,11 +17,13 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
+import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.ui.HighlightedRegion;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
@@ -124,10 +126,18 @@ public void update(@NotNull PresentationData presentation) {
myBuilder.expandTree(2);
}
+/*
@Override
public String getTestPresentation() {
return "Summary";
}
+*/
+
+ @Nullable
+ @Override
+ public String toTestString(@Nullable Queryable.PrintInfo printInfo) {
+ return "Summary";
+ }
@Override
public int getFileCount(ToDoSummary summary) {
diff --git a/src/main/java/ai/deepcode/jbplugin/ui/nodes/ToDoRootNode.java b/src/main/java/ai/deepcode/jbplugin/ui/nodes/ToDoRootNode.java
index e7e4fd1..1b9e104 100644
--- a/src/main/java/ai/deepcode/jbplugin/ui/nodes/ToDoRootNode.java
+++ b/src/main/java/ai/deepcode/jbplugin/ui/nodes/ToDoRootNode.java
@@ -7,7 +7,9 @@
import ai.deepcode.jbplugin.ui.TodoTreeBuilder;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Queryable;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
@@ -39,10 +41,18 @@ public Object getSummaryNode() {
return mySummaryNode;
}
+/*
@Override
public String getTestPresentation() {
return "Root";
}
+*/
+
+ @Nullable
+ @Override
+ public String toTestString(@Nullable Queryable.PrintInfo printInfo) {
+ return "Root";
+ }
@Override
public int getFileCount(final Object val) {
diff --git a/src/main/java/ai/deepcode/jbplugin/ui/nodes/TodoItemNode.java b/src/main/java/ai/deepcode/jbplugin/ui/nodes/TodoItemNode.java
index 46463b4..f4285d2 100644
--- a/src/main/java/ai/deepcode/jbplugin/ui/nodes/TodoItemNode.java
+++ b/src/main/java/ai/deepcode/jbplugin/ui/nodes/TodoItemNode.java
@@ -16,6 +16,7 @@
import com.intellij.openapi.editor.highlighter.HighlighterIterator;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.Queryable;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.search.TodoItem;
@@ -23,6 +24,7 @@
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.CharArrayUtil;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
@@ -194,10 +196,17 @@ public int getRowCount() {
return myAdditionalLines.size() + 1;
}
+/*
@Override
public String getTestPresentation() {
return "Item: " + getValue().getTodoItem().getTextRange();
}
+*/
+
+ @Override
+ public String toTestString(@Nullable Queryable.PrintInfo printInfo) {
+ return "Item: " + getValue().getTodoItem().getTextRange();
+ }
@Override
public int getWeight() {
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index 8495a4e..2120afb 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -22,22 +22,7 @@
-
+