From 6c73fc9267f1789742ab63d46fe56f660d6127e0 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Wed, 14 Aug 2019 22:51:43 +0900 Subject: [PATCH 1/3] support refactoring fields by a keyword argument --- resources/META-INF/plugin.xml | 2 +- .../pydantic/PydanticFieldRenameFactory.kt | 116 +++++++++++++----- 2 files changed, 83 insertions(+), 35 deletions(-) diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 8f9d443f..1474d6ee 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -1,7 +1,7 @@ com.koxudaxi.pydantic Pydantic - 0.0.11 + 0.0.12 Koudai Aono @koxudaxi { + val pyClass = element.containingClass ?: return false + if (pyClass.isSubclass("pydantic.main.BaseModel", null)) return true + } + is PyKeywordArgument -> { + val pyClass = getPyClassByPyKeywordArgument(element) ?: return false + if (pyClass.isSubclass("pydantic.main.BaseModel", null)) return true + } } return false } @@ -40,41 +48,81 @@ class PydanticFieldRenameFactory : AutomaticRenamerFactory { } class PydanticFieldRenamer(element: PsiElement, newName: String) : AutomaticRenamer() { - init { - val pyTargetExpression = (element as? PyTargetExpression) - if (pyTargetExpression?.name != null) { - - val pyClass = pyTargetExpression.containingClass - ReferencesSearch.search(pyClass as @org.jetbrains.annotations.NotNull PsiElement).forEach { psiReference -> - val callee = PsiTreeUtil.getParentOfType(psiReference.element, PyCallExpression::class.java) - callee?.arguments?.forEach { argument -> - if (argument is PyKeywordArgument) { - if (argument.name == pyTargetExpression.name) { - myElements.add(argument) - } - } + val added = mutableSetOf() + when (element) { + is PyTargetExpression -> if (element.name != null) { + val pyClass = element.containingClass + addAllElement(pyClass, element.name!!, added) + suggestAllNames(element.name!!, newName) + } + is PyKeywordArgument -> if (element.name != null) { + val pyClass = getPyClassByPyKeywordArgument(element) + addAllElement(pyClass, element.name!!, added) + suggestAllNames(element.name!!, newName) + } + } + } + + private fun addAllElement(pyClass: PyClass?, elementName: String, added: MutableSet) { + if (pyClass == null) return + added.add(pyClass) + addClassAttributes(pyClass, elementName) + addKeywordArguments(pyClass, elementName) + pyClass.getAncestorClasses(null).forEach { ancestorClass -> + if (ancestorClass.qualifiedName != "pydantic.main.BaseModel") { + if (ancestorClass.isSubclass("pydantic.main.BaseModel", null) && + !added.contains(ancestorClass)) { + addAllElement(ancestorClass, elementName, added) } - return@forEach } - suggestAllNames(element.name!!, newName) + } + PyClassInheritorsSearch.search(pyClass, true).forEach { inheritorsPyClass -> + if (inheritorsPyClass.qualifiedName != "pydantic.main.BaseModel" && ! added.contains(inheritorsPyClass)) { + addAllElement(inheritorsPyClass, elementName, added) + } + } } - } - override fun getDialogTitle(): String { - return "Rename Fields" - } + private fun addClassAttributes(pyClass: PyClass, elementName: String) { + pyClass.classAttributes.forEach { pyTargetExpression -> + if (pyTargetExpression.name == elementName) { + myElements.add(pyTargetExpression) + } + } + } - override fun getDialogDescription(): String { - return "Rename field in hierarchy to:" - } + private fun addKeywordArguments(pyClass: PyClass, elementName: String) { + ReferencesSearch.search(pyClass as PsiElement).forEach { psiReference -> + val callee = PsiTreeUtil.getParentOfType(psiReference.element, PyCallExpression::class.java) + callee?.arguments?.forEach { argument -> + if (argument is PyKeywordArgument && argument.name == elementName) { + myElements.add(argument) - override fun entityName(): String { - return "Field" - } + } + } + } + } + override fun getDialogTitle(): String { + return "Rename Fields" + } + + override fun getDialogDescription(): String { + return "Rename field in hierarchy to:" + } - override fun isSelectedByDefault(): Boolean { - return true + override fun entityName(): String { + return "Field" + } + + override fun isSelectedByDefault(): Boolean { + return true + } } + } -} + +private fun getPyClassByPyKeywordArgument(pyKeywordArgument: PyKeywordArgument) : PyClass? { + val pyCallExpression = pyKeywordArgument.parent?.parent as? PyCallExpression ?: return null + return pyCallExpression.callee?.reference?.resolve() as? PyClass ?: return null +} \ No newline at end of file From 27a811bae9b8eb61386b9b2779426232f089ea50 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Wed, 14 Aug 2019 23:22:05 +0900 Subject: [PATCH 2/3] add change logs --- resources/META-INF/plugin.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 1474d6ee..cf72fd6e 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -3,7 +3,16 @@ Pydantic 0.0.12 Koudai Aono @koxudaxi - + version 0.0.12 +

Features

+
    +
  • Support refactoring fields by a keyword argument [#34]
  • +
  • Support refactoring super-classes and inheritor-classes [#34]
  • +
  • Support ellipsis(...) in fields [#34]
  • +
  • Support Schema in fields [#31]
  • +
+ ]]>
This plugin provides autocompletion support for pydantic

Features

From 9748a5908d75bbece285affed48378d107f6e280 Mon Sep 17 00:00:00 2001 From: Koudai Aono Date: Wed, 14 Aug 2019 23:31:13 +0900 Subject: [PATCH 3/3] update README --- README.md | 2 +- resources/META-INF/plugin.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 63bc67b3..194910a3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A JetBrains PyCharm plugin for [`pydantic`](https://github.com/samuelcolvin/pyda * Model-specific `__init__`-signature inspection and autocompletion for subclasses of `pydantic.BaseModel` * Model-specific `__init__`-arguments type-checking for subclasses of `pydantic.BaseModel` * Refactor support for renaming fields for subclasses of `BaseModel` - * (If the field name is refactored from the model definition, PyCharm will present a dialog offering the choice to automatically rename the keyword where it occurs in a model initialization call. + * (If the field name is refactored from the model definition or `__init__` call keyword arguments, PyCharm will present a dialog offering the choice to automatically rename the keyword where it occurs in a model initialization call. ## How to install: diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index cf72fd6e..af644229 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -22,7 +22,7 @@
  • Model-specific __init__-signature inspection and autocompletion for subclasses of pydantic.BaseModel
  • Model-specific __init__-arguments type-checking for subclasses of pydantic.BaseModel
  • Refactor support for renaming fields for subclasses of BaseModel
  • -
  • (If the field name is refactored from the model definition, PyCharm will present a dialog offering the choice to automatically rename the keyword where it occurs in a model initialization call.
  • +
  • (If the field name is refactored from the model definition or __init__ call keyword arguments, PyCharm will present a dialog offering the choice to automatically rename the keyword where it occurs in a model initialization call.
  • pydantic.dataclasses.dataclass