[python] postfix templates: make 'for' and 'len' editable (PY-30217)

GitOrigin-RevId: a92252f98e45799b03f6b39b22c3a02b5270bd0c
This commit is contained in:
Vladislav Rassokhin
2023-03-08 19:21:04 +00:00
committed by intellij-monorepo-bot
parent 56b09dadab
commit 4d0cedd933
25 changed files with 42 additions and 76 deletions
@@ -8,9 +8,9 @@ import com.intellij.openapi.editor.Document
import com.intellij.openapi.util.Conditions
import com.intellij.psi.PsiElement
class PyEditablePostfixTemplate(templateId: String, templateName: String, liveTemplate: TemplateImpl, example: String,
conditions: Set<PyPostfixTemplateExpressionCondition?>, topmost: Boolean, provider: PostfixTemplateProvider,
private val myBuiltin: Boolean) : EditablePostfixTemplateWithMultipleExpressions<PyPostfixTemplateExpressionCondition?>(
open class PyEditablePostfixTemplate(templateId: String, templateName: String, liveTemplate: TemplateImpl, example: String,
conditions: Set<PyPostfixTemplateExpressionCondition?>, topmost: Boolean, provider: PostfixTemplateProvider,
private val builtin: Boolean) : EditablePostfixTemplateWithMultipleExpressions<PyPostfixTemplateExpressionCondition?>(
templateId, templateName, liveTemplate, example, conditions, topmost, provider) {
constructor(templateId: String, templateName: String, templateText: String, example: String,
@@ -25,9 +25,7 @@ class PyEditablePostfixTemplate(templateId: String, templateName: String, liveTe
return expressions.filter { condition.value(it) }
}
override fun isBuiltin(): Boolean {
return myBuiltin
}
override fun isBuiltin(): Boolean = builtin
override fun isEditable(): Boolean {
return expressionConditions.all {
@@ -1,32 +1,22 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.codeInsight.postfix
import com.intellij.codeInsight.template.Template
import com.intellij.codeInsight.template.impl.MacroCallNode
import com.intellij.codeInsight.template.impl.TextExpression
import com.intellij.codeInsight.template.impl.VariableNode
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider
import com.intellij.codeInsight.template.postfix.templates.SurroundPostfixTemplateBase
import com.intellij.lang.surroundWith.Surrounder
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.util.Condition
import com.intellij.psi.PsiElement
import com.jetbrains.python.PyNames
import com.jetbrains.python.psi.PyExpression
import com.jetbrains.python.psi.types.PyABCUtil
import com.jetbrains.python.psi.types.TypeEvalContext
import com.jetbrains.python.refactoring.surround.surrounders.expressions.PyForExpressionSurrounder
import com.jetbrains.python.codeInsight.liveTemplates.CollectionElementNameMacro
class PyForPostfixTemplate(name: String, provider: PostfixTemplateProvider) : PyEditablePostfixTemplate(
name, name, "for \$VAR$ in \$EXPR$:\n \$END$", "for e in expr",
setOf(PyPostfixTemplateExpressionCondition.PyIterable()), false, provider, true) {
class PyForPostfixTemplate(name: String, provider: PostfixTemplateProvider) : SurroundPostfixTemplateBase(
name, "for e in expr", PyPostfixUtils.PY_PSI_INFO, PyPostfixUtils.selectorAllExpressionsWithCurrentOffset(iterableFilter), provider) {
companion object {
val iterableFilter: Condition<PsiElement> = Condition { element ->
if (!DumbService.isDumb(element.project)) {
val expression = element as PyExpression
val context = TypeEvalContext.codeCompletion(expression.project, expression.containingFile)
val type = context.getType(expression) ?: return@Condition false
return@Condition PyABCUtil.isSubtype(type, PyNames.ITERABLE, context)
}
return@Condition false
}
override fun addTemplateVariables(element: PsiElement, template: Template) {
super.addTemplateVariables(element, template)
val name = MacroCallNode(CollectionElementNameMacro())
name.addParameter(VariableNode("EXPR", null))
template.addVariable("VAR", name, TextExpression("e"), true)
}
override fun getSurrounder(): Surrounder = PyForExpressionSurrounder()
}
@@ -2,34 +2,12 @@
package com.jetbrains.python.codeInsight.postfix
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplateProvider
import com.intellij.codeInsight.template.postfix.templates.SurroundPostfixTemplateBase
import com.intellij.lang.surroundWith.Surrounder
import com.intellij.openapi.project.DumbService
import com.intellij.openapi.util.Condition
import com.intellij.psi.PsiElement
import com.jetbrains.python.PyNames
import com.jetbrains.python.psi.PyExpression
import com.jetbrains.python.psi.types.PyABCUtil
import com.jetbrains.python.psi.types.TypeEvalContext
import com.jetbrains.python.refactoring.surround.surrounders.expressions.PyLenExpressionStatementSurrounder
class PyLenPostfixTemplate(provider: PostfixTemplateProvider) : SurroundPostfixTemplateBase(
"len", DESCR, PyPostfixUtils.PY_PSI_INFO, PyPostfixUtils.selectorAllExpressionsWithCurrentOffset(sizedFilter), provider) {
override fun getSurrounder(): Surrounder = PyLenExpressionStatementSurrounder()
class PyLenPostfixTemplate(provider: PostfixTemplateProvider) :
PyEditablePostfixTemplate("len", "len", "len(\$EXPR$)\$END$", DESCR,
setOf(PyPostfixTemplateExpressionCondition.PyBuiltinLenApplicable()), false, provider, true) {
companion object {
const val DESCR = "len(expr)"
val sizedFilter: Condition<PsiElement> = Condition { element ->
if (!DumbService.isDumb(element.project)) {
val expression = element as PyExpression
val context = TypeEvalContext.codeCompletion(expression.project, expression.containingFile)
val type = context.getType(expression) ?: return@Condition false
return@Condition PyABCUtil.isSubtype(type, PyNames.SIZED, context)
}
return@Condition false
}
}
}
@@ -1,3 +1,3 @@
def f(a:list, b:list, c:int):
for i in (2 * (a + b)) * c:
for e in (2 * (a + b)) * c:
<caret>
@@ -1,3 +1,3 @@
def f(a:list):
for i in a:
for e in a:
<caret>
@@ -1,2 +1,2 @@
for i in []:
for e in []:
<caret>
@@ -1,3 +1,3 @@
def f(a:list, b:list, c:int):
for i in (2 * (a + b)) * c:
for e in (2 * (a + b)) * c:
<caret>
@@ -1,3 +1,3 @@
def f(a:list):
for i in a:
for e in a:
<caret>
@@ -1,2 +1,2 @@
for i in []:
for e in []:
<caret>
+1 -1
View File
@@ -1 +1 @@
foo = "something".len<caret>
foo = "something".len<caret>
+1 -1
View File
@@ -1 +1 @@
foo = len("something")
foo = len("something")
+1 -1
View File
@@ -1 +1 @@
{"first": 1, "second": 2}.len<caret>
{"first": 1, "second": 2}.len<caret>
+1 -1
View File
@@ -1 +1 @@
len({"first": 1, "second": 2})
len({"first": 1, "second": 2})
+1 -1
View File
@@ -1 +1 @@
[1, 2, 3, 4].len<caret>
[1, 2, 3, 4].len<caret>
+1 -1
View File
@@ -1 +1 @@
len([1, 2, 3, 4])
len([1, 2, 3, 4])
+1 -1
View File
@@ -2,4 +2,4 @@ class MyNotSizedClass:
pass
MyNotSizedClass().len<caret>
MyNotSizedClass().len<caret>
@@ -2,4 +2,4 @@ class MyNotSizedClass:
pass
MyNotSizedClass().len
MyNotSizedClass().len
+1 -1
View File
@@ -3,4 +3,4 @@ class MySizedClass:
pass
MySizedClass().len<caret>
MySizedClass().len<caret>
+1 -1
View File
@@ -3,4 +3,4 @@ class MySizedClass:
pass
MySizedClass.len<caret>
MySizedClass.len<caret>
@@ -3,4 +3,4 @@ class MySizedClass:
pass
MySizedClass.len
MySizedClass.len
+1 -1
View File
@@ -3,4 +3,4 @@ class MySizedClass:
pass
len(MySizedClass())
len(MySizedClass())
+1 -1
View File
@@ -1 +1 @@
"something".len<caret>
"something".len<caret>
+1 -1
View File
@@ -1 +1 @@
len("something")
len("something")
+1 -1
View File
@@ -1 +1 @@
(1, 2, 3, 4).len<caret>
(1, 2, 3, 4).len<caret>
+1 -1
View File
@@ -1 +1 @@
len((1, 2, 3, 4))
len((1, 2, 3, 4))