Spellchecker: WI-27124 reload dictionary on externals changes: custom dictionaries case (.dic) + tests [IDEA-CR-23546]

This commit is contained in:
Olga Strizhenko
2017-08-01 15:53:18 +03:00
parent 2a17866658
commit b50819bb91
37 changed files with 300 additions and 5 deletions

View File

@@ -22,6 +22,9 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFileEvent;
import com.intellij.openapi.vfs.VirtualFileListener;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.PsiModificationTrackerImpl;
import com.intellij.spellchecker.dictionary.EditableDictionary;
@@ -31,7 +34,6 @@ import com.intellij.spellchecker.engine.SpellCheckerFactory;
import com.intellij.spellchecker.engine.SuggestionProvider;
import com.intellij.spellchecker.settings.SpellCheckerSettings;
import com.intellij.spellchecker.state.AggregatedDictionaryState;
import com.intellij.spellchecker.util.SPFileUtil;
import com.intellij.spellchecker.util.Strings;
import com.intellij.util.SmartList;
import com.intellij.util.containers.ContainerUtil;
@@ -41,6 +43,9 @@ import org.jetbrains.annotations.Nullable;
import java.io.InputStream;
import java.util.*;
import static com.intellij.openapi.util.io.FileUtil.isAncestor;
import static com.intellij.spellchecker.util.SPFileUtil.processFilesRecursively;
public class SpellCheckerManager {
private static final Logger LOG = Logger.getInstance("#com.intellij.spellchecker.SpellCheckerManager");
@@ -61,6 +66,38 @@ public class SpellCheckerManager {
this.project = project;
this.settings = settings;
fullConfigurationReload();
LocalFileSystem.getInstance().addVirtualFileListener(new VirtualFileListener() {
@Override
public void fileDeleted(@NotNull VirtualFileEvent event) {
final String path = event.getFile().getPath();
if (spellChecker.isDictionaryLoad(path)) {
spellChecker.removeDictionary(path);
restartInspections();
}
}
@Override
public void fileCreated(@NotNull VirtualFileEvent event) {
final String path = event.getFile().getPath();
boolean customDic = path.endsWith(".dic") && settings.getDictionaryFoldersPaths().stream().anyMatch(i -> isAncestor(i, path, true));
if (customDic) {
spellChecker.loadDictionary(new FileLoader(path, path));
restartInspections();
}
}
@Override
public void contentsChanged(@NotNull VirtualFileEvent event) {
final String path = event.getFile().getPath();
if (settings.getDisabledDictionariesPaths().contains(path)) return;
if (spellChecker.isDictionaryLoad(path)) {
spellChecker.removeDictionary(path);
spellChecker.loadDictionary(new FileLoader(path, path));
restartInspections();
}
}
});
}
public void fullConfigurationReload() {
@@ -91,8 +128,8 @@ public class SpellCheckerManager {
if (settings != null && settings.getDictionaryFoldersPaths() != null) {
final Set<String> disabledDictionaries = settings.getDisabledDictionariesPaths();
for (String folder : settings.getDictionaryFoldersPaths()) {
SPFileUtil.processFilesRecursively(folder, s -> {
boolean dictionaryShouldBeLoad =!disabledDictionaries.contains(s);
processFilesRecursively(folder, s -> {
boolean dictionaryShouldBeLoad = !disabledDictionaries.contains(s);
boolean dictionaryIsLoad = spellChecker.isDictionaryLoad(s);
if (dictionaryIsLoad && !dictionaryShouldBeLoad) {
spellChecker.removeDictionary(s);
@@ -142,7 +179,7 @@ public class SpellCheckerManager {
if (settings != null && settings.getDictionaryFoldersPaths() != null) {
final Set<String> disabledDictionaries = settings.getDisabledDictionariesPaths();
for (String folder : settings.getDictionaryFoldersPaths()) {
SPFileUtil.processFilesRecursively(folder, s -> {
processFilesRecursively(folder, s -> {
if (!disabledDictionaries.contains(s)) {
loaders.add(new FileLoader(s, s));
}

View File

@@ -0,0 +1,2 @@
<?php
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";

View File

@@ -0,0 +1,2 @@
<?php
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";

View File

@@ -0,0 +1,2 @@
<?php
echo "newword";

View File

@@ -0,0 +1,2 @@
<?php
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";

View File

@@ -0,0 +1 @@
newword

View File

@@ -0,0 +1,5 @@
<?php
echo "
newword
anotherword
";

View File

@@ -0,0 +1,5 @@
<?php
echo "
newword
<TYPO descr="Typo: In word 'anotherword'">anotherword</TYPO>
";

View File

@@ -0,0 +1,2 @@
newword

View File

@@ -0,0 +1 @@
anotherword

View File

@@ -0,0 +1,6 @@
<?php
echo "newword
anotherword
firstword
secondword
thirdword";

View File

@@ -0,0 +1,6 @@
<?php
echo "newword
anotherword
<TYPO descr="Typo: In word 'firstword'">firstword</TYPO>
<TYPO descr="Typo: In word 'secondword'">secondword</TYPO>
<TYPO descr="Typo: In word 'thirdword'">thirdword</TYPO>";

View File

@@ -0,0 +1,2 @@
newword
anotherword

View File

@@ -0,0 +1,5 @@
newword
anotherword
firstword
secondword
thirdword

View File

@@ -0,0 +1,2 @@
<?php
echo "newword";

View File

@@ -0,0 +1,2 @@
<?php
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";

View File

@@ -0,0 +1 @@
newword

View File

@@ -0,0 +1,5 @@
<?php
echo "<TYPO descr="Typo: In word 'firstword'">firstword</TYPO>
secondword
thirdword
fourthword";

View File

@@ -0,0 +1,5 @@
<?php
echo "firstword
secondword
thirdword
<TYPO descr="Typo: In word 'fourthword'">fourthword</TYPO>";

View File

@@ -0,0 +1,3 @@
firstword
secondword
thirdword

View File

@@ -0,0 +1,3 @@
secondword
thirdword
fourthword

View File

@@ -0,0 +1,2 @@
<?php
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";

View File

@@ -0,0 +1,2 @@
<?php
echo "newword";

View File

@@ -0,0 +1 @@
newword

View File

@@ -0,0 +1,2 @@
<?php
echo "<TYPO descr="Typo: In word 'newword'">newword</TYPO>";

View File

@@ -0,0 +1,2 @@
<?php
echo "newword";

View File

@@ -0,0 +1 @@
newword

View File

@@ -0,0 +1,5 @@
<?php
echo "
<TYPO descr="Typo: In word 'newword'">newword</TYPO>
anotherword
";

View File

@@ -0,0 +1,5 @@
<?php
echo "
newword
anotherword
";

View File

@@ -0,0 +1,173 @@
/*
* Copyright 2000-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.spellchecker.dictionary;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.spellchecker.SpellCheckerManager;
import com.intellij.spellchecker.inspection.SpellcheckerInspectionTestCase;
import com.intellij.spellchecker.settings.SpellCheckerSettings;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import static com.intellij.openapi.vfs.VfsUtil.findFileByIoFile;
public class CustomDictionaryTest extends SpellcheckerInspectionTestCase {
private static final String TEST_DIC = "test.dic";
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 List<String> oldPaths;
SpellCheckerSettings settings;
SpellCheckerManager spellCheckerManager;
@Override
protected void setUp() throws Exception {
super.setUp();
settings = SpellCheckerSettings.getInstance(getProject());
spellCheckerManager = SpellCheckerManager.getInstance(getProject());
oldPaths = settings.getDictionaryFoldersPaths();
settings.setDictionaryFoldersPaths(Collections.singletonList(getTestDictDirectory()));
spellCheckerManager.fullConfigurationReload();
}
@Override
protected void tearDown() throws Exception {
//noinspection SuperTearDownInFinally
super.tearDown();
settings.setDictionaryFoldersPaths(oldPaths);
}
@Override
protected String getBasePath() {
return Paths.get(getSpellcheckerTestDataPath(), "inspection", "dictionary").toString();
}
private String getTestDictionary() {
return Paths.get(getTestDictDirectory(), TEST_DIC).toString();
}
private String getTestDictDirectory() {
return Paths.get(myFixture.getTestDataPath(), getTestName(true)).toString();
}
private VirtualFile getTestDictionaryFile() {
return findFileByIoFile(Paths.get(getTestDictionary()).toFile(), true);
}
private String loadFromTestDictionary() throws IOException {
final VirtualFile file = findFileByIoFile(new File(getTestDictionary()), true);
if (file == null) return null;
return VfsUtilCore.loadText(file);
}
private void modifyDictContent(String newContent) throws IOException {
WriteAction.run(() -> VfsUtil.saveText(getTestDictionaryFile(), newContent));
}
private void doBeforeCheck() {
doTest(Paths.get(getTestDictDirectory(), "test.before.php").toString());
}
private void doAfterCheck() {
doTest(Paths.get(getTestDictDirectory(), "test.after.php").toString());
}
private void doTest() throws IOException {
final String oldDictContent = loadFromTestDictionary();
try {
doBeforeCheck();
modifyDictContent(VfsUtilCore.loadText(findFileByIoFile(Paths.get(getTestDictDirectory(), TEST_DIC_AFTER).toFile(), true)));
doAfterCheck();
}
finally {
//back to initial state
modifyDictContent(oldDictContent);
}
}
private void doNewDictTest() throws IOException {
final VirtualFile file = findFileByIoFile(Paths.get(getTestDictDirectory(), TEST_DIC_AFTER).toFile(), true);
try {
doBeforeCheck();
WriteAction.run(() -> file.copy(this, file.getParent(), NEW_TEST_DIC));
doAfterCheck();
}
finally {
//back to initial state
WriteAction.run(() -> file.getParent().findChild(NEW_TEST_DIC).delete(this));
}
}
private void doRemoveDictTest() throws IOException {
try {
doBeforeCheck();
WriteAction.run(() -> {
getTestDictionaryFile().copy(this, getTestDictionaryFile().getParent(), TEMP_DIC); // to revert it back
getTestDictionaryFile().delete(this);
});
doAfterCheck();
}
finally {
//back to initial state
WriteAction.run(() -> findFileByIoFile(Paths.get(getTestDictDirectory(), TEMP_DIC).toFile(), true).rename(this, TEST_DIC));
}
}
public void testAddDictionary() throws IOException, InterruptedException {
doNewDictTest();
}
public void testAddOneMoreDictionary() throws IOException, InterruptedException {
doNewDictTest();
}
public void testRemoveDictionary() throws IOException {
doRemoveDictTest();
}
public void testRemoveOneOfDictionaries() throws IOException {
doRemoveDictTest();
}
public void testAddToCustomDic() throws IOException {
doTest();
}
public void testAddAnotherToCustomDic() throws IOException {
doTest();
}
public void testRemoveFromCustomDic() throws IOException {
doTest();
}
public void testAddSeveralWords() throws IOException {
doTest();
}
public void testModifyDict() throws IOException {
doTest();
}
}

View File

@@ -25,7 +25,7 @@ public abstract class SpellcheckerInspectionTestCase extends LightPlatformCodeIn
return true;
}
public static String getSpellcheckerTestDataPath() {
protected static String getSpellcheckerTestDataPath() {
return "/spellchecker/testData/";
}