mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-04 23:39:07 +07:00
Spellchecker: WI-37857 Spellchecker doesn't reload dictionaries
- if whole dictionaries folder is deleted externally [IDEA-CR-24731]
This commit is contained in:
@@ -24,10 +24,8 @@ import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.ProjectManager;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFileEvent;
|
||||
import com.intellij.openapi.vfs.VirtualFileListener;
|
||||
|
||||
import com.intellij.openapi.vfs.*;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.impl.PsiModificationTrackerImpl;
|
||||
import com.intellij.spellchecker.dictionary.AggregatedDictionary;
|
||||
@@ -45,11 +43,14 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
|
||||
import static com.intellij.openapi.util.io.FileUtil.isAncestor;
|
||||
import static com.intellij.openapi.util.io.FileUtilRt.extensionEquals;
|
||||
import static com.intellij.openapi.util.io.FileUtilRt.toSystemDependentName;
|
||||
import static com.intellij.openapi.vfs.VfsUtilCore.visitChildrenRecursively;
|
||||
|
||||
public class SpellCheckerManager implements Disposable {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.SpellCheckerManager");
|
||||
@@ -62,7 +63,7 @@ public class SpellCheckerManager implements Disposable {
|
||||
private AggregatedDictionary userDictionary;
|
||||
private final SuggestionProvider suggestionProvider = new BaseSuggestionProvider(this);
|
||||
private final SpellCheckerSettings settings;
|
||||
private final VirtualFileListener myVirtualFileListener;
|
||||
private final VirtualFileListener myCustomDictFileListener;
|
||||
|
||||
public static SpellCheckerManager getInstance(Project project) {
|
||||
return ServiceManager.getService(project, SpellCheckerManager.class);
|
||||
@@ -74,9 +75,9 @@ public class SpellCheckerManager implements Disposable {
|
||||
fullConfigurationReload();
|
||||
|
||||
Disposer.register(project, this);
|
||||
|
||||
myVirtualFileListener = new CustomDictVirtualFileListener(settings);
|
||||
LocalFileSystem.getInstance().addVirtualFileListener(myVirtualFileListener);
|
||||
|
||||
myCustomDictFileListener = new CustomDictFileListener(settings);
|
||||
LocalFileSystem.getInstance().addVirtualFileListener(myCustomDictFileListener);
|
||||
}
|
||||
|
||||
public void fullConfigurationReload() {
|
||||
@@ -240,44 +241,113 @@ public class SpellCheckerManager implements Disposable {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
LocalFileSystem.getInstance().removeVirtualFileListener(myVirtualFileListener);
|
||||
LocalFileSystem.getInstance().removeVirtualFileListener(myCustomDictFileListener);
|
||||
}
|
||||
|
||||
private class CustomDictVirtualFileListener implements VirtualFileListener {
|
||||
private class CustomDictFileListener implements VirtualFileListener {
|
||||
private final SpellCheckerSettings mySettings;
|
||||
|
||||
public CustomDictVirtualFileListener(SpellCheckerSettings settings) {mySettings = settings;}
|
||||
public CustomDictFileListener(@NotNull SpellCheckerSettings settings) {mySettings = settings;}
|
||||
|
||||
@Override
|
||||
public void fileDeleted(@NotNull VirtualFileEvent event) {
|
||||
final String path = toSystemDependentName(event.getFile().getPath());
|
||||
if (spellChecker.isDictionaryLoad(path)) {
|
||||
spellChecker.removeDictionary(path);
|
||||
restartInspections();
|
||||
}
|
||||
removeCustomDictionaries(event.getFile().getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileCreated(@NotNull VirtualFileEvent event) {
|
||||
final String path = toSystemDependentName(event.getFile().getPath());
|
||||
boolean customDic = FileUtilRt.extensionEquals(path, "dic") &&
|
||||
mySettings.getDictionaryFoldersPaths().stream().anyMatch(dicFolderPath -> isAncestor(dicFolderPath, path, true));
|
||||
if (customDic) {
|
||||
spellChecker.loadDictionary(new FileLoader(path));
|
||||
restartInspections();
|
||||
loadCustomDictionaries(event.getFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fileMoved(@NotNull VirtualFileMoveEvent event) {
|
||||
final String oldPath = event.getOldParent().getPath();
|
||||
if (!locatedInDictFolders(oldPath)) {
|
||||
loadCustomDictionaries(event.getFile());
|
||||
}
|
||||
else {
|
||||
final String newPath = event.getNewParent().getPath();
|
||||
if (!locatedInDictFolders(newPath)) {
|
||||
removeCustomDictionaries(oldPath + File.separator + event.getFileName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contentsChanged(@NotNull VirtualFileEvent event) {
|
||||
final String path = toSystemDependentName(event.getFile().getPath());
|
||||
if (mySettings.getDisabledDictionariesPaths().contains(path)) return;
|
||||
|
||||
if (!spellChecker.isDictionaryLoad(path) || mySettings.getDisabledDictionariesPaths().contains(path)) return;
|
||||
|
||||
spellChecker.removeDictionary(path);
|
||||
spellChecker.loadDictionary(new FileLoader(path));
|
||||
restartInspections();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
|
||||
final VirtualFile file = event.getFile();
|
||||
if (file.isDirectory()) return;
|
||||
|
||||
if (VirtualFile.PROP_NAME.equals(event.getPropertyName())) {
|
||||
final String oldName = (String)event.getOldValue();
|
||||
if (!isDic(oldName)) {
|
||||
loadCustomDictionaries(file);
|
||||
}
|
||||
else {
|
||||
final String newName = (String)event.getNewValue();
|
||||
if (!isDic(newName)) {
|
||||
removeCustomDictionaries(file.getParent().getPath() + File.separator + oldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeCustomDictionaries(@NotNull String path) {
|
||||
path = toSystemDependentName(path);
|
||||
if (spellChecker.isDictionaryLoad(path)) {
|
||||
spellChecker.removeDictionary(path);
|
||||
restartInspections();
|
||||
}
|
||||
else if (locatedInDictFolders(path)) {
|
||||
spellChecker.removeDictionariesRecursively(path);
|
||||
restartInspections();
|
||||
}
|
||||
if (mySettings.getDictionaryFoldersPaths().contains(path)) {
|
||||
mySettings.getDictionaryFoldersPaths().remove(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadCustomDictionaries(@NotNull VirtualFile file) {
|
||||
final String path = toSystemDependentName(file.getPath());
|
||||
if (!locatedInDictFolders(path)) return;
|
||||
|
||||
if (file.isDirectory()) {
|
||||
visitChildrenRecursively(file, new VirtualFileVisitor() {
|
||||
@Override
|
||||
public boolean visitFile(@NotNull VirtualFile file) {
|
||||
final boolean isDirectory = file.isDirectory();
|
||||
final String path = file.getPath();
|
||||
if (!isDirectory && isDic(path)) {
|
||||
spellChecker.loadDictionary(new FileLoader(path));
|
||||
restartInspections();
|
||||
}
|
||||
return isDirectory;
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (isDic(path)) {
|
||||
spellChecker.loadDictionary(new FileLoader(path));
|
||||
restartInspections();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDic(String path) {
|
||||
return extensionEquals(path, "dic");
|
||||
}
|
||||
|
||||
private boolean locatedInDictFolders(@NotNull String path) {
|
||||
return mySettings.getDictionaryFoldersPaths().stream().anyMatch(dicFolderPath -> isAncestor(dicFolderPath, path, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static com.intellij.openapi.util.io.FileUtil.isAncestor;
|
||||
|
||||
public class BaseSpellChecker implements SpellCheckerEngine {
|
||||
static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.engine.BaseSpellChecker");
|
||||
|
||||
@@ -236,6 +238,14 @@ public class BaseSpellChecker implements SpellCheckerEngine {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDictionariesRecursively(@NotNull String directory) {
|
||||
bundledDictionaries.stream()
|
||||
.map(Dictionary::getName)
|
||||
.filter(dict -> isAncestor(directory, dict, true) && isDictionaryLoad(dict))
|
||||
.forEach(this::removeDictionary);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Dictionary getBundledDictionaryByName(@NotNull String name) {
|
||||
for (Dictionary dictionary : bundledDictionaries) {
|
||||
|
||||
@@ -43,4 +43,6 @@ public interface SpellCheckerEngine {
|
||||
boolean isDictionaryLoad(@NotNull String name);
|
||||
|
||||
void removeDictionary(@NotNull String name);
|
||||
|
||||
void removeDictionariesRecursively(@NotNull String directory);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
echo "newword";
|
||||
@@ -0,0 +1 @@
|
||||
newword
|
||||
@@ -30,6 +30,7 @@ import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.openapi.util.io.FileUtil.createTempDirectory;
|
||||
import static com.intellij.openapi.vfs.VfsUtil.findFileByIoFile;
|
||||
|
||||
public class CustomDictionaryTest extends SpellcheckerInspectionTestCase {
|
||||
@@ -37,6 +38,8 @@ public class CustomDictionaryTest extends SpellcheckerInspectionTestCase {
|
||||
private static final String NEW_TEST_DIC = "new_" + TEST_DIC;
|
||||
private static final String TEST_DIC_AFTER = TEST_DIC + ".after";
|
||||
private static final String TEMP_DIC = TEST_DIC + ".temp";
|
||||
private static final String TEST_DIC_DIR = "testDir" ;
|
||||
public static final String TEMP = "temp";
|
||||
private List<String> oldPaths;
|
||||
SpellCheckerSettings settings;
|
||||
SpellCheckerManager spellCheckerManager;
|
||||
@@ -186,5 +189,173 @@ public class CustomDictionaryTest extends SpellcheckerInspectionTestCase {
|
||||
public void testUtf16LEDict() throws IOException {
|
||||
doNewDictTest();
|
||||
}
|
||||
|
||||
|
||||
public void testMoveDict() throws IOException {
|
||||
final VirtualFile tempDir = findFileByIoFile(createTempDirectory(TEST_DIC_DIR, TEMP), true);
|
||||
final VirtualFile testDir = findFileByIoFile(new File(getTestDictDirectory()), true);
|
||||
final VirtualFile file = testDir.findChild(TEST_DIC_AFTER);
|
||||
|
||||
try {
|
||||
doBeforeCheck();
|
||||
|
||||
WriteAction.run(() -> {
|
||||
final VirtualFile copy = file.copy(this, tempDir, TEST_DIC);
|
||||
copy.move(this, testDir);
|
||||
});
|
||||
doAfterCheck();
|
||||
}
|
||||
finally {
|
||||
//back to initial state
|
||||
WriteAction.run(() -> {
|
||||
tempDir.delete(this);
|
||||
testDir.findChild(TEST_DIC).delete(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void testMoveDictOutside() throws IOException {
|
||||
final VirtualFile tempDir = findFileByIoFile(createTempDirectory(TEST_DIC_DIR, TEMP), true);
|
||||
final VirtualFile testDir = findFileByIoFile(new File(getTestDictDirectory()), true);
|
||||
final VirtualFile file = testDir.findChild(TEST_DIC);
|
||||
moveFileToDirAndCheck(file,testDir, tempDir);
|
||||
}
|
||||
|
||||
public void testMoveNotInDictFolder() throws IOException {
|
||||
final VirtualFile tempDir1 = findFileByIoFile(createTempDirectory(TEST_DIC_DIR, TEMP + "1"), true);
|
||||
final VirtualFile tempDir2 = findFileByIoFile(createTempDirectory(TEST_DIC_DIR, TEMP + "2"), true);
|
||||
|
||||
final VirtualFile testDir = findFileByIoFile(new File(getTestDictDirectory()), true);
|
||||
final VirtualFile file = testDir.findChild(TEST_DIC_AFTER);
|
||||
WriteAction.run(() -> file.copy(this, tempDir1, TEST_DIC));
|
||||
|
||||
try {
|
||||
doBeforeCheck();
|
||||
WriteAction.run(() -> tempDir1.findChild(TEST_DIC).move(this, tempDir2));
|
||||
doAfterCheck();
|
||||
}
|
||||
finally {
|
||||
//back to initial state
|
||||
WriteAction.run(() -> {
|
||||
tempDir1.delete(this);
|
||||
tempDir2.delete(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void testMoveInsideDictFolders() throws IOException {
|
||||
final VirtualFile testDir = findFileByIoFile(new File(getTestDictDirectory()), true);
|
||||
final VirtualFile file = testDir.findChild(TEST_DIC);
|
||||
|
||||
final String yetAnotherDirName = "yetAnotherDir";
|
||||
WriteAction.run(() -> testDir.createChildDirectory(this, yetAnotherDirName));
|
||||
final VirtualFile anotherDir = testDir.findChild(yetAnotherDirName);
|
||||
moveFileToDirAndCheck(file, testDir, anotherDir);
|
||||
}
|
||||
|
||||
private void moveFileToDirAndCheck(VirtualFile file, VirtualFile from, VirtualFile to) throws IOException {
|
||||
try {
|
||||
doBeforeCheck();
|
||||
WriteAction.run(() -> file.move(this, to));
|
||||
doAfterCheck();
|
||||
}
|
||||
finally {
|
||||
//back to initial state
|
||||
WriteAction.run(() -> {
|
||||
to.findChild(TEST_DIC).move(this, from);
|
||||
to.delete(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void testRenameToDict() throws IOException {
|
||||
final VirtualFile file = findFileByIoFile(Paths.get(getTestDictDirectory(), TEST_DIC_AFTER).toFile(), true);
|
||||
try {
|
||||
doBeforeCheck();
|
||||
WriteAction.run(() -> file.rename(this, TEST_DIC));
|
||||
doAfterCheck();
|
||||
}
|
||||
finally {
|
||||
//back to initial state
|
||||
WriteAction.run(() -> file.rename(this, TEST_DIC_AFTER));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRenameToTxt() throws IOException {
|
||||
final VirtualFile file = findFileByIoFile(Paths.get(getTestDictDirectory(), TEST_DIC).toFile(), true);
|
||||
try {
|
||||
doBeforeCheck();
|
||||
WriteAction.run(() -> file.rename(this, "test.txt"));
|
||||
doAfterCheck();
|
||||
}
|
||||
finally {
|
||||
//back to initial state
|
||||
WriteAction.run(() -> file.rename(this, TEST_DIC));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRenameStillDicExtension() throws IOException {
|
||||
final VirtualFile file = findFileByIoFile(Paths.get(getTestDictDirectory(), TEST_DIC).toFile(), true);
|
||||
try {
|
||||
doBeforeCheck();
|
||||
WriteAction.run(() -> file.rename(this, "still.dic"));
|
||||
doAfterCheck();
|
||||
}
|
||||
finally {
|
||||
//back to initial state
|
||||
WriteAction.run(() -> file.rename(this, TEST_DIC));
|
||||
}
|
||||
}
|
||||
|
||||
public void testRenameStillNotDicExtension() throws IOException {
|
||||
final VirtualFile file = findFileByIoFile(Paths.get(getTestDictDirectory(), TEST_DIC_AFTER).toFile(), true);
|
||||
try {
|
||||
doBeforeCheck();
|
||||
WriteAction.run(() -> file.rename(this, "still_not_dic.extension"));
|
||||
doAfterCheck();
|
||||
}
|
||||
finally {
|
||||
//back to initial state
|
||||
WriteAction.run(() -> file.rename(this, TEST_DIC_AFTER));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testRemoveDictDir() throws IOException {
|
||||
final VirtualFile tempDir = findFileByIoFile(createTempDirectory(TEST_DIC_DIR, TEMP), true);
|
||||
final VirtualFile testDir = findFileByIoFile(new File(getTestDictDirectory()), true);
|
||||
final VirtualFile testDictDir = testDir.findChild(TEST_DIC_DIR);
|
||||
try {
|
||||
doBeforeCheck();
|
||||
WriteAction.run(() -> {
|
||||
testDictDir.copy(this, tempDir, TEST_DIC_DIR); // to revert it back
|
||||
testDictDir.delete(this);
|
||||
});
|
||||
doAfterCheck();
|
||||
}
|
||||
finally {
|
||||
//back to initial state
|
||||
WriteAction.run(() -> {
|
||||
tempDir.findChild(TEST_DIC_DIR).copy(this, testDir, TEST_DIC_DIR);
|
||||
tempDir.delete(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void testAddDictDir() throws IOException {
|
||||
final VirtualFile testDir = findFileByIoFile(new File(getTestDictDirectory()), true);
|
||||
final VirtualFile tempDir = findFileByIoFile(createTempDirectory(TEST_DIC_DIR, TEMP), true);
|
||||
WriteAction.run(() -> testDir.findChild(TEST_DIC_AFTER).copy(this, tempDir, TEST_DIC));
|
||||
try {
|
||||
doBeforeCheck();
|
||||
WriteAction.run(() -> tempDir.copy(this, testDir, TEST_DIC_DIR));
|
||||
doAfterCheck();
|
||||
}
|
||||
finally {
|
||||
//back to initial state
|
||||
WriteAction.run(() -> {
|
||||
testDir.findChild(TEST_DIC_DIR).delete(this);
|
||||
tempDir.delete(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user