PY-12396 Fixed: Property getter live template is useful only inside python class

Separate Python liveTemplatesContext into two parts: General and Class
This commit is contained in:
Semyon Proshev
2016-02-15 14:19:19 +03:00
parent 0137d2fb92
commit 58a92fde16
11 changed files with 178 additions and 15 deletions
+8 -4
View File
@@ -8,7 +8,8 @@
<option name="HTML" value="false" />
<option name="XSL_TEXT" value="false" />
<option name="XML" value="false" />
<option name="Python" value="true" />
<option name="Python" value="false" />
<option name="Python_Class" value="true" />
<option name="Django" value="false" />
<option name="CSS_PROPERTY_VALUE" value="false" />
<option name="CSS_DECLARATION_BLOCK" value="false" />
@@ -272,7 +273,8 @@
<option name="HTML" value="false" />
<option name="XSL_TEXT" value="false" />
<option name="XML" value="false" />
<option name="Python" value="true" />
<option name="Python" value="false" />
<option name="Python_Class" value="true" />
<option name="Django" value="false" />
<option name="CSS_PROPERTY_VALUE" value="false" />
<option name="CSS_DECLARATION_BLOCK" value="false" />
@@ -292,7 +294,8 @@
<option name="HTML" value="false" />
<option name="XSL_TEXT" value="false" />
<option name="XML" value="false" />
<option name="Python" value="true" />
<option name="Python" value="false" />
<option name="Python_Class" value="true" />
<option name="Django" value="false" />
<option name="CSS_PROPERTY_VALUE" value="false" />
<option name="CSS_DECLARATION_BLOCK" value="false" />
@@ -312,7 +315,8 @@
<option name="HTML" value="false" />
<option name="XSL_TEXT" value="false" />
<option name="XML" value="false" />
<option name="Python" value="true" />
<option name="Python" value="false" />
<option name="Python_Class" value="true" />
<option name="Django" value="false" />
<option name="CSS_PROPERTY_VALUE" value="false" />
<option name="CSS_DECLARATION_BLOCK" value="false" />
+3 -3
View File
@@ -374,7 +374,9 @@
<localInspection language="Python" shortName="PyPep8NamingInspection" suppressId="PyPep8Naming" displayName="PEP 8 naming convention violation" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyPep8NamingInspection"/>
<localInspection language="Python" shortName="PyAssignmentToLoopOrWithParameterInspection" suppressId="PyAssignmentToLoopOrWithParameter" displayName="Assignment to 'for' loop or 'with' statement parameter" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyAssignmentToLoopOrWithParameterInspection"/>
<liveTemplateContext implementation="com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType"/>
<defaultLiveTemplatesProvider implementation="com.jetbrains.python.codeInsight.liveTemplates.PyDefaultLiveTemplatesProvider"/>
<liveTemplateContext implementation="com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType$General"/>
<liveTemplateContext implementation="com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType$Class"/>
<liveTemplateMacro implementation="com.jetbrains.python.codeInsight.liveTemplates.CollectionElementNameMacro"/>
<liveTemplateMacro implementation="com.jetbrains.python.codeInsight.liveTemplates.PyClassNameMacro"/>
<liveTemplateMacro implementation="com.jetbrains.python.codeInsight.liveTemplates.PyFunctionNameMacro"/>
@@ -418,8 +420,6 @@
serviceImplementation="com.jetbrains.python.sdk.PySdkService"/>
<autoImportOptionsProvider instance="com.jetbrains.python.codeInsight.imports.PyAutoImportOptions"/>
<defaultLiveTemplatesProvider implementation="com.jetbrains.python.codeInsight.liveTemplates.PyDefaultLiveTemplatesProvider"/>
<completion.contributor language="Python"
implementationClass="com.jetbrains.python.codeInsight.completion.PyClassNameCompletionContributor"/>
<weigher key="completion" implementationClass="com.jetbrains.python.codeInsight.completion.PythonCompletionWeigher" order="first"/>
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 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.
@@ -15,15 +15,18 @@
*/
package com.jetbrains.python.codeInsight.liveTemplates;
import com.intellij.codeInsight.template.FileTypeBasedContextType;
import com.intellij.codeInsight.template.EverywhereContextType;
import com.intellij.codeInsight.template.TemplateContextType;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.ProcessingContext;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyParameterList;
import com.jetbrains.python.psi.PyStringLiteralExpression;
import org.jetbrains.annotations.NotNull;
@@ -33,22 +36,37 @@ import static com.intellij.patterns.PlatformPatterns.psiElement;
/**
* @author yole
*/
public class PythonTemplateContextType extends FileTypeBasedContextType {
public PythonTemplateContextType() {
super("Python", "Python", PythonFileType.INSTANCE);
public abstract class PythonTemplateContextType extends TemplateContextType {
public PythonTemplateContextType(@NotNull String id,
@NotNull String presentableName,
@NotNull java.lang.Class<? extends TemplateContextType> baseContextType) {
super(id, presentableName, baseContextType);
}
@Override
public boolean isInContext(@NotNull PsiFile file, int offset) {
if (super.isInContext(file, offset)) {
if (isPythonLanguage(file, offset)) {
final PsiElement element = file.findElementAt(offset);
if (element != null) {
return !(isAfterDot(element) || element instanceof PsiComment || isInsideStringLiteral(element) || isInsideParameterList(element));
if (isAfterDot(element) || element instanceof PsiComment || isInsideStringLiteral(element) || isInsideParameterList(element)) {
return false;
}
return isInContext(element);
}
}
return false;
}
protected abstract boolean isInContext(@NotNull PsiElement element);
private static boolean isPythonLanguage(@NotNull PsiFile file, int offset) {
return PsiUtilCore.getLanguageAtOffset(file, offset).isKindOf(PythonLanguage.getInstance());
}
private static boolean isInsideStringLiteral(@NotNull PsiElement element) {
return PsiTreeUtil.getParentOfType(element, PyStringLiteralExpression.class, false) != null;
}
@@ -62,4 +80,28 @@ public class PythonTemplateContextType extends FileTypeBasedContextType {
psiElement().withElementType(PyTokenTypes.DOT));
return capture.accepts(element, new ProcessingContext());
}
public static class General extends PythonTemplateContextType {
public General() {
super("Python", "Python", EverywhereContextType.class);
}
@Override
protected boolean isInContext(@NotNull PsiElement element) {
return true;
}
}
public static class Class extends PythonTemplateContextType {
public Class() {
super("Python_Class", "Class", General.class);
}
@Override
protected boolean isInContext(@NotNull PsiElement element) {
return PsiTreeUtil.getParentOfType(element, PyClass.class) != null;
}
}
}
@@ -0,0 +1 @@
foo.<caret>
@@ -0,0 +1,3 @@
class C(object):
p<caret>
pass
@@ -0,0 +1,2 @@
p<caret>
pass
@@ -0,0 +1 @@
# <caret>
@@ -0,0 +1,2 @@
def foo(a, <caret>):
pass
@@ -0,0 +1 @@
"abc<caret>"
@@ -0,0 +1,3 @@
<html>
<caret>
</html>
@@ -0,0 +1,104 @@
/*
* Copyright 2000-2016 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.jetbrains.python.codeInsight.liveTemplates;
import com.intellij.codeInsight.template.TemplateContextType;
import com.intellij.testFramework.UsefulTestCase;
import com.jetbrains.python.fixtures.PyTestCase;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class PyLiveTemplatesTest extends PyTestCase {
@Override
protected String getTestDataPath() {
return super.getTestDataPath() + "/codeInsight/liveTemplates/context/";
}
public void testNotPython() {
doTest(Collections.emptyList(), "html");
}
// PY-12212
public void testAfterDot() {
doTest(Collections.emptyList());
}
// PY-13076
public void testInComment() {
doTest(Collections.emptyList());
}
// PY-12349
public void testInStringLiteral() {
doTest(Collections.emptyList());
}
// PY-12395
public void testInParameterList() {
doTest(Collections.emptyList());
}
public void testGeneral() {
doTest(
Collections.singletonList(PythonTemplateContextType.General.class)
);
}
// PY-12396
public void testClass() {
doTest(
Arrays.asList(PythonTemplateContextType.Class.class, PythonTemplateContextType.General.class)
);
}
private void doTest(@NotNull List<Class<? extends PythonTemplateContextType>> expectedContextTypes) {
doTest(expectedContextTypes, "py");
}
private void doTest(@NotNull List<Class<? extends PythonTemplateContextType>> expectedContextTypes, @NotNull String extension) {
myFixture.configureByFile(getTestName(true) + "." + extension);
UsefulTestCase.assertSameElements(
calculateEnabledContextTypes(getRegisteredContextTypes()),
expectedContextTypes
);
}
@NotNull
private List<Class<? extends PythonTemplateContextType>> calculateEnabledContextTypes(@NotNull List<PythonTemplateContextType> registeredContextTypes) {
//noinspection Convert2MethodRef
return registeredContextTypes
.stream()
.filter(type -> type.isInContext(myFixture.getFile(), myFixture.getCaretOffset()))
.map(type -> type.getClass())
.sorted((o1, o2) -> o1.getSimpleName().compareTo(o2.getSimpleName()))
.collect(Collectors.toList());
}
@NotNull
private static List<PythonTemplateContextType> getRegisteredContextTypes() {
return Arrays
.stream(TemplateContextType.EP_NAME.getExtensions())
.filter(type -> type instanceof PythonTemplateContextType)
.map(type -> (PythonTemplateContextType)type)
.collect(Collectors.toList());
}
}