properties: fix update of structure view & resource bundle editor (IDEA-170695)

This commit is contained in:
Dmitry Batkovich
2017-04-14 12:07:59 +03:00
parent ec69c6fc71
commit ba799ce032
3 changed files with 174 additions and 0 deletions
@@ -0,0 +1,82 @@
/*
* 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.lang.properties.psi.impl;
import com.intellij.lang.properties.parsing.PropertiesTokenTypes;
import com.intellij.lang.properties.psi.PropertiesFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.impl.PsiTreeChangeEventImpl;
import com.intellij.psi.impl.PsiTreeChangePreprocessorBase;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiUtilCore;
import org.jetbrains.annotations.NotNull;
public class PropertiesPsiTreeChangePreprocessor extends PsiTreeChangePreprocessorBase {
private static final TokenSet CODE_BLOCK_ELEMENTS = TokenSet.create(PropertiesTokenTypes.VALUE_CHARACTERS,
PropertiesTokenTypes.END_OF_LINE_COMMENT,
PropertiesTokenTypes.WHITE_SPACE,
PropertiesTokenTypes.KEY_VALUE_SEPARATOR);
public PropertiesPsiTreeChangePreprocessor(@NotNull PsiManager psiManager) {
super(psiManager);
}
@Override
protected void onTreeChanged(@NotNull PsiTreeChangeEventImpl event) {
if (event.isGenericChange()) return;
switch (event.getCode()) {
case BEFORE_PROPERTY_CHANGE:
case BEFORE_CHILD_REMOVAL:
case BEFORE_CHILD_ADDITION:
case BEFORE_CHILD_MOVEMENT:
case BEFORE_CHILDREN_CHANGE:
case BEFORE_CHILD_REPLACEMENT:
return;
case CHILD_ADDED:
if (isCodeBlock(event.getChild())) return;
break;
case CHILD_REMOVED:
if (isCodeBlock(event.getChild())) return;
break;
case CHILD_REPLACED:
if (isCodeBlock(event.getOldChild()) || isCodeBlock(event.getNewChild())) return;
break;
case CHILD_MOVED:
if (isCodeBlock(event.getChild())) return;
break;
case CHILDREN_CHANGED:
if (isCodeBlock(event.getChild())) return;
case PROPERTY_CHANGED:
break;
}
doIncOutOfCodeBlockCounter();
}
@Override
protected boolean acceptsEvent(@NotNull PsiTreeChangeEventImpl event) {
return event.getFile() instanceof PropertiesFile;
}
@Override
protected boolean isOutOfCodeBlock(@NotNull PsiElement element) {
throw new IllegalStateException();
}
private static boolean isCodeBlock(@NotNull PsiElement element) {
return CODE_BLOCK_ELEMENTS.contains(PsiUtilCore.getElementType(element));
}
}
@@ -123,6 +123,8 @@
<lang.inspectionSuppressor language="Properties" implementationClass="com.intellij.codeInspection.PropertiesInspectionSuppressor"/>
<qualifiedNameProvider implementation="com.intellij.ide.actions.PropertiesQualifiedNameProvider"/>
<psi.treeChangePreprocessor implementation="com.intellij.lang.properties.psi.impl.PropertiesPsiTreeChangePreprocessor"/>
</extensions>
<project-components>
@@ -0,0 +1,90 @@
/*
* 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.lang.properties;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
public class PropertiesModificationTest extends LightCodeInsightFixtureTestCase {
public void testValueEditing() {
doTestEditing(" # comment<caret>", false);
}
public void testKeyEditing() {
doTestEditing("key<caret> = value", true);
}
public void testCommentEditing() {
doTestEditing("key = value<caret>", false);
}
public void testKeyReplacement() {
doTestReplacement("<selection>key</selection> = value", true);
}
public void testValueReplacement() {
doTestReplacement("key = <selection>value</selection>", false);
}
public void testCommentReplacement() {
doTestReplacement("key = value \n# <selection>comment</selection> \n key2 = value2", false);
}
public void testKeyDeletion() {
doTestDeletion("<selection>key</selection> = value", true);
}
public void testPropertiesDeletion() {
doTestDeletion("ke<selection>y1 = value1 \n" +
"key2 = value2 \n" +
"key3 = value3 \n" +
"key4 = value4 \n" +
"key5 = val</selection>ue5 \n", true);
}
private void doTestEditing(@NotNull String text, boolean isOutOfBlockModificationExpected) {
doTest(text, () -> myFixture.type("xxx"), isOutOfBlockModificationExpected);
}
private void doTestReplacement(@NotNull String text, boolean isOutOfBlockModificationExpected) {
doTest(text, () -> WriteCommandAction.runWriteCommandAction(getProject(), () -> {
SelectionModel sel = getEditor().getSelectionModel();
getEditor().getDocument().replaceString(sel.getSelectionStart(), sel.getSelectionEnd(), "xxx");
}), isOutOfBlockModificationExpected);
}
private void doTestDeletion(@NotNull String text, boolean isOutOfBlockModificationExpected) {
doTest(text, () -> WriteCommandAction.runWriteCommandAction(getProject(), () -> {
SelectionModel sel = getEditor().getSelectionModel();
getEditor().getDocument().deleteString(sel.getSelectionStart(), sel.getSelectionEnd());
}), isOutOfBlockModificationExpected);
}
private void doTest(@NotNull String text, Runnable modificationAction, boolean isOutOfBlockModificationExpected) {
myFixture.configureByText("test.properties", text);
PsiModificationTracker tracker = myFixture.getPsiManager().getModificationTracker();
long oldMod = tracker.getOutOfCodeBlockModificationCount();
modificationAction.run();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
long newMod = tracker.getOutOfCodeBlockModificationCount();
assertTrue(isOutOfBlockModificationExpected ^ oldMod == newMod);
}
}